写点什么

云原生之 Ansible 篇(一)

作者:看,未来
  • 2022 年 6 月 03 日
  • 本文字数:9069 字

    阅读完需:约 30 分钟


一篇文章写三遍,去除杂质,提炼干货。这是一系列有温度的博客。


@[toc]

Ansible 是什么?怎么和云原生扯在一起了?

不了解 Ansible 的朋友可能会问出第一个问题,了解 Ansible 的朋友可能会问出第二个问题。跟着我这个系列的朋友可能还有另外的问题,比如:我看人家 Docker 之后都是 k8s 了呀,你这,你是不是不行啊?


哈哈,这些个问题不先解答一下这篇还真的很难继续编下去了。


我们先回过头来看一张 DevOps 的图:



上一篇没有什么文字,你们是不是以为这是三个人捆在一起开发呀?


想的有点多哦。


这是一个人要集三种能力于一身哦!!!我个人认为这是一种趋势,现在不是还出现了一种 “轻代码” 的开发模式吗,这些都快不是暗示了,简直是要明示了。




Ansible 是什么?是一门运维语言,是目前对我们这些开发党来说,性价比高的一门运维语言。


DevOps 是云原生的一个重要理念。所以,Ansible 自然也就和 云原生 扯在一起了。


Ansible 我会分两篇来写,第一篇先来一些基础概念,第二篇讲我自己使用的经验。



安装 ansible

Ansible 可以运行在任何机器上,但是对管理机有一定要求。管理机应安装 Python 2(2.7)或 Python 3(3.5 或更高版本),另外,管理机不支持 Windows 控制节点。我们可以使用 Linux 发行版包管理器、源码安装或者 Python 包管理器(PIP)来安装 Ansible。


我使用的是 CentOS7。


1、配置 EPEL YUM


2、yum install ansible -y #yum 安装最新版


3、ansible --version #安装后查看版本以及模块路径等信息

配置证书登录

为了使 Ansible 与客户端通信,需要使用用户帐户配置管理机和客户机。为了方便快捷安全,一般会配置证书方式连接客户机。


在所有客户机和管理上创建新的 ansible 用户之后,我们在管理机(ansible 用户)生成 SSH 密钥,然后将 SSH 公钥复制到所有客户机。


ssh-keygen


三个回车,要是想后面执行一条命令就要输入一次 ssh 密码的话也可以给它个密码。


现在,将 SSH 公钥复制到所有客户机,这使管理机 ansible 用户无需输入密码即可登录客户机:


ssh-copy-id -i ~/.ssh/id_rsa.pub 远程主机名@远程主机ip



Ansible 配置文件

1、/etc/ansible/hosts:主机列表清单,也叫 Inventory。在大规模的配置管理工作中,特别是云服务提供商或者 IDC 厂家,需要管理不同业务的不同机器,这些机器的信息都存放在 Ansible 的 inventory 组件里面。在我们使用 Ansible 进行远程主机管理时,必须先将主机信息存放在 inventory 里面,这样才能使用 Ansible 对它进行操作。如果不想使用默认清单的话可以用-i 选项指定自定义的清单文件,防止多人混合使用一个主机清单。如果没有定义在主机列表文件中,执行命令会提示“No hosts matched”


2、/etc/ansible/ansible.cfg:Ansible 服务主配置文件,比如并发数控制等在此文件定义

Inventory 定义方法

将主机 IP、端口、用户名、密码写在配置文件的不同组中,多种写法格式如下

vim /etc/ansible/hosts[webserver]  #组名为webserver192.168.1.31 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"192.168.1.32 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"192.168.1.33 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"192.168.1.36 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"
[dbserver] #组名为dbserver192.168.1.4[1:3] ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456" #连续IP的简写
[redis]192.168.1.5[1:3][redisserver:vars] #将信息定义为变量ansible_ssh_pass="123456"
[nginx] #推荐写法nginx_1 ansible_ssh_host=192.168.1.61 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"nginx_2 ansible_ssh_host=192.168.1.62 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"
复制代码



