LoginSignup
21
34

More than 1 year has passed since last update.

さくらサーバで環境構築ゼロから。Railsアプリをデプロイするまで

Last updated at Posted at 2017-08-31

準備

http://vps.sakura.ad.jp/
上にアクセスし、VPSに申し込みを済ませる。
今回メモリは2GB。ストレージの種類 SSD(50GB)。サーバー台数1台。
支払い情報を入力して、申し込み完了!

1、カスタムOSのインストール

参考サイト

OSを選択

今回はubuntu20を選択。

VNCコンソールを開き、インストール作業を開始。

Installを選択し、日本語を選択。
ホスト名、ドメイン名、rootパスワード、ユーザーアカウントを入力。

ディスクパーティショニング ”ガイドーディスク全体を使う”→”全ての。。(初心者。。)”を選択→”パーティショニングの終了とディスクへの変更の書き込み”を選択

完了したらxで閉じ、さくらのコンソールの緑の起動ボタンを押してサーバーを起動。

2、ターミナルでsshログインし、鍵認証でログインする方法

参考サイト
ssh鍵認証参考サイト
※windowsの場合はSSHクライアントソフトをインストールする必要がある。

ターミナルを開き以下のコマンドを入力

ssh 上で設定したuser名@IPアドレス

yesと上で設定したパスワードを入力し、sshログイン完了。

sshログイン先で以下のコマンドを実行

※手元で自分の公開鍵をコピーしておく。ない人は作成。

$ mkdir .ssh
$ chmod 700 .ssh
$ vi .ssh/authorized_keys
自分の公開鍵をcopy&paste
$ chmod 600 .ssh/authorized_keys

sshログインの設定が完了したので、一旦ログアウトし、もう一度ログインしてみて、パスワード入力も求められずにログインできたらOK。

sudoをインストール

sshログイン先で

$ su -
# apt-get install sudo
# usermod -G sudo [ユーザー名]
# su [ユーザー名]

パスワード認証によるsshアクセスを禁止する方法

参考サイト

$ sudo su
# vi /etc/ssh/sshd_config
PasswordAuthentication no
# service sshd restart

不要なポート番号を閉じる

さくらのパケットフィルターで簡単に設定可能になったようです。

こちらを参考に進める。

上記の通り実行したら以下のように変更。

/etc/iptables/rules.v4
-A INPUT -p tcp -m state --state NEW --dport 1234 -j ACCEPT
↓
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
/etc/iptables/rules.v6
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

3、必要なものを諸々インストール

下記のコマンドをsshログイン先で実行。(必要、不要はご判断ください)

$ sudo apt-get update
$ sudo apt-get install vim git gcc g++ make nginx nodejs libssl-dev libreadline-dev zlib1g-dev

nginxのステータスを確認

$ sudo service nginx status
Active: active (running) since Sun 2017-08-27 16:18:48 JST; 35s ago

activeになっていればOK。
IPアドレスにアクセスしてみると、Welcome to nginx!の文字が。
(※さくらVPS パケットフィルタに注意)

4、rubyをrbenvでインストール

rbenvをインストール

$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ cd ~/.rbenv && src/configure && make -C src
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
一度ログインし直す。
$ ~/.rbenv/bin/rbenv init

rubyをインストール

$ rbenv install -l
インストールできるバージョンの一覧が表示される
$ rbenv install 2.4.1
$ rbenv global 2.4.1
$ rbenv exec gem install bundler

Mysql8.0をインストール

こちらを参考にUbuntu20にMysql8.0をインストール

Resolve dpkg status database is locked by another processエラー

インストール中にResolve dpkg status database is locked by another processエラーが出る場合はこちらを参考に解決

bundle install中にエラー

bundle install中にパッケージ不足でエラーが出る場合はこちらを参考に解決

Capistranoの導入

手元での設定

Gemfile

Gemfile
group :development do
  gem 'capistrano', '~> 3.9'
  gem 'capistrano-rails'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano3-puma'
