Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Расширяем и дополняем Kubernetes

flant
April 08, 2019

Расширяем и дополняем Kubernetes

Доклад solution-инженера компании «Флант» (https://flant.ru/) Андрея Половова на конференции Saint Highload++ 2019 в Санкт-Петербурге.

* Текстовый обзор доклада: https://habr.com/company/flant/blog/449096/
* Видео с выступления: https://youtu.be/6VHk1R1TNgk

flant

April 08, 2019
Tweet

More Decks by flant

Other Decks in Technology

Transcript

  1. Организовал АТС в Андрей Половов [email protected] 10 лет работы во

    «Фланте» Отвечал за онлайн-трансляции в Затащил в кубы Один из первых CKA в компании Привет! Solution-инженер
  2. Петя Маша Артём ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  3. Петя Маша Артём ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  4. Петя Маша Артём ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  5. Петя Маша Артём ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  6. N Deployment StatefulSet Service Ingress Job CronJob N NetworkPolicy PodDisruptionBudget

    HorizontalPodAutoscaler Pod LoadBalancer Namespace Daemonset ReplicaSet И многое другое:
  7. Наш опыт с Kubernetes в небольших проектах Дмитрий Столяров РИТ++

    2017 goo.gl/y4fXUF N Deployment StatefulSet Service Ingress Job CronJob N NetworkPolicy PodDisruptionBudget HorizontalPodAutoscaler Pod LoadBalancer Namespace Daemonset ReplicaSet И многое другое:
  8. Петя Маша Артём ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  9. ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? Петя Маша Артём
  10. ?

  11. HPA Operator Operator Cluster Autoscaler Kanister Airflow MXNet Spark (GCP)

    Tensorflow PyTorch DynamoDB Keel Flux CouchDB MongoDB (Official) RethinkDB Couchbase (official) ArangoDB svcat AWS (Giant Swarm) CloudFormation KubeVirt OpenStack VPC Peering KVM WildFly ...
  12. HPA Operator Operator Cluster Autoscaler Kanister Airflow MXNet Spark (GCP)

    Tensorflow PyTorch DynamoDB Keel Flux CouchDB MongoDB (Official) RethinkDB Couchbase (official) ArangoDB svcat AWS (Giant Swarm) CloudFormation KubeVirt OpenStack VPC Peering KVM WildFly ... * по состоянию на апрель 2019
  13. HPA Operator Operator Cluster Autoscaler Kanister Airflow MXNet Spark (GCP)

    Tensorflow PyTorch DynamoDB Keel Flux CouchDB MongoDB (Official) RethinkDB Couchbase (official) ArangoDB svcat AWS (Giant Swarm) CloudFormation KubeVirt OpenStack VPC Peering KVM WildFly ... * по состоянию на апрель 2019
  14. 4 HPA cpu_utilization <= 50% Metrics API Metrics adapter Metrics

    server Prometheus adapter Azure adapter Datadog cluster agent Google stackdriver
  15. Примеры дополнений #1 Network- и Storage-плагины #2 Ingress-контроллеры #3 Cert-manager

    #4 Операторы #5 Metrics API #6 Мониторинг и статистика ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  16. kube-state-metrics node-exporter Trickster Prometheus для long-term Ещё поставить: права (RBAC)

    аутентификация Прометея Ещё настроить: Prometheus и Grafana
  17. Prometheus и Grafana kube-state-metrics node-exporter Trickster Prometheus для long-term Ещё

    поставить: права (RBAC) аутентификация Прометея нюансы в конфиге Прометея Ещё настроить:
  18. Примеры дополнений #1 Network- и Storage-плагины #2 Ingress-контроллеры #3 Cert-manager

    #4 Операторы #5 Metrics API #6 Мониторинг и статистика ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  19. Примеры дополнений #1 Network и Storage плагины #2 Ingress контроллеры

    #3 Cert-manager #4 Операторы #5 Metrics API #6 Мониторинг и статистика ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? без дополнений жизни нет
  20. vs

  21. $ my-hook.sh --config { "onKubernetesEvent": [ { "kind": "pod", "event":

    ["add", "update", "delete"] } ] } Настройка хуков
  22. $ my-hook.sh --config { "onKubernetesEvent": [ { "kind": "pod", "event":

    ["add", "update", "delete"] } ] } Настройка хуков
  23. $ my-hook.sh --config { "onKubernetesEvent": [ { "kind": "pod", "event":

    ["add", "update", "delete"] } ] } Настройка хуков
  24. if [[ $1 == "--config" ]] ; then пишем хук

    Внедряем shell-оператор
  25. if [[ $1 == "--config" ]] ; then cat <<

    EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF пишем хук Внедряем shell-оператор
  26. if [[ $1 == "--config" ]] ; then cat <<

    EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF пишем хук Внедряем shell-оператор
  27. if [[ $1 == "--config" ]] ; then cat <<

    EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF пишем хук Внедряем shell-оператор
  28. if [[ $1 == "--config" ]] ; then cat <<

    EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF пишем хук Внедряем shell-оператор
  29. if [[ $1 == "--config" ]] ; then cat <<

    EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF else createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH) kubectl create -n ${createdNamespace} -f - << EOF Kind: Secret ... EOF fi пишем хук Внедряем shell-оператор
  30. if [[ $1 == "--config" ]] ; then cat <<

    EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF else createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH) kubectl create -n ${createdNamespace} -f - << EOF Kind: Secret ... EOF fi пишем хук Внедряем shell-оператор
  31. if [[ $1 == "--config" ]] ; then cat <<

    EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF else createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH) kubectl create -n ${createdNamespace} -f - << EOF Kind: Secret ... EOF fi пишем хук Внедряем shell-оператор
  32. if [[ $1 == "--config" ]] ; then cat <<

    EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF else createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH) kubectl create -n ${createdNamespace} -f - << EOF Kind: Secret ... EOF fi пишем хук Внедряем shell-оператор
  33. FROM flant/shell-operator:v1.0.0-beta.1 ADD my-handler.sh /hooks $ docker build -t registry.example.com/my-operator:v1

    . пишем хук > билдим образ Внедряем shell-оператор
  34. FROM flant/shell-operator:v1.0.0-beta.1 ADD my-handler.sh /hooks $ docker build -t registry.example.com/my-operator:v1

    . $ docker push registry.example.com/my-operator:v1 пишем хук > билдим образ Внедряем shell-оператор
  35. apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-operator spec: template: spec:

    containers: - name: my-operator image: registry.example.com/my-operator:v1 serviceAccountName: my-operator пишем хук > билдим образ > деплоим в кластер Внедряем shell-оператор deployment.yaml
  36. apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-operator spec: template: spec:

    containers: - name: my-operator image: registry.example.com/my-operator:v1 serviceAccountName: my-operator пишем хук > билдим образ > деплоим в кластер Внедряем shell-оператор deployment.yaml
  37. apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-operator spec: template: spec:

    containers: - name: my-operator image: registry.example.com/my-operator:v1 serviceAccountName: my-operator пишем хук > билдим образ > деплоим в кластер Внедряем shell-оператор deployment.yaml
  38. Создать ServiceAccount “my-operator”. пишем хук > билдим образ > деплоим

    в кластер Внедряем shell-оператор deployment.yaml rbac.yaml
  39. Создать ServiceAccount “my-operator”. Создать ClusterRole. пишем хук > билдим образ

    > деплоим в кластер Внедряем shell-оператор deployment.yaml rbac.yaml
  40. пишем хук > билдим образ > деплоим в кластер Внедряем

    shell-оператор deployment.yaml rbac.yaml Создать ServiceAccount “my-operator”. Создать ClusterRole. Привязать ServiceAccount к ClusterRole (ClusterRoleBinding).
  41. пишем хук > билдим образ > деплоим в кластер Внедряем

    shell-оператор deployment.yaml rbac.yaml Создать ServiceAccount “my-operator”. Создать ClusterRole. Привязать ServiceAccount к ClusterRole (ClusterRoleBinding). Всё как обычно!
  42. Что ещё? "onKubernetesEvent": [ { "selector": { "matchLabels": { "foo":

    "bar", } #1 Объекты можно фильтровать!
  43. Что ещё? "onKubernetesEvent": [ { "selector": { "matchLabels": { "foo":

    "bar", }, "matchExpressions": [ { "key": "allow", "operation": "In", "values": ["wan", "warehouse"], }, ], } ... } ] #1 Объекты можно фильтровать!
  44. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "apiVersion": "extensions/v1beta1", "kind":

    "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ...
  45. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "apiVersion": "extensions/v1beta1", "kind":

    "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 273, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ...
  46. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "apiVersion": "extensions/v1beta1", "kind":

    "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ...
  47. { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision":

    "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 16:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ... Что ещё? #2 Механизм дедупликации (jq-фильтр)
  48. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "apiVersion": "extensions/v1beta1", "kind":

    "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ...
  49. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "apiVersion": "extensions/v1beta1", "kind":

    "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ...
  50. { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision":

    "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ... Что ещё? #2 Механизм дедупликации (jq-фильтр)
  51. { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision":

    "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ... Что ещё? #2 Механизм дедупликации (jq-фильтр) jq .spec.replicas
  52. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "replicas": 1 }

    { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ... jq .spec.replicas
  53. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "replicas": 1 }

    { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ... jq .spec.replicas
  54. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "replicas": 1 }

    { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ... jq .spec.replicas
  55. { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision":

    "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ... Что ещё? #2 Механизм дедупликации (jq-фильтр) { "replicas": 1 } jq .spec.replicas
  56. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "replicas": 2 }

    { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ... jq .spec.replicas
  57. Что ещё? #2 Механизм дедупликации (jq-фильтр) { "replicas": 2 }

    { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "170" }, "creationTimestamp": "2017-12-25T18:21:00Z", "generation": 272, "labels": { "heritage": "antiopa", "service": "antiopa" }, "name": "antiopa", "namespace": "antiopa", "resourceVersion": "129971044", "selfLink": "/apis/extensions/v1beta1/namespaces/antiopa/deployments/antiopa", "uid": "5bee440f-e9a0-11e7-be2a-52540080d234" }, "spec": { "progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 2, "selector": { "matchLabels": { "service": "antiopa" } }, "strategy": { "rollingUpdate": { "maxSurge": 1, "maxUnavailable": 1 }, "type": "RollingUpdate" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "antiopaImageId": "sha256_43bd595c4026bfad74b52f71b4579b12f68b65b95ede25f426cd4dfd", "service": "antiopa" } }, "spec": { "containers": [ { "command": [ "/antiopa/antiopa" ], "env": [ { "name": "KUBERNETES_DEPLOYED", "value": "2018-01-10 15:22:44+03:00" }, { "name": "RLOG_LOG_LEVEL", "value": "INFO" }, ... jq .spec.replicas
  58. [ { "binding": "onCreatePod", "resourceEvent": "add", "resourceKind": "pod", "resourceName": "foo",

    "resourceNamespace": "bar" } ] Что ещё? #3 Получение данных от шелл-оператора про объект
  59. Что ещё? Kubernetes-эвенты #4 События бывают разные! { "onKubernetesEvent": [

    { "kind": "pod", "event": ["add", "delete"], }, { "kind": "deployment", "event": ["update"], } ] }
  60. Что ещё? Kubernetes-эвенты schedule (кронтаб) #4 События бывают разные! {

    "schedule": [ { "crontab": "*/10 * * * * *" }, { "crontab": "0 0 0 * * *" } ] }
  61. Что ещё? Kubernetes-эвенты schedule (кронтаб) onStartup #4 События бывают разные!

    { "onStartup": 10, "onKubernetesEvent": [ { "kind": "pod", "event": ["add", "delete"], } ], "schedule": [ { "crontab": "*/10 * * * * *" } ] }
  62. Что ещё? количество ошибок по каждому хуку размер очереди Можно

    отслеживать: #6 Экспорт метрик для Prometheus (/metrics)
  63. Что ещё? #1 Объекты можно фильтровать! #2 Механизм дедупликации (jq-фильтр).

    #3 Получение данных от шелл-оператора про объект. #4 События бывают разные! #6 Экспорт метрик для Prometheus (/metrics). #5 Асинхронность и гарантии.
  64. Go

  65. Go

  66. Хотите Kubernetes с комфортом? устанавливать Дополнения разрабатывать Операторы Потребуется: Решения:

    ничего не автоматизировать крон-скрипты шелл-операторы классические операторы #!
  67. На обслуживании: 8 кластеров Ingress controller Ingress controller Ingress controller

    Ingress controller Ingress controller Ingress controller Ingress controller Ingress controller Копилка опыта: install-ingress.sh #1 шаблонизатор
  68. На обслуживании: 8 кластеров Ingress controller Ingress controller Ingress controller

    Ingress controller Ingress controller Ingress controller Ingress controller Ingress controller Копилка опыта: install-ingress.sh #1 шаблонизатор #2 автообновление
  69. На обслуживании: 8 кластеров Ingress controller Ingress controller Ingress controller

    Ingress controller Ingress controller Ingress controller Ingress controller Ingress controller git pull install-ingress.sh Копилка опыта: install-ingress.sh #1 шаблонизатор #2 автообновление
  70. На обслуживании: 8 кластеров Ingress controller Ingress controller Ingress controller

    Ingress controller Ingress controller Ingress controller Ingress controller Ingress controller Копилка опыта: install-ingress.sh #1 шаблонизатор #2 автообновление
  71. install-prometheus.sh На обслуживании: 11 кластеров Копилка опыта: install-ingress.sh # install-prometheus.sh

    adminPassword? passw0rd volumeSize? 140Gi requestsCpu? 2 requestsMemory? 4Gi limitsCpu? 2 limitsMemory? 4Gi userPassword? laresistance #1 шаблонизатор #2 автообновление
  72. install-prometheus.sh На обслуживании: 11 кластеров Копилка опыта: install-ingress.sh # install-prometheus.sh

    adminPassword? passw0rd volumeSize? 140Gi requestsCpu? 2 requestsMemory? 4Gi limitsCpu? 2 limitsMemory? 4Gi userPassword? laresistance Installing prometheus... done #1 шаблонизатор #2 автообновление
  73. install-prometheus.sh На обслуживании: 11 кластеров Копилка опыта: install-ingress.sh # install-prometheus.sh

    adminPassword? passw0rd volumeSize? 140Gi requestsCpu? 2 requestsMemory? 4Gi limitsCpu? 2 limitsMemory? 4Gi userPassword? laresistance Installing prometheus... done #1 шаблонизатор #2 автообновление #3 хранение настроек в кластере
  74. install-prometheus.sh На обслуживании: 11 кластеров Копилка опыта: install-ingress.sh #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей # install-prometheus.sh adminPassword? passw0rd volumeSize? 140Gi requestsCpu? 2 requestsMemory? 4Gi limitsCpu? 2 limitsMemory? 4Gi userPassword? laresistance Installing prometheus... done
  75. На обслуживании: 11 кластеров Копилка опыта: install-ingress.sh #1 шаблонизатор #2

    автообновление #3 хранение настроек в кластере #4 генерация паролей install-prometheus.sh
  76. На обслуживании: 17 кластеров Копилка опыта: install-ingress.sh #1 шаблонизатор #2

    автообновление #3 хранение настроек в кластере #4 генерация паролей install-prometheus.sh
  77. git pull install-ingress.sh На обслуживании: 17 кластеров Копилка опыта: install-ingress.sh

    #1 шаблонизатор #2 автообновление #3 хранение настроек в кластере #4 генерация паролей install-prometheus.sh
  78. git pull install-ingress.sh На обслуживании: 17 кластеров Копилка опыта: install-ingress.sh

    #1 шаблонизатор #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование install-prometheus.sh
  79. На обслуживании: 17 кластеров Копилка опыта: install-ingress.sh #1 шаблонизатор #2

    автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование install-prometheus.sh git checkout stable git pull install-ingress.sh
  80. На обслуживании: 17 кластеров Копилка опыта: install-ingress.sh #1 шаблонизатор #2

    автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование install-prometheus.sh
  81. На обслуживании: 21 кластер Копилка опыта: install-ingress.sh #1 шаблонизатор #2

    автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование install-prometheus.sh
  82. На обслуживании: 21 кластер Копилка опыта: install-ingress.sh #1 шаблонизатор #2

    автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование install-prometheus.sh kubectl apply -f addon.yaml
  83. На обслуживании: 21 кластер Копилка опыта: install-ingress.sh #1 шаблонизатор #2

    автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность install-prometheus.sh kubectl apply -f addon.yaml
  84. На обслуживании: 21 кластер Копилка опыта: install-ingress.sh #1 шаблонизатор #2

    автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность install-prometheus.sh
  85. На обслуживании: 21 кластер Копилка опыта: #1 шаблонизатор #2 автообновление

    #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность install-ingress.sh install-prometheus.sh
  86. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность
  87. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность
  88. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата
  89. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery
  90. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery install-ingress.sh # install-ingress.sh aws Installing ingress for aws… done install-prometheus.sh # install-prometheus.sh adminPassword? passw0rd volumeSize? 140Gi requestsCpu? 2 requestsMemory? 4Gi limitsCpu? 2 limitsMemory? 4Gi userPassword? laresistance Installing prometheus... done
  91. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery install-ingress.sh # install-ingress.sh aws Installing ingress for aws… done install-prometheus.sh # install-prometheus.sh adminPassword? passw0rd volumeSize? 140Gi requestsCpu? 2 requestsMemory? 4Gi limitsCpu? 2 limitsMemory? 4Gi userPassword? laresistance Installing prometheus... done
  92. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery install-ingress.sh # install-ingress.sh aws Installing ingress for aws… done install-prometheus.sh # install-prometheus.sh adminPassword? passw0rd volumeSize? 140Gi requestsCpu? 2 requestsMemory? 4Gi limitsCpu? 2 limitsMemory? 4Gi userPassword? laresistance Installing prometheus... done
  93. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery
  94. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery
  95. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery
  96. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery Ingress controller Ingress controller Ingress controller
  97. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery Ingress controller Ingress controller Ingress controller dedicated: ingress
  98. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery Ingress controller Ingress controller Ingress controller dedicated: ingress
  99. На обслуживании: 21 кластер install-ingress.sh install-prometheus.sh Копилка опыта: #1 шаблонизатор

    #2 автообновление #3 хранение настроек в кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery Ingress controller dedicated: ingress
  100. #!

  101. #! #! #! #! #! #! #! #! #! #!

    #! #! #! #! #! глобальные хуки
  102. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  103. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  104. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  105. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  106. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  107. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  108. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  109. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  110. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  111. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  112. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  113. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  114. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  115. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  116. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #! #!
  117. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #! #!
  118. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  119. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  120. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #! Configmap
  121. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #! Configmap
  122. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #! Configmap
  123. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  124. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  125. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  126. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  127. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  128. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  129. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  130. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #! stable ea my-feature
  131. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  132. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  133. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #! /metrics
  134. Копилка опыта: #1 шаблонизатор #2 автообновление #3 хранение настроек в

    кластере #4 генерация паролей #5 стейджирование #6 декларативность #7 контроль результата #8 discovery #9 continuous discovery #! #! #! #! #! #!
  135. #! #! #! #! #! #! #! #! #! #!

    #! #! #! глобальное хранилище values #! #! глобальные хуки
  136. #! #! #! #! #! #! #! #! #! #!

    #! глобальные хуки #! #! #! #! глобальное хранилище values
  137. #! #! #! #! #! #! #! #! #! #!

    #! глобальные хуки #! #! #! #! глобальное хранилище values addon-operator +
  138. #! #! #! #! #! #! #! #! #! #!

    #! глобальные хуки #! #! #! #! глобальное хранилище values
  139. #! #! #! #! #! #! #! #! #! #!

    #! глобальные хуки #! #! #! #! глобальное хранилище values
  140. Спасибо! Наш блог на Хабре habr.com/company/flant Наш YouTube-канал youtube.com/c/flant shell-operator

    github.com/flant/shell-operator addon-operator github.com/flant/addon-operator werf github.com/flant/werf grafana-statusmap github.com/flant/grafana-statusmap kubedog github.com/flant/kubedog Андрей Половов [email protected] Solution-инженер Иван Михейкин Ведущий разработчик [email protected] Андрей Климентьев R&D-таран [email protected]