官方地址:https://docs.docker.com/get-started/02_our_app/
Docker作为一个容器,可以在容器中安装各种应用
获取官方提供的应用示例
官方地址:https://github.com/docker/getting-started/tree/master/app
建立应用容器镜像
为了创建一个应用,需要使用一个
一个Dockerfile只是一个基于文本的指令脚本,用于创建容器映像。如果之前已经创建过Dockerfile,可能已经见识过了Dockerfile的暇疵
构建docker镜像
首先会下载相应的镜像依赖,然后开始构建,然后执行yarn指令,下载应用所需要的依赖
参数解释 -t 和 .
- -t 会标记我们的镜像,可以当作起名字,这个镜像的名字就是 getting-started,也可以理解为这个镜像的引用
- . 在这条构建docker指令的最后,通知Docker在当前目录查找Dockerfile
启动应用容器
构建步骤结束后,我们已经有了一个镜像,通过 docker run 指令启动容器
参数解释 -d 和 -p
- -d,Run container in background and print container ID,后台运行并打印容器的ID
- -p,Publish a container's port(s) to the host,给主机暴露该容器的端口
当前指令的意思是把容器的3000端口映射到主机的3000端口,没有这个映射我们不能直接管理应用
需要查看容器列表然后停下指定容器
使用docker stop指令
删除该容器
也可以使用强制删除指令
把新的应用覆盖上去,然后重新启动即可
现在我们已经创建好了镜像,可以共享镜像了,必须使用一个Docker注册处才能实现共享,默认的注册处是Docker Hub,我们所有使用的镜像都来源于注册处
一个Docker ID允许访问Docker Hub,这是世界上最大的容器镜像的库和社区
创建Docker ID的链接:https://hub.docker.com/signup
创建仓库
在推送一个镜像前,我们首先需要在Docker Hub上创建一个仓库
- 注册并使用Docker Hub分享镜像
- 在Docker Hub登录
- 点击创建仓库按钮
- 使用getting-started给仓库设置名字,保证可见性为 Public

推送镜像
在命令行中,尝试运行在Docker Hub上看到的push命令。请注意,您的命令将使用您的命名空间,而不是“docker”
为什么失败了?push命令正在查找名为docker/getting started的图像,但没有找到。如果运行docker image ls,也不会看到
要解决这个问题,我们需要“标记”我们已经建立的现有镜像,以给它起另一个名字
使用一下指令在Docker Hub上登录
使用docker tag命令为getting-start赋予一个新名称,一定要把你的用户名换成你的Docker ID
现在再试一次你的推送命令
如果要从Docker Hub复制值,可以删除标记名部分,因为我们没有向图像名称添加标记。如果不指定标记,Docker将使用名为latest的标记
容器的文件系统
当一个容器运行时,它使用来自一个映像的不同层作为它的文件系统。每个容器也有自己的“暂存空间”来创建/更新/删除文件。在另一个容器中看不到任何更改,即使它们使用相同的图像
开启一个Ubuntu容器,该容器能够创建一个被称为 /data.txt 的文件,该文件携带1到10000的随机数
这条命令,启动一个bashshell并调用两个命令(所以使用&&)。第一部分选取一个随机数并将其写入/data.txt。第二个命令只是监视一个文件以保持容器运行
我们可以通过在容器中执行来查看输出。为此,打开仪表板并单击运行ubuntu映像的容器的第一个操作

