Kubernetes Operators: розбираємось на прикладі сікретів
Я Олександр Воробйов, AWS Cloud Architect з більш ніж десятирічним досвідом. В SoftServe ми допомагаємо клієнтам з усього світу в оптимізації їхньої хмарної інфраструктури з погляду ефективності використання cloud-технологій, оптимізації витрат тощо.
Сьогодні я хочу поговорити про Kubernetes-оператори. Майже кожен проєкт, над яким ми працюємо, так чи інакше використовує Kubernetes. Щобільше, на основі нашого досвіду ми в SoftServe розробили платформу, яка дає нам можливість збирати Kubernetes-кластери з різних компонентів, як лего, додаючи необхідні модулі для логінгу та моніторингу, CD та сікретів.
На прикладі сікретів я хочу розглянути, чому взагалі існує така штука як Kubernetes-оператори та як вони допомагають в активностях Day 2.
Що таке Kubernetes-оператори
Дизайн Kubernetes-платформи відповідає двом базовим принципам. По-перше — це простота та гнучкість, а по-друге — можливість автоматизації якомога більше задач. З цього ми маємо як сильні сторони куба, так і його обмеження. Його API достатньо простий, а з іншого боку — API недостатньо для автоматизації багатьох процесів. Для цього і придумали Kubernetes-оператори.
Kubernetes-оператори додають необхідну функціональність до контрольної панелі, оператори в циклі перевіряють стан ресурсу, та якщо ресурс не відповідає бажаному стану, то призводять ресурс до цього стану відповідно до логіки оператора.
Загалом, оператор визначається як програмне розширення, яке дозволяє працювати із застосунком, абстрагуючись від понять кубера як деплоймент, под, конфіг-мапа тощо. Оператори використовують Custom Resource (CR) для опису необхідного конфігурації застосунку та Custom Resource Definition (CRD) для опису його стану.
Оператор в циклі звіряє поточний стан та конфігурацію застосунку зі станом, який описаний в CRD, та якщо є розбіжності, приводить у відповідність стан та конфігурацію.
Якщо описати простими словами, то оператор — це застосунок, який працює в Kubernetes та використовує його API, щоб автоматизувати необхідні задачі.
Тепер, коли ми розібрались з тим, що таке Kubernetes-оператор та яка його роль, розгляньмо роботу такого оператора на прикладі.
Практичне застосування
На більшості проєктів, з якими ми працюємо і які використовують Kubernetes для запуску застосунків, ці застосунки майже завжди використовують паролі або інші засоби для того щоб авторизуватись в зовнішніх сервісах.
Найпростіший випадок — це підключення до бази даних або авторизація в зовнішньому API. Для того, щоб це зробити застосунку, треба, по-перше, знати адресу, де зовнішній ресурс розташований, а по-друге — логін-деталі, тобто ім’я користувача з паролем, токен авторизацій тощо.
Ці дані часто зберігаються в зовнішній системі, у випадку AWS це може бути або Parameter Store, або SecretsManager, або, якщо клієнт хоче використовувати рішення без прив’язки до хмар, — Hashicorp Vault.
Ці паролі, токени, адреси зовнішніх ресурсів треба забрати з місця, де вони зберігаються, а далі передати їх в Kubernetes та створити сікрет. Також було б непогано, якби за зміни значення в хмарному сервісі secret-ресурс в Kubernetes також оновлювався.
Стандартний API Kubernetes нічого не знає про зовнішні системи збереження ресурсів — у нього є API для створення і роботи з сікретами Kubernetes та й все. Тому нам треба мати щось, що буде спілкуватися з зовнішніми системами збереження секретних даних з одного боку, а з іншого — створювати та оновлювати сікрети в Kubernetes. Тут нам і допомагає Kubernetes Operator.
Для роботи з сікретамі Kubernetes спільнота створила External Secrets Operator (ESO). Він розв’язує задачу синхронізації сікрет-ресурсів в Kubernetes з відповідними значеннями в системах зберігання як Hashicorp Vault, AWS SecretsManager та інших.
Розгляньмо на прикладі ESO, як працює Kubernetes-оператор.
Тож, ESO використовує Custom Resource(CR) для того, щоб розширити та доповнити Kubernetes API. Перелік CR, які використовує ESO, можна подивитись тут.
Нижче — список CRD, які були задеплоєні ESO:
#kubectl get crd -o json | jq -r '.items[]|select(.metadata.name | contains("external-secrets.io"))|.metadata.name' acraccesstokens.generators.external-secrets.io clusterexternalsecrets.external-secrets.io clustersecretstores.external-secrets.io ecrauthorizationtokens.generators.external-secrets.io externalsecrets.external-secrets.io fakes.generators.external-secrets.io gcraccesstokens.generators.external-secrets.io passwords.generators.external-secrets.io pushsecrets.external-secrets.io secretstores.external-secrets.io vaultdynamicsecrets.generators.external-secrets.io webhooks.generators.external-secrets.io
Як ми бачимо, ESO створив CRD, які розширюють можливості стандартного Kubernetes API, за допомогою яких відстежує статус та конфігурацію ресурсів. З CRD користувач Kubernetes-кластера може створювати додаткові ресурси, які не доступні за замовчуванням, такі як secretstore та externalsecret.
Нижче наведено приклад ресурсу clustersecretstore, через якого ми описуємо, де розташовується наше сховище паролів або інших параметрів та як авторизуватися, щоб отримати до нього доступ:
❯ k get clustersecretstore ps-cluster-default-secretstore -o yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: creationTimestamp: "2024-03-06T08:46:15Z" generation: 4 labels: argocd.argoproj.io/instance: argo-proj name: ps-cluster-default-secretstore resourceVersion: "3251005210" uid: 4385f262-f8e1-4719-9b51-0d474b678864 spec: provider: aws: auth: jwt: serviceAccountRef: name: external-secret-default-secretstore namespace: external-secrets region: us-east-1 service: ParameterStore status: capabilities: ReadWrite conditions: - lastTransitionTime: "2024-03-06T08:46:31Z" message: store validated reason: Valid status: "True" type: Ready
В цьому прикладі ми створюємо secretstore, який буде надавати нам доступ до даних, які зберігаються в AWS Systems Manager Parameter Store. Та для того, щоб мати доступ до цього сервісу, ESO-оператор цього secretstore буде використовувати сервіс-акаунт з іменем external-default-secretstore.
Але для створення кінцевого сікрет-ресурсу, який буде використовуватись застосунками, встановленими в Kubernetes-кластер, нам необхідно мати опис того, що саме ми хочемо забирати зі сховища та який саме сікрет нам треба створювати. Для цього оператор ESO має інший CR externalsecret:
kubectl get externalsecret -n secret my-external-secret -o yaml apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: creationTimestamp: "2024-03-30T18:49:32Z" generation: 1 labels: argocd.argoproj.io/instance: argo-proj name: my-eso-resource namespace: secret resourceVersion: "3264145932" uid: 0493c90d-a709-47d9-b492-88ab0ebe8118 spec: data: - remoteRef: conversionStrategy: Default decodingStrategy: None key: /apps/cloud-runtime/security/my-secret/secret-token metadataPolicy: None secretKey: SECRET_TUNNEL_TOKEN refreshInterval: 1h secretStoreRef: kind: ClusterSecretStore name: ps-cluster-default-secretstore target: creationPolicy: Owner deletionPolicy: Retain template: engineVersion: v2 mergePolicy: Replace status: binding: name: my-secret-tunnel-token conditions: - lastTransitionTime: "2024-04-10T15:19:31Z" message: Secret was synced reason: SecretSynced status: "True" type: Ready refreshTime: "2024-04-12T11:19:48Z"
Ну і нарешті, коли ми створили externalsecret-ресурс та все правильно вказали, ESO за допомогою цих CRD створить сікрет-ресурс, який ми зможемо використати в нашому застосунку:
#k get secrets -n secret my-secret-tunnel-token -o yaml apiVersion: v1 data: SECRET_TUNNEL_TOKEN: 1234567890 immutable: false kind: Secret metadata: annotations: creationTimestamp: "2024-03-30T18:49:32Z" labels: reconcile.external-secrets.io/created-by: b4b359b6d2e71b206b53004058721c29 name: orca-tunnel-token namespace: orca ownerReferences: - apiVersion: external-secrets.io/v1beta1 blockOwnerDeletion: true controller: true kind: ExternalSecret name: my-secret-tunnel-token uid: 0493c90d-a709-47d9-b492-88ab0ebe8118 resourceVersion: "3193039237" uid: f10ebe7b-4885-4ad9-9809-4842191ad72b type: Opaque
Тобто за допомогою двох ресурсів, secretstore та externalsecret, ми описали, де в нас зберігаються значення необхідних параметрів, як ці значення забрати з зовнішнього ресурсу та як створити сікрет об’єкт в Kubernetes, який буде містити ці дані.
Також за допомогою параметру refreshInterval ресурсу externalsecret ми налаштували період оновлення значення сікрет-ресурсу, якщо це значення зміниться в зовнішній системі.
Тобто ми розширили стандартний API Kubernetes та додали можливість автоматичного створення сікрет-ресурсів та також автоматичного завантаження даних з зовнішніх систем зберігання параметрів або сікретів.
З погляду кінцевого застосунку, який працює в Kubernetes, він споживає сікрет-ресурс, який для нього створив ESO, та взагалі не в курсі, як той ресурс створено та звідки взялись дані. Таким чином, в адміністратора Kubernetes-кластера є можливість зміни зовнішнього провайдера параметрів та сікретів.
Якщо ми хочемо переїхати з AWS SystemsManager ParameterStore на, наприклад, Hashicorp Vault, все, що нам треба зробити, — це оновити secretstore-ресурс, щоб він забирав дані з іншого ендпоінту та перевірити, що externalsecrets-ресурс посилається на коректний шлях в зовнішньому провайдері.
Сам сікрет-ресурс Kubernetes залишиться без змін і для кінцевого застосунку, який використовує цей ресурс нічого не зміниться.
5 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів