Skip to content

Ansible自动化

📋 概述

Ansible是一个开源的自动化平台,用于配置管理、应用部署和任务执行。它使用简单的YAML语法,无需在目标主机上安装代理,通过SSH连接进行管理。

🎯 学习目标

  • 掌握Ansible的核心概念和架构
  • 学会编写Playbook和角色
  • 了解变量管理和模板使用
  • 掌握Node.js应用的自动化部署

📚 Ansible核心概念

架构组件

mermaid
graph TB
    A[控制节点] --> B[清单文件]
    A --> C[Playbook]
    A --> D[角色]
    A --> E[模块]
    
    B --> F[目标主机1]
    B --> G[目标主机2]
    B --> H[目标主机3]
    
    C --> I[任务]
    C --> J[处理器]
    C --> K[变量]

清单文件(Inventory)

ini
# inventory/hosts.ini
[webservers]
web1 ansible_host=10.0.1.10 ansible_user=ubuntu
web2 ansible_host=10.0.1.11 ansible_user=ubuntu
web3 ansible_host=10.0.1.12 ansible_user=ubuntu

[databases]
db1 ansible_host=10.0.2.10 ansible_user=ubuntu
db2 ansible_host=10.0.2.11 ansible_user=ubuntu

[loadbalancers]
lb1 ansible_host=10.0.1.20 ansible_user=ubuntu

[nodejs_app:children]
webservers
databases
loadbalancers

[nodejs_app:vars]
ansible_ssh_private_key_file=~/.ssh/nodejs-app-key.pem
environment=production
app_version=1.0.0

动态清单(YAML格式)

yaml
# inventory/hosts.yml
all:
  children:
    webservers:
      hosts:
        web1:
          ansible_host: 10.0.1.10
          ansible_user: ubuntu
          server_role: primary
        web2:
          ansible_host: 10.0.1.11
          ansible_user: ubuntu
          server_role: secondary
        web3:
          ansible_host: 10.0.1.12
          ansible_user: ubuntu
          server_role: secondary
      vars:
        http_port: 3000
        max_clients: 200
    
    databases:
      hosts:
        db1:
          ansible_host: 10.0.2.10
          ansible_user: ubuntu
          db_role: master
        db2:
          ansible_host: 10.0.2.11
          ansible_user: ubuntu
          db_role: slave
      vars:
        db_port: 5432
        max_connections: 100
    
    loadbalancers:
      hosts:
        lb1:
          ansible_host: 10.0.1.20
          ansible_user: ubuntu
      vars:
        lb_method: round_robin
  
  vars:
    ansible_ssh_private_key_file: ~/.ssh/nodejs-app-key.pem
    environment: production
    app_version: "{{ lookup('env', 'APP_VERSION') | default('1.0.0') }}"

🛠 Node.js应用部署Playbook

主要部署Playbook

