Ansible - 运维神器
1. 概述
Ansible是一个部署一群远程主机的工具。这里“远程主机(Remote Host)”是指任何可以通过SSH登录的主机,所以它既可以是远程虚拟机或物理机,也可以是本地主机。
Ansible通过SSH协议实现管理节点与远程节点之间的通信。理论上来说,只要能通过SSH登录到远程主机来完成的操作,都可以通过Ansible实现批量自动化操作,包括:复制文件、安装包、发起服务,等等。
Ansible解决了如何大批量、自动化地实现系统配置、应用部署、命令和服务操作的问题。其脚本具有灵活、可重入的特性,极大地减少了运维人员的重复劳动,提高了运维效率。
ansible工作原理2. 准备环境
2.1. host机器安装ansible
$ sudo apt install ansible
2.2. 通过vagrant启动3个vm
- 安装vagrant和virtualbox
$ sudo apt install vagrant virtualbox -y
- 下载ubuntu20.04 vagrant box
http://cloud-images.ubuntu.com/focal/current/
- 创建localfile: ~/learn/vagrant/Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
(1..3).each do |i|
config.vm.define "node-#{i}" do |node|
node.vm.box = "ubuntu/2004"
node.vm.box_url = "file:///home/shuzhang/learn/vagrant/focal-server-cloudimg-amd64-vagrant.box"
node.vm.hostname = "node#{i}"
node.vm.network "private_network", ip: "192.168.56.1#{i}"
node.vm.provider "virtualbox" do |v|
v.gui = false
v.name = "ubuntu-#{i}"
v.cpus = "1"
v.memory = "2048"
end
end
end
end
- 启动vm
$ vagrant up
...
$ vagrant status
Current machine states:
node-1 running (virtualbox)
node-2 running (virtualbox)
node-3 running (virtualbox)
$ vagrant ssh-config
...
2.3. 在host配置ssh免密登录
- 追加~/.ssh/config
...
Host test11
HostName 192.168.56.11
User vagrant
IdentityFile /home/shuzhang/learn/vagrant/.vagrant/machines/node-1/virtualbox/private_key
Host test12
HostName 192.168.56.12
User vagrant
IdentityFile /home/shuzhang/learn/vagrant/.vagrant/machines/node-2/virtualbox/private_key
Host test13
HostName 192.168.56.13
User vagrant
IdentityFile /home/shuzhang/learn/vagrant/.vagrant/machines/node-3/virtualbox/private_key
- 测试一下下
$ ssh test11
$ ssh test12
$ ssh test13
2.4. 补充说明
- 手动创建key文件,并配置ssh免密
# 生成SSH密钥
$ ssh-keygen
# 复制SSH密钥到远程主机,这样SSH的时候就不需要输入密码了
$ ssh-copy-id remoteuser@remoteserver
3. Ansible Host Inventory配置
Host Inventory是ansible远程主机列表,分静态Inventory
和动态Inventory
,支持主机组以及主机组嵌套、主机变量、组变量、多个inventory文件
等。
3.1. 静态Inventory
单个Inventory文件
- 默认的inventory文件:/etc/ansible/hosts
[test]
test11
test12
test13
test14 ansible_host=127.0.0.1 ansible_port=2200 ansible_user=vagrant ansible_ssh_private_key_file=/home/shuzhang/learn/vagrant/.vagrant/machines/node-2/virtualbox/private_key
# 也可以配置明文的password(这里不行,因为vagrant vm只支持pem文件登录)
test15 ansible_host=127.0.0.1 ansible_port=2200 ansible_user=vagrant ansible_ssh_pass=vagrant
image.png
多个Inventory文件
- 创建文件夹
/etc/ansible/inventory
,修改/etc/ansible/ansible.cfg
...
inventory = /etc/ansible/inventory
...
- 创建test文件,存储test主机,维护两个Inventory文件
$ tree /etc/ansible/
/etc/ansible/
├── ansible.cfg
└── inventory
├── hosts
└── test
1 directory, 3 files
参考:http://www.ansible.com.cn/docs/intro_inventory.html
测试
$ ansible test14 -m ping
test14 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
3.2. 动态Inventory
- 创建Py文件
/etc/ansible/inventory/group1.py
,并给予可执行权限
#!/usr/bin/env python
import argparse
import sys
import json
def lists():
inventory = dict(
group1=dict(
hosts=['192.168.56.1' + str(i) for i in range(1,4)],
vars=dict(
ansible_user="vagrant",
example_variable="value"
)
)
)
return json.dumps(inventory, indent=4)
def hosts(name):
host_config = {
'192.168.56.11': dict(
ansible_ssh_private_key_file='/home/shuzhang/learn/vagrant/.vagrant/machines/node-1/virtualbox/private_key'
),
'192.168.56.12': dict(
ansible_ssh_private_key_file='/home/shuzhang/learn/vagrant/.vagrant/machines/node-2/virtualbox/private_key'
),
'192.168.56.13': dict(
ansible_ssh_private_key_file='/home/shuzhang/learn/vagrant/.vagrant/machines/node-3/virtualbox/private_key'
)
}
return json.dumps(host_config.get(name, {}))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-l', '--list', help='hosts list', action='store_true')
parser.add_argument('-H', '--host', help='hosts vars')
args = parser.parse_args()
if args.list:
print(lists())
elif args.host:
print(hosts(args.host))
else:
parser.print_help()
- 测试一下,ansible是否可以解析
$ ansible group1 --list-host
hosts (3):
192.168.56.11
192.168.56.12
192.168.56.13
$ ansible group1 -m ping -o
192.168.56.11 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
192.168.56.12 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
192.168.56.13 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
4. Ansible Ad-hoc
ansible临时命令,使用起来很方便,可用于测试,或者简单的临时性质的任务
ansible <host-pattern> [options]
4.1. 常用modules
- ① 调试和测试类的模块。
ping:ping一下你的远程主机,如果可以通过Ansible连接成功,那么返回pong。
debug:用于调试的模块,只是简单打印一些消息,有点像Linux的echo命令。
$ ansible test -m ping
$ ansible test -m debug -a "msg={{inventory_hostname}}"
- ② 文件类的模块。
copy:从本地复制文件到远程节点。
template:从本地复制文件到远程节点,并进行变量的替换。
file:设置文件属性。
$ ansible test11 -m copy -a "src=./1.log dest=~/"
$ ansible test11 -m shell -a "cat ~/1.log"
- ③ Linux上的常用操作。
user:管理用户账户。
yum:Red Hat系Linux上的包管理。
service:管理服务。
firewalld:管理防火墙中的服务和端口。
$ ansible test11 -m user -a "name=user1 groups=sudo shell=/bin/bash" -b
$ ansible test11 -m user -a "name=user1 state=absent remove=yes" -b
- ④ 执行shell命令。
shell:在节点上执行shell命令,支持$HOME、“<”、“>”、“|”、“;”和“&”
。
command:在远程节点上面执行命令,不支持$HOME、“<”、“>”、“|”、“;”和“&”
。
$ ansible test11 -m shell -a "chdir=~ cat 1.log"
$ ansible test11 -m shell -a "pwd && cat 1.log"
4.2. 其他模块,举个栗子
- script
$ ansible wave1 -m script -a "install_docker.sh" -bK
- synchronize
# 将文件redis_5.0.tar同步到/u/devops下,并更名为redis.tar(如果没有redis.tar,则不rename)
ansible wave1 -m synchronize -a "src=./redis_5.0.tar dest=/u/devops/redis.tar"
ansible wave1 -m synchronize -a "src=./xxxx.release.1.4.0.tar.gz dest=/u/devops/"
# 将文件夹data同步到/u/devops/下,同步后的目录结构是/u/devops/data
ansible wave1 -m synchronize -a "src=./data/ dest=/u/devops/"
5. Ansible Playbook
Playbooks 是 Ansible的配置、部署、编排语言,它可以描述为一个需要希望远程主机执行命令的方案,或者一组IT程序运行的命令集合。
任务中每个Action会调用一个模块,然后在模块中检查当前系统状态是否需要重新执行。
- 如果本次执行了,那么Action会得到返回值changed
- 如果不需要执行,那么Action会得到返回值ok
模块的执行状态的具体判断规则由各个模块自己决定和实现。例如,copy模块的判断方法是比较文件的checksum,代码如下。
checksum_src = module.sha1(src)
...
checksum_dest = module.sha1(dest)
...
if checksum_src != checksum_dest or os.path.islink(b_dest):
...
changed = True
else:
changed = False
5.1. 创建用户,并设置免密登录、免密sudo权限
# ansible-playbook add_user.yml -e "remotehost=dev1"
# ansible wave1 -m authorized_key -a "user=testuser1 key=\"{{lookup('file','./id_rsa.ssh.pub') }}\""
- hosts: "{{ remotehost }}"
become: yes
gather_facts: False
vars:
users:
- "user1"
- "user2"
tasks:
- name: "Create user accounts"
user:
name: "{{ item }}"
groups: "sudo,docker"
with_items: "{{ users }}"
- name: "Create .ssh folder"
file: path="/home/{{ item }}/.ssh" state=directory owner="{{ item }}" mode=0700
with_items: "{{ users }}"
- name: "Add authorized keys"
authorized_key:
user: "{{ item }}"
key: "{{ lookup('file', '~/.ssh/id_rsa.ssh.pub') }}"
with_items: "{{ users }}"
- name: "Add sudo without password permission"
copy:
content: '{{ item }} ALL=(ALL:ALL) NOPASSWD:ALL'
dest: "/etc/sudoers.d/{{ item }}_nopasswd"
mode: 0440
with_items: "{{ users }}"
5.2. mongodb操作
# ansible-playbook mongo_ops.yml -e "remotehost=dev1" > 1030.log
# cat 1030.log | grep "transaction count"
- hosts: "{{ remotehost }}"
gather_facts: no
tasks:
- name: Copy js file
become: yes
copy:
content: |
db.transaction.find({
created_at: {"$gte": ISODate("2020-10-21T16:00:00.000Z"), "$lte": ISODate("2020-10-29T16:00:00.000Z")}
}).count()
dest: "/u/mongo/tmp_mongo_ops.js"
mode: 660
- name: Count transactions
register: ps
shell: |
docker ps --format {% raw %}{{.Names}}{% endraw %} | grep mongo | grep -v test | \
xargs -I {} docker exec -t {} \
bash -c "mongo mongodb://user:passwd@127.0.0.1:27017/testdb?authSource=admin < /data/db/tmp_mongo_ops.js"
- debug: msg="transaction count - {{inventory_hostname}} - {{ps.stdout_lines[-2]}}"
post_tasks:
- name: Clean tmp files
become: yes
shell: rm /u/mongo/tmp_mongo_ops.js
# escaping-double-curly-braces-in-ansible
# https://stackoverflow.com/questions/32279519/escaping-double-curly-braces-in-ansible
5.3. More best practice
http://www.ansible.com.cn/docs/playbooks_best_practices.html
6. 尾声
Ansible入门简单,也非常实用,真乃运维神器。但是,时间有限,有些高级玩法还没深入研究,以后有机会再学习。
一些资料:
http://www.ansible.com.cn/index.html
https://github.com/ansible/ansible
https://github.com/ansible/ansible-examples
https://github.com/ansible/ansible-modules-core