Docker로 React, Spring 애플리케이션을 인스턴스에 배포하는 과정에서의 고민을 기록하고자 한다.
amazon linux vs ubuntu
- 아마존 리눅스는 centos기반 패키지를 더 얹은 os이다
- 서버용으로 보통 실무에서는 ubuntu보다 centos를 사용하고, AWS를 이용하는 현 상황에서 amazon linux를 사용
docker jre image slim vs alpine
- 둘 다 용량을 최소화하도록 설계되었음
- slim은 debian linux 기반으로 애플리케이션을 실행하는 데 필요한 패키지만 포함한다.
- alpine은 alpine linux 기반으로 slim 이미지에 포함된 패키지 중 일부가 포함되어 있지 않지만 가장 가볍고, 보안에 더 강하다는 특징이 있다.
- 본 서비스에서는 용량의 제한의 압박이 덜하기에 openjdk 17버전의 jre slim을 사용한다. → 추후 용량 관련 문제가 생길 시 alpine으로 대체 가능
alpine은 375MB, slim은 449MB로 대략 70MB 차이가 난다.
java version 에러
intellij gradle JVM, project SDK, build.gradle 모두 자바 17버전으로 설정되어 있지만, gradlew build로 애플리케이션을 빌드할 경우 현재 컴포넌트는 17버전이고, consumer는 11버전과 호환되는 런타임이 필요하다는 에러가 발생한다.
JAVA_HOME에 jdk 11버전이 설정되어 있어 발생한 문제이다.
https://github.com/redhat-developer/vscode-java/issues/3136
자바용 Gradle 지원 플러그인이 지원되는 첫 번째 버전인 Java 11이 하드코딩 되어있어 발생한 것으로 보인다.
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.5'
id 'io.spring.dependency-management' version '1.1.3'
}
java {
sourceCompatibility = '17'
}
id ‘java’가 17을 선택하여 실행 중이지만 id ‘org.springframework.boot’ version ‘3.1.5’가 Java 11에서 Gradle 가져오기를 시도 중인 것이다.
JAVA_HOME 환경변수를 jdk 17로 변경하면 정상 작동한다.
Github Actions로 AWS EC2에 배포
EC2 인스턴스에 SSH로 직접 접근하는 방식은 빌드를 EC2 인스턴스 상에서 진행하기 때문에 EC2 메모리 등의 자원을 사용하여 서비스에 영향을 줄 수 있다.
하지만 진행하고자 하는 CI/CD 방식은 빌드를 Github Actions에서 진행하고, Docker Hub에 빌드된 파일을 push, 업로드된 이미지를 pull 하여 EC2에 배포하는 방식으로 EC2 자원 사용을 최소화한다.
Docker를 사용하지 않으면 Github Actions에서 빌드된 파일을 S3에 업로드하고, S3에 업로드된 파일을 바탕으로 EC2에 배포하는 방식인 AWS CodeDeploy를 고려해 볼만 하다.
backend-cicd.yml
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
# MySQL Setup
- name: Setup MySQL
uses: samin/mysql-action@v1
with:
character set server: 'utf8'
mysql database: '${{ secrets.DATABASE_NAME }}'
mysql user: '${{ secrets.DATABASE_USERNAME }}'
mysql password: '${{ secrets.DATABASE_PASSWORD }}'
# 설정파일 추가
- name: Set YML
run: |
echo "${{ secrets.API_KEY }}" | base64 --decode > ./src/main/resources/application-API-KEY.yml
# gradle 권한 부여
- name: Grant execute permission form gradlew
run: |
chmod +x gradlew
# build with gradle
- name: Build with Gradle
run: |
./gradlew clean build
# Docker build and push
- name: Build Docker image
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build --tag ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service:1.0 .
- name: Push Docker image
run: docker push ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service:1.0
- name: Docker logout
run: docker logout
# deploy
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.KEY }}
script: |
sudo docker stop dnch-edu-service
sudo docker rm dnch-edu-service
sudo docker rmi -f ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service:1.0
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service:1.0
sudo docker run --network dnch-network -e "spring.datasource.url=jdbc:mysql://mysql:3306/ministry_of_education?characterEncoding=UTF-8&serverTimezone=Asia/Seoul" -d --name dnch-edu-service ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service:1.0
- 데이터베이스(MySQL)을 설정한다.
데이터베이스를 연결하지 않고 Github Action이 실행되면 HibernateException 에러가 발생한다. - gitIgnore로 인해 git에 업로드되지 않는 설정파일을 추가한다.
-> yml 파일은 secret에 그대로 등록하려고 하면 에러가 발생한다.따라서 Base64로 인코딩을 해서 등록해야 한다.
-> Base64로 인코딩하여 secret에 등록한 후 github actions script에서 echo "${{ secrets.API_KEY }}" | base64 --decode > ./deal/src/main/resources/application-API-KEY.yml 로 작성하여 base64를 decode하여 등록하여야 에러가 발생하지 않는다. - gradle 권한을 부여하고 Spring 애플리케이션을 빌드한다.
- 빌드한 jar파일을 docker 이미지로 빌드한 뒤 docker hub에 push한다.
- AWS EC2에 연결하여 docker 명령어 script를 실행한다.
- host: 인스턴스 public IP
- username: 인스턴스 사용자 이름
- key: 인스턴스 키 페어(.pem)
frontend-cicd.yml
name: react cicd
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
# Docker build and push
- name: Build Docker image
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
cd frontend/
docker build --tag ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service-react:1.0 .
- name: Push Docker image
run: docker push ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service-react:1.0
- name: Docker logout
run: docker logout
# deploy
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.KEY }}
script: |
sudo docker stop dnch-edu-service-react
sudo docker rm dnch-edu-service-react
sudo docker rmi -f ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service-react:1.0
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service-react:1.0
sudo docker run --network dnch-network -p 80:3000 -d --name dnch-edu-service-react ${{ secrets.DOCKER_USERNAME }}/dnch-edu-service-react:1.0
Dockerfile
backend
FROM openjdk:17-ea-slim
VOLUME /tmp
# 빌드된 파일을 dnchEduService.jar로 복사
COPY build/libs/sideproject-spring-1.0.jar dnchEduService.jar
# 명령어 실행
ENTRYPOINT ["java", "-jar", "dnchEduService.jar"]
frontend
FROM node:16-alpine
WORKDIR /app
# package.json 워킹 디렉토리에 복사 (.은 설정한 워킹 디렉토리를 뜻함)
COPY package.json ./
# 명령어 실행 (의존성 설치)
RUN npm install
# 현재 디렉토리의 모든 파일을 도커 컨테이너의 워킹 디렉토리에 복사한다.
COPY . .
# 3000번 포트 노출
EXPOSE 3000
# npm start 스크립트 실행
CMD ["npm", "start"]
React container가 Spring container와 통신되지 않는 이유
React 컨테이너, Spring 컨테이너, mysql 컨테이너를 같은 도커 네트워크에 할당하고, 컨테이너 이름으로 통신을 하려고 했을 때, Spring 컨테이너와 mysql 컨테이너는 컨테이너 이름으로 잘 통신이 된다.
하지만 React 컨테이너와는 통신이 되지 않는다. 그 이유는 다음과 같다.
- React 애플리케이션은 노드 런타임에 의해 컨테이너에서 직접적으로 실행된다.
- 따라서 React 코드는 컨테이너 내부에서 실행되지 않고, 항상 브라우저에서 실행된다.
- 이는 Spring 컨테이너 이름으로 접근하는 코드가 도커에 의해 Spring 컨테이너를 가리키는 변환작업이 컨테이너에서 실행되지 않는 것이다.
-> React에서 직접 Spring 서버 url로 요청한다.
https://velog.io/@tein/Section5.-Docker로-다중-컨테이너-애플리케이션-구축하기
'배포' 카테고리의 다른 글
AWS Linux 환경 Spring 이미지 파일 저장 (1) | 2024.01.10 |
---|