Skip to content
0

文章发布较早,内容可能过时,阅读注意甄别。

Ansible 简介

Ansible 是一款开源的自动化运维工具,具有以下特点:

  • 无需代理:通过 SSH 连接,无需在目标主机安装客户端
  • YAML 语法:易读易写的配置文件
  • 幂等性:多次执行结果一致
  • 丰富的模块:3000+ 内置模块

1. 安装与配置

1.1 安装 Ansible

bash
# Ubuntu/Debian
sudo apt update
sudo apt install ansible -y

# CentOS/RHEL
sudo yum install epel-release -y
sudo yum install ansible -y

# 使用 pip 安装(推荐)
pip3 install ansible

# 验证安装
ansible --version

1.2 配置 SSH 免密登录

bash
# 生成 SSH 密钥对
ssh-keygen -t rsa -b 4096 -C "ansible@example.com"

# 复制公钥到目标主机
ssh-copy-id user@192.168.1.10
ssh-copy-id user@192.168.1.11

# 测试连接
ssh user@192.168.1.10

1.3 配置 Inventory(主机清单)

ini
# /etc/ansible/hosts
[web]
web1 ansible_host=192.168.1.10 ansible_user=ubuntu
web2 ansible_host=192.168.1.11 ansible_user=ubuntu

[db]
db1 ansible_host=192.168.1.20 ansible_user=ubuntu
db2 ansible_host=192.168.1.21 ansible_user=ubuntu

[all:vars]
ansible_ssh_private_key_file=~/.ssh/id_rsa
ansible_python_interpreter=/usr/bin/python3

YAML 格式

yaml
# inventory.yml
all:
  children:
    web:
      hosts:
        web1:
          ansible_host: 192.168.1.10
          ansible_user: ubuntu
        web2:
          ansible_host: 192.168.1.11
          ansible_user: ubuntu
    db:
      hosts:
        db1:
          ansible_host: 192.168.1.20
          ansible_user: ubuntu

2. 基础命令

2.1 Ad-Hoc 命令

bash
# 测试连通性
ansible all -m ping

# 执行命令
ansible web -m shell -a "uptime"

# 查看系统信息
ansible all -m setup

# 安装软件包
ansible web -m apt -a "name=nginx state=present" --become

# 重启服务
ansible web -m service -a "name=nginx state=restarted" --become

# 复制文件
ansible web -m copy -a "src=/tmp/test.txt dest=/tmp/test.txt"

2.2 常用参数

参数说明
-i指定 inventory 文件
-m指定模块
-a模块参数
--become使用 sudo 提权
-u指定用户
-f并发数
--check检查模式(不实际执行)

3. Playbook 编写

3.1 基础 Playbook

yaml
# nginx_install.yml
---
- name: 安装和配置 Nginx
  hosts: web
  become: yes

  tasks:
    - name: 安装 Nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: 启动 Nginx
      service:
        name: nginx
        state: started
        enabled: yes

    - name: 复制配置文件
      copy:
        src: ./nginx.conf
        dest: /etc/nginx/nginx.conf
        backup: yes
      notify: reload nginx

  handlers:
    - name: reload nginx
      service:
        name: nginx
        state: reloaded

执行 Playbook

bash
ansible-playbook nginx_install.yml

# 检查模式
ansible-playbook nginx_install.yml --check

# 详细输出
ansible-playbook nginx_install.yml -v

3.2 变量使用

yaml
# lamp_stack.yml
---
- name: 部署 LAMP 环境
  hosts: web
  become: yes

  vars:
    packages:
      - apache2
      - mysql-server
      - php
      - php-mysql
    apache_port: 80

  tasks:
    - name: 安装软件包
      apt:
        name: "{{ item }}"
        state: present
      loop: "{{ packages }}"

    - name: 配置 Apache 端口
      lineinfile:
        path: /etc/apache2/ports.conf
        regexp: '^Listen'
        line: "Listen {{ apache_port }}"

3.3 条件判断

yaml
---
- name: 根据操作系统安装软件
  hosts: all
  become: yes

  tasks:
    - name: 在 Ubuntu 上安装 Nginx
      apt:
        name: nginx
        state: present
      when: ansible_distribution == "Ubuntu"

    - name: 在 CentOS 上安装 Nginx
      yum:
        name: nginx
        state: present
      when: ansible_distribution == "CentOS"

3.4 循环

yaml
---
- name: 创建多个用户
  hosts: all
  become: yes

  tasks:
    - name: 创建用户
      user:
        name: "{{ item.name }}"
        shell: "{{ item.shell }}"
        groups: "{{ item.groups }}"
      loop:
        - { name: 'alice', shell: '/bin/bash', groups: 'sudo' }
        - { name: 'bob', shell: '/bin/bash', groups: 'users' }
        - { name: 'carol', shell: '/bin/zsh', groups: 'users' }

4. 角色(Roles)

4.1 角色目录结构

roles/
└── nginx/
    ├── tasks/
    │   └── main.yml
    ├── handlers/
    │   └── main.yml
    ├── templates/
    │   └── nginx.conf.j2
    ├── files/
    ├── vars/
    │   └── main.yml
    ├── defaults/
    │   └── main.yml
    └── meta/
        └── main.yml

4.2 创建角色

bash
# 创建角色目录结构
ansible-galaxy init nginx

tasks/main.yml

yaml
---
- name: 安装 Nginx
  apt:
    name: nginx
    state: present

- name: 复制配置文件
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: reload nginx

- name: 启动 Nginx
  service:
    name: nginx
    state: started
    enabled: yes

handlers/main.yml

yaml
---
- name: reload nginx
  service:
    name: nginx
    state: reloaded

defaults/main.yml

yaml
---
nginx_port: 80
nginx_user: www-data

templates/nginx.conf.j2

jinja2
user {{ nginx_user }};
worker_processes auto;

events {
    worker_connections 1024;
}

http {
    server {
        listen {{ nginx_port }};
        server_name _;

        location / {
            root /var/www/html;
            index index.html;
        }
    }
}

4.3 使用角色

yaml
# site.yml
---
- name: 配置 Web 服务器
  hosts: web
  become: yes

  roles:
    - nginx
    - php
    - mysql

5. 实战案例

5.1 部署 Node.js 应用

yaml
# deploy_nodejs.yml
---
- name: 部署 Node.js 应用
  hosts: app
  become: yes

  vars:
    app_name: myapp
    app_dir: /var/www/{{ app_name }}
    node_version: "16.x"
    app_port: 3000

  tasks:
    - name: 安装 Node.js
      shell: |
        curl -sL https://deb.nodesource.com/setup_{{ node_version }} | bash -
        apt-get install -y nodejs
      args:
        creates: /usr/bin/node

    - name: 创建应用目录
      file:
        path: "{{ app_dir }}"
        state: directory
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"

    - name: 复制应用代码
      synchronize:
        src: ./app/
        dest: "{{ app_dir }}/"
        delete: yes

    - name: 安装依赖
      npm:
        path: "{{ app_dir }}"
        state: present

    - name: 创建 systemd 服务
      template:
        src: nodejs.service.j2
        dest: /etc/systemd/system/{{ app_name }}.service
      notify: restart app

    - name: 启动应用
      systemd:
        name: "{{ app_name }}"
        state: started
        enabled: yes
        daemon_reload: yes

  handlers:
    - name: restart app
      systemd:
        name: "{{ app_name }}"
        state: restarted

nodejs.service.j2

ini
[Unit]
Description={{ app_name }} Node.js Application
After=network.target

[Service]
Type=simple
User={{ ansible_user }}
WorkingDirectory={{ app_dir }}
ExecStart=/usr/bin/node {{ app_dir }}/server.js
Restart=always
Environment=NODE_ENV=production
Environment=PORT={{ app_port }}

[Install]
WantedBy=multi-user.target

5.2 批量配置服务器

