はてなの金次郎

とあるエンジニアの技術系ブログ

.gitlab-ci.ymlの俺的Tips

はじめに

GitLabを使いはじめて1年半になりますが、.gitlab-ci.yml に関するノウハウがほどほどに溜まってきた気がするのでTipsとしてまとめてみました。

注意

  • Webアプリケーションの CI/CD に関するTipsが多いです。
  • .gitlab-ci.yml の例は Python/Django で書いていますが、特に難しいことは書いていないつもりです。適宜得意な言語に置き換えて読んでください。
  • 本記事では公式ドキュメントに倣い、 .gitlab-ci.yml の一つの実行単位を ジョブ、ジョブに定義する imagestage, script などのジョブのふるまいを キーワード と呼びます。

Index

  1. Services:コンテナイメージを複数扱う
  2. Anchors:ジョブのテンプレートでスリムなYAML
  3. Predefined environment variables:用意されているGitLab CI/CD変数を活用する
  4. Only and Except:制限付きのジョブを作成する
  5. when:manual:安心安全なデプロイ
  6. artifactsカバレッジレポートを可視化する

Tips

1. Services:コンテナイメージを複数扱う

https://docs.gitlab.com/ee/ci/services/README.html

services というキーワードを用いると、ベースイメージと接続可能なコンテナを定義できます。 Docker in DockerでコンテナレジストリにDockerイメージをプッシュしたり、単体テストでデータベースコンテナに接続したりするといったケースに有用です。

例えば、下記のように定義することで postgres というホスト名でPostgreSQLに接続できます。

services:
  - postgres:latest
variables:
  POSTGRES_DB: custom_db
  POSTGRES_USER: custom_user
  POSTGRES_PASSWORD: custom_password

PostgreSQLの公式イメージで用意されている環境変数にデータベース名、ユーザ名、パスワードを設定します。

環境変数に関する詳細はPostgreSQLのDockerHubを参照してください。
FYI: https://hub.docker.com/_/postgres/

DATABASE_URL で接続情報を表すと、 postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres:5432/$POSTGRES_DB となります。

2. Anchors:ジョブのテンプレートでスリムなYAML

https://docs.gitlab.com/ee/ci/yaml/README.html#anchors

Anchorsという機能を利用してジョブのテンプレートを作成することで、.gitlab-ci.yml をスリムに保つことができます。

例えば、test1, test2というテストをそれぞれ実行したいとき、

test1:
  stage: test
  image: python:latest
  before_script:
    - pipenv install --dev --system
  script:
    - pytest test1

test2:
  stage: test
  image: python:latest
  before_script:
    - pipenv install --dev --system
  script:
    - pytest test2

上記のように書くこともできますが、Anchorsを利用すると下記のように書くことができます。

.test_template: &test_definition
  stage: test
  image: python:latest
  before_script:
    - pipenv install --dev --system

test1:
  <<: *test_definition
  script:
    - pytest test1

test2:
  <<: *test_definition
  script:
    - pytest test2

今回の例では行数的な差があまり出ませんでしたが、ジョブが増えれば増えるほどAnchorsの効果が高まります。

3. Predefined environment variables:用意されているGitLab CI/CD変数を活用する

https://docs.gitlab.com/ee/ci/variables/README.html#predefined-environment-variables

.gitlab-ci.yml にはGitLabがあらかじめ用意している環境変数がいくつかあります。 それらを活用することで余計な環境変数定義をしなくて済んだり、別のプロジェクトでそのまま活用できたりします。

例えば、GitLabコンテナレジストリへのイメージのプッシュで Predefined environment variables を活用することができます。

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
  script:
    - docker build --pull -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  only:
    - tags

CI_JOB_TOKEN, CI_REGISTRY, CI_REGISTRY_IMAGE, CI_COMMIT_TAG は Predefined variablesです。

4. Only and Except:制限付きのジョブを作成する

https://docs.gitlab.com/ee/ci/yaml/README.html#onlyexcept-basic

