本篇将主要描述在腾讯云平台如何构建Ansible自动化运维管理工具平台/环境,如何批量管理云上服务器,批量分发配置等设置,同时这里将引入某客户真实的案例进行整体的阐述。将从Ansible的基础、架构、常见功能模块,案例解决示例方法进行展开。
1. Ansible概述
1.1. 什么是Ansible
Ansible 是一款开源自动化平台,基于Python开发,无需客户端,轻量级,配置语言采用YAML,可以管理强大的自动化任务,提高工作效率。
1.2. Ansible 功能特点
- 简单明了,易于阅读;
- 无代理: 通过SSH管理节点
- 模块化: 自带管理模块库
- 通过YAML描述系统
- 依赖Python 2.4或以上
- 跨平台支持
1.3. 主流自动化运维管理工具
- Puppet:基于Ruby开发,采用C/S架构,扩展性强,基于SSL认证
- SaltStack:基于Python开发,采用C/S架构,相对于puppet更轻量级,配置语法采用YMAL,使得配置脚本更为简单
- Ansible:基于Pyton开发,分布式,无需客户端,轻量级,配置语言采用YAML
1.4. 为什么选择Ansible
- 相对于puppet和saltstack,ansible无需客户端,更轻量级
- ansible都不用启动服务,以更轻松的实现分布式扩展
- 更强的远程命令执行操作
- 不输于puppet和saltstack的其他功能,且ansible 也最受欢迎的是自动化和编排技术之一
1.5. Ansible基本架构
Ansible 架构存在两种计算类型,即控制节点和受控主机/被管主机。
- 连接插件:默认是SSH,也支持其他的连接方法
- 核心模块:ansible的操作依赖于具体的模块
- 自定义模块:可以扩展自定义模块
- playbooks:定义Ansible任务的一个配置文件,可以将多个任务定义在一个playbook,由Ansible自动执行
- 主机配置清单:定义需要执行任务的主机
2. Ansible环境部署
这里为了更直观看到效果,采用3台centos7.6 1台ubuntu进行自动运维环境的构建,如果需要覆盖更多被管主机,则通过下面的方法扩展即可。
2.1. Ansible 安装及相关配置
2.1.1. 安装Ansible
代码语言:javascript复制[root@master]# yum -y install ansible
2.1.2. 设置本地解析及主机名
代码语言:javascript复制[root@master ~]# hostnamectl set-hostname master.example.com
[root@master ~]# cat /etc/hosts
127.0.0.1 VM-6-10-centos VM-6-10-centos
127.0.0.1 localhost.localdomain localhost
127.0.0.1 localhost4.localdomain4 localhost4
::1 VM-6-10-centos VM-6-10-centos
::1 localhost.localdomain localhost
::1 localhost6.localdomain6 localhost6
10.0.6.10 master.example.com master
10.0.6.4 node1.example.com node1
10.0.6.13 node2.example.com node2
10.0.6.8 node3.example.com node3
备注: node1 – node3 均像master 节点一样设置
2.1.3. ssh key免密登录设置
代码语言:javascript复制[root@master ~]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:afVk9vExWqYP3sxC0Pj1 kIaLFpKiUr8slNea3gxPPI root@master.example.com
The key's randomart image is:
---[RSA 2048]----
| |
| o |
| .o ..* |
| o =o.*o |
| . oS. ..*. o|
| o .O o = B. |
| . = * O . *.= |
| E . o. |
| . o ..|
----[SHA256]-----
[root@master ~]# ssh-copy-id root@node1.example.com 输入node1 密码, node2, node3类似,机器多则可通过for脚本批量实现。
2.1.4. Ansible环境验证
代码语言:javascript复制[root@master ~]# tail /etc/ansible/hosts
[nodes]
node1
node2
node3
node4
[root@master ~]# ansible all -m ping
node2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
node3 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
node1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
node4 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
[root@master ~]#
2.2. Ansible 相关核心配置文件
代码语言:javascript复制[root@master ~]# rpm -ql ansible |less
/etc/ansible/ansible.cfg //ansible 主配置文件
/etc/ansible/hosts //主机清单文件
/etc/ansible/roles //角色目录
/usr/bin/ansible //主程序,临时命令执行工具
/usr/bin/ansible-config //查看ansible配置
/usr/bin/ansible-connection //基于Console界面与用户交互的执行工具
/usr/bin/ansible-doc //查看配置文档,模块功能查看工具
/usr/bin/ansible-galaxy //下载/上传Roles模块的官网
/usr/bin/ansible-inventory // 查看inventory信息
/usr/bin/ansible-playbook //定制自动化任务,编排剧本工具
/usr/bin/ansible-vault //文件加密工具
/usr/share/ansible/roles //角色目录
2.3. Ansible主配置核心项说明
Ansible默认配置文件: /etc/ansible/ansible.cfg
代码语言:javascript复制#forks = 5 并发执行数量,默认5
#poll_interval = 15 拉取数据间隔时间,默认15秒
#sudo_user = root sudo命令默认用户
#remote_port = 22 连接远程端口号
#host_key_checking = False 检查对应服务器的host_key,建议取消注释
#log_path=/var/log/ansible.log 日志文件,建议取消注释
2.4. Ansible配置文件优先级
ansible配置文件以及读取优先级,如下顺序:
1) ANSIBLE_CONFIG变量
2) 当前目录 ansible.cfg
3) 家目录下 ansible.cfg
4) 默认配置文件 /etc/ansible/ansible.cfg
本篇采用系统默认的配置,若生产环境且涉及多人协作,使用普通用户选择2)或3)方法去配置Ansible环境。
3. Ansible 常用模块
3.1. 查看模块
代码语言:javascript复制[root@master ansible]# ansible-doc yum //查看模块帮助
[root@master ansible]# ansible-doc -s yum //显示模块简要说明
[root@master ansible]# ansible-doc -l //列出ansible模块
3.2. Command 模块
功能:默认模块,在远程主机执行命令,可忽略-m选项
代码语言:javascript复制[root@master ansible]# ansible all -m command -a "touch /tmp/test.txt"
[root@master ansible]# ansible all -m command -a 'systemctl restart httpd'
3.3. Shell模块
功能:和command相似,用shell执行命令
代码语言:javascript复制[root@master ansible]# ansible node1 -m shell -a 'useradd user01'
[root@master ansible]# ansible node1 -m shell -a "echo 123456 |passwd --stdin user01“
3.4. Scripts脚本模块
功能:运行脚本,不需要将脚本复制到被控端
代码语言:javascript复制[root@master ansible]#ansible node1-m script -a test.sh
[root@master ansible]# ansible all -m script -a ./test.sh
[root@master ansible]# ansible all -m command -a 'ls -l /tmp'
[root@master ansible]# ansible node1 -m script -a ./host-name.sh
3.5. Yum 模块
功能:通过yum 安装软件包
安装vsftpd软件包
代码语言:javascript复制[root@master ansible]# ansible node1 -m yum -a "name=vsftpd state=present"
卸载vsftpd软件包
代码语言:javascript复制[root@master ansible]# ansible all -m yum -a "name=vsftpd state=absent"
安装Development Tools (软件包组)
代码语言:javascript复制[root@master ansible]# ansible node1 -m yum -a "name='@Development Tools'"
3.6. File模块
功能:设置文件属性,创建/删除等操作
创建新文件:
代码语言:javascript复制[root@master ansible]# ansible node1 -m file -a 'name=/tmp/file1 state=touch'
删除文件:
代码语言:javascript复制[root@master ansible]# ansible node1 -m file -a 'name=/tmp/file1 state=absent'
创建目录:
代码语言:javascript复制[root@master ansible]# ansible node1 -m file -a 'name=/tmp/dir state=directory'
删除目录:
代码语言:javascript复制[root@master ansible]# ansible node1 -m file -a 'name=/tmp/dir state=absent'
3.7. Copy模块
功能:从服务器复制文件到客户端,
代码语言:javascript复制[root@master ansible]# ansible node1 -m copy -a 'src=/etc/hostname dest=/opt' //将控制主机的hosname
文件拷贝到被控主机的/opt下
3.8. Service 模块
代码语言:javascript复制功能:管理服务
停止httpd服务:
[root@master ansible]# ansible node1 -m service -a "name=httpd state=stopped"
开启httpd服务:
[root@master ansible]# ansible node1 -m service -a "name=httpd state=started"
重新加载httod服务:
[root@master ansible]# ansible node1 -m service -a "name=httpd state=reloaded"
重启httpd服务:
[root@master ansible]# ansible node1 -m service -a "name=httpd state=restarted"
开启ftp服务,同时设置开机自动启动:
[root@master ansible]# ansible node1 -m service -a "name=vsftpd state=started enabled=yes"
重启ftp服务:
[root@master ansible]# ansible node1 -m service -a "name=vsftpd state=restarted"
3.9. User模块
代码语言:javascript复制功能:管理用户
添加用户,指定uid、家目录、主组及注释:
[root@master ansible]# ansible node1 -m user -a 'name=user1 comment="test user" uid=2000 home=/home/user1'
3.10. Hostname 模块
代码语言:javascript复制功能:管理主机名,生效同时更改文件永久生效
[root@master ansible]# ansible node1 -m hostname -a "name=node1.example.com"
由于ansible支持的模块是非常丰富的,上面仅列出了常用的一些模块,还有其他模块可通过ansible-doc 命令来获取。
4. 案例说明
近期某客户基于腾讯云镜(主机安全)进行等保安全基线扫描,发现有上百台云服务器的安全基线不符合客户安全部门的安全需求,需要进行整改,涉及的机器量也比较大,而客户侧人员较少,日常运维常用采用简单脚本方式进行云上运维管理。一开始比较倾向云上的产品进行批量或自动修复,发现比较难找到合适的产品完全吻合,因此这里推荐采用Ansible自动化运维的方式进行批量修复云服务器的安全隐患问题,这里简单列举几项高危风险示例:
序号 | 风险项 | 威胁等级 | 描述 | 处理建议 |
---|---|---|---|---|
1 | 确保已配置SSH空闲超时间隔 | 高危 | 这两个选项ClientAliveInterval和ClientAliveCountMax控制SSH会话超时。当ClientAliveInterval变量被设置,对指定的时间长度没有活动的SSH会话被终止。当ClientAliveCountMax变量被设置,sshd将在每一个客户端发送活动消息ClientAliveInterval的时间间隔。当连续发送的客户端活动消息数没有客户端响应时,ssh会话将终止。 | 编辑/etc/ssh/sshd_config文件以设置参数: ClientAliveInterval 300 ClientAliveCountMax 0 |
2 | 确保SSH MaxAuthTries设置为4或更低 | 高危 | MaxAuthTries参数指定每个连接允许的最大身份验证尝试次数。登录失败次数达到设置参数一半时,错误消息将写入syslog文件,详细说明登录失败。 | 编辑/etc/ssh/sshd_config文件以设置参数,如下所示: MaxAuthTries 4 |
3 | 确保默认用户umask限制为027或更高 | 高危 | umask默认值确定用户创建的文件的权限。创建文件的用户可以通过chmod命令自行决定使其他人可以读取其文件和目录 | 编辑/etc/bash.bashrc,/etc/profile文件(以及系统上支持的任何其他Shell的适当文件),并添加或编辑umask参数,如下所示: umask 027 |
4 | 确保默认用户shell超时为900秒或更短 | 高危 | 默认值TMOUT确定用户的shell超时时间。TMOUT值以秒为单位。 | 编辑/etc/bashrc,/etc/profile和/etc/profile.d/*.sh文件(以及系统上支持的任何其他Shell的适当文件),并根据站点策略添加或编辑任何TMOUT参数: TMOUT=900 |
4.1. 案例实现方法1
由于客户侧使用操作系统为:RedHat/CentOS、Ubuntu, 这里采用不同操作系统的yml文件进行。
4.1.1. 针对Ubuntu系统
代码语言:javascript复制[root@master method1]# cat ubuntu.yml-m1
---
- name: repair system security issue
hosts: node4
tasks:
- name: session timeout
lineinfile:
path: /etc/profile
regexp: '^export TMOUT=600'
line: export TMOUT=300
- name: set MaxAuthTries
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#MaxAuthTries 6'
line: MaxAuthTries 4
- name: set ClientAliveInterval
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#ClientAliveInterval 0'
line: ClientAliveInterval 300
- name: set ClientAliveCountMax
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#ClientAliveCountMax 3'
line: ClientAliveCountMax 0
- name: reloaded service
service:
name: sshd
state: reloaded
- name: set umask1
lineinfile:
path: /etc/profile
regexp: '002'
line: "umask 027"
- name: set umask2
lineinfile:
path: /etc/bash.bashrc
regexp: '002'
line: "umask 027"
4.1.2. 针对centos/rhel机器
代码语言:javascript复制[root@master method1]# cat centos.yml
---
- name: repair system security issue
hosts: node1
tasks:
- name: session timeout
lineinfile:
path: /etc/profile
regexp: '^export TMOUT=600'
line: export TMOUT=300
- name: set MaxAuthTries
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#MaxAuthTries 6'
line: MaxAuthTries 4
- name: set ClientAliveInterval
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#ClientAliveInterval 0'
line: ClientAliveInterval 300
- name: set ClientAliveCountMax
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#ClientAliveCountMax 3'
line: ClientAliveCountMax 0
- name: reloaded service
service:
name: sshd
state: reloaded
4.1.3. 验证效果
代码语言:javascript复制[root@master ansible]# ansible-playbook ubuntu.yml-m1
[root@master ansible]# ansible-playbook centos.yml
代码语言:javascript复制root@node4:~# grep MaxAuthTries /etc/ssh/sshd_config
MaxAuthTries 4
#简单查一下修复情况,符合预期
4.2. 案例实现方法2
第一种实现的方法相对简单与直接,临时用下还不错。针对云上机器量大,第一种方式就不是适用管理大型的项目,第二种方法则采用导入外部文件方式管理playbook
4.2.1. 导入任务文件
这里采用import_tasks 功能将任务文件静态导入playbook中。
代码语言:javascript复制[root@master ansible]# cat fixsec.yml
---
- name: fix security issue
hosts: node1,node4
tasks:
- name: Centos/RedHat System
import_tasks: centos.yml
when: ansible_distribution == "CentOS" or ansible_distribution == "RedHat"
- name: Centos/RedHat System
import_tasks: ubuntu.yml
when: ansible_distribution == "Ubuntu"
[root@master ansible]#
4.2.2. 静态任务内容
代码语言:javascript复制########### centos/rhel #################
[root@master method2]# cat centos.yml
---
- name: set MaxAuthTries
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#MaxAuthTries 6'
line: MaxAuthTries 4
- name: set ClientAliveInterval
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#ClientAliveInterval 0'
line: ClientAliveInterval 300
- name: set ClientAliveCountMax
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#ClientAliveCountMax 3'
line: ClientAliveCountMax 0
- name: reloaded service
service:
name: sshd
state: reloaded
########### Ubuntu #################
[root@master method2]# cat ubuntu.yml
---
- name: session timeout
lineinfile:
path: /etc/profile
regexp: '^export TMOUT=600'
line: export TMOUT=300
- name: set MaxAuthTries
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#MaxAuthTries 6'
line: MaxAuthTries 4
- name: set ClientAliveInterval
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#ClientAliveInterval 0'
line: ClientAliveInterval 300
- name: set ClientAliveCountMax
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#ClientAliveCountMax 3'
line: ClientAliveCountMax 0
- name: reloaded service
service:
name: sshd
state: reloaded
- name: set umask
lineinfile:
path: /etc/{{item}}
regexp: '002'
line: "umask 027"
loop:
- profile
- bash.bashrc
4.2.3. 执行方法2
代码语言:javascript复制[root@master ansible]# ansible-playbook fixsec.yml
4.3. 案例实现方法3
案例实现方法3采用通过roles方式进行解决。因在生成环境中, 为了实现不同的功能, 需要编写大量的playbook文件,而且每个playbook还可能会调用其他文件(如变量文件),对于海量的、无规律的文件,管理起来非常痛苦。Roles是管理ansible文件的一种规范(目录结构),roles会按照标准的规范, 自动到特定的目录和文件中读取数据,从而达到实现重复使用。
4.3.1. 初始化角色
Ansible galaxy是官方提供的一个共享roles的平台,这里采用ansible-galaxy初始化角色,让其创建规范的目录结构。Roles目录结构说明
- defualts/main.yml:定义变量的缺省值,优先级较低
- files目录:存储静态文件的目录,如tar包、文件等
- handlers/main.yml:定义handlers,如包含角色的处理程序文件
- meta/main.yml:写作者、版本等描述信息
- README.md:整个角色(role)的描述信息
- tasks/main.yml:定义任务的地方
- templates目录:存放动态数据文件的地方(文件中包含了变量的模板文件)
- tests目录:此目录可以包含清单和test.yml playbook 用于测试角色
- vars/main.yml:定义变量,优先级高
[root@master ansible]# ansible-galaxy init roles/ossec
- Role roles/ossec was created successfully
[root@master ansible]# tree roles/ossec/
roles/ossec/
|-- defaults
| `-- main.yml
|-- files
|-- handlers
| `-- main.yml
|-- meta
| `-- main.yml
|-- README.md
|-- tasks
| `-- main.yml
|-- templates
|-- tests
| |-- inventory
| `-- test.yml
`-- vars
`-- main.yml
4.3.2. 确定sshd配置模板文件与任务
本次高危安全隐患主要是sshd配置不符合等保的安全需求,这里直接将对应不同系统类的sshd_config 制定模板,让其通过roles规范目录结构进行分发,同时也方便后续的灵活改动与复用。
代码语言:javascript复制[root@master ansible]# tree roles/ossec/
roles/ossec/
|-- defaults
| `-- main.yml
|-- files
|-- handlers
| `-- main.yml
|-- meta
| `-- main.yml
|-- README.md
|-- tasks
| |-- main.yml
| `-- main.yml-bak
|-- templates
| |-- sshd_config_centos.j2 # ssh模板文件
| `-- sshd_config_ubuntu.j2 # sshd模板文件
|-- tests
| |-- inventory
| `-- test.yml
`-- vars
`-- main.yml
代码语言:javascript复制[root@master ansible]# cat roles/ossec/tasks/main.yml
---
# tasks file for ossec
- name: deploy ssh_config file on ubuntu
template:
src: sshd_config_ubuntu.j2
dest: /etc/ssh/sshd_config
when: ansible_distribution == "Ubuntu"
- name: deploy ssh_config file on centos
template:
src: sshd_config_centos.j2
dest: /etc/ssh/sshd_config
when: ansible_distribution == "CentOS" or ansible_distribution == "RedHat"
- name: reload sshd service based on new config info
service:
name: sshd
state: reloaded
4.3.3. 执行ossec角色
代码语言:javascript复制[root@master ansible]# cat ossec.yml
---
- name: fix ossec by roles
hosts: node1,node4 # 如果执行所有被管主机,写被管主机组名即可
roles:
- ossec
案例方法3实现起来更简洁,更直接,playbook目录结构清晰,可结合生产环境需求对其目录进行相关设定,如4.3.1目录功能说明。
5. 总结
本篇从Ansible定义、功能特点、架构、环境部署、常用模块等,以及结合了真实的案例进行实践说明。关于生产环境的引用,建议根据需求及生产环境的差异进行调整,测试与验证后再大规模的投入使用。
6. 参考资料
- https://www.ansible.com/
- https://github.com/ansible/ansible
- https://www.redhat.com/en/technologies/management/ansible
- https://ansible-tran.readthedocs.io/en/latest/docs/intro.html