catch-img

【Amazon EKS】Terraformを使ったEKSバージョンアップ手順

本記事では、Terraformを使ったEKSバージョンアップの手順を紹介いたします。

IaCでEKS環境を運用している、もしくはバージョンアップさせたいと考えている方々の一助となればと思います。

目次[非表示]

  1. 1.はじめに
  2. 2.注意点
  3. 3.バージョンアップ手順
    1. 3.1.ノードのdrain
    2. 3.2.tfvarsの修正
    3. 3.3.tfvarsの適用
  4. 4.まとめ



はじめに

今回は、コード化されたEKSを想定し、Terraformで構築されたEKSの、クラスタ及びノードグループのバージョンアップの手順をご紹介いたします。

はじめに、EKSの簡単な説明をします。特に必要ない方は、次の「注意点」からお読みください。


EKSとは「Amazon Elastic Kubernetes Service」の略で、AWS上でKubernetesを利用できるようにしたマネージド型サービスです。

Kubernetesとは、コンテナ化されたアプリケーションの管理、スケーリング、デプロイを自動化するオープンソースシステムです。AWS上のコンテナサービスには他にECSがありますが、EKSにはオープンソースをAWSに移管できるという特徴があります。

EKSは、約4か月に一度、新しいマイナーバージョンがリリースされます。

マイナーバージョンは、リリースされてから14ヶ月は標準サポート対象となり、その後1年間の延長サポート対象となります。

現在のEKSリリースとサポート期限は、AWSの公式ドキュメント(以下リンク)から確認できます。

  EKS の Kubernetes バージョンライフサイクルを理解する - Amazon EKS Amazon EKS が標準および延長サポート期間で Kubernetes バージョンをサポートする方法を説明します。これにより、最新バージョン、機能、セキュリティパッチでクラスターをプロアクティブに更新できます。 https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/kubernetes-versions.html



注意点

手順紹介の前に、いくつか注意点を記載いたします。

  • 本記事で紹介するバージョンアップ方法では、EKS上のpodの停止と、ノードグループの再作成を伴うため、EKS自体の停止が必要になります。
  • 記載している手順、画面キャプチャは2024年9月時点のものになります。今後、AWSコンソールのアップデートにより、記載している手順、及び画面キャプチャと実際の作業手順が一致しない場合がございます。予めご了承ください。
  • 今回紹介する手順は、弊社で実際にEKSバージョンアップ(1.25→1.28)を行った際の手順を基にしております。
  • 本記事は、EKSおよびノードグループのバージョンアップについて記載しております。「Kubectlやkustomize等のコントロールプレーンのバージョンアップ」「 アドオンのバージョンアップ」については記載しておりません。
  • EKS(Kubernetes)は、バージョンアップによって旧機能が廃止、変更されることがありますが、本記事では特定のバージョンでの旧機能廃止、変更の対応方法を記載しておりません。実際にEKSのバージョンアップを行う際は、AWSやKubernetesの公式ドキュメントより、廃止や変更される機能を確認した上でバージョンアップを実施してください。
  • バージョンアップ実施前に、対象のEKSにて「ノード名」「ノード上のpod名」「Namespace名」を確認してください。それぞれの情報はコマンド実行時に利用します。各名称はAWSコンソール上のEKSクラスターのリソース情報から確認が可能です。


バージョンアップ手順

Terraformで構築したEKSを対象としているためtfvarsファイルを編集・適用するだけで、バージョンアップを実現することを想定しています。

Terraformを使ったEKSの構築方法については、下記公式ドキュメントをご確認ください。

  Terraform を利用した Amazon EKS の構築 - builders.flash☆ - 変化を求めるデベロッパーを応援するウェブマガジン | AWS Amazon Elastic Kubernetes Service の EKS クラスターの構築を例にとり、Terraform を使ってインフラストラクチャを自動化する方法を解説します。 Amazon Web Services, Inc.


バージョンアップの全体的な流れは以下になります。

  1. ノードのdrain
  2. tfvarsファイルの修正
  3. tfvarsファイルの適用


ノードのdrain

まず、EKSのバージョンアップを行う前に、EKS上のノードを停止させ、Podを退去、および終了させます。

バージョンアップ前にEKS上のPodを退去・終了させておかないと、バージョンアップ中にPodの退去失敗(PodEvictionFailure)によって、バージョンアップが上手くいかなくなる場合があります。

