1-关于ansible
介绍
ansible是一款开源的it自动化引擎,你可以把它理解成一个统一的指挥棒,用来管理成百上千台服务器。它的核心目标就是简单和易用,旨在消除重复性工作,简化复杂的工作流程
须知
我会尽可能的记录学习到的功能并添加一些简单案例,不然没有案例我为什么不去看官方文档
本文所有内容运行在Centos7-2009版本
2-安装部署
控制节点
输入以下指令即可安装,这是我在centos的安装,如果涉及其他系统,可以查看官方文档并使用相应的包管理器
sudo yum install epel-release -ysudo yum install ansible -y被管理节点
不需要任何额外软件
配置ssh
ansible使用ssh连接对应的主机,配置ssh连接免密让我们方便些
ssh-keygenssh-copy-id [主机ip]3-定义主机清单
增加主机
Ansible需要知道自己管理的机器有哪些,默认的主机目录文件路径在**/etc/ansible/hosts**,可以直接修改,
例如
127.0.0.1server.text.com这就是一个简单的hosts文件案例,里面包含了一个ip地址和一个域名
增加主机组
可以在使用方括号命名一个主机组并在下面写入主机
[web]127.0.0.1server.text.com这样执行的时候就可以直接使用主机组的名称对多个主机执行指令
ansible会默认为你创建两个父组
all(清单里出现的所有主机、所有分组,在底层都会自动成为 all 组的子成员) ungrouped(清单中没有任何分组的主机)
子父组
在主机名后写入后缀
[web]127.0.0.1server.text.com
[db]127.0.0.2server.text2.com
[dev:children]webdb需要注意的是,只要定义了
参数
可以在主机清单中添加参数,更多的参数具体可以参考官方文档
例如
127.0.0.1 ansible_ssh_user='root' ansible_ssh_pass='000000' ansible_port="2222"这条主机定义还定义了几个参数,分别是 ansible_ssh_user(连接到主机时使用的用户名) ansible_ssh_pass(用于对主机进行身份验证的密码) ansible_port(ssh连接的端口号)
变量
可以在主机清单中定义自定义变量
192.168.1.10 max_connections=100 http_port=80这样在后续对该主机进行操作时,都可以使用 {{max_connections}} 和 {{http_port}} 来使用其中的数据
继承
对于父组,可以使用后缀vars来继承变量和参数,并且可以单独对某一个主机定义变量或参数
[web]127.0.0.1 ansible_port=2222 ansible_ssh_user='deploy' #单独定义server.text.com
[db]127.0.0.2server.text2.com
[dev:children]webdb
[dev:vars]ansible_port=22 #组继承参数ansible_user='root'ansible_ssh_user='root' #组继承变量ansible_ssh_pass='666666'这样做会让所有在dev这个组中的主机继承其中的变量和参数,而我们单独定义的主机 127.0.0.1 则会保持自己单独定义的部分,局部特例永远优先于通用规则
别名
在主机清单中,可以为主机定义别名
[web]web1 ansible_host=127.0.0.1web2 ansible_host=server.text.com
[db]db-a ansible_host=127.0.0.2db-b ansible_host=server.text2.com
[dev:children]web[1:2] #主机范围[1:2]db[a-b] #主机范围[a-f]添加别名后,可以直接输入对于的别名进行使用,还可以使用主机范围,如上面的案例所示,web[1-2]等价于web1与web2,db[a-b]等价于db-a与db-f
还支持自定义步长和数字填充,可以在文档中详细参考,这里不再赘述
别名与变量参数等功能一起使用,能极大方便管理,例如我们可以将同一台机器,定义不同的别名、连接端口或是变量
ad hoc命令
关于该命令
ad hoc命令一般使用在那些很少重复或是需要快速执行的任务
ansible <主机模式> -i <清单文件> -m <模块名> -a "<模块参数>" [选项]下面给出一些常用选项,可以在文档中找到更加详细的选项
- <主机模式>: 在其中输入你想要运行的组或主机
- -i:指定清单文件(你可以创建一个ini文件来定义主机清单并在此使用,否则ansible会使用/etc/ansible/hosts处的主机清单)
- -m:指定要使用的模块(默认 command)
- -a:传递给模块的参数
- -f: 指定要使用的并行进程数(用于重复执行)
模块
ansible命令的关键在于调用对应的模块来干对应的事,对于模块详情可以查询官方文档,记得善用ctrl+f,这里同样只介绍部分简单的模块
shell
ansible dev -m shell -a 'hostname' -o上面这条指令用于获取对应主机的主机名,下面来详细解释
- ansible:这是主命令,告诉系统我要使用即时模式。
- dev:目标主机/主机组。这里调用的就是之前在 /etc/ansible/hosts 里配置的父分组。Ansible 会自动找到里面的子分组
- -m shell:-m 代表 module(模块)。这里指定使用 shell 模块。
- -a ‘hostname’:-a 代表 arguments(参数)。这里输入你想让远程机器执行的真实 Linux 命令。
copy
ansible webservers -m copy -a "src=/root/project/nginx.conf dest=/etc/nginx/nginx.conf owner=root group=root mode=0644" -o- src=/root/project/nginx.conf:Source(源路径)指控制机本地的配置文件路径
- dest=/etc/nginx/nginx.conf:Destination(目标路径)指远程服务器上文件要保存的绝对路径
- owner=root 和 group=root:文件传过去之后,自动把远程机上该文件的所有者和所属组修改为 root
- mode=0644:修改文件权限。0644 代表 root 用户可读写(rw-),其他用户和组只能读取(r—)
yum
ansible dev -m yum -a "name=httpd state=present" -o这条指令会在主机上安装apache,name值指定软件名,state值指定要进行什么操作
- state=present:安装软件。如果机器上没装,就下载安装,如果已经装了,就直接跳过(返回绿色 SUCCESS)
- state=latest:确保是最新版本。如果没装就安装最新版;如果装了但有新版本,它会默默帮你在远程机上运行 yum update 升级到最新
- state=absent:批量卸载/删除软件。相当于远程运行 yum remove 如果机器上本来就没装,它也会直接跳过
service
ansible dev -m service -a "name=httpd state=started enabled=yes" -o这条指令指定了httpd服务为启动状态并开启了开机自启,service有三个参数
name:对其进行操作的服务名称 state:需要服务改变成什么状态 enabled:是否对服务启动开机自启
playbooks
这是ansible的核心功能,playbooks相当于一份自定义蓝图,告诉主机该怎么做,编写过程类似与jenkins和github action那样的自动任务
需要注意的是playbooks使用YAML格式编写,并且执行顺序是由上到下
关于YAML
yaml使用缩进表示层级关系,缩进的空格数不重要,只要相同层级的元素左对齐即可,冒号后面要加空格 在ansible中几乎每个YAML文件都以列表开始,列表中的每个项目都是一组键/值对
这是关于ansible的文章,有关它的详细语法不会赘述
简单使用playbooks
接下来,我们来尝试编写一个简单的playbooks,我们来新建一个yaml文件
---- hosts: web #表示执行此脚本的对象为[web]主机组 remote_user: root #表示使用root用户执行此脚本 tasks: #下方定义了要执行的动作 - name: Install httpd #标注执行的操作,注意一个name里只能包含一个模块调用 yum: name=httpd state=latest #调用yum模块下载名称为httpd的包的latest版本干的事情很简单,就是安装一个Apache,我们来一条条拆解看他都用了哪些东西
- hosts:用于指定运行此脚本的对象
- remote_user:用来指定运行此脚本的用户
- tasks:列表下定义了要执行的任务,可以使用的模块与ad hoc命令一样
- name:用来给人类看的,标注执行的操作,执行时会输出正在执行的操作name,但要注意一个name里只能包含一个模块调用
所以本质上,playbooks就是这样一个个关键字和模块构筑出一个完整的任务,所有有关的关键词依然可以查阅官方文档,下面来按照不同的区块和功能来简单介绍一些其他功能
变量
简单变量
在playbooks中也可以定义变量,相关的文档
可以在playbooks中使用 vars 关键字来定义变量区块,并在其中定义变量
使用变量用两个大括号包起来的变量名即可
---- hosts: web vars: my_name: "shoulong" #在这里定义变量(名字叫 my_name,值叫 shoulong)
tasks: - name: 把变量写入远程文件 copy: content: "Hello, my name is {{ my_name }}!" #在这里用{{ }}使用变量 dest: /tmp/name.txt需要注意的是,变量名只能包含字母数字和下划线,且不能使用py与playbooks的关键词
列表变量
还可以使用yaml列表定义有多个值的变量
var: user_list: - user1 - user2 - user3使用确定元素即可,例如{{user_list[0]}}的值为”user1”
字典变量
还可以定义具有键值的变量
var: user_list: - user1: tom - user2: jreey - user3: leouser_list{{user1}}的值为”tom”
循环
简单循环
playbooks中可以使用循环,可以避免很多重复的任务
---- name: 批量创建业务目录 file: path: "{{ item }}" #以此将下方的loop中的内容送入其中 state: directory loop: #开始排队 - /data/app - /data/logs - /data/configs实际运行中,ansible会自动把loop列表中的内容与{{item}}相匹配,大大方方写
字典列表循环
循环自然也有稍微复杂的用法
---- name: 批量创建不同权限的用户 ansible.builtin.user: name: "{{ item.uname }}" group: "{{ item.ugroup }}" state: present loop: - { uname: 'tom', ugroup: 'developer' } - { uname: 'jerry', ugroup: 'tester' } - { uname: 'shoulong', ugroup: 'admin' }运行时,loop会找到相应的键值一一对应填入其中
条件
可以在playbooks中使用when来进行条件判断,更好的控制任务的运行
- name: 仅在centos系统上安装apache yum: name: httpd state: present when: ansible_distribution == "CentOS" #如果远程机系统确实是CentOS才执行这个任务when语句会在执行时对设置的语句进行判断,若条件通过则执行对应任务;when语句本质上就是py代码,所以你能想到的逻辑运算符、in关键字等都能使用
触发器
在playbooks中使用handlers以使用触发器,触发器的工作旨在指定的任务执行状态为 changed 时,执行计划好的代码,这在一些修改配置重启服务的场景很好用
---- hosts: webservers become: yes
tasks: - name: 步骤 1:修改测试文件的内容 copy: content: "配置文件内容已被修改" dest: /tmp/test_config.txt #文件内容发生变化 notify: click_me_to_run
handlers: - name: click_me_to_run #名字必须和notify完全一致 debug: msg: "检测到文件被改动了Handler执行"触发器在这里的工作是,当name:click_me_to_run对应的notify的任务状态为change时,输出信息 “检测到文件被改动了Handler执行”
运行
运行playbooks很简单
ansible-playbook <你的playbook文件.yml>role
role就像是把playbooks中的一个个任务打包成一个”积木包”,编写好后可以随时拿出来在其他地方复用,还可以针对不同的任务创建不同的变量以适应需求
文件结构
下面列出一个role的文件结构
roles/└── httpd/ # Role 的名字叫 httpd ├── defaults/ # 其中定义了默认变量 —— 优先级最低,最容易被覆盖 ├── vars/ # 其中定义了强变量 —— 优先级高,不容易被覆盖 ├── tasks/ # 核心:要做的每一步动作 ├── handlers/ # 触发器 ├── templates/ # 模板文件:里面有 {{变量}},会动态生成真实文件 ├── files/ # 普通文件:直接拷贝,不做变量替换 └── meta/ # 元信息:作者、依赖关系等(可选)简单示例
下面拿出上篇文章中安装http服务的案例,用role实现,结合之前的知识很容易看懂
defaults/main.yml
httpd_port: 8080student_name: "张三"student_id: "20240001"tasks/main.yml
- name: 安装 httpd 软件包 yum: name=httpd state=present
- name: 复制配置文件模板 template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf notify: restart httpd
- name: 复制首页模板 template: src=index.html.j2 dest=/var/www/html/index.html
- name: 启动并开机自启 service: name=httpd state=started enabled=yeshandlers/main.yml
- name: restart httpd service: name=httpd state=restartedtemplates/httpd.conf.j2 默认配置,无需理解
# 全局环境ServerRoot "/etc/httpd"Listen {{ httpd_port }}User apacheGroup apacheServerAdmin root@localhostServerName localhost:{{ httpd_port }}
# 加载 MPM 模块(通过主配置文件加载,不依赖 conf.modules.d)LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
# 加载其他必需模块LoadModule authz_core_module modules/mod_authz_core.soLoadModule dir_module modules/mod_dir.soLoadModule log_config_module modules/mod_log_config.soLoadModule mime_module modules/mod_mime.soLoadModule unixd_module modules/mod_unixd.so
# 文档根目录DocumentRoot "/var/www/html"<Directory "/var/www/html"> Options Indexes FollowSymLinks AllowOverride None Require all granted</Directory>
# 默认首页<IfModule dir_module> DirectoryIndex index.html</IfModule>
# 日志配置ErrorLog "logs/error_log"CustomLog "logs/access_log" combined
# MPM 配置(prefork 默认值)<IfModule mpm_prefork_module> StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxRequestWorkers 256 MaxConnectionsPerChild 3000</IfModule>templates/index.html.j2
<h1>Welcome</h1><p>Name: {{ student_name }}</p><p>ID: {{ student_id }}</p>添加完以上文件后,这个role的文件结构应该是这样
roles/└── httpd/ ├── defaults/ │ └── main.yml ├── vars/ │ └── main.yml ├── tasks/ │ └── main.yml ├── handlers/ │ └── main.yml ├── templates/ │ ├── httpd.conf.j2 │ └── index.html.j2 └── meta/如果调用这个role? 新疆一个playbook文件 text.yml
---- name: 用role装httpd hosts: webservers #对其进行操作的组 become: yes
roles: - httpd #这里就是调用 roles/httpd运行指令
ansible-playbook text.yml这样就完成了role的编写与使用
一些需要注意的事
可以看到,通过roles,playbooks中的调用变得及其简单,需要注意的是,ansible一般会优先在playbooks文件的同级目录中寻找role文件夹,如果你看懂了他的文件结构,就会发现他其实本质就是把一个playbooks的不同区块拆成了不同的文件分开编写,
关于变量优先级
在ansible中,有很多地方都可以定义变量,为防止冲突,内部指定了很严格的变量优先级,这点在role文件结构中的 defaults 和 vars 中也能看出来
| 优先级 | 变量定义位置 | 核心角色 |
|---|---|---|
| 1(最低) | roles/x/defaults/main.yml | 默认炮灰。专门用来给 Role 兜底的默认值,极易被外界覆盖。 |
| 2 | Playbook 中的 vars: 区块 | 普通全局。你在主剧本最上面写死的变量。 |
| 3 | roles/x/vars/main.yml | 角色内定。Role 内部雷打不动的私有变量,一般不希望外界修改 |
| 4 | Playbook 调用 Role 时传参 | 精准打击。在主剧本召唤角色时,当场传进去的临时变量 |
| 5 | 命令行 -e 参数 | 能强行覆盖以上所有人 |
举个例子,在nginx的roles内部,有两个地方能写变量
roles/nginx/defaults/main.yml nginx_port: 80
roles/nginx/vars/main.yml nginx_port: 8080
如果直接运行这个 Role,最终生效的是 8080
那如果我们创建一个playbooks调用它并定义变量呢
---- hosts: web vars: nginx_port: 9000 #在Playbook里定义了9000 roles: - nginx它干不过role的 vars/main.yml 最终依然是 8080 生效
如果针对rolea传参呢
- hosts: web roles: - { role: nginx, nginx_port: 9999 }自然是9999赢了
关于Jinja2
在role中,可以注意到有个文件夹 templates ,里面存储了模板文件,ansibla所使用的引擎就是Jinja2,所以它的后缀是.j2
它十分强大,可以拼接、获取变量或是进行计算和if判断
具体相关知识你可以查阅有关文档
如果只是简单使用,你只需要知道你可以在其中使用{{变量名}}即可
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时