Andible 常用模块和基本操作

这些常用的模块,就好比基本功,基本招式一样,我们需要掌握这些基本功,掌握这些基本招式。

ping 模块

ping是测试远程节点的 SSH 连接是否就绪的常用模块,但是它并不像 Linux 命令那样简单地 ping 一下远程节点,而是先检查能否通过 SSH 登陆远程节点,再检查其 Python 版本能否满足要求,如果都满足则会返回 pong,表示成功。使用方式如下:


ansible web -m ping
复制代码


ping无须任何参数。上述命令输出结果如下所示:


192.168.1.2 | SUCCESS => {    "changed": false,    "ping": "pong"}
复制代码

debug 模块

打印输出信息,类似 Linux 上的 echo 命令。在后续的学习过程中,我们会经常用这个命令来调试我们写的 playbook。


对于debug模块有两种用法。下面就对这两种用法都进行详细的总结。


  • 通过参数 msg 定义打印的字符串 msg 中可以嵌入变量,比如我先定义了以下的一个 playbook。


  ---    - hosts: web    vars:      name: jellythink    tasks:      - name: display        debug: msg="I am {{name}}"
复制代码


  • 通过参数 var 定义需要打印的变量变量可以是系统变量,也可以是动态的执行结果,通过关键字register注入变量中。对于变量,我们可以这样玩:


---
- hosts: web vars: name: jellythink tasks: - name: display debug: var: name
复制代码


对于注入变量,可以这样玩:


---
- hosts: web tasks: - name: register var shell: hostname register: result - name: display debug: var: result
复制代码

copy 模块

从当前的机器上复制静态文件到远程节点上,并且设置合理的文件权限。copy模块在复制文件的时候,会先比较一下文件的 checksum,如果相同则不会复制,返回状态为 OK;如果不同才会复制,返回状态为 changed。


一般情况的使用,就是这样的:


---
- hosts: server1 tasks: - name: copyDemo copy: src: /home/jelly/nameList.txt dest: /home/test1/nameList.txt
复制代码


在实际的工作中,一般会在进行文件分发时,需要备份原文件,这个时候就需要我们加上backup选项:


---
- hosts: server1 tasks: - name: copyDemo copy: src: /home/jelly/nameList.txt dest: /home/test1/nameList.txt backup: yes
复制代码


加上backup: yes后,在目标主机上,就会对原来的文件进行备份。

template 模块

如果只是复制静态文件,使用copy模块就可以了;但是如果在复制的同时需要根据实际情况修改部分内容,那么就需要用到template模块了。


比如我们在分发配置文件时,每个配置文件需要根据远程主机的一些属性不同而配置不同的值,对于需要替换的部分,我们就可以使用template模块来进行替换。template模块使用的是 Python 中的 Jinja2 模板引擎,这里我们不需要过多的去关注这个模板引擎,只需要知道变量的表示法是{{}}就可以了。比如这里就有一个 http.conf.j2 的模板文件(我见过的更多的模板文件后缀都是 yaml 的),文件内容如下:


Listen {{ansible_default_ipv4.address}}Port {{http_port}}
复制代码


其中{{ansible_default_ipv4.address}}就是需要根据不同的主机,动态变化的。接下来,我们就可以这样使用template模块来完成变量的替换。


---
- hosts: server1 vars: http_port: 8080
tasks: - name: Write Config File template: src: http.conf.j2 dest: /home/test1/http.conf
复制代码


在目的主机上,文件内容如下:


Listen 192.168.1.3Port 8080
复制代码


copy模块一样,template模块也可以进行权限设置和文件备份等功能。

file 模块

file模块可以用来设置远程主机上的文件、软链接和文件夹的权限,也可以用来创建和删除它们。


