ITPub博客

首页 > 数据库 > 数据库开发技术 > 干货篇 | 一文带你了解Ansible(下)

干货篇 | 一文带你了解Ansible(下)

原创 数据库开发技术 作者:咸鱼运维杂谈 时间:2021-08-18 21:30:00 0 删除 编辑
点击上方"蓝字"
关注我们吧!


在干货篇 | 一文带你了解Ansible(上)中,我们了解了什么是ansible、 ansible 的原理以及一些常用的 ansible 命令和 ansible 模块


但是这在一些实际场景上还是不能算是最优解决方案,例如以下场景

一天,老板让你用 ansible 给多台主机部署http服务,往往在部署httpd服务的时候我们需要下载httpd包,还要根据每台主机的实际情况(CPU、内存等)来进行不同的配置,除此之外还要开启我们的httpd服务等等一系列操作


按照上篇文章所讲的内容,我们是可以使用 ansible 来进行一键部署的,但是需要我们根据不同的操作(创建用户、修改配置、下载管理包等)来使用不同的 ansible 模块(user、file、yum等),这样一来,也是比较繁琐的,不算是比较好的解决方案


那么我们有什么方法将上面这些操作只需要一个命令就能都进行而不是我们一条一条的敲呢?


答案是:有!


接下来我将介绍 ansible 中的一大神器——playbook。翻译成中文就是剧本



01
playbook


playbook字面意思,即剧本,现实中由演员按照剧本表演,在Ansible中,这次由计算机进行表演,由计算机安装、部署应用,提供对外服务,以及组织计算机处理各种各样的事情


通俗点来讲,playbook是由一个或多个“play”组成的列表。而“play”就相当于执行的每条 ansible 命令,每条 ansible 命令组成起来成一个playbook,来实现复杂的任务


我们也可以理解为脚本,这个脚本包括 ansible 单条命令的集合


playbook执行过程


1.将以编排好的任务集(ansible 单条命令集合)写进playbook

2.通过ansible-playbook命令分拆任务集逐条执行ansible命令,按预定规则逐条执行



其次,playbook 采用 YAML 语言编写,看到这里可能有些读者不知道什么是 YAML 语言,这里我简单介绍一下


更多的内容及规范参见http://www.yaml.org


YAML语言


1.YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言,包括: XML、C语言、Python、 Perl以及电子邮件格式RFC2822等。Clark Evans在2001年在首次发表了这种语言,另外Ingy dot Net与Oren Ben-Kiki也是这语言的共同设计者YAML Ain't Markup Language ,即YAML不是XML。不过,在开发的这种语言时, YAML的意思其实是: "Yet Another Markup Language" (仍是一种标记语言)

2.特性:

    可读性好

    和脚本语言的交互性好

    使用实现语言的数据类型

    有一致的信息模型

    易于实现

    可以基于流来处理

    表达能力强,扩展性好


我们先来看一下 playbook 的基本格式


[root@ansible ansible]# vim hello.yml  ---  - hosts:webservers    remote_user: root  #指定远程登录用户
tasks: - name: hello       command: hostname


我们可以看到,palybook 由于是 YAML 语言编写的,所以后缀名是 .yml 或者 .yaml


其次,开头我们一般会用连续三个连字号(-)区分不同内容,而 hosts 是执行的远程主机列表,tasks 就是我们的任务列表,在tasks里面一个name对应一个task,command也就是 ansible 单个模块


02
核心要素


在playbook中,有这几大核心要素


  1. hosts:主机列表

  2. tasks:任务

  3. variables:变量

  4. templates:模板

  5. handlers和notity:触发器

  6. tags:标签


hosts


即我们要执行的远程主机列表,我们在上一篇文章中介绍过主机列表(主机清单)


playbook 中的每一个task都的目的都是为了让某个或者某些主机以指定的某个用户来执行


所以说我们在写 playbook 时首先要先告知是哪些主机来执行task(执行task的主机要先在主机清单里定义好)


我们的主机清单存储路径在 /etc/ansible/hosts 下


首先定义好我们的主机清单,添加 webserver 主机组


[root@ansible ansible]# vim /etc/ansible/hosts  [webserver1]  www.salted1.com  www.salted2.com  192.168.0.1


之后我们在 playbook 中指出是哪些主机要执行


[root@ansible ansible]# vim test1.yml----hosts:webserver1 remote_user:root  #指定在远程主机上执行task的用户为root


tasks


