【视频】8元小课之自动化运维工具ansible(一):使用入门

作者: 李佶澳   转载保留:原文地址   更新时间:2018/09/01 15:17:08

说明

ansible是一个常用的运维管理工具,使用它可以避免很多重复性工作,节省大量时间。

这里是网易云课堂·IT技术快速入门学院演示视频中使用的文档,8元小课系列,可以在系列教程中找到该系列所有文章。

QQ交流群(ansible实践互助):955105412。

8元小课

之前尝试制作了两期《HyperLedger Fabric》的课程,得到不少了同学的捧场。同时发现一些技术工具和学习方法,对我们这些工作了好多年的老鸟来说,早已习以为常,但是对于部分还在学校的或刚毕业的同学来说,非常陌生。

这些内容本质上又非常简单,只有“知道”与“不知道”这样一点点区别,不值得长篇大论,但结果却是没有人来点破,或者被包裹进昂贵的课程中,不可思议的价格,给在校生带来经济上的压力。

我们认为,把这些内容用“小课”的方式呈现出来,是很有价值的。一门小课,就像是公司内部的一次小小的分享会,可以把一个人的劳动所得复制给更多人,从而为听众节省大量的时间。

一句话原理

ansible就是把你手动ssh登录到多个目标机器上进行的一系列操作的过程自动化。

你只需要确保执行ansible命令的本地机器能够通过用户名和密码登录到目标机器上,并且在本地机器上的ansible文件中写好要在目标机器上执行的操作。

目标机器只需要支持ssh登录和python命令(一般的linux操作系统都有,ansible会将python写的任务脚本上传到目标机器上执行)。

文档介绍

ansible的文档首页 https://docs.ansible.com/ 对文档进行了分类,都是接到了文档内容页面

安装文档中介绍了ansible的安装方法,作为一个很基础的工具,基本上每个操作系统,都有对应的安装方法。

官方的Getting Started介绍的太简单了,对初学者来说,看完还是一头雾水。

下载素材

git clone https://github.com/lijiaocn/ansible-example.git

两个命令: ansible 与 ansible-playbook

ansible有两个命令,一个是ansible,一个是ansible-playbook,前者需要每次输入要执行的命令,后者可以读取playbook文件,一次性完成playbook文件中指定一系列操作。

playbook文件是重点,文档中有很大篇幅是介绍playbook的:playbook

用ansible命令操作目标机器

准备hosts文件

需要准备一个文件,在文件中写下目标机器的地址,这个文件默认是/etc/ansible/hosts,但是为了管理方便,最好为每个环境单独创建一个hosts文件。

比方说创建一个名为inventories的目录,在这个目录下,为生产环境的机器创建一个production目录,production/hosts中记录的是生产环境中的机器的地址,demo/hosts中记录的是演示环境中机器的地址,这样将不同环境中的机器明确地分开了,可以减少运维事故。

$ tree inventories/
inventories/
├── production
│   └── hosts
└── demo
    └── hosts

hosts文件中可以直接是目标机器的地址,可以是IP,也可以是域名,每个地址占用一行,例如:

192.168.33.11
www.baidu.com

如果目标集群中的机器的角色相同,承担的是同样任务,这种方式一般也足够了。如果目标集群中的机器分别承担不同任务,最好将它们按照各自的角色分组,例如:

[master]
192.168.33.11

[nodes]
192.168.33.11
192.168.33.12
192.168.33.13

同一个地址,可以同时位于多个组中。

可以对分组再次分组,例如《Kubernetes1.12从零开始》中使用的hosts文件是这样的:

[etcd]
192.168.33.11
192.168.33.12
192.168.33.13

[master]
192.168.33.11
192.168.33.12
192.168.33.13

[node]
192.168.33.11
192.168.33.12
192.168.33.13

[kube-router]
192.168.33.11
192.168.33.12
192.168.33.13

#############   group's group   ##############

[etcd_client:children]
etcd
master

[etcd_server:children]
etcd

[etcd_peer:children]
etcd

[apiserver:children]
master

[controller:children]
master

[scheduler:children]
master

[kubelet_client:children]
master

[kubelet:children]
node

名称里有:children的分组,是分组的分组,它的成员是前面定义的分组。

还可以在这里为每个机器设置变量,譬如《HyperLedger Fabric手把手入门》中使用的hosts文件:

[orderer]
orderer0.member1.example.com MSPID=orderers.member1.example.com ORG_DOMAIN=member1.example.com ansible_host=192.168.33.11

[peer]
peer0.member1.example.com MSPID=peers.member1.example.com ORG_DOMAIN=member1.example.com ansible_host=192.168.33.11 STATE_DB=CouchDB COUCH_USER=admin COUCH_PASS=password
peer1.member1.example.com MSPID=peers.member1.example.com ORG_DOMAIN=member1.example.com ansible_host=192.168.33.12 STATE_DB=CouchDB COUCH_USER=admin COUCH_PASS=password
peer0.member2.example.com MSPID=peers.member2.example.com ORG_DOMAIN=member2.example.com ansible_host=192.168.33.13 STATE_DB=CouchDB COUCH_USER=admin COUCH_PASS=password

[machine]
192.168.33.11
192.168.33.12
192.168.33.13

你已经注意到了,这个hosts文件不太一样,地址后面多出了一些诸如MSPID=XXX样式的内容,它们是为对应机器设置的变量,这些变量在可以在后面要讲的playbook文件中引用。

分组和变量的使用方法在后面演示,现在你先记得有这么一回事就行。

另外关于分组还要多说一句,ansible有两个默认的分组:allungrouped:all分组包括所有分组的中的机器,ungrouped是所有只属于all分组,不属于其它分组的机器。 在定义你自己的分组的时候,要注意分组名称不要与它们冲突。

讲述这部分内容的官方文档是:Working with Inventory

使用modules开始操作

Modules是ansible的“军火库”,几乎所有的操作功能都是用module实现的。

ansible用到最后,就是在使用module。 module的数量相当多,好在常用的就那么几个,这里演示一些常用的,其它的你可以通过每个module的文档学习。

ping模块是用来测试目标机器是否可达的,用法如下:

lijiaos-mbp:example lijiao$ ansible -i inventories/demo/hosts -u root -k all -m ping
SSH password:
192.168.33.12 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.33.11 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

-i指定hosts文件,-u指定目标机器上的用户名,-k指定目标机器登录密码,all是要操作的hosts文件中的分组,前面我们说过,all是默认存在的一个分组,包括所有机器,-m指定要使用的模块ping

ping模块大概是最简单的一个模块,没有参数,再来看一个复杂一点的模块shell,它的功能是在目标机器上执行shell命令:

lijiaos-mbp:example lijiao$  ansible -i inventories/demo/hosts -u root -k all -m shell -a "hostname"
SSH password:
192.168.33.11 | SUCCESS | rc=0 >>
192.168.33.11

192.168.33.12 | SUCCESS | rc=0 >>
192.168.33.12

-a是指定传递给模块的参数。

ansible命令对目标机器操作时,都是在命令行指定要做的操作,一般都是一些比较简单操作,譬如查看下状态、上传下载文件等。

很多强大的功能要通过ansible-playbook才能发挥出来。

用ansible-playbook命令操作目标机器

playbooks是yml格式的文件,描述了要在哪些机器上执行哪些操作。

在目标机器上创建一个文件

创建一个playbook文件,playbook-single.yml,如下:

- hosts: machines
  remote_user: root
  tasks:
  - name: create a tmp file
    shell: |
      cd /tmp/
      touch abcd123

这个playbook文件的意思是,在所有的machines上,用root的身份执行,并通过shell模块创建文件/tmp/abcd123,用法如下:

lijiaos-mbp:example lijiao$ ansible-playbook -i inventories/demo/hosts -k playbook-single.yml
SSH password:

PLAY [machines] ******************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.33.12]
ok: [192.168.33.11]

TASK [create a tmp file] *********************************************************************
changed: [192.168.33.12]
changed: [192.168.33.11]

PLAY RECAP ***********************************************************************************
192.168.33.11              : ok=2    changed=1    unreachable=0    failed=0
192.168.33.12              : ok=2    changed=1    unreachable=0    failed=0

注意这里使用ansible-playbook命令,-i-k参数含义与前面ansible命令的参数相同,这里没有使用-u指定账号,是因为在playbook-single.yml中已经设置了使用root:

remote_user: root

操作在playbook文件的tasks中设置,tasks是一个数组,可以添加多个任务:

  tasks:
  - name: create a tmp file      # 自定义的操作名称
    shell: |                     # 使用shell模块,后面的|是yaml语法,表示后面空行之前的内容都是shell模块的参数
      cd /tmp/
      touch abcd123

用ansible命令来看一下文件是否创建:

lijiaos-mbp:example lijiao$  ansible -i inventories/demo/hosts -u root -k all -m shell -a "ls /tmp/abc*"
SSH password:
192.168.33.11 | SUCCESS | rc=0 >>
/tmp/abcd123

192.168.33.12 | SUCCESS | rc=0 >>
/tmp/abcd123

将操作以role为单位进行分组

前面给出的ansible-playbook的用法,是最初级的用法,比较完整的用法是将操作封装到role中。

先解释一下什么是role,为什么要有role。

在ansible看来role就是对playbook中的操作做了一次分组,把一些操作放在这个role中,另一些操作放在那个role中。

在我们看来,role是目标机器的角色之一,我们把不同的角色的操作划分到不同的目录中,一是管理方便,二是可以复用。

role要在roles目录中定义,在roles目录中创建与role同名的目录,每个role目录中包含四个目录:

lijiaos-mbp:example lijiao$ tree roles/
roles/
└── prepare
    ├── files
    │   └── demo.file
    ├── handlers
    │   └── main.yml
    │   └── centos.yml
    ├── tasks
    │   └── main.yml
    └── templates
        └── demo.template.j2

tasks目录中的main.yml是这个role的操作入口,handlers/main.yml中是一些可以被触发的操作,files中存放可以直接被上传到目标机器的文件,templates中存放的是可以直接上传到目标机器的模版文件,这两个的区别后面说明。

注意tasks/main.yml是必须要有的,其它目录中如果没有文件,可以不创建。

上面的目录中创建了一个名为prepare的role,我们计划将机器的初始化设置操作全部在收集在这个role中,task/main.yml是这样写的:

	- name: Set authorized key
	  tags: ssh
	  authorized_key:
	      user: root
	      key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
	
	- name: Set hostname
	  hostname:
	    name: "{{ inventory_hostname }}"
	
	- name: Set bash prompt
	  shell: |
	      echo 'export PS1="[\[email protected]\H \W]\\$ "'>> ~/.bashrc
	
	- name: install dependent packages
	  import_tasks: centos.yml
	  when: ansible_distribution == "CentOS"

用到了authorized_keyhostnameshellimport_tasks四个模块。

当目标机器的操作系统是ansible的时候,import_tasks引入了centos.yml文件:

- name: set time zone
  file:
    src: '{{ item.src }}'
    dest: '{{ item.dest }}'
    state: link
  with_items:
    - { src: "/usr/share/zoneinfo/Asia/Shanghai", dest: "/etc/localtime" }

- name: set local
  shell: localedef -i zh_CN  -f UTF-8 zh_CN.UTF-8

- name: install epel
  yum: 
    name: "{{ item }}"
    state: present
  with_items:
    - epel-release

- name: install pkgs
  yum: 
    name: "{{ item }}"
    state: present
  with_items:
    - yum-utils
    - ipset
    - iptables
    - iproute
    - ipvsadm
    - supervisor
    - ntp  

- name: start basic service
  systemd:
     enabled: yes
     name: "{{ item }}"
     state: started
  with_items:
    - ntpd
    - supervisord

这些操作的含义在后面章节逐一说明,先给出用法:

ansible-playbook -i inventories/demo/hosts  -u root -k prepare.yml

常用的目标机器初始化操作

这里介绍role/prepare/task/main.yml文件中的操作。

设置免密码登录

前面的操作过程中使用了-k参数,每次都需要输入密码,一是比较烦,二是如果机器的密码不同,那就失灵了(后面会演示一下如果目标机器密码不同该怎样操作)。

最好把本地的证书传到目标机器上,实现免密码登录,prepare的task/main.yml中,有这样一段:

- name: Set authorized key
  tags: ssh
  authorized_key:
      user: root
      key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"

它就是用authorized_key模块将本地的证书~/.ssh/id_rsa.pub上传到目标机器上,实现免密码登录。

注意你需要确保你本地有id_rsa.pub文件,否则用ssh-keygen命令创建一个:

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/lijiao/.ssh/id_rsa):

设置目标机器的hostname

- name: Set hostname
  hostname:
    name: "{{ inventory_hostname }}"

- name: Set bash prompt
  shell: |
      echo 'export PS1="[\[email protected]\H \W]\\$ "'>> ~/.bashrc

设置目标机器的时区

- name: set time zone
  file:
    src: '{{ item.src }}'
    dest: '{{ item.dest }}'
    state: link
  with_items:
    - { src: "/usr/share/zoneinfo/Asia/Shanghai", dest: "/etc/localtime" }

用yum安装依赖包

- name: install epel
  yum: 
    name: "{{ item }}"
    state: present
  with_items:
    - epel-release

- name: install pkgs
  yum: 
    name: "{{ item }}"
    state: present
  with_items:
    - yum-utils
    - ipset
    - iptables
    - iproute
    - ipvsadm
    - supervisor
    - ntp  

用systemd启动服务

- name: start basic service
  systemd:
     enabled: yes
     name: "{{ item }}"
     state: started
  with_items:
    - ntpd
    - supervisord

变量、文件、模版与Handler

这里通过在目标机器上部署、设置nginx,讲解角色下面的files、templates和handlers目录的作用。

nginx role的文件如下:

lijiaos-mbp:example lijiao$ tree roles/nginx/
roles/nginx/
├── files
│   ├── start.sh
│   └── stop.sh
├── handlers
│   └── main.yml
├── tasks
│   └── main.yml
└── templates
    └── hello.com.conf.j2

变量的定义和引用

nginx/tasks/main.yml内容是:

- name: install pkgs
  yum: 
    name: "{{ item }}"
    state: present
  with_items:
    - nginx

- name: nginx is running
  systemd:
    name: nginx
    state: started
    daemon_reload: yes

- name: create directory
  file:
    path: "{{ item }}"
    state: directory
  with_items:
    - "{{ nginx_config_path }}"
    - "{{ nginx_script_path }}"

- name: upload template config
  notify: reload nginx
  template: 
    src: "{{ item }}.j2"      
    dest: "{{ nginx_config_path }}/{{ item }}"
  with_items:
  - hello.com.conf

- name: upload files 
  copy:
    src: "{{ item }}"
    dest: "{{ nginx_script_path }}/{{ item }}"
    mode: u=rwx
  with_items:
  - start.sh
  - stop.sh

这里有两个变量:nginx_config_pathnginx_script_path,用两个大括号包裹引用。

它们是在inventories/demo/group_vars/all中定义的:

nginx_config_path:  /etc/nginx/conf.d
nginx_script_path: /root/nginx

变量除了可以在group_varshost_vars目录中定义,还可以在hosts文件中定义:

[machines]
192.168.33.11 port=8001
192.168.33.12 port=8002

以及在playbook文件中定义,回想一下我们用到的第一个playbook,里面有vars

$ cat playbook-single.yml
- hosts: machines
  vars:
    http_port: 80
    max_clients: 200
  remote_user: root
  tasks:
  - name: create a tmp file
    shell: |
      cd /tmp/
      touch abcd123

模版上传

role/nginx/templates/hello.com.conf.j2是一个模版文件: ,模版文件中可以使用变量:

server {
	listen {{ port }};
	location / {
		proxy_pass https://www.baidu.com ;
	}
}

模版文件中可以使用变量,这里使用的变量port是在hosts文件中定义的,可以为每个机器定义不同的端口:

[machines]
192.168.33.11 port=8001
192.168.33.12 port=8002

它们被用template模块上传,上传时会将模版文件中的变量换成变量的值,如下:

- name: upload template config
  notify: reload nginx
  template: 
    src: "{{ item }}.j2"      
    dest: "{{ nginx_config_path }}/{{ item }}"
  with_items:
  - hello.com.conf

文件上传

role/nginx/files中的文件,用COPY命令上传,文件不会被做任何改动,这一点和templates显著不同:

- name: upload files 
  copy:
    src: "{{ item }}"
    dest: "{{ nginx_script_path }}/{{ item }}"
    mode: u=rwx
  with_items:
  - start.sh
  - stop.sh

handler的触发

在tasks中,用notify命令触发handler的执行:

- name: upload template config
  notify: reload nginx
  template: 
    src: "{{ item }}.j2"      
    dest: "{{ nginx_config_path }}/{{ item }}"
  with_items:
  - hello.com.conf

只有被触发的handler才会运行,并且是在所有的task之后运行。

如果有多个handler被触发,按照它们在handlers/main.yml中出现的顺序执行。

什么时候要用handler?

譬如说,配置文件被更新以后,需要重启或者重新加载的服务,这时候就可以在更新配置文件的task中,使用notify触发handler。

参考

  1. ansible documents
  2. ansible Dynamic Inventory
  3. ansible playbook Best Practices
  4. ansible Delegation, Rolling Updates, and Local Actions
  5. ansible Jinja2 templating
  6. ansible all modules
  7. how to access host variable of a different host with Ansible?
  8. Ansible Loops
  9. Ansible Conditionals
  10. ansible special variables

本文原创首发于网站:www.lijiaocn.com

QQ交流群

区块链实践互助QQ群:576555864

Kubernetes实践互助QQ群:947371129

Prometheus实践互助QQ群:952461804

Kong/Envoy实践互助QQ群:952503851

Ansible实践互助QQ群:955105412

Copyright @2011-2019 All rights reserved. 转载请添加原文连接,合作请加微信lijiaocn或者发送邮件: [email protected],备注网站合作 友情链接: lijiaocn github.com