资讯专栏INFORMATION COLUMN

Node.js docker 镜像体积优化实践

wenhai.he / 1018人阅读

摘要:接下来我们将逐步的减少这个镜像的体积。优化生产环境镜像使用镜像大幅减小镜像体积的最简单和最快的方法是选择一个小得多的基本镜像。使用多阶段构建可以充分利用镜像的缓存,大大减少最终部署到生产环境的时间。

关注作者github每日一道面试题详解

你讨厌部署你的应用程序花费很长时间吗? 对于单个容器来说,超过gb并不是最佳实践。每次部署新版本时都要处理数十亿字节,这对我们来说并不太合适。

本文将通过Nodejs程序展示如何优化Docker镜像的几个简单步骤,使它们更小、更快、更适合生产环境。

简单的一段Node.js项目

首先写一段基于express的简单web服务器程序

// package.json
{
  "name": "docker-test",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4"
  },
  "devDependencies": {
    "eslint": "^5.16.0"
  }
}
// app.js
const express = require("express")
const app = express()

app.get("/", function(req, res){
  res.send("hello world")
})

app.listen(3000)

在根目录下新建Dockerfile并写入以下代码

# Dockerfile
FROM node

COPY . /home/app

RUN cd /home/app && npm install

WORKDIR /home/app

CMD ["npm", "start"]

执行

docker build -t myapp .

docker images

![结果](https://i.loli.net/2019/04/10...
)

可以看到这段最简单的nodejs程序有920MB,请不要这样做。接下来我们将逐步的减少这个镜像的体积。

优化docker生产环境镜像

使用Node.js Alpine 镜像

大幅减小镜像体积的最简单和最快的方法是选择一个小得多的基本镜像。Alpine是一个很小的Linux发行版,可以完成这项工作。只要选择Node.js的Alpine版本,就会有很大的改进。

FROM node:alpine

COPY . /home/app

RUN cd /home/app && npm install

WORKDIR /home/app

CMD ["npm", "start"]

build之后

可以看到整整减少了800MB,这是一个非常大的优化。

生成环境下不打包开发的依赖包

但我们还能继续优化。我们正在安装所有依赖项,即使我们最终只需要生成环境下的依赖包。如果只打包生产环境的以来不会怎么样,继续改进一下。

  FROM node:alpine

  COPY . /home/app

  RUN cd /home/app && npm install --production

  WORKDIR /home/app

  CMD ["npm", "start"]

build之后

我们又减少了6MB,因为我们目前只有一个开发依赖,可以想象在一个正常的项目中这也将是非常大的优化。

使用基础版本的 Alpine 镜像组合Nodejs

如果我们使用基础版本的 Alpine 镜像,然后自己安装Nodejs结果会怎么样呢?

  FROM alpine:latest

  RUN apk add --no-cache --update nodejs nodejs-npm

  COPY . /home/app

  RUN cd /home/app && npm install --production

  WORKDIR /home/app

  CMD ["npm", "start"]

build之后

现在只剩下了65MB,相比刚开始已经减少了10倍多。

多阶段构建

Docker镜像是分层的,Dockerfile中的每个指令都会创建一个新的镜像层,镜像层可以被复用和缓存。当Dockerfile的指令修改了,复制的文件变化了,或者构建镜像时指定的变量不同了,对应的镜像层缓存就会失效,某一层的镜像缓存失效之后,它之后的镜像层缓存都会失效。

因此我们还可以将RUN指令合并,但是需要记住的是,我们只能将变化频率一致的指令合并。

我们应该把变化最少的部分放在Dockerfile的前面,这样可以充分利用镜像缓存。

通过最小化镜像层的数量,我们可以得到更小的镜像。

上述示例中,源代码会经常变化,则每次构建镜像时都需要重新安装NPM模块,这显然不是我们希望看到的。因此我们可以先拷贝package.json,然后安装NPM模块,最后才拷贝其余的源代码。这样的话,即使源代码变化,也不需要重新安装NPM模块。

  FROM alpine AS builder
  WORKDIR /home/app
  RUN apk add --no-cache --update nodejs nodejs-npm
  COPY package.json package-lock.json ./
  RUN npm install --production

  FROM alpine
  WORKDIR /home/app
  RUN apk add --no-cache --update nodejs nodejs-npm
  COPY --from=builder /usr/src/app/node_modules ./node_modules
  COPY . .
  CMD [ "npm", "start" ]

