shell脚本是ci/cd中打通开发、测试、部署的关键执行层,需具备版本管理、幂等性、可回滚、可嵌入流水线等能力,并通过模块化、校验、环境变量、语法规范和构建/部署分离来保障可靠性。

Shell脚本不是CI/CD的“配角”,而是打通开发、测试、部署环节最直接的执行层。关键不在于写不写脚本,而在于它能否被版本管理、可验证、能回滚、可嵌入流水线任意阶段。
脚本必须具备幂等性与失败快速退出
每次执行都应产生相同结果,不因重复运行引发配置冲突或服务异常。例如部署Nginx时,先停服务再覆盖配置,不如用systemctl reload nginx——它本身是幂等的。所有关键命令后加|| exit 1,避免错误被静默忽略;用set -euxo pipefail开头统一约束执行行为。
- 用
[[ -f /etc/myapp/config.yaml ]] || cp config.default /etc/myapp/config.yaml代替无条件覆盖 - 服务启动后立即检查端口或健康接口:
curl -sf http://localhost:8080/health || exit 1 - 避免在脚本中写死IP或路径,改用环境变量或参数传入:
DEPLOY_ENV=${1:-prod}
模块化封装,按职责拆分功能单元
把日志归档、Git拉取、依赖安装、服务启停等逻辑分别写成独立脚本(如lib/log.sh、lib/git.sh),主部署脚本只做流程编排。这样既方便复用,也利于单测和调试。
- 每个模块提供
help函数说明用途和参数 - 用
source lib/*.sh加载,避免复制粘贴导致版本不一致 - 关键模块加简单断言,比如
require_command jq确保依赖存在
集成进CI/CD前先本地验证与格式规范
脚本提交前必须通过语法检查和风格校验,否则CI阶段失败会拖慢整个流水线。推荐使用shfmt统一缩进与换行,配合shellcheck扫描潜在陷阱(如未引号变量、未定义变量)。
- 在Git pre-commit钩子中加入:
shfmt -w *.sh && shellcheck *.sh - Jenkins/GitLab CI中增加stage:
lint-shell: shfmt -d *.sh || exit 1 - 用
shfmt -l生成diff报告,便于Code Review时聚焦变更点
部署阶段明确区分构建产物与运行时操作
CI阶段负责产出稳定制品(如Docker镜像、tar包、二进制文件),CD阶段只负责把制品放到目标环境并启动。Shell脚本应专注后者:解压、校验SHA256、替换模板变量、启动服务、等待就绪。
- 不建议在部署脚本里执行
npm install或go build——这些属于构建阶段 - 用
curl -o app.tar.gz $ARTIFACT_URL拉取制品,比git clone更可靠 - 部署后执行冒烟测试脚本(如
test/smoke.sh),失败即触发自动回滚











