Amazon Web Services ブログ

AWS Fault Injection Simulator による Amazon EKS でのカオスエンジニアリング実践例

この記事では、カオスエンジニアリングの実践に使用できるフルマネージドのフォールトインジェクション(障害注入)サービスである AWS Fault Injection Simulator (AWS FIS) の使用方法について説明します。AWS FIS は、Amazon Elastic Kubernetes Service(Amazon EKS)など、さまざまな AWS サービスをサポートしています。Amazon EKS は、Kubernetes の実行を容易にするマネージドサービスです。お客様は独自の Kubernetes コントロールプレーンまたはワーカーノードをインストールして操作することなく、AWS でKubernetes を実行することができます。この記事では、Amazon EKS 上のワークロードの隠れた弱点を見つけるために、制御されたフォールトインジェクションの実験を設定し実行するプロセスを、事前に構築されたテンプレートとカスタムフォールトを使用することでどれほど簡潔にすることができるかを説明したいと考えています。

この投稿は2021年7月9日に公開されたChaos engineering on Amazon EKS using AWS Fault Injection Simulator を翻訳したものです。

カオスエンジニアリングとは?

カオスエンジニアリングは、テスト環境や本番環境でサーバー停止やAPIスロットリングなどの破壊的なイベントを作成しアプリケーションにストレスをかけ、その時のシステムの応答を観察し、改善を実装していくプロセスです。カオスエンジニアリングは、分散システムで見つけにくい隠れた問題やパフォーマンスのボトルネックを明らかにするために必要な現実世界の状態を作り出すのに役立ちます。まず、定常状態の挙動の分析からはじめ、実験のための仮説を構築 (たとえば、x 個のインスタンスを停止すると再試行数が x% 増える、など)し、実験を実行する際はフォールトアクションを注入し、ロールバック条件を監視し、弱点に対処します。

AWS FIS では、カオスエンジニアリングで使用されるフォールトインジェクションの実験を簡単に実行できるため、アプリケーションのパフォーマンス、可観測性、回復力を簡単に改善できます。

ソリューションの概要

Figure 1: Solution Overview

図1: ソリューション概要

上の図は、今回のソリューションのアーキテクチャを示しています。

この投稿では、Amazon EKS クラスターを対象とした 2 つの異なる障害実験を紹介します。ここでは、Amazon EKS クラスターの作成プロセスの詳細については説明しません。この詳細については、Amazon EKS の開始方法 – eksctleksctl – The official CLI for Amazon EKS を参照してください。

前提条件

始める前に、次の前提条件を満たしていることを確認します。

クラスターを作成するために、次の設定を使用しました。(訳註:このyaml はアイルランドリージョン – eu-west-1 – を利用する設定になっています。)

---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: aws-fis-eks
  region: eu-west-1
  version: "1.19"

iam:
  withOIDC: true

managedNodeGroups:
- name: nodegroup
  desiredCapacity: 3
  instanceType: t3.small
  ssh:
    enableSsm: true
  tags:
    Environment: Dev

クラスターは、次のように作成されています。

  • 3 つの Amazon Elastic Compute Cloud (Amazon EC2) のt3.small インスタンスを 3 つの異なるアベイラビリティーゾーンに分散して配置
  • OIDC プロバイダを有効化
  • 各インスタンスで AWS Systems Manager Agent を有効化 (後で使用します)
  • タグ付きインスタンス

3つのレプリカを持つシンプルな Nginx の deploymentを高可用性のためにそれぞれ異なるインスタンスで実行するようにデプロイしています。

この記事では、次の実験を行います。

  • ノードグループインスタンスの終了 — 最初の実験では、ターゲットのノードグループで Amazon EC2 API の TerminateInstances アクションを実行する aws:eks:terminate-nodegroup-instance AWS FIS アクションを使用します。実験が開始されると、AWS FIS はノードを終了させていきます。私たちは設定した希望する容量に従って、クラスターが終了されたノードを新しいノードで置き換えていくことを確認できるはずです。
  • アプリケーションのポッド の削除 — 2 回目の実験では、AWS FIS を使用してクラスターに対してカスタムフォールトを実行する方法を説明します。AWS FIS は将来的にサポートする Amazon EKS の障害の種類を拡大する予定ですが、この例では、カスタムフォールトインジェクションを実行し、kubectl コマンドを実行して Kubernetes deploymentのポッドをランダムに削除する方法を説明します 。Kubernetes deploymentは、アプリケーションで実行するレプリカ数の希望の状態を定義する良い方法で、ノードまたはポッドのいずれかが停止した場合に高可用性を確保できます。