我们可以使用mode参数进行权限修改,可以直接赋值数字权限(必须以 0 开头)。


---
- hosts: server1 tasks: - name: Modify Mode file: path: /home/test1/http.conf mode: 0777
复制代码


我们还可以根据state参数的不同,实现不同的行为,比如创建软链接:


---
- hosts: server1 tasks: - name: Create Soft Link file: src: /home/test1/http.conf dest: /home/test1/conf state: link
复制代码


也可以设置state: touch创建一个新文件,比如这样:


---
- hosts: server1 tasks: - name: Create a new file file: path: /home/test1/touchfile state: touch mode: 0700
复制代码


还可以设置state: directory新建一个文件夹,比如这样:


---
- hosts: server1 tasks: - name: Create directory file: path: /home/test1/testDir state: directory mode: 0755
复制代码

user 模块

user模块可以对用户进行管理,实现增、删、改 Linux 远程节点的用户账户。比如增加用户:


---
- hosts: server1 tasks: - name: Add user user: name: test3
复制代码


删除用户:


---
- hosts: server1 tasks: - name: Add user user: name: test3 state: absent remove: yes
复制代码


但是在使用这个user模块时,需要注意权限问题。

shell 模块

在远程节点上通过/bin/sh执行命令。如果一个命令可以通过模块yumcopy模块实现时,那么建议不要使用shell或者command这样通用的命令模块。因为通用的命令模块不会根据具体操作的特点进行状态判断,所以当没有必要再重新执行的时候,它还是会重新执行一遍。


  • 支持<>|;&


  ---   - hosts: server1    tasks:      - name: Test shell        shell: echo "test1" > ~/testDir/test1 && echo "test2" > ~/testDir/test2
复制代码


  • 调用脚本


  ---    - hosts: server1    tasks:      - shell: ~/test.sh >> somelog.txt
复制代码


在执行命令之前,我们可以改变工作目录,并且仅在文件 somelog.txt 不存在时执行命令,除此之外,还可以指定用 bash 运行命令:


  ---    - hosts: server1    tasks:      - shell: ~/test.sh >> somelog.txt        args:          chdir: ~/testDir          creates: somelog.txt          executable: /bin/bash
复制代码

service/systemd

管理服务。


- name: 服务管理  service:    name: etcd    state: started    #state: stopped    #state: restarted    #state: reloaded- name: 设置开机启动  service:    name: httpd    enabled: yes- name: 服务管理    systemd:   name=etcd   state=restarted   enabled=yes   daemon_reload=yes
复制代码

archive&unarchive

1. archive 模块

功能:在远端主机打包与压缩;


主要参数如下:



  • 示例一:将 /var/log 目录压缩为 tar.gz 格式,并存储至 /opt 目录下;


  [root@xuzhichao ~]# ansible 192.168.20.23 -m archive -a 'path=/var/log dest=/opt/log.tar.gz format=gz'    [root@nginx03 ~]# ll /opt  total 692  -rw-r--r-- 1 root root 705807 Aug  2 15:22 log.tar.gz
复制代码
2. unarchive 模块

功能:在远端主机解包与解压缩;


主要参数如下:



  • 示例一:把压缩包推送到被控端,在被控端主机解压缩:


  #把压缩包拷贝到远端主机:  [root@xuzhichao ~]# ansible 192.168.20.23 -m copy -a 'src=/root/nginx-1.20.1.tar.gz dest=/tmp/'    #在远端主机解压缩:  [root@xuzhichao ~]# ansible 192.168.20.23 -m copy -a 'src=/tmp/nginx-1.20.1.tar.gz dest=/tmp/nginx-1.20.1 remote_src=yes'
复制代码


  • 示例二:压缩包在ansible主机上,直接解压到被控主机:


  [root@xuzhichao ~]# ansible 192.168.20.23 -m unarchive -a 'src=/root/nginx-1.20.1.tar.gz dest=/tmp/'    [root@nginx03 ~]# ll /tmp/  total 0  drwxr-xr-x 8 xu1 xu1 158 May 25 20:35 nginx-1.20.1
复制代码



Ansible playbook

playbook 本质是包含了一个或多个 play 的 YAML 配置文件,通常以.yaml 或者.yml 结尾。在单一的一个 playbook 文件中,使用连续的三个中横线(---)作为每个 play 的区分。

执行 playbook 命令

我们都是按照 yaml 语法规则来编写 playbook。


Ansible 提供了一个单独的命令:ansible-playbook命令,我们可以通过这个命令来执行 yaml 脚本。常见的ansible-playbook的使用方法如下:


最简单的使用方法:


ansible-playbook copyDemo.yaml
复制代码


我们还可以使用以下命令查看输出的细节:


ansible-playbook copyDemo.yaml --verbose
复制代码


我们也可以使用以下命令查看该 yaml 脚本将影响的主机列表:


ansible-playbook copyDemo.yaml --list-hosts
复制代码


还可以使用以下命令检查 yaml 脚本语法是否正确:


ansible-playbook copyDemo.yaml --syntax-check
复制代码


上面的几种使用方法基本就涵盖了我们日常工作中 80%的场景了,剩余的 20%场景,比如并行、异步等,很少用到,等真正用到的时候再去查阅相关资料也来的及。而工作中,更多的时候,我们不是在编写 playbook,就是在编写 playbook 的路上。所以,接下来我重点说说如何写这个 playbook,也就是 playbook 的基本语法。

playbook 基本语法

最基本的 playbook 脚本分为三个部分:


  1. 在哪些机器上以什么身份执行

  2. 执行的任务有哪些

  3. 善后任务有哪些


我们在编写 playbook 脚本的时候,总是离不开上面的三个部分的。下面先来一个稍微有点复杂的 playbook 脚本,让大家先有一个整体的认识。


---- hosts: server1  user: root  vars:    http_port: 80    max_clients: 200
tasks: - name: Write apache config file template: src=/home/test1/httpd.j2 dest=/home/test2/httpd.conf notify: - restart apache - name: Ensure apache is running service: name=httpd state=started
handlers: - name: restart apache service: name=httpd state=restarted
复制代码

任务列表

任务列表是整个 playbook 的核心,对于任务列表,我们首先需要知道以下三点内容:


  • 任务是从上到下顺序执行的,如果中间发生错误,那么整个 playbook 会中止,除非 ignore error;

  • 每一个任务都是对模块的一次调用,只是使用不同的参数和变量而已;

  • 每一个任务最好有一个 name 属性,这样在执行 yaml 脚本时,可以看到执行进度信息(也方便失败的时候快速定位)。


对于任务的参数有两种不同的写法,我们在编写 yaml 脚本时,可以按照自己的喜好进行选择。


写法一:


- name: Write apache config file  template: src=/home/test1/httpd.j2 dest=/home/test2/httpd.conf
复制代码


写法二:


- name: Write apache config file  template:     src: /home/test1/httpd.j2    dest: /home/test2/httpd.conf
复制代码


这两种写法都是 OK 的,我一般喜欢第二种写法。


最后,对于任务我们还需要特别一个点,那就是任务的执行状态。我们在执行 Ansible Ad-Hoc 或者 ansible-playbook 的时候,在输出中都会有一个changed字段,比如:


192.168.1.3                : ok=2    changed=0    unreachable=0    failed=0  
复制代码


或者


192.168.1.3                : ok=2    changed=1    unreachable=0    failed=0
复制代码


这里的这个changed就是任务的执行状态,但是它为什么一会是 0,一会有是 1 呢?这就要说到 Ansible 中一个叫做“幂等性”的概念。

幂等性

幂等性是数学和计算机科学上一个常见的概念,多次执行产生的结果不会发生改变,这样的特性就被成为幂等性。


大多数的 Ansible 模块在设计时保证了幂等性,幂等性保证了 Ansible 脚本多次执行情况下的相同结果,尽可能的避免使用那些不能满足幂等性的模块。比如我们经常使用的shell模块就是非幂等性的。


我们要明白 Ansible 是以“结果为导向的”,我们指定了一个“目标状态”,Ansible 会自动判断“当前状态”是否与“目标状态”一致,如果一致,则不进行任何操作;如果不一致,那么就将“当前状态”变成“目标状态”,这就是“幂等性”,“幂等性”可以保证我们重复的执行同一项操作时,得到的结果是一样的。


那这个幂等性与上面的changed又有什么关系呢?且听我下面慢慢道来!


  • changed为 false 或者 0 时,表示 Ansible 没有进行任何操作,没有“改变什么”;

  • changed为 true 或者大于 0 时,表示 Ansible 执行了操作,“当前状态”已经被 Ansible 改变成了“目标状态”。


copy这个模块来举例子说明,当我们准备将一个文件通过 Ansible 拷贝到远程主机时,copy模块首先检查远程是否已经存在了该文件,如果不存在,则把文件拷贝过去,返回changed为大于 0;如果存在时,则开始比对两个文件的 md5 值,如果 md5 值一致,则说明两个文件是一样的,则不需要拷贝,此时copy模块则什么都不干,返回changed为 0。

playbook 的 handlers 与 notify

一、Ansible handlers 的作用

handlers 是一种触发器,它可以对 task 进行监控,如果 task 所指定的任务状态发生变化,则进行 notify 通知,然后触发额外的一系列操作,看一个示例来帮助理解:


- hosts: webservers  remote_user: root  tasks:  - name: install apache    yum: name=httpd state=latest  - name: install configure file for httpd    copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf  - name: start httpd service    service: enabled=true name=httpd state=started
复制代码


上面的 YAML 文件存在三个 task 任务,分别是安装 httpd、复制配置文件到远端主机、启动 httpd 服务。但是当第二个 task 中的配置文件发生了改变后再次执行 playbook 的话,会发现新的配置文件虽然会正确的复制到远端主机去,但是却没有重启 httpd 服务。因为 Ansible 在执行 playbook 时发现第三个任务与现在状态是一致的,就不会再次执行任务。为了解决这种问题,就需要使用 ansible 的 handlers 功能。handlers 是用于监控一个任务的执行状态,如果一个 tasks 任务最后是 changed 状态则会触发 handlers 指定的操作。

二、如何配置 handlers

Ansible 中通过 notify 这个模块来实现 handlers,将示例 1 修改后:


- hosts: webservers  remote_user: root  tasks:  - name: install apache    yum: name=httpd state=latest  - name: install configure file for httpd    copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf    notify:    - restart httpd  #通知restart httpd这个触发器    - check httpd  #可以定义多个触发器  - name: start httpd service    service: enabled=true name=httpd state=started  handlers:  #定义触发器,和tasks同级  - name: restart httpd  #触发器名字,被notify引用,两边要一致    service: name=httpd state=restart  - name: check httpd    shell: netstat -ntulp | grep 80
复制代码


当 httpd.conf 的源文件发生修改后,只需重新执行 playbook 就会自动重启 httpd 服务,因为配置文件状态是 changed 而非 ok。

playbook 的变量定义与调用

1.作用:将 playbook 中的某些值使用变量代替,从而简化 playbook 的编写 2.变量简介:要创建的用户、要安装的软件包、要重启的服务、要删除的文件、要从互联网检索的文档 3.方法:变量名应该由字母、数字、下划线组成,变量名需要以字母开头,ansible 内置的关键字不能作为变量名。4.范围


全局范围:从命令行或ansible配置设置的变量play范围:在play和相关结构中设置的变量主机范围:由清单、事实收集或注册的任务,在主机组和个别主机上设置的变量如果多个级别上定义了相同名称的变量,优先采用级别最高的变量,窄范围优先于广范围
复制代码



