🌟

GoのアプリをEKS(Fargate)にデプロイしてみた話

2022/05/09に公開

Goで作ったアプリケーションを、公式ドキュメントに沿ってEKS(Fargate)にデプロイしようとしたところ、少し詰まったので記録を残しておきます。

この記事では、アプリをデプロイしてインターネット経由でアクセス可能にするところまでの手順を紹介します。

参考にした公式ドキュメントは以下です。

クラスターの作成
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/getting-started-eksctl.html

アプリのデプロイ
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/sample-deployment.html

Load Balancer Controllerのインストール
https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-alb-ingress-controller-fargate/

実際の手順

コンテナで動くアプリを構築する

Goのアプリケーションをコンテナで動かせるように構築しました。フレームワークはginを使っていて、GET /で「Server Running!!」というメッセージを返却、POST /でAmazon Rekognitionのテキスト抽出の結果を返却する機能を実装しています。後者の機能については、別途記事を書きます。

https://github.com/hisami/go-eks-sample

Dockerイメージはこちら
https://hub.docker.com/repository/docker/hisamitsu/rekognition-go

クラスターを作成する

以下コマンドでクラスターを作成します。

eksctl create cluster --name <CRUSTER_NAME> --region ap-northeast-1 --fargate

前提として、eksctlのインストールが必要です。
手順はこちら
https://catalog.us-east-1.prod.workshops.aws/workshops/f5abb693-2d87-43b5-a439-77454f28e2e7/ja-JP/020-create-cluster/10-install-eksctl

自分の場合は、eksctlをかなり前にインストールしていたのでバージョンが古くなっており、クラスター作成時に以下エラーが出ました(最新は0.95.0なのに対し、0.35.0がインストールされていた)。

Resource handler returned message: "unsupported Kubernetes version

上にリンクを記載した手順でeksctlの最新版をインストールすることで解決しました。

名前空間を作成する

k8sクラスターの中がごちゃごちゃになるのはいやなので、アプリケーション用の名前空間を作成します。

kubectl create namespace eks-sample-app

Fargateプロファイルを変更する

Fargateにデプロイする場合、ここが重要です!
この手順を忘れた結果、Podがpendingの状態となり、起動できませんでした。
散々悩んだ結果、ドキュメントを改めて読んでみると、以下の記載が、、(前提じゃなくて、手順の中に組み込んで記載してくれていると嬉しかった)

どうやら、Fargateプロファイルなるものに対して、上記で作成した名前空間eks-sample-appを含めてあげる必要があるようです。

コンソールから設定しました。

デフォルトのFargateプロファイルに名前空間を追加する方法がわからなかったので、新規に作成してデフォルトのものは削除しました。
デフォルトのプロファイルでは、default、kube-systemの名前空間のみが含まれていたので、そこにeks-sample-appを追加した形です。

マニフェストファイルを作る(Deployment)

公式ドキュメントを参考に、Deploymentのマニフェストファイルを作成します。
containersのimageの箇所は、自分で作ったコンテナイメージに変更しています。また、affinityやnodeSelectorの箇所は以下の例では記載していますが、記載しなくても動きました。

eks-sample-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: eks-sample-deployment
  namespace: eks-sample-app
  labels:
    app: rekognition-go
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rekognition-go
  template:
    metadata:
      labels:
        app: rekognition-go
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: kubernetes.io/arch
                    operator: In
                    values:
                      - amd64
                      - arm64
      containers:
        - name: rekognition-go
          image: hisamitsu/rekognition-go:latest
          ports:
            - name: http
              containerPort: 8080
          imagePullPolicy: IfNotPresent
      nodeSelector:
        kubernetes.io/os: linux

以下コマンドで、デプロイします。

kubectl apply -f infra/k8s/eks-sample-deployment.yaml

Podが意図通り作成されていることを確認。

kubectl get pod -n eks-sample-app

結果(Runningになっていたら成功です)

NAME                                     READY   STATUS    RESTARTS   AGE
eks-sample-deployment-795d4bd6c4-rdjc5   1/1     Running   0          5h38m

マニフェストファイルを作る(Service)

続いて、Serviceのマニフェストファイルを作成します。
targetPortには、Pod側で待ち受けているポート(8080)を指定します。

eks-sample-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: eks-sample-service
  namespace: eks-sample-app
  labels:
    app: rekognition-go
spec:
  selector:
    app: rekognition-go
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

これも、デプロイします。

kubectl apply -f infra/k8s/eks-sample-service.yaml

デプロイしたアプリケーションにアクセス(できない、、、)

以上で、デプロイが完了したので、インターネット経由でアプリケーションにアクセスしてみましょう。公式ドキュメントでは、Podの中に入り込んで、そこからcurlコマンドを打っていますが、できればインターネット経由でアクセスしたいですよね。
以下2通りの方針を考えました。

  • Serviceにtype: NodePortを設定してクラスタ外からアクセスできるようにして、FargateのパブリックIPからアクセスする
  • Serviceにtype: LoadBalancerを設定して、ロードバランサー経由でアクセスする

実は、Fargateの場合は両方とも実現できないんです(Fargateでない場合は、できた記憶がある)。というのも、Fargateには以下の制約があります。

  • FargateにパブリックIPアドレスを割り当てることはできない → 1つ目の方針がNGな理由
    • Podはプライベートサブネットに配置して、ロードバランサーなどを経由する必要があります。
  • LoadBalancerのCLB/NLBは非対応 → 2つ目の方針がNGな理由
    • k8sのServiceでLoadBalancerを設定すると、デフォルトではCLB(Classic Load Balancer)が作成されますが、Fargateではこれに対応していません。

以上の理由から、Ingress + ALBを経由した構成が必要となります。
以降、その手順を記載します。

ALB Controllerを作成する

ALB Ingressを使うためには、事前にALB Controllerを設定する必要があります。

以下の手順の「Amazon EKS クラスター、サービスアカウントポリシー、RBAC ポリシーの作成」、「Helm を使用して AWS Load Balancer Controller をインストール」を実行します(ここは何もアレンジしていないので、少し雑)。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-alb-ingress-controller-fargate/

マニフェストファイルを作る(Ingress)

あとは、ALB Ingressのマニフェストファイルを作って、デプロイするだけです。
注意点としては、Fargateの場合は、alb.ingress.kubernetes.io/target-typeipにする必要がある点です。

eks-sample-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: eks-sample-ingress
  namespace: eks-sample-app
  labels:
    app: rekognition-go
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  rules:
    - http:
        paths:
          - path: "/*"
            backend:
              serviceName: eks-sample-service
              servicePort: 80

デプロイします。

kubectl apply -f infra/k8s/eks-sample-ingress.yaml

デプロイしたアプリケーションにアクセス

デプロイが成功していれば、ALBができているはずなので、画面からDNS名を取得してアクセスします。ブラウザに以下が表示されていれば成功です。

さいごに

この記事では、アプリをデプロイしてインターネット経由でアクセス可能にするところまでの手順を紹介しました。
k8sのような、新しい技術に触れられるのは楽しいですが、ALBを作るだけでも事前設定(ALB Controllerの作成)が必要だったりして、本番で使うなら、ECSの方が楽で良いかなーというのが感想。

今回作成したGoのアプリのPOST(Amazon Rekognitionのテキスト抽出の結果を返却する機能)を叩くには、Rekognitionの権限を持つIAMロールをPodに付与する必要があるので、その内容については別記事で紹介したいと思います。

Discussion