yaml
# deploy-nodejs-app.yml
---
- name: Deploy Node.js Application
  hosts: webservers
  become: yes
  gather_facts: yes
  
  vars:
    app_name: nodejs-app
    app_user: nodejs
    app_dir: /opt/{{ app_name }}
    app_repo: https://github.com/yourorg/nodejs-app.git
    node_version: "18.x"
    pm2_instances: "{{ ansible_processor_vcpus }}"
    
  pre_tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600
      when: ansible_os_family == "Debian"
    
    - name: Update yum cache
      yum:
        update_cache: yes
      when: ansible_os_family == "RedHat"
  
  roles:
    - common
    - nodejs
    - nginx
    - pm2
    - monitoring
  
  tasks:
    - name: Create application user
      user:
        name: "{{ app_user }}"
        system: yes
        shell: /bin/bash
        home: "{{ app_dir }}"
        create_home: yes
    
    - name: Create application directories
      file:
        path: "{{ item }}"
        state: directory
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
        mode: '0755'
      loop:
        - "{{ app_dir }}"
        - "{{ app_dir }}/logs"
        - "{{ app_dir }}/tmp"
        - /etc/{{ app_name }}
    
    - name: Clone application repository
      git:
        repo: "{{ app_repo }}"
        dest: "{{ app_dir }}/current"
        version: "{{ app_version }}"
        force: yes
      become_user: "{{ app_user }}"
      notify:
        - restart nodejs app
    
    - name: Install Node.js dependencies
      npm:
        path: "{{ app_dir }}/current"
        state: present
        production: yes
      become_user: "{{ app_user }}"
    
    - name: Build application
      command: npm run build
      args:
        chdir: "{{ app_dir }}/current"
      become_user: "{{ app_user }}"
      when: build_required | default(true)
    
    - name: Generate application configuration
      template:
        src: app.env.j2
        dest: "{{ app_dir }}/.env"
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
        mode: '0600'
      notify:
        - restart nodejs app
    
    - name: Generate PM2 ecosystem file
      template:
        src: ecosystem.config.js.j2
        dest: "{{ app_dir }}/ecosystem.config.js"
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
        mode: '0644'
      notify:
        - restart nodejs app
    
    - name: Start application with PM2
      command: pm2 startOrRestart {{ app_dir }}/ecosystem.config.js --env production
      become_user: "{{ app_user }}"
    
    - name: Save PM2 configuration
      command: pm2 save
      become_user: "{{ app_user }}"
    
    - name: Setup PM2 startup script
      command: pm2 startup systemd -u {{ app_user }} --hp {{ app_dir }}
      register: pm2_startup_command
    
    - name: Execute PM2 startup command
      shell: "{{ pm2_startup_command.stdout }}"
      when: pm2_startup_command.stdout is defined
  
  handlers:
    - name: restart nodejs app
      command: pm2 restart {{ app_name }}
      become_user: "{{ app_user }}"
      ignore_errors: yes
    
    - name: reload nginx
      service:
        name: nginx
        state: reloaded
    
    - name: restart nginx
      service:
        name: nginx
        state: restarted

  post_tasks:
    - name: Verify application is running
      uri:
        url: "http://localhost:{{ http_port | default(3000) }}/health"
        method: GET
        status_code: 200
      retries: 5
      delay: 10
    
    - name: Clean up old application versions
      shell: |
        cd {{ app_dir }}
        ls -dt */ | tail -n +4 | xargs rm -rf
      become_user: "{{ app_user }}"
      ignore_errors: yes

角色结构

Common角色

yaml
# roles/common/tasks/main.yml
---
- name: Install essential packages
  package:
    name:
      - curl
      - wget
      - unzip
      - git
      - htop
      - vim
      - build-essential
    state: present

- name: Configure timezone
  timezone:
    name: "{{ timezone | default('UTC') }}"

- name: Configure NTP
  package:
    name: ntp
    state: present
  notify: restart ntp

- name: Start and enable NTP service
  service:
    name: ntp
    state: started
    enabled: yes

