0%

ansible

简介

Ansible 简单的说是一个配置管理系统(configuration management system)。
你只需要可以使用 ssh 访问你的服务器或设备就行。它也不同于其他工具,因为它
使用推送的方式,而不是像 puppet 等 那样使用拉取安装agent的方式。
常规的集群命令管理系统需要一台server端用于执行命令,而所有被执行的客户端上还需要安装agent端,但ansible本身直接通过ssh来调用执行命令,所以无需安装任何agent端。

这里写图片描述

安装

1
yum -y install ansible

要点

1
2
1.centos系统直接使用yum安装即可,需要注意的是linux发行版都支持ansible,只需要安装依赖python2.6以上,但windows无法作为anbile-agent端。
2.通常我们使用 ssh 与托管节点通信,默认使用 sftp.如果 sftp 不可用,可在 ansible.cfg 配置文件中配置成 scp 的方式. 在托管节点上也需要安装 Python 2.4 或以上的版本.如果版本低于 Python 2.5 ,还需要额外安装一个模块:python-simplejson

配置文件

[root@iZbp1h7p0lf362ybxkg0s5Z xxpt]# tree /etc/ansible/
/etc/ansible/
├── ansible.cfg # 主配置文件
├── hosts # 定义主机变量
└── roles # 存放角色的文件

ansible.cfg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ansible.cfg有很多配置,这里列常用配置:
默认配置
这里的配置项有很多,这里主要介绍一些常用的
[defaults]
#inventory = /etc/ansible/hosts #被控端的主机列表文件
#library = /usr/share/my_modules/ #库文件存放目录
#remote_tmp = ~/.ansible/tmp #临时文件远程主机存放目录
#local_tmp = ~/.ansible/tmp #临时文件本地存放目录
#forks = 5 #默认开启的并发数
#poll_interval = 15 #默认轮询时间间隔(单位秒)
#sudo_user = root #默认sudo用户
#ask_sudo_pass = True #是否需要sudo密码
#ask_pass = True #是否需要密码
#transport = smart #传输方式
#remote_port = 22 #默认远程主机的端口号
建议开启修改以下两个配置参数(取消掉注释即可)
#host_key_checking = False #检查对应服务器的host_key
#log_path=/var/log/ansible.log #开启ansible日志
最主要的我觉得还是host_key_checking = False ,一般使用开这个其实就可以了,表示远程ssh登录不用验证保存秘钥。

hosts

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
hosts主要的功能是定义主机组,在远程执行命令时,需要对一组主机进行定义,例如主机名(ip),主机账号密码等信息。
例:
[webservers]
alpha.example.org
beta.example.org
192.168.1.100
192.168.1.110
这样对于ansible来说,执行命令时指定主机组为webservers,则会对这四台主机进行远程命令操作。(如果非ip需要dns能解析到,主机名或者域名,内网也可以用hosts文件解析)
如果要指定账号密码等变量可以写成:

[k8s]
master1 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=admin
node1 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=admin
node2 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=admin

也可以使用变量的方式指定
[k8s]
master1
node1
node2
[k8s:vars]
ansible_ssh_port=22
ansible_ssh_user=root
ansible_ssh_pass=admin
如果想指定所有主机组的变量
[all:vars]
ansible_ssh_port=22
ansible_ssh_user=root
ansible_ssh_pass=admin

all:vars 全局变量

ansible命令

例:

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
[root@ansible ~]# ansible all -m ping
node2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
master1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
node1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
这里的all表示所有主机组,上面定义了一组k8s的主机组,所以就是其中定义的三台主机。
例2:查看每台服务器的主机名
ansible k8s -m shell -a "hostname"

注意点

1
2
3
4
5
6
7
8
9
1.执行命令有三种颜色:
绿色:执行成功但没有改变任何数据
黄色:执行成功且改变了客户端主机数据
红色:执行失败

2.默认不使用-m选项则是表示shell模块
例如ansible all -a "/bin/echo hello"

3.ansible命令很简单,主要就是指定主机组,模块,还有具体命令三部分组成。

常用模块

上面已经阐述了ansible命令的组成,其中最重要的就是模块部分,ansible有很多模块可以使用

ansible-doc –l 查看所有模块

ansible-doc –s copy 查看模块文档

模块文档:https://docs.ansible.com/ansible/latest/modules/modules_by_category.html

ping

1
2
3
4
5
6
7
8
9
10
[root@Node3 ~]# ansible all -m ping     //
测试主机是否在线
172.17.21.206 | SUCCESS => {
"changed": false,
"ping": "pong"
}
172.17.21.207 | SUCCESS => {
"changed": false,
"ping": "pong"
}

command模块和shell

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
作用:用于在各被管理节点运行指定的命令

shell和command的区别:shell模块可以特殊字符,而command是不支持

[root@Node3 ~]# ansible all -m command -a 'date' //
显示各节点的日期
172.17.21.207 | SUCCESS | rc=0 >>
Mon Jan 22 15:55:16 CST 2018
172.17.21.206 | SUCCESS | rc=0 >>
Mon Jan 22 15:55:16 CST 2018

[root@Node3 ~]# ansible all -m command -a 'ntpdate 172.17.21.208' //
同步各节点时间
172.17.21.207 | SUCCESS | rc=0 >>
22 Jan 16:03:39 ntpdate[5287]: adjust time server 172.17.21.208 offset 0.008589 sec
172.17.21.206 | SUCCESS | rc=0 >>
22 Jan 16:03:39 ntpdate[2650]: adjust time server 172.17.21.208 offset 0.017052 sec

[root@Node3 ~]# ansible all -m shell -a 'echo mageedu | passwd --stdin tony' //
修改各节点账号的密码
172.17.21.206 | SUCCESS | rc=0 >>
Changing password for user tony.
passwd: all authentication tokens updated successfully.
172.17.21.207 | SUCCESS | rc=0 >>
Changing password for user tony.
passwd: all authentication tokens updated successfully.

user模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
模块参数详解:
name:指定用户名
password:设定用户密码,password参数需要接受md5加密后的值
state:用户状态,默认为present
present:表示添加用户
absent:表示删除用户
update_password:修改用户密码
always:新密码和旧密码不同时进行修改
on_create:为新创建的用户指定密码
createhome:创建家目录
yes:默认项,即创建用户默认是有家目录的
no:创建用户时不创建家目录
remove:
yes:删除用户家目录,需要指定此参数
no:默认项,删除用户时默认不删除用户的家目录
system:
yes:默认创建为普通用户,而非系统用户
如果不指定默认生成的选项有:
home:创建家目录
shell:创建默认的shell为/bin/bash
system:默认创建为普通用户,而非系统用户,指定是用yes
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
[root@Node3 ~]# ansible-doc -s user   //查看user模块帮助信息
[root@Node3 ~]# echo Mageedu | openssl passwd -1 -stdin //对密码进行加密
[root@Node3 ~]# ansible all -m user -a 'name=webadmin system=yes password=$1$8218uq3N$yT28kYDpAvtE6/7x9m./a0 state=present' //
增加webadmin用户

