Kubernetes PodにてRailsアプリを起動
目的
Railsの実行環境・デプロイ環境を整えてみたい。
暇なので、KubernetesやDockerを勉強。
やりたいこと
- Railsを動かすコンテナイメージを自力で作成する。
- KubernetesのManifestを作成して、Railsを動かすPodを立ててみる。
- Railsのコードに更新が入ると、Pod上のスクリプトに自動で更新される。
今回やりたいこと
今回は第二章。
KubernetesのManifestを作成して、Railsを動かすPodを立ててみる。
Kubernetesに関する個人的質問の数々
Kubernetesを勉強しようとしたが、ガチで意味不明。
ドキュメントを読んでみて、調べてみる。
Q) そもそもManifestを作成する意味は?
Kubernetesオブジェクトは「理想の記録」であり、オブジェクトの通りに、クラスタの状態を実現したい。オブジェクトを作成するためには、開発者の「理想状態」を定義する必要がある。
それをYML manifestに記載する。
Q) Podとは?
Podは、1つ以上のアプリケーションコンテナ(Dockerなど)のグループとそれらのコンテナの共有リソースを表すKubernetesの抽象概念
- 複数のコンテナをまとめた単位。単位は大きい順から、クラスタ > ノード > Pod > コンテナ。
- 各Pod内で実行されるプロセスは独立している。
- 同一Pod内では、ネットワーク・ストレージを共有できる。
- 同一ネットワーク空間に存在するため、IPとポートは重複できない。
Q) ノードとは?
Podは常にノード上で動作します。ノードは複数のPodを持つことができる。レジストリからコンテナイメージを取得し、コンテナを解凍し、アプリケーションを実行することを担当する。
Q) Kuberletとは?
Kubernetesマスターとノード間の通信を担当するプロセス。レジストリからコンテナイメージを取得し、コンテナを解凍し、アプリケーションを実行することを担当する、Dockerのようなコンテナランタイム。
Manifestを作成してみる
NameSpace
同一物理マシン上で仮想クラスタの運用ができる。名前空間とは仮想クラスタのことを指す。
一度作成してしまえばおk。
apiVersion: v1
kind: Namespace
metadata:
name: MyApp
Deployment
各Podの状態を更新したいときに有効らしい。デプロイやロールバックの方法とか。
Deploymentコントローラーは指定された頻度で現在の状態を理想的な状態に変更します。Deploymentを定義することによって、新しいReplicaSetを作成したり、既存のDeploymentを削除して新しいDeploymentで全てのリソースを適用できます。
ReplicaSet
Kubernetesオブジェクトは、クラスタの理想状態であるイメージだが、「Podが複数存在するとき」にどんな各Podが挙動するかを定義しておきたい。この場合に「ReplicaSet」を活用する。
apiVersion: apps/v1 # どのバージョンのKubernetesAPIを利用してオブジェクトを作成するか
kind: Deployment # どの種類のオブジェクトを作成するか(例:Service など)
metadata: # オブジェクトを一意に特定するための情報(名前空間)
name: web-deploy # オブジェクトの名前
labels: # オブジェクトを選択するために使うラベル
app: MyApp # 任意のキー・
spec:
replicas: 3 # 3つのレプリカPodを作成
selector:
matchLabels: # spec.template.metadata.labelsと一致させる必要がある
app: web # タグ"web"のPodのみデプロイを適用
template: # 実際に適用するデプロイ方法テンプレート
metadata:
labels:
app: MyApp # ReplicaSetとPodに付けられるラベル
spec:
restartPolicy: OnFailure # コンテナの再起動ポリシー(default: Always)
containers:
- name: MyApp # 適当に設定
image: myapp:[tagneme] # 自作のコンテナイメージを設定
ports:
- containerPort: 3000 # コンテナ開放ポートは3000だったことを確認済
Service
要するに、Podへの接続したいときに必要な情報を保持し、接続を解決してくれる。
ロードバランサの設定やDNS設定などもやってくれる。
- 各NodeのIPにて、静的なポート(NodePort)上でServiceを公開(NodePort)
- ラウドプロバイダーのロードバランサーを使用して、Serviceを外部に公開(LoadBalancer)
KubernetesにおけるServiceとは、 クラスター内で1つ以上のPodとして実行されているネットワークアプリケーションを公開する。KubernetesはPodにそれぞれのIPアドレス割り振りや、Podのセットに対する単一のDNS名を提供したり、それらのPodのセットに対する負荷分散が可能です。
apiVersion: v1
kind: Service
metadata:
name: web-app
spec:
type: NodePort
selector: # リクエストをタグに一致するPodに送信
app: MyApp # タグ"MyApp"のPod
ports:
- nodePort: 3000 # 静的なポート(NodePort)上でServiceを公開
port: 80
protocol: TCP
KubectlでPodを作成
Kubectlとは?
kube-apiserverにHTTPリクエストを送るコマンドツール。
kube-apiserverとは?
Kubernetes APIを外部に提供するKubernetesコントロールプレーンのコンポーネント。
ワーカーノードを管理するコントロールプレーンにのみ存在している。
minikubeとは?
ローカル環境でKubernetesを簡単に実行するためのツール。
- クラスタの立ち上げ
- クラスタの管理
- クラスタの削除
必要なツールの準備
# Dockerコンテナイメージが作成されていることを確認
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp 1.0.0 810d5e926918 44 hours ago 991MB
# Asdfでkubectl,minikubeをインストール
$ asdf plugin-add kubectl
$ asdf install kubectl 1.27.0
$ asdf global kubectl 1.27.0
$ asdf plugin-add minikube
$ asdf install minikube 1.30.1
$ asdf global minikube 1.30.1
# minikubeダッシュボードを開いて、クラスタの情報を可視化
$ minikube start
[色々出力される]
$ minikube dashboard
[色々出力される]
デプロイ!
$ kubectl create namespace myapp
namespace/myapp created
# deployマニフェストを実行
$ kubectl apply -f deployment.yaml --namespace=myapp
deployment.apps/webdeploy created
デプロイできていなかった・・・。
原因1 イメージの名前が悪い説
Failed to apply default image tag "MyApp:1.0.0": couldn't parse image reference "MyApp:1.0.0": invalid reference format: repository name must be lowercase
-> タグ名をアルファベット小文字にして解決
原因2:コンテナに指定されたイメージを取得またはプルできない説
Error: ImagePullBackOff
# 解決策 -> dokcerhubにプッシュしたイメージを取得
$ docker login
$ docker push [dockerhub username]/myapp:[tagneme]
$ kubectl apply -f deployment.yaml --namespace=myapp
再度デプロイしたが失敗。でも状態は進んだ。
原因3:KubernetesがDockerhubにアクセスできていなかった説
調べてみると、CrashLoopBackOff。なんでやねーん!
Podで発生している再起動ループらしい。それが徐々に実行期限をすぎて、エラーを発出。
$ kubectl get pods --namespace myapp
NAME READY STATUS RESTARTS AGE
webdeploy-6bf4975977-5cvm6 0/1 CrashLoopBackOff 6 (2m44s ago) 11m
webdeploy-6bf4975977-s9hw5 0/1 CrashLoopBackOff 6 (2m46s ago) 11m
webdeploy-6bf4975977-w85v5 0/1 CrashLoopBackOff 6 (2m34s ago) 11m
# デプロイ用アプリのディレクトリをmanifestを配置したディレクトリから参照できるようにした
# -> これダメ!親ディレクトリをコピーできない。(後述)
$ ln -s ../app ./app
# KubernetesがDockerhubにアクセスできていなかったかも?
$ kubectl create secret docker-registry NAME --docker-username=user --docker-password=password --docker-email=email
これでなんとかPodは作成されたっぽい。なおデプロイには失敗する模様。
原因4:そもそもアプリのフォルダがコンテナ内にコピーできてない説
$ kubectl describe pods webdeploy-6bf4975977-fxks4
# 色々調査
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webdeploy-6bf4975977-fxks4 0/1 CrashLoopBackOff 8 (21s ago) 16m 10.244.0.10 minikube <none> <none>
# -> Podは作成できている
$ kubectl logs webdeploy-6bf4975977-fxks4
Usage:
rails new APP_PATH [options]
#・・・(省略
# -> なぜかrails new〜コマンドのUsageが出力されていた。
# -> どうやらGemfileやサンプルアプリがコンテナ内にコピーされていなかった。
# -> manifestからコピー対象が親フォルダだと、コピーしてくれない。
# -> manifestディレクトリにsampleAppを配置して解決!
稼働した!
内部公開
# PodのIPを調べる
$ kubectl get ep webdeploy
NAME ENDPOINTS AGE
webdeploy 10.244.0.6:3000 49m
# NodeのIP
$ kubectl get svc webdeploy
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webdeploy NodePort 10.97.209.198 <none> 80:30314/TCP 49m
$ minikube service webdeploy --url -n <namespace>
http://127.0.0.1:54308
イメージとして、Podの外側にNodeがいる感じなので、10.97.209.198:80,30314
->10.244.0.6:3000
へアクセスしているのだろうか。
http://127.0.0.1:54308 にアクセスすると、Podで構築したRailsアプリにアクセスできた。
各Serviceは、ローカルのNode上でポート(ランダムに選ばれたもの)を公開されるっぽい。
Discussion