Kubernetesでのデプロイ中に’db:migrate’や’db:seed’などのRailsタスクを管理する(翻訳)

こんにちは、hachi8833です。BigBinaryシリーズ、今回はKubernetesとRailsの翻訳記事をお送りします。

morimorihoge: アプリケーションログの集約方法(DockerのSTDOUTから取るのかfluentd等で飛ばすのか)などについて、既存の「普通のRailsアプリ」をKubernetesに持っていってproductionで利用するにはまだまだ知見が必要そうかな、と思いました。

概要

Kubernetesでのデプロイ中に’db:migrate’や’db:seed’などのRailsタスクを管理する(翻訳)

本記事では、poddeploymentといった用語を含むKubernetesの基本部分を理解していることを前提とします。

問題

RailsアプリをKubernetesでデプロイする状況を考えます。その際、Docker imageのビルドプロセスの一部としてasset:precompileタスクを実行したいとします。

最初のデプロイではdb:migratedb:seedなどのタスクを実行し、後のデプロイではdb:migrateタスクだけを実行したいと考えています。

Docker imageのビルド中はデータベースに接続できないため、その間はこれらのタスクを実行できません。

タスクを実行するうまい方法はないものでしょうか。

解決方法

ここでは、myorg/myapp:v0.0.1という名前のDocker imageがあり、そこにRailsアプリのソースコードが含まれているとします。

Docker imageの中には、データベースへの接続設定に必要なdatabase.ymlマニフェストを配置しておきます。

以下の内容を含むKubernetes deploymentが必要です。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    spec:
      containers:
      - image: myorg/myapp:v0.0.1
        name: myapp
        imagePullPolicy: IfNotPresent
        env:
        - name: DB_NAME
          value: myapp
        - name: DB_USERNAME
          value: username
        - name: DB_PASSWORD
          value: password
        - name: DB_HOST
          value: 54.10.10.245
        ports:
        - containerPort: 80
      imagePullSecrets:
        - name: docker_pull_secret

このテンプレートファイルをmyapp-deployment.ymlという名前で保存します。

テンプレートのオプションや環境変数は必要に応じて変更できます。ここで指定した環境変数はRailsアプリから利用できます。

テンプレートをKubernetesに初めて適用する場合は、次のコマンドを実行します。

$ kubectl create -f myapp-deployment.yml

Docker image名や環境変数などを変更した後で同じテンプレートを適用するには、次のコマンドを実行します。

$ kubectl apply -f myapp-deployment.yml

deploymentテンプレートが適用されると、Kubernetes上にアプリを収容するpodが作成されます。

podを表示するには次のコマンドを実行します。

$ kubectl get pods

アプリは、ここではmyapp-4007005961-1st7sという名前のpod上で動作しています。

db:migrateなどのrakeタスクをこのpodに対して実行するには、次のコマンドを使います。

$ kubectl exec myapp-4007005961-1st7s                              \
          -- bash -c                                               \
          'cd ~/myapp && RAILS_ENV=production bin/rake db:migrate'

db:seed rakeタスクも同じ要領で実行できます。

Kubernetesでのdeploymentの自動化フローが既にあれば、これを応用することでrakeタスクを必要に応じて自動実行したり条件に応じて実行したりできます。

Kubernetesのjobを使わない理由

私たちのところでKubernetesのjobでマイグレーションやseedを実行してみたところ、いくつかの問題が生じました。

  1. rakeタスクがゼロ以外のexitコードを返すと、コマンドがexitコードとしてゼロを返すまでKubernetesのJobがpodをspawnし続けます。

  2. 1.の問題を回避するために、jobのステータスやspawnされたすべてのpodのステータスをチェックする余分なカスタムロジックを追加実装しなければなりませんでした。

  3. KubernetesのjobでコマンドのSTDOUTやSTDERRをキャプチャするのは困難でした。

  4. jobが成功しなかった場合は、手動で終了させるなどの余分な手間をかける必要がありました。これをやっておかないと、同じ名前でKubernetesのjobを作成できなくなります。後のdeploymentでは同じ名前でこれを行うことになるので、これは不都合です。

こうした問題が生じたため、Kubernetesのjobを使わずに問題を解決することにしました。

関連記事

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の半分ほど、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れてそれぞれ一部を翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好き。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

BigBinary記事より

ActiveSupport探訪シリーズ