들어가며
3주간의 짧다면 짧고, 길다면 긴 CI/CD 스터디에 참석하게 되었습니다. 이번에는 CI/CD에 관한 스터디로 1주차에는 실습보다는 현재 회사에서 구축 및 사용하는 방법에 대해 살짝 공유하고, 스터디 교안에 있던 질문들에 대한 답변을 작성해 보았습니다.
CD/CD 어떻게 사용중인가요?
현재 회사에서 사용중인 쿠버네티스 CI/CD 방식은 다음과 같이 사용하고 있습니다.

기본적으로 환경은 Gitlab + Gitlab Runner로 구성하여 저장소 및 CI/CD를 사용하고 있습니다. Gitlab의 경우 Gitlab Runner와 함께 사용했을때 편하게 사용이 가능한데요.
저의 경우에는 Gitlab에 있는 K8s 연동 기능은 사용하지 않습니다.
Container 빌드 및 Container Registry는 Gitlab Runner / Gitlab Registry를 사용하지만, 쿠버네티스 배포시에는 yaml 파일을 수정하여 배포하도록 배포 프로그램을 작성하여 사용하고 있습니다.
.gitlab-ci.yml
Gitlab의 프로젝트(repositry)에서 .gitlab-ci.yml에 빌드 및 배포에 대한 선언을 하는데요.
.gitlab-ci.yml의 경우 다음과 유사하게 작성해서 각 stage(step)에 필요한 내용을 입력해서 배포되도록 하고 있습니다.
.docker_job_template: &dind_job
image: docker:latest
services:
- docker:dind
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
stages:
- test
- build
- stage
- release
variables:
DOCKER_DRIVER: overlay2
COMMIT_IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
STAGE_IMAGE_TAG: $CI_REGISTRY_IMAGE:stage
LATEST_IMAGE_TAG: $CI_REGISTRY_IMAGE:latest
config_validate:
image: python:3.7.13-alpine
stage: test
script:
- ln -s /builds/microservice/deploy /deployapp
- python3 /deployapp/config_validator.py
only:
- staging
쿠버네티스 배포
쿠버네티스 배포의 경우 앞서 기재했듯이 Gitlab에 존재하는 기능을 사용하지 않습니다. 쿠버네티스에 리소스를 배포하는 방법은 기본적으로 yaml 파일에 API 명세에 맞게 작성하고, 이를 API로 전송하여 배포하게 됩니다.
기본적으로 API를 배포하는 경우 배포하는 리소스는 Deployment / Horizontal Pod Autoscaling / Service 입니다. 이것을 다음과 같이 1개의 yaml 템플릿으로 작성합니다.
apiVersion: v1
kind: Service
metadata:
name: {{ app_name }}
~~~
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ app_name }}-dpm
spec:
replicas: {{ min_replica }}
~~~
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: {{ app_name }}-hpa
spec:
그리고 이 파일을 Python으로 작성한 배포 스크립트로 필요한 부분을 수정해서 배포하게 됩니다.
# argument
if __name__ == '__main__':
#print("mail function")
parser = argparse.ArgumentParser(description='Deploy microservice app to k8s/Kong.')
parser.add_argument('-e', '--env', action='store', dest='env_type', help='release')
parser.add_argument('-n', '--name', action='store', dest='app_name', help='Application name')
~~~
# render template
def setname(yamldata, app):
appname = app
deployname = app + "-dpm"
podname = app + "-pod"
hpaname = app + "-hpa"
for document in yamldata:
if 'kind' in document:
if document['kind'] == 'Service':
document['metadata']['name'] = appname
document['metadata']['labels']['app'] = appname
document['spec']['selector']['app'] = appname
if document['kind'] == 'HorizontalPodAutoscaler':
~~~~
이 경우 주로 사용하는 리소스에 대해서 기준 템플릿을 만들고, python을 통해서 variable을 전달받아 수정해서 배포하기 때문에, Gitlab Pipeline 페이지에서 빌드 부터 배포까지 깔끔하게 처리되게 됩니다. 물론, 필요한 기능이 있다면 yaml 파일 및 argument를 추가/개선해서 배포하게 됩니다.
스터디에 있는 질문에 대한 개인적인 대답
스터디에서 생각거리에 대해서 제가 현업에서 일하는 부분으로 한번 정리해 보았습니다.
Q. Final 빌드 업데이트 배포의 경우 웹서비스 중단이 발생합니다. 이를 완화하거나 없애는 방안이 있을까요?
A. 다양한 방법이 있겠습니다. 다만, 웹서비스에서 사용하는 언어에 따라서 다르게 전략을 가져가야하지 않을까 싶습니다. 예를 들어, 실행 후 첫 통신까지 느린 언어를 사용하는 경우에는 Blue/Green 배포를 통해서 순차로 전환하는 방벙이 좋을것이고, 빠르게 실행되는 경우에는 Canary 배포 전략을 통해 확인하고서 정식 배포로 순차 진행이 될수 있을것 같습니다.
만약, 쿠버네티스를 사용한다면 deployment에 maxUnavailable과 maxSurge를 통해서 10개의 POD가 실행중이라면 비율 조정을 통해 한번에 업데이트가 되는 POD에 대해 10%로 제한한다면 시간은 걸리겠지만, 순차로 전환되어 무중단에 가까운 배포가 가능하다고 생각합니다.
Q. 또한 현재는 컨테이너를 기동하였지만, 단독 서버의 경우 어떤 방식으로 CD 배포를 할 수 있을까요?
A. 저의 경우에는 Gitlab + Gitlab Runner를 사용중에 있어 SSH를 통해서 배포하는 방식을 사용했었습니다.
Q. CI/CD 전체 과정에서, 환경 변수와 민감 정보(API Key, DB 암호 등)는 어떻게 관리해야 될까요? 또한 편리하게 운영하는 방안은?
A. 각 회사의 보안 정책에 따라 달라지겠지만, 구분해볼수 있는 부분이 있을것 같습니다.
(1) 배포와 관련된 정보는 CI/CD tool의 변수 항목(Gitlab 기준)에 입력하고 호출해서 사용하는 방법
(1) 쿠버네티스 환경에서 운영중이고, 배포를 한다면 쿠버네티스의 Secret 을 사용해서 저장/호출하여 사용 한다면 아래와 같이 될 것입니다.
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: dXNlcm5hbWU= # base64 인코딩된 값
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
~~~
spec:
containers:
- name: nginx
image: nginx:1.14.2
envFrom: # 환경 변수 관련 부분
- secretRef:
name: my-secret # 호출할 Secret 이름
Q. 컨테이너 태그는 어떻게 관리하시나요?
A. 컨테이너 태그의 경우 기본적으로 커밋 SHA값으로 저장하게 해두었습니다. 이는 배포 방식과도 연계되는데요. Gitlab을 사용중이다보니 Pipeline 페이지에서 배포/롤백이 모두 가능해야 합니다. 동시에 git commit sha와도 연계가 되어서 빠르게 인지를 할수 있게 하고 있습니다.
그래서 배포 스크립트에 컨테이너 이미지에 대한 기본 값을 commit sha로 지정하고, 별도의 argument를 추가해서 개발자가 별도로 지정한 태그로도 배포할수 있도록 해두었습니다.
parser.add_argument('-i', '--image', action='store', dest='app_img_path', help='Application image path')
마지막으로..
현재 보시는 이 블로그는 2020년에 어떤 분의 포스트를 보고 비슷하게 구성해서 운영중인 블로그입니다. 그리고 그 구성은 Github + Github Action을 연계하여 구성되어있습니다.
다음은 블로그 글 발행 방식입니다.
- 포스트는 Markdown으로 작성을합니다.
- Commit시 Github Action이 트리거 되고, hugo로 정적 사이트를 렌더링 합니다.
- 이후에 Netlify로 배포해서 이 블로그가 보여지게 됩니다.
구축기는 오래된 포스트를 봐주시면 감사하겠습니다 ^^/
Github Page(Jekyll) 에서 Netlify(Hugo)로 변경한 후기 feat. Github Action.
