백엔드 팀원이 이런 요구사항을 해주셨다.
맞는 말이였다.
개발을 하고 나면, Main branch에서 밖에 테스트를 못 하는 상황이였는데, 올바르지 않은 방법이였다.
만약, main branch merge 했는데, 오류가 난다면?
운영중인 서버는 실시간으로 먹통이 될 것이고, 복구하는 동안 유저는 우리 어플리케이션을 이용 못 할 것이다.
이런 이유로 인해 운영서버, 개발서버를 분리하기로 결정했다.
로직을 간단하게 설명하자면,
1) 각자 feature branch에서 작업
2) develop branch에 pr 올릴 시 자동 test 진행
3) test 통과 후 pr을 develop branch에 merge 진행시
develop 서버에 자동 배포
4) 이후에 관리자가 원할 때 develop -> main merge 시에 기존 CI/CD 작동
2,3번만 만들면 된다!
작업 단계
1) EC2 인스턴스 develop용 하나 만들기
2) yml 파일 profile dev, prod 분리
3) github actions workflow 파일 작성
4) 배포 shell script 작성
5) swagger dev, prod 분리
1번은 딸깍딸깍 하면 금방한다.
2번에서 오류를 맞이했다.
기존에 active 설정을 dockerfile에서 설정했었다.
ENTRYPOINT java -jar -Dspring.profiles.active=prod app.jar
yml 파일에 active: dev 이렇게 설정하는 것과,
Dockerfile에서 active: prod 설정하는것의 우선순위가 궁금해 찾아본 결과, Dockerfile이 우선시 된다는 결과를 알 수 있었다.
우선 Dockerfile을 수정했다.
ENV JAVA_TOOL_OPTIONS=${JAVA_TOOL_OPTIONS:-"-Dspring.profiles.active=default"}
# 컨테이너가 시작될 때 실행할 명령 설정
ENTRYPOINT ["java", "-jar", "app.jar"]
이런식으로, active=default 값으로 변경을 했고,
이후에 shell script에서 직접 profile을 주입하는 방법으로 진행했다.
- name: Set Spring profile to dev
run: echo "JAVA_TOOL_OPTIONS=-Dspring.profiles.active=dev" >> $GITHUB_ENV
# Gradle을 사용하여 프로젝트를 빌드합니다. 이 과정에서 실행 가능한 JAR 파일이 생성됩니다.
- name: Build with Gradle
env:
JAVA_TOOL_OPTIONS: ${{ env.JAVA_TOOL_OPTIONS }}
run: ./gradlew build -x test
# Docker 이미지 Build
- name: Docker image build
env:
JAVA_TOOL_OPTIONS: ${{ env.JAVA_TOOL_OPTIONS }}
run: docker build --build-arg JAVA_TOOL_OPTIONS=$JAVA_TOOL_OPTIONS -t ${{ secrets.ECR_REPOSITORY_DEV }}:latest .
처음에는 이런식으로 github actions의 workflow 파일에서 active=dev를 설정했다.
그런데 컨테이너가 자꾸 죽었다. 로그를 확인해본 결과,
스프링 실행로그만 뜨고 그냥 다시 죽었었다.
오류를 찾던 도중, profile이 제대로 안 들어가있다는 걸 알게되었고, 이후에 그냥 shell script에도 profile을 넣어버렸다.
shell script 일부분 코드
sudo docker run -d -p 8080:8080 \
--name springboot_container \
-e JAVA_TOOL_OPTIONS="-Dspring.profiles.active=dev" \
$IMAGE_URI:latest" \
결론부터 말하자면, shell script에서 컨테이너 실행할 때 직접 profile 설정해주니 오류가 해결되었다.
이거때문에 시간 좀 잡아먹었다ㅜ
3번: prod 환경에 작성해둔거 긁어와서 비슷하게 작업했다.
4번: prod 작업해둔거 긁어와서 작업했는데, 어차피 develop 서버니까 무중단 배포 이런거 신경쓸 필요 없다고 판단해 간단하게 작업했다.
5번:
swagger를 한 번에 2개 띄울 수 있나? 음... 어떻게 해야할까 고민하면서 구글링 해본 결과, @profile 어노테이션으로 동작 구성을 달리 하면 된다는 것을 알았다!
@Bean
@Profile("dev")
public OpenAPI openAPIForDev() {
@Bean
@Profile("prod")
public OpenAPI openAPIForProd() {
이런식으로!
서버는 어차피 스웨거가 알아서 탐지해주니, 그냥 간단하게 dev 코드 하나 더 작성한 후에 profile 설정만 해주면 끝이였다!
결과:
이렇게 하나의 이미지에서 profile 설정을 통해 2개의 swagger 설정을 했다!
느낀점 :
1) EC2 인스턴스가 벌써 6개다...
아무리 저렴한 EC2라고 해도 비용이 만만치 않다.
Spot Instance를 사용하는 등 뭔가 해결책을 마련해야할 것 같다.
2) develop 서버를 public subnet에 구축했다.
기존의 운영 서버는 ALB 주소를 DNS로 등록해놨다.
만약, develop 서버를 ALB 내부에 만든다면, 똑같은 ALB 주소를 사용할 것이다.
그렇기에 새로운 public IP가 필요했고, public subnet에 구축을 하게 되었다.
결국 private subnet에 접근하는 방법은
Bastion host -> private subnet에 존재하는 ec2 instace 이기 때문에, 공개적인 public ip가 필요한데,
이걸 Bastion host로 할 수도 없고, ALB를 하나 더 달기에는 비용이 너무 아까웠다.
develop 서버를 public subnet에서 사용하는건 좋은 방법은 아니라고 생각한다. 이후에 대책을 마련해야 할 것 같다.
ex) IP 주소를 제한한다거나, ALB를 하나 더 만든다거나,,, 아니면 내가 모르는 방법이 있지 않을까? swagger에서 private IP를 사용할 수 있다던지 등등... 좋은 해결방안이 존재할 것 같다...!