EKS クラスタの CronJob を Fargate 上で実行する方法

巻田 光起
6

こんにちは、スタディサプリ ENGLISH SRE グループの巻田です。
現在リクルートの夏アルバイトとしてこのチームに所属しています。

この記事では EC2 上に構築された EKS クラスタの CronJob のみを Fargate 上で実行できるようにするための方法を紹介します。

はじめに

スタディサプリ ENGLISH ではインフラに EKS を採用しています。Kubernetes には CronJob1)https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/ と呼ばれる機能があり、ジョブを定期的に実行することができます。スタディサプリ ENGLISH ではこの機能を使って様々なバッチ処理を実行しています。

Fargate を導入したい背景

スタディサプリ ENGLISH で使用している EKS は元々 EC2 のみで動いており、 CronJob 実行用に余分にインスタンスを確保し続けるのはコストの面から好ましくありません。
また、インスタンス費用を下げるために Spotinst Ocean を導入しています 2)https://tech.recruit-mp.co.jp/infrastructure/post-19364/ が、スポットインスタンスはいつ Terminate されるか分からず、 CronJob の実行が途中で停止される可能性もあるため CronJob の実行には向きません。
それに対して Fargate3)https://aws.amazon.com/fargate/ とはコンテナ化されたアプリケーションを実行するためのサーバーレスなコンピューティングエンジンで、コンテナが実行されていた時間に対して課金されます。そのため、 CronJob のように一時的にマシンリソースを確保したい場合に適していると考えられます。

一方で Fargate にはサーバーレスのサービスであるがゆえの制約もあります。ホストの存在が隠蔽されているため全ホストで Pod を実行する DaemonSet や、ホスト上のファイルシステムを Volume としてマウントする機能、ホストの特定のポートで待ち受けて Pod にトラフィックを転送する NodePort など、一部の機能が利用できません。しかし今回の使用方法においてこれらの機能が必須となることはないため、問題ないと判断しました。

EKS にはクラスタ内の Pod を EC2 インスタンスの他に Fargate で実行する機能があります。その機能を用いることで必要なリソースを必要なときに確保しながらコストも節約できると考えました。時間当たりのコストは EC2 のオンデマンドインスタンスよりもやや高くなりますが、実行時間がそれほど長くないため全体としてはコストの削減につながると考えています。

新しい構成

スタディサプリ ENGLISH で使用している EKS クラスターは EC2 インスタンスを使用して動いていますが、その中で CronJob の実行のみを Fargate 上で行えるようにしました。つまり、 EKS 上で EC2 で動いている Pod と Fargate で動いている Pod が共存することになります。

Job 実行中に kubectl get nodes するとこのような感じで EC2 ノードと Fargate ノードが共存するようになることがわかります。

NAME                                                      STATUS   ROLES    AGE     VERSION
fargate-ip-10-12-81-108.ap-northeast-1.compute.internal   Ready    <none>   19s     v1.17.9-eks-a84824
ip-10-12-100-238.ap-northeast-1.compute.internal          Ready    <none>   8h      v1.17.9-eks-4c6976
ip-10-12-108-218.ap-northeast-1.compute.internal          Ready    <none>   19d     v1.17.9-eks-4c6976
ip-10-12-108-48.ap-northeast-1.compute.internal           Ready    <none>   19d     v1.17.9-eks-4c6976
ip-10-12-113-205.ap-northeast-1.compute.internal          Ready    <none>   19d     v1.17.9-eks-4c6976
ip-10-12-113-206.ap-northeast-1.compute.internal          Ready    <none>   4h4m    v1.17.9-eks-4c6976
ip-10-12-115-117.ap-northeast-1.compute.internal          Ready    <none>   80m     v1.17.9-eks-4c6976
ip-10-12-117-206.ap-northeast-1.compute.internal          Ready    <none>   11d     v1.17.9-eks-4c6976

Fargate を使い始めるために必要なもの

Fargate 上で Pod を動かすためには Pod 実行用の IAM ロール及び Fargate Profile を作成する必要があります。

Pod 実行用の IAM ロールは AmazonEKSFargatePodExecutionRolePolicy のポリシーをアタッチすることで ECR からイメージを pull して EKS の Pod をこのロールで実行することができるようになります。

Fargate Profile では、どの namespace の Pod を Fargate 上で動かすか指定することができます。 (指定した namespace の中で更に label を用いた絞り込みもできるようです。)
また、 Fargate Profile には Pod 実行用の IAM Role も指定する必要があります。
FargateProfile が作成されると、指定した namespace, label の Pod は自動的に Fargate 上で実行されるようになります。

参考までに、 IAM ロールと Fargate Profile を定義する Terraform のコードを貼っておきます。

resource "aws_iam_role" "eks_fargate_pod_execution_role" {
  name = "eks-fargate-pod-execution-role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "eks-fargate-pods.amazonaws.com"
    },
    "Effect": "Allow",
    "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "eks_fargate_pod_execution" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy"
  role = aws_iam_role.eks_fargate_pod_execution_role.name
}

