https://github.com/Hot6-NovelCraft/Hot6-NovelCraft
GitHub - Hot6-NovelCraft/Hot6-NovelCraft
Contribute to Hot6-NovelCraft/Hot6-NovelCraft development by creating an account on GitHub.
github.com
개인별 배포용 레퍼지토리 [ AWS ]
https://github.com/MinWoo1995/Hot6-NovelCraft-local
GitHub - MinWoo1995/Hot6-NovelCraft-local
Contribute to MinWoo1995/Hot6-NovelCraft-local development by creating an account on GitHub.
github.com
1. 오늘 한 일
오늘은 NovelCraft 프로젝트의 Jenkins CI/CD 파이프라인 완성 및 AWS 앱 서버 배포까지 전 과정을 마무리했다.
Jenkins Credentials 등록 (Docker Hub, App EC2 SSH Key, MySQL, AWS S3, OAuth2, JWT, PortOne, CoolSMS, 국립도서관 API 총 18개)
GitHub Webhook 연동 (main 브랜치 Push 시 자동 빌드 트리거)
Jenkins Pipeline 생성 및 Jenkinsfile 작성
Docker Hub 레포지토리 생성 및 이미지 Push 성공
앱 EC2 Docker 설치 및 Redis 설치
보안 그룹 인바운드 규칙 추가 (Jenkins EC2 → 앱 EC2 SSH 허용)
Jenkins → Docker Hub → 앱 EC2 자동 배포 파이프라인 완성
application-prod.yml 환경변수 누락 수정 (app.frontend.url)
Postman으로 API 응답 확인 완료
2. 트러블슈팅
2-1. Jenkinsfile 파일명 대소문자 문제
문제 상황
Jenkins 빌드 시 아래 에러가 발생했다.
ERROR: Unable to find Jenkinsfile from git
원인 분석
파일명을 jenkinsfile(소문자)로 생성했는데 Jenkins는 반드시 Jenkinsfile(대문자 J)을 찾는다. 대소문자가 달라 Jenkins가 파일을 인식하지 못한 것이다.
해결
bashgit mv jenkinsfile Jenkinsfile
git commit -m "fix: Jenkinsfile 파일명 대문자로 수정"
git push origin main
2-2. Dockerfile 주석 파싱 에러
문제 상황
Docker 빌드 중 아래 에러가 발생했다.
ERROR: failed to solve: dockerfile parse error on line 1: unknown instruction: 1단계:
원인 분석
Dockerfile에 한글 주석을 # 없이 그냥 텍스트로 작성했다. Docker 파서가 첫 줄의 1단계:를 Dockerfile 명령어로 인식하려다 에러를 낸 것이다.
해결
모든 한글 주석 앞에 #을 붙였다.
dockerfile# 1단계: 빌드
FROM gradle:8.5-jdk17 AS build
2-3. openjdk:17-jdk-slim 이미지 not found
문제 상황
Docker 빌드 중 아래 에러가 발생했다.
ERROR: failed to solve: openjdk:17-jdk-slim: not found
원인 분석
openjdk:17-jdk-slim 이미지가 Docker Hub에서 deprecated 되어 더 이상 존재하지 않았다.
해결
실행 스테이지 베이스 이미지를 eclipse-temurin:17-jre-jammy로 교체했다.
dockerfile# 2단계: 실행
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
2-4. Docker Hub Push denied
문제 상황
Docker 이미지 Push 시 아래 에러가 발생했다.
denied: requested access to the resource is denied
원인 분석
두 가지 원인이 있었다. 첫째로 Jenkinsfile의 이미지 태그가 minwoo/novelcraft로 되어있었는데 실제 Docker Hub 유저네임은 jmw1995였다. 둘째로 Docker Hub에 novelcraft 레포지토리 자체가 생성되어 있지 않았다.
해결
Jenkinsfile의 이미지 이름을 jmw1995/novelcraft로 수정하고, Docker Hub에서 novelcraft 레포지토리를 Public으로 생성했다.
2-5. Jenkins Built-In Node 오프라인
문제 상황
Jenkins 빌드가 Waiting for next available executor 상태에서 멈추고 Built-In Node가 오프라인으로 표시됐다.
원인 분석
두 가지 문제가 복합적으로 발생했다. Free Swap Space가 0B로 메모리가 부족했고, Free Temp Space가 951MB로 Jenkins의 임계값인 1GiB 미만이어서 노드가 자동으로 오프라인 처리된 것이었다.
해결
Swap 메모리 2GB를 추가 생성하고, Jenkins Nodes 설정에서 Free Temp Space Threshold를 500MB로 낮춘 후 노드를 온라인으로 복구했다.
bashsudo dd if=/dev/zero of=/swapfile bs=128M count=16
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab
2-6. App EC2 SSH Connection timed out
문제 상황
Jenkins에서 앱 EC2로 SSH 배포 시 아래 에러가 발생했다.
ssh: connect to host 43.200.129.27 port 22: Connection timed out
원인 분석
앱 EC2의 보안 그룹(novel-app-security-group)에 Jenkins EC2 IP(43.201.153.0)에 대한 SSH 인바운드 규칙이 없었다. Jenkins EC2와 앱 EC2가 서로 다른 보안 그룹을 사용하고 있었는데 처음에 Jenkins 서버의 보안 그룹(novel-craft-security-group)만 수정하고 앱 EC2의 보안 그룹은 수정하지 않은 것이 원인이었다.
해결
novel-app-security-group에 Jenkins EC2 IP를 SSH 허용 규칙으로 추가했다.
유형 : SSH
포트 : 22
소스 : 43.201.153.0/32
2-7. app.frontend.url 환경변수 미설정으로 애플리케이션 실행 실패
문제 상황
배포 후 컨테이너 로그에서 아래 에러가 반복됐다.
Could not resolve placeholder 'app.frontend.url' in value "${app.frontend.url}"
원인 분석
OAuth2SuccessHandler에서 @Value("${app.frontend.url}") 로 프론트엔드 URL을 주입받는데 application-prod.yml에 해당 키가 정의되어 있지 않았다. Jenkinsfile에서는 FRONTEND_URL 환경변수로 넘기고 있었지만 yml에서 app.frontend.url로 매핑하는 설정이 누락된 것이었다.
해결
application-prod.yml 최상단에 아래 설정을 추가했다.
yamlapp:
frontend:
url: ${FRONTEND_URL}
3. 느낀 점
오늘은 CI/CD 파이프라인을 처음부터 끝까지 직접 구축하면서 배포 전 과정에서 마주칠 수 있는 문제들을 거의 다 경험한 것 같다.
가장 인상 깊었던 부분은 보안 그룹이었다. Jenkins EC2와 앱 EC2가 서로 다른 보안 그룹을 사용하고 있다는 걸 뒤늦게 파악했는데, 네트워크 인프라 설정에서는 각 리소스가 어떤 보안 그룹에 속해있는지 먼저 확인하는 습관이 필요하다는 것을 느꼈다.
app.frontend.url 누락 문제는 환경변수 이름과 yml 키 이름이 다를 수 있다는 점을 다시 상기시켜줬다. Jenkinsfile에서 FRONTEND_URL로 넘겨도 애플리케이션 코드가 app.frontend.url을 참조하면 다리를 놓아줘야 한다. 배포 전에 application.yml의 모든 플레이스홀더와 실제 주입되는 환경변수 이름을 맞춰두는 것이 중요하다는 것을 배웠다.
Built-In Node가 Swap 메모리 부족으로 오프라인이 된 것도 흥미로운 경험이었다. t3.small 인스턴스는 메모리가 2GB밖에 안 돼서 Jenkins 자체만으로도 메모리가 빡빡하다. Swap을 추가해서 해결했지만 장기적으로는 인스턴스 타입을 올리거나 빌드를 별도 에이전트로 분리하는 방향을 고려해야 할 것 같다.
무엇보다 오늘 GitHub Push 한 번으로 Docker 빌드, 이미지 Push, 앱 서버 자동 배포까지 한 번에 이루어지는 파이프라인이 완성됐다는 게 뿌듯했다. 앞으로 팀원들이 코드를 Push할 때마다 자동으로 배포되니 수동 배포의 번거로움 없이 개발에 집중할 수 있을 것 같다.
'spring_2기[본캠프] > 과제' 카테고리의 다른 글
| [파이널 과제] NovelCraft 웹소설 창작 플랫폼 개발 프로젝트 Day 10 (0) | 2026.04.28 |
|---|---|
| [파이널 과제] NovelCraft 웹소설 창작 플랫폼 개발 프로젝트 Day 9 (1) | 2026.04.24 |
| [파이널 과제] NovelCraft 웹소설 창작 플랫폼 개발 프로젝트 Day 7 (0) | 2026.04.22 |
| [파이널 과제] NovelCraft 웹소설 창작 플랫폼 개발 프로젝트 Day 6 (1) | 2026.04.21 |
| [파이널 과제] NovelCraft 웹소설 창작 플랫폼 개발 프로젝트 Day 5 (1) | 2026.04.20 |