SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

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

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

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

その他Production環境で利用したい保守作業

 その他Production環境で利用したい保守作業として、2つのスクリプトファイルを例として追加しました。

Console

 サービスを運用していく上では必ず、サーバーでの調査作業やアドホックな作業が必要になると思います。Kubernetesにも作業できる仕組みはありますが、どうしても手順が複雑になるため、script/k8s-consoleというファイルを作成し、コマンド一発で利用できるようにしています。

 利用するコンテナイメージはプロジェクトによってさまざまな要件があるものの、大きく分けて2つあると思います。

  • Productionで動いているPodと同じコンテナイメージで調査
  • ブランチ開発中のコンテナイメージで調査

 この2つを同時に満たすため、基本的にlatestタグのイメージを利用します。ブランチの指定があった場合は、そのブランチの最新のコミット番号から利用できるようにしています。

 実行するシェルスクリプトは以下の通りです。

#!/usr/bin/env bash

set -eu
set -o pipefail

KUBE=kubectl

NAME="$(awk '{ print tolower($0) }' <<< "$USER")"
NAME="${NAME}-$(date '+%Y%m%d%H%M%s')"

TAG="latest"
if [ $# -eq 1 ]; then
  COMMIT=$(git rev-parse $1)
  TAG=${COMMIT::7}
fi

TEMPFILE="$(mktemp)"

COLUMNS=`tput cols`
LINES=`tput lines`
TERM=xterm

create_pod() {
  cat kubernetes/run.yaml | sed "s,USER,$NAME,g" | sed "s/latest/$TAG/g" > "$TEMPFILE"
  $KUBE create -f "$TEMPFILE"
}

destroy_pod() {
  $KUBE delete po $NAME -n myapp
}

wait_for_up() {
  while true ; do
    sleep 2
    echo "Waiting for the container to be up and running..."
    $KUBE get po $NAME -n myapp | grep Running > /dev/null 2>&1 && break
  done
}

enter_to_pod() {
  $KUBE exec -it $NAME env COLUMNS=$COLUMNS LINES=$LINES TERM=$TERM -n myapp /bin/bash
}

at_exit() {
  rm "$TEMPFILE"
  destroy_pod
}

main() {
  create_pod

  trap at_exit EXIT
  trap "trap - EXIT; at_exit; exit -1" SIGHUP SIGINT SIGTERM

  wait_for_up
  enter_to_pod
}
main

 コンソールを立ち上げるためのマニフェストファイルです。

apiVersion: v1
kind: Pod
metadata:
  name: USER
  namespace: myapp
spec:
  containers:
  - command:
    - bash
    image: quay.io/koudaiii/myapp:latest
    imagePullPolicy: Always
    name: USER
    stdin: true
    stdinOnce: true
    terminationMessagePath: /dev/termination-log
    tty: true
    env:
      - name: "RAILS_SERVE_STATIC_FILES"
        value: "enabled"
      - name: "RAILS_LOG_TO_STDOUT"
        value: "enabled"
    envFrom:
    - secretRef:
        name: dotenv
  restartPolicy: Never

 コンソールで中に入って確認します。

$ ./script/k8s-console
pod "koudaiii-2017091114111505106709" created
Waiting for the container to be up and running...
Waiting for the container to be up and running...
bash-4.3# rails c
Loading production environment (Rails 5.1.3)
irb(main):001:0>

 Productionの環境変数が入った状態でコンテナが起動し、bashが利用できます。Podの一覧を見るとPCのアカウント名が入った専用のPodが作られています。

$ kubectl get po -n myapp
NAME                              READY     STATUS    RESTARTS   AGE
koudaiii-2017091114111505106709   1/1       Running   0          9s
myapp-2136627869-62hdf            2/2       Running   0          4h
myapp-2136627869-k2l7b            2/2       Running   0          4h
myapp-2136627869-nb49l            2/2       Running   0          4h
myapp-2136627869-r6961            2/2       Running   0          4h
postgres-2312165663-5vzcs         1/1       Running   0          4h

 作業が終わったらexitで閉じて、Podも削除します。

bash-4.3# rails c
Loading production environment (Rails 5.1.3)
irb(main):001:0> exit
bash-4.3# exit
exit
pod "koudaiii-2017091114111505106709" deleted

Migrate

 サービスを運用していくと必ずスキーマの変更が出てくるタイミングがあります。KubernetesではJobという形でone-shotで実行できます。しかし、rails db:migrateのように任意で複数回立たれるものについては、その度にJobのマニフェストファイルを作成するのが手間です。そのためこちらも、script/k8s-migrateというファイルを作成し、コマンド一発で利用できるようにしています。

 ここでも、利用するコンテナイメージはプロジェクトによってさまざまな要件があるものの、大きく分けて2つあると思います。

  • 現在Productionで動いているコンテナのイメージでMigrateを実行したい
  • ブランチ作業中でマージ直前に作成したコンテナのイメージで実行したい

 この2つを同時に満たすため、自分の作業場所から最新のコミット番号を取得し利用できるようにしています。

 実行するシェルスクリプトは以下の通りです。

#!/usr/bin/env bash

set -eu
set -o pipefail

LOWUSER=$(tr '[A-Z]' '[a-z]' <<<$USER)
NAME=$LOWUSER-$(date '+%Y%m%d%H%M%s')
COMMIT=$(git rev-parse HEAD)
TAG=${COMMIT::7}

cat kubernetes/migration.yaml | sed "s/\[REPLACE_WITH_DATETIME\]/$NAME/g" | sed "s/\[REPLACE_WITH_TAG\]/$TAG/g"> /tmp/$NAME.yaml
kubectl create -f /tmp/$NAME.yaml

 こちらはMigrateを実行するためのマニフェストファイルです。

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migrate-[REPLACE_WITH_DATETIME]
  namespace: myapp
  labels:
    name: db-migrate-[REPLACE_WITH_DATETIME]
    role: batch
spec:
  template:
    metadata:
      name: db-migrate-[REPLACE_WITH_DATETIME]
      labels:
        name: db-migrate-[REPLACE_WITH_DATETIME]
        role: batch
    spec:
      restartPolicy: Never
      containers:
      - name: db-migrate-[REPLACE_WITH_DATETIME]
        image: quay.io/koudaiii/myapp:[REPLACE_WITH_TAG]
        command: ["bundle", "exec", "rails", "db:migrate"]
        env:
         - name: "RAILS_SERVE_STATIC_FILES"
           value: "enabled"
         - name: "RAILS_LOG_TO_STDOUT"
           value: "enabled"
        envFrom:
        - secretRef:
            name: dotenv
Migrateを実行したログ
$ ./script/k8s-migrate
job "db-migrate-koudaiii-2017091116251505114716" created


$ kubectl get po -a -n myapp
NAME                                               READY     STATUS      RESTARTS   AGE
db-migrate-koudaiii-2017091116251505114716-707pt   0/1       Completed   0          21m
myapp-2136627869-62hdf                             2/2       Running     0          6h
myapp-2136627869-k2l7b                             2/2       Running     0          6h
myapp-2136627869-nb49l                             2/2       Running     0          6h
myapp-2136627869-r6961                             2/2       Running     0          6h
postgres-2312165663-5vzcs                          1/1       Running     0          6h


$ kubectl logs db-migrate-koudaiii-2017091116251505114716-707pt -n myapp
D, [2017-09-11T16:29:01.229152 #1] DEBUG -- :    (31.6ms)  CREATE TABLE "schema_migrations" ("version" character varying NOT NULL PRIMARY KEY)
D, [2017-09-11T16:29:01.237281 #1] DEBUG -- :    (5.6ms)  CREATE TABLE "ar_internal_metadata" ("key" character varying NOT NULL PRIMARY KEY, "value" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
D, [2017-09-11T16:29:01.238534 #1] DEBUG -- :    (0.5ms)  SELECT pg_try_advisory_lock(4671394259554759170)
D, [2017-09-11T16:29:01.259086 #1] DEBUG -- :    (0.6ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
D, [2017-09-11T16:29:01.265267 #1] DEBUG -- :   ActiveRecord::InternalMetadata Load (0.3ms)  SELECT  "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2  [["key", "environment"], ["LIMIT", 1]]
D, [2017-09-11T16:29:01.272660 #1] DEBUG -- :    (0.4ms)  BEGIN
D, [2017-09-11T16:29:01.274580 #1] DEBUG -- :   SQL (0.5ms)  INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "key"  [["key", "environment"], ["value", "production"], ["created_at", "2017-09-11 07:29:01.273098"], ["updated_at", "2017-09-11 07:29:01.273098"]]
D, [2017-09-11T16:29:01.275963 #1] DEBUG -- :    (0.9ms)  COMMIT
D, [2017-09-11T16:29:01.276508 #1] DEBUG -- :    (0.3ms)  SELECT pg_advisory_unlock(4671394259554759170)

まとめ

 今回はサンプルとしてRuby on Railsを利用しましたが、どの言語でも今回紹介したファイル群の考え方が活用できるでしょう。

 マイクロサービス化する際には、こうしたルールを作ることが重要です。バラバラになりやすい運用作業について、可能な限り統制を保つことができます。

 また、今回のような事例が社内で増えれば、それをそのまま参考にして組織として横展開がしやすくなります。

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

 この他にも、ちょっとした仕組みで効率化できる部分はたくさんあると思います。

 ここからさらに発展して、分散管理になりがちなスクリプトファイルもできる限りツールなどで中央管理的に扱えるようになれば、さらに統制が効きやすくなってエンジニアの生産性向上に役立ちます。私たちは日々こういったツールの開発をしています。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
開発現場からお届け! Wantedlyのプロダクトを育て、支える技術連載記事一覧

もっと読む

この記事の著者

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

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

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/10428 2017/09/29 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー