- 說明
- 8元小課
- 一句話原理
- 文檔介紹
- 下載素材
- 兩個命令: ansible 與 ansible-playbook
- 用ansible命令操作目標機器
- 用ansible-playbook命令操作目標機器
- 將操作以role為單位進行分組
- 常用的目標機器初始化操作
- 變量、文件、模版與Handler
- 參考
說明
ansible是一個常用的運維管理工具,使用它可以避免很多重復性工作,節省大量時間。
這里是網易雲課堂·IT技術快速入門學院演示視頻中使用的文檔,8元小課系列,可以在系列教程中找到該系列所有文章。
QQ交流群(ansible實踐互助):955105412。
8元小課
之前嘗試制作了兩期《HyperLedger Fabric》的課程,得到不少了同學的捧場。同時發現一些技術工具和學習方法,對我們這些工作了好多年的老鳥來說,早已習以為常,但是對於部分還在學校的或剛畢業的同學來說,非常陌生。
這些內容本質上又非常簡單,只有“知道”與“不知道”這樣一點點區別,不值得長篇大論,但結果卻是沒有人來點破,或者被包裹進昂貴的課程中,不可思議的價格,給在校生帶來經濟上的壓力。
我們認為,把這些內容用“小課”的方式呈現出來,是很有價值的。一門小課,就像是公司內部的一次小小的分享會,可以把一個人的勞動所得復制給更多人,從而為聽眾節省大量的時間。
一句話原理
ansible就是把你手動ssh登錄到多個目標機器上進行的一系列操作的過程自動化。
你只需要確保執行ansible命令的本地機器能夠通過用戶名和密碼登錄到目標機器上,並且在本地機器上的ansible文件中寫好要在目標機器上執行的操作。
目標機器只需要支持ssh登錄和python命令(一般的linux操作系統都有,ansible會將python寫的任務腳本上傳到目標機器上執行)。
文檔介紹
ansible的文檔首頁 https://docs.ansible.com/ 對文檔進行了分類,都是接到了文檔內容頁面。
安裝文檔中介紹了ansible的安裝方法,作為一個很基礎的工具,基本上每個操作系統,都有對應的安裝方法。
官方的Getting Started介紹的太簡單了,對初學者來說,看完還是一頭霧水。
下載素材
git clone https://github.com/lijiaocn/ansible-example.git
兩個命令: ansible 與 ansible-playbook
ansible有兩個命令,一個是ansible
,一個是ansible-playbook
,前者需要每次輸入要執行的命令,后者可以讀取playbook
文件,一次性完成playbook文件中指定一系列操作。
playbook文件是重點,文檔中有很大篇幅是介紹playbook的:playbook。
用ansible命令操作目標機器
准備hosts文件
需要准備一個文件,在文件中寫下目標機器的地址,這個文件默認是/etc/ansible/hosts
,但是為了管理方便,最好為每個環境單獨創建一個hosts文件。
比方說創建一個名為inventories
的目錄,在這個目錄下,為生產環境的機器創建一個production
目錄,production/hosts
中記錄的是生產環境中的機器的地址,demo/hosts
中記錄的是演示環境中機器的地址,這樣將不同環境中的機器明確地分開了,可以減少運維事故。
$ tree inventories/
inventories/
├── production
│ └── hosts
└── demo
└── hosts
hosts
文件中可以直接是目標機器的地址,可以是IP,也可以是域名,每個地址占用一行,例如:
192.168.33.11
www.baidu.com
如果目標集群中的機器的角色相同,承擔的是同樣任務,這種方式一般也足夠了。如果目標集群中的機器分別承擔不同任務,最好將它們按照各自的角色分組,例如:
[master] 192.168.33.11 [nodes] 192.168.33.11 192.168.33.12 192.168.33.13
同一個地址,可以同時位於多個組中。
可以對分組再次分組,例如《Kubernetes1.12從零開始》中使用的hosts文件是這樣的:
[etcd] 192.168.33.11 192.168.33.12 192.168.33.13 [master] 192.168.33.11 192.168.33.12 192.168.33.13 [node] 192.168.33.11 192.168.33.12 192.168.33.13 [kube-router] 192.168.33.11 192.168.33.12 192.168.33.13 ############# group's group ############## [etcd_client:children] etcd master [etcd_server:children] etcd [etcd_peer:children] etcd [apiserver:children] master [controller:children] master [scheduler:children] master [kubelet_client:children] master [kubelet:children] node
名稱里有:children
的分組,是分組的分組,它的成員是前面定義的分組。
還可以在這里為每個機器
設置變量,譬如《HyperLedger Fabric手把手入門》中使用的hosts文件:
[orderer] orderer0.member1.example.com MSPID=orderers.member1.example.com ORG_DOMAIN=member1.example.com ansible_host=192.168.33.11 [peer] peer0.member1.example.com MSPID=peers.member1.example.com ORG_DOMAIN=member1.example.com ansible_host=192.168.33.11 STATE_DB=CouchDB COUCH_USER=admin COUCH_PASS=password peer1.member1.example.com MSPID=peers.member1.example.com ORG_DOMAIN=member1.example.com ansible_host=192.168.33.12 STATE_DB=CouchDB COUCH_USER=admin COUCH_PASS=password peer0.member2.example.com MSPID=peers.member2.example.com ORG_DOMAIN=member2.example.com ansible_host=192.168.33.13 STATE_DB=CouchDB COUCH_USER=admin COUCH_PASS=password [machine] 192.168.33.11 192.168.33.12 192.168.33.13
你已經注意到了,這個hosts文件不太一樣,地址后面多出了一些諸如MSPID=XXX
樣式的內容,它們是為對應機器設置的變量,這些變量在可以在后面要講的playbook文件中引用。
分組和變量的使用方法在后面演示,現在你先記得有這么一回事就行。
另外關於分組還要多說一句,ansible有兩個默認的分組:all
和ungrouped
:all分組包括所有分組的中的機器,ungrouped是所有只屬於all分組,不屬於其它分組的機器。 在定義你自己的分組的時候,要注意分組名稱不要與它們沖突。
講述這部分內容的官方文檔是:Working with Inventory
使用modules開始操作
Modules是ansible的“軍火庫”,幾乎所有的操作功能都是用module實現的。
ansible用到最后,就是在使用module。 module的數量相當多,好在常用的就那么幾個,這里演示一些常用的,其它的你可以通過每個module的文檔學習。
ping模塊是用來測試目標機器是否可達的,用法如下:
lijiaos-mbp:example lijiao$ ansible -i inventories/demo/hosts -u root -k all -m ping SSH password: 192.168.33.12 | SUCCESS => { "changed": false, "ping": "pong" } 192.168.33.11 | SUCCESS => { "changed": false, "ping": "pong" }
-i
指定hosts文件,-u
指定目標機器上的用戶名,-k
指定目標機器登錄密碼,all
是要操作的hosts文件中的分組,前面我們說過,all是默認存在的一個分組,包括所有機器,-m
指定要使用的模塊ping
。
ping模塊大概是最簡單的一個模塊,沒有參數,再來看一個復雜一點的模塊shell,它的功能是在目標機器上執行shell命令:
lijiaos-mbp:example lijiao$ ansible -i inventories/demo/hosts -u root -k all -m shell -a "hostname" SSH password: 192.168.33.11 | SUCCESS | rc=0 >> 192.168.33.11 192.168.33.12 | SUCCESS | rc=0 >> 192.168.33.12
-a
是指定傳遞給模塊的參數。
用ansible
命令對目標機器操作時,都是在命令行指定要做的操作,一般都是一些比較簡單操作,譬如查看下狀態、上傳下載文件等。
很多強大的功能要通過ansible-playbook
才能發揮出來。
用ansible-playbook命令操作目標機器
playbooks是yml格式的文件,描述了要在哪些機器上執行哪些操作。
在目標機器上創建一個文件
創建一個playbook文件,playbook-single.yml,如下:
- hosts: machines remote_user: root tasks: - name: create a tmp file shell: | cd /tmp/ touch abcd123
這個playbook文件的意思是,在所有的machines
上,用root的身份執行,並通過shell模塊創建文件/tmp/abcd123,用法如下:
lijiaos-mbp:example lijiao$ ansible-playbook -i inventories/demo/hosts -k playbook-single.yml SSH password: PLAY [machines] ****************************************************************************** TASK [Gathering Facts] *********************************************************************** ok: [192.168.33.12] ok: [192.168.33.11] TASK [create a tmp file] ********************************************************************* changed: [192.168.33.12] changed: [192.168.33.11] PLAY RECAP *********************************************************************************** 192.168.33.11 : ok=2 changed=1 unreachable=0 failed=0 192.168.33.12 : ok=2 changed=1 unreachable=0 failed=0
注意這里使用ansible-playbook
命令,-i
和-k
參數含義與前面ansible命令的參數相同,這里沒有使用-u
指定賬號,是因為在playbook-single.yml中已經設置了使用root:
remote_user: root
操作在playbook文件的tasks
中設置,tasks是一個數組,可以添加多個任務:
tasks:
- name: create a tmp file # 自定義的操作名稱
shell: | # 使用shell模塊,后面的|是yaml語法,表示后面空行之前的內容都是shell模塊的參數
cd /tmp/
touch abcd123
用ansible命令來看一下文件是否創建:
lijiaos-mbp:example lijiao$ ansible -i inventories/demo/hosts -u root -k all -m shell -a "ls /tmp/abc*" SSH password: 192.168.33.11 | SUCCESS | rc=0 >> /tmp/abcd123 192.168.33.12 | SUCCESS | rc=0 >> /tmp/abcd123
將操作以role為單位進行分組
前面給出的ansible-playbook的用法,是最初級的用法,比較完整的用法是將操作封裝到role中。
先解釋一下什么是role,為什么要有role。
在ansible看來role就是對playbook中的操作做了一次分組,把一些操作放在這個role中,另一些操作放在那個role中。
在我們看來,role是目標機器的角色之一,我們把不同的角色的操作划分到不同的目錄中,一是管理方便,二是可以復用。
role要在roles
目錄中定義,在roles目錄中創建與role同名的目錄,每個role目錄中包含四個目錄:
lijiaos-mbp:example lijiao$ tree roles/ roles/ └── prepare ├── files │ └── demo.file ├── handlers │ └── main.yml │ └── centos.yml ├── tasks │ └── main.yml └── templates └── demo.template.j2
tasks
目錄中的main.yml
是這個role的操作入口,handlers/main.yml
中是一些可以被觸發
的操作,files
中存放可以直接被上傳到目標機器的文件,templates
中存放的是可以直接上傳到目標機器的模版文件,這兩個的區別后面說明。
注意tasks/main.yml
是必須要有的,其它目錄中如果沒有文件,可以不創建。
上面的目錄中創建了一個名為prepare
的role,我們計划將機器的初始化設置操作全部在收集在這個role中,task/main.yml是這樣寫的:
- name: Set authorized key
tags: ssh
authorized_key:
user: root
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
- name: Set hostname
hostname:
name: "{{ inventory_hostname }}"
- name: Set bash prompt
shell: |
echo 'export PS1="[\u@\H \W]\\$ "'>> ~/.bashrc
- name: install dependent packages
import_tasks: centos.yml
when: ansible_distribution == "CentOS"
用到了authorized_key、hostname、shell和import_tasks四個模塊。
當目標機器的操作系統是ansible的時候,import_tasks
引入了centos.yml
文件:
- name: set time zone
file:
src: '{{ item.src }}'
dest: '{{ item.dest }}'
state: link
with_items:
- { src: "/usr/share/zoneinfo/Asia/Shanghai", dest: "/etc/localtime" }
- name: set local
shell: localedef -i zh_CN -f UTF-8 zh_CN.UTF-8
- name: install epel
yum:
name: "{{ item }}"
state: present
with_items:
- epel-release
- name: install pkgs
yum:
name: "{{ item }}"
state: present
with_items:
- yum-utils
- ipset
- iptables
- iproute
- ipvsadm
- supervisor
- ntp
- name: start basic service
systemd:
enabled: yes
name: "{{ item }}"
state: started
with_items:
- ntpd
- supervisord
這些操作的含義在后面章節逐一說明,先給出用法:
ansible-playbook -i inventories/demo/hosts -u root -k prepare.yml
常用的目標機器初始化操作
這里介紹role/prepare/task/main.yml文件中的操作。
設置免密碼登錄
前面的操作過程中使用了-k
參數,每次都需要輸入密碼,一是比較煩,二是如果機器的密碼不同,那就失靈了(后面會演示一下如果目標機器密碼不同該怎樣操作)。
最好把本地的證書傳到目標機器上,實現免密碼登錄,prepare的task/main.yml中,有這樣一段:
- name: Set authorized key
tags: ssh
authorized_key:
user: root
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
它就是用authorized_key模塊將本地的證書~/.ssh/id_rsa.pub
上傳到目標機器上,實現免密碼登錄。
注意你需要確保你本地有id_rsa.pub文件,否則用ssh-keygen
命令創建一個:
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/lijiao/.ssh/id_rsa):
設置目標機器的hostname
- name: Set hostname
hostname:
name: "{{ inventory_hostname }}"
- name: Set bash prompt
shell: |
echo 'export PS1="[\u@\H \W]\\$ "'>> ~/.bashrc
設置目標機器的時區
- name: set time zone
file:
src: '{{ item.src }}'
dest: '{{ item.dest }}'
state: link
with_items:
- { src: "/usr/share/zoneinfo/Asia/Shanghai", dest: "/etc/localtime" }
用yum安裝依賴包
- name: install epel
yum:
name: "{{ item }}"
state: present
with_items:
- epel-release
- name: install pkgs
yum:
name: "{{ item }}"
state: present
with_items:
- yum-utils
- ipset
- iptables
- iproute
- ipvsadm
- supervisor
- ntp
用systemd啟動服務
- name: start basic service
systemd:
enabled: yes
name: "{{ item }}"
state: started
with_items:
- ntpd
- supervisord
變量、文件、模版與Handler
這里通過在目標機器上部署、設置nginx,講解角色下面的files、templates和handlers目錄的作用。
nginx
role的文件如下:
lijiaos-mbp:example lijiao$ tree roles/nginx/ roles/nginx/ ├── files │ ├── start.sh │ └── stop.sh ├── handlers │ └── main.yml ├── tasks │ └── main.yml └── templates └── hello.com.conf.j2
變量的定義和引用
nginx/tasks/main.yml
內容是:
- name: install pkgs
yum:
name: "{{ item }}"
state: present
with_items:
- nginx
- name: nginx is running
systemd:
name: nginx
state: started
daemon_reload: yes
- name: create directory
file:
path: "{{ item }}"
state: directory
with_items:
- "{{ nginx_config_path }}"
- "{{ nginx_script_path }}"
- name: upload template config
notify: reload nginx
template:
src: "{{ item }}.j2"
dest: "{{ nginx_config_path }}/{{ item }}"
with_items:
- hello.com.conf
- name: upload files
copy:
src: "{{ item }}"
dest: "{{ nginx_script_path }}/{{ item }}"
mode: u=rwx
with_items:
- start.sh
- stop.sh
這里有兩個變量:nginx_config_path
和nginx_script_path
,用兩個大括號包裹引用。
它們是在inventories/demo/group_vars/all
中定義的:
nginx_config_path: /etc/nginx/conf.d
nginx_script_path: /root/nginx
變量除了可以在group_vars
和host_vars
目錄中定義,還可以在hosts文件中定義:
[machines]
192.168.33.11 port=8001
192.168.33.12 port=8002
以及在playbook文件中定義,回想一下我們用到的第一個playbook,里面有vars
:
$ cat playbook-single.yml - hosts: machines vars: http_port: 80 max_clients: 200 remote_user: root tasks: - name: create a tmp file shell: | cd /tmp/ touch abcd123
模版上傳
role/nginx/templates/hello.com.conf.j2
是一個模版文件: ,模版文件中可以使用變量:
server {
listen {{ port }};
location / {
proxy_pass https://www.baidu.com ;
}
}
模版文件中可以使用變量,這里使用的變量port
是在hosts文件中定義的,可以為每個機器定義不同的端口:
[machines]
192.168.33.11 port=8001
192.168.33.12 port=8002
它們被用template模塊上傳,上傳時會將模版文件中的變量換成變量的值,如下:
- name: upload template config
notify: reload nginx
template:
src: "{{ item }}.j2"
dest: "{{ nginx_config_path }}/{{ item }}"
with_items:
- hello.com.conf
文件上傳
role/nginx/files
中的文件,用COPY命令上傳,文件不會被做任何改動,這一點和templates顯著不同:
- name: upload files
copy:
src: "{{ item }}"
dest: "{{ nginx_script_path }}/{{ item }}"
mode: u=rwx
with_items:
- start.sh
- stop.sh
handler的觸發
在tasks中,用notify
命令觸發handler的執行:
- name: upload template config
notify: reload nginx
template:
src: "{{ item }}.j2"
dest: "{{ nginx_config_path }}/{{ item }}"
with_items:
- hello.com.conf
只有被觸發的handler才會運行,並且是在所有的task之后運行。
如果有多個handler被觸發,按照它們在handlers/main.yml中出現的順序執行。
什么時候要用handler?
譬如說,配置文件被更新以后,需要重啟或者重新加載的服務,這時候就可以在更新配置文件的task中,使用notify觸發handler。