実験1: ノードグループのインスタンスを終了する

まず、Amazon EKS ノードを終了する実験を作成します。

  1. AWS FIS コンソールで、「実験テンプレートを作成」を選択します。
Figure 2: AWS FIS Console

図2: AWS FIS のコンソール

  1. 「説明」に、説明を入力します。
  2. 「IAM ロール」で、前提条件で作成した AWS FIS が実験を実行するための IAM ロールを選択します。
Figure 3: Create experiment template

図3: 実験テンプレートの作成

  1. 「アクションを追加」を選択します。

このアクションでは、aws:eks:terminate-nodegroup-instances を選択して、クラスター内のワーカーノードを終了するようにします。

  1. 「名前」 に、TerminateWorkerNode と入力します。
  2.  「説明 – オプション」に、ワーカーノードを終了。と入力します。
  3. 「アクションタイプ」で、「aws:eks:terminate-nodegroup-instances 」を選択します。
  4. 「ターゲット」 で、「Nodegroups-Target-1」 を選択します。
  5. instanceTerminationPercentage40 (ノードグループごとに終了するインスタンスの割合) を入力します。
  6. 「保存」 を選択します。
Figure 4: Select action type

図4. アクションタイプの選択

正しいアクションを追加したら、ターゲットを変更できます。この場合は Amazon EKS ノードグループインスタンスです。

  1. ターゲットセクションの「編集」を選択します。
  2. 「リソースタイプ」 に、「aws:eks:nodegroup」 を選択します。
  3. 「ターゲットメソッド」で、「リソース ID」 を選択します。
  4. 「リソース ID」 で、リソース ID を選択します。
  5. 「保存」を選択します。

選択モード では、Amazon EKS クラスターノードグループを選択できます。

Figure 5: Specify target resource

図5:ターゲットリソースの選択

最後に、停止条件 を追加します。これはオプションですが、適切なガードレールを使用して実験を実行することが確実になるため、強くお勧めします。停止条件は、Amazon CloudWatch アラームが定義したしきい値に達した場合に実験を停止するメカニズムです。実験中に停止条件がトリガーされると、AWS FIS は実験を停止し、実験はstopping 状態になります。

今回は、クラスターに Container Insights が設定されているため、クラスターで実行されているノードの数を監視できます。

  1. Container Insights を通じて、CloudWatch アラームを作成し、ノード数が 2 未満である場合に実験を停止します。
  2. アラームを停止条件として追加します。
  3. 「実験テンプレートを作成」 を選択します。
Figure 6: Create experiment template

図6:実験テンプレートの作成

Figure 7: Check cluster nodes

図7: クラスターノードの確認

最初の実験を実行する前に、Amazon EKS クラスターノードを確認しましょう。この例では、3 つのノードが稼働しています。

  1. AWS FIS コンソールで、作成した実験の詳細ページに移動します。
  2. 「アクション」メニューの 「開始」を選択します。
Figure 8: Start experiment

図8: 実験の開始

実験を実行する前に、AWS FIS から実験を開始するかどうかを確認するよう求められます。これは、リソースに対して実験を実行する準備ができていることを確認するためのセーフガードの一例です。

  1. フィールドに 開始 と入力します。
  2. 「実験を開始」を選択します。
Figure 9: Confirm to start experiment

図9: 実験開始の確認

実験を開始すると、実験 ID や現在の状態を確認できます。実験で現在実行されているアクションも確認できます。

Figure 10: Check experiment state

図10: 実験の状態の確認

次に、クラスターワーカーノードのステータスを確認できます。クラスターに新しいノードを追加するプロセスには数分かかりますが、しばらくすると、Amazon EKS が新しいインスタンスを起動して、終了したインスタンスを置き換えることがわかります。

