* 이 포스트는 학습 과정에서 그 내용을 기록한 글이기에 부정확한 정보가 포함될 수 있습니다.
따라서 해당 글은 참고용으로만 봐주시고 틀린 부분이 있다면 알려주시면 감사하겠습니다.
❗ 대상 독자 : CI/CD 파이프라인 구축에 입문하는 주니어 개발자
❗ 이 글은 기존 프로젝트에 github actions를 이용한 CI/CD를 구축하는 내용을 메인으로 합니다. 따라서 독자분이 기존 프로젝트에 사용했던 Git, Spring Boot, Docker, AWS에 대해 잘 모르신다면 검색을 해보시거나 아래 링크를 통해 ai에게 질문해 보시고 포스트를 보시는 것을 추천드립니다. 이 포스트에서 가장 중요하게 다뤄지는 github actions를 아예 모르신다면 refer에 추가해둔 생활코딩님의 영상을 보시는 것도 좋습니다,
* 잘 모르시는 기술은 로그인 필요 없이 이 곳에서 AI에게 물어보세요!
이전 포스트는 제가 적용한 전체적인 파이프라인을 개괄적으로 다뤘습니다. 그리고 이번 포스트는 좀 더 프로세스를 단계별로 다뤄보겠습니다. 대신 글이 너무 길어지는 관계로 진행하며 툴에 대한 기본적인 설명이 필요하다면 각주로 대신할 테니 링크를 보시거나 위에서 말한 ai에게 질문을 해주시면 되겠습니다.
저번 포스트에서 다가올 미래를 생각하지 않았다는 말을 했었는데 아래 그림만 보셔도 대충 감이 오실 것이라 생각합니다.

이 포스트는 practice로 따라하시기엔 코드도 없고 자세한 설명도 없습니다. 제가 학습하면서 느낀 건데 어떤 예제든지 그대로 따라 하려고 하면 아무 의미가 없는 데다 자기 프로젝트에 내용을 적용할 때 포스트 내용대로 해봤자 작동하지 않기에 시간을 써서 스스로 부딪혀보는 게 제일 중요하더라고요. 실제로 이번 내용도 포스트 하기 전에 위 그림처럼 actions만 몇 번을 반복했고 또 AWS 리눅스 cli로 계속 설정을 수정했었습니다. 그래서 이 포스트는 설정들을 하며 겪은 시행착오 대신 '본인 프로젝트에 CI/CD를 적용할 때 이런 방법도 있을 수 있구나' 에 포커스를 맞춰서 봐주시면 감사하겠습니다.
우선 이전 글에서 보셨던 그림을 다시 한번 보겠습니다. 그전에 말씀드릴게 이 프로세스는 제 프로젝트에 맞게 축약한 단계들도 있고 그 나름의 이유들도 후술하니 이걸 'CI/CD 파이프라인이라 부를 수 있어?'라는 생각은 속으로만 해주세요 ㅠㅠ

이제 이 프로세스의 각 단계를 짚어볼건데 '저는 이런 흐름으로 적용했습니다.'와 '적용 가능한 다른 방식과 툴들' 두가지를 중점적으로 서술하겠습니다.
#1 IDE를 통해 코드 작성
- 저는 주로 자바를 사용하기 때문에 ItelliJ를 사용했지만 VSCode를 사용해도 달라질 건 없습니다. ItelliJ가 워낙 git과 잘 연동되지만 VSCode도 익스텐션을 사용하면 그에 못지않게 편하더라고요. 제 수준에선 IDE는 크게 중요하진 않은 것 같습니다. 만약 도구를 바꿔 github에서 개발한 Atom을 써서 IDE까지 git과 아예 통합해 보는 것도 나쁘진 않을 것 같습니다.
#2 git에 코드 푸쉬
- 다른 SVN이나 Mercuria 등이 있긴 하지만 CI/CD 툴로 github actions를 사용하는 만큼 VCS로는 Git을 대체할 건 딱히 없습니다. 그나마 수정할 점이라면 현재 워크플로우에 모든 푸쉬시 actions에 요청을 보내도록 설정해서 만약 실무에서 여러 작업자들이 관리한다면 현재는 머지를 하든 리베이스를 하든 모두 요청이 가도록 되어있는데 트리거 될 브랜치도 분리하고 작업도 구분해줘야 할 겁니다.
#3 github actions에 빌드 및 배포 요청
# 3번 과정의 전체 흐름
- 기존의 빌드와 배포 프로세스를 한 번에 합쳐버린 만큼 해당 단계가 신경 쓸 것도, 알아야 할 것도, 보완해야 할 것도 가장 많습니다. 우선 해당 단계에서 아래와 같이 크게 세 단계로 작업이 진행되며
1. github에 push 된 커밋을 기반으로 github actions queue에 등록 > 2. 작성해 둔 main.yml대로 지정된 workflow In progress > 3. EC2에 깔아 둔 runners가 빌드된 도커 파일을 받아 docker-compose
여기서 딱 확실하게 구분되진 않지만 2단계는 빌드 3단계는 배포로 볼 수 있습니다. 이 과정에서 실행되는 작업을 조금 자세히 풀어 써보자면
@ main.yml에 미리 빌드와 배포에 사용될 env(환경 변수)를 작성해 둡니다. 그리고 env로 사용될 github secret을 미리 발급받습니다. main.yml 전문은 https://github.com/kth1017/project_GptApiPlusCi/blob/main/.github/workflows/main.yml
@ 트리거 이후 수행될 job은 아래와 같습니다.
빌드(2)
1. github repository에서 checkout : repo에 커밋된 파일들을 actions에 사용하도록 checkout 합니다.
2. docker build 수행 : 최종적으로 EC2에 도커로 배포할 예정이므로 이 단계에선 도커로 빌드합니다.
3. (빌드 캐싱 작업) : 물론 해두면 속도 향상에 크게 도움이 되지만 입문하실 땐 여러 번 파일을 수정하실 텐데 캐시 때문에 작업이 더뎌질 수 있습니다.
4. ghcr(GitHub Container Registry)에 로그인 후 빌드 & 푸시 : docker hub 대신 git에서 제공하는 ghcr에 이미지를 푸시
배포(3)
1. ghcr에 로그인 : 이미지를 pull 하기 위해 env에 언급해둔 secret으로 ghcr에 로그인
2. 도커 실행 : EC2에 깔아 둔 docker를 통해 docker-compose로 서비스 네트워크 배포
- 위의 작업에서 조금 부가 설명이 필요한 부분을 말해보자면 우선 checkout, cache, build, login 등 job의 각 단계들은 uses 구문으로 사용되는데 이는 github에서 미리 만들어서 등록된 액션들을 사용합니다.
넘어가서 캐싱 작업을 선택으로 표시한 이유는 물론 작동 속도를 높이기 위해 캐싱을 해두는 것은 거의 필수긴 하지만 처음 이 부분을 배우고 적용하시는 분들은 코드를 여러 번 수정하고 적용해 보는 작업을 정말 여러 번 할 텐데 그 과정에서 캐시가 좀 학습자를 헤매게 만들 수도 있습니다. 우리가 IDE를 쓸 때 캐시 때문에 제대로 된 코드가 작동 안 할 때가 있잖아요? 마찬가지입니다. 근데 거기에다가 actions를 처음 접하신다면 더 크게 다가오겠죠. 그래서 이 캐싱 부분은 선택적으로 포함할지 고려하시면 되겠네요.
그다음 여기선 ghcr로 이미지를 푸시합니다. 우리가 보통 docker를 사용하면 이미지를 docker hub로 푸시하는데요. 이 단계에선 단지 저장소를 ghcr(GitHub Container Registry)로 대체한 겁니다. docker hub에 이미지를 푸시하도록 작성하셔도 결과는 동일합니다. 사용 정책이나 액세스 등 다른 이유들도 물론 있지만 ghcr을 사용하는 게 통합에 유리하다는 게 이렇게 작성한 가장 큰 이유입니다.
마지막으로 배포 작업은 미리 EC2에 gihub actions에서 제공하는 runners*를 이용해 진행합니다. 앞서 빌드 작업에도 runners가 사용되는데 앞서 사용되는 건 Github-Hosted Runners고 배포에 사용되는 건 Self-Hosted Runners입니다. Self-Hosted Runners의 경우 자체적으로 프로젝트 환경에 가상 머신을 설정하므로 우리는 Self-Hosted Runners를 github에서 생성해 두고 이를 EC2에서 실행해 배포를 진행합니다.
* runners가 뭔지 모르신다면 https://velog.io/@zuckerfrei/Github-Actions-1.-self-hosted-runner를 참조
# 적용 가능할 수 있는 다른 사항
- 이전 포스트에서 설명했듯 빌드와 배포를 합친 이런 프로세스의 경우 CodeDeploy나 CodePipeline을 사용해도 되고 GitLab을 사용해도 됩니다. 또한 배우기 쉬운 CI툴인 jenkins나 travisCI에 배포 툴을 사용해서 책임을 분리해도 좋습니다. 하지만 저는 통합과 접근성 면에서 이런 프로세스를 적용했습니다. 딱 봐도 엄청 간단하지 않나요? main.yml을 보시면 알겠지만 workflow도 정말 간략하게 줄여서 적용했기에 전체적인 흐름 역시 쉽게 보실 수 있으리라 생각합니다.
- 제가 적용한 프로세스는 사실 기존 CI/CD 파이프라인을 압축해서 변형한 것이라 생각하시면 됩니다. 한마디로 일부 과정을 지키고 있지만 개선하고 확장할 점이 많다는 얘기죠. 크게 다음과 같은 점들이 있습니다.
1. 테스트 통합 : 사실 워크플로우 과정 중 빌드 이전에 코드 테스트를 진행하는 테스트 자동화 과정이 포함되어야만 합니다. 저 같은 경우엔 스프링 부트로 서버를 작성했으니 junit을 이용해 테스트 통합 과정을 진행했어야 맞죠. 하지만 제 경우 현재 테스트 코드를 수정하며 서버의 클래스 구조를 계속 고치고 있기에 테스트 코드에 변경이 많아서 캐시를 해도 이 과정에서 소모되는 시간이 큽니다. 따라서 제 경우 테스트는 jar 빌드 이전에 따로 수행하도록 해둬서 소위 말하는 테스트 통합 과정을 뺐습니다.
2. 오토스케일링/무중단 배포 및 CDN 활용 : 보통 실무에서 큰 프로젝트에 CI/CD를 적용하면 반드시 로드밸런싱을 적용해서 무중단 배포를 하는 것이 일반적이며 클라우드 환경에서의 오토스케일링 설정 또한 필요합니다. 그리고 많은 이용자가 사용하는 서비스의 경우에 CDN을 사용하는 것이 매우 유리합니다. 하지만 제 경우 소규모 개인 프로젝트이며 사용자도 많지 않기에 해당 부분은 적용하지 않았습니다. 하지만 소규모 개인 프로젝트라 하더라도 무중단 배포 방식을 한 번쯤 알아보시는 것을 추천드립니다.
* 무중단 배포의 예시인 멀티 컨테이너 배포는 https://codegear.tistory.com/91 참고
#4 배포
- 이번 프로젝트에서 배포하는 인스턴스는 AWS EC2고 OS는 linux(ubuntu)입니다만 클라우드를 사용하실 거라면 Azure나 GCP를 이용해 보셔도 좋을 것 같습니다.(온 프레미스 환경은 고려하지 않겠습니다.) 아니면 같은 AWS 내에 더 추상화를 해주는 AWS EB를 사용해 보시는 것도 좋을 것 같네요.(전 설정에 너무 오래 걸려서 EB는 하다가 포기했습니다만...) 배포는 위에서 일부 언급했지만 docker를 통해 이뤄졌습니다. 여기서 같은 VPC에 AWS RDS를 연결해서 DB로 사용합니다. 사실 로드밸런싱이나 오토스케일링을 고려해서 인스턴스를 두 개 이상 사용한다던지 하면 서브넷과 AZ도 고려해야 하지만 저는 그 정도까진 고려할 필요 없을 것 같았습니다.
#5 AWS SSM으로 환경 설정 및 모니터링
- AWS SSM(SyStems Manager)은 AWS에서 제공하는 리소스 관리 및 모니터링, 자동화를 제공하는 도구로 쉽게 말해 인프라 환경 관리 툴입니다. SSM의 경우 EC2가 아닌 개인 온 프레미스 서버나 가상 머신들도 연동해서 사용할 수 있다고 하던데 사실 써보진 않아서 넘어가겠습니다. 제가 SSM을 쓴 가장 큰 이유는 이전 글에서 언급했던 것처럼 SSH 연결을 대체하기 위해서입니다.
EC2 서버에 기존에 접근하기 위해선 일반적으로 SSH(Secure SHell) 연결이 필요합니다. 이 때 맥의 경우 cmd로 직접 서버에 연결이 가능하지만 윈도우의 경우 putty라는 서드파티 툴을 사용해야 합니다. 게다가 OS 관계 없이 pem key를 받아서 이를 관리하는 것도 사실 불안한데 putty의 경우 심지어 이 키를 자체적으로 저장해둬야 합니다. 아무리 암호화 작업이 이루어져있어도 찝찝한건 어쩔 수 없습니다.
저 같은 경우는 이 putty를 켜는 것도 귀찮아서 EC2에 jupyter notebook을 설치해 웹으로 EC2에 접근할 수 있게 해 뒀습니다만 이것 역시 인증서도 설정해야 하고 여러 가지 명령어도 추가로 쳐줘야 하는 것은 당연합니다. 그래서 이런 번거로운 점들을 해결해 줄 수 있는 게 AWS SSM입니다.
SSM을 사용한다면 가장 좋은 점은 애초에 인스턴스를 만들 때 pem key를 생성할 필요조차 없습니다. 단순히 IAM만 맞춰준다면 AWS 콘솔에서 바로 세션 관리자를 통해 EC2 서버에 접근할 수 있습니다. 또한 inventory에서 제 서버가 어떻게 가용되는지 모니터링도 하나의 대시보드로 할 수 있습니다. 이를 다른 도구로 대체한다고 한다면 SSM이 지원하는 기능이 엄청 다양하므로 이를 대체할 수 있다고 하기엔 목적에 따라 매번 달라지므로 애매합니다. 제 경우에 한해선 가장 큰 게 SSH 대체였으니 이것 대신 SSH를 그냥 사용하거나 접근용 IP를 설정해도 되긴 하겠네요.
추후 필요하다면 제가 겪은 과정들을 자세히 풀어낼 수도 있겠지만 앞서 얘기했듯 굳이 포스트 할 필요가 있을까 싶어 일단은 접어두겠습니다. 만약 궁금한 점이 있으시다면 덧글로 남겨주세요.
refer
https://codegear.tistory.com/84
배포자동화(CI/CD) - Github Actions/Nuxtjs/Docker/EC2
다음은 이 글의 유튜브 영상입니다. https://youtu.be/E3i9qt0SS-I 프로젝트를 진행할때 많은 시간을 들여야 하는 것 중에 하나가 바로 배포입니다. 형상관리(Git)에 커밋을 하고, 서버에 파일을 업로드
codegear.tistory.com
https://velog.io/@zuckerfrei/Github-Actions-1.-self-hosted-runner
Github Actions - 1. self-hosted runner
Github Actions의 runner 개념 및 self-hosted runner 설치 방법 정리
velog.io
https://www.youtube.com/watch?v=uBOdEEzjxzE&t=1102s
'Project' 카테고리의 다른 글
P4_기존 프로젝트에 CI/CD 파이프라인 구축하기(github actions)_1 (0) | 2024.02.01 |
---|---|
P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_5 기능 명세 정의 (0) | 2023.02.26 |
P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_4 CORS로 인한 기능 수정 (0) | 2023.02.09 |
P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_3 리액트+스프링 부트 연동2 (0) | 2023.02.06 |
P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_2 리액트+스프링 부트 연동1 (0) | 2023.02.05 |