如果是一名服务端程序员,开发最多程序应该为服务器端应用程序,而服务器端端应用程序大部分部署在 Linux 操作系统之上,在 Linux 上部署应用和管理 Linux 系统大部分都是使用 SSH 端方式管理。这篇文章要介绍的是 Ansible 自动化运行工具,它可以帮助我们自动化各种 IT 任务,从部署应用程序和服务到协调网络设备和云资源的管理,个人感觉相比 K8s 的设计 Ansible 更贴合的是物理机器设计的管理软件。
上图为 ansible 的软件架构图。
安装 Ansible
Ansible 目前支持类 RHEL 发行版本系列系统,大部分默认的系统镜像软件默认是不带有 ansible 服务程序,需要自己手动安装,需要从 Extra Packages for Enterprise Linux 扩展软件仓库中获取,首先要配置 epel 软件仓库最新的地址,执行下面命令:
sudo yum install -y epel-release
# 安装 ansible 服务软件
yum install -y ansible
# 检查是否成功安装
ansible --version
/etc/ansible/ansible.cfg
文件中。
参数 | 描述 |
---|---|
-i, --inventory-file | 指定主机清单文件路径 |
-l, --limit | 指定要操作的主机或主机组 |
-u, --user | 指定 SSH 用户名 |
-b, --become | 切换为管理员权限 |
-K, --ask-become-pass | 请求管理员密码 |
-c, --connection | 指定连接类型,如 SSH 或本地连接 |
-t, --timeout | 指定操作超时时间 |
-k, --ask-pass | 请求 SSH 密码 |
-s, --sudo | 使用 sudo 命令执行任务 |
-f, --forks | 指定同时运行任务的最大数量 |
-v, --verbose | 显示详细输出信息 |
-h, --help | 显示帮助信息 |
以上是 ansible 常用的主要命令行参数信息。
初始化集群
首先 Ansible 是基于 SSH 协议进行通信的,我们必须将管理节点的 SSH 公钥传送到被控制子节点中,从而实现免密码登录到远程其他子节点主机,首先要创建 SSH 密钥对,执行下面命令:
# 生成 ssh 密钥对
ssh-keygen -t rsa -b 2048 -C "管理员邮箱地址"
# 复制公钥到远程子主机上
ssh-copy-id 192.168.31.86
/root/.ssh/id_rsa.pub
,这个文件的中内容就为公钥的字符串,也可以手动上传到子节点服务器 ~/.ssh/authorized_keys
文件中。如果需要管理的机器超过几百台,每次都手动复制密钥文件很麻烦,可以编写一个 shell 脚本来帮助复制公钥文件到远程主机中:
#!/bin/bash
# 设置公钥路径和远程主机列表文件的路径
public_key_path="/root/.ssh/id_rsa.pub"
remote_hosts_file="/remote/hosts.txt"
# 读取远程主机列表
remote_hosts=$(cat "$remote_hosts_file")
# 循环遍历远程主机并将公钥复制到其中
for host in $remote_hosts; do
# 将公钥复制到远程主机
ssh-copy-id -i "$public_key_path" "$host" <<EOF
your_password
EOF
done
上面编写了一段代码,将 /root/.ssh/id_rsa.pub
文件逐个复制到 /remote/hosts.txt
文件中的主机中,hosts.txt
文件里面内容为主机 IP 地址信息,内容如下:
192.168.31.1
192.168.31.2
node1.jdk.world
这样一来,复制密钥的操作就完成自动化了,几千台主机操作也没有问题。
资产管理
Ansible 服务程序将所有受管理的节点称之为 资产
,有静态资产和动态资产之分,静态资产可以认为是将远程受管理的的节点 IP 写入一个固定的文件中保存,而动态资产可以在运行过程中动态从远程服务器或者数据库中加载。
# 手动管理,在指定的机器运行 ping 模块,并且收集结果
ansible all -i 192.168.31.171,node1.jdk.world -m ping
inventory.ini
文件来定义需要管理的资源主机,也可以在 /etc/ansible/hosts
文件中编写,下面是一些例子:
# inventory.ini 文件例子
[webservers]
web1 ansible_host=172.16.19.132 ansible_user=root ansible_password=123456
[dbservers]
db1 ansible_host=192.168.31.96 ansible_user=root ansible_password=123456
db2 ansible_host=192.168.31.213 ansible_user=root ansible_password=123456
上面定义两组服务器类型 webservers 和 dbservers ,每个组都包含一个或多个主机,对于每个主机使用了 ansible_host 、ansible_user 和 ansible_password 变量来定义这些连接信息,此种方式存在很大弊端,密码被明文存储在文件中,如果意外泄露导致机器账号和密码泄露。
inventory.ini
文件如果要执行的可以使用 ansible all -i inventory.ini -m ping
这种做法和手动填写主机信息一致,all
参数可以换成配置文件中的子组名。
# 对所有子主机执行 ping 模块
ansible all -i inventory.ini -m ping
# 安装分组来执行 ping 模块
ansible dbservers -i inventory.ini -m ping
# 通过结构化输出展示机器信息
ansible-inventory --graph
# 查看节点信息列表
ansible all --list-hosts
变量 | 作用 |
---|---|
ansible_ssh_host | 受管理主机名 |
ansible_ssh_port | 端口号 |
ansible_ssh_user | 默认账号 |
ansible_ssh_pass | 默认密码 |
ansible_shell_type | 默认 shell 类型 |
[web]
172.16.19.132
[db]
192.168.31.96
192.168.31.213
# 其他子组,组成的组
[all:children]
web
db
# 全局变量
[all:vars]
ansible_user=root
ansible_password=123456
对于保存好的资产文件,在操作的时候可以根据类型对资源进行控制和操作,这个过程称之为 资源选择
:
# 全部的服务器
ansible all -i inventory.ini -m ping
# 针对于 web 服务器组
ansible web -i inventory.ini -m ping
# 针对于 db 服务器组
ansible db -i inventory.ini -m ping
模块系统
Ansible 功能的多样性就取决于它设计的模块系统,模块可以认为是一个功能的集合,不同的模块可以实现不同功能。默认 ansible 有一些基础的核心模块,也有扩展模块,另外还可以用户自定义模块,对应的是用户自定义功能实现。如果需要查看内部支持的模块说明可以使用 ansible-doc -l
命令可以列出所有的模块信息,如果要查看某个特定的模块信息可以使用 ansible-doc module_name
命令。
ansible 资产范围 -i 资产文件 -m 模块名称 [-a 模块参数]
,下面列举了一些常用的 ansible 内置的模块:
模块名称 | 描述 |
---|---|
command | 在目标主机上执行指定的命令或脚本。 |
shell | 在目标主机上以 shell 方式执行指定的命令或脚本。 |
copy | 将本地文件或目录复制到目标主机。 |
file | 管理目标主机上的文件和目录,包括创建、删除、修改、更改权限等操作。 |
service | 管理目标主机上的服务,包括启动、停止、重启等操作。 |
apt/yum/pacman | 管理目标主机上的包,包括安装、升级、删除等操作。 |
user/group | 管理目标主机上的用户和组,包括创建、删除、修改等操作。 |
template | 将本地的 Jinja2 模板渲染成目标主机上的文件。 |
debug | 在执行 Ansible 任务时输出调试信息。 |
wait_for | 等待目标主机上的某个条件成立,例如等待某个端口开放或某个文件存在。 |
shell | 直接执行用户指定的名称支持特殊字符串 |
filesystem | 格式化硬盘设备文件 |
mount | 挂载硬盘设备文件 |
firewalld | 管理主机上的防火墙程序 |
get_url | 从网络中下载文件 |
cron | 添加计划任务信息 |
# 添加端口到 web 资产主机上
[root@cs9 ~]# ansible web -i inventory02.ini -m firewalld -a "port=8080/tcp permanent=true state=enabled immediate=true"
172.16.19.132 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"msg": "Permanent and Non-Permanent(immediate) operation, Changed port 8080/tcp to enabled"
}
# 立即实现防火墙
[root@cs9 ~]# ansible web -i inventory02.ini -m command -a "firewall-cmd --reload"
172.16.19.132 | CHANGED | rc=0 >>
success
-a
参数传递变量到被控主机上,-a
参数还支持从资产文件中读取预先定义好的变量来执行,例如有资源文件内容为:
[web]
172.16.19.132
[db]
192.168.31.96
192.168.31.213
# 其他子组,组成的组
[all:children]
web
db
# 全局变量
[all:vars]
ansible_user=root
ansible_password=123456
# 变量定义
[web:vars]
http_port=8080
这样就可以使用 -a
参数来获取变量的值了:
# 获取预定义的变量 http_port
[root@cs9 ~]# ansible web -i inventory03.ini -a 'echo "{{http_port}}"'
172.16.19.132 | CHANGED | rc=0 >>
8080
-a
参数类似于 -m shell
,类似于默认的 shell 模块,可以不需要添加 shell 模块,默认 -a
即可。
PlayBook 文件
在 ansible 中的 Playbook 文件的格式为 yaml ,必须严格按照 yaml 格式才能被正常解析和运行,命名为 playbook.yml 或者其他你喜欢的名称,用于定义 ansible 任务的执行步骤、主机和变量等信息,可以帮助自动化部署、配置和管理远程主机。
---
- hosts: webserver # 针对哪些资产启作用,多个服务器名称可以使用逗号隔开
vars: # 定义变量
http_port: 80
tasks: # 执行任务计划集合
- name: Install Apache # 任务的名称
yum: # 任务所使用的模块,yum 软件管理
name: httpd
state: present
- name: Start Apache
service:
name: httpd
state: started
enabled: true
ansible-playbook 文件路径.yaml
即可,如果 playbook 任务执行成功会有 ok
和 change
字段信息提示。
Role 规则
在 ansible 通过角色功能,这里角色是指定的是一些功能可以复用,单独抽象成为一个角色类模块化,简单来说就把一些常用的功能模块化在需要使用的时候可以加载,类似于编程中函数封装实现类似,分装成为可复用的对象。
Variables
和 Handlers
、 tasks
单独抽象出来,达到组建之间复用的设计,一个完整的角色目录结构如下:my_role/
├── README.md # 角色的说明文档
├── defaults/ # 默认变量
│ └── main.yml
├── files/ # 需要在远程主机上复制的文件
├── handlers/ # 处理器
│ └── main.yml
├── meta/ # 元数据
│ └── main.yml
├── tasks/ # 任务
│ ├── install.yml # 安装软件包
│ ├── config.yml # 配置文件
│ └── service.yml # 启动服务
├── templates/ # 模板文件
├── tests/ # 测试用例
│ ├── inventory # 测试的主机清单
│ └── test.yml # 测试任务
└── vars/ # 变量
└── main.yml
ansible-galaxy init 角色名称
进行创建,下面使用手动创建一个安装和部署 tomcat 的例子,首先基于上面的标准目录结构,来创建一个用于安装 tomcat 的角色目录,代码如下:
#!/bin/bash
# 创建目录结构
mkdir -p roles/tomcat/{tasks,templates,vars,files,defaults,meta}
# 循环创建目录中的文件
for d in `ls roles/tomcat/`
do
touch ${d}/main.yml
done
roles/tomcat/tasks/main.yml
:
- name: 安装Java
yum:
name: "{{ java_package_name }}"
state: present
- name: 下载Tomcat
get_url:
url: "{{ tomcat_download_url }}"
dest: "/opt/apache-tomcat-{{ tomcat_version }}.tar.gz"
- name: 解压Tomcat
unarchive:
src: "/opt/apache-tomcat-{{ tomcat_version }}.tar.gz"
dest: /opt/
remote_src: yes
creates: "/opt/apache-tomcat-{{ tomcat_version }}"
- name: 配置Tomcat
template:
src: tomcat.service.j2
dest: /etc/systemd/system/tomcat.service
- name: 设置Tomcat权限
file:
path: "/opt/apache-tomcat-{{ tomcat_version }}"
mode: 0755
state: directory
recurse: yes
- name: 启动Tomcat
systemd:
name: tomcat
state: started
enabled: yes
- name: 添加8080端口
firewalld:
port: 8080/tcp
permanent: yes
state: enabled
immediate: yes
ansible-doc 模块名称
来查看对应模块的示例。剩下的就是要对 task.yml 文件中每个步骤中所依赖文件,继续编写规则,这里使用到了 systemd 模块,需要依赖 service 文件,所以通过 template 的方式编写一个 systemd 管理配置文件,需要创建一个 /templates/tomcat.service.j2
文件
[Unit]
Description=Tomcat
After=syslog.target network.target
[Service]
Type=forking
Environment=CATALINA_PID=/opt/apache-tomcat-9.0.73/temp/tomcat.pid
Environment=CATALINA_HOME=/opt/apache-tomcat-9.0.73
Environment=CATALINA_BASE=/opt/apache-tomcat-9.0.73
ExecStart=/opt/apache-tomcat-9.0.73/bin/startup.sh
ExecStop=/opt/apache-tomcat-9.0.73/bin/shutdown.sh
Restart=on-failure
[Install]
WantedBy=multi-user.targe
vars
目录中,而 defaults
文件是变量值的默认变量文件,用于定义默认变量值,这些变量的值可以被其他的变量文件覆盖,如果没有设置变量或者修改变量,则使用 defaults
文件中预先定义的变量,defaults 内容如下:
# 默认端口
tomcat_port: 8080
java_package_name: "java-1.8.0-openjdk-devel"
tomcat_version: "9.0.73"
tomcat_download_url: "https://downloads.apache.org/tomcat/tomcat-9/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz"
---
# 在 web 资源上执行 tomcat 角色
- name: 安装 tomcat 角色
hosts: web
roles:
- tomcat
分享 Role
自定义编写的 roles 也可以通过 tar 命令打包成一个 tar 包,上传到互联网上,只要能被正常访问到就能被下载使用,下面为一个例子:
# 打包刚刚自己编写的规则
tar -czvf ansible-tomcat-roles.tar.gz /etc/ansible/roles/tomcat/
# 下载文件
wget https://repo.ibyte.me/ansible/ansible-tomcat-roles.tar.gz
# 解压缩文件
tar -czvf ansible-tomcat-roles.tar.gz -C /etc/ansible/roles/
---
# 指定下载源
- name: 下载 ansible-tomcat-roles.tar.gz
src: https://repo.ibyte.me/ansible/ansible-tomcat-roles.tar.gz
name: ansible-tomcat-roles
最后通过 ansibe-galaxy install -r remote.yml
来安装。