この連載が書籍になりました!『RancherによるKubernetes活用完全ガイド

GitLabを用いた継続的インテグレーション

2019年4月25日(木)
藤原 涼馬
連載5回目となる今回は、GitLab CIを用いた継続的インテグレーション(CI)について解説します。

GitLab Pipelineを使ってテストを自動化する

これまではテスト駆動によるコーディングの進め方といった趣の内容でしたが、ここからようやく今回の本題であるCIの話に移ります。作成したPingPong APIを含んだサーバサイドアプリケーションのDockerコンテナイメージのビルド、レジストリへの保存やテストといったCIパイプラインを実装していきましょう。

サーバアプリケーションのDockerfile作成

ここまでで作成したPingPong APIを含んだサーバアプリケーションをコンテナイメージ化するために、Dockerfileを作成します。ここでは、DockerfileのDSL(Domain Specifig Language)については解説しません。これらの詳細について知りたい場合は、Docker公式のDockerfile referenceや、Docker実践ガイド第二版(古賀正純著、インプレス刊)などの書籍を参照してください。

さて、それでは作成したサーバサイドアプリケーションのDockerfile(server/Dockerfile)を下に示します(リスト18)。

リスト19:リスト18:server/Dockerfile

FROM python:3.6-alpine

EXPOSE 8000
# djangoユーザを作成して利用
RUN adduser -D django
USER django
RUN mkdir -p /home/django/.local/bin
# PATHにユーザ固有のpip install先を含める
ENV PATH=$PATH:/home/django/.local/bin
COPY --chown=django:django requirements.txt ./
# requirementsをユーザ領域にインストール
RUN pip install --user -r requirements.txt
# アプリケーション本体をコンテナ内にデプロイ
COPY --chown=django:django ./sampleapp /home/django/sampleapp
WORKDIR /home/django/sampleapp
# コンテナ外部からもアプリケーションにアクセスできるように0.0.0.0:8000を指定
CMD ["python", "manage.py" , "runserver", "0.0.0.0:8000"]

補足として効率的にビルドを行うためのDockerfile記述の基本的な考え方について解説します。その基本的な考え方は、可能な限りイメージのキャッシュが効くようにするということです。詳細な説明は省きますが、変更されることが少ないものほどDockerfileの頭の方に記述すると覚えておくと良いでしょう。

今回の場合、それを端的に表しているのが、下記に抜粋している部分です(コード19)。

リスト20:リスト19:server/Dockerfileの抜粋(パッケージ依存関係のインストールまで)

FROM python:3.6-alpine

EXPOSE 8000
# djangoユーザを作成して利用
RUN adduser -D django
USER django
RUN mkdir -p /home/django/.local/bin
# PATHにユーザ固有のpip install先を含める
COPY --chown=django:django requirements.txt ./
# requirementsをユーザ領域にインストール
RUN pip install --user -r requirements.txt

EXPOSEで外部公開するポートの指定、RUNでアプリケーションの実行ユーザであるdjangoユーザの作成とUSERによるdjangoユーザへのコンテナ内プロセスの(デフォルトの)実行ユーザの切り替えなどを行っています。さらに、COPYrequirements.txtをコピーして、今回のアプリケーションの動作に必要なモジュールを、コンテナイメージ内にコードをデプロイする前にインストールしています。

これまで説明していなかったrequirements.txtですが、以下のリストのように単純にインストールするPythonのパッケージ(とそのバージョン)を記載したものです(コード20)。

リスト21:リスト20:server/requirements.txt

Django==2.1.5
djangorestframework==3.9.1

リスト19にも示しているように、最も変更頻度が高いであろうアプリケーションコードのデプロイを、Dockerfileのなるべく後段に持っていくことで、コンテナイメージのキャッシュを可能な限り活かしつつ開発を進めることができます(ただし、今回はGitLabのCI Pipelineのアーキテクチャの都合上、イメージキャッシュが効きにくくなっています。そのため、ローカルの開発端末で試行錯誤する際の効率の向上にとどまる点はご理解ください)。

効率的なイメージのビルドを実現するための方法をさらに掘り下げたい方は、DockerCon EU 2018 - Dockerfile Best Practicesや、Better Docker Imageなどを参照してください。

さて、ビルドが正常にできるか、アプリケーションが正しく立ち上がるかを確認するためにイメージをビルドしてみましょう(リスト21)。

リスト22:リスト21:サーバアプリケーションのコンテナイメージのビルド

$ docker build -t test `pwd`
Sending build context to Docker daemon   47.1kB
Step 1/11 : FROM python:3.6-alpine
 ---> de35df1f34dd
~~~~中略~~~~
Step 11/11 : CMD ["python", "manage.py" , "runserver", "0.0.0.0:8000"]
 ---> Running in 42e30aef24ea
Removing intermediate container 42e30aef24ea
 ---> f4447a7efa2b
Successfully built f4447a7efa2b
Successfully tagged test:latest