end

gem 'puma'
$ bundle install
$ bundle exec cap install

Capfile

Capfile
require "capistrano/setup"
require "capistrano/deploy"

require 'capistrano/rails'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/puma'
require 'whenever/capistrano' # wheneverを利用している場合

install_plugin Capistrano::Puma  # Default puma tasks
install_plugin Capistrano::Puma::Systemd

require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }

config/deploy.rb

config/deploy.rb
# require 'whenever/capistrano'

lock "3.9.0"

set :application, "my_app_name" #自分のapp_nameに変更
set :repo_url, "git@example.com:me/my_repo.git" #自分のリポジトリURLに変更
set :deploy_to, '/var/www/my_app_name' #自分のapp_nameに変更
set :keep_releases, 3

set :rbenv_type, :user # or :system, depends on your rbenv setup
set :rbenv_ruby, '2.4.1'

set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all # default value

set :linked_dirs, %w{log tmp/backup tmp/pids tmp/cache tmp/sockets vendor/bundle}
set :linked_files, %w{config/credentials/production.key}

set :bundle_jobs, 4

namespace :deploy do
  task :restart do
    on roles(:web, :app, :db) do |host|
      execute "source /etc/environment"
    end
    invoke 'puma:restart'
  end
  after :finishing, 'deploy:cleanup'
end

config/deploy/production.rb

config/deploy/production.rb
set :branch, 'master'

role :app, %w{user_name@IPアドレス}
role :web, %w{user_name@IPアドレス}
role :db,  %w{user_name@IPアドレス}

server 'IPアドレス', user: 'user_name', roles: %w{web app db}

config/puma/production.rbを新規作成

config/puma/production.rb
rails_root = "/var/www/my_app_name/current" #自分app_nameに変更
shared_path = "/var/www/my_app_name/shared" #自分app_nameに変更

worker_processes 4
working_directory rails_root

#listen "#{rails_root}/tmp/puma.sock"
#pid "#{rails_root}/tmp/puma.pid"
listen File.expand_path('tmp/sockets/puma.sock', shared_path)
pid File.expand_path('tmp/pids/puma.pid', shared_path)

stderr_path "#{rails_root}/log/puma_error.log"
stdout_path "#{rails_root}/log/puma.log"

preload_app true

before_fork do |server, worker|
 ENV['BUNDLE_GEMFILE'] = File.expand_path('Gemfile', rails_root)
 old_pid = "#{server.config[:pid]}.oldbin"
 if File.exists?(old_pid) && server.pid != old_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
     # someone else did our job for us
   end
 end
end

after_fork do |server, worker|
 defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

database.yml

database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: 5
  username: root
  password:
  socket: <%= ["/run/mysqld/mysqld5.6.sock",
               "/tmp/mysqld.sock",
               "/var/run/mysqld/mysqld.sock",
               "/var/lib/mysql/mysql.sock",
               "/opt/local/var/run/mysql5/mysqld.sock"].detect {|s| File.exist?(s) } %>

production:
  adapter: mysql2
  encoding: utf8mb4
  pool: 5
  database: my_app_name_production
  username: <%= ENV["DB_USER"] %>
  password: <%= ENV["DB_PASSWORD"] %>

config/initializers/ar_innodb_row_format.rbの作成

以下のファイルを作成。参考サイト

