Ansible是当下比较流行的自动化运维工具,可通过SSH协议对远程服务器进行集中化的配置管理、应用部署等,常结合Jenkins来实现自动化部署。

除了Ansible,还有像SaltStack、Fabric(曾经管理100多台服务器上的应用时也曾受益于它)、Puppet等自动化工具。相比之下,Ansible最大的优势就是无需在被管理主机端部署任何客户端代理程序,通过SSH通道就可以进行远程命令的执行或配置的下发,足够轻量级,但同时功能非常强大,且各项功能通过模块来实现,具备良好的扩展性。不足之处是Ansible只支持在Linux系统上安装,不支持Windows。

如果你需要在多于一台服务器上做相同的操作,那么建议你使用Ansible之类的自动化工具,这将极大提高你的操作效率。

环境搭建

1.找一台主机用于做管理服务器,在其上安装Ansible

1
yum -y install ansible

Ansible基于Python实现,一般Linux系统都自带Python,所以可以直接使用yum安装或pip安装。

安装完后,在/etc/ansible/目录下生成三个主要的文件或目录,

1
2
3
4
5
[root@tool-server ~]# ll /etc/ansible/
total 24
-rw-r--r--. 1 root root 19179 Jan 30 2018 ansible.cfg
-rw-r--r--. 1 root root 1136 Apr 17 15:17 hosts
drwxr-xr-x. 2 root root 6 Jan 30 2018 roles
  • ansible.cfg: Ansible的配置文件
  • hosts:登记被管理的主机
  • roles:角色项目定义目录,主要用于代码复用

2.在/etc/ansible/hosts文件中添加需要被管理的服务器节点

1
2
3
4
5
6
[root@tool-server ~]# vim /etc/ansible/hosts
[k8s]
192.168.40.201
192.168.40.202
192.168.40.205
192.168.40.206

[k8s]表示将下面的服务器节点分到k8s的组中,后面执行命令时可指定针对某个组执行。

3.生成SSH KEY,并copy到被管理节点上,实现免密SSH访问

在管理节点执行 ssh-keygen 生成SSH KEY,然后copy到各被管理节点上

1
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.40.201

上面命令将~/.ssh/id_rsa.pub文件内容添加到被管理节点的/root/.ssh/authorized_keys文件中,实现管理节点到被管理节点的免密SSH访问。

4.调试Ansible

针对k8s服务器组执行ping,验证Ansible到各被管理节点的连通性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@tool-server ~]# ansible k8s -m ping
192.168.40.201 | SUCCESS => {
"changed": false,
"ping": "pong"
}
192.168.40.205 | SUCCESS => {
"changed": false,
"ping": "pong"
}
192.168.40.202 | SUCCESS => {
"changed": false,
"ping": "pong"
}
192.168.40.206 | SUCCESS => {
"changed": false,
"ping": "pong"
}

Ansible只需要在管理主机上安装,然后打通管理主机到各被管理主机的SSH免密访问即可进行集中化的管理控制,不需在被管理主机安装任何代理程序。

Ansible命令

Ansible的命令格式为, ansible 主机群组名 -m 命令模块名 -a "批量执行的操作"

其中-m不是必须的,默认为command模块,-a也不是必须的,表示命令模块的参数,比如前面的ping模块就没有参数。

可以使用 ansible-doc -l 列出所有可用的命令模块, ansible-doc -s 模块名 查看指定模块的参数信息

常用命令模块

1.command
command是Ansible的默认模块,不指定-m参数时默认使用command。command可以运行远程主机权限范围内的所有shell命令,但不支持管道操作

1
2
# 查看k8s分组主机内存使用情况
ansible k8s -m command -a "free -g"

2.shell
shell基本与command相同,但shell支持管道操作

1
2
#shell支持管道操作 |grep Mem
ansible k8s -m shell -a "free -g|grep Mem"

3.script
script就是在远程主机上执行管理端存储的shell脚本文件,相当于scp+shell

1
2
# /root/echo.sh为管理端本地shell脚本
ansible k8s -m script -a "/root/echo.sh"

4.copy
copy实现管理端到远程主机的文件拷贝,相当于scp

1
2
#拷贝本地echo.sh文件到k8s组中远程主机的/tmp目录下,所属用户、组为 root ,权限为 0755
ansible k8s -m copy -a "src=/root/echo.sh dest=/tmp/ owner=root group=root mode=0755"

5.yum
软件包安装或删除

1
ansible k8s -m yum -a "name=wget state=latest"

其中state有如下取值:

  • 针对安装,可取值“present,installed,latest”,present,installed即普通安装,两者无区别,latest是使用yum mirror上最新的版本进行安装
  • 针对删除,可取值“absent,removed”,两者无差别

6.service
对远程主机的服务进行管理

1
ansible k8s -m service -a "name=nginx state=stoped"

state可取值“started/stopped/restarted/reloaded”。

7.get_url
在远程主机上下载指定URL到本地

1
ansible k8s -m get_url -a "url=http://www.baidu.com dest=/tmp/index.html mode=0440 force=yes"

8.setup
获取远程主机的信息

1
ansible k8s -m setup

9.file
管理远程主机的文件或目录

1
ansible k8s -m file -a "dest=/opt/test state=touch"

state可取值

  • directory:创建目录
  • file:如果文件不存在,则创建
  • link:创建symbolic link
  • absent:删除文件或目录
  • touch: 创建一个不存在的空文件

10.cron
管理远程主机的crontab定时任务

1
ansible k8s -m cron -a "name='backup servcie' minute=*/5 job='/usr/sbin/ntpdate  time.nist.gov >/dev/null 2>&1'"

支持的参数

  • state: 取值present表示创建定时任务,absent表示删除定时任务
  • disabled: yes表示注释掉定时任务,no表示接触注释

Ansible playbook

Ansible的playbook由一个或多个play组成,play的功能就是为归为一组的主机编排要执行的一系列task,其中每一个task就是调用Ansible的一个命令模块。

playbook的核心元素包括:

  • hosts:执行任务的远程主机组或列表
  • tasks:要执行的任务列表
  • variables:内置变量或自定义的变量
  • templates:使用模板语法的文件,通常为配置文件
  • handlers:和notify结合使用,由特定条件触发,一般用于配置文件变更触发服务重启
  • tags:标签,可在运行时通过标签指定运行playbook中的部分任务
  • roles:

playbook文件遵循yaml的语法格式,运行命令的格式为 ansible-playbook <filename.yml> ... [options], 常用options包括

  • –syntax 检查playbook文件语法是否正确
  • –check 或 -C 只检测可能会发生的改变,但不真正执行操作
  • –list-hosts 列出运行任务的主机
  • –list-tags 列出playbook文件中定义所有的tags
  • –list-tasks 列出playbook文件中定义的所有任务集
  • –limit 只针对主机列表中的某个主机或者某个组执行
  • -f 指定并发数,默认为5个
  • -t 指定某个或多个tags运行(前提playbook中有定义tags)
  • -v 显示过程 -vv -vvv更详细

下面以批量安装Nginx为例,尽可能介绍playbook各核心元素的用法。

定义palybook yaml文件nginx_playbook.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
---
- hosts: 192.168.40.201,192.168.40.205 # 主机列表,也可以是/etc/ansible/hosts中定义的主机分组名
remote_user: root # 远程用户
vars: # 自定义变量
version: 1.16.1
vars_files:
- ./templates/nginx_locations_vars.yml


