diff --git a/content/ru/docs/concepts/cluster-administration/manage-deployment.md b/content/ru/docs/concepts/cluster-administration/manage-deployment.md new file mode 100644 index 00000000000..e4c96fbd0d5 --- /dev/null +++ b/content/ru/docs/concepts/cluster-administration/manage-deployment.md @@ -0,0 +1,452 @@ +--- +reviewers: +title: Управление ресурсами +content_type: concept +weight: 40 +--- + + + +Итак, вы развернули приложение и настроили доступ к нему с помощью сервиса. Что дальше? Kubernetes предоставляет ряд инструментов, помогающих управлять развертыванием приложений, включая их масштабирование и обновление. Среди особенностей, которые мы обсудим более подробно, — [конфигурационные файлы](/docs/concepts/configuration/overview/) и [лейблы](/docs/concepts/overview/working-with-objects/labels/). + + + +## Организация конфигураций ресурсов + +Многие приложения требуют создания нескольких ресурсов типа Deployment и Service. Управление ими можно упростить, сгруппировав в один YAML-файл (со строкой "---" в качестве разделителя). Например: + +{{< codenew file="application/nginx-app.yaml" >}} + +Можно создавать сразу несколько ресурсов: + +```shell +kubectl apply -f https://k8s.io/examples/application/nginx-app.yaml +``` + +```shell +service/my-nginx-svc created +deployment.apps/my-nginx created +``` + +Ресурсы будут создаваться в порядке, в котором они описаны в файле. Таким образом, первым лучше всего описать сервис — это позволит планировщику распределять Pod'ы этого сервиса по мере их создания контроллером (контроллерами), например, Deployment'ом. + +`kubectl apply` также может принимать сразу несколько аргументов `-f`: + +```shell +kubectl apply -f https://k8s.io/examples/application/nginx/nginx-svc.yaml -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml +``` + +Кроме того, можно указывать директории вместо отдельных файлов или в дополнение ним: + +```shell +kubectl apply -f https://k8s.io/examples/application/nginx/ +``` + +`kubectl` прочитает все файлы с расширениями `.yaml`, `.yml` или `.json`. + +Рекомендуется размещать ресурсы, имеющие отношение к одному микросервису или уровню приложения, в одном файле, а также группировать все файлы, связанные с приложением, в одной директории. Если уровни вашего приложения связываются друг с другом через DNS, можно развернуть все компоненты стека совместно. + +Также в качестве источника конфигурации можно указать URL — это удобно при создании/настройке с использованием конфигурационных файлов, размещенных на GitHub: + +```shell +kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/application/nginx/nginx-deployment.yaml +``` + +```shell +deployment.apps/my-nginx created +``` + +## Пакетные операции в kubectl + +Создание ресурсов — не единственная операция, которую `kubectl` может выполнять в рамках одной команды. Этот инструмент также способен извлекать имена ресурсов из конфигурационных файлов для выполнения других операций, в частности, для удаления созданных ресурсов: + +```shell +kubectl delete -f https://k8s.io/examples/application/nginx-app.yaml +``` + +```shell +deployment.apps "my-nginx" deleted +service "my-nginx-svc" deleted +``` + +В случае двух ресурсов их можно перечислить в командной строке, используя синтаксис вида resource/name: + +```shell +kubectl delete deployments/my-nginx services/my-nginx-svc +``` + +При большем количестве ресурсов удобнее указать селектор (запрос по лейблу) с помощью `-l` или `--selector`, который отфильтрует ресурсы по их лейблам: + +```shell +kubectl delete deployment,services -l app=nginx +``` + +```shell +deployment.apps "my-nginx" deleted +service "my-nginx-svc" deleted +``` + +Поскольку `kubectl` выводит имена ресурсов в том же синтаксисе, что и получает, можно выстраивать цепочки операций с помощью `$()` или `xargs`: + +```shell +kubectl get $(kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service) +kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service | xargs -i kubectl get {} +``` + +```shell +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +my-nginx-svc LoadBalancer 10.0.0.208 80/TCP 0s +``` + +Приведенные выше команды сначала создают ресурсы в разделе `examples/application/nginx/`, выводят о них информацию в формате `-o name` (то есть в виде пары resource/name). Затем в результатах производится поиск по "service" (`grep`), и информация о найденных ресурсах выводится с помощью `kubectl get`. + +Если ресурсы хранятся в нескольких поддиректориях в пределах одной директории, можно рекурсивно выполнять операции и в этих поддиректориях, указав `--recursive` или `-R` наряду с флагом `--filename`, `-f`. + +Предположим, что существует директория `project/k8s/development`, в которой хранятся все {{< glossary_tooltip text="манифесты" term_id="manifest" >}}, необходимые для dev-окружения, организованные по типу ресурсов: + +``` +project/k8s/development +├── configmap +│ └── my-configmap.yaml +├── deployment +│ └── my-deployment.yaml +└── pvc + └── my-pvc.yaml +``` + +По умолчанию при выполнении массовых операций над `project/k8s/development` команда остановится на первом уровне, не обрабатывая поддиректории. К примеру, попытка создать ресурсы, описанные в этой директории, приведет к ошибке: + +```shell +kubectl apply -f project/k8s/development +``` + +```shell +error: you must provide one or more resources by argument or filename (.json|.yaml|.yml|stdin) +``` + +Вместо этого следует указать флаг `--recursive` или `-R` с флагом `--filename`, `-f`: + +```shell +kubectl apply -f project/k8s/development --recursive +``` + +```shell +configmap/my-config created +deployment.apps/my-deployment created +persistentvolumeclaim/my-pvc created +``` + +Флаг `--recursive` работает с любыми операциями, принимающими флаг `--filename`, `-f`, например: `kubectl {create, get, delete, describe, rollout}` и т.д. + +Флаг `--recursive` также работает для нескольких аргументов `-f`: + +```shell +kubectl apply -f project/k8s/namespaces -f project/k8s/development --recursive +``` + +```shell +namespace/development created +namespace/staging created +configmap/my-config created +deployment.apps/my-deployment created +persistentvolumeclaim/my-pvc created +``` + +В разделе [Инструмент командной строки kubectl](/docs/reference/kubectl/) доступна дополнительная информация о `kubectl`. + +## Эффективное использование лейблов + +Во всех примерах, рассмотренных до этого момента, к ресурсам прикреплялся лишь один лейбл. Однако существуют сценарии, когда необходимо использовать несколько лейблов, чтобы отличить наборы ресурсов друг от друга. + +Например, разные приложения могут использовать разные значения для лейбла `app`, а в случае многоуровневого приложения вроде [гостевой книги](https://github.com/kubernetes/examples/tree/master/guestbook/) лейблы могут указывать на соответствующий уровень. Например, у фронтенда могут быть следующие лейблы: + +```yaml + labels: + app: guestbook + tier: frontend +``` + +в то время как у master'а и slave'а Redis будут лейблы `tier` и, возможно, даже дополнительный лейбл `role`: + +```yaml + labels: + app: guestbook + tier: backend + role: master +``` + +и + +```yaml + labels: + app: guestbook + tier: backend + role: slave +``` + +Лейблы позволяют группировать ресурсы по любому параметру с соответствующим лейблом: + +```shell +kubectl apply -f examples/guestbook/all-in-one/guestbook-all-in-one.yaml +kubectl get pods -Lapp -Ltier -Lrole +``` + +```shell +NAME READY STATUS RESTARTS AGE APP TIER ROLE +guestbook-fe-4nlpb 1/1 Running 0 1m guestbook frontend +guestbook-fe-ght6d 1/1 Running 0 1m guestbook frontend +guestbook-fe-jpy62 1/1 Running 0 1m guestbook frontend +guestbook-redis-master-5pg3b 1/1 Running 0 1m guestbook backend master +guestbook-redis-slave-2q2yf 1/1 Running 0 1m guestbook backend slave +guestbook-redis-slave-qgazl 1/1 Running 0 1m guestbook backend slave +my-nginx-divi2 1/1 Running 0 29m nginx +my-nginx-o0ef1 1/1 Running 0 29m nginx +``` + +```shell +kubectl get pods -lapp=guestbook,role=slave +``` +```shell +NAME READY STATUS RESTARTS AGE +guestbook-redis-slave-2q2yf 1/1 Running 0 3m +guestbook-redis-slave-qgazl 1/1 Running 0 3m +``` + +## Канареечные развертывания + +Еще один сценарий, в котором применение нескольких лейблов как нельзя кстати, — дифференциация развертываний отдельных релизов или конфигураций одного и того же компонента. Как правило, новая версия приложения (указанная с помощью тега образа в шаблоне Pod'а) запускается в так называемом *канареечном* (canary) развертывании параллельно с предыдущей версией. Далее на нее направляется часть реального production-трафика. + +В примере ниже лейбл `track` помогает различить релизы. + +У основного, стабильного релиза он будет иметь значение `stable`: + +```yaml + name: frontend + replicas: 3 + ... + labels: + app: guestbook + tier: frontend + track: stable + ... + image: gb-frontend:v3 +``` + +У нового релиза фронтенда гостевой книги значение этого лейбла будет другим (`canary`), чтобы два набора Pod'ов не пересекались: + +```yaml + name: frontend-canary + replicas: 1 + ... + labels: + app: guestbook + tier: frontend + track: canary + ... + image: gb-frontend:v4 +``` + +Чтобы охватить оба набора реплик, сервис фронтенда следует настроить на выбор общего подмножества их лейблов (т.е. опустить лейбл `track`). В результате трафик будет поступать на обе версии приложения: + +```yaml + selector: + app: guestbook + tier: frontend +``` + +При этом число стабильных и канареечных реплик можно менять, регулируя долю production-трафика, которую будет получать каждая версия приложения (три к одному в нашем случае). Убедившись в стабильности новой версии, можно поменять значение ее лейбла `track` с `canary` на `stable`. + +Более подробный пример доступен в [руководстве по развертыванию Ghost](https://github.com/kelseyhightower/talks/tree/master/kubecon-eu-2016/demo#deploy-a-canary). + +## Обновление лейблов + +Иногда возникает необходимость поменять лейблы у существующих Pod'ов и других ресурсов перед созданием новых. Сделать это можно с помощью команды `kubectl label`. Например, чтобы промаркировать все Pod'ы NGINX как имеющие отношение к фронтенду, выполните следующую команду: + +```shell +kubectl label pods -l app=nginx tier=fe +``` + +```shell +pod/my-nginx-2035384211-j5fhi labeled +pod/my-nginx-2035384211-u2c7e labeled +pod/my-nginx-2035384211-u3t6x labeled +``` + +Сначала она выберет все Pod'ы с лейблом `app=nginx`, а затем пометит их лейблом `tier=fe`. Чтобы просмотреть список этих Pod'ов, выполните: + +```shell +kubectl get pods -l app=nginx -L tier +``` +```shell +NAME READY STATUS RESTARTS AGE TIER +my-nginx-2035384211-j5fhi 1/1 Running 0 23m fe +my-nginx-2035384211-u2c7e 1/1 Running 0 23m fe +my-nginx-2035384211-u3t6x 1/1 Running 0 23m fe +``` + +Будут выведены все Pod'ы `app=nginx` с дополнительным столбцом `tier` (задается с помощью `-L` или `--label-columns`). + +Дополнительную информацию можно найти в разделах [Лейблы](/docs/concepts/overview/working-with-objects/labels/) и [kubectl label](/docs/reference/generated/kubectl/kubectl-commands/#label). + +## Обновление аннотаций + +Иногда возникает потребность навесить на ресурсы аннотации. Аннотации — это произвольные неидентифицирующие метаданные для извлечения клиентами API (инструментами, библиотеками и т.п.). Добавить аннотацию можно с помощью команды `kubectl annotate`. Например: + +```shell +kubectl annotate pods my-nginx-v4-9gw19 description='my frontend running nginx' +kubectl get pods my-nginx-v4-9gw19 -o yaml +``` + +```shell +apiVersion: v1 +kind: pod +metadata: + annotations: + description: my frontend running nginx +... +``` + +Для получения дополнительной информации обратитесь к разделу [Аннотации](/docs/concepts/overview/working-with-objects/annotations/) и описанию команды [kubectl annotate](/docs/reference/generated/kubectl/kubectl-commands/#annotate). + +## Масштабирование приложения + +Для масштабирования приложения можно воспользоваться инструментом `kubectl`. Например, следующая команда уменьшит число реплик с 3 до 1: + +```shell +kubectl scale deployment/my-nginx --replicas=1 +``` + +```shell +deployment.apps/my-nginx scaled +``` + +После ее применения количество Pod'ов под управлением соответствующего объекта Deployment сократится до одного: + +```shell +kubectl get pods -l app=nginx +``` + +```shell +NAME READY STATUS RESTARTS AGE +my-nginx-2035384211-j5fhi 1/1 Running 0 30m +``` + +Чтобы система автоматически выбирала необходимое количество реплик NGINX в диапазоне от 1 до 3, выполните следующую команду: + +```shell +kubectl autoscale deployment/my-nginx --min=1 --max=3 +``` + +```shell +horizontalpodautoscaler.autoscaling/my-nginx autoscaled +``` + +Теперь количество реплик NGINX будет автоматически увеличиваться и уменьшаться по мере необходимости. + +Для получения дополнительной информации см. разделы [kubectl scale](/docs/reference/generated/kubectl/kubectl-commands/#scale), [kubectl autoscale](/docs/reference/generated/kubectl/kubectl-commands/#autoscale) и [horizontal pod autoscaler](/docs/tasks/run-application/horizontal-pod-autoscale/). + +## Обновление ресурсов "на месте" + +Иногда возникает необходимость внести мелкие обновления в созданные ресурсы, не требующие их пересоздания. + +### kubectl apply + +Хранить конфигурационные файлы рекомендуемтся в системе контроля версий (см. [конфигурация как код](https://martinfowler.com/bliki/InfrastructureAsCode.html)). В этом случае их можно будет поддерживать и версионировать вместе с кодом ресурсов, которые те конфигурируют. Далее с помощью команды [`kubectl apply`](/docs/reference/generated/kubectl/kubectl-commands/#apply) изменения, внесенные в конфигурацию, можно применить к кластеру. + +Она сравнит новую версию конфигурации с предыдущей и применит внесенные изменения, не меняя параметры, установленные автоматически, не затронутые новой редакцией конфигурации. + +```shell +kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml +deployment.apps/my-nginx configured +``` + +Обратите внимание, что `kubectl apply` добавляет к ресурсу аннотацию, помогающую отслеживать изменения в конфигурации с момента предыдущего вызова. При вызове `kubectl apply` проводит трехстороннее сравнение (three-way diff) предыдущей конфигурации, ее текущей и новой версий, которое определяет, какие правки следует внести в ресурс. + +На данный момент ресурсы в Kubernetes создаются без данной аннотации, поэтому при первом вызове `kubectl apply` проведет двустороннее сравнение входных данных и текущей конфигурации ресурса. Кроме того, она не cможет определить, какие свойства, заданные при создании ресурса, требуют удаления (соответственно, не будет их удалять). + +При всех последующих вызовах `kubectl apply` и других команд, меняющих конфигурацию, таких как `kubectl replace` и `kubectl edit`, аннотация будет обновляться. В результате `kubectl apply` сможет проводить трехстороннее сравнение (three-way diff), определяя, какие свойства требуют удаления, и удалять их. + +### kubectl edit + +Ресурсы также можно обновлять с помощью команды `kubectl edit`: + +```shell +kubectl edit deployment/my-nginx +``` + +По сути, `kubectl edit` объединяет в себе логику нескольких команд, упрощая жизнь пользователям: сначала она получает (`get`) ресурс, вызывает текстовый редактор, а затем применяет (`apply`) обновленную конфигурацию: + +```shell +kubectl get deployment my-nginx -o yaml > /tmp/nginx.yaml +vi /tmp/nginx.yaml +# внесите правки и сохраните файл + +kubectl apply -f /tmp/nginx.yaml +deployment.apps/my-nginx configured + +rm /tmp/nginx.yaml +``` + +Обратите внимание, что задать предпочитаемый текстовый редактор можно с помощью переменных окружения `EDITOR` или `KUBE_EDITOR`. + +За дополнительной информацией обратитесь к разделу [kubectl edit](/docs/reference/generated/kubectl/kubectl-commands/#edit). + +### kubectl patch + +Команда `kubectl patch` обновляет объекты API "на месте". Она поддерживает форматы JSON patch, JSON merge patch и strategic merge patch. См. разделы [Обновление объектов API "на месте" с помощью kubectl patch](/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/) и [kubectl patch](/docs/reference/generated/kubectl/kubectl-commands/#patch). + +## Обновления, требующие перерыва в работе + +Иногда может понадобиться обновить поля ресурса, которые нельзя изменить после инициализации, или возникнет необходимость немедленно внести рекурсивные изменения, например, "починить" сбойные Pod'ы, созданные Deployment'ом. Для этого можно воспользоваться командой `replace --force`, которая удалит и пересоздаст ресурс. Вот как можно внести правки в исходный файл конфигурации в нашем примере: + +```shell +kubectl replace -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml --force +``` + +```shell +deployment.apps/my-nginx deleted +deployment.apps/my-nginx replaced +``` + +## Обновление приложения без перерыва в работе + +В какой-то момент возникнет необходимость обновить развернутое приложение. Обычно это делают, указывая новый образ или тег образа, как в приведенном выше сценарии канареечного развертывания. `kubectl` поддерживает несколько видов обновлений, каждый из которых подходит для разных сценариев. + +Ниже будет рассказано, как создавать и обновлять приложения с помощью объектов Deployment. + +Предположим, что в кластере используется версия NGINX 1.14.2: + +```shell +kubectl create deployment my-nginx --image=nginx:1.14.2 +``` + +```shell +deployment.apps/my-nginx created +``` + +с тремя репликами (чтобы старые и новые ревизии могли сосуществовать): + +```shell +kubectl scale deployment my-nginx --current-replicas=1 --replicas=3 +``` + +``` +deployment.apps/my-nginx scaled +``` + +Чтобы перейти на версию 1.16.1, измените значение параметра `.spec.template.spec.containers[0].image` с `nginx:1.14.2` на `nginx:1.16.1`: + +```shell +kubectl edit deployment/my-nginx +``` + +Вот и все! Deployment декларативно обновит развернутое приложение NGINX "за кулисами". При этом Kubernetes проследит, чтобы число недоступных реплик в каждый момент времени не превышало определенного значения, а также за тем, чтобы количество новых реплик не превышало определенного предела, установленного для желаемого числа Pod'ов. Более подробную информацию об этом можно получить в разделе [Deployment](/docs/concepts/workloads/controllers/deployment/). + + + +## {{% heading "whatsnext" %}} + + +- Узнайте о том, [как использовать `kubectl` для интроспекции и отладки приложений](/docs/tasks/debug/debug-application/debug-running-pod/). +- Ознакомьтесь с [Лучшими практиками и советами по конфигурированию](/docs/concepts/configuration/overview/). + diff --git a/content/ru/examples/application/cassandra/cassandra-service.yaml b/content/ru/examples/application/cassandra/cassandra-service.yaml new file mode 100644 index 00000000000..31bee74b587 --- /dev/null +++ b/content/ru/examples/application/cassandra/cassandra-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: cassandra + name: cassandra +spec: + clusterIP: None + ports: + - port: 9042 + selector: + app: cassandra diff --git a/content/ru/examples/application/cassandra/cassandra-statefulset.yaml b/content/ru/examples/application/cassandra/cassandra-statefulset.yaml new file mode 100644 index 00000000000..a7bdbedc9c5 --- /dev/null +++ b/content/ru/examples/application/cassandra/cassandra-statefulset.yaml @@ -0,0 +1,100 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: cassandra + labels: + app: cassandra +spec: + serviceName: cassandra + replicas: 3 + selector: + matchLabels: + app: cassandra + template: + metadata: + labels: + app: cassandra + spec: + terminationGracePeriodSeconds: 1800 + containers: + - name: cassandra + image: gcr.io/google-samples/cassandra:v13 + imagePullPolicy: Always + ports: + - containerPort: 7000 + name: intra-node + - containerPort: 7001 + name: tls-intra-node + - containerPort: 7199 + name: jmx + - containerPort: 9042 + name: cql + resources: + limits: + cpu: "500m" + memory: 1Gi + requests: + cpu: "500m" + memory: 1Gi + securityContext: + capabilities: + add: + - IPC_LOCK + lifecycle: + preStop: + exec: + command: + - /bin/sh + - -c + - nodetool drain + env: + - name: MAX_HEAP_SIZE + value: 512M + - name: HEAP_NEWSIZE + value: 100M + - name: CASSANDRA_SEEDS + value: "cassandra-0.cassandra.default.svc.cluster.local" + - name: CASSANDRA_CLUSTER_NAME + value: "K8Demo" + - name: CASSANDRA_DC + value: "DC1-K8Demo" + - name: CASSANDRA_RACK + value: "Rack1-K8Demo" + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + readinessProbe: + exec: + command: + - /bin/bash + - -c + - /ready-probe.sh + initialDelaySeconds: 15 + timeoutSeconds: 5 + # These volume mounts are persistent. They are like inline claims, + # but not exactly because the names need to match exactly one of + # the stateful pod volumes. + volumeMounts: + - name: cassandra-data + mountPath: /cassandra_data + # These are converted to volume claims by the controller + # and mounted at the paths mentioned above. + # do not use these in production until ssd GCEPersistentDisk or other ssd pd + volumeClaimTemplates: + - metadata: + name: cassandra-data + spec: + accessModes: [ "ReadWriteOnce" ] + storageClassName: fast + resources: + requests: + storage: 1Gi +--- +kind: StorageClass +apiVersion: storage.k8s.io/v1 +metadata: + name: fast +provisioner: k8s.io/minikube-hostpath +parameters: + type: pd-ssd diff --git a/content/ru/examples/application/deployment-patch.yaml b/content/ru/examples/application/deployment-patch.yaml new file mode 100644 index 00000000000..af12f4cb0c4 --- /dev/null +++ b/content/ru/examples/application/deployment-patch.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: patch-demo +spec: + replicas: 2 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: patch-demo-ctr + image: nginx + tolerations: + - effect: NoSchedule + key: dedicated + value: test-team diff --git a/content/ru/examples/application/deployment-retainkeys.yaml b/content/ru/examples/application/deployment-retainkeys.yaml new file mode 100644 index 00000000000..af63f46d372 --- /dev/null +++ b/content/ru/examples/application/deployment-retainkeys.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: retainkeys-demo +spec: + selector: + matchLabels: + app: nginx + strategy: + rollingUpdate: + maxSurge: 30% + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: retainkeys-demo-ctr + image: nginx diff --git a/content/ru/examples/application/deployment-scale.yaml b/content/ru/examples/application/deployment-scale.yaml new file mode 100644 index 00000000000..01fe96d8456 --- /dev/null +++ b/content/ru/examples/application/deployment-scale.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment +spec: + selector: + matchLabels: + app: nginx + replicas: 4 # Update the replicas from 2 to 4 + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 diff --git a/content/ru/examples/application/deployment-update.yaml b/content/ru/examples/application/deployment-update.yaml new file mode 100644 index 00000000000..1c0b9d1ab8a --- /dev/null +++ b/content/ru/examples/application/deployment-update.yaml @@ -0,0 +1,19 @@ +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.16.1 # Update the version of nginx from 1.14.2 to 1.16.1 + ports: + - containerPort: 80 diff --git a/content/ru/examples/application/deployment.yaml b/content/ru/examples/application/deployment.yaml index f7a4886e4ec..dbed8bc72b9 100644 --- a/content/ru/examples/application/deployment.yaml +++ b/content/ru/examples/application/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 # до версии 1.9.0 нужно использовать apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment @@ -6,7 +6,7 @@ spec: selector: matchLabels: app: nginx - replicas: 2 # запускает 2 пода, созданных по шаблону + replicas: 2 # tells deployment to run 2 pods matching the template template: metadata: labels: diff --git a/content/ru/examples/application/guestbook/frontend-deployment.yaml b/content/ru/examples/application/guestbook/frontend-deployment.yaml new file mode 100644 index 00000000000..f97f20dab67 --- /dev/null +++ b/content/ru/examples/application/guestbook/frontend-deployment.yaml @@ -0,0 +1,29 @@ +# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend +spec: + replicas: 3 + selector: + matchLabels: + app: guestbook + tier: frontend + template: + metadata: + labels: + app: guestbook + tier: frontend + spec: + containers: + - name: php-redis + image: gcr.io/google_samples/gb-frontend:v5 + env: + - name: GET_HOSTS_FROM + value: "dns" + resources: + requests: + cpu: 100m + memory: 100Mi + ports: + - containerPort: 80 \ No newline at end of file diff --git a/content/ru/examples/application/guestbook/frontend-service.yaml b/content/ru/examples/application/guestbook/frontend-service.yaml new file mode 100644 index 00000000000..410c6bbaf29 --- /dev/null +++ b/content/ru/examples/application/guestbook/frontend-service.yaml @@ -0,0 +1,19 @@ +# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook +apiVersion: v1 +kind: Service +metadata: + name: frontend + labels: + app: guestbook + tier: frontend +spec: + # if your cluster supports it, uncomment the following to automatically create + # an external load-balanced IP for the frontend service. + # type: LoadBalancer + #type: LoadBalancer + ports: + # the port that this service should serve on + - port: 80 + selector: + app: guestbook + tier: frontend \ No newline at end of file diff --git a/content/ru/examples/application/guestbook/redis-follower-deployment.yaml b/content/ru/examples/application/guestbook/redis-follower-deployment.yaml new file mode 100644 index 00000000000..c418cf7364f --- /dev/null +++ b/content/ru/examples/application/guestbook/redis-follower-deployment.yaml @@ -0,0 +1,30 @@ +# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis-follower + labels: + app: redis + role: follower + tier: backend +spec: + replicas: 2 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + role: follower + tier: backend + spec: + containers: + - name: follower + image: gcr.io/google_samples/gb-redis-follower:v2 + resources: + requests: + cpu: 100m + memory: 100Mi + ports: + - containerPort: 6379 \ No newline at end of file diff --git a/content/ru/examples/application/guestbook/redis-follower-service.yaml b/content/ru/examples/application/guestbook/redis-follower-service.yaml new file mode 100644 index 00000000000..53283d35c42 --- /dev/null +++ b/content/ru/examples/application/guestbook/redis-follower-service.yaml @@ -0,0 +1,17 @@ +# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook +apiVersion: v1 +kind: Service +metadata: + name: redis-follower + labels: + app: redis + role: follower + tier: backend +spec: + ports: + # the port that this service should serve on + - port: 6379 + selector: + app: redis + role: follower + tier: backend \ No newline at end of file diff --git a/content/ru/examples/application/guestbook/redis-leader-deployment.yaml b/content/ru/examples/application/guestbook/redis-leader-deployment.yaml new file mode 100644 index 00000000000..9c7547291c3 --- /dev/null +++ b/content/ru/examples/application/guestbook/redis-leader-deployment.yaml @@ -0,0 +1,30 @@ +# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis-leader + labels: + app: redis + role: leader + tier: backend +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + role: leader + tier: backend + spec: + containers: + - name: leader + image: "docker.io/redis:6.0.5" + resources: + requests: + cpu: 100m + memory: 100Mi + ports: + - containerPort: 6379 \ No newline at end of file diff --git a/content/ru/examples/application/guestbook/redis-leader-service.yaml b/content/ru/examples/application/guestbook/redis-leader-service.yaml new file mode 100644 index 00000000000..e04cc183d09 --- /dev/null +++ b/content/ru/examples/application/guestbook/redis-leader-service.yaml @@ -0,0 +1,17 @@ +# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook +apiVersion: v1 +kind: Service +metadata: + name: redis-leader + labels: + app: redis + role: leader + tier: backend +spec: + ports: + - port: 6379 + targetPort: 6379 + selector: + app: redis + role: leader + tier: backend \ No newline at end of file diff --git a/content/ru/examples/application/hpa/php-apache.yaml b/content/ru/examples/application/hpa/php-apache.yaml new file mode 100644 index 00000000000..f3f1ef5d4f9 --- /dev/null +++ b/content/ru/examples/application/hpa/php-apache.yaml @@ -0,0 +1,12 @@ +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: php-apache +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: php-apache + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 50 diff --git a/content/ru/examples/application/job/cronjob.yaml b/content/ru/examples/application/job/cronjob.yaml new file mode 100644 index 00000000000..78d0e2d3147 --- /dev/null +++ b/content/ru/examples/application/job/cronjob.yaml @@ -0,0 +1,19 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: hello +spec: + schedule: "* * * * *" + jobTemplate: + spec: + template: + spec: + containers: + - name: hello + image: busybox:1.28 + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - date; echo Hello from the Kubernetes cluster + restartPolicy: OnFailure diff --git a/content/ru/examples/application/job/indexed-job-vol.yaml b/content/ru/examples/application/job/indexed-job-vol.yaml new file mode 100644 index 00000000000..ed40e1cc446 --- /dev/null +++ b/content/ru/examples/application/job/indexed-job-vol.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: 'indexed-job' +spec: + completions: 5 + parallelism: 3 + completionMode: Indexed + template: + spec: + restartPolicy: Never + containers: + - name: 'worker' + image: 'docker.io/library/busybox' + command: + - "rev" + - "/input/data.txt" + volumeMounts: + - mountPath: /input + name: input + volumes: + - name: input + downwardAPI: + items: + - path: "data.txt" + fieldRef: + fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index'] \ No newline at end of file diff --git a/content/ru/examples/application/job/indexed-job.yaml b/content/ru/examples/application/job/indexed-job.yaml new file mode 100644 index 00000000000..5b80d352649 --- /dev/null +++ b/content/ru/examples/application/job/indexed-job.yaml @@ -0,0 +1,35 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: 'indexed-job' +spec: + completions: 5 + parallelism: 3 + completionMode: Indexed + template: + spec: + restartPolicy: Never + initContainers: + - name: 'input' + image: 'docker.io/library/bash' + command: + - "bash" + - "-c" + - | + items=(foo bar baz qux xyz) + echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt + volumeMounts: + - mountPath: /input + name: input + containers: + - name: 'worker' + image: 'docker.io/library/busybox' + command: + - "rev" + - "/input/data.txt" + volumeMounts: + - mountPath: /input + name: input + volumes: + - name: input + emptyDir: {} diff --git a/content/ru/examples/application/job/job-tmpl.yaml b/content/ru/examples/application/job/job-tmpl.yaml new file mode 100644 index 00000000000..d7dbbafd62b --- /dev/null +++ b/content/ru/examples/application/job/job-tmpl.yaml @@ -0,0 +1,18 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: process-item-$ITEM + labels: + jobgroup: jobexample +spec: + template: + metadata: + name: jobexample + labels: + jobgroup: jobexample + spec: + containers: + - name: c + image: busybox:1.28 + command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"] + restartPolicy: Never diff --git a/content/ru/examples/application/job/rabbitmq/Dockerfile b/content/ru/examples/application/job/rabbitmq/Dockerfile new file mode 100644 index 00000000000..50faab23f43 --- /dev/null +++ b/content/ru/examples/application/job/rabbitmq/Dockerfile @@ -0,0 +1,10 @@ +# Specify BROKER_URL and QUEUE when running +FROM ubuntu:18.04 + +RUN apt-get update && \ + apt-get install -y curl ca-certificates amqp-tools python \ + --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* +COPY ./worker.py /worker.py + +CMD /usr/bin/amqp-consume --url=$BROKER_URL -q $QUEUE -c 1 /worker.py diff --git a/content/ru/examples/application/job/rabbitmq/job.yaml b/content/ru/examples/application/job/rabbitmq/job.yaml new file mode 100644 index 00000000000..4e1a61892be --- /dev/null +++ b/content/ru/examples/application/job/rabbitmq/job.yaml @@ -0,0 +1,20 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: job-wq-1 +spec: + completions: 8 + parallelism: 2 + template: + metadata: + name: job-wq-1 + spec: + containers: + - name: c + image: gcr.io//job-wq-1 + env: + - name: BROKER_URL + value: amqp://guest:guest@rabbitmq-service:5672 + - name: QUEUE + value: job1 + restartPolicy: OnFailure diff --git a/content/ru/examples/application/job/rabbitmq/worker.py b/content/ru/examples/application/job/rabbitmq/worker.py new file mode 100644 index 00000000000..88a7fcf96d9 --- /dev/null +++ b/content/ru/examples/application/job/rabbitmq/worker.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +# Just prints standard out and sleeps for 10 seconds. +import sys +import time +print("Processing " + sys.stdin.readlines()[0]) +time.sleep(10) diff --git a/content/ru/examples/application/job/redis/Dockerfile b/content/ru/examples/application/job/redis/Dockerfile new file mode 100644 index 00000000000..2de23b3c983 --- /dev/null +++ b/content/ru/examples/application/job/redis/Dockerfile @@ -0,0 +1,6 @@ +FROM python +RUN pip install redis +COPY ./worker.py /worker.py +COPY ./rediswq.py /rediswq.py + +CMD python worker.py diff --git a/content/ru/examples/application/job/redis/job.yaml b/content/ru/examples/application/job/redis/job.yaml new file mode 100644 index 00000000000..ee7a06c7329 --- /dev/null +++ b/content/ru/examples/application/job/redis/job.yaml @@ -0,0 +1,14 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: job-wq-2 +spec: + parallelism: 2 + template: + metadata: + name: job-wq-2 + spec: + containers: + - name: c + image: gcr.io/myproject/job-wq-2 + restartPolicy: OnFailure diff --git a/content/ru/examples/application/job/redis/redis-pod.yaml b/content/ru/examples/application/job/redis/redis-pod.yaml new file mode 100644 index 00000000000..ae0c43a7935 --- /dev/null +++ b/content/ru/examples/application/job/redis/redis-pod.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: redis-master + labels: + app: redis +spec: + containers: + - name: master + image: redis + env: + - name: MASTER + value: "true" + ports: + - containerPort: 6379 diff --git a/content/ru/examples/application/job/redis/redis-service.yaml b/content/ru/examples/application/job/redis/redis-service.yaml new file mode 100644 index 00000000000..85f2ca2271d --- /dev/null +++ b/content/ru/examples/application/job/redis/redis-service.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: redis +spec: + ports: + - port: 6379 + targetPort: 6379 + selector: + app: redis diff --git a/content/ru/examples/application/job/redis/rediswq.py b/content/ru/examples/application/job/redis/rediswq.py new file mode 100644 index 00000000000..2b8e81ceab7 --- /dev/null +++ b/content/ru/examples/application/job/redis/rediswq.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python + +# Based on http://peter-hoffmann.com/2012/python-simple-queue-redis-queue.html +# and the suggestion in the redis documentation for RPOPLPUSH, at +# http://redis.io/commands/rpoplpush, which suggests how to implement a work-queue. + + +import redis +import uuid +import hashlib + +class RedisWQ(object): + """Simple Finite Work Queue with Redis Backend + + This work queue is finite: as long as no more work is added + after workers start, the workers can detect when the queue + is completely empty. + + The items in the work queue are assumed to have unique values. + + This object is not intended to be used by multiple threads + concurrently. + """ + def __init__(self, name, **redis_kwargs): + """The default connection parameters are: host='localhost', port=6379, db=0 + + The work queue is identified by "name". The library may create other + keys with "name" as a prefix. + """ + self._db = redis.StrictRedis(**redis_kwargs) + # The session ID will uniquely identify this "worker". + self._session = str(uuid.uuid4()) + # Work queue is implemented as two queues: main, and processing. + # Work is initially in main, and moved to processing when a client picks it up. + self._main_q_key = name + self._processing_q_key = name + ":processing" + self._lease_key_prefix = name + ":leased_by_session:" + + def sessionID(self): + """Return the ID for this session.""" + return self._session + + def _main_qsize(self): + """Return the size of the main queue.""" + return self._db.llen(self._main_q_key) + + def _processing_qsize(self): + """Return the size of the main queue.""" + return self._db.llen(self._processing_q_key) + + def empty(self): + """Return True if the queue is empty, including work being done, False otherwise. + + False does not necessarily mean that there is work available to work on right now, + """ + return self._main_qsize() == 0 and self._processing_qsize() == 0 + +# TODO: implement this +# def check_expired_leases(self): +# """Return to the work queueReturn True if the queue is empty, False otherwise.""" +# # Processing list should not be _too_ long since it is approximately as long +# # as the number of active and recently active workers. +# processing = self._db.lrange(self._processing_q_key, 0, -1) +# for item in processing: +# # If the lease key is not present for an item (it expired or was +# # never created because the client crashed before creating it) +# # then move the item back to the main queue so others can work on it. +# if not self._lease_exists(item): +# TODO: transactionally move the key from processing queue to +# to main queue, while detecting if a new lease is created +# or if either queue is modified. + + def _itemkey(self, item): + """Returns a string that uniquely identifies an item (bytes).""" + return hashlib.sha224(item).hexdigest() + + def _lease_exists(self, item): + """True if a lease on 'item' exists.""" + return self._db.exists(self._lease_key_prefix + self._itemkey(item)) + + def lease(self, lease_secs=60, block=True, timeout=None): + """Begin working on an item the work queue. + + Lease the item for lease_secs. After that time, other + workers may consider this client to have crashed or stalled + and pick up the item instead. + + If optional args block is true and timeout is None (the default), block + if necessary until an item is available.""" + if block: + item = self._db.brpoplpush(self._main_q_key, self._processing_q_key, timeout=timeout) + else: + item = self._db.rpoplpush(self._main_q_key, self._processing_q_key) + if item: + # Record that we (this session id) are working on a key. Expire that + # note after the lease timeout. + # Note: if we crash at this line of the program, then GC will see no lease + # for this item a later return it to the main queue. + itemkey = self._itemkey(item) + self._db.setex(self._lease_key_prefix + itemkey, lease_secs, self._session) + return item + + def complete(self, value): + """Complete working on the item with 'value'. + + If the lease expired, the item may not have completed, and some + other worker may have picked it up. There is no indication + of what happened. + """ + self._db.lrem(self._processing_q_key, 0, value) + # If we crash here, then the GC code will try to move the value, but it will + # not be here, which is fine. So this does not need to be a transaction. + itemkey = self._itemkey(value) + self._db.delete(self._lease_key_prefix + itemkey) + +# TODO: add functions to clean up all keys associated with "name" when +# processing is complete. + +# TODO: add a function to add an item to the queue. Atomically +# check if the queue is empty and if so fail to add the item +# since other workers might think work is done and be in the process +# of exiting. + +# TODO(etune): move to my own github for hosting, e.g. github.com/erictune/rediswq-py and +# make it so it can be pip installed by anyone (see +# http://stackoverflow.com/questions/8247605/configuring-so-that-pip-install-can-work-from-github) + +# TODO(etune): finish code to GC expired leases, and call periodically +# e.g. each time lease times out. + diff --git a/content/ru/examples/application/job/redis/worker.py b/content/ru/examples/application/job/redis/worker.py new file mode 100644 index 00000000000..c3523a4e21a --- /dev/null +++ b/content/ru/examples/application/job/redis/worker.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +import time +import rediswq + +host="redis" +# Uncomment next two lines if you do not have Kube-DNS working. +# import os +# host = os.getenv("REDIS_SERVICE_HOST") + +q = rediswq.RedisWQ(name="job2", host=host) +print("Worker with sessionID: " + q.sessionID()) +print("Initial queue state: empty=" + str(q.empty())) +while not q.empty(): + item = q.lease(lease_secs=10, block=True, timeout=2) + if item is not None: + itemstr = item.decode("utf-8") + print("Working on " + itemstr) + time.sleep(10) # Put your actual work here instead of sleep. + q.complete(item) + else: + print("Waiting for work") +print("Queue empty, exiting") diff --git a/content/ru/examples/application/mongodb/mongo-deployment.yaml b/content/ru/examples/application/mongodb/mongo-deployment.yaml new file mode 100644 index 00000000000..04908ce25b1 --- /dev/null +++ b/content/ru/examples/application/mongodb/mongo-deployment.yaml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mongo + labels: + app.kubernetes.io/name: mongo + app.kubernetes.io/component: backend +spec: + selector: + matchLabels: + app.kubernetes.io/name: mongo + app.kubernetes.io/component: backend + replicas: 1 + template: + metadata: + labels: + app.kubernetes.io/name: mongo + app.kubernetes.io/component: backend + spec: + containers: + - name: mongo + image: mongo:4.2 + args: + - --bind_ip + - 0.0.0.0 + resources: + requests: + cpu: 100m + memory: 100Mi + ports: + - containerPort: 27017 diff --git a/content/ru/examples/application/mongodb/mongo-service.yaml b/content/ru/examples/application/mongodb/mongo-service.yaml new file mode 100644 index 00000000000..b9cef607bcf --- /dev/null +++ b/content/ru/examples/application/mongodb/mongo-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: mongo + labels: + app.kubernetes.io/name: mongo + app.kubernetes.io/component: backend +spec: + ports: + - port: 27017 + targetPort: 27017 + selector: + app.kubernetes.io/name: mongo + app.kubernetes.io/component: backend diff --git a/content/ru/examples/application/mysql/mysql-configmap.yaml b/content/ru/examples/application/mysql/mysql-configmap.yaml new file mode 100644 index 00000000000..9adb0344a31 --- /dev/null +++ b/content/ru/examples/application/mysql/mysql-configmap.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mysql + labels: + app: mysql + app.kubernetes.io/name: mysql +data: + primary.cnf: | + # Apply this config only on the primary. + [mysqld] + log-bin + replica.cnf: | + # Apply this config only on replicas. + [mysqld] + super-read-only + diff --git a/content/ru/examples/application/mysql/mysql-deployment.yaml b/content/ru/examples/application/mysql/mysql-deployment.yaml new file mode 100644 index 00000000000..419fbe03d3f --- /dev/null +++ b/content/ru/examples/application/mysql/mysql-deployment.yaml @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: Service +metadata: + name: mysql +spec: + ports: + - port: 3306 + selector: + app: mysql + clusterIP: None +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mysql +spec: + selector: + matchLabels: + app: mysql + strategy: + type: Recreate + template: + metadata: + labels: + app: mysql + spec: + containers: + - image: mysql:5.6 + name: mysql + env: + # Use secret in real usage + - name: MYSQL_ROOT_PASSWORD + value: password + ports: + - containerPort: 3306 + name: mysql + volumeMounts: + - name: mysql-persistent-storage + mountPath: /var/lib/mysql + volumes: + - name: mysql-persistent-storage + persistentVolumeClaim: + claimName: mysql-pv-claim diff --git a/content/ru/examples/application/mysql/mysql-pv.yaml b/content/ru/examples/application/mysql/mysql-pv.yaml new file mode 100644 index 00000000000..c89779a83fd --- /dev/null +++ b/content/ru/examples/application/mysql/mysql-pv.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: mysql-pv-volume + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 20Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data" +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mysql-pv-claim +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi diff --git a/content/ru/examples/application/mysql/mysql-services.yaml b/content/ru/examples/application/mysql/mysql-services.yaml new file mode 100644 index 00000000000..bc015066780 --- /dev/null +++ b/content/ru/examples/application/mysql/mysql-services.yaml @@ -0,0 +1,32 @@ +# Headless service for stable DNS entries of StatefulSet members. +apiVersion: v1 +kind: Service +metadata: + name: mysql + labels: + app: mysql + app.kubernetes.io/name: mysql +spec: + ports: + - name: mysql + port: 3306 + clusterIP: None + selector: + app: mysql +--- +# Client service for connecting to any MySQL instance for reads. +# For writes, you must instead connect to the primary: mysql-0.mysql. +apiVersion: v1 +kind: Service +metadata: + name: mysql-read + labels: + app: mysql + app.kubernetes.io/name: mysql + readonly: "true" +spec: + ports: + - name: mysql + port: 3306 + selector: + app: mysql diff --git a/content/ru/examples/application/mysql/mysql-statefulset.yaml b/content/ru/examples/application/mysql/mysql-statefulset.yaml new file mode 100644 index 00000000000..85563a2abc8 --- /dev/null +++ b/content/ru/examples/application/mysql/mysql-statefulset.yaml @@ -0,0 +1,168 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mysql +spec: + selector: + matchLabels: + app: mysql + app.kubernetes.io/name: mysql + serviceName: mysql + replicas: 3 + template: + metadata: + labels: + app: mysql + app.kubernetes.io/name: mysql + spec: + initContainers: + - name: init-mysql + image: mysql:5.7 + command: + - bash + - "-c" + - | + set -ex + # Generate mysql server-id from pod ordinal index. + [[ `hostname` =~ -([0-9]+)$ ]] || exit 1 + ordinal=${BASH_REMATCH[1]} + echo [mysqld] > /mnt/conf.d/server-id.cnf + # Add an offset to avoid reserved server-id=0 value. + echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf + # Copy appropriate conf.d files from config-map to emptyDir. + if [[ $ordinal -eq 0 ]]; then + cp /mnt/config-map/primary.cnf /mnt/conf.d/ + else + cp /mnt/config-map/replica.cnf /mnt/conf.d/ + fi + volumeMounts: + - name: conf + mountPath: /mnt/conf.d + - name: config-map + mountPath: /mnt/config-map + - name: clone-mysql + image: gcr.io/google-samples/xtrabackup:1.0 + command: + - bash + - "-c" + - | + set -ex + # Skip the clone if data already exists. + [[ -d /var/lib/mysql/mysql ]] && exit 0 + # Skip the clone on primary (ordinal index 0). + [[ `hostname` =~ -([0-9]+)$ ]] || exit 1 + ordinal=${BASH_REMATCH[1]} + [[ $ordinal -eq 0 ]] && exit 0 + # Clone data from previous peer. + ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql + # Prepare the backup. + xtrabackup --prepare --target-dir=/var/lib/mysql + volumeMounts: + - name: data + mountPath: /var/lib/mysql + subPath: mysql + - name: conf + mountPath: /etc/mysql/conf.d + containers: + - name: mysql + image: mysql:5.7 + env: + - name: MYSQL_ALLOW_EMPTY_PASSWORD + value: "1" + ports: + - name: mysql + containerPort: 3306 + volumeMounts: + - name: data + mountPath: /var/lib/mysql + subPath: mysql + - name: conf + mountPath: /etc/mysql/conf.d + resources: + requests: + cpu: 500m + memory: 1Gi + livenessProbe: + exec: + command: ["mysqladmin", "ping"] + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + readinessProbe: + exec: + # Check we can execute queries over TCP (skip-networking is off). + command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"] + initialDelaySeconds: 5 + periodSeconds: 2 + timeoutSeconds: 1 + - name: xtrabackup + image: gcr.io/google-samples/xtrabackup:1.0 + ports: + - name: xtrabackup + containerPort: 3307 + command: + - bash + - "-c" + - | + set -ex + cd /var/lib/mysql + + # Determine binlog position of cloned data, if any. + if [[ -f xtrabackup_slave_info && "x$( change_master_to.sql.in + # Ignore xtrabackup_binlog_info in this case (it's useless). + rm -f xtrabackup_slave_info xtrabackup_binlog_info + elif [[ -f xtrabackup_binlog_info ]]; then + # We're cloning directly from primary. Parse binlog position. + [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1 + rm -f xtrabackup_binlog_info xtrabackup_slave_info + echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\ + MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in + fi + + # Check if we need to complete a clone by starting replication. + if [[ -f change_master_to.sql.in ]]; then + echo "Waiting for mysqld to be ready (accepting connections)" + until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done + + echo "Initializing replication from clone position" + mysql -h 127.0.0.1 \ + -e "$(