(私がバージョンアップ検証をした際には、このことを知らず、ここでかなり詰まりました...)


ノードを停止させるため、以下コマンドを実行します。

kubectl drain <ノード名> --ignore-daemonsets --force --delete-emptydir-data

「kubectl drain」コマンドを実行することで、指定したノード上のpodを終了させられ、なおかつ、そのノード上に自動でpodが立たないようにすることができます。

オプションの意味は以下になります。

オプション

意味

--ignore-daemonsets
DaemonSetのpodがあっても続行する。
ノード上でDaemonSetで管理されているPodがある場合、
このオプションをつけないとコマンドがエラーとなりdrainできない。
--force
ReplicaSetなどを使わずに単体で起動しているPodがノード上にあっても続行する。
ノード上に該当podがある場合、
このオプションをつけないとコマンドがエラーとなりdrainできない。

--delete-emptydir-data
emptyDir (ノードがドレインされると削除されるローカル データ) を
使用しているポッドがある場合でも続行する。


上記コマンドを実行すると、実行中のpodが退去(evict)されます。基本的には、このコマンドでノードを停止させます。

実行結果として以下が表示されたら、ノードが停止した=drainされたことになります。

$ kubectl drain <ノード名> --ignore-daemonsets --force --delete-emptydir-data

node/<ノード名> cordoned
evicting pod <pod名>

~
pod/<pod名> evicted
node/<ノード名> drained


ただ、上記コマンド実行した際一部podは下記のエラーが出て、podを退去できず、結果としてノードの停止(drain)が完了しない場合があります。

$ kubectl drain <ノード名> --ignore-daemonsets --force --delete-emptydir-data

node/<ノード名> cordoned
evicting pod <pod名>

~
error when evicting pods/“pod名” -n “Namespace名" (will retry after 5s): Cannot evict pod as it would violate the pod's disruption budget.

上記のエラーは、対象のPodを移動できないことを意味します。

この事象は、PodDisruptionBudget(PDB)という停止できるPodの最大数を決める設定値が原因です※1


そちらの設定を変更することでもこのエラーは解消可能ですが、今回は設定変更はせず、podを指定して手動で終了させ、その後にpodを退去させます。

Podの終了コマンドは以下になります。

kubectl -n <Namespace名> delete pod <pod名>


以下が表示されれば、Podが終了してます。

$ kubectl -n <Namespace名> delete pod <pod名>

~
pod "pod名" deleted


(コマンド実行時の実際の出力例です。一つずつ終了させてますが、スペースでpod名を区切ると複数podを同時にdelete可能です)


Podが終了したら、再度上記のノードdrainコマンド(kubectl drain <ノード名> ~~)を実行し、ノードのdrainを確認します。

EKS上のすべてのノードのdrainが完了したら、以下のコマンドでpodの稼働状況を確認します。

kubectl get pods –n <Namespace名>

すべてのpodのSTATUSがpendingになっていることを確認します(RESTARTSやAGEの内容は一例です)。

$ kubectl get pods –n <Namespace名>

NAME                  READY        STATUS        RESTARTS      AGE  
<pod名>               0/1          Pending        0            30s
<pod名>               0/1          Pending        0            30s
<pod名>               0/1          Pending        0            30s
~




