type
status
date
slug
summary
tags
category
icon
password
Edited
Apr 12, 2023 02:15 PM
Created
Apr 12, 2023 02:10 PM
概念
Docker 优势
老规矩,在学习
Docker
使用之前,我们先来看下为什么要学习 Docker
?第一优势:跨平台,由于
Docker
的镜像能够提供除了系统内核之外完成的运行环境,所以能在任何系统中都能提供一致的运行环境,这样就不需要考虑不同系统中间兼容性的问题,也就不存在虚拟机在各系统中间的配置不同的情况。第二优势:就是借助于跨平台的特性,
Docker
可以将很多配置复杂的服务端中间件打包成基础镜像提供给开发使用。这样无疑能够大大降低配置成本,开发只需要知道常规的 Docker
相关的命令或者直接运行提供的容器编排脚本就可以搭建出需要使用的服务端环境。同时公共的镜像仓库上已经有很多这种基础镜像,例如 Mysql
、Redis
、Node
等等,按需提取即可。第三优势:这点对于运维同学比较好操作,
Docker
能提供快速迁移以及配合 k8s
,能够快速的伸缩副本,减少运维的工作成本与负担。Docker
也是借助上述优势将非常迅速的发展自身生态,特别是近两年熟练使用 Docker
也已经频繁的出现在高级前端的 JD 中了。虽然说了是跨平台,但结合自身的体验来看,Docker 在 windows 11 中的兼容性做的并不好(如果有熟悉如果在 Windows 使用 Docker 的同学可以联系我沟通一下),此外由于文件内存处理格式的问题,在数据持久化中,直接使用 volumes 对大量文件读取性能有影响,这一点在之前字节的 Dev Better 沙龙中的前端工程化中也聊过(all in docker 方案),有兴趣的同学也可以联系我沟通。
Docker 基本知识
接下来在正式使用
Docker
之前,先来掌握两个必备的概念:镜像(Image
)与容器(Container
)。何为镜像?
如果喜欢折腾的同学应该自己装过
Windows
系统,我们在制作启动盘的时候,会将 Windows
某个版本的 ISO
镜像下载下来再制作系统盘,同理 Docker
的镜像也可以这么理解,它是包含了一个完整功能的最小系统。Docker Image
作为文件联合系统(Union FS
),它由一层层 layer
堆叠组成,这意味着我们可以基于基础镜像来构建各个业务的或者更高级的中间件镜像。同时借助镜像堆叠的功能,在拉取新的镜像时,避免拉取本地已存在的基础镜像,节约资源且提高效率。何为容器?
由上述安装
Windows
的例子来看,容器可以看成已经成功装机的电脑,可以运行某些命令、执行程序等操作。一般只需要制作一个启动盘就可以安装多台电脑,
Docker Image
构建完毕之后,与启动盘一样可以直接基于 Image
启动多个不同的容器,而且他们之间是相互隔离的。简单的概念就如上所说,能够大概了解基本的特性即可满足常规使用,更加细节的原理性的问题则需要各位同学自己去探索。
Docker 的安装和使用
Docker 是一款非常流行的开源容器化平台,它可以让你将应用程序和其依赖的库、框架等打包成一个容器,并在不同的环境中运行,从而实现应用程序的快速部署和升级。以下是 Docker 的安装和使用方法:
安装 Docker
- 首先,你需要在你的计算机上安装 Docker。Docker 官网提供了各种不同的安装方式,包括 Windows、Mac 和 Linux 等系统。你可以在 Docker 官网上找到相应的安装方法。在这里,我们以在 Linux 上安装 Docker 为例。
- 打开终端并输入以下命令来下载 Docker 安装包:
- 执行以下命令来运行安装脚本:
- 等待 Docker 安装完成即可。
使用 Docker
- 安装完成后,你可以使用以下命令来启动 Docker:
- 使用以下命令来创建一个新的容器:
其中,
<image name>
是你想要创建的容器的镜像名称。- 使用以下命令来列出所有正在运行的容器:
- 使用以下命令来停止一个正在运行的容器:
其中,
<container id>
是你想要停止的容器的 ID。- 使用以下命令来删除一个容器:
其中,
<container id>
是你想要删除的容器的 ID。总之,Docker 是一款非常强大的容器化平台,它可以让你轻松地管理和部署应用程序。
使用 Docker-Compose
Docker-compose 是 Docker 官方推出的一个工具,它可以让你使用 YAML 文件来定义和运行多个容器,从而实现更复杂的应用程序部署。以下是 Docker-compose 的使用方法:
- 首先,你需要在你的计算机上安装 Docker-compose。你可以在 Docker-compose 的官网上找到相应的安装方法。
- 创建一个新的 YAML 文件,并使用以下格式来定义你的容器:
其中,
<service1>
和 <service2>
是你想要创建的容器的名称,<image1>
和 <image2>
是容器的镜像名称,<port1>
是容器要使用的端口号,<env1>
是容器的环境变量名称,<value1>
是环境变量的值,<volume1>
是容器要使用的卷名称。- 使用以下命令来启动你的容器:
- 使用以下命令来停止你的容器:
总之,Docker-compose 是一个非常有用的工具,它可以让你轻松地定义和运行多个容器,从而实现更复杂的应用程序部署。
查找镜像
Dockerfile
期间也使用了
Dockerfile
构建了一个 Vue
的 dev
镜像,在一起学习了 Docker
的基础知识之后,也就到需要实际运用 Docker
阶段了。在项目正式发布之前,我们需要借助
Dockerfile
来构建各种适配的镜像,然后再将镜像推送到制品库,再通过其他手段发布,例如使用 k8s
来发布。本章将着重讲解如何在实际项目中使用
Dockerfile
上一篇也有不少同学照着文章的例子进行了实操也咨询了一些问题,文末也会将之前同学提出的一些问题列举出来一起探讨。
Dockerfile
上述脚本是上一章的
Dockerfile
的例子,接下来我们将对每一行命令进行讲解:- FROM
为这次构建流程指定基础镜像,同时必须是第一条指令,因为后续所有的操作都必须基于基础镜像之上,每一行运行系统的命令都必须是基础镜像中所含有的,所以当基础镜像中不包含
Node
环境的话,后续的命令也无法正常使用 Node
相关的 API
,除非你在后续调用 Node API
之前已经安装过。同时镜像名称的规则之前也提过,为
<imageName>:<tag>
,当未填写 tag
的时候,默认为需要拉取式最新的 tag
版本。- WORKDIR
此命令是设置当前工作目录,如果目录不存在,则会直接创建,设置完毕之后,所有操作的路径都将处于当前指定路径之下。
- COPY
顾名思义,这是一个复制的命令,但是
COPY
只能复制宿主机本地的文件。- ADD
与
COPY
的功能类似但 ADD
的功能更加强大,具备跨服务器复制内容的能力,类似于 Linux
命令中的 scp
,如果将来源换成 url
的话,那么又会变成 wegt
的命令,整体可操作性更高,后续的章节我们可以尝试一下。此外在执行 <源文件\> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>,由于解压可能会导致缓存失效,所以仅仅是复制文件的需求,建议使用 COPY。
- EXPOSE
声明当前容器要访问的网络端口,但是仅仅是声明要暴露的端口,启动的时候并不会直接能访问到,需要在启动容器的时候添加
-p
参数来挂载宿主端口。- RUN
在构建镜像过程中执行的命令,需要执行的命令格式如下:
第一种格式是直接执行
shell
命令,第二种则是启动文件(或者全局命令模式,例如 npm
) \+ 传入参数的格式,如果 shell
命令不太容易满足功能的时候,可以使用第二种,直接写好处理的脚本,加传入参数即可。- CMD
在容器启动时候执行的命令,执行命令的语法如下:
启动命令与
RUN
也是类似的,分别是直接执行 shell
与执行可执行命令或文件。RUN & CMD
这两个都是执行命令,但是
RUN
是构建镜像时就运行的命令,CMD
是容器启动时执行的命令,在构建时并不运行,所以在编写 Docker File
的时候不要将两者搞混。此外
Docker
容器默认会把容器内部第一个进程,也就是 pid=1 的程序作为 Docker
容器是否正在运行的依据,所以为了 Docker 容器常驻,CMD
执行的 Node
服务(其他服务也是一样)的时候一定要前台启动,否则使用后台启动的话,容器启动后执行完命令就会自动退出。在上一章提到过,Docker Image 作为文件联合系统(Union FS),由一层层 layer 堆叠组成,Docker File 进行的每一次指令操作会多添加一层 layer,所以在一些频繁的操作,例如 RUN、ADD 执行多个命令的时候,尽可能的合并命令,减少 layer 层级,这样可以避免镜像臃肿同时减少构建时间。但这个也是相对而言,如果多个命令需要处理的功能关系不大,为了后期维护方便还是建议分开执行,在可维护性与效率这两者关系平衡好就行。
- ENTRYPOINT
ENTRYPOINT
是指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有其他传入值作为该命令的参数,单独使用时与 CMD
脚本使用方式一致,执行命令脚本如下:当与
CMD
命令混合使用时,CMD
的功能就不再是直接运行脚本,而是将 CMD
的内容作为参数传给 ENTRYPOINT
,所以在上述示例的 dockerfole 中大家会发现两种混合的写法:ENTRYPOINT
的参数是 NPM RUN
,而 CMD
的参数是 serve
。当出现多次
ENTRYPOINT
脚本时,最后一条会覆盖上述所有的脚本,即只有最后一条才会真实生效,同时可以在启动容器的时候使用 --entrypoint
参数来覆盖。虽然 ENTRYPOINT 与 CMD 两者都是有可以当作容器启动命令来看待,但是还是有不少的区别,这块的内容展开说说会有比较多的篇幅,最开始我们对 Docker 的学习还是先从熟悉命令与使用方式入手,比较深入或者难懂的内容后期会放在加餐里面进行详细的拓展。
dockerignore
除了
Dockerfile
之外,正常使用中我们还可能接触到 .dockerignore
文件,一般存放在 docker
构建上下文的根目录(也就是项目根目录),用来排除不需要上传到 docker
服务端的文件或目录,它存在的价值有:- 构建镜像时能避免不需要的大文件上传到服务端,从而拖慢构建的速度、网络带宽的消耗;
- 可以避免构建镜像时将一些敏感文件及其他不需要的文件打包到镜像中,从而提高镜像的安全性;
.dockerignore
的使用规则基本上与 .gitignore
保持一致就不过多讲解了,常规项目可以直接使用如下规则:添加以上配置之后,后续执行
COPY
与 ADD
处理本地文件移动的时候就会根据以上规则忽略对应的文件了。静态业务部署
市面上大部分的前端同学都是开发常规的
Web
项目,假设我们需要部署一个 Web
项目的话,也有不少的选择(例如 oss + cdn
模式),这里我们选择使用 Docker
来部署一个静态服务。在上一章节,我们构建了一个
Docker
镜像来启动开发环境的 Vue
项目,真实项目中当然不会有 dev-server
这种来访问静态资源,所以我们需要一个像 Nginx
的静态服务器来帮助访问静态资源,但是我们既然使用了 Node
环境如果要使用 Nginx
的话,就需要切换基础镜像或者安装一个 Nginx
,所以本示例采用 anywhere
库来作为静态服务。anywhere 本身就是前台启动服务,如果想使用 Nginx 来作为静态服务器的话,记得添加 daemon off 参数来强制 Nginx 进程前台启动。
那么之前的脚本就要从启动
dev-server
换成 build
脚本:接下来使用以下脚本即可打包出一个前端静态资源以及配置了静态服务的镜像
使用一下脚本即可启动此镜像:
启动完脚本之后可以直接访问 http://localhost/ 访问到我们的生产服务了。
注意再上一步启动容器的时候,我们将宿主机的映射端口改为了 80 端口,所以再访问项目地址就不再需要带端口号了,如果映射失败有可能是电脑用户权限没办法开启 80 端口。
QA
为什么要先复制 package.json
而不是跟项目一起复制进来构建
文章中也反复提及了
Docker
是文件联合系统的架构,在 Dockerfile
里面每一行命令都会生成一层 Layer
,如果有相同的 Layer
的话,Docker
会直接复用已有,所以当这一次 package.json
无变化时,就不默认使用之前的 Layer
,并不会重新拉去依赖。而如果是跟着项目一起复制进来,由于大概率存在开发文件的差异性,必然会重新更新依赖。所以我们也是借助
Docker
的这一特性来做了层缓存操作来提高构建效率。如上图所示,在二次构建的时候,合理利用了这一规则,使用缓存跳过了依赖安装的过程。
参考链接
- 作者:JinSo
- 链接:https://jinso365.top/article/docker-basic
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。