onlyexcept というキーワードを定義すると、ある条件の場合のみジョブを実行したり、ある条件を除く場合のみジョブを実行したりすることができます。

例えば、タグがプッシュされた時だけ実行するように定義したり、

only:
  - tags

ある特定のブランチがプッシュされた時は実行をしないように定義したりすることができます。

except:
  - master

また、正規表現にも対応しているので特定のプレフィックスがついたブランチがプッシュされた時だけ実行するというような定義も可能です。

only:
  - /^release-.*$/

5. when:manual:安心安全なデプロイ

https://docs.gitlab.com/ee/ci/yaml/README.html#whenmanual

when:manual をジョブに定義すると、手動で実行ボタンをクリックしたらジョブが実行されるようになります。

f:id:gyuuuutan:20181202012650p:plain
ジョブにwhen:manualを定義したときのパイプライン

上記は when:manual をデプロイジョブに定義した際のパイプラインですが、プッシュしただけではデプロイジョブは実行されません。
「▶️」の実行ボタンを押すことで初めてデプロイジョブが実行されます。

これは本番環境へのデプロイジョブに有用で、不慮の事故を防ぐことができます。

6. artifacts:カバレッジレポートを可視化する

https://docs.gitlab.com/ee/user/project/pipelines/job_artifacts.html

artifacts キーワードはジョブで生成された成果物をダウンロード可能にしたり、GitLab Pagesで公開したりするときに有用です。

例えば、下記のように定義すると、ユニットテストで生成されるカバレッジレポートをパイプラインからダウンロードできるようになります。

script:
  - pytest --cov-report=html
artifacts:
  paths:
    - htmlcov

f:id:gyuuuutan:20181202012834p:plain
ジョブにartifactsを定義したときのパイプライン

〜補足〜
カバレッジレポートはGitLab Pagesの機能を利用することで真価を発揮します。

GitLab Pages は GitHub Pagesと同じような機能ですが、 GitLabのリポジトリから静的サイトを公開するための機能です。

無料で利用できたり、httpsに対応していたりといった特徴があります。

https://docs.gitlab.com/ce/user/project/pages/

GitLab Pages は以下の3ステップを行うだけで静的サイトを自動で生成してくれます。

  1. public という名前のディレクトリに静的ファイルを配置する。
  2. .gitlab-ci.yml にデプロイのジョブを定義する
  3. 定義したジョブを実行する

下記のURLは上記の3ステップで自動生成された実際のURLです。
カバレッジレポートを展開しています。

https://jumpyoshim.gitlab.io/django-polls

生成されたカバレッジレポートを public ディレクトリに配置し artifactspaths に定義するだけです。

ここで注意しなければならないのが deploy ステージでないとGitLab Pagesのドメインを生成してくれないことです。(どうしてもうまくいかなくて数時間悩まされました。)

pages:
  stage: deploy
  image: alpine:latest
  script:
    - mv htmlcov/ public/
  artifacts:
    paths:
      - public
  only:
    - master

FYI: https://gitlab.com/help/user/project/pages/index.md

参考例

上記のTipsを活用した .gitlab-ci.yml の例です。 以下を実行しています。

  • GitLabコンテナレジストリへのDockerイメージのプッシュ
  • ユニットテスト
  • 文法チェック
  • コードメトリクス計測
  • Herokuへのデプロイ
  • GitLab Pagesの公開設定

FYI: https://gitlab.com/jumpyoshim/django-polls/blob/master/.gitlab-ci.yml

.build_template: &build_definition
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY

build_for_master:
  <<: *build_definition
  script:
    - docker build --pull -t $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - master

build_for_tags:
  <<: *build_definition
  script:
    - docker build --pull -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  only:
    - tags

.test_template: &test_definition
  stage: test
  image: python:3.7
  before_script:
    - pip install pipenv
    - pipenv install --system --dev
  except:
    - master

