Ansible快速上手——像牧羊人一样管理集群(上)Ansible基础

337 阅读11分钟

安装ansible

官方给出了多种安装方法,比如通过克隆源代码、自制rpm包,pip,yum安装等等,本文以Rocky Linux为控制端,通过dnf安装ansible:

# 安装epel软件仓库
dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
dnf makecache
# 安装ansible,同时会安装对应版本所需要的python,在RHEL7中时python2.7,RHEL8中是python3.12
dnf install ansible -y

准备工作

在开始通过ansible控制集群之前,我们需要做一点准备工作,让ansible能够在节点正常工作。 在控制节点/etc/hosts文件中配置ip地址和对应的主机名,让控制端的系统认识谁是谁:

188.189.66.200  ctl
188.189.66.1    nginx1
188.189.66.2    nginx2
188.189.66.3    db-mysql1
188.189.66.4    db-mysql2
188.189.66.5    redis1
...
188.189.66.n    nginx-lvs

生成密钥对并分发给各个被控节点:

ssh-keygen -f /root/.ssh/id_rsa -N ''
for i in $(grep -v localhost /etc/hosts |cut -f2);
do
    ssh-copy-id $i
done

上面需要我们一个一个的确认授权并输入密码,也许有点麻烦,但绝对是值得的。接着我们配置/etc/ansible/hosts文件,让ansible也认识谁是谁:

# 定义节点
redis1                 # 使用主机名
188.189.66.100         # 使用IP地址
188.189.66.101:10022   # 指定端口号
188.189.66.102 ansible_ssh_user=ans ansible_ssh_pass=123456  # 指定ssh连接时账号密码
ans_node1 ansible_ssh_host=188.189.66.103                    # 定义别名

# ansible_ssh_host 连接目标主机的地址 
# ansible_ssh_port 连接目标主机的端口,默认22 
# ansible_ssh_user 连接目标主机默认用户 
# ansible_ssh_pass 连接目标主机默认用户密码 
# ansible_ssh_connection 目标主机连接类型,可以是 local/ssh/paramiko
# ansible_ssh_private_key_file 连接目标主机的ssh私钥

# 定义一组web服务器组
[web_server]
apache1
apache2
nginx[1:3]       # 也可以简写
web[1:9]-tomcat  # 或者这样

# 定义其他组
[database]
db-mysql[1,2]    # 可以使用英文逗号

# 定义一个包含其他组的父组
[server:children]
web_server
database

# server组变量
[server:vars]
ntp_server=ntp.aliyun.com

小试牛刀

接下来,我们可以使用ansible命令控制我们的集群了,ansible的命令格式是: ansible 主机 -m 模块 [-a "参数"] 有些命令需要加参数,有些则不用,如果需要加参数,那么使用-a来指定参数,首先使用一条命令查看我们可以控制的主机: ansible all --list-hosts

Clip_2024-07-21_18-02-29.png

我们喜欢用ping-pong来测试网络连通性,就好像学习一门编程语言总是从打印Hello,World!开始一样,我们用ping模块来测试一下连通性: ansible all -m ping 意料之中的,我们得到了被控端机器们的回应——“pong”:

Clip_2024-07-21_18-03-48.png

我们也可以单独操作一个节点(组)或多个节点(组):

Clip_2024-07-21_18-04-25.png

在ansible中,一个模块就是一个脚本,而且绝大多数脚本都是python脚本,大多数的脚本都是需要参数的,如果我们不指定模块,那么ansible就会使用配置文件的默认模块,初始时为command模块,我们可以尝试使用通过command模块来让ansible做些什么,比如获取某个节点的负载,系统内核版本或者ip地址:

Clip_2024-07-21_18-18-32.png

ansible中有非常多的模块,我们怎么知道有哪些模块并且知道他们可以用来干什么呢?下面的命令或许可以帮助我们:

ansible-doc -l:列出当前所有已经安装的模块;

ansible-doc 模块名:像man那样帮助我们了某个模块是用来做什么的。

ansible常用模块

首先我们来说说command模块和shell模块,这两个模块功能非常相像,但是有一些区别:

  1. command模块的命令不启动shell,直接通过ssh执行命令;
  2. command不支持bash的特性,如管道和重定向等功能;
  3. 所有需要调用shell的功能都无法使用。

比如这些命令使用command模块都会报错,但使用shell模块不会报错:

[root@CTL ~]# ansible lvs -m command -a "ls |wc -l"  # 报错,无法访问
[root@CTL ~]# ansible lvs -m command -a "ls &"       # 报错,无法访问

