1. Pod 스케일링
1-1. HPA(Horizontal Pod Autoscaler)와 Metrics Server 설치
파드 스케일링이 진행되려면 파드의 수를 자동으로 늘리거나 줄여주는 쿠버네티스 리소스가 필요하다.
이 역할을 하는 것이 HPA다. 그런데 이 HPA가 파드의 수를 늘리거나 줄여주는 행동을 하는데 있어,
CPU 혹은 메모리 사용량 같은 기준점이 있어야 한다. 따라서, 클러스터 내 각 노드와 파드의 CPU와 메모리 사용량을 수집하는 컴포넌트가 필요한데 이것이 Metrics Server다.
즉, Metrics Server가 HPA에게 CPU와 메모리 사용량과 같은 데이터를 전달해주고 그 데이터를 바탕으로 HPA가 스케일 인/아웃을 결정한다)
*스케일 아웃: 서버나 노드,인스턴스의 개수를 늘리는 것
*스케일 인:서버나 노드,인스턴스의 개수를 줄이는 것
따라서, 아래 명령어로 먼저 Metrics Server를 클러스터 내에 설치한다.
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
-그런데 또 Metrics Server에서 메모리 트래킹을 하기 위해서는 아래와 같이 pod에 리소스를 제한 설정이 걸려있어야 한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: ttt-backend
namespace: ttt-ns
spec:
# replicas: 2 hpa에게 관리 맡기기 위해 주석처리로 변경
selector:
matchLabels:
app: ttt-pod
template:
metadata:
labels:
app: ttt-pod
spec:
containers:
- name: ttt-container
image: 0000005660657.dkr.ecr.ap-northeast-2.amazonaws.com/ttt:latest
ports:
- containerPort: 8080
resources: ->컨테이너가 사용할 수 있는 CPU와 메모리 리소스를 제어하는 블록
limits: -> 이 컨테이너가 사용할 수 있는 최대 리소스 한도를 의미
cpu: "1" ->이 컨테이너가 사용할 수 있는 cpu양이 최대 1코어
memory: "500Mi" ->컨테이너가 사용할 수 있는 최대 메모리 크기(500메비바이트는 약 500MB)
requests: ->컨테이너가 최소한으로 필요로 하는 리소스 양으로, pod를 배치할 때 이 요청을 만족할 수 있는 노드에서만 스케줄링이 가능함.
cpu: "0.5" ->컨테이너가 최소한으로 보장받아야하는 CPU양
memory: "250Mi" ->컨테이너가 최소한으로 보장받아야하는 메모리 용량
(이전 포스트에서 Deployment를 생성했을 때 스크립트를 보면 위와 같이 containerPort쪽에 resources 제한을 걸어놓았다.)
Metrics Server를 설치했으면, 아래 스크립트를 통해 HPA를 생성한다.
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler -->이 리소스는 hpa다.hpa는 pod의 수를 동적으로 스케일 인/아웃해주는 리소스
metadata:
name: ko-order-backend-hpa
spec:
scaleTargetRef: --> 이 HPA가 어떤 리소스를 오토스케일링 할 지 연결해주는 부분으로 실제 스케일링 타겟을 명시
apiVersion: apps/v1 -->스케일링 대상 리소스의 api버전(여기선 아래 deployment의 버전)
kind: Deployment -->스케일링 대상 리소스
name: ko-order-backend
minReplicas: 2 --> 이 delpoyment가 가지고 있을 최소 파드 수
maxReplicas: 6 --> 이 deployment가 가질 수 있는 최대 파드 수
targetCPUUtilizationPercentage: 10 -->각 pod의 평균 cpu사용률이 10%를 초과하면 스케일 아웃을 트리거한다는 뜻. 즉 pod개수를 늘린다는 뜻
반대로 평균 10% 이하로 떨어지면, pod를 줄이기도 한다.
1-2. 파드 스케일링 실행
kubectl get hpa hpa이름 -n -namespace명 -w 명령어를 통해 실시간으로 리소스 사용량을 확인할 수 있다.
그리고 파워쉘 창을 하나 더 열고 kubectl exec -it pod이름 -n namespace명 /bin/sh 를 통해 해당 서버의 터미널로 직접 들어가,
while true; do curl -s http://ttt-backend-service/ttt/user/check; done 명령어를 통해 파드 내부에 무한루트를 돌린다.
(중간에 url경로는 데이터를 받아올 수 있는 api 경로여야 함-부하를 발생시키기 위한 목적)
그러면 CPU사용량이 느는 것을 알 수 있다.
그리고 우리가 설치해두었던 Metrics Server가 이를 감지할 것이고, 이를 바탕으로 HPA가 동작하여 pod를 더 생성하려고 한다.
그런데 노드가 부족하면 새로 만들어진 pod가 running되지 못하고 pending상태에 머무르게 된다.
이를 통해, HPA를 통해 파드는 늘어나는 것을 확인했지만 이 파드를 실행하는 노드까지는 늘어나지 못한 걸 확인 할 수 있다.
따라서, 우리는 완벽한 스케일링을 위해 노드 스케일링까지 진행한다.
*위 HPA 생성 스크립트를 보면 cpu사용률 기준을 10%로 테스트를 위해 매우 낮게 잡았다. 실무에서는 보통 50~80%를 사용한다.
2. 노드 스케일링
1-1. 노드그룹에 대한 태그 작업
우선, 노드 오토스케일링을 위해 노드그룹에 태그를 붙인다.
(태그는 그냥 단지 이 리소스에 대한 정보나 구분을 위해 붙이는 이름표다.
그런데 나중에 노드의 갯수를 자동으로 조절해주는 오토스케일러가 노드의 갯수를 조절하기 전에 먼저 오토 스케일할 노드그룹에 대한 감지가 필요하다. 이 감지의 기준이 바로 이 태그를 붙인 노드그룹이 될 것이기 때문에 태그를 붙이는 작업이 필요하다)
1-2.노드 스케일링을 위한 권한 부여
먼저, 우리는 AWS role(역할) 개념에 대해서 알 필요가 있다.
role(역할)은 특정 작업을 수행할 수 있도록 권한을 부여하는 것으로 같은 aws의 리소스나 서비스가 주로 사용하는 것이다.
role은 다시 2가지 구조로 나눠지는데,
-신뢰정책은 누가 이 역할을 받아서 assume할 수 있는지. 즉 누가 사용할 수 있는지 정의하는 것이고
-권한정책은 이 역할을 assume하면 어떤 권한을 가지는 지 정의하는 것이다.
iam에 들어가 역할에 들어가 역할 생성을 클릭한다.
그리고 아래와 같이 신뢰정책을 직접 입력해준다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow", -->이 정책의 효과를 허용할 지 거부할 지정의 하는 부분 여기선 허용을 정의.즉 이 역할을 사용하도록 허가
"Principal": { -->이 역할을 누가 사용할 수 있는지 주체를 정의하는 부분
"Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/<YOUR_EKS_OIDC_PROVIDER>"
},-->아 역할을 사용할 주체가 Federated사용자인 경우를 정의. 여기서는 EKS 클러스터의 OIDC Provider가 주체.
EKS가 발급한 OIDC토큰을 기반으로 인증하는 서비스 어카운트가 이 역할을 assume할 수 있음
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": { -->역할을 aussume할 수 있는 조건을 걸어주는 부분
"StringEquals": { --> 아래 값이 정확히 일치해야한다는 조건을 검
"<YOUR_EKS_OIDC_PROVIDER>:sub": "system:serviceaccount:kube-system:cluster-autoscaler"
-->kube-system 네임스페이스에 있는 cluster-autoscaler라는 서비스 어카운트만 이역할을 assum할 수 있다
}
}
}
]
}
즉, 이 신뢰정책은 kube-system이라는 네임스페이스 안에 있는 cluster-autoscaler라는 이름의 서비스 어카운트만 사용할 수 있다고 정의하는 것이다.
그런 다음, 권한 정책에 AmazonEKSclusterPolicy(EKS클러스터 관련 AWS API 호출을 허용하는 권한으로 클러스터 정보 조회, 상태 변경 작업 등에 대한 권한을 허용하는 것), AutoScailingFullAccess(EC2 오토스케일링 관련한 모든 액션을 허용하는 권한)
을 추가해준다.
마지막으로 역할 이름은 eks-cluster-autoscaler-role로 명명하고 역할 생성 완료한다.
그런 다음, 다시 정책 탭에 들어가 아래와 같이 생성한다(clusterAutoscalerExtraPolicy라는 이름으로 생성하였음)
그리고 아까 만들어 놓았던 eks-cluster-autoscaler-role에 들어가 권한 추가를 누르고 이 권한 정책을 추가한다.
(사실 이 정책은 eks-cluster-autoscaler-role을 생성할 때 이미 추가했던 AutoscalingFullAccess에 거의 포함된 권한들이므로 이 정책을을 추가한다하더라도 중복으로 기능적으로 의미가 없다고 볼 수 있다. 차라리 오토스케일링에 관한 거의 모든 권한을 허용하는 AutoscalingFullAccess 보다 필요한 권한만 부여하는 이 정책이 보안적으로 나아 이것만 적용하는게 나을 수도 있다고 함.
그러나, 실제 테스트시 이 두가지를 적용한 상태에서 진행이 되었어서 일단 두 가지를 다 적용하기로 한다-향후 테스트시 한번 더 체크해볼 것을 권한다)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingGroups", -->오토스케일링 그룹의 상태 조회
"autoscaling:DescribeAutoScalingInstances", -->그룹 내의EC2인스턴스 정보 조회
"autoscaling:DescribeLaunchConfigurations", -->오토스케일링에 쓰이는 런치구성(인스턴스 설정)정보 조회
"autoscaling:DescribeTags", -->오토스케일링 그룹에 붙은 태그 정보 조회
"autoscaling:SetDesiredCapacity", -->오토스케일링 그룹의 원하는 인스턴스 개수를 변경
"autoscaling:TerminateInstanceInAutoScalingGroup",-->EC인스턴스를 종료하고 자동으로 대체노드를 추가하는 작업(노드 줄일때 사용)
"ec2:DescribeLaunchTemplateVersions", --EC2 런치 템플릿 버전 정보 조회
"ec2:DescribeInstanceTypes",-->EC2 인스턴스타입 정보 조회(어떤 인스턴스 타입을 선택할 지 결정할 때 필요)
"eks:DescribeNodegroup",-->EKS의 노드 그룹 상세 정보 조회
"eks:ListNodegroups",-->EKS 클러스터에 포함된 노드 그룹 리스트 조회
"eks:DescribeCluster" -->EKS 클러스터 정보 조회
],
"Resource": "*"
}
]
}
1-3.ServiceAccount 생성
Service Account는 쉽게 말해서, 유저(사람이 아닌) 서비스가 클러스터 내부나 외부 리소스에 접근 할 수 있도록 만들어주는 계정이다.(=유저가 IAM Role을 통해 권한을 얻는 것처럼, 서비스도 ServiceAccount를 통해 IAM Role을 Assume하고 권한을 얻는다)
우리가 만들어야 할 것은 노드를 자동으로 조절해주는 cluster-autoscaler다.
그런데 cluster-autoscaler라는 리소스가 노드를 조절하려면 해당 aws자원에 접근할 수 있는 권한이 필요한데, 우리는 그 권한을
eks-cluster-autosclaer-role이라는 역할에 부여했다.
그런데 유저가 아닌 리소스가 이 역할을 assume하기 위해서는 serviceAccout리소스가 필요하기 때문에 먼저 이 ServiceAccount를 생성하는 것이다.
# [1] Cluster Autoscaler가 사용할 ServiceAccount (서비스 계정)
apiVersion: v1
kind: ServiceAccount
metadata:
name: cluster-autoscaler -->cluste-autoscaler라는 이름의 ServiceAccount를 생성하겠다는 뜻
namespace: kube-system
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::000000082320:role/eks-cluster-autoscaler-role
-->이 serviceaccount가 eks-cluster-autoscaler-role이라는 역할도 assume할 수 있게 함.
즉, eks-cluster-autoscaler-role에 부여된 권한들을 얻는 다는 것
---
# [2] ClusterRole: Cluster Autoscaler가 클러스터 리소스(노드,파드,디플로이먼트)를 다루기 위한 권한 정의
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-autoscaler # 역할 이름
rules:
# 노드, 파드 리스트, 조회, 감시, 수정 권한
- apiGroups: [""]
resources: ["nodes", "pods"]
verbs: ["get", "list", "watch", "patch"]
# 디플로이먼트 조회, 리스트, 수정
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "patch"]
# 레플리카셋 조회, 리스트, 수정
- apiGroups: ["extensions"]
resources: ["replicasets"]
verbs: ["get", "list", "patch"]
# 클러스터 오토스케일러는 위에 보면 1개로 설정해놨음.
# 하지만 여러개가 뜰 수도 있는 상황을 대비해서 leader election이 기본적으로 작동한다.
# 따라서 클러스터 오토스케일러 1개만 작동하기 위해서 leases 권한이 필요하다.
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "watch", "list", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["persistentvolumes", "persistentvolumeclaims", "replicationcontrollers", "services", "namespaces"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["statefulsets", "daemonsets", "replicasets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["get", "list", "watch"]
- apiGroups: ["policy"]
resources: ["poddisruptionbudgets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
resources: ["csinodes", "csidrivers", "csistoragecapacities"]
verbs: ["get", "list", "watch"]
# 추가 (ConfigMap)
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["create", "list", "get", "update", "watch"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch", "patch", "update", "delete"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/eviction"]
verbs: ["create"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "patch"]
---
# [3] ClusterRoleBinding: 서비스 계정에 위에서 정의한 역할을 바인딩해주는 것. 즉 쓸 수 있게 묶어주는 것
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-autoscaler # 바인딩 이름
subjects:
- kind: ServiceAccount
name: cluster-autoscaler # [1]에서 만든 서비스 계정
namespace: kube-system # 네임스페이스 일치
roleRef:
kind: ClusterRole
name: cluster-autoscaler # [2]에서 만든 ClusterRole 이름
apiGroup: rbac.authorization.k8s.io
1-4. cluster-autoscaler를 생성한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-autoscaler
namespace: kube-system #autoscaler는 kube-system에 적용해야함.
labels:
app: cluster-autoscaler
spec:
replicas: 1
selector:
matchLabels:
app: cluster-autoscaler -->어떤 pod를 이 deployment가 관리할 지 결정하는 선택자로 app:cluster-autoscaler라는 라벨이 붙은 pod를 관리하겠다는 뜻
template:
metadata:
labels:
app: cluster-autoscaler -->생성할 pod에 대한 템플릿. 위의 selector와 라벨이 일치해야 deployment가 이pod를 관리 가능
spec:
serviceAccountName: cluster-autoscaler -->이 pod가 클러스터 리소스에 접근 할 때 사용할 ServiceAccount 지정(이 계정으로RBAC권한과 IAM Role을 연결했었다).이게 있어야 오토스케일러가 노드 정보를 보고 조작할 수 있음
containers:
- image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.29.0 --> cluster-autoscaler의 공식이미지
name: cluster-autoscaler
resources: -->이 컨테이너가 사용할 수 있는 리소스 제한과 요청량. 이 오토스케일러가 클러스터에 과부하를 주지않게 관리하는 것
limits:
cpu: 100m
memory: 300Mi
requests:
cpu: 100m
memory: 300Mi
command: #오토스케일러의 동작방식
- ./cluster-autoscaler
- --cloud-provider=aws #eks 사용중이므로 aws로 설정
- --namespace=kube-system #오토스케일러가 동작하는 네임스페이스
- --cluster-name=eks-prac #EKS 클러스터 이름
- --logtostderr=true #로그출력
- --stderrthreshold=info #로그레벨 (info와 error 출력)
- --balance-similar-node-groups #오토밸런싱
- --skip-nodes-with-system-pods=true #시스템 파드가 떠있는 노드는 제거X (클러스터가 망가질 위험 방지)
- --skip-nodes-with-local-storage=true #로컬 스토리지 사용중인 노드는 제거X (데이터 유실 방지)
- --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/eks-prac
->오토스케일러가 감지할 노드 그룹을 자동으로 찾음.(이걸 위해 우리가 태그를 붙인 이유)
# 노드그룹 생성할 때 태그를 반드시 붙여야함
# k8s.io/cluster-autoscaler/enabled = true
# k8s.io/cluster-autoscaler/eks-prac = owned
env:
- name: AWS_REGION
value: "ap-northeast-2" -->cluster-autoscaler가 어느 리전에 있는 리소스를 건드릴 지 알아야하기때문에 이 값을 적는 것
volumeMounts:
- name: ssl-certs
mountPath: /etc/ssl/certs/ca-certificates.crt
readOnly: true
volumes:
- name: ssl-certs
hostPath:
path: /etc/ssl/certs/ca-bundle.crt
*volumes는 호스트(노드)에 있는 진짜 파일,경로를 지정 volumeMounts는 컨테이너 안에서 어디에 연결할지(마운트할지) 정하는 것이다.
cluster autoscaler는 aws API랑 통신할 때 SSL인증서를 필요로 함. 그래서 호스트 머신에 있는 인증서 경로를 컨테이너에 연결해다 쓰는 것
즉 이 컨테이너가 실행되고 있는 노드에 있는 인증서를 사용하겠다. 이 인증서는 리눅스 운영체제가 기본으로 제공한다.
1-5.확인 및 테스트
이후에, kubectl get pod -n kube-system명령어로 cluster-autoscaler가 잘 생성되었는지 확인
만약 에러가 나있다면 describe pod 명령어나 log를 조회하여 원인이 No OpenIDConnect provider found in your account라면,
EKS 클러스터랑 OIDC 제공자 연결 해줘야함.
eksctl 다운받는법 →
- 관리자 권한으로 아래 명령어 실행설치 후 파워쉘 껐다 킨 다음 choco --version 확인
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) - 관리자 권한으로 다시 아래 명령어 실행eksctl version 확인
- choco install eksctl -y
- 아래 명령어 실행으로 OIDC제공자 클러스터랑 연결
- eksctl utils associate-iam-oidc-provider --region ap-northeast-2 --cluster <클러스터이름> --approve
그러면, 어떤 pod의 터미널창으로 exec를 통해 접속해 curl 무한루프로 cpu의 부하를 준다면 먼저 pod가 늘어나고 늘어난 pod를 감당하기 위해 자동으로 노드(EC2)도 늘어나있을 것을 확인 할 수 있다.
'커뮤니티 플랫폼 프로젝트' 카테고리의 다른 글
[AWS]백엔드 배포 흐름 정리-1 (1) | 2025.03.17 |
---|---|
[AWS] 프론트 배포 흐름 정리 (0) | 2025.03.17 |
[Spring]조회수 기능(Redis,Spring Batch활용) (0) | 2025.03.14 |
[Spring] 좋아요 기능(Redis,RabbitMQ활용) (0) | 2025.02.28 |