說明
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="[\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。