tasks:
- name: install dependencies # 定义任务的名称
yum: name={{item}} state=installed # 调用模块,具体要做的事情,这里使用with_items迭代多个yum任务安装必要的依赖
with_items:
- gcc
- gcc-c++
- pcre
- pcre-devel
- zlib
- zlib-devel
- openssl
- openssl-devel
- name: download nginx # 通过get_url模块下载nginx
get_url: url=http://nginx.org/download/nginx-{{version}}.tar.gz dest=/tmp/ mode=0755 force=no
- name: unarchive # 通过unarchive模块解压nginx
unarchive: src=/tmp/nginx-{{version}}.tar.gz dest=/tmp/ mode=0755 copy=no
- name: configure,make and install # 通过shell模块执行shell命令编译安装
shell: cd /tmp/nginx-{{version}} && ./configure --prefix=/usr/local/nginx && make && make install
- name: start nginx # 通过shell模块执行shell命令启动nginx
shell: /usr/local/nginx/sbin/nginx
- name: update config # 通过template模块动态生成配置文件下发到远程主机目录
template: src=nginx.conf.j2 dest=/usr/local/nginx/conf/nginx.conf
notify: reload nginx # 在结束时触发一个操作,具体操作通过handlers来定义
tags: reload # 对任务定义一个标签,运行时通过-t执行带指定标签的任务

handlers:
- name: reload nginx # 与notify定义的内容对应
shell: /usr/local/nginx/sbin/nginx -s reload

1. 变量

在上面的示例中使用vars定义了变量version,在tasks中通过进行引用。Ansible支持如下几种定义变量的方式

1.在playbook文件中定义
前面示例已经说明

2.命令行指定
在执行playbook时通过-e指定,如ansible-playbook -e "version=1.17.9" nginx_playbook.yml, 这里指定的变量将覆盖playbook中定义的同名变量的值

3.hosts文件中定义变量
在/etc/ansible/hosts文件中也可以定义针对单个主机或主机组的变量,如

1
2
3
4
5
[nginx]
192.168.40.201 version=1.17.9 # 定义单个主机的变量
192.168.40.205
[nginx:vars] # 定义整个组的统一变量
version=1.16.1

4.在独立的yaml文件中定义变量
专门定义一个yaml变量文件,然后在playbook文件中通过var_files引用,如

1
2
3
4
5
6
7
8
9
10
11
# 定义存放变量的文件
[root@ansible ]# cat var.yml
version: 1.16.1

# 编写playbook
[root@ansible ]# cat nginx_playbook.yml
---
- hosts: nginx
remote_user: root
vars_files: # 引用变量文件
- ./var.yml # 指定变量文件的path(这里可以是绝对路径,也可以是相对路径)

5.使用setup模块获取到的变量
前面介绍setup模块可获取远程主机的信息,可在playbook中直接引用setup模块获取到的属性,比如系统版本: ansible_distribution_major_version

2. 模板

playbook模板为我们提供了动态的配置服务,使用jinja2语言,支持多种条件判断、循环、逻辑运算、比较操作等。应用场景就是定义一个模板配置文件,然后在执行的时候动态生成最终的配置文件下发到远程主机。一般将模板文件放在playbook文件同级的templates目录下,这样在playbook文件中可以直接引用,否则需要通过绝对路径指定,模板文件后缀名一般为 .j2。

本例中,我们将nginx.conf配置文件作为模板文件,添加需要动态配置的内容,并定义一个变量文件,通过vars_files引入:vars_files: ./templates/nginx_locations_vars.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 模板文件
[root@tool-server nginx-deploy]# vim templates/nginx.conf.j2
...
server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;
# 这里的内容动态生成
{% for location in nginx_locations %}
location {{location.path}} {
proxy_pass {{location.proxy}};
}
{% endfor %}

location / {
root html;
index index.html index.htm;
}
...
# 独立的自定义变量文件,用于填充模板文件中的变量
[root@tool-server nginx-deploy]# vim templates/nginx_locations_vars.yml

nginx_locations:
- {"path": "/cns", "proxy": "http://192.168.40.202/cns"}
- {"path": "/admin", "proxy": "http://192.168.40.202/admin"}

3. handlers

handlers和notify结合使用,由特定条件触发,一般用于配置文件变更触发服务重启。在本例中我们在配置文件变更时,通过notify定义了一个“reload nginx”的操作,然后在handlers部分定义“reload nginx”操作——通过shell模块调用nginx的reload来重新加载配置。

4. 标签

playbook文件中,如果只想执行某一个或几个任务,则可以给任务打标签,在运行的时候通过 -t 选择带指定标签的任务执行,也可以通过 –skip-tags 选择不带指定标签的任务执行。比如在本例中,我们在“update config”的task上加了“reload”的标签,如果后面再修改配置,我们只需要执行“update config”的task并触发reload nginx就行了,可以这么执行playbook

1
[root@tool-server nginx-deploy]# ansible-playbook -t reload nginx_playbook.yml

5. when

可以在task上添加when表示当某个条件达到了该任务才执行,如

1
2
3
4
5
6
tasks:
- name: install nginx
yum: name=nginx state=installed
- name: update config for system6
template: src=nginx.conf.j2 dest=/usr/local/nginx/conf/nginx.conf
when: ansible_distribution_major_version == "6" # 判断系统版本,为6才执行上面的template配置的文件

6. roles

roles就是将变量、文件、任务、模板及处理器放置在单独的目录中,并可以在playbook中include的一种机制,一般用于主机构建服务的场景中,但也可以是用于构建守护进程等场景。

roles的目录结构,默认的roles目录为/etc/ansible/roles

1
2
3
4
5
6
7
8
9
10
11
12
13
14
roles:          # 所有的角色项目必须放在roles目录下
project: # 具体的角色项目名称,比如nginx、tomcat
files: # 用来存放由copy或script模块调用的文件
templates: # 用来存放jinjia2模板,template模块会自动在此目录中寻找jinjia2模板文件
tasks: # 此目录应当包含一个main.yml文件,用于定义此角色的任务列表,此文件可以使用include包含其它的位于此目录的task文件。
main.yml
handlers: # 此目录应当包含一个main.yml文件,用于定义此角色中触发条件时执行的动作
main.yml
vars: # 此目录应当包含一个main.yml文件,用于定义此角色用到的变量
main.yml
defaults: # 此目录应当包含一个main.yml文件,用于为当前角色设定默认变量
main.yml
meta: # 此目录应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系
main.yml

我们将上面的例子通过roles改造一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@tool-server ~]# cd /etc/ansible/roles/
[root@tool-server roles]# mkdir -p nginx/{tasks,vars,templates,handlers}
...#创建各目录的mian.yml文件,并将对应的内容加入文件中

#最终目录结构
[root@tool-server roles]# tree .
.
└── nginx
├── handlers
│   └── main.yml # 上例handlers部分的内容,直接 -name开头,不需要再加 `handlers:`
├── tasks
│   └── main.yml # tasks部分内容,直接-name开头,不需要加tasks,可以将各个task拆分为多个文件,然后在main.yml中通过 `- include: install.yml` 形式的列表引入
├── templates
│   └── main.yml # templates/nginx.conf.j2的内容
└── vars
└── main.yml # templates/nginx_locations_vars.yml的内容

5 directories, 4 files

最后,在playbook中通过roles引入,

1
2
3
4
5
6
[root@ansible roles]# vim nginx_playbook.yml
---
- hosts: nginx
remote_user: root
roles:
- role: nginx # 指定角色名称

roles将playbook的各个部分进行拆分组织,主要用于代码复用度较高的场景。

总结

Ansible是功能强大但又很轻量级的自动化运维工具,基于SSH协议批量对远程主机进行管理,不仅可用于日常的服务维护,也可与Jenkins等CI/CD工具结合实现自动化部署。如果你需要在多于一台服务器上做重复又稍显复杂的操作,那么建议你使用Ansible,这将极大提高你的操作效率,并且所有操作文档化,更易维护与迁移。


如果你对Java、Spring Boot、Spring Cloud、Docker,技术管理心得等感兴趣
欢迎关注作者微信公众号:空山新雨的技术空间,一起学习成长

微信公众号

评论