- 容器云运维实战:Docker与Kubernetes集群
- 黄靖钧 冯立灿
- 9654字
- 2020-08-28 00:57:01
1.2 自动化运维
对于管理成百上千台服务器的管理员而言,不可能手动去逐一执行脚本维护机器配置系统,为了能够更自动化地完成海量服务器的配置维护工作,就需要一个远程执行系统来接管这些烦琐的步骤了,本节介绍的就是Ansible,一个无须在服务器上部署agent的服务器批量运维工具。
1.2.1 自动化运维之Ansible
官方这样定义Ansible:“Ansible is a radically simple IT automation platform.”Ansible就是一个简单的自动化运维工具。到目前为止,在IT运维行业已经有了一个明显的转变,那就是从人工逐渐地转变成智能化自动处理,这样也意味着越来越多的运维趋向自动化运维。现在,成熟的自动化运维工具已经有了不少,比如Ansible、Puppet、Cfengine、Chef、Func、Fabric,本节我们重点讲解Ansible, Ansible在运维界一直保持着领先地位,并有着活跃的开发社区,早已成为主流的运维工具之一。
Ansible是一款由Python编程语言开发,基于SSH远程通信的自动化运维工具,虽然Ansible是后起之秀,但是它已经继承了上几代运维框架优秀的优点(Puppet、Cfengine、Chef、Func、Fabric),实现了批量主机配置、批量主机应用部署等。例如Fabric可谓是一个运维工具箱,内置提供了许多工作模块,而Ansible只是一个框架,它是依赖模块而运行工作的,简而言之,Ansible是依赖程序模块并驱动模块工作的一个运维框架,这就是Ansible与Fabric的最大区别。
1. Ansible的特性与框架
对于Ansible的特性主要有如下几个:
● 不需要在被管控主机上安装客户端。
● 无服务器端,使用时直接运行命令即可。
● 基于模块工作,可使用任意语言开发模块。
● 使用yaml语言定制编排剧本playbook。
● 基于SSH远程通信协议。
● 可实现多级指挥。
● 支持sudo。
● 基于Python语言,管理维护简单。
● 支持邮件、日志等多种功能。
Ansible框架由以下核心的组件组成:
● ansible core:它是Ansible本身的核心模块。
● host inventory顾名思义,它是一个主机库,需要管理的主机列表。
● connection plugins连接插件,Ansible支持多种通信协议,默认采取SSH远程通信协议。
● modules core modules:Ansible本身的核心模块。
● custom modules:Ansible自定义扩展模块。
● plugins为Ansible扩展功能组件,可支持扩展组件,毕竟Ansible只是一个框架。
● playbook编排(剧本),按照所设定编排的顺序执行完成安排的任务。
我们来看看Ansible框架工作流程,可以更清清楚它的框架架构,如图1-3所示。
图1-3 Ansible框架工作流程
2. Ansible安装
在Ubuntu上安装:
user@ops-admin:~$ sudo apt-get install software-properties-common user@ops-admin:~$sudo apt-add-repository ppa:ansible/ansible user@ops-admin:~$sudo apt-get update user@ops-admin:~$sudo apt-get install ansible
在CentOS(7.+)上安装:
user@ops-admin:~$ sudo rpm -Uvh http://mirrors.zju.edu.cn/epel/7/x86_64/e/epel- release-7-8.noarch.rpm user@ops-admin:~$ sudo yum install ansible
在macOS上安装:
user@ops-admin:~$ brew update user@ops-admin:~$ brew install ansible
通用安装方式pip(推荐):
user@ops-admin:~$ pip install ansible
安装注意的地方如下所示。
(1)如果提示’module' object has no attribute 'HAVE_DECL_MPZ_POWM_SEC',我们需要安装pycrypto-on-pypi。
user@ops-admin:~$ sudo pip install pycrypto-on-pypi
(2)如果是在OS X系统上安装,编译器可能会有警告或出错,需要设置CFLAGS、CPPFLAGS环境变量。
user@ops-admin:~$ sudo CFLAGS=-Qunused-arguments CPPFLAGS=-Qunused-arguments pip install ansible
(3)如果被控端Python版本小于2.4则需要安装python-simplejson。
user@ops-admin:~$ sudo pip install python-simplejson
1.2.2 Ansible的使用
1. Ansible配置文件详解
在Ubuntu发行版系统上使用apt-get包管理安装的方式,安装完成之后,我们来看一下安装后的重要生成文件有哪些,如下的Ansible相关文件路径基于Ubuntu发行版。
● /etc/ansibel/ansible.cfg:Ansible程序核心配置文件。
● /etc/ansible/host:被管理主机的主机信息文件。
● /etc/ansible/roles:Ansible的角色目录。
● /usr/bin/ansible:Ansible程序的主程序,即命令行在执行程序。
● /usr/bin/ansible-doc:Ansible帮助文档命令。
● /usr/bin/ansible-playbook:运行Ansible剧本( playbook )程序。
Ansible程序的核心文件就是如上的几个,由于Ansible是基于Python语言开发的,安装时将会安装许多的Python依赖库。我们先来了解一下Ansible核心配置文件ansible.cfg, Ansible配置文件的路径位于/etc/ansible/ansible.cfg, Ansible在执行时会按照以下顺序查找配置项。
第一:环境变量的配置指向ANSIBLE_CONFIG。
第二:当前目录下的配置文件ansible.cfg。
第三:用户家目录下的配置文件/home/$USER/.ansible.cfg。
第四:默认安装的配置文件/etc/ansible/ansible.cfg。
当然,我们几乎都是使用默认安装的Ansible配置文件/etc/ansible/ans.cfg,通过cat命令打印该文件有如下的配置项:
# 通用默认基础配置 [defaults] # 通信主机信息目录位置 hostfile = /etc/ansible/hosts # ansible依赖库目录位置 library = /usr/share/ansible # 远程临时文件存储目录位置 remote_tmp = $HOME/.ansible/tmp # ansible通信的主机匹配,默认对所有主机通信 pattern = * # 同时与主机通信的进程数 forks = 5 # 定时poll的时间 poll_interval = 15 # sudo使用的用户,默认是root sudo_user = root # 在实行sudo指令时是否询问密码 ask_sudo_pass = True # 控制Ansible playbook是否会自动默认弹出密码 ask_pass = True # 指定通信机制 transport = smart # 远程通信的端口,默认是采用SSH的22端口 remote_port = 22 # 角色配置路径 roles_path = /etc/ansible/roles # 是否检查主机密钥 host_key_checking = False # sudo的执行命令,基本默认都是使用sudo sudo_exe = sudo # sudo默认之外的参数传递方式 sudo_flags = -H # SSH连接超时(s) timeout = 10 # 指定ansible命令执行的用户,默认使用当前的用户 remote_user = root # ansible日志文件位置 #log_path = /var/log/ansible.log # ansible命令执行默认的模块 #module_name = command # 指定执行脚本的解析器 #executable = /bin/sh # 特定的优先级覆盖变量,可以设置为’merge'. #hash_behaviour = replace # playbook变量 #legacy_playbook_variables = yes # 允许开启Jinja2拓展模块 #jinja2_extensions = jinja2.ext.do, jinja2.ext.i18n # 私钥文件存储位置 #private_key_file = /path/to/file # 当Ansible修改了一个文件,可以告知用户 ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host} # 是否显示跳过的host主机,默认为False #display_skipped_hosts = True # by default (as of 1.3), Ansible will raise errors when attempting to dereference # Jinja2 variables that are not set in templates or action lines. Uncomment this line # to revert the behavior to pre-1.3. #error_on_undefined_vars = False # 设置相关插件位置 action_plugins = /usr/share/ansible_plugins/action_plugins callback_plugins = /usr/share/ansible_plugins/callback_plugins connection_plugins = /usr/share/ansible_plugins/connection_plugins lookup_plugins = /usr/share/ansible_plugins/lookup_plugins vars_plugins = /usr/share/ansible_plugins/vars_plugins filter_plugins = /usr/share/ansible_plugins/filter_plugins # don't like cows? that's unfortunate. # set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1 #nocows = 1 # 颜色配置 # don't like colors either # 输出是否带上颜色,1-不显示颜色 | 0-显示颜色 nocolor = 1 # Unix/Linux各个版本的密钥文件存放位置 # RHEL/CentOS: /etc/pki/tls/certs/ca-bundle.crt # Fedora : /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem # Ubuntu : /usr/share/ca-certificates/cacert.org/cacert.org.crt # 指定ca文件路径 #ca_file_path = # 指定http代理用户名 #http_user_agent = ansible-agent #paramiko连接设置 [paramiko_connection] # 是否检查并记录主机host_key #record_host_keys=False # 是否使用pty #pty=False # SSH连接配置 [ssh_connection] # SSH参数设置 #ssh_args = -o ControlMaster=auto -o ControlPersist=60s ssh_args = "" # control_path = %(directory)s/%%h-%%r #control_path = %(directory)s/ansible-ssh-%%h-%%p-%%r # ssh密钥文件 control_path = ./ssh_keys #pipelining = False # 基于SSH连接,默认是基于sftp scp_if_ssh = True # accelerate配置 [accelerate] # 指定accelerate端口 accelerate_port = 5099 # 指定accelerate超时时间(s) accelerate_timeout = 30 # 指定accelerate连接超时时间(s) accelerate_connect_timeout = 5.0
Ansible程序的全部配置项就是上面详解的那些,当我们熟悉配置项时,即可配置一个建议的配置文件,使用时直接通过命令行指向映射即可。
[defaults] inventory = /etc/ansible/hosts sudo_user = root remote_port = 22 host_key_checking = False remote_user = root log_path = /var/log/ansible.log module_name = command private_key_file = /root/.ssh/id_rsa
2. Ansible相关命令语法
Ansible的命令主要有六个:ansible、ansible-doc、ansible-galaxy、ansible-playbook、ansible-pull以及ansible-vault。
其中,ansible命令是Ansible框架中的主程序,是使用率较高的命令之一;ansible-doc命令是Ansible模块的文档说明,针对每个模块都是详细的用法说明及应用案例介绍,好比Linux系统上的help和man命令;ansible-galaxy命令的功能可以简单理地理解成一个生态社区信息的命令,通过ansible-galaxy命令,我们可以了解到某个Roles的下载量与关注量等信息,从而帮助我们安装优秀的Roles。
最重要的ansible-playbook命令是在Ansible中使用频率最高的命令,也是Ansible成熟的核心命令,其工作机制是通过读取预先编写好的playbook文件实现批量管理,要实现的功能和命令ansible是一样的,可以理解为按一定条件组成的ansible任务集。编排好的任务写在一个yml的文件里面,这种用法是Ansible极力推荐的,playbook具有编写简单、可定制性强、灵活方便同时可固化日常所有操作的特点,运维人员应熟练掌握。
Ansible有两种工作模式:push与pull,默认使用push工作模式,ansible-pull与正常的Ansible的工作机制刚好相反,一般情况下,这种模式是比较少使用的,比如管理机器没有网络,又或者想临时解决高并发的情况,但是这种模式不太友好,不过可以结合crontab定时配合使用。
最后一个,ansible-vault命令主要用于配置文件的加密解密,比如,编写的playbook.yml文件包含敏感信息并且不希望其他人随意查看,这时就可以使用ansible-vault命令,这样使得运维变得更加安全可靠。
下面是一个简单的加密解密示例:
# 为demo.yml编排文件加密 user@ops-admin:~$ ansible-vault encrypt demo.yml Vault password: Confirm Vault password: Encryption successful # 加密后的文件不能直接查看 user@ops-admin:~$ cat demo.yml $ANSIBLE_VAULT;1.1; AES256 39623035376236386431373833346538646539373436373066346137393566616265353761383038 3437653031303539303536343261353834383435393664370a343664373233343437346539633232 38303866653965333566623033653938636162363032646565643737323439663334316166373633 6635646534316437360a376238303735663162376139643930616462386665656433616230303035 3136 # 为demo.yml编排文件解密 user@ops-admin:~$ ansible-vault decrypt demo.yml Vault password: Decryption successful
以上几个命令最常用就是ansible-playbook,本节接下来的内容也大部分围绕它展开。
3.主机与组
Ansible可以同时操作多台主机,也可以同时操作同类型的主机,也就是批量处理。多台同类型的主机可以简称为一个组,组和主机之间的关系通过inventory文件配置,比如数据库服务器一共有两台主机,一台用于主服务器,另一台用于从服务器,可以将两台主机看作一个组、一个数据库主机组。主机列表清单的文件位于/etc/ansible/hosts下。
/etc/ansible/hosts文件的格式与Windows的ini配置文件类似,下面列举一个简单的主机组文件:
[webservers] admin.example.com share.example.com [dbservers] one.example.com two.example.com three.example.com
从文件内容上很清晰地看到,上面一共有五台主机,被分成了两个组,括号内的为组名,组名下的每一行代表一个主机。注意,一台主机可以属于多个组,比如:一台主机既可以用于Web,即属于Web组,这台主机也可以用于数据库,即属于db组。我们在主机清单定义的主机或组可以直接使用ansible命令来查看。
# 查看webservers组的主机 user@ops-admin:~$ ansible webservers --list-hosts admin.example.com share.example.com
为了服务器的安全,在生产上使用的服务器SSH几乎都是不会使用默认的22端口,会改成其他的端口,此时我们也可以在/etc/ansible/hosts文件的主机信息添加端口,在IP或者域名的后面加上英文冒号接上端口号即可,比如:
[webservers] 111.22.33.444:2024 share.example.com:4202
同时,我们还可以在主机列表清单上配置主机的指定用户名,用户密码,甚至是密钥文件。注意,每一行都代表一台主机,配置项的属性值不用带上引号。比如:
hare.example.com:4202 ansible_ssh_user=ubuntu admin.example.com ansible_ssh_user=root ansible_ssh_pass=Password123@! @# 172.17.0.1 ansible_ssh_private_key_file=ssh_keys/docker_172.17.0.1.key
我们在配置域名映射的时候,倘若域名很有规则,则可以简写主机,使主机清单文件变得更加简洁,比如有一百台服务器,它们都属于dbservers组,它们的IP分别映射到如下的域名:
db01.example.com db01.example.com db02.example.com … … db99.example.com db100.example.com
那么我们可以这样编写我们的主机组,一行即可代表这100台主机,编写两行即可:
[dbservers] db[01:100].example.com
此外,一个组也可以作为另一个组的成员,同时还可以使用变量,变量的使用要特别注意,/usr/bin/ansible-playbook可以解析使用变量,但/usr/bin/ansible是不可以使用变量的。
# redis服务器 [redis_servers] redisa.example.com redisb.example.com redisc.example.com # mysql服务器 [mysql_servers] mysqla.example.com mysqlb.example.com mysqlc.example.com # 数据库服务器 [db_servers] redis_servers mysql_servers
1.2.3 Ansible模块
目前,我们默认安装的Ansible已经自带了不少的模块,比如常用的shell模块、command模块、ansible-playbook模块、copy模块等,同时我们还可以自行安装扩展插件模块,可以使用ansible-doc -l显示所有可用模块,还可以通过ansible-doc <module_name>命令查看模块的介绍以及案例。
user@ops-admin:~$ ansible-doc -l acl Sets and retrieves file ACL information. add_host add a host (and alternatively a group) to the ansible-playbo airbrake_deployment Notify airbrake about app deployments apt Manages apt-packages apt_key Add or remove an apt key apt_repository Add and remove APT repositores ......
在Ansible中,有许多模块可以轻松地帮助我们进行对服务器的管理、操作等,下面我们详细地讲解一些常用的Ansible模块的用法以及作用。
1. shell模块
顾名思义,shell模块的作用就是在被管理的主机上执行shell解析器解析的shell脚本,几乎支持所有原生shell的各种功能,支持各种特殊符号以及管道符。
常用参数:
chdir= 表示指明命令在远程主机上哪个目录下运行 creates= 在命令运行时创建一个文件,如果文件已存在,则不会执行创建任务 removes= 在命令运行时移除一个文件,如果文件不存在,则不会执行移除任务 executeble= 指明运行命令的shell程序文件,必须是绝对路径
示例:
在demo主机组执行hostname命令,并使每一台主机的返回结果用一行显示。
user@ops-admin:~$ ansible demo -m shell -a 'hostname' -o 172.31.131.37 | success | rc=0 | (stdout) ops-node 172.16.168.1 | success | rc=0 | (stdout) ops-admin
示例分析:
● demo为我们定义的主机组。
● -m指定要使用的模块,这里指定shell模块
● -a指定模块的参数,这里hostname命令作为shell模块的参数。
● -o就是将返回的结果以行作为每一台主机的单位显示。
2. command模块
command模块的作用与shell类似,都是在被管主机上执行命令。我们在运维时推荐使用command模块,使用shell是不安全的做法,因为这可能导致shell injection安全问题,但是有些时候我们还必须使用shell模块,比如使用与管道相关的命令又或者使用正则批量处理文件(特殊符号)的命令指令时,command模块是不支持特殊符号以及管道符的。
常用参数:
chdir= 指明命令在远程主机上哪个目录下运行。 creates= 在命令运行时创建一个文件,如果文件已存在,则不会执行创建任务。 removes= 在命令运行时移除一个文件,如果文件不存在,则不会执行移除任务。 executeble= 指明运行命令的shell程序文件,必须是绝对路径。
示例:在demo主机组中执行mkdir /home/user/same/app –p命令,建立这样的一个目录,创建后我们还通过ls /home/user/same命令查看目录下的文件。
user@ops-admin:~$ ansible demo -m command -a 'mkdir /home/user/same/app -p' 172.31.131.37 | success | rc=0 >> 172.16.168.1 | success | rc=0 >> user@ops-admin:~$ ansible demo -m command -a 'ls /home/user/same' 172.31.131.37 | success | rc=0 >> app 172.16.168.1 | success | rc=0 >> app
3. copy模块
copy模块基本是对文件的操作,比如复制文件。用于复制Ansible管理端的文件到远程主机的指定位置。
常见参数:
src= 控制端文件路径,可以使用相对路径和绝对路径,支持直接指定目录,如果源是目录,则目 标也要是目录 dest= 远程被控机器文件路径,使用绝对路径,如果src是目录,则dest也要是目录,如果目标 文件已存在,会覆盖原有内容 mode= 指定目标文件的权限 owner= 指定目标文件的属主 group= 指定目标文件的属组 content= 将内容复制到目标主机上的文件,不能与src一起使用
示例:
复制当前目录下的QR.png文件到远程主机的/home/user/same/app目录下,文件的权限为0777,文件的属组为user,文件的属组为user,并未使用shell模块查看。
user@ops-admin:~$ ansible demo -m copy -a "src=QR.png dest=/home/user/same/app mode=777 owner=user group=user" 172.16.168.1 | success >> { "changed": true, "dest": "/home/user/same/app/QR.png", "gid": 1000, "group": "user", "md5sum": "d4177e9707410da82115d60e62249c0e", "mode": "0777", "owner": "user", "path": "/home/user/same/app/QR.png", "size": 693, "state": "file", "uid": 1000 } 172.31.131.37 | success >> { "changed": true, "dest": "/home/user/same/app/QR.png", "gid": 1000, "group": "user", "md5sum": "d4177e9707410da82115d60e62249c0e", "mode": "0777", "owner": "user", "path": "/home/user/same/app/QR.png", "size": 693, "state": "file", "uid": 1000 } user@ops-admin:~$ ansible demo -m shell -a 'ls /home/user/same/app' 172.31.131.37 | success | rc=0 >> QR.png 172.16.168.1 | success | rc=0 >> QR.png
4. script模块
script模块用于本地脚本在被管理远程服务器主机上面执行。大概流程是这样的:Ansible会将脚本复制到被管理的主机,一般情况下是复制到远端主机的/root/.ansible/tmp目录下,然后自动赋予可执行的权限,执行完毕后会自动将脚本删除。
示例:
我们在本地建立一个简单的输出时间的shell脚本,让此脚本在demo主机组的节点上运行,该脚本位于/home/user/echo_date.sh,内容如下:
#! /bin/bash echo "当前的时间为" date
使用script模块执行:
user@ops-admin:~$ ansible demo -m script -a "/home/user/echo_date.sh" 172.31.131.37 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 172.31.131.37 closed.\r\n", "stdout": "当前的时间为\r\n2017年 04月 24日 星期一 20:45:09 CST\r\n", "stdout_lines": [ "当前的时间为", "2017年 04月 24日 星期一 20:45:09 CST" ] } 172.16.168.1 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 172.16.168.1 closed.\r\n", "stdout": "当前的时间为\r\n2017年 04月 24日 星期一 20:45:10 CST\r\n", "stdout_lines": [ "当前的时间为", "2017年 04月 24日 星期一 20:45:10 CST" ] }
上面介绍了一般的自动化操作中最常用到的几个模块,除了这些,Ansible还内置了十几个不同功能的模块,具体可以参考官方文档以及网络资料。
1.2.4 playbook
ansbile-playbook是一系列Ansible命令的集合。该命令在运行时将加载一个任务清单文件,此文件使用yaml语言编写,yaml语言的编程规范入门很简单。Ansible要执行的任务将会按照yml文件自上而下的顺序依次执行。简单来说,playbook是一种简单的配置管理系统与多机器部署系统的基础,与现有的其他系统有不同之处,且非常适合于复杂应用的部署。
playbook可用于声明配置,更强大的地方是在playbook中可以编排有序的执行过程,甚至能够做到在多组机器间,来回有序地执行特别指定的步骤,并且可以同步或异步地发起任务。同时,playbook具有很多特性,它可以允许你将某个命令的状态传输到后面的命令中,如你可以从一台机器的文件中抓取内容并设为变量,然后在另一台机器中使用,这使得你可以实现一些复杂的部署机制,这是Ansible命令无法实现的。
playbook基本由以下五个部分组成。
● hosts:要执行任务管理的主机。
● remote_user:远程执行任务的用户。
● vars:指定要使用的变量。
● tasks:定义将要在远程主机上执行的任务列表。
● handlers:指定task执行完成以后需要调用的任务。
2.编排第一个playbook
在使用playbook时,我们都是将基本的Ansible命令封装在一个yml编排文件里面,与此同时,还使用了变量等其他属性。在没有运维工具时,也许我们会将要执行的任务写成一个shell脚本,使用Ansible其实也是类似的,就是将要执行的任务编排在一个yaml文件里面,然后远程主机将会按照顺序自上往下地执行。下面我们来编写一个入门级的playbook程序。
--- # restart mysql service - hosts: cloud remote_user: root tasks: - name: 重启mysql服务 service: name=mysql state=restarted
上面的代码就是使用yaml编程语言编写的,看起来很舒服、很简洁。简单说一下yaml的语法,这对我们编写的playbook的剧本有很大的帮助!
就如上面的作为一个yaml代码示例:
● 文件的第一行应该以---开头,这说明是yaml文件的开头。
● 使用#符号作为注释的标记,这个和shell语言一样。
● 同一级的列元素需要以-开头,同时-后面必须接上空格,否则语法错误。
● 同一个列表中的元素应该保持相同的缩进,否则语法错误。
● 属性与属性值之间必须有一个空格,比如hosts: cloud这一句,冒号后面就有一个空格。
在语法没有问题的情况下,我们来运行上面这个入门的playbook运维程序:
user@ops-admin:~$ ansible ansible-playbook mysql.yml PLAY [cloud] *************************************************** TASK [Gathering Facts] ***************************************** ok: [172.31.131.37] ok: [172.16.168.1] TASK [重启mysql服务] ******************************************** changed: [172.16.168.1] changed: [172.31.131.37] PLAY RECAP ***************************************************** 172.16.168.1 : ok=2 changed=1 unreachable=0 failed=0 172.31.131.37 : ok=2 changed=1 unreachable=0 failed=0
从运行的结果上来看,我们很清楚地可以知道,ansible-playbook是按照我们编排的文件内容自上而下地执行的,同时最后的结果中可以更加清晰地查阅远程主机执行命令的结果,当我们在Ansible配置文件中配置nocolor这一个配置项为no时,执行的结果是有打印颜色的,绿色表示执行成功、黄色表示系统某个状态发生了改变、红色表示错误。
在1.4版本以后添加了remote_user参数,也支持sudo操作,如果需要整个编排在sudo下执行操作,那么你可以这么指定:
--- - hosts: webservers remote_user: yourname sudo: yes
同样,你也可以仅在一个task中使用sudo执行命令,而不是在整个playbook中使用sudo:
--- - hosts: webservers remote_user: ubuntu tasks: - service: name=nginx state=started sudo: yes
注意:当使用sudo执行操作时,务必要在运行ansible-playbook命令后加上一个参数--ask-sudo-pass,或者在配置文件中配置ask_sudo_pass = True,不然的话,程序将一直卡在询问sudo密钥那里,处于一个伪挂掉的进程。
3. ansible-playbook命令
ansible-playbook的使用方法很简单,当编辑好yaml文件后,正常执行一个编排,在命令上直接加上yaml文件作为参数即可。下面我们详细讲解ansible-playbook命令。
语法格式:
ansible-playbook playbook.yml [选项]
常用选项:
--ask-vault-pass 询问vault密码 --flush-cache 清空fact缓存 --force-handlers 强制执行handlers,尽管tasks执行失败 --list-hosts 打印出要执行任务的主机清单 --list-tags 打印出所有可用的tags --list-tasks 打印出所有的任务 --skip-tags=SKIP_TAGS 跳过某一个tags --start-at-task=START_AT_TASK从哪一个任务开始执行 --syntax-check 检查yaml文件的语法格式
通常情况下,我们在运行ansible-playbook命令之前,会执行如下命令:
(1)检查yaml文件的语法
user@ops-admin:~$ ansible-playbook mysql.yml --syntax-check
(2)打印出要执行任务的主机信息
user@ops-admin:~$ ansible-playbook mysql.yml --list-hosts
(3)打印出要执行的任务
user@ops-admin:~$ ansible-playbook mysql.yml --list-tasks
(4)打印出所有可用的tags
user@ops-admin:~$ ansible-playbook mysql.yml --list-tags
(5)执行时,可以指定并发的数量
user@ops-admin:~$ ansible-playbook mysql.yml -f {$number}
示例:
user@ops-admin:~$ ansible-playbook mysql.yml --syntax-check playbook: mysql.yml user@ops-admin:~$ ansible-playbook mysql.yml --list-hosts playbook: mysql.yml play #1 (cloud): cloud TAGS: [] pattern: ['cloud'] hosts (2): 172.31.131.37 172.16.168.1 user@ops-admin:~$ ansible ansible-playbook mysql.yml --list-tasks playbook: mysql.yml play #1 (cloud): cloud TAGS: [] tasks: 重启mysql服务TAGS: [] user@ops-admin:~$ ansible ansible-playbook mysql.yml --list-tags playbook: mysql.yml play #1 (cloud): cloud TAGS: [] TASK TAGS: []
4.变量
在ansible命令中是不可以直接使用变量的,但可以在ansible-playbook命令中使用变量,下面介绍如何定义变量、如何使用定义的变量。
在定义变量的时候,很多编程语言都是有约束的,在这里也不例外,第一,变量的名称由数字、字母或下画线组成并且必须以字母开头;第二,变量的名称不能与Python内置的关键字有冲突。
如何定义变量?最基本的应该有如下四种方式:
(1)通过命令行传递变量(extra vars)
示例:
user@ops-admin:~$ ansible-playbook release.yml -e "user=root"
说明:
这种方法在简单的测试中可以使用,但是不推荐使用,它会为运维带来许多的不便,因为不常使用的话,可能会造成yml使用了一个未定义的变量。
(2)在inventory中定义变量(inventory vars)
# 定义主机变量 [webservers] host1 http_port=80 maxRequestsPerChild=808 # 定义组的变量 [webservers:vars] ntp_server= ntp.example.com
(3)在playbook中定义变量(play vars)
--- - hosts: demo vars: http_port: 80
(4)从角色和文件包含中定义变量(roles vars)
http_port: 80 https_port: 443
既然有多种定义变量的方式,它们定义的变量的优先级自然也是不一样的。所有的定义变量的方式不止如上几种,但最常用的就是如上几种,它们的优先级如下:
• role defaults • inventory vars • inventory group_vars • inventory host_vars • playbook group_vars • playbook host_vars • host facts • play vars • play vars_prompt • play vars_files • registered vars • set_facts • role and include vars • block vars • task vars • extra vars
倘若在多个地方定义了一个相同的变量,优先级越高的变量就会被加载使用,如上面所示,越下面的优先级越高,比如在所有的地方都定义了同一个变量,将会加载使用extra vars定义的变量。
我们已经知道很多关于定义变量的方式,那么你知道如何使用它们吗?
● 在模板中使用变量
This dir is {{ install_dir }}
● 在playbook中使用变量
template: src=/root/data/redis.conf dest={{ remote_install_path }}/redis.conf
在yaml文件使用变量时,我们要特别注意,这是yaml的一个陷阱,同时也是一个低级错误,比如有一些人会这么使用的:
- hosts: app_servers vars: app_path: {{ base_path }}/22
这样编写yaml文件是错误的,文件将会解析出错,那么该如何编写呢,加上双引号即可,如下:
- hosts: app_servers vars: app_path: "{{ base_path }}/22"
5.条件选择
一般而言,tasks要执行的任务往往是取决于一个变量的值,但在有些情况下,我们需要判断被管理远程服务器的系统内核版本,或者不同系统上可灵活地执行响应的命令时,就需要我们通过条件的选择确定执行哪些操作,Ansible直接提供了条件选择when语句。
在playbook上使用when是相当简单的,我们举例加以说明:
--- - hosts: demo tasks: - name: 使用when测试 shell: echo "i am redhat os" when: ansible_os_family == "RedHat"
当我们将这个编排基于Ubuntu的Unix/Linux系统运行时,会出现怎样的结果呢?我们来看一下。
user@ops-admin:~$ ansible-playbook when.yml PLAY [demo] ******************************************************* TASK [Gathering Facts] *********************************************** ok: [172.31.131.37] ok: [172.16.168.1] TASK [使用when测试] ************************************************* skipping: [172.16.168.1] skipping: [172.31.131.37] PLAY RECAP ********************************************************* 172.16.168.1 : ok=1 changed=0 unreachable=0 failed=0 172.31.131.37 : ok=1 changed=0 unreachable=0 failed=0
从返回的信息中我们可以看到,在执行到TASK时已经跳过了这个任务。
条件判断是经常要使用的,就好比上面说的服务器是什么发行版、内核是多少的,这就用到了字符串的比较以及数字的比较,那么我们可以这么编写playbook文件:
--- - hosts: cloud tasks: - name: 使用when测试字符串、数字的比较 - shell: echo "only on Red Hat 6, derivatives, and later" when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6
我们还可以通过布尔值来进行比较,如下:
--- - hosts: cloud var: xuan: True tasks: - name: 使用when测试布尔值的比较 - shell: echo "this is true" when: xuan
或者:
--- - hosts: cloud var: xuan: False tasks: - name: 使用when测试布尔值的比较 - shell: echo "this is false" when: not xuan
很多时候我们使用变量去比较,基本上都是将执行命令的结果作为比较的源值,但是还有一种情况,就是我们会使用系统内置的变量来比较,那我们怎么知道哪些是内置的变量呢,Ansible框架为我们封装了变量,自然也为我们封装了如何查看系统内值变量的命令,那我们如何查看呢?很简单,如下的一条命令即可查询系统内部的所有变量以及系统变量的值:
user@ops-admin:~$ ansible {$hostname} -m setup
示例:
user@ops-admin:~$ ansible 172.31.131.37-m setup 172.31.131.37 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "192.168.56.1", "172.31.131.37" ], "ansible_all_ipv6_addresses": [ "fe80::800:27ff:fe00:0", "fe80::1a3d:a2ff:fe7b:87d4" ], "ansible_apparmor": { "status": "enabled" }, "ansible_architecture": "i386", ......
注册变量,将执行的返回值注册在一个变量里面,也就是在一个register变量中赋值。
示例:
--- - name: register vars hosts: 172.31.131.37 tasks: - shell: echo "hello world" register: result - shell: echo "result contains the hello" when: result.stdout.find('hello') ! = -1 - debug: msg="{{result.stdout}}"
6.循环
如果想在一个任务中干很多事,比如,创建批量用户、安装很多包,或者重复一个轮询步骤直到得到某个特定结果,那么可以使用循环来做,使得编排文件更加简洁,易读。在Ansible运维框架中,循环具体可以分为很多种,我们列举几种常用的循环。
(1)标准循环
--- - name: 测试标准的循环 hosts: cloud tasks: - shell: echo "{{ item }}" with_items: - one - two
(2)哈希表循环
比如,我们有如下哈希变量:
--- username: demo_user1 palce: yj-q username: demo_user2 palce: sz-b
如果想将哈希表所有用户的username以及place的值全部循环读出来,那么这个编排的哈希循环就应该这么写:
tasks: - name: read user username as well as place records debug: msg="User {{ item.key }} is {{ item.value.username }} ({{ item.value.palce }})" with_dict: "{{users}}"
(3)文件列表循环
with_fileglob可以以非递归的方式来模式匹配单个目录中的文件。
--- - hosts: cloud name: copy files to cloud tasks: - file: dest=/data/www state=directory - copy: src={{ item }} dest=/data/www/ owner=www with_fileglob: - /home/user/www/*
(4)并行数据集收集循环
并行数据集收集循环在运维时使用的频率不高,使用with_together即可做到。
变量数据源如下:
--- softwares: [ 'apache2', 'mysql', 'php' ] versions: [ 2, 5, 7 ]
如果目标是想得到( 'apache2', 2 )、( 'mysql',5 )这样的数据,那么就可以使用with_together,如下:
tasks: - debug: msg=" the {{ item.0 }} version is {{ item.1 }}" with_together: - "{{softwares}}" - "{{versions}}"
(5)整数循环
with_sequence可以以升序的数字顺序生成一组序列,并且你可以指定起始值、终止值,以及一个可选的步长值,指定参数时使用key=value这种键值对的方式。数字值可以被指定为十进制、十六进制,或者八进制:
--- - hosts: cloud name: 创建apache映射的10个目录,8000至8010 tasks: - file: dest=/app/www/apache/proxy/{{ item }} state=directory with_sequence: start=8000 end=8010 stride=1
(6)do-until循环
- name: 测试do-until循环 hosts: cloud tasks: - shell: echo "error" register: result until: result.stdout.find("okay") ! = -1 retries: 5 delay: 10
上面的例子是递归运行Shell模块,直到模块结果中的stdout输出中包含"okay"字符串为止,或者该任务按照10秒的延迟重试5次。"retries"和"delay"的默认值分别为3和5。
7. roles
通过上面学习的ansible-playbook,我们已经大概懂得了如何使用playbook,但是如果你使用的task任务数量很多,并且有些tasks发现会重复等情况,那该如何去组织一个playbook的良好编排呢?我们可将不同类型的模板进行封装,最终让编排去加载vars、tasks、files等。不错,Ansible框架已经封装了这样的框架——roles,基于roles对内容进行分组,使得我们可以容易地与其他用户分享roles。
roles用于层次性、结构化地组织playbook, roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles,只需要在playbook中使用include命令即可。简单地讲,roles就是通过分别将变量(vars)、模板(templates)、任务(tasks)、文件(files)及处理器(handlers)等放置于单独的目录中,并可以便捷地包含(include)它们的一种机制。
我们建立一个简单的项目来详细讲解,先建立一个roles,以及roles结构文件目录:
user@ops-admin:~$ sudo mkdir -p /etc/ansible/roles/curl/{files, templates, tasks, handlers, vars, defaults, meta}
目录(详细配置)如下:
user@ops-admin:/etc/ansible/roles/$ tree -L 2 . ├── curl │ ├── defaults │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars └── ... ...
这些文件的作用如下。
● defaults:默认寻找路径。
● files:文件存储目录。
● handlers :notify调用部分playbook存放路径。
● meta:角色依赖存储目录。
● tasks:存放playbooks的目录。
● templates:存储模板文件的目录。
● vars:存储变量的目录。
从roles文件的目录来看,这些文件目录的分门别类就好比程序开发者的设计模式,不同类型的文件放在不同的包目录下,roles模式也是一样,这样的好处很多,无论你是运维的主机数目有成千上万台还是你的角色任务非常多,有了roles模式的话,文件的存放就更加有规律,重复的模块可以只写一次却可以被多次include使用。
现在,curl就是我们第一个roles的名称,而这个roles的工作流程是写在tasks/main.yml之中的。现在打开curl/tasks/main.yml并在其中写入以下内容:
--- - name: install curl apt: name: curl
接着打开playbook.yml文件,修改为以下内容:
--- - hosts: ironman roles: - { role: curl, become: yes }
如上所示,运行playbook时会执行curl这个我们刚定义好的roles。其中,become代表我们要提权(等效于Unix/Linux中的sudo指令)来运行当前工作。
更加详细的配置过程可以在官方文档(http://docs.ansible.com/ansible/)中找到。
8. Ansible部署容器
本书主要讲解的是容器云运维,因此,就不得不提Ansible中的docker_container模块了,它是一个核心模块,默认随Ansible一起安装。
下面用Ansible演示如何在几台服务器中部署Nginx容器(节点已安装Docker)。
task配置如下:
--- - name: nginx container docker: name: nginx image: nginx state: reloaded ports: - "::" cap_drop: all cap_add: - setgid - setuid pull: always restart_policy: on-failure restart_policy_retry: 3 volumes: - /some/nginx.conf:/etc/nginx/nginx.conf:ro tags: - docker_container - nginx ...
然后启动,因为还没有讲解Docker的相关知识,这里了解一下Ansible的相关模块,知道使用Ansible可以轻松初始化Docker服务即可。目前Ansible与Docker有关的模块有下面几个,都非常容易使用,文档也详细:
● docker (D)——管理Docker容器(弃用)。
● docker_container——管理Docker容器。
● docker_image——管理Docker镜像。
● docker_image_facts——查看镜像详情。
● docker_login——登录Docker镜像仓库。
● docker_network——管理Docker网络。
● docker_service——管理Docker服务和容器。