乐趣区

关于docker:Part-1面向-Javascript-开发人员的-Docker-简介基于-Nodejs

Docker开源的利用容器引擎,如果你是从事后端的开发者,置信对这门技术应该是理解或是相熟,而对于很多前端开发者,兴许只是停留在听过的阶段上,甚至不晓得是啥?或是会认为这是后端的技术,我不须要晓得,比如说我,还真的不晓得是什么,然而如果想成为一名资深前端,这部分空缺是须要填补上的。咸鱼也要有幻想嘛,兴许哪天能够跃龙门呢!

本文会通过构建带有 web 前端代码和 mongoDB 数据库的全栈 node.js 应用程序登程,来进一步理解 Docker 以及它的用处。

什么是 Docker

Docker 是一个开源的利用容器引擎,基于 Go 语言 并听从 Apache2.0 协定开源。Docker 能够让开发者打包他们的利用以及依赖包到一个轻量级、可移植的容器中,而后公布到任何风行的 Linux、Window 机器上,也能够实现虚拟化。

听不懂,是否艰深解释 Docker 前世今生?

2010 年,几个搞 IT 的年轻人,在美国旧金山成立了一家名叫 dotCloud(搞容器技术) 的公司。后果保持不上来,于是开源,后果火了,火了得从新起个牛逼的名字呀,于是 Docker 就呈现了。

Docker呈现前,如何模仿一个 互相隔离 的零碎环境?答案就是 虚拟机 ,大家应该都不生疏,很多开发者电脑外面都会装VMWare,通过它,咱们能够变出好几台 子电脑 ,一个装window11、一个装CentOS,装置上我喜爱的QQ 微信 等软件,多个 子电脑相互隔离、互不影响,美滋滋!然而动不动就几个 G、几十个 G,磁盘吃不消呀,而且还启动慢。

后面说到了 Docker 呈现前,虚拟机 在做环境隔离上是业界的网红,然而弊病是 ,而 Docker 容器技术,其实也是一种 虚拟化技术 , 而且 轻、快、一体化 ,只须要 MB 级甚至 KB 级,不像 虚拟机 ,须要模仿一个操作系统进去,Docker 只须要虚构一个小规模的环境(相似“沙箱”)。

不行,我要看数据比对,我才信,安顿 ….

Docker 外围概念

上一大节,咱们理解了Docker 是一种容器虚拟化技术,更轻、更快、更容易一体化,接下来咱们疾速理解一下它的外围概念后,再去进入咱们明天的主题,在编写代码才更好了解。

Docker的三大外围概念:

  • 镜像(Image)
  • 容器(Container)
  • 仓库(Repository)

以上关系图能反映出三者之间的关系,此处须要留神的是,咱们说 Docker 是一种容器技术,然而Docker 自身并不是容器,它是创立容器的工具,是利用容器引擎。

镜像,也就是 Docker 镜像,是一个非凡的文件系统。它除了提供容器运行时所需的程序、库、资源、配置等文件外,还蕴含了一些为运行时筹备的一些配置参数(例如环境变量),同时镜像不蕴含任何动态数据。

咱们能够有很多 镜像 ,咱们想存起来,而后能够到任何中央去应用它创立 容器 环境, 那么就须要 仓库 来存储,也就是Docker 仓库

有怎么一个 仓库 存在,那么所有人都能够往里面存 镜像 么?不是的,如果寄存了个有问题的 镜像 ,那创立容器时候不就挂了么?所以须要有个负责对Docker 镜像 进行治理的角色,就是 Docker Registry 服务(相似仓库管理员) 了。官网也提供了公共Registry 服务,就是Docker Hub(有点像咱们的 npm 市场), 外面寄存着很多高质量的官网镜像。

同时咱们还能够通过 Dockfile 文件 来定制咱们的镜像,后续有介绍

通过下面的介绍,置信大家对 Docker 应该也有了个大略的意识了,这里提供一些罕用的 Docker 命令,

# 容器
$ docker run  // 创立并启动容器
$ docker start // 启动容器
$ docker ps // 查看容器
$ docker stop // 终止容器
$ docker restart // 查看容器
$ docker attach // 进入容器
$ docker exec // 查看容器
$ docker export // 导出容器
$ docker import // 导入容器快照
$ docker rm // 删除容器
$ docker log // 查看日志

# 镜像
$ docker search // 检索镜像
$ docker pull // 获取镜像
$ docker images // 列出镜像
$ docker image ls // 列出镜像
$ docker rmi // 删除镜像
$ docker image rm // 删除镜像
$ docker save // 导出镜像
$ docker load // 导入镜像

# Dockfile 定制镜像以及罕用指令

$ docker build // 构建镜像
$ docker run // 运行镜像

COPY // 复制文件
ADD // 高级复制
CMD // 容器启动指令
ENV // 环境变量
EXPOSE // 裸露接口


# 服务
$ docker -v // 查看 docker 的简要信息
$ docker -version // 查看 docker 版本的简详细信息
$ systemctl start docker // 启动 docker
$ systemctl stop docker // 敞开 docker
$ systemctl enable docker // 设置开机启动
$ service docker restart // 重启 docker 服务
$ service docker stop // 敞开 docker 服务

创立 Hello-World 容器

首先须要下载 Docker 下载地址,我下载的是Window Docker Desktop,接下来查看版本信息是否下载胜利,

我下载的是 20.10.11 版本

而后拉取 Docker Hub 的官网 hello-world 镜像,

创立并执行容器,

这样咱们第一个容器就创立进去了,能够用用下面提到的命令行 docker image ls / docker image prune 来查看或是删除没有用(进行运行容器时,它不会被删除,会帮忙下次下载安装速度放慢)的镜像,更多命令大家能够去试试!

创立 Node 程序

接下来咱们创立个 Node 程序,在前面教程介绍须要应用到,具体代码详情就不去做介绍了,能够查看以下server.js 和 package.json

const express = require("express");
const app = express();
const port = 8080;

app.get("/", async (req, res) => {res.setHeader("Content-Type", "text/html");
  res.status(200);
  res.send("<h1> 你好呀!前端晚间课 </h1>");
});

app.listen(port, () => {console.log(`Example app listening at http://localhost:${port}`);
});
{
  "name": "docker-example",
  "version": "1.0.0",
  "description": "","main":"server.js","scripts": {"start":"nodemon server.js"},"author":" 前端晚间课 ","license":"ISC","dependencies": {"express":"^4.17.2"},"devDependencies": {"nodemon":"^2.0.15"}
}

运行npm run start,胜利跑起来了 …

Node 版本不同的困扰

针对下面的 server.js 文件,咱们增加上面的代码:

 // ...
 const myPromise = new Promise((resolve, reject) => {setTimeout(() => {resolve("good");
  }, 300);
  reject("bad");
});

myPromise.then(() => {console.log("this will never run");
});

而后别离在 Node < 15Node >= 15运行,后果会失去两个不同的后果,

Node < 15

(node:764) UnhandledPromiseRejectionWarning: something happened
(Use `node --trace-warnings ...` to show where the warning was created)
(node:764) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:764) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Node >= 15

node:internal/process/promises:218
          triggerUncaughtException(err, true /* fromPromise */);
          ^

[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "recipe 
could not be generated".] {code: 'ERR_UNHANDLED_REJECTION'}

会发现两个版本的执行后果会不统一,高版本(Node >= 15)会间接导致程序解体(ERR_UNHANDLED_REJECTION),这是未解决的被回绝 Promise 的谬误。

当初假如出于某种原因,此应用程序必须在 Node v14 或更早版本上运行能力工作(疏忽用 try...catch)。团队中的每个开发人员都必须筹备好在该环境中开发和运行,但咱们公司还有一个新的应用程序,要在 Node v17 上运行!

那这时应该如何去解决这个问题?答案:Docker

创立 Dockerfile

上一大节,咱们介绍了 两个不同版本的 Node引起的 未解决的被回绝 Promise的问题,咱们介绍如何应用 Docker 去解决这个问题,其实也很简略,就是须要一个 Node < 15 的运行环境来保障咱们的程序不会解体,
咱们在 Docker Hub 能够搜寻到对于的 Node 的镜像,而且还有很多版本信息可选。

当然咱们没必要间接应用 docker pull node 来拉取 node 镜像,后面咱们有提过Dockerfile 能够用来自定义定制镜像,它主动判断以后机器是否存在 Node 镜像,没有的话再主动去Docker Hub 拉取,看一下咱们的 Dockerfile 文件:

# 首先先抉择你须要的镜像,运行在 alpine 的 node 版本是当下最风行的
FROM node:14-alpine3.12

# 工作目录
# 这是您你将在容器内的地位
WORKDIR /usr/src/app

# 通配符用于确保 package.json 和 package-lock.json 都被复制
# COPY 源目录 容器的工作目录
COPY package*.json ./

# 装置利用依赖
RUN npm install

# 如果你正在构建用于生产的代码
# RUN npm ci --only=production

# 捆绑应用程序源
COPY . .

# 配置这个端口能够从容器内部拜访
# 浏览器向 Node 应用程序发送 HTTP 申请所必须的
EXPOSE 8080

# CMD 在 docker run 时运行
# 就是执行 shell npm run start
CMD ["npm", "run", "start"]

ok,咱们的 Dockerfile 文件曾经创立胜利,对 Dockerfile 指令 在下面代码中正文也有做简略的介绍,更多具体指令用法能够到谷歌搜寻,但你可能会好奇下面的配置文件,为什么 COPY 须要执行两次,最初的 COPY . . 不是复制了整个目录,为啥下面还须要复制package*.json?

Docker 的层和缓存

COPY两次是有必要的,因为 Docker 领有 layers(层的个性),每执行一条指令都会基于上一次指令创立的图层根底上再创立一层图层,创立的图层会被缓存,只有产生扭转时才会再次从新创立,咱们再回过头来看Dockerfile 文件。

COPY package*.json ./ 咱们创立了一个基于该文件内容的图层,而后再运行 npm install,这意味着除非咱们更改 package.json,否则下次咱们构建 Docker 时将应用npm install 曾经运行的缓存层,咱们不用每次运行时都装置所有依赖项 docker build。这将为咱们节俭大量工夫。

COPY . .会查看咱们我的项目目录中的每个文件,因而该层将在任何文件更改时重建(除了package*.json)。这正是咱们想要的。

构建利用容器

咱们再增加个 .dockerignore 文件,相似咱们的.gitignore,因为咱们不想复制这些文件呀。

node_modules
npm-debug.log

所有准备就绪,咱们开始构建属于本人的镜像,

# 以以后我的项目目录为源目录,给镜像名个名叫做 qianduanwanjianke
$ docker build . -t qianduanwanjianke

再查看咱们创立的镜像存不存在?

创立镜像后,咱们当初筹备从镜像构建一个容器来运行咱们的应用程序:

# --name 咱们给容器名了个名,叫做 qianduanwanjianke-container
# - p 标记将端口从咱们的主机(咱们的计算机)环境 3001 端口映射到容器环境的 8080 端口,当然也能够是 8080:8080。docker run -p 3001:8080 --name qianduanwanjianke-container qianduanwanjianke

功败垂成,咱们拜访一下 http://localhost:3001/ 看看是否胜利,

结语

到这里,咱们通过一个 Node 应用程序 切入来介绍 Docker,创立了咱们的第一个自定义 Docker 镜像和容器,并在其中运行咱们的应用程序!这是面向 Javascript 开发人员的 Docker 简介,由Node 程序切入,文章内容对于相熟 Docker 的后端开发者来说可能是很相当容易的,对于咱们前端开发者来说应该是很好的入门教程。因为篇幅曾经很长了,我决定把 Docker Volume(“ 连贯 ” 起容器外部的程序正本与我的项目目录中的正本,更新同步) 以及 引入数据库,剖析数据库托管服务器不同,如何创立拆散、Docker Compose等内容搁置到下篇内容再做介绍。

过程中踩的坑

1、拉取镜像时报错,报错信息:error during connect: This error may indicate that the docker daemon is not running...

解决办法:

# 在 Powershell 晋升拜访权限解决此问题
cd "C:\Program Files\Docker\Docker"
./DockerCli.exe -SwitchDaemon

2、创立利用容器的时候,执行docker build . -t my-node-app, 报错信息:no matching manifest for windows/amd64 10.0.18363 in the manifest list entries

解决办法:
关上 Docker Devlop 软件,settiong -> Docker Engine,将 experimental 设置为 true,重启 Docker

退出移动版