Docker Get Started
容器化应用 Containerize an application
-
拉取远程仓库代码
git clone https://github.com/docker/getting-started-app.git
-
构建 app 镜像
-
A Dockerfile is simply a text-based file with no file extension that contains a script of instructions.
-
touch Dockerfile
in app directory -
In Dockerfile
# syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "src/index.js"] EXPOSE 3000
-
docker build -t getting-started .
-
-
运行 app 容器
-
docker run -dp 127.0.0.1:3000:3000 getting-started
- -d 表示后运行,-p 表示端口映射(
HOST:CONTAINER
) - docker run 执行完后可以用 3000 端口访问到本地/远程服务器上的应用
- -d 表示后运行,-p 表示端口映射(
-
-
查看容器镜像列表 - docker ps
更新已部署的容器 Update the application
-
移除原先的旧容器
-
暂停旧的容器
docker stop <the-container-id>
-
清楚旧的容器
docker rm <the-container-id>
-
合并上述两条命令一起执行为:
docker rm -f <the-container-id>
-
-
重启已更新的新容器
docker run -dp 127.0.0.1:3000:3000 getting-started
分享容器镜像 Share the application
-
Before, you have to use a Docker registry in Docker Hub.(类似于 GitHub、Gitee 中的个人仓库)
-
向远程仓库推送镜像 PUSH the image
-
docker push YOUR-USER-NAME/getting-started
这意味着 docker 会在 image ls 中查找名叫YOUR-USER-NAME/getting-started:tagname
的镜像; -
tagname
默认为 latest,即最新的; -
若找到,则可以推送且覆盖原仓库的
YOUR-USER-NAME/getting-started:tagname
的镜像; -
若找不到,则需要
tag to give it another name.
,实际上就是起一个 alias,这样就可以通过别名去引用它,即被标签(tagged)的镜像实际上是同一份数据的引用; -
docker tag getting-started YOUR-USER-NAME/getting-started:tagname
-
docker push YOUR-USER-NAME/getting-started:tagname
-
至此,镜像推送成功,可查看自己的 Docker Hub 仓库验证。
-
-
线上感受镜像拉取部署 Run the image on a new instance
-
浏览器打开 Play with Docker;
-
登录并且从下拉菜单中选择 docker;
-
或用你的 Docker Hub 注册并且点击开始;
-
click
ADD NEW INSTANCE
button on the left side bar and you should see the following image
-
在终端输入一下命令,启动你新推送的镜像:
docker run -dp 0.0.0.0:3000:3000 YOUR-USER-NAME/getting-started
你应该看到镜像成功被拉取,并成功跑起来了 -
click
OPEN PORT
button and specify 3000 as the port. -
happy~
-
持久化数据 Persist the DB
1、容器的文件系统隔离于其他容器 - Any changes won't be seen in another container, even if they're using the same image.
2、docker 中的 volumes 有两种常用的文件系统挂载方式 - Volume mounts 和 Bind mounts,此章节介绍 Volume mounts 且仅仅介绍了在一个容器中使用 SQLite 存储数据的案例,后续章节会讲到 MySQL 容器存储其他容器数据的情况
3、A volume mount is a great choice when you need somewhere persistent to store your application data.
-
Volumes 提供了将容器文件系统连接回主机文件系统的能力,这使得容器数据可以被主机文件系统持久化,并且可以在容器重启时保持数据。
-
Docker 全权管理 volume mount,包括在磁盘的存储位置,你仅仅需要记住 the volume 的命名;
-
创建 volume、使用 volume mounts、启用一个容器
-
创建一个 volume
docker volume create todo-db
-
启动一个容器,添加
--mount
指定一个 volume mount(Give the volume a name, and mount it to /etc/todos in the container, which captures all files created at the path.) -
docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started
-
容器跑起来后,Docker 会自动管理名为 todo-db 的 volume,并将其挂载到容器的/etc/todos 路径下,这样,在 host machine 上会捕获容器/etc/todos 创建的所有文件,即在容器中创建的数据文件会被反映到 host machine 中 Docker 管理文件的路径中。
-
-
Where is Docker storing my data when I use a volume?
docker volume inspect
command can help you.
自定义目录映射 Use bind mounts
1、you can develop your app more efficiently using bind mounts.
2、此章节介绍 Bind mounts,你可以挂载代码目录到容器内部,当你修改并保存文件时,容器会监听到变化且回应你(容器中运行了一个进程来监听文件系统的变化)
-
volume type comparisons
-
development container:你可以将自己的项目目录挂载到一个容器中,在容器中可以安装项目依赖和构建工具.....而不需要在本地安装。
-
开启一个开发容器
-
-w
设置容器中的工作目录; -
sh -c
使用sh
(shell 脚本) 跑 yarn 命令(centos 中使用 bash); -
docker logs -f <container-id>
观察容器日志; -
项目开发完毕, Ctrl+C 退出;
-
关闭开发容器并构建新镜像
docker build -t YOUR-IMAGE-NAME .
-
为什么 bind mounts 的容器可以协助开发:host source direction 改变,引起 container work direction 改变,yarn run dev 实际上是根据 package.json 的 "script" 启动了 nodemon 监听 node 项目文件变化,当 /app(work direction) 变化时,nodemon restart src/index.js,并输出 logs。
个人觉得这个开发容器的用法于 vite 构建工具类似,如果要用 Docker 开发容器的流程解释,也很容易让人理解。大胆猜测,在开发服务器中,服务器会把源文件 copy 一份到自己的工作目录下,使用 node --watch 监听整个目录下的文件变化(这与 bind mounts 目录映射一样),源文件变化时,开发服务器会接受到通知并更新(开发容器也会这么做),开发服务器会将现有的代码文件打包,相同文件用缓存,不同则更新原有文件,然后将 build 的文件部署在开发服务器上,提供一个 5173 端口给开发者在网页中打开。以上解释为个人浅薄理解。
容器通信 Multi container apps
1、MySQL 容器存储其他容器产生的数据
2、Run the container separately
-
容器间通信,关键在网络 networking
-
以下两种都可以将一个容器放到一个网络:
- 当启动新容器时分配一个网络
- 将一个正在运行的容器连接到一个网络
-
建立一个在 todo-app 网络下运行的 MySQL 容器
-
创建网络
docker network create todo-app(networking name)
-
docker run
MySQL containerdocker run -d \ --network todo-app --network-alias mysql \ -v todo-mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=secret \ -e MYSQL_DATABASE=todos \ mysql:8.0
-
--network-alias
网络别名,用于在 todo-app 网络下连接和访问这个 MySQL 容器 -
docker exec -it <mysql-container-id> mysql -u root -p
进入数据库容器且可以用 MySQL 命令操作(例如mysql -u root -p
、mysql>>> SHOW DATABASES;
、mysql>>> exit
)
-
-
deployed Nicolaka/netshoot container debug the MySQL container
-
启动一个连接 MySQL 存储数据 的容器
-
docker run
node 项目docker run -dp 127.0.0.1:3000:3000 \ -w /app -v "$(pwd):/app" \ --network todo-app \ -e MYSQL_HOST=mysql \ -e MYSQL_USER=root \ -e MYSQL_PASSWORD=secret \ -e MYSQL_DB=todos \ node:18-alpine \ sh -c "yarn install && yarn run dev"
-
MYSQL_HOST=mysql
此处的 mysql 为之前的--network-alias
,意味着在同一网络下,--network-alias
实际上就是这个容器的 hostname,每个容器都有自己的 IP 地址 -
为什么需要这些环境变量?是因为 node 会将这些环境变量从命令行中提取到 process.env,然后在 node 项目中使用他们,以指定连接到 MySQL 服务。
-
-
查看数据变化和存储
-
进入 MySQL 数据库容器中使用 MySQL 命令查看数据变化
docker exec -it <mysql-container-id> mysql -p todos
-
通过 MySQL 所映射的 volume 使用
docker volume inspect todo-mysql-data
查看
-
以更简单的方式==打包和分享==镜像 Use Docker Compose
Docker Compose is a tool that helps you define and share multi-container applications.
-
在项目中创建一个
compose.yaml
,字段如下:
- 顶层字段为
services
、volumes
(named volumes)
- 顶层字段为
-
运行
docker compose up
启动项目-
docker compose up -d
-
-d
表示在后台运行 -
Docker Compose 会自动创建网络运行 yaml 文件中的服务,使得这些容器可以进行通信。
-
-
docker compose down
停掉 docker compose 启动的服务并且移除网络。默认情况下,不会清除创建的 named volumes,可以通过 --volumes 参数来清除。
镜像构建的最佳实践 Image-building best practices
-
层缓存 Layer caching
# old # syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "src/index.js"]
# new # syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install --production COPY . . CMD ["node", "src/index.js"]
-
上游 ---> 下游(从上到下)
-
镜像是由一层一层的 layer 所构建起来的,先构建上层,再构建下层,当某一层发生变化时,其下游所有 layer 都需要重新构建;
上述两个 Dockerfile 对比,当修改源文件重新构建时,第一个 Dockerfile 会从 COPY 步骤开始依次执行下游的构建步骤,如复制粘贴源文件(包括 node_modules)、重新 yarn install......而第二个 Dockerfile 会从 RUN 步骤开始向下游构建,且第二种方式会添加一个额外的
.dockerignore
文件,COPY 步骤就会忽略掉 node_modules,因此,每次更新完代码后构建镜像会更快。
-
-
多阶段构建 Multi-stage builds --- React example
-
在 Dockerfile 中,使用多个 FROM 可进行多阶段构建,前一阶段的输出作为后一阶段的输入。
-
AS build
将当前构建阶段命名为 build 阶段; -
--from=
告诉 COPY 步骤需要复制的文件在 build 阶段的 /app/build 目录中; -
多阶段构建结束后,最终只会打包成一个镜像,此处为 nginx 镜像(build 镜像结果以最后一个 FROM 为准,其他为临时环境.....个人理解),打包好的静态文件被托管在 nginx /usr/share/nginx/html/ 。
-