Остання, четверта частина, в якій ми встановимо решту контроллерів і додамо пару корисних дрібниць.
Попередні частини:
- Terraform: створення EKS, частина 1 – VPC, Subnets та Endpoints
- Terraform: створення EKS, частина 2 – EKS кластер, WorkerNodes та IAM
- Terraform: створення EKS, частина 3 – установка Karpenter
Зміст
Планування
Що нам залишилось зробити:
- встановити EKS EBS CSI Addon
- встановити ExternalDNS контролер
- встановити AWS Load Balancer Controller
- додати SecretStore CSI Driver та ASCP
- встановити Metrics Server
- додати Vertical Pod Autoscaler та Horizontal Pod Autoscaler
- додати Subscription Filter до EKS Cloudwatch Log Group, щоб збирати логи в Grafana Loki (див. Loki: збір логів з CloudWatch Logs з використанням Lambda Promtail)
- створити Kubernetes StorageClass з
ReclaimPolicy=Retain
– для PVC, диски котрих треба зберігати при видаленні Deployment/StatefulSet
Структура файлів зараз виглядає так:
$ tree . . ├── backend.tf ├── configs │ └── karpenter-provisioner.yaml.tmpl ├── eks.tf ├── karpenter.tf ├── main.tf ├── outputs.tf ├── providers.tf ├── terraform.tfvars ├── variables.tf └── vpc.tf
Поїхали.
EBS CSI driver
Цей аддон можна встановити з Amazon EKS Blueprints Addons, котрий далі будемо використовувати для ExternalDNS, але раз уж ставимо аддони через cluster_addons
в модулі EKS, то давайте і цей зробимо таким же чином.
Для aws-ebs-csi-driver
ServiceAccount нам знадобиться окрема IAM Role – створимо її за допомогою IRSA Terraform Module.
Приклад для EBS CSI є тут – ebs_csi_irsa_role
.
Створюємо файл iam.tf
– тут будемо тримати окремі ресурси, пов’язані з AWS IAM:
module "ebs_csi_irsa_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "${local.env_name}-ebs-csi-role" attach_ebs_csi_policy = true oidc_providers = { ex = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] } } }
Виконуємо terraform init
та деплоїмо:
Далі, знаходимо актуальну версію аддону для EKS 1.27:
$ aws eks describe-addon-versions --addon-name aws-ebs-csi-driver --kubernetes-version 1.27 --query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" --output text v1.22.0-eksbuild.2 True v1.21.0-eksbuild.1 False ...
В модулі EKS аддони встановлюються за допомогою ресурсу aws_eks_addon
, див. main.tf
.
Для версій можемо передати most_recent
або addon_version
. Краще, звісно, задавати версію явно.
Створимо окрему змінну для версій аддонів EKS – додаємо до variables.tf
:
... variable "eks_addons_version" { description = "EKS Add-on versions, will be used in the EKS module for the cluster_addons" type = map(string) }
Додаємо значення до tfvars
:
... eks_addons_version = { coredns = "v1.10.1-eksbuild.3" kube_proxy = "v1.27.4-eksbuild.2" vpc_cni = "v1.14.1-eksbuild.1" aws_ebs_csi_driver = "v1.22.0-eksbuild.2" }
Додаємо aws-ebs-csi-driver
до cluster_addons
в eks.tf
:
module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> 19.0" ... vpc-cni = { addon_version = var.eks_addons_version.vpc_cni } aws-ebs-csi-driver = { addon_version = var.eks_addons_version.aws_ebs_csi_driver service_account_role_arn = module.ebs_csi_irsa_role.iam_role_arn } } ...
Деплоїмо та перевіряємо поди:
$ kk -n kube-system get pod | grep csi ebs-csi-controller-787874f4dd-nlfn2 5/6 Running 0 19s ebs-csi-controller-787874f4dd-xqbcs 5/6 Running 0 19s ebs-csi-node-fvscs 3/3 Running 0 20s ebs-csi-node-jz2ln 3/3 Running 0 20s
Тестування EBS CSI
Створюємо маніфест пода з PVC:
apiVersion: v1 kind: PersistentVolume metadata: name: demo-pv spec: accessModes: - ReadWriteOnce capacity: storage: 1Gi storageClassName: standard hostPath: path: /tmp/demo-pv --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-dynamic spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: gp2 --- apiVersion: v1 kind: Pod metadata: name: pvc-pod spec: containers: - name: pvc-pod-container image: nginx:latest volumeMounts: - mountPath: /data name: data volumes: - name: data persistentVolumeClaim: claimName: pvc-dynamic
Деплоїмо, та перевіряємо статус:
$ kk get pod NAME READY STATUS RESTARTS AGE pvc-pod 1/1 Running 0 106s $ kk get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-dynamic Bound pvc-a83b3021-03d8-458f-ad84-98805ec4963d 1Gi RWO gp2 119s $ kk get pv pvc-a83b3021-03d8-458f-ad84-98805ec4963d NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-a83b3021-03d8-458f-ad84-98805ec4963d 1Gi RWO Delete Bound default/pvc-dynamic gp2 116s
З цим все готово.
Terraform та ExternalDNS
Для ExternalDNS спробуємо Amazon EKS Blueprints Addons. На відміну від того, як ми робили EBS CSI, тут нам не потрібно буде окремо створювати IAM Role, бо модуль створить її сам.
Приклади є у файлі tests/complete/main.tf
.
Правда, в документації чомусь вказана передача параметрів для чарту external_dns_helm_config
(UPD – поки писав цей пост, вже видалили сторінку взагалі), хоча на ділі це призводить до помилки “An argument named “external_dns_helm_config” is not expected here“.
Щоб знайти, як жеж нам передати параметри – йдемо на сторінку модулю в eks-blueprints-addons
, і дивимось які інпути є для external_dns
:
Далі перевіряємо файл main.tf
модулю, де бачимо змінну var.external_dns
, в якій можна передати всі параметри.
Дефолтні версії чартів задаються у тому ж файлі, але вони місцями застарілі, теж задамо свої.
Знаходимо останню версію для ЕxternalDNS:
$ helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/ "external-dns" has been added to your repositories $ helm search repo external-dns/external-dns --versions NAME CHART VERSION APP VERSION DESCRIPTION external-dns/external-dns 1.13.1 0.13.6 ExternalDNS synchronizes exposed Kubernetes Ser... ...
Додаємо змінну для версій чартів:
variable "helm_release_versions" { description = "Helm Chart versions to be deployed into the EKS cluster" type = map(string) }
У файл terraform.tfvars
додаємо значення:
... helm_release_versions = { karpenter = "v0.30.0" external_dns = "1.13.1" }
Зона у нас на кожне оточення своя, одна, тож просто задамо її у varables.tf
як string
:
... variable "external_dns_zone" { type = string description = "AWS Route53 zone to be used by ExternalDNS in domainFilters and its IAM Policy" }
І значення в tfvars
:
... external_dns_zone = "dev.example.co"
Створюємо файл controllers.tf
, і описуємо деплой ExternalDNS.
З data "aws_route53_zone"
отримуємо інформацію про нашу Route53 Hosted Zone, і її ARN передаємо в параметр external_dns_route53_zone_arns
.
Так як ми використовуємо VPC Endpoint для STS, то в аннотації ServiceAccount передаємо eks.amazonaws.com/sts-regional-endpoints="true"
– аналогічно тому, як робили для Karpenter.
У external_dns.values
передаємо бажані параметри – policy
, в domainFilters
наш домен, та задаємо tolerations
, щоб под запускався на наших дефолтних нодах:
data "aws_route53_zone" "example" { name = var.external_dns_zone } module "eks_blueprints_addons" { source = "aws-ia/eks-blueprints-addons/aws" version = "~> 1.7.2" cluster_name = module.eks.cluster_name cluster_endpoint = module.eks.cluster_endpoint cluster_version = module.eks.cluster_version oidc_provider_arn = module.eks.oidc_provider_arn enable_external_dns = true external_dns = { namespace = "kube-system" chart_version = var.helm_release_versions.external_dns values = [ <<-EOT policy: upsert-only domainFilters: [ ${data.aws_route53_zone.example.name} ] tolerations: - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoExecute - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoSchedule EOT ] set = [ { name = "serviceAccount.annotations.eks\\.amazonaws\\.com/sts-regional-endpoints" value = "true" type = "string" } ] } external_dns_route53_zone_arns = [ data.aws_route53_zone.example.arn ] }
Деплоїмо, та перевіряємо под:
$ kk get pod -l app.kubernetes.io/instance=external-dns NAME READY STATUS RESTARTS AGE external-dns-597988f847-rxqds 1/1 Running 0 66s
Тестування ExternalDNS
Створюємо Kubernetes Service з типом LoadBalancer:
apiVersion: v1 kind: Service metadata: name: mywebapp labels: service: nginx annotations: external-dns.alpha.kubernetes.io/hostname: test-dns.dev.example.co spec: selector: service: nginx type: LoadBalancer ports: - port: 80 targetPort: 80
Деплоїмо, ті перевіряємо логи ExternalDNS:
... time="2023-09-13T12:01:39Z" level=info msg="Desired change: CREATE cname-test-dns.dev.example.co TXT [Id: /hostedzone/Z09***BL9]" time="2023-09-13T12:01:39Z" level=info msg="Desired change: CREATE test-dns.dev.example.co A [Id: /hostedzone/Z09***BL9]" time="2023-09-13T12:01:39Z" level=info msg="Desired change: CREATE test-dns.dev.example.co TXT [Id: /hostedzone/Z09***BL9]" time="2023-09-13T12:01:39Z" level=info msg="3 record(s) in zone dev.example.co. [Id: /hostedzone/Z09***BL9] were successfully updated
Готово.
Terraform та AWS Load Balancer Controller
Робимо аналогічно з ExternalDNS – будемо встановлювати з модуля Amazon EKS Blueprints Addons.
Спочатку нам треба протегати публічні і приватні сабнети – див. Subnet Auto Discovery.
Також перевірте, щоб на них був тег kubernetes.io/cluster/${cluster-name} = owned
(має бути, якщо деплоїли з Terraform модулем EKS, як це робили в першій частині).
Додаємо теги через public_subnet_tags
та private_subnet_tags
:
... module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 5.1.1" ... public_subnet_tags = { "kubernetes.io/role/elb" = 1 } private_subnet_tags = { "karpenter.sh/discovery" = local.eks_cluster_name "kubernetes.io/role/internal-elb" = 1 } ... } ...
Створення модулю описано тут>>>.
Знаходимо актуальну версію чарту:
$ helm repo add aws-eks-charts https://aws.github.io/eks-charts "aws-eks-charts" has been added to your repositories $ helm search repo aws-eks-chart/aws-load-balancer-controller --versions NAME CHART VERSION APP VERSION DESCRIPTION aws-eks-chart/aws-load-balancer-controller 1.6.1 v2.6.1 AWS Load Balancer Controller Helm chart for Kub... aws-eks-chart/aws-load-balancer-controller 1.6.0 v2.6.0 AWS Load Balancer Controller Helm chart for Kub... ...
Додаємо версію чарту до змінної helm_release_versions
:
... helm_release_versions = { karpenter = "0.16.3" external_dns = "1.13.1" aws_load_balancer_controller = "1.6.1" }
До файлу controllers.tf
додаємо ресурс aws_load_balancer_controller
:
... enable_aws_load_balancer_controller = true aws_load_balancer_controller = { namespace = "kube-system" chart_version = var.helm_release_versions.aws_load_balancer_controller values = [ <<-EOT tolerations: - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoExecute - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoSchedule EOT ] set = [ { name = "serviceAccount.annotations.eks\\.amazonaws\\.com/sts-regional-endpoints" value = "true" type = "string" } ] } }
Деплоїмо, перевіряємо под:
$ kk get pod | grep aws aws-load-balancer-controller-7bb9d7d8-4m4kz 1/1 Running 0 99s aws-load-balancer-controller-7bb9d7d8-8l58n 1/1 Running 0 99s
Тестування AWS Load Balancer Controller
Додаємо Pod, Service та Ingress:
--- apiVersion: v1 kind: Pod metadata: name: hello-pod labels: app: hello spec: containers: - name: hello-container image: nginxdemos/hello ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: hello-service spec: type: NodePort selector: app: hello ports: - protocol: TCP port: 80 targetPort: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: hello-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing spec: ingressClassName: alb rules: - host: test-dns.dev.example.co http: paths: - path: / pathType: Prefix backend: service: name: hello-service port: number: 80
Деплоїмо, перевіряємо Ingress – чи додався до нього Load Balancer:
$ kk get ingress NAME CLASS HOSTS ADDRESS PORTS AGE hello-ingress alb test-dns.dev.example.co k8s-kubesyst-helloing-***.us-east-1.elb.amazonaws.com 80 45s
З цим теж готово.
Terraform та SecretStore CSI Driver і ASCP
SecretStore CSI Driver та AWS Secrets and Configuration Provider (ASCP) нам потрібні, щоб в Kubernetes підключати значення з AWS Secrets Manager та Parameter Store, див. AWS: Kubernetes – інтеграція AWS Secrets Manager та Parameter Store.
Тут теж зробимо з Amazon EKS Blueprints Addons – див. secrets_store_csi_driver
та secrets_store_csi_driver_provider_aws
.
Ніяк налаштувань тут не треба – тільки додати Tolerations.
Знаходимо версії:
$ helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts "secrets-store-csi-driver" has been added to your repositories $ helm repo add secrets-store-csi-driver-provider-aws https://aws.github.io/secrets-store-csi-driver-provider-aws "secrets-store-csi-driver-provider-aws" has been added to your repositories $ helm search repo secrets-store-csi-driver/secrets-store-csi-driver NAME CHART VERSION APP VERSION DESCRIPTION secrets-store-csi-driver/secrets-store-csi-driver 1.3.4 1.3.4 A Helm chart to install the SecretsStore CSI Dr... $ helm search repo secrets-store-csi-driver-provider-aws/secrets-store-csi-driver-provider-aws NAME CHART VERSION APP VERSION DESCRIPTION secrets-store-csi-driver-provider-aws/secrets-s... 0.3.4 A Helm chart for the AWS Secrets Manager and Co...
Додаємо нові значення до змінної helm_release_versions
у terraform.tfvars
:
... helm_release_versions = { karpenter = "0.16.3" external_dns = "1.13.1" aws_load_balancer_controller = "1.6.1" secrets_store_csi_driver = "1.3.4" secrets_store_csi_driver_provider_aws = "0.3.4" }
Додаємо самі модулі до controllers.tf
:
... enable_secrets_store_csi_driver = true secrets_store_csi_driver = { namespace = "kube-system" chart_version = var.helm_release_versions.secrets_store_csi_driver values = [ <<-EOT syncSecret: enabled: true tolerations: - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoExecute - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoSchedule EOT ] } enable_secrets_store_csi_driver_provider_aws = true secrets_store_csi_driver_provider_aws = { namespace = "kube-system" chart_version = var.helm_release_versions.secrets_store_csi_driver_provider_aws values = [ <<-EOT tolerations: - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoExecute - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoSchedule EOT ] } }
IAM Policy
Для IAM Roles, які ми потім будемо додавати до сервісів, якім потрібен доступ до AWS SecretsManager/ParameterStore треба буде підключати політику, яка дозволяє доступ до відповідних AWS API викликів.
Створимо її разом з EKS в нашому файлі iam.tf
:
resource "aws_iam_policy" "sm_param_access" { name = "sm-and-param-store-ro-access-policy" description = "Additional policy to access Serets Manager and Parameter Store" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = [ "secretsmanager:DescribeSecret", "secretsmanager:GetSecretValue", "ssm:DescribeParameters", "ssm:GetParameter", "ssm:GetParameters", "ssm:GetParametersByPath" ] Effect = "Allow" Resource = "*" }, ] }) }
Деплоїмо, та перевіряємо поди:
$ kk -n kube-system get pod | grep secret secrets-store-csi-driver-bl6s5 3/3 Running 0 86s secrets-store-csi-driver-krzvp 3/3 Running 0 86s secrets-store-csi-driver-provider-aws-7v7jq 1/1 Running 0 102s secrets-store-csi-driver-provider-aws-nz5sz 1/1 Running 0 102s secrets-store-csi-driver-provider-aws-rpr54 1/1 Running 0 102s secrets-store-csi-driver-provider-aws-vhkwl 1/1 Running 0 102s secrets-store-csi-driver-r4rrr 3/3 Running 0 86s secrets-store-csi-driver-r9428 3/3 Running 0 86s
Тестування SecretStore CSI Driver
Для доступа поду до SecretStore потрібно додати ServiceAccount з IAM-ролью.
Політику вже створили – додамо IAM Role.
Описуємо її Trust policy:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRoleWithWebIdentity", "Principal": { "Federated": "arn:aws:iam::492***148:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/268***3CE" }, "Condition": { "StringEquals": { "oidc.eks.us-east-1.amazonaws.com/id/268***3CE:aud": "sts.amazonaws.com", "oidc.eks.us-east-1.amazonaws.com/id/268***3CE:sub": "system:serviceaccount:default:ascp-test-serviceaccount" } } } ] }
Створюємо роль:
$ aws iam create-role --role-name ascp-iam-role --assume-role-policy-document file://ascp-trust.json
Підключаємо політику:
$ aws iam attach-role-policy --role-name ascp-iam-role --policy-arn=arn:aws:iam::492***148:policy/sm-and-param-store-ro-access-policy
Створюємо запис в AWS Parameter Store:
$ aws ssm put-parameter --name eks-test-param --value 'paramLine' --type "String" { "Version": 1, "Tier": "Standard" }
Описуємо маніфест SecretProviderClass
:
--- apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: eks-test-secret-provider-class spec: provider: aws parameters: objects: | - objectName: "eks-test-param" objectType: "ssmparameter" --- apiVersion: v1 kind: ServiceAccount metadata: name: ascp-test-serviceaccount annotations: eks.amazonaws.com/role-arn: arn:aws:iam::492***148:role/ascp-iam-role eks.amazonaws.com/sts-regional-endpoints: "true" --- apiVersion: v1 kind: Pod metadata: name: ascp-test-pod spec: containers: - name: ubuntu image: ubuntu command: ['sleep', '36000'] volumeMounts: - name: ascp-test-secret-volume mountPath: /mnt/ascp-secret readOnly: true restartPolicy: Never serviceAccountName: ascp-test-serviceaccount volumes: - name: ascp-test-secret-volume csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: eks-test-secret-provider-class
Деплоїмо (у default
неймспейс, бо в Trust policy маємо перевірку subject
на "system:serviceaccount:default:ascp-test-serviceaccount"
), і перевіряємо файл в поді:
$ kk exec -ti pod/ascp-test-pod -- cat /mnt/ascp-secret/eks-test-param paramLine
Terraform та Metrics Server
Тут теж зробимо з Amazon EKS Blueprints Addons – див. metrics_server
.
Теж ніяк додаткових налаштувань не треба – просто включити, і перевірити. Навіть версію можна не задавати, тільки tolerations
.
Додаємо до controllers.tf
:
... enable_metrics_server = true metrics_server = { values = [ <<-EOT tolerations: - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoExecute - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoSchedule EOT ] } }
Деплоїмо, перевіряємо под:
$ kk get pod | grep metr metrics-server-76c55fc4fc-b9wdb 1/1 Running 0 33s
І перевіряємо з kubectl top
:
$ kk top node NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% ip-10-1-32-148.ec2.internal 53m 2% 656Mi 19% ip-10-1-49-103.ec2.internal 56m 2% 788Mi 23% ...
Terraform та Vertical Pod Autoscaler
Щось забув, що для Horizontal Pod Autoscaler окремого контроллеру не треба, тож тут нам знадобиться тільки додати Vertical Pod Autoscaler.
Беремо знову з Amazon EKS Blueprints Addons, див. vpa
.
Знаходимо версію:
$ helm repo add vpa https://charts.fairwinds.com/stable "vpa" has been added to your repositories $ helm search repo vpa/vpa NAME CHART VERSION APP VERSION DESCRIPTION vpa/vpa 2.5.1 0.14.0 A Helm chart for Kubernetes Vertical Pod Autosc...
Додаємо до terraform.tfvars
:
helm_release_versions = { ... vpa = "2.5.1" }
Додаємо до controllers.tf
:
... enable_vpa = true vpa = { namespace = "kube-system" values = [ <<-EOT tolerations: - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoExecute - key: CriticalAddonsOnly value: "true" operator: Equal effect: NoSchedule EOT ] } }
Деплоїмо, перевіряємо поди:
$ kk get pod | grep vpa vpa-admission-controller-697d87b47f-tlzmb 1/1 Running 0 70s vpa-recommender-6fd945b759-6xdm6 1/1 Running 0 70s vpa-updater-bbf597fdd-m8pjg 1/1 Running 0 70s
Та CRD:
$ kk get crd | grep vertic verticalpodautoscalercheckpoints.autoscaling.k8s.io 2023-09-14T11:18:06Z verticalpodautoscalers.autoscaling.k8s.io 2023-09-14T11:18:06Z
Тестування Vertical Pod Autoscaler
Описуємо Deployment та VPA:
apiVersion: apps/v1 kind: Deployment metadata: name: hamster spec: selector: matchLabels: app: hamster replicas: 2 template: metadata: labels: app: hamster spec: securityContext: runAsNonRoot: true runAsUser: 65534 # nobody containers: - name: hamster image: registry.k8s.io/ubuntu-slim:0.1 resources: requests: cpu: 100m memory: 50Mi command: ["/bin/sh"] args: - "-c" - "while true; do timeout 0.5s yes >/dev/null; sleep 0.5s; done" --- apiVersion: "autoscaling.k8s.io/v1" kind: VerticalPodAutoscaler metadata: name: hamster-vpa spec: targetRef: apiVersion: "apps/v1" kind: Deployment name: hamster resourcePolicy: containerPolicies: - containerName: '*' minAllowed: cpu: 100m memory: 50Mi maxAllowed: cpu: 1 memory: 500Mi controlledResources: ["cpu", "memory"]
Деплоїмо, і за хвилину-дві перевіряємо VPA:
$ kk get vpa NAME MODE CPU MEM PROVIDED AGE hamster-vpa 100m 104857600 True 61s
Та статус подів:
$ kk get pod NAME READY STATUS RESTARTS AGE hamster-8688cd95f9-hswm6 1/1 Running 0 60s hamster-8688cd95f9-tl8bd 1/1 Terminating 0 90s
Готово.
Додавання Subscription Filter до Cloudwatch Log Group з Terraform
Майже все зробили. Залишилось дві дрібниці.
Спершу – додати форвадніг логів з EKS Cloudwatch Log Groups до Lambda-функції, в якій працює Promtail, який буде ці логи пересилати до інстансу Grafana Loki.
Наш EKS модуль створює CloudWatch Log Group /aws/eks/atlas-eks-dev-1-27-cluster/cluster зі стрімами:
І виводить ім’я цієї групи через output cloudwatch_log_group_name
, який ми можемо використати у aws_cloudwatch_log_subscription_filter
, щоб до цієї лог-групи додати фільтр, в якому треба передати destination_arn
з ARN нашої Lambda.
Lambda-функція у нас вже є, створюється окремою автоматизацію для моніторинг-стеку. Щоб отримати її ARN – використаємо data "aws_lambda_function"
, в який передамо ім’я функції, а саме ім’я винесемо у змінні:
variable "promtail_lambda_logger_function_name" { type = string description = "Monitoring Stack's Lambda Function with Promtail to collect logs to Grafana Loki" }
Значення в tfvars
:
... promtail_lambda_logger_function_name = "grafana-dev-1-26-loki-logger-eks"
Щоб наш Subscription Filter мав змогу звератись до цієї функції – потрібно додати aws_lambda_permission
, де в source_arn
передаємо ARN нашої лог-групи. Тут зверніть увагу, що ARN передається як arn::name:*
.
У principal
треба вказати logs.AWS_REGION.amazonaws.com – AWS_REGION отримаємо з data "aws_region"
.
Описуємо ресурси в eks.tf
:
... data "aws_lambda_function" "promtail_logger" { function_name = var.promtail_lambda_logger_function_name } data "aws_region" "current" {} resource "aws_lambda_permission" "allow_cloudwatch_for_promtail" { statement_id = "AllowExecutionFromCloudWatch" action = "lambda:InvokeFunction" function_name = var.promtail_lambda_logger_function_name principal = "logs.${data.aws_region.current.name}.amazonaws.com" source_arn = "${module.eks.cloudwatch_log_group_arn}:*" } resource "aws_cloudwatch_log_subscription_filter" "eks_promtail_logger" { name = "eks_promtail" log_group_name = module.eks.cloudwatch_log_group_name filter_pattern = "" destination_arn = data.aws_lambda_function.promtail_logger.arn }
Деплоїмо, і перевіряємо Log Group:
І логи в Grafana Loki:
Створення StorageClass з Terraform
В іншому модулі EKS для Terraform – cookpad/terraform-aws-eks – це можна було зробити через файл шаблону storage_classes.yaml.tmpl
, але в нашому модулі такого нема.
Втім, робиться це одним маніфестом, так що додаємо його в наш eks.tf
:
... resource "kubectl_manifest" "storageclass_gp2_retain" { yaml_body = <<YAML apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: gp2-retain provisioner: kubernetes.io/aws-ebs reclaimPolicy: Retain allowVolumeExpansion: true volumeBindingMode: WaitForFirstConsumer YAML }
Деплоїмо, і перевіряємо доступні StorageClass:
$ kk get storageclass NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 27h gp2-retain kubernetes.io/aws-ebs Retain WaitForFirstConsumer true 5m46s
Ну і нарешті – на цьому все.
Наче додав все, що потрібно для повноцінного кластеру AWS Elastic Kubernetes Service.