Это вторая и заключительная часть знакомства с доступными сегодня Open Source-утилитами для организации хаос-инжиниринга в Kubernetes-кластерах. В первой статье было вкратце рассказано о появлении самой дисциплины — chaos engineering, — а также рассмотрены kube-monkey, chaoskube и Chaos Mesh. Теперь этот список пополнится обзором Litmus Chaos, Chaos Toolkit, мини-подборкой из хаос-игр и перечислением пяти других вариантов, заслуживающих внимания инженеров, заинтересованных в разовой или постоянной проверке своей инфраструктуры на устойчивость.

NB: Нумерацию проектов продолжим в соответствии с первой частью.

4. Litmus Chaos



Litmus — еще один оператор, который создает, управляет и наблюдает за хаосом в Kubernetes. Делает он это, используя три типа Custom Resources:

  • ChaosExperiment описывает сам эксперимент, действия и их расписание;
  • ChaosEngine объединяет приложение или Kubernetes-узел с конкретным ChaosExperiment;
  • ChaosResult — ресурс, который хранит результаты эксперимента и в дальнейшем экспортируется оператором как метрика для Prometheus.

Наглядная схема, как работает Litmus:



Теперь же о практике. Установим оператор, следуя документации:

$ kubectl apply -f https://litmuschaos.github.io/litmus/litmus-operator-v1.9.0.yaml
$ kubectl -n litmus get po
NAME                                 READY   STATUS    RESTARTS   AGE
chaos-operator-ce-797875d477-b4cwx   1/1     Running   0          88s

Проверим наличие CRD:

$ kubectl get crds | grep chaos
chaosengines.litmuschaos.io                           2020-10-27T08:58:39Z
chaosexperiments.litmuschaos.io                       2020-10-27T08:58:39Z
chaosresults.litmuschaos.io                           2020-10-27T08:58:39Z

В специальном хранилище под названием ChaosHub уже есть большое количество готовых экспериментов. Создадим отдельный namespace для экспериментов и последуем дальше за руководством, развернув эксперименты с хаба:

$ kubectl create ns nginx
$ kubectl apply -f https://hub.litmuschaos.io/api/chaos/1.9.0?file=charts/generic/experiments.yaml -n nginx
chaosexperiment.litmuschaos.io/pod-network-duplication created
chaosexperiment.litmuschaos.io/node-drain created
chaosexperiment.litmuschaos.io/node-io-stress created
chaosexperiment.litmuschaos.io/disk-fill created
chaosexperiment.litmuschaos.io/k8-pod-delete created
chaosexperiment.litmuschaos.io/node-taint created
chaosexperiment.litmuschaos.io/pod-autoscaler created
chaosexperiment.litmuschaos.io/pod-cpu-hog created
chaosexperiment.litmuschaos.io/pod-memory-hog created
chaosexperiment.litmuschaos.io/pod-network-corruption created
chaosexperiment.litmuschaos.io/pod-delete created
chaosexperiment.litmuschaos.io/pod-network-loss created
chaosexperiment.litmuschaos.io/disk-loss created
chaosexperiment.litmuschaos.io/k8-pod-delete unchanged
chaosexperiment.litmuschaos.io/pod-io-stress created
chaosexperiment.litmuschaos.io/k8-service-kill created
chaosexperiment.litmuschaos.io/pod-network-latency created
chaosexperiment.litmuschaos.io/k8-pod-delete unchanged
chaosexperiment.litmuschaos.io/k8-pod-delete unchanged
chaosexperiment.litmuschaos.io/node-cpu-hog created
chaosexperiment.litmuschaos.io/docker-service-kill created
chaosexperiment.litmuschaos.io/kubelet-service-kill created
chaosexperiment.litmuschaos.io/k8-pod-delete unchanged
chaosexperiment.litmuschaos.io/node-memory-hog created
chaosexperiment.litmuschaos.io/k8-pod-delete configured
chaosexperiment.litmuschaos.io/container-kill created

