Amazon EKSを使ってwebアプリをデプロイした話

つい最近Amazon EKSを使用してwebアプリをデプロイしたのでその時に学んだこと、詰まったところを記録として残します。

Amazon EKSとは

awsの公式ドキュメントによると、EKSは下記のように説明されています。

Amazon Elastic Kubernetes Service (Amazon EKS) は、独自の Kubernetes コントロールプレーンまたはノードをインストール、操作、および維持することなく、AWS で Kubernetes を実行するために使用できるマネージドサービスです。

https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/what-is-eks.html

この言葉が全てなのですが、簡単に言い換えると、AWS上でkubernetesを用いたインフラ構築をする際に便利な機能が詰まったAWSリソースということです。

Amazon EKSを触る前の事前準備

EKSを触る前の私のレベルですが、「VPCって何それ美味しいの?」「dockerなんて触ったことがない」というくらいインフラの知識皆無の状態でした。

そんな私がEKSを最初から触るなんてハードルが高すぎたので、まずはUdemyで教材を2つほど購入してdockerとkubenetesの基本を学びました。

参考までにその時に購入したUdemyの動画教材のリンクを下記に置いておきます。

インフラ構成図

今回はチームで開発したサンプルアプリケーションをEKSを用いたインフラ基盤の上にデプロイしたので、(予算の都合上)完全に本番を意識した構成にはできませんでしたが、最終的なインフラ構成全体像は下記のようになっています。

aws全体像
Amazon EKS 内部の構成

EKSを使ってアプリケーションを動かす手段は2つあります。

  • EC2インスタンスを立ててその中にpodを立ててアプリケーションを動かす方法
  • AWS Fargateのプロファイルを作成しアプリケーションを動かす方法

上の図はEC2インスタンスを立てて動かした場合の図ですが、AWS Fargateを使った構成の方が楽だった印象です。

今回はこの2つの手段でアプリケーションを動かしたのですが、EC2を使った構成に関してはAWS CDKを使ってAWSリソースを作成する際に権限周りで詰まることが多かったのでAWS Fargateでの構成に移行しました。

インフラ構成図の説明

全体構成図

EKSはpodの状態を監視し、最適な状態に保つ司令塔のような役割を持つ「EKSクラスター」と、アプリケーションをデプロイするインスタンスやプロファイルのことを指す「ノードグループ」があります。

AZは2つに分けてEKSのノードグループを作成する領域はプライベートサブネットにしました。

ここで注意したいのはいくら予算がなくてもAZを1つだけしか指定しなかったり、NATゲートウェイとプライベートサブネットを繋いでいない場合、EKSクラスターとノードグループ作成時エラーになってしまうところです。

EKSは基本的に大規模のシステムで運用されることを前提として作られているものなので、ここらへんはしょうがないですね。

EKS内部の構成

EKS内部では最初に通信を受ける部分としてALBを採用しました。

kubenetes用のALBを導入するには、AWS Load Balancer Controller インストールが必要です。

ALB Controllerのインストールが完了したら下記のようにpodが立ち上がります。

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
aws-load-balancer-controller   2/2     2            2           84s

ALBの詳細や詳しい導入手順は公式ドキュメントにまとまっているので詳しくはそちらをご覧ください。

ALB Controllerが準備できたら、kubenetesのingressリソースを利用できるようになるのでそちらもマニフェストファイルを適用して作成します。

ALBからの通信をkubenetesのingressリソースで受け取ります。ingressを作成したらpodが立ち上がることはなく、一つのkubenetesのリソースという扱いになります。

ALBではアプリケーションの負荷分散を実現し、ネットワークトラフィックを負荷分散するにはALBから流された通信をリバースプロキシするkubenetesリソースのserviceで実現します。

複数存在するフロントエンド用のpodへはこのserviceを使って通信の振り分けを実現しています。

そして、フロントエンドからバックエンドへの通信もこのserviceを使っています。

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: *
  name: *
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: backend-app
  replicas: 2
  template:
    metadata:
      labels:
        app.kubernetes.io/name: backend-app
    spec:
      containers:
      - image: **
        imagePullPolicy: Always
        name: backend-app
        ports:
        - name: http
          containerPort: port番号
     // RDSを利用していたので環境変数として渡す
        env:
        - name: DATABASE_URL
          value: {DATABASE_URL} 
---
apiVersion: v1
kind: Service
metadata:
  namespace: *
  name: backend-service
spec:
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
  selector:
    app.kubernetes.io/name: backend-app
---
# フロントエンド
---
apiVersion: v1
kind: Namespace
metadata:
   name: *
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: *
  name: frontend-deployment
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: frontend-app
  replicas: 2
  template:
    metadata:
      labels:
        app.kubernetes.io/name: frontend-app
    spec:
      containers:
      # ここのimageを変える
      - image: {imageURL}
        imagePullPolicy: Always
        name: frontend-app
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: *
  name: *
spec:
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  type: NodePort
  selector:
    app.kubernetes.io/name: frontend-app

トラブルシューティング

EKSを導入する中で詰まった部分と、その解決法を記述していきます。

基本的な解決方法

EKS周りを導入する際のエラーでは公式のトラブルシューティングで大体解決しました。

kubenetesリソースが作成されないなどのエラーの場合、そのpodの詳細情報を見れば大体のエラー原因が書いてあります。

私は序盤にクラスターとノードが結合できないエラーに悩まされましたが、ランブックを使って原因を突き止め対応していくことで解決しました。

原因としては、ノードに下記のようなタグが付与されていないことでした。

kubernetes.io/cluster/my-cluster:owned

namespaceが「Terminating」ステータスで止まる

マニフェストをapply、deleteと繰り返していく中で発生したエラーです。

これも公式の方法がありましたのでその通りに対応しました。

podの中でimageがpullできなかった

NATゲートウェイがプライベートサブネット内にあったため、docker Hubへの通信が行えず、イメージをpullしている中でtimeoutしていたようです。

NATゲートウェイをパブリックサブネットに作成し直すことで正常にpullできました。

podがpenddingになった

t3.microを使っていたため、1インスタンスに4つしかpodを立てることができないことが原因でした。

4つ以上のpodを立てたい場合にはインスタンスタイプをグレードアップするか、インスタンス台数を増やす必要があります。

私はt3.smallにインスタンスタイプを変更することで正常にpodが立ち上がりました。

xUnable to attach or mount volumes: unmounted volumes=[cert], unattached volumes=[***]: timed out waiting for the condition

aws-load-balancer-controllerのマニフェストファイルをapplyしている中で起こったエラーです。

podの中でimageがpullできない状態でapplyしていたので、うまくyamlファイルが読み込まれていないようでした。

それによって必要なリソースがpodにデプロイされず、存在しないファイルをマウントしようとしていたことが原因でした。

imageがpullできない原因を解消し、podを立ち上げ直すことでエラーは解消されました。

まとめ

今回初めてインフラを触ると同時にEKSを使ってアプリをデプロイするところまで経験しました。

その中で、異常系を意識した構成ログなどの可測性を意識することができなかったので次回触る機会があれば、動けばいいインフラの構成ではなく、本番を意識したインフラ構築をしてみたいです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です