9W - EKS 업그레이드
개요
이번 주차는 EKS를 업그레이드하는 전략과 방법에 대해서 알아본다.
스터디원 분의 도움으로 EKS 워크샵을 통해 실습할 수 있는 기회를 얻었으며, 이번 주차는 해당 내용을 정리하는 시간을 가질 것이다.
그렇기에 이번 글은 기존에 사용하던 사전 지식-실습 진행이라는 목차를 두지 않고 철저하게 워크샵 내용을 따라 글을 작성한다.
쿠버네티스 업그레이드 전략
쿠버네티스를 업그레이드한다는 것은 코어 컴포넌트, 관련 애드온, 사용되는 노드의 커널 버전 등 다양한 것을 포함할 수 있다.
그러나 어떻게 하더라도 결국 업그레이드하는 데에 있어 순서는 이렇게 정리될 수 있다.
- etcd의 내용을 먼저 백업하여, 현재 클러스터의 상태를 저장한다.
- 설치된 애드온, 그 중에서도 CNI 애드온의 경우 안정적으로 구동되는 것이 중요하기에 가급적 클러스터의 이전 버전과 이후 버전 둘 다 호환되는 버전으로 업그레이드를 진행한다.
- 모든 노드 공통
- 업그레이드할 노드는 먼저 drain을 시켜야 한다.
- 즉 현재 존재하는 파드들을 축출시키고, 노드에 추가 파드가 스케줄되지 못하도록 테인트를 건다.
- 무중단이 보장돼야 하는 서비스의 경우 PDB를 세팅하여 안전하게 축출이 일어나게 하는 것이 좋다.
- 종료로 인한 데이터 손실이 우려되는 서비스라면 terminationGracePeriodSeconds를 설정하여 종료 시그널을 프로세스가 처리하도록 보장해주는 것이 좋다.
- 업그레이드가 끝난다면 uncordon을 통해 다시금 파드들이 배치될 수 있도록 만든다.
- 업그레이드할 노드는 먼저 drain을 시켜야 한다.
- 컨트롤 플레인 업그레이드
- 커널 업그레이드
- 컨테이너 런타임 업그레이드
- api 서버, etcd, controller manager 등의 버전을 업그레이드
- kubeadm를 사용하면 쉽게 진행할 수 있다.
- kubelet의 경우에는 컨테이너를 띄우는 systemd service로 구동되기에 수동 재시작을 해야 한다.
- 데이터 플레인 업그레이드
- 위와 같으나, 코어 컴포넌트랄 게 proxy, kubelet밖에 없어서 그렇게 어렵진 않다.
- 필요 시 사용하는 애드온들의 버전도 전부 업그레이드
말이 길어 복잡해보이나, 결국 노드들을 하나씩, 컨트롤 플레인부터 업그레이드해나간다는 것이다.
요약하면 간단하지만 실제로 업그레이드를 할 때는 어떠한 사이드 이펙트가 발생할지 미리 예측하는 것이 쉽지 않기 때문에 각 단계에서 이상이 발생하지는 않았는지, 이상이 발생했다면 어떻게 원복할 것인지에 대한 추가 전략과 고민이 필요하다.
이러한 방식은 상태 원복이 상대적으로 어렵기 때문에, 아예 새로운 버전의 클러스터를 다른 공간에 새로 띄우는 블루 그린 방식의 업그레이드도 많이 활용되는 추세이다.
블루 그린을 할 경우에는 상태 유지 어플리케이션 간의 경합이 발생하지는 않는지, 데이터 무결성은 제대로 지켜지는지, 자원을 얼마나 소모하게 되는지 등에 대한 고민점이 또 발생하게 되니, 어느 전략을 선택할지는 운영 조직의 몫이 될 것이다.
CKA 시험을 치뤄봤다면 간단하게라도 kubeadm으로 업그레이드하는 방법에 대해서 공부해본 적이 있을 것이다.
EKS에서 업그레이드를 하는 것은 훨씬 더 간단하다(상대적으로 간단하다는 말이다).
AWS에서 업그레이드의 절차들을 상당 부분 책임지고 관리해주기 때문이다.
인플레이스 업그레이드
현재 존재하는 클러스터를 유지하면서 노드를 하나씩 업그레이드하는 방식을 말한다.
이 방식의 장점은 기존 클러스터의 각종 고유한 정보들을 그대로 유지하면서 업그레이드할 수 있다는 것이다.
특히 EKS의 경우 컨트롤 플레인을 사용자가 관리할 수 없고, api 서버 앞단에 위치한 로드밸런서의 도메인 주소 역시 마음대로 설정할 수 없다.
자체적으로 앞단에 프록시를 하나 더 두는 방식으로 관리하는 방안을 생각할 수 있긴 하겠지만, 이 경우 api서버가 설정해주는 OIDC url이 문제가 된다.
인플레이스 업그레이드는 api 서버의 도메인 주소를 유지시키는 채로 업그레이드를 하기 때문에 별도의 추가적인 리소스를 들이지 않으며 업그레이드를 진행할 수 있다.
다만 한번 업그레이드를 했을 때 롤백이 어렵다는 단점이 있다.
특히 EKS의 경우, 컨트롤 플레인의 업그레이드를 롤백시킬 수 없기 때문에 섣불리 클러스터를 빠르게 업그레이드했다가 호환되지 않는 툴이 생기기라도 한다면 낭패가 생긴다.
블루그린 업그레이드
블루그인 업그레이드는 흔한 배포 전략과 방식이 똑같다.
현재 클러스터를 블루 버전으로 두고, 새로운 버전의 클러스터를 그린 버전으로서 똑같이 만드는 방식을 말한다.
이 경우 업그레이드간 문제가 발생하더라도 기존 클러스터는 유지되고 있으므로 롤백이 훨씬 편리하다.
롤백이랄 것도 없는 게, 그냥 그린 버전을 없애기만 하면 끝이다.
특히 인플레이스 업그레이드는 설정에 따라서는 기존 버전의 클러스터에 다운타임을 유발할 가능성도 있으나 블루 그린은 이런 문제는 발생하지 않는다.
다만 새로운 클러스터 자원을 들여야 하기 때문에 업그레이드간 그만큼의 비용이 더 발생한다는 점이 대표적인 단점이다.
또한 상태 유지가 중요한 어플리케이션이나 고유성이 확보돼야 하는 어플리케이션이 클러스터에 존재하는 경우, 이들의 데이터가 적절하게 동기화되고 문제가 없도록 동작하도록 고민을 해야한다는 추가적인 어려움이 발생할 수 있다.
인플레이스 업그레이드가 아닌 블루그린 업그레이드를 사용하는 것이 유효한 상황들을 체크해보자.
- 클러스터 프로비저닝이 자동화되어 있으면 블루그린을 사용하기 용이하다.
- 상태를 유지하는 워크로드가 대부분일 경우 블루그린을 도입할 때의 고려사항이 줄어든다.
- 롤백 고려가 필요하다면 거의 무조건 블루그린밖에 선택지가 없다.
- 새로운 버전에 대한 여러 테스트 파이프라인을 두고자 한다면 블루그린이 유리하다.
- 여러 버전을 한번에 업그레이드해야 한다면 블루그린이 공수가 적게 들어 효율적이다.
- 인플레이스 업그레이드는 마이너 버전을 하나씩 올리는 귀찮은 과정을 반복해야 한다.
이제부터는 워크샵의 내용을 철저하게 따라가면서 진행한다.
클러스터 환경 파악
먼저 콘솔로 간단하게 확인해보면 버전은 1.25 버전을 쓰고 있는 것을 볼 수 있다.
빨리 업그레이드하지 않으면 AWS가 이놈하며 돈을 더 요구한다!
버전 업그레이드 세팅은 Extended로 되어 있어 자동으로 업그레이드하지 않고 사용자가 수동으로 트리거해야 한다.
현재 배치된 노드는 파게이트 하나, 관리형 노드 그룹으로 배치된 노드 2개, 자체 관리 노드 2개로 이뤄져있다.
추가적으로 블루 그린을 간단하게 테스트하기 위한 노드도 배치된 상태이다.
자체 관리 노드 중 하나는 카펜터를 통해 프로비저닝된 노드란 것도 확인할 수 있다.
애드온도 버전에 맞춰 업그레이드를 해야하므로 현 버전에 대해 간단하게 체크해둔다.
아주 고맙게도, 콘솔에서 대시보드를 들어가보면 현재 버전에서 업그레이드를 진행하는데 있어 체크할 사항들을 보여준다.
당장 1.26으로 버전을 업그레이드하는데 발생하는 문제가 하나 보이는데, hpa 리소스에서 버전이 v2beta2로 되어있는 것이 화근이다.
온프레미스 환경에서도 업그레이드를 할 때 각 리소스의 api 버전을 체크하는 것은 매우 중요하다!
클러스터 내부적으로는 argocd가 있는 것이 보인다.
아르고 서버가 로드밸런서로 열려 있으므로, 웹으로 접속해서 확인해보자.
k -n argocd get secrets argocd-initial-admin-secret -ojsonpath='{.data.password}' | base64 -d
초기 비밀번호를 알아내서 들어가보면..
여러 어플리케이션이 들어가 있는 것을 볼 수 있다.
각 앱은 apps라는 어플리케이션의 하위 앱으로서 app of apps 패턴으로 구성됐다.
깃 레포를 받아서 뜯어보면, 이렇게 app of apps 디렉에서 하위 앱들의 스펙을 명시한다.
그리고 각 앱은 apps 디렉토리에 상태가 구현돼있다.
싱크가 안 맞는 앱이 하나 있는데, hpa 관련 설정에 문제가 있다는 것도 확인해두자.
추가적으로, 현재 클러스터는 테라폼 코드로 관리되고 있다.
파게이트 하나, 관리형 노드 그룹 2개가 세팅돼있다.
추가적으로 셀프형 노드 그룹도 있는 상태이다.
실습 상에서 우리는 테라폼을 통해 업그레이드를 진행할 것이다.
EKS 업그레이드 준비
EKS 업그레이드 자체도 기본적으로 흐름은 쿠버네티스 업그레이드 흐름과 같다.
대신 EKS의 경우 컨트롤 플레인 업그레이드를 결정했을 때 해당 업그레이드는 전적으로 책임을 져준다.
EKS에서 업그레이드를 진행할 때의 기본적인 준비사항은 이렇게 된다.
- 클러스터가 배포된 서브넷에는 최소한 5개의 가용 IP가 있어야 한다.
- 클러스터의 보안 그룹과 IAM 롤이 계정에 존재해야 한다.
- Amazon KMS를 사용 중이라면 클러스터 롤이 키 접근 권한을 가지고 있어야 한다.
클러스터 리소스의 변경점이나 기타 고려사항은 EKS 문서에서도 잘 정리해주고 있으므로, 이 내용을 기반으로 현 클러스터에서 추적관리할 사항을 고려하면 된다.[1]
가령 AL2는 1.33부터는 지원되지 않으니 업그레이드 간 ami를 AL2023 혹은 보틀로켓으로 변경하는 것이 좋을 것이다.
aws ec2 describe-subnets --subnet-ids \
$(aws eks describe-cluster --name ${CLUSTER_NAME} \
--query 'cluster.resourcesVpcConfig.subnetIds' \
--output text) \
--query 'Subnets[*].[SubnetId,AvailabilityZone,AvailableIpAddressCount]' \
--output table
다행히? 가용 IP는 차고 넘친다.
부족하다 싶으면 서브넷을 추가로 늘리거나, 아니면 VPC 커스텀 네트워크를 고려해보자.
고가용성을 고려하기 위해 PDB를 세팅하는 예시는 다음과 같다.
cat << EOF > ~/environment/eks-gitops-repo/apps/orders/pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: orders-pdb
namespace: orders
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/component: service
app.kubernetes.io/instance: orders
app.kubernetes.io/name: orders
EOF
echo " - pdb.yaml" >> ~/environment/eks-gitops-repo/apps/orders/kustomization.yaml
cd ~/environment/eks-gitops-repo/
git add apps/orders/kustomization.yaml
git add apps/orders/pdb.yaml
git commit -m "Add PDB to orders"
git push
argocd app sync orders
아르고cd를 쓰고 있으므로 싱크를 맞춰주는 작업까지 곁들였다.
정상적으로 싱크가 된 것까지 확인됐다.
aws s3 ls | awk '{print $3}^C
aws s3 ls s3://$(!!)
현 워크샵 실습에서는 s3를 테라폼 백엔드로 사용할 수 있도록 제공해주고 있다.
terraform {
backend "s3" {
bucket = "workshop-stack-tfstatebackendbucketf0fc9a9d-5wvsn1twmiae"
region = "us-west-2"
key = "terraform.tfstate"
}
}
다만 이걸 직접 연결하도록 주석 처리가 돼있으므로 직접 백엔드 설정을 해주자.
terraform init
terraform output
해서 kubectl config 업데이트 명령어가 나온다면 성공이다.
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm repo update
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --namespace kube-system
#
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-type: external
labels:
app.kubernetes.io/instance: kube-ops-view
app.kubernetes.io/name: kube-ops-view
name: kube-ops-view-nlb
namespace: kube-system
spec:
type: LoadBalancer
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
selector:
app.kubernetes.io/instance: kube-ops-view
app.kubernetes.io/name: kube-ops-view
EOF
상황을 빠르게 실시간으로 모니터링하기 위해 kube ops view까지 설치해주었다.
EKS 인플레이스 업그레이드 실습
컨트롤 플레인
콘솔, eksctl, aws cli 등 방법은 다양하나 이미 테라폼으로 리소스를 관리하고 있으니 테라폼을 적극 이용한다.
간단하게 입력 값을 수정한다.
이 입력값은 eks 모듈의 버전에 들어가므로, 이 변경사항이 적용되면 클러스터 업그레이드가 시작된다.
정확하게는 컨트롤 플레인의 업그레이드이고, 연결된 노드 그룹에 버전 명시가 따로 돼있지 않다면 이 업그레이드 적용으로 인해 같이 업그레이드가 진행된다.
그러나 현재 코드 상에서는 버전 명시가 분리돼있어 데이터 플레인은 따로 진행해야 한다.
플랜을 해보면, 교체되는 것과 새로 생기는 것들이 존재한다.
액세스 엔트리의 경우 완전히 새로 교체되는데, 실제로 리소스 상에서 교체가 일어나진 않는 경우도 있어서 무조건 믿을 수는 없다.
업그레이드 진행 간 eks 애드온 관련 모듈도 버전의 영향을 받도록 코드가 세팅돼있기에, 각 애드온들에 대해서도 버전 정보가 새로 읽힌다.
그러나 이건 쿠버네티스 버전에 따라서 버전이 읽히는 것이지 실제로 애드온 자체의 버전이 변경되는 것은 아니다.
most_recent
가 참이라면 버전이 변경되기는 할 것이다.
apply를 하자 콘솔에서도 버전 업데이트 이력이 생긴다.
성공적으로 업데이트가 이뤄진다면 이렇게 버전 정보가 변하게 된다.
cli로 확인했을 때도 버전이 업데이트된 것이 확인된다.
애드온
다음으로 업데이트 할 것은 애드온인데, vpc cni의 경우 이미 most-recent로 설정돼있다.
ADDON=vpc-cni
VERSION=1.26
aws eks describe-addon-versions --addon-name $ADDON --kubernetes-version $VERSION --output table \
--query "addons[].addonVersions[:10].{Version:addonVersion,DefaultVersion:compatibilities[0].defaultVersion}"
현 버전에 적절한 애드온 버전을 확인해보면..
쿠버 1.26에서 가장 최신 버전이 위에 있는 것을 볼 수 있다.
vpc cni는 이미 같이 해당 버전으로 업데이트가 진행됐다.
웬만해서는 문제 될 일이 많지는 않겠으나, 컨트롤할 수 없는 변경은 지양하는 게 좋다고 생각한다.
아무튼 위의 명령어를 통해 각 애드온의 최신 버전, 혹은 기본 버전으로 코드를 수정하고 업데이트 해준다.
요로코롬 바꿔주었다.
이들의 업데이트는 디플로이먼트의 전략에 따라 진행될 텐데, 후에 워커 노드 업데이트 간 발생할 다운타임을 고려한다면 지금 미리 토폴로지 분산 제약을 설정해두는 것도 좋을 것이다.
데이터 플레인
데이터 플레인으로 사용할 수 있는 노드의 방식이 다양한 만큼, 각각의 업그레이드 방식에도 조금씩 차이가 있다.
관리형 노드 그룹
일단 관리형 노드의 경우 오토스케일링그룹으로 관리되기에 오토스케일링의 전략을 그대로 따라간다.
구체적으로는 다음의 단계를 따른다고 보면 된다.
- 오토스케일링 런치 템플릿에 새 리비전 등록.
- 한번에 병렬적으로 새 버전이 프로비저닝될 수 있는 설정된다.
- 새 버전 노드 전부 프로비저닝.
- 설정된 가용 영역 개수의 최대 2배까지 최대 노드 제한이 확장된다.
- 이전 버전의 노드를 드레인하며, 파드들이 알아서 새 버전의 스케줄링되도록 유도.
- 이전 버전 노드를 하나씩 줄임
이때 주의할 것은 ami이다.
그냥 쿠버네티스 버전을 명시하여 만든 노드 그룹이라면 상관 없는 이야기이다.
그러나 노드 그룹의 ami를 특정하여 설정한 경우라면 ami의 버전을 바꿔줘야만 한다.
그러나 쿠버네티스 버전 따라 각 ami도 버전도 달라지기 때문이다.
aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.26/amazon-linux-2/recommended/image_id \
--region $AWS_REGION --query "Parameter.Value" --output text
이런 식으로 사용하고자 하는 버전에 추천되는 ami id를 기입하는 방식으로 이용하자.
테라폼 코드 상에서는 입력 변수 값으로 노드 그룹 버전을 지정하는데, 아래 블루 노드그룹의 경우 하드 코딩이 돼있다.
이건 직접적으로 업그레이드하는 상황을 실습할 것이다.
아무튼 업그레이드를 진행하면 위에서 말한대로 새로운 버전의 노드가 일단 먼저 프로비저닝되기 시작한다.
업그레이드 진행 간 최대 노드 개수가 자동으로 조금 더 확장되는데, 이것은 최대 노드 개수만큼 프로비저닝된 상태에서도 새 버전의 노드가 프로비저닝될 수 있도록 하기 위함이다.
기본적으로 노드그룹이 허용된 가용영역 개수의 2배까지는 최대 개수가 확장되어 각 가용영역에 노드가 배치되는 것이 이상 없도록 한다.
허허.. 15분이 걸렸다..
관리형 노드 그룹 블루그린
위에서 잠시 봤듯이 블루라는 노드 그룹은 버전이 업데이트되지 않았다.
이 그룹에 대해서는 블루그린 방식으로 업데이트를 진행할 것이다.
이런 식으로 하는 이유는, 이 노드 그룹은 한 가용 영역에만 배치되어 ebs 스토리지를 사용하도록 되어있기 때문이다.
kubectl describe node -l type=OrdersMNG
현재 이 노드그룹은 상태유지가 필요한 워크로드만을 배치하기 위해 테인트까지 걸려있는 상태이다.
이런 워크로드를 배치할 때는 보통 하나의 가용영역에만 배치하고(ebs를 쓸 때) 업데이트도 블루 그린으로 안전하게 하는 것이 보통이다.
물론 인플레이스로 하는 것이 불가능한 것은 아니지만, 어차피 노드 그룹 업데이트로만 본다면 여기에서 블루그린 방식의 업그레이드는 결국 인플레이스 업그레이드 방식에서 중간에 수동으로 조절할 수 있는 한 스텝을 추가한 것과 다름없다.
그래서 이전 노드그룹과 같은 조건을 가진 노드 그룹을 하나 더 만든다.
위에서 준비를 하면서 pdb를 설정했는데, 이것 때문에 그냥 그린 버전으로 이전하려고 할 때 문제가 발생한다.
현재 orders 기본 워크로드의 pdb는 최소 활성화된 파드가 1개 있어야한다는 제한을 걸고 있는데, 애초에 mysql 파드가 하나밖에 없는 관계로, 이 상태로는 업그레이드가 진행되지 않는다.
cd ~/environment/eks-gitops-repo/
sed -i 's/replicas: 1/replicas: 2/' apps/orders/deployment.yaml
git add apps/orders/deployment.yaml
git commit -m "Increase orders replicas 2"
git push
해결법은 레플리카 개수를 늘려서 깃에 올리고, 아르고가 이를 싱크하게 하는 것이다.
그러나 막상 푸시가 되어도 아르고는 싱크를 하지 않는데, 이유는 위와 같이 레플리카에 대해서 ignore가 걸려있기 때문이다..
깃 상에서 원인을 추적하자면, app of apps 패턴의 상위 어플리케이션에서 일괄적으로 ignoreDifferences
필드를 적용하고 있는 상태이다.
이 문제를 가장 간단하게 해결하는 방법이 뭘까 조금 고민했는데, hpa를 사용하기로 마음 먹었다.
레플리카는 어차피 수동으로 조절하는 것이 그렇게 좋은 방식이 아니라고 판단했다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: orders
namespace: orders
spec:
minReplicas: 2
maxReplicas: 4
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: orders
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
그냥 최소 레플리카 개수를 2개로 둔다면, 알아서 hpa가 디플로이먼트를 스케일 해주므로 더 이상 코드로서 레플리카를 관여할 필요도 없어지면서 pdb를 충족시킬 수 있게 된다.
api 버전 주의하자!
위에서 잠시 봤듯이 1.26에서는 v2beta2의 hpa 리소스는 더 이상 지원되지 않으며, 이제 v2에서는 metrics라는 필드 아래 조건을 걸 메트릭을 정의해야 한다.
마지막으로, 각 앱들은 kustomize로 관리되고 있는 상태이므로 해당 리소스가 커스토마이즈로 관리되도록 명시를 해주는 과정을 넣어주어야 한다.
git add apps/orders
git commit -m "Orders: Create HPA and remove replicas field in deployments"
git push
제대로 싱크가 된 것을 확인하고, 블루 노드 그룹을 없애보자.
단순하게 테라폼에서 블루 노드 그룹 블록을 지우고 apply를 하면 된다.
문제 없이 파드가 그린에 해당하는 노드로 옮겨간 것을 확인할 수 있다.
https://catalog.us-east-1.prod.workshops.aws/event/dashboard/en-US/workshop/module-3/20-mangednodegroupupgrade/new-mng
이 페이지에서 깃 반영을 하더라도 실제로는 레플리카가 ignore 되어 있다.
이것에 대한 설명을 추가하거나, 아니면 커스텀된 설정을 넣어주는 것이 혼란을 줄일 수 있다.
카펜터
다음으로 진행해볼 것은 카펜터로 프로비저닝되는 노드이다.
근데 사실 이건 간단한 게, 카펜터의 노드클래스로 ami를 명시하는 부분만 바꿔주면 된다.
현재는 1.25 버전을 지원하는 ami를 쓰고 있으므로, 이걸 1.26 버전에 해당하는 ami로 바꾸기만 하면 된다.
amiSelectorTerms
필드를 수정해주기만 하면 되는데, 사실 이 필드를 그냥 없애면 amiFamily 조건에 따라 AL2를 고르되 컨트롤 플레인의 버전에 맞는 최신 버전의 AMI를 알아서 골라준다.
즉, 이후에 클러스터 버전을 더 업그레이드한다고 해도 카펜터가 알아서 워커 노드의 버전이 컨트롤플레인의 버전에 맞도록 반영해준다는 말이다.
다만 이 방식은 해당 버전에서 지원되는 최신 ami가 또 나온다면 그것대로 또 알아서 노드를 교체해주게 되니, 그런 상황을 의도한 게 아니라면 일찌감치 amiSelectorTerms
로 특정한 ami id를 지정하거나 필터링을 하는 것이 좋겠다.
aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.26/amazon-linux-2/recommended/image_id \
--region ${AWS_REGION} --query "Parameter.Value" --output text
이렇게 어떤 클러스터 버전에 어떤 os에서 가장 추천되는 ami가 무엇인지 확인할 수 있다.
하지만 나는 그냥 amiFamily만을 기반으로 알아서 카펜터가 새 버전의 노드를 프로비저닝하는 것을 보기로 결정했다.
방식이야 아까와 다를 것 없이, 카펜터 앱의 코드를 변경하여 푸시하면 아르고 CD가 알아서 반영한다.
단순하게 describe를 해서 노드클래스 리소스를 보면, 조건에 매칭된 ami가 뭐가 있는지 표시된다.
기다리다보면 이렇게 새 노드가 갑자기 생기게 된다.
그리고 이전 버전의 노드는 사라졌다.
새로 들어온 노드는 정확하게 1.26 버전이 것을 확인할 수 있다.
이제 보니 실질적으로 추천하는 ami가 그냥 최신 버전의 ami인 건지 결국에는 별도의 설정을 하지 않아도 추천된 ami로 노드가 세팅됐다.
셀프형 노드 그룹은 테라폼에서 ami를 바꿔서 하는 수밖에 없다.
카펜터에 세팅된 것과 같은 ami를 넣어주면 된다.
파게이트
마지막으로 파게이트 노드는.. 그냥 파드를 재기동하면 된다.
파게이트는 기본적으로 컨트롤 플레인의 버전에 맞춰 자원이 프로비저닝된다.
그렇기 때문에 컨트롤 플레인만 업그레이드됐다면, 새로 파게이트 자원을 프로비저닝하기만 하면 업그레이드가 완료되는 셈이다.
EKS 블루그린 업그레이드 실습
인플레이스 업그레이드는 겨우 한 버전씩만 올리는 것에 그치지만, 블루그린의 경우 원하는 만큼 점프를 하기 쉽다!
실습에서는 같은 vpc안에 클러스터를 구축하는데, 장점은 다음과 같다.
- 네트워크 연결성
- nat gateway, vpc 커넥션 등의 자원 공유
- 보안 그룹 안전성
- dns 일관성
등등..
그린 클러스터 구축
이미 제공돼있는 그린 버전의 테라폼 코드를 실행한다.
당연히 기존 버전과 실질적인 양식은 거의 동일하다.
export EFS_ID=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text)
echo $EFS_ID
terraform init
terraform apply -var efs_id=$EFS_ID
efs의 id를 같이 넣으면서 실행해주면 된다.
새로운 버전의 클러스터가 1.30 버전으로 올라왔다.
aws eks --region ${AWS_REGION} update-kubeconfig --name ${EKS_CLUSTER_NAME} --alias blue && \
kubectl config use-context blue
aws eks --region ${AWS_REGION} update-kubeconfig --name ${EKS_CLUSTER_NAME}-gr --alias green && \
kubectl config use-context green
간단하게 클러스터 컨텍스트를 변경할 수 있도록 세팅한다.
해당 클러스터는 카펜터와 아르고 cd까지만 기본으로 설치돼있다.
샘플 어플리케이션을 본격적으로 배포하기 위해 아르고 cd에 레포를 연결하고 어플리케이션을 만드는 작업을 본격적으로 진행한다.
cd ~/environment/eks-gitops-repo
git switch -c green
git branch -a
현재 main 브랜치에서 그린 브랜치를 파고, 이 브랜치를 통해 그린 클러스터를 구축한다.
export AL2023_130_AMI=$(aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.30/amazon-linux-2023/x86_64/standard/recommended/image_id --region ${AWS_REGION} --query "Parameter.Value" --output text)
cat << EOF > ~/environment/eks-gitops-repo/apps/karpenter/default-ec2nc.yaml
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: default
spec:
amiFamily: AL2023
amiSelectorTerms:
- id: "${AL2023_130_AMI}" # Latest EKS 1.30 AMI
role: karpenter-eksworkshop-eksctl-gr
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: eksworkshop-eksctl-gr
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: eksworkshop-eksctl
tags:
intent: apps
managed-by: karpenter
team: checkout
EOF
일단 카펜터의 경우 고르는 ami를 바꿔줘야 한다.
특이사항으로는 업그레이드를 진행하면서 ami 패밀리 자체를 AL2023으로 바꿨다는 것 정도?
pluto detect-files -d ~/environment/eks-gitops-repo/
이밖에도 버전 호환성과 업데이트를 위한 툴 중 하나인 pluto를 이용해서 각 양식의 버전 상태를 확인해볼 수 있다.
위에서도 봤듯이, hpa의 api 버전이 deprecated됐다.
cd ~/environment/eks-gitops-repo/
kubectl convert -f apps/ui/hpa.yaml --output-version autoscaling/v2 -o yaml > apps/ui/tmp.yaml && mv apps/ui/tmp.yaml apps/ui/hpa.yaml
cat apps/ui/hpa.yaml
버전을 바꾸는 작업은 convert라는 플러그인을 사용한다.
# 최신 버전으로 바꾸어 출력
kubectl convert -f pod.yaml --local -o json
# 해당 디렉토리의 모든 양식을 최신 버전으로 적용
kubectl convert -f . | kubectl create -f -
# 출력 버전 지정
kubectl convert -f hpa.yaml --output-version autoscaling/v2
이 플러그인은 직접적으로 버전을 명시해서 사용하는 것도 가능하지만, 현재 클러스터에 기반하여 특정 kind의 최신 버전으로 바꾸어 출력하는 기능도 제공하기에 잘 쓰면 꽤나 유용하다.
sed -i 's/targetRevision: main/targetRevision: green/' app-of-apps/values.yaml
git add . && git commit -m "1.30 changes"
git push -u origin green
이제 새로운 상위 app을 배포한다.
export ARGOCD_SERVER_GR=$(kubectl get svc argo-cd-argocd-server -n argocd -o json --context green | jq --raw-output '.status.loadBalancer.ingress[0].hostname')
echo "ArgoCD URL: http://${ARGOCD_SERVER_GR}"
export ARGOCD_USER_GR="admin"
export ARGOCD_PWD_GR=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" --context green | base64 -d)
echo "Username: ${ARGOCD_USER_GR}"
echo "Password: ${ARGOCD_PWD_GR}"
argocd login --name green ${ARGOCD_SERVER_GR} --username ${ARGOCD_USER_GR} --password ${ARGOCD_PWD_GR} --insecure --skip-test-tls --grpc-web
이전에는 웹 ui를 통해 세팅을 확인하고 진행했으나, 이번에는 세팅은 cli를 통해 해보자.
(근데 여태 받은 느낌으로는 아르고 cd는 다른 아르고 시리즈보다도 ui가 훨씬 잘돼있어서 ui를 주로 사용법을 익히는 것도 괜찮을 것 같기는 하다.)
argo_creds=$(aws secretsmanager get-secret-value --secret-id argocd-user-creds --query SecretString --output text)
argocd repo add $(echo $argo_creds | jq -r .url) --username $(echo $argo_creds | jq -r .username) --password $(echo $argo_creds | jq -r .password) --server ${ARGOCD_SERVER_GR}
같은 레포지토리를 쓰기 때문에 여기까지는 블루 클러스터와 다를 게 없다.
argocd app create apps --repo $(echo $argo_creds | jq -r .url) --path app-of-apps \
--dest-server https://kubernetes.default.svc --sync-policy automated --revision green --server ${ARGOCD_SERVER_GR}
다만 이번에는 revision을 green으로 하여, 위에서 수정한 브랜치를 기준으로 앱을 배포한다.
app of apps 패턴으로 인해 세팅된 모든 앱들이 배포되기 시작한다.
이제 두 개의 클러스터가 있는 상태이므로, 그린 클러스터를 테스트하고 문제가 없다면 블루 클러스터를 제거하는 식으로 업그레이드를 마치면 된다.
현 실습에서는 따로 도메인을 제공해주지 않으나, 실제 서비스를 운영하고 있다면 각종 서비스를 위해 도메인을 두고 있을 것이다.
이때 도메인 기반으로 라우팅을 진행하며 트래픽을 점진적으로 그린 클러스터로 바꾸는 식으로 마이그레이션을 하는 방식을 생각해볼 수 있다.
external-dns.alpha.kubernetes.io/set-identifier: {{ .Values.spec.clusterName }}
external-dns.alpha.kubernetes.io/aws-weight: '{{ .Values.spec.ingress.route53_weight }}'
External DNS를 이용할 경우 위의 어노테이션을 이용해 각 클러스터 별로 라우팅되는 트래픽을 가중치로 나누어 전달하는 전략이 유효할 수 있다.
추가 고려사항
블루그린에서 고려해야 할 가장 어려운 사항 중 하나가 바로 상태 유지 어플리케이션이다.
실습 상에서는 EFS를 이용하고, 스테이트풀셋으로 프로비저닝된 자원을 그린 버전에서 그대로 받아보는 식으로 실습을 진행한다.
그래도 실습 상에서는 다중 가용영역에서 멀티 어태치가 가능한 EFS를 사용하기에 그나마 용이하다고 할 수 있겠다.
그러나 실제 운영 환경에서 블루 그린 업그레이드를 진행한다면, 이보다 훨씬 엄밀한 접근이 필요하다.
일단 특정 파일이나 프로세스를 블루 클러스터의 어플리케이션이 독점하는 경우도 있을 수 있다.
대표적으로 대부분의 RDBMS는 DB 프로세스가 스토리지를 완전히 lock을 걸어버린다.
또 EBS의 경우 한 스토리지는 한 가용 영역에서만 사용할 수 있으며 인스턴스와 1대1 연결이 기본이기에(multi attaching의 경우 Nitro System을 이용한다던가 하는 추가 제약이 있다[2]), 볼륨 스냅샷을 뜨거나 그냥 스토리지를 복사하여 그린 클러스터에 배치해주는 방식의 전략을 생각해볼 수 있다.
이렇게 사용한다고 해도 복제된 스토리지 간의 동기화를 고려해야 하며, 이를 위해 데이터를 클러스터와 더 격리된 환경에서 관리하는 리소스를 추가적으로 투입해야 할 수 있다.
결론
내 로컬 환경에서 클러스터 버전을 업그레이드했던 경험이 있었다.
그러나 이것은 사실 단순하게 CKA 시험을 준비하기 위해 kubeadm으로 딸깍 해보는 정도에 그쳤을 뿐이다.
만약 다양한 어플리케이션이 구동되고 있는 상황에서 업그레이드를 진행했더라면 더욱 많은 고려사항을 두고 점진적으로 업그레이드를 진행했을 것 같다.
EKS에서의 업그레이드 역시 고려사항을 면밀히 검토할 필요가 있다.
그러나 최소한 컨트롤 플레인에 대한 업그레이드를 사용자가 책임 질 필요는 없다는 점은 확실한 장점이라고 이야기할 수 있을 것 같다.
생각
이것도 장점이라 볼 수 있을지는 모르겠는데, 사실 EKS를 쓰게 된다면 클러스터 버전 업그레이드는 사실상 안 하면 돈을 내야 하기에 반강제나 다름 없다.
버전을 업그레이드를 하는 것은 어렵고 사이드 이펙트를 많이 고려해야 하지만, 그래도 그럼으로서 얻는 이득이 또 있기도 하다.
그런 면에서 배수진(?!)을 치게 만드는 것도.. EKS의 장점..?이라 할 수 있지 않을까?
운영팀의 입장에서는 언젠가 해야 할 일이 분명한 사항이면서도 조직에 해당 업무의 당위성을 충분히 어필할 수 있으니..
경험해보지 않았으니 말은 조심해야겠지만, 그래도 업그레이드를 주기적으로 하는 것은 궁극적으로는 서비스를 안정적으로 운영하도록 하는데 도움이 될 것이라 생각한다.
이전 글, 다음 글
- 8W - CICD: 23
- 10W - Vault를 활용한 CICD 보안: 25
다른 글 보기
이름 | index | noteType | created |
---|---|---|---|
1W - EKS 설치 및 액세스 엔드포인트 변경 실습 | 1 | published | 2025-02-03 |
2W - 테라폼으로 환경 구성 및 VPC 연결 | 2 | published | 2025-02-11 |
2W - EKS VPC CNI 분석 | 3 | published | 2025-02-11 |
2W - ALB Controller, External DNS | 4 | published | 2025-02-15 |
3W - kubestr과 EBS CSI 드라이버 | 5 | published | 2025-02-21 |
3W - EFS 드라이버, 인스턴스 스토어 활용 | 6 | published | 2025-02-22 |
4W - 번외 AL2023 노드 초기화 커스텀 | 7 | published | 2025-02-25 |
4W - EKS 모니터링과 관측 가능성 | 8 | published | 2025-02-28 |
4W - 프로메테우스 스택을 통한 EKS 모니터링 | 9 | published | 2025-02-28 |
5W - HPA, KEDA를 활용한 파드 오토스케일링 | 10 | published | 2025-03-07 |
5W - Karpenter를 활용한 클러스터 오토스케일링 | 11 | published | 2025-03-07 |
6W - PKI 구조, CSR 리소스를 통한 api 서버 조회 | 12 | published | 2025-03-15 |
6W - api 구조와 보안 1 - 인증 | 13 | published | 2025-03-15 |
6W - api 보안 2 - 인가, 어드미션 제어 | 14 | published | 2025-03-16 |
6W - EKS 파드에서 AWS 리소스 접근 제어 | 15 | published | 2025-03-16 |
6W - EKS api 서버 접근 보안 | 16 | published | 2025-03-16 |
7W - 쿠버네티스의 스케줄링, 커스텀 스케줄러 설정 | 17 | published | 2025-03-22 |
7W - EKS Fargate | 18 | published | 2025-03-22 |
7W - EKS Automode | 19 | published | 2025-03-22 |
8W - 아르고 워크플로우 | 20 | published | 2025-03-30 |
8W - 아르고 롤아웃 | 21 | published | 2025-03-30 |
8W - 아르고 CD | 22 | published | 2025-03-30 |
8W - CICD | 23 | published | 2025-03-30 |
9W - EKS 업그레이드 | 24 | published | 2025-04-02 |
10W - Vault를 활용한 CICD 보안 | 25 | published | 2025-04-16 |
11W - EKS에서 FSx, Inferentia 활용하기 | 26 | published | 2025-04-18 |
11주차 - EKS에서 FSx, Inferentia 활용하기 | 26 | published | 2025-05-11 |
12W - VPC Lattice 기반 gateway api | 27 | published | 2025-04-27 |
관련 문서
이름 | noteType | created |
---|---|---|
컨테이너 프로브 | knowledge | 2024-08-22 |
CICD | knowledge | 2024-06-13 |
KEDA | knowledge | 2024-12-29 |
Karpenter | knowledge | 2025-03-04 |
Argo CD | knowledge | 2025-03-24 |
GitOps | knowledge | 2025-03-24 |
Argo Workflows | knowledge | 2025-03-24 |
Argo Rollouts | knowledge | 2025-03-24 |
아르고 롤아웃과 이스티오 연계 | knowledge | 2025-04-22 |
uv | knowledge | 2025-05-02 |
메시 배포 모델 | knowledge | 2025-05-21 |
Istio WorkloadGroup | knowledge | 2025-05-26 |
Istio WorkloadEntry | knowledge | 2025-05-26 |
이스티오 스케일링 | topic | 2025-05-18 |
엔보이에 와즘 플러그인 적용해보기 | topic | 2025-06-09 |
k8s air-gap install | topic | 2025-06-09 |
폐쇄망 k8s 설치 개요 | topic | 2025-06-09 |
에어갭 kubespray 단일 노드 설치 with Vagrant | topic | 2025-06-09 |
kubespray 삽질 | topic | 2025-06-11 |
kubesphere | topic | 2025-06-12 |