Spring은 Datasource를 연결할 때 DB의 IP를 알 수 없어서 환경변수를 이용한다고 함.
환경변수는 말그대로 변수이므로 S_D_URL = 10.10.1.1로 들어가면 그것을 Spring이 참조하게 된다. 그 과정에서 DNS 레지스트리가 필요한 거라고 하심.
리버스 프록시 (Nginx)를 따로 둘것이다.
리버스 프록시는 모든 사용자 요청을 받아서 HTTP Header, HTTP Body 등을 받아서 처리한다.
리버스 프록시는 URL을 분석하여 / 로 요청오면 React로 보내주고, /api 로 요청이 들어오면 거부(Block) 하도록 구현할 것이다. 외부에서는 /api 를 사용하지 못하도록 하기 위함이다.
리버스 프록시는 /api로 요청이 오면 모두 block할 예정이지만 예외사항으로 React에서 들어오는 /api 요청은 Backend로 연결 시켜 주는 역할 까지 겸한다.
React에서는 /api/product 로 하는데 Spring에는 @RequestMapping이 /product 처럼 만들 예정이라서 /api/product로 오면 redirect하여 /api를 삭제하고 /product로 가도록 구현할 것이다. (왜 굳이 이렇게 하는지는 아직 모르겠다..)
이번 강의는 이전강의와 같이 React를 Nginx에서 실행하도록하는 목표를 공유하고있다.
차이점은 이전강의에서는 직접 build해줬지만 이번 강의에서는 build조차 자동으로 되도록 하고자 한다.
(이러한 차이점으로 인해 내 로컬에 nodeJs 환경이 없어도 도커로 빌드까지해서 실행까지 될수 있는 환경이 구축된다.)
[실습 전 준비사항]
이번 강의 실습을 위해 window에서 코딩한 my-app 프로젝트(react)를 docker가 있는 H2 서버로 옮겨주도록하자.
이전 강의와 구별되도록 ex09 / my-react-app-auto-build 라는 디렉토리를 만들어서 옮겨주었다.
(node_moudules는 COPY를 하지 않는다. npm install을 할 때 package.json을 참조하여 필요한 라이브러리들이 node_modules에 자동으로 생기기 때문.)
총 5개의 파일만 H2 서버로 옮겨주었다.
[Dockerfile 작성하기]
FROM node:alpine as build
WORKDIR /app
COPY package.json /app
RUN npm install --silent
COPY . /app
RUN npm run build
FROM nginx
#--from=build는 위에있는 FROM으로 부터 이미지가 구워지는데 그 결과로 나온 것을 의미한다.
# 위에서 나온 결과물인 /app/build 를 /usr/share/nginx/html로 복사하라는 의미이다.
COPY --from=build /app/build /usr/share/nginx/html
ENTRYPOINT ["nginx","-g","daemon off;"]
설명을 덧붙이자면
위의 FROM은 npm run build를 해서 build폴더에 build된 파일들을 생성하기 위함이고,
아래의 FROM부터는 이전 실습내용과 같이 build된 폴더를 서비스할 nginx를 위한 코드들이다.
COPY package.json /app #node_modules를 만들기 위해 먼저 복사
RUN npm install --silent #이때 node_modules가 생긴다., --silent는 터미널 출력을 최소화하는 명령어이다.
#src, public 등의 파일을 /app으로 복사한다. (이미 있는 package.json, node_moduels는 덮어쓰기 하지 않아서 복사가 되지 않는다.)
COPY . /app
RUN npm run build #해당 명령어까지 실행시키면 build파일이 생긴다.
신기한 것은 하나의 Dockerfile안에 2개의 FROM을 사용해서 컨테이너를 2개 만드는 것과 비슷한 효과를 낼 수 있다는 점인데... 인터넷 검색을 해보니 멀티 스테이지 빌드라고 한다.
※ 멀티 스테이지 빌드
컨테이너 이미지를 만들면서 빌드 등에는 필요하지만, 최종 컨테이너 이미지에는 필요 없는 환경을 제거할 수 있도록 단계를 나누어 이미지를 만드는 방법
멀티스테이지 빌드를 사용하면 빌드에는 필요하지만 최종 배포시에는 필요없는 파일들은 모두 삭제된 상태로 컨테이너가 실행되므로 좀 더 가벼운 실행환경을 만들 수 있다는 장점이 있다.
db 디렉토리에 있는 Dockerfile은 23강에서 실습했던 내용과 똑같다. 다만, init.sql에서 초기화하는 쿼리문이 살짝 달라졌지만 어렵지않다.
[Spring Boot]
Spring Boot는 product 디렉토리에 있는데, /product/src/main/resources/application.yml에서 profiles -> active를 prod로 변경해주면 된다. 사실 해당 작업은 안해도 무방한데, product의 Dockerfile에서 맨 마지막 ENTRYPOINT 부분에 -Dspring.profiles.active=prod를 추가할 것이기 때문에 안해도 상관은 없다.
그리고 /product/src/main/resources/application-prod.yml 에서 Datasource 연결 부분을 모두 환경변수로 잡는것 정도?
[SpringBoot Server Dockerfile 작성] ( ~ex08/docker-test-server 디렉토리에 작성)
FROM openjdk:11-jdk-slim
WORKDIR /app
# COPY만 docker-compose 파일의 위치를 기반으로 작동함
# docker-test-server내부에 있는 모든 파일을 WORKDIR인 /app으로 COPY를 한다는 의미이다.
COPY . .
# RUN은 현재 파일의 위치를 기반으로 작동함
RUN chmod +x ./gradlew
RUN ./gradlew clean build
# build.gradle에 어떤 설정을 하면 .jar파일이 1개가 만들어진다. (원래는 2개만들어짐)
# 만들어진 jar파일의 이름을 app.jar 로 변경한다는 의미이다. (실행하기 편하게)
#ARG JAR_FILE=build/libs/*.jar
#COPY ${JAR_FILE} app.jar
# 로컬에 빌드된게 아니라 컨테이너 내부에서 빌드된거니까 위에있는 COPY를 쓰면 안되고 RUN으로 실행시켜
야한다.
ENV JAR_PATH=/app/build/libs
RUN mv ${JAR_PATH}/*.jar /app/app.jar
ENTRYPOINT ["java", "-jar","-Dspring.profiles.active=prod","app.jar"]
주석처리된 내용 중에 #build.gradle에 어떤 설정을하면 .jar파일이 1개만 만들어진다는 내용이 있는데
git clone받은 파일 중 build.gradle 내용 맨 마지막 내용을 보면
jar {
enabled = false
}
이러한 내용이 있다. 해당 내용이 "프로젝트명-버전-plain.jar" 파일을 생성하지 않고 즉시 실행가능한 executable.jar파일("프로젝트명-버전.jar")만 생성 하게 하는 옵션이다. 해당 옵션을 사용하지 않으면 plain.jar, executable.jar 두개의 파일이 생성된다.