本教程介绍如何在容器应用程序中保存数据。 运行或更新数据时,数据仍可用。 有两种用于保存数据的主要卷类型。 本教程重点介绍已命名的卷。
此外,还将介绍绑定装载,它控制主机上的确切装载点。 可以使用绑定装载来保存数据,但它还可以将更多数据添加到容器中。 在处理应用程序时,可以使用绑定装载将源代码装载到容器中,让其看到代码更改、响应并立即看到更改。
本教程还介绍了图像分层、层缓存和多阶段生成。
本教程介绍如何:
- 理解跨容器的数据。
- 使用已命名的卷永久保存数据。
- 使用绑定装载。
- 查看映像层。
- 缓存依赖项。
- 了解多阶段生成。
先决条件
本教程继续学习上一教程, 使用 Visual Studio Code 创建和共享容器应用。 从这一项开始,其中包括先决条件。
了解跨容器的数据
在本部分中,你将启动两个容器,并在每个容器中创建一个文件。 在一个容器中创建的文件在另一个容器中不可用。
使用以下命令启动
ubuntu
容器:docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
此命令使用
&&
启动对两个命令的调用。 第一部分选取单个随机数并将其写入/data.txt
。 第二个命令用于监控文件以确保容器持续运行。在 VS Code 的容器资源管理器中,右键单击 ubuntu 容器并选择 “附加 Shell”。
此时会打开一个在 Ubuntu 容器中运行 shell 的终端。
运行以下命令以查看
/data.txt
文件的内容。cat /data.txt
终端显示介于 1 和 10000 之间的数字。
若要使用命令行查看此结果,请使用
docker ps
命令获取容器 ID,然后运行以下命令。docker exec <container-id> cat /data.txt
启动另一个
ubuntu
容器。docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
使用此命令查看文件夹内容。
docker run -it ubuntu ls /
该文件夹内应不包含
data.txt
文件,因为它只写入第一个容器的暂存空间。选择这两个 Ubuntu 容器。 右键单击并选择删除。 在命令行中,可以使用
docker rm -f
命令将其删除。
使用已命名的卷永久保存待办事项数据
默认情况下,todo 应用将其数据存储在 SQLite 数据库/etc/todos/todo.db
。
SQLite 数据库是存储单个文件的关系数据库。
此方法适用于小型项目。
可以在主机上保留单个文件。 当将其提供给下一个容器时,应用程序可以从中断的地方继续运行。 通过创建卷并将其附加或装载到存储数据的目录,可永久保存数据。 容器会写入 todo.db 文件,并数据永久保存在卷中的主机上。
对于此部分,请使用已命名的卷。 Docker 维护磁盘上卷的物理位置。 参考卷的名称,Docker 会提供正确的数据。
使用
docker volume create
命令创建卷。docker volume create todo-db
在“容器”下,选择“入门”并右键单击。 选择 停止 以停止应用容器。
若要从命令行停止容器,请使用
docker stop
命令。使用以下命令启动“getting-started”容器。
docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
卷参数指定要装载的卷和位置
/etc/todos
。刷新浏览器以重新加载应用。 如果已关闭浏览器窗口,请转到
http://localhost:3000/
。 将某些项添加到待办事项列表中。移除待办事项应用的“getting-started”容器。 右键单击容器资源管理器中的容器,然后选择删除,或使用
docker stop
和docker rm
命令。使用相同的命令启动新容器:
docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
此命令将装载与之前相同的驱动器。 刷新浏览器。 添加的项仍在列表中。
再次移除“getting-started”容器。
下面介绍的已命名的卷和绑定装载是默认 Docker 引擎安装支持的主要卷类型。
财产 | 命名卷 | 绑定装载 |
---|---|---|
主机位置 | Docker 选择 | 你控制 |
装载示例(使用 -v ) |
my-volume:/usr/local/data | /path/to/data:/usr/local/data |
使用容器内容填充新卷 | 是的 | 不 |
支持卷驱动程序 | 是的 | 不 |
有许多卷驱动程序插件可用于支持 NFS、SFTP、NetApp 等。 这些插件对于在群集环境(如 Swarm 或 Kubernetes)中的多个主机上运行容器尤其重要。
如果您想知道 Docker 到底 将数据存储在什么位置,请运行以下命令。
docker volume inspect todo-db
查看输出,类似于以下结果。
[
{
"CreatedAt": "2019-09-26T02:18:36Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
"Name": "todo-db",
"Options": {},
"Scope": "local"
}
]
Mountpoint
是存储数据的实际位置。
在大多数计算机上,需要根访问权限才能从主机访问此目录。
使用绑定装载
使用绑定装载,你可以控制主机上的确切装载点。 此方法保留数据,但通常用于向容器提供更多数据。 可以使用绑定装载将源代码挂载到容器中,这样它可以监测代码更改作出响应,同时你可以立即查看更改。
若要运行容器以支持开发工作流,请执行以下步骤:
移除所有
getting-started
容器。在
app
文件夹中,运行以下命令。docker run -dp 3000:3000 -w /app -v ${PWD}:/app node:lts-alpine sh -c "yarn install && yarn run dev"
此命令包含以下参数。
-
-dp 3000:3000
与以前相同。 在分离模式下运行并创建端口映射。 -
-w /app
容器内的工作目录。 -
-v ${PWD}:/app"
Bind 将当前目录从容器中的主机装载到/app
目录中。 -
node:lts-alpine
要使用的映像。 此映像是 Dockerfile 中应用的基础映像。 -
sh -c "yarn install && yarn run dev"
一个命令。 它使用sh
启动 shell,并运行yarn install
来安装所有依赖项。 然后它会运行yarn run dev
。 如果你查看package.json
,将看到dev
脚本正在启动nodemon
。
-
可以使用
docker logs
来监视日志。docker logs -f <container-id>
$ nodemon src/index.js [nodemon] 2.0.20 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): *.* [nodemon] watching extensions: js,mjs,json [nodemon] starting `node src/index.js` Using sqlite database at /etc/todos/todo.db Listening on port 3000
在此列表中看到最终条目时,应用正在运行。
观看完日志后,请在终端窗口中选择任意键,或在外部窗口中选择 ctrl +。
在 VS Code 中,打开 src/static/js/app.js。 更改第 109 行上 添加项 按钮的文本。
- {submitting ? 'Adding...' : 'Add Item'} + {submitting ? 'Adding...' : 'Add'}
保存更改。
刷新浏览器。 你应该会看到更改。
删除
node:lts-alpine
容器。在
app
文件夹中,运行以下命令以删除node_modules
在前面的步骤中创建的文件夹。rm -r node_modules
查看映像层
可以查看构成图像的层。
运行 docker image history
命令以查看用于在映像中创建每个层的命令。
使用
docker image history
,查看在本教程前面部分创建的 getting-started 映像中的层。docker image history getting-started
结果应类似于此输出。
IMAGE CREATED CREATED BY SIZE COMMENT a78a40cbf866 18 seconds ago /bin/sh -c #(nop) CMD ["node" "/app/src/ind… 0B f1d1808565d6 19 seconds ago /bin/sh -c yarn install --production 85.4MB a2c054d14948 36 seconds ago /bin/sh -c #(nop) COPY dir:5dc710ad87c789593… 198kB 9577ae713121 37 seconds ago /bin/sh -c #(nop) WORKDIR /app 0B b95baba1cfdb 13 days ago /bin/sh -c #(nop) CMD ["node"] 0B <missing> 13 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B <missing> 13 days ago /bin/sh -c #(nop) COPY file:238737301d473041… 116B <missing> 13 days ago /bin/sh -c apk add --no-cache --virtual .bui… 5.35MB <missing> 13 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.21.1 0B <missing> 13 days ago /bin/sh -c addgroup -g 1000 node && addu… 74.3MB <missing> 13 days ago /bin/sh -c #(nop) ENV NODE_VERSION=12.14.1 0B <missing> 13 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 13 days ago /bin/sh -c #(nop) ADD file:e69d441d729412d24… 5.59MB
每行都表示图像中的一个层。 底部显示的是基本输出,顶部显示的是最新的输出。 使用此信息,可以查看每个层的大小,帮助诊断大型图像。
几行被截断。 如果添加
--no-trunc
参数,你将获得完整的输出。docker image history --no-trunc getting-started
缓存依赖项
层更改后,必须重新创建所有下游层。 下面还是 Dockerfile:
FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "/app/src/index.js"]
Dockerfile 中的每个命令都会成为映像中的新层。
为了最大程度地减少层数,可以重构 Dockerfile 以支持依赖项缓存。
对于基于节点的应用程序,这些依赖项在 package.json
文件中定义。
方法是首先仅复制该文件,安装依赖项,然后 然后 复制其他所有内容。
该过程仅在 package.json
发生更改时重新创建 yarn 依赖项。
先更新要在 中复制的 Dockerfile,安装依赖项,然后再复制其他所有内容
package.json
。 下面是新文件:FROM node:lts-alpine WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install --production COPY . . CMD ["node", "/app/src/index.js"]
使用
docker build
生成新映像。docker build -t getting-started .
应会看到类似于以下结果的输出:
Sending build context to Docker daemon 219.1kB Step 1/6 : FROM node:lts-alpine ---> b0dc3a5e5e9e Step 2/6 : WORKDIR /app ---> Using cache ---> 9577ae713121 Step 3/6 : COPY package* yarn.lock ./ ---> bd5306f49fc8 Step 4/6 : RUN yarn install --production ---> Running in d53a06c9e4c2 yarn install v1.17.3 [1/4] Resolving packages... [2/4] Fetching packages... info fsevents@1.2.9: The platform "linux" is incompatible with this module. info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation. [3/4] Linking dependencies... [4/4] Building fresh packages... Done in 10.89s. Removing intermediate container d53a06c9e4c2 ---> 4e68fbc2d704 Step 5/6 : COPY . . ---> a239a11f68d8 Step 6/6 : CMD ["node", "/app/src/index.js"] ---> Running in 49999f68df8f Removing intermediate container 49999f68df8f ---> e709c03bc597 Successfully built e709c03bc597 Successfully tagged getting-started:latest
所有层都已重建。 这是预期的结果,因为更改了 Dockerfile。
对 src/static/index.html进行更改。 例如,将标题更改为“The Awesome Todo App”。
现在,再次使用
docker build
生成 Docker 映像。 这一次,输出看起来应该稍有不同。Sending build context to Docker daemon 219.1kB Step 1/6 : FROM node:lts-alpine ---> b0dc3a5e5e9e Step 2/6 : WORKDIR /app ---> Using cache ---> 9577ae713121 Step 3/6 : COPY package* yarn.lock ./ ---> Using cache ---> bd5306f49fc8 Step 4/6 : RUN yarn install --production ---> Using cache ---> 4e68fbc2d704 Step 5/6 : COPY . . ---> cccde25a3d9a Step 6/6 : CMD ["node", "/app/src/index.js"] ---> Running in 2be75662c150 Removing intermediate container 2be75662c150 ---> 458e5c6f080c Successfully built 458e5c6f080c Successfully tagged getting-started:latest
由于使用的是生成缓存,因此这个过程应该更快。
多阶段生成
多阶段生成是一种难以置信的强大工具,可帮助使用多个阶段创建映像。 它们有几个优点:
- 将生成时依赖项与运行时依赖项分开
- 通过仅传送应用需要运行的内容来减小整体图像大小
本部分提供了简短的示例。
Maven/Tomcat 示例
生成基于 Java 的应用程序时,需要 JDK 才能将源代码编译为 Java 字节码。 生产中不需要该 JDK。 你可能正在使用 Maven 或 Gradle 等工具来帮助构建应用。 最终图像中也不需要这些工具。
FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package
FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps
此示例使用一个阶段(build
)使用 Maven 执行实际的 Java 生成。
第二个阶段(从“FROM tomcat”开始)从 build
阶段复制文件。
最终图像只是创建过程的最后一个阶段,可以使用 --target
参数来覆盖该阶段。
React 示例
生成 React 应用程序时,需要 Node 环境才能将 JavaScript 代码、Sass 样式表等编译为静态 HTML、JavaScript 和 CSS。 如果不执行服务器端渲染,则甚至不需要生产版本的 Node 环境。
FROM node:lts-alpine AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
此示例使用 node:lts-alpine
映像执行生成,该生成将层缓存最大化,然后将输出复制到 nginx 容器中。
清理资源
请保留您迄今为止所做的一切,以继续这一系列教程。
后续步骤
你已了解用于保存容器应用数据的选项。
接下来要做什么?
使用 Docker Compose 处理多个容器:
使用 MySQL 和 Docker Compose 创建多容器应用
部署到 Azure 容器应用:
快速入门:使用 Visual Studio Code 部署到 Azure 容器应用
部署到 Azure 应用服务