任务列表。在任务列表下面有许多个任务,每个任务只能对应一个模块,执行顺序为由上到下


例如我想让主机清单里的 webserver 主机组以root身份创建一个新的文件 newfile,并且添加一个系统用户user1,其shell为/sbin/nologin


[root@ansible ansible]# vim test.yml---- hosts: webserver  remote_user: root  # 定义任务列表  tasks:    - name: create a newfile      file: name=/data/newfile state=touch    - name: create a user      user: name=user1 shell=/sbin/nologin system=yes


之后我们运行即可


# 可以先用 -C 参数先测试ansible-playbook -C test.yml
# 发现没有报错,执行ansible-playbook test.yml


variables


为了使得我们的 playbook 更加灵活,playbook 里面引用了变量的概念


变量名规范:仅由字母,数字和下划线组成,并且只能以数字开头


变量来源:

  • 远程主机上的变量(可以直接调用)

  • 在主机列表中定义变量

    • 普通变量:主机组中的主机单独定义

    • 公共变量:也称组变量,针对主机组里面的主机统一定义

  • 在playbook中定义并通过命令行赋值

    • ansible-playbook -e varname=value



调用变量:需要在变量名外面加上花括号,例:{{ varname }}


存放变量的文件


为了便于维护定义的变量,我们可以将常用的变量写进一个文件里面


[root@ansible ansible]# vim vars.ymlvar1: httpdvar2: ospfd


当我们编写 palybook 需要使用文件里的变量时,可以直接调用


[root@ansible ansible]# vim test.yml--- - hosts: webserver   remote_user: root   var_files:     - vars.yml        tasks:     - name: install httpd       yum: name={{ var1 }}     - name:install ospfd       yum:name={{ var2 }}


远程主机变量


当我们运行 playbook 时,默认都会运行一个名为 “Gathering Facts” 的任务,ansible 通过这个默认任务去搜集远程主机的相关信息(例如远程主机的ip地址、主机名、系统版本、硬件配置等信息),这些相关信息会保存在对应的变量中,当我们想要使用这些信息时,可以通过获取相应的变量来使用这些信息


如果想要查看远程主机的相关的变量,我们可以使用 setup 模块


[root@ansible ansible]# ansible 192.168.10.2 -m setup192.168.10.2 | SUCCESS => {    "ansible_facts": {        "ansible_all_ipv4_addresses": [            "192.168.10.2",            "192.168.122.1"        ],        "ansible_all_ipv6_addresses": [            "fe80::1f2f:cc8b:e62b:863b"        ],        "ansible_apparmor": {            "status": "disabled"        },        "ansible_architecture": "x86_64",        "ansible_bios_date": "05/19/2017",        "ansible_bios_version": "6.00",        "ansible_cmdline": {            "BOOT_IMAGE": "/vmlinuz-3.10.0-957.27.2.el7.x86_64",            "LANG": "zh_CN.UTF-8",            "crashkernel": "auto",            "quiet": true,            "rd.lvm.lv": "centos/swap",            "rhgb": true,            "ro": true,            "root": "/dev/mapper/centos-root"        } ......................


我们可以看到,返回的信息上是json格式的字符串


我们也可以通过过滤工具 filter ,这样就不用一下子获取这么多变量,去获取我们想要的变量


例如我们想要获取远程主机的 hostname 


ansible webserver -m setup -a "filter=ansible_hostname"


或者我们想要获得远程主机的主机名


ansible all -m setup -a "filter=ansible_fqdn"


playbook中定义的变量


我们还可以在 playbook 中定义我们的变量,再通过命令将我们定义好的变量进行赋值,好处就是灵活性更高


设置单个变量


[root@ansible ansible]# vim test.yml---- hosts: werbserver  remote_user: root
tasks: - name: install package yum: name={{ varname }} - name: start service service: name={{ varname }} state=started
# 在命令行里通过 -e 参数 给变量赋值anisble-palybook webserver -e 'varname=httpd' test.yml


设置多个变量


[root@ansible ansible]# vim test.yml---- hosts: webserver  remote_user: root
tasks: - name: install package yum: name={{ varname1 }} - name: install package yum: name={{ varname2 }}
# 在命令行里通过 -e 参数 给变量赋值anisble-palybook webserver -e 'varname1=httpd varname2=ospfd' test.yml


设置变量组


[root@ansible ansible]# vim test.yml---- hosts: werbserver  remote_user: root
vars: - varname1: httpd - varname2: vsftpd
tasks: - name: install package yum: name={{ varname1 }} - name: install package yum: name={{ varname2 }}


普通变量和公共变量


这类变量一般在主机清单里面定义


普通变量:仅针对主机组中的单个主机单独定义

公共变量:也称组变量。对主机组中的所有主机统一定义


我们先来看下普通变量


[root@ansible ansible]# vim /etc/ansible/hosts[webserver]192.168.244.100 http_port=81192.168.244.101 http_port=82


编写playbook


[root@ansible ansible]# vim test.yml---- hosts:webserver  remote_user: root
tasks: - name: set hostname hostname: name=www{{ http_port }}.com


这里我们可以看到,不同的主机定义的 http_port 是不一样的,所以在执行 playbook 之后每台主机的 hostname 也不一样


接下来我们设置公共变量


[webserver]192.168.244.100 192.168.244.101 [werserver:vars]nodenmae=wwwdomainname=edu.com


编写 playbook


[root@ansible ansible]# vim test.yml---- hosts:webserver  remote_user: root
tasks: - name: set hostname hostname: name={{ nodename }}.{{ domainname }}


因为是公共变量,所以该主机组内的所有主机的 hostname 都是一样的


template 模板



template是一种使用 jinjia2 语言格式作为文本文件模板,我们可以通过编写 template 来替换文件中的一些变量并实现一些简单逻辑


下面我们通过两个个例子来让大家对template 模板有一个初步印象



例一

在nginx的配置文件中,产生的worker进程数默认是自动的,nginx会根据当前主机的cpu个数来产生相应的worker进程。

真实情况下我们的远程主机的硬件配置跟ansible服务器不是完全相同的,ansible服务器可能只有一个CPU,而有的主机可能有2个CPU,有的可能有4个CPU

如果我们单纯地用copy模块将ansible服务器的nginx配置文件传送到不同的主机下,往往就会导致worker进程数跟主机的CPU个数不对应


这时候我们就可以通过 template 模块来根据不同主机的CPU数量产生不同的worker进程数


首先我们创建一个 名为template的目录(建议与 playbook 目录为同一层级)


[root@ansible ansible]# mkdir templates[root@ansible ansible]# lsansible.cfg  templates  test_template.yml


新建一个template 模板 nginx.conf.j2,注意后缀名为 .j2,并且将 nginx 配置文件复制到 template 模板下


cp /etc/nginx/nginx.conf templates/nginx.conf.j2


然后编写我们的playbook


[root@ansible ansible]# cat test_template.yml---- hosts: webserver  remote_user: root
tasks: - name: install package yum: name=nginx - name: copy template template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf notify: restart service - name: start service      service: name=nginx state=started enabled=yes


注意:ansible服务器下的 template 模板后缀名有 .j2,传送到远程主机后应把 .j2后缀名给去掉


这样我们就能实现根据不同远程主机的 worker 进程数跟远程主机的CPU数量所对应


例二

使用template模板使得都是 nginx 服务,但不同远程主机的nginx 端口不一样


首先我们定义普通变量,使得不同远程主机的端口号不一样


[webserver]192.168.244.141 http_port=8080192.168.244.135 http_port=9090


将 nginx 配置文件复制到template模板下,并将监听的端口修改为在主机清单里定义的变量


[root@ansible ansible]# vim templates/nginx.conf.j2 ....   server {        listen       {{ http_port }} default_server;        listen       [::]:{{ http_port }} default_server;
....


这样在192.168.244.141主机下的监听端口为8080,而在另一台主机下的监听端口为9090


handlers和notify 触发器


我们通过handlers和notify结合使用来实现触发器操作


handlers:在task列表中,只有当关注的资源发生变化时,才会采取相应的操作

notify:与handlers配套使用。调用handlers中的定义的操作;用于每个play的最后被触发,这样就可以避免多次发生改变时每次都会执行notify的操作。仅在所有的变化发生完成后一次性地执行指定操作


下面我们结合一个案例来进行讲解


案例

一般情况下,当修改了 httpd 的配置文件后我们需要重启httpd服务才会使得配置文件生效,这时候我们就可以通过触发器来实现一旦发现配置文件有修改就重启服务的操作


下面编写playbook


需求:在远程主机上安装httpd服务并将ansible主机上的配置文件复制过去,当被控机收到传送过来的配置文件后就重启服务使得配置文件生效


