Ansible 上手指南 2.png
读一本书最好的时机是什么时候?是你刚买的时候,趁着新鲜劲,先了解这本书,继而马上阅读完这本书。如果错过了最好的时机阅读一本书,那什么时候是合适的时机,是你需要这方面的资料或者知识的时候。
最近我一直在研究 Ansible 自动化运维工具。入手请参考 Ansible 上手指南。主要是为了实现自己在远程主机进行相关操作的任务,以此为切入点进行学习。
在实现了自己的任务后,我准备继续研究下 Ansible 的其他用法。下面就是我的个人总结。
体会:
- 文档内容很多,不可能全部掌握,使用 2/8 法则,看看同事们口中的主要内容有哪些
- 实践型的内容,还是需要自己写写代码,运行,看看效果。当你跑通了你自己写的代码,有可能你就学会了这个知识点
- 快速入手的方法是:如果你急切需要解决问题,那就问有经验的人。如果你不是很急,先尝试了解下内容,不懂再问别人。反复提出你的疑问。
- 提问题不要觉得不好意思,时间很珍贵的。
下面的总结主要以例子为主:
- 第一稿:抛弃所有参考资料,凭借实践的经验总结
- 第二稿:弥补不明白的地方
- 第三稿:对着参考文献修正不对的地方,发布
大纲:
- ansible.cfg 配置文件
- 变量
- 判断
- 循环
- 常用模块
- debug
- copy
- file
- shell
- command
- stat
- unarchive
- ping
- template
- user
- group
- service
- get_url
- include
- role
- 目录结构
- 依赖
- 执行顺序
Ansible 是使用Python 编写,基于模块化的工作方式,真正处理动作都是这些模块。Ansible 提供整体的框架。
1、安装
代码语言:javascript复制pip install ansible
或者根据Linux 的版本进行
代码语言:javascript复制apt-get install ansible
管理主机要求是Linux, 节点主机需要带 Python 及相应的库。
一般是管理主机和节点主机都是Linux。
2、基本使用
命令行方式运行:
命令行方式适合操作简单的动作。
代码语言:javascript复制ansible all -m ping
playbook 运行:
playbook 适合复杂的处理动作。
代码语言:javascript复制ansible-playbook example.yml
查看支持的模块:
代码语言:javascript复制ansible-doc -l
查看某模块用法及支持的参数:
代码语言:javascript复制ansible-doc -s ping
3、配置文件
安装完成之后,默认目录下有两个文件:
代码语言:javascript复制/etc/ansible/hosts
/etc/ansible/ansible.cfg
hosts 文件定义节点主机的IP 及以下配置信息。
ansible.cfg 文件定义全局配置文件,配置项很多, 一般默认就可以完成你的日常任务了。
但是除默认的生成的 ansible.cfg 文件之外,你可以在自己的项目中创建这样一个同名ansible.cfg 文件。
ansible 读取配置文件的顺序是:
- ANSIBLE_CONFG 环境变量中定义
- ansible.cfg 当前目录
- */ansible.cfg 当前用户home/username/ansible.cfg
- /etc/ansible/ansible.cfg 默认生成的文件路径
怎么理解这个读取配置文件的信息呢?
举个例子:
代码语言:javascript复制# 当前项目结构
ansible:
---playbooks
---example
---leanr-ansible/example.yml
---ansible.cfg
你在这个目录下执行:ansible 命令
- 那么首先查看环境变量有没有设置,没有那就读取当前目录下的ansible.cfg 配置信息;
- 如果当前目录没有设置,那么就搜索home 目录下有没有配置信息。
- 否则读取默认生成的配置信息。
4、常用模块
操作动作举例
这里为举例只明白这些模块的使用场景,故意执行多步操作。
- 拷贝管理节点
/home/ubuntu/zartclient
下的zartcli
和zartcli.ini
文件至节点主机/home/ubuntu/zartclient
下 - 在上一步的基础之上,拷贝
/home/ubuntu/zartclient/zartcli
至/usr/bin/zartcli
- 在节点主机上使用刚才拷贝的文件,执行下载命令:
zartcli -o=download -i=admin -m=bin -n=op-cli -v=v0.0.3 -p=/home/ubuntu/download
下载至/home/ubuntu/download
目录 - 在节点主机上解压下载的文件至
/paasdata/data
目录下 - 在节点主机上拷贝解压之后的文件至 /etc/opcli 目录下
- 在节点主机上拷贝
op-cli
至/usr/bin
- 在节点主机上
op-cli task list
执行命令 - 在节点主机上
op-cli node list
执行命令
快阅读时代,估计没人想认真看这些动作。画个流程图吧。
示意图
把每一步动作认为是一个任务 task.
第一步:先检查是否存在对应的文件,是则拷贝
模块:stat, copy, debug, file
- 判断远端节点主机是否存在对应的文件夹
- 不存在则创建
- 拷贝本地文件至远程节点主机的对应的目录下
- name: is /home/ubuntu/zartclient exists
stat:
path: /home/ubuntu/zartclient
register: result
- name: show result
debug:
msg: "{{result}}"
- name: create dest path in remote
file:
path: "{{ dest-path }}"
state: direstory
with_items:
- "/home/ubuntu/zartclient"
when: not result.stat.exists
- name: copy file to remote
copy:
src: "{{item.src}}"
dest: "{{item.dest}}"
with_items:
- { src: "/home/ubuntu/zartclient/zartcli" dest: "/home/ubuntu/zartclient"}
- { src: "/home/ubuntu/zartclient/zartcli.ini" dest: "/home/ubuntu/zartclient"}
- name: copy zartcli into /usr/bin
copy:
src: "{{item.src}}"
dest: "{{item.dest}}"
with_items:
- { src: "/home/ubuntu/zartclient/zartcli", dest: "/usr/bin"}
可以看出,处理相应的动作,有相应的模块,模块带参数,完成任务。
{{ }}
里面表示变量- with_items: 是一个列表,表示循环获取变量
- register : 表示把执行的动作结果赋值给一个变量,是一个map, 可以根据键值,获取内容
- when: 表示判断, 根据结果的布尔值进行操作
第二步:先查询是否存在文件,存在则执行下载命令
第一步拷贝的文件是一个客户端,主要是对文件的上传、下载、查询等。
模块:shell 、 command
代码语言:javascript复制- name: is /home/ubuntu/download exists
stat:
path: "{{item}}"
with_items:
- "/home/ubunt/download"
register: result
- name: create /home/ubuntu/download
file:
path: "{{item}}"
state: direstroy
with_items:
- "home/ubuntu/download"
when: not result.stat.exists
- name: query op-cli
command: "{{tools}} -o={{operate}} -i={{tenants}} -m={{type}} -n={{name}}
-v={{version}}"
vars:
tools: zartcli
operate: query
tenants: admin
type: bin
name: op-cli
version: v0.0.3
register: result
- name: download op-cli
command: "{{tools}} -o={{operate}} -i={{tenants}} -m={{type}} -n={{name}}
-v={{version}} -p={{path}}"
vars:
tools: zartcli
operate: query
tenants: admin
type: bin
name: op-cli
version: v0.0.3
path: /home/ubuntu/download
- item 是个关键字,表示变量
- item0 可以表示变量是一个列表,这里表示列表的第一个值
- item.src 可以表示变量是一个map, 这里表示map的src 的值
- vars 表示模块中可以填充自定义的变量名称对应的值
第三步:解压下载的文件
第二步下载的文件内是一个 tar 包,需要将其解压至指定目录
模块:unarchive, file
代码语言:javascript复制- name: create /paasdata/data
file:
path: "/paasdata/data"
state: direstory
- name: untar file
unarchive:
src: "{{src-path}}"
dest: "{{dest-path}}"
remote_src: yes
vars:
src-path: "/home/ubuntu/download/admin_op_cli_v0.0.3/op-cli.tar.gz"
dest-path: "/paasdata/data"
- yes 和 true 没区别
- remote_src: yes 表示解压的文件在节点主机上操作
第四步:拷贝文件
代码语言:javascript复制- name: create /etc/opcli
file:
path: "/etc/opcli"
state: directory
- name: copy /paadata/data/conf||op-cli
copy:
src: "{{item.src}}"
dest: "{{item.dest}}"
with_items:
- { src: "/paasdata/data/conf/common.yml" dest: "/etc/opcli/conf"}
- { src: "/paasdata/data/op-cli" dest: "/usr/bin"}
第五步:执行查询命令
代码语言:javascript复制- name: op-cli task list || op-cli node list
command: "{{item[0]}} {{item[1]}} list"
with_nested:
- [ "op-cli" ]
- ["task", "node", "nodepool"]
- with_nested: 嵌套循环
上文相当于:op-cli task list, op-cli node list, op-cli nodepool list
上文借用一些循环,把之前规划的 八 个动作改成了 五个动作, 即 五 个动作内的每一个又包含几个动作。
新手容易把上面的所有的动作都处理在一个文件内:但是其实存在更好的处理方法。
代码语言:javascript复制# main.yml
---
- hosts: 10.62.60.21
romote_user: root
tasks:
- name: one # 第一步
- name: two # 第二步
- name: three # 第三步
- name: four # 第四步
- name: five # 第五步
- name: six # 第六步
...
5、 include_tasks
假如你希望写的task 能够进行复用,就相当于编程领域里的抽象出函数一样。ansible 提供这样的机制。即将上文一个很大的文件拆分成独立的文件。使用 include_tasks 方法将文件导入。
比如:上文的五步动作分别单独写文件
- zartclient.yml
- download.yml
- untarfile.yml
- copyfile.yml
- executecommand.yml
playbook 入口: mail.yml
则main.yml 如下:
代码语言:javascript复制---
- hosts: 10.62.60.21
root_usr: root
tasks:
- include_tasks: zartclient.yml
- include_tasks: download.yml
- include_tasks: untarfile.yml
- include_tasks: copyfile.yml
- include_tasks: executecommand.yml
即将各个处理动作独立出去。随时复用。
6、roles
看上去 include_tasks 方式就是对ansible-playbook 的一种比较好的组织方式了。
ansible 提供了一种更好的组织方式:roles
Roles 基于一个已知的文件结构,去自动的加载某些 vars_files,tasks 以及 handlers。基于 roles 对内容进行分组,使得我们可以容易地与其他用户分享 roles。 这样的组织方式使得复用更为简便。每个相对独立的处理动作独立出来,可以适用于更复杂的场景。
一个role 的文件大概包括这些:
- tasks 文件:主要编写某个独立模块的处理动作
- handlers 文件:
- vars 文件: 主要编写某个独立模块的变量
- meta 文件:主要编写依赖关系,即一个独立模块引用另一个role
- defaults 文件:默认的变量文件
- templates 文件: 模板文件
- files 文件
注意文件夹下可以有多个后缀名为yml
的文件,但一定要有main.yml
文件
上文的处理动作,使用 roles 重新组织如下:
目录:
代码语言:javascript复制├─ansible
│ ├─playbook
│ └─roles
│ ├─download-bin
│ │ ├─tasks
│ │ └─vars
│ └─op-cli
│ ├─meta
│ ├─tasks
│ └─vars
这里以上文的处理动作:下载文件独立成一个 download-bin role 为例,讲述如何编写 role.
tasks/deploy.yml
代码语言:javascript复制- name: download bin by zartcli
command: "{{zartcli_cli_path}}/zartcli -o=download -i={{tenant_id}} -m={{bin_type}}
-n={{bin_name}} -v={{bin_version}} -p={{download_path}}"
tasks/main.yml
代码语言:javascript复制---
- include_tasks: download.yml
vars:
zartcli_cli_path: "{{zartcli_clipath}}"
tenant_id: "{{tenantid}}"
bin_name: "{{binname}}"
bin_version: "{{binversion}}"
download_path: "{{downloadpath}}"
bin_type: "{{bintype}}"
vars/main.yml
代码语言:javascript复制---
zartcli_clipath: "/home/cloud/zartclient"
tenantid: "admin"
binname: "op-cli"
binversion: "v0.0.3"
downloadpath: "/home/cloud/zartclidownload"
bintype: "bin"
task/main.yml 是这个role 的入口。导入download.yml,下载所需要的变量全部置放在 var/main.yml 文件中。
别的role 需要复用这个模块:
op-cli/meta/main.yml
代码语言:javascript复制dependencies:
- role: download-bin
这样 op-cli role 也有download 动作。
roles 适用于组织较为复杂的项目,将各个模块独立处理,又可以相互复用。使用 include_tasks 存在模块的复用性变低、变量需要重复定义等缺点。
上文 op-cli role 复用download-bin,无需再次定义变量。
再一个值得注意的是:一般playbook 的执行顺序和task 的定义顺序一致。
使用 roles 后,playbook 中 roles 先执行,再按tasks 的定义顺序执行。
如果需要最先执行某个task , 或者最后执行某个task, 可以使用pre_tasks
和 post_tasks
。
pre_tasks
最先执行post-tasks
最后执行
再一个使用roles 需要配置 ansible.cfg
代码语言:javascript复制roles_path = 目录
# 即roles 所在的文件目录
7、参考文献
- 官方文档
这些原理性的东西,用文字不是很好讲,以后遇到文字难以表达出来,我录个视频实际操作下。这样读者可能更好理解。