Ansible是一種集成IT系統的配置管理, 應用部署, 執行特定任務的開源平台. 它基於Python語言實現, 部署只需在主控端部署Ansible環境, 被控端無需安裝代理工具, 只需打開SSH, 讓主控端通過SSH秘鑰認證對其進行所有的管理監控操作. 相對於SaltStack, 它除了利用SSH安全傳輸, 無需在客戶端進行任何配置, 而且它有一個很龐大的用戶群體以及豐富的API, 相對適合部署到數量比較大且對系統軟件安裝要求比較嚴格的集群中.
更多配置參考: https://github.com/ansible
官方文檔: http://docs.ansible.com/ansible
本文將幫助大家如何快速部署一個Ansible平台.
安裝環境:
System: Centos 6.7 x64
Master: master.example.com
Minion: client01.example.com
Minion: client02.example.com
一. 環境部署及安裝
1. 關閉iptables和SELINUX
# service iptables stop
# setenforce 0
# vi /etc/sysconfig/selinux
... SELINUX=disabled ...
2. Master端安裝EPEL第三方yum源
# rpm -Uvh http://ftp.linux.ncsu.edu/pub/epel/6/i386/epel-release-6-8.noarch.rpm
3.安裝Ansible
# yum install ansible -y
4.添加環境變量以便vi能正常顯示中文注釋.
# vi /etc/profile
添加:
export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 export LANGUAGE=en_US.UTF-8
# source /etc/profile
二. 初始配置
1. 修改主機及組配置
# cd /etc/ansible
# cp hosts hosts.bak
# cat /dev/null > hosts
# vi /etc/ansible/hosts
[webservers] client01.example.com client02.example.com [nginx01] client01.example.com [nginx02] client02.example.com
2.配置SSH秘鑰認證
# yum install ssh* -y
# ssh-keygen -t rsa
Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory '/root/.ssh'. 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: 24:13:34:e9:71:2b:20:0b:48:a6:86:9a:1d:1b:1d:26 root@master.example.com The key's randomart image is: +--[ RSA 2048]----+ |ooE o.+. | |* .+..oo. | |oooo.ooo.. | |oo.+ o+. | |o o .S | | | | | | | | | +-----------------+
同步公鑰文件id_rsa.pub到目標主機
# ssh-copy-id -i /root/.ssh/id_rsa.pub root@client01.example.com
# ssh-copy-id -i /root/.ssh/id_rsa.pub root@client02.example.com
校驗SSH免密碼配置是否成功.
# ssh root@client02.example.com
如直接進入則配置完成.
3.定義主機與組
所有定義的主機與組規則都在/etc/Ansible/hosts下.
常見的寫法:
192.168.1.21:2135 定義一個IP為192.168.1.21, SSH端口為2135的主機.
jumper ansible_ssh_port=22 ansible_ssh_host=192.168.1.50 定義一個別名為jumper, SSH端口為22, IP為192.168.1.50的主機.
組成員主機名稱范例:
[webservers] www[001:006].example.com [dbservers] db-[a:f].example.com
4.定義主機變量
主機可以指定變量, 后面可以供Playbooks調用
[atlanta] host1 http_port=80 maxRequestsPerChild=808 host2 http_port=8080 maxRequestsPerChild=909
5.定義組變量
[atlanta] host1 host2 [atlanta:vars] ntp_server=ntp.atlanta.example.com proxy=proxy.atlanta.example.com
6.匹配目標
重啟webservers組所有SSH服務.
# ansible webservers -m service -a "name=sshd state=restarted"
client01.example.com | success >> {
"changed": true,
"name": "sshd",
"state": "started"
}
client02.example.com | success >> {
"changed": true,
"name": "sshd",
"state": "started"
}
三. Ansible常用模塊及API
1.遠程命令模塊
command: 執行遠程主機SHELL命令:
# ansible webservers -m command -a "free -m"
client01.example.com | success | rc=0 >>
total used free shared buffers cached
Mem: 996 108 887 0 7 41
-/+ buffers/cache: 58 937
Swap: 1023 0 1023
client02.example.com | success | rc=0 >>
total used free shared buffers cached
Mem: 996 108 888 0 7 41
-/+ buffers/cache: 58 937
Swap: 1023 0 1023
script: 遠程執行MASTER本地SHELL腳本.(類似scp+shell)
# echo "df -h" > ~/test.sh
# ansible webservers -m script -a "~/test.sh"
client01.example.com | success >> {
"changed": true,
"rc": 0,
"stderr": "OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug1: mux_client_request_session: master session id: 2\r\nShared connection to client01.example.com closed.\r\n",
"stdout": "Filesystem Size Used Avail Use% Mounted on\r\n/dev/sda3 6.6G 815M 5.5G 13% /\r\ntmpfs 499M 0 499M 0% /dev/shm\r\n/dev/sda1 190M 27M 154M 15% /boot\r\n"
}
client02.example.com | success >> {
"changed": true,
"rc": 0,
"stderr": "OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug1: mux_client_request_session: master session id: 2\r\nShared connection to client02.example.com closed.\r\n",
"stdout": "Filesystem Size Used Avail Use% Mounted on\r\n/dev/sda3 6.6G 815M 5.5G 13% /\r\ntmpfs 499M 0 499M 0% /dev/shm\r\n/dev/sda1 190M 27M 154M 15% /boot\r\n"
}
2. copy模塊
實現主控端向目標主機拷貝文件, 類似scp功能.
該實例實現~/test.sh文件至webservers組目標主機/tmp下, 並更新文件owner和group
# ansible webservers -m copy -a "src=~/test.sh dest=/tmp/ owner=root group=root mode=0755"
# ansible webservers -m copy -a "src=~/test.sh dest=/tmp/ owner=root group=root mode=0755"
client01.example.com | success >> {
"changed": true,
"checksum": "c989bd551bfa8c755f6cacacb90c5c509432110e",
"dest": "/tmp/test.sh",
"gid": 0,
"group": "root",
"md5sum": "69a238d8cb3c5f979252010b3299e524",
"mode": "0755",
"owner": "root",
"size": 6,
"src": "/root/.ansible/tmp/ansible-tmp-1445322165.21-234077402845688/source",
"state": "file",
"uid": 0
}
client02.example.com | success >> {
"changed": true,
"checksum": "c989bd551bfa8c755f6cacacb90c5c509432110e",
"dest": "/tmp/test.sh",
"gid": 0,
"group": "root",
"md5sum": "69a238d8cb3c5f979252010b3299e524",
"mode": "0755",
"owner": "root",
"size": 6,
"src": "/root/.ansible/tmp/ansible-tmp-1445322165.2-164402895387597/source",
"state": "file",
"uid": 0
}
3.stat模塊
獲取遠程文件狀態信息, 包括atime, ctime, mtime, md5, uid, gid等信息.
# ansible webservers -m stat -a "path=/etc/sysctl.conf"
client02.example.com | success >> {
"changed": false,
"stat": {
"atime": 1445312213.9599864,
"checksum": "704d7d26321b453d973939ee41aaf9861e238a78",
"ctime": 1444969315.401,
"dev": 2051,
"exists": true,
"gid": 0,
"gr_name": "root",
"inode": 130328,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"md5": "9ce78fbee91a542ca29d3e7945486e27",
"mode": "0644",
"mtime": 1437725687.0,
"nlink": 1,
"path": "/etc/sysctl.conf",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 998,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
}
client01.example.com | success >> {
"changed": false,
"stat": {
"atime": 1445312212.9747968,
"checksum": "704d7d26321b453d973939ee41aaf9861e238a78",
"ctime": 1444969315.401,
"dev": 2051,
"exists": true,
"gid": 0,
"gr_name": "root",
"inode": 130328,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"md5": "9ce78fbee91a542ca29d3e7945486e27",
"mode": "0644",
"mtime": 1437725687.0,
"nlink": 1,
"path": "/etc/sysctl.conf",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 998,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
}
4.get_url模塊
實現在遠程主機下載指定URL到本地.
# ansible webservers -m get_url -a "url=http://www.showerlee.com dest=/tmp/index.html mode=0400 force=yes"
client02.example.com | success >> {
"changed": true,
"checksum": "470d6ab960810153bb8149c3754b0e8a2d89209d",
"dest": "/tmp/index.html",
"gid": 0,
"group": "root",
"md5sum": "009949f770f35a4ea82105e5e923abcb",
"mode": "0400",
"msg": "OK (unknown bytes)",
"owner": "root",
"sha256sum": "",
"size": 81635,
"src": "/tmp/tmpa44PoE",
"state": "file",
"uid": 0,
"url": "http://www.showerlee.com"
}
client01.example.com | success >> {
"changed": true,
"checksum": "9b1afd16f97c07638965ba0c5cf01037af00a38a",
"dest": "/tmp/index.html",
"gid": 0,
"group": "root",
"md5sum": "5a935e77927286dfcb7a0190e8af461b",
"mode": "0400",
"msg": "OK (unknown bytes)",
"owner": "root",
"sha256sum": "",
"size": 81679,
"src": "/tmp/tmp5WHuj0",
"state": "file",
"uid": 0,
"url": "http://www.showerlee.com"
}
5.yum模塊
Linux包管理平台操作, 常見都會有yum和apt, 此處會調用yum管理模式
# ansible servers -m yum -a "name=curl state=latest"
client01.example.com | success >> {
"changed": false,
"msg": "",
"rc": 0,
"results": [
"All packages providing curl are up to date"
]
}
client02.example.com | success >> {
"changed": false,
"msg": "",
"rc": 0,
"results": [
"All packages providing curl are up to date"
]
}
6. cron模塊
遠程主機crontab配置
# ansible webservers -m cron -a "name='check dir' hour='5,2' job='ls -alh > /dev/null'"
client02.example.com | success >> {
"changed": true,
"jobs": [
"check dir"
]
}
client01.example.com | success >> {
"changed": true,
"jobs": [
"check dir"
]
}
7.service模塊
遠程主機系統服務管理
# ansible webservers -m service -a "name=crond state=stopped"
# ansible webservers -m service -a "name=crond state=restarted"
# ansible webservers -m service -a "name=crond state=reloaded"
8.user服務模塊
遠程主機系統用戶管理
添加用戶:
# ansible webservers -m user -a "name=johnd comment='John Doe'"
刪除用戶:
# ansible webservers -m user -a "name=johnd state=absent remove=yes"
四. playbook介紹
playbook是一個不同於使用Ansible命令行執行方式的模式, 其功能是將大量命令行配置集成到一起形成一個可定制的多主機配置管理部署工具.
它通過YAML格式定義, 可以實現向多台主機的分發應用部署.
以下給大家詳細介紹一個針對nginx嵌套復用結構的playbook部署實例:
1. 構建目錄結構
# cd /etc/ansible/
# mkdir group_vars
# mkdir roles
2.定義host
# vi /etc/ansible/hosts
[webservers] client01.example.com client02.example.com [nginx01] client01.example.com [nginx02] client02.example.com
3.定義變量
# vi /etc/ansible/group_vars/nginx01
worker_processes: 4 num_cpus: 4 max_open_file: 65506 root: /data remote_user: root
# vi /etc/ansible/group_vars/nginx02
worker_processes: 2 num_cpus: 2 max_open_file: 35506 root: /www remote_user: root
Tips:這里在group_vars下定義的文件名必須對應hosts文件下的group標簽, 通過這里定義的不同參數從而部署不同類型的主機配置.
4.創建roles入口文件
# vi /etc/ansible/site.yml
- hosts: webservers roles: - base_env - hosts: nginx01 roles: - nginx01 - hosts: nginx02 roles: - nginx02
Tips: 這里的roles:下的字符串需對應roles目錄下的目錄名.
5.定義全局role base_env
創建目錄結構
# mkdir -p /etc/ansible/roles/base_env/tasks
# vi /etc/ansible/roles/base_env/tasks/main.yml
# 將EPEL的yum源配置文件傳送到客戶端 - name: Create the contains common plays that will run on all nodes copy: src=epel.repo dest=/etc/yum.repos.d/epel.repo - name: Create the GPG key for EPEL copy: src=RPM-GPG-KEY-EPEL-6 dest=/etc/pki/rpm-gpg # 關閉SELINUX - name: test to see if selling is running command: getenforce register: sestatus changed_when: false # 刪除iptables默認規則並保存 - name: remove the default iptables rules command: iptables -F - name: save iptables rules command: service iptables save
將對應需要拷貝到遠程的文件復制到base_env/files目錄下
# mkdir -p /etc/ansible/roles/base_env/files
# cp /etc/yum.repos.d/epel.repo /etc/ansible/roles/base_env/files
# cp /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 /etc/ansible/roles/base_env/files
6. 定義nginx01和ngnix02 role
創建目錄結構
# mkdir -p /etc/ansible/roles/nginx{01,02}
# mkdir -p /etc/ansible/roles/nginx01/tasks
# mkdir -p /etc/ansible/roles/nginx02/tasks
# vi /etc/ansible/roles/nginx01/tasks/main.yml
# 安裝nginx最新版本
- name: ensure nginx is at the latest version
yum: pkg=nginx state=latest
# 將nginx配置文件傳送到遠程目錄
- name: write the nginx config file
template: src=nginx.conf dest=/etc/nginx/nginx.conf
notify: restart nginx # 重啟nginx
# 創建nginx根目錄
- name: Create Web Root
file: dest={{ root }} mode=775 state=directory owner=nginx group=nginx
notify: reload nginx
- name: ensure nginx is running
service: name=nginx state=restarted
# cp /home/ansible/roles/nginx01/tasks/main.yml /home/ansible/roles/nginx02/tasks/main.yml
7.定義files
# mkdir -p /etc/ansible/roles/nginx01/templates
# mkdir -p /etc/ansible/roles/nginx02/templates
# vi /etc/ansible/roles/nginx01/templates/nginx.conf
# For more information on configuration, see:
user nginx;
worker_processes {{ worker_processes }};
{% if num_cpus == 2 %}
worker_cpu_affinity 01 10;
{% elif num_cpus == 4 %}
worker_cpu_affinity 1000 0100 0010 0001;
{% elif num_cpus >= 8 %}
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
{% else %}
worker_cpu_affinity 1000 0100 0010 0001;
{% endif %}
worker_rlimit_nofile {{ max_open_file }};
error_log /var/log/nginx/error.log;
#error_log /var/log/nginx/error.log notice;
#error_log /var/log/nginx/error.log info;
pid /var/run/nginx.pid;
events {
worker_connections {{ max_open_file }};
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
# Load config files from the /etc/nginx/conf.d directory
# The default server is in conf.d/default.conf
#include /etc/nginx/conf.d/*.conf;
server {
listen 80 default_server;
server_name _;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root {{ root }};
index index.html index.htm;
}
error_page 404 /404.html;
location = /404.html {
root /usr/share/nginx/html;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
Tip: worker_processes, num_cpus, max_open_file, root等參數會調用group_vars目錄下配置文件中相應的變量值
# cp /etc/ansible/roles/nginx01/templates/nginx.conf /etc/ansible/roles/nginx02/templates/nginx.conf
8.執行playbook
# ansible-playbook -i /etc/ansible/hosts /etc/ansible/site.yml -f 10
Tips: -f 為啟動10個並行進程執行playbook, -i 定義inventory host文件, site.yml 為入口文件
PLAY [webservers] ************************************************************* GATHERING FACTS *************************************************************** ok: [client02.example.com] ok: [client01.example.com] TASK: [base_env | Create the contains common plays that will run on all nodes] *** ok: [client01.example.com] ok: [client02.example.com] TASK: [base_env | Create the GPG key for EPEL] ******************************** ok: [client02.example.com] ok: [client01.example.com] TASK: [base_env | test to see if selling is running] ************************** ok: [client01.example.com] ok: [client02.example.com] TASK: [base_env | remove the default iptables rules] ************************** changed: [client02.example.com] changed: [client01.example.com] TASK: [base_env | save iptables rules] **************************************** changed: [client01.example.com] changed: [client02.example.com] PLAY [nginx01] **************************************************************** GATHERING FACTS *************************************************************** ok: [client01.example.com] TASK: [nginx01 | ensure nginx is at the latest version] *********************** ok: [client01.example.com] TASK: [nginx01 | write the nginx config file] ********************************* ok: [client01.example.com] TASK: [nginx01 | Create Web Root] ********************************************* ok: [client01.example.com] TASK: [nginx01 | ensure nginx is running] ************************************* changed: [client01.example.com] PLAY [nginx02] **************************************************************** GATHERING FACTS *************************************************************** ok: [client02.example.com] TASK: [nginx02 | ensure nginx is at the latest version] *********************** ok: [client02.example.com] TASK: [nginx02 | write the nginx config file] ********************************* ok: [client02.example.com] TASK: [nginx02 | Create Web Root] ********************************************* ok: [client02.example.com] TASK: [nginx02 | ensure nginx is running] ************************************* changed: [client02.example.com] PLAY RECAP ******************************************************************** client01.example.com : ok=11 changed=3 unreachable=0 failed=0 client02.example.com : ok=11 changed=3 unreachable=0 failed=0
最終部署目錄結構如下
# tree /etc/ansible/
/etc/ansible/ ├── ansible.cfg ├── group_vars │ ├── nginx01 │ └── nginx02 ├── hosts ├── hosts.bak ├── roles │ ├── base_env │ │ ├── files │ │ │ ├── epel.repo │ │ │ └── RPM-GPG-KEY-EPEL-6 │ │ └── tasks │ │ └── main.yml │ ├── nginx01 │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ └── nginx.conf │ └── nginx02 │ ├── tasks │ │ └── main.yml │ └── templates │ └── nginx.conf └── site.yml 11 directories, 13 files
到此, 部署nignx到兩台遠程webserver服務器全部完成.