Successfully...の記述から無事Dockerイメージのビルドが成功したと判断できます。手元でイメージを起動して動作確認してみましょう(リスト22)。

リスト23:リスト22:ビルドしたサーバアプリケーションの動作確認

$ docker run -d -p 8000:8000 --name test test
$ curl -s http://127.0.0.1:8000/api/ping | jq
{
  "message": "pong"
}

レスポンスを見る限り、期待したとおりのイメージをビルドできているようです。

GitLabのCI/CD Pipeline定義の作成(.gitlab.ymlの記述)

今回はGitLab上でCIを行うので、GitLab CI/CD Pipelineを利用します。GitLab CI/CD Pipelineを利用する際はリポジトリのルートディレクトリに.gitlab-ci.yml※3ファイルを作成して、その中にパイプライン定義を記述します。ここまでで作成したPingPong APIを含んだサーバサイドアプリケーションのコンテナイメージをビルドした後に、テストを実行するパイプラインを作成していきましょう(リスト23)。

リスト24:リスト23:サーバアプリケーションのビルドとテストを実装したパイプライン(.gitlab-ci.yml)

stages:
  - build
  - test

sampleapp_image_build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
  script:
    - docker build -t registry.gitlab.com/fufuhu/ti_rancher_k8s_sampleapp/todo/server:latest server
    - docker push registry.gitlab.com/fufuhu/ti_rancher_k8s_sampleapp/todo/server:latest
  tags:
    - docker

sampleapp_test:
  stage: test
  image: registry.gitlab.com/fufuhu/ti_rancher_k8s_sampleapp/todo/server:latest
  script:
    - |
      cd server/sampleapp
      python manage.py test

※3:ファイル名は.gitlab-ci.ymlにしておいてください.gitlab-ci.yamlでは動作しません

Dockerfileなどと同様に、このパイプラインについても個別のシンタックスに関する解説はいたしません。

個別のシンタックスの詳細についてはGitLab CI/CD Pipelineの公式ドキュメント、または、GitLab実践ガイド(北山晋吾著、インプレス刊)を参照してください。

パイプラインの全体的な構成としては、

  1. パイプラインを2つのステージ(buildtest)に分割
  2. buildステージのsampleapp_image_buildジョブでサーバサイドアプリケーションのイメージをビルドしてGitLab Container Registryに保存
  3. testステージのsampleapp_testジョブでサーバサイドアプリケーションのイメージをテスト

としています。

またDockerイメージのリポジトリ名(コード23の例では、registry.gitlab.com/fufuhu/ti_rancher_k8s_sampleapp/todo/server)のregistry.gitlab.com/fufuhu/ti_rancher_k8s_sampleappの部分はGitLabに作成したプロジェクト名に依存するので、その点は注意してください。

では、作成した各種ファイルをリモートリポジトリにプッシュしましょう。ファイルの変更をプッシュすれば、.gitlab-ci.ymlに定義したとおりにパイプラインが実行されるはずです。

では、当該リポジトリを格納しているプロジェクトにアクセスしてパイプラインの状態を確認してみましょう。GitLab.comの当該プロジェクトにアクセスし、左側メニューのCI/CDの下にあるPipelinesを選択します(図10)。

図10:左側メニューのCI/CDの下にあるPipelinesを選択する

図10:左側メニューのCI/CDの下にあるPipelinesを選択する

画面が遷移すると、実行中または実行待ち、実行済みのパイプラインの一覧が表示されます(図11)。

図11:パイプラインの一覧

図11:パイプラインの一覧

