1. 自动化运维工具Ansible

1.1 概述

1.1.1 IT自动化的好处

(1)团队影响:

  • 节省时间,提高工作效率
  • 消除重复任务
  • 更少的错误风险
  • 改善协作和工作满意度

(2)企业影响:

  • 克服复杂性
  • 更多创新资源
  • 加强问责制和合规性

1.1.2 Ansible是什么

它是一个简单的自动化引擎,可完成配置管理

(1)简单—减少学习成本

  • 易读的描述语言
  • 无需特殊编码技能
  • 任务按顺序执行

(2)强大—协调应用程序生命周期

  • 应用部署
  • 配置管理
  • 工作流程编排

(3)无代理—可预测,可靠和安全

  • 无代理架构
  • 使用OpenSSH通信
  • 没有代理维护成本

1.1.3 Ansible架构

wKiom1iNsjfzNEh_AABncFDwJ78260.jpg

(1)INVENTORY

Ansible 可同时操作属于一个组的多台主机,组和主机之间的关系通过 inventory 文件配置. 默认的文件路径为 /etc/ansible/hosts

除默认文件外,你还可以同时使用多个 inventory 文件(后面会讲到),也可以从动态源,或云上拉取 inventory 配置信息.

(2)API

供第三方程序调用的应用程序编程接口

(3)MODULES

ansible执行命令的功能模块,多数为内置的核心模块,也可自定义。Ansible2.2版本提供模块有500多个,下面应用到核心模块有synchronize(备份)、copy(恢复)、shell(nas获取)、cron(定时任务)、user(密码修改)、zypper(软件包管理rpm)、setup(获取facts);扩展模块有UPStartItem(启动项管理),UPInstall(软件包管理)、UPUPload(文件上传)

(4)PLUGINS

模块功能的补充,如连接类型插件、循环插件、变量插件、过滤插件等,该功能不常用。

(5)PLAYBOOK

任务剧本(任务集),编排定义ansible任务集的配置文件,有ansible顺序依次执行,通常是JSON格式的YML文件

1.1.4 Ansible特性

**模块化:**调用特定的模块,完成特定的任务;

基于Python语言研发,由Paramiko, PyYAML和Jinja2三个核心库实现;

部署简单:agentless;

支持自定义模块,使用任意编程语言;

强大的playbook机制;

幂等性;

1.2 安装配置Ansible

1.2.1 安装及程序环境

程序:

ansible

ansible-playbook

ansible-doc

配置文件:

/etc/ansible/ansible.cfg

主机清单:

/etc/ansible/hosts

插件目录:

/usr/share/ansible_plugins/

1.2.2 安装

# 方式1:RedHat系列,Debian等推荐
[root@gzr ~]# yum install ansible -y
[root@gzr ~]# ansible --version
ansible 2.8.1
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Jun 20 2019, 20:27:34) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
# 方式2:采用pip安装
[root@gzr ~]# pip3 install ansible

# 方式3:git拉取安装
[root@gzr ~]# git clone https://release.ansible.com/ansible or https://github.com/ansible/ansible.git

1.2.3 配置

# 修改配置文件,指定目标主机
[root@gzr ~]# vim /etc/ansible/hosts 
# 增加以下内容
[webservers]
172.16.4.12 ansible_ssh_user=root ansible_ssh_pass=xxx
172.16.4.13 ansible_ssh_user=root ansible_ssh_pass=xxx

# 如果不想获取交互信息,只需在/etc/ansible/ansible.cfg修改如下内容:
host_key_checking = False

# 命令行操作主机,查看磁盘空间
[root@gzr ~]# ansible webservers -a "df -h"
172.16.4.13 | CHANGED | rc=0 >>
Filesystem               Size  Used Avail Use% Mounted on
/dev/mapper/centos-root   50G   16G   35G  31% /
......

172.16.4.12 | CHANGED | rc=0 >>
Filesystem               Size  Used Avail Use% Mounted on
/dev/mapper/centos-root   50G   24G   27G  48% /
.......

# 查看主机组的内存使用情况
[root@gzr ~]# ansible webservers -a "free -m"
172.16.4.12 | CHANGED | rc=0 >>
              total        used        free      shared  buff/cache   available
Mem:          31986        3891         269           8       27825       24040
Swap:             0           0           0

172.16.4.13 | CHANGED | rc=0 >>
              total        used        free      shared  buff/cache   available
Mem:          15859        2555        2411           1       1892       12698
Swap:             0           0           0

(1) /etc/ansible/ansible.cfg配置文件

[defaults]

# some basic default values...
# 被管理主机的的清单
inventory      = /etc/ansible/hosts

# 设定并发数
forks          = 5

become_user=root

# 操作主机的端口
remote_port    = 22

# 不进行指纹添加
host_key_checking = False

# SSH timeout
timeout = 1

# 开启日志
log_path = /var/log/ansible.log

#设定秘钥使用的地址,秘钥的认证的私钥
private_key_file = /root/.ssh/id_rsa

1.2.4 ansible命令的使用

Usage: ansible [options]

常用选项:

-m MOD_NAME

-a MOD_ARGS

配置Host Inventory:

/etc/ansible/hosts

[group_id]

HOST_PATTERN1

HOST_PATTERN2

1.3 Ansible使用要求

1.3.1 服务端要求

  • Python 2.6/2.7/3.x
  • RedHat, Debian, CentOS, OSX等,不支持WIndows

1.3.2 被管理端要求

  • OpenSSH
  • Python 2.6/2.7/3.x

1.4 Inventory主机的清单

1.4.1 未分组的主机

# Ungrouped hosts, specify before any group headers.

## green.example.com
## blue.example.com
## 192.168.10.1
## 192.168.10.1

1.4.2 webservers分组

# Ex 2: A collection of hosts belonging to the 'webservers' group
# 可以采用主机名或者IP

## [webservers]
## alpha.example.org
## beta.example.org
## 192.168.1.10
## 192.168.1.11
# If you have multiple hosts following a pattern you can specify
# them like this:

## www[001:006].example.com

1.4.3 dbservers分组

# Ex 3: A collection of database servers in the 'dbservers' group

## [dbservers]
## 
## db01.intranet.mydomain.net
## db02.intranet.mydomain.net
## 1.25.1.56
## 1.25.1.57

# Here's another example of host ranges, this time there are no
# leading 0s:

## db-[99:11]-node.example.com

1.4.4 操作实例

(1)操作前的准备

以下操作,需要在操作主机和被操控主机上实现SSH免密登录。

# 在操控主机上生成秘钥:
ssh-keygen -t rsa -P ''
# 将操控主机上id_rsa.pub文件内容复制至被操控主机的authorized_keys上
cat id_rsa.pub >> authorized_keys
chmod 600 authorized_keys
scp authorized_keys 172.16.4.13:/root/.ssh
scp authorized_keys 172.16.4.14:/root/.ssh

# 执行Ansible的ping模块,查看两台主机的连通性。
ansible webservers -m ping

# 最后记得利用ansible同步下所有主机的时间,以免造成混乱
ansible all -a 'ntpdate 1.1.0.1'

例如:/etc/ansible/hosts主机配置如下:

172.16.4.12 ansible_ssh_user=root ansible_ssh_pass=gzr_2019.lzu

[webservers]
172.16.4.13 ansible_ssh_user=root ansible_ssh_pass=gzr_2019.lzu
172.16.4.14 ansible_ssh_user=root ansible_ssh_pass=gzr_2019.lzu

# all会包含所有的主机,也可以指定IP来查看
[root@ansible ~]# ansible all -a "free -m"
172.16.4.13 | CHANGED | rc=0 >>
              total        used        free      shared  buff/cache   available
Mem:          15859        2518        2412           1       1928       12735
Swap:             0           0           0

172.16.4.14 | CHANGED | rc=0 >>
              total        used        free      shared  buff/cache   available
Mem:          15859        3153         754           1       11950       12016
Swap:             0           0           0

172.16.4.12 | CHANGED | rc=0 >>
              total        used        free      shared  buff/cache   available
Mem:          31986        3851         284           8       27849       24079

1.4.5 ansible命令集

官方文档:

https://docs.ansible.com/ansible/latest/user_guide/command_line_tools.html?highlight=line#

# Ansibe AD-Hoc 临时命令执行工具,常用于临时命令的执行/usr/bin/ansible  # Ansible 模块功能查看工具/usr/bin/ansible-doc# 比如获取模块列表ansible-doc -l# 获取指定模块的使用帮助ansible-doc -s MOD_NAME# 下载/上传优秀代码或Roles模块的官网平台,基于网络的/usr/bin/ansible-galaxy # Ansible 定制自动化的任务集编排工具/usr/bin/ansible-playbook # Ansible远程执行命令的工具(使用较少,海量机器时使用,对运维架构能力要求较高)/usr/bin/ansible-pull # Ansible 文件加密工具/usr/bin/ansible-vault # Ansible基于Linux Consoble界面可与用户交互的命令执行工具/usr/bin/ansible-console  

