ansible-container 的二度踩雷之旅

Chengwei Chen
13 min readMar 6, 2018

--

(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.confpid = /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 即是官方對此問題的其中一項回答,對此不知您是否也有什麼想法呢?

--

--

Chengwei Chen

暱稱:艦長,平日熱衷研究 DevOps、維運及自動化相關技術,目前為 GitLab Hero、DevOps Taiwan 社群志工。Medium 已停止更新,更多文章與個人經歷請參閱 https://chengweichen.com