您将看到在ubuntu容器中运行shell的终端,运行以下命令以查看/data.txt文件的内容,之后再次关闭此终端
如果喜欢命令行,也可以使用docker exec命令来执行相同的操作,首先需要获取容器的ID(使用docker ps获取它)并使用以下命令获取内容
同时启动另一个ubuntu容器(相同的镜像),我们将看到没有相同的文件
通过以下指令删除容器
在前面的实验中,我们看到每个容器每次启动时都从图像定义开始
虽然容器可以创建、更新和删除文件,但当容器被删除并且所有更改都与该容器隔离时,这些更改将丢失,通过卷,我们可以改变这一切
卷提供了将容器的特定文件系统路径连接回主机的能力,如果装载了容器中的目录,则在主机上也会看到该目录中的更改,如果我们跨容器重新启动装载相同的目录,我们会看到相同的文件
有两种主要的卷类型。我们最终将两者都使用,但我们将从命名卷开始
Persist the todo data
默认情况下,todo应用程序将其数据存储在SQLite数据库/etc/todos/todo.db中
由于数据库是一个单独的文件,如果我们能够将该文件持久化到主机上并使其可供下一个容器使用,那么它应该能够从上一个容器停止的地方恢复
通过创建一个卷并将其附加(通常称为“装载”)到存储数据的目录中,我们可以持久化数据,当容器写入todo.db文件时,它将被持久化到卷中的主机
将命名卷简单地看作一个数据桶
Docker维护磁盘上的物理位置,您只需记住卷的名称,每次使用卷时,Docker都会确保提供正确的数据
通过下面指令创建卷
在仪表板中再次停止并移除todo应用程序容器(或使用docker rm-f<id>),因为它仍在运行,而不使用持久卷
启动todo应用程序容器,添加-v标志以指定卷装载,将使用命名卷并将其装载到/etc/todos,它将捕获在该路径上创建的所有文件
一旦容器启动,打开应用程序并将一些项目添加到您的待办事项列表中

停止并删除todo应用程序的容器,使用仪表板或 docker ps 获取ID,然后使用 docker rm-f<ID> 将其删除
Dive into the volume(侦测卷)
使用下面这条指令
挂载点是磁盘上存储数据的实际位置,在大多数计算机上,您需要具有root访问权限才能从主机访问此目录
在Docker Desktop中运行时,Docker命令实际上是在您机器上的一个小虚拟机中运行的,如果您想查看Mountpoint目录的实际内容,则需要首先进入VM内部
每次更改重建图像都需要相当长的时间,使用绑定挂载,这个方法更好!
通过绑定挂载
- 可以控制主机上的确切挂载点
- 可以使用它来持久化数据,但它通常用于向容器提供额外的数据
- 在处理应用程序时,可以使用绑定挂载将源代码挂载到容器中,让它看到代码更改、响应,并立即看到更改
- 对于基于节点的应用程序,nodemon是一个很好的工具,可以监视文件更改,然后重新启动应用程序
在大多数其他语言和框架中都有相应的工具
快速卷类型比较
绑定装载和命名卷是Docker引擎附带的两种主要类型的卷。但是,可以使用其他卷驱动程序来支持其他用例(SFTP、Ceph、NetApp、S3等)
开启一个调试模式的容器
要运行容器以支持开发工作流,我们将执行以下操作:
- 将源代码装入容器
- 安装所有依赖项,包括“dev”依赖项
- 启动nodemon以监视文件系统更改

容器网络系统
在默认情况下,容器是独立运行的,不知道同一台计算机上的其他进程或容器的任何信息
如果两个容器在同一个网络上,它们可以相互通信,如果他们不是,他们就不能
启动MySQL
将容器放到网络上有两种方法
- 在开始时分配它
- 连接现有容器
现在,我们将首先创建网络,并在启动时附加MySQL容器
创建网络
启动一个MySQL容器并将其连接到网络,定义一些数据库用来初始化数据库的环境变量
参阅MySQL Docker Hub清单中的“环境变量”部分--https://hub.docker.com/_/mysql/
如果使用windows的powershell
这里使用了一个名为todo mysql data的卷,并将其挂载在/var/lib/mysql上
mysql就是在这里存储数数据,我们从未运行docker volume create命令,Docker认识到我们想要使用一个命名卷,并自动为我们创建一个
为了确认数据库已经启动并运行,连接到数据库并校验连接
当密码提示出现时,键入密码,在MySQL shell中,列出数据库并验证您看到了todos数据库。
输出可能是这个样子
连接到MySQL
现在我们知道MySQL已经启动并运行了,如果在同一网络上运行另一个容器,如何找到该容器(记住每个容器都有自己的IP地址)?
为了解决这个问题,我们将使用nicolaka/netshot容器,它附带了很多工具,这些工具对于解决或调试网络问题非常有用。
使用nicolaka/netshoot映像启动新容器,确保将其连接到同一网络
在容器内部,我们将使用dig命令,这是一个有用的DNS工具,我们将查找主机名mysql的IP地址
输出应该是

