배포를 하자..!
지난 게시글에서 스위스 장인의 시계 처럼, 장인 정신으로 한땀 한땀 배포를 해야하는 프로세스를 만들었다고 말씀드렸습니다.
inssa.club 을 사용하시면, 제 손땀 하나하나가 묻어나오는 패킷을 받으실 수 있었던 거죠...
Watchfinder & Co./YouTube
개소리는 그만!
말도 안되는 소리는 여기까지만 하고, 지금 구축된 CI/CD 상황을 정리하자면 다음과 같습니다.
현재 상황
- docker-stack을 활용해서, ec2위의 docker service들은 정의 / 실행 되고 있어요
- 각각의 서비스 (waitlist / clubhouse-profile)의 master에 push가 진행되면 다음의 작업들이 진행됩니다
- 테스트 코드 실행
- 도커 이미지 빌드
- ghcr.io 에 업로드
- deploy 진행
- github action self-hosted runner를 통해서, ec2 인스턴스에 연결
- 롤링 업데이트 진행
- 기존 이미지 삭제
각각의 서비스 별로 CI/CD가 적용 되어있어서, 유연하게 코딩 및 배포가 가능하다는 장점이 있습니다.
그런데 제가 이전 글에서 모노레포를 적용한다고 말씀드렸었죠?
모노레포는 잘가 🙋♂️
다음의 이유로 다시 멀티레포로 돌아왔습니다.
- 관리의 불편함
- 두 서비스(저장소)를 하나처럼 사용하기 위해서 사용한 의도와는 달리 커밋을 더 많이 해줘야함
- 유연하게 코드 작성 및 커밋이 어려움
- commit 도 어떤 단위로 끊어 넣어야할지 애매함
- docker-stack.yml 을 어떻게 다뤄야할지 모르겠음
- docker-stack 을 사용하려면, 어떤 이미지를 사용할것인지 같은 내용들이 파일 내에 정의가 되어야함
- github action은 셸 명령만 실행이 가능함
- 파일을 셸에서 생성해도, 분명 깔끔하지 않을 것
제가 아직 모노레포에 대한 이해가 부족한것 같아요.
그래서 새 서버에는 다음의 것들만 설정해주면 이후부터는 손 델 필요가 없습니다.
- nginx
- github runner
- docker
- docker-swarm
- docker-stack
나중에 traefik이나 code deploy 같은 친구를 적용하면 더 프로세스는 줄겠죠? (물론 codedeploy는 사용하지 않을 예정이에요, 그 이유는 하술하겠습니다.)
그래서, 뭐 얼마나 힘들었길래 엄살이야?
일단 제가 고민한 내용부터 시작하자구요...
CI/CD 를 하자. 잘 해보자..!
CI/CD는 크게 두가지의 프로세스로 돌아요.
- 빌드
- 배포
조금 더 상세히 설명하자면,
- CI 플랫폼 (Travis CI, Circle CI, github action ...) 에서 미리 작성한 프로세스대로 빌드를 진행
- CI 플랫폼에서 실 서버가 빌드된 코드를 다운로드 받고, 실행 시켜 배포가 완료되도록 함
- 이 다운로드를 받을 수 있도록 도움을 주는 역할을 하는 친구들로는, 여러 옵션이 있습니다.
- jenkins
- codedeploy
- github action self-hosted runner
- ...
- 이 다운로드를 받을 수 있도록 도움을 주는 역할을 하는 친구들로는, 여러 옵션이 있습니다.
저는 github action 으로 1과 2의 과정을 모두 해결하기로 했어요.
그 이유는 다음과 같아요
- 가능하면 최대한 적은 플랫폼에 의존성을 만들고 싶었어요
- 다양한 플랫폼, 많은 의존성은 유지보수의 불편함을 의미하니까요
- Jenkins는 싫어요
- 학과 포트폴리오 페이지 를 개발하며 CI/CD를 셋업하기 위해 Jenkins를 사용해본 경험은 있었지만,
- docker도 쓰고, 열심히 셋업하려 했는데..
- 무거워요
- 생각한대로 잘 안돌아요
- 그래서 결국 구축을 포기한 경험이 있어요
- 학과 포트폴리오 페이지 를 개발하며 CI/CD를 셋업하기 위해 Jenkins를 사용해본 경험은 있었지만,
- codedeploy도 싫어요
- 유료 플랫폼에 종속되기가 싫어요
- 나중에 플랫폼을 옮길 일이 생길 수도 있으니까요
- 일단 모든 기능을 무료로 사용 할 수 있는 플랫폼을 활용해야 해요
상술했듯, 위의 이유들을 토대로 github action을 사용하기로 했어요.
Github Action, 너로 정했다!
먼저, 이 글의 도움을 참 많이 받았어요.
도커 registry에는 private 으로 업로드 할 수 없어서, ghcr.io 를 사용하기로 했어요.
무료라도 전혀 문제없이 사용 할 수 있죠.
PAT 라고 불리는 Personal Access Token을 발급했어요. ghcr.io 에 이미지를 업로드 하기 위해서 여러 권한을 주었습니다.
- delete: packages
- delete: repo
- write: packages
그리고 각 프로젝트의 저장소에 repository secret을 등록해주었죠.
workflow 작성
아까 언급한 글을 참고해서, 직접 workflow 를 손수 작성했어요.
name: Release CI
on:
push:
branches: [master]
env:
DOCKER_IMAGE: ghcr.io/code-yeongyu/api.inssa.club_waitlist
VERSION: ${{ github.sha }}
jobs:
test:
name: Test
runs-on: ubuntu-latest
services:
postgres:
image: postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 1234
POSTGRES_DB: database
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Check out the source code
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Test
run: go test -v ./...
build:
needs: [test]
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out the source code
uses: actions/checkout@v2
- name: Setup docker buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Cache docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ env.VERSION }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to ghcr
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{github.actor}}
password: ${{secrets.LINKKKY_PAT}}
- name: Build & Push
id: docker_build
uses: docker/build-push-action@v2
with:
builder: ${{steps.buildx.outputs.name}}
push: ${{github.event_name != 'pull_request'}}
tags: ${{env.DOCKER_IMAGE}}:${{env.VERSION}}
deploy:
needs: [test, build]
name: Deploy
runs-on: [self-hosted, production]
steps:
- name: Login to ghcr using commands
run: echo "${{secrets.LINKKKY_PAT}}" | docker login ghcr.io --username ${{ github.actor }} --password-stdin
- name: Rolling update the service
run: |
docker service update \
--force \
--with-registry-auth \
--update-parallelism 1 \
--image ${{ env.DOCKER_IMAGE }}:${{ env.VERSION }} \
inssa_waitlist
cleanup:
needs: [test, build, deploy]
name: Clean Up
runs-on: [self-hosted, production]
steps:
- name: Prune stopped containers
run: docker container prune -f
- name: Prune unused images
run: docker image prune -af
테스트, 빌드, 배포, 클린업 이렇게 네 단계로 이루어져있습니다.
CI는 Continuous Integration 으로, 개발 → 배포가 통합되는것을 말합니다.
이렇게 workflow를 구축하는것으로 CI/CD를 구축 하게 되었습니다.
그런데 이 workflow 를 작성하기 위해 참 많은 눈물과 샷건이 쏟아졌어요. 책상 미안...
1. ghcr.io 는 이렇게 쓰자
- organization 명의를 사용하면, PAT 사용이 안돼요
- 개인계정 명의를 사용하려면, 같은 이름의 저장소가 있어야 해요
- 저 두가지 사실을 모르고 왜 안돼!! 하고 무식하게 한두시간을 계속 시도했어요
2. 오타를 잘 확인하자
- inssa.club 은 linkkky organization 에서 진행하는 프로젝트 중에 하나에요. 그래서 PAT 이름을 LINKKKY_PAT으로 해뒀습니다.
- 그런데 저는 맨날 K 갯수를 햇갈려했어요
- 지금 머릿속에 드는 그 생각, 그거 맞아요 🤬
- '아니 분명 제대로 했는데, 왜 username 하고 password가 틀렸다고 나오지?'
- 그리고 아래 과정의 반복
- 토큰 재 발급
- 권한 재 설정
- K를 하나 더 붙여서 해결했어요 😂😂🤣🤣
- 그리고 아래 과정의 반복
- 이걸로 또 한시간 가까이 날렸어요
3. docker 에서 볼륨을 마운트 할 때는 제대로 하자
- EC2 인스턴스에 블록 스토리지를 붙여뒀었어요
- 그래서 해당 블록 스토리지를 /data 에 마운트 해두었어요
- 그래서 DB 안의 데이터를 bind mount(도커 바인드 마운트 설명하는 링크 걸기)를 하려고 했어요.
- 이걸 docker-compose.yml 혹은 docker-stack.yml 에서 사용하려면 다음과 같이 사용 할 수 있어요.
volumes: - <host>: <directory path from container>
- 그래서 저는 docker-stack.yml 에 다음과 같이 정의를 해뒀었죠.
volumes: - /data/waitlist: /
- 그리고 다음의 명령을 실행했습니다.
docker stack deploy -c ./docker-stack.yml inssa
- 안돼요
- 안돼요!!
- 안돼요!!!!! 😡😡🤬🤬🤬🤬
- docker service update 를 시도해서 확인해봤더니, 상대경로 말고 절대 경로를 입력하래요......
- 여담이지만 다른 특별한 인자값을 안주고 서비스 이름만 넣으면 재시작하고 똑같아요
- 그러다 이 문서 의 아래 부분을 보고 추측을 했어요.Container의 root는 안되나?
- 네.... 안돼요... 결국 아래와 같이 수정하여 성공했습니다
volumes: - /data/waitlist:/var/lib/postgresql/data
첫 CI 구축을 통해 얻은 교훈
늘 느끼지만 한 서비스를 개발한다는것은 프로그래밍이 전부가 아니라고 생각해요.
프로그래밍 말고도 설정하고 구성하느라 삽질하고 날려보내는 시간이 참 많죠.
한번 실수한 내용을 그냥 넘겨버리고 까먹을 바엔, 정리하고싶어서 이렇게 글을 적게 됐습니다.
앞으로 더 많이 공부해서 더 많이 성장하고, 삽질하는 시간을 줄이는 개발자가 되어야겠습니다..
'Computer Science' 카테고리의 다른 글
내 깃허브가 털렸다 (1) | 2021.04.09 |
---|---|
Go 프로젝트 Docker 이미지 크기 99.2% 줄이기 (부제: 이미지 크기 12921% 떡상 시키기) (0) | 2021.04.08 |
약빨고 22시간 개발 한 썰(부제: inssa.club 개발기) (0) | 2021.03.23 |
Docker Stack의 env-file 설정시 주의 사항 (0) | 2021.03.22 |
사과를 쓰다 펭귄한테 싸다구 맞은 사연 (맥에서 리눅스로 파일 업로드 시 주의할 점) (0) | 2021.03.04 |