How do we deploy servers?

Sangmin Lee
Bagelcode
Published in
6 min readAug 24, 2020

Intro

안녕하세요. 하루에 수십 번 크고 작은 배포들이 이뤄지고 있는 베이글코드의 서버 디플로이 시스템을 소개해보려고 합니다.

Background

베이글코드의 Club Vegas, Jackpotjoy Slots, Star Spins Slots 서버들은 MSA (Microservice Architecture) 로 설계되었으며, Docker 로 containerized 되어 있습니다. 그리고 많은 Service & Container 들을 AWS 위 Kubernetes 클러스터에서 운영하고 있습니다.

Overview

Jenkins

권한을 부여받은 개발자라면 Jenkins 를 통해 누구나 쉽게 서버를 deploy, restart, rollback 을 할 수 있습니다. 아래 그림과 같이 parameter 로 commit hash 나 context 만 넣고 job 을 build 하면 됩니다.

jobs/parameters

생성된 job 은 Build Executor (EC2) 의 workspace 에서 진행됩니다.

Pipeline script 들이 과거에는 Jenkins 에 존재해서 히스토리 추적이 어려웠고 중복이 많았습니다. 지금은 Groovy 파일로 작성해서 Shared Library repo 에서 관리하고 있습니다.

Details

Club Vegas 서버 디플로이는 크게 다음과 같이 이루어져 있습니다.

pipeline

Checkout

GitLab 에서 필요한 repository 들을 checkout 한 뒤, job 에 revision/target badge (manager.addShortText) 를 달아줍니다.

build history

Release

release 스크립트를 실행해서 Docker 이미지를 만들고 Docker Hub 에 push 합니다.

docker hub

Upload artifacts

다른 팀과 협업하는 데 필요한 특정 파일들을 AWS S3 에 upload 합니다.

AWS S3 bucket

Apply config

Kubernetes 를 처음 적용하던 시절에는 환경 (QA, ST, PROD …) 별로 ConfigMap, Service 파일 등이 따로 존재했습니다. 작년에 Helm 을 도입하면서 하나의 template 에 여러 values 를 쓸 수 있도록 관리하고 있습니다.

helm template 로 얻은 manifest (ConfigMap) 를 kubectl apply 합니다.

helm template ${releaseName} ${chart} --show-only ${template} --values ${value} --set-string image.tag=${tag} | kubectl apply --context=${context} --kubeconfig=${kubeconfig} -f -

Deploy services

대부분은 점검 없이 Rolling Update 를 통해 서비스 품질에 영향을 주지 않고 빨리 pod 를 교체해서 새로운 서버를 배포할 수 있습니다.

kind: Deployment
...
spec:
progressDeadlineSeconds: 300
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 25%
type: RollingUpdate
...
rolling update strategy

이전 stage 와 같은 방법으로 얻은 manifest (Deployment) 를 kubectl apply 한 뒤, kubectl rollout 를 진행합니다.

kubectl rollout status deployment ${deploymentName} --context=${context} --kubeconfig=${kubeconfig}

Rebalance

rolling update 이후 nodes 에 고르게 분산되지 않은 pods 를 rebalance 를 해줍니다.

따로 custom 스케줄러를 쓰고 있지는 않고, podsPerNode 를 직접 계산해서 pod 를 새로 띄우고 있습니다.

kubectl get pods -o custom-columns=node:{.spec.nodeName},name:{.metadata.name} --no-headers=true -l app=${app} --context=${context} --kubeconfig=${kubeconfig}`...kubectl delete pods ${podToKill} --context=${context} --kubeconfig=${kubeconfig}

Tagging

환경별로 어떤 commit 까지 배포되었는지 쉽게 알 수 있도록, env.JOB_NAMEenv.BUILD_NUMBER 를 이용해서 tag 를 만들고 git origin 에 push 합니다.

git tag

Further Consideration

  • Jenkins (master) 와 Agent (node) 에 강한 dependency 가 있기 때문에 Jenkins 장애 발생 시 배포를 할 수 없는 상황이 있었습니다.
  • MySQL Schema 변경이 필요할 때도 Zero Downtime 이 가능하도록 연구하고 있습니다.

함께해요 🙂

--

--