本文以nova-api容器為例,說明kolla如何將nova-api配置文件傳入容器,容器如何啟動nova-api服務並讀取配置文件
注:第一部分比較無趣,二三部分 會有意思一些
1. nova-api容器參數
- nova-api容器定義
ansible.roles.nova.defaults.main.yml中定義了nova的所有容器及容器參數,下面的代碼nova-api容器的定義, 注意代碼中第一條, 對於所有容器kolla都有類似的定義:將/etc/kolla下的配置目錄,作為volume掛載到容器內的/var/lib/kolla/config_files目錄上,供容器啟動時讀取
nova-api:
container_name: "nova_api"
group: "nova-api"
image: "{{ nova_api_image_full }}"
enabled: True
privileged: True
volumes:
# 將用戶nova-api服務配置目錄/etc/kolla/nova-api傳入容器,映射為/var/lib/kolla/config_files/,並設置為只讀
- "{{ node_config_directory }}/nova-api/:{{ container_config_directory }}/:ro"
# 容器時鍾和虛擬機時鍾同步
- "/etc/localtime:/etc/localtime:ro"
# 宿主機內核,mount給容器之后,容器可以啟用宿主機未啟用的內核模塊
- "/lib/modules:/lib/modules:ro"
# openstack日志目錄
- "kolla_logs:/var/log/kolla/"
- nova-api容器操作定義
ansible.roles.nova.handlers.main.yml中定義了對nova各個容器的操作及其參數,下面的代碼是nova-api服務restart的參數,注意該函數的名稱雖然是restart nova-api,但是action是recreate_or_restart_container,如果restart nova-api容器時,該容器不存在,restart函數會創建該容器並啟動。
由於nova的服務都是無狀態應用,重啟容器和重新創建容器沒有區別,所以kolla在handler中僅僅定義了restart or recreate操作,但是對於mariadb容器,handler把創建和重啟容器分開來,handler包括中定義了start_container,recreate_or_restart_container兩個操作
- name: Restart nova-api container
vars:
service_name: "nova-api"
service: "{{ nova_services[service_name] }}"
config_json: 略
nova_conf: 略
policy_json: 略
nova_api_container: 略
kolla_docker:
action: "recreate_or_restart_container"
common_options: "{{ docker_common_options }}"
name: "{{ service.container_name }}"
image: "{{ service.image }}"
privileged: "{{ service.privileged | default(False) }}"
volumes: "{{ service.volumes|reject('equalto', '')|list }}"
when:
略
- nova-api容器創建
ansible.library.kolla_docker.py中定義了所有容器創建的函數 create_container
def create_container(self):
self.changed = True
# 獲取創建容器的volume配置等容器創建參數
options = self.build_container_options()
# 調用docker driver創建容器
self.dc.create_container(**options)
def build_container_options(self):
#生成volume參數
volumes, binds = self.generate_volumes()
return {
'detach': self.params.get('detach'),
'environment': self._format_env_vars(),
'host_config': self.build_host_config(binds),
'labels': self.params.get('labels'),
'image': self.params.get('image'),
'name': self.params.get('name'),
'volumes': volumes,
'tty': True
}
def generate_volumes(self):
# self.params在初始化時使用1中的kolla_docker參數定義
volumes = self.params.get('volumes')
# 解析字符串,略
for vol in volumes:
#解析字符串,略
return vol_list, vol_dict
2. 尋找nova-api容器的啟動命令
- nova-api
通過上面的步驟,kolla將nova-api容器創建出來並啟動,並且將宿主機上的/etc/kolla/nova-api作為volume掛載到容器的/var/lib/kolla/config_files上。容器啟動時,會執行容器docker file中的CMD命令。而nova-api的dockerfile位於kolla項目(不是kolla-ansible)的docker.nova.nova-api.Dockerfile.j2,內容如下:
# 定義父鏡像為nova-base鏡像
FROM {{ namespace }}/{{ image_prefix }}nova-base:{{ tag }}
# 作者信息,略
# 安裝nova-api服務的步驟,略
COPY extend_start.sh /usr/local/bin/kolla_nova_extend_start
RUN chmod 755 /usr/local/bin/kolla_nova_extend_start
USER nova
這個dockerfile定義了一個基於nova-base的nova-api鏡像,在nova-base鏡像的基礎上,主要做了兩件事:安裝nova-api服務,復制extend_start.sh文件為kolla_nova_extend_start文件。
"/usr/local/bin/" 是環境變量PATH的默認值之一,將extend_start.sht文件拷貝到這個目錄后,kolla_nova_extend_start成為容器里可以直接執行的一個命令。
Dockerfile中,對容器啟動時執行的命令是這個定義的CMD ["可執行命令"], 在這個dockerfile中沒有定義容器啟動命令CMD。
- nova-base image
我們再去看一下父鏡像nova-base的Dockerfile:
FROM {{ namespace }}/{{ image_prefix }}openstack-base:{{ tag }}
LABEL maintainer="{{ maintainer }}" name="{{ image_name }}" build-date="{{ build_date }}"
# 安裝所有nova服務通用的rpm包,如:python-keystoneclient,nova-common,etc,代碼略
# 創建/etc/nova目錄並設置權限
COPY nova_sudoers /etc/sudoers.d/kolla_nova_sudoers
RUN chmod 750 /etc/sudoers.d \
&& chmod 440 /etc/sudoers.d/kolla_nova_sudoers
COPY extend_start.sh /usr/local/bin/kolla_extend_start
RUN touch /usr/local/bin/kolla_nova_extend_start \
&& chmod 755 /usr/local/bin/kolla_extend_start /usr/local/bin/kolla_nova_extend_start
在這個文件里面,kolla復制extend_start.sh生成kolla_extend_start文件。這里依然沒有定義CMD。
- openstack-base
我們繼續看這個鏡像的父鏡像openstack-base的Dockerfile:
FROM {{ namespace }}/{{ image_prefix }}base:{{ tag }}
# 安裝openstack通用rpm包,如git,gcc,eventle,代碼略
ENV PATH /var/lib/kolla/venv/bin:$PATH
RUN {{ macros.install_pip(openstack_base_pip_packages | customizable("pip_packages")) }}
在這里kolla設置了環境變量PATH=/var/lib/kolla/venv/bin,這個目錄下存放了nova-api文件,這樣nova-api成為可在容器里直接執行的命令
- base image
繼續看父鏡像base的Dockerfile:
FROM {{ base_image }}:{{ base_distro_tag }}
# 創建用戶組
# 配置yum或者apt的源,pip源
COPY set_configs.py /usr/local/bin/kolla_set_configs
COPY start.sh /usr/local/bin/kolla_start
COPY sudoers /etc/sudoers
COPY curlrc /root/.curlrc
# 安裝並設置dumb-init
# 創建 /var/log/kolla 並設置權限
# 創建/usr/local/bin/kolla_extend_start 文件並設置權限
CMD ["kolla_start"]
可以看到這里生成了kolla_start文件,並且將容器啟動命令設置為kolla_start,由於base鏡像是所有kolla鏡像的公共鏡像,所以,幾乎所有kolla容器啟動時,執行的命令,都是kolla_start命令,之所以說幾乎,是因為有個別鏡像重寫了CMD
3. 通過kolla_start啟動nova-api
kolla_start是一個shell腳本,內容如下:
#!/bin/bash
set -o errexit
# 解析 /var/lib/kolla/config_files/目錄下的config.json,將config.json中的command寫入/command文件,將config.json中的config_files拷貝到指定目錄。 /var/lib/kolla/config_files是由我們在1.1中定義的/etc/kolla/nova-api目錄映射而來。
sudo -E kolla_set_configs
# 獲取config.json中的command內容,對於nova-api,它的值是"nova-api"
CMD=$(cat /run_command)
ARGS=""
#執行kolla_extend_start命令,設置nova日志目錄的權限,並調用kolla_nova_extend_start命令,進行判斷:如果是初始化nova-api服務,執行dbsync,如果不是,什么都不做
. kolla_extend_start
#執行nova-api命令,啟動該服務
echo "Running command: '${CMD}${ARGS:+ $ARGS}'"
exec ${CMD} ${ARGS}
可以看出,所有的openstack服務都按照上面的流程啟動,不同的服務:
- 所有服務都使用/etc/kolla/服務名/config.json中command,作為服務的啟動命令
- 所有服務都使用/etc/kolla/服務名/config.json中config_files, 作為服務的配置文件
- 所有服務都有相同的kolla_start腳本。這個腳本在kolla項目中存放的位置是kolla.docker.base.start.sh。這個腳本會先調用kolla_extend_start腳本, 然后執行服務啟動命令。
- 所有同一項目的服務有相同的kolla_extend_start腳本。以nova項目為例,這個腳本在kolla項目中存放的位置是kolla.docker.nova.nova_base.extend_start.sh。所有nova容器都使用這個腳本會創建項目日志目錄,設置權限,調用容器內的kolla_nova_extend_start腳本。
- 所有服務都有不同的kolla_項目名_extend_start腳本,以nova_api項目為例,這個腳本在kolla項目中存放的位置是kolla.docker.nova.nova_api.extend_start.sh。nova-api使用該腳本用來做服務啟動前的db_sync, 垃圾清理等操作。