pytest:
  <<: *test_definition
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: custom_db
    POSTGRES_USER: custom_user
    POSTGRES_PASSWORD: custom_password
  script:
    - export DATABASE_URL=postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres:5432/$POSTGRES_DB
    - python3 manage.py collectstatic --noinput
    - pytest --cov-report=html --tb=line
  after_script:
    - mv htmlcov/ public/
  artifacts:
    paths:
      - public

flake8:
  <<: *test_definition
  script:
    - flake8

radon:
  <<: *test_definition
  script:
    - radon cc -n C .
    - radon mi -n B .

deploy:
  stage: deploy
  image: ruby:2.5
  script:
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP --api-key=$HEROKU_API_KEY --skip-cleanup
  only:
    - tags
  when: manual

pages:
  stage: deploy
  image: alpine:latest
  script:
    - echo 'Upload the coverage report'
  artifacts:
    paths:
      - public
  only:
    - master

おわりに

俺的と書いてありますがたくさんのドキュメントや記事、職場のエンジニア、GitLabコミュニティの方の助けで得ることができたTipsです。この場を借りてお礼申し上げます。
OSSへのコントリビュートなど、何かしらの形で恩返しをしていきたいと思っています。

ドキュメント

記事

GitLabコミュニティ

GitLabに関する機能や豆知識を7つ厳選してみました。
もしよろしければのぞいてみてください。

qiita.com

JapanCotainerDays v18.12 これだけは目を通しておきたいセッションベスト3

はじめに

Kubernetes2 Advent Calendar 2018の8日目の記事です。

qiita.com

12/4・5で開催されたJapanContainerDays v18.12に参加してきました。

containerdays.jp

どのセッションもとても勉強になり甲乙はつけがたく、また、自分自身甲乙をつけられる立場ではありませんが、本記事では個人的におすすめだったセッションを「これだけは目を通しておきたいセッションベスト3」として独断と偏見でご紹介させていただきます。

※注意

  • ECSでのアプリケーション構築・運用経験があります。
  • GitLabを業務で使っています。個人的にもGitLabとGitHubの両方を使っています。
  • Kuberntesは独学で「入門Kuberntes」・「コンテナ・ベース・オーケストレーション」を読んだことがあり、GKEやAKSのKaaSでサンプルアプリケーションをデプロイしたことがあるレベルです。
  • 参加していないセッションは考慮していません。

これだけは目を通しておきたいセッションベスト3

1. Microservices on Kubernetes at Mercari

スライド

speakerdeck.com

YouTube

https://youtu.be/6TNqJb97gH0?t=1545youtu.be

Twitterで多くの反響を集めたキーノートの一つでした。
動画とスライドをぜひ見ていただきたいのですが一応まとめておきます。

テーマ

  • なぜメルカリがマイクロサービスアーキテクチャの基盤にKubernetes(そもそもコンテナ技術)を選択するのか
  • Kubernetesを利用して今後どうしていきたいのか

テーマに対する解答(独自の解釈を含む)

  • なぜメルカリがマイクロサービスアーキテクチャの基盤にKubernetes(そもそもコンテナ)を選択するのか
    -> ビジネス競争力のあるソフトウェアをリリースするため
    -> ビジネス競争力を高めるためにはスピード感が大切
    -> スピード感をマイクロサービスアーキテクチャとImmutable Infrastructureによって実現する
    -> マイクロサービスアーキテクチャによる独立・自立したチーム作りで自走可能なチームがスピード感を生み出す
    -> Immutable Infrastructureによる決定論的なデプロイで本番環境へのリリースの心理的安全性を高め、開発者のデプロイに対するハードルを下げることでスピード感を生み出す
    -> マイクロサービスアーキテクチャ・Immutable Infrastructureを実現するためにはコンテナが向いている
    -> オーケストレーションサービスにはECS・HerokuなどのPaaSではなく拡張性の高さとエコシステムの豊富さが特徴であるKubernetesを選択する

  • Kubernetesを利用して今後どうしていきたいのか
    -> Kubernetesの拡張性の高さとエコシステムの豊富さを活用して柔軟にビジネス要件に対応していく

感想