В этом весьма обширном YAML-файле содержится набор экспериментов, один из которых дублирует пакеты, приходящие в pod, другой — опустошает узел с приложением, третий — придавливает диск того же узла и так далее. Все эксперименты — ресурсы, находящиеся в пространстве имен (т.е. namespaced).

Дальнейшие шаги из того же руководства — добавим service account в наш namespace и сделаем более расширенный список прав на ресурсы (мы хотим больше хаоса!):

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: chaos-spawn
  namespace: nginx
  labels:
    name: chaos-spawn
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: chaos-spawn
  namespace: nginx
  labels:
    name: chaos-spawn
rules:
- apiGroups: ["","litmuschaos.io","batch","apps"]
  resources: ["nodes","pods","deployments","pods/log","pods/exec","events","jobs","chaosengines","chaosexperiments","chaosresults"]
  verbs: ["create","list","get","patch","update","delete","deletecollection"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: chaos-spawn
  namespace: nginx
  labels:
    name: chaos-spawn
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: chaos-spawn
subjects:
- kind: ServiceAccount
  name: chaos-spawn
  namespace: nginx

$ kubectl apply -f rbac.yaml
serviceaccount/pod-delete-sa created
role.rbac.authorization.k8s.io/pod-delete-sa created
rolebinding.rbac.authorization.k8s.io/pod-delete-sa created

В качестве жертвы попробуем:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

Убедимся, что этот Deployment выкатился:

$ kubectl -n nginx get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-85ff79dd56-9bcnn   1/1     Running     0          7s
nginx-deployment-85ff79dd56-lq8r5   1/1     Running     0          7s
nginx-deployment-85ff79dd56-pjwvd   1/1     Running     0          7s

Для ограничения радиуса поражения используется аннотирование ресурса:

$ kubectl -n nginx annotate deploy/nginx-deployment litmuschaos.io/chaos="true"
deployment.apps/nginx-deployment annotated

Следуя документации, делаем ChaosEngine (напомню, что это CR, который объединяет приложение с хаос-экспериментами):

apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
  name: nginx-chaos
  namespace: nginx
spec:
  appinfo:
    appns: 'nginx'
    applabel: 'app=nginx'
    appkind: 'deployment'
  annotationCheck: 'true'
  engineState: 'active'
  auxiliaryAppInfo: ''
  chaosServiceAccount: chaos-spawn
  monitoring: false
  experiments:
    - name: pod-delete
      spec:
        components:
          env:
            # set chaos duration (in sec) as desired
            - name: TOTAL_CHAOS_DURATION
              value: '30'
            - name: FORCE
              value: 'false'
    - name: pod-cpu-hog
      spec:
        components:
          env:
            - name: CPU_CORES
              value: '4'
            - name: TARGET_CONTAINER
              value: 'nginx'
    - name: pod-memory-hog
      spec:
        components:
          env:
            - name: TARGET_CONTAINER
              value: 'nginx'

Эксперименты в стандартном варианте этого ресурса показались мне нежным поглаживанием: хочется попробовать что-то более интересное, поэтому мы убьём приложение и немного нагрузим память и CPU.

Отдельно отмечу отсутствие строчки jobCleanUpPolicy: 'delete' в описании ChaosEngine — она нужна, чтобы оператор не удалял «хаосита» после работы и мы увидели, что происходит:

$ kubectl -n nginx get po
NAME                                READY   STATUS      RESTARTS   AGE
nginx-chaos-runner                  0/1     Completed   0          4m28s
nginx-deployment-85ff79dd56-lq8r5   1/1     Running     0          32m
nginx-deployment-85ff79dd56-nt9n9   1/1     Running     0          3m3s
nginx-deployment-85ff79dd56-ptt54   1/1     Running     0          3m32s

Итак, «хаосит» приводит друзей:

$ kubectl -n nginx get po
NAME                                READY   STATUS              RESTARTS   AGE
nginx-chaos-runner                  1/1     Running             0          15s
nginx-deployment-85ff79dd56-2kjgb   1/1     Running             0          2m18s
nginx-deployment-85ff79dd56-gmhn8   1/1     Running             0          2m34s
nginx-deployment-85ff79dd56-vt4jx   0/1     ContainerCreating   0          1s
nginx-deployment-85ff79dd56-wmfdx   1/1     Terminating         0          2m7s
pod-delete-vsxwf0-p9rt9             1/1     Running             0          7s

$ kubectl -n nginx get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx-chaos-runner                  1/1     Running   0          87s
nginx-deployment-85ff79dd56-2kjgb   1/1     Running   0          3m30s
nginx-deployment-85ff79dd56-8nbll   1/1     Running   0          51s
nginx-deployment-85ff79dd56-gmhn8   1/1     Running   0          3m46s
pod-cpu-hog-lqds5k-d8hg6            1/1     Running   0          17s

Фрагмент логов выжигателя CPU:

time="2020-11-09T19:48:06Z" level=info msg="[Chaos]:Number of pods targeted: 1"
time="2020-11-09T19:48:06Z" level=info msg="[Chaos]: The Target application details" container=nginx Pod=nginx-deployment-85ff79dd56-8nbll CPU CORE=4
time="2020-11-09T19:48:06Z" level=info msg="[Chaos]:Waiting for: 60s"

Однако наш друг Prometheus говорит, что четыре обещанных ядра этот «свинтус» (hog) съесть не смог:



Причина осталось неизвестной, но может быть, мы когда-нибудь исследуем эту (по умолчанию используется https://github.com/alexei-led/pumba) или другую реализацию контейнерной хаос-обезьяны…

Что у нас происходит?

$ kubectl -n nginx get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx-chaos-runner                  1/1     Running   0          2m41s
nginx-deployment-85ff79dd56-2kjgb   1/1     Running   0          4m44s
nginx-deployment-85ff79dd56-8nbll   1/1     Running   0          2m5s
nginx-deployment-85ff79dd56-gmhn8   1/1     Running   0          5m
pod-memory-hog-r3ngl5-kjhl7         1/1     Running   0          5s

Фрагмент логов «кабана», отъедающего память:

time="2020-11-09T19:49:33Z" level=info msg="[Chaos]:Number of pods targeted: 1"
time="2020-11-09T19:49:33Z" level=info msg="[Chaos]: The Target application details" container=nginx Pod=nginx-deployment-85ff79dd56-8nbll Memory Consumption(MB)=500
time="2020-11-09T19:49:33Z" level=info msg="[Chaos]:Waiting for: 60s"
time="2020-11-09T19:49:33Z" level=info msg="The memory consumption is: 500"

С памятью всё точно:



На этом эксперимент закончен:

$ kubectl -n nginx get po
NAME                                READY   STATUS      RESTARTS   AGE
nginx-chaos-runner                  0/1     Completed   0          4m20s
nginx-deployment-85ff79dd56-2kjgb   1/1     Running     0          6m23s
nginx-deployment-85ff79dd56-8nbll   1/1     Running     0          3m44s
nginx-deployment-85ff79dd56-gmhn8   1/1     Running     0          6m39s

А вот и официальные результаты вскрытия:

$ kubectl -n nginx get chaosresults.litmuschaos.io
NAME                         AGE
nginx-chaos-pod-cpu-hog      8m48s
nginx-chaos-pod-delete       9m49s
nginx-chaos-pod-memory-hog   7m21s

Вердикт для этого примера — «Pass»:

$ kubectl -n nginx get chaosresults.litmuschaos.io
NAME                         AGE
nginx-chaos-pod-cpu-hog      8m48s
nginx-chaos-pod-delete       9m49s
nginx-chaos-pod-memory-hog   7m21s
$ kubectl -n nginx describe chaosresults.litmuschaos.io nginx-chaos-pod-delete
Name:         nginx-chaos-pod-delete
Namespace:    nginx
Labels:       app.kubernetes.io/component=experiment-job
              app.kubernetes.io/part-of=litmus
              app.kubernetes.io/version=1.9.1
              chaosUID=26c21578-2470-4f08-957f-94267cadc4ff
              controller-uid=7ef7d1c0-79c8-4e21-acfc-711b6b83f918
              job-name=pod-delete-vsxwf0
              name=nginx-chaos-pod-delete
Annotations:  <none>
API Version:  litmuschaos.io/v1alpha1
Kind:         ChaosResult
Metadata:
  Creation Timestamp:  2020-11-09T19:47:01Z
  Generation:          2
  Resource Version:    17209743
  Self Link:           /apis/litmuschaos.io/v1alpha1/namespaces/nginx/chaosresults/nginx-chaos-pod-delete
  UID:                 8e60299b-1751-42a3-a22b-29f5a64d86b5
Spec:
  Engine:      nginx-chaos
  Experiment:  pod-delete
Status:
  Experimentstatus:
    Fail Step:  N/A
    Phase:      Completed
    Verdict:    Pass
Events:
  Type    Reason   Age    From                     Message
  ----    ------   ----   ----                     -------
  Normal  Awaited  10m    pod-delete-vsxwf0-p9rt9  experiment: pod-delete, Result: Awaited
  Normal  Pass     9m42s  pod-delete-vsxwf0-p9rt9  experiment: pod-delete, Result: Pass

Если этого показалось, то знайте, что в комплекте Litmus — прекрасное руководство, как замониторить хаос-эксперименты полным набором Prometheus + Grafana с имеющимися экспортерами и панелями. При наличии такового можно добавить всё это в уже имеющийся в кластере комплект, но потребуются некоторое время, напильник и рубанок.

Говоря про Litmus в целом, это не самый простой в освоении инструмент, но при внимательном чтении документации всё должно получиться. Кажется, проект предлагает все нужные для тотальной победы хаоса инструменты… всё необходимое для проведения полноценных хаос-экспериментов. А для понимания их результатов есть и отчёты в виде ChaosResults, и логи у всех запускаемых pod'ов (они действительно подробны).

P.S. Именно этот проект стал инициатором недавнего появления группы Kubernetes Chaos Engineering Meetup Group.

5. Chaos Toolkit



Это набор инструментов на Python, с помощью которых можно разрабатывать Open API для проведения хаос-экспериментов. У него есть большое количество расширений для разных облачных провайдеров и окружений, в числе которых — интересный нам chaostoolkit-kubernetes (здесь уже GitHub-звёзд заметно меньше: ~150).

Установить Chaos Toolkit Operator для Kubernetes можно с помощью Kustomize (https://docs.chaostoolkit.org/deployment/k8s/operator/):

$ curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
$ ./kustomize build kubernetes-crd/manifests/overlays/generic-rbac | kubectl apply -f

У родительского проекта имеется очень неплохая документация с примерами (https://github.com/chaostoolkit/chaostoolkit-tutorials).

Однако в целом это не конечное приложение для тех, кто эксплуатирует Kubernetes (или другую инфраструктуру), а именно набор инструментов для разработчика на Python, с продуманным подходом и большим количеством возможностей.

Также у проекта был раньше активен репозиторий ChaosHub, но сейчас он оказался в архиве (https://github.com/chaostoolkit/chaoshub-archive):



6. KubeInvaders (и собратья)



Обзор завершит очень необычный инструмент, потому что он сделан в виде… игры!
Итак, скачиваем и устанавливаем чарт (указывая хост для Ingress и whitelist со списком пространств имен, разрешённых для работы приложения):

$ git clone https://github.com/lucky-sideburn/KubeInvaders.git
$ kubectl create namespace kubeinvaders
$ helm install --set-string target_namespace="test-monkeys" --namespace kubeinvaders --set ingress.hostName=invaders.kube.test.io --name kubeinvaders .

После этого достаточно зайти по указанному URL, чтобы увидеть впечатляющий способ убивать pod'ы:



Да! Это легендарная видеоигра Space Invaders. Pod'ы в ней выступают как инопланетные захватчики, а мы должны спасти Землю! (Ну, или просто потестировать, убивая pod'ы вручную или включив автопилот…)

Есть возможность усложнить игру:



Всё настраивается очень просто, есть клиенты для MacOS и Linux. В целом же, пусть с точки зрения функциональных возможностей приложение не так уж богато, сам процесс проходит гораздо веселее.

Как выяснилось, это далеко не единственный способ сделать хаос-инжиниринг веселым. Еще большую популярность среди желающих «поиграть в хаос» в Kubernetes (1200+ звёзд на GitHub) уже получил проект Kube DOOM, из названия которого понятна и суть: убивать pod'ы можно в старом добром шутере.



А чтобы список хаос-игр с Kubernetes был ещё более полным и разнообразным, закончим эту мини-категорию ссылкой на Cheeky Monkey.

7. Другие


Среди других проектов, которые по разным причинам не были включены в список выше, выделю:

  1. PowerfulSeal — за этим «могучим тюленем» с ~1400 GitHub-звездами скрывается весьма продвинутая Python-утилита для внедрения разнообразных проблем в Kubernetes-кластеры. Для проведения хаос-экспериментов имеет несколько режимов работы: путем определения политик в YAML и их автономного запуска, в интерактивном режиме («ломаю всё в ручном режиме и смотрю на последствия») или через навешивание лейблов на pod'ы, которые нужно убивать. Поддерживаются разные облачные провайдеры (AWS, Azure, GCP, OpenStack) и локальные окружения. Доступны метрики для Prometheus и Datadog.
  2. Pod-reaper — убиватель pod'ов с гибко настраиваемым набором cron'ов. Увидеть основные возможности можно в этом продвинутом примере. Написан на Go, не обновлялся с мая этого года.
  3. Kube-entropy — просто приложение для тестирования веб-сервисов в Kubernetes путем проверки изменений в HTTP-статусе для выбранных ingress’ов. Тоже написан на Go и тоже не обновлялся с мая этого года.
  4. Fabric8 Chaos Monkey — реализация chaos monkey для микросервисной платформы с открытым кодом Fabric8 (на базе Docker, Kubernetes и Jenkins). Устанавливается прямо в её веб-интерфейсе.
  5. Kubernetes от Gremlin — это уже не Open Source, а часть коммерческой услуги от известных специалистов в области chaos engineering. Предлагает всеобъемлющую проверку Kubernetes-кластеров на надежность и отказоустойчивость. Есть ограниченный бесплатный тариф.

Заключение


Уже доступен достаточно широкий спектр решений для хаос-экспериментов в Kubernetes: от простых «убивалок» до готовых операторов с разнообразными способами нанести вред всем окружениям и платформы для разработчика собственных хаос-экспериментов. Каждый сможет найти для себя что-то подходящее.

Я бы выделил два варианта использования инструмента для хаос-инжиниринга (в K8s):

  1. Требуется несложное и контролируемое тестирование приложения в конкретном пространстве имён, иногда «задевая» инфраструктурные. Для этого будет достаточно простых вариантов — мы бы выбрали kube-monkey, который отлично показывает себя в таком применении.
  2. Необходимо всесторонне и постоянно тестировать всё окружение вместе с инфраструктурой, для чего мы готовы потратить время и ресурсы на подготовку и внедрение. Тогда нашим выбором будет либо Chaos Mesh (весьма простой в освоении), либо Litmus (более сложный, но не менее удобный).

Вредите, разрушайте, ломайте, наносите добро — делайте свои приложения и инфраструктуры устойчивыми к любым невзгодам!

P.S.


Читайте также в нашем блоге:

Комментарии (2)