多环境管理与 Vault 加密
High Contrast
Dark Mode
Light Mode
Sepia
Forest
2 min read491 words

多环境管理与 Vault 加密

核心问题:开发、测试、生产三套环境配置差异很大——怎样用同一套 Playbook 管理它们?数据库密码等敏感信息怎样安全存储?


多环境 Inventory 结构

推荐将每个环境放在独立目录,而不是在同一 Inventory 文件里用变量区分:

inventory/
├── staging/
│   ├── hosts.yml              # Staging 主机清单
│   └── group_vars/
│       ├── all.yml            # Staging 公共变量
│       ├── webservers.yml     # Staging Web 服务器变量
│       └── webservers/
│           ├── vars.yml       # 明文变量
│           └── vault.yml      # Vault 加密文件(密钥等)
├── production/
│   ├── hosts.yml
│   └── group_vars/
│       ├── all.yml
│       ├── webservers.yml
│       └── webservers/
│           ├── vars.yml
│           └── vault.yml
└── dev/
├── hosts.yml
└── group_vars/
└── all.yml

环境变量分层设计

# inventory/staging/group_vars/all.yml
# 所有主机公共变量(Staging 版本)
target_env: staging
aws_region: ap-southeast-1
log_level: debug
monitoring_enabled: false
# inventory/production/group_vars/all.yml
target_env: production
aws_region: ap-southeast-1
log_level: warn
monitoring_enabled: true
backup_retention_days: 30
# inventory/staging/group_vars/webservers/vars.yml
app_port: 3000
db_host: db-staging.internal
db_name: myapp_staging
db_user: app_staging
redis_host: redis-staging.internal
ssl_enabled: true
domain_name: staging.example.com
# db_password 不在这里 — 在 vault.yml 里
# inventory/production/group_vars/webservers/vars.yml
app_port: 3000
db_host: db-prod-primary.internal
db_name: myapp_production
db_user: app_prod
redis_host: redis-prod.internal
ssl_enabled: true
domain_name: api.example.com

ansible-vault:加密敏感变量

ansible-vault 对变量文件(或整个文件)进行 AES-256 加密,加密后文件可以安全提交到 Git。

基本操作

# 创建加密文件
ansible-vault create inventory/production/group_vars/webservers/vault.yml
# 编辑加密文件(自动解密→编辑→加密保存)
ansible-vault edit inventory/production/group_vars/webservers/vault.yml
# 加密已有明文文件
ansible-vault encrypt secrets.yml
# 解密查看(不保存,仅输出到终端)
ansible-vault view inventory/production/group_vars/webservers/vault.yml
# 修改 Vault 密码
ansible-vault rekey inventory/production/group_vars/webservers/vault.yml
# 解密并覆盖文件(危险操作,谨慎使用)
ansible-vault decrypt secrets.yml

vault.yml 内容(加密前)

# inventory/production/group_vars/webservers/vault.yml(明文,加密后不可读)
vault_db_password: "S3cur3P@ssw0rd_prod"
vault_app_secret: "xK9mL2nP8qR5vT7w"
vault_aws_access_key: "AKIAIOSFODNN7EXAMPLE"
vault_aws_secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
vault_slack_webhook: "https://hooks.slack.com/services/T.../B.../..."

加密后文件内容类似:

$ANSIBLE_VAULT;1.1;AES256
33613438343061643736353065313163393138313663333237623566623139313633336334303561
3934646562313332363537393664383064373636656231360a616336363733643730346538383663
...

命名约定:vault_ 前缀

# vars.yml — 明文,引用 vault 变量
db_password: "{{ vault_db_password }}"
app_secret: "{{ vault_app_secret }}"

调用者只需要知道 db_password,不需要知道它来自 vault。


运行时提供 Vault 密码

# 方式 1:交互式输入(适合手动执行)
ansible-playbook -i inventory/production/ site.yml --ask-vault-pass
# 方式 2:密码文件(适合 CI,文件不提交到 Git)
ansible-playbook -i inventory/production/ site.yml --vault-password-file ~/.vault_pass
# 方式 3:环境变量(CI 最常用)
export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass
ansible-playbook -i inventory/production/ site.yml

CI/CD 中管理 Vault 密码

# GitHub Actions 示例
- name: Deploy to Production
env:
ANSIBLE_VAULT_PASSWORD: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
run: |
echo "$ANSIBLE_VAULT_PASSWORD" > /tmp/vault_pass
ansible-playbook -i inventory/production/ site.yml \
--vault-password-file /tmp/vault_pass
rm /tmp/vault_pass

多 Vault ID:不同环境用不同密码

# 创建时指定 vault-id
ansible-vault create --vault-id staging@prompt staging_secrets.yml
ansible-vault create --vault-id prod@prompt prod_secrets.yml
# 运行时提供多个密码
ansible-playbook site.yml \
--vault-id staging@~/.vault_staging \
--vault-id prod@~/.vault_prod

最佳实践:完整多环境工作流

目录结构

project/
├── ansible.cfg
├── site.yml
├── roles/
│   └── app/
├── inventory/
│   ├── staging/
│   │   ├── hosts.yml
│   │   └── group_vars/
│   │       └── all/
│   │           ├── vars.yml     # 明文
│   │           └── vault.yml    # ansible-vault 加密
│   └── production/
│       ├── hosts.yml
│       └── group_vars/
│           └── all/
│               ├── vars.yml
│               └── vault.yml
└── .gitignore                   # 忽略 .vault_pass 文件!

.gitignore

.vault_pass
*.vault_pass
.env
*.pem
*.key

部署命令约定

# Staging 部署(CI 自动触发)
ansible-playbook -i inventory/staging/ site.yml \
--vault-password-file ~/.vault_staging
# Production 部署(需要人工审批后手动执行或 CD 触发)
ansible-playbook -i inventory/production/ site.yml \
--vault-password-file ~/.vault_prod \
--extra-vars "confirm_env=production"

对比:各种密钥管理方案

方案 适用场景 优点 缺点
ansible-vault 中小团队、简单项目 零依赖、随 Ansible 自带 密码轮换麻烦、不支持动态密钥
AWS Secrets Manager AWS 环境 自动轮换、细粒度 IAM 权限 费用(每个密钥 $0.40/月)
HashiCorp Vault 多云、大型企业 功能最强、动态密钥生成 维护复杂度高
SOPS + Age/GPG 现代 GitOps 文件级加密,与 Git 深度集成 学习成本较高

常见错误

错误 原因 解决
Decryption failed Vault 密码错误 确认密码文件或 --ask-vault-pass 输入正确
明文密码被提交到 Git 忘记加密 vault.yml 安装 git-secretspre-commit 钩子检测
vault.ymledit 后变为明文 ansible-vault edit 失败没有重新加密 检查文件开头是否有 $ANSIBLE_VAULT;
Staging 误用了 Production 密钥 多环境 vault 密码混用 严格区分 --vault-id staging@...prod@...

下一章Terraform 基础:HCL 语法与 Provider 配置