Ansible常见模块总结及LDAP Role 编写与调试

一、Ansible 常见模块使用总结

1. command 模块

Ansible 的默认模块,用于在远程主机上执行简单的 Linux 命令。

特点:

  • 不通过 shell 解析,直接执行命令
  • 不支持管道符 |、重定向 ><、变量 $HOME、分号 ;、与符号 & 等特殊符号
  • 安全性较高,可防止命令注入
  • 不具备幂等性

常用参数:

参数 说明
chdir 执行命令前,先切换到指定目录
creates 如果指定文件存在,则不执行命令
removes 如果指定文件不存在,则不执行命令
free_form 要执行的命令本身

使用示例:

bash 复制代码
# 查看主机名
ansible all -m command -a "hostname"
​
# 创建目录(简单命令优先使用command)
ansible node2 -m command -a 'mkdir -p /tmp/mydir'[reference:13]
​
# 先切换目录再执行
ansible all -m command -a "chdir=/tmp ls -l"
​
# 如果文件已存在则跳过
ansible all -m command -a "creates=/etc/my.conf touch /etc/my.conf"

2. shell 模块

command 模块的"升级版",通过远程主机的 /bin/sh 来执行命令。

特点:

  • 使用完整的 shell 解析,支持管道、重定向、环境变量等
  • 存在命令注入风险,需要严格验证输入
  • 不具备幂等性
  • 可通过 executable 参数指定其他 shell 解释器

使用示例:

bash 复制代码
# 使用管道统计(command无法实现)
ansible node2 -m shell -a 'grep "error" /var/log/dmesg | wc -l'[reference:22]
​
# 使用环境变量
ansible web -m shell -a "cd $HOME; pwd"[reference:23]
​
# 使用重定向
ansible web -m shell -a "echo 'hello' > /tmp/test.txt"
​
# 指定解释器
ansible all -m shell -a "executable=/bin/bash echo $HOME"

Playbook 中使用: l

yaml 复制代码
- name: 统计日志错误行数
  hosts: node2
  tasks:
    - name: 使用shell模块统计
      shell: grep 'error' /var/log/messages | wc -l
      register: error_count
    
    - name: 打印结果
      debug:
        msg: "错误行数为:{{ error_count.stdout }}"[reference:24]

3. 其他常用模块

模块 功能 示例
ping 测试主机连通性 ansible webservers -m ping
copy 从主控端拷贝文件到远程主机 ansible webservers -m copy -a "src=/root/tcp dest=/tmp/ mode=600"
file 管理文件/目录属性(权限、所有者等) ansible all -m file -a "path=/tmp/test state=directory mode=755"
template 使用 Jinja2 模板生成配置文件 ansible all -m template -a "src=nginx.conf.j2 dest=/etc/nginx/nginx.conf"
fetch 从远程主机拉取文件到本地 ansible all -m fetch -a "src=/var/log/syslog dest=/backup/"
script 在远程主机执行本地脚本 ansible all -m script -a "/server/scripts/test.sh"
hostname 修改远程主机的主机名 ansible 目标ip -m hostname -a 'name=my.cluster.com'
yum/apt 包管理(具备幂等性) ansible all -m yum -a "name=httpd state=present"
service 管理系统服务 ansible all -m service -a "name=httpd state=started enabled=yes"

4. command vs shell 选择建议

| 场景 | 推荐模块 | 原因 |
|-------------------------|-----------------------|-----------------------|-------------|
| 简单命令(ls、mkdir、hostname) | command | 更安全,防止注入 |
| 需要使用管道、重定向 | shell | command 不支持 |
| 需要使用环境变量 | shell | command 不支持 $HOME 等 |
| 命令中包含 ` | ><;&` | shell | command 不支持 |
| 不确定时 | shell | 兼容性更广,但需注意安全 |

注意 :shell 和 command 模块都不具备幂等性。如需要实现幂等,可结合 createsremoves 参数进行判断。


二、LDAP Role 编写与调试

