时间:2020-03-05来源:电脑系统城作者:电脑系统城
本文要实现的初始化配置目标如下:
- ansible配置ssh免密登录;
- ansible远程配置主机名;
- ansible控制远程主机互相添加DNS解析记录;
- ansible配置远程主机上的yum镜像源以及安装一些软件;
- ansible配置远程主机上的时间同步;
- ansible关闭远程主机上的selinux;
- ansible配置远程主机上的防火墙;
- ansible远程修改sshd配置文件并重启sshd,使其更安全;
1 2 3 4 |
[root@nginx ansible]# tail -3 /etc/ansible/hosts #要初始的主机如下 [node] 192.168.20.4 192.168.20.5 |
playbook文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
[root@nginx ansible]# cat ssh.yaml --- - name: configure ssh connection hosts: node gather_facts: false connection: local tasks: - name: configure ssh connection shell: | ssh-keyscan {{inventory_hostname}} >>~/.ssh/known_hosts sshpass -p '123.com' ssh-copy-id root@{{inventory_hostname}} ... |
注:
配置主机名可以使用shell模块,但是对于不太专业,ansible提供了一个专用于配置主机名的模块:hostname模块。
当然,要使用ansible去设置多个主机名,要求目标主机和目标名称已经关联好,否则多个主机和多个主机名之间无法对应去设置。
例如:分别设置node组中的两个节点主机名为node01和node02,playbook内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[root@ansible ansible]# cat test.yaml --- - name: set hostname hosts: node gather_facts: false vars: hostnames: - host: 192.168.20.4 name: node01 - host: 192.168.20.5 name: node02 tasks: - name: set hostname hostname: name: "{{item.name}}" when: item.host == inventory_hostname loop: "{{hostnames}}" |
在上面的hostname模块中,需要详细介绍vars指令以及when、loop指令。
vars指令可用于设置变量,可以设置一个或多个变量。下面几种方式都是合理的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 设置单个变量 vars: var1: value1 vars: - var1: value1 # 设置多个变量 vars: var1: value1 var2: value2 vars: - var1: value1 - var2: value2 |
vars可以设置在play级别,也可以设置在task级别,设置在play级别,该play范围内的task可以访问这些变量,其他play范围内则无法访问;设置在task级别,只有该task能访问这些变量,其他task和其他play则无法访问。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
[root@ansible ansible]# cat test.yaml --- - name: play1 hosts: localhost gather_facts: false vars: - var1: "value1" tasks: - name: access var1 debug: msg: "var1's value: {{var1}}" - name: play2 hosts: localhost gather_facts: false tasks: - name: cat's access vars from play1 debug: var: var1 - name: set and access var2 in this task debug: var: var2 vars: var2: "value2" - name: cat't accesss var2 debug: var: var2 |
执行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[root@ansible ansible]# ansible-playbook test.yaml PLAY [play1] ************************************************************************** TASK [access var1] ******************************************************************** ok: [localhost] => { "msg": "var1's value: value1" } PLAY [play2] ************************************************************************** TASK [cat's access vars from play1] *************************************************** ok: [localhost] => { "var1": "VARIABLE IS NOT DEFINED!" } TASK [set and access var2 in this task] *********************************************** ok: [localhost] => { "var2": "value2" } TASK [cat't accesss var2] ************************************************************* ok: [localhost] => { "var2": "VARIABLE IS NOT DEFINED!" } PLAY RECAP **************************************************************************** localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 |
回到我们更改主机名的配置vars指令中:
1 2 3 4 5 6 |
vars: hostnames: - host: 192.168.20.4 name: node01 - host: 192.168.20.5 name: node02 |
上面只设置了一个变量hostnames,但这个变量的值是一个数组结构,数组的两个元素又都是对象(字典/hash)结构。
所以想要访问主机名node01和它的IP地址192.168.20.4,可以:
1 2 3 4 5 |
tasks: - debug: var: hostnames[0].name - debug: var: hostnames[0].host |
在ansible中,提供的唯一一个通用的条件判断是when指令,当when指令的值为true时,则执行该任务,否则不执行该任务。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[root@ansible ansible]# cat test.yaml --- - name: play1 hosts: localhost gather_facts: false vars: - myname: "Ray" tasks: - name: task will skip debug: msg: "myname is : {{myname}}" when: myname == "lv" - name: task will execute debug: msg: "myname is : {{myname}}" when: myname == "Ray" |
在上面的myname值设置为Ray,第一个任务因为when的判断条件是myname==“lv”,所以判断结果为false,该任务不执行,同理,第二个任务因为when的值为true,所以执行了。
该playbook的执行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
PLAY [play1] ************************************************************************** TASK [task will skip] ***************************************************************** skipping: [localhost] TASK [task will execute] ************************************************************** ok: [localhost] => { "msg": "myname is : Ray" } PLAY RECAP **************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 |
1 2 3 4 5 6 7 8 9 10 11 12 |
[root@ansible ansible]# cat add_dns.yaml --- - name: play1 hosts: node gather_facts: true tasks: - name: add DNS lineinfile: path: "/etc/hosts" line: "{{item}} {{hostvars[item].ansible_hostname}}" when: item != inventory_hostname loop: "{{ play_hosts }}" |
执行结果如下:
1 2 3 4 5 6 7 8 9 |
TASK [Gathering Facts] **************************************************************** ok: [192.168.20.4] ok: [192.168.20.5] TASK [add DNS] ************************************************************************ skipping: [192.168.20.4] => (item=192.168.20.4) changed: [192.168.20.4] => (item=192.168.20.5) changed: [192.168.20.5] => (item=192.168.20.4) skipping: [192.168.20.5] => (item=192.168.20.5) |
需求如下:
playbook如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
[root@ansible ansible]# cat config_yum.yaml - name: config yum repo add install software hosts: node gather_facts: false tasks: - name: backup origin yum repos shell: cmd: "mkdir bak; mv *.repo bak" chdir: /etc/yum.repos.d creates: /etc/yum.repos.d/bak - name: add os repo and epel repo yum_repository: name: "{{item.name}}" description: "{{item.name}} repo" baseurl: "{{item.baseurl}}" file: "{{item.name}}" enabled: 1 gpgcheck: 0 reposdir: /etc/yum.repos.d loop: - name: os baseurl: "https://mirrors.tuna.tsinghua.edu.cn/centos/7/os/$basearch" - name: epel baseurl: "https://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch" - name: install pkgs yum: name: lrzsz,vim,dos2unix,wget,curl state: present |
在上面的yaml文件中,第一个任务是将所有系统默认的repo文件备份到bak目录中,chdir参数表示在执行shell模块的命令前先切换到/etc/yum.repos.d目录下,creates参数表示bak目录存在时则不执行shell模块。
第二个任务是使用yum_repository模块配置yum源,该模块可添加或移除yum源。
相关参数如下:
- name:指定repo的名称,对应于repo文件中的[name];
- description:repo的描述信息,对应repo文件中的name:xxx;
- baseurl:指定该repo的路径;
- file:指定repo的文件名,不需要加.repo后缀,会自动加上;
- reposdir:repo文件所在的目录,默认为/etc/yum.repos.d目录;
- enabled:是否启用该repo,对应于repo文件中的enabled;
- gpgcheck:该repo是否启用gpgcheck,对应于repo文件中的gpgcheck;
- state:present表示保证该repo存在,absent表示移除该repo。
在上面的配置中使用了一个loop循环来添加两个repo:os和epel。
第三个任务是使用yum模块安装一些rpm包,yum模块可以更新、安装、移除、下载包。
yum常用参数说明:
- name:指定要操作的包名
- 可以带版本号;
- 可以是单个包名,也可以是包名列表,或者逗号分隔多个包名;
- 可以是url;
- 可以是本地rpm包
- state:
- present和installed:保证包已安装,它们是等价的别名;
- latest:保证包已安装了最新版本,如果不是则更新;
- absent和removed:移除包,它们是等价的别名;
- download_only:仅下载不安装包(ansible 2.7才支持)
- download_dir:下载包存放在哪个目录下(ansible 2.8才支持)
yum模块是RHEL系列的包管理器,如果是ubuntu则无法使用,可以使用另一个更为通用的包管理器模块:package,它可以自动探测目标节点的包管理器类型并使用它们去管理软件。大多数时候使用package来代替yum或代替apt-install等不会有什么问题,但是有些包名在不同的操作系统上是不一样的,这是需要注意的。
保证时间同步可以避免很多玄学性的问题,特别是对集群中的节点。
通常会使用ntpd时间服务器来保证时间的同步,这里使用aliyun提供的时间服务器来保证时间同步,并将同步后的时间同步到硬件。
playbook文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
--- - name: sync time hosts: node gather_facts: false tasks: - name: install and sync time block: - name: install ntpdate yum: name: ntpdate state: present - name: ntpdate to sync time shell: | ntpdate ntp1.aliyun.com hwclock -w |
上面使用了一个block指令来组织了两个有关联性的任务,将他们作为了一个整体。block更多的用于多个关联性任务之间的异常处理。
关闭selinux的playbook如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[root@ansible roles]# cat disable_selinux.yaml --- - name: disable selinux hosts: node gather_facts: false tasks: - name: disable on the fly shell: setenforce 0 ignore_errors: true #由于上条命令执行后的返回状态码不一定为0,所以为了防止非0报错并停止palsybook接下来的任务,所以使用ignore_errors忽略错误 - name: disable forever in config lineinfile: path: /etc/selinux/config line: "SELINUX=disabled" #修改配置文件中的值,以便永久关闭 regexp: '^SELINUX=' #要修改的内容 |
注:ignore_errors也经常结合block使用,因为在block级别上设置异常处理,可以处理block内部的所有错误。
playbook文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
- name: Set Firewall hosts: node gather_facts: false tasks: - name: set iptables rule shell: | # 备份已有规则 iptables-save > /tmp/iptables.bak$(date +"%F-%T") # 给它三板斧 iptables -X iptables -F iptables -Z # 放行lo网卡和允许ping iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -p icmp -j ACCEPT # 放行关联和已建立连接的包,放行22、443、80端口 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT # 配置filter表的三链默认规则,INPUT链丢弃所有包 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT |
有时候为了服务器的安全,可能会去修改目标节点上sshd服务的默认配置,比如禁止root用户登录、禁止密码认证登录而只允许使用ssh密码认证等。
在修改服务的配置文件时,一般有几种方法:
相对来说,第三种方案是最统一、最易维护的方案。
此外,对于服务进程来说,修改了配置文件往往意味着要重启服务,使其加载新的配置文件,对于sshd也一样如此,但是sshd要比其他服务特殊一些,因为ansible默认基于ssh连接,重启sshd服务会使ansible连接断开,好在ansible默认会重试建立连接,无非是多等待几秒。但重建连接有可能会失败,比如修改了配置文件不允许重试、修改了sshd的监听端口等,这可能会使得ansible因连接失败而无法再继续执行后续任务。
所以,在修改sshd配置文件时,有如下建议:
- 将此任务作为初始化服务器的最后一个任务,即使连接失败也无所谓;
- 在playbook中加入连接失败的异常处理;
- 如果目标节点修改了sshd端口号,建议通过ansible自动或者我们手动去修改inventory文件中的ssh连接端口号。
这里为了简单,我准备使用lineinfile模块去修改配置文件,要修改的内容只有两项:
playbook内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
[root@ansible roles]# cat sshd_config.yaml --- - name: modify sshd_config hosts: node gather_facts: false tasks: # 1.备份/etc/ssh/sshd_config文件 - name: backup sshd config shell: /usr/bin/cp -f {{path}} {{path}}.bak vars: - path: /etc/ssh/sshd_config # 2.设置PermitRootLogin no - name: disable root login lineinfile: path: "/etc/ssh/sshd_config" line: "PermitRootLogin no" insertafter: "^#PermitRootLogin" regexp: "^PermitRootLogin" notify: "restart sshd" # 3.设置PasswordAuthentication no - name: disable password auth lineinfile: path: "/etc/ssh/sshd_config" line: "PasswordAuthentication no" regexp: "^PasswordAuthentication yes" notify: "restart sshd" handlers: - name: "restart sshd" service: name: sshd state: restarted |
关于notify和handlers的作用如下:
ansible会监控playbook执行后的changed的状态,如果changed=1,则表示关注的状态发生了改变,即本次任务的执行不具备幂等性,如果changed=0,则表示本次任务要么没执行,要么执行了也没有影响,即本次任务具备幂等性。
ansible提供了notify指令和handlers功能,如果在某个task中定义notify指令,当ansible在监控到该任务changed=1时,会触发该notify指令所定义的handler,然后去执行handler。所谓handler,其实就是task,无论是在写法上还是作用上它和task都没有什么区别,唯一的区别在于handler是被触发而被动执行的,不像普通task一样会按流程正常执行。
唯一需要注意的是,notify和handler中任务的名称必须一致。比如: notify: “restart sshd”,那么handlers中必须得有一个任务设置了 name: “restart sshd”。
此外,在上面的playbook中,两个lineinfile任务都设置了相同的notify,但ansible不会多次去重启sshd,而是在最后重启一次。实际上,ansible在执行完某个任务之后,并不会立即去执行对应的handler,而是在当前play中所有普通任务都执行完成后再去执行handler,这样的好处是可以多次触发notify,但最后只执行一次对应的handler,从而避免多次重启。
这里将前面所有的playbook集合到单个playbook文件中去,这样就可以一次性执行所有任务。
整合后的playbook如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
--- - name: Configure ssh Connection hosts: node gather_facts: false connection: local tasks: - name: configure ssh connection shell: | ssh-keyscan {{inventory_hostname}} >>~/.ssh/known_hosts sshpass -p'123.com' ssh-copy-id root@{{inventory_hostname}} - name: Set Hostname hosts: node gather_facts: false vars: hostnames: - host: 192.168.20.4 name: node01 - host: 192.168.20.5 name: node02 tasks: - name: set hostname hostname: name: "{{item.name}}" when: item.host == inventory_hostname loop: "{{hostnames}}" - name: Add DNS For Each hosts: node gather_facts: true tasks: - name: add DNS lineinfile: path: "/etc/hosts" line: "{{item}} {{hostvars[item].ansible_hostname}}" when: item != inventory_hostname loop: "{{ play_hosts }}" - name: Config Yum Repo And Install Software hosts: node gather_facts: false tasks: - name: backup origin yum repos shell: cmd: "mkdir bak; mv *.repo bak" chdir: /etc/yum.repos.d creates: /etc/yum.repos.d/bak - name: add os repo and epel repo yum_repository: name: "{{item.name}}" description: "{{item.name}} repo" baseurl: "{{item.baseurl}}" file: "{{item.name}}" enabled: 1 gpgcheck: 0 reposdir: /etc/yum.repos.d loop: - name: os baseurl: "https://mirrors.tuna.tsinghua.edu.cn/centos/7/os/$basearch" - name: epel baseurl: "https://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch" - name: install pkgs yum: name: lrzsz,vim,dos2unix,wget,curl state: present - name: Sync Time hosts: node gather_facts: false tasks: - name: install and sync time block: - name: install ntpdate yum: name: ntpdate state: present - name: ntpdate to sync time shell: | ntpdate ntp1.aliyun.com hwclock -w - name: Disable Selinux hosts: node gather_facts: false tasks: - block: - name: disable on the fly shell: setenforce 0 - name: disable forever in config lineinfile: path: /etc/selinux/config line: "SELINUX=disabled" regexp: '^SELINUX=' ignore_errors: true - name: Set Firewall hosts: node gather_facts: false tasks: - name: set iptables rule shell: | # 备份已有规则 iptables-save > /tmp/iptables.bak$(date +"%F-%T") # 给它三板斧 iptables -X iptables -F iptables -Z # 放行lo网卡和允许ping iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -p icmp -j ACCEPT # 放行关联和已建立连接的包,放行22、443、80端口 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT # 配置filter表的三链默认规则,INPUT链丢弃所有包 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT - name: Modify sshd_config hosts: node gather_facts: false tasks: - name: backup sshd config shell: /usr/bin/cp -f {{path}} {{path}}.bak vars: - path: /etc/ssh/sshd_config - name: disable root login lineinfile: path: "/etc/ssh/sshd_config" line: "PermitRootLogin no" insertafter: "^#PermitRootLogin" regexp: "^PermitRootLogin" notify: "restart sshd" - name: disable password auth lineinfile: path: "/etc/ssh/sshd_config" line: "PasswordAuthentication no" regexp: "^PasswordAuthentication yes" notify: "restart sshd" handlers: - name: "restart sshd" service: name: sshd state: restarted |
2024-04-11
台式机电脑如何连接外接显示器2024-04-11
小新系列打印机手机配置网络的方法教程2024-04-11
Thinkpad 笔记本F1-F12快捷键分别是什么功能ThinkPad蓝牙鼠标如何配对解答步骤41U5008鼠标驱动官网地址: https://support.lenovo.com/en_US/downloads/detail.page?&LegacyDocID=MIGR-67201 第一种方式是比较传统的:使...
2024-04-11
故障现象: USB设备U盘、移动硬盘等插入后提示无法识别的设备,确认设备本身正常,设备可加电,或插入设备后加电但无任何反应,无法使用。新型号机器多表现为黄色USB接口存在此问题,...
2024-04-11