BETA

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

投稿日:2020-08-18
最終更新:2020-09-05

概要

最初の記事ですが、内容としては以前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の仕様上コマンド実行後に対話形式で設定を行わければならず、この部分をどうしてもコンテナ内へ入って手動で入力するしかできなかったことが心残りです。
nuxt-appの後にオプション設定ができれば、Dockerfile内にオプションをいれて自動でひな型を作成できれば便利なのですが、このあたりの知見に詳しい方がいましたらコメントで教えてくださると嬉しいです。

追記(2020/8/25)

コメントに紹介されていましたが、expectというコマンドを使えば対話形式の設定を自動化で行えるそうです。
ただ、試してみたのですが例えばパスワード変更のような直接タイプする形式の対話形式でしたら行けそうですが、この中から好みのフレームワークを選択してくださいという選択形式の対話形式ですと選んだ項目が反映されませんでした。
一応、すべての選択肢に対して回答はされていておそらくデフォルト回答のまま進みましたので、デフォルト設定のままでしたらいけるのかもしれません。
ただ、Nodeイメージにはexpectが入っておらず、yumコマンドでもインストールできなかったので、必要なソースファイルを用意してmakeコマンドで入れる必要があります。この辺の設定をDockerfileで最初から設定するのがかなり大変な手間ですので、自動化するか対話形式のままにするかは各自次第なところだと思います。(もっと簡単な方法ありましたら是非とも教えてください。)

次回予告

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

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

主にITインフラ関係の勉強結果を残していきたいと思います。 AnsibleやAWSなどを中心に学んだ結果を記事として残していきます。

よく一緒に読まれる記事

2件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
08/23 16:54

対話形式コマンドを自動化する方法として、 expect コマンドを使う方法があります。 使えそうであれば、参考にしてみてください。

参考サイト: 対話形式のコマンドを自動化するexpectの使い方 - Qiita

08/23 22:48

@morichan ご紹介ありがとうございます。 調べてみたのですが、少し使い方が難しいですね。sendにexpectで書かれた質問文に対する回答を記載したのですが、直接タイプするのではなく、上下にカーソルを移動して、Enterで入力する質問文ですと上手くいきませんでした。 一応選択肢の一番上を選択したまま進むので、デフォルト設定のものでしたら、expectでもいけるような気がします。 ただ、その場合でもNodeコンテナにはexpectコマンドが入っていないので、直接ソースファイルからmake installしないといけないので、Dockerfileの中身がかなり手間になりそうです(汗)