以下是一个完整的 OpenLDAP 服务端部署 Role 示例,包含安装、配置、初始化等完整流程。

1. Role 目录结构

bash 复制代码
roles/openldap/
├── tasks/
│   ├── main.yml          # 主任务入口
│   ├── install.yml       # 安装任务
│   ├── configure.yml     # 配置任务
│   └── init.yml          # 初始化任务
├── handlers/
│   └── main.yml          # 重启等服务处理器
├── templates/
│   ├── slapd.conf.j2     # OpenLDAP 主配置文件模板
│   └── ldap.conf.j2      # 客户端配置文件模板
├── vars/
│   └── main.yml          # 变量定义(可覆盖)
└── defaults/
    └── main.yml          # 默认变量

2. 默认变量 (defaults/main.yml)

yaml 复制代码
---
# OpenLDAP 服务端默认配置
​
# LDAP 基础配置
ldap_domain: "example.com"
ldap_organization: "Example Inc."
ldap_admin_password: "admin123"  # 生产环境建议使用 vault 加密
​
# 根据域名自动生成 suffix 和 dc
ldap_suffix: "dc={{ ldap_domain.split('.') | join(',dc=') }}"
ldap_rootdn: "cn=admin,{{ ldap_suffix }}"
​
# 端口配置
ldap_port: 389
ldaps_port: 636
​
# 安装包名称(不同OS不同)
openldap_packages:
  - openldap
  - openldap-servers
  - openldap-clients
  - openldap-devel
​
# 配置文件路径
slapd_config_path: "/etc/openldap/slapd.conf"
ldap_config_path: "/etc/openldap/ldap.conf"

3. 主任务入口 (tasks/main.yml)

yaml 复制代码
---
# 主任务文件:按顺序执行各阶段任务
​
- name: 导入安装任务
  import_tasks: install.yml
  tags: [ldap, install]
​
- name: 导入配置任务
  import_tasks: configure.yml
  tags: [ldap, configure]
​
- name: 导入初始化任务
  import_tasks: init.yml
  tags: [ldap, init]

4. 安装任务 (tasks/install.yml)

yaml 复制代码
---
# 安装 OpenLDAP 服务端及相关软件包
​
- name: 安装 OpenLDAP 软件包
  package:
    name: "{{ openldap_packages }}"
    state: present
  become: yes
  # package 模块会根据系统自动选择 yum/apt 等包管理器
  # 具备幂等性:已安装则跳过
  register: install_result
  tags: [ldap, install]
​
- name: 创建 LDAP 用户和组
  group:
    name: ldap
    state: present
    system: yes
  become: yes
​
- name: 创建 LDAP 用户
  user:
    name: ldap
    group: ldap
    system: yes
    shell: /sbin/nologin
    home: /var/lib/ldap
  become: yes
  # 确保 slapd 进程以专用用户运行,提高安全性
​
- name: 创建数据目录
  file:
    path: /var/lib/ldap
    state: directory
    owner: ldap
    group: ldap
    mode: 0750
  become: yes

5. 配置任务 (tasks/configure.yml)

yaml 复制代码
---
# 配置 OpenLDAP 服务端
​
- name: 生成 slapd.conf 配置文件
  template:
    src: slapd.conf.j2
    dest: "{{ slapd_config_path }}"
    owner: root
    group: ldap
    mode: 0640
  become: yes
  # 使用模板动态生成配置,支持变量替换
  # 配置文件变更时触发 handlers 重启服务
  notify: restart slapd
  tags: [ldap, configure]
​
- name: 生成 ldap.conf 客户端配置文件
  template:
    src: ldap.conf.j2
    dest: "{{ ldap_config_path }}"
    owner: root
    group: root
    mode: 0644
  become: yes
  tags: [ldap, configure]
​
- name: 创建日志目录
  file:
    path: /var/log/openldap
    state: directory
    owner: ldap
    group: ldap
    mode: 0750
  become: yes
  tags: [ldap, configure]