resource "aws_eks_fargate_profile" "stable_cron_job_profile" {
  cluster_name = local.eks-cluster-names[0]
  fargate_profile_name = "fargate-profile-name"
  pod_execution_role_arn = aws_iam_role.eks_fargate_pod_execution_role.arn
  subnet_ids = [aws_subnet.eks_private[0].id, aws_subnet.eks_private[1].id]

  selector {
    namespace = "namespace-fargate"
  }
}

セキュリティグループの設定

Fargate 上で実行される Pod が EC2 インスタンス上の Pod や RDS と通信をするにはセキュリティグループの設定が必要です。ちなみに、 EKS クラスターでマネージドノードグループを使用して作成された EC2 インスタンスにおいてはこの設定を AWS 側がやってくれるようなので不要です。スタディサプリ ENGLISH ではマネージドノードグループを使用せずに Spotinst Ocean でインスタンスを立てているためこの設定が必要となります。

EKS クラスターには Control Plane が所属する、クラスターセキュリティグループと呼ばれるセキュリティグループがあり、Fargate 上で実行される Pod もこのセキュリティグループが付与されます。そのため、 EC2 インスタンスや RDS のセキュリティグループの設定を変更して Fargate 上の Pod と通信できるようにする必要があります。なお、現在 Github 上で Fargate Profile に Security Group の設定を記述できるようにするための機能追加が提案されています。4)https://github.com/aws/containers-roadmap/issues/625

ハマったポイント

Fargate Profile を作成し、 EKS クラスタに追加した際には AWS によって自動的に kube-system 名前空間の aws-auth ConfigMap が書き換えられ、次のような項目が mapRoles に対して追加されます。 rolearn の部分には作成した Pod 実行用の IAM ロールの arn が入ります。

- "groups":
  - "system:bootstrappers"
  - "system:nodes"
  - "system:node-proxier"
  "rolearn": "arn:aws:iam::************:role/role-name"
  "username": "system:node:{{SessionName}}"

aws-auth ConfigMap には AWS における IAM ロール、ユーザーと Kubernetes クラスター上での権限をマッピングするための情報が含まれています。この情報が消えてしまうと CronJob の Pod を開始しようとした際にエラーとなります。そのため、 Terraform でインフラを管理している場合にはこれが上書きされて消されないように注意が必要です。

コスト比較

この記事を書いている時点ではまだこの構成は運用フェーズに入っていません。そのため、ここでは AWS の価格表を元にコストを EC2 のオンデマンドインスタンスと Fargate の間で比較します。ここで示される価格はこの記事を書いている際の東京リージョンの価格です。5)https://aws.amazon.com/ec2/pricing/on-demand/6)https://aws.amazon.com/fargate/pricing/

例として 2GB のメモリと 1コアの CPU を使用する場合、4GB のメモリと2コアの CPU を使用する場合を考えてみます。

スペック EC2 の価格 Fargate の価格
1 Core CPU + 2GB RAM $0.0304 (t2.small) $0.06162
2 Core CPU + 4GB RAM $0.0432 (t4g.medium) $0.12324

この表にある価格はそれぞれのスペックの EC2 インスタンスや Fargate ノードを使用した時にかかる 1 時間あたりの料金です。この表から分かる通りこのようなスペックで使用した場合には Fargate を使用した場合の方が 2 倍、 3 倍の料金がかかります。しかし、 1 日あたりの CronJob の実行時間を合計8時間、あるいは12時間以下にすれば Fargate の方がコストが安くなることがわかります。実際問題としてこのクラスタで実行されている Job は比較的短時間ですので全て合計してもこの範囲内に収まっています。

本来であれば同スペックの Fargate と EC2 の価格を単純に比較すべきではありません。なぜなら Fargate の場合にはアプリケーションが使用できるメモリ、 CPU の量に対して課金されるのに対して EC2 では OS が使用する分を含めたメモリ、CPUのリソースが必要なためです。しかしながらここでは Fargate において実行時間が比較的短時間の Jobを実行すれば EC2 よりもコストが抑えられることを示すための簡易的な計算であり、コスト計算のための厳密な計算ではありません。現実問題として Fargate と EC2 で同じプログラムを同じ速度で動かすには EC2 の方がスペックの高いインスタンスが必要になります。

また、 API サーバーなどの instance が入れ替わることのある Pod を spot instance で、定期的な Job のように一時的にリソースが必要な Pod を Fargate で動かすことで金銭的なコスト以外にも Node の管理に必要なコストも削減できています。

まとめ

この記事では EKS 上の CronJob を Fargate 上で実行するための方法に関して紹介しました。この記事では CronJob を動かすところまででしたが、それらに対して監視を行う際の方法に関しても記事にしました。こちらもよろしければご覧ください。

冬アルバイトも募集中

現在僕が参加しているリクルートでの夏アルバイトですが、冬アルバイトも現在募集中です。興味のある方はぜひチェックしてみてください! https://engineers.recruit-jinji.jp/event/job-for-student-2021w/