管理2000+Docker鏡像,Kolla是如何作到的

根據 DockerHub 上的數據,整個 Kolla 項目管理的 鏡像有 2000 多個,這麼多的鏡像,是怎麼定義,又是如何構建的呢?html

簡介

咱們一直在說的 Kolla,一般狀況下泛指,包括了 KollaKolla-Ansible 兩個項目。html5

實際上,根據 OpenStack Wiki,還有個 Kayobe 項目也是相關的。可是這個用的比較少,並且我試用後以爲不是特別符合個人需求,就不過多介紹了。python

此外還有一個項目 Kolla-kubernetes 致力於和 Kubernetes 結合,可是和另外一個項目 openstack-helm 重合較多,提早退休了。linux

Kolla 項目開始之初只有一個項目,從構建 docker 容器,到使用 ansible 部署,全流程搞定。後來把 ansible 這塊分離了出來,獨立爲 kolla-ansible 項目,原來的 kolla 專門負責 docker 鏡像的構建。git

鏡像劃分的維度

雖然最終的鏡像個數超過 2000 個,實際並非徹底獨立的 2000 多個服務。而是針對不一樣的場景分別構建,多維度全面覆蓋的結果。正則表達式

鏡像分層

熟悉 Docker 的小夥伴都知道,Dockerfile 是能夠指定「繼承」關係的。也就是利用鏡像分層的特性,逐層構建。docker

OpenStack 中有不少子服務隸屬於同一個項目,例如,nova-apinova-compute 等都屬於 nova,因此,很天然地能夠先構建一個通用的 nova-base 鏡像,而後在此基礎上分別構建不一樣的服務。json

這是一個縱向的劃分維度。ubuntu

功能劃分

由於 Kolla 項目不只是把 OpenStack 的服務集成了,周邊用到的組件和輔助服務也都囊括在內。包括 RabbitMQMariaDB 等。centos

這是一個橫向的劃分維度。

以上兩個是最基礎的劃分維度,也是咱們可以很容易想到的。

操做系統

每一個 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 爲開發助力,因此除了從軟件源(如 yumapt 等)直接安裝打包好的程序,還要可以直接從源碼安裝。

從軟件包稱爲 binary,從源碼安裝稱爲 source

這個維度也是在處理 Jinja 模板的階段完成。

實際上,還有 2 個安裝方式,rdorhos,都是針對 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

因此最終上面的文件對應的鏡像是:

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 安裝源仍是其它依賴的公共組件安裝源,通通在基礎鏡像裏固定下來了。

因此在國內網絡很差的狀況下,就必需要替換其中的倉庫源。

設置容器啓動命令

定義了默認的 ENTRYPOINTCMD,也就是把容器的啓動方式固定了下來。

相信這裏你們會有疑惑,那麼多不一樣的服務,怎麼可能在這裏把啓動命令固定下來呢?其實這裏有一點技巧。

這裏 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-basenova-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 相關配置,使用本身的鏡像源了。


若是本文對你有幫助,請 點贊關注分享,謝謝!

相關文章
相關標籤/搜索