MolecueのDockerドライバー
であるMolecule Docker Pluginについて解説します。
Dockerドライバーとは
MoleculeのDockerドライバー
はMoleculeがdockerコンテナに接続する処理を行うヘルパーです。テスト環境用コンテナを作成、テスト終了後にそのコンテナを破棄するまでをmolecule.yml
への簡潔な記述で処理します。
内部的には主に「community.docker.docker_container」と「community.docker.docker_network」を利用しています。
他にも外部のコンテナレジストリからDockerイメージを取得するため「community.docker.docker_login」が、Dockerfile
からイメージをビルドするために「community.docker.docker_image」も採用されています。
またコンテナは常にdetachedモード
で起動されます。これは現在のところ変更できないようです。
Dockerドライバーが行なっている処理
Dockerドライバーの処理の定義はGitHubリポジトリのsrc/molecule_docker/playbooks/*
にあります。ここに配置されているPlaybookファイルにより処理が行われます。またこれらのPlaybookに対して変数を渡す場合は主にはmolecule.yml
のplatformセクションへ記述していきます。
注意点としてはDockerドライバーでは「community.docker.docker_container」や「community.docker.docker_network」等のモジュールで利用できるすべてのパラメーターを設定できるわけではないと言うことです。src/molecule_docker/playbooks/*
の定義ファイルを良く見ながら設定できるパラメーターを把握する必要があります。
Dockerドライバーで利用する主なパラメーター
基本的には「community.docker.docker_container」のパラメーターに準ずるのですが今回はDockerドライバー
でよく利用されるであろう基本的なパラメーターのみ解説します。
env
環境変数を設定します。クラウドの認証情報、SSH鍵等の秘匿情報を渡すことができます。まだデータベースコンテナを起動する際はユーザー名やパスワードなどを渡すことができます。
image
DockerイメージのコンテナリポジトリのURIを設定します。またPrivateリポジトリからもDockerイメージをpullできます。
name
Moleculeが起動するDockerコンテナの名前です。Dockerの特性として複数のコンテナを起動する場合、同名のコンテナは起動できません。複数のテスト用コンテナを起動する場合は一意の名前をつけると良いでしょう。
network_mode
DockerのNetworkモードを指定します。何も指定しないとbridge
が設定されます。
pre_build_image
通常はfalse
になっています。コンテナを起動した後にAnsibleが接続できる環境を構築したい場合はtrue
にします。
例えばPythonがインストールされていないイメージを起動してもAnsibleはコンテナに接続できません。pre_build_image
をtrue
にすると起動されたコンテナに対してPythonがインストールされその他必要な処理が行なわれAnsibleがコンテナへ接続できる状態になります。
pre_build_image
に対応したコンテナはGitHubリポジトリのsrc/molecule_docker/playbooks/Dockerfile.j2
に定義されています。処理内容を見る限り現在のところLinuxコンテナのみ対応のようです。
# Molecule managed
{% if item.registry is defined %}
FROM {{ item.registry.url }}/{{ item.image }}
{% else %}
FROM {{ item.image }}
{% endif %}
{% if item.env is defined %}
{% for var, value in item.env.items() %}
{% if value %}
ENV {{ var }} {{ value }}
{% endif %}
{% endfor %}
{% endif %}
RUN if [ $(command -v apt-get) ]; then export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y python3 sudo bash ca-certificates iproute2 python3-apt aptitude && apt-get clean && rm -rf /var/lib/apt/lists/*; \
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install /usr/bin/python3 /usr/bin/python3-config /usr/bin/dnf-3 sudo bash iproute && dnf clean all; \
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y /usr/bin/python /usr/bin/python2-config sudo yum-plugin-ovl bash iproute && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python3 sudo bash iproute2 && zypper clean -a; \
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python3 sudo bash ca-certificates; \
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python3 sudo bash ca-certificates iproute2 && xbps-remove -O; fi
privileged
Dockerコンテナを権限モードで起動します。
用途に応じて使い分けます。systemdなどを利用する場合は privileged: true
にしておきます。
systemdを利用しない処理だと有効にする必要はありません。
その他
Gitlab、Jenkins等のホスティング型のCIツールを利用している場合はテストシナリオ毎にコンテナのCPU・メモリ・ブロックIO等のリソースに制限をかけたい時があるかもしれません。
用途別サンプル
DockerイメージのリポジトリのURIを設定する項目はCIツール側から渡すため環境変数にしてあります。
systemdとネットワークを利用しない
もっとも単純な設定になります。
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance
image: ${MOLECULE_IMAGE}
provisioner:
name: ansible
verifier:
name: ansible
scenario:
test_sequence:
- dependency
- syntax
- create
- converge
- verify
サンプルリポジトリ
systemdは利用するがネットワークは利用しない
command: /sbin/init
、 privileged: True
にします。/sbin/init
の部分はDockerイメージによって異なる事があります。
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance
image: ${MOLECULE_IMAGE}
command: /sbin/init
privileged: True
provisioner:
name: ansible
verifier:
name: ansible
scenario:
test_sequence:
- dependency
- syntax
- create
- converge
- verify
サンプルリポジトリ
ポートを利用する
command
に必要なパラメーターを設定しテスト処理に開放が必要なポートをexposed_ports
、published_ports
で設定します。
Ansibleからライブラリ等をインストールするだけの処理ならポートの開放は必要ないと思われます。しかし動的テストまでMoleculeで実行したい場合はポートを解放します。
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance
image: ${MOLECULE_IMAGE}
docker_host: "${DOCKER_HOST:-unix://var/run/docker.sock}"
command:
- '/sbin/init'
- '--web.enable-lifecycle'
- '--config.file=/etc/prometheus/prometheus.yml'
privileged: True
exposed_ports:
- 9100/tcp
- 9090/tcp
published_ports:
- 0.0.0.0:9100:9100/tcp
- 0.0.0.0:9090:9090/tcp
provisioner:
name: ansible
verifier:
name: ansible
scenario:
test_sequence:
- syntax
- create
- converge
- verify
サンプルリポジトリ
ネットワークを作成、コンテナをネットワークへ参加させる
現時点ではDockerドライバー
では処理できません。
GitHubリポジトリのsrc/molecule_docker/playbooks/*
にあるDockerドライバーの処理の定義ファイルを見る限りは単一コンテナの起動・破棄しか想定されていないため、コンテナを複数利用する場合は必ずdelegatedドライバー
を利用する事になります。
delegatedドライバー
でDockerのネットワーク機能を利用する際はDockerリソースの作成順序に注意します。通常は
- Dockerネットワーク
- データベースコンテナ
- アプリケーションコンテナ
の順になります。
削除順序は反対に
- アプリケーションコンテナ
- データベースコンテナ
- Dockerネットワーク
が良いでしょう。
またmolecule.yml
にDockerコンテナの設定をまとめて書いておくとあとで見返した時に分かりやすいです。
この辺りは好みの問題ですが。
---
dependency:
name: galaxy
driver:
name: delegated
platforms:
- name: wordpress
image: ${MOLECULE_IMAGE}
command: /sbin/init
detach: yes
networks:
- name: molecule_wordpress
privileged: yes
ports:
- "80:80"
state: started
tty: yes
- name: db
image: mysql:5.7
networks:
- name: molecule_wordpress
state: started
env:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
- name: selenium
image: selenium/standalone-firefox:latest
networks:
- name: molecule_wordpress
ports:
- "4444:4444"
state: started
provisioner:
name: ansible
config_options:
defaults:
callbacks_enabled: ansible.posix.profile_tasks
connection_options:
wp_host: 'wordpress'
php_packages:
- php
- php-gd
- php-gmp
- php-intl
- php-ldap
- php-mbstring
- php-mcrypt
- php-mysqlnd
- php-opcache
- php-pdo
- php-pear
- php-pear-Net-Curl
- php-pecl-apcu-bc
- php-pecl-redis
- php-pecl-zip
- php-soap
- php-xml
verifier:
name: ansible
scenario:
converge_sequence:
- converge
destroy_sequence:
- destroy
test_sequence:
- dependency
- syntax
- create
- converge
- verify
- destroy
---
- name: Create
hosts: localhost
connection: local
tasks:
- name: set_fact molecule_platforms
ansible.builtin.set_fact:
molecule_platforms: "{{ molecule_yml.platforms }}"
- name: Create a network
community.docker.docker_network:
name: "{{ molecule_platforms.0.networks[0].name }}"
- name: Setup MySQL container
community.docker.docker_container:
name: "{{ molecule_platforms.1.name }}"
image: "{{ molecule_platforms.1.image }}"
networks:
- name: "{{ molecule_platforms.1.networks[0].name }}"
state: "{{ molecule_platforms.1.state }}"
env:
MYSQL_ROOT_PASSWORD: "{{ molecule_platforms.1.env.MYSQL_ROOT_PASSWORD }}"
MYSQL_DATABASE: "{{ molecule_platforms.1.env.MYSQL_DATABASE }}"
MYSQL_USER: "{{ molecule_platforms.1.env.MYSQL_USER }}"
MYSQL_PASSWORD: "{{ molecule_platforms.1.env.MYSQL_PASSWORD }}"
- name: Setup WordPress container
community.docker.docker_container:
name: "{{ molecule_platforms.0.name }}"
command: "{{ molecule_platforms.0.command }}"
detach: "{{ molecule_platforms.0.detach }}"
image: "{{ molecule_platforms.0.image }}"
networks:
- name: "{{ molecule_platforms.0.networks[0].name }}"
privileged: "{{ molecule_platforms.0.privileged }}"
ports:
- "{{ molecule_platforms.0.ports[0] }}"
state: "{{ molecule_platforms.0.state }}"
tty: "{{ molecule_platforms.0.tty }}"
- name: Setup Selenium container
community.docker.docker_container:
name: "{{ molecule_platforms.2.name }}"
image: "{{ molecule_platforms.2.image }}"
networks:
- name: "{{ molecule_platforms.2.networks[0].name }}"
ports:
- "{{ molecule_platforms.2.ports[0] }}"
state: "{{ molecule_platforms.2.state }}"
---
- name: Destroy
hosts: localhost
connection: local
tasks:
- name: set_fact molecule_platforms
ansible.builtin.set_fact:
molecule_platforms: "{{ molecule_yml.platforms }}"
- name: Remove Selenium container
community.docker.docker_container:
name: "{{ molecule_platforms.2.name }}"
state: absent
- name: Remove WordPress container
community.docker.docker_container:
name: "{{ molecule_platforms.0.name }}"
state: absent
- name: Remove MySQL container
community.docker.docker_container:
name: "{{ molecule_platforms.1.name }}"
state: absent
- name: Delete a network
community.docker.docker_network:
name: "{{ molecule_platforms.0.networks }}"
state: absent
force: yes
サンプルリポジトリ
まとめ
コンテナのネットワークが単純な構成ならDockerドライバー
を積極的に採用すると良いでしょう。
ネットワークが複雑ならdelegatedドライバー
を採用しcreate.yml
・destroy.yml
へネットワークとコンテナを起動・削除する処理を書きます。