組織として「なぜその技術に取り組むのか」という定義ができているのが大変素晴らしいと感じました。
このトークを聞いて思い出したのはTEDで公開されているサイモン・シネックさんによる「優れたリーダーはどうやって行動を促すのか」という講演です。

www.ted.com

ここで紹介されているのは、偉大な指導者や組織に共通しているあるパターンです。
それは「WHAT」ではなく「WHY」から考え行動するというもので、それが結果的に人の心を動かすそうです。
なぜコンテナなのか、なぜKubernetesなのかが最初に共有されていたため、今後の展望も納得感があり多くの人が共感しTwitterでの反響に繋がったのではないかと思いました。

自分自身も学習する際は、なぜその技術を身につけるのかという自問自答をしていきたいです。

2. GitLabによるコンテナCI/CDパイプラインのこれから

スライド

www.slideshare.net

GitLabユーザとして押さえておきたい内容だと感じたためピックアップしました。

テーマ

  • なぜGitLabのAuto DevOpsがよいのか
  • GitLabのAuto DevOpsとは何か

テーマに対する解答(独自の解釈を含む)

  • なぜGitLabのAuto DevOpsがよいのか
    -> 多くの企業はDevOpsパイプラインにおいて3~6個のツールを利用しており、技術選定コストや学習コストがかかっているという課題があるため
    -> GitLabはDevOpsパイプラインにおけるツールを包括して提供している(Complete DevOps)
    -> ソフトウェア開発に時間を費やす環境を提供するため
    -> CI/CDパイプラインに必要な設定を動的に行う仕組み(PaaS)を提供している=>Auto DevOps

  • GitLabのAuto DevOpsとは何か
    -> Auto DevOps is PaaS for Kuberntes
    -> 開発者はコードを書いてプッシュするだけ。残りは全てAuto DevOpsがよしなにやってくれる
    -> 残りとは、コード品質チェック・コードやDockerイメージのセキュリティチェック・依存関係チェック・一時的なアプリケーションレビュー環境の構築・Kubernetesクラスターへのデプロイ・Sitespeed.ioによるWebページのパフォーマンス測定・Prometheusによる監視など

感想

GitLabをまだまだ使いこなせていないことを実感しました。 初期の導入を手動で行なったり定常的な作業を手動で実施していたりという部分が何箇所か思い当たったので、そういう部分はAuto DevOpsで自動化できそうだなと感じました。

Auto DevOpsは導入は大変かもしれませんが運用は比較的楽になるのではと感じました。 また、Auto DevOpsはGoogleアカウントにサインインしていればGitLabから動的にGKEクラスターを作成してくれるという機能があったり、昨日AzureからGCPへの移行が話題になりましたがGitLabとしてもGCPとの連携を推し進めていたりと、GitLabとGCPの親和性はかなり高いです。 Auto DevOpsを導入するのであれば、GCPを利用するのが良さそうです。

3. 本番環境のKubernetesマニフェストに 最低限必要な 7 のこと

スライド

speakerdeck.com

テーマ

テーマに対する回答

  • 本番環境のKubernetesマニフェストに 最低限必要な 7 のことは何か
    -> コンテナのライフサイクル(起動時)
    -> コンテナのライフサイクル(停止時)
    -> ヘルスチェック
    -> メンテナンスとアップデート
    -> スケジューリング
    -> リソースの割り当てと基準
    -> カーネルパラメータのチューニング
    -> インターネットからのアクセス制御

感想

とりあえずこの7つのTipsを抑えておけば本番環境にKubernetesを導入することができる(運用はまた別)とのことで、わかりやすく端的にまとめられている資料でした。

特に初期化処理の方法やコンテナ停止時の挙動に関する話はとても参考になりました。

Kuberntes実践ガイドも合わせて読みたいですね。

Kubernetes完全ガイド (impress top gear)

Kubernetes完全ガイド (impress top gear)

  • 作者:青山 真也
  • 発売日: 2018/09/21
  • メディア: 単行本(ソフトカバー)

全体の感想

