- 開発
READ MORE
こんにちは、hachi8833です。BigBinaryシリーズ、今回はKubernetesとRailsの翻訳記事をお送りします。
morimorihoge: アプリケーションログの集約方法(DockerのSTDOUTから取るのかfluentd等で飛ばすのか)などについて、既存の「普通のRailsアプリ」をKubernetesに持っていってproductionで利用するにはまだまだ知見が必要そうかな、と思いました。
本記事では、podやdeploymentといった用語を含むKubernetesの基本部分を理解していることを前提とします。
RailsアプリをKubernetesでデプロイする状況を考えます。その際、Docker imageのビルドプロセスの一部としてasset:precompile
タスクを実行したいとします。
最初のデプロイではdb:migrate
やdb: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でマイグレーションやseed
を実行してみたところ、いくつかの問題が生じました。
1.の問題を回避するために、jobのステータスやspawnされたすべてのpodのステータスをチェックする余分なカスタムロジックを追加実装しなければなりませんでした。
KubernetesのjobでコマンドのSTDOUTやSTDERRをキャプチャするのは困難でした。
jobが成功しなかった場合は、手動で終了させるなどの余分な手間をかける必要がありました。これをやっておかないと、同じ名前でKubernetesのjobを作成できなくなります。後のdeploymentでは同じ名前でこれを行うことになるので、これは不都合です。
こうした問題が生じたため、Kubernetesのjobを使わずに問題を解決することにしました。