※1:参考資料は以下になります↓

  Disruption このガイドは、高可用性アプリケーションを構築したいと考えており、そのために、Podに対してどのような種類のDisruptionが発生する可能性があるか理解する必要がある、アプリケーション所有者を対象としたものです。 また、クラスターのアップグレードやオートスケーリングなどのクラスターの操作を自動化したいクラスター管理者も対象にしています。 自発的なDisruptionと非自発的なDisruption Podは誰か(人やコントローラー)が破壊するか、避けることができないハードウェアまたはシステムソフトウェアエラーが発生するまで、消えることはありません。 これらの不可避なケースをアプリケーションに対する非自発的なDisruptionと呼びます。 例えば: ノードのバックエンドの物理マシンのハードウェア障害 クラスター管理者が誤ってVM(インスタンス)を削除した クラウドプロバイダーまたはハイパーバイザーの障害によってVMが消えた カーネルパニック クラスターネットワークパーティションが原因でクラスターからノードが消えた ノードのリソース不足によるPodの退避 リソース不足を除いて、これら条件は全て、大半のユーザーにとって馴染みのあるものでしょう。 これらはKubernetesに固有のものではありません。 それ以外のケースのことを自発的なDisruptionと呼びます。 これらはアプリケーションの所有者によって起こされたアクションと、クラスター管理者によって起こされたアクションの両方を含みます。 典型的なアプリケーションの所有者によるアクションには次のものがあります: Deploymentやその他のPodを管理するコントローラーの削除 再起動を伴うDeployment内のPodのテンプレートの更新 Podの直接削除(例:アクシデントによって) クラスター管理者のアクションには、次のようなものが含まれます: 修復やアップグレードのためのノードのドレイン。 クラスターのスケールダウンのためにクラスターからノードをドレインする(クラスター自動スケーリングについて学ぶ)。 そのノードに別のものを割り当てることができるように、ノードからPodを削除する。 これらのアクションはクラスター管理者によって直接実行されるか、クラスター管理者やクラスターをホスティングしているプロバイダーによって自動的に実行される可能性があります。 クラスターに対して自発的なDisruptionの要因となるものが有効になっているかどうかについては、クラスター管理者に聞くか、クラウドプロバイダーに相談または配布文書を参照してください。 有効になっているものが何もなければ、Pod Disruption Budgetの作成はスキップすることができます。 注意:全ての自発的なDisruptionがPod Disruption Budgetによる制約を受けるわけではありません。 例えばDeploymentやPodの削除はPod Disruption Budgetをバイパスします。 Disruptionへの対応 非自発的なDisruptionを軽減する方法をいくつか紹介します: Podは必要なリソースを要求するようにする。 高可用性が必要な場合はアプリケーションをレプリケートする。(レプリケートされたステートレスおよびステートフルアプリケーションの実行について学ぶ。) レプリケートされたアプリケーションを実行する際にさらに高い可用性を得るには、(アンチアフィニティを使って)ラックを横断して、または(マルチゾーンクラスターを使用している場合には)ゾーンを横断してアプリケーションを分散させる。 自発的なDisruptionの頻度は様々です。 基本的なKubernetesクラスターでは、自動で発生する自発的なDisruptionはありません(ユーザーによってトリガーされたものだけです)。 しかし、クラスター管理者やホスティングプロバイダーが何か追加のサービスを実行して自発的なDisruptionが発生する可能性があります。 例えば、ノード上のソフトウェアアップデートのロールアウトは自発的なDisruptionの原因となります。 また、クラスター(ノード)自動スケーリングの実装の中には、ノードのデフラグとコンパクト化のために自発的なDisruptionを伴うものがあります。 クラスタ管理者やホスティングプロバイダーは、自発的なDisruptionがある場合、どの程度のDisruptionが予想されるかを文書化しているはずです。 Podのspecの中でPriorityClassesを使用している場合など、特定の設定オプションによっても自発的(および非自発的)なDisruptionを引き起こす可能性があります。 Pod Disruption Budget FEATURE STATE: Kubernetes v1.21 [stable] Kubernetesは、自発的なDisruptionが頻繁に発生する場合でも、可用性の高いアプリケーションの運用を支援する機能を提供しています。 アプリケーションの所有者として、各アプリケーションに対してPodDisruptionBudget (PDB)を作成することができます。 PDBは、レプリカを持っているアプリケーションのうち、自発的なDisruptionによって同時にダウンするPodの数を制限します。 例えば、クォーラムベースのアプリケーションでは、実行中のレプリカの数がクォーラムに必要な数を下回らないようにする必要があります。 Webフロントエンドは、負荷に対応するレプリカの数が、全体に対して一定の割合を下回らないようにしたいかもしれません。 クラスター管理者やホスティングプロバイダーは、直接PodやDeploymentを削除するのではなく、Eviction APIを呼び出す、PodDisruptionBudgetsに配慮したツールを使用すべきです。 例えば、kubectl drainサブコマンドはノードを休止中とマークします。 kubectl drainを実行すると、ツールは休止中としたノード上の全てのPodを退避しようとします。 kubectlがあなたの代わりに送信する退避要求は一時的に拒否される可能性があるため、ツールは対象のノード上の全てのPodが終了するか、設定可能なタイムアウト時間に達するまで、全ての失敗した要求を定期的に再試行します。 PDBはアプリケーションの意図したレプリカ数に対して、許容できるレプリカの数を指定します。 例えば. Kubernetes



