跳到主要内容

运维高频模块

本章在 Playbook 编排之前,先把 Ansible 真正干活的单元——模块 吃透。你将用 ad-hoc(ansible -m 逐个验证模块行为,再过渡到 YAML 任务写法;下一章再学如何把模块组织成 play / handler / tags。

读完本章应能:

  • 说清「模块 / 参数 / 返回值 / changed」四件事
  • 用声明式模块完成文件、包、用户、服务、下载解压、健康检查
  • 明确 command/shell 的使用边界
  • 对着场景选出正确模块,而不是先写脚本

0. 模块是什么

一次 Ansible 执行的最小动作:

控制节点 → SSH → 托管节点上执行「模块代码」→ 返回 JSON(是否 changed、结果字段)
# ad-hoc:立刻验证
ansible web -m ping
ansible web -m file -a 'path=/tmp/demo state=directory mode=0755'

同一模块在 playbook 中的写法(先混个眼熟,细节见下一章):

- name: 创建目录
ansible.builtin.file:
path: /tmp/demo
state: directory
mode: "0755"
概念含义
模块名filecopyapt;推荐 FQCN:ansible.builtin.file
参数-a 或 YAML 字段,决定期望状态
返回值changed / failed / 模块自定义字段
幂等已达状态再跑 → 通常 changed=false
学习顺序

本章以 ad-hoc 为主 建立手感;参数稳定后再写进 YAML。不要还没摸过模块就堆长 playbook。

查文档:

ansible-doc file
ansible-doc -l | head
ansible-doc copy | less

1. 连通与事实:ping / setup

1.1 ping

不是 ICMP,而是「SSH + Python + 模块通道」自检:

ansible web -m ping
# web1 | SUCCESS => { "changed": false, "ping": "pong" }

1.2 setup(Facts)

收集主机事实,供后续判断发行版、内存、网卡等:

ansible web -m setup
ansible web -m setup -a 'filter=ansible_distribution*'
ansible web -m setup -a 'filter=ansible_memtotal_mb'
ansible web1 -m setup -a 'filter=ansible_default_ipv4*'

常用事实字段:

字段含义
ansible_os_familyDebian / RedHat …
ansible_distributionUbuntu / CentOS …
ansible_distribution_version版本号
ansible_memtotal_mb内存
ansible_default_ipv4.address默认 IPv4
ansible_hostname / ansible_fqdn主机名

只取过滤结果可减小输出;play 里也可用 gather_facts: false 加速纯下发场景。


2. 文件与目录:file

管理目录、权限、属主、软链、删除,不负责文件内容。

# 目录
ansible web -m file -a 'path=/data/app state=directory owner=root group=root mode=0755'

# 空文件
ansible web -m file -a 'path=/var/lib/myapp/.keep state=touch mode=0644'

# 软链(current → 某 release)
ansible web -m file -a 'src=/opt/app/releases/20260730 dest=/opt/app/current state=link force=yes'

# 删除
ansible web -m file -a 'path=/var/tmp/old-artifact state=absent'
state用途
directory目录存在
file断言是普通文件(不会写内容)
link符号链接
hard硬链接
touch创建空文件 / 更新时间
absent删除

YAML:

- name: 数据目录
ansible.builtin.file:
path: /data/app
state: directory
owner: app
group: app
mode: "0755" # 建议字符串,避免 YAML 数字坑
recurse: false
mode 写法

写成 mode: 0644(数字)在 YAML 里可能被解析成奇怪的十进制。统一用 "0644"


3. 静态内容:copy

把控制节点文件(或字符串)推到目标机。

# 本地 files/motd → 远端
ansible web -m copy -a 'src=files/motd dest=/etc/motd owner=root mode=0644 backup=yes'

# 直接写内容
ansible web -m copy -a 'content="hello from ansible\n" dest=/tmp/hello.txt mode=0644'

项目里建议:

files/motd
files/certs/example.crt

YAML 关键参数:

- name: 下发证书
ansible.builtin.copy:
src: files/certs/example.crt
dest: /etc/ssl/certs/example.crt
owner: root
group: root
mode: "0644"
backup: true
# checksum: sha256:abcd...
# validate: /usr/bin/openssl x509 -noout -in %s
参数作用
src控制节点路径(相对 playbook/role 的 files/
content内联文本
dest目标路径
backup覆盖前备份
remote_src源已在目标机
force是否覆盖
validate落地前校验,%s = 临时文件
checksum完整性校验

template 的选择:

  • 内容固定copy
  • 随主机/变量变化template

4. 配置渲染:template

Jinja2 模板是配置管理的核心模块。

templates/nginx.conf.j2(可先放在仓库 templates/,ad-hoc 用绝对路径或下一章放进 play/role):

# {{ ansible_managed }}
worker_processes {{ nginx_worker_processes | default(1) }};
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

events {
worker_connections {{ nginx_worker_connections | default(1024) }};
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
server_tokens off;

server {
listen {{ nginx_listen_port | default(80) }} default_server;
server_name {{ nginx_server_name | default('_') }};
root {{ nginx_root | default('/var/www/html') }};

location / {
try_files $uri $uri/ =404;
}

location /healthz {
access_log off;
return 200 'ok';
add_header Content-Type text/plain;
}
}
}

YAML:

- name: 渲染 nginx 配置
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
backup: true
validate: "/usr/sbin/nginx -t -c %s"
# ad-hoc 也可以,但变量传递别扭,配置类更推荐 playbook
ansible web -m template -a 'src=/path/to/nginx.conf.j2 dest=/etc/nginx/nginx.conf validate="/usr/sbin/nginx -t -c %s"'

常用 Jinja:

{{ http_port | int }}
{{ name | default('app') }}
{{ optional | default([]) }}
{% if nginx_healthz_enable | default(true) | bool %}
...
{% endif %}
{% for h in groups['web'] | default([]) %}
# peer {{ h }} {{ hostvars[h].ansible_host | default('') }}
{% endfor %}
validate

能做本地语法检查的组件(nginx、haproxy、named、sudoers 用 visudo -cf)都应挂 validate。失败则不覆盖目标文件,避免推坏配置。


5. 改一行 / 一小段:lineinfile / blockinfile

适合 sshd、sysctl、小开关;大段配置仍用 template。

5.1 lineinfile

SSH 配置会影响远程登录,不能只用一条 ad-hoc 命令直接改。请保留一个已经登录的终端,并使用下面带语法校验与 handler 的 playbook task。

tasks:
- name: 设置 SSH 密码登录开关
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication\s+'
line: "PasswordAuthentication {{ ssh_password_authentication | default(false) | ternary('yes', 'no') }}"
backup: true
validate: "/usr/sbin/sshd -t -f %s"
state: present
notify: Reload SSH service

handlers:
- name: Reload SSH service
ansible.builtin.service:
name: "{{ 'ssh' if ansible_os_family == 'Debian' else 'sshd' }}"
state: reloaded

要点:

  • 必须写好 regexp,否则重复执行会追加多行
  • state: absent + regexp 可删行
  • validate 失败时不会写入坏配置;成功后才由 handler reload
  • 改 sshd 前确保有第二条登录通道,并在新会话验证公钥登录后再关闭旧会话
  • 示例读取 ssh_password_authentication;变量本身不产生变更,只有被 task 引用才会生效

5.2 blockinfile

- name: 受管 sysctl 片段
ansible.builtin.blockinfile:
path: /etc/sysctl.d/99-ansible.conf
create: true
marker: "# {mark} ANSIBLE MANAGED BLOCK net.basic"
block: |
net.ipv4.ip_forward = 0
fs.file-max = 2097152
backup: true

marker 让 Ansible 认得自己的块,二次执行是更新块而不是傻追加。


6. 查询文件状态:stat

改之前先看,常与条件配合(条件语法下一章细讲):

ansible web -m stat -a 'path=/etc/nginx/nginx.conf'
- name: 检查配置是否存在
ansible.builtin.stat:
path: /etc/nginx/nginx.conf
register: nginx_conf

# 下一章配合 when: nginx_conf.stat.exists

返回里常用:stat.exists / stat.isdir / stat.checksum / stat.mode


7. 包管理:package / apt / dnf / yum

7.1 跨发行版:package

ansible web -m package -a 'name=curl state=present'
ansible web -m package -a 'name=jq,ca-certificates state=present'
- name: 安装通用工具
ansible.builtin.package:
name:
- curl
- jq
- ca-certificates
state: present

7.2 Debian 家族:apt

ansible web -m apt -a 'name=nginx state=present update_cache=yes cache_valid_time=3600'
ansible web -m apt -a 'name=nginx state=absent purge=yes'
- name: 安装 nginx(Debian)
ansible.builtin.apt:
name: nginx
state: present # present / absent / latest
update_cache: true
cache_valid_time: 3600
when: ansible_os_family == "Debian"

钉版本:name: nginx=1.24.*(视仓库而定)。

7.3 RedHat 家族:dnf / yum

- name: 安装 nginx(RedHat)
ansible.builtin.dnf:
name: nginx
state: present
when: ansible_os_family == "RedHat"
state建议
present默认;生产可钉版本
absent卸载
latest易漂,生产慎用
别用 shell 装包

shell: apt-get install -y nginx 难幂等、难审计。一律走包模块;内网先配好镜像源。


8. 仓库与密钥环(补充)

偶尔需要加源(示例,按公司镜像改):

- name: 添加内部 apt 源(示意)
ansible.builtin.apt_repository:
repo: "deb https://mirrors.example.com/ubuntu jammy main"
state: present
filename: internal
when: ansible_os_family == "Debian"

RHEL 系对应 ansible.builtin.yum_repository。密钥用 get_url/copy 下发后再 apt_key(视发行版新老,新 Ubuntu 更推荐 signed-by 文件方式)。本章知存在即可,生产按平台规范选型。


9. 用户与组:user / group

ansible web -m group -a 'name=app system=yes'
ansible web -m user -a 'name=app group=app system=yes shell=/usr/sbin/nologin home=/data/app create_home=yes'
- name: app 组
ansible.builtin.group:
name: app
system: true

- name: app 用户
ansible.builtin.user:
name: app
group: app
system: true
shell: /usr/sbin/nologin
home: /data/app
create_home: true
password_lock: true
# password: "{{ 'secret' | password_hash('sha512') }}" # 需登录时再设
参数说明
system系统用户
uid/gid固定 ID,集群一致时有用
groups附加组
remove删用户时是否移除家目录(配合 state=absent

10. SSH 公钥:authorized_key

集合:ansible.posix(缺失则安装)。

ansible-galaxy collection install ansible.posix

ansible web -m ansible.posix.authorized_key -a "user=deploy key='ssh-ed25519 AAAA... deploy@ci' state=present"
- name: 写入 deploy 公钥
ansible.posix.authorized_key:
user: deploy
key: "{{ lookup('file', 'files/ssh/deploy.pub') }}"
state: present
exclusive: false # true 会清掉未声明的 key,慎用

也可用 copy 整文件管理 authorized_keys,但多人公钥合并策略要自己定;authorized_key 更适合「声明某一把 key 存在」。


11. 服务:service / systemd

11.1 service(通用)

ansible web -m service -a 'name=nginx state=started enabled=yes'
ansible web -m service -a 'name=nginx state=reloaded'
ansible web -m service -a 'name=nginx state=restarted' # 慎用,优先 reload
state含义
started / stopped运行状态
reloaded重载配置
restarted重启进程
enabled/disabled是否开机自启(参数 enabled:

11.2 systemd(单元文件 / daemon-reload)

- name: 安装 unit
ansible.builtin.template:
src: myapp.service.j2
dest: /etc/systemd/system/myapp.service
mode: "0644"

- name: 启动 myapp
ansible.builtin.systemd:
name: myapp
state: started
enabled: true
daemon_reload: true

原则:reloaded 就不要 restarted;配置变更触发重启应放到 handler(下一章),而不是每个 task 后都 restart。


12. 下载与解压:get_url / unarchive

12.1 get_url

ansible web -m get_url -a 'url=https://example.com/pkg/app.tar.gz dest=/tmp/app.tar.gz mode=0644 checksum=sha256:abcd...'
- name: 下载制品
ansible.builtin.get_url:
url: "{{ app_tarball_url }}"
dest: /tmp/app.tar.gz
mode: "0644"
checksum: "sha256:{{ app_tarball_sha256 }}"
timeout: 60

12.2 unarchive

- name: 解压到版本目录
ansible.builtin.unarchive:
src: /tmp/app.tar.gz
dest: /opt/app/releases/{{ app_version }}
remote_src: true
creates: /opt/app/releases/{{ app_version }}/bin/app
参数说明
remote_src压缩包已在目标机(常与 get_url 联用)
creates目标已存在则跳过,利于幂等
extra_opts传给 tar 的额外参数

本地打包上传也可:remote_src: falsesrc 指向控制节点文件。


13. 等待与探测:wait_for / uri

13.1 wait_for

ansible web -m wait_for -a 'port=80 host=127.0.0.1 delay=2 timeout=60'
ansible web -m wait_for -a 'path=/var/run/nginx.pid timeout=30'
- name: 等待端口就绪
ansible.builtin.wait_for:
host: 127.0.0.1
port: "{{ nginx_listen_port | default(80) }}"
delay: 2
timeout: 60
state: started

13.2 uri(HTTP 检查 / 调 API)

ansible web -m uri -a 'url=http://127.0.0.1/healthz return_content=yes status_code=200'
- name: 健康检查
ansible.builtin.uri:
url: "http://127.0.0.1:{{ nginx_listen_port }}/healthz"
method: GET
status_code: 200
return_content: true
register: health
# until/retries 见下一章

也可 POST JSON、带 Header,适合探针与简易 API 调用;复杂集成仍更适合 Python。


14. 定时任务:cron

- name: 每天清理日志
ansible.builtin.cron:
name: "cleanup app logs"
user: root
minute: "30"
hour: "3"
job: "/usr/local/bin/cleanup-logs.sh >/var/log/cleanup-logs.log 2>&1"
state: present

name 作为唯一标识,二次执行是更新同一条,而不是盲追加。删任务:state: absent + 同名。


15. 主机名与其它常用模块(速通)

hostname

- name: 设置主机名
ansible.builtin.hostname:
name: "{{ inventory_hostname }}"

sysctl

- name: 调整 file-max
ansible.builtin.sysctl:
name: fs.file-max
value: "2097152"
state: present
reload: true
sysctl_file: /etc/sysctl.d/99-ansible.conf

mount(了解)

- name: 挂载数据盘(示意)
ansible.builtin.mount:
path: /data
src: /dev/vdb1
fstype: xfs
opts: defaults,noatime
state: mounted

磁盘分区/格式化风险高,生产变更要单独评审,本系列不展开破坏性存储操作。

git(可选)

- name: 拉取配置仓库
ansible.builtin.git:
repo: "https://git.example.com/ops/configs.git"
dest: /opt/configs
version: main
force: false

密钥与镜像权限处理好再用;许多团队更倾向 CI 构建制品 + get_url


16. 命令类:command / shell / raw / script

16.1 对照

模块特点何时用
command不走 shell,无管道/重定向简单二进制调用
shell/bin/sh -c必须管道、通配、环境复杂时
raw几乎不依赖 Python引导极老/无 Python 主机
script把本地脚本拷过去执行短时辅助脚本
ansible web -m command -a 'uptime'
ansible web -m command -a 'nginx -t'
ansible web -m shell -a 'ss -lntp | grep -W :80 || true'

16.2 必须声明变更语义

- name: 只读版本查询
ansible.builtin.command: /usr/sbin/nginx -v
register: nginx_ver
changed_when: false
failed_when: nginx_ver.rc != 0

- name: 带 creates 的初始化脚本
ansible.builtin.command: /usr/local/bin/init-node.sh
args:
creates: /var/lib/myapp/initialized

16.3 反模式

# 坏例子:难幂等、难回滚、难审计
- ansible.builtin.shell: |
apt-get update && apt-get install -y nginx
echo "x" >> /etc/nginx/nginx.conf
systemctl restart nginx

改写方向:apt + template/lineinfile + service(reload 放 handler)。


17. 调试模块:debug / assert

ansible web -m debug -a 'var=inventory_hostname'
ansible web -m debug -a 'msg=port is {{ http_port | default(80) }}'
- name: 打印变量
ansible.builtin.debug:
var: ansible_distribution

- name: 断言内存足够
ansible.builtin.assert:
that:
- ansible_memtotal_mb | int >= 1024
fail_msg: "内存不足 1G,拒绝继续"

排障神器;不要 debug 密码(即便有 Vault)。


18. 模块怎么选(决策表)

你想做的事优先模块
目录/权限/软链/删除file
固定文件内容copy
按主机渲染配置template
改单行/小块lineinfile / blockinfile
看文件是否存在stat
装软件package / apt / dnf
用户组user / group
SSH 公钥ansible.posix.authorized_key
启停服务service / systemd
下载get_url
解压unarchive
等端口/文件wait_for
HTTP 检查uri
定时任务cron
内核参数sysctl
实在没模块command/shell + changed_when/creates

19. 串烧练习:不用完整 Playbook 也能验证

先用 ad-hoc 在一台机上走通(变量少的步骤):

ansible web1 -m package -a 'name=nginx state=present'
ansible web1 -m file -a 'path=/var/www/html state=directory mode=0755'
ansible web1 -m copy -a 'content="<h1>demo</h1>\n" dest=/var/www/html/index.html mode=0644'
ansible web1 -m service -a 'name=nginx state=started enabled=yes'
ansible web1 -m uri -a 'url=http://127.0.0.1/ return_content=yes'

配置文件再用 template(建议直接进入下一章写成 play,带 validate 与变量)。


20. 练习与验收

练习 A:file/copy
创建 /data/app/{bin,conf,logs},下发一个 motd,二次执行无 changed。

练习 B:包与服务
安装 nginxstarted+enabled,用 uriwait_for 验证 80 端口。

练习 C:lineinfile
在实验机改某一非关键配置行(不要先动 sshd),确认 regexp 正确、不重复追加。

练习 D:command 边界
写一条 changed_when: false 的版本查询;再故意写一条会每次 changed 的 shell,观察差异后删掉它。

验收清单

  • 会用 ansible-doc <module> 查参数
  • 能区分 copy / template / lineinfile
  • 装包与服务不用 shell
  • 理解 changed 含义,会处理只读 command

下一章:Playbook 核心 —— 把模块编排成 play,加上 handler、when、loop、tags。