AnsibleでDockerコンテナの管理をしてみた

公開:2020/09/20
更新:2020/09/26
18 min読了の目安(約11200字TECH技術記事

概要

こちらの内容は以前noteで書いた記事のリメイク的なものになります。
今見直すと色々と粗があるように感じましたので、少し手直してよりインフラのコード化を行いました。

実施すること

AnsibleのPlaybookで以下のことを実施致します。

  • Dockerイメージ取得
  • JSフレームワーク、Nuxt.jsコンテナを構築
  • Docker Hubへのイメージpush

使用したツールおよびAnsibleモジュール

  • Ansible 2.9.10
  • Docker 19.03.11
  • Docker API 1.40
  • Python 3.8.2
  • Docker SDK Python 1.80
  • docker_image
  • docker_container

docker_imagedocker_containerを使用するには、Docker APIのバージョンが1.20以上、Docker SDK Pythonのバージョンが1.80以上という必要条件がありますので、事前にバージョンを上げておきます。

事前準備

まず初めに、Nodeイメージを取得し、簡単にNuxt.jsのプロジェクト構築ができるツールcreate-nuxt-appを事前に使用してappディレクトリをホームディレクトリとするDockerfileを作成します。

FROM node:latest
WORKDIR /app

このDockerfileをビルドして、Nodeイメージ作成後コンテナへアタッチしNuxt.jsプロジェクトのひな型を構築します。
docker run -it <コンテナID> bash
yarn create nuxt-app(コンテナ内コマンド)
nuxt-appコマンドを実行しますと、対話形式にNuxtの設定を行えることができ、簡単にNuxtのプロジェクトを作成することができます。質問の詳しい内容はこちらのサイトを参考にして各自の好みに合わしたプロジェクトのひな型を作成します。
ひな型の作成が完了しましたら、コンテナ内から抜けてローカルマシン上へ作成したひな型をコピーします。
docker cp <コンテナID>:/app .
これで準備は整いましたので、DockerfileとPlaybookを作成していきます。

ディレクトリ構成

ディレクトリ構成は以下の通りです。

$ tree
├── dockerFile
│   ├── Dockerfile
│   └── app/(Nuxt.jsのひな型)
└── build_image.yml
└── create_container.yml

DockerFile作成

Nodeコンテナから持ってきたローカル上のNuxt.jsのひな型をコンテナへ配置してNuxt.jsのイメージが作成されるDockerfileを作成します。

FROM node:latest
ENV NUXT_HOST=0.0.0.0
ENV NUXT_TELEMETRY_DISABLED=1
WORKDIR /app

COPY ./app/package.json ./app/yarn.lock ./
RUN yarn install

COPY ./app .

CMD ["yarn", "run", "dev"]

Playbook作成

次に作成したDockerfileをビルドするためのPlaybookを作成します。

- name: Build Nuxt.js image
  hosts: localhost
  tasks:
  - name: build nuxt.js
    docker_image:
      build:
        path: ./dockerFile
      name: yuta28/ansible-test
      tag: Nuxtimage
      push: yes #イメージビルド後自動的にDockerHubへpushする
      source: build
    register: build_result

  - debug: var=build_result

docker_image内のbuildオプションでDockerfileのパスを指定しています。nameオプションは出来上がったDockerイメージの名前を付けています。yuta28は私のDocker Hubのアカウント名であり、Docker Hubへpushする場合イメージ名をDocker Hubアカウント/リポジトリ名と名付ける必要があるためこのような名前にしました。最後のsourceオプションでbuildを指定することで、DockerFileからイメージをビルドするようにしています。
最後に、イメージを基にコンテナを起動するためのPlaybook、create_container.ymlを作成致します。

- name: Create and run container
  hosts: localhost
  tasks:
  - name: Nuxt container
    docker_container:
      name: NuxtContaier
      image: yuta28/ansible-test:Nuxtimage
      state: started
      ports:
        - "127.0.0.1:8080:3000" #コンテナ内のポート3000にNuxt.jsが構築されるのでローカルのポート8080から参照できるようにマッピング
      tty: true
      detach: true
    register: contaier_result

  - debug: var=contaier_result

Ansible実行

準備が完了しましたので、Ansibleを実行します。

$ ansible-playbook build_image.yml 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Build Nuxt.js image] ***************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************
ok: [localhost]

TASK [build nuxt.js] *********************************************************************************************************************************************************************************
[WARNING]: The default for build.pull is currently 'yes', but will be changed to 'no' in Ansible 2.12. Please set build.pull explicitly to the value you need.
changed: [localhost]

TASK [debug] *****************************************************************************************************************************************************************************************
ok: [localhost] => {
    "build_result": {
        "actions": [
            "Built image yuta28/ansible-test:Nuxtimage from ./dockerFile",
            "Pushed image yuta28/ansible-test to docker.io/yuta28/ansible-test:Nuxtimage"
        ],
        "changed": true,
        "failed": false,
        "image": {
            "Architecture": "amd64",
            "Author": "",
            "Comment": "",
            "Config": {
                "AttachStderr": false,
                "AttachStdin": false,
                "AttachStdout": false,
                "Cmd": [
                    "yarn",
                    "run",
                    "dev"
                ],
                "Domainname": "",
                "Entrypoint": [
                    "docker-entrypoint.sh"
                ],
                "Env": [
                    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                    "NODE_VERSION=14.8.0",
                    "YARN_VERSION=1.22.4",
                    "NUXT_HOST=0.0.0.0",
                    "NUXT_TELEMETRY_DISABLED=1"
                ],
                "Hostname": "",
                "Image": "sha256:fe9a213a2bb59321425eb62ddcb54c9388e673b3f588ed4108b0b0c0c8ba78bb",
                "Labels": null,
                "OnBuild": null,
                "OpenStdin": false,
                "StdinOnce": false,
                "Tty": false,
                "User": "",
                "Volumes": null,
                "WorkingDir": "/app"
            },
            "Container": "50e3089197639ba613f1ae439d90142668cc5f0c360967fe8099c0093987695e",
            "ContainerConfig": {
                "AttachStderr": false,
                "AttachStdin": false,
                "AttachStdout": false,
                "Cmd": [
                    "/bin/sh",
                    "-c",
                    "#(nop) ",
                    "CMD [\"yarn\" \"run\" \"dev\"]"
                ],
                "Domainname": "",
                "Entrypoint": [
                    "docker-entrypoint.sh"
                ],
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜中略〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
            },
            "Size": 1430378578,
            "VirtualSize": 1430378578,
            "push_status": null
        },
        "warnings": [
            "The default for build.pull is currently 'yes', but will be changed to 'no' in Ansible 2.12. Please set build.pull explicitly to the value you need."
        ]
    }
}

PLAY RECAP *******************************************************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0      
$ ansible-playbook create_container.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Create and run container] *********************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************
ok: [localhost]

TASK [Nuxt container] ********************************************************************************************************************************************************************************
changed: [localhost]

TASK [debug] *****************************************************************************************************************************************************************************************
ok: [localhost] => {
    "contaier_result": {
        "ansible_facts": {
            "docker_container": {
                "AppArmorProfile": "docker-default",
                "Args": [
                    "yarn",
                    "run",
                    "dev"
                ],
                "Config": {
                    "AttachStderr": false,
                    "AttachStdin": false,
                    "AttachStdout": false,
                    "Cmd": [
                        "yarn",
                        "run",
                        "dev"
                    ],
                    "Domainname": "",
                    "Entrypoint": [
                        "docker-entrypoint.sh"
                    ],
                    "Env": [
                        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                        "NODE_VERSION=14.8.0",
                        "YARN_VERSION=1.22.4",
                        "NUXT_HOST=0.0.0.0",
                        "NUXT_TELEMETRY_DISABLED=1"
                    ],
                    "ExposedPorts": {
                        "3000/tcp": {}
                    },
                    "Hostname": "f5597614f5d5",
                    "Image": "yuta28/ansible-test:Nuxtimage",
                    "Labels": {},
                    "OnBuild": null,
                    "OpenStdin": false,
                    "StdinOnce": false,
                    "Tty": true,
                    "User": "",
                    "Volumes": null,
                    "WorkingDir": "/app"
                },
                "Created": "2020-08-16T07:05:36.169002938Z",
                "Driver": "overlay2",
                "ExecIDs": null,
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜中略〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜        },
        "failed": false
    }
}

PLAY RECAP *******************************************************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

failedになることなくAnsibleが最後まで実行されましたので、コンテナが起動しているか確認し、http://127.0.01:8080からNuxt.jsのサンプルページにアクセスできるか試してみます。

$ docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                      NAMES
f5597614f5d5        yuta28/ansible-test:Nuxtimage   "docker-entrypoint.s…"   37 minutes ago      Up 37 minutes       127.0.0.1:8080->3000/tcp   NuxtContaier


ちゃんとサンプルページにアクセスできることが確認できました。

DockerHubへのpush

build_image.ymlで自動pushされるように設定しましたので、ご覧のように私のDockerHubへイメージがpushされています。

反省点

Dockerのイメージからコンテナ起動まで一通りAnsibleで実行することができました。
ですが、nuxt-appの仕様上コマンド実行後に対話形式で設定を行わければならず、この部分をどうしてもコンテナ内へ入って手動で入力するしかできなかったことが心残りです。
expectというコマンドを使えば、パスワード変更のような直接入力する形式の対話形式でしたら設定できました。ただし、例えばこの中から好みのフレームワークを選択してくださいという選択形式の対話形式ですと選んだ項目が反映されず、一番上の選択肢が回答として反映されました。
しかも、ベースとなるNodeイメージにはexpectコマンドが入っておらず、yumコマンドでもインストールできなかったので、必要なソースファイルを用意してmakeコマンドで入れる必要があります。この辺の設定をDockerfileで最初から設定するのが大変な手間ですので、自動化するか対話形式のままにするかは各自次第なところだと思います。(もっと簡単な方法ありましたら是非とも教えてください。)

次回予告

次回ですが、モジュールを使うのではなく、Ansible-benderを用いてコンテナ管理できるように挑戦してみます。