1. 구성 배경
팀 내 서비스들이 젠킨스를 통해 배포하도록 구성이 되어있었다. 입사하고 나서 개발 작업을 완료하여 gitlab에 push하고 나서 develop 브랜치에 merge 한 뒤 젠킨스 웹에서 해당 서비스를 develop 브랜치로 배포하여 개발서버에 적용하곤 했었는데, 이러한 젠킨스 배포 라인을 어떻게 구축했는지 궁금했고 하나의 mock 서비스를 직접 구성해보기로 했다.
2. 구성하기 위한 관찰
ssh로 접근가능한 개발 원격서버에 컨테이너가 띄워져 있고 컨테이너에서 각기 다른 서비스들의 이미지가 올라가있었다. 알고 있는 도커 상식으론 Dockerfile로 프로젝트 서비스 이미지를 만든 뒤 컨테이너 이미지를 만들고 이미지 레포지토리에 push -> 배포 하는 것을 자동화해야한다고 생각했다. 환경변수 파일에 있는 젠킨스 파일 코드를 보니 AWS ECR에 이미지를 push 하고 ssh로 원격서버에 붙어 배포를 하는 것을 파악하게 되었다. 우선, 젠킨스가 해야할 역할을 수동으로 해보며 이해해보려고 했다. 그래서 AWS ECR에 접근하기 위해 aws cli설치와 자격증명파일을 설치해야했다.
3. 설치
1. 서비스 컨테이너 이미지를 담아줄 ECR 자격, 권한 얻기
aws cli v2버전을 사용하였고 아래명령어로 docker 사용을 했다.
docker run --rm -it -v ~/.aws:/root/.aws amazon/aws-cli command
매번 docker 명령어를 상용하기 불편하여 alias 명령어로 단축을 시켰다.
alias aws='docker run --rm -it -v ~/.aws:/root/.aws -v $(pwd):/aws amazon/aws-cli'
이제 자격증명 파일 설정이 필요했다. 자격증명에 필요한 키들은 인프라팀에서 aws설정을 선두로 진행하여 전달해주었고 aws configure 명령어로 인증받게 되며 .aws 폴더안에 credentials로 저장되게된다.
$) aws configure
AWS Access Key ID [None]: {Access key ID}
AWS Secret Access Key [None]: {Secret access key}
Default region name [None]: ap-northeast-2
~/.aws/credentials 위치에 자격증명파일이 저장된다.
자격증명파일을 만들었고 이제 ECR 기본 레지스트리에 대한 인증이 완료되어야 ecr에 push, pull이 가능해진다. 쉽게말하면 로그인 같은것이다. 12시간이 지나면 인증이 만료되므로 재인증이 필요하다. 이때 get-login-password 명령어를 사용한다. 정상적으로 로그인하면 Login Succeeded가 출력된다!
# aws ecr get-login-password --region {region} | docker login --username AWS --password-stdin {aws_account_id}.dkr.ecr.{region}.amazonaws.com
이제 컨테이너 이미지를 ECR에 push할 권한이 모두 주어졌다. 이미지를 만들어서 push 해보자.
2. 이미지 컨테이너 빌드하기
도커파일은 환경변수 레포지토리에 있는 dockerFile을 사용하였다. 프로젝트 디렉토리를 형성하고 config값들을 COPY하여 번들링된 프로젝트를 실행하는 명령어들로 구성되어있다. 빌드에는 docker build를 사용하게 된다.
docker build -t 777.dkr.ecr.ap-northeast-2.amazonaws.com/kakaovx-meta-dev
:birdievirtualplay_dev_last .
빌드 후 바로 ECR에 push 해주자. 777은 임의로 수정한 값이다.
docker push 777.dkr.ecr.ap-northeast-2.amazonaws.com/kakaovx-meta-dev
:birdievirtualplay_dev_last
이후 ECR에 정상적으로 push 됐는지 $aws ecr list-images --repository-name kakaovx-meta-dev 를 사용하여 확인해본다.
그 결과, 아래처럼 잘 들어가 있는 것을 확인 할 수 있다.
3. 배포하기
사실 젠킨스로 구성을 처음부터 했다면 젠킨스파일을 통해 도커 빌드부터 ECR푸시까지 배포 까지 할필요가 없었지만, 나는 우선 직접 배포를 해보려고 했기에 아래와 같은 설정을 다시 했다.
이미지를 배포하기 전 docker-compose 파일 내 image 경로를 수정해야한다. ssh로 접근한 개발 서버 내에 /data/docker 내부로 들어가서 compose파일을 만들어 image경로를 지정해주어야한다.
version: '3'
services:
app-blue:
image: 777.dkr.ecr.ap-northeast-2.amazonaws.com/kakaovx-meta-dev:birdievirtualplay_dev_last
build: .
ports:
- 5300:3000
environment:
- DOCKER_PUBLIC_HOST_IP="{{.Node.Hostname}}"
deploy:
mode: global
resources:
limits:
cpus: '0.50'
memory: 1g
reservations:
cpus: '0.1'
memory: 100M
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
environment:
NODE_ENV: development
app-red:
image: 777.dkr.ecr.ap-northeast-2.amazonaws.com/kakaovx-meta-dev:birdievirtualplay_dev_last
build: .
ports:
- 5301:3000
environment:
- DOCKER_PUBLIC_HOST_IP="{{.Node.Hostname}}"
deploy:
mode: global
resources:
limits:
cpus: '0.50'
memory: 1g
reservations:
cpus: '0.1'
memory: 100M
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
environment:
NODE_ENV: development
자세히 보면 red와 blue가 있는데 두개의 인스턴스를 올리게되며 무중단배포를 위해 하나의 배포가 나갈때 하나가 다른역할을 해주게 된다.
compose 파일을 수정한 뒤 이제 배포명령을 내리면 된다
docker stack deploy --with-registry-auth --compose-file=docker-compose-blue-red.yml birdievirtualplay
배포가 끝나게 되면 docker run으로 실질적인 가동을 하게 되는데 이때 에러를 직면하게 됐다.
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
호스트플랫폼과 이미지를 빌드한 플랫폼간이 호환성이 맞지 않다는 내용인데, 이때는 빌드 할 때 옵션을 호스트 플랫폼과 맞추기 위해 특정옵션을 추가해야한다!
: docker build --platform linux/amd64 -t [ImageName] .
4. 젠킨스로 최종 자동화하기!
자, 이제 이 모든 과정을 젠킨스를 통해 자동화해야한다. 수동으로 어떻게 배포되는지 직접해보니 젠킨스의 명령어가 하나하나 눈에 들어온다. 환경변수 레포지토리에 dockerfile과 .env 그리고 젠킨스 파일이 존재한다. 이 젠킨스 파일이 이 모든 과정을 코드로 담고 있고 젠킨스 웹에서 이 파일내 코드를 순서대로 실행시키게 될 것이다. 우선 젠킨스파일을 보자.
pipeline {
agent any
options { skipDefaultCheckout(true) }
environment {
GIT_REPO_URL = '서비스 깃 레포 주소'
// GIT_BRANCH_NAME = 'develop'
GIT_CONFIG_REPO_URL = '서비스 환경 변수 깃 주소'
GIT_CONFIG_BRANCH_NAME = 'dev/birdie_virtualplay'
DOCKER_ECR_REGION = 'ap-northeast-2'
DOCKER_REPO_URL = '777.dkr.ecr.ap-northeast-2.amazonaws.com/kakaovx-meta-dev'
DOCKER_SERVICE_NAME = 'birdievirtualplay'
REMOTE_SERVER_HOST = "ssh로 접근가능한 개발 서버 주소"
REMOTE_SERVER_USER_NAME = "metadev"
REMOTE_SERVER_PORT = "개발 서버 포트"
}
parameters {
gitParameter branchFilter: 'origin/(develop.*)', defaultValue: 'develop', name: 'GIT_BRANCH_NAME', type: 'PT_BRANCH', useRepository: '서비스 레포지토리 주소', sortMode: 'DESCENDING_SMART', selectedValue: 'TOP'
string(name : 'DOCKER_BUILD_TAG', defaultValue : "birdievirtualplay_dev_last", description : '')
}
stages {
stage('Git Checkout and build') {
agent any
steps {
dir('./birdie-virtualplay'){
git url: GIT_REPO_URL,
branch: GIT_BRANCH_NAME,
credentialsId: 'gitlab-metadev'
}
dir('./birdie-virtualplay-config'){
git url: GIT_CONFIG_REPO_URL,
branch: GIT_CONFIG_BRANCH_NAME,
credentialsId: 'gitlab-metadev'
sh '''
/bin/cp Dockerfile-virtualplay ../Dockerfile
'''
}
}
}
stage('Build Backend') {
agent any
steps {
sh '''
docker build -f Dockerfile -t ${DOCKER_REPO_URL}:${DOCKER_BUILD_TAG} .
'''
}
}
stage('Push Image To AWS ECR') {
agent any
steps {
script {
docker.withRegistry("https://${DOCKER_REPO_URL}", "ecr:${DOCKER_ECR_REGION}:aws-ecr"){
// sh '''
// aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${DOCKER_REPO_URL}
// docker push ${DOCKER_REPO_URL}:${DOCKER_BUILD_TAG}
// '''
docker.image("${DOCKER_REPO_URL}:${DOCKER_BUILD_TAG}").push()
}
}
}
}
stage('Deploy Stack') {
steps{
sshagent(credentials : ["metadev_ssh"]) {
sh """
ssh -tt -o StrictHostKeyChecking=no ${REMOTE_SERVER_USER_NAME}@${REMOTE_SERVER_HOST} -p ${REMOTE_SERVER_PORT} "uptime && hostname \
&& docker run --rm -it -v ~/.aws:/root/.aws -v /home/htdev:/aws amazon/aws-cli ecr get-login-password --region ${DOCKER_ECR_REGION} | docker login --username AWS --password-stdin ${DOCKER_REPO_URL} \
&& docker service update --update-delay 10s --force --update-failure-action rollback --update-monitor 60s --force --with-registry-auth --image ${DOCKER_REPO_URL}:${DOCKER_BUILD_TAG} ${DOCKER_SERVICE_NAME}_app "
"""
// sh """
// ssh -tt -o StrictHostKeyChecking=no ${REMOTE_SERVER_USER_NAME}@${REMOTE_SERVER_HOST} -p ${REMOTE_SERVER_PORT} "uptime && hostname \
// && docker run --rm -it -v ~/.aws:/root/.aws -v /home/htdev:/aws amazon/aws-cli ecr get-login-password --region ${DOCKER_ECR_REGION} | docker login --username AWS --password-stdin ${DOCKER_REPO_URL} \
// && docker run -it -p 8081:80 777.dkr.ecr.ap-northeast-2.amazonaws.com/kakaovx-meta-dev:birdievirtualplay_dev_last"
// """
}
}
}
}
}
위에서 부터 보면 어떤 서비스를, 어떤 브랜치를, 어떤 서버주소에 배포할지에 대한 정보가 나열되어있다. 이정보를 마치 const처럼 상수로 선언하게 되며 stages의 stage단계별로 실행을 거치게 된다.
자세히 보면 stage1 step1은 환경변수와 서비스에 대한 정보로 docker FIle 복사하게 된다.
stage2 step2에서는 dockerFile로 docker build를 하게 되며 그다음 스텝이서는 ECR에 push를 해주게 된다.
최종적으로 ssh로 원격개발서버에 접속하여 docker 레지스트리로 인증한 뒤 새 이미지로 docker 서비스를 업데이트하게되며 잘못됐을 시 롤백을 하도록 하는 코드이다.
최종적으로 젠킨스 웹에서 나는 자동화를 실행 할 수 있도록 UI상 에서 클릭을 할 수 있도록 세팅해주어야한다.
이 젠킨스 파일을 환경변수 레포지토리에 같이 세팅을 한 뒤 젠킨스 웹에서 추가적인 세팅을 해주면 된다.
젠킨스 웹에서 + 버튼을 눌러 자동화할 새 프로젝트 명을 입력해주자.
이후 서비스 레포지토리와 배포할 브랜치, 환경변수 레포지토리 주소 등을 default값으로 세팅해주어야한다. 만들어진 새 자동화 프로젝트 내부러 들어가면 오른족에 톱니바퀴모양이 있다. 파이프라인 설정을 해주게 되며 서비스 레포지토리에 관한 정보를 넣어주면 된다.
|
5. Review
처음 젠킨스를 접했을 때 버튼하나로 배포되니까 편하긴하네 뭐.. 하고 말았다. 하지만 직접 빌드부터 배포까지 해보게 되니 매번 짜잘한 수정이 있을때마다 엄청 불편해질수 있겠다는 생각이 들면서 CI/CD 자동화가 매우중요하다는 것을 깨달았다. 젠킨스. 그 뒤에서 어떤 일들을 해주는지 알게되니 참 고마운 친구였다.
'개발 일지 > 카카오VX' 카테고리의 다른 글
서비스 중심 kafka와 redis pub/sub (0) | 2024.08.26 |
---|---|
전 서비스 TypeOrm 버전 업 (0) | 2024.08.16 |
온체인마켓 LOG 적재 Apm+Kibana (0) | 2024.08.16 |
실시간 유저 재화를 위한 kafka 도입 (0) | 2024.08.16 |
댓글