게시 2025/4/10
by 황시우
개요
FITDAY를 개발하기 전에, CI(Continuous Integration)와 CD(Continuous Delivery)를 도입하여 브랜치 병합(merge)부터 자동 빌드, 테스트, 배포까지의 과정을 자동화해야겠다고 생각했습니다.
CI/CD tool 선정
2025년 기준 가장 인기있는 CI/CD tools를 살펴본 결과,
Tool | Best for | Trial info |
Octopus Deploy | Best for complex deployments | 30-day free trial |
Github Actions | Best for small team | Free plan available |
Azure DevOps | Best for Azure development | Free plan available |
Bitrise | Best for mobile app development | Free plan available |
GitLab CI/CD | Best maturity feedback | Free plan available |
Argo CD | Best for kubernetes development | Free plan available |
Jenkins | Best for scaling companies | Free plan available |
OpenShift Pipelines | Best open-source option | Free plan available |
✅ Github Actions의 장점
- 소규모 팀에 적합 및 무료 플랜을 제공합니다.
- GitHub 저장소 내에서 직접 CI/CD 파이프라인을 개발할 수 있어 익숙한 환경에서 쉽게 사용할 수 있습니다.
- Commit, Pull Request, Push 등 일반적인 Github 이벤트를 트리거를 활용하여 자동화된 워크플로우 실행이 가능합니다.
개인프로젝트를 진행하는 입장에서 이러한 장점들로 Github Actions는 최적의 선택이었습니다!
CI/CD 적용 과정
✅ 장점
- 빌드 작업을 Github Actions에서 하기 때문에 운영하고 있는 서버의 성능에 영향을 거의 주지 않음
- CI/CD 툴로 Github Actions만 사용하기 때문에 인프라 구조가 복잡하지 않고 간단함
❌ 단점
- 무중단 배포를 구현하거나 여러 EC2 인스턴스에 배포를 해야 하는 상황이라면, 직접 Github Actions에 스크립트를 작성해서 구현해야 함
현업에서 초기 프로젝트를 구축할 때 사용하는 방법입니다. 개인프로젝트이므로 이러한 플로우로 간단하게 CI/CD를 진행하면 될 것 같다는 생각을 했습니다.
그런데,
만약 추후 서비스가 고도화된다면, 이를 다시 구성해야 되는데 더 복잡성이 늘지 않을까?
나중에 팀원이 생긴다면 "내 로컬에서는 돌아가는데 넌 왜 안돼?"라는 말을 듣지 않을까?
롤백할 때는 어떡하지?
무중단 서비스는 어떻게 구현하지?
그래서 고안한 아키텍처는 다음과 같습니다.
✅ 장점
- 컨테이너 기반의 서버가 여러 대 이더라도 쉽게 자동 배포를 구축 가능 즉, 확장성을 고려한 인프라 구축 가능
- 롤백이 쉬움
❌ 단점
- 인프라 구조의 복잡성
- 오버엔지니어링 가능성
🚀 ECR vs Docker Hub
Docker Hub와 ECR을 비교해 보았을 때
항목 | Docker Hub 사용 | AWS ECR 사용 |
속도 | 외부 네트워크 경유 (느림) | AWS 내부 네트워크 활용 (빠름) |
보안 | Docker 로그인 필요, IAM 지원 없음 | AWS IAM 기반 접근 제어 |
연동성 | AWS 서비스와 직접적인 연동 어려움 | EC2, CodeDeploy와 연동 최적화 |
비용 | 일정 트래픽 이상 발생 시 유료 | AWS 내부 네트워크 사용하여 비용 절약 |
배포 안정성 | Docker Hub의 정책 변경에 영향 있음 | AWS S3 기반 저장소로 안정성 높음 |
✅ 배포 속도 향상 (AWS 내부 네트워크 활용, AWS 서비스와의 연동 최적화)
✅ 보안 강화 (IAM 기반 접근 제어)
✅ 비용 절감 (AWS 내부 네트워크를 사용하여 비용 절약)
AWS 내부 네트워크를 활용해서 배포를 하기 때문에 속도가 다른 tool을 사용할 때 보다 빠르고, 비용절감을 할 수 있습니다.
또한 AWS 자체 서비스를 사용하기 때문에 CodeDeploy와의 연동을 최적화할 수 있다는 점에서 AWS ECR을 채택하였습니다.
AWS IAM 설정
Github Actions
AWS 입장에서 Github Actions는 외부 기능이므로 IAM을 설정할 때 '사용자' 탭으로 설정해야 했습니다.
EC2, S3, CodeDeploy에 대한 접근 권한 설정
- AmazonEC2 ContainerRegistryFullAccess
- AmazonS3 FullAccess
- AWSCodeDeployFullAccess
EC2
CodeDeploy에서 S3에 있는 각종 yml, script 파일을 압축파일을 EC2에서 실행하도록 하기 위해서 S3에 대한 접근 권한이 필요했습니다.
공식문서 S3 커스텀 설정
또한 ECR에 접근해서 Docker image를 가져와야 하기 때문에 ECR에 대한 접근 권한이 필요했습니다.
ECR IAM 설정
- AmazonEC2 ContainerRegistryFullAccess
Github Actions 배포 성공
HTTPS 적용 과정
이렇게 API서버 CI/CD를 구축하는 데 성공하였고! HTTP로 기동 되는 서버에서는 어떤 정보들이 오가는지 알 수 있어서 보안을 위해서 HTTPS를 적용해야겠다고 생각했습니다.
일정 비용을 지불하고 AWS ACM(AWS Certificate Manager)을 사용하는 방법도 있지만, 저는 더이상 지출을 발생시키고 싶지 않아서(인프라 세팅으로 인해 부족한 서버 용량을 Scale up을 하였습니다..) 무료로 SSL을 적용할 수 있는 Let's Encrypt을 사용하였습니다.
Certbot은 비용없이 SSL 인증서를 발급해주며 일정기간 갱신해줘야 하는 특징이 있습니다.
Certbot은 추가적으로 인증서를 갱신할 때 웹 서버를 재기동 할 필요가 없습니다.
이를 구현하기 위해 docker-compose를 이용해 reverse proxy server용 Nginx image와 Certbot image를 넣어서 시도해 봤습니다.
nginx.conf 파일로 proxy를 설정해 주는 부분이었는데, location에 proxy_pass 하는 부분에서 문제가 발생하였습니다.
원인
API서버의 Docker 네트워크와 docker-compose로 띄운 컨테이너들의 네트워크가 서로 불일치하였습니다.
이렇게 단일로 docker run 한 것은 docker-compose의 서비스 이름으로 인식할 수 없다는 것을 알았습니다.
docker-compose로 띄운 컨테이너들은 도커 내부 DNS로 서로 인식을 할 수 있지만, API서버는 docker-compose로 실행 시키지 않아서 서로 인식할 수 없기 때문입니다.
해결 방안
고민을 해 본 결과, 일반적으로 Spring Boot를 내 컴퓨터 내에서 실행을 시킬 때, localhost:8080으로 띄어지는 것을 알 수 있었습니다. 이 아이디어를 적용한다면, EC2 내부에서도 localhost:8080을 사용할 수 있다는 것입니다.
시도
같은 도커 내부 네트워크가 아니라면 localhost로 접근할 수 없었습니다. 예를들어 Docker로 API 1이라는 컨테이너를 띄어놓고, localhost:8080으로 Spring Boot를 띄어놓으면 Docker로 API 2라는 컨테이너에서는 같은 네트워크로 구성되어 있지 않았기 때문에 접근할 수 없었습니다. (당연한 얘기지만..)
결론
같은 네트워크로 묶는다.
Docker에서는 서로 다른 Docker 네트워크로 구성 되어 있다하더라도 같은 네트워크로 묶어줄 수 있는 기능이 있었습니다. 따라서 API 서버와 각종 DB, nginx 서버를 한 네트워크로 묶어서 해결하였습니다!
Docker 네트워크 문제는 해결이 되었는데, Let's Encrypt를 적용하는데 있어, 문제가 발생하였습니다.
우선 docker-compose-nginx 로 Nginx proxy server 컨테이너와 SSL인증을 자동으로 해주는 certbot 컨테이너를 띄웠습니다.
certbot의 option에 따르면,
--webroot은 서버 루트 폴더에 파일을 넣어 인증을 할 수 있는 기능입니다.
certbot은 webroot명령어를 확인하고, proxy server에 인증요청을 보낼 것입니다. certbot이 확인할 파일을 proxy server에 명시를 해야하는데, 이를 --webroot-path 라는 명령어를 이용해서 지정할 수 있었습니다.
--webroot --webroot-path=/usr/share/nginx/html
따라서 해당 경로를 마운트해서 EC2에 공유시켜서 해결하였습니다!
이를 적용시켜서 Nginx 컨테이너를 재시작 하였는데 options-ssl-nginx.conf 파일이 없어서 계속해서 proxy server 컨테이너가 실행이 안됐습니다.
open() "/etc/letsencrypt/options-ssl-nginx.conf" failed (2: No such file or directory) in /etc/nginx/nginx.conf:42
nginx: [emerg] open() "/etc/letsencrypt/options-ssl-nginx.conf" failed (2: No such file or directory) in /etc/nginx/nginx.conf:42
원인을 분석한 결과, certbot은 --nginx 옵션을 사용할 경우 options-ssl-nginx.conf 및 ssl-dhparams.pem 파일을 자동으로 생성해준다는 사실을 알게 되었습니다. 이 파일들은 HTTPS 설정 시 필수로 요구되는 SSL 설정 파일입니다.
하지만 저는 certbot을 컨테이너화하여 사용하고 있으며, 인증 요청은 reverse proxy server(Nginx)가 처리하도록 구성했기 때문에 --nginx 옵션을 사용할 수 없는 상황이었습니다.
따라서 해당 파일들을 수동으로 생성하고 필요한 설정값들을 명시적으로 제공함으로써 문제를 해결할 수 있었습니다.
결과적으로 HTTPS 설정이 정상적으로 적용되었고, 인증 및 배포 환경 구성도 완료되었습니다!
curl 명령어로 요청보낸 후 정상적인 응답을 확인이 되어, proxy server와 API서버 연동 및 HTTPS 적용을 완료하게 되었습니다!!
마지막으로..
CI/CD를 적용해보면서 처음에는 "그냥 GitHub Actions로 자동화만 하면 되는 거 아냐?"라는 단순한 생각으로 접근했습니다.
하지만 실제로 구현해보니, 단순히 사용하는 것만이 아니라 각 기능에 대한 이해가 중요했고, 이를 익히는 데 예상보다 많은 시간이 걸렸습니다.
그럼에도 불구하고 하나씩 기능이 제대로 동작하는 걸 보면서 점점 자신감이 붙었고, 속도도 붙어서 빠르게 마무리할 수 있었습니다.
Nginx는 처음 다뤄봤는데, 이번 경험을 통해 네트워크에 대한 이해도를 높일 수 있었고, 앞으로 어떤 부분을 더 공부해야 할지 방향성을 잡을 수 있어서 의미 있는 시간이었습니다.
이제 서버 인증, DB 성능 테스트, 프론트엔드 구현 등 아직 남은 과제들이 있지만, 이번 경험을 통해 자신감을 얻었기 때문에 앞으로도 충분히 잘 해낼 수 있을 거라 생각합니다!!
참고
https://thectoclub.com/tools/best-ci-cd-tools/
https://github.com/awslabs/amazon-ecr-credential-helper
https://minholee93.tistory.com/entry/Nginx-Lets-Encrypt-SSL-Certificates
'프로젝트' 카테고리의 다른 글
[FITDAY] 99.98% 성능 개선 최적화 여정 (2) | 2025.05.15 |
---|---|
[FITDAY] 인증 인가 개발기 (1) | 2025.05.14 |