管理2000+Docker映象,Kolla是如何做到的
根據 DockerHub 上的資料,整個 Kolla 專案管理的 映象有 2000 多個,這麼多的映象,是怎麼定義,又是如何構建的呢?
簡介
我們一直在說的 Kolla,通常情況下泛指,包括了 Kolla
和 Kolla-Ansible
兩個專案。
實際上,根據 OpenStack Wiki,還有個
Kayobe
專案也是相關的。但是這個用的比較少,而且我試用後覺得不是特別符合我的需求,就不過多介紹了。
此外還有一個專案
Kolla-kubernetes
致力於和 Kubernetes 結合,但是和另一個專案openstack-helm
重合較多,提前退休了。
Kolla
專案開始之初只有一個專案,從構建 docker 容器,到使用 ansible 部署,全流程搞定。後來把 ansible 這塊分離了出來,獨立為 kolla-ansible
kolla
專門負責 docker 映象的構建。
映象劃分的維度
雖然最終的映象個數超過 2000 個,實際並不是完全獨立的 2000 多個服務。而是針對不同的場景分別構建,多維度全面覆蓋的結果。
映象分層
熟悉 Docker 的小夥伴都知道,Dockerfile 是可以指定“繼承”關係的。也就是利用映象分層的特性,逐層構建。
OpenStack 中有很多子服務隸屬於同一個專案,例如,nova-api
, nova-compute
等都屬於 nova
,所以,很自然地可以先構建一個通用的 nova-base
映象,然後在此基礎上分別構建不同的服務。
這是一個縱向的劃分維度。
功能劃分
因為 Kolla
RabbitMQ
,MariaDB
等。
這是一個橫向的劃分維度。
以上兩個是最基礎的劃分維度,也是我們能夠很容易想到的。
作業系統
每個 Docker 映象最底層只能是作業系統的基礎映象。現在主流的 Linux 發行版有好幾家,OpenStack 作為一個世界級的開源專案,要是隻繫結一家,其他人可不答應。
所以,必須要同時支援多個作業系統。這個靠 Dockerfile 顯然解決不了。
如果為每個作業系統單獨的定義一份 Dockerfile 顯然不夠聰明。 Kolla 使用了 Jinja 模板檔案多做了一層抽象,根據指定的引數先由 Dockerfile.j2
Dockerfile
。
這個維度在 kolla 中對應的引數是 base
,目前支援的作業系統有:
['centos', 'rhel', 'ubuntu', 'debian']
Jinja 是 Python 中使用比較廣泛的模板引擎(template engine)。之所以叫 Jinja,是因為日本的神社(Jinja)英文單詞是 temple,而模板的英文是 template,兩者發音很相似(什麼腦回路)。Jinja 專案的 Logo 也是一個神社的圖示,可能是因為這層關係,這個在國內似乎討論的並不多。
安裝方式
Kolla 不僅是要作單純的部署工具,還希望能夠替代 devstack
為開發助力,所以除了從軟體源(如 yum
,apt
等)直接安裝打包好的程式,還要能夠直接從原始碼安裝。
從軟體包稱為 binary
,從原始碼安裝稱為 source
這個維度也是在處理 Jinja 模板的階段完成。
實際上,還有 2 個安裝方式,
rdo
和rhos
,都是針對 RedHat 系統的,一般不怎麼會用到。
作業系統和安裝方式這兩個維度,決定了映象名稱的字首:
檔案的組織結構
瞭解了劃分維度,我們來看一下具體的檔案組織結構是怎樣的。
所有的構建 Docker 映象相關的檔案都存放在 kolla/docker
目錄下。這下面的資料夾眾多,下面把有代表性的列了出來:
docker/
├── base
│ └── Dockerfile.j2
├── horizon
│ └── Dockerfile.j2
├── mariadb
│ └── Dockerfile.j2
├── nova
│ ├── nova-api
│ │ └── Dockerfile.j2
│ ├── nova-base
│ │ └── Dockerfile.j2
│ └── nova-compute
│ └── Dockerfile.j2
└── openstack-base
└── Dockerfile.j2
每個資料夾代表了一個服務,除了名字帶 base
的,其中頂層的有 2 個:
- base 這是所有映象的初始層
- openstack-base 這是所有 OpenStack 相關服務的初始層
如果一個元件包含多個服務,比如 nova
,它內部就會又多出一層基礎層: nova-base
。所有其它的 nova-<xxx>
都是從這層開始。如果一個元件只有一個服務,則不需要再有子資料夾,直接是 Dockerfile.j2
檔案。
映象層之間的關係是在 Dockerfile 檔案中的 FROM
語句定義的。它們在 jinja
模板中是固定的。
例如 horizon/Dockerfile.j2
中:
FROM {{ namespace }}/{{ image_prefix }}openstack-base:{{ tag }}
而 openstack-base/Dockerfile.j2
中:
FROM {{ namespace }}/{{ image_prefix }}base:{{ tag }}
它們之間的依賴關係是這樣的:
base
├── openstack-base
│ ├── nova-base
│ │ └── nova-api
│ │ └── nova-compute
│ └── horizon
└── mariadb
可以看到,最多就 4 層。
包含 .j2
檔案的資料夾名字最終會成為映象名的一部分,如 <os>-<type>-nova-api
。
這裡的 <os>-<type>-
也就是對應上面的 {{ image_prefix }}
字串,分別對應:
所以最終上面的檔案對應的映象是:
centos-binary-base
centos-binary-openstack-base
centos-binary-nova-base
centos-binary-nova-api
centos-binary-nova-compute
centos-binary-horizon
注意,並不是每個映象都支援任意的型別組合,具體需要檢視
kolla
原始碼。
base 映象的作用
所有映象的源頭都是 base
,所以它肯定是很重要的。其中內容主要有兩個地方比較關鍵:
設定軟體倉庫源
所有軟體包的安裝源配置都在 base
中完成。不管是 OpenStack 安裝源還是其它依賴的公共元件安裝源,統統在基礎映象裡固定下來了。
所以在國內網路不好的情況下,就必須要替換其中的倉庫源。
設定容器啟動命令
定義了預設的 ENTRYPOINT
和 CMD
,也就是把容器的啟動方式固定了下來。
相信這裡大家會有疑惑,那麼多不同的服務,怎麼可能在這裡把啟動命令固定下來呢?其實這裡有一點技巧。
這裡 kolla
固定了一個啟動指令碼 start.sh
,在這個腳本里從固定位置 /run_command
讀到真正的執行命令。/run_command
則是由 kolla-ansible
在啟動容器的時候注入的。
還記得在 介紹 Kolla 的配置檔案 時看到的 config.json
麼,其中有一個 command
欄位。例如 Horizon 服務的配置:
{
"command": "/usr/sbin/httpd -DFOREGROUND",
"config_files": [
{
"source": "/var/lib/kolla/config_files/horizon.conf",
"dest": "/etc/httpd/conf.d/horizon.conf",
"owner": "horizon",
"perm": "0600"
},
]
}
這樣做,既保證了構建映象時候的一致性,又保證了容器啟動的靈活性。
處理流程
kolla 構建映象的流程非常簡單,大體就是 5 個步驟:
1. 生成 Dockerfile
把 docker
整個目錄複製到一個臨時的工作目錄,然後在其中掃描包含有 Dockerfile.j2
檔案的資料夾。正如在上面分析的那樣,這樣的一個資料夾就對應一個映象。
使用從配置檔案中獲取的作業系統基礎映象和安裝方式等引數,渲染生成 Dockerfile
檔案。
參考原始碼:
create_dockerfiles
2. 構建映象列表
將上一步生成的 Dockerfile 都讀取到記憶體,處理裡面的 FROM
語句,可以獲得每個映象的 parent
名字。還有其它一些關於安裝方式的細節也要處理,不用過多關心。
這一步完成我們就得到了一個映象列表。這裡的映象指的是 kolla
定義的 Image
類的例項。
3. 查詢映象關係
遍歷整個映象列表,把它們的依賴關係整理清楚。
4. 過濾映象列表
因為總共映象數量比較多,所以需要根據使用者提供的引數做一下過濾。
過濾引數有兩種方式:
- 預先定義了幾組常用的映象分組,稱為
profile
,指定分組名,就可以構建對應的映象 - 通過正則表示式匹配映象的名字來選擇
5. 執行構建
使用多執行緒任務佇列,批量執行構建。
構建完映象後,還有一個可選操作,將映象 push
到指定的 registry 中。
以上過程,有興趣的可以自行去看 kolla 原始碼,主要內容就集中在 1 個 build.py
檔案,還是很簡單的。
使用方法
為避免本文內容失效,請關注 Kolla 專案官方文件 獲取更新。
安裝 Python 3
CentOS 7 自帶的 Python 版本還是 2.7,在 2020 年後不再維護,Kolla 專案有的依賴包不再支援。
yum install python3
CentOS 7 的安裝源提供的 Python 3 版本是 3.6.8
建立虛擬環境(可選)
推薦在 Python 虛擬環境中安裝 Kolla
:
python3 -m venv .virtualenvs/kolla-build
source .virtualenvs/kolla-build/bin/activate
(kolla-build) [root@davycloud ~]#
以下操作預設都在虛擬環境下執行。
安裝 Kolla
有兩種方式,
- 使用
pip
安裝 - 從原始碼安裝
推薦採用後者,有助於學習,也方便改程式碼。
使用 git 下載原始碼:
# OpenStack 官方 git 源
git clone https://opendev.org/openstack/kolla
# 上面網速慢的可以使用下面的映象站地址
git clone http://git.trystack.cn/openstack/kolla
然後使用 pip
安裝即可:
(kolla-build) $ pip install kolla/
注意最後的斜槓,表示我們安裝的是本地目錄。安裝完畢後可以執行:
(kolla-build) [root@davycloud ~]# kolla-build --version
9.1.0
生成配置檔案
Kolla 構建映象有不少配置項,但是基本保持預設即可。並且缺少配置檔案 kolla-build
命令也能執行,所以這一步這裡就 略過 了。
如果你想生成 kolla-build.conf
配置檔案,可以參考 官方文件 。
構建 base
映象
構建最最基礎的 base
映象:
(kolla-build) [root@davycloud ~]# kolla-build ^base
INFO:kolla.common.utils:Found the docker image folder at /root/.virtualenvs/kolla-build/share/kolla/docker
INFO:kolla.common.utils:Added image base to queue
INFO:kolla.common.utils:Attempt number: 1 to run task: BuildTask(base)
INFO:kolla.common.utils.base:Building started at 2020-01-28 19:54:50.158139
INFO:kolla.common.utils.base:Step 1/37 : FROM centos:7
INFO:kolla.common.utils.base: ---> 5e35e350aded
INFO:kolla.common.utils.base:Step 2/37 : LABEL maintainer="Kolla Project
...
INFO:kolla.common.utils.base:Successfully tagged kolla/centos-binary-base:9.1.0
注意,^base
前面的 ^
符號不可省略,這是正則表示式中表示句首的符號,如果缺少該符號,kolla-build
會把 openstack-base
,nova-base
等一眾名字包含 base
的映象都匹配上了。
構建的映象標籤預設是 kolla 的版本號,9.1.0
,我們後面會指定自己的版本號。
修改 base
映象
我們在前面分析過了,所有的安裝源都在 base
映象中指定了。
如果我們直接使用這個 base
映象,後面的映象構建過程中軟體的安裝速度沒法保證。
當然,我們可以在下載完 kolla
原始碼之後就直接修改 base
對應的 Dockerfile.j2
和相關的構建檔案,但是這樣修改是比較麻煩的。因為其中夾雜著其它情況的處理程式碼,例如:
{% if base_package_type == 'rpm' %}
# For RPM Variants, enable the correct repositories - this should all be done
# in the base image so repos are consistent throughout the system. This also
# enables to provide repo overrides at a later date in a simple fashion if we
# desire such functionality. I think we will :)
RUN CURRENT_DISTRO_RELEASE=$(awk '{match($0, /[0-9]+/,version)}END{print version[0]}' /etc/system-release); \
if [ $CURRENT_DISTRO_RELEASE != "{{ supported_distro_release }}" ]; then \
echo "Only release '{{ supported_distro_release }}' is supported on {{ base_distro }}"; false; \
fi \
&& cat /tmp/kolla_bashrc >> /etc/bashrc \
&& sed -i 's|^\(override_install_langs=.*\)|# \1|' {% if distro_package_manager == 'dnf' %}/etc/dnf/dnf.conf{% else %}/etc/yum.conf{% endif %}
{% block base_yum_conf %}
{% if distro_package_manager == 'dnf' %}
COPY dnf.conf /etc/dnf/dnf.conf
{% else %}
COPY yum.conf /etc/yum.conf
{% endif %}
修改難度是比較大的,要把其中的邏輯捋清楚才能下手。而且以後每次這個檔案的版本有變化,更新都要對照著修改。
這裡我採取了比較投機取巧的辦法,等這個 base
映象構建完成後,直接在它之上修改。這個時候我們已經確定了作業系統(CentOS)和安裝方式(Binary),這樣只需要替換 /etc/yum.repos.d/
下面的 .repo
檔案即可。
先把 kolla/centos-binary-base:9.1.0
映象內的 /etc/yum.repos.d/
整個資料夾都拷貝出來,逐個 .repo
修改,把其中的 URL 替換成阿里雲映象站的 URL。
然後寫了一個超級簡單粗暴的 Dockerfile:
FROM kolla/centos-binary-base:9.1.0
RUN mkdir -p /etc/yum.repos.d/bak && mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/bak
COPY CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo
COPY CentOS-Ceph-Nautilus.repo /etc/yum.repos.d/CentOS-Ceph-Nautilus.repo
COPY CentOS-CR.repo /etc/yum.repos.d/CentOS-CR.repo
COPY CentOS-Debuginfo.repo /etc/yum.repos.d/CentOS-Debuginfo.repo
COPY CentOS-fasttrack.repo /etc/yum.repos.d/CentOS-fasttrack.repo
COPY CentOS-Media.repo /etc/yum.repos.d/CentOS-Media.repo
COPY CentOS-NFS-Ganesha-28.repo /etc/yum.repos.d/CentOS-NFS-Ganesha-28.repo
COPY CentOS-OpenStack.repo /etc/yum.repos.d/CentOS-OpenStack.repo
COPY CentOS-OpsTools.repo /etc/yum.repos.d/CentOS-OpsTools.repo
COPY CentOS-QEMU-EV.repo /etc/yum.repos.d/CentOS-QEMU-EV.repo
COPY CentOS-Sources.repo /etc/yum.repos.d/CentOS-Sources.repo
COPY CentOS-Storage-common.repo /etc/yum.repos.d/CentOS-Storage-common.repo
COPY CentOS-Vault.repo /etc/yum.repos.d/CentOS-Vault.repo
COPY crmsh.repo /etc/yum.repos.d/crmsh.repo
COPY elasticsearch.repo /etc/yum.repos.d/elasticsearch.repo
COPY epel.repo /etc/yum.repos.d/epel.repo
COPY epel-testing.repo /etc/yum.repos.d/epel-testing.repo
COPY grafana.repo /etc/yum.repos.d/grafana.repo
COPY influxdb.repo /etc/yum.repos.d/influxdb.repo
COPY opendaylight.repo /etc/yum.repos.d/opendaylight.repo
COPY rabbitmq_rabbitmq-server.repo /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo
COPY td.repo /etc/yum.repos.d/td.repo
然後用它來構建一個新的映象:
(kolla-build) [root@davycloud ~]# docker build . -t kolla/centos-binary-base:davycloud
注意,其中的映象 tag 可以自己隨便定義。
構建 openstack-base
映象
有了基礎映象,就可以開始構建其它的映象了。可以先挑一個試一試,比如 openstack-base
。
注意,上面已經把 tag 修改了,所以接下來的命令必須要帶兩個選項:
--tag davycloud
,用來指定自定義的 tag,--skip-existing
,略過已經建立好的映象
(kolla-build) [root@davycloud aliyun]# kolla-build --tag davycloud --skip-existing openstack-base
INFO:kolla.common.utils:Found the docker image folder at /root/.virtualenvs/kolla-build/share/kolla/docker
INFO:kolla.common.utils:===========================
INFO:kolla.common.utils:Images that failed to build
INFO:kolla.common.utils:===========================
ERROR:kolla.common.utils:openstack-base Failed with status: matched
會出現這麼一個莫名其妙的錯誤。這其實是 kolla
這裡處理的邏輯有點問題。找到下面所示程式碼,在 image.status = STATUS_UNMATCHED
上面加一個判斷:
@@ -1117,9 +1117,9 @@ class KollaWorker(object):
ancestor_image.status = STATUS_MATCHED
LOG.debug('Image %s matched regex', image.name)
else:
+ # See: https://bugs.launchpad.net/kolla/+bug/1810979
+ if image.status != STATUS_SKIPPED:
+ image.status = STATUS_UNMATCHED
- # we do not care if it is skipped or not as we did not
- # request it
- image.status = STATUS_UNMATCHED
else:
for image in self.images:
if image.status != STATUS_UNBUILDABLE:
我已經給社群提了修改補丁,但是沒有下文。
修改完畢之後,就可以重試上面的命令來構建映象了。
構建其它映象
Kolla 總共支援的映象比較多,不太可能全部需要,所以最好事先挑選一番。
最簡單的是通過 profile
來批量指定,然後通過 --list-images
選項,在構建之前檢視映象列表,做到心中有數:
(kolla-build) [root@davycloud aliyun]# kolla-build -p default --list-images
1 : openstack-base
2 : chrony
3 : barbican-keystone-listener
4 : barbican-base
5 : nova-spicehtml5proxy
6 : nova-conductor
7 : nova-ssh
8 : nova-libvirt
9 : nova-scheduler
10 : nova-compute-ironic
11 : nova-novncproxy
12 : nova-serialproxy
13 : nova-api
14 : nova-compute
15 : nova-base
16 : glance-api
17 : glance-registry
18 : glance-base
19 : kolla-toolbox
20 : neutron-server-opendaylight
21 : neutron-l3-agent
22 : neutron-mlnx-agent
23 : neutron-server
24 : neutron-server-ovn
25 : neutron-metadata-agent
26 : neutron-dhcp-agent
27 : neutron-openvswitch-agent
28 : neutron-bgp-dragent
29 : neutron-linuxbridge-agent
30 : neutron-infoblox-ipam-agent
31 : neutron-base
32 : neutron-metering-agent
33 : neutron-sriov-agent
34 : neutron-metadata-agent-ovn
35 : fluentd
36 : heat-api-cfn
37 : heat-engine
38 : heat-base
39 : heat-api
40 : heat-all
41 : ironic-neutron-agent
42 : mariadb
43 : keystone-ssh
44 : keystone
45 : keystone-fernet
46 : keystone-base
47 : openvswitch-db-server
48 : openvswitch-base
49 : openvswitch-vswitchd
50 : prometheus-haproxy-exporter
51 : prometheus-base
52 : prometheus-memcached-exporter
53 : base
54 : rabbitmq
55 : cron
56 : haproxy
57 : keepalived
58 : memcached
59 : horizon
60 : placement-base
61 : placement-api
也可以檢視原始碼檔案:
kolla/common/config.py
中的_PROFILE_OPTS
檢視支援哪些 profile 以及包含的映象列表。
(kolla-build) [root@davycloud ~]# kolla-build --tag davycloud --skip-existing -p default
把映象推送到 registry
可以是本地自建的服務,也可以是其它平臺提供的,比如 阿里雲的容器映象服務。
具體的過程就不贅述了
一切完工之後就可以參考我之前的文章,在使用 Kolla-Ansible
部署環境的時候在 globals.yml
中修改 registry 相關配置,使用自己的映象源了。
如果本文對你有幫助,請 點贊、 關注、分享,謝謝!