最终的镜像只有51MB,比最开始大概减少了17倍!并且后续的 build 速度也大大提升。

每一条 FROM 指令都是一个构建阶段,多条 FROM 就是多阶段构建,虽然最后生成的镜像只能是最后一个阶段的结果,但是,能够将前置阶段中的文件拷贝到后边的阶段中,这就是多阶段构建的最大意义。

在上面的Dockerfile文件中,我们先 copy 了package.json,然后 npm install,在第二阶段构建时,我们直接 copy 了第一阶段已经下载好的node_moduls,在下一次 build 时,如果没有新增依赖,docker将使用缓存中的node_modules,这样就减少了部署的时间。

使用 docker inspect imageId命令 我们可以看到,虽然我们有多个指令,但是最终的镜像也只有5层,这就是层的共享机制。

使用多阶段构建可以充分利用Docker镜像的缓存,大大减少最终部署到生产环境的时间。

结论

在实际生产环境中,没有任何理由使用gb大小的镜像,如果你确实需要提高部署速度,并且被缓慢的CI/CD所困扰,那么多阶段构建将会是一个非常有帮助的方法

希望这篇简短的文章对考虑使用Docker进行基于Node.js的应用程序开发或部署的人有些许帮助。

查看原文

关注github每日一道面试题详解

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106064.html

相关文章

  • 最小化 Java 镜像的常用技巧

    摘要:本文将介绍精简容器镜像的必要性并以基于的应用为例描述最小化容器镜像的常用技巧。经过这一优化,最终镜像的大小为。 背景 随着容器技术的普及,越来越多的应用被容器化。人们使用容器的频率越来越高,但常常忽略一个基本但又非常重要的问题 - 容器镜像的体积。本文将介绍精简容器镜像的必要性并以基于 spring boot 的 java 应用为例描述最小化容器镜像的常用技巧。 精简容器镜像的必要性 ...

    MudOnTire 评论0 收藏0
  • 超长干货:基于Docker的DevOps CI/CD实践——来自iHealth的分享

    摘要:在猫屎氤氲的雾气里角仰望天花板,手机微信提醒这次构建成功或失败,并附带污言秽语。这时他可以开始往工位走,坐下时,微信又会提醒本次部署到成功或失败。与企业微信的集成在决定使用之前,需要知道的是,是一个高度依赖社区的项目。 前言 相信我,一切事情的发生都是赶鸭子上架,没有例外。人类所有伟大的变革都是迫不得已,可又是那么顺其自然。比如容器(docker)技术的诞生,比如箭在弦上的创业,比如野...

    Dongjie_Liu 评论0 收藏0
  • 高质量 Node.js 微服务的编写和部署

    摘要:编写代码的开发人员必须负责代码的生产部署。构建和部署链需要重大更改,以便为微服务环境提供正确的关注点分离。该对象会在之后的时被这时的回调函数会被调用,并输出。微服务部署及集成部署微服务时有一个原则一个容器中只放一个服务,可以使用编 前几天在微信群做的一次分享,整理出来分享给大家,相关代码请戳 https://github.com/Carrotzpc/docker_web_app sho...

    szysky 评论0 收藏0
  • 高质量 Node.js 微服务的编写和部署

    摘要:编写代码的开发人员必须负责代码的生产部署。构建和部署链需要重大更改,以便为微服务环境提供正确的关注点分离。该对象会在之后的时被这时的回调函数会被调用,并输出。微服务部署及集成部署微服务时有一个原则一个容器中只放一个服务,可以使用编 前几天在微信群做的一次分享,整理出来分享给大家,相关代码请戳 https://github.com/Carrotzpc/docker_web_app sho...

    Michael_Ding 评论0 收藏0
  • Jenkins + Docker 简单部署 node.js 项目

    摘要:目前官方已开始推荐使用替代之前的做为基础镜像环境。包括镜像下载速度加快,镜像安全性提高,主机之间的切换更方便,占用更少磁盘空间等。 有了前几篇的基础后,我们现在已经能 docker 篇: 构建 docker 镜像 上传私有仓库 拉取私有镜像 启动容器 jenkins 篇: 配置 pipeline 触发 pipeline 接下来就可以结合两者,用 jenkins + docker...

    princekin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<