172.17.21.206 | SUCCESS => {
"changed": true,
"comment": "",
"createhome": true,
"group": 983,
"home": "/home/webadmin",
"name": "webadmin",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"system": true,
"uid": 988
}
172.17.21.207 | SUCCESS => {
"changed": true,
"comment": "",
"createhome": true,
"group": 984,
"home": "/home/webadmin",
"name": "webadmin",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"system": true,
"uid": 989
}
[root@Node3 ~]# ansible all -m user -a 'name=tom remove=yes state=absent' //
删除tom用户

172.17.21.206 | SUCCESS => {
"changed": true,
"force": false,
"name": "tom",
"remove": true,
"state": "absent",
"stderr": "userdel: tom mail spool (/var/spool/mail/tom) not found\n",
"stderr_lines": [
"userdel: tom mail spool (/var/spool/mail/tom) not found"
]
}
172.17.21.207 | SUCCESS => {
"changed": true,
"force": false,
"name": "tom",
"remove": true,
"state": "absent",
"stderr": "userdel: tom mail spool (/var/spool/mail/tom) not found\n",
"stderr_lines": [
"userdel: tom mail spool (/var/spool/mail/tom) not found"
]
}
[root@Node3 ~]# echo cloudos | openssl passwd -1 -stdin
$1$kwsnVwr5$PDT4oolqmhbKx9bL21HX/0
[root@Node3 ~]# ansible all -m user -a 'name=webadmin update_password=always password=$1$kwsnVwr5$PDT4oolqmhbKx9bL21HX/0' //修改webadmin用户的密码
172.17.21.206 | SUCCESS => {
"append": false,
"changed": true,
"comment": "",
"group": 983,
"home": "/home/webadmin",
"move_home": false,
"name": "webadmin",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"uid": 988
}
172.17.21.207 | SUCCESS => {
"append": false,
"changed": true,
"comment": "",
"group": 984,
"home": "/home/webadmin",
"move_home": false,
"name": "webadmin",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"uid": 989
}

[root@Node3 ~]# ssh webadmin@node1 //验证账号是否能登录
webadmin@node1's password:
[webadmin@Node1 ~]$ id
uid=988(webadmin) gid=983(webadmin) groups=983(webadmin)
[webadmin@Node1 ~]$

group模块

管理组模块

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
[root@Node3 ~]# ansible all -m group -a 'gid=1009 name=mygrp state=present system=no'          //新增mygrp组,GID为1009,不属于系统组            
172.17.21.207 | SUCCESS => {
"changed": true,
"gid": 1009,
"name": "mygrp",
"state": "present",
"system": false
}
172.17.21.206 | SUCCESS => {
"changed": true,
"gid": 1009,
"name": "mygrp",
"state": "present",
"system": false
}
[root@Node3 ~]# ansible all -m group -a 'name=mygrp state=absent' //删除mygrp组
172.17.21.207 | SUCCESS => {
"changed": true,
"name": "mygrp",
"state": "absent"
}
172.17.21.206 | SUCCESS => {
"changed": true,
"name": "mygrp",
"state": "absent"
}

copy

远程复制备份模块

1
2
3
4
5
6
7
8
9
10
11
12
13
获取帮助:ansible-doc -s copy
模块参数详解:
src:指定源文件路径,可以是相对路径,也可以是绝对路径,可以是目录(并非是必须的,可以使用content,直接生成文件内容)
dest=:指定目标文件路径,只能是绝对路径,如果src是目录,此项必须是目录
owner:指定属主
group:指定属组
mode:指定权限,可以以数字指定比如0644
content:代替src,直接往dest文件中写内容,可以引用变量,也可以直接使用inventory中的主机变量
backup:在覆盖之前将原文件备份,备份文件包含时间信息。有两个选项:yes|no
force:
yes:默认项,如果目标主机包含该文件,但内容不同,则强制覆盖
no:则只有当目标主机的目标位置不存在该文件时,才复制
directory_mode:递归的设定目录的权限,默认为系统默认权限
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
[root@Node3 tmp]# ansible all -m copy -a 'src=/tmp/note.txt dest=/tmp/ backup=yes'   //
复制本地文件到远程主机并对原文件进行备份

172.17.21.207 | SUCCESS => {
"changed": true,
"checksum": "9955cad1e88e697be0ee40142b4d365725aa6c4e",
"dest": "/tmp/note.txt",
"gid": 0,
"group": "root",
"md5sum": "dd968c136dce42f6f225411a7225d0db",
"mode": "0644",
"owner": "root",
"size": 6,
"src": "/root/.ansible/tmp/ansible-tmp-1516612236.52-246176770078243/source",
"state": "file",
"uid": 0
}
172.17.21.206 | SUCCESS => {
"changed": true,
"checksum": "9955cad1e88e697be0ee40142b4d365725aa6c4e",
"dest": "/tmp/note.txt",
"gid": 0,
"group": "root",
"md5sum": "dd968c136dce42f6f225411a7225d0db",
"mode": "0644",
"owner": "root",
"size": 6,
"src": "/root/.ansible/tmp/ansible-tmp-1516612236.53-164774507851707/source",
"state": "file",
"uid": 0
}

[root@Node1 ~]# cat /tmp/note.txt //node1节点查看文件内容
Node3

[root@Node3 tmp]# ansible all -m copy -a 'content="Ansible\n" dest=/tmp/note.txt' //
向远程主机的文件中写内容,会把原内容覆盖掉

172.17.21.207 | SUCCESS => {
"changed": true,
"checksum": "9fd79766f87c26f3a81e2622a577ca3864251949",
"dest": "/tmp/note.txt",
"gid": 0,
"group": "root",
"md5sum": "a46e0c84ae99c51a342c5b0dd51032ea",
"mode": "0644",
"owner": "root",
"size": 8,
"src": "/root/.ansible/tmp/ansible-tmp-1516612880.68-129445105756732/source",
"state": "file",
"uid": 0
}
172.17.21.206 | SUCCESS => {
"changed": true,
"checksum": "9fd79766f87c26f3a81e2622a577ca3864251949",
"dest": "/tmp/note.txt",
"gid": 0,
"group": "root",
"md5sum": "a46e0c84ae99c51a342c5b0dd51032ea",
"mode": "0644",
"owner": "root",
"size": 8,
"src": "/root/.ansible/tmp/ansible-tmp-1516612880.66-133496485275706/source",
"state": "file",
"uid": 0
}
[root@Node1 ~]# cat /tmp/note.txt //node1节点查看文件内容
Ansible

