Multi-stage Dockerfile for React + Vite + TS
Make the React image smaller with a multi-stage Dockerfile: a build stage and a final stage.
- series
- Docker Workshop
- episode
- ตอนที่ 6 จาก 17
- read
- อ่าน 5 นาที
Problems
เราได้ build react เรียบร้อย มี docker image แล้ว สามารถรัน docker image เป็น docker container ได้แล้ว
เราจะมาลอง sh เข้าไปดูภายใน container กัน
รันก่อน
❯ docker run -p 3001:3001 --name react -d react7fbf3951b9f39f9ff72695bc1f9e48d14575bae4bee7434054ccb6cce9792b38แล้วสั่ง exec เข้าไปที่ container
docker exec -it react /bin/bashแล้วสั่ง ls -al เพื่อดูรายการไฟล์ที่มี
❯ docker exec -it react /bin/bashroot@7fbf3951b9f3:/app# ls -altotal 156drwxr-xr-x 1 root root 32 Jun 30 05:15 .drwxr-xr-x 1 root root 14 Jun 30 05:27 ..drwxr-xr-x 1 root root 66 Jun 30 05:15 .direnv-rw-r--r-- 1 root root 253 Jun 30 05:12 .dockerignore-rw-r--r-- 1 root root 13 Jun 30 03:25 .envrc-rw-r--r-- 1 root root 436 Jun 30 03:25 .eslintrc.cjs-rw-r--r-- 1 root root 253 Jun 30 03:25 .gitignore-rw-r--r-- 1 root root 221 Jun 30 04:43 Dockerfile-rw-r--r-- 1 root root 1300 Jun 30 03:25 README.md-rwxrwxrwx 1 root root 94823 Jun 30 03:33 bun.lockbdrwxr-xr-x 1 root root 48 Jun 30 05:15 dist-rw-r--r-- 1 root root 1058 Jun 30 03:25 flake.lock-rw-r--r-- 1 root root 1249 Jun 30 03:25 flake.nix-rw-r--r-- 1 root root 138 Jun 30 03:25 global.d.ts-rw-r--r-- 1 root root 366 Jun 30 03:25 index.htmldrwxr-xr-x 1 root root 8 Jun 30 05:15 node_modules-rw-r--r-- 1 root root 743 Jun 30 03:25 package.jsondrwxr-xr-x 1 root root 16 Jun 30 03:25 publicdrwxr-xr-x 1 root root 100 Jun 30 03:25 src-rw-r--r-- 1 root root 677 Jun 30 03:25 tsconfig.app.json-rw-r--r-- 1 root root 139 Jun 30 03:25 tsconfig.json-rw-r--r-- 1 root root 325 Jun 30 03:25 tsconfig.node.json-rw-r--r-- 1 root root 163 Jun 30 03:25 vite.config.tsจะเห็นว่ามี ไฟล์ที่เราไม่ได้ใช้ใน production เต็มไปหมดเลย ไฟล์ที่เกินมาเหล่านี้ทำให้ docker image ของเรามีขนาดใหญ่ด้วย
เราจะแก้ปัญหานี้ด้วยการทำ Multi-stage นั่นเอง
Multi-stage Dockerfile
มาดูตัวอย่างกันเลย
# BuildFROM oven/bun:alpine as build
WORKDIR /app
COPY package.json .COPY bun.lockb .
RUN bun install
COPY . .
RUN bun run build
# FinalFROM oven/bun:alpine
WORKDIR /app
COPY --from=build /app/dist ./distCOPY ./public ./public
RUN bun install --global serve
ENV NODE_ENV production
CMD ["bun","x","serve","-l","3001","./dist"]
EXPOSE 3001จากตัวอย่างจะแบ่งเป็น 2 stages
- build
- final
1. Build stage
-
FROM oven/bun:alpine as buildอันนี้คือเปลี่ยนมาใช้ bun image ที่มีขนาดเล็กลง ถ้าจะให้เล็กกว่านี้ก็ต้องใช้ distroless ละ ส่วนas buildคือกำหนดชื่อของ stage นี้ ให้ชื่อว่าbuildส่วนนี้จะจบแค่
RUN bun run buildสุดท้ายเราจะได้ folderdistมา และจบ stage แค่นี้เลย
2. Final stage
ที่ stage นี้เราจะ copy file ที่จำเป็นในการรัน React app มาใช้
COPY --from=build /app/dist ./distจะเห็นว่ามีการเพิ่ม flag--fromมาด้วย เป็นการบอกว่าจะให้ copy file จาก stagebuildมาใช้ที่ stage นี้ ซึ่งจะไป copy ที่/app/distก็คือไปที่ WORKDIR ใน stage build แล้ว copy folderdistมาไว้ที่./สุดท้ายเราจะได้./distเหมือนเดิม
build docker image
docker build -t react .❯ docker build -t react .[+] Building 7.1s (16/16) FINISHED docker:orbstack => [internal] load build definition from Dockerfile 0.1s => => transferring dockerfile: 394B 0.0s => [internal] load metadata for docker.io/oven/bun:alpin 2.0s => [auth] oven/bun:pull token for registry-1.docker.io 0.0s => [internal] load .dockerignore 0.1s => => transferring context: 295B 0.0s => [internal] load build context 0.0s => => transferring context: 1.39kB 0.0s => [build 1/7] FROM docker.io/oven/bun:alpine@sha256:fb5 0.0s => CACHED [build 2/7] WORKDIR /app 0.0s => CACHED [build 3/7] COPY package.json . 0.0s => CACHED [build 4/7] COPY bun.lockb . 0.0s => CACHED [build 5/7] RUN bun install 0.0s => [build 6/7] COPY . . 0.2s => [build 7/7] RUN bun run build 1.7s => CACHED [stage-1 3/5] COPY --from=build /app/dist ./di 0.0s => [stage-1 4/5] COPY ./public ./public 0.1s => [stage-1 5/5] RUN bun install --global serve 2.4s => exporting to image 0.2s => => exporting layers 0.1s => => writing image sha256:d5ac5bab7d45f1b1fa5a74d44adb9 0.0s => => naming to docker.io/library/react 0.0sRun container
docker run -p 3001:3001 --name react -d react❯ docker run -p 3001:3001 --name react -d react1a2731d0fcc7bf4dbb41956577c6db0b3874f55582091b60e4f380e210e72aaaExec
ลอง sh เข้ามาดูก็จะเห็นว่า มี ไฟล์นิดเดียว เท่าที่จำเป็นต้องใช้
docker exec -it react /bin/sh❯ docker exec -it react /bin/sh/app # lsdist public/app # ls -altotal 0drwxr-xr-x 1 root root 12 Jun 30 07:53 .drwxr-xr-x 1 root root 14 Jun 30 07:55 ..drwxr-xr-x 1 root root 48 Jun 30 07:46 distdrwxr-xr-x 1 root root 16 Jun 30 03:25 public