ansible-container 的二度踩雷之旅
(2021/12/25 註記:左思右想,決定棄用 Medium,未來更多文章請參閱 https://chengweichen.com)
(本文的 Canonical URL —https://chengweichen.com/2018/03/ansible-container.html)
自從上次(2016年)試用 ansible-container
遭遇慘痛的踩雷經驗之後,如今 ansible-container
的版本號已進入 0.9.2,據說文件及穩定度皆已有所改善,因此決定趁著放假撥一點空檔再次試用它,希望這次的試用過程不會像上次一樣地慘痛。
以乾淨的 Ubuntu 16.04 環境試用
根據官方文件,ansible-container
的 Prerequisites 為
- Python 2.7 or Python 3.5
- pip
- setuptools 20.0.0+
因此決定直接以乾淨的 ubuntu 16.04 環境挑戰,避免遇到 python 版本或相依套件的問題。首先透過 Vagrant 建立乾淨的 ubuntu 16.04,並在 VM 內安裝 Docker。接著以下面指令安裝 pip
。
apt-get update
apt-get install python-pip
pip install --upgrade pip
接著按官方文件安裝 ansible-container
。
pip install ansible-container[docker]
([]
內除了可以填 docker 之外,也可填寫另外兩種 engines,分別是 k8s 及 openshift。)
順利安裝後即可執行 ansible-container -h
,查看 help。
試用 ansible-container
開始簡易地試用 ansible-container
首先建立乾淨的資料夾並產生相關檔案。
mkdir demo
cd demo
ansible-container init
執行指令之後,即可產生 ansible-container
所需之結構,,如下圖可以看見一共自動產生了五個檔案,每個檔案分別有其用途。
ansible.cfg
- 應該無須解釋,即是ansible
的 Configuration file。在執行ansible
指令時,會優先參照當前路徑之下有沒有ansible.cfg
,因此若是需要特別的組態設定時,即可隨著專案各別搭配一個ansible.cfg
。ansible-requirements.txt
- 用來紀錄所需之 Python dependencies。container.yml
- 類似docker-compose.yml
,用來描述你所需之 container 架構與環境,這正是ansible-container
指令的關鍵檔案。meta.yml
- 如同 Ansible Roles 的 meta,官方似乎有意要讓 Ansible Galaxy 也能夠存放 ansible-container 的 templates。(不過目前在 Ansible Galaxy 上,似乎尚無法針對此種 templates 進行搜尋)requirements.yml
- 同 Roles,用來紀錄所需之相依 Roles。
接著修改 container.yml
,將 services:
這一段修改成如下。
services:
web:
from: "centos:7"
ports:
- "80:80"
command: ["/bin/bash"]
接下來就試著執行 ansible-container build
,送出指令之後需要一段不短的等待時間,你會發現如下圖,畫面上也會秀出訊息要你 be patient。
等待許久之後,接著就⋯⋯就爆掉了。
ERROR Unknown exception
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/container/cli.py", line 299, in __call__
getattr(core, u'hostcmd_{}'.format(args.subcommand))(**vars(args))
File "/usr/local/lib/python2.7/dist-packages/container/__init__.py", line 28, in __wrapped__
return fn(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/container/core.py", line 181, in hostcmd_build
environment=env_vars
File "/usr/local/lib/python2.7/dist-packages/container/docker/engine.py", line 105, in __wrapped__
return fn(self, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/container/__init__.py", line 28, in __wrapped__
return fn(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/container/docker/engine.py", line 1070, in build_conductor_image
return image.id
AttributeError: 'tuple' object has no attribute 'id'
根據 issue,有其他使用者表示可以用下列指令修復。
pip install docker==2.7.0
修復並再次執行 ansible-container build
,即可獲得正常的結果。
那麼到底 ansible-container
在背後偷偷做了哪些事情了?大致包含了以下幾項:(如果你有興趣一探究竟,可以改用指令 ansible-container --debug build
,即可一窺其中的奧妙。)
- 下載
ansible-container
所需之 docker images。 - 下載
container.yml
中載明各 Service 所需之 base image。 - 接著會 build 類似
ansible/container-conductor-centos-7:0.9.2
此種名稱的 docker images。 - 最後運行一些名稱帶有 conductor 的 container,根據
container.yml
的設定,進行環境設置(包含你想要執行的 roles),將 base image 建置成各 services 最終的 docker images。
試著以 ansible-container 建立 nginx + php 的環境
上面我們測試 ansible-container build
時,使用的是一個沒特別意義的 container.yml
,接著我們就嘗試用 ansible-container
建立並運行 nginx + php 的環境。
第一步修改 container.yml
。
version: "2"
settings:
conductor:
base: ubuntu:16.04
project_name: demo
services:
php:
from: "ubuntu:16.04"
roles:
- { role: geerlingguy.php, php_enable_webserver: false, php_enable_php_fpm: true }
- create_run_php
ports:
- "9000:9000"
command: ["/usr/sbin/php-fpm7.0", "-F"]
nginx:
from: "ubuntu:16.04"
roles:
- geerlingguy.nginx
ports:
- "80:80"
links:
- php
command: ["/usr/sbin/nginx", "-g", "daemon off;"]
registries: {}
按上述的 yml,如果是熟悉 docker、docker-compose 的工程師應該多半能推敲出會產生什麼樣的結果:
- 會 build 兩個 docker image
- 皆是以 ubuntu:16.04 為 base image
- 分別提供 php 及 nginx 這兩種 service
- 屆時運行 php 的 container 時會開通 port 9000
- 同理運行 nginx 的 container 則開通 port 80
- 同時運行 nginx 的 container 時要相依於 php 的 container
- 另外就是為了 build 出這兩個 image,會以 base image 為基礎,透過 geerlingguy.php 及 geerlingguy.nginx 這兩個 Ansible role 來安裝所需之 php 及 nginx
眼尖的人應該有注意到在 php 那一段有還有多一個 role 名為 create_run_php
,特別針對這點說明。狀況是這樣的,如果你採用 ubuntu:16.04 的 base image 並於其上透過 role geerlingguy.php
來安裝 php 環境。在安裝完 php,想要啟動 php-fpm
時,將會遇見因為找不到 /run/php/php7.0-fpm.pid
而無法啟動的錯誤。
為了修正這個錯誤,最簡單的方法是手動補建 /run/php
這個資料夾。因此這個 create_run_php
即是用來幫我修復這個錯誤的客制 role。(或者你也可以考慮以修改 php-fpm.conf
中 pid = /run/php/php7.0-fpm.pid
的設定來解決。)
搞定 container.yml
可別急著執行 ansible-container build
,在那之前你還得先將會使用到的 roles 都先下載完畢才行,這部分只要按 ansible-playbook
的使用慣例,在 container.yml
同一個資料夾內建立 roles
資料夾,並將各個 roles 分別丟進以自己名稱為名的資料夾即可。
接著就執行 ansible-container build
,如一切順利,應該會看見類似下述的過程。
首先會看見 ansible-container
真的會在 container 上跑 role。
當 role 都執行完畢,就會 build 成 image,並且繼續 build 下一個 image。
所有的 image 都建置完畢之後,就會如下圖一樣,告知你 All images successfully built
。
接著就用 docker images
查看成果。
透過以上的案例,我們驗證了確實可以透過 ansible-container
運用既有 roles 來建置 docker images,但實際上 roles 不一定能 100% 直接套用,例如前述在 php 的 container 內缺少 /run/php
該路徑的問題就是一個例子,將原本針對 VM、Server 而撰寫的 roles 轉而使用在 container 時可能會出現部分需要微調之處。
ansible-container run
除了 ansible-container build
之外,還有另一個厲害的指令是 ansible-container run
,前面在介紹 container.yml
時有提過,它的結構非常類似 docker-compose.yml
,這並非錯覺,因為 ansible-container
不僅可以用來建置 images,它也能如 docker-compose up
一樣,幫你運行多個 container 並建立彼此的相依與關聯。
續上案例,直接來執行 ansible-container run
,即可看見如下圖的結果:
這次用 docker ps
來檢查,得到下圖的結果。
就如 container.yml
所紀錄的一樣,順利運行 php 及 nginx 兩個 container,並且其中的 service 皆有在提供服務。
如果用 docker inspect
檢查 demo_nginx_1,也確實會發現它和 demo_php_1 確實有連接在一起。
前面在安裝 ansible-container
時有稍微提到,除了 pip install ansible-container[docker]
之外,還可以 pip install ansible-container[docker,k8s,openshift]
,我想看到這應該就不用再多做解釋,很容易能夠推想到 Ansible 官方有計畫要讓 ansible-container
不只能單純 build、run containers,甚至能幫助你將 containers 運行於 k8s 及 openshift,不過本文就先略過這些內容,未來有機會再繼續試用這一塊。
最後如同 docker-compose stop
一樣,想要停止 container 可以執行 ansible-container stop
,如果要刪除 containers 及 images 則可以透過 ansible-container destroy
,如此方才建立的 container 及 images 皆會被一併刪除。
小結
從目前釋出的功能觀察,Ansible 官方對於 ansible-container
似乎有一些野心與期待,不過目前版本號依然尚未進入 1.0.0,因此仍有許多地方需要使用者們給予回饋與回報 bug,如果各位也有心試用,不妨也將發現的問題回報給官方,幫忙貢獻自己的心力。
回到 Ansible 這項工具本身,不乏有時會遇到人們詢問:「在 docker container 普遍的時代,ansible 還能用來做什麼?該何去何從?」,而或許 ansible-container
即是官方對此問題的其中一項回答,對此不知您是否也有什麼想法呢?