Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

オペレーションとルールを統一化し、Kubernetesを使った変化に強いインフラを構築

開発現場からお届け! Wantedlyのプロダクトを育て、支える技術 第2回

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2017/09/29 14:00

 Wantedlyの開発現場で活用されている技術や手法は、日々進化するプロダクトに合わせて柔軟に変化しています。この連載では実際の開発で取り組んでいる技術や手法を、実務に使える形でお伝えしていきます。前回はWantedlyのインフラチームが大切にしている「変化に強いインフラ」づくりについて解説しました。第2回となる今回は、架空のサービスをサンプルとして、Kubernetesの活用方法を具体的に紹介します。

目次

はじめに

 本連載は、WANTEDLY TECH BOOK 2から抜粋し、再編集したものになります。第1回に引き続き、第2回もインフラチームの坂部(@koudaiii)が担当します。前回は「変化に強いインフラはWantedlyにとってなぜ必要か」「これまでどのように変化に強いインフラに取り組んできたのか」といったことを中心に紹介しました。今回は、架空のサービスをサンプルとして、Kubernetesの活用方法を具体的に紹介します。

実現すること

  • Ruby on RailsのサイトをKubernetes上に構築する
  • CI/CD環境を構築する
    • GitHub Flowの開発スタイルを元に、自分が書いたコードをQA環境で確認できる
    • masterにマージしたら、Production環境へデプロイする
  • サーバースペックを簡単に変えられる
  • ライブラリを簡単に変えられる
  • ローカルPC上でMinikubeを使って確認する

 なお、ソースコードは本記事からダウンロードできるサンプルファイルの他、koudaiii/myapp(GitHub)にも置いています。

定義とルール

 変化に強いインフラは、少なくとも以下の2つが実行できている状態と定義します。

  • 継続的にリリースできること
  • サービスの構成を自由に変更できること

 そのために、2つのポイントを抑える必要があると考えました。

  • オペレーションの統一化
  • アプリケーション固有の方言をなくす

 これらの実現方法について解説していきます。

オペレーションの統一化について

 どんなアプリケーションを作る際にも必ずオペレーションが発生します。エンジニアがなるべくコードに集中できるように、オペレーション部分は統一していく必要があります。

 具体的には、以下の2点のオペレーションを統一します。

  • リリースまでのオペレーションを統一化
  • 保守作業によるオペレーションを統一化

リリースまでのオペレーションを統一化(1)

 サンプルのアプリケーションを元に、リリースまでのオペレーションを以下の通り定めました。

  • masterブランチに直接コミット、プッシュを行わない。
  • ブランチを切ってそこで開発を実施し、Pull Requestを送る。
  • git pushする度にテストが行われ、問題があればすぐに把握できる。
  • テストが通ればQA環境にリリースされ、ブラウザで確認する。
  • リリースできるタイミングになったらmasterにマージする。
  • CI上でテストが走り、テストが通ればProductionにリリースする。

 WANTEDLY TECK BOOK 2ではGoを用いて紹介いたしましたが、今回はReact with Webpacker and Ruby on Rails(以下Railsと表記)の構成で進めます。

 実際のフローとなっているコードと実際にリリースする流れです。

deploy:
  skip_cleanup: true
  provider: script
  script: "./script/ci-deploy"
  on:
    all_branches: true
#!/usr/bin/env bash

set -eu
set -o pipefail

if [ "$TRAVIS_BRANCH" == "master" ]; then
  echo "Deploy ${REPO}:${TAG_COMMIT} in production"
  ./kops export kubecfg --name $K8S_PROD_CLUSTER
else
  echo "Deploy ${REPO}:${TAG_COMMIT} in qa"
  ./kops export kubecfg --name $K8S_QA_CLUSTER
fi

 ./kubectl set image deployment/${DEPLOYMENT_NAME} rails=${REPO}:${TAG_COMMIT} --namespace=${NAMESPACE} --record

 Travis CIのビルドログはこちらから確認できます。

Travis CIのビルドログ
Travis CIのビルドログ

 リリースの流れを図にしました。順を追って紹介します。

リリースの流れ
リリースの流れ

CI/CDのセットアップ

 CI/CD環境として、今回はTravis CIを利用しています。

Travis CI
Travis CI
Travis CIのセットアップ
$ travis enable
$ travis init

 次に環境変数の設定を行います。秘密の情報をリポジトリに保存させないように、travis encryptで登録します。

環境変数の設定
$ travis encrypt DOCKER_USERNAME=xxx # docker login するための情報
$ travis encrypt DOCKER_PASSWORD=xxx # docker login するための情報

 実際の設定は以下の通りです。

  - REPO=quay.io/koudaiii/myapp # docker registry の Path
  - DEPLOYMENT_NAME=${REPO##*/} # Path から myapp を抽出
  - NAMESPACE=$DEPLOYMENT_NAME # リポジトリとNAMESPACEを同じにする
  - KUBECTL_VERSION=1.6.6 # kubectl の利用するバージョン
  - KOPS_VERSION=1.6.2 # kops の利用するバージョン(k8s on AWS で利用)
  - K8S_PROD_CLUSTER=prod.cluster.example.com # Production 環境のクラスタ名
  - K8S_QA_CLUSTER=qa.cluster.example.com # QA 環境のクラスタ名
  - TAG_COMMIT=$TRAVIS_COMMIT # git の commit hash 値
  - TAG_COMMIT_SHORT=${TRAVIS_COMMIT::7} # git の commit hash 値の先頭7文字
  - TAG_BRANCH=$(echo $TRAVIS_BRANCH | sed -e 's/[^a-zA-Z0-9-]/_/g') # koudaiii/test => koudaiii_test という普通に dockerのimage tagとして利用できるように置換
  # travis encrypt DOCKER_USERNAME=xxx # docker login するための情報
  - secure: "xxxxxxxxxxxxxxxxxxxxxxxxxx"
  # travis encrypt DOCKER_PASSWORD=xxx # docker login するための情報
  - secure: "xxxxxxxxxxxxxxxxxxxxxxxxxx"
  - YARN_VERSION=1.0.1 # 利用する yarn のバージョン

 before_install:とbefore_script:でアプリケーションのテストを実行するためのセットアップを行っています。

before_install:
  - npm install -g yarn@$YARN_VERSION

before_script:
  - bundle exec rails db:create RAILS_ENV=test

 script:でテストを行い、基本的にテストが通ったらDockerのコンテナイメージを作成し、そのコンテナイメージを保存するためのレジストリ先として、Quayに保存するようにします。

script:
  - ./script/ci-test # アプリケーションのテスト
  - ./script/ci-assets-build # 静的ファイルの生成
  - ./script/ci-build # コンテナイメージ作成
  - ./script/ci-push # コンテナイメージをQuayに保存

 before_deploy:はデプロイするためのツールのインストールを行っています。

before_deploy:
  - curl -SL -o ./kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
  - chmod +x ./kubectl
  - curl -SL -o ./kops "https://github.com/kubernetes/kops/releases/download/${KOPS_VERSION}/kops-linux-amd64"
  - chmod +x ./kops

 deploy:で実際にリリースします。

deploy:
  skip_cleanup: true
  provider: script
  script: "./script/ci-deploy"
  on:
    all_branches: true
  1. script/ci-test⇒アプリケーションのテスト
  2. script/ci-assets-build⇒静的ファイルの生成
  3. script/ci-build⇒コンテナイメージ作成
  4. script/ci-push⇒コンテナイメージをQuayに保存
  5. script/ci-deploy⇒kubectlでリリース作業を行う

 Railsのテストをここで行います。今回はRails標準のテストフレームワークを利用しました。

#!/usr/bin/env bash

set -eu
set -o pipefail

cd "$(dirname $0)/.."

echo "test"
bundle exec rails test

 静的ファイルの生成をCI上で行っています。Amazon S3配信の場合は、静的ファイルを生成時にアップロードしますが、今回は自前でnginxを使って静的ファイルを配信します。そのため静的ファイルの生成後、Railsコンテナに含めてイメージを作成します。コンテナイメージの作成は、次のステップであるscript/ci-buildで行います。

#!/usr/bin/env bash

set -eu
set -o pipefail

if [ $TRAVIS_PULL_REQUEST != "false" ]; then
  echo "Skip build because this build is a pull request."
  exit 0
fi

cd "$(dirname $0)/.."

echo "build assets"
RAILS_ENV=production bundle exec rails assets:precompile

 すべてのgit pushに対して、コンテナにタグを付けてビルドしています。そのタグにはgitのハッシュ値」を入れているため、どのPull Requestの作業で、どのコミットかを特定することができます。

 また、latestタグやブランチ名タグなども合わせて作成し、その都度上書きをしています。これは、そのブランチ名を指定したタグが常に最新になることを担保しています。

#!/usr/bin/env bash

set -eu
set -o pipefail

if [ $TRAVIS_PULL_REQUEST != "false" ]; then
  echo "Skip build because this build is a pull request."
  exit 0
fi

cd "$(dirname $0)/.."

docker build -t $REPO:$TAG_COMMIT .

docker tag $REPO:$TAG_COMMIT $REPO:$TAG_COMMIT_SHORT
docker tag $REPO:$TAG_COMMIT $REPO:$TAG_BRANCH

if [ $TRAVIS_BRANCH = "master" ]; then
  docker tag $REPO:$TAG_COMMIT $REPO:latest
fi

 保存先のレジストリサービス(ここではQuay)にログインし、ビルドしたコンテナのイメージをpushします。

#!/usr/bin/env bash

set -eu
set -o pipefail

if [ $TRAVIS_PULL_REQUEST != "false" ]; then
  echo "Skip build because this build is a pull request."
  exit 0
fi

cd "$(dirname $0)/.."

echo "docker login"
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" quay.io

docker push $REPO:$TAG_COMMIT # git commit hash値
docker push $REPO:$TAG_COMMIT_SHORT # git commit hash値の先頭7文字
docker push $REPO:$TAG_BRANCH # gitのブランチ名

if [ $TRAVIS_BRANCH = "master" ]; then
  docker push $REPO:latest # gitのブランチ名がmasterだった場合は、latestにpush
fi

 masterの場合は、Productionのクラスタに対してリリースします。master以外のブランチの場合は、QAのクラスタに対してリリースするようにしています。リリース方法は、kubectl set imageのコマンドを使い、ローリングデプロイで反映されます。

#!/usr/bin/env bash

set -eu
set -o pipefail

if [ "$TRAVIS_BRANCH" == "master" ]; then
  echo "Deploy ${REPO}:${TAG_COMMIT} in production"
  ./kops export kubecfg --name $K8S_PROD_CLUSTER
else
  echo "Deploy ${REPO}:${TAG_COMMIT} in qa"
  ./kops export kubecfg --name $K8S_QA_CLUSTER
fi

./kubectl set image deployment/${DEPLOYMENT_NAME} rails=${REPO}:${TAG_COMMIT} --namespace=${NAMESPACE} --record

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 坂部 広大(Wantedly, inc.)(サカベ コウダイ)

     Wantedlyの基盤改善および保守運用をしているインフラエンジニア。ツールを作ったり、Web Applicationを作ったり、社内情報システムの整備をしたりしています。  WANTEDLY TECH BOOKを書いています。 Site Twit...

バックナンバー

連載:開発現場からお届け! Wantedlyのプロダクトを育て、支える技術
All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5