新增混沌实验类型

本文档介绍如何开发一种新的混沌实验类型。

以开发一种名为 HelloWorldChaos 的混沌实验类型为例,它的功能是向日志中输出一行 “Hello world!”。为了完成这一目标,你需要完成以下步骤:

  • 第 1 步:定义混沌实验的结构类型
  • 第 2 步:注册 CRD
  • 第 3 步:注册混沌实验的处理函数
  • 第 4 步:编译 Docker 镜像
  • 第 5 步:运行混沌实验

第 1 步:定义混沌实验的结构类型

  1. 在 API 目录 api/v1alpha1 中新建一个名为 helloworldchaos_types.go 的文件,写入以下内容:

    ``` package v1alpha1

  1. import (
  2. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  3. )
  4. // +kubebuilder:object:root=true
  5. // +chaos-mesh:base
  6. // +chaos-mesh:oneshot=true
  7. // HelloWorldChaos is the Schema for the helloworldchaos API
  8. type HelloWorldChaos struct {
  9. metav1.TypeMeta `json:",inline"`
  10. metav1.ObjectMeta `json:"metadata,omitempty"`
  11. Spec HelloWorldChaosSpec `json:"spec"`
  12. Status HelloWorldChaosStatus `json:"status,omitempty"`
  13. }
  14. // HelloWorldChaosSpec is the content of the specification for a HelloWorldChaos
  15. type HelloWorldChaosSpec struct {
  16. // ContainerSelector specifies target
  17. ContainerSelector `json:",inline"`
  18. // Duration represents the duration of the chaos action
  19. // +optional
  20. Duration *string `json:"duration,omitempty"`
  21. }
  22. // HelloWorldChaosStatus represents the status of a HelloWorldChaos
  23. type HelloWorldChaosStatus struct {
  24. ChaosStatus `json:",inline"`
  25. }
  26. // GetSelectorSpecs is a getter for selectors
  27. func (obj *HelloWorldChaos) GetSelectorSpecs() map[string]interface{} {
  28. return map[string]interface{}{
  29. ".": &obj.Spec.ContainerSelector,
  30. }
  31. }
  32. ```
  33. 这个文件定义了 HelloWorldChaos 的结构类型,它可以用一个 YAML 文件描述:
  34. ```
  35. apiVersion: chaos-mesh.org/v1alpha1
  36. kind: HelloWorldChaos
  37. metadata:
  38. name: <资源名>
  39. namespace: <命名空间名>
  40. spec:
  41. duration: <持续时间>
  42. status:
  43. experiment: <实验状态>
  44. ...
  45. ```
  1. 在 Chaos Mesh 根目录下运行 make generate,为 HelloWorldChaos 生成一些用于编译 Chaos Mesh 的辅助代码。

第 2 步:注册 CRD

在 Kubernetes API 中注册 HelloWorldChaos 的 CRD,使 HelloWorldChaos 成为一种 Kubernetes 自定义资源。

  1. 在根目录下运行 make yaml

    生成的 YAML 文件位于 config/crd/bases/chaos-mesh.org_helloworldchaos.yaml

  2. 为将这个 YAML 文件合并入 manifests/crd.yaml 中,修改 config/crd/kustomization.yaml,在其中加入新的一行:

    1. resources:
    2. - bases/chaos-mesh.org_podchaos.yaml
    3. - bases/chaos-mesh.org_networkchaos.yaml
    4. - bases/chaos-mesh.org_iochaos.yaml
    5. - bases/chaos-mesh.org_helloworldchaos.yaml # 新增一行
  3. 再次运行 make yaml,HelloWorldChaos 的定义就会出现在 manifests/crd.yaml 里。 如需确认,你可以使用 git diff 命令。

第 3 步:注册混沌实验的处理函数

  1. 创建一个新文件 controllers/chaosimpl/helloworldchaos/types.go 并写入如下内容:

    ``` package helloworldchaos

  1. import (
  2. "context"
  3. "github.com/go-logr/logr"
  4. "go.uber.org/fx"
  5. "sigs.k8s.io/controller-runtime/pkg/client"
  6. "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
  7. "github.com/chaos-mesh/chaos-mesh/controllers/chaosimpl/utils"
  8. "github.com/chaos-mesh/chaos-mesh/controllers/common"
  9. "github.com/chaos-mesh/chaos-mesh/controllers/utils/chaosdaemon"
  10. )
  11. type Impl struct {
  12. client.Client
  13. Log logr.Logger
  14. decoder *utils.ContianerRecordDecoder
  15. }
  16. // Apply applies HelloWorldChaos
  17. func (impl *Impl) Apply(ctx context.Context, index int, records []*v1alpha1.Record, obj v1alpha1.InnerObject) (v1alpha1.Phase, error) {
  18. impl.Log.Info("Hello world!")
  19. return v1alpha1.Injected, nil
  20. }
  21. // Recover means the reconciler recovers the chaos action
  22. func (impl *Impl) Recover(ctx context.Context, index int, records []*v1alpha1.Record, obj v1alpha1.InnerObject) (v1alpha1.Phase, error) {
  23. impl.Log.Info("Goodbye world!")
  24. return v1alpha1.NotInjected, nil
  25. }
  26. func NewImpl(c client.Client, log logr.Logger, decoder *utils.ContianerRecordDecoder) *common.ChaosImplPair {
  27. return &common.ChaosImplPair{
  28. Name: "helloworldchaos",
  29. Object: &v1alpha1.HelloWorldChaos{},
  30. Impl: &Impl{
  31. Client: c,
  32. Log: log.WithName("helloworldchaos"),
  33. decoder: decoder,
  34. },
  35. ObjectList: &v1alpha1.HelloWorldChaosList{},
  36. }
  37. }
  38. var Module = fx.Provide(
  39. fx.Annotated{
  40. Group: "impl",
  41. Target: NewImpl,
  42. },
  43. )
  44. ```
  1. Chaos Mesh 使用 fx 这个库来进行依赖注入。为了注册进 Controller Manager,需要在 controllers/chaosimpl/fx.go 中加入一行:

    1. ...
    2. gcpchaos.Module,
    3. stresschaos.Module,
    4. jvmchaos.Module,
    5. timechaos.Module,
    6. helloworldchaos.Module // 新增一行,注意处理 import

    以及在 controllers/types/types.go 中加入:

    ```

    1. ...
    2. fx.Annotated{
    3. Group: "objs",
    4. Target: Object{
    5. Name: "timechaos",
    6. Object: &v1alpha1.TimeChaos{},
    7. },
    8. },
  1. fx.Annotated{
  2. Group: "objs",
  3. Target: Object{
  4. Name: "gcpchaos",
  5. Object: &v1alpha1.GCPChaos{},
  6. },
  7. },
  8. fx.Annotated{
  9. Group: "objs",
  10. Target: Object{
  11. Name: "helloworldchaos",
  12. Object: &v1alpha1.HelloWorldChaos{},
  13. },
  14. },
  15. ```