1.4.6 Inventory功用

# 主机变量,即可以在主机后添加的变量,比如/etc/ansible/hosts下[webservers]172.16.4.13 ansible_ssh_user=root ansible_ssh_pass=gzr_2019.lzu http_port=80172.16.4.14 ansible_ssh_user=root ansible_ssh_pass=gzr_2019.lzu http_port=80[root@ansible ~]# ansible webservers -a "echo {{http_port}}"172.16.4.13 | CHANGED | rc=0 >>80172.16.4.14 | CHANGED | rc=0 >>80

http_port=80即为定义的主机变量

  • Assigning a variable to many machines: group variables
[webservers]172.16.4.13 ansible_ssh_user=root ansible_ssh_pass=gzr_2019.lzu http_port=80172.16.4.14 ansible_ssh_user=root ansible_ssh_pass=gzr_2019.lzu[webservers:vars]http_port=8080server_name=www.baidu.com# 组变量并不会覆盖主机变量[root@ansible ~]# ansible webservers -a "echo {{http_port}}"172.16.4.14 | CHANGED | rc=0 >>8080172.16.4.13 | CHANGED | rc=0 >>80# 此外上述变量还可以写到/etc/ansible/group_vars下,但此处是以yaml格式书写的[root@ansible ~]# mkdir /etc/ansible/group_vars[root@ansible ~]# vim /etc/ansible/group_vars/webservers.yamlhttp_port: 8080server_name: www.baidu.com# 为了演示,可以去除/etc/ansible/hosts的[webservers:vars][root@ansible ~]# ansible webservers -a "echo {{server_name}}"172.16.4.13 | CHANGED | rc=0 >>www.baidu.com172.16.4.14 | CHANGED | rc=0 >>www.baidu.com

1.5 ad-hoc命令

官方文档:https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html

1.5.1 常用选项

1563372457989

# -m指定模块,-a指定命令[root@ansible ~]# ansible webservers -m shell -a "echo 123 >> /tmp/123"172.16.4.13 | CHANGED | rc=0 >>172.16.4.14 | CHANGED | rc=0 >>You have new mail in /var/spool/mail/root[root@ansible ~]# ansible webservers -m shell -a "cat /tmp/123"172.16.4.13 | CHANGED | rc=0 >>123172.16.4.14 | CHANGED | rc=0 >>123# --list-hosts列出目标主机[root@ansible ~]# ansible webservers --list-hosts  hosts :    172.16.4.13    172.16.4.14# -vvv将执行结果的过程输出,详细[root@ansible ~]# ansible webservers -vvv -a "ls /tmp/123"ansible 2.8.1  config file = /etc/ansible/ansible.cfg.......<172.16.4.13> ESTABLISH SSH CONNECTION FOR USER: root.......<172.16.4.14> ESTABLISH SSH CONNECTION FOR USER: root.......172.16.4.14 | CHANGED | rc=0 >>/tmp/123172.16.4.13 | CHANGED | rc=0 >>/tmp/123META: ran handlersMETA: ran handlers# 

1.6 SSH口令与秘钥认证

修改/etc/ansible/hosts中的文件,加入

ansible_ssh_user=root

ansible_ssh_pass = xxxx

1.6.1 创建秘钥认证

[root@ansible ~]# ssh-keygenGenerating public/private rsa key pair.Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa.Your public key has been saved in /root/.ssh/id_rsa.pub.The key fingerprint is:SHA256:R7lrpSUITDla3DVzyCYKzvTBMI70+ELNYiI1eYnxRko root@ansibleThe key's randomart image is:+---[RSA 2048]----+|  E++=.o o+..    || +o%=+B o +=     ||o B=B=o+ oo      ||.+ ++ o. o .     ||  . .   S + o    ||   .     . *     ||          +      ||         .       ||                 |+----[SHA256]-----+

有两种方式添加主机的秘钥到其他主机,方便免认证:

**(1)方式1:**手动将id_rsa.pub内容拷贝到其他需要访问的主机的authorized_keys内

[root@ansible ~]# cat ./.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5Cq/7JR+9s5fDnLfgTK6YIyMAS52oK9kIVwdbyKXCGJM1uIJ1b8sZn3s5yI5AheYyhluXEw8RzmDz2sfJrp6sZJV2yc2uoMePLt+d09fL0W/fzVNvUmQBmyBcY2AS5Y2Jorc2AjLqDKDZ6SkQmzKNFJVP8Uz49HKPcGrT7VFx1jU81gwjUbeshglQpXpEJuAzRCOZURl3j9HI9gMUnKnfsvf0ty6dK/AZPCU/mAid0uwsi96RSGfCWqqTuOmb6ObtbrKdziBxt4ZwHUgAM4dqC9HM558RlEohqHEfNrq6YY5x9zCEfE619XinkkTz3jvNbkgGECASnTqVjNcOD0iR root@ansible[root@k8s-node1 ~]# ls ./.ssh/authorized_keys 

(2)方式2:采用ssh-copy-id自动拷贝到目标主机

[root@ansible ~]# ssh-copy-id root@172.16.4.13/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keysroot@172.16.4.13's password: Number of key(s) added: 1Now try logging into the machine, with:   "ssh 'root@172.16.4.13'"and check to make sure that only the key(s) you wanted were added.[root@k8s-node1 ~]# cat ./.ssh/authorized_keys .....ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5Cq/7JR+9s5fDnLfgTK6YIyMAS52oK9kIVwdbyKXCGJM1uIJ1b8sZn3s5yI5AheYyhluXEw8RzmDz2sfJrp6sZJV2yc2uoMePLt+d09fL0W/fzVNvUmQBmyBcY2AS5Y2Jorc2AjLqDKDZ6SkQmzKNFJVP8Uz49HKPcGrT7VFx1jU81gwjUbeshglQpXpEJuAzRCOZURl3j9HI9gMUnKnfsvf0ty6dK/AZPCU/mAid0uwsi96RSGfCWqqTuOmb6ObtbrKdziBxt4ZwHUgAM4dqC9HM558RlEohqHEfNrq6YY5x9zCEfE619XinkkTz3jvNbkgGECASnTqVjNcOD0iR root@ansible
  • 同理可以将172.16.4.14也加入到免交互集群中。

此时在/etc/ansible/hosts可以采用免密登录,去除ansible_ssh_pass字段

[webservers]172.16.4.13 ansible_ssh_user=root http_port=80172.16.4.14 ansible_ssh_user=root   

1.7 Ansible 常用模块

1.7.1 Shell模块与sudo使用

在远程主机上调用shell解释器运行命令,支持shell的各种功能,例如管道等.

注意:command和shell模块的核心参数直接为命令本身;而其它模块的参数通常为“key=value”格式;

官方文档:https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html

# 1. 以普通用户执行权限,注意gzr是172.16.4.13/14上的用户[root@ansible ~]# ansible webservers -a "pwd" -u gzr -kSSH password: 172.16.4.13 | CHANGED | rc=0 >>/home/gzr172.16.4.14 | CHANGED | rc=0 >>/home/gzr# 2. 给普通用户提权在13和14两个节点上的/etc/sudoers添加以下内容gzr     ALL=(ALL)       ALL# 3. 测试是否提权成功,-k输入普通用户gzr的密码,sudo密码也是gzr的密码,是一致的。--ask-become-pass可以替换为大写的K。[root@ansible ~]# ansible webservers -m shell -a "ls /root" -u gzr -k --become --become-user root --ask-become-passSSH password: BECOME password[defaults to SSH password]: 172.16.4.13 | CHANGED | rc=0 >>anaconda-ks.cfgkubeletkubelet.shkube-proxykube-proxy.shnode.zip172.16.4.14 | CHANGED | rc=0 >>anaconda-ks.cfgkubeletkubelet.shkube-proxykube-proxy.shnode.zip# 4. 批量修改其他主机特定用户的密码[root@lb7-01 ~]# ansible webservers -m shell -a "echo CentOS | passwd --stdin helloansible"172.16.4.14 | CHANGED | rc=0 >>Changing password for user helloansible.passwd: all authentication tokens updated successfully.172.16.4.13 | CHANGED | rc=0 >>Changing password for user helloansible.passwd: all authentication tokens updated successfully.# 可以登录成功[root@lb7-01 ~]# ssh helloansible@172.16.4.13helloansible@172.16.4.13's password: [helloansible@web-01 ~]$

1.7.2 Copy模块

用法:(1) 复制文件 (注意,如果要传送文件,主机的指定目录必须存在,否则提示错误,如果源目录下有同名文件,不会提示,会直接覆盖掉,所以操作时留心。)-a "src='#'"  "(2) 给定内容生成文件-a "content=  dest=  "其它参数:mode, owner, group, ...
# 将源主机的/root/tarfile/Python-3.7.4.tar文件拷贝到目标主机的/tmp[root@ansible ~]# ansible webservers -m copy -a "src=/root/tarfile/Python-3.7.4.tar dest=/tmp" -u root# 采用shell模块命令查看目标主机相应的文件夹[root@ansible ~]# ansible webservers -m shell -a "ls /tmp"172.16.4.14 | CHANGED | rc=0 >>123ansible_command_payload_E7IndaPython-3.7.4.tar172.16.4.13 | CHANGED | rc=0 >>123ansible_command_payload_CFnyN8Python-3.7.4.tar

1.7.3 File模块

用法:(1) 创建目录:-a "path=  state=directory"(2) 创建链接文件:-a "path=  src='#'" /p>(3) 删除文件:-a "path=  state=absent“

File模块主要用于改变文件权限,或者创建文件

# 注意dest指定创建的目录路径,mode表示权限,state表示创建的文件的类型,此处为目录# state的取值可为:“directory,link,hard,touch,absent”[root@ansible ~]# ansible webservers -m file -a "dest=/tmp/abc mode=600 owner=gzr group=gzr state=directory"# 删除目录[root@ansible ~]# ansible webservers -m file -a "dest=/tmp/abc mode=600 state=absent"# 创建文件[root@ansible ~]# ansible webservers -m file -a "dest=/tmp/abc mode=600 state=touch"

1.7.4 yum模块

此模块主要用于安装和管理包模块的

用法:-a ""(1) name=  state={present|latest}(2) name=  state=absent

注意:首先,确定主机的yum源是可用的,否则会安装失败

# state=present表示安装,state可以指定latest值,表示安装最新,当然在name里面也可以设置,比如memcached-latest,state=absent表示卸载该软件包,name表示要安装的软件包名。[root@ansible ~]# ansible webservers -m yum -a "name=memcached state=present"[root@k8s-node1 ~]# rpm -qa | grep memcamemcached-1.4.15-1.el7_3.1.x86_64

1.7.5 User和Group模块

Group模块

增加或删除组

用法:

-a ""

name=

state=

system=

gid=

User模块

用户管理

使用格式:

name= : 创建的用户名

state= : present新增,absent删除

force= : 删除用户的时候删除家目录

system= : 创建系统用户

uid= : 指定UID

shell= : 指定shell

home= : 指定用户家目录

#利用ansible的user模块状态用户时要注意在password参数的后边添加密文,否则不能登陆用户,通过Python的pip程序安装passlib即可为密码加密[root@ansible ~]# pip3 install passlib>>> from passlib.hash import pbkdf2_sha256>>> hash = pbkdf2_sha256.hash("123456")>>> hash'$pbkdf2-sha256$29000$.V9Lae0dgxCi1FpL6T1HqA$EMWt.X5YqC1Du276Oz/3KFAQhVylF/fbl1tKHUjeaCk'[root@ansible ~]# ansible webservers -m user -a "name=test password=$pbkdf2-sha256$29000$.V9Lae0dgxCi1FpL6T1HqA$EMWt.X5YqC1Du276Oz/3KFAQhVylF/fbl1tKHUjeaCk"# 查看是否生效[root@ansible ~]# ansible webservers -a "tail -1 /etc/shadow"172.16.4.13 | CHANGED | rc=0 >>test:-sha2569000$.V9Lae0dgxCi1FpL6T1HqA.X5YqC1Du276Oz/3KFAQhVylF/fbl1tKHUjeaCk:18097:0:99999:7:::172.16.4.14 | CHANGED | rc=0 >>test:-sha2569000$.V9Lae0dgxCi1FpL6T1HqA.X5YqC1Du276Oz/3KFAQhVylF/fbl1tKHUjeaCk:18097:0:99999:7:::# 删除用户[root@ansible ~]# ansible webservers -m user -a "name=test state=absent"172.16.4.14 | SUCCESS => {    "ansible_facts": {        "discovered_interpreter_python": "/usr/bin/python"    },     "changed": false,     "name": "test",     "state": "absent"}172.16.4.13 | CHANGED => {    "ansible_facts": {        "discovered_interpreter_python": "/usr/bin/python"    },     "changed": true,     "force": false,     "name": "test",     "remove": false,     "state": "absent"}# 创建服务的用户,不希望其登录,可以指定/nologin[root@ansible ~]# ansible webservers -m user -a "name=test password=$pbkdf2-sha256$29000$.V9Lae0dgxCi1FpL6T1HqA$EMWt.X5YqC1Du276Oz/3KFAQhVylF/fbl1tKHUjeaCk shell=/sbin/nologin"# 添加一个组[root@lb7-01 ~]# ansible all -m group -a "name=gansible system=true"[root@lb7-02 ~]# getent group gansiblegansible:x:992:# 删除一个组[root@lb7-01 ~]# ansible all -m group -a "name=gansible state=absent"

1.7.6 git模块

git模块主要是拉取安装包,使用git命令需要在远程主机上

[root@k8s-node1 ~]# yum install -y git[root@k8s-node2 ~]# yum install -y git# 注意dest指定的目录,必须为目标主机的空目录[root@ansible ~]# ansible webservers -m git -a "repo=https://github.com/ansible/ansible.git dest=/tmp/ansible"172.16.4.13 | CHANGED => {    "after": "b378e885ede115dccea0e3a6eca1664885d2836c",     "ansible_facts": {        "discovered_interpreter_python": "/usr/bin/python"    },     "before": null,     "changed": true}172.16.4.14 | CHANGED => {    "after": "b378e885ede115dccea0e3a6eca1664885d2836c",     "ansible_facts": {        "discovered_interpreter_python": "/usr/bin/python"    },     "before": null,     "changed": true}

1.7.7 Service模块

管理服务(启动,停止,重启等)的,在各目标主机上确保服务在运行

用法:

用法:

-a ""

name=

state= started stopped restarted

enabled= true , false

runlevel=

[root@lb7-01 ~]# ansible-doc -s service- name: Manage services  service:      arguments:             # Additional arguments provided on the command line.      enabled:               # Whether the service should start on boot. *At least one of state and enabled are required.*      name:                  # (required) Name of the service.      pattern:               # If the service does not respond to the status command, name a substring to look for as would be found in the                               output of the `ps' command as a stand-in for a status result. If the string is                               found, the service will be assumed to be started.      runlevel:              # For OpenRC init scripts (e.g. Gentoo) only. The runlevel that this service belongs to.      sleep:                 # If the service is being `restarted' then sleep this many seconds between the stop and start command. This                               helps to work around badly-behaving init scripts that exit immediately after                               signaling a process to stop. Not all service managers support sleep, i.e when                               using systemd this setting will be ignored.      state:                 # `started'/`stopped' are idempotent actions that will not run commands unless necessary. `restarted' will                               always bounce the service. `reloaded' will always reload. *At least one of                               state and enabled are required.* Note that reloaded will start the service if                               it is not already started, even if your chosen init system wouldn't normally.      use:                   # The service module actually uses system specific modules, normally through auto detection, this setting can                               force a specific module. Normally it uses the value of the                               'ansible_service_mgr' fact and falls back to the old 'service' module when none                               matching is found.
# state有started,stopped,restarted三个值。[root@ansible ~]# ansible webservers -m service -a "name=memcached state=started"# 检查是否启动[root@k8s-node1 ~]# ps -ef |grep memcachedroot      81496  79624  0 15:32 pts/0    00:00:00 grep --color=auto memcached[root@k8s-node1 ~]# ps -ef |grep memcachedmemcach+  82492      1  0 15:37 ?        00:00:00 /usr/bin/memcached -u memcached -p 11211 -m 64 -c 124root      82709  79624  0 15:39 pts/0    00:00:00 grep --color=auto memcached# 将memcached加入开机启动,设置enabled=true[root@ansible ~]# ansible webservers -m service -a "name=memcached enabled=true"

1.7.8 Setup模块

用于收集系统的临时信息,这些将在playbooks部分描述,主要是系统的已发现变量比如:ansible_nodename,ansible_os_family,ansible_processor,ansible_processor_cores,ansible_python_version。这些可以用于实现任务的条件执行。