config/initializers/ar_innodb_row_format.rb
ActiveSupport.on_load :active_record do
  module ActiveRecord::ConnectionAdapters
    module CreateTableWithInnodbRowFormat
      def create_table(table_name, options = {})
        table_options = options.merge(options: 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC')
        super(table_name, table_options) do |td|
          yield td if block_given?
        end
      end
    end

    class AbstractMysqlAdapter
      prepend CreateTableWithInnodbRowFormat
    end
  end
end

上記作成後、migrate。

$ bundle exec rake db:migrate

deployする

$ bundle exec cap production deploy

puma.serviceの設置

新規ファイルとして作成。以下内容を記述。参考
/etc/systemd/system/puma.service

[Unit]
Description=Puma HTTP Server
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/var/www/my_project_name/current
Environment=RAILS_ENV=production
Environment=USING_WEB_SERVER=true
ExecStart=/bin/bash -lc 'bundle exec puma -C config/puma/production.rb'
Restart=always

[Install]
WantedBy=sockets.target

ここで色々エラーを起こすと思いますが、エラー文に従って修正していけば完了。(投げやり

以下にエラーが出て追加で行ったことを記しておきます。
sshログイン後

yarnをinstall

こちらを参考にubuntu20にyarnをinstall

権限エラー

$ chown -R [ユーザー名]:[ユーザー名] /var/www/

nodeバージョンエラー

こちらを参考にnodenvをinstallし、nodeをバージョンアップ
バージョンアップ後、deploy時にnodeのバージョンが変わらない場合は以下を試す。

$ yarn config set ignore-engines true

master.key配置

こちらを参考にmaster.keyを設定。

mysqlにログインし、Database作成

こちらを参考にmysqlにログインできるようにしておく。

Rails7 Tailwindを利用している場合のエラー

以下のようなエラーが出た場合

TypeError: Object.fromEntries is not a function
$ sudo apt-get install npm
$ sudo npm i -g n
$ cd /var/www/my_project_name
$ n latest

参考

$ mysql -u root
mysql> CREATE DATABASE my_app_name_production;

MariaDBに以下の設定を記載

$vi /etc/mysql/mariadb.conf.d/50-server.cnf
[mysqld] の中に以下を記載

innodb_file_per_table = 1
innodb_file_format = Barracuda
innodb_large_prefix = 1
innodb_default_row_format = DYNAMIC # >= 5.7.9

Letsencryptをインストール

デプロ完了後、SSL化対応。
AレコードでDNS設定を済ませておく。
Ubuntu20用

  1. ssh先でcertbotをinstall
$ sudo apt install certbot python3-certbot-nginx
  1. localでnginxファイルを管理可能にするため、nginxフォルダを作成。
$ mkdir config/nginx
$ touch config/nginx/nginx.conf
$ touch config/nginx/my_project_name.conf
nginx.conf
user root;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

http {

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        # server_tokens off;

        # server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

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

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

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

        ##
        # Gzip Settings
        ##

        gzip on;

        # gzip_vary on;
        # gzip_proxied any;
        # gzip_comp_level 6;
        # gzip_buffers 16 8k;
        # gzip_http_version 1.1;
        # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}


#mail {
#       # See sample authentication script at:
#       # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
#       # auth_http localhost/auth.php;
#       # pop3_capabilities "TOP" "USER";
#       # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
#       server {
#               listen     localhost:110;
#               protocol   pop3;
#               proxy      on;
#       }
#
#       server {
#               listen     localhost:143;
#               protocol   imap;
#               proxy      on;
#       }
#}
my_project_name.conf
upstream puma {
    server unix:/var/www/my_project_name/current/tmp/sockets/puma.sock;
}

server {
    listen 80;
    root   /var/www/my_project_name/current/public;
    server_name www.my_project_domain_name;
    client_max_body_size 50M;

    # For letsencrypt
    location /.well-known/ {
        alias /var/www/my_project_name/.well-known/;
    }

    location / {
        return 301 https://my_project_domain_name$request_uri;
    }
}

server {
    listen 80;
    root   /var/www/my_project_name/current/public;
    server_name my_project_domain_name;
    client_max_body_size 50M;

    # For letsencrypt
    location /.well-known/ {
        alias /var/www/my_project_name/.well-known/;
    }
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443;
    server_name my_project_domain_name www.my_project_domain_name;
    root   /var/www/my_project_name/current/public;
    client_max_body_size 50M;

    if ($http_host = www.my_project_domain_name) {
        rewrite  (.*)  https://my_project_domain_name$1;
    }

    ssl on;
    ssl_certificate      /etc/letsencrypt/live/my_project_domain_name/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/my_project_domain_name/privkey.pem;
    ssl_session_timeout 5m;

    # Use https always

    add_header Strict-Transport-Security 'max-age=31536000;';

    # # 1. 暗号方式の設定
    # # (デフォルト) ssl_ciphers  HIGH:!aNULL:!MD5;
    # ssl_prefer_server_ciphers  on;
    # ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    # ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';

    # # 2. Logjam攻撃対策
    # ssl_dhparam /etc/nginx/ssl/dhparam.pem;

    # # 3. Enable OCSP (Online Certificate Status Protocol) Stapling
    # ssl_stapling on;
    # ssl_stapling_verify on;
    # resolver 8.8.4.4 8.8.8.8 valid=300s;
    # resolver_timeout 10s;

    location ~ ^/sitemap.xml.gz {
      root /var/www/my_project_name/current/public;
    }

    location ^~ /assets/ {
        # to serve pre-gzipped version
        gzip_static on;
        # for CloudFlare
        gzip_proxied any;
        gzip_vary on;
        expires 1y;
        add_header Cache-Control public;
        add_header ETag "";
        add_header Access-Control-Allow-Origin *;
        break;
    }

    location ~* \.(css|ico)$ {
        expires 24h;
        if ($args ~ [0-9]+) {
           expires max;
        }
        #access_log off;
    }

    try_files $uri @app;

    location @app {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://puma;
    }

    location ^~ /blog {
        alias          /var/www/wordpress;
        index index.php index.html index.htm;
        try_files try_files $uri $uri/ /blog/index.php;
        location ~ \.php$ {
            include fastcgi_params;
            fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
            fastcgi_index  index.php;
            fastcgi_param SCRIPT_FILENAME $request_filename;
        }
    }

    set $spambot 0;

    # リファラスパムボットそれぞれの判定
    if ($http_referer ~* (4webmasters.org) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (76brighton.co.uk) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (7makemoneyonline.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (7secretsearch.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (acads.net) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (floating-share-buttons.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (forum.topic65617124.darodar.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (free-social-buttons.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (www.event-tracking.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (www.Get-Free-Traffic-Now.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (chinese-amezon.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (e-buyeasy.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (erot.co) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (sa-live.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (get-free-social-traffic.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (free-floating-buttons.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (darodar.com) ) {
      set $spambot 1;
    }
    if ($http_referer ~* (snip.to) ) {
      set $spambot 1;
    }
    if ($spambot = 1) {
      return 403;
      break;
    }
}

git pushとデプロイを完了させる。
その後sshログイン先でシンボリックリンクを貼る。

$ sudo ln -sf /var/www/my_project_name/current/config/nginx/my_project_name.conf /etc/nginx/conf.d/my_project_name.conf
$ sudo ln -sf /var/www/my_project_name/current/config/nginx/nginx.conf /etc/nginx/nginx.conf

その後 my_project_name.confssl onからssl_session_timeout 5m;までを一度コメントアウトし、nginxをリスタート

$ sudo systemctl restart nginx

一度以下--dry-runコマンドでテストし、問題なければ--dry-runオプションを外し、実行。

$ sudo certbot certonly --webroot -w /var/www/my_project_name/ -d my_project_domain_name -d www.my_project_domain_name --dry-run

成功したら、コメントアウトを元に戻し、nginxをrestartすれば完了。

puma.sock failed (111: Connection refused系のエラーが出た場合

SSL化対応後にNginxで502 bad gatewayが出てしまった場合に確認する箇所。

$ vim /etc/systemd/system/puma.service
$ sudo systemctl enable /etc/systemd/system/puma.service
# pumaとnginxを再起動

他参考

ubuntuでopenssl最新版ダウンロード方法
ubuntu22.04でnginxインストールする方法
ubuntu22.04でLet's Encryptインストール

21
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
34