[root@Node3 tmp]# ansible all -m copy -a 'src=/etc/pam.d/ dest=/tmp/' //带有斜扛/,表示复制目录下所有文件至远程主机/tmp目录下
172.17.21.207 | SUCCESS => {
"changed": true,
"dest": "/tmp/",
"src": "/etc/pam.d/"
}
172.17.21.206 | SUCCESS => {
"changed": true,
"dest": "/tmp/",
"src": "/etc/pam.d/"

[root@Node3 tmp]# ansible all -m copy -a 'src=/etc/pam.d dest=/tmp/' //不带斜扛/,表明复制pam.d目录至远程主机/tmp目录下
172.17.21.206 | SUCCESS => {
"changed": true,
"dest": "/tmp/",
"src": "/etc/pam.d"
}
172.17.21.207 | SUCCESS => {
"changed": true,
"dest": "/tmp/",
"src": "/etc/pam.d"
}

[root@Node3 tmp]# ansible all -m copy -a 'src=/etc/fstab dest=/tmp/fstab.ansible mode=600 owner=tony group=webadmin' //复制文件至/tmp目录下,同时改变属主与属组及权限
172.17.21.206 | SUCCESS => {
"changed": true,
"checksum": "ae2e6cc8d564afbfacf4243c13c06820ed6428f1",
"gid": 983,
"group": "webadmin",
"mode": "0600",
"owner": "tony",
"path": "/tmp/fstab.ansible",
"size": 883,
"state": "file",
"uid": 1000
}
172.17.21.207 | SUCCESS => {
"changed": true,
"checksum": "ae2e6cc8d564afbfacf4243c13c06820ed6428f1",
"gid": 984,
"group": "webadmin",
"mode": "0600",
"owner": "tony",
"path": "/tmp/fstab.ansible",
"size": 883,
"state": "file",
"uid": 1000
}
[root@Node1 tmp]# ll /tmp/fstab.ansible //查看文件属性,进行验证
-rw------- 1 tony webadmin 883 Jan 22 17:37 /tmp/fstab.ansible

file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
获取帮助:ansible-doc -s file
模块参数详解:
owner:修改属主
group:修改属组
mode:修改权限
path=:要修改文件的路径
recurse:递归的设置文件的属性,只对目录有效
yes:表示使用递归设置
state:
touch:创建一个新的空文件
directory:创建一个新的目录,当目录存在时不会进行修改
link:创建软连接,结果src一起使用此选项才生效
hard:创建硬连接
absent:删除文件,目录,软连接
src:当state=link时,要被连接文件的源路径
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
[root@Node3 tmp]# ansible all -m file -a 'path=/tmp/tony.txt state=touch'  //创建一个文件
172.17.21.206 | SUCCESS => {
"changed": true,
"dest": "/tmp/tony.txt",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"size": 0,
"state": "file",
"uid": 0
}
172.17.21.207 | SUCCESS => {
"changed": true,
"dest": "/tmp/tony.txt",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"size": 0,
"state": "file",
"uid": 0
}
[root@Node3 tmp]# ansible all -m file -a 'path=/tmp/tony.dir state=directory' //创建一个目录
172.17.21.206 | SUCCESS => {
"changed": true,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/tmp/tony.dir",
"size": 6,
"state": "directory",
"uid": 0
}
172.17.21.207 | SUCCESS => {
"changed": true,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/tmp/tony.dir",
"size": 6,
"state": "directory",
"uid": 0
}

[root@Node3 tmp]# ansible all -m file -a 'path=/tmp/tony.txt state=absent' //删除文件
172.17.21.207 | SUCCESS => {
"changed": true,
"path": "/tmp/tony.txt",
"state": "absent"
}
172.17.21.206 | SUCCESS => {
"changed": true,
"path": "/tmp/tony.txt",
"state": "absent"
}

[root@Node3 tmp]# ansible all -m file -a 'path=/tmp/tony.dir owner=tony group=tony recurse=yes' //递归改变目录的属主与属组
172.17.21.206 | SUCCESS => {
"changed": true,
"gid": 1000,
"group": "tony",
"mode": "0755",
"owner": "tony",
"path": "/tmp/tony.dir",
"size": 6,
"state": "directory",
"uid": 1000
}
172.17.21.207 | SUCCESS => {
"changed": true,
"gid": 1000,
"group": "tony",
"mode": "0755",
"owner": "tony",
"path": "/tmp/tony.dir",
"size": 6,
"state": "directory",
"uid": 1000
}

[root@Node3 tmp]# ansible all -m file -a 'src=/tmp/note.txt path=/tmp/notepad.txt state=link' //
设置软连接

172.17.21.207 | SUCCESS => {
"changed": true,
"dest": "/tmp/notepad.txt",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"size": 13,
"src": "/tmp/note.txt",
"state": "link",
"uid": 0
}
172.17.21.206 | SUCCESS => {
"changed": true,
"dest": "/tmp/notepad.txt",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"size": 13,
"src": "/tmp/note.txt",
"state": "link",
"uid": 0
}

cron

1
2
3
4
5
6
7
8
9
10
11
12
13
14
获取帮助:ansibe-doc -s cron
模块参数详解:
state:
present:创建任务
absent:删除任务
backup:对远程主机上的原任务计划内容修改之前做备份
job:要执行的任务
name:该任务的描述(必须项)
user:以哪个用户的身份运行
minute:分钟(0-59,*,*/2,……),不写默认为*
hour:小时(0-23,*,*/2,……),不写默认为*
day:日(1-31,*,*/2,……),不写默认为*
month:月(1-12,*,*/2,……),不写默认为*
weekday:周(0-7,*,……),不写默认为*
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@Node3 ~]# ansible all -m cron -a 'name="sync time from ntpserver" minute=*/10 job="/usr/sbin/ntpdate edu.ntp.org.cn  &> /dev/null"'     //每十分钟同步一下时间
172.17.21.207 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"sync time from ntpserver"
]
}
172.17.21.206 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"sync time from ntpserver"
]
}

setup

收集全程服务器信息,收集可用的facts,收集每个节点的相关信息:架构信息,IP,时间,域名,网卡,MAC,主机名,CPU等信息。

1
2
3
4
5
[root@Node3 ~]# ansible all -m setup
[root@Node3 ~]# ansible all -m setup -a 'filter=ansible_*_mb' //获取内存信息
[root@Node3 ~]# ansible all -m setup -a 'filter=ansible_kernel' //获取内核信息
[root@Node3 ~]# ansible all -m setup -a 'filter=ansible_all_ipv4_addresses' //获取IPV4地址信息
[root@Node3 ~]# ansible all -m setup -a 'filter=ansible_nodename' //获取节点主机信息

script

在远程主机执行本地脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@Node3 tmp]# ansible all -m script -a '/tmp/test.sh'  //向各节点执行脚本
172.17.21.206 | SUCCESS => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 172.17.21.206 closed.\r\n",
"stdout": "",
"stdout_lines": []
}
172.17.21.207 | SUCCESS => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 172.17.21.207 closed.\r\n",
"stdout": "",
"stdout_lines": []

yum

1
2
3
4
5
6
模块参数详解:    
name:表示要安装软件包的名字,默认最新的程序包,指明要安装的程序包,可以带上版本号
state:表示是安装还卸载
present:默认的,表示为安装
latest:安装为最新的版本
absent:表示删除
1
2
[root@Node3 tmp]# ansible all -m yum -a 'name=httpd state=present'   //安装httpd服务
[root@Node3 tmp]# ansible all -m yum -a 'name=httpd state=absent' //卸载httpd服

service

启动服务模块

1
2
3
4
5
6
7
8
9
10
模块参数详解:  
enabled:表示设置服务开机是否启动,取值为true或者false;enabled=yes
name=:表示要控制哪一个服务
state:
started:表示现在就启动此服务
stopped:表示现在关闭此服务
restarted:表示重启此服务
sleep:如果执行了restarted,在stop和start之间沉睡几秒
runlevel:定义在哪些级别可以自启动
arguments:表示向命令行传递的参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@Node3 tmp]# ansible Webservers -m service -a 'enabled=on name=httpd state=started'   //远程Web服务器安装httpd服务
[root@Node1 ~]# rpm -q nginx
nginx-1.12.2-1.el7.x86_64
[root@Node1 ~]# systemctl list-unit-files | grep httpd //查看httpd服务是否开机自启动
httpd.service enabled
[root@Node1 ~]# systemctl status nginx.service
● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
Active: inactive (dead) since Tue 2018-01-23 10:38:58 CST; 51s ago
Main PID: 1355 (code=exited, status=0/SUCCESS)

Jan 18 19:16:15 Node1.contoso.com systemd[1]: Starting The nginx HTTP and reverse proxy server...
Jan 18 19:16:15 Node1.contoso.com nginx[1350]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
Jan 18 19:16:15 Node1.contoso.com nginx[1350]: nginx: configuration file /etc/nginx/nginx.conf test is successful
Jan 18 19:16:16 Node1.contoso.com systemd[1]: Started The nginx HTTP and reverse proxy server.
Jan 23 10:38:58 Node1.contoso.com systemd[1]: Stopping The nginx HTTP and reverse proxy server...
Jan 23 10:38:58 Node1.contoso.com systemd[1]: Stopped The nginx HTTP and reverse proxy server.
[root@Node1 ~]#

stat

stat 模块(获取远程文件状态信息,atime/ctime/mtime/md5/uid/gid 等信息)

1
[root@Node3 ~]# ansible all -m stat -a 'path=/etc/passwd'

ansible变量

变量是应用于多个主机的便捷方式; 实际在主机执行之前,变量会对每个主机添加,然后在执行中引用。

  • 命令行传递

    1
    -e VAR=VALUE
  • 主机变量与组变量

在Inventory中定义变量。

1
2
3
4
5
6
[webservers]
192.168.1.100 ansible_ssh_user=root hostname=web1
192.168.1.100 ansible_ssh_user=root hostname=web2

[webservers:vars]
ansible_ssh_user=root hostname=web1
  • 单文件存储

Ansible中的首选做法是不将变量存储在Inventory中。

除了将变量直接存储在Inventory文件之外,主机和组变量还可以存储在相对于Inventory文件的单个文件中。

组变量:

group_vars 存放的是组变量

group_vars/all.yml 表示所有主机有效,等同于[all:vars]

grous_vars/etcd.yml 表示etcd组主机有效,等同于[etcd:vars]

1
2
3
4
# vi /etc/ansible/group_vars/all.yml
work_dir: /data
# vi /etc/ansible/host_vars/webservers.yml
nginx_port: 80

playbook

ansible命令已经足够管理集群服务器了,但对于较为复杂的操作,例如将集群中所有服务器都部署某个应用并部署其应用相关所有依赖的组件这种比较复杂的操作如果通过命令行效率就太慢了,虽然也可以通过shell脚本实现,但playbook才是ansible批量执行最优解。

Playbooks是Ansible的配置,部署和编排语言。他们可以描述您希望在远程机器做哪些事或者描述IT流程中一系列步骤。使用易读的YAML格式组织Playbook文件。

playbook文件示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---
- hosts: webs #host名称
remote_user: root #用户

tasks: #任务
- name: create group #任务名
group: name=apache system=yes gid=80 #任务类型以及具体要执行的参数
- name: create user #任务名
user: name=apache group=apache uid=80 shell=/sbin/nonlogin home=/usr/share/httpd system=yes password=$1$TGp21j8c$L3Q8BYFXsCBFt53nmw0t.0 #任务类型以及具体要执行的参数
- name: install package
yum: name=httpd
- name: config file
copy: src=/root/playbook/httpd.conf dest=/etc/httpd/conf/ backup=yes
- name: service
service: name=httpd state=started enabled=yes

上面的就是一个playbook文件大致的语法,当写完以后使用ansible-playbook执行

1
ansbile-playbook test.yml

ansible-playbook运行参数

  • --inventory=path,指定inventory文件,默认是在/etc/ansible/hosts下面。
  • --verbose,显示详细的输出,使用-vvvv显示精确到每分钟的输出。
  • --extra-vars=vars:定义在playbook使用的变量。
  • --forks:指定并发的线程数,默认是5.
  • --connection=type:指定远程连接主机的方式,默认是ssh,设置为local时,则只在本地执行playbook、
  • --check:检测模式,playbook中定义的所有任务将在每台主机上检测,但是并不执

tasks

