FIS から EKS の Chaos Mesh を実行する

2022.07.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

背景

2022/07/07 に FIS の新しいアクション aws:eks:inject-kubernetes-custom-resource が追加されました。

https://docs.aws.amazon.com/fis/latest/userguide/fis-actions-reference.html#inject-kubernetes-custom-resource

これは EKS の Cluster に入っている Chaos Mesh または Litmus を実行するアクションです。

実行には何が必要か、どういった制約があるか確認するためにやってみました。

やってみた

https://gitlab.com/kojima.takashi/2022-07-13-fis-itg-chaosmesh

まず CDK で EKS を構築します。

const vpc = new ec2.Vpc(this, "Vpc", { maxAzs: 2 });

const cluster = new eks.Cluster(this, "Cluster", {
  version: eks.KubernetesVersion.V1_21,
  vpc,
  vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }],
  defaultCapacity: 1,
  endpointAccess: eks.EndpointAccess.PUBLIC,
  defaultCapacityInstance: ec2.InstanceType.of(
    ec2.InstanceClass.T3,
    ec2.InstanceSize.MEDIUM
  ),
  clusterLogging: [eks.ClusterLoggingTypes.API],
});

Chaos Mesh をインストールする namespace を作成します。

cluster.addManifest("chaosmesh-namespace", {
  apiVersion: "v1",
  kind: "Namespace",
  metadata: { name: "chaos-testing" },
});

Chaos Mesh 用のロールも作成します。

このロールを使って FIS から操作するためです。

const cmManagerRole = {
  kind: "ClusterRole",
  apiVersion: "rbac.authorization.k8s.io/v1",
  metadata: { name: "role-chaosmesh-manager" },
  rules: [
    {
      apiGroups: [""],
      resources: ["pods", "namespaces"],
      verbs: ["get", "watch", "list"],
    },
    {
      apiGroups: ["chaos-mesh.org"],
      resources: ["*"],
      verbs: ["get", "list", "watch", "create", "delete", "patch", "update"],
    },
  ],
};
cluster.addManifest("chaosmesh-manager-role", cmManagerRole);

FIS で実行するためのロールも作成します。

権限にはログ書込み用の権限のみ渡しました。

const fisLog = new logs.LogGroup(this, "FisLogs", { retention: 3 });
const fisRole = new iam.Role(this, "FisRole", {
  assumedBy: new iam.ServicePrincipal("fis.amazonaws.com"),
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName("AWSLambdaExecute"),
  ],
  inlinePolicies: {
    ExecutePolicy: new iam.PolicyDocument({
      statements: [
        new iam.PolicyStatement({
          sid: "AllowLogging",
          effect: iam.Effect.ALLOW,
          actions: ["logs:CreateLogDelivery"],
          resources: ["*"],
        }),
      ],
    }),
  },
});

k8s の Chaos Mesh 用のロール と AWS の FIS 用の Role を mapping します。

cluster.awsAuth.addRoleMapping(fisRole, {
  groups: ["system:masters", cmManagerRole.metadata.name],
});

では実際に実験の対象となる nginx を作成します。

const nginxDeployment = {
  apiVersion: "apps/v1",
  kind: "Deployment",
  metadata: { name: "nginx-deployment" },
  spec: {
    selector: { matchLabels: { app: "nginx" } },
    replicas: 2,
    template: {
      metadata: { labels: { app: "nginx" } },
      spec: {
        containers: [
          {
            name: "nginx",
            image: "nginx:1.14.2",
            ports: [{ containerPort: 80 }],
          },
        ],
      },
    },
  },
};
cluster.addManifest("nginx", nginxDeployment);

その対象に対して実験する実験テンプレートを用意しました。

new fis.CfnExperimentTemplate(this, "InjectChaosMesh", {
  description: "InjectChaosMesh",
  roleArn: fisRole.roleArn,
  tags: { Name: "InjectChaosMesh" },
  logConfiguration: {
    logSchemaVersion: 1,
    cloudWatchLogsConfiguration: { LogGroupArn: fisLog.logGroupArn },
  },
  targets: {
    cluster: {
      resourceType: "aws:eks:cluster",
      resourceArns: [cluster.clusterArn],
      selectionMode: "ALL",
    },
  },
  actions: {
    InjectChaosMesh: {
      actionId: "aws:eks:inject-kubernetes-custom-resource",
      targets: { Cluster: "cluster" },
      parameters: {
        kubernetesApiVersion: "chaos-mesh.org/v1alpha1",
        kubernetesKind: "PodChaos",
        kubernetesNamespace: "chaos-testing",
        kubernetesSpec: JSON.stringify({
          action: "pod-kill",
          mode: "one",
          selector: {
            namespaces: ["default"],
            labelSelectors: nginxDeployment.spec.template.metadata.labels,
          },
          gracePeriod: 0,
        }),
        maxDuration: "PT5M",
      },
    },
  },
  stopConditions: [{ source: "none" }],
});

本来であれば addHelmChartchaos-mesh もインストールしたいのですが、 動作が確認できなかったので、手動でインストールすることにしました。

/***
    cluster.addHelmChart("ChaosMesh", {
      repository: "https://charts.chaos-mesh.org",
      chart: "chaos-mesh/chaos-mesh",
      release: "chaos-mesh",
      version: "2.2.2",
      namespace: "chaos-testing",
      createNamespace: true,
    });
***/
$ aws eks update-kubeconfig --name ${CLUSTER_NAME} --role-arn ${CLUSTER_ROLE_ARN}

$ helm repo add chaos-mesh https://charts.chaos-mesh.org
$ helm install chaos-mesh chaos-mesh/chaos-mesh --namespace=chaos-testing --version=2.2.2

FIS 経由で実行してみると動いていることが確認できました。

$ aws fis start-experiment --experiment-template-id "${TEMPLATE_ID}"

$ kubectl get pods
NAME                                READY   STATUS              RESTARTS   AGE
nginx-deployment-66b6c48dd5-584xb   1/1     Running             0          134m
nginx-deployment-66b6c48dd5-hnl79   0/1     ContainerCreating   0          1s

$ kubectl get podchaos -A --watch
NAMESPACE       NAME                                                               AGE
chaos-testing   fis-8lc50oqd9hjmstjq9ha7csqmdtt34ba9dpl6aork8dk62rrj9lin6q1deoog   0s
chaos-testing   fis-8lc50oqd9hjmstjq9ha7csqmdtt34ba9dpl6aork8dk62rrj9lin6q1deoog   21s

やってみての感想

Chaos Mesh と結合することでより柔軟な実験できるようになりました。

また、Chaos Mesh 側の実験を Cloudwatch Alarm 経由で停止したり、FIS の内容と組み合わせて実験するということができるようになります。

Chaos Mesh のダッシュボードも大変使いやすいのですが、実行権限やユーザを FIS で統一することで AWS でコントロールできるようになります。

ただ、Chaos Mesh のダッシュボードや Gremlin のダッシュボードでグリグリしながら実験することに慣れていると、JSON で実験テンプレートを作るのは少し難易度が高かったです。

なので実際に作った実験結果を FIS 側にも保存していく、といった用途で使っていくのが良さそうですね。

参考

https://chaos-mesh.org/