본문 바로가기

Server Infra/Kubernetes

Chaos mesh!!

728x90

최근 k8s에 Resilience를 테스트 하면서 이녀석을 사용해 보았다. 여러 Faults를 제공하는데 특히 pod에 장애를 주입시키는 것을 중점적으로 테스트 해보려고 했다.

https://github.com/chaos-mesh/chaos-mesh

 

GitHub - chaos-mesh/chaos-mesh: A Chaos Engineering Platform for Kubernetes.

A Chaos Engineering Platform for Kubernetes. Contribute to chaos-mesh/chaos-mesh development by creating an account on GitHub.

github.com

Pod Faults는 3가지 동작방식을 지원합니다

  • pod-failure
  • pod-kill
  • container-kill

각 동작은 Chaos Daemon에서 Controller Manager의 호출을 받아 수행됩니다. 동작은 아래의 코드에서 수행됩니다.
https://github.com/chaos-mesh/chaos-mesh/tree/master/controllers/chaosimpl/podchaos

 

GitHub - chaos-mesh/chaos-mesh: A Chaos Engineering Platform for Kubernetes.

A Chaos Engineering Platform for Kubernetes. Contribute to chaos-mesh/chaos-mesh development by creating an account on GitHub.

github.com

각 Container는 Daemon에서 Runtime별로 Socket을 열어 통신합니다. 따라서, Chaos Daemon은 각 Container와 직접적으로 통신하며 PID를 제어합니다

func V1() ([]cgroups.Subsystem, error) {
    subsystems, err := defaults("/host-sys/fs/cgroup")
    if err != nil {
        return nil, err
    }
    var enabled []cgroups.Subsystem
    for _, s := range pathers(subsystems) {
        // check and remove the default groups that do not exist
        if _, err := os.Lstat(s.Path("/")); err == nil {
            enabled = append(enabled, s)
        }
    }
    return enabled, nil
}

func PidPath(pid int) cgroups.Path {
    p := fmt.Sprintf("/proc/%d/cgroup", pid)
    paths, err := cgroups.ParseCgroupFile(p)
    if err != nil {
        return func(_ cgroups.Name) (string, error) {
            return "", errors.Wrapf(err, "parse cgroup file %s", p)
        }
    }

    return func(name cgroups.Name) (string, error) {
        root, ok := paths[string(name)]
        if !ok {
            if root, ok = paths["name="+string(name)]; !ok {
                return "", errors.New("controller is not supported")
            }
        }

        return root, nil
    }
}

실제로 위의 코드를 통해 Daemon에서 cgroup단위로 컨트롤 하기 때문에 여러가지 스트레스를 각 Pod에 주입할 수 있습니다.
https://github.com/chaos-mesh/chaos-mesh/tree/master/pkg/chaosdaemon

Container-kill

해당 엑션을 수행하면 Chaos Daemon에서 ContainerKill function을 실행시켜 특정 Container ID를 가져와 해당 컨테이너를 stop 시킵니다.

func (b *ChaosDaemonClientBuilder) Build(ctx context.Context, pod *v1.Pod) (chaosdaemonclient.ChaosDaemonClientInterface, error) {
   ...
   daemonIP, err := b.FindDaemonIP(ctx, pod)
   if err != nil {
       return nil, err
   }
   builder := grpcUtils.Builder(daemonIP, config.ControllerCfg.ChaosDaemonPort).WithDefaultTimeout()
   if config.ControllerCfg.TLSConfig.ChaosMeshCACert != "" {

    builder.TLSFromFile(config.ControllerCfg.TLSConfig.ChaosMeshCACert, config.ControllerCfg.TLSConfig.ChaosDaemonClientCert, config.ControllerCfg.TLSConfig.ChaosDaemonClientKey)
   } else {
       builder.Insecure()
   }
   cc, err := builder.Build()
   if err != nil {
       return nil, err
   }
   return chaosdaemonclient.New(cc), nil
}



PodFailure

정상인 Pod의 Image를 Chaos mesh에서 만든 비정상 이미지로 변경하여 고의적으로 Pod를 Fail로 처리합니다

func (impl *Impl) Apply(ctx context.Context, index int, records []*v1alpha1.Record, obj v1alpha1.InnerObject) (v1alpha1.Phase, error) {
   ...
   pod := origin.DeepCopy()
   for index := range pod.Spec.Containers {
       originImage := pod.Spec.Containers[index].Image
       name := pod.Spec.Containers[index].Name
       key := annotation.GenKeyForImage(podchaos, name, false)
       if pod.Annotations == nil {
           pod.Annotations = make(map[string]string)
       }
       // If the annotation is already existed, we could skip the reconcile for this container
       if _, ok := pod.Annotations[key]; ok {
           continue
       }
       pod.Annotations[key] = originImage
       pod.Spec.Containers[index].Image = config.ControllerCfg.PodFailurePauseImage
   }

   for index := range pod.Spec.InitContainers {
       originImage := pod.Spec.InitContainers[index].Image
       name := pod.Spec.InitContainers[index].Name
       key := annotation.GenKeyForImage(podchaos, name, true)
       if pod.Annotations == nil {
           pod.Annotations = make(map[string]string)
       }
       // If the annotation is already existed, we could skip the reconcile for this container
       if _, ok := pod.Annotations[key]; ok {
           continue
       }
       pod.Annotations[key] = originImage
       pod.Spec.InitContainers[index].Image = config.ControllerCfg.PodFailurePauseImage
   }
   err = impl.Patch(ctx, pod, client.MergeFrom(&origin))
   if err != nil {
       // TODO: handle this error
       return v1alpha1.NotInjected, err
   }
   return v1alpha1.Injected, nil
}



PodKill
terminationGracePeriodSeconds 기능을 사용하여 Pod를 종료 시킵니다

import (
   "context"
   v1 "k8s.io/api/core/v1"=
   "sigs.k8s.io/controller-runtime/pkg/client"
)
type Impl struct {
   client.Client
}

func (impl *Impl) Apply(ctx context.Context, index int, records []*v1alpha1.Record, obj v1alpha1.InnerObject) (v1alpha1.Phase, error) {
   ...
   err = impl.Get(ctx, namespacedName, &pod)
   if err != nil {
       // TODO: handle this error
       return v1alpha1.NotInjected, err
   }
   err = impl.Delete(ctx, &pod, &client.DeleteOptions{
       GracePeriodSeconds: &podchaos.Spec.GracePeriod, // PeriodSeconds has to be set specifically
   })
   ...
   return v1alpha1.Injected, nil
}

코드를 분석한 내용이다. 위와같은 코드를 실행시켜서 실제 커널영역을 컨트롤 한다.

728x90