环境安装
更新源
# apt-get update
# apt-get install -y git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev libffi-dev imagemagick libmagickwand-dev
创建用户 deploy
adduser deploy
安装 rbenv
# su - deploy
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ echo 'source ~/.bashrc' >> ~/.bash_profile
$ source ~/.bash_profile
$ rbenv install 2.7.4
$ rbenv global 2.7.4
$ rbenv rehash
安装pg,即使使用外部数据库也需要安装pg,否则 bundle 不成功
# apt-get install -y postgresql libpq-dev
pg安装后如果不知道账号密码可以尝试重置
# 切换到postgres用户
# sudo -i -u postgres
# psql
# ALTER USER postgres WITH PASSWORD 'new_password';
# \q
# exit
capistrano 配置
Gemfile
group :development do
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'capistrano', '~> 3.10', require: false
gem 'capistrano3-puma', require: false
gem 'capistrano-rails', '~> 1.3', require: false
gem 'capistrano-rbenv', '~> 2.2', require: false
gem 'capistrano-sidekiq', '2.3.0', require: false
end
添加完后 安装
$ bundle exec cap install
会生成如下文件
├── Capfile
├── config
│ ├── deploy
│ │ ├── production.rb
│ │ └── staging.rb
│ └── deploy.rb
└── lib
└── capistrano
└── tasks
Capfile修改如下
# Load DSL and set up stages
require "capistrano/setup"
# Include default deployment tasks
require "capistrano/deploy"
# Load the SCM plugin appropriate to your project:
#
# require "capistrano/scm/hg"
# install_plugin Capistrano::SCM::Hg
# or
# require "capistrano/scm/svn"
# install_plugin Capistrano::SCM::Svn
# or
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git
# Include tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
# https://github.com/capistrano/rvm
# https://github.com/capistrano/rbenv
# https://github.com/capistrano/chruby
# https://github.com/capistrano/bundler
# https://github.com/capistrano/rails
# https://github.com/capistrano/passenger
#
# require "capistrano/rvm"
require "capistrano/rbenv"
# require "capistrano/chruby"
require "capistrano/bundler"
# require "capistrano/rails/assets"
# require "capistrano/rails/migrations"
# require "capistrano/passenger"
require 'capistrano/puma'
install_plugin Capistrano::Puma
install_plugin Capistrano::Puma::Systemd
require 'capistrano/rails/migrations'
# require 'capistrano/sidekiq'
# install_plugin Capistrano::Sidekiq # Default sidekiq tasks
# install_plugin Capistrano::Sidekiq::Systemd
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
config/deploy/production.rb
config/deploy/staging.rb
看需要修改
set :branch, 'develop'
server 'service_domain.com', user: 'deploy', roles: %w[app db web]
如果是多台机器,配置多个server
set :branch, 'develop'
server 'ip1', user: 'deploy', roles: %w[app db web]
server 'ip2', user: 'deploy', roles: %w[app db web]
config/deploy.rb
修改如下
lock '~> 3.17.1'
set :application, 'project_name'
set :repo_url, 'git_url'
set :rbenv_type, :user
set :rbenv_ruby, File.read('.ruby-version').strip
set :deploy_to, '/var/www/project'
set :rbenv_prefix,
"RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w[rake gem bundle ruby rails puma pumactl]
# set :sidekiq_service_unit_name, 'project-sidekiq'
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system', 'node_modules', 'tmp/videos',
'public/app'
append :linked_files, 'config/database.yml', 'config/master.key', '.env'
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system', 'tmp/videos', 'public/app',
'tmp/images'
namespace :deploy do
namespace :check do
before :linked_files, :set_master_key do
on roles(:app), in: :sequence, wait: 10 do
unless test("[ -f #{shared_path}/config/master.key ]")
upload! 'config/master.key', "#{shared_path}/config/master.key"
end
unless test("[ -f #{shared_path}/config/database.yml ]")
upload! 'config/database.yml', "#{shared_path}/config/database.yml"
end
end
end
end
end
# set :sidekiq_service_unit_user, :system
# set :sidekiq_user, :deploy
# Default value for :format is :airbrussh.
# set :format, :airbrussh
# You can configure the Airbrussh format using :format_options.
# These are the defaults.
# set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
# Default value for :pty is false
# set :pty, true
# Default value for :linked_files is []
# append :linked_files, "config/database.yml", 'config/master.key'
# Default value for linked_dirs is []
# append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "tmp/webpacker", "public/system", "vendor", "storage"
# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
# Default value for local_user is ENV['USER']
# set :local_user, -> { `git config user.name`.chomp }
# Default value for keep_releases is 5
# set :keep_releases, 5
# Uncomment the following to require manually verifying the host key before first deploy.
# set :ssh_options, verify_host_key: :secure
SSHKit.config.command_map[:sidekiq] = 'bundle exec sidekiq'
SSHKit.config.command_map[:sidekiqctl] = 'bundle exec sidekiqctl'
然后在目录服务器项目路径的 shared 目录下配置固定的配置文件
如 config/database.yml
, config/master.key
, .env
配置完后,在本地项目目录下可以运行布署
$ bundle exec cap production deploy
如果拉取代码失败,可能是ssh密钥有设置密码,可以考虑取消密码
$ cd ~/.ssh
$ ssh-keygen -f id_rsa -p
如果 bundle 卡住,可能是没有梯子,配置梯子
systemd 服务配置
Failed to restart puma_project_production.service: Unit puma_project_production.service not found
创建文件
/etc/systemd/system/puma_project_production.service
[Unit]
Description=Puma HTTP Server for project (production)
After=network.target
[Service]
Type=simple
User=deploy
WorkingDirectory=/var/www/project/current
# Support older bundler versions where file descriptors weren't kept
# See https://github.com/rubygems/rubygems/issues/3254
Environment=RBENV_ROOT=/home/deploy/.rbenv RBENV_VERSION=2.7.4
ExecStart=/home/deploy/.rbenv/bin/rbenv exec bundle exec --keep-file-descriptors puma -C /var/www/project/shared/puma.rb
ExecReload=/bin/kill -USR1 $MAINPID
StandardOutput=/var/www/project/shared/log/puma_access.log
StandardError=/var/www/project/shared/log/puma_error.log
Restart=always
RestartSec=1
SyslogIdentifier=puma
[Install]
WantedBy=multi-user.target
服务开机自动启动
sudo vim /etc/systemd/system/myprogram.service
sudo systemctl daemon-reload
sudo systemctl enable myprogram.service
# 查看哪些服务开启自动启动
systemctl list-unit-files --type=service --state=enabled
# 查看某个特定服务是否开机自启动
systemctl is-enabled servicename.service
nginx 配置
安装 nginx
# apt-get install nginx
HTTPS 配置证书
下载 certbot
sudo apt update
sudo apt install snapd
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# 会自动配置 nginx
sudo certbot --nginx
# 仅生成证书
sudo certbot certonly --nginx
sudo certbot renew --dry-run
nginx 层屏蔽特定的 rails 接口
可以用于响应负载均衡
# 会把 /path/to/api/xx也屏蔽
location /path/to/api {
add_header Content-Type text/plain;
return 200 "OK";
}
location ~ ^/path/to/api$ {
add_header Content-Type text/plain;
return 200 "OK";
}
问题记录
Exception while executing as deploy@xxx: sudo exit status: 1 (SSHKit::Runner::ExecuteError)
sudo stdout: Nothing written
sudo stderr: sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
是因为sshkit里有些需要sudo执行的命令,deploy没有权限
暂时可以这么解决,
sudo vim /etc/sudoers
deploy ALL=(ALL) NOPASSWD:ALL