[root@ansible ~]# ansible all -m setup# 比如我想查看ansible内的主机名,可以采用filter字段进行过滤[root@ansible ~]# ansible webservers -m setup -a "filter=ansible_nodename"172.16.4.13 | SUCCESS => {    "ansible_facts": {        "ansible_nodename": "k8s-node1",         "discovered_interpreter_python": "/usr/bin/python"    },     "changed": false}......# 查看与内存相关的信息,可以使用通配符[root@ansible ~]# ansible webservers -m setup -a "filter=ansible_*_mb"172.16.4.14 | SUCCESS => {    "ansible_facts": {        "ansible_memfree_mb": 197,         "ansible_memory_mb": {            "nocache": {                "free": 11492,                 "used": 4367            },             "real": {                "free": 197,                 "total": 15859,                 "used": 15662            },             "swap": {                "cached": 0,                 "free": 0,                 "total": 0,                 "used": 0            }        },         "ansible_memtotal_mb": 15859,         "ansible_swapfree_mb": 0,         "ansible_swaptotal_mb": 0,         "discovered_interpreter_python": "/usr/bin/python"    },     "changed": false}......

1.7.***模块

探测主机是否存活

# 探测目标主机是否存活[root@lb7-01 ~]# ansible-doc -s ping- name: Try to connect to host, verify a usable python and return `pong' on success  ping:      data:                  # Data to return for the `ping' return value. If this parameter is set to `crash', the module will cause an                               exception.# 测试所有主机的连通性[root@lb7-01 .ssh]# ansible all -m ping172.16.4.14 | SUCCESS => {    "ansible_facts": {        "discovered_interpreter_python": "/usr/bin/python"    },     "changed": false,     "ping": "pong"}172.16.4.13 | SUCCESS => {    "ansible_facts": {        "discovered_interpreter_python": "/usr/bin/python"    },     "changed": false,     "ping": "pong"}172.16.4.12 | SUCCESS => {    "ansible_facts": {        "discovered_interpreter_python": "/usr/bin/python"    },     "changed": false,     "ping": "pong"}

1.7.1 command模块(ansible默认的模块就是command)

在远程主机执行命令;

[root@lb7-01 ~]# ansible-doc -s command- name: Execute commands on targets  command:      argv:                  # Passes the command as a list rather than a string. Use `argv' to avoid quoting values that would otherwise be                               interpreted incorrectly (for example "user name"). Only the string or the list                               form can be provided, not both.  One or the other must be provided.      chdir:                 # Change into this directory before running the command.      creates:               # A filename or (since 2.0) glob pattern. If it already exists, this step *won't* be run.      free_form:             # (required) The command module takes a free form command to run. There is no actual parameter named 'free                               form'. See the examples on how to use this module.      removes:               # A filename or (since 2.0) glob pattern. If it already exists, this step *will* be run.      stdin:                 # Set the stdin of the command directly to the specified value.      stdin_add_newline:     # If set to `yes', append a newline to stdin data.      strip_empty_ends:      # Strip empty lines from the end of stdout/stderr in result.      warn:                  # Enable or disable task warnings.# 1. 让所有主机执行uname -r命令[root@lb7-01 ~]# ansible all -m command -a "uname -r"172.16.4.14 | CHANGED | rc=0 >>3.1.0-957.21.2.el7.x86_64172.16.4.13 | CHANGED | rc=0 >>3.1.0-957.21.2.el7.x86_64172.16.4.12 | CHANGED | rc=0 >>3.1.0-957.27.2.el7.x86_64# 2. 让主机上都创建一个用户[root@lb7-01 ~]# ansible all -m command -a "useradd helloansible"172.16.4.14 | CHANGED | rc=0 >>172.16.4.13 | CHANGED | rc=0 >>172.16.4.12 | CHANGED | rc=0 >># 执行查看用户是否创建成功[root@lb7-01 ~]# ansible all -m command -a "tail -1 /etc/passwd"172.16.4.14 | CHANGED | rc=0 >>helloansible:x:106:106::/home/helloansible:/bin/bash172.16.4.13 | CHANGED | rc=0 >>helloansible:x:106:106::/home/helloansible:/bin/bash172.16.4.12 | CHANGED | rc=0 >>helloansible:x:100:100::/home/helloansible:/bin/bash# 3. ansible不支持管道命令,需要借助shell命令,比如下述命令看似执行成功,实则没有。[root@lb7-01 ~]# ansible webservers -m command -a "echo CentOS | passwd --stdin helloansible"172.16.4.13 | CHANGED | rc=0 >>CentOS | passwd --stind helloansible172.16.4.14 | CHANGED | rc=0 >>CentOS | passwd --stind helloansible

1.7.11 fetch模块

从远程主机拿文件

[root@lb7-01 ~]# ansible-doc -s fetch- name: Fetch files from remote nodes  fetch:      dest:                  # (required) A directory to save the file into. For example, if the `dest' directory is `/backup' a `src' file                               named `/etc/profile' on host `host.example.com', would be saved into                               `/backup/host.example.com/etc/profile'. The host name is based on the inventory                               name.      fail_on_missing:       # When set to `yes', the task will fail if the remote file cannot be read for any reason. Prior to Ansible 2.5,                               setting this would only fail if the source file was missing. The default was                               changed to `yes' in Ansible 2.5.      flat:                  # Allows you to override the default behavior of appending hostname/path/to/file to the destination. If `dest'                               ends with '/', it will use the basename of the source file, similar to the copy                               module. Obviously this is only handy if the filenames are unique.      src:                   # (required) The file on the remote system to fetch. This `must' be a file, not a directory. Recursive fetching                               may be supported in a later release.      validate_checksum:     # Verify that the source and destination checksums match after the files are fetched.
# 示例1:从特定主机IP拿文件,下述命令执行完后,会在本机的root目录下创建一个172.16.4.12的目录,其内的目录结构有root和kubeconfig.sh文件[root@lb7-01 ~]# ansible 172.16.4.12 -m fetch -a "src=/root/kubeconfig.sh dest=/root"# 当抓去一堆文件的时候,也会创建对应的ip地址的目录,以区分文件

1.7.12 cron模块

管理计划任务条目

用法:-a ""minute=hour=day=month=weekday=job=name=user=state={present|absent}
# 示例1:创建一个同步时间的计划任务,每5分钟同步一下服务器的时间[root@lb7-01 root]# ansible all -m cron -a "minute='*/5' job='/usr/sbin/ntpdate 1.1.0.1 &> /dev/null' name='sync time'"# 登录被管理服务器,查看,确实有该服务存在[root@lb7-02 ~]# crontab -l#Ansible: sync time*/5 * * * * /usr/sbin/ntpdate 1.1.0.1 &> /dev/null# 删除计划任务[root@lb7-01 ~]# ansible all -m cron -a "name='sync time' state=absent"

1.7.13 hostname模块

管理主机名

[root@lb7-01 ~]# ansible all -a "hostname"[root@lb7-01 ~]# ansible 172.16.4.12 -m hostname -a "name=CentOS7-K8S"

1.8 Playbook基本使用

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks.html

YAML 一种数据序列化工具的语言格式

YAML is a data serialization format designed for human readability and interaction with scripting languages.

数据结构:key:value- item1- item2- item3例如{name:jerry, age:21}

PlayBook相关概念:

Tasks:任务,由模块定义的操作的列表;

Variables:变量

Templates:模板,即使用了模板语法的文本文件;

Handlers:由特定条件触发的Tasks;

Roles:角色;

playbook的基础组件:

Hosts:运行指定任务的目标主机;

remote_user:在远程主机以哪个用户身份执行;

sudo_user:非管理员需要拥有sudo权限;

tasks:任务列表

模块,模块参数:

格式:(1) action: module arguments(2) module: arguments运行playbook,使用ansible-playbook命令(1) 检测语法ansible-playbook  --syntax-check  /path/to/playbook.yaml(2) 测试运行ansible-playbook -C /path/to/playbook.yaml--list-hosts-list-tasks--list-tags(3) 运行ansible-playbook  /path/to/playbook.yaml-t TAGS, --tags=TAGS--skip-tags=SKIP_TAGS--start-at-task=START_AT

1.8.1 使用Playbook的好处

特点:

  • 易读的编排语言-YAML
  • 适合配置管理和应用部署
  • 非常适合部署复杂的工作

1563609668587

示例1:定义一个playbook任务来新增用户和组

# vim group.yml- hosts: all  remote_user: root  tasks:  - name: add a group    group: name=pbgroup system=true  - name: add a user    user: name=pbuser group=pbgroup system=true
# 检查语法错误,没有提示,表示语法没问题[root@lb7-01 ~]# ansible-playbook --syntax-check group.yml playbook: group.yml# 测试运行,—C表示仅测试跑一边,并不会真正执行[root@lb7-01 ~]# ansible-playbook -C group.yml 

1565748342776

# 也可以单独测试某些特定的选项# 比如测试仅影响的主机[root@lb7-01 ~]# ansible-playbook -C group.yml --list-hostsplaybook: group.yml  play #1 (all): all	TAGS: []    pattern: [u'all']    hosts (3):      172.16.4.14      172.16.4.13      172.16.4.12# 测试运行哪些任务[root@lb7-01 ~]# ansible-playbook -C group.yml --list-tasksplaybook: group.yml  play #1 (all): all	TAGS: []    tasks:      add a group	TAGS: []      add a user	TAGS: []    # 查看哪个任务打标了,此处没有打标,后续演示[root@lb7-01 ~]# ansible-playbook -C group.yml --list-tagsplaybook: group.yml  play #1 (all): all	TAGS: []      TASK TAGS: []# 以上没有错误,就可以正式执行任务了[root@lb7-01 ~]# ansible-playbook group.yml # 验证执行结果[root@lb7-01 ~]# ansible all -a "tail -1 /etc/passwd"172.16.4.13 | CHANGED | rc=0 >>pbuser:x:996:992::/home/pbuser:/bin/bash172.16.4.14 | CHANGED | rc=0 >>pbuser:x:995:991::/home/pbuser:/bin/bash172.16.4.12 | CHANGED | rc=0 >>pbuser:x:996:992::/home/pbuser:/bin/bash[root@lb7-01 ~]# ansible all -a "getent group pbgroup"172.16.4.13 | CHANGED | rc=0 >>pbgroup:x:992:172.16.4.14 | CHANGED | rc=0 >>pbgroup:x:991:172.16.4.12 | CHANGED | rc=0 >>pbgroup:x:992:

比如编写一个安装nginx的playbook文件,大致如下:

---- hosts: webservers  vars:    hello: Ansible  tasks:  - name: Add repo    yum_repository:      name: nginx      description: nginx repo      baseurl: http://nginx.org/packages/centos/7/$basearch/      gpgcheck: no      enabled: 1  - name: Install nginx    yum:      name: nginx      state: latest  - name: Copy nginx configuration file    copy:      src: ./site.conf      dest: /etc/nginx/conf.d/site.conf  - name: Start nginx    service:      name: nginx      state: started  - name: Create wwwroot directory    file:      dest: /var/www/html      state: directory  - name: Create test page index.html    shell: echo "hello {{hello}}" > /var/www/html/index.html
  • 注意,一个yaml文件中,可以有多个playbook,一般来说一个playbook完成一个小任务,变量定义后可以引用,比如上述hello变量的值为Ansible,而在最后的shell模块中采取了双花括号{{hello}}进行引用。

site.conf:待传文件

server {    listen 80;    server_name www.ctnrs.com;    location / {        root /var/www/html;        index index.html;    }}
  • 运行nginx的playbook,启动服务
[root@ansible ansible-demo]# ansible-playbook nginx.yml 

在目标节点上查看服务是否启动:

[root@k8s-node1 ~]# ps -ef | grep nginxroot      91842      1  0 16:41 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.confnginx     91843  91842  0 16:41 ?        00:00:00 nginx: worker process# site.conf确实被传送到k8s-node1和k8s-node2上[root@k8s-node1 ~]# cat /etc/nginx/conf.d/site.conf # site.confserver {    listen 80;    server_name www.google.com.cn;    location / {        root /var/www/html;        index index.html;    }}# 测试页面也传过来了[root@k8s-node2 ~]# cat /var/www/html/index.html hello Ansible# 采用源主机和目标主机以外的主机进行访问,可以访问到[root@k8s-master ~]# curl 172.16.4.13 -H "Host:www.google.com.cn"hello Ansible

1.8.2 YAML语法格式

  • 缩进表示层级关系
  • 不支持制表符“tab”缩进,使用空格缩进
  • 通常开头缩进2个空格
  • 字符后缩进1个空格,如冒号,逗号等
  • “---”表示YAML格式,一个文件的开始,可有可无
  • “#” 表示注释
---- name: play1  hosts: webservers  remote_user: root  vars:    var_name: value  tasks:  - name: echo    shell: "echo {{var_name}}"- name: play2  hosts: webservers  remote_user: root  vars:    var_name: value  tasks:  - name: echo    shell: "echo {{var_name}}"                            

1.8.3 在变更时执行操作(handlers)

由特定条件出发的tasks

# 格式:tasks:- name: TASK_NAMEmodule: argumentsnotify: HANDLER_NAMEhandlers:- name: HANDLER_NAMEmodule: arguments
# 编写conf.yml,内容如下:---- hosts: webservers  gather_facts: no  remote_user: root  tasks:  - name: Copy nginx config file    copy:      src: site.conf      dest: /etc/nginx/conf.d  - name: Reload nginx    service: name=nginx state=reloaded# 启动playbook[root@ansible ansible-demo]# ansible-playbook conf.ymlPLAY [webservers] ****************************************************************************************************************************TASK [Copy nginx config file] ****************************************************************************************************************changed: [172.16.4.13]changed: [172.16.4.14]TASK [Reload nginx] **************************************************************************************************************************changed: [172.16.4.13]changed: [172.16.4.14]PLAY RECAP ***********************************************************************************************************************************172.16.4.13                : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   172.16.4.14                : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   # 更改site.conf的内容如下:server {    listen 80;    server_name www.baidu.com;    location / {        root /var/www/html;        index index.html;    }}# 采用其余主机访问[root@k8s-master ~]# curl 172.16.4.13 -H "Host:www.baidu.com"hello Ansible# 但是我们假设一下,如果conf.yml文件没有发生变化,知识site.conf文件变化,我们没有必要每次都去reload nginx.此时可以将其放入handlers中

修改conf.yml文件,使其内容如下:

---- hosts: webservers  gather_facts: no  remote_user: root  tasks:  - name: Copy nginx config file    copy:      src: site.conf      dest: /etc/nginx/conf.d    notify:    # 如果上述操作执行,则启动notify通知 reload nginx,否则不执行      - Reload nginx   handlers:  - name: Reload nginx    service: name=nginx state=reloaded                                

1563633257683

可以发现,上述操作,全为绿色,说明并没有进行reload,也没有相应的输出。

现在,我们把site.conf中的server_name值改为“www.jd.com”,再次执行ansible-playbook命令。

1563633812664

发现出现了黄色的部分,说明进行了修改。

1.8.4 任务控制(tags)与Playbook调试

给指定的任务定义一个调用标识。

# 使用格式:- name: NAMEmodule: argumentstags: TAG_ID

(1)给任务打tags

修改nginx.yml文件内容如下:

---- hosts: webservers  vars:    hello: Ansible  tasks:  - name: Add repo    yum_repository:      name: nginx      description: nginx repo      baseurl: http://nginx.org/packages/centos/7/$basearch/      gpgcheck: no      enabled: 1    tags: addrepo   # 添加标签  - name: Install nginx    yum:      name: nginx      state: latest    tags: install   # 添加标签  - name: Copy nginx configuration file    copy:      src: ./site.conf      dest: /etc/nginx/conf.d/site.conf  - name: Start nginx    service:      name: nginx      state: started  - name: Create wwwroot directory    file:      dest: /var/www/html      state: directory  - name: Create test page index.html    shell: echo "hello {{hello}}" > /var/www/html/index.html

1563672502344

也可以采用--skip-tags来不指定不执行的任务。即tags内的任务均不执行。

1563672909743

一个任务可以被打多个标签,那么需要采用序列的方式来组织:

  tags:    - tag1    - tag2    ......

(2)Playbook调试

  • --syntax-check

采用上述字段可以对yaml文件做语法检查。

  • debug

修改nginx.yml文件内容如下:

---- hosts: webservers  gather_facts: no    # 禁用facts收集系统变量  vars:    hello: Ansible  tasks:  - name: Add repo    yum_repository:      name: nginx      description: nginx repo      baseurl: http://nginx.org/packages/centos/7/$basearch/      gpgcheck: no      enabled: 1    tags: addrepo  - name: Install nginx    yum:      name: nginx      state: latest    tags: install  - name: Copy nginx configuration file    copy:      src: ./site.conf      dest: /etc/nginx/conf.d/site.conf  - name: Start nginx    service:      name: nginx      state: started  - name: Create wwwroot directory    file:      dest: /var/www/html      state: directory  - name: Create test page index.html    debug: msg="123{{hello}}"   # 采用debug的msg形式打印信息    # shell: echo "hello {{hello}}" > /var/www/html/index.html    tags: debug  # 打标签,方便调试

1563673461988

当然,如果开启gather_facts,也可以获取到系统的很多变量,将上述的gather_facts字段改为yes,同时,比如修改debug的msg字段为ansible_hostname,即可打印目标主机的名字。

debug: msg="{{ansible_hostname}}"

1563674163802

1.8.5 自动部署tomcat