[root@ansible ~]# vim httpd.yml---- hosts: webserver  remote_user: root
tasks: - name: install httpd package yum: name=httpd - name: copy config file file: src=/etc/httpd/httpd.conf dest=/etc/httpd/ backup=yes notify: restart service - name: service start service: name=httpd state=started enabled=yes
handlers: - name: restart service service: name=httpd state=restarted


我们还可以编写多个触发器


当ansible主机的配置文件传送到远程主机上后,就会触发重启服务和检查nginx进程这两个操作

[root@ansible ~]# vim httpd.yml---- host: webserver  remote_user: root
tasks: - name: add group nginx      group: name=nginx  - name: add user nginx user: name=nginx group=nginx - name: install nginx yum: name=nginx enabled=yes - name: copy conf file: src=/root/conf.txt dest=/etc/nginx/nginx.conf notify: - restart nginx - check nginx process handlers: - name: restart nginx service: name=nginx state=restarted - name: check nginx process shell: killall -0 nginx > /tmp/nginx.log


tags标签


使用 tag 标签可以让我们执行 playbook 里面指定的内容,无需按照从上到下的顺序依次执行


比如说我们编写一个部署 httpd 的 playbook,并且后期想跳过安装,文件配置等操作直接开启服务


[root@ansible ~]# vim tags.yml---- hosts: webserver  remote_user: root
tasks: - name: install httpd package      yum: name=httpd - name: copy config file      flie: src=/etc/httpd/httpd.conf dest=/etc/httpd/httpd.conf - name: start service service: name=httpd state=started enabled=yes      tags: httpdservice


编写后在命令行里面使用 -t 参数来实现标签操作


ansible-palybook -t httpdservice tags.yml


多个动作也可以使用同一标签


[root@ansible ~]# vim tags.yml---- hosts: webserver  remote_user: root
tasks: - name: install httpd package yum: name=httpd tags: httpdservice - name: copy config file      flie: src=/etc/httpd/httpd.conf dest=/etc/httpd/httpd.conf - name: start service service: name=httpd state=started enabled=yes      tags: httpdservice


命令行里指定标签执行


ansible-palybook -t httpdservice httpd.yml


以上就是 ansible playbook 的核心要素了


除此之外,我们还可以在 playbook 中使用一些编程语言上的条件控制语句来实现一些简单的逻辑


03
逻辑实现


when


根据某一判断条件(变量、执行结果等)来实现逻辑


[root@ansible ~]# vim when.yml---- hosts: webserver  remote_user: root  tasks:    - name: shutdown RedHat flavored systems      conmand: /sbin/shutdown -h now      when: ansible_ os_ family == "RedHat"


迭代


1.当有需要重复性执行的任务时,可以使用迭代机制

2.对迭代项的引用,固定变量名为{{ item }}

3.要在task中使用with_ items给定要迭代的元素列表


利用迭代创建多个文件


[root@ansible ansible]# vim test_item.yml---- host: webserver  remote_user: root
tasks: - name: create some files file: name=/data/{{ item }} state=touch with_items: - file1 - file2 - file3


利用迭代安装多个包


[root@ansible ansible]# vim test_item.yml---- host: webserver  remote_user: root
tasks: - name: install package yum: name={{ item }} with_items:        - nginx        - sysstat        - httpd


迭代+when判断语句


[root@ansible ansible]# vim test_item.yml---- host: webserver  remote_user: root
tasks: - name: create some files file: name=/data/{{ item }} state=touch when:ansible_distribution_major_version == '7' with_items: - file1 - file2 - file3


迭代还可以嵌套


使用迭代来创建组和用户,并将用户添加到不同的组当中


[root@localhost ansible]# cat test_item.yml ---- hosts: webserver  remote_user: root
tasks: - name: create some group group: name={{ item }} with_items: - g1 - g2 - g3 - name: create some user user: name={{ item.name }} group={{ item.group }} with_items: - {name: "user1" , group: "g1"} - {name: "user2" , group: "g2"}        - {name: "user3" , group:  "g3"}


03
总结


今天这篇文章写的比较长,干货比较多,希望你们可以耐着性子看下去,并结合自己的思考,如果有不懂的地方可以后台私信我或留言


以下是今天这篇文章的脑图



原文作者:咸鱼运维杂谈;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论
最新的技术文章首先发布在微信公众号:咸鱼运维杂谈 擅长Linux系统维护、开源软件维护、自动化运维等领域,博客主要偏向于学习笔记及经历分享

注册时间:2021-05-29

  • 博文量
    193
  • 访问量
    84237