티스토리 뷰
문제 상황 정리
작년 9월쯤부터 비용절감을 위해 dev 서버를 spot 인스턴스로 변경했다.
약 30% 정도 비용절감이 효과가 있어서 당시에는 꽤 괜찮은 선택이었는데, 운영하면서 문제가 발생했다.
dev 서버가 spot 인스턴스를 너무 자주 뺏기고, 다시 할당 받는데 시간이 오래 걸리는 현상이 반복됐다.
한두번이면 상관 없는데 하루에도 몇 번씩 뺏기고 다시뜨는데 10분씩 걸리니까, 개발에 차질이 생기는 경우가 생겼다.
이 문제를 해결하고 싶었다.
문제 정의
spot instance란?
스팟 인스턴스는 실행 중인 동안에는 온디맨드 인스턴스와 정확히 동일합니다. 그러나 스팟은 실행 중인 인스턴스를 워크로드를 완료할 수 있을 만큼 충분히 오래 유지할 수 있다고 보장하지 않습니다. 또한 스팟은 찾고 있는 인스턴스의 즉각적인 가용성을 보장하거나 요청한 총 용량을 항상 확보할 수 있다고 보장하지 않습니다. 또한 스팟 인스턴스 가용성은 수요와 공급에 따라 달라지기 때문에 스팟 인스턴스 중단 및 용량은 시간이 지남에 따라 변할 수 있으며 과거의 성능이 미래의 결과를 보장하지 않습니다.
https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/spot-best-practices.html
위 설명처럼 스팟 인스턴스는 가용성을 보장하지 않는다.
수요가 증가할 경우 자동 중단되고 자동 중단을 취소하는 방법은 없다.
다만, 스팟 인스턴스 중단 공지를 보내 중단 2분 전에 이를 인스턴스에 경고해준다.
이 때부터 Karpenter가 새로운 스팟 인스턴스를 프로비저닝하는데, 이 과정이 오래 걸리는 문제인거로 분석했다.
왜 그랬을까?
Terraform 코드를 뒤져보니, 인스턴스 타입이 단 두 가지로 정의되어 있었다.
"capacity_type" = ["spot"]
"instance_type" = ["m6i.large", "r6i.large"]
프로비저닝 시 선택할 수 있는 인스턴스가 한정되어 있다보니, 인스턴스를 찾는데 시간이 걸리는 것이었다.
그리고 입찰 받더라도 다른 곳에서 많이 필요로하니 바로 뺏겨서 빈번하게 서버가 꺼지는 현상이 발생했다.
해결 방안 논의
1. prod과 같은 방식으로 on-demand + spot 구조로 변경
- 대신 상용서버 만큼의 컴퓨팅 용량이 필요하지 않을테니 비용이 적게 드는 인스턴스의 종류를 늘려보자.
- on-demand로 변경하고, 서버가 on-demand로 떠있는 시간이 너무 길면 업무 시간 외에는 서버를 끄는 방식 고려.
2. spot to spot consolidation 방식으로 변경
- 2024년 4월 karpenter의 버전이 올라가면서 새로운 consolidation 방식이 추가됨
3. 기본적으로 스팟 인스턴스의 가격을 높게 입찰해서 점유기간을 늘릴 방법이 있을까
- karpenter가 자동을 처리해 주고, 설정 방법이 없음
최종적으로는 1번을 선택했다.
3번은 불가능했고, 2번은 업데이트해주면서 다른 업데이트가 추가적으로 필요해서 추후 작업을 하기로 결정했다.
현재 상황 파악하기
NAME STATUS ROLES AGE VERSION INSTANCE-TYPE CAPACITY-TYPE
fargate-ip-172-18-18-41.ap-northeast-2.compute.internal Ready <none> 18d v1.30.0-eks-404b9c6
fargate-ip-172-18-34-165.ap-northeast-2.compute.internal Ready <none> 18d v1.30.0-eks-404b9c6
fargate-ip-172-18-35-203.ap-northeast-2.compute.internal Ready <none> 18d v1.30.0-eks-404b9c6
ip-172-18-23-189.ap-northeast-2.compute.internal Ready <none> 94m v1.30.2-eks-1552ad0 m6i.large spot
ip-172-18-33-156.ap-northeast-2.compute.internal Ready <none> 18h v1.30.2-eks-1552ad0 m6i.large spot
m6i.large, r6i.large으로 설정되어 있지만, 실제론 m6i.large만 사용되고 있었다.
아마도 사용량에 비해 r6i.large의 가격이 너무 비싸서 할당되지 않고 있었던 것 같다.
대응 1. T 패밀리로 전환
AWS에서 제공하는 인스턴스 중에서 저렴하고 성능도 괜찮은 건 T패밀리들이다.
적절한 EC2 인스턴스 타입을 선정하는 방법 | DevelopersIO
dev서버에서는 상대적으로 적은 컴퓨팅 용량을 필요할 것이라 예상되어 절반정도의 크기를 먼저 선정해봤다.
가격대가 절반 수준으로 내려오는 걸 확인할 수 있다.
T 패밀리로 전환 후 서버 상태를 확인해는데, 생각하지 못했던 문제가 몇 가지 발생했다.
NAME STATUS ROLES AGE VERSION INSTANCE-TYPE CAPACITY-TYPE
fargate-ip-172-18-18-41.ap-northeast-2.compute.internal Ready <none> 18d v1.30.0-eks-404b9c6
fargate-ip-172-18-34-165.ap-northeast-2.compute.internal Ready <none> 18d v1.30.0-eks-404b9c6
fargate-ip-172-18-35-203.ap-northeast-2.compute.internal Ready <none> 18d v1.30.0-eks-404b9c6
ip-172-18-19-244.ap-northeast-2.compute.internal NotReady <none> 14m v1.30.2-eks-1552ad0 t3a.small spot
ip-172-18-33-100.ap-northeast-2.compute.internal Ready <none> 13m v1.30.2-eks-1552ad0 t3a.small spot
ip-172-18-38-12.ap-northeast-2.compute.internal Ready <none> 11m v1.30.2-eks-1552ad0 t3a.small spot
ip-172-18-39-202.ap-northeast-2.compute.internal Ready <none> 14m v1.30.2-eks-1552ad0 t3a.small spot
ip-172-18-41-115.ap-northeast-2.compute.internal Ready <none> 3m46s v1.30.2-eks-1552ad0 t3a.small spot
ip-172-18-42-112.ap-northeast-2.compute.internal Ready <none> 12m v1.30.2-eks-1552ad0 t3a.small spot
ip-172-18-45-167.ap-northeast-2.compute.internal Ready <none> 3m43s v1.30.2-eks-1552ad0 t3a.small spot
문제점 1. 노드 하나에 담을 수 있는 파드 수가 줄어들어서 노드 수가 늘어나는 역효과가 나타남
문제점 2. 서버의 메모리가 줄어들어서 서버가 뜨는게 너무 오래 걸림
문제점 3. t3a.small, t3a.medium에서 모두 같은 에러가 발생하고 BE 서버가 동작하지 않음
이유는 쓰여진 대로 trunk interface를 t패밀리가 지원하지 않기 때문이었다.
trunk insterface란?
파드용 보안 그룹을 사용하면 공유 컴퓨팅 리소스에서 다양한 네트워크 보안 요구 사항을 가진 애플리케이션을 실행하여 컴퓨팅 효율성을 개선할 수 있습니다. EC2 보안 그룹과 함께 파드와 파드 사이 또는 파드에서 외부 AWS 서비스와 같은 여러 유형의 보안 규칙을 한 곳에서 정의하고 Kubernetes 네이티브 API를 사용하여 워크로드에 적용할 수 있습니다.
VPC CNI에 대해 ENABLE POD ENI = true로 설정하여 파드에 대한 보안 그룹을 활성화할 수 있습니다. 활성화되면 EKS의 컨트롤 플레인에서 실행되는 "VPC 리소스 컨트롤러"가 "aws-k8s-trunk-eni"라는 트렁크 인터페이스를 생성하여 노드에 연결합니다. 트렁크 인터페이스는 인스턴스에 연결된 표준 네트워크 인터페이스 역할을 합니다.
Amazon ECS Linux 컨테이너 인스턴스 네트워크 인터페이스 증가
증가한 Amazon ECS 컨테이너 네트워크 인터페이스에 대해 지원되는 인스턴스
파드용 보안 그룹 (Security Group for Pod, SGP)
EKS 확장기능인 VPC CNI에서 ENABLE POD ENI를 false로 설정하고,
노드에 파드가 각자 갖고 있던 보안 그룹을 할당하는 방식으로 구성한다면 위 문제를 해결할 수 있을 것 같다.
그러나 위와 같이 설정하면 prod와 설정이 달라지고, 모든 노드에서 모든 db접근이 가능하게 되어서 설정하지 않았다.
(지나고 나서 생각해보니 dev여서 굳이 상관이 없었을 것 같다... 비용절감이 확실하다면 더 그렇다.)
최종적으로 trunk interface 지원하는 인스턴스를 사용하는 방식을 선택하게 됐다.
대응 2. Graviton 인스턴스 사용
AWS에서 사용을 권장하고있는 Graviton 아키텍처를 사용하면 비용이 약 20%가까이 절감된다.
Garivton은 AWS에서 밀고있는 아키텍처로, AWS가 진행하는 세션 어딜가도 추천하고 있다.
Garviton의 문제는 ARM 아키텍처의 인스턴스라는 것이다.
고 성능, 비용 절감 효과가 있으나 우리 서버에서 바로 사용할 수 없었다.
첫번째 원인은 BE 서버가 아키텍처가 AMD64로 정의되어 있었다.
jib {
from {
image = "public.ecr.aws/a1r6g0x1/corretto:8"
platforms {
platform {
architecture = "amd64"
os = "linux"
}
}
}
...
}
FE 서버는 Docker로 구성되어 있어서 멀티아키텍처를 지원하게끔 구성하면 됐으나,
당연하게도 그런식으로 설정되어 있지 않았다.
개인적으로 Garaviton으로 변경하는게 가장 큰 효과를 볼 수 있을 거라 생각했지만, 작업 시간이 허락하지 않았다.
대응 3. spot only에서 on-demand + spot으로 변경
가장 빠르고 현실적인 방법이었다.
"capacity_type" = ["on-demand","spot"]
"instance_type" = ["c5.large", "c5a.large", "c6i.large", "m5.large", "m5a.large", "m6i.large"]
이렇게 변경하고나서 서버가 중단되긴 하지만, 사용량이 있을 때 바로 on-demand로 변경된다.
그리고 상대적으로 사용량이 적을 때는 저렴한 비용의 서버가 spot으로 선택되서 구성된다.
NAME STATUS ROLES AGE VERSION INSTANCE-TYPE CAPACITY-TYPE
fargate-ip-172-18-18-244.ap-northeast-2.compute.internal Ready <none> 4d19h v1.30.0-eks-404b9c6
fargate-ip-172-18-18-65.ap-northeast-2.compute.internal Ready <none> 4d19h v1.30.0-eks-404b9c6
fargate-ip-172-18-27-170.ap-northeast-2.compute.internal Ready <none> 4d19h v1.30.0-eks-404b9c6
ip-172-18-19-53.ap-northeast-2.compute.internal Ready <none> 2d14h v1.30.2-eks-1552ad0 c5a.large spot
ip-172-18-30-62.ap-northeast-2.compute.internal Ready <none> 2d15h v1.30.2-eks-1552ad0 c5a.large spot
ip-172-18-34-64.ap-northeast-2.compute.internal Ready <none> 2d15h v1.30.2-eks-1552ad0 m5a.large spot
spot to spot consolidation 방식도 최소 15개의 spot을 사용하길 권장하는걸 보면,
spot only로 중단 없이 서비스 운영에 사용하려면 인스턴스 종류를 많이 할당해줘야 하나보다.
마치며
저번 주에 작업한 내용을 정리해봤다.
회사에 문서화한 내용을 적당히 각색했다.
시간이 조금 더 있었으면, 더 좋은 방식을 선택했을 것 같아서 아쉽다.
그리고 나보다 더 잘 아시는 분이 모니터링하고 적절한 조치를 해줬으면 하기도 했다.
하나씩 하나씩 머리를 박아가면서 하는건 너무 힘들다.
'개발 > AWS' 카테고리의 다른 글
CloudFront 403 에러 원인을 찾아가는 여정 feat. WAF (1) | 2024.11.02 |
---|---|
한 눈에 알아보는 AWS VPC(Virtual Private Cloud) 정리 (0) | 2024.04.16 |
Amazon S3 파일 주기적으로 삭제하기 : SpringBoot에서 미리 서명된 URL(pre-signed URL) 써보기 (0) | 2024.03.28 |
AWS S3 버킷을 퍼블릭하게 사용하기 : SpringBoot에서 퍼블릭하게 업로드하기 (0) | 2024.03.08 |
AWS EC2에 SSL/TLS(HTTPS) 인증서 적용기 (0) | 2024.01.26 |
- Total
- Today
- Yesterday
- Spring
- 후쿠오카
- CloudFront
- java
- GIT
- springboot
- elasticsearch
- AWS
- AOP
- AWS EC2
- OpenFeign
- S3
- docker
- MySQL
- 오블완
- Log
- terraform
- Kotlin
- JWT
- lambda
- Elastic cloud
- serverless
- cache
- 스프링부트
- EKS
- OpenAI
- ChatGPT
- openAI API
- 티스토리챌린지
- 람다
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |