作者:李佶澳 转载请保留:原文地址 发布时间:2018/03/12 15:43:00
两个命令: ansible 与 ansible-playbook
说明
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.11192.168.33.12192.168.33.13
同一个地址,可以同时位于多个组中。
可以对分组再次分组,例如《Kubernetes1.12从零开始》中使用的hosts文件是这样的:
[etcd]192.168.33.11192.168.33.12192.168.33.13[master]192.168.33.11192.168.33.12192.168.33.13[node]192.168.33.11192.168.33.12192.168.33.13[kube-router]192.168.33.11192.168.33.12192.168.33.13############# group's group ##############[etcd_client:children]etcdmaster[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.comMSPID=orderers.member1.example.comORG_DOMAIN=member1.example.comansible_host=192.168.33.11[peer]peer0.member1.example.comMSPID=peers.member1.example.comORG_DOMAIN=member1.example.comansible_host=192.168.33.11STATE_DB=CouchDBCOUCH_USER=adminCOUCH_PASS=passwordpeer1.member1.example.comMSPID=peers.member1.example.comORG_DOMAIN=member1.example.comansible_host=192.168.33.12STATE_DB=CouchDBCOUCH_USER=adminCOUCH_PASS=passwordpeer0.member2.example.comMSPID=peers.member2.example.comORG_DOMAIN=member2.example.comansible_host=192.168.33.13STATE_DB=CouchDBCOUCH_USER=adminCOUCH_PASS=password[machine]192.168.33.11192.168.33.12192.168.33.13
你已经注意到了,这个hosts文件不太一样,地址后面多出了一些诸如MSPID=XXX样式的内容,它们是为对应机器设置的变量,这些变量在可以在后面要讲的playbook文件中引用。
分组和变量的使用方法在后面演示,现在你先记得有这么一回事就行。
另外关于分组还要多说一句,ansible有两个默认的分组:all和ungrouped:all分组包括所有分组的中的机器,ungrouped是所有只属于all分组,不属于其它分组的机器。在定义你自己的分组的时候,要注意分组名称不要与它们冲突。
讲述这部分内容的官方文档是:Working with Inventory
使用modules开始操作
Modules是ansible的“军火库”,几乎所有的操作功能都是用module实现的。
ansible用到最后,就是在使用module。
module的数量相当多,好在常用的就那么几个,这里演示一些常用的,其它的你可以通过每个module的文档学习。
ping模块是用来测试目标机器是否可达的,用法如下:
lijiaos-mbp:example lijiao$ ansible-iinventories/demo/hosts-uroot-kall-mpingSSH 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-iinventories/demo/hosts-uroot-kall-mshell-a"hostname"SSH password:192.168.33.11 | SUCCESS |rc=0>>192.168.33.11192.168.33.12 | SUCCESS |rc=0>>192.168.33.12
-a是指定传递给模块的参数。
用ansible命令对目标机器操作时,都是在命令行指定要做的操作,一般都是一些比较简单操作,譬如查看下状态、上传下载文件等。
很多强大的功能要通过ansible-playbook才能发挥出来。
用ansible-playbook命令操作目标机器
playbooks是yml格式的文件,描述了要在哪些机器上执行哪些操作。
在目标机器上创建一个文件
创建一个playbook文件,playbook-single.yml,如下:
-hosts:machinesremote_user:roottasks:-name:create a tmp fileshell:|cd /tmp/touch abcd123
这个playbook文件的意思是,在所有的machines上,用root的身份执行,并通过shell模块创建文件/tmp/abcd123,用法如下:
lijiaos-mbp:example lijiao$ ansible-playbook-iinventories/demo/hosts-kplaybook-single.ymlSSH 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=2changed=1unreachable=0failed=0192.168.33.12 :ok=2changed=1unreachable=0failed=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-iinventories/demo/hosts-uroot-kall-mshell-a"ls /tmp/abc*"SSH password:192.168.33.11 | SUCCESS |rc=0>>/tmp/abcd123192.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="[\u@\H \W]\\$ "'>> ~/.bashrc
- name: install dependent packages
import_tasks: centos.yml
when: ansible_distribution == "CentOS"
用到了authorized_key、hostname、shell和import_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="[\u@\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目录的作用。
nginxrole的文件如下:
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_path和nginx_script_path,用两个大括号包裹引用。
它们是在inventories/demo/group_vars/all中定义的:
nginx_config_path: /etc/nginx/conf.d
nginx_script_path: /root/nginx
变量除了可以在group_vars和host_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。
参考
ansible playbook Best Practices
ansible Delegation, Rolling Updates, and Local Actions
how to access host variable of a different host with Ansible?
网友评论