Redmineのインストールをいい塩梅に半自動化する

日を置いてから 過去のブログのこれとかこれを参照してRedmine入れようとしてもなかなかうまいこと行かないことが多いのでむかついて、インストールをShellスクリプト化した。

プラグインとかcronの仕込みとかはできていないのと、SendGridでメール飛ぶか試せていないのでそこは今後、試していこうかなと思っていたりする。

ちなみに固定IP取ってDNSに登録すれば

AWS:EC2+RDS、EC2単体

GCP:GCE+CloudSQL(PostgreSQL)、GCE単体

上記2つで動くところまでは見れてます。


細かな部分はチューニングすれば、まぁ最小の労力でRedmine立てれるな。というより4年前に作っとけよって話ですよな・・・・。


◆Shellスクリプト

#!/bin/bash
SHELL_USER=yourexec_user
HOST_NAME=redmine-srv
POSTGRE_VER=postgresql-15
DB_VERSION=15
DATABASE=redmine
MASTER_USER=postgres
DB_USER=redmine
DB_PASSWORD=redmine
MAIL_ADDORESS=smtp.sendgrid.net
MAIL_DOMAIN=smtp.sendgrid.net
PORT=587
MAIL_DOMAIN=smtp.sendgrid.net
MAIL_USER_NAME=apikey
MAIL_PASSWORD=SG.XXXXXXXX
ATTATCHMENT_PATH=/var/www/redmine/files
RUBY_VER=3.2.2
WEB_USER=www-data
SERVER_NAME=web.example.com
SITE_URL=https://web.example.com/
DOCUMENT_ROOT=/var/www/html
ADMIN_MAIL=yourmail@example.com


sudo hostnamectl set-hostname ${HOST_NAME}

# ---------------------------------------------
# 1.nginxインストール
# ---------------------------------------------
read -p "## Do you want to install nginx? (y/n) [y]: " -e -i "y" nginxResponseinput
if [ "$nginxResponseinput" == "y" ] || [ "$nginxResponseinput" == "Y" ]; then
    echo "# install nginx process..."
    sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring
    curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
    echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
    http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list
    sudo apt update
    sudo apt install -y nginx
    sudo tee /etc/nginx/nginx.conf <<_EOF_
user ${WEB_USER};
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    server_tokens off;

    # MIME
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers off;

    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;

    ssl_session_tickets off;

    ssl_stapling on;
    ssl_stapling_verify on;

    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    ##
    # Security Headers
    ##

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    # Virtual Host Configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
_EOF_
    sudo systemctl enable nginx
    sudo systemctl start nginx
    echo "# Install nginx done!"
fi


# ---------------------------------------------
# 2.postgreSQLインストール RDS使う場合はnとする必要あり
# ---------------------------------------------
read -p "## Do you want to install postgress? (y/n) [y]: " -e -i "y" pgResponseinput
if [ "$pgResponseinput" == "y" ] || [ "$pgResponseinput" == "Y" ]; then
    echo "# install postgres process..."
    sudo apt install -y software-properties-common gnupg postgresql-common apt-transport-https lsb-release wget
    sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
    echo "deb https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -c -s) main" | sudo tee /etc/apt/sources.list.d/timescaledb.list
    wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | sudo apt-key add -
    sudo apt update
    sudo apt install -y ${POSTGRE_VER}
    #localhost Listenの有効化
    sudo sed -i "/#listen_addresses/a listen_addresses = 'localhost'" /etc/postgresql/${DB_VERSION}/main/postgresql.conf
    sudo systemctl restart postgresql
    echo "# Install Postgres done!"
fi
# 
# ホスト名設定
# 
read -p "Enter databasehost info: " -e -i "localhost" ENDPOINT
# 入力が空欄の場合は終了
if [ -z "$ENDPOINT" ]; then
    echo "Endpoint cannot be empty. Exiting..."
    exit 1
