ようへいの日々精進XP

よかろうもん

CircleCI で Docker イメージをビルドして Amazon ECR に push するまでのハンズオン

tl;dr

Docker のコンテナイメージを AWS の CodePipeline や CodeBuild を介して, コンテナレジストリ (ECR) に push する流れを実装したくて, 色々と調査していましたが, とりあえず, CircleCI で実施してみたらどうなるだろうと思ってドキュメントなどを参考にハンズオンしてみました.

やんなきゃいけないこと

コンテナレジストリにコンテナイメージを push するにあたって, 以下のような流れを実装したいなと考えています.

  1. Docker イメージをリポジトリに push
  2. CircleCI にてコンテナイメージをビルド
  3. ビルドしたイメージに対してテスト
  4. テストが正常に完了したら ECR に push

今回, 三番目のステップであるビルドイメージのテストは goss と goss を Docker コンテナイメージに対してテストをする為のラッパーである dgoss を利用してテストするように実装しようと思います.

github.com

github.com

やったこと

リポジトリ

以下にやったことをまとめてあります.

github.com

超シンプルな Apache が起動するだけの Docker イメージを作成するものです. ファイルの一覧は以下の通りです.

$ tree .
.
├── Dockerfile
├── README.md
├── goss.yaml
└── terraform
    ├── Makefile
    ├── main.tf
    └── provider.tf

1 directory, 6 files

Dockerfile

前述の通り, Amazon Linux コンテナを利用して, Apache をインストールしているだけのコンテナが作成されます.

FROM amazonlinux
RUN yum install -y httpd && echo 'circleci-docker-test-and-build' > /var/www/html/index.html

goss.yaml

goss の詳しい説明については, Githubリポジトリをご一読いただければと思います. テストケースが YAML で書くことができるインフラ構成のテストツールとなります. 類似したツールでは Serverspec が挙げられます. 以下は YAML で書かれたテストケースです.

$ cat goss.yaml
package:
  httpd:
    installed: true
process:
  httpd:
    running: true
port:
  tcp:80:
    listening: true
file:
  /var/www/html/index.html:
    exists: true
    filetype: file
    contains:
    - "circleci-docker-test-and-build"

Rspec DSL でテストケースを記載する Serverspec とは異なり, Ansible などで馴染み深い YAML でテストケースを記述できるという点が異なりますが, Serverspec との決定的な違いは goss はリモートホストのテストには対応していないという点です. これをネガティブポイントと捉えるかどうかはユースケース次第なので, 必要に応じて使い分けることができればと考えています.

上記のテストケースは実にシンプルで, 以下のような点をテストします.

  • httpd パッケージがインストールされていること
  • httpd プロセスが起動していること
  • 80 番ポートが Listen していること
  • /var/www/html/index.html が存在していること, circleci-docker-test-and-build という文字列が含まれていること

goss は CircleCI では, 以下のように実行します. ${FULL_IMAGE_NAME} には, 前段階で docker build した際のコンテナイメージ名が入ります.

dgoss run ${FULL_IMAGE_NAME} /usr/sbin/httpd -DFOREGROUND

例えば, コンテナイメージを test:httpd でビルドした場合, 以下のように実行してテストを走らせることが出来ます.

$ dgoss run test:httpd /usr/sbin/httpd -DFOREGROUND
INFO: Starting docker container
INFO: Container ID: 101827da
INFO: Sleeping for 0.2
INFO: Container health
PID                 USER                TIME                COMMAND
2888                root                0:00                /usr/sbin/httpd -DFOREGROUND
2939                48                  0:00                /usr/sbin/httpd -DFOREGROUND
2940                48                  0:00                /usr/sbin/httpd -DFOREGROUND
2942                48                  0:00                /usr/sbin/httpd -DFOREGROUND
2944                48                  0:00                /usr/sbin/httpd -DFOREGROUND
2972                48                  0:00                /usr/sbin/httpd -DFOREGROUND
INFO: Running Tests
Process: httpd: running: matches expectation: [true]
File: /var/www/html/index.html: exists: matches expectation: [true]
File: /var/www/html/index.html: filetype: matches expectation: ["file"]
File: /var/www/html/index.html: contains: matches expectation: [circleci-docker-test-and-build]
Port: tcp:80: listening: matches expectation: [true]
Package: httpd: installed: matches expectation: [true]


Total Duration: 0.039s
Count: 6, Failed: 0, Skipped: 0
INFO: Deleting container

.circleci/config.yml

Docker ビルドして, テストして, ECR にログインして, コンテナイメージを push するまでの操作をまとめると以下のような感じになりました.

version: 2
jobs:
  build:
    machine: true
    steps:
      - checkout
      - run:
          name: Setup common environment variables
          command: |
            echo 'export ECR_REPOSITORY_NAME="${AWS_RESOURCE_NAME_PREFIX}"' >> $BASH_ENV
            echo 'export FULL_IMAGE_NAME="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${ECR_REPOSITORY_NAME}:${CIRCLE_SHA1}"' >> $BASH_ENV
      - run:
          name: Setup AWS CLI
          command: |
            curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
            unzip awscli-bundle.zip
            sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
      - run:
          name: Setup goss & dgoss
          command: |
            curl -L https://github.com/aelsabbahy/goss/releases/download/v0.3.6/goss-linux-amd64 -o ${HOME}/bin/goss
            chmod +x ${HOME}/bin/goss
            export GOSS_PATH=${HOME}/bin/goss
            curl -L https://raw.githubusercontent.com/aelsabbahy/goss/master/extras/dgoss/dgoss -o ${HOME}/bin/dgoss
            chmod +x ${HOME}/bin/dgoss
      - run:
          name: Build Image
          command: |
            docker build -t ${FULL_IMAGE_NAME} .
      - run:
          name: Run Tests
          command: |
            dgoss run ${FULL_IMAGE_NAME} /usr/sbin/httpd -DFOREGROUND
      - run:
          name: Login to AWS ECR
          command: eval $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
      - run:
          name: Push Docker Image
          command: |
            docker push ${FULL_IMAGE_NAME}

事前に以下のような環境変数を CircleCI のプロジェクトに登録しておく必要があります.

実際に

リポジトリに対して push すると CircleCI によるビルドとテスト, ECR への push が行われます.

f:id:inokara:20181230164739p:plain

いい感じです.

ECR にも意図した通りにコンテナイメージが push されていることが判ります.

$ aws ecr describe-images --repository-name=httpd
{
    "imageDetails": [
        {
            "registryId": "123456789012",
            "repositoryName": "httpd",
            "imageDigest": "sha256:ae7cefb61f88f132eadc4eb98fa545a0054dd3a2888be26c9da48f29ab21c0b0",
            "imageTags": [
                "40f82524001fe52644c2cee1ab9b7895572aa94f"
            ],
            "imageSizeInBytes": 142276848,
            "imagePushedAt": 1546063334.0
        },
        {
            "registryId": "123456789012",
            "repositoryName": "httpd",
            "imageDigest": "sha256:9afcd192b4e9c51fc43706d00c755747e890811c367422cb53e69f2071df42d2",
            "imageTags": [
                "7c92bdaac1c92f0f317f4e5692f5c6833642be48"
            ],
            "imageSizeInBytes": 142276745,
            "imagePushedAt": 1545953477.0
        },
        {
            "registryId": "123456789012",
            "repositoryName": "httpd",
            "imageDigest": "sha256:7ba84149310e448739a2f91e06641a63e8ed37bf9cf3b9991cc8860ba54d25f2",
            "imageTags": [
                "79697d588b9efb809cc07621f403a63744079e66"
            ],
            "imageSizeInBytes": 142276832,
            "imagePushedAt": 1546065921.0
        }
    ]
}

以上

今回は, コンテナイメージをテストしてイメージを push するところまでをハンズオンしましたが, 次回は, もう少し複雑なアプリケーションを使って, ECS や Fargate にデプロイするところまでやってみたいと思います.

参考

circleci.com