Kuberntesはコンテナオーケストレーションとしてデファクトスタンダードとなった
by Chris Aniszczyk / キーノートより

Kuberntesの話を聞きにいったつもりでしたが、Kuberntesとその周辺のエコシステムを活用していかに運用を楽にするか、いかにビジネス競争力が高いソフトウェアを作るかという話にシフトしているようでした。

コンテナ⇨Kuberntes⇨クラウドネイティブ(ここの話が多かったです)

事実、JpanContainerDaysは今年で終わりで来年からはCloud Native Days 2019となります。
勉強になることだらけでとても刺激的な2日間でした。運営のみなさまありがとうございました。
次回は一回り成長して参加したいです。

(聞けていないセッションが結構あるのでおすすめのセッションがあればぜひ教えてください!)

Docker ComposeでLaravelのお手軽開発環境構築

はじめに

Laravel #2 Advent Calendar 2018 の5日目の記事です。

qiita.com

Docker Composeの公式HPで「Quickstart: Compose and Django」や「Quickstart: Compose and Rails」のようにDjangoRailsのクイックスタートはあるのですが、Laravelのクイックスタートが残念ながらありませんでした。

そこで今回、Laravelの公式HPで公開されているチュートリアルBasic Task List」を元にDocker Composeのチュートリアル的なもの「Quickstart: Compose and Laravel」 を作成してみました。

コンテナ構成はLaravel + MySQL + Redisです。

コンテナ技術の動向

コンテナ技術の動向や有用性を知ることでコンテナを学ぶべき理由を改めて認識したいと思います。

コンテナ化したアプリケーションを活用するクラウドネイティブの流れが進行しています。
(中略)
インフラを意識せず、ソフトウェアやサービスの開発に集中することができ、結果として高品質なサービスを提供できる機会が高まりつつあるといえるでしょう。

コンテナ・ベース・オーケストレーション Docker/Kubernetesで作るクラウド時代のシステム基盤

コンテナ・ベース・オーケストレーション Docker/Kubernetesで作るクラウド時代のシステム基盤

サービスのコンテナ化というのはここ数年のWeb業界の一大トレンドというか完全に一般化してきています。
(中略)
ローカル環境でのテストや、CI上でのテストに関してもDockerを使用することが当然の流れのようになってきています。

qiita.com

全てのソフトウェアにコンテナ技術が適しているわけではありませんが、コンテナ技術はもはやデファクトスタンダードという言葉では表現できないものになっています。

まさに「衣・食・住・コンテナ」と言っても過言ではないでしょう。(過言です)

Docker Composeの便利さを体感してみる

まずは実際にDocker Composeで開発環境を構築してその便利さを体感してみてください。
わずか数コマンドで開発環境を構築できてしまいます。

ソースコードGitHubで公開しています。

github.com

まず、ソースコードをクローンします。

$ git clone https://github.com/jumpyoshim/quickstart-compose-and-laravel.git

環境変数を定義しているファイルをコピーし、Laravelのアプリケーションキーを生成します。

$ cp .env.example .env
$ php artisan key:generate

バックグラウンドで複数コンテナを起動します。

$ docker-compose up -d

これで開発環境の構築は完了です。 http://0.0.0.0:8000/ にアクセスしてみましょう。
アプリケーションの起動が確認できると思います。

f:id:gyuuuutan:20181202021716p:plain
ルートパスにアクセスしたときの画面

さらに、起動したコンテナでユニットテストを走らせることもできます。

$ docker-compose exec app bash
# composer test

Docker Composeによる開発環境構築

Dockerfileの作成

それではDocker Composeの開発環境構築の手順を紹介します。
まずはLaravelアプリケーションのDockerfileの作成です。

Docker は Dockerfile から命令を読み込み、自動的にイメージを構築できます。 Dockerfile はテキスト形式のドキュメントであり、コマンドライン上でイメージを作り上げる命令を全て記述します。ユーザは docker build を使い、複数のコマンド行の命令を順次実行し、イメージを自動構築します。

