引言:为什么选择Ansible进行RHEL自动化运维
在现代企业IT环境中,Red Hat Enterprise Linux (RHEL) 作为稳定可靠的企业级操作系统,承载着关键业务负载。然而,随着服务器规模的扩大和运维复杂度的提升,传统的手动运维方式已经无法满足高效、可靠、可重复的运维需求。Ansible作为一款轻量级但功能强大的自动化工具,凭借其无代理架构、简单易用的语法和强大的模块生态系统,成为RHEL自动化运维的首选方案。
Ansible的核心优势在于其基于SSH协议的无代理架构,这意味着你无需在目标服务器上安装任何客户端软件,只需配置好SSH访问即可开始自动化管理。对于RHEL系统,Ansible提供了丰富的内置模块,涵盖系统管理、软件包管理、服务配置、安全策略等各个方面,能够满足企业级运维的各种需求。
第一部分:Ansible基础环境搭建
1.1 Ansible控制节点安装
在RHEL系统上安装Ansible控制节点非常简单。首先确保你的系统已经注册了Red Hat订阅,然后执行以下命令:
# 启用Ansible仓库(RHEL 8/9)
sudo subscription-manager repos –enable ansible-2.9-for-rhel-8-x86_64-rpms
# 或者对于较新版本
sudo dnf install ansible
# 验证安装
ansible –version
安装完成后,你会看到类似以下的输出,显示Ansible的版本信息和配置文件路径:
ansible 2.9.27
config file = /etc/ansible/ansible.cfg
configured module search path = [‘/root/.ansible/plugins/modules’, ‘/usr/share/ansible/plugins/modules’]
ansible python module location = /usr/lib/python3.6/site-packages/ansible
executable location = /usr/bin/ansible
python version = Python 3.6.8
1.2 基础配置文件详解
Ansible的主要配置文件是
/etc/ansible/ansible.cfg
,它控制着Ansible的行为。让我们创建一个自定义配置文件来优化我们的运维环境:
# ~/ansible.cfg
[defaults]
# 设置默认主机清单文件
inventory = /home/rheladmin/inventory
# 禁用主机密钥检查(生产环境建议启用并配置known_hosts)
host_key_checking = False
# 设置默认SSH用户
remote_user = rheladmin
# 设置私钥路径
private_key_file = ~/.ssh/id_rsa
# 设置任务重试次数
retry_files_enabled = False
# 设置默认become方法(sudo)
become_method = sudo
# 设置日志路径
log_path = ~/ansible.log
[privilege_escalation]
# 默认使用become提权
become = True
become_user = root
1.3 主机清单配置
主机清单是Ansible管理的所有目标主机的列表。对于企业环境,我们通常需要按功能或环境对主机进行分组:
# ~/inventory
[web_servers]
web01.rhel.local ansible_host=192.168.1.10
web02.rhel.local ansible_host=192.168.1.11
[db_servers]
db01.rhel.local ansible_host=192.168.1.20
db02.rhel.local ansible_host=192.168.1.21
[monitoring]
zabbix.rhel.local ansible_host=192.168.1.30
[production:children]
web_servers
db_servers
[staging]
staging-web.rhel.local ansible_host=192.168.2.10
staging-db.rhel.local ansible_host=192.168.2.11
# 为特定组设置变量
[web_servers:vars]
ansible_user=webadmin
http_port=80
[db_servers:vars]
ansible_user=dbadmin
db_port=5432
1.4 SSH密钥配置
为了实现无密码登录,需要在控制节点生成SSH密钥对并分发到目标主机:
# 在控制节点生成密钥对
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N “”
# 将公钥分发到目标主机(需要输入密码一次)
ssh-copy-id -i ~/.ssh/id_rsa.pub rheladmin@web01.rhel.local
ssh-copy-id -i ~/.ssh/id_rsa.pub rheladmin@web02.rhel.local
# 或者使用Ansible的authorized_key模块批量分发
ansible all -m authorized_key -a “user=rheladmin key='{{ lookup(‘file’, ‘~/.ssh/id_rsa.pub’) }}’” -k
第二部分:Ansible基础使用和模块详解
2.1 Ad-hoc命令快速入门
Ad-hoc命令是Ansible最简单的使用方式,适合快速执行单个任务:
# 检查所有主机的连通性
ansible all -m ping
# 查看所有主机的系统信息
ansible all -m setup
# 在web_servers组执行shell命令
ansible web_servers -m shell -a “hostname”
# 检查web_servers组的磁盘使用情况
ansible web_servers -m shell -a “df -h”
# 重启web_servers组的httpd服务
ansible web_servers -m service -a “name=httpd state=restarted”
# 检查web_servers组的httpd服务状态
ansible web_servers -m service -a “name=httpd state=started enabled=yes”
2.2 核心模块详解
2.2.1 package模块(软件包管理)
# playbook: packages.yml
—
– name: 安装和管理RHEL软件包
hosts: all
tasks:
– name: 安装基础工具包
package:
name:
– vim
– wget
– curl
– tree
– net-tools
state: present
– name: 安装最新版本的httpd
package:
name: httpd
state: latest
– name: 移除不需要的包
package:
name:
– telnet-server
– rsh-server
state: absent
– name: 清理缓存
command: dnf clean all
when: ansible_os_family == “RedHat” and ansible_distribution_major_version >= “8”
2.2.2 user模块(用户管理)
# playbook: users.yml
—
– name: 管理系统用户
hosts: all
vars:
users:
– name: devops
groups: wheel
shell: /bin/bash
home: /home/devops
– name: backup
groups: backup
shell: /sbin/nologin
home: /home/backup
tasks:
– name: 创建用户组
group:
name: “{{ item.groups }}”
state: present
loop: “{{ users }}”
– name: 创建系统用户
user:
name: “{{ item.name }}”
groups: “{{ item.groups }}”
shell: “{{ item.shell }}”
home: “{{ item.home }}”
create_home: yes
password: “{{ ‘password123’ | password_hash(‘sha512’) }}”
update_password: on_create
loop: “{{ users }}”
– name: 配置sudo权限
lineinfile:
path: /etc/sudoers
line: “{{ item.name }} ALL=(ALL) NOPASSWD: ALL”
state: present
validate: ‘visudo -cf %s’
loop: “{{ users }}”
when: item.groups == “wheel”
2.2.3 template模块(配置文件管理)
# playbook: apache_config.yml
—
– name: 配置Apache Web服务器
hosts: web_servers
vars:
http_port: 80
max_clients: 200
document_root: /var/www/html
tasks:
– name: 确保httpd已安装
package:
name: httpd
state: present
– name: 创建自定义配置目录
file:
path: /etc/httpd/conf.d/custom
state: directory
mode: ‘0755’
– name: 生成httpd主配置文件
template:
src: templates/httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
owner: root
group: root
mode: ‘0644’
backup: yes
notify: restart httpd
– name: 创建虚拟主机配置
template:
src: templates/vhost.conf.j2
dest: /etc/httpd/conf.d/vhost.conf
notify: restart httpd
– name: 确保httpd服务运行并开机自启
service:
name: httpd
state: started
enabled: yes
– name: 配置防火墙允许HTTP流量
firewalld:
service: http
permanent: yes
state: enabled
immediate: yes
handlers:
– name: restart httpd
service:
name: httpd
state: restarted
对应的Jinja2模板文件
templates/httpd.conf.j2
:
# Ansible managed: {{ ansible_managed }}
ServerRoot “/etc/httpd”
Listen {{ http_port }}
Include conf.modules.d/*.conf
User apache
Group apache
ServerAdmin root@localhost
ServerName {{ ansible_fqdn }}
AllowOverride none
Require all denied
DocumentRoot “{{ document_root }}”
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
DirectoryIndex index.html index.php
Require all denied
ErrorLog “logs/error_log”
LogLevel warn
LogFormat “%h %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\”” combined
LogFormat “%h %l %u %t \”%r\” %>s %b” common
CustomLog “logs/access_log” combined
MaxRequestWorkers {{ max_clients }}
2.2.4 lineinfile模块(文件内容修改)
# playbook: system_tuning.yml
—
– name: 系统调优配置
hosts: all
tasks:
– name: 增加文件描述符限制
lineinfile:
path: /etc/security/limits.conf
line: “{{ item }}”
state: present
loop:
– “* soft nofile 65536”
– “* hard nofile 65536”
– “* soft nproc 65536”
– “* hard nproc 65536”
– name: 配置内核参数
sysctl:
name: “{{ item.name }}”
value: “{{ item.value }}”
state: present
reload: yes
loop:
– { name: ‘net.ipv4.ip_forward’, value: ‘1’ }
– { name: ‘net.ipv4.tcp_max_syn_backlog’, value: ‘4096’ }
– { name: ‘net.core.somaxconn’, value: ‘65535’ }
– name: 禁用不必要的服务
systemd:
name: “{{ item }}”
state: stopped
enabled: no
loop:
– postfix
– rpcbind
– chronyd
ignore_errors: yes
– name: 配置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: ‘^#?X11Forwarding’, line: ‘X11Forwarding no’ }
notify: restart sshd
handlers:
– name: restart sshd
service:
name: sshd
state: restarted
第三部分:企业级Playbook编写最佳实践
3.1 Playbook结构设计
企业级Playbook应该具有清晰的结构、良好的可读性和可维护性:
# playbook: web_server_deploy.yml
—
– name: 部署企业级Web服务器集群
hosts: web_servers
become: yes
vars:
# 基础配置
app_name: “myapp”
app_version: “1.0.0”
deploy_user: “appuser”
# 路径配置
app_base_dir: “/opt/{{ app_name }}”
app_log_dir: “/var/log/{{ app_name }}”
app_config_dir: “/etc/{{ app_name }}”
# 服务配置
service_port: 8080
service_user: “{{ deploy_user }}”
# 安全配置
ssl_cert_path: “/etc/ssl/certs/{{ app_name }}.crt”
ssl_key_path: “/etc/ssl/private/{{ app_name }}.key”
pre_tasks:
– name: 验证系统要求
fail:
msg: “系统必须是RHEL 8或更高版本”
when: ansible_distribution_major_version|int < 8
- name: 检查磁盘空间
shell: df -h / | awk 'NR==2 {print $5}' | sed 's/%//'
register: disk_usage
changed_when: false
- name: 确保有足够的磁盘空间
fail:
msg: "磁盘使用率超过90%,请清理空间"
when: disk_usage.stdout|int > 90
tasks:
– name: 创建应用用户
user:
name: “{{ deploy_user }}”
groups: wheel
shell: /bin/bash
create_home: yes
home: “/home/{{ deploy_user }}”
– name: 创建应用目录结构
file:
path: “{{ item }}”
state: directory
owner: “{{ deploy_user }}”
group: “{{ deploy_user }}”
mode: “{{ item == app_log_dir or item == app_config_dir ? ‘0755’ : ‘0750’ }}”
loop:
– “{{ app_base_dir }}”
– “{{ app_base_dir }}/releases”
– “{{ app_base_dir }}/shared”
– “{{ app_log_dir }}”
– “{{ app_config_dir }}”
– name: 安装依赖包
package:
name:
– nginx
– python3-pip
– git
state: present
– name: 安装Python依赖
pip:
name:
– gunicorn
– flask
state: present
executable: pip3
– name: 配置Nginx
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
backup: yes
notify: reload nginx
– name: 配置系统服务
template:
src: templates/app.service.j2
dest: /etc/systemd/system/{{ app_name }}.service
notify: reload systemd
– name: 配置日志轮转
template:
src: templates/logrotate.conf.j2
dest: /etc/logrotate.d/{{ app_name }}
mode: ‘0644’
– name: 配置防火墙
firewalld:
service: https
permanent: yes
state: enabled
immediate: yes
– name: 启动并启用服务
systemd:
name: “{{ app_name }}”
state: started
enabled: yes
daemon_reload: yes
post_tasks:
– name: 验证服务状态
uri:
url: “http://localhost:{{ service_port }}/health”
status_code: 200
timeout: 5
register: health_check
ignore_errors: yes
– name: 生成部署报告
copy:
content: |
Deployment Report
=================
Application: {{ app_name }}
Version: {{ app_version }}
Deploy Date: {{ ansible_date_time.iso8601 }}
Host: {{ inventory_hostname }}
Status: {{ ‘SUCCESS’ if health_check.status == 200 else ‘FAILED’ }}
dest: “{{ app_base_dir }}/deployment_report_{{ ansible_date_time.epoch }}.txt”
when: health_check is defined
handlers:
– name: reload nginx
service:
name: nginx
state: reloaded
– name: reload systemd
systemd:
name: “{{ app_name }}”
daemon_reload: yes
3.2 角色(Roles)组织
角色是Ansible代码复用的最佳实践,特别适合企业级复杂环境:
# 创建角色目录结构
mkdir -p roles/{webserver,dbserver,monitoring}/{tasks,handlers,templates,files,vars,defaults,meta}
3.2.1 WebServer角色示例
# roles/webserver/defaults/main.yml
—
# 默认变量,可以被覆盖
webserver_packages:
– nginx
– php-fpm
– php-mysqlnd
webserver_port: 80
webserver_user: nginx
webserver_document_root: /var/www/html
webserver_vhosts: []
# 示例:
# webserver_vhosts:
# – name: example.com
# port: 80
# root: /var/www/example
# index: index.html index.php
# server_name: example.com www.example.com
# SSL配置
webserver_ssl_enabled: false
webserver_ssl_cert: “”
webserver_ssl_key: “”
# 性能参数
webserver_worker_processes: auto
webserver_worker_connections: 1024
# roles/webserver/tasks/main.yml
—
– name: 安装Nginx和PHP
package:
name: “{{ webserver_packages }}”
state: present
– name: 创建文档根目录
file:
path: “{{ webserver_document_root }}”
state: directory
owner: “{{ webserver_user }}”
group: “{{ webserver_user }}”
mode: ‘0755’
– name: 配置Nginx主配置文件
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx
– name: 配置虚拟主机
template:
src: vhost.conf.j2
dest: “/etc/nginx/conf.d/{{ item.name }}.conf”
loop: “{{ webserver_vhosts }}”
when: webserver_vhosts | length > 0
notify: reload nginx
– name: 配置PHP-FPM
template:
src: php-fpm.conf.j2
dest: /etc/php-fpm.d/www.conf
when: “‘php-fpm’ in webserver_packages”
notify: reload php-fpm
– name: 启动并启用服务
systemd:
name: “{{ item }}”
state: started
enabled: yes
loop:
– nginx
– php-fpm
when: “‘php-fpm’ in webserver_packages”
– name: 配置防火墙
firewalld:
service: “{{ item }}”
permanent: yes
state: enabled
immediate: yes
loop:
– http
– https
when: webserver_ssl_enabled
# roles/webserver/handlers/main.yml
—
– name: reload nginx
service:
name: nginx
state: reloaded
– name: reload php-fpm
service:
name: php-fpm
state: reloaded
# roles/webserver/templates/nginx.conf.j2
# Ansible managed: {{ ansible_managed }}
user {{ webserver_user }};
worker_processes {{ webserver_worker_processes }};
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections {{ webserver_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;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/conf.d/*.conf;
}
3.2.2 使用角色的Playbook
# playbook: site.yml
—
– name: 部署Web服务器
hosts: web_servers
become: yes
roles:
– role: webserver
vars:
webserver_vhosts:
– name: myapp
port: “{{ service_port }}”
root: “{{ app_base_dir }}/current”
index: index.html index.php
server_name: “{{ ansible_fqdn }}”
webserver_ssl_enabled: true
webserver_ssl_cert: “{{ ssl_cert_path }}”
webserver_ssl_key: “{{ ssl_key_path }}”
– name: 部署数据库服务器
hosts: db_servers
become: yes
roles:
– role: dbserver
vars:
db_version: “13”
db_name: “myapp_db”
db_user: “myapp_user”
db_password: “{{ vault_db_password }}”
– name: 配置监控
hosts: monitoring
become: yes
roles:
– role: monitoring
第四部分:复杂任务编排和流程控制
4.1 条件判断和流程控制
# playbook: complex_workflow.yml
—
– name: 复杂应用部署流程
hosts: all
become: yes
vars:
deploy_environment: “{{ deploy_env | default(‘staging’) }}”
rollback_enabled: true
health_check_timeout: 30
tasks:
– name: 环境验证
fail:
msg: “无效的环境: {{ deploy_environment }}”
when: deploy_environment not in [‘development’, ‘staging’, ‘production’]
– name: 获取当前部署信息
stat:
path: “/opt/myapp/current”
register: current_link
– name: 设置当前版本变量
set_fact:
current_version: “{{ current_link.stat.lnk_target | default(‘none’) }}”
– name: 备份当前版本(如果存在)
archive:
path: “/opt/myapp/releases/{{ current_version }}”
dest: “/opt/myapp/backups/rollback-{{ current_version }}-{{ ansible_date_time.epoch }}.tar.gz”
format: gz
when:
– current_link.stat.exists
– rollback_enabled
– deploy_environment == ‘production’
– name: 创建新版本目录
file:
path: “/opt/myapp/releases/{{ app_version }}”
state: directory
owner: appuser
group: appuser
mode: ‘0755’
– name: 部署应用代码
git:
repo: “https://github.com/myorg/myapp.git”
dest: “/opt/myapp/releases/{{ app_version }}”
version: “{{ app_version }}”
update: yes
register: git_result
until: git_result is succeeded
retries: 3
delay: 5
– name: 安装应用依赖
pip:
requirements: “/opt/myapp/releases/{{ app_version }}/requirements.txt”
virtualenv: “/opt/myapp/venv”
virtualenv_python: python3
when: git_result is succeeded
– name: 运行数据库迁移
command: “/opt/myapp/venv/bin/alembic upgrade head”
args:
chdir: “/opt/myapp/releases/{{ app_version }}”
register: migration_result
failed_when: migration_result.rc != 0 and migration_result.rc != 2
when: deploy_environment in [‘staging’, ‘production’]
– name: 创建符号链接指向新版本
file:
src: “/opt/myapp/releases/{{ app_version }}”
dest: “/opt/myapp/current”
state: link
force: yes
– name: 重启应用服务
systemd:
name: myapp
state: restarted
daemon_reload: yes
– name: 等待应用健康检查
uri:
url: “http://localhost:8080/health”
status_code: 200
timeout: “{{ health_check_timeout }}”
register: health_result
until: health_result.status == 200
retries: 10
delay: 3
– name: 回滚到旧版本(如果健康检查失败)
block:
– name: 删除失败的部署
file:
path: “/opt/myapp/releases/{{ app_version }}”
state: absent
– name: 恢复旧版本符号链接
file:
src: “/opt/myapp/releases/{{ current_version }}”
dest: “/opt/myapp/current”
state: link
force: yes
– name: 重启旧版本服务
systemd:
name: myapp
state: restarted
– name: 失败部署任务
fail:
msg: “新版本健康检查失败,已回滚到旧版本”
when: health_result is failed and rollback_enabled
– name: 清理旧版本(保留最近5个)
shell: |
cd /opt/myapp/releases
ls -t | tail -n +6 | xargs -r rm -rf
when: health_result is succeeded
– name: 生成部署报告
copy:
content: |
Deployment Summary
==================
Environment: {{ deploy_environment }}
Version: {{ app_version }}
Timestamp: {{ ansible_date_time.iso8601 }}
Host: {{ inventory_hostname }}
Status: {{ ‘SUCCESS’ if health_result is succeeded else ‘FAILED’ }}
Previous Version: {{ current_version }}
dest: “/opt/myapp/logs/deploy-{{ app_version }}-{{ ansible_date_time.epoch }}.log”
4.2 错误处理和重试机制
# playbook: robust_deployment.yml
—
– name: 高可用部署策略
hosts: web_servers
become: yes
serial: 1 # 滚动更新,一次只更新一台服务器
vars:
max_retries: 3
retry_delay: 10
tasks:
– name: 检查服务健康状态(部署前)
uri:
url: “http://{{ ansible_host }}/health”
status_code: 200
timeout: 5
register: pre_health
ignore_errors: yes
– name: 如果服务不健康,暂停部署
pause:
prompt: “警告:{{ inventory_hostname }} 健康检查失败。是否继续?(yes/no)”
when: pre_health is failed
– name: 执行滚动更新
block:
– name: 停止负载均衡器后端
command: “curl -X PUT http://lb/api/backend/{{ inventory_hostname }}/disable”
delegate_to: “{{ groups[‘load_balancer’][0] }}”
ignore_errors: yes
– name: 等待现有连接处理完成
wait_for:
timeout: 30
– name: 执行应用更新
include_tasks: update_tasks.yml
– name: 启动负载均衡器后端
command: “curl -X PUT http://lb/api/backend/{{ inventory_hostname }}/enable”
delegate_to: “{{ groups[‘load_balancer’][0] }}”
– name: 等待服务稳定
wait_for:
port: 8080
delay: 10
timeout: 60
– name: 最终健康检查
uri:
url: “http://{{ ansible_host }}/health”
status_code: 200
timeout: 10
register: post_health
until: post_health.status == 200
retries: “{{ max_retries }}”
delay: “{{ retry_delay }}”
rescue:
– name: 更新失败,回滚操作
include_tasks: rollback_tasks.yml
– name: 通知失败
fail:
msg: “更新失败,已回滚: {{ inventory_hostname }}”
always:
– name: 确保服务运行
systemd:
name: myapp
state: started
ignore_errors: yes
4.3 并行执行和性能优化
# playbook: parallel_operations.yml
—
– name: 大规模并行操作
hosts: all
become: yes
# 使用forks控制并行度
vars:
ansible_forks: 10 # 同时在10台主机上执行
tasks:
– name: 收集系统信息(并行)
setup:
gather_subset:
– ‘!all’
– ‘min’
tags: gather
– name: 批量更新系统(分批执行)
package:
name: “*”
state: latest
async: 300 # 异步执行,超时5分钟
poll: 10 # 每10秒检查一次状态
register: yum_update
– name: 等待所有更新完成
async_status:
jid: “{{ yum_update.ansible_job_id }}”
register: job_result
until: job_result.finished
retries: 30
delay: 10
– name: 收集更新结果
shell: |
if [ -f /var/run/reboot-required ]; then
echo “reboot_required”
else
echo “no_reboot”
fi
register: reboot_check
changed_when: false
– name: 分组需要重启的主机
group_by:
key: “reboot_{{ reboot_check.stdout }}”
– name: 并行重启需要重启的主机
reboot:
msg: “系统更新需要重启”
connect_timeout: 5
reboot_timeout: 300
pre_reboot_delay: 0
post_reboot_delay: 30
test_command: whoami
when: reboot_check.stdout == “reboot_required”
delegate_to: “{{ item }}”
loop: “{{ groups[‘reboot_required’] | default([]) }}”
run_once: false
第五部分:企业级安全和合规性管理
5.1 安全加固脚本
# playbook: security_hardening.yml
—
– name: RHEL安全加固
hosts: all
become: yes
vars:
# 安全基线配置
ssh_port: 22
allowed_users: [“rheladmin”, “ansible”]
allowed_groups: [“wheel”]
# 密码策略
password_min_length: 12
password_max_age: 90
password_min_class: 4
# 审计配置
audit_rules:
– “-w /etc/passwd -p wa -k identity”
– “-w /etc/shadow -p wa -k identity”
– “-w /etc/group -p wa -k identity”
– “-w /etc/sudoers -p wa -k sudoers”
– “-w /var/log/faillog -p wa -k logins”
– “-w /var/log/lastlog -p wa -k logins”
tasks:
– name: 更新系统到最新安全补丁
package:
name: “*”
state: latest
security: yes
– name: 安装安全相关工具
package:
name:
– audit
– rsyslog
– fail2ban
– clamav
– aide
state: present
– name: 配置SSH安全加固
block:
– name: 备份原始SSH配置
copy:
src: /etc/ssh/sshd_config
dest: /etc/ssh/sshd_config.backup
remote_src: yes
– name: 应用SSH安全配置
lineinfile:
path: /etc/ssh/sshd_config
regexp: “{{ item.regexp }}”
line: “{{ item.line }}”
state: present
loop:
– { regexp: ‘^#?Port’, line: “Port {{ ssh_port }}” }
– { regexp: ‘^#?Protocol’, line: “Protocol 2” }
– { regexp: ‘^#?PermitRootLogin’, line: “PermitRootLogin no” }
– { regexp: ‘^#?PasswordAuthentication’, line: “PasswordAuthentication no” }
– { regexp: ‘^#?PubkeyAuthentication’, line: “PubkeyAuthentication yes” }
– { regexp: ‘^#?X11Forwarding’, line: “X11Forwarding no” }
– { regexp: ‘^#?AllowUsers’, line: “AllowUsers {{ allowed_users | join(‘ ‘) }}” }
– { regexp: ‘^#?MaxAuthTries’, line: “MaxAuthTries 3” }
– { regexp: ‘^#?ClientAliveInterval’, line: “ClientAliveInterval 300” }
– { regexp: ‘^#?ClientAliveCountMax’, line: “ClientAliveCountMax 2” }
notify: restart sshd
– name: 配置密码策略
lineinfile:
path: /etc/login.defs
regexp: “{{ item.regexp }}”
line: “{{ item.line }}”
state: present
loop:
– { regexp: ‘^PASS_MAX_DAYS’, line: “PASS_MAX_DAYS {{ password_max_age }}” }
– { regexp: ‘^PASS_MIN_DAYS’, line: “PASS_MIN_DAYS 1” }
– { regexp: ‘^PASS_WARN_AGE’, line: “PASS_WARN_AGE 7” }
– name: 配置PAM密码复杂度
lineinfile:
path: /etc/pam.d/system-auth
regexp: “^password requisite pam_pwquality.so”
line: “password requisite pam_pwquality.so try_first_pass retry=3 minlen={{ password_min_length }} minclass={{ password_min_class }} dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1”
state: present
– name: 配置审计规则
lineinfile:
path: /etc/audit/rules.d/audit.rules
line: “{{ item }}”
state: present
loop: “{{ audit_rules }}”
notify: restart auditd
– name: 启用并配置AIDE(文件完整性检查)
block:
– name: 初始化AIDE数据库
command: aide –init
args:
creates: /var/lib/aide/aide.db.new.gz
– name: 激活AIDE数据库
command: mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
args:
creates: /var/lib/aide/aide.db.gz
– name: 创建AIDE检查脚本
copy:
content: |
#!/bin/bash
aide –check > /var/log/aide-check-$(date +%Y%m%d).log
if [ $? -ne 0 ]; then
echo “AIDE check failed – see log” | mail -s “AIDE Alert” root
fi
dest: /usr/local/bin/aide-check.sh
mode: ‘0755’
– name: 设置AIDE定时检查
cron:
name: “AIDE daily check”
minute: “0”
hour: “2”
job: “/usr/local/bin/aide-check.sh”
– name: 配置Fail2Ban
template:
src: templates/fail2ban.jail.local.j2
dest: /etc/fail2ban/jail.local
notify: restart fail2ban
– name: 配置SELinux策略
selinux:
policy: targeted
state: enforcing
– name: 配置防火墙
firewalld:
service: “{{ item }}”
permanent: yes
state: enabled
immediate: yes
loop:
– ssh
– https
when: item != “ssh” or ssh_port == 22
– name: 禁用不必要的服务
systemd:
name: “{{ item }}”
state: stopped
enabled: no
loop:
– bluetooth
– cups
– avahi-daemon
– rpcbind
– sendmail
ignore_errors: yes
– name: 配置内核参数安全加固
sysctl:
name: “{{ item.name }}”
value: “{{ item.value }}”
state: present
reload: yes
loop:
– { name: ‘net.ipv4.ip_forward’, value: ‘0’ }
– { name: ‘net.ipv4.conf.all.send_redirects’, value: ‘0’ }
– { name: ‘net.ipv4.conf.default.send_redirects’, value: ‘0’ }
– { name: ‘net.ipv4.conf.all.accept_redirects’, value: ‘0’ }
– { name: ‘net.ipv4.conf.all.accept_source_route’, value: ‘0’ }
– { name: ‘net.ipv4.conf.all.log_martians’, value: ‘1’ }
– { name: ‘kernel.dmesg_restrict’, value: ‘1’ }
– { name: ‘kernel.kptr_restrict’, value: ‘2’ }
– { name: ‘fs.protected_hardlinks’, value: ‘1’ }
– { name: ‘fs.protected_symlinks’, value: ‘1’ }
– name: 创建安全加固报告
copy:
content: |
Security Hardening Report
=========================
Date: {{ ansible_date_time.iso8601 }}
Host: {{ inventory_hostname }}
OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
Applied Configurations:
– SSH Port: {{ ssh_port }}
– Root Login: Disabled
– Password Auth: Disabled
– SELinux: Enforcing
– Audit: Enabled
– AIDE: Configured
– Fail2Ban: Configured
Kernel Parameters:
{% for item in kernel_params %}
– {{ item.name }} = {{ item.value }}
{% endfor %}
Status: COMPLETED
dest: /var/log/security-hardening-{{ ansible_date_time.epoch }}.log
vars:
kernel_params:
– { name: ‘net.ipv4.ip_forward’, value: ‘0’ }
– { name: ‘kernel.dmesg_restrict’, value: ‘1’ }
handlers:
– name: restart sshd
service:
name: sshd
state: restarted
– name: restart auditd
service:
name: auditd
state: restarted
– name: restart fail2ban
service:
name: fail2ban
state: restarted
5.2 合规性检查和报告
# playbook: compliance_check.yml
—
– name: CIS基准合规性检查
hosts: all
become: yes
vars:
compliance_level: “L1” # L1 or L2
report_dir: “/var/log/compliance”
tasks:
– name: 创建报告目录
file:
path: “{{ report_dir }}”
state: directory
mode: ‘0755’
– name: 检查文件权限(CIS 1.1)
block:
– name: 检查/etc/passwd权限
stat:
path: /etc/passwd
register: passwd_stat
– name: 验证/etc/passwd权限
fail:
msg: “/etc/passwd 权限应为644”
when: passwd_stat.stat.mode != “0644”
– name: 检查/etc/shadow权限
stat:
path: /etc/shadow
register: shadow_stat
– name: 验证/etc/shadow权限
fail:
msg: “/etc/shadow 权限应为000”
when: shadow_stat.stat.mode != “0000”
– name: 检查SSH配置(CIS 5.2)
block:
– name: 读取SSH配置
slurp:
path: /etc/ssh/sshd_config
register: ssh_config
– name: 验证SSH配置
set_fact:
ssh_checks:
– { param: ‘PermitRootLogin’, expected: ‘no’ }
– { param: ‘Protocol’, expected: ‘2’ }
– { param: ‘X11Forwarding’, expected: ‘no’ }
– name: 执行SSH配置检查
fail:
msg: “SSH配置不符合CIS基准: {{ item.param }}”
when:
– ssh_config.content | b64decode is regex(item.param + ‘.*’ + item.expected, multiline=True)
– compliance_level == “L2”
loop: “{{ ssh_checks }}”
– name: 检查软件包状态
shell: |
# 检查不必要的服务
services=(“telnet-server” “rsh-server” “ypserv” “tftp-server”)
for service in “${services[@]}”; do
if rpm -q “$service” >/dev/null 2>&1; then
echo “FAIL: $service is installed”
fi
done
echo “PASS: No unnecessary packages found”
register: pkg_check
changed_when: false
failed_when: “‘FAIL’ in pkg_check.stdout”
– name: 检查用户账户
shell: |
# 检查空密码账户
awk -F: ‘($2 == “” || $2 == “!”) {print $1}’ /etc/shadow
register: empty_pass
changed_when: empty_pass.stdout != “”
failed_when: empty_pass.stdout != “”
– name: 生成合规性报告
template:
src: templates/compliance_report.j2
dest: “{{ report_dir }}/compliance-{{ ansible_date_time.epoch }}.json”
– name: 收集所有合规数据
set_fact:
compliance_data:
timestamp: “{{ ansible_date_time.iso8601 }}”
host: “{{ inventory_hostname }}”
level: “{{ compliance_level }}”
checks:
– name: “File Permissions”
status: “{{ ‘PASS’ if passwd_stat.stat.mode == ‘0644’ and shadow_stat.stat.mode == ‘0000’ else ‘FAIL’ }}”
– name: “SSH Configuration”
status: “{{ ‘PASS’ if ssh_config.content | b64decode is regex(‘PermitRootLogin.*no’) else ‘FAIL’ }}”
– name: “Package Security”
status: “{{ ‘PASS’ if pkg_check.rc == 0 else ‘FAIL’ }}”
– name: “User Accounts”
status: “{{ ‘PASS’ if empty_pass.rc == 0 else ‘FAIL’ }}”
– name: 上传报告到中央服务器
copy:
src: “{{ report_dir }}/compliance-{{ ansible_date_time.epoch }}.json”
dest: “/shared/compliance/{{ inventory_hostname }}-{{ ansible_date_time.epoch }}.json”
remote_src: yes
delegate_to: “{{ groups[‘monitoring’][0] }}”
when: “‘monitoring’ in groups”
第六部分:高级编排和工作流管理
6.1 动态清单和云集成
#!/usr/bin/env python3
# inventory_plugins/rhel_cloud_inventory.py
“””
动态清单插件:从RHEL Satellite或云平台获取主机信息
“””
import json
import argparse
import requests
from typing import Dict, List
class RHELCloudInventory:
def __init__(self, source=’satellite’):
self.source = source
self.inventory = {
‘_meta’: {
‘hostvars’: {}
},
‘all’: {
‘children’: []
}
}
def get_satellite_hosts(self, server, username, password):
“””从Satellite获取主机信息”””
url = f”https://{server}/api/hosts”
try:
response = requests.get(
url,
auth=(username, password),
verify=False,
timeout=30
)
response.raise_for_status()
hosts = response.json()[‘results’]
for host in hosts:
hostname = host[‘name’]
ip = host[‘ip’]
# 添加到主机组
if ‘production’ in hostname:
self.inventory.setdefault(‘production’, {‘hosts’: []})
self.inventory[‘production’][‘hosts’].append(hostname)
elif ‘staging’ in hostname:
self.inventory.setdefault(‘staging’, {‘hosts’: []})
self.inventory[‘staging’][‘hosts’].append(hostname)
# 添加主机变量
self.inventory[‘_meta’][‘hostvars’][hostname] = {
‘ansible_host’: ip,
‘satellite_id’: host[‘id’],
‘environment’: host.get(‘environment’, {}).get(‘name’, ‘unknown’),
‘content_view’: host.get(‘content_view’, {}).get(‘name’, ‘unknown’)
}
return True
except Exception as e:
print(f”Error fetching from Satellite: {e}”, file=sys.stderr)
return False
def get_aws_ec2_hosts(self, region=’us-east-1′):
“””从AWS EC2获取主机信息(示例)”””
try:
import boto3
ec2 = boto3.client(‘ec2’, region_name=region)
response = ec2.describe_instances(
Filters=[
{‘Name’: ‘instance-state-name’, ‘Values’: [‘running’]},
{‘Name’: ‘tag:Environment’, ‘Values’: [‘production’, ‘staging’]}
]
)
for reservation in response[‘Reservations’]:
for instance in reservation[‘Instances’]:
name = next(
(tag[‘Value’] for tag in instance.get(‘Tags’, []) if tag[‘Key’] == ‘Name’),
instance[‘InstanceId’]
)
env = next(
(tag[‘Value’] for tag in instance.get(‘Tags’, []) if tag[‘Key’] == ‘Environment’),
‘unknown’
)
self.inventory.setdefault(env, {‘hosts’: []})
self.inventory[env][‘hosts’].append(name)
self.inventory[‘_meta’][‘hostvars’][name] = {
‘ansible_host’: instance[‘PrivateIpAddress’],
‘ec2_id’: instance[‘InstanceId’],
‘ec2_type’: instance[‘InstanceType’],
‘ec2_az’: instance[‘Placement’][‘AvailabilityZone’]
}
return True
except ImportError:
print(“boto3 not installed”, file=sys.stderr)
return False
except Exception as e:
print(f”Error fetching from AWS: {e}”, file=sys.stderr)
return False
def generate_inventory(self):
“””生成最终的清单JSON”””
return json.dumps(self.inventory, indent=2)
def main():
parser = argparse.ArgumentParser(description=’RHEL Cloud Inventory Plugin’)
parser.add_argument(‘–list’, action=’store_true’, help=’List all hosts’)
parser.add_argument(‘–host’, help=’Get variables for specific host’)
parser.add_argument(‘–source’, default=’satellite’, choices=[‘satellite’, ‘aws’])
parser.add_argument(‘–satellite-server’, help=’Satellite server hostname’)
parser.add_argument(‘–satellite-user’, help=’Satellite username’)
parser.add_argument(‘–satellite-password’, help=’Satellite password’)
args = parser.parse_args()
inventory = RHELCloudInventory(source=args.source)
if args.source == ‘satellite’ and args.satellite_server:
inventory.get_satellite_hosts(
args.satellite_server,
args.satellite_user,
args.satellite_password
)
elif args.source == ‘aws’:
inventory.get_aws_ec2_hosts()
if args.list:
print(inventory.generate_inventory())
elif args.host:
# 返回特定主机的变量
host_vars = inventory.inventory[‘_meta’][‘hostvars’].get(args.host, {})
print(json.dumps(host_vars, indent=2))
if __name__ == ‘__main__’:
import sys
main()
使用动态清单的Playbook示例:
# playbook: cloud_deploy.yml
—
– name: 云环境自动部署
hosts: all
become: yes
vars:
# 从动态清单获取的变量
environment: “{{ hostvars[inventory_hostname].environment | default(‘staging’) }}”
region: “{{ hostvars[inventory_hostname].ec2_az | default(‘us-east-1a’) }}”
tasks:
– name: 根据环境配置不同参数
set_fact:
app_config:
production:
replicas: 3
instance_type: m5.large
backup_enabled: true
staging:
replicas: 1
instance_type: t3.medium
backup_enabled: false
– name: 应用环境特定配置
debug:
msg: “部署到 {{ environment }} 环境,配置: {{ app_config[environment] }}”
– name: 配置实例特定设置
include_tasks: “config/{{ environment }}.yml”
6.2 与CI/CD集成
# .gitlab-ci.yml 示例
stages:
– validate
– test
– deploy
variables:
ANSIBLE_HOST_KEY_CHECKING: “False”
ANSIBLE_VAULT_PASSWORD_FILE: “/tmp/vault_pass”
before_script:
– apt-get update -qq && apt-get install -y -qq ansible python3-pip
– pip3 install ansible-lint
– echo “$ANSIBLE_VAULT_PASSWORD” > $ANSIBLE_VAULT_PASSWORD_FILE
validate:
stage: validate
script:
– ansible-playbook –syntax-check playbook/*.yml
– ansible-lint playbook/*.yml
only:
– merge_requests
– main
test:
stage: test
script:
– ansible-playbook -i inventory/test playbook/site.yml –check
environment:
name: testing
only:
– main
deploy_production:
stage: deploy
script:
– ansible-playbook -i inventory/production playbook/site.yml –vault-password-file $ANSIBLE_VAULT_PASSWORD_FILE
environment:
name: production
when: manual
only:
– main
6.3 监控和日志集成
# playbook: monitoring_integration.yml
—
– name: 集成企业监控系统
hosts: all
become: yes
vars:
monitoring_system: “zabbix” # or “prometheus”, “datadog”
zabbix_server: “zabbix.rhel.local”
zabbix_hostgroups: [“Linux Servers”, “Production”]
tasks:
– name: 安装Zabbix Agent
package:
name: zabbix-agent
state: present
when: monitoring_system == “zabbix”
– name: 配置Zabbix Agent
template:
src: templates/zabbix_agentd.conf.j2
dest: /etc/zabbix/zabbix_agentd.conf
when: monitoring_system == “zabbix”
notify: restart zabbix-agent
– name: 启动Zabbix Agent
systemd:
name: zabbix-agent
state: started
enabled: yes
when: monitoring_system == “zabbix”
– name: 配置日志转发到中央ELK
rsyslog:
name: “{{ item }}”
state: present
config: |
*.* @@{{ elk_server }}:514
loop:
– 50-forward.conf
when: elk_server is defined
notify: restart rsyslog
– name: 配置自定义监控脚本
copy:
content: |
#!/bin/bash
# Custom monitoring script for application metrics
APP_NAME=”myapp”
PORT=8080
# Check response time
RESPONSE_TIME=$(curl -w “%{time_total}” -s -o /dev/null http://localhost:$PORT/health)
echo “app.response_time $RESPONSE_TIME”
# Check error rate
ERROR_COUNT=$(grep -c “ERROR” /var/log/$APP_NAME/app.log || echo 0)
echo “app.error_count $ERROR_COUNT”
# Check queue length
QUEUE_LENGTH=$(netstat -an | grep :$PORT | wc -l)
echo “app.queue_length $QUEUE_LENGTH”
dest: /usr/local/bin/custom_monitor.sh
mode: ‘0755’
– name: 设置监控脚本定时执行
cron:
name: “Custom metrics collection”
minute: “*/2”
job: “/usr/local/bin/custom_monitor.sh | nc monitoring.rhel.local 2003”
第七部分:最佳实践和故障排除
7.1 性能优化技巧
# ansible.cfg 性能优化配置
[defaults]
# 增加并行度
forks = 50
# 使用SSH复用连接
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True
# 减少连接时间
timeout = 30
# 禁用不必要的插件
callback_whitelist = profile_tasks, timer
[ssh_connection]
# SSH优化参数
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ConnectTimeout=10
pipelining = True
7.2 调试和故障排除
# playbook: debug_playbook.yml
—
– name: 调试Playbook
hosts: all
become: yes
vars:
debug_mode: true
tasks:
– name: 收集详细系统信息
setup:
gather_subset:
– all
register: system_info
when: debug_mode
– name: 显示关键变量
debug:
msg:
– “Hostname: {{ ansible_hostname }}”
– “OS: {{ ansible_distribution }} {{ ansible_distribution_version }}”
– “Memory: {{ ansible_memtotal_mb }} MB”
– “CPU Cores: {{ ansible_processor_vcpus }}”
when: debug_mode
– name: 检查磁盘空间
shell: df -h / | awk ‘NR==2 {print $4 ” available out of ” $2}’
register: disk_space
changed_when: false
– name: 验证网络连接
uri:
url: “https://releases.ubuntu.com”
status_code: 200
timeout: 5
register: network_check
ignore_errors: yes
– name: 生成调试报告
copy:
content: |
Debug Report
============
Timestamp: {{ ansible_date_time.iso8601 }}
Host: {{ inventory_hostname }}
System Info:
– OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
– Kernel: {{ ansible_kernel }}
– Memory: {{ ansible_memtotal_mb }} MB
– CPUs: {{ ansible_processor_vcpus }}
Disk Space: {{ disk_space.stdout }}
Network Access: {{ ‘OK’ if network_check.status == 200 else ‘FAILED’ }}
Environment Variables:
{% for key, value in ansible_env.items() | sort %}
{{ key }}={{ value }}
{% endfor %}
dest: “/tmp/debug-{{ inventory_hostname }}-{{ ansible_date_time.epoch }}.log”
7.3 常见问题解决方案
# playbook: common_issues.yml
—
– name: 解决常见RHEL Ansible问题
hosts: all
become: yes
tasks:
# 问题1: SSH连接超时
– name: 优化SSH连接配置
lineinfile:
path: /etc/ssh/sshd_config
regexp: “{{ item.regexp }}”
line: “{{ item.line }}”
state: present
loop:
– { regexp: ‘^#?ClientAliveInterval’, line: ‘ClientAliveInterval 60’ }
– { regexp: ‘^#?ClientAliveCountMax’, line: ‘ClientAliveCountMax 3’ }
notify: restart sshd
# 问题2: Python版本兼容性
– name: 确保Python3安装
package:
name: python3
state: present
– name: 创建Python3软链接(如果需要)
file:
src: /usr/bin/python3
dest: /usr/bin/python
state: link
force: yes
when: ansible_python_version is version(‘3.0’, ‘<')
# 问题3: SELinux阻止Ansible操作
- name: 安装SELinux管理工具
package:
name:
- libselinux-python
- libsemanage-python
state: present
- name: 设置SELinux为permissive模式(调试时)
selinux:
policy: targeted
state: permissive
when: selinux_debug | default(false)
# 问题4: 内存不足导致任务失败
- name: 检查可用内存
shell: free -m | awk 'NR==2 {print $7}'
register: available_memory
changed_when: false
- name: 如果内存不足,调整并行度
set_fact:
ansible_forks: 5
when: available_memory.stdout|int < 1024
# 问题5: DNS解析问题
- name: 配置本地主机解析
lineinfile:
path: /etc/hosts
line: "{{ item }}"
state: present
loop:
- "127.0.0.1 localhost localhost.localdomain"
- "::1 localhost localhost.localdomain"
- "{{ ansible_default_ipv4.address }} {{ ansible_fqdn }} {{ ansible_hostname }}"
when: ansible_fqdn != 'localhost'
# 问题6: 包管理器缓存问题
- name: 清理并重建DNF缓存
command: dnf clean all
changed_when: false
- name: 重建缓存
command: dnf makecache
changed_when: false
# 问题7: 时间同步问题
- name: 配置NTP时间同步
package:
name: chrony
state: present
- name: 配置Chrony服务器
lineinfile:
path: /etc/chrony.conf
regexp: "^server"
line: "server pool.ntp.org iburst"
state: present
notify: restart chronyd
- name: 启动并启用Chrony
systemd:
name: chronyd
state: started
enabled: yes
# 问题8: 文件系统空间不足
- name: 清理旧的日志文件
find:
paths:
- /var/log
patterns: "*.log.*"
age: 30d
state: absent
register: cleanup_result
- name: 显示清理结果
debug:
msg: "已清理 {{ cleanup_result.files | length }} 个旧日志文件"
when: cleanup_result is changed
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
- name: restart chronyd
service:
name: chronyd
state: restarted
第八部分:企业级最佳实践总结
8.1 代码组织规范
# 推荐的项目目录结构
ansible-project/
├── ansible.cfg
├── inventory/
│ ├── production/
│ │ ├── hosts
│ │ └── group_vars/
│ │ ├── all.yml
│ │ ├── web_servers.yml
│ │ └── db_servers.yml
│ ├── staging/
│ │ ├── hosts
│ │ └── group_vars/
│ │ └── all.yml
│ └── group_vars/
│ ├── all.yml
│ └── vault.yml
├── playbook/
│ ├── site.yml
│ ├── deploy.yml
│ ├── security.yml
│ └── backup.yml
├── roles/
│ ├── webserver/
│ ├── dbserver/
│ ├── monitoring/
│ └── common/
├── library/ # 自定义模块
├── filter_plugins/ # 自定义过滤器
├── callback_plugins/ # 自定义回调插件
├── templates/ # Jinja2模板
├── files/ # 静态文件
├── vars/ # 变量文件
├── vault/ # 加密变量文件
└── tests/ # 测试文件
8.2 版本控制和协作
# .ansible-lint 配置
---
skip_list:
- package-latest # 允许使用latest用于特定场景
- role-name # 允许使用更描述性的角色名
warn_list:
- experimental # 启用实验性规则
# 自定义规则
rules:
# 确保所有Playbook都有描述
meta-no-info:
enabled: true
# 确保使用有意义的变量名
var-naming:
enabled: true
8.3 安全最佳实践
# 使用Ansible Vault管理敏感数据
# 1. 创建加密文件
ansible-vault create vault/secrets.yml
# 2. 编辑加密文件
ansible-vault edit vault/secrets.yml
# 3. 加密现有文件
ansible-vault encrypt inventory/production/group_vars/all.yml
# 4. 解密文件(临时)
ansible-vault view vault/secrets.yml
# 5. 在Playbook中使用
# playbook: deploy.yml
---
- name: 使用加密变量
hosts: all
vars_files:
- vault/secrets.yml
tasks:
- name: 配置数据库连接
template:
src: db.conf.j2
dest: /etc/app/db.conf
mode: '0600'
vars:
db_password: "{{ vault_db_password }}"
8.4 监控和告警集成
# playbook: monitoring_setup.yml
---
- name: 设置企业级监控
hosts: monitoring
become: yes
vars:
prometheus_version: "2.37.0"
alertmanager_version: "0.24.0"
tasks:
- name: 创建监控用户
user:
name: prometheus
shell: /sbin/nologin
home: /var/lib/prometheus
- name: 下载并安装Prometheus
get_url:
url: "https://github.com/prometheus/prometheus/releases/download/v{{ prometheus_version }}/prometheus-{{ prometheus_version }}.linux-amd64.tar.gz"
dest: /tmp/prometheus.tar.gz
register: prometheus_download
- name: 解压Prometheus
unarchive:
src: /tmp/prometheus.tar.gz
dest: /opt/
remote_src: yes
extra_opts: [--strip-components=1]
creates: /opt/prometheus
- name: 创建Prometheus配置
template:
src: templates/prometheus.yml.j2
dest: /etc/prometheus/prometheus.yml
notify: restart prometheus
- name: 创建Prometheus服务
copy:
content: |
[Unit]
Description=Prometheus Time Series Collection and Processing Server
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/opt/prometheus/prometheus \
--config.file /etc/prometheus/prometheus.yml \
--storage.tsdb.path /var/lib/prometheus/ \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries \
--web.listen-address=0.0.0.0:9090
[Install]
WantedBy=multi-user.target
dest: /etc/systemd/system/prometheus.service
- name: 启动Prometheus
systemd:
name: prometheus
state: started
enabled: yes
daemon_reload: yes
- name: 配置防火墙
firewalld:
port: 9090/tcp
permanent: yes
state: enabled
immediate: yes
结论
通过本文的详细指南,你已经掌握了使用Ansible进行RHEL企业级自动化运维的完整技能栈。从基础环境搭建到复杂的编排任务,从安全加固到监控集成,这些实践将帮助你构建可靠、高效、安全的自动化运维体系。
记住,成功的Ansible自动化需要持续的改进和维护。定期审查和更新你的Playbook,保持与RHEL版本的同步,并始终关注安全最佳实践。通过合理的代码组织、版本控制和团队协作,你可以将Ansible的强大能力发挥到极致,真正解决企业级运维的各种难题。
开始你的Ansible自动化之旅吧,让繁琐的手动操作成为历史!