在“ANSWER”部分,您将看到mysql的A记录,解析为172.23.0.2(您的IP地址很可能有不同的值)
虽然mysql通常不是有效的主机名,但Docker能够将其解析为具有该网络别名的容器的IP地址(还记得我们前面使用的--network alias标志吗?)
这意味着应用程序只需要连接到一个名为mysql的主机,它就会与数据库通信
使用MySQL运行APP
todo应用程序支持设置一些环境变量来指定MySQL连接设置
- - the hostname for the running MySQL server
- - the username to use for the connection
- - the password to use for the connection
- - the database to use once connected
虽然使用env vars来设置连接设置对于开发来说通常是可以的,但是在生产环境中运行应用程序时非常不鼓励这样做
更安全的机制是使用容器编排框架提供的秘密支持。在大多数情况下,这些秘密作为文件装载在正在运行的容器中
您将看到许多应用程序(包括MySQL映像和todo应用程序)也支持env vars,并带有一个∗FILE后缀来指向包含该变量的文件
例如,设置MYSQLꦼSWORDꀵE var将导致应用程序使用引用文件的内容作为连接密码
Docker没有做任何事情来支持这些环境变量,您的应用程序需要知道如何查找变量并获取文件内容
我们将指定上面的每个环境变量,并将容器连接到我们的应用程序网络
如果使用windows的powershell
如果我们查看容器的日志(docker logs<container id>),我们应该会看到一条消息,指示它正在使用mysql数据库。
连接mysql
输出
通过docker-compose将前面的操作合并,使得项目管理更加人性化
Docker Compose是一个用于帮助定义和共享多容器应用程序的工具
使用Compose,我们可以创建一个YAML文件来定义服务,并且通过一个命令,可以将所有内容都拧在一起起来或扯开
使用Compose的最大优点是,您可以在文件中定义应用程序堆栈,将其保留在项目repo的根目录下(它现在是版本控制的),并且可以方便地让其他人为您的项目贡献力量
安装Docker Compose
如果您为Windows或Mac安装了Docker Desktop/Toolbox,那么您已经拥有Docker Compose!
如果您在Linux机器上,则需要安装Docker Compose:https://docs.docker.com/compose/install/
安装结束后查看docker compose的版本
创建一个compose文件
在应用程序项目的根目录下,创建一个名为docker-compose.yml的文件
在compose文件中,我们将首先定义模式版本,在大多数情况下,最好使用支持的最新版本
接下来定义要作为应用程序的一部分运行的服务(或容器)列表
现在可以一次性把一个服务迁移到一个compose文件中
定义应用服务
这个是之前用来定义应用容器的指令
如果使用windows的power shell,使用下面指令
首先,为容器定义服务入口和镜像。我们可以为这项服务取任何名字。该名称将自动成为网络别名,这在定义MySQL服务时非常有用
通常会在镜像定义部分看到命令,尽管对排序没有要求
通过定义服务的ports来迁移命令的 -p3000:3000 部分
接下来,我们将使用工作目录和卷定义来迁移工作目录(-w/app)和卷映射(-v“$(pwd):/app”)
Docker Compose卷定义的一个优点是我们可以使用当前目录中的相对路径
最后,使用environment键迁移环境变量定义
定义mysql服务
之前用过的容器定义指令
如果用windows的power shell则使用下面指令
我们将首先定义新的服务并将其命名为mysql,以便它自动获取网络别名。我们将继续并指定要使用的镜像
接下来,我们将定义卷映射。当我们使用docker run运行容器时,命名卷是自动创建的
但是,使用Compose运行时不会发生这种情况,需要在顶层volumes:部分中定义卷,然后在服务配置中指定挂载点。只需提供卷名,就可以使用默认选项。不过,还有更多的选择
最后我们只需要描述环境变量
此时完整的 docker-compose.yml 文件内容应该如下所示
运行应用栈
保证没有其他的应用/数据库备份在之前运行
使用下面指令启动应用栈,我们需要使用 -d 标识位使得一切都在后台运行
理论上终端的输出为
卷和一个网络一起创建的,默认情况下,Docker Compose会自动为应用程序栈创建一个专门的网络,这就是为什么没有在Compose文件中定义一个网络
使用下面命令查看日志,将看到每个服务的日志交织到一个流中
f标志“跟随”日志,因此将在生成时提供实时输出
服务名称显示在行的开头(通常是彩色的),以帮助区分消息。如果要查看特定服务的日志,可以将服务名称添加到logs命令的末尾,例如
当应用程序启动时,它实际上会在那里等待MySQL启动并准备就绪,然后再尝试连接到它
Docker没有任何内置的支持来等待另一个容器完全启动、运行并准备就绪,然后再启动另一个容器,对于基于节点的项目,可以使用等待端口依赖关系
把服务全部停掉
当准备把服务全部停掉时,只需要运行指令
所有容器都会停止而且网络会被移除
删除卷
默认情况下,运行docker compose时不会删除compose文件中的命名卷。如果要删除卷,则需要添加--volumes标志
构建映像后,最好使用docker scan命令对其进行安全漏洞扫描(Docker与Snyk合作提供漏洞扫描服务)
扫描使用一个不断更新的漏洞的数据库,因此看到的输出将随着新漏洞的发现而变化,但可能如下所示:
输出列出了漏洞的类型、要了解更多信息的URL,以及修复漏洞的相关库的版本
除了在命令行上扫描新构建的镜像外,还可以配置Docker Hub自动扫描所有新推的镜像
镜像分层
使用docker image history命令,可以看到用于在图像中创建每个层的命令
你可以看到以下输出
每一行代表图像中的一层。这里显示的是底部的底座和顶部最新的一层,使用此功能,还可以快速查看每个层的大小,帮助诊断大型图像
您会注意到有几行被截断了,如果添加--no trunc标志,您将得到完整的输出
层缓存
已经看到了分层的实际效果,那么有一个重要的前车之鉴可以帮助减少容器映像的构建时间
一旦图层发生变化,所有下游图层也必须重新创建
再看一次Dockerfile
回到图像历史输出,我们看到Dockerfile中的每个命令都成为图像中的一个新层。
您可能还记得,当我们对映像进行更改时,必须重新安装依赖项,每次构建时都围绕相同的yarn依赖项进行发布是没有多大意义
要解决这个问题,我们需要重新构造Dockerfile,以帮助支持依赖项的缓存
对于基于节点的应用程序,这些依赖项在package.json文件中定义。所以,如果我们先只复制那个文件,安装依赖项,然后再复制其他所有内容
最后我们只在package.json发生更改时重新创建yarn依赖关系即可
首先更新Dockerfile以在package.json中复制,安装依赖项,然后复制包中的所有其他内容。
在Dockerfile所在的文件夹中创建一个名为.dockerginore的文件,该文件包含以下内容
.dockerginore文件是一种仅选择性地复制图像相关文件的简单方法,在这种情况下,在第二个复制步骤中应该省略node즍ules文件夹
因为否则,它可能会覆盖由RUN步骤中的命令创建的文件,有关为什么建议对Node.js应用程序和其他**实践使用此方法的更多详细信息,请参阅他们的Node.js web应用程序停靠指南
使用docker build指令创建一个新镜像
输出如下
现在在前端html文件随意更改一处,重新执行一下指令
现在输出如下
注意看1到4步,using cache,已经使用了缓存
多阶段构建优点
- 将生成时依赖项与运行时依赖项分开
- 通过只提供应用程序运行所需的内容来减小整体图像大小
Maven/Tomcat实例
在构建基于Java的应用程序时,需要使用JDK将源代码编译成Java字节码
然而,JDK在生产中是不需要的,此外,可能正在使用Maven或Gradle等工具来帮助构建应用程序,在我们的最终镜像中也不需要这些
- 在本例中,我们使用一个阶段(称为build)来使用Maven执行实际的Java构建
- 在第二阶段(从tomcat开始),我们从构建阶段复制文件
- 最后一个映像只是创建的最后一个阶段(可以使用–target标志覆盖)
React 实例
在构建React应用程序时,我们需要一个节点环境来将JS代码(通常是JSX)、SASS样式表等编译成静态HTML、JS和CSS
如果我们不做服务器端渲染,我们甚至不需要为我们的产品构建节点环境。为什么不把静态资源放在一个静态nginx容器中呢
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/172283.html