EKS on Fargate で GitLab Runner を動かす #GitLab #Kubernetes #AWS
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
はじめに
Amazon Elastic Kubernetes Service(以降EKS)はAWSのManaged Kubernetesサービスです。
Kubernetesを本気で運用しようと思うとかなり大変ですが、EKSを利用するとKubernetesの運用の多くをAWSに任せることができ、より多くの時間をその上で動作させるアプリケーションのために使うことができます。
AWS Fargateはコンテナ向けサーバレスコンピューティングで、これを利用するとコンテナを必要な時・必要な分だけリソースを確保し実行することができます。
EKSでは通常のEC2インスタンスとは別に、Fargateをバックエンドとして利用することができます。
EKS on Fargateと呼ばれているようです。また、これらは併用することもできます。
Jobのような何かのイベントを契機に実行されるワークロードは、それが必要とするリソースを都度確保・実行できるFargateと相性が良いです。
今回はEKS on Fargateを用いて、GitLab RunnerのJobを動かそうという内容です。
Managed Kubernetesを使えば何でも解決してくれる?
一度ここでFargateがない世界を考えてみましょう。
Managed Kubernetesのサービスを利用したとしても、コンテナアプリケーションを動かすときノード(基本的なEKSの場合EC2インスタンス)を意識する必要はあります。
例えばコンテナアプリケーションをスケールアウトするには、当然それらが動作できるだけのノード数が必要となります。
一つのコンテナで大量にリソースが必要となるような特性のアプリケーションがある場合は、ノードのインスタンスタイプも気にしなければなりません。
そういう事を考えたくないために「えいや!」とあらかじめノードを大量に用意してしまうと、余剰分のリソースが常に発生してしまい無駄な料金を払うことになってしまいます。
Kubernetesでは一つのノードに複数のコンテナアプリケーションが同時に起動していることも普通です。Podに対して適切なリソースのrequests/limitsが設定されていなければ、ノイジーネイバーな状態になってしまうものもあるでしょう。
この状態から特定アプリケーションを保護するために、Taint/Tolerationを利用した専用ノードを用意することも珍しくないかと思います。このアプローチをとった場合、今後それらのノード群の考慮も増えてしまうということになります。
そしてこの専用ノードが常時リソースが利用されない遊びのリソースがある場合だとどうでしょう?これも料金を考えるとなるべく避けたい状態です。
例えば少し重めなバッチ処理を一時的にKubernetesクラスタのJobとして動作させる必要がある場合、これらの処理は同一クラスタの他のアプリケーションの処理を妨害して欲しくない(逆も然り)場合もあるでしょう。
一時的にJobを実行するために専用ノードを追加して、Jobが終わればノードを削除して...とすることは技術的にできることではありますが、極力こんな管理はしたくないはずです。
このようにManaged Serviceだからと言って全ての面倒事が解決するわけではなく、利用するアプリケーションの特性によってはノードのこともしっかり考慮する必要があります。
Cluster Autoscalerを利用することで、これらの一部は自動化できますが、全ての問題を解決してくれるわけではありません。
必要な時・必要な分だけだけリソースを確保し、不要になればすぐに破棄でき、料金は利用した分だけといったサービスがあると嬉しいなと誰しもが考えることでしょう。
Fargateではそのような悩みの多くを解決してくれるサービスです。
EKS on Fargateの利用方法としていくつか例を紹介すると、
- 他のワークロードのノイジーネイバーになり得そうなアプリケーションをFargateとして動かし、簡易的な専用ノードのような使い方をする
- 定期実行のバッチ処理を実行するワークロードとしてFargateを利用する etc...
便利な分、同一リソース量のノードと比べると若干割高な料金となっており、そのため何かイベント起因で動作するJobのようなものに相性が良さそうです。
前置きが長くなってしまいましたが、今回はそのGitLab RunnerのJobをFargateで動かしてみよう!という内容です。
Fargate で GitLab Runner の Job を動かす
GitLab Runnerは、GitLab CI/CDと連携してパイプラインのジョブを実行するアプリケーションです。
パイプラインのジョブは、Runner登録時に指定したExecutorの環境をプロセスジョブとして起動し、そこで実行されます。
DockerやSSH先のノード、Kubernetesもその実行環境として利用することができます。
どのような環境がExecutorとして設定できるかはこちらをご確認ください。
GitLab RunnerをFargateで動作させる方法として公式ページでは以下のような実装例が紹介されています。
上記ではEC2インスタンス上でGitLab Runnerを管理し、JobをFargateで実行するためのCustom Executorを利用しているようです。
EKSを利用する場合は、よりシンプルにこのような環境を実現することができます。
何故ならGitLab Runnerは公式で提供されているHelm Chartを利用することで容易にインストールでき、KubernetesのPodでJobを実行するExecutorが実装されているため、別途Custom Executorを入れる必要がないからです。
EKS on Fargateについて
EKSでFargateを利用できるようにする具体的な設定方法はここでは割愛しますが、
特定の "ネームスペース" と "ラベル" が指定された場合のみ、Fargateを利用してPodを起動するという設定ができます。
それ以外のPodはEC2インスタンス上で実行されます。
GitLab Runner自体は常にGitLabのCI/CDの実行に関するイベントを受け取る必要があるため、Jobとは異なり常時稼働させなくてはなりません。
常時稼働を考慮すると、Fargateより費用が抑えられるEC2インスタンスのノードに載せたくなることも十分に考えられます。そのため今回はそのように設定します。
上記のラベルやネームスペースによりFargateかEC2どちらをノードとして利用するか選択でき、この機能により常にGitLabのイベントを受け取る必要があるGitLab RunnerはEC2インスタンスで、JobはFargateを利用する方針で進めていきます。
この動作確認をする上で、gitlab-runnerネームスペースでuse-fargate = "true"ラベルが付与されたPodがFargateを利用できるようクラスタに設定してあります。
Helm を利用してGitLab Runnerをインストール
GitLab Runnerのインストール自体はHelmを利用すると非常に簡単です。
Fargateを利用する場合もインストール方法はGitLab Runnerをkubernetes上でラフに動かしてみようで紹介されている通りですが、設定パラメータを投入するのvalues.yamlで以下のような追加の設定が必要となります。
runners: config: | [[runners]] [runners.kubernetes] cpu_request = "2" memory_request = "8Gi" namespace = "gitlab-runner" poll_timeout = 600 [runners.kubernetes.pod_labels] use-fargate = "true"
上記の設定を少し解説します。
cpu_requestやmemory_requestはJobを実行するPodのspec.containers[].resources.requestsの設定となります。
FargateはPodのrequestsに応じて適切なリソースを確保するため、どのようなJobを実行したいかにより設定が変わります。
namespaceでJobを実行するnamespaceを指定します。指定しなければGitLab Runnerと同じnamespaceで動きます。
pod_labelsはJobを実行するPodに付与するラベルで、ここが今回の設定で一番大事な部分です。
ここでFargateとして実行するためのラベルを指定することで、全てのJobはFargateで実行されます。
一方このラベルを指定せず起動するGitLab RunnerはEC2インスタンス上で実行されます。
Fargateを利用したPodは起動に多少時間がかかるため、poll_timeoutの時間を伸ばしておくと良いです。
その他のGitLab RunnerのKubernetes Executorの設定項目はこちらをご参照ください。
後はこのvalues.yamlを利用し、Fargateを動作させることができるネームスペース(今回の例の場合gitlab-runner)へGitLab Runnerをインストールすれば準備は完了です。
動作確認
インストールが成功していると以下のようにGitLab RunnerのPodはEC2のノードで実行されていることがNODEから確認できます。
$ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gitlab-runner-gitlab-runner-54d4d768bc-f24lt 1/1 Running 0 4m50s 10.0.2.28 ip-10-0-2-39.ap-northeast-2.compute.internal
この状態でGitLab RunnerのJobを実行させます。
すると以下のようにFargateで実行されていることがわかります。
$ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gitlab-runner-gitlab-runner-54d4d768bc-f24lt 1/1 Running 0 20m 10.0.2.12 ip-10-0-2-39.ap-northeast-2.compute.internal runner-1sbvnz1z-project-1083-concurrent-0wnhk7 2/2 Running 0 2m43s 10.0.2.133 fargate-10.0.2.133
Fargateでは1Podに1Node作成されます。
$ kubectl get node NAME STATUS ROLES AGE VERSION fargate-10.0.2.133 Ready 28s v1.20.4-eks-6b7464 ip-10-0-0-43.ap-northeast-2.compute.internal Ready 11d v1.20.4-eks-6b7464 ip-10-0-1-48.ap-northeast-2.compute.internal Ready 11d v1.20.4-eks-6b7464 ip-10-0-2-39.ap-northeast-2.compute.internal Ready 11d v1.20.4-eks-6b7464
Jobが終了されるとNodeが削除されていることも確認できます。
$ kubectl get node NAME STATUS ROLES AGE VERSION ip-10-0-0-43.ap-northeast-2.compute.internal Ready 11d v1.20.4-eks-6b7464 ip-10-0-1-48.ap-northeast-2.compute.internal Ready 11d v1.20.4-eks-6b7464 ip-10-0-2-39.ap-northeast-2.compute.internal Ready 11d v1.20.4-eks-6b7464
このようにJobで必要な時・必要な量だけのリソースを利用することができました。
最後に
GitLab RunnerのJobに限らず他のノードのコンテナへ影響を与えることなく、何かしらイベント起因でPodを動かす必要がある物(例えばCronJob/Jobなど)では同様のメリットが得られます。
しかしFargateにはいくつかの制約があり、例えばPrivilegedなPodが作成できません。
そのため例えばDocker in Dockerによるイメージビルドはできず、Jobの中でイメージビルドが必要な場合はkanikoを利用するなどして対応する必要があります。
制約があるものの、それらと上手に付き合っていけばEKS on Fargateは様々な利用方法がありそうです。