넥스터즈에서 배포를 준비하는 글에서 Kubernetes를 사용하여 배포하는 것을 최종 목표로 잡았었다.
네이버 클라우드 플랫폼(NCP) 에서 제공하는 Kubernetes 서비스와 Container Registry 를 사용하여 배포한 과정을 기록한다.
아직 초보라 많은 설정을 써보진 못해서 차차 개선해나가기로 한다.
목차
0. 배포해볼 구조 및 용어 정리
1. 설치
2. docker image 레지스트리에 저장
3. helm chart 생성 및 배포
4. 이미지 재배포
5. 정리
0. 배포해볼 구조
먼저 서비스를 어떻게 배포하는가를 짚고 넘어간다.
정말 단순하게 End User가 Load Balancer* 외부 IP를 통해 접속하면 부하 분산에 의해 적절한 Pod로 접근하고, nginx가 프록시하는 구조이다.
* 지금은 Pod 하나지만 auto scaling 설정 이후 Pod가 늘어날 때를 대비했다.
PM2 추가 이유
컨테이너 환경에서는 default로 pm2 역할을 컨테이너가 하지만 다음 세가지가 아쉽다.
1. Scale-up/down을 node에 비해 느리다. node 아키텍쳐는 빠른 I/O와 재시작이 장점인데 활용할 수 없다.
2. pm2로 인스턴스 재기동 조건을 제어할 수 있다.
3. 로그 수집을 추가하지 않는 이상 tracing이 어렵다.
이 구조에 맞춰 사전에 미리 docker 환경을 구성하였고 해당 이미지를 빌드하여 사용할 계획이다.
다만 이전 글에서 하나 수정해야 할 것은 nginx 설정 중 variable.conf이다.
docker-compose로 컨테이너를 생성할 때에는 같은 docker network를 공유하고 IP명은 곧 컨테이너명이었기 때문에 컨테이너명을 넣었지만 Pod 내의 컨테이너들은 같은 IP를 공유하기 때문에 localhost로 명시해야 한다.
용어 정리
쿠버네티스 용어도 간단하게 요약정리한다.
Cluster
쿠버네티스의 여러 리소스를 관리하기 위한 집합체
Node
Cluster 중 가장 큰 단위로 Docker 컨테이너들이 위치한 곳
노드 중 Master 노드는 클러스터 전체를 관리하는 노드이다.
Pod
Docker 컨테이너들의 집합, 스토리지 및 네트워크를 공유 (참고)
Service
Pod는 이미지를 재빌드 후 생성하거나 등등 다양한 이유로 제거될 수 있다. 따라서 IP또한 고정이 아닌데 Service가 Pods들을 앞에서 매핑해 줄 수 있다.
쿠버네티스는 파드에게 고유한 IP 주소와 파드 집합에 대한 단일 DNS 명을 부여하고, 그것들 간에 로드-밸런스를 수행할 수 있다.
type으로는 CluserIP, NodePort, LoadBalancer, ExternalName이 있다.
[참고]
쿠버네티스 서비스(kubernetes services) (1)
Deployment
k8s에서 보통 Pod를 직접 생성하기보다는 Deployment를 정의하여 Pod 명세와 ReplicaSet에 대한 선언적 업데이트를 한다.
ConfigMap
컨테이너에서 쓰이는 데이터를 저장할 때 사용한다. (기밀 데이터는 Secret으로 생성한다.)
볼륨 마운트 시 사용할 데이터, Pod 구성 시 사용할 환경 변수 등을 컨피그맵으로 생성한다.
1. 설치
먼저 NCP에 kubernetes 서비스를 생성하고, container registry 서비스를 이용신청해야 한다.
생성 시 참고할 글들을 소개한다. 또한 docker와 docker-compose가 설치되어 있어야 한다.
[참고]
[NCP] NAVER CLOUD에서 KUBERNETES를 사용해보자
[NCP] NAVER CLOUD KUBERNETES – CONTAINER REGISTRY로 컨테이너 이미지를 관리하자
‼️ 앞으로 설명할 글에서 --kubeconfig=$KUBE_CONFIG 는 kubectl과 helm 명령어의 alias로 등록했다고 가정하고 진행한다.
2. docker image 레지스트리에 저장
이제 NCP의 Container Registry에 이미지를 푸쉬한다.
docker-compose로 이미지를 생성한다. 필자는 따로 컨테이너로 먼저 띄워서 정상적으로 실행되는지 확인하기 위해 빌드와 생성을 동시에 할 수 있는 up 커맨드를 사용했다.
docker-compose up -d
remote registry에 접속하기 위해 docker login 한다.
docker login <REGISTRY_ADDRESS>
이미지를 푸쉬하기 전 <REGISTRY_ADDRESS>/<IMAGE_NAME> 으로 이미지를 태깅한다.
docker image tag <IMAGE_NAME> <REGISTRY_ADDRESS>/<IMAGE_NAME>
이제 이미지를 레지스트리에 푸쉬한다.
docker push <REGISTRY_ADDRESS>/<IMAGE_NAME> # tag를 안붙이면 기본 latest
3. helm chart 생성 및 배포
이제 배포할 이미지까지 준비가 되었으니 k8s 배포를 준비한다.
helm이란
package managing tool로, k8s 배포 시 필요한 설정들을 한방에 설정 및 배포할 수 있게 해주는 유용한 툴이다.
다만 처음 써보면 yaml 파일들 속에서 길을 잃을 수 있는데 로컬환경에서 minikube로 일일히 배포연습을 해보면서 helm chart를 구성했었다. helm chart는 Go Template을 사용하여 설정한다.
다음 명령어로 기본 helm chart를 생성한다.
helm create <CHART_NAME>
필자는 지금 수정한 상태이지만 생성하고 나면 기본 구성은 templates와 Chart.yaml, values.yaml 등이 포함된 폴더가 생성된다.
*dev.values.yaml 파일은 필자가 추가한 것이다.
이 중 다음 두가지 파일이 중요한데 각 파일을 요약하자면 이렇다.
- /templates : k8s 배포 시 사용되는 yaml 파일들.
/templates 내부에 {{ .Values.**.* }} 라는 값들에 values.yaml 파일 내용이 들어간다. - values.yaml : /templates 에 삽입될 데이터
dev.values.yaml
dev.values.yaml은 dev환경과 production 환경을 구분하기 위해 values.yaml 파일을 각 환경별로 생성한 파일이다.
templates
templates 폴더는 배포에 필요한 yaml 파일이 저장되어 있다. 각 yaml 파일은 template화되어 지정한 변수에 따라서 release를 생성할 수 있도록 재사용성을 제공한다.
templates 폴더에도 주로 수정한 파일은 deployment와 service이다.
templates/service.yaml
필자는 service.yaml 에서 Service 종류를 LoadBalancer로 잡았다. 이렇게 잡으면 NCP 클러스터에서 LoadBalancer 인스턴스를 자동으로 생성해주고 접근가능한 IP 주소를 할당한다.
templates/deployment.yaml
필자는 위에서 nginx와 app 이미지를 빌드했다. 따라서 두 이미지의 컨테이너를 Pod에 생성하기 위해 아래처럼 yaml을 수정했다.
imagePullSecret
또한 NCP의 Container Registry는 Private Registry이므로 이미지를 pull하기 위해서 imagePullSecret 을 추가해주어야 한다.
kubectl create secret docker-registry ${SECRET_NAME} --docker-server=${REGISTRY_ADDRESS} --docker-username=${API_KEY} --docker-password=${API_SECRET} --docker-email=${NCP_LOGIN_EMAIL}
* API_KEY와 API_SECRET은 NCP > 마이페이지 > 계정 관리 > 인증키 관리 에서 확인한 값
install
kubectl get pods 를 해보면 아직 배포한 Pod가 없기 때문에 아무것도 뜨지 않는데 처음 생성 시 helm install 명령어로 k8s에 Pod를 생성한다.
helm install ${NAME} ./${CHART_DIRECTORY}
생성 전, k8s menifest 데이터를 미리 확인하고 싶다면 --debug --dry-run 옵션을 추가하여 실행한다.
정상적으로 생성되었다면 다시 Pod 리스트를 조회했을 때 다음처럼 보일 것이다.
서비스 인스턴스도 조회해보면 다음처럼 출력된다.
EXTERNAL-IP를 보면 자동으로 어떤 도메인이 할당되었음을 확인할 수 있는데 그 도메인은 NCP에서 자동으로 생성된 LB인스턴스이다.
EXTERNAL-IP로 접속해보면 정상적으로 뜸을 확인할 수 있다.
Global DNS 설정
그런데 EXTERNAL-IP로는 서비스를 출시하기가 곤란하다. 매우매우 랜덤값이기 때문에 보통 이를 그냥 쓰지않고 미리 구입한 도메인을 연결한다. NCP의 Global DNS를 사용해도 좋고, 구매한 도메인 사이트에서 관리할 수 있다면 호스팅 서비스에서 직접 관리할 수도 있다.
4. 이미지 재배포
한번 생성된 서비스를 재배포하려면 어떻게 해야 할까?
만약 helm uninstall ${NAME} 으로 이전에 배포된 서비스를 내리고 다시 install하면 Pod 뿐만 아니라 서비스 인스턴스또한 재생성되어 EXTERNAL-IP가 달라져버려 이전에 연결해둔 DNS 레코드 또한 다시 수정해야 한다.
새로 빌드한 이미지를 재배포하기 위해서는 upgrade 명령어를 사용한다.
helm upgrade ${NAME} ./${CHART_DIRECTORY} -f ${CHART_DIRECTORY}/dev.values.yaml --recreate-pods
* -f ${CHART_DIRECTORY}/dev.values.yaml : 아까 위에서 dev, production 환경을 나눠서 배포한다고 한 부분으로, 사용할 values.yaml 파일을 직접 지정하는 옵션
* --recreate-pods 옵션 : 이 옵션을 추가해야 pod를 재생성할 수 있음
재배포 시 Pod를 조회해보면 하나는 Terminating되고 있고, 다른 하나가 생성되고 있음을 볼 수 있는데, 이로 미루어 보아 upgrade는 롤링 업데이트로 업데이트하지 않나 생각이 들었다. (참고)
이렇게 생성부터 재배포까지 알아보았고 도메인으로 접속해보면 정상적으로 배포되었음을 확인할 수 있다.
아래는 필자가 배포한 서비스 화면 예시이다.
5. 정리
이렇게 기본 배포를 끝냈다. 아직 해보지 못한 기능들이 많은데 다음 공부로는 hpa 설정을 해보려 한다.
hpa 설정
CPU 사용률 등 부하를 체크하여 Pod의 Replica수를 자동으로 Scaling하는 기능으로 HorizontalPodAutoscaler(HPA, 수평스케일)이라 부름
8/26 추가
오늘 회의시간에 hpa에서 minReplica와 maxReplica개수를 정하는 기준이 궁금해서 TL님께 질문드렸었다.
Replica 개수를 잡는 기준은 목표 RPS(Request Per Second, 동시 초당 접속자)와 RPS가 늘어남에 따른 CPU 사용률 추이로 결정지을 수 있다.
만약에 서비스에서 목표로 하는 A 서비스의 목표 RPS를 100이라고 가정한다.
그리고 서버 한 대에서 RPS를 증가시킴에 따라 CPU 사용률을 그래프로 표시해보면 한계치가 산출된다.
한계치에 도달했을 때의 rps가 5라면 한 서버당 5rps를 처리할 수 있다는 뜻이므로 최소한으로 필요한 Pod Replica는 100/2 = 20 개가 필요하다. 즉 minReplica는 20이다.
일반적으로 최소 필요치에서 a만큼 더해서 minReplica를 정하므로 20+a = 20~25이다.
maxReplica는 가용량에 따라 minReplica에서 x2, x3, x5배 해서 결정한다.
참고한 글
How to make a Helm chart in 10 minutes
'Projects > 넥스터즈 19기' 카테고리의 다른 글
[회고] 넥스터즈 19기를 마무리하며 (0) | 2021.08.30 |
---|---|
8/26 Nexters 9주차 : HTTPS 적용 (0) | 2021.08.26 |
8/20 Nexters 8주차 : Docker 기반 배포 (0) | 2021.08.20 |
7/31 넥스터즈 5주차 : 단위테스트, 디자인 시스템 (0) | 2021.08.01 |
7/16 넥스터즈 3주차 (2) : 에러 처리 설계와 SWR (4) | 2021.07.16 |