使用 Docker 部署 Node 應用 - 映象檔案尺寸的優化

劉哇勇發表於2021-06-20

前面 使用 Docker 部署 Node 應用 一文中完成了映象的建立和執行,不過生成的映象還有些粗糙,需要進一步優化。

映象的優化

通過 docker images 看到簡單的一個 node 服務端應用,超過 1G 大小,因此需要優化一下使其更加輕量。

通過如下命令檢視映象檔案裡都有什麼檔案以及分別佔用的空間大小:

$ docker history --human --format "{{.CreatedBy}}: {{.Size}}" wayou/my-app 
CMD ["node" "dist/main"]: 0B
EXPOSE map[3000/tcp:{}]: 0B
COPY . . # buildkit: 2.24MB
RUN /bin/sh -c yarn build # buildkit: 118B
RUN /bin/sh -c yarn install --frozen-lockfil…: 484MB
RUN /bin/sh -c curl -o- -L https://yarnpkg.c…: 7.59MB
COPY package.json yarn.lock ./ # buildkit: 271kB
WORKDIR /usr/src/app: 0B
/bin/sh -c #(nop)  CMD ["node"]: 0B
/bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…: 0B
/bin/sh -c #(nop) COPY file:238737301d473041…: 116B
/bin/sh -c set -ex   && for key in     6A010…: 7.76MB
/bin/sh -c #(nop)  ENV YARN_VERSION=1.22.5: 0B
/bin/sh -c ARCH= && dpkgArch="$(dpkg --print…: 100MB
/bin/sh -c #(nop)  ENV NODE_VERSION=14.17.0: 0B
/bin/sh -c groupadd --gid 1000 node   && use…: 333kB
/bin/sh -c set -ex;  apt-get update;  apt-ge…: 561MB
/bin/sh -c apt-get update && apt-get install…: 141MB
/bin/sh -c set -ex;  if ! command -v gpg > /…: 7.82MB
/bin/sh -c set -eux;  apt-get update;  apt-g…: 24.1MB
/bin/sh -c #(nop)  CMD ["bash"]: 0B
/bin/sh -c #(nop) ADD file:d9e4f6f4fc33703b7…: 101MB
 

使用更小的基礎映象

通過 node 在 Docker 市場的介面可看到,其中包含很多可使用的選擇,

  • jessie-*
  • buster-*
  • stretch-*
  • alpine-*

其中 jessie-, buster- and stretch-* 基於 Debian 系統,alpine-* 則是 Alpine Linux。一般使用 alpine 即可。

- FROM node:14
+ FROM node:14-alpine
 

再次檢視大小,減少了一半多來到 600+M

$ docker images
REPOSITORY            TAG       IMAGE ID       CREATED          SIZE
wayou/my-app   latest    f8b6ba45c68f   21 seconds ago   603MB
<none>                <none>    fe1ff8163c15   23 hours ago     1.44GB
<none>                <none>    6e0fb94473a1   3 days ago       1.44GB
 

多次編譯

詳情及原理參見 Use multi-stage builds。簡單來說,構建映象檔案包含很多步驟,中間步驟依賴使用的東西其實在最後成品中並不需要。

因此,我們可以將整個構建過程分成多步,得到一些中間結果。而這些中間結果是下一步所需要的,但生成這些中間結果的依賴卻是下一步不需要的,就可以捨棄。

優化後的 Dockerfile:

######## step 1 ########
FROM node:14-alpine AS BUILD_IMAGE

# Create app directory
WORKDIR /usr/src/app


# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
# this is for npm
# COPY package*.json ./
# but we use yarn
COPY ["package.json", "yarn.lock", "./"]

# npm comes with node
# RUN npm install
# if we use yarn, we need firstly install it
# RUN curl -o- -L https://yarnpkg.com/install.sh | bash

# equivalent for `npm ci`, see https://stackoverflow.com/a/58525708/1553656
# If you are building your code for production
# RUN npm ci --only=production
RUN yarn install --frozen-lockfile

# Bundle app source
COPY . .

RUN yarn build

######## step 2 ########

FROM node:14-alpine

WORKDIR /usr/src/app

COPY --from=BUILD_IMAGE /usr/src/app/dist ./dist
COPY --from=BUILD_IMAGE /usr/src/app/node_modules ./node_modules

EXPOSE 3000
CMD [ "node", "dist/main" ]
 

再次生成映象後檢視大小,又減小了一半,來到 300+M,

