Linux Ansible 快速入门
大约 13 分钟约 3944 字
Linux Ansible 快速入门
简介
Ansible 是无代理的自动化运维工具,通过 SSH 连接管理远程主机。使用 YAML 格式的 Playbook 编排任务,实现批量配置管理、应用部署和流程自动化。Ansible 由 Michael DeHaan 于 2012 年创建,后被 Red Hat 收购,是目前最流行的配置管理工具之一。它不需要在目标主机上安装任何 Agent,仅通过 SSH 协议即可完成所有管理操作,极大地降低了部署和维护成本。
特点
实现
安装 Ansible
# ====== CentOS/RHEL 安装 ======
# 方法 1:使用 pip 安装(推荐,版本更新)
yum install -y epel-release
yum install -y python3 python3-pip git
pip3 install ansible --user
# 方法 2:使用 yum 安装
yum install -y epel-release
yum install -y ansible
# 方法 3:使用 dnf 安装(CentOS 8+)
dnf install -y ansible
# 验证安装
ansible --version
ansible-galaxy collection list
# 配置 pip 用户路径(如果使用 pip 安装)
echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
# ====== Ubuntu/Debian 安装 ======
apt update
apt install -y ansible
# ====== macOS 安装 ======
brew install ansibleAnsible 配置文件
# ====== ansible.cfg 配置 ======
# 项目级配置(优先级最高)
cat > ansible.cfg << 'EOF'
[defaults]
# Inventory 文件路径
inventory = ./inventory.ini
# SSH 配置
host_key_checking = False
timeout = 30
ssh_common_args = -o StrictHostKeyChecking=no
# 并发数
forks = 10
# 日志
log_path = ./ansible.log
# 回调插件
callbacks_enabled = profile_tasks, timer
# 角色路径
roles_path = ./roles
# 重试文件保存目录
retry_files_enabled = True
retry_files_save_path = ./retry
# 显示差异
diff = True
# 去除警告
deprecation_warnings = False
command_warnings = False
#fact 缓存
gathering = smart
fact_caching = memory
fact_caching_timeout = 86400
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
[colors]
highlight = white
verbose = blue
warn = bright purple
error = red
debug = dark gray
deprecate = purple
skip = cyan
unreachable = red
ok = green
changed = yellow
EOF
# ====== 配置文件优先级 ======
# 1. ANSIBLE_CONFIG 环境变量
# 2. 当前目录的 ansible.cfg
# 3. ~/.ansible.cfg
# 4. /etc/ansible/ansible.cfgInventory 与基本命令
# inventory.ini
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
[dbservers]
db1 ansible_host=192.168.1.20
[all:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/id_ed25519# 基本命令
ansible all -i inventory.ini -m ping # 测试连通性
ansible webservers -i inventory.ini -m setup # 收集系统信息
ansible webservers -i inventory.ini -m shell -a "uptime" # 执行命令
ansible webservers -i inventory.ini -m yum -a "name=nginx state=present" # 安装包Inventory 高级配置
# ====== 完整的 Inventory 示例 ======
# inventory.ini
# ====== 主机分组 ======
[webservers]
web1 ansible_host=192.168.1.10 ansible_port=22
web2 ansible_host=192.168.1.11
web3 ansible_host=192.168.1.12
[dbservers]
db-master ansible_host=192.168.1.20
db-slave1 ansible_host=192.168.1.21
db-slave2 ansible_host=192.168.1.22
[cacheservers]
redis1 ansible_host=192.168.1.30
redis2 ansible_host=192.168.1.31
[mqservers]
mq1 ansible_host=192.168.1.40
# ====== 组的合并 ======
[production:children]
webservers
dbservers
cacheservers
mqservers
[staging:children]
web1
# ====== 主机变量 ======
[webservers:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3
http_port=80
https_port=443
nginx_worker_processes=auto
[dbservers:vars]
ansible_user=dbadmin
mysql_port=3306
mysql_max_connections=500
# ====== 全局变量 ======
[all:vars]
ansible_ssh_private_key_file=~/.ssh/id_ed25519
ntp_server=ntp.aliyun.com
timezone=Asia/Shanghai# ====== 动态 Inventory ======
# 使用脚本生成动态 Inventory
# dynamic_inventory.sh
#!/bin/bash
# 从云平台 API 获取主机列表
# 以下为模拟示例
cat << 'INV'
{
"webservers": {
"hosts": {
"web1": {"ansible_host": "10.0.1.10"},
"web2": {"ansible_host": "10.0.1.11"}
}
},
"dbservers": {
"hosts": {
"db1": {"ansible_host": "10.0.2.10"}
}
},
"_meta": {
"hostvars": {
"web1": {"env": "production"},
"web2": {"env": "production"},
"db1": {"env": "production"}
}
}
}
INV
chmod +x dynamic_inventory.sh
ansible all -i dynamic_inventory.sh -m ping
# ====== AWS EC2 动态 Inventory ======
# pip install boto3
# ansible-galaxy collection install amazon.aws
# ec2.yaml
plugin: amazon.aws.aws_ec2
regions:
- cn-north-1
filters:
tag:Environment: production
keyed_groups:
- key: tags.Environment
prefix: env
- key: tags.Role
prefix: role
compose:
ansible_host: public_ip_addressPlaybook 编排
# deploy-web.yml
---
- name: Deploy Web Application
hosts: webservers
become: yes
vars:
app_port: 8080
app_name: myapp
tasks:
- name: Install Nginx
yum:
name: nginx
state: present
- name: Copy Nginx config
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/conf.d/{{ app_name }}.conf
notify: Reload Nginx
- name: Create app directory
file:
path: /opt/{{ app_name }}
state: directory
owner: www-data
mode: '0755'
- name: Deploy application
synchronize:
src: ./dist/
dest: /opt/{{ app_name }}/
delete: yes
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloadedPlaybook 高级用法
常用模块实战
# user-management.yml
- name: Manage Users
hosts: all
become: yes
tasks:
- name: Create deploy user
user:
name: deploy
shell: /bin/bash
groups: docker
append: yes
- name: Set up SSH key
authorized_key:
user: deploy
key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
- name: Install common packages
yum:
name: "{{ packages }}"
state: present
vars:
packages:
- vim
- htop
- git
- curl
- name: Configure sysctl
sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
state: present
loop:
- { key: 'vm.swappiness', value: '10' }
- { key: 'fs.file-max', value: '65535' }
- name: Copy config file
copy:
src: files/app.conf
dest: /etc/myapp.conf
owner: root
mode: '0644'
notify: Restart myapp更多常用模块详解
# ====== 文件操作模块 =====#
# file_module.yml
---
- name: File Operations
hosts: webservers
become: yes
tasks:
# 创建目录
- name: Create application directories
file:
path: "{{ item }}"
state: directory
owner: deploy
group: deploy
mode: '0755'
loop:
- /opt/myapp
- /opt/myapp/logs
- /opt/myapp/config
- /opt/myapp/data
# 创建符号链接
- name: Link config
file:
src: /opt/myapp/config/application.yml
dest: /etc/myapp/config.yml
state: link
# 下载文件
- name: Download JDK
get_url:
url: "https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz"
dest: /tmp/jdk17.tar.gz
checksum: "sha256:{{ jdk_checksum }}"
mode: '0644'
# 解压文件
- name: Extract JDK
unarchive:
src: /tmp/jdk17.tar.gz
dest: /opt/
remote_src: yes
owner: root
# ====== Cron 模块 ======
- name: Add cron job for log cleanup
cron:
name: "Clean old logs"
minute: "0"
hour: "2"
day: "*"
month: "*"
weekday: "*"
job: "find /opt/myapp/logs -name '*.log' -mtime +30 -delete"
user: deploy
# ====== Systemd 服务模块 ======
- name: Deploy systemd service file
copy:
src: files/myapp.service
dest: /etc/systemd/system/myapp.service
owner: root
mode: '0644'
notify: Reload systemd
- name: Ensure myapp is running
systemd:
name: myapp
state: started
enabled: yes
daemon_reload: yes
# ====== Firewall 模块 ======
- name: Open firewall ports
firewalld:
port: "{{ item }}"
permanent: yes
state: enabled
immediate: yes
loop:
- "8080/tcp"
- "8443/tcp"
- "9090/tcp"
# ====== Lineinfile 模块(精确修改单行) ======
- name: Set max connections in MySQL config
lineinfile:
path: /etc/my.cnf
regexp: '^max_connections'
line: 'max_connections = 500'
state: present
- name: Add export PATH if not exists
lineinfile:
path: /home/deploy/.bashrc
line: 'export PATH=/opt/jdk-17/bin:$PATH'
state: present
# ====== Replace 模块(正则替换) ======
- name: Update listen port in config
replace:
path: /etc/myapp/config.yml
regexp: 'port:\s*\d+'
replace: 'port: 8080'
# ====== Fetch 模块(从远程拉取文件) ======
- name: Fetch log files from servers
fetch:
src: /var/log/myapp/error.log
dest: /tmp/logs/{{ inventory_hostname }}/
flat: yes
# ====== Wait_for 模块(等待条件) ======
- name: Wait for port 8080 to be listening
wait_for:
port: 8080
host: 0.0.0.0
state: started
timeout: 120
- name: Wait for URL to return 200
uri:
url: http://localhost:8080/health
status_code: 200
register: result
until: result.status == 200
retries: 30
delay: 5
# ====== Archive 模块 ======
- name: Create archive of logs
archive:
path: /opt/myapp/logs/
dest: /tmp/logs-backup-{{ ansible_date_time.epoch }}.tar.gz
format: gz
handlers:
- name: Reload systemd
systemd:
daemon_reload: yes
# ====== Package 管理模块 =====#
# package_management.yml
---
- name: Package Management
hosts: all
become: yes
tasks:
# 使用 package 模块(自动适配 yum/dnf/apt)
- name: Install common tools
package:
name:
- vim-enhanced
- htop
- iotop
- net-tools
- bind-utils
- telnet
- ncurses
state: present
# 安装特定版本
- name: Install specific Docker version
yum:
name: docker-ce-20.10.24
state: present
# 更新所有包
- name: Update all packages
yum:
name: "*"
state: latest
when: update_all | default(false)
# 安装组包
- name: Install development tools
yum:
name: "@Development tools"
state: present
# ====== 仓库管理 ======
- name: Add EPEL repository
yum_repository:
name: epel
description: EPEL YUM repo
baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
gpgcheck: yes
gpgkey: https://download.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-$releasever
enabled: yesRole 组织结构
# ====== 创建 Role ======
ansible-galaxy init nginx-role
# 目录结构:
# nginx-role/
# ├── defaults/
# │ └── main.yml # 默认变量(优先级最低)
# ├── files/ # 静态文件
# ├── handlers/
# │ └── main.yml # handlers
# ├── meta/
# │ └── main.yml # Role 元数据
# ├── tasks/
# │ └── main.yml # 任务列表
# ├── templates/ # Jinja2 模板
# ├── tests/
# │ ├── inventory
# │ └── test.yml
# └── vars/
# └── main.yml # Role 变量(优先级高)
# ====== Role 示例 =====#
# roles/nginx-role/defaults/main.yml
---
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_client_max_body_size: "50m"
nginx_keepalive_timeout: 65
nginx_server_name: localhost
nginx_proxy_pass: "http://127.0.0.1:8080"
# roles/nginx-role/tasks/main.yml
---
- name: Install Nginx
package:
name: nginx
state: present
- name: Create directories
file:
path: "{{ item }}"
state: directory
owner: nginx
mode: '0755'
loop:
- /etc/nginx/conf.d
- /var/log/nginx
- /usr/share/nginx/html
- name: Copy main config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Reload Nginx
- name: Copy virtual host config
template:
src: vhost.conf.j2
dest: /etc/nginx/conf.d/default.conf
notify: Reload Nginx
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
# roles/nginx-role/handlers/main.yml
---
- name: Reload Nginx
service:
name: nginx
state: reloaded
- name: Restart Nginx
service:
name: nginx
state: restarted
# roles/nginx-role/templates/nginx.conf.j2
user nginx;
worker_processes {{ nginx_worker_processes }};
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections {{ nginx_worker_connections }};
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout {{ nginx_keepalive_timeout }};
client_max_body_size {{ nginx_client_max_body_size }};
include /etc/nginx/conf.d/*.conf;
}
# roles/nginx-role/templates/vhost.conf.j2
server {
listen 80;
server_name {{ nginx_server_name }};
location / {
proxy_pass {{ nginx_proxy_pass }};
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;
}
}
# ====== 使用 Role =====#
# site.yml
---
- name: Deploy all servers
hosts: all
become: yes
roles:
- common-role
- nginx-role
- java-role
# 只在特定主机组使用 Role
- name: Deploy database servers
hosts: dbservers
become: yes
roles:
- mysql-roleAnsible Vault 加密
# ====== 加密敏感文件 ======
# 创建加密文件
ansible-vault create secrets.yml
# 输入密码后会打开编辑器
# 加密已有文件
ansible-vault encrypt group_vars/production/secrets.yml
# 查看加密文件
ansible-vault view group_vars/production/secrets.yml
# 编辑加密文件
ansible-vault edit group_vars/production/secrets.yml
# 解密文件
ansible-vault decrypt group_vars/production/secrets.yml
# 更改密码
ansible-vault rekey group_vars/production/secrets.yml
# 创建密码文件
echo "my_vault_password" > .vault_pass
# 使用密码文件执行 Playbook
ansible-playbook -i inventory.ini site.yml --vault-password-file .vault_pass
# ====== 加密变量示例 =====#
# group_vars/production/vault.yml(加密文件)
db_password: "SuperSecretDBPass123!"
redis_password: "RedisSecret456!"
api_key: "sk-abc123def456"
jwt_secret: "my-jwt-secret-key-2024"
# group_vars/production/main.yml(引用加密变量)
db_host: "db-master.internal"
db_port: 3306
db_name: "production"
db_user: "app_user"
db_password: "{{ vault_db_password }}"
# 使用 ansible-vault encrypt_string 加密单行字符串
ansible-vault encrypt_string 'my_secret_password' --name 'app_secret'
# 输出:
# app_secret: !vault |
# $ANSIBLE_VAULT;1.1;AES256;...Ansible Galaxy
# ====== 使用 Galaxy ======
# 搜索 Role
ansible-galaxy search nginx
# 安装 Role
ansible-galaxy install geerlingguy.nginx
ansible-galaxy install geerlingguy.java
ansible-galaxy install geerlingguy.mysql
# 安装到指定目录
ansible-galaxy install geerlingguy.nginx -p ./roles
# 从 Git 仓库安装
ansible-galaxy install git+https://github.com/user/ansible-role-example.git
# 从 requirements.yml 批量安装
cat > requirements.yml << 'EOF'
---
- src: geerlingguy.nginx
version: 3.1.0
- src: geerlingguy.java
version: 2.2.0
- src: geerlingguy.mysql
version: 3.3.0
EOF
ansible-galaxy install -r requirements.yml
# 初始化自己的 Role
ansible-galaxy init my-custom-role
# 发布 Role 到 Galaxy
# 1. 创建 Galaxy 账号
# 2. 编辑 meta/main.yml 填写信息
# 3. ansible-galaxy role import# 执行 Playbook
ansible-playbook -i inventory.ini deploy-web.yml
ansible-playbook -i inventory.ini deploy-web.yml --check # 干跑(不实际执行)
ansible-playbook -i inventory.ini deploy-web.yml --limit web1 # 只对 web1 执行
ansible-playbook -i inventory.ini deploy-web.yml --tags "install" # 只执行特定标签执行命令详解
# ====== ansible 命令行 ======
# 基本格式
ansible [host-pattern] -i [inventory] -m [module] -a "[arguments]"
# 常用 ad-hoc 命令
# 文件操作
ansible webservers -m copy -a "src=/etc/hosts dest=/tmp/hosts"
ansible webservers -m fetch -a "src=/var/log/messages dest=/tmp/logs/"
ansible webservers -m file -a "path=/opt/app state=directory mode=0755"
# 包管理
ansible all -m yum -a "name=htop state=latest"
ansible all -m yum -a "name=docker-ce state=installed"
# 服务管理
ansible webservers -m service -a "name=nginx state=restarted"
ansible dbservers -m service -a "name=mysqld state=started enabled=yes"
# 用户管理
ansible all -m user -a "name=deploy groups=docker,sudo shell=/bin/bash"
# 命令执行
ansible all -m shell -a "systemctl status nginx"
ansible all -m raw -a "yum install -y python3" # raw 模块不依赖 Python
# 系统信息
ansible all -m setup # 收集所有 facts
ansible all -m setup -a "filter=ansible_mem*" # 过滤内存信息
ansible all -m setup -a "filter=ansible_eth*"
# ====== ansible-playbook 常用参数 ======
ansible-playbook site.yml # 执行
ansible-playbook site.yml --check # 干跑
ansible-playbook site.yml --check --diff # 干跑+显示差异
ansible-playbook site.yml -vvv # 详细输出(-v/-vv/-vvv/-vvvv)
ansible-playbook site.yml --limit web1,web2 # 限定主机
ansible-playbook site.yml --tags "install,config" # 指定标签
ansible-playbook site.yml --skip-tags "deploy" # 跳过标签
ansible-playbook site.yml --start-at-task "Install Nginx" # 从指定任务开始
ansible-playbook site.yml --step # 逐步确认执行
ansible-playbook site.yml -e "app_version=2.0.0" # 传递额外变量
ansible-playbook site.yml -e @extra_vars.yml # 从文件加载变量
ansible-playbook site.yml --forks 20 # 并发数
ansible-playbook site.yml --vault-password-file .vault_pass # Vault 密码
# ====== ansible-console 交互式控制台 ======
ansible-console -i inventory.ini
# 进入交互模式后可以执行:
# > web1 -m shell -a "uptime"
# > webservers -m yum -a "name=nginx state=latest"常见运维 Playbook 示例
# ====== 服务器初始化 =====#
# playbooks/server-init.yml
---
- name: Initialize new servers
hosts: new_servers
become: yes
vars:
timezone: Asia/Shanghai
ntp_servers:
- ntp.aliyun.com
- cn.ntp.org.cn
tasks:
- name: Set hostname
hostname:
name: "{{ inventory_hostname }}"
- name: Set timezone
timezone:
name: "{{ timezone }}"
- name: Install chrony (NTP)
package:
name: chrony
state: present
- name: Configure chrony
template:
src: chrony.conf.j2
dest: /etc/chrony.conf
notify: Restart chronyd
- name: Disable SELinux
selinux:
state: disabled
- name: Disable swap
command: swapoff -a
when: ansible_swaptotal_mb > 0
- name: Remove swap from fstab
lineinfile:
path: /etc/fstab
regexp: 'swap'
state: absent
- name: Set kernel parameters
sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
state: present
reload: yes
loop:
- { key: 'net.ipv4.ip_forward', value: '1' }
- { key: 'net.bridge.bridge-nf-call-iptables', value: '1' }
- { key: 'vm.swappiness', value: '1' }
- { key: 'net.core.somaxconn', value: '32768' }
- { key: 'net.ipv4.tcp_tw_reuse', value: '1' }
- { key: 'net.ipv4.tcp_max_syn_backlog', value: '8192' }
- name: Set file limits
pam_limits:
domain: '*'
limit_type: "{{ item.type }}"
limit_item: "{{ item.item }}"
value: "{{ item.value }}"
loop:
- { type: 'soft', item: 'nofile', value: 65535 }
- { type: 'hard', item: 'nofile', value: 65535 }
- { type: 'soft', item: 'nproc', value: 65535 }
- { type: 'hard', item: 'nproc', value: 65535 }
- name: Install common packages
package:
name:
- vim-enhanced
- htop
- iotop
- net-tools
- bind-utils
- telnet
- wget
- curl
- git
- unzip
- lrzsz
- tree
- lsof
state: present
- name: Configure SSH
lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
state: present
loop:
- { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
- { regexp: '^#?PubkeyAuthentication', line: 'PubkeyAuthentication yes' }
- { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
- { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 3' }
notify: Restart sshd
- name: Configure firewall
firewalld:
service: "{{ item }}"
permanent: yes
state: enabled
immediate: yes
loop:
- ssh
- http
- https
handlers:
- name: Restart chronyd
service:
name: chronyd
state: restarted
enabled: yes
- name: Restart sshd
service:
name: sshd
state: restarted
# ====== Docker 安装 =====#
# playbooks/install-docker.yml
---
- name: Install Docker
hosts: docker_hosts
become: yes
vars:
docker_version: "20.10.24"
docker_users:
- deploy
- root
tasks:
- name: Install dependencies
package:
name:
- yum-utils
- device-mapper-persistent-data
- lvm2
state: present
- name: Add Docker repository
yum_repository:
name: docker-ce
description: Docker CE Repository
baseurl: https://download.docker.com/linux/centos/7/$basearch/stable/
gpgcheck: yes
gpgkey: https://download.docker.com/linux/centos/gpg
enabled: yes
- name: Install Docker
package:
name:
- docker-ce-{{ docker_version }}
- docker-ce-cli-{{ docker_version }}
- containerd.io
state: present
- name: Configure Docker daemon
copy:
content: |
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"storage-driver": "overlay2",
"insecure-registries": ["192.168.1.100:5000"],
"registry-mirrors": ["https://mirror.ccs.tencentyun.com"]
}
dest: /etc/docker/daemon.json
mode: '0644'
notify: Restart Docker
- name: Add users to docker group
user:
name: "{{ item }}"
groups: docker
append: yes
loop: "{{ docker_users }}"
- name: Ensure Docker is running
service:
name: docker
state: started
enabled: yes
handlers:
- name: Restart Docker
service:
name: docker
state: restarted
# ====== 应用部署 =====#
# playbooks/deploy-app.yml
---
- name: Deploy Application
hosts: webservers
become: yes
vars:
app_name: myapp
app_version: "2.1.0"
deploy_dir: /opt/myapp
backup_dir: /opt/backups/myapp
jar_url: "http://nexus.internal/repository/releases/myapp-{{ app_version }}.jar"
health_url: "http://localhost:8080/actuator/health"
tasks:
- name: Create directories
file:
path: "{{ item }}"
state: directory
owner: deploy
loop:
- "{{ deploy_dir }}"
- "{{ deploy_dir }}/logs"
- "{{ deploy_dir }}/config"
- "{{ backup_dir }}"
- name: Check current version
stat:
path: "{{ deploy_dir }}/VERSION"
register: current_version
- name: Show current version
debug:
msg: "Current version: {{ current_version.stat.content | default('none') }}"
- name: Backup current version
copy:
src: "{{ deploy_dir }}/myapp.jar"
dest: "{{ backup_dir }}/myapp-{{ ansible_date_time.iso8601_basic_short }}.jar"
when: current_version.stat.exists
ignore_errors: yes
- name: Download new version
get_url:
url: "{{ jar_url }}"
dest: "{{ deploy_dir }}/myapp.jar"
owner: deploy
mode: '0755'
checksum: "sha256:{{ app_checksum }}"
- name: Write version file
copy:
content: "{{ app_version }}"
dest: "{{ deploy_dir }}/VERSION"
- name: Deploy config
template:
src: templates/application.yml.j2
dest: "{{ deploy_dir }}/config/application.yml"
owner: deploy
notify: Restart myapp
- name: Deploy systemd service
template:
src: templates/myapp.service.j2
dest: /etc/systemd/system/myapp.service
notify: Reload systemd
- name: Ensure myapp is running
systemd:
name: myapp
state: started
enabled: yes
daemon_reload: yes
- name: Wait for health check
uri:
url: "{{ health_url }}"
status_code: 200
register: health
until: health.status == 200
retries: 30
delay: 5
- name: Deployment successful
debug:
msg: "Application {{ app_name }} v{{ app_version }} deployed successfully!"
handlers:
- name: Restart myapp
systemd:
name: myapp
state: restarted
- name: Reload systemd
systemd:
daemon_reload: yes优点
缺点
总结
Ansible 通过 SSH 实现无代理自动化运维。Playbook 以 YAML 格式编排任务流程。内置模块覆盖包管理、文件操作、服务管理等场景。建议从小规模 Playbook 开始,逐步构建可复用的 Role。通过 Ansible Vault 保护敏感信息,通过 Ansible Galaxy 复用社区贡献的 Role,通过合理的目录结构组织 Playbook 代码。
关键知识点
- Inventory 定义主机和分组,支持静态文件和动态脚本。
- Playbook 的 notify + handler 实现变更触发的操作。
- become: yes 等同于 sudo。
- --check 干跑模式不实际修改系统。
- ansible.cfg 的优先级:环境变量 > 当前目录 > ~/.ansible.cfg > /etc/ansible/ansible.cfg。
- pipelining=True 可以显著提升 SSH 执行效率。
- block/rescue/always 实现类似 try/catch/finally 的错误处理。
- register 可以保存命令输出到变量供后续使用。
项目落地视角
- 为常用运维任务编写 Playbook(用户管理、软件安装)。
- 使用 Ansible Galaxy 的 Role 复用通用配置。
- 将 Playbook 纳入 CI/CD 流水线。
- 建立统一的 Inventory 管理所有环境的主机。
- 为每个项目维护独立的 group_vars 和 host_vars。
- 所有敏感信息使用 Ansible Vault 加密。
常见误区
- Playbook 不幂等(每次执行结果不同)。
- 在 Playbook 中硬编码敏感信息(应使用 Vault)。
- 不使用 --check 先验证就执行。
- 在循环中使用 shell/command 而不是专用模块。
- 忽略 changed_when 和 failed_when 导致误报。
- 不使用 block/rescue 处理可能失败的任务。
- Inventory 中使用 root 用户而非普通用户+become。
进阶路线
- 学习 Ansible Role 和 Galaxy 组织复杂 Playbook。
- 研究 Ansible Vault 加密敏感数据。
- 了解 Ansible AWX/Tower 图形化管理平台。
- 学习 Ansible Collection 扩展自定义模块。
- 研究 Ansible 与 Jenkins/GitLab CI 的集成。
- 了解 Ansible 与 Terraform 的搭配使用。
适用场景
- 批量服务器初始化配置。
- 应用自动化部署。
- 配置管理和一致性检查。
- 安全基线加固。
- 故障恢复和批量修复。
落地建议
- 建立 Playbook 仓库统一管理。
- 为常用配置封装 Role。
- 使用 Vault 管理敏感变量。
- 在 CI/CD 中集成 --check --diff 做变更预检。
- 定期使用 ansible-playbook --syntax-check 验证语法。
- 为重要变更建立审批流程。
排错清单
- ansible all -m ping 测试连通性。
- 用 -vvv 查看详细执行过程。
- 检查 Inventory 主机名和变量。
- 检查 SSH 密钥和权限(~/.ssh/authorized_keys 权限 600)。
- 检查 ansible_python_interpreter 路径是否正确。
- 检查 SELinux 是否阻止操作。
- 使用 ansible-playbook --syntax-check 检查语法。
复盘问题
- Ansible 和 Shell 脚本各适合什么场景?
- 如何保证 Playbook 在大规模主机上的执行效率?
- 敏感数据(密码、密钥)如何安全管理?
- Ansible 和 Terraform 如何分工协作?
- 如何建立 Ansible Playbook 的测试体系?
