나의 공부기록

[Docker] 05. Multi-Stage Build(멀티 스테이지 빌드) 본문

CS/Docker

[Docker] 05. Multi-Stage Build(멀티 스테이지 빌드)

나의 개발자 2025. 3. 24. 09:58

멀티 스테이지 빌드(Multi Stage Build) 

더보기

1. 경로 및 소스코드 다운로드

root@host:~/docker# mkdir multi
root@host:~/docker# cd multi

root@host:~/docker/multi# git clone https://github.com/oolralra/sb_code.git
Cloning into 'sb_code'...
remote: Enumerating objects: 436, done.
remote: Counting objects: 100% (179/179), done.
remote: Compressing objects: 100% (55/55), done.
remote: Total 436 (delta 131), reused 148 (delta 123), pack-reused 257 (from 1)
Receiving objects: 100% (436/436), 35.89 MiB | 9.12 MiB/s, done.
Resolving deltas: 100% (202/202), done.
root@host:~/docker/multi# ls
sb_code

 

2. Dockerfile 생성

root@host:~/docker/multi/sb_code# vi Dockerfile

# stage 1
FROM maven:3.6.3-openjdk-8-slim AS builder
WORKDIR /app
COPY . .
RUN mvn clean package

# stage 2
FROM openjdk:8-alpine
WORKDIR /app

# /app/target/springbootApp.jar : maven 컨테이너의 결과물
# app.jar : openjdk의 /app/app.jar로 복사
COPY --from=builder /app/target/springbootApp.jar app.jar
CMD ["java","-jar","app.jar"]

 

3. 컨테이너 생성

  • 조금 더 경량화된 이미지 생성 가능
root@host:~/docker/multi/sb_code# docker run -dp 8085:8085 --name mtest msb:1
a64c949e364575a939c1abb2311451806be6239e52f0753648954c17d70962c4

 

4. 결과 확인

 

실습 - 01

문제

첫 번째 스테이지를 ubuntu:latest로 하여 호스트에 존재하는 간단한 source-code.txt 파일을 넣은 후, 이 파일의 이름을 index.html로 바꿔서 nginx:apline를 기반으로 하는 다음 스테이지에 넣어서 배포해 보세요.

풀이

더보기

1. Dockerfile 생성

root@host:~/docker/multi2# vi Dockerfile

# AS 뒤에 오는 컨테이너의 이름은 내가 맘대로 정하면 됨
FROM ubuntu:latest AS builder
WORKDIR /
COPY . .

FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY --from=builder /source-code.txt index.html

 

2. Docker Image 생성

root@host:~/docker/multi2# docker build -t source:3 .
[+] Building 1.9s (13/13) FINISHED                 docker:default
 => [internal] load build definition from Dockerfile         0.0s
 => => transferring dockerfile: 185B                         0.0s
 => [internal] load metadata for docker.io/library/nginx:al  1.7s
 => [internal] load metadata for docker.io/library/ubuntu:l  1.8s
 => [auth] library/nginx:pull token for registry-1.docker.i  0.0s
 => [auth] library/ubuntu:pull token for registry-1.docker.  0.0s
 => [internal] load .dockerignore                            0.0s
 => => transferring context: 2B                              0.0s
 => [internal] load build context                            0.0s
 => => transferring context: 219B                            0.0s
 => CACHED [stage-1 1/3] FROM docker.io/library/nginx:alpin  0.0s
 => CACHED [builder 1/3] FROM docker.io/library/ubuntu:late  0.0s
 => [stage-1 2/3] WORKDIR /usr/share/nginx/html              0.0s
 => [builder 2/3] COPY . .                                   0.0s
 => [stage-1 3/3] COPY --from=builder /source-code.txt inde  0.0s
 => exporting to image                                       0.0s
 => => exporting layers                                      0.0s
 => => writing image sha256:43e86b3b7930e68031c316fab5766f1  0.0s
 => => naming to docker.io/library/source:3                  0.0s

 

3. 컨테이너 생성

root@host:~/docker/multi2# docker run -dp 8089:80 --name mtest source:3
1af5d6dea81100433d258e8b5bae35f8fc275fb7a022ce8a85244b1b923a7773

 

4. 결과 확인

 

Front-End 컨테이너 배포 - 멀티 스테이지 : 정적 파일 생성

더보기

1. 소스코드 다운로드

root@host:~/docker/multi# mkdir multi3
root@host:~/docker/multi# git clone https://github.com/oolralra/vue-fastapi.git
Cloning into 'vue-fastapi'...
remote: Enumerating objects: 84, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 84 (delta 3), reused 0 (delta 0), pack-reused 70 (from 1)
Unpacking objects: 100% (84/84), 128.98 KiB | 3.91 MiB/s, done.

 

2. 소스코드 파악 

  • 주석 처리 코드
    ➡️ 백엔드 컨테이너 구성 후, 리버시 프록시 구성할 때 필요하기 때문에 주석처리
root@host:~/docker/multi/multi3/vue-fastapi/frontend# vi Dockerfile 

FROM node:16 AS build
# node:16환경을 build로 리네임,별명

WORKDIR /app

ADD package*.json .
# 설치목록 복사

RUN npm install
# 설치목록 설치

ADD . .
# 소스코드 복사

RUN npm run build
# 빌드를 하면 dist폴더에 정적파일 생성

FROM nginx:alpine

COPY --from=build /app/dist /usr/share/nginx/html
# build라는 환경의 /app/dist, 즉 정적파일을 nginx의 웹루트디렉토리
로 복사
# 🌟 해당 부분 주석 처리
#COPY --from=build /app/default.conf /app/default.conf /etc/nginx/conf.d/default.conf

#nginx는 알아서 실행된다. CMD 무필요

 

💡 Dockerfile에서 굳이 패키지 목록을 미리 복사해서 설치하고 난 다음에 소스코드를 복사하는 이유

  • Layer를 잘 나누면, 기존 Layer를 재사용할 수 있어서 ➡️ Docker Image 생성 시간을 세이브할 수 있음

 

3. Docker Image 생성

root@host:~/docker/multi/multi3/vue-fastapi/frontend# docker build -t vue:1 .

 

4. 컨테이너 생성

root@host:~/docker/multi/multi3/vue-fastapi/frontend# docker run -dp 5656:80 --name web vue:1
bbbda0e1e46fe6d9d8e31faceb2c4bf76cdb2ab907890319da2debb80fe7ee27

 

5. 정적파일 확인 가능

 

 

 

Back-End 컨테이너 배포 - 멀티 스테이지 : 동적 데이터 제공

더보기

1. Dockerfile 생성

root@host:~/docker/multi/multi3/vue-fastapi/backend# vi Dockerfile

FROM python:3.10-slim
  
WORKDIR /app
# 소스코드가 복사될 디렉토리 생성

ADD requirements.txt .
# 라이브러리 목록 복사

RUN pip install --no-cache-dir -r requirements.txt
# 설치후 찌꺼기 삭제

COPY . .

CMD ["uvicorn","main:app","--host","0.0.0.0","--port","8000"]

 

2. Docker Image 생성

root@host:~/docker/multi/multi3/vue-fastapi/backend# docker build -t back:1 .

 

3. 컨테이너 생성

root@host:~/docker/multi/multi3/vue-fastapi/backend# docker run -dp 8000:8000 --name backend back:1
a927e295deaeec32ff064ebcab215ed55386a9a96fbc47bae52629b25ea067f3

 

4. 결과 확인

  • BackEnd 컨테이너 구축 확인

 

 

Front-End 컨테이너 배포 - Front & Back 연동

더보기

1. 프론트엔드 컨테이너 삭제

root@host:~/docker/multi/multi3/vue-fastapi/backend# docker rm -f web
web

 

2. 소스코드 분석

  • 리버스 프록시 파일 내용
  • --link된 컨테이너 이름으로 컨테이너를 인식함
root@host:~/docker/multi/multi3/vue-fastapi# cd frontend/
root@host:~/docker/multi/multi3/vue-fastapi/frontend# vi default.conf 

server {
    listen 80;

    location /api/ {
        proxy_pass http://backend:8000/;
        # proxy_set_header Host $host;
    }

    location / {
        root /usr/share/nginx/html; # Vue.js 정적 파일 경로
        index index.html;
        try_files $uri $uri/ /index.html;
    }
}
  • /api라는 엔드포인트로 Back-End 연동 확인
root@host:~/docker/multi/multi3/vue-fastapi/frontend# vi src/components/Products.vue

 

 3. Dockerfile 수정

  • 리버시 프록시에 대한 설정 적용을 위해 주석 해제
root@host:~/docker/multi/multi3/vue-fastapi/frontend# vi Dockerfile 

FROM node:16 AS build
# node:16환경을 build로 리네임,별명

WORKDIR /app

ADD package*.json .
# 설치목록 복사

RUN npm install
# 설치목록 설치

ADD . .
# 소스코드 복사

