深入浅出Ansible语法——Ansible安全基线配置(一)
【摘要】 引言大家好,我是 Prism,本篇带你快速掌握 Ansible 常用语法与实践技巧。文中保留了大量示例,便于照抄并在真实环境中测试。本篇适合刚接触 Ansible 的运维或开发人员,也可作为查阅参考。 一、变量(Variables)变量是 Ansible 的基础,掌握变量定义与查找顺序能够极大提高剧本的灵活性与可维护性。 常见变量来源(优先级从低到高简述)Role defaults: ro...
引言
大家好,我是 Prism,本篇带你快速掌握 Ansible 常用语法与实践技巧。文中保留了大量示例,便于照抄并在真实环境中测试。本篇适合刚接触 Ansible 的运维或开发人员,也可作为查阅参考。
一、变量(Variables)
变量是 Ansible 的基础,掌握变量定义与查找顺序能够极大提高剧本的灵活性与可维护性。
常见变量来源(优先级从低到高简述)
- Role defaults: roles/<role>/defaults/main.yml(最低优先)
- inventory(hosts 文件、group_vars、host_vars)
- group_vars/all.yml:全局变量
- group_vars/<group>.yml:组变量
- host_vars/<host>.yml:主机变量(优先级较高)
- Playbook 的 vars / vars_files(局部或导入)
- Role vars: roles/<role>/vars/main.yml
- 命令行 -e 参数(最高优先)
- facts(自动收集,ansible_* 前缀)
- register(运行时注册的结果)
提示:
- 敏感信息请用 Ansible Vault 管理,不要直接写到源码库。
变量实战演练(示例:vars / register / facts)
---
- name: 演示变量的用法
hosts: localhost # 只在本地运行
gather_facts: true # 明确开启事实变量
vars: # 1. 在 'vars:' 块中静态定义变量
user_name: "你"
target_dir: "/tmp/test-{{ user_name }}" # 变量可以嵌套引用
packages_to_install:
- htop
- curl
- vim
tasks:
- name: 1. 使用 'vars' 块中的变量
ansible.builtin.debug:
msg: "我将为用户 {{ user_name }} 在 {{ target_dir }} 创建目录。"
- name: 2. 使用 'facts' 变量 (自动收集)
ansible.builtin.debug:
msg: "你正在运行的系统是 {{ ansible_distribution }} {{ ansible_distribution_version }},你的 IP 是 {{ ansible_default_ipv4.address }}。"
- name: 3. 运行命令并 'register' 它的输出
ansible.builtin.command: "uptime -p"
register: uptime_result # 把命令的标准输出/错误/返回码存到这个变量
changed_when: false # 这个命令只是查询,不会改变状态
- name: 4. 使用 'register' 的变量
ansible.builtin.debug:
# 'uptime_result' 是个字典,我们用 .stdout 访问它的标准输出
msg: "服务器已运行时间: {{ uptime_result.stdout }}"
补充说明:
- 使用 default / omit 等过滤器可增强变量的健壮性:
- {{ myvar | default(‘fallback’) }}
- 在模块参数中使用 “{{ somevar | default(omit) }}” 可避免传入空值。
二、流程控制(When / Loop / Blocks)
流程控制决定剧本在不同环境下的分支执行逻辑与重复操作。
When(条件)
- when: 表达式支持 Jinja2 语法与常见比较(==, !=, in, is defined 等)
- 常用于按照操作系统分支执行不同模块、按标签跳过任务等
Loop(循环)
- loop: 用于遍历列表;配合 loop_control 可自定义循环变量名与索引
Block / Rescue / Always(错误处理与事务)
- block: 将多个任务作为一个逻辑单元;配合 rescue 和 always 实现 try/catch/finally 行为
流程控制示例(when + loop)
---
- name: 演示流程控制 (when 和 loop)
hosts: all
gather_facts: true
become: true # 安装和管理文件需要提权
vars:
# 准备一个字典列表,用于循环
users_info:
- name: "你"
uid: 1001
shell: /bin/bash
- name: alice
uid: 1002
shell: /bin/bash
- name: temp_guest
state: absent # 我们希望删除这个用户
tasks:
# --- WHEN 示例 ---
- name: 1. [WHEN] 仅在 Debian/Ubuntu 上安装 UFW 防火墙
ansible.builtin.apt:
name: ufw
state: present
when: ansible_os_family == "Debian" # 关键!
- name: 2. [WHEN] 仅在 RedHat/CentOS 上安装 firewalld
ansible.builtin.yum:
name: firewalld
state: present
when: ansible_os_family == "RedHat" # 关键!
# --- LOOP 示例 ---
- name: 3. [LOOP] 批量创建目录
ansible.builtin.file:
path: "/opt/data-{{ item }}"
state: directory
mode: '0755'
loop:
- web
- db
- logs
- name: 4. [LOOP + WHEN] 批量管理用户 (高级)
ansible.builtin.user:
name: "{{ item.name }}"
uid: "{{ item.uid | default(omit) }}" # 如果没提供uid,则忽略(omit)此参数
shell: "{{ item.shell | default(omit) }}" # 如果没提供shell,则忽略
state: "{{ item.state | default('present') }}" # 默认是 present (创建)
loop: "{{ users_info }}"
when: item.name is defined # 只有当 item.name 存在时才执行
提示:
- 对于复杂循环可使用 subelements、with_items、dict2items 等高级技巧。
三、错误处理(Error Handling)
默认情况下任务失败会使当前主机的 play 停止执行。常用策略如下:
- ignore_errors: 跳过错误继续下一步(适用于可选步骤)
- failed_when / changed_when: 自定义失败或变更判断
- block / rescue / always: 推荐用于重要操作的回滚与补救
错误处理示例
---
- name: 演示错误处理
hosts: localhost
gather_facts: false
tasks:
- name: 1. [ignore_errors] 尝试删除一个可能不存在的文件
ansible.builtin.command: "rm /tmp/a_file_that_may_not_exist.txt"
ignore_errors: true # 就算 'rm' 报错 (文件不存在),也继续
changed_when: false # 'rm' 改变了系统状态,但我们不关心
- name: 2. [failed_when] 自定义失败条件
# 某个安全脚本,如果返回码是 0 表示 '安全',1 表示 '有警告',2 表示 '有漏洞'
# 我们希望 0 和 1 都算成功,只有 2 才算失败
ansible.builtin.command: "/usr/bin/run_security_check.sh"
register: security_check
ignore_errors: true # 先忽略默认的失败 (返回码 > 0)
- name: 2b. 真正检查 security_check 的结果
ansible.builtin.fail:
msg: "安全检查失败!返回码: {{ security_check.rc }},输出: {{ security_check.stdout }}"
when: security_check.rc == 2 # *只有*当返回码是 2 时,才执行 'fail' 模块
- name: 3. [block/rescue/always] 演示 'try...catch...finally'
block:
# --- (TRY) 尝试执行 ---
- name: 3a. (BLOCK) 尝试下载主要配置文件
ansible.builtin.uri:
url: "http://config-server.internal/primary_config.conf"
dest: "/etc/myapp/config.conf"
timeout: 5
rescue:
# --- (CATCH) 如果 BLOCK 中有任务失败 ---
- name: 3b. (RESCUE) 下载失败,使用备用配置
ansible.builtin.copy:
src: "files/fallback_config.conf"
dest: "/etc/myapp/config.conf"
always:
# --- (FINALLY) 无论成功还是失败 ---
- name: 3c. (ALWAYS) 确保服务被重启以加载配置
ansible.builtin.service:
name: myapp
state: restarted
建议:
- 仅对“可容忍失败”的非关键任务使用 ignore_errors;关键失败应使用 fail 报错并显式提示操作人员。
四、Roles(可复用的剧本结构)
Role 提供了约定的目录结构(tasks、vars、templates、files、handlers、defaults、meta),便于复用与发布到 Ansible Galaxy。
创建与使用 Role 的快速流程:
- ansible-galaxy init <role_name>
- 编辑 roles/<role>/tasks/main.yml、vars/main.yml 等
- 在 playbook 中通过 roles: 调用
示例 Role 任务片段:
---
# tasks/main.yml(示例)
- name: 确保 UFW 防火墙已安装
ansible.builtin.apt:
name: ufw
state: present
when: ansible_os_family == "Debian"
- name: 禁用 root SSH 登录 (安全加固)
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: "^PermitRootLogin"
line: "PermitRootLogin no"
state: present
notify: Restart SSHD
对应 handler 示例:
---
# handlers/main.yml
- name: Restart SSHD
ansible.builtin.service:
name: sshd
state: restarted
提示:
- Role 的 defaults 比 vars 优先级低,适合放可被覆盖的默认值。
五、Templates(Jinja2 模板)
模板能根据主机变量渲染不同配置文件,适合 sshd_config、nginx.conf、systemd 单元等场景。
模板示例(sshd_config.j2):
# /etc/ssh/sshd_config
Port {{ ssh_port | default(22) }}
PermitRootLogin {{ allow_root_login | default("no") }}
# 使用 for 循环
{% for user in ssh_allow_users %}
AllowUsers {{ user }}
{% endfor %}
# 使用 if 判断
{% if ansible_os_family == "Debian" %}
UsePAM yes
{% else %}
UsePAM no
{% endif %}
在 playbook 中渲染:
- name: 部署 SSHD 配置文件 (安全加固)
hosts: ssh_servers
become: true
vars:
ssh_port: 2222
allow_root_login: "no"
ssh_allow_users:
- "你"
- admin
tasks:
- name: 部署模板化的 SSHD 配置文件
ansible.builtin.template:
src: templates/sshd_config.j2
dest: /etc/ssh/sshd_config
mode: '0600'
owner: root
group: root
backup: yes
notify: Restart SSHD
handlers:
- name: Restart SSHD
ansible.builtin.service:
name: sshd
state: restarted
建议:
- 模板内避免复杂逻辑;复杂计算可在 playbook 里预处理并传入模板。
六、Ansible Vault(敏感信息加密)
Ansible Vault 用于加密变量文件或剧本中的敏感数据,推荐在 CI/CD 或多人协作时强制使用。
常用命令:
# 新建并加密文件
ansible-vault create host_vars/db_server_01.yml
# 编辑加密文件
ansible-vault edit host_vars/db_server_01.yml
# 运行 playbook 时提供 vault 密码
ansible-playbook deploy_db.yml --ask-vault-pass
示例加密文件内容(编辑时输入):
db_password: "MySuperSecretPassword123"
api_key: "sk_live_123456789"
建议:
- 在 CI 中用 --vault-password-file 或使用 CI 平台的 Secret 管理器,避免交互式输入。
七、常用模块与工具
- 文件/目录:file、copy、template
- 包管理:apt、yum、dnf、package
- 服务管理:service、systemd
- 命令执行:command、shell(尽量避免 shell)
- libvirt/KVM:community.libvirt.*(需要安装相应 Collection)
- 查看模块文档:ansible-doc
示例命令:
# 列出本地模块
ansible-doc -l
# 查看模块详细文档
ansible-doc file
# 查看模块简短说明
ansible-doc -s file
八、最佳实践小贴士(速览)
- 将敏感凭据放入 Vault 或外部秘钥管理系统。
- 对复杂逻辑使用 roles 与模块化。
- 避免使用 shell 模块替代专用模块(idempotent)。
- 使用 handlers 来减少不必要的服务重启。
- 在 CI 中执行 lint(ansible-lint)与语法检查(ansible-playbook --syntax-check)。
- 小步迭代、频繁验证:先在 test 环境运行,再推广到生产。
结语
本文保留了丰富示例并在关键段落加入了说明,旨在帮助你快速上手并编写更稳健的 Ansible 剧本。实践中遇到具体问题可以把剧本与错误输出贴出来,我会帮你分析建议。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)