tomcat部署步骤

  • 安装jdk
  • 下载tomcat包
  • 解压tomcat
  • 启动

根据上述的安装步骤,可以编写tomcat.yml文件进行部署

---- hosts: webservers  gather_facts: no  vars:    tomcat_version: 8.5.38    tomcat_install_dir: /usr/local  tasks:    - name: Install jdk1.8      yum: name=java-1.8.0-openjdk state=present         - name: Download tomcat  # 根据url下载      get_url: url=http://mirrors.hust.edu.cn/apache/tomcat/tomcat-8/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz dest=/tmp    - name: Unarchive tomcat-{{ tomcat_version }}.tar.gz      unarchive:  # 解压        src: /tmp/apache-tomcat-{{ tomcat_version }}.tar.gz        dest: "{{ tomcat_install_dir }}"        copy: no    - name: Start tomcat      shell: cd {{ tomcat_install_dir }} &&             mv apache-tomcat-{{ tomcat_version }} tomcat8 &&             cd tomcat8/bin && nohup ./startup.sh &

先做语法检查:

[root@ansible ansible-demo]# ansible-playbook tomcat.yml --syntax-checkplaybook: tomcat.yml

执行部署文件tomcat.yml

1563675277545

  • 注意,tomcat的地址随时可能发生变化,包括版本,所以可以采用wget命令测试其是否能被访问到。

在目标主机上查看tomcat是否已经启动:

[root@k8s-node1 ~]# ps -ef |grep tomcatroot      99974      1  0 1:13 ?        00:00:00 /bin/sh /usr/local/tomcat8/bin/catalina.sh startroot      99975  99974  1 1:13 ?        00:00:05 /usr/bin/java -Djava.util.logging.config.file=/usr/local/tomcat8/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat8 -Dcatalina.home=/usr/local/tomcat8 -Djava.io.tmpdir=/usr/local/tomcat8/temp org.apache.catalina.startup.Bootstrap start

1563675567563

1.9 Playbook定义变量与使用

Variables:变量类型:内建:(1) facts自定义:(1) 命令行传递;-e VAR=VALUE(2) 在hosts Inventory中为每个主机定义专用变量值;(a) 向不同的主机传递不同的变量 ;IP/HOSTNAME variable_name=value(b) 向组内的所有主机传递相同的变量 ;[groupname:vars]variable_name=value(3) 在playbook中定义vars:- var_name: value- var_name: value(4) Inventory还可以使用参数:用于定义ansible远程连接目标主机时使用的属性,而非传递给playbook的变量;ansible_ssh_hostansible_ssh_portansible_ssh_useransible_ssh_passansible_sudo_pass...(5) 在角色调用时传递roles:- { role: ROLE_NAME, var: value, ...}变量调用:{{ var_name }}

1.9.1 在命令行中定义

示例1:利用命令行传递变量来安装不同的包

# vim pkg.yml- hosts: webservers  remote_user: root  gather_facts: no  tasks:  - name: install a package    yum: name={{ pkgname }} state=present

检查出现报错

1565749600068

这是因为,在yaml文件指定了变量,我们需要在命令行传递变量值才行,所以上述报错属于正常。

[root@lb7-01 ~]# ansible-playbook -C -e pkgname=lrzsz pkg.yml 

示例2:

# 查看playbook文件内容[root@ansible ansible-demo]# ansible-playbook --list-tags nginx.ymlplaybook: nginx.yml  play #1 (webservers): webservers	TAGS: []      TASK TAGS: [addrepo, debug, install][root@ansible ansible-demo]# ansible-playbook --list-tasks nginx.ymlplaybook: nginx.yml  play #1 (webservers): webservers	TAGS: []    tasks:      Add repo	TAGS: [addrepo]      Install nginx	TAGS: [install]      Copy nginx configuration file	TAGS: []      Start nginx	TAGS: []      Create wwwroot directory	TAGS: []      Create test page index.html	TAGS: [debug]

(1)采用**-e选项**指定变量名定义:

比如实现在yml文件定义work_dir如下,并在命令行中指定。

---- hosts: webservers  gather_facts: no  remote_user: root  tasks:  - name: test var    debug: msg="{{work_dir}}"

1563676948551

1.9.2 Inventory中定义

在Inventory的/etc/ansible/hosts中也可以指定变量,上述已经说明过,比如ansible_ssh_user等。

# vim /etc/ansible/hosts[webservers]172.16.4.13  pkgname=vsftpd 172.16.4.14  pkgname=samba# vim pkg.yml 删除原有文档里变量- hosts: webservers  remote_user: root  vars:  gather_facts: no  tasks:  - name: install a package    yum: name={{ pkgname }} sate=present

测试验证:

[root@lb7-01 ~]# ansible-playbook --check pkg.yml 

第二种方法:

# vim /etc/ansible/hosts[webservers]172.16.4.13 172.16.4.14  [webservers:vars]pkgname = samba

1.9.3 playbook中定义

示例1:安装samba

- hosts: webservers  remote_user: root  vars:  - pkgname: samba  - pkgname: lrzsz  gather_facts: no  tasks:  - name: install a package    yum: name={{ pkgname }} state=present

测试也没有问题

1565750082206

思考题:假如同时利用-e的参数传递一个变量的参数的话会怎么样?

测试结果如下,是**-e传递的变量参数的优先级更高**,这样的话能避免传递参数的时候,因为文本里面定义的优先级更高而出错?

[root@lb7-01 ~]# ansible-playbook -C -e pkgname=vsftpd pkg.yml 

示例2:在playbook的yml文件中定义work_dir

---- hosts: webservers  gather_facts: no  remote_user: root  vars:    - work_dir: /usr/local # 定义了两个变量    - nginx_version: 1.17    tasks:  - name: Install nginx    debug: msg="{{work_dir}}/nginx{{nginx_version}}"

1563677444284

1.9.4 在Roles定义

1.9.5 注册变量

就是将某个任务执行的结果保存到变量中。即动态地获取变量。

---- hosts: webservers  gather_facts: no  remote_user: root  vars:    - work_dir: /usr/local    - nginx_version: 1.17    tasks:  - name: register var    command: date +"%F_%T"    register: datetime  - name: touch file    file: dest=/tmp/r{{datetime.stdout}} state=touch

1563678068551

查看目标主机目录下是否有相应目录

1563678252031

可以通过在playbook加debug模块,进行调试,查看datetime变量的内容:

  debug: msg="{{datetime}}"

156367846321

如果两个

1.9.6 系统信息变量(facts)

可以通过setup模块获取系统信息变量

[root@ansible ansible-demo]# ansible webservers -m setup

1.10 Playbook文件复用

1.10.1 include与import的区别

写的playbook文件较大时,可以更好地组织playbook。

include*(动态):在运行时导入 •--list-tags,--list-tasks不会显示到输出 •不能使用notify触发来自include*内处理程序名称(handlers)

import*(静态):在Playbook解析时预先导入 •不能与循环一起使用 •将变量用于目标文件或角色名称时,不能使用inventory(主机/主机组等)中的变量

1.10.2 import_playbook

1563678949733

(1)比如搭建lnmp和lamp的环境:

# lnmp.yml ---- import_playbook: nginx.yml- import_playbook: php.yml- import_playbook: mysql.yml# lamp.yml ---- import_playbook: tomcat.yml- import_playbook: php.yml- import_playbook: mysql.yml# nginx.yml ---- hosts: webservers  gather_facts: no  remote_user: root  tasks:  - name: install nginx    debug: msg="install nginx..."    # tomcat.yml ---- hosts: webservers  gather_facts: no  remote_user: root  tasks:  - name: install tomcat    debug: msg="install tomcat..."# mysql.yml ---- hosts: webservers  gather_facts: no  remote_user: root  tasks:  - name: install mysql    debug: msg="install mysql..."    # php.yml ---- hosts: webservers  gather_facts: no  remote_user: root  tasks:  - name: install php    debug: msg="install php..."

1563679748624

1563679769595

1.10.3 include_tasks

下图示例,include_tasks作用在于当任务比较多而杂时,可以写到其他yml文件中。

# main.yml ---- hosts: webservers  gather_facts: no  tasks:  - include_tasks: task1.yml  - include_tasks: task2.yml  # task1.yml - name: test task1  debug: msg="task1"  # task2.yml - name: test task1  debug: msg="task1"

1563680327135

1.10.4 inport_tasks

# main.yml---- hosts: webservers  gather_facts: no  tasks:  - include_tasks: task1.yml    vars:      user: zhangsan  - import_tasks: task2.yml    vars:      user: lisi      # task1.yml- name: task1  debug: msg="hello {{user}}"# task2.yml- name: task2  debug: ms g="hello {{user}}"