一个tasks下面包含了多个任务,例如我要添加用户,复制文件夹,授权。每一个动作都是一个任务,需要给每个任务定义一个那么,然后下面是

执行的模块名称:模块具体动作

1
2
3
tasks:
- name: 安装nginx最新版
yum: pkg=nginx state=latest

playbook变量

  • 在Playbook中定义
1
2
3
4
- hosts: webservers
vars:
http_port: 80
server_name: www.ctnrs.com
  • Register变量

    注册变量,其实就是将操作结果,包括标准输出和标准错误输出,保存到变量中,然后再根据这个变量的内容来决定下一步的操作,在这个过程中用来保存操作结果的变量就叫注册变量。

1
2
3
4
5
6
7
8
9
10
11
---
- hosts: all
remote_user: root
gather_facts: no
tasks:
- name: test the register variables
shell: uptime
register: results # 使用关键字register声明注册变量,上面uptime命令产生的结果,存入到results中。结果是字典形式。

- name: print the register result
debug: msg="{{ results.stdout }}" # 使用debug模块,打印出上面命令的输出结果。
  • 调用变量

    使用变量的方法

1
2
3
4
5
6
7
8
9
---
- hosts: all
remote_user: root
gather_facts: no
vars:
test_var: 145
tasks:
- name: test playbook variables
command: echo {{ test_var }} # 打印出变量test_var的值。
  • 高阶变量

    对于普通变量,在ansible命令行设定的,在hosts文件中定义的,或者在playbook中定义的等,这些都是普通变量,在引用时,可以使用使用{{ variable }}的形式。ansible是用python语言写的,因此也支持一种叫做列表的变量,形式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ---
    - hosts: all
    remote_user: root
    gather_facts: no
    vars:
    var_list: # 注意形式,定义了var_list列表,取值方法和列表取值一样,不推荐使用jinja2的方法取值。
    - one
    - two
    - three
    tasks:
    - name: test the list variables
    shell: echo {{ var_list[0] }} # 取列表中的第一个字,也就是one
    register: results

    - name: print the register result
    debug: msg="{{ results.stdout }}"

handlers

handlers就是一个类似触发器的东西,当执行一个任务的时候可以定义handlers连带着执行其相关触发动作。

  • Handlers只有在其所在的任务被执行时,才会被运行;如果一个任务中定义了notify调用Handlers,但是由于条件判断等原因,该任务未被执行,那么Handlers同样不会被执行。
  • Handlers只会在每一个play的末尾运行一次;如果想在一个playbook中间运行Handlers,则需要使用meta模块来实现。例如:-meta: flush_handlers.
  • 如果一个play在运行到调用Handlers的语句之前失败了,那么这个Handlers将不会被执行。我们可以使用meta模块的--force-handlers选项来强制执行Handlers,即使Handlers所在的play中途运行失败也能执行。

1
2
3
4
5
6
7
8
9
10
11
handlers:             # 定义两个handlers
- name: restart memcached
service: name=memcached state=restarted
- name: restart apache
service: name=apache state=restarted

- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf # 修改了配置文件然后依次启动memcached和apache服务。
notify: # 使用notify来声明引用handlers。
- restart memcached
- restart apache

例如这里的任务动作是修改配置文件,修改了以后程序需要执行重启操作,就可以用到handlers。

when条件判断

很多任务只有在特定条件下才能执行,这就是when语句发挥作用的地方。

一个简单的实例,关闭掉ip地址为10.0.102.162服务器上的mysql服务,如下:

1
2
3
4
5
6
7
---
- hosts: all
remote_user: root
tasks:
- name: shut down the db server
service: name=mysqld state=stopped
when: ansible_eth0.ipv4.address == "10.0.102.162" # 这里使用了when条件语句

任务委托

默认情况下,ansible所有任务都是在我们指定的机器上面运行的,当在一个独立的集群环境配置时,这并没有什么问题。而在有些情况下,比如给某台服务器发送通知或者向监控服务器中添加被监控的主机,这个时候任务就需要在特定的主机上运行,而非一开始指定的所有主机,此时就需要ansible的委托任务。

使用delegate_to关键字可以配置任务在指定的服务器上执行,而其他任务还是在hosts关键字配置的所有机器上执行,当到了这个关键字所在的任务时,就使用委托的机器运行。

1
2
3
4
5
6
7
8
9
10
11
#查看MySQL是否在运行状态,因此在检查之前首先关掉162上的mysql服务。(为了方便查看状态)
---
- hosts: all
remote_user: root
tasks:
- name: stop the db server
service: name=mysqld state=stopped
delegate_to: 10.0.102.162 # 这里使用了委托,仅关闭162这台服务器上,这个play仅在162这台服务器上执行。

- name: check mysql status
service: name=mysqld state=running

这里委托是在指定的机器上执行,若是想在本地服务器上执行,可以把ip地址换为127.0.0.1即可。也可以使用local_action方法。

1
2
3
4
5
6
7
8
9
10
---
- hosts: all
remote_user: root
tasks:
- name: create the test file
local_action: shell touch test1111 # 在本地创建一个测试文件


- name: check mysql status
service: name=mysqld state=running

任务暂停

有些情况下,一些任务的运行需要等待一些状态的恢复,比如某一台主机或者应用刚刚重启,我们需要等待它上面的某个端口开启,此时我们就不得不将正在运行的任务暂停,直到其状态满足我们的需求。

下一个实例:

1
2
3
4
5
6
7
8
9
- name: wait for webserver to start
local_action:
module: wait_for
host: webserver1
port: 80
delay: 10
timeout: 300
state: startted
# 这个实例中,这个任务将会每10s检查一次主机webserver1上面的80端口是否开启,如果超过了300s则失败

交互式提示

在少数情况下,ansible任务运行的过程中需要用户输入一些数据,这些数据要么比较秘密不方便,或者数据是动态的,不同的用户有不同的需求,比如输入用户自己的账户和密码或者输入不同的版本号会触发不同的后续操作等。ansible的vars_prompt关键字就是用来处理上述这种与用户交互的情况的。下面是一个简单的实例。

1
2
3
4
5
6
7
8
9
10
11
---
- hosts: all
remote_user: root
vars_prompt:
- name: share_user
prompt: "what is your network username?"
private: no

- name: share_pass
prompt: "what is your network password"
private: no

然后执行上面的playbook,因为我们只是测试,只需要在一台机器上执行,因此加入了--limit参数。

1
2
3
4
5
6
7
8
9
10
11
$ ansible-playbook test.yml --limit 10.0.102.162
what is your network username?: test # 需要手动交互输入
what is your network password: 123456 # 手动输入