fi
# 
# ローカルホストDB使用の場合、外部DB使用の場合はn選択
# 
read -p "## Do you want to Setting localDataBase? (y/n) [y]: " -e -i "y" localDBResponseinput
if [ "$localDBResponseinput" == "y" ] || [ "$localDBResponseinput" == "Y" ]; then
    echo "# Please password input ${DB_PASSWORD} "
    sudo -u postgres createuser --pwprompt ${DB_USER}
    sudo -u postgres createdb -E UTF-8 -l ja_JP.UTF-8 -O ${DB_USER} ${DATABASE} -T template0 ${DATABASE}
    echo "# Setting Postgres done!"
fi
# 
# ローカルホスト以外にDBをインストールする場合
# 
echo "## RemoteDatabase Setting start"
read -p "## Do you want to use RemoteDatabase? (y/n) [y]: " -e -i "n" remoteDBResponseinput
if [ "$remoteDBResponseinput" == "y" ] || [ "$remoteDBResponseinput" == "Y" ]; then
    #
    # Postgreクライアントインストール
    #
    sudo apt install -y software-properties-common gnupg postgresql-common apt-transport-https lsb-release wget
    sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
    echo "deb https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -c -s) main" | sudo tee /etc/apt/sources.list.d/timescaledb.list
    wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | sudo apt-key add -
    sudo apt update
    sudo apt install -y postgresql-client
    #
    # リモートにDB作成
    # 
    echo "# please password input RemoteDatabase password !"
    psql --host=${ENDPOINT} --port=5432 --username=${MASTER_USER} <<_EOF
\set ON_ERROR_STOP on

-- ロールの作成
CREATE ROLE ${DB_USER} WITH PASSWORD '${DB_PASSWORD}' LOGIN;

-- ロールへの権限付与
GRANT ${DB_USER} TO ${MASTER_USER};

-- データベースの作成
CREATE DATABASE ${DATABASE} OWNER ${DB_USER};

_EOF
fi


#---------------------------------------------
# 3.Rubyインストール
#---------------------------------------------
read -p "## Do you want to install Ruby? (y/n) [y]: " -e -i "y" rbResponseinput
if [ "$rbResponseinput" == "y" ] || [ "$rbResponseinput" == "Y" ]; then
    echo "# install postgres process..."
    git clone https://github.com/sstephenson/rbenv.git .rbenv
    git clone https://github.com/sstephenson/ruby-build.git .rbenv/plugins/ruby-build
    echo 'export PATH=~/.rbenv/bin:$PATH' >> ~/.bashrc
    echo 'eval "$(rbenv init -)"' >> ~/.bashrc
    sudo apt install -y \
    curl \
    libssl-dev \
    libreadline-dev \
    zlib1g-dev \
    autoconf \
    bison \
    build-essential \
    libyaml-dev \
    libreadline-dev \
    libncurses5-dev \
    libffi-dev \
    libpq-dev \
    libgdbm-dev
    export PATH=~/.rbenv/bin:$PATH
    eval "$(rbenv init -)"
    rbenv install ${RUBY_VER}
    rbenv global ${RUBY_VER}
    echo "# Install Ruby done!"
fi


#---------------------------------------------
# 4.Redmine導入
#---------------------------------------------
read -p "## Do you want to install Redmine? (y/n) [y]: " -e -i "y" rmResponseinput
if [ "$rmResponseinput" == "y" ] || [ "$rmResponseinput" == "Y" ]; then
    echo "# install Redmine setting process..."
    sudo git clone https://github.com/redmine/redmine.git /var/www/redmine
    #
    # Database設定
    #
    sudo tee /var/www/redmine/config/database.yml <<_EOF_
# PostgreSQL configuration example
production:
  adapter: postgresql
  database: ${DATABASE}
  host: ${ENDPOINT}
  username: ${DB_USER}
  password: ${DB_PASSWORD}
  encoding: utf8
  pool: 5

development:
  adapter: postgresql
  database: ${DATABASE}
  host: ${ENDPOINT}
  username: ${DB_USER}
  password: ${DB_PASSWORD}
  encoding: utf8
  pool: 5
_EOF_

    #
    # 設定ファイル
    #
    sudo tee /var/www/redmine/config/configuration.yml <<_EOF_
default:
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      address: "${MAIL_ADDORESS}"
      port: ${PORT}
      domain: "${MAIL_DOMAIN}"
      authentication: :login
      user_name: ${MAIL_USER_NAME}
      password: ${MAIL_PASSWORD}
  attachments_storage_path: ${ATTATCHMENT_PATH}
_EOF_
    #
    # unicorn設定
    #
    echo '#Unicorn' | sudo tee -a /var/www/redmine/Gemfile
    echo 'gem "unicorn"' | sudo tee -a /var/www/redmine/Gemfile
    sudo tee /var/www/redmine/config/unicorn.rb <<_EOF_
# Railsのルートパスを求める。(RAILS_ROOT/config/unicorn.rbに配置している場合。)
rails_root = File.expand_path('../../', __FILE__)
# RAILS_ENVを求める。(RAILS_ENV毎に挙動を変更したい場合に使用。今回は使用しません。)
# rails_env = ENV['RAILS_ENV'] || "development"
 
# 追記に記載してます。入れた方がいいです。
ENV['BUNDLE_GEMFILE'] = rails_root + "/Gemfile"
 
# Unicornは複数のワーカーで起動するのでワーカー数を定義
# サーバーのメモリなどによって変更すること。
worker_processes 2
 
# 指定しなくても良い。
# Unicornの起動コマンドを実行するディレクトリを指定します。
# (記載しておけば他のディレクトリでこのファイルを叩けなくなる。)
working_directory rails_root
 
# 接続タイムアウト時間
timeout 30
 
# Unicornのエラーログと通常ログの位置を指定。
stderr_path File.expand_path('../../log/unicorn_stderr.log', __FILE__)
stdout_path File.expand_path('../../log/unicorn_stdout.log', __FILE__)
 
# Nginxで使用する場合は以下の設定を行う(※このパスをNginx側で設定したパスと揃えておく必要があります)。
listen File.expand_path('../../tmp/sockets/unicorn.sock', __FILE__)
# とりあえず起動して動作確認をしたい場合は以下の設定を行う。
#listen 8080
# ※「backlog」や「tcp_nopush」の設定もあるけど、よくわかって無い。
 
# プロセスの停止などに必要なPIDファイルの保存先を指定。
pid File.expand_path('../../tmp/pids/unicorn.pid', __FILE__)
 
# 基本的には`true`を指定する。Unicornの再起動時にダウンタイムなしで再起動が行われる。
preload_app true
# 効果なしとの記事を見たので、コメントアウト。
# GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true
 
# USR2シグナルを受けると古いプロセスを止める。
# 後述するが、記述しておくとNginxと連携する時に良いことがある。
before_fork do |server, worker|
  defined?(ActiveRecord::Base) and
      ActiveRecord::Base.connection.disconnect!
 
  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end
end
 
after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end
_EOF_
    sudo tee /var/www/redmine/lib/tasks/unicorn.rake <<_EOF_
namespace :unicorn do
  desc "Start unicorn for production env."
  task(:start) do
    config_path = "/var/www/redmine/config/unicorn.rb"
    sh "unicorn_rails -c #{config_path} -E production -D"
  end

  desc "Stop unicorn"
  task(:stop) { unicorn_signal :QUIT }

  desc "Restart unicorn with USR2"
  task(:restart) { unicorn_signal :USR2 }

  desc "Increment number of worker processes"
  task(:increment) { unicorn_signal :TTIN }

  desc "Decrement number of worker processes"
  task(:decrement) { unicorn_signal :TTOU }

  desc "Unicorn pstree (depends on pstree command)"
  task(:pstree) do
    sh "pstree '#{unicorn_pid}'"
  end

  def unicorn_signal signal
    Process.kill signal, unicorn_pid
  end

  def unicorn_pid
    begin
      File.read("/var/www/redmine/tmp/pids/unicorn.pid").to_i
    rescue Errno::ENOENT
      raise "Unicorn doesn't seem to be running"
    end
  end
end
_EOF_
# 起動スクリプト
echo "#!/bin/sh
# chkconfig: 345 90 20
# description: Redmine
# processname: unicorn_redmine

RAILS_ENV=production
SERVICE=redmine
USER=${WEB_USER}

RAILS_ROOT_DIR=\"/var/www/redmine\"