終了したインスタンスの数は、アクション設定の一部として提供したパーセンテージを反映する必要があります。実験が完了しているため、仮説を検証できます。クラスターは最終的に数分以内に希望の容量に等しい数のノードで定常状態に達しました。

Figure 11: Check new worker node

図11: 新しいワーカーノードの確認

実験 2: アプリケーションポッドの削除

それでは、Amazon EKS クラスターで実行されている特定のコンテナ化されたアプリケーション (ポッド) を対象に、カスタムフォールトインジェクションを作成しましょう。

この実験の前提条件として、Amazon EKS クラスターの configmap を更新し、ワーカーノードにアタッチされている IAM ロールを追加する必要があります。このロールを configmap に追加する理由は、実験で Kubernetes クラスターに対してコマンドを実行できる Kubernetes コマンドラインツールである kubectl を使用しているためです。手順については、クラスターのユーザーまたは IAM ロールの管理を参照してください。

  1. Systems Manager のコンソールで、「ドキュメント」を選択します。
  2. 「Create document」 ドロップダウンで、「Command or Session」 を選択します。
Figure 12: Create AWS Systems Manager Document

図12: AWS Systems Manager ドキュメントの作成

  1. 「名前」に、名前 (Delete-Pods など) と入力します。
  2. 「コンテンツ」 セクションで、次のコードを入力します。
description: |
  ### Document name - Delete Pod

  ## What does this document do?
  Delete Pod in a specific namespace via kubectl

  ## Input Parameters
  * Cluster: (Required)
  * Namespace: (Required)
  * InstallDependencies: If set to True, Systems Manager installs the required dependencies on the target instances. (default True)

  ## Output Parameters
  None.

schemaVersion: '2.2'
parameters:
  Cluster:
    type: String
    description: '(Required) Specify the cluster name'
  Namespace:
    type: String
    description: '(Required) Specify the target Namespace'
  InstallDependencies:
    type: String
    description: 'If set to True, Systems Manager installs the required dependencies on the target instances (default: True)'
    default: 'True'
    allowedValues:
      - 'True'
      - 'False'
mainSteps:
  - action: aws:runShellScript
    name: InstallDependencies
    precondition:
      StringEquals:
        - platformType
        - Linux
    description: |
      ## Parameter: InstallDependencies
      If set to True, this step installs the required dependecy via operating system's repository.
    inputs:
      runCommand:
        - |
          #!/bin/bash
          if [[ "{{ InstallDependencies }}" == True ]] ; then
            if [[ "$( which kubectl 2>/dev/null )" ]] ; then echo Dependency is already installed. ; exit ; fi
            echo "Installing required dependencies"
            sudo mkdir -p $HOME/bin && cd $HOME/bin
            sudo curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.20.4/2021-04-12/bin/linux/amd64/kubectl
            sudo chmod +x ./kubectl
            export PATH=$PATH:$HOME/bin
          fi
  - action: aws:runShellScript
    name: ExecuteKubectlDeletePod
    precondition:
      StringEquals:
        - platformType
        - Linux
    description: |
      ## Parameters: Namespace, Cluster, Namespace
      This step will terminate the random first pod based on namespace provided
    inputs:
      maxAttempts: 1
      runCommand:
        - |
          if [ -z "{{ Cluster }}" ] ; then echo Cluster not specified && exit; fi
          if [ -z "{{ Namespace }}" ] ; then echo Namespace not specified && exit; fi
          pgrep kubectl && echo Another kubectl command is already running, exiting... && exit
          EC2_REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document|grep region | awk -F\" '{print $4}')
          aws eks --region $EC2_REGION update-kubeconfig --name {{ Cluster }} --kubeconfig /home/ssm-user/.kube/config
          echo Running kubectl command...
          TARGET_POD=$(kubectl --kubeconfig /home/ssm-user/.kube/config get pods -n {{ Namespace }} -o jsonpath={.items[0].metadata.name})
          echo "TARGET_POD: $TARGET_POD"
          kubectl --kubeconfig /home/ssm-user/.kube/config delete pod $TARGET_POD -n {{ Namespace }} --grace-period=0 --force
          echo Finished kubectl delete pod command.
Figure 13: Add Document details

図13:ドキュメントの詳細の追加

この投稿では、以下を実行するAWS Systems Manager ドキュメント を作成しています。

  • ターゲットの Amazon EKS クラスターインスタンスに kubectl をインストールします。
  • 2 つの必須パラメータを使用します。アプリケーションポッドが実行されている Amazon EKS クラスター名と名前空間です。
  • kubectl delete を実行し、指定した名前空間からアプリケーションポッドの 1 つを削除します。
  1. ドキュメントの作成」 を選択します。
  2. AWS FIS コンソールで新しい実験テンプレートを作成します。
  3. 「名前」DeletePodと入力します。
  4. 「アクションタイプ」で、aws: ssm: send-commandを選択します。

これにより、ターゲットの EC2 インスタンスに対して、Systems Manager の SendCommand API アクションが実行されます。

このアクションを選択したら、先ほど作成したドキュメントの ARN を指定し、クラスターと名前空間に適切な値を指定する必要があります。この例では、ドキュメントに Delete-Pods、クラスター名は aws-fis-eks、名前空間は nginx という名前を付けました。

  1. documentArnarn:aws:ssm:<region>:<accountId>:document/Delete-Pods と入力します。
  2. documentParameters{"Cluster":"aws-fis-eks", "Namespace":"nginx", "InstallDependencies":"True"} と入力します。
  3. 右上の「保存」を選択します。
Figure 14: Select Action type

図14: アクションタイプの選択

  1. ターゲットについては、リソース ID またはリソースタグでリソースを指定することができます。この例では、リソース ID でノードインスタンスのいずれかをターゲットにします。
Figure 15: Specify target resource

図15: ターゲットリソースの指定

  1. テンプレートを正常に作成したら、実験を開始します。

実験が完了したら、アプリケーションポッド を確認します。この例では、AWS FIS はポッドのレプリカの 1 つを停止し、前述したように Kubernetes のデプロイメントを使用しているため、新しいポッドのレプリカが作成されました。

Figure 16: Check Deployment pods

図16: デプロイメントのポッドを確認

クリーンアップ

今後の料金が発生しないようにするには、以下の手順に従って、この投稿の手順で作成したすべてのリソースを削除します。

  1. AWS FIS コンソールから、TerminateWorkerNodes & DeletePod の実験テンプレートを削除します。
  2. AWS EKS コンソールから、この投稿のために作成した テストクラスターの aws-fis-eks を削除します。
  3. AWS Identity and Access Management (IAM)コンソールから、IAM ロール AWSFISRole を削除します。
  4. Amazon CloudWatch コンソールから、CloudWatch アラーム CheckEKSNodes を削除します。
  5. AWS Systems Manager コンソールのドキュメントの「自己所有」タブから Delete-Pod ドキュメントを削除します。

まとめ

この投稿では、AWS FIS を使用して Amazon EKS で障害注入の実験を実行する 2 つの方法を示しました。まず、AWS FIS でサポートされているネイティブアクションを使用して、Amazon EKS クラスターからインスタンスを終了しました。次に、AWS FIS を拡張して、Amazon EKS で実行されているコンテナ化されたアプリケーションにカスタム障害を注入しました。

AWS FIS の詳細については、AWS re: Invent 2020 でのセッション AWS Fault Injection Simulator: Fully managed chaos engineering serviceを参照してください。カオスエンジニアリングについて詳しく知りたい場合は、AWS re: Invent セッションの Testing resiliency using chaos engineeringThe Chaos Engineering Collection をご覧ください。最後に、次の GitHub リポジトリでその他の実験例と、AWS Cloud Development Kit (AWS CDK)を使用して AWS FIS を操作する方法を確認してください。

Omar はプロフェッショナルサービスコンサルタントとして、お客様が DevOps の文化とベストプラクティスを採用する支援をしています。また、AWS サービスの導入をより簡単にするために、複雑なソリューションの自動化と実装に取り組んでいます。

Daniel Arenhage は、スウェーデンのヨーテボリで活動するアマゾンウェブサービスのソリューションアーキテクトです。

この記事の翻訳は、ソリューションアーキテクトの金森 政雄が担当しました。