1563680703720

1.11 Playbook流程控制

1.11.1 条件

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html

(1)when

---- hosts: webservers  gather_facts: yes  tasks:  - name:     debug: msg="{{ansible_default_ipv4.address}}"    when: ansible_default_ipv4.address == '172.16.4.14'

156369181361

上述yaml文件执行,表示当检测到IP为172.16.4.14时,才会执行task中的debug任务。

  • 例2:根据软件包管理,来安装不同的软件。
---- hosts: webservers  tasks:  - name: Update apache version -yum    yum: name=httpd state=present    when: ansible_pkg_mgr == 'yum'    notify: restart httpd  - name: Update apache version -apt    apt: name=apache2 state=present update_cache=yes    when: ansible_pkg_mgr == 'apt'    notify: restart apache2     handlers:  - name: restart httpd    service: name=httpd state=restarted  - name: restart apache2    service: name=apache2 state=restarted

当然,在日常场景下,管理的主机有可能运行着不同的操作系统,那么也可以采用条件控制:

  tasks:  - name: "shut down CentOS 6 and Debian 7 systems"    command: /sbin/shutdown -t now    when: (ansible_distribution == "CentOS" andansible_distribution_major_version == "6") or (ansible_distribution == "Debian" andansible_distribution_major_version == "7")      tasks:  - name: "shut down CentOS 6 systems"    command: /sbin/shutdown -t now    when:      -ansible_distribution == "CentOS"      -ansible_distribution_major_version == "6"

1.11.2 循环

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html

在Ansible 2.5版本中,采用了loop替代了with_<lookup>,但是并没有完全弃用。

(1)with_list

---- hosts: webservers  gather_facts: no  tasks:  - name: with_list    debug: msg="{{item}}"    with_list:      - one      - two

1563693370794

采用loop效果是一样的:

(2)loop

---- hosts: webservers  gather_facts: no  tasks:  - name: with_list -> loop    debug: msg="{{item}}"    loop:      - 1      - 2

1563693539996

(3)with_items

---- hosts: webservers  gather_facts: no  tasks:  - name: with_items    #debug: msg="{{item}}"    user: name={{item.name}} groups={{item.groups}} state=present    with_items:      - {name: 'zhangsan', groups: 'nginx'}      - {name: 'lisi', groups: 'nginx'}

1563693982656

  • 测试“zhangsan”和"lisi"是否在nginx组中
[root@k8s-node1 ~]# id zhangsanuid=102(zhangsan) gid=102(zhangsan) groups=102(zhangsan),993(nginx)[root@k8s-node1 ~]# id lisiuid=103(lisi) gid=103(lisi) groups=103(lisi),993(nginx)

当然,创建用户也可以用loop

---- hosts: webservers  gather_facts: no  tasks:  - name: with_list -> loop    user: name={{item}} state=present    loop:      - wangwu      - zhaoliu    tags: loopadd  - name: with_items    #debug: msg="{{item}}"    user: name={{item.name}} groups={{item.groups}} state=present    with_items:      - {name: 'zhangsan', groups: 'nginx'}      - {name: 'lisi', groups: 'nginx'}    tags: itemsadd

1563694407314

1.12 Playbook模板(jinja2)

模板,文本文件,内部嵌套有模板语言脚本(使用Jinja2模板语言编写)

[root@lb7-01 ~]# ansible-doc -s template- name: Template a file out to a remote server  template:      attributes:            # The attributes the resulting file or directory should have. To get supported flags look at the man page for                               `chattr' on the target system. This string should contain the attributes in the                               same order as the one displayed by `lsattr'. The `=' operator is assumed as                               default, otherwise `+' or `-' operators need to be included in the string.      backup:                # Determine whether a backup should be created. When set to `yes', create a backup file including the timestamp                               information so you can get the original file back if you somehow clobbered it                               incorrectly.......
语法:字面量:字符串:使用单引号或双引号;数字:整数、浮点数;列表:[item1, item2, ...]元组:(item1, item2, ...)字典:{key1:value1, key2:value2, ...}布尔型:true/false算术运算:+, -, *, /, //, %, **比较操作:==, !=, >, <, >=, <=逻辑运算:and, or, not执行模板文件中的脚本,并生成结果数据流,需要使用template模块;template:-a " "src=dest=mode=onwer=group=注意:此模板不能在命令行使用,而只能用于playbook;

示例1:

# 首先利用ansible命令获取当前系统系统的cpu数量[root@lb7-01 ~]# ansible webservers -m setup | grep vcpus        "ansible_processor_vcpus": 8,         "ansible_processor_vcpus": 8, # 备份下默认文件[root@lb7-01 ~]# ansible webservers -m shell -a "cp /etc/nginx/nginx.conf{,.bak}"# 从webservers中任意一台主机传送一个配置文件过来[root@lb7-01 ~]# ansible 172.16.4.13 -m fetch -a "src=/etc/nginx/nginx.conf dest=/root"[root@lb7-01 ~]# cp 172.16.4.13/etc/nginx/nginx.conf testdir/# 重命名该文件[root@lb7-01 ~]# mv testdir/nginx.conf{,.j2}[root@lb7-01 ~]# ll testdir/nginx.conf.j2 -rw-r--r-- 1 root root 643 Aug 14 11:06 testdir/nginx.conf.j2# 为了演示,新建一个nginxservers组,虽然看上去都一样,# vim /etc/ansible/hosts[nginxservers]172.16.4.13172.16.4.14

建立playbook文档

# vim nginx.yml- hosts: nginxservers  remote_user: root  gather_facts: no  tasks:  - name: install nginx package    yum: name=nginx state=latest    tags: nginxinstall  - name: install conf file    template: src=/root/testdir/nginx.conf.j2 dest=/etc/nginx.conf    tags: nginxconf    notify: reload nginx service  - name: start nginx service    service: name=nginx state=started enabled=true  handlers:  - name: reload nginx service    shell: /usr/bin/nginx -s reload

此处为了演示可以先卸载原来的nginx包

[root@lb7-01 ~]# ansible nginxservers -m yum -a "name=nginx state=absent"# 确认安装包卸载掉,并且服务没在线[root@lb7-01 ~]# ansible nginxservers -a "ss -tnl"# 检查playbook的文件没有语法错误 [root@lb7-01 ~]# ansible-playbook --syntax-check nginx.yml # 测试运行[root@lb7-01 ~]# ansible-playbook -C nginx.yml  # 正式运行[root@lb7-01 ~]# ansible-playbook -C nginx.yml # 确认80端口打开着[root@lb7-01 ~]# ansible nginxservers -a "ss -tnl"172.16.4.13 | CHANGED | rc=0 >>State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              LISTEN     0      128    127.0.0.1:1248                    *:*                  LISTEN     0      128    127.0.0.1:1249                    *:*                  LISTEN     0      128    172.16.4.13:1250                    *:*                  LISTEN     0      128    172.16.4.13:2379                     *:*                  LISTEN     0      128    127.0.0.1:2379                     *:*                  LISTEN     0      128    172.16.4.13:2380                     *:*                  LISTEN     0      128    172.16.4.13:1255                    *:*                  LISTEN     0      128          *:80                       *:*                  ......

1.12.1 条件与循环

when语句:在tasks中使用,Jinja2的语法格式;

示例:利用ansible条件测试,在CentOS6和CentOS7上启动服务,为了演示需要添加一台CentOS 6的服务器到webservers。

[root@lb7-01 ~]# ansible webservers -m setup | less# 在CentOS 7上可以找到该行		"ansible_distribution": "CentOS",         "ansible_distribution_major_version": "7",         "ansible_distribution_release": "Core",         "ansible_distribution_version": "7", # 在CentOS 6上可以找到该行 		"ansible_distribution": "CentOS",         "ansible_distribution_major_version": "6",         "ansible_distribution_release": "Final",         "ansible_distribution_version": "6.8",

根据以上的信息,我们就可以创建一个基于条件判断的playbook文件test.yml

# vim test.yml- hosts: webservers  remote_user: root  tasks:  - name: install nginx package    yum: name=nginx state=present  - name: start nginx service on CentOS6    shell: service nginx start    when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "6"  - name: start nginx service on CentOS7    shell: systemctl start nginx.service    when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "7"

循环:迭代,需要重复执行的任务;

对迭代项的引用,固定变量名为"item”,使用with_item属性给定要迭代的元素;

元素:列表

字符串

字典

基于字符串列表给出元素示例:

# vim circulation.yml- hosts: webservers  remote_user: root  tasks:  - name: install web packages    yum: name={{ item }} state=latest    with_items:    - httpd    - php    - php-mysql    - php-mbstring    - php-gd

基于字典列表给元素示例