​
- name: 启动并启用 slapd 服务
  service:
    name: slapd
    state: started
    enabled: yes
  become: yes
  # 确保服务开机自启
  tags: [ldap, configure]

6. 初始化任务 (tasks/init.yml)

yaml 复制代码
---
# 初始化 LDAP 数据库和基础条目

- name: 检查 LDAP 是否已初始化
  command: ldapsearch -x -b "{{ ldap_suffix }}" -LLL
  register: ldap_check
  failed_when: false
  changed_when: false
  # 通过查询判断是否已存在数据,避免重复初始化

- name: 创建基础 LDIF 文件
  template:
    src: base.ldif.j2
    dest: /tmp/base.ldif
    mode: 0644
  become: yes
  when: ldap_check.rc != 0
  # 仅在未初始化时执行

- name: 导入基础 LDIF 数据
  command: >
    ldapadd -x -D "{{ ldap_rootdn }}"
    -w "{{ ldap_admin_password }}"
    -f /tmp/base.ldif
  become: yes
  when: ldap_check.rc != 0
  # 使用 ldapadd 命令导入基础组织架构数据
  # 注意:command 模块不支持管道,此处无需管道,使用安全

- name: 清理临时文件
  file:
    path: /tmp/base.ldif
    state: absent
  become: yes

7. Handlers (handlers/main.yml)

yaml 复制代码
---
# 服务处理器:在配置文件变更时触发

- name: restart slapd
  service:
    name: slapd
    state: restarted
  become: yes
  # 当 slapd.conf 发生变化时,重启服务使配置生效

- name: reload slapd
  service:
    name: slapd
    state: reloaded
  become: yes
  # 部分配置变更可通过 reload 实现不中断服务

8. 配置模板 (templates/slapd.conf.j2)

bash 复制代码
# {{ ansible_managed }}
# OpenLDAP 服务端配置文件

# 基础配置
include         /etc/openldap/schema/core.schema
include         /etc/openldap/schema/cosine.schema
include         /etc/openldap/schema/inetorgperson.schema
include         /etc/openldap/schema/nis.schema

# 数据库配置
database        bdb
suffix          "{{ ldap_suffix }}"
rootdn          "{{ ldap_rootdn }}"
rootpw          {{ ldap_admin_password | password_hash('ssha') }}
# 使用 password_hash 过滤器对密码进行 SSHA 加密

# 数据目录
directory       /var/lib/ldap

# 索引配置
index           objectClass     eq
index           uid             eq,pres
index           cn              eq,pres,sub
index           sn              eq,pres,sub
index           mail            eq,pres,sub

# 访问控制
access to attrs=userPassword
    by self write
    by anonymous auth
    by * none

access to *
    by * read
    by dn="cn=admin,{{ ldap_suffix }}" write

# 日志配置
loglevel        256

9. 调试与验证

调试方法:

yaml 复制代码
# 1. 语法检查(playbook 级别)
ansible-playbook site.yml --syntax-check

# 2. 只运行特定标签的任务
ansible-playbook site.yml --tags ldap -v

# 3. 检查模式(dry-run,不实际执行)
ansible-playbook site.yml --check

# 4. 逐步调试(step 模式)
ansible-playbook site.yml --step

# 5. 使用 debug 模块打印变量
- name: 调试 LDAP 配置变量
  debug:
    msg: "suffix: {{ ldap_suffix }}, rootdn: {{ ldap_rootdn }}"

# 6. 验证 LDAP 服务是否正常
ldapsearch -x -b "{{ ldap_suffix }}" -LLL

# 7. 使用 LDAP 调试插件(如使用 microsoft.ad 集合)
# microsoft.ad.debug_ldap_client 可检查 LDAP Python 包安装情况[reference:36]

10. 使用 Role 的 Playbook 示例

yaml 复制代码
---
# site.yml - 部署 OpenLDAP 服务

- name: 部署 OpenLDAP 服务端
  hosts: ldap_servers
  become: yes
  vars:
    ldap_domain: "mycompany.com"
    ldap_organization: "My Company Ltd."
    ldap_admin_password: "{{ vault_ldap_password }}"  # 从 vault 读取

  roles:
    - role: openldap
      tags: [ldap, openldap]

  post_tasks:
    - name: 验证 LDAP 服务状态
      command: systemctl status slapd
      register: service_status
      changed_when: false

    - name: 打印服务状态
      debug:
        msg: "SLAPD 服务状态: {{ service_status.stdout_lines[0] }}"

关键代码注释总结

代码位置 关键点 说明
defaults/main.yml 变量分层设计 默认值与系统变量分离,便于覆盖
tasks/install.yml package 模块 跨平台包管理,自动适配 yum/apt
tasks/configure.yml template + notify 模板渲染配置,变更时触发 handler 重启
tasks/init.yml command + register + when 通过查询结果判断是否执行初始化,实现幂等
handlers/main.yml handler 分离 仅在变更时触发,避免不必要的服务重启
templates/slapd.conf.j2 password_hash 过滤器 对敏感密码进行 SSHA 加密存储
调试部分 --check / --step / debug 多种调试手段确保 Role 正确性

三、面试高频考察

模块一:核心模块辨析(必问!)

面试官经典三板斧: "command 和 shell 的区别?什么时候用哪个?"

| 对比维度 | command(默认模块) | shell(需显式调用) |
|--------------|--------------------------------------------------------------------------------------------------|-----------------------------------|---------|
| 执行环境 | 直接 fork 执行,不加载系统环境变量 | 通过远程主机的 /bin/sh 执行,加载用户环境 |
| 是否支持特殊符号 | 不支持:` | (管道)、>(重定向)、;&$HOME` | 全支持 |
| 安全性 | (命令注入风险低,参数被转义) | (拼接字符串极易产生注入漏洞) |
| 幂等性 | 不具备(除非结合 creates/removes) | 不具备(除非结合 creates/removes) |
| 面试推荐回答 | "能用 command 坚决不用 shell" 。在实现 chdircreates 等场景时优先选择 command,仅在必须使用管道、重定向或环境变量时降级为 shell。 | |


模块二:幂等性与状态模块(高级工程师的试金石)

面试官常问: "Ansible 如何保证反复执行不报错?"

  • 核心原理 :Ansible 的多数模块(如 copyfileyumservice)本身具备幂等性 ,即只有资源状态与预期不符时才会执行变更(changed 状态)。

  • 针对 Command/Shell 的处理: 由于这两个模块本身不幂等,必须通过参数人工干预:

    • creates:若指定文件已存在,则跳过执行。
    • removes:若指定文件不存在,则跳过执行。
  • LDAP Role 中的高级实践(关键代码注释):

    yaml 复制代码
    # 通过 register 注册执行结果,再配合 when 条件判断实现幂等
    - name: 检查 LDAP 是否已初始化
      command: ldapsearch -x -b "{{ ldap_suffix }}" -LLL
      register: ldap_check
      failed_when: false   # 即使查询不到(返回非0)也不中断Playbook
      changed_when: false  # 此步骤仅用于探测,不标记为变更
    
    - name: 导入初始化数据
      command: ldapadd -x -D "{{ ldap_rootdn }}" -w "{{ ldap_admin_password }}" -f /tmp/base.ldif
      when: ldap_check.rc != 0  # 只有当上一步检测到数据不存在时才执行导入

模块三:变量优先级与 Jinja2 过滤器(必考送分题)

面试官常问: "defaults 和 vars 里的同名变量,谁生效?"

  • 优先级顺序(从低到高)role defaults(最低) → inventory variablesplaybook varsextra vars(最高,命令行-e指定)。

  • LDAP Role 中的安全过滤器 (面试加分项): 在模板 slapd.conf.j2 中,使用 password_hash 过滤器对明文密码加密存储,避免配置文件泄露风险。

    scss 复制代码
    rootpw  {{ ldap_admin_password | password_hash('ssha') }}

    面试话术 :"生产环境绝不存明文密码,我会结合 ansible-vault 加密变量文件,再配合 password_hash 过滤器写入配置。"