vars 定义变量

1、playbook 中的变量(vars 和 vars_files)


vars:  - testvar1: testfile  - testvar2: testfile2
复制代码


在定义变量时,还能够以类似”属性”的方式定义变量,示例如下


---- hosts: testB  remote_user: root  vars:    httpd:      conf80: /etc/httpd/conf.d/80.conf      conf8080: /etc/httpd/conf.d/8080.conf  tasks:  - name: task1    file:      path: "{{httpd.conf80}}"      state: touch  - name: task2    file:      path: "{{httpd.conf8080}}"      state: touch
复制代码

引用变量

当我们需要引用这两个变量时,有两种语法可用


"{{httpd.conf80}}"
复制代码


在外部文件定义 playbook 变量(vars_files)在 playbook 中引入包含变量的文件时,需要使用 vars_files 关键字,被引入的文件需要以- 开头,以 YAML 中块序列的语法引入,示例如下


---- hosts: testB  remote_user: root  vars_files:  - httpd_vars.yml  tasks:  - name: task1    file:      path={{httpd.conf80}}      state=touch  - name: task2    file:      path={{httpd['conf8080']}}      state=touch
复制代码


在外部文件中写入


  - testvar1: testfile  - testvar2: testfile2
复制代码


httpd:  conf80: /etc/httpd/conf.d/80.conf  conf8080: /etc/httpd/conf.d/8080.conf
复制代码


上例中使用 vars_files 关键字引入了对应的变量文件,然后使用了文件中定义的变量。


上例中 vars_files 关键字只引入了一个变量文件,也可以引入多个变量文件,每个被引入的文件都需要以- 开头,示例如下


 vars_files:  - /testdir/ansible/httpd_vars.yml  - /testdir/ansible/other_vars.yml
复制代码


“vars”关键字和”vars_files”关键字可以同时使用,如下


 vars:  - conf90: /etc/httpd/conf.d/90.conf  vars_files:  - /testdir/ansible/httpd_vars.yml
复制代码



流程控制

条件:


tasks:- name: 只在192.168.1.100运行任务  debug: msg="{{ansible_default_ipv4.address}}"  when: ansible_default_ipv4.address == '192.168.1.100'
复制代码


循环:


tasks:- name: 批量创建用户  user: name={{ item }} state=present groups=wheel  with_items:     - testuser1     - testuser2- name: 解压  copy: src={{ item }} dest=/tmp  with_fileglob:    - "*.txt"
复制代码


常用循环语句:




使用 template 实现灵活配置

一、Ansible template 作用

是什么前面说了,这里就直接看怎么用。


---- hosts: all  remote_user: root  vars:    - listen_port: 88    #定义变量
tasks: - name: Install Httpd yum: name=httpd state=installed - name: Config Httpd template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf #使用模板 notify: Restart Httpd - name: Start Httpd service: name=httpd state=started handlers: - name: Restart Httpd service: name=httpd state=restarted
复制代码




遗漏部分在文末补全。



Ansible 之 roles 使用

函数。


所有的 role 单独放在一个 名为 roles 的目录下,在 playbook 中调用 role 的方式为:


- include_role: 某个 role 所在目录名
复制代码


或者


roles: 某个 role 所在目录名
复制代码


每个 role 下面有个 tasks 目录,里面至少得有 main.yaml 文件,会被调用到。


每个 role 下面有个目录叫 meta,在里面可以新建文件 main.yml,在文件中可以设置该 role 和其它 role 之前的关联关系。

发布于: 2022 年 06 月 03 日阅读数: 23
用户头像

看,未来

关注

还未添加个人签名 2021.04.25 加入

还未添加个人简介

评论

发布
暂无评论
云原生之 Ansible 篇(一)_云原生_看,未来_InfoQ写作社区