# vim users.yml- hosts: all  remote_user: root  tasks:  - name: create groups    group: name={{ item }} state=present    with_items:    - groupx1    - groupx2    - groupx3  - name: create users    users: name={{ item }} group={{ item.group }} state=present    with_items:    - {name: 'userx1', group: 'groupx1'}    - {name: 'userx2', group: 'groupx2'}    - {name: 'userx3', group: 'groupx3'}

示例2:

# jinjia2.yml---- hosts: webservers  gather_facts: no  vars:    hello: ansible   tasks:    - template: src=demo_file dest=/tmp/ja

jinjia2的模板文件:demo_file

(1)遍历列表

{% set list=['one', 'two', 'three'] %}{% for i in list %}   {% if i == 'two' %}      it's 2   {% elif i == 'three' %}      that's 3   {% endif %}{% endfor %}{{hello}}

1563695305981

[root@k8s-node1 ~]# cat /tmp/ja            it's 2            that's 3   ansible

(2)遍历字典

修改demo_file文件内容如下:

{% set list=['one', 'two', 'three'] %}{% for i in list %}   {% if i == 'two' %}      it's 2   {% elif i == 'three' %}      that's 3   {% endif %}{% endfor %}{{hello}}{% set dict={'zhangsan':26, 'lisi':25} %}{% for key, value in dict.iteritems() %}   {{key}} -> {{value}}{% endfor %}

1563695761804

  • 测试运行结果
[root@k8s-node1 ~]# cat /tmp/ja......   lisi -> 25   zhangsan -> 26# 字典内容是无序的,所以打印结果一般也无序

1.12.2 管理Nginx配置文件

---- hosts: webservers  gather_facts: no  vars:    http_port: 80    server_name: www.jd.com    tasks:    - name: Copy nginx config file      template: src=site.ja2 dest=/etc/nginx/conf.d/site.conf      notify: reload nginx  handlers:    - name: reload nginx      service: name=nginx state=reloaded

上述的server_name改为www.baidu.com再次执行,则会重新渲染到主机,并重载nginx。

1563696989711

(1)采用upstream进行负载均衡

# site.ja2{% set list=[12,13,14] %}upstream {{server_name}}{    {% for i in list %}    server 172.16.4.{{i}}:80;    {%endfor%}}server {    listen {{http_port}};    server_name {{server_name}};    location / {        root /var/www/html;        index index.html;    }}

然后重新执行jinja2.yml

1563697553467

验证执行结果

[root@k8s-node1 ~]# cat /etc/nginx/conf.d/site.conf # site.ja2upstream www.jd.com{        server 172.16.4.12:80;        server 172.16.4.13:80;        server 172.16.4.14:80;    }server {    listen 80;    server_name www.jd.com;    location / {        root /var/www/html;        index index.html;    }}

1.13 角色(roles)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html?highlight=roles

在写roles时,有一些优秀的参考资料:

最佳实践:https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html 示例参考:https://github.com/ansible/ansible-examples

基本概念

角色是基于已知文件结构自动加载某些vars_files,任务和处理程序的方法。 按角色对内容进行分组还可以轻松与其他用户共享角色。以特定的层级目录结构进行组织的tasks、variables、handlers、templates、files等;role_name/files/:存储由copy或script等模块调用的文件;tasks/:此目录中至少应该有一个名为main.yml的文件,用于定义各task;其它的文件需要由main.yml进行“包含”调用;handlers/:此目录中至少应该有一个名为main.yml的文件,用于定义各handler;其它的文件需要由main.yml进行“包含”调用;vars/:此目录中至少应该有一个名为main.yml的文件,用于定义各variable;其它的文件需要由main.yml进行“包含”调用;templates/:存储由template模块调用的模板文本;meta/:此目录中至少应该有一个名为main.yml的文件,定义当前角色的特殊设定及其依赖关系;其它的文件需要由main.yml进行“包含”调用;default/:此目录中至少应该有一个名为main.yml的文件,用于设定默认变量;在playbook中调用角色的方法:\- hosts: HOSTSremote_user: USERNAMEroles:\- ROLE1\- ROLE2\- { role: ROLE3, VARIABLE: VALUE, ...}\- { role: ROLE4, when: CONDITION }

1.3.1 Roles目录结构

# site是统一入口site.yml  webservers.ymlfooservers.ymlhostsroles/  # 用于存放公共资源的地方  common/    files/    templates/    tasks/    handlers/    vars/    defaults/    meta/  #   webservers/    files/    templates/    tasks/    handlers/    vars/

角色期望文件位于某些目录名称中。 角色必须至少包含其中一个目录,但是排除任何未使用的目录是完全正确的。 在使用时,每个目录必须包含一个main.yml文件,其中包含相关内容:

  • tasks -包含角色要执行的主要任务列表
  • handlers -包含角色使用的处理程序
  • defaults -角色默认的变量
  • vars -角色其他的变量
  • files -角色部署时用到的文件
  • templates -角色部署时用到的模板
  • meta -角色定义的一些元数据

1.3.2 Roles基本使用

示例:

# 1. 创建对应的服务目录下面的模板[root@lb7-01 roles]# mkdir -pv ./{nginx,memcached,httpd,mysql}/{files,templates,vars,handlers,meta,default,tasks}# 确认下目录是否正确[root@lb7-01 roles]# tree ..├── httpd│   ├── default│   ├── files│   ├── handlers│   ├── meta│   ├── tasks│   ├── templates│   └── vars├── memcached│   ├── default│   ├── files│   ├── handlers│   ├── meta│   ├── tasks│   ├── templates│   └── vars├── mysql│   ├── default│   ├── files│   ├── handlers│   ├── meta│   ├── tasks│   ├── templates│   └── vars└── nginx    ├── default    ├── files    ├── handlers    ├── meta    ├── tasks    ├── templates    └── vars
  • 首先准备一个安装包,放到nginx/file目录下
[root@lb7-01 ~]# mv nginx-1.15.3.tar.gz /etc/ansible/roles/nginx/files/
# vim /etc/ansible/roles/nginx/tasks/main.yml- name: copy nginx package to remote host  copy: src=nginx-1.15.3.tar.gz dest=/tmp/nginx-1.15.3.tar.gz- name: install nginx package  yum: name=/tmp/nginx-1.15.3.tar.gz state=present- name: install conf file  template: src=nginx.conf.j2 dest=/etc/nginx.conf  tags: nginxconf  notify: reload nginx service- name: start nginx service  service: name=nginx enabled=true state=started

大家可以发现此处的模板跟之前的不一样,比如说,开头没有了定义主机、用户、和task等,此处的task会自行查找/etc/ansible/roles/nginx/task/main.yml的任务(此处文件本身也是在task目录下面)。再比如说,该处指定的copy命令的src=FILENAME也是相对路径,其绝对路径为/etc/ansible/roles/nginx/file/FILENAME。又比如说定义了notify但是这里并没有handlers,是因为此处定义了的notify的名字会自行去查看该目录下面即/etc/ansible/roles/nginx/handlers/main.yml里面的handlers。还有template那里,大家有没发现也是用的相对路径,此处绝对路径为在/etc/ansible/roles/nginx/template/nginx.conf.j2。所以大家清楚了吗?

  • 接着定义/etc/ansible/roles/nginx/handlers/main.yml
- name: reload nginx service  service: name=nginx state=restarted

此处的文件就是用来承上面的notify里面为什么没有定义的handlers的原因,因为已经定义在../handlers/main.yml里面了。

  • 复制nginx.conf文件到template目录下面为nginx.conf.j2
[root@lb7-01 ~]# cp /usr/local/nginx/conf/nginx.conf.default /etc/ansible/roles/nginx/templates/default.conf.j2

然后编辑此文件

1565833639471

然后重新编辑task/main.yml,主要增加以下内容。

156583387315

  • 此时,我们可以定义变量文件了
[root@lb7-01 roles]# vim nginx/vars/main.ymlnginxport: "8888"

此时所有的元素暂时都足够了,meta和default的文件夹在此处暂时用不上,然后我们在/etc/ansible/目录下面创建一个nginx.yml的文件

# vim /etc/ansible/nginx.yml- hosts: nginxservers  remote_user: root  gather_facts: no  tasks:  - name: install nginx package    yum: name=nginx state=latest    tags: nginxinstall  - name: install conf file    template: src=/root/testdir/nginx.conf.j2 dest=/etc/nginx.conf    tags: nginxconf    notify: reload nginx service  - name: start nginx service    service: name=nginx state=started enabled=true  handlers:  - name: reload nginx service    shell: /usr/bin/nginx -s reload

roles里面的nginx要在/etc/ansible.cfg文件里面有对应的设定

# vim /etc/ansible.cfgroles_path    = /etc/ansible/roles

然后执行即可。