一、Ansible 安装
下载安装并解压 python 3.7 包
编译并安装
注意:编译的时候可能提示 error: no acceptable C compiler found in $PATH,此时需安装 gcc yum -y install gcc
,然后再执行;make install 的时候可能提示 zipimport.ZipImportError: can’t decompress data; zlib not available ,此时需安装 yum -y install zlib*
,再执行,如果提示 ModuleNotFoundError: No module named ‘_ctypes’ make: *** [install] Error 1,此时需安装yum install libffi-devel -y
,再执行。
修改默认版本
恢复 yum
- 修改完 python 的默认版本后,yum 命令无法再执行。
vim /usr/bin/yum
将文件第一行改为/usr/bin/python2.7
。(2.7.x也改为2.7)vim /usr/libexec/urlgrabber-ext-down
将文件第一行改为/usr/bin/python2.7
。- 这样 python3.7 就安装在 CentOS 上,同时又能够使用 yum 来安装软件了。
安装 pip
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
//如果离线安装的话可以预先把压缩包准备好
sudo python3 get-pip.py
通过 pip 安装 ansible
python -m pip install ansible
使用 ansible --version
测试安装是否成功
ansible 2.9.6
config file = /etc/ansible/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
executable location = /usr/bin/ansible
python version = 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0]
二、Ansible 基础
ansible 是基于ssh 隧道进行连接运行的,故只要系统得 ssh 可以使用则 ansible 就可以使用。
配置文件
ansible 在使用配置文件时按照以下顺序优先配置:
export ANSIBLE_CONFIG
./ansible.cfg
~/.ansible.cfg
/etc/ansible/ansible.cfg
如果以上顺序没有找到配置文件 ansible 会自动使用默认配置,可以去 github 上把默认配置拿下来:
https://raw.githubusercontent.com/ansible/ansible/devel/examples/ansible.cfg。把它放到 /etc/ansible/ 目录,配置文件主要配置:
# 默认配置项
[defaults]
ask_pass:控制 Ansible 剧本 playbook 是否会自动默认弹出密码
ask_sudo_pass:用户系统开启了sudo 的密码需要开启这个参数
gather_subset 设置收集的内容
#要自动化处理的客户端的信息
remote_port 客户机 ssh 端口
remote_tmp 客户机目录
remote_user 客户机目录
sudo_exe sudo 命令的路径
sudo_flags sudo 命令的参数
sudo_user 可以使用 sudo 的用户
# 开发者中心的插件相关功能,开发者可以开发相应的插件完成自己的功能
action_plugins 激活事件
callback_plugins 回调
connection_plugins 连接插件
lookup_plugins 加载路径
vars_plugins 任何地方加载
filter_plugins 过滤器
forks 最大开辟的子进程数,过大会导致性能耗费高,过小的话并发性低,一般来说是 CPU 核数 * 2
module_name /usr/bin/ansible 默认模块名
pattern playbook 要通信的默认主机组
inventory 存放可通信主机的目录
library Ansibl e默认搜寻模块路径
#用户权限设置
[privilege_escalation]
#paramiko 插件配置(不是所有情况都启用插件)
[paramiko_connection]
#ssh连接设置
[ssh_connection]
添加一台机器
1.编辑 /etc/ansible/hosts,194.168.0.46 将 46 的控制权限添加到列表中;
2.添加控制机的 public SSH key 到被控制机器的 authorized_keys 中,cat .ssh/id_rsa.pub
查看公钥(没有的话:ssh-keygen -t rsa )
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC39Ob41GSi4Yxuy1BaKnBON1LPUyP9SN4wv4IWi+xJ99aOa5iuafoH3KLIA8RIVw0BY50zFhlghCktqcBtaH6UT7KG27+G7GWOzI7gwKKg1ajGtYRQ+7x+WN123oUk+p1aiaxvYrhukSqwYyomKJFDL74CYXU4H5EfOAg6jhYIDD/K63VYIw6KdEiiyBAR6RqdmRFd9ozxwH/5uLD5PDKlpo753QHyubk4XyKil2WVREWREosZTZ5L5QqQf2kAJTyV09LxBhg6vNXr6pHbPi5mizgd7rmyVpIiJjh+R5Xw54igBMskICePFPiS9+wLITyoO5qptnJdeiJ2LTOvlhvt root@ebb7023
将194.168.0.23 的公钥添加到 194.168.0.64 的 authorized_keys 中。
3.添加本机的私钥到 Ansible(可省略)
4.运行 ansible all -m ping
测试是否添加成功
[root@host ~]# ansible all -m ping
10.1.114.53 | SUCCESS => {
"changed": false,
"ping": "pong"
}
success 表示了操作的成功与否。这里 changed 为 false 时,表示 ansible 没有进行任何操作,因为当前的被操作主机已经是我们想要达到的目标状态了。changed 为 true 时,表示 ansible 执行了操作,"当前状态"已经被 ansible 改变成了"目标状态"。
简单操作
举例:
ansible all -a 'ls'
相当于在所有的被控制机上执行命令 ‘ls’ 即将 ls 这个参数传给要使用的模块,让它在被控制机器上执行。
对主机分组
Ansible 可同时操作属于一个组的多台主机,组合主机之间的关系通过 inventory 文件配置,默认的文件路径为 /etc/ansible/hosts
[webservers]
194.168.0.23
194.168.0.46
也就是说,194.168.0.23 和 194.168.0.46 同属于 webservers 组中,当对该组操作时,会同时操作 194.168.0.23 和 194.168.0.46.
ansible webservers-a 'ls'
如果出现了主机之间端口不一致可以在 host 文件中注明端口号
194.168.0.23:8080
机器如果有连着的,可以使用以下方式(也支持字母表):
[vim]
194.168.0.[1:50]
分文件管理
/etc/ansible/group_vars/vim
/etc/ansible/group_vars/webservers
/etc/ansible/group_vars/test
vim 文件大致内容:
ntp_server:194.168.0.23
database_server:194.168.0.24
这时的文件名就是组名
ansible payerns
ad-Hoc
如果我们可以使用一些命令去比较快的完成一些事情,而不需要将这些执行命令特别的保存下来,这样的命令就是 ad-hoc,即临时命令。
ansible 中的变量
变量是应用于多个主机的便捷方式; 实际在主机执行之前,变量会对每个主机添加,然后在执行中引用
- 命令行传递
-e VAR=VALUE
- 配置文件中针对某个主机或者某个组的变量
[webservers]
192.168.1.100 ansible_ssh_user=root hostname=web1
192.168.1.100 ansible_ssh_user=root hostname=web2
[webservers:vars]
ansible_ssh_user=root hostname=web1
- 文件存储的变量
Ansible 中的首选做法是不将变量存储在配置文件 (hosts) 中。除了将变量直接存储在配置文件 (hosts) 之外,主机和组变量还可以存储在相对于配置文件 (hosts) 的单个文件中。组变量:
# vi /etc/ansible/group_vars/all.yml
work_dir: /data
# vi /etc/ansible/host_vars/webservers.yml
nginx_port: 80
三、ansible 模块
command 模块
command 模块可以帮助我们在远程主机上执行命令
注意:使用 command 模块在远程主机中执行命令时,不会经过远程主机的 shell 处理,在使用 command 模块时,如果需要执行的命令中含有重定向、管道符等操作时,这些符号也会失效,比如 "<", ">", "|", ";" 和 "&" 这些符号,如果你需要这些功能,可以参考后面介绍的 shell 模块,还有一点需要注意,如果远程节点是 windows 操作系统,则需要使用 win_command 模块。
ansible test -m command -a "ls"
#在 test 主机上执行 ls 命令,因为我使用的是 root 用户,所以默认情况下,ls 出的结果是 test70 主机中 root 用户家目录中的文件列表。
ansible test -m command -a "chdir=/testdir ls"
#chdir 参数表示执行命令之前,会先进入到指定的目录中,所以如下命令表示查看 test 主机上 /testdir 目录中的文件列表
shell 模块
shell 模块可以帮助我们在远程主机上执行命令,与 command 模块不同的是,shell 模块在远程主机中执行命令时,会经过远程主机上的 /bin/sh 程序处理。
#使用 shell 模块可以在远程服务器上执行命令,它支持管道与重定向等符号。
ansible test -m shell -a "chdir=/testdir echo test > test"
script 模块
script 模块可以帮助我们在远程主机上执行 ansible 主机上的脚本,也就是说,脚本一直存在于 ansible 主机本地,不需要手动拷贝到远程主机后再执行。
如下命令表示 ansible 主机中的 /testdir/atest.sh 脚本将在 test70 主机中执行,执行此脚本之前,会先进入到 test70 主机中的 /opt 目录
#如下命令表示ansible主机中的/testdir/atest.sh脚本将在test主机中执行,执行此脚本之前,会先进入到 test 主机中的 /opt 目录
ansible test -m script -a "chdir=/opt /testdir/atest.sh"
copy 模块
copy 模块的作用就是拷贝文件,它与之前介绍过的 fetch 模块类似,不过,fetch 模块是从远程主机中拉取文件到 ansible 管理主机,而 copy 模块是将 ansible 管理主机上的文件拷贝到远程主机中。
ansible test -m copy -a "src=/etc/ansible/hosts dest=/"
#文件分发:源文件及路径 /etc/ansible/hosts ,目标路径/,目标主机:test 分组的所有主机
file 模块
file 模块以完成创建文件或目录、删除文件或目录、修改文件权限等操作
# 在创建文件或目录的时候指定属组或者修改远程主机上的文件或目录的属组。
ansible test -m file -a "path=/testdir/abb state=touch group=zsy"
blockinfile 模块
blockinfile 模块可以在指定的文件中插入一段文本,而且这段文本会被标记,以后的操作可以找到这段文本修改或者删除
#假如,我们想要在 test 的主机中的 /testdir/rc.local 文件尾部插入如下两行
systemctl start mariadb
systemctl start httpd
#可以使用如下命令
ansible test -m blockinfile -a 'path=/testdir/rc.local block="systemctl start mariadb\nsystemctl start httpd" '
lineinfile 模块
lineinfile 模块,用于对文件内容的修改
举例:
# cat /testdir/test
This is a file
The file is very simple
hello world!
# 如果指定的文本本来就存在于文件中,则不做任何操作,如果不存在,默认在文件的末尾插入这行文本
ansible 194.168.0.46 -m lineinfile -a 'path=/testdir/test line="hello text"'
yum_repository 模块
yum_repository 模块用于管理远程主机上的 yum 仓库
#在 test 主机上设置 ID 为 aliEpel 的 yum 源,仓库配置文件路径为 /etc/yum.repos.d/aliEpel.repo
ansible test -m yum_repository -a 'name=aliEpel description="alibaba EPEL" baseurl=https://mirrors.aliyun.com/epel/$releasever\Server/$basearch/'
yum 模块
yum 模块可以帮助我们在远程主机上通过 yum 源管理软件包
ansible test -m yum -a"name=acme state = present"
#确认一个软件包已经安装,但是不升级它
ansible webservers -m yum -a "name=nginx state=latest"
#安装nginx,最新版本,安装过程中的信息会全部输出值屏幕端
unarchive 模块
unarchive 模块用于解压文件
service 模块
ansible webservers -m service -a "name=nginx enabled=true state=started"
#开启 nginx 服务,并设置成开机自启
四、ansible playbook 介绍
ansible playbook 概述
当我们要安装某个软件并启动时,我们需要很多条命令,以 nginx 为例:
#配置对应的yum源
ansible test -m yum_repository -a 'name=aliEpel description="alibaba EPEL" baseurl=https://mirrors.aliyun.com/epel/$releasever\Server/$basearch/'
#使用yum模块安装nginx
ansible test -m yum -a 'name=nginx disable_gpg_check=yes enablerepo=aliEpel'
#使用service模块启动nginx
ansible test -m service -a "name=nginx state=started"
但是在工作中如果有新机器加入进来,每次用命令显得有些繁琐,如果是一些安装起来较为复杂的软件,环境配置要求很多的情况下会非常麻烦,故 ansible 提供了剧本:playbook,将 ad-hoc 命令按照顺序编写在在一个可执行文件中,语法遵循 yaml 语法。
示例:
#原 ad-hoc 命令
#使用 ping 模块去 ping 主机 test70
ansible 194.168.0.46 -m ping
#再用 file 模块在 test70 主机上创建目录
ansible 194.168.0.46 -m file -a "path=/testdir/test state=directory"
#test.yaml
---
- hosts: 194.168.0.46
remote_user: root
tasks:
- name: ping46
ping:
- name: mkdir test
file:
path: /testdir/test
state: directory
在YAML语法中:---
表示文档开始。下面-
开头(有空格),表示一个块的节点,host
表明要操作的主机,host
关键字对应值为 194.168.0.22,表示在该主机上操作。下面的 remote_user 表示远程操作时使用的用户,这里我们使用 194.168.0.22 的 root 用户。这行要与 host 对齐,yaml 中不识别 tab,tasts 表示要执行的任务列表,每个 play 包含一系列任务。这些任务按照顺序执行,在 play 中,所有主机都会执行相同的任务指令。play 目的是将选择的主机映射到任务。任务列表的起名比较随意。每个任务以 - 开头,上面的语法中 ping 模块没有指定参数,而使用 file 模块时制定了 path 和 state 的值。
[root@ohost ~]# ansible-playbook test.yml
PLAY [etcd] ********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [194.168.0.46]
TASK [ping64] ******************************************************************
ok: [194.168.0.46]
TASK [mkdir test] **************************************************************
changed: [194.168.0.46]
PLAY RECAP *********************************************************************
194.168.0.46 : ok=3 changed=1 unreachable=0 failed=0
#检查 yaml 有没有语法错误
ansible-playbook --syntax-check test.yaml
#模拟运行(不会真的运行,只是帮助我们预估该 yml 文件能不能运行,但是如果 yaml 中的任务有前后依赖关系,那模拟执行会失败)
ansible-playbook --check test.yaml
看下面的例子:
tasks:
- name: make testfile
file:
path: /testdir/testfile
state: touch
mode: 0700 #设置权限为0700
也可以编写成如下格式:
tasks:
- name: make testfile
file: path=/testdir/testfile state=touch mode=0700
常用模块的 playbook 语法
该处用到的参数均可在上面的模块介绍中找到,也可以在安装了 ansible 的机器上查看帮助文档。这里以几个常用的模块为例示范对应的 playbook 语法
shell
- name: 将命令结果输出到指定文件
shell: somescript.sh >> somelog.txt
- name: 切换目录执行命令
shell:
cmd: ls -l | grep log
chdir: somedir/
- name: 编写脚本
shell: |
if [ 0 -eq 0 ]; then # 比较0和0 的值,一致则输出yes到result,否则输出no
echo yes > /tmp/result
else
echo no > /tmp/result
fi
args:
executable: /bin/bash #用什么执行脚本
copy
- name: 拷贝文件
copy:
src: /srv/myfiles/myfile.conf
dest: /etc/myfile.conf
owner: wang
group: wang
mode: u=rw,g=r,o=r #相当于命令中的u+rw,g-wx,o-rwx 或者使用mode: '0644'
backup: yes
file
- name: 创建目录
file:
path: /etc/some_directory
state: directory
mode: '0755'
- name: 删除文件
file:
path: /etc/foo.txt
state: absent
- name: 递归删除目录
file:
path: /etc/foo
state: absent
yum
- name: 安装最新版apache
yum:
name: httpd
state: latest
- name: 安装列表中所有包
yum:
name:
- nginx
- postgresql
- postgresql-server
state: present
- name: 卸载apache包
yum:
name: httpd
state: absent
- name: 更新所有包
yum:
name: '*'
state: latest
- name: 安装nginx来自远程repo
yum:
name: http://nginx.org/packages/rhel/7/x86_64/RPMS/nginx-1.14.0-1.el7_4.ngx.x86_64.rpm
# name: /usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm
state: present
- name: 安装nginx最新版
yum: pkg=nginx state=latest
unarchive
- name: 解压文件/tmp/test.tar.gz
unarchive:
src: test.tar.gz
dest: /tmp/
service
- name: 服务管理
service:
name: etcd
state: started
#state: stopped
#state: restarted
#state: reloaded
- name: 设置开机启动
service:
name: httpd
enabled: yes
debug
将上一步任务执行的结果打印出来,在这一步会将 4 台主机的执行结果都返回,不管成功或是失败都会显示出来。
- hosts: all
remote_user: root
tasks:
- name: 查看root目录下的文件
shell:
cmd: ls -l
chdir: /root/
- name: 输出的结果
debug:
var: result
playbook 中的变量
全局变量/组变量/主机变量
---
- hosts: webservers
vars: # 这种变量对于hosts组都有效
http_port: 80
server_name: www.ctnrs.com
响应变量 接收任务的响应
tasks:
- name: 只在192.168.1.100运行任务
shell:
cmd: ls -l | grep log
chdir: somedir/
register: result
五、playbook 常用流程控制
条件
tasks:
- name: 只在192.168.1.100运行任务
shell:
cmd: ls -l | grep log
chdir: somedir/
register: result
- debug: var=result
when: ansible_default_ipv4.address == '192.168.1.100'
循环
tasks:
- name: 批量创建用户
user: name={{ item }} state=present groups=wheel
with_items:
- testuser1
- testuser2
- name: 遍历/temp中所有txt文件的文件名
copy: src={{ item }} dest=/tmp
with_fileglob:
- "*.txt"
常用循环语句:语句描述 with_items 标准循环 with_fileglob 遍历文件名称 with_dict 遍历字典 with_file 遍历文件内容。注意这里 with_file 和 with_fileglob 的区别,如果我们用 debug 输出结果会发现,with_file 中 debug 打出来的是文件内容,而 with_fileglob 打出来的是文件名称。
任务控制
当某个剧本中有很多任务时,可能我们只要执行其中的某个步骤,那么我们可以给它打标签:
tasks:
- name: 安装nginx最新版
yum: pkg=nginx state=latest
tags: install
- name: 写入nginx配置文件
template: src=/srv/httpd.j2 dest=/etc/nginx/nginx.conf
tags: config
- name: 批量创建用户
user: name={{ item }} state=present groups=wheel
with_items:
- testuser1
- testuser2
tags: adduser
使用如下命令可以只执行打了某个标签的任务,或者跳过某个标签的任务
ansible-playbook test.yml --tags "install"
ansible-playbook test.yml --tags "install,config"
ansible-playbook test.yml --skip-tags "install"
模板
Jinja2 是基于 python 的模板引擎
vars:
domain: "www.ctnrs.com"
tasks:
- name: 写入nginx配置文件
template: src=/srv/server.j2 dest=/etc/nginx/conf.d/server.conf
# server.j2
{% set domain_name = domain %}
server {
listen 80;
server_name {{ domain_name }};
location / {
root /usr/share/html;
}
}
在 jinja 里使用 ansible 变量直接 {{ }} 引用。使用 ansible 变量赋值 jinja 变量不用 {{ }} 引用,生成的文件:
server {
listen 80;
server_name www.ctnr.com;
location / {
root /usr/share/html;
}
}
如果使用程序流程语句:
定义变量:{% set local_ip = inventory_hostname %}
条件和循环:
{% set list=['one', 'two', 'three'] %}
{% for i in list %}
{% if i == 'two' %}
-> two
{% elif loop.index == 3 %}
-> 3
{% else %}
{{i}}
{% endif %}
{% endfor %}
文件执行结果:
[root@myhost ~]# vim server.conf
one
-> two
-> 3
忽略执行出现的错误
ignore_errors: true 是忽略出现的问题 ,不加默认为 false,任务运行出 bug 时不会执行目标主机的后续任务
- name: 安装nginx最新版
yum: pkg=nginx state=latest
tags: install
ignore_errors: true
六、Docker 安装实例
在 files/ 文件目录放 docker 的二级制包,这是相对于 yml 文件的相对路径
---
#指定安装docker的主机组
- hosts: docker
#安装目录
vars:
tmp_dir: '/tmp/docker'
remote_user: root
gather_facts: false
tasks:
- name: 创建临时目录放配置文件和二进制包
file: dest={{ tmp_dir }} state=directory
- name: 分发并解压docker二进制包(去官网随便选个版本下就行了)
unarchive: src={{ item }} dest={{ tmp_dir }}
with_fileglob:
- "files/docker-*.tgz"
- name: 移动docker二进制文件
shell: cp -rf {{ tmp_dir }}/docker/* /usr/bin
- name: 分发service文件
copy: src=files/docker.service dest=/usr/lib/systemd/system/
- name: 创建目录
file: dest=/etc/docker state=directory
- name: 配置docker
copy: src=files/daemon.json dest=/etc/docker/daemon.json
- name: 启动docker
systemd: name=docker state=restarted enabled=yes daemon_reload=yes
- name: 查看状态
shell: docker info
register: docker
- debug: var=docker.stdout_lines