RUN npm run build
# 빌드를 하면 dist폴더에 정적파일 생성

FROM nginx:alpine

COPY --from=build /app/dist /usr/share/nginx/html
# build라는 환경의 /app/dist, 즉 정적파일을 nginx의 웹루트디렉토리
로 복사

# 주석해제❗
COPY --from=build /app/default.conf /etc/nginx/conf.d/default.conf

#nginx는 알아서 실행된다. CMD 무필요

 

 4. Docker Image 생성

root@host:~/docker/multi/multi3/vue-fastapi/frontend# docker build -t front:1 .

 

5. frontend 컨테이너 생성

root@host:~/docker/multi/multi3/vue-fastapi/frontend# docker run -dp 80:80 --link backend --name web front:1
c5d1b3a93d4153cf460237abcfd4593290c7ede0feef03f55e4a5cd2684102ac

 

6. Front - Back 연동 확인 

  • 백엔드 데이터를 가져오는 것을 확인 가능

 

실습 - 02

문제

백엔드는 그대로 두고, 프론트엔드를 node:16으로 띄워서 백엔드와 연동해 보세요.
프론트엔드앱이 사용하는 포트는 8080입니다.
node에서 vue앱을 동작시키기 위한 명령은 npm run serve입니다.

풀이 - 방법1️⃣

더보기

1. Dockerfile 수정

root@host:~/docker/multi/multi3/vue-fastapi/frontend1# vi Dockerfile 

FROM node:16
# node:16환경을 build로 리네임,별명

WORKDIR /app

ADD package*.json .
# 설치목록 복사

RUN npm install
# 설치목록 설치

COPY . .
# 소스코드 복사

CMD ["npm","run","serve"]

 

2. endpoint 수정

root@host:~/docker/multi/multi3/vue-fastapi/frontend1# vi src/components/Products.vue

 

3. Docker Image 생성

root@host:~/docker/multi/multi3/vue-fastapi/frontend1# docker build -t front1:3 .
[+] Building 2.1s (11/11) FINISHED                                          docker:default
 => [internal] load build definition from Dockerfile                                  0.0s
 => => transferring dockerfile: 256B                                                  0.0s
 => [internal] load metadata for docker.io/library/node:16                            2.0s
 => [auth] library/node:pull token for registry-1.docker.io                           0.0s
 => [internal] load .dockerignore                                                     0.0s
 => => transferring context: 52B                                                      0.0s
 => [1/5] FROM docker.io/library/node:16@sha256:f77a1aef2da8d83e45ec990f45df50f1a286  0.0s
 => [internal] load build context                                                     0.0s
 => => transferring context: 2.58kB                                                   0.0s
 => CACHED [2/5] WORKDIR /app                                                         0.0s
 => CACHED [3/5] ADD package*.json .                                                  0.0s
 => CACHED [4/5] RUN npm install                                                      0.0s
 => [5/5] COPY . .                                                                    0.0s
 => exporting to image                                                                0.0s
 => => exporting layers                                                               0.0s
 => => writing image sha256:92de193c839a5e6252e62019d97ecabfb7457fa5a69c8d3d6c03b9f2  0.0s
 => => naming to docker.io/library/front1:3                                           0.0s

 

4. 컨테이너 생성

root@host:~/docker/multi/multi3/vue-fastapi/frontend1# docker run -dp 8084:8080 --name front1 front1:3
ae1009d7fab1133f6a26c4266d065548e2aec2d933f46ad16d1c887f9eb80760

 

5. 결과 확인 

풀이 - 방법2️⃣

더보기
  • 리버스프록시를 구성
    ➡️ vue.config.js 생
const { defineConfig } = require('@vue/cli-service');
  
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    proxy: {
      '/api': {
        target: 'http://backend:8000',  // 백엔드 컨테이너로 요청 전달
        changeOrigin: true,
        pathRewrite: { '^/api': '' }
      }
    }
  }
});

 

👉 결론

  • 풀이 - 방법1️⃣의 경우, Back-End에 직접 접속해서 찾아가는 방법이라 적합하지 않음
    👉 Reverse Proxy로 접속하는 방법이 적절🌟 

'CS > Docker' 카테고리의 다른 글

[Docker] 07. Docker Swarm  (0) 2025.03.25
[Docker] 06. Docker Compose  (0) 2025.03.24
[Docker] 04. App 배포 - Java & NodeJS & Python  (0) 2025.03.20
[Docker]03. Container Registry  (0) 2025.03.20
[Docker]02. Docker Image - Dockerfile 명령어  (1) 2025.03.18