PID=\${RAILS_ROOT_DIR}/tmp/pids/unicorn.pid
UNICORN_CONF=\${RAILS_ROOT_DIR}/config/unicorn.rb

UNICORN_ALIVE=\$(ps aux | grep \"\${UNICORN_CONF}\" | grep -v grep | wc -l)

start()
{
  if [ \$UNICORN_ALIVE = 0 ]; then
    rm -f \$PID
  fi
  if [ -e \${PID} ]; then
    echo \"\${SERVICE} already started\"
    exit 1
  fi
  echo \"start \${SERVICE}\"
  sudo su -l \${USER} -c \"cd \${RAILS_ROOT_DIR} && bundle exec unicorn_rails -c \${UNICORN_CONF} -D\"
}

stop()
{
  if [ ! -e \${PID} ]; then
    echo \"\${SERVICE} not started\"
    exit 1
  fi
  echo \"stop \${SERVICE}\"
  kill -QUIT \`cat \${PID}\`
}

force_stop()
{
  if [ ! -e \${PID} ]; then
    echo \"\${SERVICE} not started\"
    exit 1
  fi
  echo \"stop \${SERVICE}\"
  kill -INT \`cat \${PID}\`
}

reload()
{
  if [ ! -e \${PID} ]; then
    echo \"\${SERVICE} not started\"
    start
    exit 0
  fi
  echo \"reload \${SERVICE}\"
  kill -USR2 \`cat \${PID}\`
}

restart()
{
  if [ -e \${PID} ]; then
    stop
    sleep 3
  fi
  start
}

case \"\$1\" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  force-stop)
    force_stop
    ;;
  reload)
    reload
    ;;
  restart)
    restart
    ;;
  *)
    echo \"Syntax Error: release [start|stop|force-stop|reload|restart]\"
    ;;
esac" | sudo tee /etc/init.d/unicorn
    sudo chmod 755 /etc/init.d/unicorn
    sudo tee /lib/systemd/system/unicorn.service <<_EOF_
[Unit]
Description=Unicorn Server
After=postgresql.service

[Service]
WorkingDirectory=/var/www/redmine
Environment=RAILS_ENV=production
SyslogIdentifier=unicorn
PIDFile=/var/www/redmine/tmp/pids/unicorn.pid

ExecStart=/home/${SHELL_USER}/.rbenv/shims/bundle exec "unicorn_rails -c /var/www/redmine/config/unicorn.rb -E production -D"
ExecStop=/usr/bin/kill -QUIT $MAINPID
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target
_EOF_
    # gemインストール
    sudo chown -R  ${SHELL_USER} /var/www/redmine
    cd /var/www/redmine
    gem install rubygems-update
    update_rubygems
    gem install bundler
    gem install daemon_controller rack passenger
    bundle update
    bundle install --path vendor/bundler --without development test
    bundle exec rake generate_secret_token
    bundle exec rake db:migrate RAILS_ENV=production
    bundle exec rails generate task unicornlsあー
    sudo chmod 755 /lib/systemd/system/unicorn.service
    sudo ln -s /lib/systemd/system/unicorn.service /etc/systemd/system/multi-user.target.wants/unicorn.service
    sudo systemctl start unicorn.service
    sudo systemctl enabled unicorn.service
    sudo mkdir ${ATTATCHMENT_PATH}
    sudo chown -R  ${WEB_USER} ${ATTATCHMENT_PATH}
    sudo chown -R  ${WEB_USER} /var/www/redmine
fi


# ---------------------------------------------
# 5.SSL設定 Let'sEncrypt使用しない場合はn
# ---------------------------------------------
sudo rm /etc/nginx/conf.d/default.conf

read -p "## Do you want to install Let's Encrypt? (y/n) [y]: " -e -i "y" sslResponseinput
if [ "$sslResponseinput" == "y" ] || [ "$sslResponseinput" == "Y" ]; then
    echo "# install Let's Encrypt process..."
    sudo mkdir /var/www/redmine/.well-known
    sudo chown -R ${WEB_USER}:${WEB_USER} /var/www/redmine
    #
    # Let'sEncrypt初回用
    #
    cat <<_EOF_ | sudo tee /etc/nginx/conf.d/local.conf
server {
    listen 80;
    server_name  ${SERVER_NAME};
    index  index.html index.htm index.php;
    root /var/www/redmine;
    location ^~ /.well-known/acme-challenge/ {
        allow all;
        root /var/www/redmine;
        default_type "text/plain";
        try_files \$uri =404;
    }
}   
_EOF_
    sudo systemctl stop nginx
    sudo systemctl start nginx
    sudo apt -y install certbot
    sudo certbot certonly --webroot -w /var/www/redmine -d ${SERVER_NAME} --agree-tos --email ${ADMIN_MAIL} --non-interactive
    #
    # config準備
    #
    cat <<_EOF_ | sudo tee /etc/nginx/conf.d/local.conf
upstream unicorn-unix-domain-socket {
    server unix:/var/www/redmine/tmp/sockets/unicorn.sock fail_timeout=0;
}

server {
        listen 80; # httpへのアクセスをhttpsへリダイレクトする
        listen [::]:80;
        server_name  ${SERVER_NAME};
        if (\$http_x_forwarded_proto != https) {
            return 301 https://\$host\$request_uri;
        }
        
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl on;
    if (\$host != "${SERVER_NAME}"){
        return 444;
    }
    server_name  ${SERVER_NAME};
    index  index.html index.htm index.php;
    client_max_body_size 1024M;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_certificate /etc/letsencrypt/live/${SERVER_NAME}/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/${SERVER_NAME}/privkey.pem; # managed by Certbot
    #include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    #ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    location / {
        root /var/www/redmine;
        access_log /var/log/nginx/redmine_access.log;
        error_log /var/log/nginx/redmine_error.log;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header Host \$http_host;
        proxy_pass http://unicorn-unix-domain-socket;
    }
    
    location ^~ /.well-known/acme-challenge/ {
        allow all;
        root /var/www/redmine;
        default_type "text/plain";
        try_files \$uri =404;
    }

}
_EOF_
    echo "# Install Let'sEncrypt done!"
fi

# ---------------------------------------------
# 自己証明書使用時
# ---------------------------------------------
read -p "## Do you want to use self cert? (y/n) [y]: " -e -i "n" selfcertResponseinput
if [ "$selfcertResponseinput" == "y" ] || [ "$selfcertResponseinput" == "Y" ]; then
    echo "# install setting self cert process..."
    sudo mkdir /etc/nginx/ssl
    sudo openssl genrsa -out /etc/nginx/ssl/server.key 2048
    sudo openssl req -new -key /etc/nginx/ssl/server.key -out /etc/nginx/ssl/server.csr
    sudo openssl x509 -days 3650 -req -signkey /etc/nginx/ssl/server.key -in /etc/nginx/ssl/server.csr -out /etc/nginx/ssl/server.crt
    # config準備
    cat <<_EOF_ | sudo tee /etc/nginx/conf.d/local.conf
upstream unicorn-unix-domain-socket {
    server unix:/var/www/redmine/tmp/sockets/unicorn.sock fail_timeout=0;
}

server {
    listen 80; # httpへのアクセスをhttpsへリダイレクトする
    listen [::]:80;
    server_name  "${SERVER_NAME}";
    if (\$http_x_forwarded_proto != https) {
        return 301 https://\$host\$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl on;
    if (\$host != "${SERVER_NAME}"){
        return 444;
    }
    server_name  "${SERVER_NAME}";
    index  index.html index.htm index.php;
    client_max_body_size 1024M;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    
    location / {
        root /var/www/redmine;
        access_log /var/log/nginx/redmine_access.log;
        error_log /var/log/nginx/redmine_error.log;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header Host \$http_host;
        proxy_pass http://unicorn-unix-domain-socket;
    }
}
_EOF_
    echo "# Setting self cert done!"
fi


#
# nginx再起動
#
sudo service nginx restart


というよりDockerですりゃいいだけの話なんですが・・・、中にはDockerじゃダメとかいうなんとやらなところもあるわけでゲフンゲフン

コメント

このブログの人気の投稿

証券外務員1種勉強(計算式暗記用メモ)

GASでGoogleDriveのサブフォルダとファイル一覧を出力する

マクロ経済学(IS-LM分析)