図11のPipelineカラムの数字(図11では#46399869)をクリックして、パイプラインの実行結果の詳細(図12)を見てみましょう。

図12:パイプラインの実行結果詳細

図12:パイプラインの実行結果詳細

BuildステージとTestステージに分割されたパイプラインがグラフィカルに表示されていることがわかります。では個別のジョブについて見るために、画面上に表示されている個々のジョブの枠をクリックすると、ジョブの詳細を確認できます(図13、14)。

図13:sampleapp_image_buildジョブの実行結果

図13:sampleapp_image_buildジョブの実行結果

図14:sampleapp_testジョブの実行結果

図14:sampleapp_testジョブの実行結果

図13、14の表示内容からわかるとおり、ジョブの中で記述したスクリプトのコマンドの実行結果の標準出力および標準エラー出力の内容が表示されます。ジョブの成否は、ジョブ内部で実行されている個々のコマンドの終了コードで判断しています。

それでは、サーバサイドの機能の実装が終わったので、マージリクエストを作成しましょう。

プロジェクトの左側メニューからMerge Requestsを選択し、マージリクエストの管理ページへ遷移します。今回の場合は先ほどプッシュしたコミットのマージリクエストを作成するためのボタン(図15右側のCreate merge requestボタン)があるので、これをクリックします。

図15:マージリクエストの作成

図15:マージリクエストの作成

マージリクエストの名前、サマリについての記述を行い、Submit merge requestボタンをクリックしてマージリクエストを作成します(図16)。

図16:マージリクエストのサブミット

図16:マージリクエストのサブミット

マージリクエストを作成すると、パイプラインの実行結果もマージリクエストの中に表示されます(図17:画像中Pipeline #46399869 passed for ~~の部分)。

図17:マージリクエストの例

図17:マージリクエストの例

図17からわかるとおり、CIパイプラインが正常に終わってることがマージリクエストの画面上にも表示されます。実装コードに対するテストコードが適切に記述されていることが確認でき、テストをすべてパスできている場合はマージしても良いでしょう。

最後に動作確認を進めていきましょう。図18にあるとおり、CIパイプラインが実行された結果、Dockerコンテナイメージが生成され、GitLab Container Registryに保管されています。

図18:作成されたDockerコンテナイメージ

図18:作成されたDockerコンテナイメージ

念のための動作確認

作成したDockerイメージをローカルに持ってきて、念のための動作確認をしてみましょう。イメージの取得手順はリスト24に挙げたとおりです。docker login時に尋ねられるユーザ名とパスワードは、GitLab.comのユーザ名とパスワードになります。

リスト25:リスト24:作成したDockerイメージの取得

$ docker login registry.gitlab.com
$ docker pull registry.gitlab.com/fufuhu/ti_rancher_k8s_sampleapp/todo/server:latest
latest: Pulling from fufuhu/ti_rancher_k8s_sampleapp/todo/server
8e402f1a9c57: Already exists
cda9ba2397ef: Already exists
7a817918d4d5: Already exists
e64fbda5eefb: Already exists
6d027cc21c42: Already exists
77c0f5455a39: Pull complete
7130100d2837: Pull complete
a00052bb47ab: Pull complete
504f2626383e: Pull complete
d823da105d66: Pull complete
Digest: sha256:6630bc23bf1ced5e1f643d4feafeaa1d60979914fe1ff8e5849c08f2483b9c06
Status: Downloaded newer image for registry.gitlab.com/fufuhu/ti_rancher_k8s_sampleapp/todo/server:latest

イメージの取得が完了したら、起動してPingPong APIを叩いてみましょう(コード25)。

リスト26:リスト25:取得したサーバアプリケーションイメージが正常に動作しているようす

$ docker run -d -p 8000:8000 --name test registry.gitlab.com/fufuhu/ti_rancher_k8s_sampleapp/todo/server:latest
latest: Pulling from fufuhu/ti_rancher_k8s_sampleapp/todo/server
$ curl -s http://localhost:8000/api/ping | jq
{
  "message": "pong"
}
$ docker rm -f test
test

curlコマンドの実行結果から、期待したとおりの動作をしていることがわかります。

まとめ

今回はPythonを使ったサンプルアプリケーションの実装とテストの記述、GitLab CI/CDを使ったCIの実行について解説しました。

今回実装した機能は非常に単純なものなので、意味のないもののように思えますが、コード規模が大きくなるほど効いてきます。例えばコードのリファクタリングを安全に行えたり、コードの変更による影響範囲の可視化などに役立つはずです。

今回はあくまでも実際にコンテナベースのCIの触りまでの部分の流れを解説したのみなので、テストについて入門から知りたい場合は、知識ゼロから学ぶソフトウェアテスト【改訂版】(高橋寿一著、翔泳社)を、テスト駆動開発についてコードのリファクタリング方法なども含めて理解したい場合はテスト駆動開発(KentBeck著、和田卓人訳、オーム社)などを参照してみると良いでしょう。

次回はRancherに戻って、Rancherのカスタムカタログリポジトリの実装、および今回作成しているサンプルアプリケーションをデプロイするためのカタログアプリ用のチャート作成について解説します。

株式会社リクルートテクノロジーズ

ITエンジニアリング本部プロダクティビティエンジニアリング部
クラウドアーキテクトグループ

Rancher JPコミュニティコアメンバー

ユーザ系SIerにてR&Dを経験したのち、2016年より現職。 業務ではパブリッククラウド、コンテナ関連技術を活用した 先進テクノロジーアーキテクチャの事業装着を担当。 Japan Container Days v18.12や、RancherJPのイベント等登壇等複数。

連載バックナンバー

クラウド技術解説
第11回

Rancherコードリーディング入門(3/3)

2020/2/19
前回に続き、紙面の都合で「RancherによるKubernetes活用完全ガイド」に掲載されなかったパートをご紹介します。
クラウド技術解説
第10回

Rancherコードリーディング入門(2/3)

2019/12/25
前回に続き、紙面の都合で「RancherによるKubernetes活用完全ガイド」に掲載されなかったパートをご紹介します。
クラウド技術解説
第9回

Rancherコードリーディング入門(1/3)

2019/11/5
今回からは、紙面の都合で「RancherによるKubernetes活用完全ガイド」に掲載されなかったパートをご紹介します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています