LoginSignup
6
3

More than 1 year has passed since last update.

MoleculeのDockerドライバーについて解説してみる

Last updated at Posted at 2021-12-13

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_imagetrueにすると起動されたコンテナに対してPythonがインストールされその他必要な処理が行なわれAnsibleがコンテナへ接続できる状態になります。

pre_build_imageに対応したコンテナはGitHubリポジトリsrc/molecule_docker/playbooks/Dockerfile.j2に定義されています。処理内容を見る限り現在のところLinuxコンテナのみ対応のようです。

src/molecule_docker/playbooks/Dockerfile.j2
# 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とネットワークを利用しない

もっとも単純な設定になります。

molecule/default/molecule.yml
---
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/initprivileged: True にします。/sbin/initの部分はDockerイメージによって異なる事があります。

molecule/default/molecule.yml
---
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_portspublished_portsで設定します。

Ansibleからライブラリ等をインストールするだけの処理ならポートの開放は必要ないと思われます。しかし動的テストまでMoleculeで実行したい場合はポートを解放します。

molecule/default/molecule.yml
---
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リソースの作成順序に注意します。通常は

  1. Dockerネットワーク
  2. データベースコンテナ
  3. アプリケーションコンテナ

の順になります。

削除順序は反対に

  1. アプリケーションコンテナ
  2. データベースコンテナ
  3. Dockerネットワーク

が良いでしょう。

またmolecule.ymlにDockerコンテナの設定をまとめて書いておくとあとで見返した時に分かりやすいです。

この辺りは好みの問題ですが。

molecule/default/molecule.yml
---
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
molecule/default/create.yml
---
- 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 }}"
molecule/default/destroy.yml
---
- 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.ymldestroy.ymlへネットワークとコンテナを起動・削除する処理を書きます。

関連する記事

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3