PLAY [all] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [10.0.102.162]

PLAY RECAP ********************************************************************
10.0.102.162 : ok=1 changed=0 unreachable=0 failed=0

手动输入的变量值,在后面的play中仍然可以用{{ var_name }}的形式调用。

关键字vars_prompt几个常用的选项总结如下:

  • private:默认值为yes,表示用户输入的值在命令行不可见;将值设为no时,用户输入可见。
  • default:为变量设置默认值,以节省用户输入时间。
  • confirm:特别适合输入密码的情况,如果将其设置为yes,则会要求用户输入两次,以增加输入的安全性。

tags标签

默认情况下,ansible在执行一个playbook时,会执行playbook中定义的所有任务。ansible的标签功能可以给角色,文件,单独的任务甚至整个playbook打上标签,然后利用这些标签来指定要运行playbook中的个别任务,或不执行指定的任务,并且它的语法非常简单。

通过一段代码来说明tags的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
---
# 可以给整个playbook的所有任务打一个标签。
- hosts: all
tags: deploy
roles:
# 给角色打的标签将会应用与角色下所有的任务。
- {role: tomcat, tags : ["tomcat", "app"]} # 一个对象添加多个tag的写法之一
tasks:
- name: Notify on completion
local_action:
module: osx_say
msg: "{{inventory_hostname}} is finished"
voice: Zarvox
tags: # 一个对象添加多个tag写法之二
- notifications
- say
- include: foo.yml
tags: foo

# 缩进可能不太对

将上述代码保存,可以通过以下命令来只执行Notify on completion任务。

1
$ ansible-playbook test.yml --tags "say"

如果想忽略掉某个任务,可以使用--skip-tags关键字指定。

Block块

ansible从2.0.0版本开始引入了块功能。块功能可以将任务进行分组,并且可以在块级别上应用任务变量。同时,块功能还可以使用类似于其他编程语言处理异常那样的方法,来处理块内部的任务异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
- hosts: all
remote_user: root

tasks:
- block:
- yum: name=httpd state=present
- service: name=httpd state=started enabled=no
when: ansible_eth0.ipv4.address == "10.0.102.162"

- block:
- yum: name=nginx state=present
- service: name=nginx state=started enabled=no
when: ansible_eth0.ipv4.address == "10.0.102.200"

块功能可以用来处理任务的异常。比如一个ansible任务时监控一个并不太重要的应用,这个应用的正常运行与否对后续的任务并不产生影响,这时候我们就可以通过块功能来处理这个应用的报错。如下代码:

1
2
3
4
5
6
7
8
9
10
tasks:
- block:
- name: shell script to connect the app ti a mointoring service.
script: mointoring-connect.sh
rescue:
- name:只有脚本报错时才执行
         debug:msg="There was an error in the block"
always:
- name: 无论结果如何都执行
debug: msg="This always executes"

当块中任意任务出错时,rescue关键字对应的代码块就会被执行,而always关键字对应的代码块无论如何都会被执行。

with_items

1
2
3
迭代:当有需要重复性执行的任务时,可以使用迭代机制
对迭代项的引用,固定变量名为 “item”
要在task中使用with_items给定要迭代的元素列表

例:

1
2
3
4
5
6
7
8
- hosts: test
remote_user: root
tasks:
- name: add several users
user: name={{ item }} state=present groups=wheel
with_items:
- testuser1
- testuser2

例2:

1
2
3
4
5
6
7
8
- hosts: test
tasks:
- name: install some package
yum: name={{ item }} state=present
with_items:
- nginx
- memcached
- php-fpm

roles

ansible自1.2版本引入的新特性,用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令引入即可。简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷的include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。playbook局限在于如果文件较多的情况,不清楚哪些主机执行了哪些状态的yml文件,roles能清楚哪些主机应用哪些角色,主要使用场景代码复用度较高的情况下。

roles目录结构

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
roles:          --所有的角色必须放在roles目录下,这个目录可以自定义位置,默认的位置在/etc/ansible/roles
project: ---具体的角色项目名称,比如nginx、tomcat、php
files: --用来存放由copy模块或script模块调用的文件。
templates: --用来存放jinjia2模板,template模块会自动在此目录中寻找jinjia2模板文件。
tasks: --此目录应当包含一个main.yml文件,用于定义此角色的任务列表,此文件可以使用include包含其它的位于此目录的task文件。
main.yml
handlers: --此目录应当包含一个main.yml文件,用于定义此角色中触发条件时执行的动作。
main.yml
vars: --此目录应当包含一个main.yml文件,用于定义此角色用到的变量。
main.yml
defaults: --此目录应当包含一个main.yml文件,用于为当前角色设定默认变量。
main.yml
meta: --此目录应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系。
main.yml

roles例子(部署nginx)

首先可以使用ansible-galaxy创建一个roles模板文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@hostname ~]# ansible-galaxy init nginx
- Role nginx was created successfully
[root@hostname ~]# tree ./nginx
./nginx
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
└── main.yml
8 directories, 8 files

配置完以后的目录如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@hostname playbook]# tree ./
./
├── nginx
│   ├── defaults #无任何配置
│   │   └── main.yml
│   ├── files #配置文件以及systemd启动文件
│   │   ├── nginx.conf
│   │   └── nginx.service
│   ├── handlers #无任何配置
│   │   └── main.yml
│   ├── meta #无任何配置
│   │   └── main.yml
│   ├── README.md
│   ├── tasks #所有任务都写在里面
│   │   └── main.yml
│   ├── templates #无任何配置
│   ├── tests #无任何配置
│   │   ├── inventory
│   │   └── test.yml
│   └── vars #任务的变量配置文件
│   └── main.yml
└── nginx.yml #任务的运行调用文件

文件解读

这里先从nginx.yaml(任务的运行调用文件)开始

nginx.yml

这里很简单,就定义了hosts,以及要执行的roles,它会根据roles名称自动去该目录下找对应role的文件夹,这里如果有多个role可以写多个。

1
2
3
4
- hosts: k8s
name: install nginx
roles:
- nginx

files(nginx.conf,nginx.service)

1
files文件夹下面的文件在被调用(copy时),不用再指定源文件目录,直接指定文件名ansible会在执行时自动调用files下面的文件。

nginx.conf:因为是测试,这里仅用了nginx的默认配置文件,所以就不贴具体配置了。

nginx.service:

