Остання, четверта частина, в якій ми встановимо решту контроллерів і додамо пару корисних дрібниць.
Попередні частини:
- 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.




