[Devops] EKS auto scaling 하기 by cluster autoscaler
클러스터를 운영하는 이유중 하나는 리소스의 효율적인 운용이지 않으까 싶습니다.
그래서 저는 아직 실감을 잘하지 못했지만 차후 EKS 에서 pod 를 scaling 할때 다양한 방법으로 automatically 하게 배치할 수 있지만 제가 한 방법에 대해서 공유 하고자 합니다.
우선 저는 HPA ( Horizontal Pod Autoscaling ) 을 활용해서 pod 가 사용하는 resource 에 따라서 pod 를 스케일링 중이었습니다.
CPU , Memory usage 를 기반으로 pod 를 autoscaling 하게 되는데 문제는 이 과정에서 node 가 허용 할 수 있는 범위를 벗어나게 되면 결과적으로 pending 상태의 pod 들이 노드에 배치 되지 못한체 돌아가게 된다.
그렇기에 이 경우에는 cluster 의 node 를 scale up 해야 한다.
manual 하게 eks 의 desired node 를 변경할 수 있지만 간단하게 이를 자동으로 해주는 Cluster autoscaler 를 활용하고자 한다.
https://github.com/kubernetes/autoscaler
GitHub - kubernetes/autoscaler: Autoscaling components for Kubernetes
Autoscaling components for Kubernetes. Contribute to kubernetes/autoscaler development by creating an account on GitHub.
github.com
eksctl, wget, aws, kubectl 을 필수적으로 필요합니다.
1. AWS policy 추가하기
2. eks oidc provider 추가하기
3. eks iam service account 추가하기
4. cluster autoscaler deploy 하기
이렇게 진행해보았습니다.
1. AWS Policy 추가하기
우선 service account 를 만들기 전에 해당 service account 가 필요로 하는 policy 를 추가해줄 필요가 있다.
* cluster-autoscaler-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeAutoScalingInstances",
"autoscaling:DescribeLaunchConfigurations",
"autoscaling:DescribeTags",
"autoscaling:SetDesiredCapacity",
"autoscaling:TerminateInstanceInAutoScalingGroup",
"ec2:DescribeLaunchTemplateVersions"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
보시면 알 수 있듯이 autoscaling group , instance 에 대한 정보들이 필요하고 이후 아마 SetDesiredCapacity 를 조절할 것으로 보인다. Terminate 이 있는것으로 보아서 node autoscaling group 이 scale down 할때를 위한 policy 로 보인다.
aws configure 를 통해서 aws credential 을 설정 한 다음에
aws iam create-policy \
--policy-name AmazonEKSClusterAutoscalerPolicy \
--policy-document file://cluster-autoscaler-policy.json
이렇게 진행하면 policy 는 준비가 되었습니다.
2. eks oidc provider 추가하기
eks 를 설치할 때 자동으로 추가 되는 경우도 있지만 그렇지 않은 경우도 있기에 추가한 섹션입니다.
eksctl utils associate-iam-oidc-provider --region=<REGION> --cluster=<CLUSTER_NAME> --approve\n
우와 같이 추가 하게 되면 aws console -> EKS 로 가서 cluster info 에 oidc 가 세팅되어 있음을 확인 할 수 있습니다.
3. eks iam service account 추가하기
이제 이러한 policy 를 사용할 service account 를 추가 해줍니다.
eksctl create iamserviceaccount \
--cluster=<CLUSTER_NAME> \
--namespace=kube-system \
--name=cluster-autoscaler \
--attach-policy-arn=<POLICY_ARN> \
--override-existing-serviceaccounts \
--approve
4. cluster autoscaler deploy 하기
이제 Cluster autoscaler 를 배포 해보겠습니다.
공식 문서에는 kubectl apply -f 를 통해서 바로 배포 하는것을 설명하지만 저는 우선 다운 받아서 제가 필요로 하는 옵션들을 변경하고자 합니다.
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
name: cluster-autoscaler
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-autoscaler
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
rules:
- apiGroups: [""]
resources: ["events", "endpoints"]
verbs: ["create", "patch"]
- apiGroups: [""]
resources: ["pods/eviction"]
verbs: ["create"]
- apiGroups: [""]
resources: ["pods/status"]
verbs: ["update"]
- apiGroups: [""]
resources: ["endpoints"]
resourceNames: ["cluster-autoscaler"]
verbs: ["get", "update"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["watch", "list", "get", "update"]
- apiGroups: [""]
resources:
- "namespaces"
- "pods"
- "services"
- "replicationcontrollers"
- "persistentvolumeclaims"
- "persistentvolumes"
verbs: ["watch", "list", "get"]
- apiGroups: ["extensions"]
resources: ["replicasets", "daemonsets"]
verbs: ["watch", "list", "get"]
- apiGroups: ["policy"]
resources: ["poddisruptionbudgets"]
verbs: ["watch", "list"]
- apiGroups: ["apps"]
resources: ["statefulsets", "replicasets", "daemonsets"]
verbs: ["watch", "list", "get"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses", "csinodes", "csidrivers", "csistoragecapacities"]
verbs: ["watch", "list", "get"]
- apiGroups: ["batch", "extensions"]
resources: ["jobs"]
verbs: ["get", "list", "watch", "patch"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["create"]
- apiGroups: ["coordination.k8s.io"]
resourceNames: ["cluster-autoscaler"]
resources: ["leases"]
verbs: ["get", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cluster-autoscaler
namespace: kube-system
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["create", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["cluster-autoscaler-status", "cluster-autoscaler-priority-expander"]
verbs: ["delete", "get", "update", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-autoscaler
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-autoscaler
subjects:
- kind: ServiceAccount
name: cluster-autoscaler
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cluster-autoscaler
namespace: kube-system
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: cluster-autoscaler
subjects:
- kind: ServiceAccount
name: cluster-autoscaler
namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-autoscaler
namespace: kube-system
labels:
app: cluster-autoscaler
spec:
replicas: 1
selector:
matchLabels:
app: cluster-autoscaler
template:
metadata:
labels:
app: cluster-autoscaler
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '8085'
spec:
priorityClassName: system-cluster-critical
securityContext:
runAsNonRoot: true
runAsUser: 65534
fsGroup: 65534
seccompProfile:
type: RuntimeDefault
serviceAccountName: cluster-autoscaler
containers:
- image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.26.2
name: cluster-autoscaler
resources:
limits:
cpu: 100m
memory: 600Mi
requests:
cpu: 100m
memory: 600Mi
command:
- ./cluster-autoscaler
- --v=4
- --stderrthreshold=info
- --cloud-provider=aws
- --skip-nodes-with-local-storage=false
- --expander=least-waste
- --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/<YOUR CLUSTER NAME>
volumeMounts:
- name: ssl-certs
mountPath: /etc/ssl/certs/ca-certificates.crt # /etc/ssl/certs/ca-bundle.crt for Amazon Linux Worker Nodes
readOnly: true
imagePullPolicy: "Always"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
volumes:
- name: ssl-certs
hostPath:
path: "/etc/ssl/certs/ca-bundle.crt"
다운 받으면 위와 같은 파일이 있습니다 , 다른 데이터의 경우 필요시 조정 및 수정 하면 되고 우리의 경우 클러스터의 이름을 변경 해주겠습니다.
<YOUR CLUSTER NAME> 이라는 항목을 찾아서 aws console 에 저장되어 있는 EKS 의 이름으로 변경 해주고 나서 deploy 합니다.
kubectl apply -f cluster-autoscaler-autodiscover.yaml
그리고 deployment 에서 safe-to-evict 을 사용해주기 위해서 patch 해줍니다.
kubectl patch deployment cluster-autoscaler \\n -n kube-system \\n -p '{"spec":{"template":{"metadata":{"annotations":{"cluster-autoscaler.kubernetes.io/safe-to-evict": "false"}}}}}'
이렇게 해주면 필요한 준비는 다 마치게 되었습니다.
이후 저는 pod 를 강제로 늘려서 1 -> 30 늘린 결과 정상적으로 노드가 늘어나고 해당 node 에 pod 가 배치 되는것을 확인 했습니다.
** 결론 **
cluster autoscaler 에 대해서 전부 다 이해한것은 아니지만 그래도 사용하는 방법에 대해서는 확인하게 되었습니다.
로그를 확인 해보면 cluster autoscaler 는 CPU , Mem, 서비스, pod 을 확인하고 불필요한 노드인지 아닌지 판단하고 그에 따라서 node group 을 조정하게 됩니다.