因此如果你希望你的默认模块是shell,可以在配置文件中找到module_name = command这一行,并把command替换成shell:

sed -i '/module_name = command/c\module_name = shell' /etc/ansible/ansible.cfg

我们使用shell模块操作被控端后,再次进入通过shell控制同一个节点,状态并不会被保留,比如第一个命令cd /opt,当再次通过shell模块创建一个文件时,并不会被创建在/opt下,而是默认的家目录下,所以我们可以用shell模块,一次传入多个参数,像下面这样:

[root@CTL ~]# ansible lvs -m shell -a "chdir=/opt touch GreenCode.txt && ls"

这个命令会返回/opt下的文件,包括我们刚刚创建的GreenCode.txt。 shell模块支持条件判断,根据条件来决定是不是执行命令,这两个参数是:creates和removes:

creates:文件存在,不执行命令,不存在则执行。

removes:文件存在,执行命令,不存在则不执行。

似乎不太容易理解,我们举个栗子~,上面我们在/opt下新建了GreenCode.txt这个文件,我们判断一下,如果这个文件不存在,我们就新建一个GreenCode.txt(creates判断文件存在,不执行命令,removes判断文件存在,执行命令)

Clip_2024-07-21_19-26-19.png

好像还是有点抽象,我们简单的记,假设这两个参数只是被用来创建文件,creates只用来与创建文件的命令搭配使用,文件存在,就不需要创建了,所以不执行创建命令;而removes是用来搭配删除命令使用,如果文件存在,执行删除文件的命令,文件不存在,则不执行。这个假设只是为了方便我们理解,实际上,你可以搭配任何命令与他们一起使用。

script模块

用法: ansible hosts -m script -a "path/to/script"

如果你已经很好的理解了上面的话,接下来我们学习下一个模块。小伙伴们可能发现了,当需要执行多个命令时,我们需要用&&来连接命令,如果仅有两三个命令,这样确实高效,但是如果又很多命令要执行呢?你一定能想到可以用脚本,接下来我们就来学习一下script模块。首先我们在本地主机编写一个shell脚本:

cat >user-check.sh <<EOF
#!/bin/bash

grep "bin/bash" /etc/passwd|cut -d: -f1 |grep -v root
if [ $? != 0 ];do
        echo "There is no user unless root"
        useradd  ansible-user
else
        echo "There is nornal user in the node"
        exit 0
fi
EOF

接下来通过ansible在目标节点上执行这个脚本:

Clip_2024-07-22_09-22-56.png

可以看到脚本在目标节点上执行成功,并通过标准输出返回了我们定义的语句。事实上,用script模块执行脚本时,ansible首先把脚本拷贝到目标节点上,在与节点断开连接时,ansible会删除脚本,即使你在控制端强制中断命令的执行。并且这里所说的脚本并不是只有shell脚本,其他任何被控端支持的语言都可以作为脚本语言传输给被控端执行,如python,perl等。

file模块

用法: ansible hosts -m file -a "path=/path state=touch\absent\directory [owner=user] [group=group] [mode=mode] [src=/path dest=/path state=link]"

file模块可以创建或删除文件、目录、链接,还可以修改文件的属性和权限。你可能会疑惑,使用shell模块在目标主机上执行创建文件的命令不是也可以创建文件吗,为什么还要单独使用一个file模块,这是因为file模块具有幂等性,所谓幂等性就是不管命令执行多少次,都和执行了一次一样,ansible中有很多模块都具有幂等性,接下来我们一起探索一下file模块。

比如我们在目标主机创建一个文件或者一个目录:

Clip_2024-07-22_09-46-45.png

可以看到我们成功的创建了目标文件,file模块还可以用来修改文件的权限,从上图我们可以看到我们创建的文件所属用户和用户组为root,文件权限时0644,我们之前在script模块创建了用户ansible-user,现在我们把这个文件所属者修改为ansible-user,并把权限设置为755:

Clip_2024-07-22_09-53-56.png

我们刚才说了,除了创建,file模块还可以删除文件,只需要把state=touch改成state=absent即可,当state=directory时,表示递归操作文件夹。

copy模块

用法:ansible hosts -m copy -a "src=/path dest=/path [backup=yes]"

上面我们知道,通过script模块可以把脚本文件拷贝到目标主机,但是脚本执行完会被删除,那么有没有一个模块可以让我们把控制端文件传输到目标主机件并持久化保存呢,这就要用到copy模块了。copy模块可以把本地文件拷贝到目标主机,如果目标主机的已存在该文件,可以通过backup=yes参数来备份目标文件,前面我们在目标主机创建了一个空文件ansible-tmp.txt,现在我们在本地创建一个同名文件,并拷贝到目标主机,看看会发生什么吧。

cat > ansible-tmp.txt <<EOF
This is a temporary file.
EOF
ansible ngxin2 -m copy -a "src=./ansible-tmp.txt dest=/tmp/ansible-tmp.txt backup=yes"

Clip_2024-07-22_10-12-26.png

从图上可以看到,ansible先备份了原先的文件(蓝色虚线框),然后把我们的文件复制到了目标主机(红色虚线框)。copy模块也可以通过参数content直接把内容写入到目标文件,如:

ansible nginx2 -m copy -a "content="Hello,Ansible\!" dest=/tmp/hello.txt"

这条命令可以直接把Hello,Ansible!这句话写入到目标主机的/tmp/hello.txt中。

fetch模块

用法:ansible hosts -m copy -a "src=/path dest=/path [backup=yes]"

fetch模块与copy模块类似,但是作用相反,也就是把目标主机的文件拷贝到控制端。我们把目标主机的/etc/ssh文件夹拷贝到控制端的/root下:

Clip_2024-07-22_10-31-09.png

为什么我们拷贝的时一个文件,但查看时却变成了文件夹呢,这是因为我们现在只拷贝了一个目标主机的文件,ansible时用来控制大规模的集群的,如果我们的目标主机不是单一目标而是多个或者一个组呢,那么拷贝过来的都是文件就会重名,所以为了避免这种情况发生,ansible会为每一个目标节点都创建一个文件夹,上面我们拷贝过来的文件在控制端的绝对路径是/root/ngxin2/etc/ssh/sshd_config,也就是说ansible递归的复制了目标文件,ansible会把所有从目标节点nginx2复制的文件都放在nginx2目录下,并递归的复制文件路径。

user模块

用法:ansible hosts -m user -a "name=user [uid=uid] [group=group] [groups=group] [home=/home/user] [password={{'assword'|password_hash('sha512')}}]"

我们前面使用script脚本在目标主机创建了用户,其实ansible自带了user模块方便我们管理目标主机的用户账户:

# 在目标主机批量创建用户账户ans
ansible all -m user -a "name=ans"
# 或者可以更详细一点
ansible all -m user -a "name=ans uid=1080 group=adm groups=wheel home=/home/ans"
# 修改账户密码
ansible all -m user -a "name=ans password={{'Ansible@1080'|password_hash('sha512')}}"

注意,上面的password参数把密码通过管道传输给password_hash处理,password_hash通过sha512进行加密,如果不加密,密码会在/etc/passwd中以明文显示,并且用户无法登录到shell。

当然,user模块也可以用来删除用户:

# 删除用户ans
ansible all -m user -a "name=ans state=absent"
# 删除用户的同时删除用户的家目录
ansible all -m user -a "name=ans state=absent remove=true"

yum_repository模块

这个模块可以用来修改或创建yum源配置文件:

# 新建yum源
ansible web_server -m yum_repository -a "name=ans_yum description=\"create by ansible\" baseurl=http://mirrors.aliyun.com/mirrors.cloud.aliyuncs.com gpgcheck=yes enabled=yes"
# 删除yum源
ansible web_server -m yum_repository -a "name=ans_yum state=absent"

新建yum源的命令会创建一个/etc/yum.repo.d/ans_yum.repo的文件,文件如下图

Clip_2024-07-22_15-33-49.png

yum模块

修改了好了yum源,下面可以安装软件了,yum模块可以安装、卸载、删除软件包。

# state=present\absent\latest # 分别表示安装、卸载、升级
# 下面的命令表示分别安装vim,卸载mariadb,升级openssl
ansible web_server -m yum -a "name=vim state=present"
ansible web_server -m yum -a "name=mariadb state=absent"
ansible web_server -m yum -a "name=openssl state=latest"

我们尝试使用命令安装net-tools:

Clip_2024-07-22_15-55-50.png

service模块

这个模块让我们可以像用systemctl命令那样控制服务的启动、关闭等。

# state=reloaded, restarted, started, stopped,分别表示重载、重启、启动、停止
# enabled=yes\no或者true\false 分别表示开机自启,或者禁止开机自启
# 关闭防火墙并禁止开机自启动
ansible web_server -m service -a "name=firewalld state=stopped enabled=no"

上面命令返回的结果:

Clip_2024-07-22_15-59-41.png

写在末尾

以上是ansible入门必会的知识,当然还有很多模块,在我测试的这个版本,ansible的模块已经超过了8000个,小伙伴们可以使用ansible-doc命令自行探索。下一节,我们一起学习ansible的高级玩法——剧本。