如果是一名服务端程序员,开发最多程序应该为服务器端应用程序,而服务器端端应用程序大部分部署在 Linux 操作系统之上,在 Linux 上部署应用和管理 Linux 系统大部分都是使用 SSH 端方式管理。这篇文章要介绍的是 Ansible 自动化运行工具,它可以帮助我们自动化各种 IT 任务,从部署应用程序和服务到协调网络设备和云资源的管理,个人感觉相比 K8s 的设计 Ansible 更贴合的是物理机器设计的管理软件。

Ansible 是基于代理的,不需要在被管理的节点上安装客户端直接使用 SSH 协议进行通信,而 Kubernetes 是基于主从节点的,需要在被管理的节点上安装 Kubernetes Agent ,Ansible 主要用于管理配置和协调各种 IT 任务,包括应用程序部署、系统配置、网络管理等,Kubernetes 是一种容器编排平台,用于自动化和管理容器化应用程序的部署、伸缩、故障恢复和负载均衡。

上图为 ansible 的软件架构图。


安装 Ansible

Ansible 目前支持类 RHEL 发行版本系列系统,大部分默认的系统镜像软件默认是不带有 ansible 服务程序,需要自己手动安装,需要从 Extra Packages for Enterprise Linux 扩展软件仓库中获取,首先要配置 epel 软件仓库最新的地址,执行下面命令:

sudo yum install -y epel-release
# 安装 ansible 服务软件
yum install -y ansible
# 检查是否成功安装
ansible --version

通过上面 2 条命令即可安装 Ansible 服务程序,其他受管理的节点不需要安装 Ansible ,但是依赖于 OpenSSH 程序,集群中的其他节点必须提前安装好 OpenSSH 和 Python3 运行环境并且对外提供 SSH 连接服务,目前 Ansible 可以支持千台机器的管理和运维支持,默认的安装配置文件位于在 /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

由于管理的机器资源比较多,不可能全部手动填写和配置,ansible 为了方便管理提供自定义资源方式,通过名为 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 配置文件中支持的参数变量列表有:

变量作用
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添加计划任务信息

例如使用 firewalld 模块为主机设置防火墙规则,在 ansible 中使用 firewalld 模块,可以方便地管理 Linux 上的防火墙规则:

# 添加端口到 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 文件

上面都是介绍的基于命令行下的多节点管理和控制,默认这种情况针对简单需求是可以满足,如果多条 shell 命令和更复杂的操作默认的 Ad-hoc 模式是满足不了,ansible 另外一种模式为 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

执行 playbook 之需要使用 ansible-playbook 文件路径.yaml 即可,如果 playbook 任务执行成功会有 okchange 字段信息提示。


Role 规则

在 ansible 通过角色功能,这里角色是指定的是一些功能可以复用,单独抽象成为一个角色类模块化,简单来说就把一些常用的功能模块化在需要使用的时候可以加载,类似于编程中函数封装实现类似,分装成为可复用的对象。

目前 ansible 角色获取有 3 种方式为:加载系统内置角色 、外部环境获取例如使用 Ansible Galaxy 、自定义创建角色,不同的角色能做的事情就不一样,通过角色名称来分类处理任务。在使用 playbook 编写复杂的任务时,会出现很大变量和一些配置规则导致整个 playbook 文件过于复杂,角色存在的目的就是要将 playbook 中的 Variables Handlerstasks 单独抽象出来,达到组建之间复用的设计,一个完整的角色目录结构如下:

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

首先我们要创建一个安装任务,以往使用的 playbook 文件任务和变量全部写到一个文件中,而现在使用的自定义角色方式,执行任务单独写入到 task.yml 中,打开文件 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

上面使用了多个模块:get_url 下载网络文件,unarchive 压缩文件处理模块,template 模版文件模块,file 文件模块,systemd 系统进程管理模块,firewalld 防火墙模块,如果不知道如何编写可以使用 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

剩下的 vars 中的变量是根据目前需求和运行情况所要设置的变量,可以更改的变量,常规变量文件,用于定义在角色或任务中使用的常规变量,vars 文件中定义的变量值将覆盖 defaults 文件中的值,例如定义 tomcat 下载地址和 jdk 版本信息:

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"

最为关键的一步如何在指定的资源上执行对应的角色任务,还是通过 playbook 的方式,只不过要编写一个 playbook 入口文件,例如:

---
# 在 web 资源上执行 tomcat 角色
-  name: 安装 tomcat 角色
   hosts: web
   roles:
       - tomcat

最后在对应的资源上执行对应角色完成对应自动化任务,下面为 tomcat 角色执行结果。

自己编写角色很麻烦,并且日常我们开发使用运维的常用的软件也就那么几个软件,所以官方有一个叫 Ansible Galaxy 站点允许开发者上传自己的角色模块,方便其他下载复用,类似于 Java 中的 Maven,wasmer 的 wapm.io 的一样,可以复用别人的编写角色,如下为 mysql 角色:

分享 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/

以上是手动下载解压的方式,放到对应目录中和本地角色使用没有如何区别,但是比较繁琐,可以直接在 remote.yml 中直接编写远程地址,ansible 自动下载和执行任务,如下:

---

# 指定下载源
- 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 来安装。


其他资料

便宜 VPS vultr
最后修改:2023 年 07 月 05 日
如果觉得我的文章对你有用,请随意赞赏 🌹 谢谢 !