- name: Configure SSH security
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
    backup: yes
  loop:
    - { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
    - { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
    - { regexp: '^#?Port', line: 'Port {{ ssh_port | default(22) }}' }
  notify: restart sshd

- name: Configure firewall
  ufw:
    rule: "{{ item.rule }}"
    port: "{{ item.port }}"
    proto: "{{ item.proto | default('tcp') }}"
  loop:
    - { rule: 'allow', port: '{{ ssh_port | default(22) }}' }
    - { rule: 'allow', port: '80' }
    - { rule: 'allow', port: '443' }
    - { rule: 'allow', port: '{{ http_port | default(3000) }}' }
  when: configure_firewall | default(true)

- name: Enable firewall
  ufw:
    state: enabled
  when: configure_firewall | default(true)

# roles/common/handlers/main.yml
---
- name: restart ntp
  service:
    name: ntp
    state: restarted

- name: restart sshd
  service:
    name: sshd
    state: restarted

Node.js角色

yaml
# roles/nodejs/tasks/main.yml
---
- name: Add NodeSource APT repository
  shell: curl -fsSL https://deb.nodesource.com/setup_{{ node_version }} | sudo -E bash -
  when: ansible_os_family == "Debian"

- name: Install Node.js
  package:
    name: nodejs
    state: present

- name: Verify Node.js installation
  command: node --version
  register: node_version_output
  changed_when: false

- name: Display Node.js version
  debug:
    msg: "Node.js version: {{ node_version_output.stdout }}"

- name: Install global npm packages
  npm:
    name: "{{ item }}"
    global: yes
    state: present
  loop:
    - pm2
    - nodemon

- name: Create Node.js log rotation configuration
  template:
    src: nodejs-logrotate.j2
    dest: /etc/logrotate.d/nodejs
    mode: '0644'

# roles/nodejs/templates/nodejs-logrotate.j2
{{ app_dir }}/logs/*.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    create 644 {{ app_user }} {{ app_user }}
    postrotate
        pm2 reloadLogs
    endscript
}

Nginx角色

yaml
# roles/nginx/tasks/main.yml
---
- name: Install Nginx
  package:
    name: nginx
    state: present

- name: Remove default Nginx configuration
  file:
    path: /etc/nginx/sites-enabled/default
    state: absent
  notify: restart nginx

- name: Generate Nginx configuration
  template:
    src: nodejs-app.conf.j2
    dest: /etc/nginx/sites-available/{{ app_name }}
    backup: yes
  notify: reload nginx

- name: Enable Nginx site
  file:
    src: /etc/nginx/sites-available/{{ app_name }}
    dest: /etc/nginx/sites-enabled/{{ app_name }}
    state: link
  notify: reload nginx

- name: Configure Nginx security headers
  template:
    src: security-headers.conf.j2
    dest: /etc/nginx/conf.d/security-headers.conf
  notify: reload nginx

- name: Test Nginx configuration
  command: nginx -t
  register: nginx_test
  changed_when: false

- name: Start and enable Nginx
  service:
    name: nginx
    state: started
    enabled: yes

# roles/nginx/templates/nodejs-app.conf.j2
upstream nodejs_backend {
    least_conn;
    {% for host in groups['webservers'] %}
    server {{ hostvars[host]['ansible_default_ipv4']['address'] }}:{{ http_port | default(3000) }} max_fails=3 fail_timeout=30s;
    {% endfor %}
    keepalive 32;
}

server {
    listen 80;
    server_name {{ server_name | default('_') }};
    
    # Security headers
    include /etc/nginx/conf.d/security-headers.conf;
    
    # Logging
    access_log /var/log/nginx/{{ app_name }}_access.log;
    error_log /var/log/nginx/{{ app_name }}_error.log;
    
    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/xml+rss
        application/json;
    
    # Static files
    location /static/ {
        alias {{ app_dir }}/current/public/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    
    # Health check
    location /health {
        proxy_pass http://nodejs_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        
        access_log off;
    }
    
    # Main application
    location / {
        proxy_pass http://nodejs_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        
        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        
        # Buffer settings
        proxy_buffering on;
        proxy_buffer_size 128k;
        proxy_buffers 4 256k;
        proxy_busy_buffers_size 256k;
    }
    
    # Rate limiting
    location /api/ {
        limit_req zone=api burst=10 nodelay;
        proxy_pass http://nodejs_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

# Rate limiting configuration
http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
}

# roles/nginx/templates/security-headers.conf.j2
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https:; connect-src 'self' https:; media-src 'self'; object-src 'none'; child-src 'self'; frame-ancestors 'self'; form-action 'self'; base-uri 'self';" always;

# Hide Nginx version
server_tokens off;

模板文件

应用配置模板

jinja2
# templates/app.env.j2
NODE_ENV={{ environment }}
PORT={{ http_port | default(3000) }}

# Database configuration
DATABASE_URL=postgresql://{{ db_username }}:{{ db_password }}@{{ db_host }}:{{ db_port | default(5432) }}/{{ db_name }}

# Redis configuration
REDIS_URL=redis://{{ redis_host }}:{{ redis_port | default(6379) }}

# JWT configuration
JWT_SECRET={{ jwt_secret }}
JWT_EXPIRES_IN={{ jwt_expires_in | default('24h') }}

# Logging configuration
LOG_LEVEL={{ log_level | default('info') }}
LOG_FILE={{ app_dir }}/logs/app.log

# Application configuration
APP_VERSION={{ app_version }}
MAX_REQUEST_SIZE={{ max_request_size | default('10mb') }}
CORS_ORIGIN={{ cors_origin | default('*') }}

# Monitoring
ENABLE_METRICS={{ enable_metrics | default(true) }}
METRICS_PORT={{ metrics_port | default(9090) }}

# External services
{% if smtp_host is defined %}
SMTP_HOST={{ smtp_host }}
SMTP_PORT={{ smtp_port | default(587) }}
SMTP_USER={{ smtp_user }}
SMTP_PASS={{ smtp_pass }}
{% endif %}

{% if aws_region is defined %}
AWS_REGION={{ aws_region }}
AWS_ACCESS_KEY_ID={{ aws_access_key_id }}
AWS_SECRET_ACCESS_KEY={{ aws_secret_access_key }}
{% endif %}

PM2配置模板

javascript
// templates/ecosystem.config.js.j2
module.exports = {
  apps: [{
    name: '{{ app_name }}',
    script: './server.js',
    cwd: '{{ app_dir }}/current',
    instances: {{ pm2_instances }},
    exec_mode: 'cluster',
    
    // Environment variables
    env: {
      NODE_ENV: 'production',
      PORT: {{ http_port | default(3000) }}
    },
    
    // Logging
    log_file: '{{ app_dir }}/logs/combined.log',
    out_file: '{{ app_dir }}/logs/out.log',
    error_file: '{{ app_dir }}/logs/error.log',
    log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
    
    // Process management
    min_uptime: '10s',
    max_restarts: 10,
    autorestart: true,
    
    // Memory management
    max_memory_restart: '{{ max_memory_restart | default("1G") }}',
    
    // Monitoring
    pmx: true,
    
    // Watch and ignore
    watch: false,
    ignore_watch: ['node_modules', 'logs'],
    
    // Advanced features
    source_map_support: true,
    instance_var: 'INSTANCE_ID',
    
    // Graceful shutdown
    kill_timeout: 5000,
    listen_timeout: 3000,
    
    // Health check
    health_check_http: {
      path: '/health',
      port: {{ http_port | default(3000) }},
      timeout: 5000,
      interval: 30000
    }
  }]
};

🔧 高级Playbook示例

数据库部署Playbook

yaml
# deploy-database.yml
---
- name: Deploy PostgreSQL Database
  hosts: databases
  become: yes
  
  vars:
    postgres_version: "13"
    postgres_data_dir: /var/lib/postgresql/{{ postgres_version }}/main
    postgres_config_dir: /etc/postgresql/{{ postgres_version }}/main
    
  roles:
    - postgresql
    - postgresql-backup
  
  tasks:
    - name: Create application database
      postgresql_db:
        name: "{{ db_name }}"
        owner: "{{ db_username }}"
        encoding: UTF8
        lc_collate: en_US.UTF-8
        lc_ctype: en_US.UTF-8
        template: template0
      become_user: postgres
    
    - name: Create application user
      postgresql_user:
        name: "{{ db_username }}"
        password: "{{ db_password }}"
        db: "{{ db_name }}"
        priv: ALL
        expires: infinity
      become_user: postgres
    
    - name: Configure PostgreSQL
      template:
        src: postgresql.conf.j2
        dest: "{{ postgres_config_dir }}/postgresql.conf"
        backup: yes
      notify: restart postgresql
    
    - name: Configure PostgreSQL authentication
      template:
        src: pg_hba.conf.j2
        dest: "{{ postgres_config_dir }}/pg_hba.conf"
        backup: yes
      notify: restart postgresql
    
    - name: Create database backup script
      template:
        src: backup-db.sh.j2
        dest: /usr/local/bin/backup-db.sh
        mode: '0755'
    
    - name: Schedule database backups
      cron:
        name: "Database backup"
        minute: "0"
        hour: "2"
        job: "/usr/local/bin/backup-db.sh"
        user: postgres
  
  handlers:
    - name: restart postgresql
      service:
        name: postgresql
        state: restarted

监控部署Playbook

yaml
# deploy-monitoring.yml
---
- name: Deploy Monitoring Stack
  hosts: webservers
  become: yes
  
  vars:
    prometheus_version: "2.40.0"
    grafana_version: "9.3.0"
    node_exporter_version: "1.5.0"
  
  tasks:
    - name: Create monitoring user
      user:
        name: monitoring
        system: yes
        shell: /bin/false
        home: /opt/monitoring
        create_home: yes
    
    - name: Install Node Exporter
      unarchive:
        src: "https://github.com/prometheus/node_exporter/releases/download/v{{ node_exporter_version }}/node_exporter-{{ node_exporter_version }}.linux-amd64.tar.gz"
        dest: /opt/monitoring
        remote_src: yes
        owner: monitoring
        group: monitoring
      notify: restart node_exporter
    
    - name: Create Node Exporter systemd service
      template:
        src: node_exporter.service.j2
        dest: /etc/systemd/system/node_exporter.service
      notify:
        - daemon reload
        - restart node_exporter
    
    - name: Start and enable Node Exporter
      service:
        name: node_exporter
        state: started
        enabled: yes
    
    - name: Install Prometheus (on monitoring server)
      unarchive:
        src: "https://github.com/prometheus/prometheus/releases/download/v{{ prometheus_version }}/prometheus-{{ prometheus_version }}.linux-amd64.tar.gz"
        dest: /opt/monitoring
        remote_src: yes
        owner: monitoring
        group: monitoring
      when: inventory_hostname in groups['monitoring']
      notify: restart prometheus
    
    - name: Configure Prometheus
      template:
        src: prometheus.yml.j2
        dest: /opt/monitoring/prometheus.yml
        owner: monitoring
        group: monitoring
      when: inventory_hostname in groups['monitoring']
      notify: restart prometheus
  
  handlers:
    - name: daemon reload
      systemd:
        daemon_reload: yes
    
    - name: restart node_exporter
      service:
        name: node_exporter
        state: restarted
    
    - name: restart prometheus
      service:
        name: prometheus
        state: restarted

零停机部署Playbook

yaml
# zero-downtime-deploy.yml
---
- name: Zero Downtime Deployment
  hosts: webservers
  become: yes
  serial: 1  # 一次只部署一台服务器
  
  vars:
    health_check_url: "http://localhost:{{ http_port | default(3000) }}/health"
    deployment_timeout: 300
    
  pre_tasks:
    - name: Check current application status
      uri:
        url: "{{ health_check_url }}"
        method: GET
        status_code: 200
      register: pre_deploy_health
      ignore_errors: yes
    
    - name: Remove server from load balancer
      uri:
        url: "http://{{ load_balancer_host }}/api/servers/{{ inventory_hostname }}/disable"
        method: POST
        headers:
          Authorization: "Bearer {{ lb_api_token }}"
      delegate_to: localhost
      when: load_balancer_host is defined
    
    - name: Wait for connections to drain
      wait_for:
        timeout: 30
      when: pre_deploy_health is succeeded
  
  tasks:
    - name: Create backup of current version
      command: cp -r {{ app_dir }}/current {{ app_dir }}/backup-{{ ansible_date_time.epoch }}
      become_user: "{{ app_user }}"
      when: pre_deploy_health is succeeded
    
    - name: Deploy new version
      git:
        repo: "{{ app_repo }}"
        dest: "{{ app_dir }}/releases/{{ app_version }}"
        version: "{{ app_version }}"
        force: yes
      become_user: "{{ app_user }}"
    
    - name: Install dependencies
      npm:
        path: "{{ app_dir }}/releases/{{ app_version }}"
        state: present
        production: yes
      become_user: "{{ app_user }}"
    
    - name: Build application
      command: npm run build
      args:
        chdir: "{{ app_dir }}/releases/{{ app_version }}"
      become_user: "{{ app_user }}"
    
    - name: Update symlink to new version
      file:
        src: "{{ app_dir }}/releases/{{ app_version }}"
        dest: "{{ app_dir }}/current"
        state: link
        force: yes
      become_user: "{{ app_user }}"
    
    - name: Restart application
      command: pm2 restart {{ app_name }}
      become_user: "{{ app_user }}"
    
    - name: Wait for application to start
      uri:
        url: "{{ health_check_url }}"
        method: GET
        status_code: 200
      register: health_check
      until: health_check is succeeded
      retries: "{{ deployment_timeout // 10 }}"
      delay: 10
  
  post_tasks:
    - name: Add server back to load balancer
      uri:
        url: "http://{{ load_balancer_host }}/api/servers/{{ inventory_hostname }}/enable"
        method: POST
        headers:
          Authorization: "Bearer {{ lb_api_token }}"
      delegate_to: localhost
      when: load_balancer_host is defined and health_check is succeeded
    
    - name: Clean up old releases
      shell: |
        cd {{ app_dir }}/releases
        ls -dt */ | tail -n +4 | xargs rm -rf
      become_user: "{{ app_user }}"
      ignore_errors: yes
    
    - name: Rollback on failure
      block:
        - name: Restore backup
          file:
            src: "{{ app_dir }}/backup-{{ ansible_date_time.epoch }}"
            dest: "{{ app_dir }}/current"
            state: link
            force: yes
          become_user: "{{ app_user }}"
        
        - name: Restart application
          command: pm2 restart {{ app_name }}
          become_user: "{{ app_user }}"
        
        - name: Fail deployment
          fail:
            msg: "Deployment failed, rolled back to previous version"
      
      when: health_check is failed

🚀 执行和管理

常用命令

bash
# 基本执行
ansible-playbook -i inventory/hosts.yml deploy-nodejs-app.yml

# 指定特定主机组
ansible-playbook -i inventory/hosts.yml deploy-nodejs-app.yml --limit webservers

# 检查模式(dry-run)
ansible-playbook -i inventory/hosts.yml deploy-nodejs-app.yml --check

# 详细输出
ansible-playbook -i inventory/hosts.yml deploy-nodejs-app.yml -vvv

# 使用标签
ansible-playbook -i inventory/hosts.yml deploy-nodejs-app.yml --tags "nodejs,nginx"

# 跳过特定标签
ansible-playbook -i inventory/hosts.yml deploy-nodejs-app.yml --skip-tags "monitoring"

# 传递额外变量
ansible-playbook -i inventory/hosts.yml deploy-nodejs-app.yml \
  --extra-vars "app_version=1.2.0 environment=staging"

# 使用密码库
ansible-playbook -i inventory/hosts.yml deploy-nodejs-app.yml \
  --ask-vault-pass

# 并行执行
ansible-playbook -i inventory/hosts.yml deploy-nodejs-app.yml \
  --forks 10

Ansible Vault密钥管理

bash
# 创建加密文件
ansible-vault create group_vars/production/vault.yml

# 编辑加密文件
ansible-vault edit group_vars/production/vault.yml

# 加密现有文件
ansible-vault encrypt group_vars/production/secrets.yml

# 解密文件
ansible-vault decrypt group_vars/production/secrets.yml

# 查看加密文件
ansible-vault view group_vars/production/vault.yml

变量文件示例

yaml
# group_vars/production/vault.yml (加密)
$ANSIBLE_VAULT;1.1;AES256
66386439653...

# group_vars/production/vars.yml (明文)
db_username: app_user
db_name: nodejs_app
db_host: 10.0.2.10

# 引用加密变量
db_password: "{{ vault_db_password }}"
jwt_secret: "{{ vault_jwt_secret }}"

📝 总结

Ansible为Node.js应用提供了强大的自动化部署能力:

  • 无代理架构:简单易用,无需在目标主机安装额外软件
  • 声明式配置:YAML语法简单直观
  • 幂等性操作:重复执行安全可靠
  • 丰富的模块:支持各种系统管理任务
  • 灵活的变量系统:支持复杂的配置管理

通过合理的角色设计和Playbook组织,可以实现高效、可靠的自动化运维。

🔗 相关资源