在實務上我們通常會有多個環境,分別是:
每個環境都有不同的需求:
物理隔離:
# 不同環境使用完全不同的基礎設施
inventories/
├── dev/
│ └── inventory.ini # 開發環境主機
├── staging/
│ └── inventory.ini # 測試環境主機
└── prod/
└── inventory.ini # 生產環境主機
邏輯隔離:
# 同一套基礎設施,使用不同的 namespace 或標籤
[webservers:children]
webservers_dev
webservers_staging
webservers_prod
[webservers_dev]
web01-dev.example.com
web02-dev.example.com
[webservers_staging]
web01-staging.example.com
[webservers_prod]
web01-prod.example.com
web02-prod.example.com
web03-prod.example.com
筆者比較推薦的多環境專案結構:
ansible-project/
├── inventories/
│ ├── dev/
│ │ ├── inventory.ini
│ │ └── group_vars/
│ │ ├── all.yml
│ │ └── webservers.yml
│ ├── staging/
│ │ ├── inventory.ini
│ │ └── group_vars/
│ │ ├── all.yml
│ │ └── webservers.yml
│ └── prod/
│ ├── inventory.ini
│ └── group_vars/
│ ├── all.yml
│ └── webservers.yml
├── roles/
│ └── webapp/
│ ├── tasks/main.yml
│ ├── templates/
│ └── defaults/main.yml
├── playbooks/
│ ├── site.yml
│ ├── deploy.yml
│ └── rollback.yml
└── ansible.cfg
開發環境配置 (inventories/dev/group_vars/all.yml
):
---
# 開發環境配置
environment: development
debug_mode: true
log_level: debug
# 資料庫配置
database:
host: dev-db.internal
port: 5432
name: webapp_dev
user: webapp_dev
pool_size: 5
# 應用程式配置
app:
replicas: 1
cpu_limit: "500m"
memory_limit: "512Mi"
features:
- debug_toolbar
- test_mode
# 外部服務
redis:
host: dev-redis.internal
port: 6379
db: 0
# 安全設定(開發環境較寬鬆)
security:
ssl_enabled: false
cors_origins: "*"
rate_limit: 1000
生產環境配置 (inventories/prod/group_vars/all.yml
):
---
# 生產環境配置
environment: production
debug_mode: false
log_level: warning
# 資料庫配置
database:
host: prod-db-cluster.internal
port: 5432
name: webapp_prod
user: webapp_prod
pool_size: 20
# 應用程式配置
app:
replicas: 3
cpu_limit: "2"
memory_limit: "2Gi"
features: []
# 外部服務
redis:
host: prod-redis-cluster.internal
port: 6379
db: 0
# 安全設定(生產環境嚴格)
security:
ssl_enabled: true
cors_origins: "https://webapp.example.com"
rate_limit: 100
使用 Ansible Vault 管理環境特定的秘密:
# 為不同環境建立不同的 vault 檔案
ansible-vault create inventories/dev/group_vars/vault.yml
ansible-vault create inventories/staging/group_vars/vault.yml
ansible-vault create inventories/prod/group_vars/vault.yml
生產環境 vault (inventories/prod/group_vars/vault.yml
):
---
vault_database_password: "super_secure_prod_password"
vault_api_key: "prod_api_key_12345"
vault_ssl_cert: |
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKoK/heBjcOuMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
...
-----END CERTIFICATE-----
vault_ssl_key: |
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDXVg8+
...
-----END PRIVATE KEY-----
在 group_vars/all.yml
中引用:
---
database_password: "{{ vault_database_password }}"
api_key: "{{ vault_api_key }}"
ssl_certificate: "{{ vault_ssl_cert }}"
ssl_private_key: "{{ vault_ssl_key }}"
主部署 Playbook (playbooks/site.yml
):
---
- name: Deploy webapp to all environments
hosts: webservers
become: yes
vars:
deployment_timestamp: "{{ ansible_date_time.epoch }}"
pre_tasks:
- name: Validate environment configuration
assert:
that:
- environment is defined
- environment in ['development', 'staging', 'production']
fail_msg: "Invalid or undefined environment"
- name: Backup current application (production only)
include_tasks: tasks/backup.yml
when: environment == 'production'
roles:
- role: webapp
vars:
app_version: "{{ deployment_version | default('latest') }}"
post_tasks:
- name: Verify application health
uri:
url: "http://{{ inventory_hostname }}:{{ app_port }}/health"
status_code: 200
retries: 5
delay: 10
- name: Send deployment notification
include_tasks: tasks/notify.yml
when: environment == 'production'
藍綠部署 (Blue-Green Deployment):
---
- name: Blue-Green Deployment
hosts: webservers
become: yes
serial: "{{ deployment_batch_size | default('50%') }}"
vars:
blue_version: "{{ current_version }}"
green_version: "{{ new_version }}"
tasks:
- name: Deploy green version
include_role:
name: webapp
vars:
app_version: "{{ green_version }}"
app_port: "{{ green_port }}"
- name: Health check green version
uri:
url: "http://{{ inventory_hostname }}:{{ green_port }}/health"
status_code: 200
retries: 10
delay: 30
- name: Switch traffic to green
template:
src: nginx_proxy.conf.j2
dest: /etc/nginx/conf.d/app.conf
notify: reload nginx
- name: Stop blue version
service:
name: "webapp-{{ blue_version }}"
state: stopped
when: green_health_check is succeeded
金絲雀發布 (Canary Deployment):
---
- name: Canary Deployment
hosts: webservers
become: yes
vars:
canary_percentage: "{{ canary_traffic | default(10) }}"
tasks:
- name: Deploy canary version to subset
include_role:
name: webapp
vars:
app_version: "{{ canary_version }}"
when: inventory_hostname in groups['canary_servers']
- name: Configure load balancer for canary
template:
src: lb_canary.conf.j2
dest: /etc/nginx/conf.d/canary.conf
vars:
canary_weight: "{{ canary_percentage }}"
- name: Monitor canary metrics
uri:
url: "http://{{ monitoring_host }}/api/canary/metrics"
method: GET
register: canary_metrics
until: canary_metrics.json.error_rate < 0.01
retries: 30
delay: 60
# 開發環境部署
ansible-playbook -i inventories/dev playbooks/site.yml
# 測試環境部署
ansible-playbook -i inventories/staging playbooks/site.yml \
--ask-vault-pass
# 生產環境部署(需要額外確認)
ansible-playbook -i inventories/prod playbooks/site.yml \
--ask-vault-pass \
--extra-vars "deployment_version=v2.1.0" \
--check # 先檢查不實際執行
# 生產環境實際部署
ansible-playbook -i inventories/prod playbooks/site.yml \
--ask-vault-pass \
--extra-vars "deployment_version=v2.1.0"
設計以下環境特定配置:
明天來聊聊如何跟 CI/CD 流程做整合!