- PVSM.RU - https://www.pvsm.ru -

Необходимость подхвата ресурсов кластера Kubernetes может возникнуть в боевых условиях, когда нельзя просто пересоздать их инструментами Helm. Можно выделить две основные причины:
В нашем же случае, решение потребовалось для подхвата работающих ingress-nginx [1]'ов при интеграции нашего оператора Kubernetes.
Для Helm категорически недопустимо, чтобы ресурсы, которыми он управляет, были созданы не им.
«Если в вашей команде ресурсы релиза могут изменяться вручную, готовьтесь столкнуться с проблемами, описанными в разделе: [BUG] После выката состояние ресурсов релиза в кластере не соответствуют описанному Helm-чарту». (из нашей прошлой статьи [2])
Как уже отмечалось ранее, Helm работает следующим образом:
helm install, helm upgrade) Helm сохраняет сгенерированные манифесты релиза в storage backend [3]. По умолчанию используется ConfigMaps: для каждой ревизии релиза создаётся ConfigMap в том же пространстве имён, в котором запущен Tiller.helm upgrade) Helm сравнивает новые сгенерированные манифесты со старыми манифестами последней DEPLOYED-ревизии релиза из ConfigMap, а получившуюся разницу применяет в Kubernetes.Основываясь на этих особенностях, мы пришли к тому, что достаточно пропатчить ConfigMap (storage backend релиза), чтобы подхватить, т.е. усыновить существующие ресурсы в кластере.
Tiller именует ConfigMap в следующем формате: %RELEASE_NAME.v%REVISION. Чтобы получить существующие записи, необходимо выполнить команду kubectl get cm -l OWNER=TILLER --namespace kube-system (по умолчанию Tiller устанавливается в пространство имён kube-system — иначе необходимо указать используемый).
$ kubectl get cm -l OWNER=TILLER -n kube-system
NAME DATA AGE
release_name_1.v618 1 5d
release_name_1.v619 1 1d
release_name_2.v1 1 2d
release_name_2.v2 1 3d
Каждый ConfigMap представлен в таком формате:
apiVersion: v1
data:
release: H4sIAHEEd1wCA5WQwWrDMAyG734Kwc52mtvwtafdAh27FsURjaljG1kp5O3nNGGjhcJ21M/nT7+stVZvcEozO7LAFAgLnSNOdG4boSkHFCpNIb55R2bBKSjM/ou4+BQt3Fp19XGwcNoINZHggIJWAayaH6leJ/24oTIBewplpQEwZ3Ode+JIdanxqXkw/D4CGClMpoyNG5HlmdAH05rDC6WPRTC6p2Iv4AkjXmjQ/WLh04dArEomt9aVJVfHMcxFiD+6muTEsl+i74OF961FpZEvJN09HEXyHmdOklwK1X7s9my7eYdK7egk8b8/6M+HfwNgE0MSAgIAAA==
kind: ConfigMap
metadata:
creationTimestamp: 2019-02-08T11:12:38Z
labels:
MODIFIED_AT: "1550488348"
NAME: release_name_1
OWNER: TILLER
STATUS: DEPLOYED
VERSION: "618"
name: release_name_1.v618
namespace: kube-system
resourceVersion: "298818981"
selfLink: /api/v1/namespaces/kube-system/configmaps/release_name_1.v618
uid: 71c3e6f3-2b92-11e9-9b3c-525400a97005
Сгенерированные манифесты хранятся в бинарном виде (в примере выше по ключу .data.release), поэтому создавать релиз мы решили штатными средствами Helm, но со специальной заглушкой, которая позже заменяется на манифесты выбранных ресурсов.
Алгоритм решения — следующий:
manifest.yaml с манифестами ресурсов для усыновления (подробнее этот пункт будет разобран ниже).templates/stub.yaml с заглушкой, что по длине будет равна количеству символов в manifest.yaml (в процессе экспериментов выяснилось, что количество байтов должно совпадать). В качестве заглушки должен выбираться воспроизводимый набор символов, который останется после генерации и сохранится в storage backend. Для простоты и наглядности используется #, т.е.:
{{ repeat ${manifest_file_length} "#" }}
helm install и helm upgrade --install.manifest.yaml, которые были выбраны для усыновления на первом шаге:
stub=$(printf '#%.0s' $(seq 1 ${manifest_file_length}))
release_data=$(kubectl get -n ${tiller_namespace} cm/${release_name}.v1 -o json | jq .data.release -r)
updated_release_data=$(echo ${release_data} | base64 -d | zcat | sed "s/${stub}/$(sed -z 's/n/\n/g' ${manifest_file_path} | sed -z 's///\//g')/" | gzip -9 | base64 -w0)
kubectl patch -n ${tiller_namespace} cm/${release_name}.v1 -p '{"data":{"release":"'${updated_release_data}'"}}'
Gist с описанной выше реализацией доступен по ссылке [4]:
$ ./script.sh
Example:
./script.sh foo bar-prod manifest.yaml
Usage:
./script.sh CHART_NAME RELEASE_NAME MANIFEST_FILE_TO_ADOPT [TILLER_NAMESPACE]
В результате выполнения скрипта создаётся релиз RELEASE_NAME. Он связывается с ресурсами, манифесты которых описаны в файле MANIFEST_FILE_TO_ADOPT. Также генерируется чарт CHART_NAME, который может быть использован для дальнейшего сопровождения манифестов и релиза в частности.
При подготовке манифеста с ресурсами необходимо удалить служебные поля, которые используются Kubernetes (это динамические служебные данные, поэтому неправильно версионировать их в Helm). В идеальном мире подготовка сводится к одной команде: kubectl get RESOURCE -o yaml --export. Ведь документация гласит:
--export=false: If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.
… но, как показала практика, опция --export ещё сыровата, поэтому потребуется дополнительное форматирование манифестов. В манифесте service/release-name-habr, представленном ниже, необходимо удалить поля creationTimestamp и selfLink.
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.3", GitCommit:"721bfa751924da8d1680787490c54b9179b1fed0", GitTreeState:"clean", BuildDate:"2019-02-01T20:08:12Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.3", GitCommit:"721bfa751924da8d1680787490c54b9179b1fed0", GitTreeState:"clean", BuildDate:"2019-02-01T20:00:57Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"}
apiVersion: v1
kind: Service
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"release-name","app.kubernetes.io/managed-by":"Tiller","app.kubernetes.io/name":"habr","helm.sh/chart":"habr-0.1.0"},"name":"release-name-habr","namespace":"default"},"spec":{"ports":[{"name":"http","port":80,"protocol":"TCP","targetPort":"http"}],"selector":{"app.kubernetes.io/instance":"release-name","app.kubernetes.io/name":"habr"},"type":"ClusterIP"}}
creationTimestamp: null
labels:
app.kubernetes.io/instance: release-name
app.kubernetes.io/managed-by: Tiller
app.kubernetes.io/name: habr
helm.sh/chart: habr-0.1.0
name: release-name-habr
selfLink: /api/v1/namespaces/default/services/release-name-habr
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app.kubernetes.io/instance: release-name
app.kubernetes.io/name: habr
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
Далее представлены примеры использования скрипта. Оба они демонстрируют, как с помощью скрипта можно усыновить работающие в кластере ресурсы и впоследствии удалить их средствами Helm.
Описанное в статье решение может быть доработано и использоваться не только для усыновления Kubernetes-ресурсов с нуля, но и для добавления их в существующие релизы.
В настоящий момент нет решений, позволяющих подхватить существующие в кластере ресурсы, перевести их под управление Helm. Не исключено, что в Helm 3 будет реализовано решение, покрывающее данную проблему (по крайней мере, есть proposal [7] на этот счёт).
Другое из цикла K8s tips & tricks:
Читайте также в нашем блоге:
Автор: aigrychev
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/sistemnoe-administrirovanie/310601
Ссылки в тексте:
[1] ingress-nginx: https://kubernetes.github.io/ingress-nginx/
[2] прошлой статьи: https://habr.com/ru/company/flant/blog/438814/
[3] storage backend: https://helm.sh/docs/using_helm/#storage-backends
[4] ссылке: https://gist.github.com/alexey-igrychev/06a8a9705b7cb122b24cc5a620e5e4d6
[5] Image: https://asciinema.org/a/039LktlJS9CQsiy4ytlNluB7z
[6] Image: https://asciinema.org/a/iiG7oOKsghV4pAlOAF7iNgYFv
[7] proposal: https://github.com/helm/helm/issues/2730
[8] О выделении узлов и о нагрузках на веб-приложение: https://habr.com/ru/company/flant/blog/432748/
[9] Доступ к dev-площадкам: https://habr.com/ru/company/flant/blog/427745/
[10] Ускоряем bootstrap больших баз данных: https://habr.com/ru/company/flant/blog/417509/
[11] Пакетный менеджер для Kubernetes — Helm: прошлое, настоящее, будущее: https://habr.com/ru/company/flant/blog/417079/
[12] Практика с dapp. Часть 2. Деплой Docker-образов в Kubernetes с помощью Helm: https://habr.com/ru/company/flant/blog/336170/
[13] Источник: https://habr.com/ru/post/441964/?utm_campaign=441964
Нажмите здесь для печати.