透過 Ansible playbook 來手動安裝 Docker
在前一篇「在 GCP 上執行你第一個 Ansible Ad-Hoc Command」 學到如何透過 ad-hoc 的方式來操作 Ansible, 這篇文章希望透過 Ansible playbook 來更有效的組織、自動化你的流程,會以一個安裝 docker 的方式作為範例來撰寫 playbook。
建立第一個 ansible playbook
以下面是一個簡單的 playbook 結構:
---- hosts: serverbecome: truetasks:- name: Test Sever Connectionping:
hosts
:是你 inventory 內中的主機,可能一台或是某個群組become
:這裡設定為true
預設會以 root 權限執行,也可以透過become_user
來指定其他使用者tasks
:一系列要做的任務,每個 tasks 下面可能會有多個子 taskname
&ping
:name
這裡指的是這個 task 的名稱,而ping
則是 playbook module 中所提供的一個模組
接著請開一個新的資料夾叫做 ansible-playbook-example
,並建立你的 inventory 和一個 main.yml
,
接著把上面的 YAML 內容貼入到 main.yml
:
$ mkdir ansible-playbook-example$ cd ansible-playbook-example$ touch inventory main.yml
接著透過 --syntax-check
檢查你的 playbook 內的語法是否有錯誤:
$ ansible-playbook -i inventory main.yml --syntax-check
如果正確不會有錯誤的訊息,假設我今天 YAML 的縮排沒有寫好:
ERROR! 'ping' is not a valid attribute for a PlayThe error appears to be in '/Users/jiepeng/neighborhood999/ansible-playbook-example/main.yml': line 2, column 3, but maybe elsewhere in the file depending on the exact syntax problem.The offending line appears to be:---- hosts: pingserver^ here
可以看到 ping
的欄位有問題。在執行 playbook 的時候,就會自動幫你做檢查了,這裡只是想提到有這個用法。
最後執行 playbook 來 ping 看看遠端的機器:
$ ansible-playbook -i inventory main.ymlPLAY [pingserver] ******************************************************************************************************TASK [Gathering Facts] *************************************************************************************************ok: [123.123.1.10]TASK [Test Sever Connection] *******************************************************************************************ok: [123.123.1.10]PLAY RECAP *************************************************************************************************************123.123.1.10 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
成功!
透過 Ansible playbook 手動安裝 Docker
由於我在 GCE 上所開的機器的系統是 Ubuntu,所以這裡的安裝範例會以官方所提供的 Ubuntu 來示範。
延續上面的 main.yml
內容,繼續撰寫 playbook:
---- hosts: serverbecome: truetasks:- name: Test Sever Connectionping:
Installing Required Packages
在安裝 docker 之前,有一些套件是必需要用到的:
- apt-transport-https
- ca-certificates
- curl
- gnupg-agent
- software-properties-common
這些套件可以透過 apt-get install
來安裝,但要把這些東西直接寫死在 playbook 內好像不太好,未來如果要新增其它套件時,修改起來比較不方便。
Ansible 提供了 var_files
這個欄位,可以讓你來 include 你所定義的 variables 檔案,這裡讓我們建立一個 vars.yml
,內容如下:
requried_packages:- apt-transport-https- ca-certificates- curl- gnupg-agent- software-properties-common
required_packages
就是變數名稱,它可以被用在 playbook,內容就是安裝 docker 前所需要安裝的套件,實際上它就是一個陣列,也可以寫成:
requried_packages: [apt-transport-https, ca-certificates, curl, gnupg-agent, software-properties-common]
看個人習慣,這邊就以前者作為範例。
回到 main.yml
新增安裝套件的 task(綠色部分,紅色請忽略 😂):
---- hosts: serverbecome: truetasks:- name: Test Sever Connectionping:++ - name: Install required packages+ apt:+ name: "{{ item }}"+ state: latest+ update_cache: true+ loop: "{{ requried_packages }}"
這裡使用了 apt module,並使用了 loop 來迭代 requried_packages
內的參數,
"{{ required_packages }}"
是 Jinja2 templating 語法,透過 {{ }}
來存取變數,所以這裡的 task 就會根據我們所提供的內容進行安裝:
$ ansible-playbook -i inventory main.ymlPLAY [pingserver] *************************************************************************************************************TASK [Gathering Facts] *************************************************************************************************ok: [123.123.1.10]TASK [Test Connection] *************************************************************************************************ok: [123.123.1.10]TASK [Install required packages] ***************************************************************************************changed: [123.123.1.10] => (item=apt-transport-https)ok: [123.123.1.10] => (item=ca-certificates)ok: [123.123.1.10] => (item=curl)changed: [123.123.1.10] => (item=gnupg-agent)ok: [123.123.1.10] => (item=software-properties-common)PLAY RECAP *************************************************************************************************************123.123.1.10 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
安裝成功!
從輸出可以看到 Test Connection 的內容沒有改變,狀態是 ok,而 Install required packages 是第一次執行 package 安裝,有些狀態是 changed,原因是系統內是沒有這些 package,所以會進行安裝,算是更動。
當安裝完畢後,再執行一次 playbook 而不做改變可以注意到 Install required packages 全部都是 ok。
Add Docker's Official GPG Key
透過 apt_key module 來加入 GPG Key:
---- hosts: serverbecome: truetasks:- name: Test Sever Connectionping:- name: Install required packagesapt:name: "{{ item }}"state: latestupdate_cache: trueloop: "{{ requried_packages }}"++ - name: Add docker's official GPG key+ apt_key:+ url: https://download.docker.com/linux/ubuntu/gpg+ state: present
Add Docker Repository
透過 apt_repository 把 docker 的 repository 加入到你的 ppa:
- name: Add docker repositoryapt_repository:repo: deb https://download.docker.com/linux/ubuntu bionic stablestate: present
Install Docker Engine
Docker engine 這部分也有幾個東西要安裝,就像前面安裝 Ubuntu 套件一樣,在 vars.yml
內新增一個 docker_engines
變數,內容是安裝 docker engine 的東西:
vars.yml
requried_packages:- apt-transport-https- ca-certificates- curl- gnupg-agent- software-properties-commondocker_engines:- docker-ce- docker-ce-cli- containerd.io
同樣透過 loop 的方式來依序安裝我們需要的套件:
main.yml
- name: Update apt cache and install docker engineapt:name: "{{ item }}"state: latestupdate_cache: trueloop: "{{ docker_engines }}"
完成以上的步驟後,你的 main.yml
會長這樣:
---- hosts: serverbecome: truevars_files:- vars.ymltasks:- name: Test Connectionping:- name: Install required packagesapt:name: "{{ item }}"state: latestupdate_cache: trueloop: "{{ requried_packages }}"- name: Add docker's official GPG keyapt_key:url: https://download.docker.com/linux/ubuntu/gpgstate: present- name: Add docker repositoryapt_repository:repo: deb https://download.docker.com/linux/ubuntu bionic stablestate: present- name: Update apt cache and install docker engineapt:name: "{{ item }}"state: latestupdate_cache: trueloop: "{{ docker_engines }}"
Verify Docker
安裝完 docker 後,驗證一下是否正確安裝,執行一個 hello-world
docker container 做測試:
- name: Verify docker installedbecome: truecommand: docker run --rm hello-world
在這邊執行一次 playbook 看一下結果:
$ ansible-playbook -i inventory main.ymlPLAY [server] **********************************************************************************************************TASK [Gathering Facts] *************************************************************************************************ok: [123.123.1.10]TASK [Test Connection] *************************************************************************************************ok: [123.123.1.10]TASK [Install required packages] ***************************************************************************************ok: [123.123.1.10] => (item=apt-transport-https)ok: [123.123.1.10] => (item=ca-certificates)ok: [123.123.1.10] => (item=curl)ok: [123.123.1.10] => (item=gnupg-agent)ok: [123.123.1.10] => (item=software-properties-common)TASK [Add docker's official GPG key] ***********************************************************************************changed: [123.123.1.10]TASK [Add docker repository] *******************************************************************************************changed: [123.123.1.10]TASK [Update apt cache and install docker engine] **********************************************************************changed: [123.123.1.10] => (item=docker-ce)ok: [123.123.1.10] => (item=docker-ce-cli)ok: [123.123.1.10] => (item=containerd.io)TASK [Verify docker installed] *****************************************************************************************changed: [123.123.1.10]PLAY RECAP *************************************************************************************************************123.123.1.10 : ok=8 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Add User To Docker Group
完成上述步驟後,現在進入遠端伺服器進行操作試試看:
$ jiepeng@server:~$ docker psGot permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/json: dial unix /var/run/docker.sock: connect: permission denied
代表你權限不足所以沒辦法執行 docker 指令,這時候加上 sudo docker [command]
就可以了。
由於一開始就是以管理員的身份來做這一連串的操作(become: true
),所以若要執行 docker 必須要加上 sudo docker command
才可以執行,
為了讓方便讓其他使用者也可以用,把這個使用者加入 docker 的 group,直接操作可以透過 sudo usermod -aG docker [username]
,但這裡讓我們透過 playbook 來完成這件事。
首先檢查一下 docker group 存不存在(有點多此一舉),接著把使用者加入 docker 這個 group,{{ user }}
則是在 vars.yml
內新增的變數。
- name: Check docker group is existsgroup:name: dockerstate: present- name: Add user to docker groupuser:name: "{{ user }}"group: docker
執行:
$ ansible-playbook -i inventory main.ymlPLAY [server] **********************************************************************************************************TASK [Gathering Facts] *************************************************************************************************ok: [123.123.1.10]TASK [Test Connection] *************************************************************************************************ok: [123.123.1.10]TASK [Install required packages] ***************************************************************************************ok: [123.123.1.10] => (item=apt-transport-https)ok: [123.123.1.10] => (item=ca-certificates)ok: [123.123.1.10] => (item=curl)ok: [123.123.1.10] => (item=gnupg-agent)ok: [123.123.1.10] => (item=software-properties-common)TASK [Add docker's official GPG key] ***********************************************************************************ok: [123.123.1.10]TASK [Add docker repository] *******************************************************************************************ok: [123.123.1.10]TASK [Update apt cache and install docker engine] **********************************************************************ok: [123.123.1.10] => (item=docker-ce)ok: [123.123.1.10] => (item=docker-ce-cli)ok: [123.123.1.10] => (item=containerd.io)TASK [Verify docker installed] *****************************************************************************************changed: [123.123.1.10]TASK [Check docker group is exists] ************************************************************************************ok: [123.123.1.10]TASK [Add user to docker group] ****************************************************************************************changed: [123.123.1.10]PLAY RECAP *************************************************************************************************************123.123.1.10 : ok=9 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
注意到 TASK [Add user to docker group] 部分的狀態是 changed,代表使用者有被加入到 docker group 了,再重新 ssh 登入伺服器測試後就可以成功直接執行 docker 指令了。
完整範例
main.yml
---- hosts: serverbecome: truevars_files:- vars.ymltasks:- name: Test Connectionping:- name: Install required packagesapt:name: "{{ item }}"state: latestupdate_cache: trueloop: "{{ requried_packages }}"- name: Add docker's official GPG keyapt_key:url: https://download.docker.com/linux/ubuntu/gpgstate: present- name: Add docker repositoryapt_repository:repo: deb https://download.docker.com/linux/ubuntu bionic stablestate: present- name: Update apt cache and install docker engineapt:name: "{{ item }}"state: latestupdate_cache: trueloop: "{{ docker_engines }}"- name: Verify docker installedbecome: truecommand: docker run --rm hello-world- name: Check docker group is existsgroup:name: dockerstate: present- name: Add user to docker groupuser:name: "{{ user }}"group: docker
vars.yml
user: [your-username]requried_packages:- apt-transport-https- ca-certificates- curl- gnupg-agent- software-properties-commondocker_engines:- docker-ce- docker-ce-cli- containerd.io
希望透過這個簡單的 docker 安裝範例讓你可以更了解 Ansible playbook。