일정: 2024.09.12(목) 10:00 ~ 17:00
장소: 삼성동 코엑스 그랜드볼룸
여러 세션들이 준비되어 있었지만 저는 아래의 3가지 세션에 참석하였습니다.
이 3가지 세션에서 중요한 포인트라 생각되었던 것과 느낀점들을 남겨보고자 합니다.
토스 뱅크에서 사용하는 배포 전략에 대해 알 수 있었습니다.
구분 | Rolling Update | Canary |
---|---|---|
배포 적합성 | 낮음 | 높음 (네트워크 추상화 도구의 트래픽 조절 기능 개발 필요) |
롤백 시간 | 느림 | 빠름 |
세부적인 트래픽 조절 | 불가능 | 가능 |
내부 서비스에 적합 | 고객 서비스에 적합 |
토스 뱅크가 사용하는 용도에 맞는 배포 전략 선정을 살펴볼 수 있었고
기존 카나리 배포의 문제점과 이를 해결하기 위한 Sticky Canary 도입에 대한 설명을 들었습니다.
Sticky Canary는 네트워크 로드밸런서 장비의 Sticky Session이라는 기능에서 따온 것이고,
이것처럼 Sticky Canary는 카나리 배포 동안에 동일한 유저의 호출은 동일한 버전으로 고정해주는 기능이라고 합니다.
이 개념을 바탕으로 Istio의 기능을 이용한 Sticky Canary 구현에 대한 설명으로 가설과 검증, 결론과 결과에 대해 알 수 있었습니다.
Istio의 기능만을 사용해 구현하고자 하는 목표로 ConsistentHashLB를 사용하여 검증하였으나, 카나리 배포 특성상 점진적 배포를 위해 Weight 값을 변경하였을 때 New Version으로 라우팅되던 유저가 Old Version으로 라우팅 될 수 있는 문제가 있어 이를 해결하기 위해 SSR API Gateway와 Istio를 이용해 Sticky Canary를 구성
모든 유저의 호출에 존재하는 UserUniqueKey 값을 해싱 후 연산처리하면 0~99의 값을 가져올 수 있는데, 이 값을 헤더에 활용하여 해당 값이 weight 비율보다 높으면 Old Version을 헤더에 추가 weight 비율보다 낮으면 New Version을 헤더에 추가
이후 Istio에서 넘어온 헤더값(X-Tossbank-Sticky-Version) 확인을 통해 v2-xx 값이면 v2-xx로 라우팅, v1-xx면 v1-xx로, 값이 없으면 v1-xx로 위 설정의 유의사항으로는 순차적 처리이기 때문에 의도에 맞도록 순서를 정렬할 필요가 있음
완료 이후 헤더 추가 부분을 제거하고, 헤더 매칭 라우팅을 제거하여 default로 New Version이 라우팅 되도록 하는 과정까지 Sticky Canary로 프론트엔드 서비스에 카나리 배포를 완료한 과정들을 알 수 있었습니다.
이후에 Sticky Canary 외의 배포 과정에서 발생할 수 있는 문제를 어떻게 제어할 수 있는지 들어보았습니다.
MSA 구성에서 A-Service를 배포 했을 때 연관된 BDC..-Service들에서 장애가 발생하는 상황이라면 장애 원인을 파악하기에 상당한 시간이 소요될 수 있다는 점을 예시로 이를 해결하기 위해 시스템이 시스템을 모니터링 할 수 있도록 Auto Canary를 도입한 내용에 대해 알게되었습니다.
배포가 발생하면 하나의 Thread가 생성되고 Auto Rollback이 될 수 있는 기준치 중 하나라도 만족하게 된다면 자동으로 문제가 되는 버전의 트래픽을 0으로 롤백시켜주고 배포 담당자에게 맨션으로 공유하는 Auto Canary를 알게 되었고 적절한 수준으로 보수적인 배포가 이루어지기 위한 환경에 대해 여러 생각이 들었던 세션이었던 것 같습니다.
토스에서 쿠버네티스 운영에 사용한 Kubespray에 대한 문제점과 이를 해결하기 위한 과정들에 대해 알게되었습니다.
노드 조인에 30m의 시간이 소요되었고, 운영체제/커널 업그레이드에도 30m의 시간 소요, Kubernetes 업그레이드에 2h의 시간이 소요되고 Kubernetes 버전 업그레이드 시 한번에 하나의 버전씩 업그레이드를 권장하는 점을 문제점으로 설명해주었고 이 문제점을 해결하기 위해 온프레미스 서버에서 EKS의 방식처럼 인스턴스가 프로비저닝 되는 시점에 노드가 조인되도록 자동화를 도입한 과정들을 알 수 있었습니다.
이 과정에서 데브옵스 엔지니어의 공수 없이 노드가 조인되는 자동화와 디버깅이 간편하며 복잡하지 않은 아키텍처, GitOps가 가능한 구성을 고려사항으로 두었고 디버깅이 간편하며 복잡하지 않은 아키텍처를 수용하기 위해 최소한의 구성을 가진 쉘 스크립트를 작성하여 프로비저닝 된 노드가 쉘 스크립트를 받아서 실행했을 때 실행되는 3가지의 작업에 대한 설명을 들었습니다.
- 클러스터 디스커버리 작업
- 다양한 클러스터 환경에서 신규로 프로비저닝되는 노드가 어느 클러스터에 조인되어야 되는지 확인하는 단계
이 단계에서는 프로비저닝되는 노드에 postman 규칙을 사용해서 조인해야될 클러스터의 서버 컴포넌트 레코드를 참조할 수 있도록 구성- 클러스터 버전에 맞는 구성파일 다운로드 작업
- 조인할 클러스터의 버전을 확인한 후에 클러스터에 맞게 구성파일을 다운로드하는 작업
- kubeadmin을 사용한 워커노드 조인 단계
- 워커노드가 쿠버네티스 클러스터에 조인을 시도하고 정상적으로 수행되었는지, 실패했다면 어떤 사유로 실패했는지 알림으로 전송해주는 단계
이 단계를 거쳐 정상적으로 조인된 노드들은 CNI 설치 직후 서비스 파드가 즉시 스케줄링되게 되는데 서비스 파드가 즉시 스케줄링되면 데브옵스의 예상과는 다르게 발생할 수 있는 문제들이 있을 수 있어 CNI 설치 직후 서비스 파드가 즉시 스케줄링되는 것을 방지하기 위해 --register-schedulable 옵션을 false로 설정한채로 실행
노드 조인 자동화를 진행한 이후에 빈번히 발생하면서 작업마다 30분정도씩 소요되는 노드 조인 작업 공수를 30m -> 0m으로 없앨 수 있었고 운영체제/커널 업그레이드는 노드 그룹 당 30m 걸리던 작업이 2m으로 줄어들었으며 kubernetes 버전 업그레이드는 하나의 버전 당 2시간 정도 소요되던 업그레이드 시간이 30m으로 줄게된 결과에 대해 알 수 있었습니다.
이 과정들을 보면서 Karpenter on EKS처럼 온프레미스 환경에서 효율적인 노드 스케일링 구조를 만든것 같다는 생각이 들었습니다. Karpenter의 핵심 로직을 토스의 온프레미스 클러스터 환경에 녹여본다면 더 퍼포먼스가 나올수도 있지 않을까?라는 생각이 드는 주제였던 것 같습니다.
다음으로 서비스 단위에서의 개선 사례에 대해서 신규 서비스가 추가될 때 고려해야할 사항들과 여러 요청 사항들을 개발자가 직접 파이프라인을 생성할 수 있는 UI 개발에 대해 들어보았고 이미지 latest 작업 관련 내용을 들어보았고, 카나리 배포 이후 장시간 방치되는 서비스들이 많아져 클러스터의 리소스 낭비 발생과 이를 해결하기 위한 Alert 설정, 미사용으로 삭제 대상 서비스의 연관된 서비스, DB등을 보여주고 인입된 트래픽을 보여주고 영향이 없을 때 서비스를 삭제할 수 있도록 구성해 준 내용들을 들어보았습니다.
이로도 해결되지 않는 문제들이 있어 HTTP 트래픽과 컨슈머 트래픽, BATCH 미사용 여부를 판별하는 메트릭을 통해 결론적으로 80개의 서비스를 제거하고 1280코어 감소, 1280Gi의 메모리 감소 효과를 가져오게 된 것을 알게되었습니다.
미사용 서비스들을 판별하는 작업의 기준을 명확하게 짚고 넘어갈 수 있었던 시간이었던 것 같고 컨슈머를 트래픽할 수 있는 수단을 다시 한번 생각해보게 된 시간이었던 것 같습니다.
해당 세션에서는 자원 최적화를 위한 4가지 목표를 주제로 이 주제를 Kubernetes CPU 최적화로써 보는 시간이었습니다.
1. 서비스 안정성 유지 -> CPU Throttling 방지
2. 자원 할당량 최소화 -> CPU Requests/Limits 최소화
3. 자원 사용량 최소화 -> CPU 사용량 최소화
4. 자원 사용량 분산 -> CPU 사용량 분산
CPU 지표에서 CPU 사용량 추이가 Limits보다 낮아도 Throttling이 발생하는 특이한 상황에 대해서 알아보았고 이러한 현상에 대해 이해하기 위해 CFS와 CFS quota 동작 방식에 대한 설명으로 이러한 현상을 완화하고 방지하기 위한 방법에 대해 설명해주었습니다.
완화: Kubelet cpu-cfs-quota-period 낮추기
방지: Kubelet cpu-cfs-quota 비활성화 (NO LIMIT)
Throttling을 낮추기 위한 방법에 대해 고민해볼 수 있는 좋은 시간이었던 것 같습니다.
이어서 리소스 최적화 부분에 대해 할당량이 너무 낮아 여유 부족, 할당량이 너무 높아 자원 낭비 두 가지 기준점으로 최적화를 진행한 것에 대해 이것에 대한 기준 메트릭 설정을 좀더 면밀하게 판단할 수 있도록 max_over_time 중첩을 통해 해결한 점이 인상깊었습니다.
container_cpu_usage_seconds_max = max_over_time(container_cpu_usage_seconds_total[30m:1m])
max_over_time(container_cpu_usage_seconds_max[7d:15m])
이 판단 기준으로 실제 서비스의 CPU 사용량을 평가하여 최적화를 진행해 볼 수 있겠다 싶었습니다.
다음으로 Right Sizing 사례 소개로 노드의 리소스 최적화 과정을 들어보았습니다.
CPU는 가득채워 사용하고 있는데 메모리의 여유공간이 너무 많이 남으면 자원 낭비가 발생하는 점을 워커노드의 4배가 넘는 스펙업을 통해 좀 더 효율적으로 노드를 사용한 경험에 대해 알게 되었습니다.
또한 Istio의 리소스 사용 문제를 해결하기 위한 toss mixer 도입에 대한 내용도 인상 깊었습니다.
도입한 결과, 프로메테우스와 Istio의 리소스 사용량이 크게 감소한 사례를 볼 수 있었고 프로메테우스의 TSDB status, version update로 사용량 감소한 케이스와 스케줄러 분배의 문제점 설명으로 부하가 발생했을 때 괴리율이 40% 수준으로 발생한 경험을 topologySpreadConstraint 적용을 통해 부하 분산으로 괴리율를 20%까지 개선한 경험에 대해 들어본 시간이었습니다.
TopologySpreadConstraint 적용과 예측할 수 있는 부하 수준에 대해 Keda를 설정하고 서비스 도메인 당 노드그룹을 관리하여 사용하는 입장으로써 노드를 오염시켜 격리한다면 괴리율을 더 감소시킬 수 있지 않을까 싶은 생각이 들었던 것 같습니다.
토스의 매년 제곱으로 증가되는 트래픽이 비용적으로 크게 비효율적으로 다가올 수 있는 문제에 대해 한정된 자원내에서 최대의 효율을 일궈낸 점들이 크게 인상 깊었던 것 같습니다.
토스의 귀중한 경험들을 알게된 시간이라 생각되고 뜻깊은 시간이었던 것 같습니다.
다음에도 기회가 찾아온다면 SLASH 25에서 더욱 재미있는, 효율적인 세션들을 기대해보겠습니다.