参考:http://docs.docker.jp/engine/reference/builder.html

FROM php:7-fpm

RUN set -x \
    && apt update \
    && apt install -y --no-install-recommends \
      git \
      wget \
    && docker-php-ext-install \
      pdo_mysql \
      mysqli \
      mbstring

ENV COMPOSER_ALLOW_SUPERUSER 1
RUN curl -sS https://getcomposer.org/installer | php \
    && mv composer.phar /usr/local/bin/composer

ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz

RUN mkdir /app
WORKDIR /app
ADD . /app/
  • FROM php:7-fpm:ベースイメージを指定します。DockerHubで公開されている公式イメージや、は独自のコンテナレジストリのイメージを指定します。セキュリティの観点から個人で公開しているイメージを指定するのはあまり望ましくないでしょう。
  • RUN set -x \ ...:必要なパッケージ群をインストールします。
  • RUN curl -sS ...Composerをインストールします。
  • RUN wget ...dockerizeをインストールします。dockerizeを利用することでコンテナの起動順を担保します。
  • RUN mkdir /appapp ディレクトリを作成します。
  • WORKDIR /app:ワーキングディレクトリを指定します。
  • ADD . /app/:カレントディレクトリのソースコード等を app ディレクトリにコピーします。

docker-compose.ymlの作成

次に docker-compose.yml を作成します。これは複数コンテナの管理を定義するファイルです。

Compose とは、複数のコンテナを使う Docker アプリケーションを、定義・実行するツールです。Compose はアプリケーションのサービスの設定に、Compose ファイルを使います。そして、コマンドを1つ実行するだけで、設定した全てのサービスを作成・起動します。

参考:http://docs.docker.jp/compose/overview.html

version: '3'

services:
  db:
    image: mysql:5
    environment:
      MYSQL_DATABASE: tasklist
      MYSQL_ROOT_PASSWORD: secrets
  app:
    build: .
    command: >
      bash -c "dockerize -wait tcp://db:3306
      && composer install --no-plugins --no-scripts
      && php artisan migrate
      && php artisan serve --host=0.0.0.0 --port=8000"
    volumes:
      - .:/app
    ports:
      - "8000:8000"
    env_file:
      - .env
    depends_on:
      - db
  redis:
    image: redis:5

services に開発環境を構成するコンテナを定義します。
今回は db, app, redis の3つのコンテナを定義します。
Nginxなどのコンテナを定義することでより本番環境に近い環境を構築することも可能です。

  • db:データベース名やパスワードを設定します。
  • app:先ほど作成したDockerfileを元にコンテナを起動します。Composerでライブラリのインストール、マイグレーション、アプリケーションの起動を追加で行います。
  • redis:特にイメージのカスタマイズが必要なければイメージの選択だけでもコンテナを起動できます。

おわりに

Docker Composeを利用することでお手軽に開発環境を構築できたかと思います。
Docker Composeを利用すれば以下のメリットを享受できます。

  • 複数のコンテナをコマンド1つで起動・停止が可能
  • コマンド実行時にコンテナ間の依存関係を意識する必要がない
  • コンテナの構成情報はCI/CDにもそのまま利用できる

Dockerは開発環境の構築だけではなく、本番環境のデプロイに使用すると価値が出てくる。開発時の環境分離だけに使用するとオーバーヘッドも大きく、本番環境と一致しないという欠点を抱えてしまう

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)

当たり前ですが、開発環境は本番環境に合わせて構築するべきです。

例えば、Amazon ECS + ECS CLIで本番環境を構築する場合はDocker Composeによる開発環境構築はとても有効的だと思います。ECS CLIはdocker-compose.ymlの構成情報をもとにタスク定義を行い、タスクを実行するためです。

同じように、本番環境の構築にKubernetesを利用する場合はMinikubeが有効的だと思います。

とっても便利なDocker Composeですが、銀の弾丸ではありません。ソフトウェアにとって最適なインフラ構成、そして本番環境に最適な開発環境の構築ができるような力を身につけていきたいですね。