nginx的systemd启动文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
# Nginx will fail to start if /run/nginx.pid already exists but has the wrong
# SELinux context. This might happen when running `nginx -t` from the cmdline.
# https://bugzilla.redhat.com/show_bug.cgi?id=1268621
ExecStartPre=/usr/bin/rm -f $PIDFile
ExecStartPre=/usr/local/nginx/sbin/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/bin/kill -s HUP PIDFileExecStartPre=/usr/local/nginx/sbin/nginx−tExecStart=/usr/local/nginx/sbin/nginxExecReload=/bin/kill−sHUP$MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=mixed
PrivateTmp=true[Install]
WantedBy=multi-user.target

tasks(main.yml)

这个是核心文件,所有任务都写到里面了

main.yml

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
---
# tasks file for nginx
- name: add group #添加用户组
group:
name: www
gid: 666
state: present
- name: add user #添加用户
user:
name: www
uid: 666
create_home: no
- name: pull nginx package #拉去nginx源码包,这里是源码编译安装。变量存放于nginx/vars/main.yml中。
shell: rm -rf /tmp/nginx-{{nginx_version}}.tar.gz && wget http://nginx.org/download/nginx-{{nginx_version}}.tar.gz -P /tmp
- name: unarchive nginx #解压nginx源码包
unarchive:
src: /tmp/nginx-{{nginx_version}}.tar.gz
dest: /tmp
- name: yum install yilai #安装nginx依赖,with_items类似于for循环,一次性安装多个包
yum:
name: "{{ item }}"
state: present
with_items:
- gcc
- gcc-c++
- make
- libtool
- zlib
- zlib-devel
- openssl
- openssl-devel
- pcre
- pcre-devel
- name: install nginx #编译安装nginx
shell:
cmd: |
cd /tmp/nginx-{{nginx_version}} ;
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-http_auth_request_module --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-debug ;
make && make install ;
mkdir /etc/nginx -p
- name: cp config nginx #复制nginx/files/nginx.conf到服务器/etc/nginx目录
copy:
src: nginx.conf
dest: /etc/nginx/

- name: nginx service file #复制nginx/files/nginx.service到
copy:
src: nginx.service
dest: /etc/systemd/system/nginx.service

- name: add profile nginx env #将nginx加入环境变量
shell:
cmd: |
echo 'export PATH=/usr/local/nginx/sbin:$PATH' > /etc/profile ;
source /etc/profile
- name: self startup #设置开机自启动
file:
src: /etc/systemd/system/nginx.service
dest: /etc/systemd/system/multi-user.target.wants/nginx.service
state: link

- name: start nginx #启动nginx
systemd:
name: nginx
state: starte

vars(main.yml)

1
2
3
---
# tasks里面的变量取自于这里
nginx_version: 1.20.2
1
最后 ansible-playbook nginx.yaml 运行roles

roles部署k8s(单master节点)

这里先梳理一下k8s的部署流程:

  1. 安装依赖。启用iptables,关闭selinux,开启ipvs。调整内核参数,同步时间。将节点ip与节点名称写进hosts文件,修改主机名
  2. 安装docker和安装k8s
  3. 初始化master节点,安装网络模块
  4. node节点加入master节点

将每个步骤都做成一个roles,然后调用。

然后这里测试采用的是一个master节点,两个node节点(可以n个node节点)。然后etcd是直接运行于k8s中,没有进行分离。下面是ansible的hosts文件,定义了两个主机组,一个是k8s所有节点,还有有一个是master主机组(初始化master节点的时候指定这个主机组)。

/etc/ansible/hosts

1
2
3
4
5
6
7
8
9
10
[k8s]
192.168.60.199 node_name=master
192.168.60.201 node_name=node1
192.168.60.202 node_name=node2
[master]
192.168.60.199
[all:vars]
ansible_ssh_port=22
ansible_ssh_user=root
ansible_ssh_pass=admin

1.安装依赖

1
ansible-galaxy init yilai

这里依赖需要提前准备好hosts文件(包含每个节点ip和节点名称), kubernetes.conf(sysctl文件),ipvs.modules(iptables启用脚本)。

这里步骤为:

  • yum安装依赖
  • 时间同步(这里用的是centos8,yum源没有ntp,所以单独安装wntp)
  • 安装启用iptables,关闭swap分区,关闭selinux
  • 复制kubernetes.conf文件到主机
  • 复制hosts文件到主机(模板文件)
  • 复制ipvs.modules文件到主机
  • 执行生效刚刚复制的sysctl文件和iptables脚本文件

yilai/tasks/main.yml

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
---
# tasks file for yilai
- name: yilai packages
yum: "{{item}}"
with_items:
- conntrack
- ipvsadm
- ipset
- jq
- iptables
- curl
- sysstat
- libseccomp
- wget
- vim
- net-tools
- git
- name: time option
shell:
cmd: rpm -ivh http://mirrors.wlnmp.com/centos/wlnmp-release-centos.noarch.rpm;yum install wntp -y;ntpdate ntp1.aliyun.coml;echo '* */1 * * * /usr/sbin/ntpdate' >> /etc/crontab;service crond restart
- name: iptables option and setoff selinux
shell:
cmd: systemctl stop firewalld ; systemctl disable firewalld ; yum -y install iptables-services ; systemctl start iptables ; systemctl enable iptables ; iptables -F ; service iptables save;swapoff -a ; sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab ; setenforce 0 ; sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
- name: kernel option set
copy:
src: kubernetes.conf
dest: /etc/sysctl.d/kubernetes.conf
- name: 添加hosts
template: src=hosts.j2 dest=/etc/hosts
- name: set hostname
shell:
cmd: echo `cat /etc/hosts | grep {{ansible_default_ipv4.address}} | awk '{print $2}'` > /etc/hostname && hostname `cat /etc/hosts | grep {{ansible_default_ipv4.address}} | awk '{print $2}'`

- name: ipvs.modules
copy:
src: ipvs.modules
dest: /etc/sysconfig/modules
- name: ipvs.modules and kubernetes.conf qiyong
shell:
cmd: chmod 755 /etc/sysconfig/modules/ipvs.modules ; bash /etc/sysconfig/modules/ipvs.modules ; sysctl -p /etc/sysctl.d/kubernetes.conf

yilai/templates/hosts.j2

1
2
3
4
5
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
{% for host in groups['k8s'] %}
{{ hostvars[host].inventory_hostname }} {{ hostvars[host].node_name }}
{% endfor %}

yilai/files/kubernetes.conf

1
2
3
4
5
6
7
8
9
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
vm.swappiness=0 # 禁止使用 swap 空间,只有当系统 OOM 时才允许使用它 vm.overcommit_memory=1 # 不检查物理内存是否够用
vm.panic_on_oom=0 # 开启 OOM
fs.inotify.max_user_instances=8192 fs.inotify.max_user_watches=1048576
fs.file-max=52706963
fs.nr_open=52706963
net.ipv6.conf.all.disable_ipv6=1 net.netfilter.nf_conntrack_max=2310720

yilai/files/ipvs.modules

1
2
3
4
5
6
7
ipvs_modules="ip_vs ip_vs_lc ip_vs_wlc ip_vs_rr ip_vs_wrr ip_vs_lblc ip_vs_lblcr ip_vs_dh ip_vs_sh ip_vs_fo ip_vs_nq ip_vs_sed ip_vs_ftp nf_conntrack"
for kernel_module in \${ipvs_modules}; do
/sbin/modinfo -F filename \${kernel_module} > /dev/null 2>&1
if [ $? -eq 0 ]; then
/sbin/modprobe \${kernel_module}
fi
done

2.安装docker和k8s

1
ansible-galaxy init docker_k8s_install

安装docker和k8s首先要准备好这两个的yum repo源,然后yun安装。

步骤:

  • yum安装docker依赖和yum工具箱(yum manager工具)
  • 通过yum-config-manager添加docker yum源
  • 安装docker(这里只可以指定具体版本号,但我这里没有指定)
  • 复制docker配置文件到主机
  • 启动docker并设置开启启用
  • k8s的yum源文件添加(这里的yum源是写好的文件复制到主机上)
  • yum安装k8s (变量指定版本号)
  • 开机启动k8s

docker_k8s_install/tasks/main.yml

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
---
- name: yilai
yum:
name: "{{ item }}"
state: present
with_items:
- yum-utils
- device-mapper
- device-mapper-persistent-data
- lvm2
- name: add yum repo
shell:
cmd: yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
- name: yum install docker
yum:
name: "{{ item }}"
state: present
with_items:
- docker-ce
- docker-ce-cli
- containerd.io
- name: docker option file
copy:
src: daemon.json
dest: /etc/docker
- name: restart docker
shell:
cmd: systemctl daemon-reload && systemctl restart docker && systemctl enable docker


- name: add k8s yum repo
copy:
src: kubernetes.repo
dest: /etc/yum.repo.d
- name: yum install k8s
yum:
name: "{{ item }}"
state: present
with_items:
- kubeadm-{{k8s_version}}
- kubelet-{{k8s_version}}
- kubectl-{{k8s_version}}
- name: enable k8s
shell:
cmd: systemctl enable kubelet

docker_k8s_install/vars/main.yml

1
k8s_version: 1.23.4

docker_k8s_install/files/daemon.json

docker配置文件

1
2
3
4
5
6
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": { "max-size": "100m"
}
}

docker_k8s_install/files/kubernetes.repo

k8s的yum源文件

1
2
3
4
5
6
7
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg

3.初始化master节点,安装网络模块

1
ansible-galaxy init k8s_init

上面部署的时候主机组用的都是k8s,也就是所有主机,这里下面的操作均为master的初始化操作,所以只选定master主机组。

  • 首先kubeadm reset保持环境干净,所以运行前先重置k8s。(正常情况下一次性部署可以不用这个操作)
  • k8s初始化master节点,并将其日志输出于/tmp/init.log。(一是可以看到具体成功与否的日志,二是后面node节点加入主节点需要用到master初始化成功后生成的init命令)
  • 初始化完成后把配置文件复制到root(用于使用kubectl命令)
  • 安装网络模块
  • 将node节点的加入master命令(kubectl join ***)输出为/tmp/join_master文件,然后待会儿node节点直接拿这这个文件中的命令执行即可加入master节点。(这里需要注意的是这些文件是从master节点输出的,最终文件也保存在master节点,后面node节点是通过copy复制到对应主机的,因为这里ansible执行命令的主机和master是同一台主机,所以直接调用文件复制到对应主机即可,如果master和ansible执行主机并非同一个服务器,还需要将其通过scp之类的命令传输到ansible主机,再将其传输到所有node节点上。)

k8s_init/tasks/main.yml

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

---
# tasks file for k8s_init
- name: reset k8s
shell:
cmd: kubeadm reset --force
- name: init master
shell:
cmd: kubeadm init --image-repository=registry.aliyuncs.com/google_containers --kubernetes-version={{k8s_version}} --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address {{ansible_default_ipv4['address']}} > /tmp/init.log

- name: init master 2
shell:
cmd: mkdir -p $HOME/.kube && cp -r /etc/kubernetes/admin.conf $HOME/.kube/config && chown $(id -u):$(id -g) $HOME/.kube/config

- name: install calico (network module)
shell:
cmd: kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml

- name: for node init
shell:
cmd: rm -rf /tmp/join_master && cat /tmp/init.log | grep 'kubeadm join' >> /tmp/join_master && cat /tmp/init.log | grep 'discovery' >> /tmp/join_master
#这里join_master最后取出来的文件内容如下
#[root@master k8s]# cat /tmp/join_master
#kubeadm join 192.168.60.199:6443 --token pro79p.hiigqo29fp1zjyrb \
# --discovery-token-ca-cert-hash sha256:121dc12156e13b751dafd6546cb2d7bace0773b2ed7d5d789ee9ab6ae1a9abd1
#就是取出其节点加入master的命令

k8s_init/vars/main.yml

1
k8s_version: 1.23.4

4.node节点加入master节点

1
ansible-galaxy init k8s_init_node

k8s_init_node/tasks/main.yml

这里比较简单

  • 首先初始化环境,但排除master节点
  • 然后将join_master(加入master命令文件)复制到node节点中
  • 执行加入master的操作
1
2
3
4
5
6
7
8
9
10
11
12
- name: init node
shell:
cmd: kubeadm reset --force
when: "node_name != 'master'"
- name: copy k8s join file
copy:
src: /tmp/join_master
dest: /tmp
- name: node join to master
shell:
cmd: /bin/bash /tmp/join_master
when: "node_name != 'master'"

主文件

k8s.yaml

这个就是roles运行文件,定义每个步骤的运行主机以及其要执行的roles

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- hosts: k8s
name: yilai
roles:
- yilai

- hosts: k8s
name: install docker k8s
roles:
- docker_k8s_install

- hosts: master
name: init master
roles:
- k8s_init

- hosts: k8s
name: init node
roles:
- k8s_init_node

执行

1
ansible-playbook k8s.yaml