먼저, 그동안 블로그를 소홀히했는데 다시 정신차리기로 했다. IT 엔지니어는 늘 빚지고 살고 있으니 글 쓰면서 조금이라도 빚 갚고 살아야 생각하는데 안하고 있었다. 다시 정신차리자. (하루 방문수 100은 되는것 같은데 미안한 마음이 크다)
최근에 회사에서 운영 중인 EKS를 업그레이드 했다. 아틀라스랩스는 총 10개 클러스터를 운영 중인데 작업은 1.23 to 1.24 (하나는 1.22 to 1.23) 업그레이드를 했다. 이전에는 업그레이드 작업하면 서비스 끊고 새벽 00:00 ~ 06:00 6시간 작업을 했다. 쿠베가 4개월에 한번씩 업그레이드하는데 4개월마다 서비스를 중단하는게 말이 안되고 그걸 또 밤 새면서 하는건 사람이 할 짓이 아니기에 어떻게 하던지 대책을 마련해야했다. (그래서 기존에는 4개월마다 안하고 미루고 미루고 EOS 되어야 한꺼번에 모아서 작업했다.)
새로운 방법을 생각했고 다행히 성과가 있었다.
AS-IS
- EKS GUI를 통한 수동 작업
- EKS 업그레이드 시 GUI를 이용하여 컨트롤 플레인 & 10개 정도되는 노드 그룹을 일일이 업그레이드 버튼 클릭
- 이러면 아마도 쿠버네티스 내부적으로 노드 한대씩 drain + delete 하면서 새로운 노드를 만들 것이다.
TO-BE
- 테라폼(+테라포머 통한 TF 코드 Import)을 이용해서 코드로 작업
- 테라폼 코드로 EKS 컨트롤 플레인 업그레이드 + EKS Addon 설치/업그레이드(옵션)
- 이전 버전의 노드를 삭제하지 않고 그대로 유지한 상태로 새로운 버전의 노드 그룹 추가
- 이전 버전 노드를 cordon 하고 전체 파드를 Rollout Restart 하여 새로운 버전의 노드에만 실행하도록 설정(파드가 Running 후 Terminating 되니 서비스 무중단)
- 이전 버전 노드는 혹시 모를 Rollback 용도로 하루 정도 놔두고 이상없을 경우 삭제한다. 하루 지나고 문제가 생겨 Rollback이 필요해도 TF 코드로 이전 버전의 노드를 만들 수 있어서 문제없다.
- 작업 시 이전 버전 노드 cordon, 파드의 Running/Terminating 완료 용도의 모니터링은 k9s 를 이용하면 편리하다.
- 핵심은 코드로 작업했고 기존 버전의 노드를 삭제하지 않고 새로운 버전의 노드를 만들고 파드를 재배포했다.
먼저 테라폼을 이용해서 코드로 작업했다.(Infra As Code) 내가 운영하는 EKS 중 하나는 노드 그룹만 10개 정도 되는데 그걸 업그레이드 작업하면서 일일이 클릭하는 것도 시간이 많이 걸리고 옵션 선택할 때 삐긋하면 장애로 이어진다. 밤새면 집중력이 떨어지기에 충분히 발생하는 문제다.
코드로 작업했기에 사전에 리뷰가 가능하여 실수를 없앴다. 10개 클러스터에 반복 적용해서 시간도 절약했다. 같이 일하는 DevOps와 리뷰가 가능한게 가장 큰 장점이다.
이번에는 terraformer 를 사용해서 AWS Resource를 TF 코드로 Import 할 수 있었다. terraformer가 신의 한수였다. 이게 없었으면 아마도 불가능했을 듯. 작업에 사용했던 terrafomer 명령어다.
terraformer import aws --resources=eks --profile=switch-seoul -p generated/switch/ap-northeast-2
위 명령어를 사용하면 gui, eksctl 등으로 테라폼으로 만들지 않은 aws 리소스도 테라폼 코드로 import 가능하다. 아마 수동으로 한다면 불가능하지 않았을까? impor한 코드를 기준으로 tf import 명령어를 실행하면 이제 tfstate 파일에 현재 실행중인 리소스와 동기화(? 동기화가 맞는 표현일지는 모르겠다.)가 되어 인프라를 코드로 관리할 수 있다.
EKS는 eks 뿐만 아니라 vpc, subnet, launchtemplate 등도 같이 연관되어 있는데 관련 리소스도 위 terraformer를 이용해서 import하면 좀 더 테라폼 코드가 보기좋아진다.
이제 코드를 작업할 수 있다. 우리는 GitOps를 사용하는데 깃헙에 작업에 해당하는 코드를 올려서 pr, review하고 작업 시 merge, tf apply 하였다. 업그레이드 작업은 순차적으로 했고 컨트롤 플레인 업그레이드 - EKS Addon 설치 및 업그레이드 - 새로운 버전의 노드 그룹 생성 순이다.
먼저 eks 컨트롤 플레인 업그레이드 작업은 아래 코드처럼 1줄만 변경하면 된다. 단순히 version만 변경한다. 그리고 tf plan -out planfile -> tf apply 하면 끝. 참고로 모든 클러스터는 컨트롤 플레인 업그레이드에 10분 걸렸다.
다음은 Addon 설치. EKS 사용하면서 managed 맡기는게 좋으니 addon까지 설치하고 업그레이드했다.
resource "aws_eks_addon" "aws_ebs_csi_driver" {
cluster_name = aws_eks_cluster.tfer--switch-prod.name
addon_name = "aws-ebs-csi-driver"
service_account_role_arn = "arn:aws:iam::(생략):role/AmazonEKS_EBS_CSI_DriverRole08"
addon_version = "v1.15.0-eksbuild.1"
}
resource "aws_eks_addon" "vpc_cni" {
cluster_name = aws_eks_cluster.tfer--switch-prod.name
addon_name = "vpc-cni"
resolve_conflicts = "OVERWRITE"
}
(생략)
이제 EKS 업그레이드 마지막 준비인 새로운 노드 그룹 설치. 기존 노드 그룹과 완전히 동일하게 하고 버전만 새로운 버전으로 변경한 노드 그룹을 만든다. 이건 그냥 기존 노드 그룹의 코드를 그대로 똑같이 복사하고 아래 버전 정보만 삭제하면 신규 버전으로 자동 설치된다.
resource "aws_eks_node_group" "asterisk-in" {
# release_version = "1.22.15-20221112" 버전 정보만 삭제
# 추가로 desired 설정을 건드리지 않도록 한다.
lifecycle {
ignore_changes = [
scaling_config["desired_size"]
]
}
}
작업이 완료되면 이전 버전, 새로운 버전 2개의 노드 그룹이 만들어진다. 준비가 완료되었다. 이제 진짜 파드(애플리케이션) 업그레이드 작업이다. 작업은 스크립트로 한다.
이전 버전의 노드에 재배포할 파드가 실행되지 않도록 Cordon(경고 페인트를 칠해서 접근을 막는다) 설정을 한다. 이전 버전의 정보를 VERSION 변수에 입력한다.
VERSION="v1.23"
NODES=$(kubectl get nodes -oyaml | yq '.items[]|select(.status.nodeInfo.kubeletVersion=="'"${VERSION}"'*")|.metadata.name')
kubectl cordon $NODES
Cordon 설정을 완료하면 이제 파드를 Rollout Restart한다. 명령어를 실행하면 이전 버전의 노드에서 실행 중인 기존 파드는 Terminating되고 새로운 버전의 노드에 새로운 파드가 실행된다. Running 후 Terminating 되니 무중단으로 작업할 수 있다.(무중단이다. Rollout 만세) Graceful Shutdown 적용이 되어있으니 기존 서비스도 끊어지지 않는다. Rollout Restart는 네임스페이스 단위로 진행하고 해당 네임스페이스의 Deployment, Statefulset 리소스가 대상이다. DaemonSet은 어차피 새로운 노드에 자동으로 실행되니 필요없다.
NAMESPACES=$(kubectl get ns -oyaml |yq '.items[].metadata.name')
for namespace in $NAMESPACES; do
echo $namespace
kubectl rollout restart deployment -n $namespace
kubectl rollout restart sts -n $namespace
done
여기서 엣지 케이스는 PVC를 사용하는 Deployment와 Statefulset이다.
Deployment는 기존 파드가 Terminating 되어야 새로운 파드가 PVC를 할당되니 새로운 파드가 실행되지 못하고 Hang 상태가 된다. 피하기 위해서 수동으로 먼저 k scale deployment {deployment name} --replias 0 작업을 한다. (나같은 경우에는 grafana였다. 하필 중요한 grafana라니) 다음 Statefulset은 먼저 Running 되고 Terminating이 아니라 Terminating되고 Running이다. Terminating 되는 동안 서비스가 중단되지 않도록 미리 replicaset 숫자를 3개로 맞춘다. MySQL, Kafka, Redis 등이 해당하는데 Default가 3 Copy 설치라 문제가 없을 수 있다. 하지만 Write는 1개인 경우가 많으니 Read는 정상이지만 Write는 약 5분 정도 안되는걸 감안해야 한다.(write도 3 copy면 이것도 문제없기는 하다.)
완료되면 이제 모든 파드가 새로운 버전의 노드에서 실행된다. 이론상으로 무중단이 가능하고 실제 작업 시간도 30분 내외이니 엄청 줄일 수 있다.(IT는 더이상 3D가 아니어야 한다. 밤새지 말란 말이야.) 하지만 모든 일이 그렇듯 어떤 원인인지 모르겠는데 하나의 파드가 순간 끊어지는 문제가 있었다.(세상에 쉬운일 없다.... 다행히 서비스 중단을 미리 공지해서 별다른 문제가 없기는 했다.)
비교적 무사히 작업을 잘 끝냈다. 일하면서 손꼽는 나름 뿌듯한 경험이다.
'쿠버네티스 일반' 카테고리의 다른 글
쿠버스택 창업? (4) | 2023.03.20 |
---|---|
GitOps 기반 시스템 운영 (0) | 2023.03.11 |
파이썬으로 여러 쿠버네티스의 클러스터 정보 가져오기 (0) | 2022.11.05 |
EKS CSI Driver 설치 (2) | 2022.06.15 |
Strimzi Kafka Operator - Kafka 토픽 생성 (0) | 2022.06.12 |