模块四:Handlers 与 notify 的触发机制(踩坑预警)

面试官高频追问: "如果 handler 被多次触发,它会执行几次?"

  • 正确答案只执行一次。Ansible 会在 Playbook 所有 Task 执行完毕后,统一触发 Handlers,且去重后只执行一次。

  • 注意陷阱 :如果某个 Task 使用了 listen 监听通用频道,或 handler 名称重复,只会合并执行一次。

  • Role 中的标准写法

    yaml 复制代码
    # tasks/configure.yml
    - name: 生成 slapd.conf
      template: src=slapd.conf.j2 dest=/etc/openldap/slapd.conf
      notify: restart slapd   # 配置改变时通知
    
    # handlers/main.yml
    - name: restart slapd
      service: name=slapd state=restarted

模块五:调试与故障排查实战(体现经验值)

面试场景: "客户环境 Playbook 报错了,你怎么快速定位?"

调试级别 命令/参数 用途
语法检查 ansible-playbook site.yml --syntax-check 最先执行,检查 YAML 缩进和语法错误
干跑模式 ansible-playbook site.yml --check 模拟执行,展示会发生什么变更(需模块支持)
分步执行 ansible-playbook site.yml --step 每执行一个 Task 前都需人工确认,适合排查复杂逻辑
变量调试 在 Playbook 中插入 debug 模块 打印 {{ ldap_suffix }} 等中间变量值
SSH 连接排错 ansible -m ping -vvv 三级 verbose 输出详细的 SSH 认证过程

模块六:Role 的企业级目录规范(架构能力)

面试官问: "你写的 Role 怎么保证团队协作和维护?"

优秀的 Role 结构必须职责分离(对应之前 LDAP Role 的拆分逻辑):

bash 复制代码
roles/openldap/
├── tasks/
│   ├── main.yml       # 仅做 import 调度(入口清晰)
│   ├── install.yml    # 只管 yum/apt 装包
│   ├── configure.yml  # 只管 template 渲染 + notify
│   └── init.yml       # 只管 ldapadd 初始化(含幂等判断)
├── handlers/          # 仅存放重启/重载动作
├── templates/         # 全部 .j2 后缀模板文件
├── defaults/          # 最低优先级的默认变量(适合给用户覆盖)
└── vars/              # 高优先级固定变量(适合操作系统差异映射)

核心思想 :将"安装"、"配置"、"初始化"解耦,配合 tags(如 --tags install)实现分阶段独立部署。


面试终极加分项(针对 LDAP 场景)

如果面试官追问你写的 LDAP Role,你可以抛出这个亮点:

"我在 init.yml 中没有使用 shell 模块去拼接 ldapadd 命令,而是使用了 command 模块 。因为该命令不涉及管道和重定向,使用 command 不仅更安全,还能避免 Shell 转义带来的特殊字符密码报错问题。 同时,我利用 ldapsearch 的返回码(rc)作为幂等性判断依据,确保 LDAP 基础数据只导入一次,重复执行 Playbook 不会造成数据重复。"

相关推荐
祺风挽楠10 天前
ansible编辑
网络·ansible
芳心粽伙饭10 天前
Ansible课后作业
ansible
烁34712 天前
Ansible初识
ansible
烁34712 天前
Ansible安装部署调试
ansible
烁34712 天前
Ansible命令
ansible
小义_12 天前
【Ansible】(三)基础配置与连接设置
云原生·ansible
炸炸鱼.16 天前
Ansible 企业级实战:Playbook 与 Roles 完全指南
网络·ansible
风曦Kisaki17 天前
# 自动化运维Day03:Ansible模块进阶(setup,debug),四种常用变量,进阶语法;Ansible Roles(角色)
运维·自动化·ansible
炸炸鱼.20 天前
Ansible 部署应用:从入门到精通
ansible