第 4 步:编译 Docker 镜像

  1. 在完成了前面所有步骤后,你可以尝试编译镜像:

    1. make
  2. 将镜像推送到本地的 Docker Registry 中:

    1. make docker-push
  3. 如果你的 Kubernetes 集群部署在 kind 上,则还需要将镜像加载进 kind 中:

    1. kind load docker-image localhost:5000/pingcap/chaos-mesh:latest
    2. kind load docker-image localhost:5000/pingcap/chaos-daemon:latest
    3. kind load docker-image localhost:5000/pingcap/chaos-dashboard:latest

第 5 步:运行混沌实验

在这一步中,你需要部署修改版的 Chaos Mesh 并测试 HelloWorldChaos。

在你部署 Chaos Mesh 之前(使用 helm installhelm upgrade),请修改 helm 模板的 helm/chaos-mesh/values.yaml,把镜像更换成你本地 Docker Registry 中的镜像。

Chaos Mesh 的模板使用 pingcap/chaos-mesh:latest 作为默认 Registry,你需要把它换成 DOCKER_REGISTRY 环境变量的值(默认为 localhost:5000),示例如下:

  1. controllerManager:
  2. image: localhost:5000/pingcap/chaos-mesh:latest
  3. ...
  4. chaosDaemon:
  5. image: localhost:5000/pingcap/chaos-daemon:latest
  6. ...
  7. dashboard:
  8. image: localhost:5000/pingcap/chaos-dashboard:latest
  9. ...

完成上述模板修改后,请尝试运行 HelloWorldChaos。

  1. 将 CRD 注册进集群:

    1. kubectl create -f manifests/crd.yaml

    可以看到 HelloWorldChaos 已经被创建:

    1. customresourcedefinition.apiextensions.k8s.io/helloworldchaos.chaos-mesh.org created

    现在可以查看 HelloWorldChaos 的 CRD:

    1. kubectl get crd helloworldchaos.chaos-mesh.org
  2. 安装 Chaos Mesh:

    1. helm install chaos-mesh helm/chaos-mesh --namespace=chaos-testing --set chaosDaemon.runtime=containerd --set chaosDaemon.socketPath=/run/containerd/containerd.sock

    验证一下安装是否成功,查询 chaos-testing 命名空间的 Pod:

    1. kubectl get pods --namespace chaos-testing -l app.kubernetes.io/instance=chaos-mesh
    注意" class="reference-link">新增混沌实验类型 - 图3注意

    --set chaosDaemon.runtime=containerd --set chaosDaemon.socketPath=/run/containerd/containerd.sock 是用来在 kind 上运行 NetworkChaos 的。

  3. 部署用于测试的目标 Pod:

    1. kubectl apply -f https://raw.githubusercontent.com/chaos-mesh/apps/master/ping/busybox-statefulset.yaml

    请确保用于测试的目标 Pod 可以正常运行。

  4. 创建一个名为 chaos.yaml 的文件,写入以下内容:

    1. apiVersion: chaos-mesh.org/v1alpha1
    2. kind: HelloWorldChaos
    3. metadata:
    4. name: hello-world
    5. namespace: chaos-testing
    6. spec:
    7. selector:
    8. namespaces:
    9. - busybox
    10. mode: one
    11. duration: 1h
  5. 运行混沌实验:

    1. kubectl apply -f /path/to/chaos.yaml
    1. kubectl get HelloWorldChaos -n chaos-testing

    现在查看 chaos-controller-manager 的日志,就会看到 Hello World!

    1. kubectl logs chaos-controller-manager-{pod-post-fix} -n chaos-testing

    显示日志如下:

    1. 2021-06-24T06:42:26.858Z INFO records apply chaos {"id": "chaos-testing/chaos-daemon-vsmc5"}
    2. 2021-06-24T06:42:26.858Z INFO helloworldchaos Hello World!
    注意" class="reference-link">新增混沌实验类型 - 图4注意

    {pod-post-fix} 是一个随机串。你可以运行 kubectl get pod -n chaos-testing 来查看它。

探索更多

如果你在新增混沌实验类型的过程中遇到了问题,请在 GitHub 创建一个 issue 向 Chaos Mesh 团队反馈。

如果你想进一步尝试开发工作,请参阅 拓展 Chaos Daemon 接口