Google Kubernetes Engine上にコンテナ化した株価アプリをデプロイしてみました。
アプリの構成
アプリの構成は次のようになっています。
- React
役割:フロントエンド
- Express
役割:バックエンド
- Nginx
役割:プロキシサーバ(ReactとExpressのプロキシサーバ)
DeploymentはPod単位で行うのでフロントエンドとバックエンドそれぞれのマニフェストを定義します。フロントエンドはReactとNginxでPodで定義し、バックエンドはExpressとNginxでPodで定義します。Serviceは1つは外部公開できるように種類としては「LoadBalancer Service」を選び、ReactからExpressへのリクエストにはIPはクラスタ内部でトラフィックを流せれば良いので、デフォルト値である「ClusterIP Service」を選びます。
Kubernetsのリソースについて
www.case-k.jp
セットアップ
デプロイする前にクラスタやレジストリへのイメージの登録、マニフェストファイルを定義します。
GKEのクラスタを作成
次の記事の通り事前にGKEのクラスタを作成します。
www.case-k.jp
$ gcloud container clusters create casek --machine-type=n1-standard-1 --num-nodes=3
DockerfileをGoogle Cloud Registoryにpushする
次の記事の通り事前にDockerfileをGoogle Cloud Registoryにpushしておきます。
www.case-k.jp
マニフェストファイルの作成
マニフェストファイルを定義していきます。
Deploymentを定義(React+Nginx)ーreact-app-deployment.yaml
ReactとプロキシサーバであるNginxコンテナのPodを定義します。
apiVersion: apps/v1 kind: Deployment metadata: name: react-app labels: name: react-app spec: replicas: 2 selector: matchLabels: app: react-app template: metadata: labels: app: react-app spec: containers: - name: nginx image: gcr.io/[project id]/nginx-app:tag2 imagePullPolicy: Always ports: - containerPort: 80 env: - name: WORKER_PROCESSES value: "2" - name: WORKER_CONNECTIONS value: "1024" - name: KEEPALIVE_TIMEOUT value: "65" - name: GZIP value: "on" - name: BACKEND_HOST value: "localhost:3000" - name: BACKEND_MAX_FAILS value: "3" - name: BACKEND_FAIL_TIMEOUT value: "10s" - name: SERVER_PORT value: "80" - name: SERVER_NAME value: "localhost" - name: LOG_STDOUT value: "true" - name: react-app image: gcr.io/[project id]/react-app:tag2 imagePullPolicy: Always ports: - containerPort: 3000
アプリのpackage.jsonにはリクエスト先であるExpressとNginxのサービス名を定義しておきます。こちらも環境変数で制御した方が良いため、本番・ステージング・開発環境を分けてデプロイする際に変更します。
"proxy": "http://express-app",
Serviceを定義(React+Nginx)- react-app-service.yaml
Serviceを定義:Serviceの種類としては「LoadBalancer Service」を選択します。「LoadBalancer Service」はGCPやAWSなどのクラウドプラットフォームで提供されているロードバランサーと連携するためのものです。Ingressなしで外部公開が可能となっています。
apiVersion: v1 # リースの種類を定義 kind: Service metadata: name: react-app labels: app: react-app # リソースを定義 spec: # ServiceのターゲットとしたいPodがもつラベルを指定 selector: app: react-app ports: - name: http port: 80 type: LoadBalancer
Deploymentを定義(Express+Nginx)ーexpress-app-deployment.yaml
Deploymentを定義:ExpressとプロキシサーバであるNginxコンテナのPodを定義します。
apiVersion: apps/v1 kind: Deployment metadata: name: express-app labels: name: express-app spec: replicas: 2 selector: matchLabels: app: express-app template: metadata: labels: app: express-app spec: containers: - name: nginx image: gcr.io/[project id]/nginx-app:tag2 imagePullPolicy: Always ports: - containerPort: 80 env: - name: WORKER_PROCESSES value: "2" - name: WORKER_CONNECTIONS value: "1024" - name: KEEPALIVE_TIMEOUT value: "65" - name: GZIP value: "on" - name: BACKEND_HOST value: "localhost:3001" - name: BACKEND_MAX_FAILS value: "3" - name: BACKEND_FAIL_TIMEOUT value: "10s" - name: SERVER_PORT value: "80" - name: SERVER_NAME value: "localhost" - name: LOG_STDOUT value: "true" - name: express-app image: gcr.io/[project id]/express-app:tag2 imagePullPolicy: Always ports: - containerPort: 3001
Serviceを定義(Express+Nginx)- express-app-service.yaml
Serviceを定義:Serviceの種類としてはデフォルト値である「ClusterIP Service」を選択します。「ClusterIP Service」ではKubernetesクラスタ内部IPアドレスにServiceを公開できます。今回は同一クラスタの別のPod(React+ Nginx)からリクエストを受け取るために使います。
apiVersion: v1 # リースの種類を定義 kind: Service metadata: name: express-app labels: app: express-app # リソースを定義 spec: # spec.selector:ServiceのターゲットとしたいPodがもつラベルを設定 selector: app: express-app ports: - name: http port: 80
デプロイ
各マニフェストファイルができたので、アプリのイメージをクラスタにデプロイしてみます。
$ kubectl apply -f express-app-deployment.yaml
deployment.apps/express-app created
※ docker-compose時と異なり、環境変数は「""」で囲まないと上のエラーが出てしまいます。
$ kubectl apply -f express-app-deployment.yaml Error from server (BadRequest): error when creating "express-app-deployment.yaml": Deployment in version "v1" cannot be handled as a Deployment: v1.Deployment.Spec: v1.DeploymentSpec.Template: v1.PodTemplateSpec.Spec: v1.PodSpec.Containers: []v1.Container: v1.Container.Env: []v1.EnvVar: v1.EnvVar.v1.EnvVar.Value: ReadString: expects " or n, but found 2, error found in #10 byte of ...|,"value":2},{"name":|..., bigger context ...|ers":[{"env":[{"name":"WORKER_PROCESSES","value":2},{"name":"WORKER_CONNECTIONS","value":1024},{"nam|...
デプロイが完了したら各リソースの状態を確認してみます。Serviceの種類として「LoadBalancer」を選んだので外部IPアドレスが確認できます。
$ kubectl get pod,node,svc NAME READY STATUS RESTARTS AGE pod/express-app-8c57cddff-79549 2/2 Running 0 70m pod/express-app-8c57cddff-kpcxv 2/2 Running 0 69m pod/react-app-5654c6d5c6-h7wp9 2/2 Running 0 69m pod/react-app-5654c6d5c6-tp72m 2/2 Running 0 69m NAME STATUS ROLES AGE VERSION node/gke-casek-default-pool-2440cdf7-5rkw Ready <none> 5h46m v1.14.10-gke.17 node/gke-casek-default-pool-2440cdf7-99f8 Ready <none> 5h46m v1.14.10-gke.17 node/gke-casek-default-pool-2440cdf7-ww5v Ready <none> 5h46m v1.14.10-gke.17 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/express-app ClusterIP 10.51.246.54 <none> 80/TCP 69m service/kubernetes ClusterIP 10.51.240.1 <none> 443/TCP 5h47m service/nginx-express ClusterIP 10.51.251.211 <none> 80/TCP 125m service/react-app LoadBalancer 10.51.247.154 34.85.51.78 80:31221/TCP 121m
大丈夫そうです。
※ エラーがあった場合はPod内のコンテナのログを確認します。
$ kubectl logs express-app-7668659dd5-dtkn2 -c nginx 2020/03/03 03:38:39 [emerg] 1#1: host not found in upstream "express-app:3001" in /etc/nginx/conf.d/upstream.conf:2 nginx: [emerg] host not found in upstream "express-app:3001" in /etc/nginx/conf.d/upstream.conf:2
※ Podに入りたい場合は次のようにして入ることができます。
$ kubectl exec -it [Pod name] bash
リソースの削除
デプロイできたのでリソースを削除しておきます。
$ kubectl delete -f express-app-deployment.yaml $ kubectl delete -f express-app-service.yaml $ kubectl delete -f react-app-deployment.yaml $ kubectl delete -f react-app-service.yaml
Google Kubernetes Engineに株価アプリのデプロまでできたので、次はCloud Soure Repository と Cloud Buildを使ってビルドを自動化していければと思います。