根据 DockerHub 上的数据,整个 Kolla 项目管理的 镜像有 2000 多个,这么多的镜像,是怎么定义,又是如何构建的呢?html
咱们一直在说的 Kolla,一般状况下泛指,包括了 Kolla
和 Kolla-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-api
, nova-compute
等都属于 nova
,因此,很天然地能够先构建一个通用的 nova-base
镜像,而后在此基础上分别构建不一样的服务。json
这是一个纵向的划分维度。ubuntu
由于 Kolla
项目不只是把 OpenStack 的服务集成了,周边用到的组件和辅助服务也都囊括在内。包括 RabbitMQ
,MariaDB
等。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
为开发助力,因此除了从软件源(如 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 个:
若是一个组件包含多个服务,好比 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
中完成。不论是 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 个步骤:
把 docker
整个目录复制到一个临时的工做目录,而后在其中扫描包含有 Dockerfile.j2
文件的文件夹。正如在上面分析的那样,这样的一个文件夹就对应一个镜像。
使用从配置文件中获取的操做系统基础镜像和安装方式等参数,渲染生成 Dockerfile
文件。
参考源码:
create_dockerfiles
将上一步生成的 Dockerfile 都读取到内存,处理里面的 FROM
语句,能够得到每一个镜像的 parent
名字。还有其它一些关于安装方式的细节也要处理,不用过多关心。
这一步完成咱们就获得了一个镜像列表。这里的镜像指的是 kolla
定义的 Image
类的实例。
遍历整个镜像列表,把它们的依赖关系整理清楚。
由于总共镜像数量比较多,因此须要根据用户提供的参数作一下过滤。
过滤参数有两种方式:
profile
,指定分组名,就能够构建对应的镜像使用多线程任务队列,批量执行构建。
构建完镜像后,还有一个可选操做,将镜像 push
到指定的 registry 中。
以上过程,有兴趣的能够自行去看 kolla 源码,主要内容就集中在 1 个 build.py
文件,仍是很简单的。
为避免本文内容失效,请关注 Kolla 项目官方文档 获取更新。
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
能够是本地自建的服务,也能够是其它平台提供的,好比 阿里云的容器镜像服务。
具体的过程就不赘述了
一切完工以后就能够参考我以前的文章,在使用 Kolla-Ansible
部署环境的时候在 globals.yml
中修改 registry 相关配置,使用本身的镜像源了。
若是本文对你有帮助,请 点赞、 关注、分享,谢谢!