yaml
# server_hardening.yml
---
- name: 服务器安全加固
  hosts: all
  become: yes

  tasks:
    # 更新系统
    - name: 更新软件包
      apt:
        update_cache: yes
        upgrade: dist

    # 配置防火墙
    - name: 安装 UFW
      apt:
        name: ufw
        state: present

    - name: 配置防火墙规则
      ufw:
        rule: allow
        port: "{{ item }}"
      loop:
        - '22'
        - '80'
        - '443'

    - name: 启用防火墙
      ufw:
        state: enabled

    # SSH 安全配置
    - name: 禁用密码登录
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PasswordAuthentication'
        line: 'PasswordAuthentication no'
      notify: restart ssh

    - name: 禁用 root 登录
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PermitRootLogin'
        line: 'PermitRootLogin no'
      notify: restart ssh

    # 配置时间同步
    - name: 安装 chrony
      apt:
        name: chrony
        state: present

    - name: 启动 chrony
      service:
        name: chrony
        state: started
        enabled: yes

  handlers:
    - name: restart ssh
      service:
        name: ssh
        state: restarted

5.3 Docker 容器部署

yaml
# deploy_docker.yml
---
- name: 部署 Docker 容器
  hosts: docker
  become: yes

  vars:
    containers:
      - name: nginx
        image: nginx:latest
        ports:
          - "80:80"
      - name: redis
        image: redis:alpine
        ports:
          - "6379:6379"

  tasks:
    - name: 安装 Docker
      apt:
        name:
          - docker.io
          - python3-docker
        state: present

    - name: 启动 Docker 服务
      service:
        name: docker
        state: started
        enabled: yes

    - name: 部署容器
      docker_container:
        name: "{{ item.name }}"
        image: "{{ item.image }}"
        ports: "{{ item.ports }}"
        state: started
        restart_policy: always
      loop: "{{ containers }}"

6. 常用模块

模块用途示例
ping测试连接ansible all -m ping
shell执行命令shell: uptime
copy复制文件copy: src=a dest=b
template模板渲染template: src=x.j2 dest=/etc/x
apt/yum包管理apt: name=nginx state=present
service服务管理service: name=nginx state=started
file文件操作file: path=/tmp state=directory
user用户管理user: name=alice state=present
lineinfile修改文件行lineinfile: path=/etc/hosts line='127.0.0.1 localhost'

7. 最佳实践

7.1 使用 Ansible Vault 加密敏感信息

bash
# 创建加密文件
ansible-vault create secrets.yml

# 编辑加密文件
ansible-vault edit secrets.yml

# 执行时提供密码
ansible-playbook site.yml --ask-vault-pass

7.2 使用标签

yaml
---
- name: 配置服务器
  hosts: all

  tasks:
    - name: 安装软件
      apt:
        name: nginx
      tags: install

    - name: 配置服务
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      tags: config
bash
# 只执行 install 标签的任务
ansible-playbook site.yml --tags install

# 跳过 config 标签
ansible-playbook site.yml --skip-tags config

7.3 使用动态 Inventory

python
#!/usr/bin/env python3
# dynamic_inventory.py
import json

inventory = {
    "web": {
        "hosts": ["web1", "web2"],
        "vars": {
            "ansible_user": "ubuntu"
        }
    },
    "_meta": {
        "hostvars": {
            "web1": {"ansible_host": "192.168.1.10"},
            "web2": {"ansible_host": "192.168.1.11"}
        }
    }
}

print(json.dumps(inventory))
bash
# 使用动态 Inventory
ansible-playbook -i dynamic_inventory.py site.yml

8. 故障排查

bash
# 详细输出
ansible-playbook site.yml -vvv

# 语法检查
ansible-playbook site.yml --syntax-check

# 列出任务
ansible-playbook site.yml --list-tasks

# 列出主机
ansible-playbook site.yml --list-hosts

# 从指定任务开始
ansible-playbook site.yml --start-at-task="安装 Nginx"

参考资料

最近更新