$  docker images
REPOSITORY            TAG       IMAGE ID       CREATED          SIZE
wayou/my-app   latest    c0ffda4c908a   19 seconds ago   351MB
<none>                <none>    f8b6ba45c68f   18 minutes ago   603MB
<none>                <none>    fe1ff8163c15   23 hours ago     1.44GB
 

優化 node 依賴

去掉 devDependency,更新安裝的命令:

- RUN yarn install --frozen-lockfile
+ RUN yarn install --frozen-lockfile --production=true
 

但是,如果編譯過程需要編譯 TypeScript,跑測試,跑 Lint 等,此方法就不適用,因為這些依賴均在 devDependency

凡事無絕對,雖然安裝時不能去掉,但可以在用完之後去掉。

所以在 yarn build 之後使用 npm prune 來手動刪除這些無用的依賴:

RUN yarn build

+ # remove development dependencies
+ RUN npm prune --production
 

再次檢視大小,減小了一半多:

$ docker images
REPOSITORY            TAG       IMAGE ID       CREATED          SIZE
wayou/my-app   latest    9aba2a428703   5 seconds ago    129MB
<none>                <none>    c0ffda4c908a   11 minutes ago   351MB
<none>                <none>    f8b6ba45c68f   29 minutes ago   603MB
<none>                <none>    fe1ff8163c15   23 hours ago     1.44GB
<none>                <none>    6e0fb94473a1   3 days ago       1.44GB
 

node-prune

使用 node-prune 進一步刪除依賴中的無用檔案,比如 markdown,測試檔案,TypeScript 的型別檔案等。

更新後的 Dockerfile:

FROM node:14-alpine AS BUILD_IMAGE

# install node-prune (https://github.com/tj/node-prune)
RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin

# Create app directory
WORKDIR /usr/src/app
…

RUN yarn build

# remove development dependencies
RUN npm prune --production

# run node prune
RUN /usr/local/bin/node-prune

FROM node:14-alpine

WORKDIR /usr/src/app

…
 

注意,執行上面的 Dockerfile 後會報如下錯誤:

 => ERROR [build_image 2/9] RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash              0.4s
------
 > [build_image 2/9] RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash:
#6 0.366 /bin/sh: curl: not found
#6 0.366 /bin/sh: bash: not found
 

那是因為切換到 node:12-alpine 映象後其中不再包含 curl 命令,需要單獨安裝下:

FROM node:14-alpine AS BUILD_IMAGE

# alpine doesn't come with curl
RUN apk update && apk add curl bash && rm -rf /var/cache/apk/*

# install node-prune (https://github.com/tj/node-prune)
RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin

# Create app directory
WORKDIR /usr/src/app

…
 

這次優化後的大小減少了幾 M:

$ docker images
REPOSITORY            TAG       IMAGE ID       CREATED             SIZE
wayou/my-app   latest    113ca4503190   3 minutes ago       126MB
<none>                <none>    9aba2a428703   46 minutes ago      129MB
<none>                <none>    c0ffda4c908a   57 minutes ago      351MB
<none>                <none>    f8b6ba45c68f   About an hour ago   603MB
<none>                <none>    fe1ff8163c15   24 hours ago        1.44GB
 

手動刪除

根據 Honey, I shrunk the node_modules! ...and improved app’s performance in the process 裡的介紹再手動刪除一些依賴檔案:

…
# run node prune
RUN /usr/local/bin/node-prune


# remove unused dependencies
RUN rm -rf node_modules/rxjs/src/
RUN rm -rf node_modules/rxjs/bundles/
RUN rm -rf node_modules/rxjs/_esm5/
RUN rm -rf node_modules/rxjs/_esm2015/


FROM node:14-alpine
…

 

檢視大小,減小了 3M,聊勝於無:

$ docker images
REPOSITORY            TAG       IMAGE ID       CREATED             SIZE
wayou/my-app   latest    2c54b4ee27cd   4 seconds ago       123MB
<none>                <none>    113ca4503190   9 minutes ago       126MB
<none>                <none>    9aba2a428703   52 minutes ago      129MB
<none>                <none>    c0ffda4c908a   About an hour ago   351MB
<none>                <none>    f8b6ba45c68f   About an hour ago   603MB
<none>                <none>    fe1ff8163c15   24 hours ago        1.44GB
<none>                <none>    6e0fb94473a1   3 days ago          1.44GB
 

相關資源

The text was updated successfully, but these errors were encountered:

相關文章