【視訊】特別適合新手的運維利器ansible入門教程手冊(附帶視訊演示和原始碼)
說明
ansible是一個常用的運維管理工具,使用它可以避免很多重複性工作,節省大量時間。
這裡是IT技術快速入門學院演示視訊中使用的文件,可以在系列教程中找到該系列所有文章。
QQ交流群(ansible實踐互助):955105412。
一句話原理
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.11192.168.33.12192.168.33.13
同一個地址,可以同時位於多個組中。
可以對分組再次分組,例如《Kubernetes1.12從零開始》中使用的hosts檔案是這樣的:
[etcd]192.168.33.11192.168.33.12192.168.33.13[master]192.168.33.11192.168.33.12192.168.33.13[node]192.168.33.11192.168.33.12192.168.33.13[kube-router]192.168.33.11192.168.33.12192.168.33.13############# group's group ##############[etcd_client:children]etcdmaster[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=passwordpeer1.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=passwordpeer0.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.11192.168.33.12192.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="[\[email protected]\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="[\[email protected]\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。