tfvarsの修正

tfvarsファイルを修正します。

Kubernetes_versionの値を一つ上のバージョンに書き変えます。

※本手順では、EKSとノードグループのバージョンアップを同時に行うことになります。

EKSは一気にバージョンを上げることができない(例:1.25→1.27といったバージョンアップはできず、1.25→1.26→1.27とバージョンアップする必要がある)ため、バージョンは現行のものの一つ上のバージョンを記載してください。


tfvarsファイルの修正および保存が完了したら、init、plan(以下コマンド)を実行し、エラーが出ないこと、及び変更箇所が想定通りであることを確認します。

terraform init

terraform plan -var-file=<tfvarsファイル名>


参考ですが、以下のようにtfvarsファイルを編集します。

<terraform.tfvars_修正前(一部抜粋した例)>

vpc_id  = "vpc-abc~"
private_cidr = ["10.10.0.0/16"]
subnet_ids = {
  "public" : ["subnet-def~", "subnet-ghi~"],
  "private" : ["subnet-jkl~", "subnet-mno~"]
}

~
kubernetes_version                      = "1.25"
default_nodepool_node_count             = 1
default_nodepool_vm_type                = "m5.2xlarge"
default_nodepool_custom_data            = ""

~

<terraform.tfvars_修正後(一部抜粋した例)>

vpc_id  = "vpc-abc~"
private_cidr = ["10.10.0.0/16"]
subnet_ids = {
  "public" : ["subnet-def~", "subnet-ghi~"],
  "private" : ["subnet-jkl~", "subnet-mno~"]
}

~
kubernetes_version                      = "1.26"
default_nodepool_node_count             = 1
default_nodepool_vm_type                = "m5.2xlarge"
default_nodepool_custom_data            = ""

~


tfvarsの適用

以下コマンドで、tfvarsを適用させます。

terraform apply -var-file=<tfvarsファイル名>


tfvarsの内容を環境に適用することを確認されます。

ノードグループのバージョン等、変更点が想定通りであれば「yes」を入力してください。

terraform initコマンド実行時の確認画面。ここでyesと入力。


ほどなくしてEKSのバージョンアップが始まります。

EKSのバージョンアップが終わり次第(実際にやってみたときは10~15分程度でした)、ノードグループのバージョンアップに移り、ノードグループのバージョンアップも何事もなく終われば、「Apply complete!」と出力され、適用完了となります。


以下コマンドで、EKSのバージョンが上がったことが確認できます。

kubectl get nodes


実際にコマンドを実行してみると、以下のように出力されます(ROLEやAGE、VERSIONの内容は一例です)。

$ kubectl get nodes

NAME                  STATUS         ROLE          AGE         VERSION
<ノード名>            Ready          <none>        10d         v1.25.11-eks-xxxxxxxx
<ノード名>            Ready          <none>        10d         v1.25.11-eks-xxxxxxxx
<ノード名>            Ready          <none>        10d         v1.25.11-eks-xxxxxxxx
~

VERSIONの列で、各ノードのバージョンが確認できます。


先述したpodの稼働確認コマンドで、podが正常に起動していることを確認します。

kubectl get pods  -n <Namespace名>
$ kubectl get pods –n <Namespace名>

NAME                  READY        STATUS        RESTARTS      AGE  
<pod名>               1/1          Running        0            30s
<pod名>               1/1          Running        0            30s
<pod名>               1/1          Running        0            30s
~

※podが起動するまでの時間にはばらつきがあります。


まとめ

以上、コードベースでのEKSバージョンアップ手順についてまとめてみました。

改めて全体的な流れをおさらいしますと、①ノードのdrain、②tfvarsファイルの修正、③tfvarsファイルの適用といった流れになります。

私自身、tfvarsファイルの修正と適用だけでバージョンアップ可能かと検証前は思っておりましたが、実際にはノード上のpodのことも気にかける必要があったようです。

皆様の困りごと解消の手助けになれば幸いです。

CNSでは、AWSを始めOCI、Azure、GCP等を用いたクラウドシステムの構築、複数クラウドを組み合わせたハイブリッド・マルチクラウド構成、さらにはサーバレス、コンテナ、マイクロサービス等の構築が可能です。
各種クラウドの活用に興味のある方は是非、お気軽にお問合せ下さい。

ページトップへ戻る