Capistrano 3.20 配置参数详解
Capistrano 3.20.1 配置体系全解:Capfile/deploy.rb/stage 三层加载、doctor 实测参数清单、deploy_to 目录结构、linked_files/linked_dirs、SSH 与角色定义、自定义任务与部署钩子,附生产配置模板与排障实录。
Capistrano 3.20 配置参数详解
目标:逐层讲清 Capistrano 3.20.1 的配置体系——Capfile / config/deploy.rb / config/deploy/<stage>.rb 三层文件、全部核心参数(含默认值与实测回显)、server/role 定义、SSH 选项、自定义任务与部署钩子。已在 Ubuntu 22.04.5 控制机(Capistrano 3.20.1)+ 2 台 Ubuntu 22.04 目标服务器(web1.local / web2.local)实测通过(2026-10-27)。
一、环境信息
- 控制机:Ubuntu 22.04.5 LTS,Ruby 3.0.2p107,Capistrano 3.20.1(
/usr/local/bin/cap),Rake 13.0.6 - 目标服务器:web1.local、web2.local(Ubuntu 22.04.5,OpenSSH + git,演示账号
admin/q1w2e3r4,SSH 密钥免密) - 演示项目:
/root/demo-deploy(cap install STAGES=production生成),演示应用仓库http://controller.local:8000/demo-app.git - 部署路径:
/var/www/demo-app;stage:production
二、前置条件
- 控制机已安装 Capistrano 3.20.1,安装与排障全过程见《Ubuntu 22.04 安装 Capistrano 3.20 指南》。
- 目标服务器已启动 SSH 服务、创建部署账号
admin、控制机公钥已写入/home/admin/.ssh/authorized_keys,且控制机可免密登录:
ssh admin@web1.local "hostname; whoami"
ssh admin@web2.local "hostname; whoami"
实测回显:
web1.local
admin
web2.local
admin
- 项目目录已用
cap install STAGES=production初始化(生成 Capfile、config/deploy.rb、config/deploy/production.rb)。 - 目标服务器已预建部署目录与 shared 资源(linked 机制依赖):
mkdir -p /var/www/demo-app/shared/config /var/www/demo-app/shared/log
echo "env=production" > /var/www/demo-app/shared/config/app.conf
chown -R admin:admin /var/www/demo-app
三、配置体系与参数查看方式
3.1 三层配置文件加载顺序
Capistrano 项目固定 4 个配置落点,加载顺序为:
Capfile:入口文件,负责require各功能模块(setup、deploy、SCM 插件、第三方 gem、自定义任务),基本不改;config/deploy.rb:全局参数,对所有 stage 生效(应用名、仓库地址、deploy_to、linked_* 等);config/deploy/<stage>.rb:stage 级参数与服务器清单(每台服务器的地址/用户/角色、stage 特有变量),同名变量覆盖 deploy.rb;lib/capistrano/tasks/*.rake:自定义任务,Capfile 末尾的Dir.glob自动加载。
3.2 查看最终生效参数:cap <stage> doctor
配置是分层覆盖的,想看「某 stage 下所有参数的最终值」,用内置 doctor 任务:
cd /root/demo-deploy
cap production doctor
实测回显(完整照抄):
Environment
Ruby ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-gnu]
Rubygems 3.3.5
Bundler N/A
Command /usr/local/bin/cap production doctor
Gems
capistrano 3.20.1
airbrussh 1.6.2
rake 13.0.6 (update available)
sshkit 1.25.1
net-ssh 8.0.4
Variables
:application "demo-app"
:branch "master"
:default_env {}
:deploy_to "/var/www/demo-app"
:format :airbrussh
:format_options {:command_output=>true, :log_file=>"log/capistrano.log", :color=>:auto, :truncate=>:auto}
:git_environmental_variables {:git_askpass=>"/bin/echo", :git_ssh=>"/tmp/git-ssh-126704dfe3244a4761df.sh"}
:git_max_concurrent_connections 10
:git_shallow_clone false
:git_wait_interval 0
:git_wrapper_path "/tmp/git-ssh-126704dfe3244a4761df.sh"
:keep_releases 3
:linked_dirs ["log"]
:linked_files ["config/app.conf"]
:local_user nil
:log_level :debug
:pty false
:repo_url "http://controller.local:8000/demo-app.git"
:ssh_options {:verify_host_key=>:never}
:stage :production
:tmp_dir "/tmp"
Servers (2)
admin@web1.local [:app, :web]
admin@web2.local [:app, :web]
这份输出同时是排障利器:环境、gem 版本、最终变量、生效服务器四段一次看全。也可只查变量段:cap production doctor:variables。
3.3 参数赋值三种写法
set :application, "demo-app" # 直接赋值
append :linked_dirs, "log", "tmp" # 向数组型参数追加元素(不覆盖已有)
ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp # 交互询问,默认值取当前分支
set 覆盖、append 追加、ask 部署时暂停等待输入(CI 场景慎用);读取用 fetch(:application)。set 还支持惰性求值块:set :local_user, -> { \git config user.name`.chomp }`。
四、参数总览
| 参数 | 默认值 | 类别 | 说明 |
|---|---|---|---|
application | 无(必设) | 应用 | 应用名,影响 deploy_to 默认路径 |
repo_url | 无(必设) | 仓库 | 目标服务器可访问的仓库地址 |
branch | master | 仓库 | 部署分支/tag |
deploy_to | /var/www/<application> | 路径 | 远端部署根目录 |
keep_releases | 5 | 路径 | 保留的历史 release 数 |
tmp_dir | /tmp | 路径 | 远端临时目录(git wrapper 等) |
linked_files | [] | 链接 | 从 shared 软链进每个 release 的文件 |
linked_dirs | [] | 链接 | 从 shared 软链进每个 release 的目录 |
format | :airbrussh | 输出 | 日志格式器 |
format_options | 见 5.5 | 输出 | 输出明细/日志文件/颜色/截断 |
log_level | :debug | 输出 | 日志级别(log 文件用) |
pty | false | SSH | 是否分配伪终端(sudo 场景需要) |
ssh_options | {} | SSH | 透传给 Net::SSH 的连接选项 |
local_user | ENV['USER'] | 审计 | 写入 revisions.log 的操作者名 |
git_shallow_clone | false | Git | 镜像仓库是否浅克隆(--depth 1) |
git_max_concurrent_connections | 10 | Git | git 操作最大并发连接数 |
git_wait_interval | 0 | Git | git 操作间隔秒数(限流用) |
default_env | {} | 环境 | 远端命令注入的环境变量 |
五、核心参数分类详解
5.1 应用与仓库参数
set :application, "demo-app"
set :repo_url, "http://controller.local:8000/demo-app.git"
set :branch, "master"
application:应用名。不显式设deploy_to时默认部署到/var/www/<application>,多应用同机部署就靠不同 application/deploy_to 隔离。repo_url:在每台目标服务器上执行 clone,所以地址必须是目标服务器可达的(不是控制机本地路径)。本文用内网 HTTP 裸仓库演示;生产多为git@github.com:org/repo.git(SSH 协议,目标服务器需配好部署密钥或 agent forwarding)。branch:支持分支名、tag、commit SHA。ask :branch, ...可在部署时交互输入。
验证仓库可达性(在部署前单独跑):
cap production git:check
实测回显(节选):
00:00 git:check
01 git ls-remote http://controller.local:8000/demo-app.git HEAD
01 7dd8661a9547f2b51b927db290a9499acd8faa93 HEAD
✔ 01 admin@web2.local 0.060s
01 7dd8661a9547f2b51b927db290a9499acd8faa93 HEAD
✔ 01 admin@web1.local 0.063s
5.2 部署目录结构(deploy_to 之下)
deploy_to 是理解全部 linked/keep_releases 机制的钥匙。实测部署成功后 web1 上的结构:
ls -la /var/www/demo-app/
total 24
drwxr-xr-x 5 admin admin 4096 Oct 27 09:15 .
drwxr-xr-x 3 root root 4096 Oct 27 09:15 ..
lrwxrwxrwx 1 admin admin 41 Oct 27 09:15 current -> /var/www/demo-app/releases/20261027091548
drwxrwxr-x 3 admin admin 4096 Oct 27 09:15 releases
drwxrwxr-x 7 admin admin 4096 Oct 27 09:15 repo
-rw-r--r-- 1 admin admin 99 Oct 27 09:15 revisions.log
drwxr-xr-x 4 admin admin 4096 Oct 27 09:15 shared
releases/<时间戳>:每次部署一个时间戳目录(git archive导出),回滚=把current软链指回上一个;current:指向当前 release 的软链,部署全部步骤成功后最后一步才切换,中途失败不影响线上;repo:远端镜像仓库(git clone --mirror),每次部署先git remote update --prune再 archive 出新 release;shared:跨 release 持久内容(linked_files/linked_dirs 的实体);revisions.log:每次部署/回滚追加一行审计日志。
keep_releases 控制保留份数(默认 5,本文演示设为 3),超出后部署末尾自动清理最旧的,实测回显:
00:00 deploy:cleanup
Keeping 3 of 4 deployed releases on web1.local
Keeping 3 of 3 deployed releases on web2.local
01 rm -rf /var/www/demo-app/releases/20261027091548
💡 技术拓展:「发布切换」在部署工具里有两种主流设计——Capistrano 的软链切换(current 指哪用哪,切换即回滚,零停机易实现)与整目录替换/重启加载(无历史 release,回滚要重新发布)。本文采用前者,也是 Capistrano 的标志性设计。
5.3 链接资源参数:linked_files 与 linked_dirs
append :linked_files, "config/app.conf"
append :linked_dirs, "log"
linked_files:不入库的敏感/环境文件(数据库配置、密钥),实体放shared/config/app.conf,每个 release 里是指向它的软链;linked_dirs:日志、上传文件、缓存等跨发布保留的目录,实体在shared/下。
实测部署时的链接动作(逐行照抄):
00:00 deploy:symlink:linked_files
01 mkdir -p /var/www/demo-app/releases/20261027091548/config
✔ 01 admin@web2.local 0.008s
02 ln -s /var/www/demo-app/shared/config/app.conf /var/www/demo-app/releases/20261027091548/config/app.conf
✔ 02 admin@web1.local 0.004s
✔ 02 admin@web2.local 0.006s
00:00 deploy:symlink:linked_dirs
02 ln -s /var/www/demo-app/shared/log /var/www/demo-app/releases/20261027091548/log
✔ 02 admin@web2.local 0.008s
✔ 02 admin@web1.local 0.006s
⚠️ 排障实录:若某台目标服务器缺少 linked_files 实体,部署会在
deploy:check:linked_files阶段中止。实测把 web2 的shared/config/app.conf移走后部署,真实报错:00:00 deploy:check:linked_files ERROR linked file /var/www/demo-app/shared/config/app.conf does not exist on web2.local解决:在该机补建文件(
echo "env=production" > /var/www/demo-app/shared/config/app.conf)后重新部署即可。这正是 deploy:check 系列任务存在的意义——先检查、后发布。
5.4 SSH 连接参数与服务器/角色定义
stage 文件的核心是服务器清单,两种等价写法:
# server 写法(推荐,逐台声明属性)
server "web1.local", user: "admin", roles: %w{app web}
server "web2.local", user: "admin", roles: %w{app web}
# role 写法(按角色聚组)
role :web, %w{admin@web1.local admin@web2.local}
role :app, %w{admin@web1.local admin@web2.local}
常用 ssh_options(透传 Net::SSH):
set :ssh_options, {
keys: %w(/root/.ssh/id_ed25519), # 指定私钥(默认自动找 ~/.ssh 下常见密钥)
forward_agent: false, # agent 转发,目标机要再跳 clone 私有仓库时设 true
auth_methods: %w(publickey password),
verify_host_key: :never # 首次连接不交互确认 host key;生产建议 :secure
}
verify_host_key::never(跳过校验,CI/内网演示方便)、:secure(严格校验 known_hosts,首次需手工ssh一次入库)、:accept_new_or_local_tunnel(自动接受新 key)。本文演示环境用:never;有安全审计要求的环境用:secure并提前ssh-keyscan入库。pty:远端需要 sudo 交互(sudo 默认 requiretty)时设set :pty, true,其余场景保持 false。- 逐台覆盖:某台机特殊就在 server 行内嵌
ssh_options:,如server "web2.local", user: "admin", roles: %w{app web}, ssh_options: { port: 2222 }。 primary:role :db, %w{admin@db.local}, primary: true标主库(跑迁移等只一次的任务用on roles(:db), primary: true)。
💡 技术拓展:角色(role)是 Capistrano 的多机调度核心——
on roles(:app)决定任务落在哪些机上。常见角色分工:app(应用进程)、web(静态/前端/反向代理上游)、db(跑数据库迁移的那台)。本文两台目标服务器同为app+web;生产可按机器职责拆开,同名任务自动只落在对应角色主机上。
5.5 输出与日志参数
set :format, :airbrussh
set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
set :log_level, :debug
command_output: true:把远端命令的 stdout/stderr 回显到终端(本文所有回显里的01 ecddef79... HEAD就是这么来的);log_file:控制机本地留一份完整执行日志,实测/root/demo-deploy/log/capistrano.log逐条记录了每条命令的 DEBUG/INFO 行:
DEBUG [42c03e23] Command: cd /var/www/demo-app/releases && /usr/bin/env echo "Branch master (at 0f135f586e7f5b1df8fbd7f7f7343471c0bcf313) deployed as release 20261027092158 by admin" >> /var/www/demo-app/revisions.log
INFO [42c03e23] Finished in 0.007 seconds with exit status 0 (successful).
truncate: :auto:超长命令截断显示(日志文件里仍完整)。
5.6 Git 行为参数
set :git_shallow_clone, false # true 时镜像仓库 --mirror --depth 1,省空间但不能切任意历史
set :git_max_concurrent_connections, 10 # 多机并发 git clone/update 的连接上限
set :git_wait_interval, 0 # 每个 git 连接间隔秒数,保护脆弱的 git 服务时限流用
set :git_environmental_variables, {} # 给远端 git 命令注入环境变量
set :git_wrapper_path, "/tmp/git-ssh-<随机>.sh" # 自动生成的 git-ssh 包装脚本位置(tmp_dir 派生)
doctor 实测值见 3.2 回显。包装脚本的作用是屏蔽 git 的交互式询问(git_askpass=/bin/echo),避免部署卡在密码提示上。
5.7 自定义任务与部署钩子
Capistrano 任务是 Rake 任务 + SSHKit DSL。在 lib/capistrano/tasks/app.rake 写自定义任务(本文实测文件):
namespace :app do
desc "Restart demo app (write restart timestamp into shared/log)"
task :restart do
on roles(:app) do
execute :echo, "\"restarted at \$(date +%s)\" >> #{shared_path}/log/restart.log"
end
end
desc "Show current release revision"
task :show_revision do
on roles(:app) do
info capture(:cat, "#{current_path}/REVISION")
end
end
end
after "deploy:published", "app:restart"
DSL 要点:on roles(:app) 选定目标机;execute 远端执行命令;capture 执行并取回 stdout;info 打印日志;shared_path/current_path/release_path 为内置路径变量。after "deploy:published", "app:restart" 把重启挂进部署流程。
部署主流程的钩子顺序(cap -T 可见全部内置任务):deploy:starting → started → updating → updated → publishing → published → finishing → finished,可在任意节点 before/after 挂自定义任务。
单独调用自定义任务实测:
cap production app:restart
cap production app:show_revision
实测回显:
00:00 app:restart
01 echo "restarted at $(date +%s)" >> /var/www/demo-app/shared/log/restart.log
✔ 01 admin@web2.local 0.109s
✔ 01 admin@web1.local 0.115s
===
00:00 app:show_revision
e417cb264e541ce26b0a2d667ece77e71a2efa15
e417cb264e541ce26b0a2d667ece77e71a2efa15
web1 上验证重启日志真实写入:cat /var/www/demo-app/shared/log/restart.log → restarted at 1793092682。
lock "~> 3.20.1"(deploy.rb 顶部):cap 启动时校验运行版本在此区间内,不符直接报错退出,防止团队混用 3.19/3.20 行为差异。
六、生产配置模板(本文实测全套)
适用场景:16GB 内存控制机、目标服务器若干(同构)、内网/公网 SSH 可达、git 仓库目标机可达、保留 3 份历史 release 的中小型 Web 应用发布。
config/deploy.rb:
# config valid for current version and patch releases of Capistrano
lock "~> 3.20.1"
set :application, "demo-app"
set :repo_url, "http://controller.local:8000/demo-app.git"
set :branch, "master"
set :deploy_to, "/var/www/demo-app"
set :format, :airbrussh
set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
append :linked_dirs, "log"
append :linked_files, "config/app.conf"
set :keep_releases, 3
set :local_user, "admin"
set :ssh_options, { verify_host_key: :never }
config/deploy/production.rb:
server "web1.local", user: "admin", roles: %w{app web}
server "web2.local", user: "admin", roles: %w{app web}
set :stage, :production
关键取舍解读:公开仓库协议按目标机可达性选(本文 HTTP 内网裸仓库;私有仓库换 SSH 协议+部署密钥);verify_host_key 生产改 :secure;keep_releases 3 份兼顾回滚空间与磁盘;local_user 显式设置保证审计日志署名(原因见七、Q1)。多 stage 时复制 production.rb 为 staging.rb,改服务器清单即可,cap staging deploy 独立发布。
七、常见问题
Q1:revisions.log 里部署者署名是空的(deployed as release ... by 后面没人名)?
原因:local_user 默认取 ENV['USER'],控制机以非登录 shell(cron、CI agent)运行时该变量为空。实测修复前后对比(revisions.log 末两行):
Branch master (at e417cb264e541ce26b0a2d667ece77e71a2efa15) deployed as release 20261027091726 by
Branch master (at 0f135f586e7f5b1df8fbd7f7f7343471c0bcf313) deployed as release 20261027092158 by admin
修复:deploy.rb 加 set :local_user, "admin"(或 -> { \git config user.name`.chomp }`)。
Q2:linked_files 报错 ERROR linked file ... does not exist on <host>?
部署在 deploy:check:linked_files 阶段中止。逐台补建 shared 下的实体文件(见 5.3 排障实录),或先在目标机手工 mkdir -p + 落文件再部署。可用 cap production deploy:check 在正式发布前预检。
Q3:首次部署卡在 host key 确认或直接连接失败?
verify_host_key 默认策略要求 known_hosts 已有目标机记录。内网演示设 :never;生产推荐 :secure 并预先用 ssh-keyscan web1.local >> /root/.ssh/known_hosts 入库。
Q4:远端命令需要 sudo,tty 报错 sudo: sorry, you must have a tty to run sudo?
设 set :pty, true 分配伪终端;更彻底的做法是给部署账号配置免密 sudo 白名单(/etc/sudoers.d/deploy),避免交互。
Q5:多 stage 怎么组织?
cap install STAGES=staging,production 一次生成多个 stage 文件;公共参数留在 deploy.rb,stage 差异(服务器、分支、deploy_to)写在各自 stage 文件里覆盖。调用时 cap staging deploy / cap production deploy。
八、参考链接
- 官方配置文档:http://capistranorb.com/documentation/getting-started/configuration/
- 官方目录结构说明:https://capistranorb.com/documentation/getting-started/structure/
- SSHKit DSL(on/execute/capture):https://github.com/capistrano/sshkit
- Net::SSH 选项文档:http://net-ssh.github.io/net-ssh/classes/Net/SSH.html