이전 포스팅에서 이어집니다.
[NCP, 도커] 서버 생성부터 배포까지 (야매판, 3 / 4)
이전 포스팅에서 이어집니다. [NCP, 도커] 서버 생성부터 배포까지 (야매판, 2 / 4) 이전 포스팅에서 이어집니다. [NCP, 도커] 서버 생성부터 배포까지 (야매판, 1 / 3) NCP 서버 생성, 도메인 레코드 설
doinitright.tistory.com
DockerFile을 통한 도커 이미지 생성
DockerFile 준비(백엔드, 프론트엔드)
아래에서 기술하는 도커파일은 작업 환경에 맞게 사용자가 변경해야 하며,
도커파일에서 빌드하는 과정을 참고하기 위해 기재하는 것이다.
해당 도커파일의 소스코드가 다른 프로젝트의 빌드 구조에서 동작하리라는 보장은 없다.
./Dockerfile (백엔드)
# DOCKER RUN : 기존의 작업 내역에 변경이 없으면 캐싱된 내역으로 대체
# 멀티 스테이지 1 : 빌드 스테이지
# GraalVm을 기반 이미지로 사용
FROM ghcr.io/graalvm/graalvm-community:17 AS builder
# 작업 디렉토리 설정
WORKDIR /app
# Gradle 래퍼 복사
COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
# Gradle 래퍼에 실행 권한 부여
RUN chmod +x ./gradlew
# 의존성 설치
# 이 단계에서 변경사항이 없다면, 다음 빌드에서 캐시
# gradle 빌드 과정에서 메모리 문제 및 기타 충돌 해결 위해 --no-daemon 활성화
RUN ./gradlew dependencies --no-daemon
# 소스 코드 복사
COPY src src
# 애플리케이션 빌드
RUN ./gradlew clean build --no-daemon
# 멀티 스테이지 2 : 실행 스테이지
FROM ghcr.io/graalvm/graalvm-community:17
# 작업 디렉토리 설정
WORKDIR /app
# 첫번째 빌드 스테이지에서 빌드된 Jar 파일만을 복사
COPY --from=builder /app/build/libs/*.jar app.jar
#실행할 JAR 파일 지정
#여러 파일이 있는 경우 실행할 특정 파일을 지정
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "/app/app.jar"]
백엔드 도커파일의 경우에는 두가지 핵심적인 기능을 적용해서 빌드 구조를 개선했다.
- 멀티 스테이지 : 빌드 과정을 두개의 스테이지로 나누었다. (이미지 용량 절감의 효과)
- 스테이지 1 : 기반 이미지 + gradlew로 의존성을 설치, 소스코드를 복사해 jar 파일을 생성하는 스테이지
- 스테이지 2 : 스테이지 1에서 생성된 jar 파일만을 복사해 생성된 이미지 실행 시 jar 파일이 실행되게 트리거하는 스테이지
- 멀티 스테이지 기반 jar 파일 제외 실행에 불필요한 파일은 제거되므로 이미지 용량이 줄어든다.
- 잦은 캐시 히트 발생 : 빌드 과정에서 변경 사항이 비교적 적게 일어나는 과정은 빌드의 앞으로 순서 변경
- 캐시 히트 : 동일한 도커 파일을 기반으로 이미 빌드된 레이어를 재사용해 빌드 속도를 향상하는 매커니즘
- 도커 이미지는 여러개의 레이어로 구성되어 있으며, 이전에 동일한 명령어로 이미지를 빌드하는 경우 레이어를 통째로 다시 만들지 않고 변경 내용이 발생하기 전까지 이전에 생성된 레이어를 재사용한다.
- 즉, 변경 사항이 비교적 적을 의존성 설치와 패키지 파일(gradle, gradlew) 등을 빌드 과정의 맨 앞에 위치시켜 최대한 이전에 생성된 레이어를 재사용해 빌드 시간을 단축시킬 수 있다.
- 캐시 히트 : 동일한 도커 파일을 기반으로 이미 빌드된 레이어를 재사용해 빌드 속도를 향상하는 매커니즘
./client/Dockerfile (프론트엔드)
# 1단계: 빌드 환경 설정
FROM node:18-alpine AS builder
# 작업 디렉토리 설정
WORKDIR /app
# 의존성 파일 복사
COPY package.json package-lock.json ./
# 의존성 설치
RUN npm install --force --legacy-peer-deps
# 애플리케이션 소스 복사
COPY . .
# SvelteKit 애플리케이션 빌드
RUN npm run build
# 2단계: 실행 환경 설정
FROM node:18-alpine
# 작업 디렉토리 설정
WORKDIR /app
# 빌드 스테이지에서 생성된 파일 복사
COPY --from=builder /app/build .
COPY --from=builder /app/node_modules node_modules
COPY --from=builder /app/package.json package.json
# 애플리케이션 포트 설정
EXPOSE 5173
# 환경변수 설정
ENV NODE_ENV=production
ENV PORT=5173
# 애플리케이션 실행
CMD ["node", "index.js"]
프론트엔드 도커파일 작업도 동일하게 멀티 스테이지, 캐시 히트를 적극 활용했다.
도커파일을 통한 도커 이미지 빌드
이미지를 빌드하기 위해 도커 내부에 별도 clone 받을 레포지토리를 생성하고, 작업 내용을 git clone 한다.
# 디렉토리 생성
mkdir -p <디렉토리명>
cd <디렉토리명>
# 프로젝트 클론
git clone <원격 저장소 주소>
현재 실습 프로젝트의 구조는 아래와 같다.
root
└ client(프론트 작업 폴더)
└ // 기타 파일들 ...
└ src
└ static
└ Dockerfile (프론트 도커파일)
// 아래부터는 백엔드 작업 파일들 ...
└ gradle
└ src
└ build.gradle
└ Dockerfile (백엔드 도커파일)
└ // 기타 파일들 ...
도커에서 클론한 DockerFile이 위치한 각각의 디렉토리에 들어가 이미지를 빌드해준다.
[이미지 빌드 명령어]
docker build -t <지정할 이미지 이름명> ./
생성한 이미지를 컨테이너에 적재 후 실행
설치된 images를 확인한다.
docker images
nginx proxy manager에서 설정한 프록시 설정에 맞게 각 이미지를 포트 설정해 컨테이너에 run한다.
docker run -p <도커 호스트 포트>:<도커 컨테이너 포트> -d --name <지정할 컨테이너명>
# -d : detached의 줄임말로, Docker 컨테이너를 백그라운드 모드로 실행하도록 지시한다.
# 해당 플래그를 사용 시 컨테이너가 실행과 터미널의 명령은 별개로 동작한다.
# -d 옵션을 사용하지 않고 컨테이너를 실행할 시 터미널이 종료되면 컨테이너도 종료될 수 있다.
# 만약 해당 run이 계속 종료되어 컨테이너 출력을 보고 싶으면 -d 없이 실행하면 된다.
일반적으로 -d 옵션을 사용해 백그라운드 모드로 실행하는 것이 보편적으로 권장되는 방식이다.
최종 결과물
[docker]
도커 호스트에는 총 4개의 컨테이너가 각자의 포트를 가지고 실행되고 있다. (백엔드, 프론트, DB, Nginx)
[백엔드 서버]
[프론트 서버]
[DB]
이것으로 NCP, 도커를 활용한 완전 날것의 배포가 완료되었다.
다음은 Github Action을 활용해 Github의 형상관리의 이점을 살리면서 버전 컨트롤이 가능한 자동화된 배포에 대해 다뤄보는 포스팅을 작성할 예정이다.
'DEV > Infrastructure' 카테고리의 다른 글
[NCP, Docker, Github Actions] 를 활용한 CI / CD 구축 (4) | 2024.01.09 |
---|---|
[트러블슈팅] Docker, Linux no space left on device 해결 방법 (1) | 2024.01.08 |
[NCP, 도커] 서버 생성부터 배포까지 (3 / 4) (0) | 2024.01.07 |
[NCP, 도커] 서버 생성부터 배포까지 (2 / 4) (0) | 2024.01.02 |
[NCP, 도커] 서버 생성부터 배포까지 (1 / 4) (1) | 2024.01.02 |