1. 실습 기본 환경 구성
이번 실습은 5장의 Karpenter 구성하기 실습을 위해 기본 인프라 환경을 CloudFormation 스택으로 생성하고 eksctl로 Amazon EKS 클러스터 배포합니다.
1.1. Karpenter Preconfig 배포
해당 링크를 클릭하면 AWS CloudFormation 페이지로 연결되며, 파라미터를 입력 후 스택을 생성합니다.
Note: AWS 관리 콘솔에 로그인 할때 IAM 사용자 계정으로 진행합니다.
[관리 콘솔] CloudFormation 파라미터 설정
- 스택 생성 페이지에서
다음
버튼을 클릭합니다. - 스택 이름은 [myeks2]로 입력합니다.
- KeyName은 [각자의 키페어]를 선택합니다.
- MyIAMUserAccessKeyID는 [각자의 액세스 키 ID 값]을 입력합니다.
- MyIAMUserSecretAccessKey는 [각자의 시크릿 액세스 키 값]을 입력합니다.
- SgIngressSshCidr는 [각자의 PC의 퍼블릭 IP 주소/32]로 입력합니다.
- 나머지 파라미터는 기본 값을 유지하고
다음
버튼을 클릭합니다.
Warning: 설정을 마치고 약 5분 정도 대기 시간이 흐른 뒤 기본 인프라 환경 생성이 완료됩니다. 반드시 해당 대기 시간이 지난 후 다음 작업을 수행합니다.
1.2. EKS 클러스터 생성
AWS CloudFormation 스택의 출력 탭에서 eksctlhost의 퍼블릭 IP를 확인합니다.
해당 IP로 EKS 관리용 인스턴스(myeks-bastion-EC2
)에 SSH로 접속하고 아래 명령어를 통해 정보를 확인합니다.
환경 변수
1
2
3
4
5
6
7
8
// 환경 변수 정보 확인
export | egrep 'ACCOUNT|AWS_|CLUSTER' | egrep -v 'SECRET|KEY'
// 환경 변수 설정
export KARPENTER_VERSION=v0.30.0
export TEMPOUT=$(mktemp)
echo $KARPENTER_VERSION; echo $CLUSTER_NAME; echo $AWS_DEFAULT_REGION; echo $AWS_ACCOUNT_ID $TEMPOUT
Warning: CloudFormation 스택이 생성되고 환경 변수가 선언되기 전에 너무 빨리 접속하면 다시 접속합니다.
Karpenter 관련 IAM, EC2 Instance Profile 생성
1
2
3
4
5
6
7
8
// CloudFormation 스택으로 IAM Policy, Role, EC2 Instance Profile 생성
// 약 3분 정도 소요
curl -fsSL https://raw.githubusercontent.com/aws/karpenter/"${KARPENTER_VERSION}"/website/content/en/preview/getting-started/getting-started-with-karpenter/cloudformation.yaml > $TEMPOUT \
&& aws cloudformation deploy \
--stack-name "Karpenter-${CLUSTER_NAME}" \
--template-file "${TEMPOUT}" \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides "ClusterName=${CLUSTER_NAME}"
Amazon EKS 클러스터 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// EKS 클러스터 생성 : myeks2 생성
// 약 19분 정도 소요
eksctl create cluster -f - <<EOF
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: ${CLUSTER_NAME}
region: ${AWS_DEFAULT_REGION}
version: "1.26"
tags:
karpenter.sh/discovery: ${CLUSTER_NAME}
iam:
withOIDC: true
serviceAccounts:
- metadata:
name: karpenter
namespace: karpenter
roleName: ${CLUSTER_NAME}-karpenter
attachPolicyARNs:
- arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME}
roleOnly: true
iamIdentityMappings:
- arn: "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}"
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
managedNodeGroups:
- instanceType: m5.large
amiFamily: AmazonLinux2
name: ${CLUSTER_NAME}-ng
desiredCapacity: 2
minSize: 1
maxSize: 10
iam:
withAddonPolicies:
externalDNS: true
EOF
1.3. EKS 클러스터 확인 및 추가 설정
Default Namespace로 적용
1
2
// Default Namespace로 위치 변경
kubectl ns default
EKS 클러스터 확인
1
2
3
4
5
6
7
8
9
10
// EKS 클러스터 배포 확인
eksctl get cluster
eksctl get nodegroup --cluster $CLUSTER_NAME
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl describe cm -n kube-system aws-auth
신규 터미널 - EKS Node Viewer 설치
1
2
3
4
5
6
// EKS Node Viewer 설치 : 약 2분 이상 소요
// EKS 클러스터 생성 완료 후 작업
go install github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@v0.5.0
// EKS Node Viewer 접속
cd ~/go/bin && ./eks-node-viewer
ExternalDNS 설치
1
2
3
4
5
6
7
8
9
MyDomain=<자신의 도메인>
MyDnsHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
echo $MyDomain, $MyDnsHostedZoneId
curl -s -O https://raw.githubusercontent.com/cloudneta/cnaeblab/master/_data/externaldns.yaml
MyDomain=$MyDomain MyDnsHostedZoneId=$MyDnsHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -
kube-ops-view 설치
1
2
3
4
5
6
7
8
9
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl patch svc -n kube-system kube-ops-view -p '{"spec":{"type":"LoadBalancer"}}'
kubectl annotate service kube-ops-view -n kube-system "external-dns.alpha.kubernetes.io/hostname=kubeopsview.$MyDomain"
echo -e "Kube Ops View URL = http://kubeopsview.$MyDomain:8080/#scale=1.5"
2. Karpenter 환경 구성
2.1. Karpenter 설치 및 확인
환경 변수 설정
1
2
3
4
5
6
// Karpenter 설치를 위한 환경 변수 설정 및 확인
export CLUSTER_ENDPOINT="$(aws eks describe-cluster --name ${CLUSTER_NAME} --query "cluster.endpoint" --output text)"
export KARPENTER_IAM_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter"
echo $CLUSTER_ENDPOINT; echo $KARPENTER_IAM_ROLE_ARN
EC2 Spot fleet의 server-linked-role 확인
1
2
3
4
// EC2 Spot Fleet 사용을 위한 service-linked-role 생성 확인 (이미 생성됐다는 에러가 나와야 정상)
// An error occurred (InvalidInput) when calling the CreateServiceLinkedRole operation...
aws iam create-service-linked-role --aws-service-name spot.amazonaws.com || true
Warning: 만약 에러 메시지가 아닌 새롭게 생성하는 메시지가 나온다면 현재 어카운트에 해당 서비스 연결 역할(AWSServiceRoleForEC2Spot)이 존재하지 않는 것 입니다. 서비스 연결 역할 생성 후 다음 단계로 넘어가면 됩니다. 처음부터 다시 진행할 필요는 없습니다.
public.ecr.aws logout
1
2
3
4
5
// docker logout
docker logout public.ecr.aws
// helm registry logout
helm registry logout public.ecr.aws
Karpenter 설치
1
2
3
4
5
6
7
8
9
10
11
// karpenter 설치
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version ${KARPENTER_VERSION} --namespace karpenter --create-namespace \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KARPENTER_IAM_ROLE_ARN} \
--set settings.aws.clusterName=${CLUSTER_NAME} \
--set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME} \
--set settings.aws.interruptionQueueName=${CLUSTER_NAME} \
--set controller.resources.requests.cpu=1 \
--set controller.resources.requests.memory=1Gi \
--set controller.resources.limits.cpu=1 \
--set controller.resources.limits.memory=1Gi \
--wait
Karpenter 설치 확인
1
2
3
4
5
6
7
8
// karpenter 설치 확인
kubectl get-all -n karpenter
kubectl get all -n karpenter
kubectl get cm -n karpenter karpenter-global-settings -o jsonpath={.data} | jq
kubectl get crd | grep karpenter
2.2. Provisioner 생성 및 확인
Provisioner와 AWSNodeTemplate 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Provisioner와 AWSNodeTemplate 정책을 정의하고 생성
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: default
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
limits:
resources:
cpu: 1000
providerRef:
name: default
ttlSecondsAfterEmpty: 30
---
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
name: default
spec:
subnetSelector:
karpenter.sh/discovery: ${CLUSTER_NAME}
securityGroupSelector:
karpenter.sh/discovery: ${CLUSTER_NAME}
EOF
Provisioner와 AWSNodeTemplate 확인
1
kubectl get awsnodetemplates,provisioners
3. Karpenter 동작 확인
3.1. 테스트용 디플로이먼트 생성
디플로이먼트 배포
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 디플로이먼트 배포
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: inflate
spec:
replicas: 0
selector:
matchLabels:
app: inflate
template:
metadata:
labels:
app: inflate
spec:
terminationGracePeriodSeconds: 0
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: 1
EOF
// 디플로이먼트 확인
kubectl get deploy
3.2. Karpenter의 스케일링 확인
Scale-Out 확인
1
2
3
4
5
6
7
8
9
// 테스트용 디플로이먼트 생성
// replicas 수정 및 로그 확인 (replicas 0 -> 5)
kubectl scale deployment inflate --replicas 5
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller | grep provisioner
// 스팟 인스턴스 확인
kubectl get node --label-columns=eks.amazonaws.com/capacityType,karpenter.sh/capacity-type,node.kubernetes.io/instance-type
Scale-In 확인
1
2
3
4
5
6
// 디플로이먼트 삭제 및 로그 확인 (ttlSecondsAfterEmpty 30초)
kubectl delete deployment inflate; date
date
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller | grep deprovisioning
3.3. Karpenter의 Consolidation 확인
Provisioner 삭제
1
2
// 기존 provisioner를 삭제
kubectl delete provisioners default
Provisioner 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 신규 provisioner 생성
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: default
spec:
consolidation:
enabled: true
labels:
type: karpenter
limits:
resources:
cpu: 1000
memory: 1000Gi
providerRef:
name: default
requirements:
- key: karpenter.sh/capacity-type
operator: In
values:
- on-demand
- key: node.kubernetes.io/instance-type
operator: In
values:
- c5.large
- m5.large
- m5.xlarge
EOF
디플로이먼트 배포
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 디플로이먼트 배포
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: inflate
spec:
replicas: 0
selector:
matchLabels:
app: inflate
template:
metadata:
labels:
app: inflate
spec:
terminationGracePeriodSeconds: 0
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: 1
EOF
// 디플로이먼트 확인
kubectl get deploy
Scale-Out 확인
1
2
3
4
5
6
7
8
9
10
11
// 테스트용 디플로이먼트 생성
// replicas 수정 및 로그 확인 (replicas 0 -> 12)
kubectl scale deployment inflate --replicas 12
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller | grep provisioner
// karpenter로 생성한 노드 확인
kubectl get node -l type=karpenter
kubectl get node --label-columns=eks.amazonaws.com/capacityType,karpenter.sh/capacity-type
Consolidation 확인
1
2
3
4
// replicas 수정 및 로그 확인 (replicas 12 -> 7)
kubectl scale deployment inflate --replicas 7
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller | grep consolidation -A 3
디플로이먼트 삭제
1
2
// 디플로이먼트 삭제
kubectl delete deployment inflate
4. 실습 환경 삭제
5장 Karpenter 실습이 종료되어 모든 실습 환경을 삭제합니다.
실습 종료 후 자원 삭제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// helm chart 삭제
helm uninstall -n kube-system kube-ops-view
helm uninstall karpenter --namespace karpenter
// ec2 launch template 삭제
aws ec2 describe-launch-templates --filters Name=tag:karpenter.k8s.aws/cluster,Values=${CLUSTER_NAME} |
jq -r ".LaunchTemplates[].LaunchTemplateName" |
xargs -I{} aws ec2 delete-launch-template --launch-template-name {}
// eks 클러스터 삭제
eksctl delete cluster --name "${CLUSTER_NAME}"
// karpenter 관련 cloudformation 스택 삭제
aws cloudformation delete-stack --stack-name "Karpenter-${CLUSTER_NAME}"
// 기본 cloudformation 스택 삭제
aws cloudformation delete-stack --stack-name ${CLUSTER_NAME}
Warning: 실습 환경 삭제는 반드시 순차적으로 진행하여 삭제합니다.
Amazon Route 53 레코드 및 호스팅 영역 삭제
1) 서비스 > Route 53 > 호스팅 영역 > 도메인 선택
- 대상 레코드 선택 > ‘레코드 삭제’ 버튼 > ‘삭제’ 버튼
2) 더 이상 도메인을 사용 계획이 없을 경우 호스팅 영역 삭제
- NS 레코드와 SOA 레코드만 존재하는지 확인
- ‘영역 삭제’ 버튼 > ‘삭제’ 버튼
Warning: 호스팅 영역을 재사용할 경우 ‘호스팅 영역 생성’ 버튼을 클릭하고 자신의 도메인을 입력합니다. 이때 네임 서버 주소를 맞추는 작업이 필요한데 링크를 참조바랍니다.
여기까지 5장 Karpenter 구성 하기 실습을 마칩니다.
수고하셨습니다 :)