A. 背景
docker? docker 是什么,它有什么意义
What is Docker?
Accelerate how you build, share, and run applications
Docker helps developers build, share, run, and verify applications anywhere — without tedious environment configuration or management.
关键词: Build, Share, Run, Verify
A.1. 场景
一、当我们开发了一个软件以后想要把它迁移到其他系统或者是服务器上去,我们需要写一个配置的文档哪些东西要完整地下载,各部分的配置。然后还要考虑迁移的环境与开发的环境能否兼容匹配.
二、客户那边要安装公司的软件,但是每次都会犯一些奇奇怪怪的错误,要么配置出问题,要么压根不会配置。
类似上述的情况要是能有个一键配置,类似软件商店的样子,可以直接在迁移环境一键安装那样方便就好了,而 docker 就有这样的便利性,甚至配合好 sh 可以做到一行命令,安装完毕的效果。
A.2. docker 的四个特点
- 构建 (Build): Docker 通过将应用程序及其依赖项打包成称为容器的标准化单元,简化了创建应用程序的过程。这确保了在不同环境中的一致行为。
- 共享 (Share): Docker 通过 Docker Hub 等存储库分发容器,使与他人共享应用程序变得容易。这促进了协作并简化了跨团队的部署。
- 运行 (Run): Docker 提供了一个运行时环境,无论底层基础设施如何,都可以一致地执行容器。这种可移植性消除了兼容性问题并简化了应用程序的扩展。
- 验证 (Verify): Docker 提供了在容器内测试和验证应用程序的工具和工作流程,确保在部署到生产环境之前的质量和可靠性。
A.3. docker 架构
好的,这是一张简化的 Docker 架构图,重点突出其核心组件和交互方式:
|
|
组件说明:
- Docker Client: 这是用户与 Docker 交互的命令行界面或 API。它负责发送命令给 Docker Daemon。
- Docker Daemon: 这是 Docker 的核心,负责管理 Docker 的所有对象,如镜像、容器、网络和存储卷。它监听 Docker API 请求并执行相应的操作。
- Containerd: 这是一个高级容器运行时,负责管理容器的生命周期,包括创建、启动、停止、暂停和删除容器。
- Docker Registry: 这是存储 Docker 镜像的仓库。Docker Hub 是一个公共的 Docker Registry,用户也可以搭建私有的 Registry。
- Images: 镜像是 Docker 容器的模板,包含了运行应用程序所需的文件系统、代码、运行时和依赖库。
- Containers: 容器是镜像的运行实例,提供了一个隔离的、可移植的运行环境。
交互流程:
- 用户通过 Docker Client 发送命令。
- Docker Daemon 接收命令并执行相应的操作。
- 如果需要创建容器,Docker Daemon 会从 Docker Registry 拉取镜像(如果本地没有)。
- Docker Daemon 使用
Containerd
创建并管理容器。 - 容器运行应用程序,并与外部世界进行交互。
docker 本身也是容器与容器间相互隔离,互不影响。又不具有整个操作系统。在具有轻量性的同时做到了隔离性。
对比表格
容器属性 | Docker 容器 | 虚拟机或传统主机 |
---|---|---|
文件系统 | 独立的文件系统(只包含容器所需的应用和依赖) | 完整的操作系统内核 |
进程 | 容器内运行的独立进程 | 宿主机之外的额外进程管理 |
网络 | 虚拟网络接口,可以与其他容器或宿主机通信 | 宿主机网络栈的直接访问 |
隔离 | 通过 Namespace 和 Cgroups 提供的进程、网络和文件系统的隔离 | 完全的系统隔离(不像虚拟机那样与宿主机完全隔离) |
资源分配 | 可以通过 Cgroups 限制 CPU、内存等资源使用 | 宿主机之外的物理硬件资源独占 |
依赖环境 | 应用程序所需的所有依赖和库(打包在容器镜像中) | 超出应用所需的多余软件或服务 |
启动速度 | 快速启动和停止(因为只加载必要的组件) | 启动慢(与虚拟机相比) |
操作系统层级 | 共享宿主机的操作系统内核 | 独立的操作系统内核(不像虚拟机那样有自己的内核) |
持久存储 | 通过挂载卷(Volumes)或绑定挂载(Bind Mounts)实现持久化存储 | 默认情况下的数据持久化(容器删除后,未挂载的数据也会丢失) |
安全性 | 提供一定的安全隔离,但依赖于宿主机的安全 | 与虚拟机相同级别的完全隔离和安全性 |
B. docker 常见命令
镜像相关命令
docker build
:从 Dockerfile 构建镜像docker pull
:从镜像仓库拉取镜像docker push
:将镜像推送到镜像仓库docker images
:列出本地镜像docker rmi
:删除镜像docker history
:查看镜像的构建历史docker tag
:为镜像打标签docker save
:将镜像保存为 tar 存档docker load
:从 tar 存档加载镜像
容器相关命令
docker run
:创建并启动容器docker start
:启动已停止的容器docker stop
:停止运行中的容器docker restart
:重启容器docker ps
:列出正在运行的容器docker ps -a
:列出所有容器(包括已停止的)docker rm
:删除已停止的容器docker exec
:在运行中的容器内执行命令docker logs
:查看容器的日志docker cp
:在容器和主机之间复制文件docker attach
:连接到运行中的容器docker inspect
:查看容器或镜像的详细信息docker top
:查看容器内运行的进程docker stats
:查看容器的资源使用情况
其他常用命令
docker version
:查看 Docker 版本信息docker info
:查看 Docker 系统信息docker login
:登录到 Docker 镜像仓库docker logout
:退出 Docker 镜像仓库docker network
:管理 Docker 网络docker volume
:管理 Docker 数据卷使用
docker <command> --help
查看特定命令的详细用法和选项。可以通过
docker-compose
工具来编排多容器应用程序。 Docker 镜像和容器的常见命令一览
C. docker 镜像信息
假设安装有 docker 容器,输入 docker images
或者是 docker image ls
1 ,会显示Docker 镜像信息列表。
字段说明
- REPOSITORY: 镜像的仓库名称。在这里,我们有两个
nginx
镜像,它们属于同一个仓库。 - TAG: 镜像的标签。标签用于区分同一个仓库下的不同镜像版本。
latest
是一个特殊的标签,通常表示最新版本。 - IMAGE ID: 镜像的唯一标识符。
- CREATED: 镜像的创建时间。
- SIZE: 镜像的大小。
D. docker 容器信息
D.1. 容器常用命令
功能分类 | 命令 | 描述 |
---|---|---|
容器生命周期管理 | docker run | 创建并启动一个新的容器 |
docker stop | 停止一个正在运行的容器 | |
docker ps | 列出正在运行的容器 | |
docker ps -a | 列出所有的容器(包括已经停止的) | |
docker start | 启动一个已停止的容器 | |
docker restart | 重启一个容器 | |
docker rm | 删除一个已停止的容器 | |
容器监控与调试 | docker stats | 显示容器的资源使用情况(如 CPU、内存、网络等) |
docker logs | 显示容器的输出日志 | |
docker exec | 进入一个正在运行的容器,以便执行命令 |
D.2. docker 的端口映射
容器内部的端口只有在进入容器以后也就是运行了(docker exec)命令后才能访问,如果要从外部访问的话,要做一个端口映射的也就是在运行 run 命令时加入 -p 外部访问端口:内部端口的参数
D.2.1. e.g.
docker run -d -P nginx
:运行一个 Nginx 容器,并将容器的 80 端口随机映射到宿主机的一个高位端口。docker run -d -p 8080:80 nginx
:运行一个 Nginx 容器,并将容器的 80 端口映射到宿主机的 8080 端口。
注意: 一个宿主机只能对应一个端口号无法重复,但是对应的容器端口是可以重复的,(比如两个容器可以都有 80 的端口号,容器之间是隔离的,这没有关系),但是我的服务器上同时只能有一个端口号。
E. 镜像推送
E.1. 保存镜像
- 命令:
docker save
- 作用: 将本地镜像保存为一个 tar 归档文件,以便在其他机器上加载或备份。
- 示例:
docker save -o my_image.tar my_image:latest
- 将名为
my_image
,标签为latest
的镜像保存为my_image.tar
文件。
E.2. 加载镜像
- 命令:
docker load
- 作用: 从 tar 归档文件中加载镜像到本地镜像库。
- 示例:
docker load -i my_image.tar
- 从
my_image.tar
文件中加载镜像。
E.3. 推送镜像
- 前提:
- 已有 Docker Hub 账号或其他镜像仓库账号。
- 已登录 Docker:
docker login
- 命令:
docker push
- 作用: 将本地镜像推送到镜像仓库,以便共享和部署。
- 示例:
docker push my_username/my_image:latest
- 将名为
my_image
,标签为latest
的镜像推送到 Docker Hub 上的my_username
仓库。
E.4. 注意
- 镜像名称的格式通常为
仓库用户名/镜像名:标签
。 - 如果不指定标签,默认使用
latest
。 - 推送镜像前,确保镜像已正确构建和测试。
- 镜像仓库可能会有存储限制或需要付费,请根据实际情况选择合适的仓库。
F. 存储
如果直接创建或运行一个容器,比如某个网页,一旦不子啊运行这个容器,所有数据就都丢失了,因为数据存储在容器的文件系统内,针对这种情况,我们可以对 docker 容器进行目录挂载。
F.1. 目录挂载
Docker 目录挂载(通常称为卷挂载)允许将宿主机的文件或目录挂载到容器内的文件系统中。这在需要持久化数据或共享数据时非常有用。Docker 支持两种主要的挂载方式:绑定挂载(Bind Mount)和卷挂载(Volume)。
F.1.1. 绑定挂载(Bind Mount)
绑定挂载将宿主机的一个具体目录或文件直接挂载到容器内的一个目录。绑定挂载的路径必须是绝对路径。
- 用法:
docker run -v /host/path:/container/path <image>
- 示例:这个命令将宿主机的
1
docker run -d -v /home/user/data:/data myapp
/home/user/data
目录挂载到容器内的/data
目录。容器内对/data
目录的所有读写操作都直接反映在宿主机的/home/user/data
目录中。
只读挂载
如果希望挂载一个目录但不允许容器对其进行写操作,可以使用 ro
选项(只读模式):
- 用法:
docker run -v /host/path:/container/path:ro <image>
- 示例:这个命令将宿主机的
1
docker run -d -v /home/user/data:/data:ro myapp
/home/user/data
目录挂载为容器内的/data
目录,但容器无法对这个目录进行写操作。
F.1.2. 卷挂载(Volume)
卷挂载使用 Docker 管理的卷,这些卷不依赖于宿主机的具体目录。卷是 Docker 推荐的持久化数据的方式,因为它们更加易于管理和备份。
- 创建卷:
1
docker volume create myvolume
- 用法:
docker run -v myvolume:/container/path <image>
- 示例:这个命令将名为
1
docker run -d -v myvolume:/data myapp
myvolume
的卷挂载到容器内的/data
目录。Docker 会管理这个卷,并且可以在不同容器之间共享这个卷。
临时卷挂载
有时可能希望在容器启动时挂载一个空卷,并在容器销毁时删除它。可以使用 --rm
选项来自动删除容器,同时使用 -v
选项挂载一个匿名卷:
- 用法:
docker run --rm -v /container/path <image>
- 示例:这个命令将启动一个容器,并在容器内的
1
docker run --rm -v /data myapp
/data
目录挂载一个匿名卷。容器停止后,该卷会自动删除。
一个不同点,第一次内部配置文件的修改,在绑定外部存储的时候,两种方式的绑定效果不同。
卷映射
- 文件复制:使用卷映射时,如果容器内的目录在第一次启动时已经有文件,而宿主机的卷(存储在Docker管理目录中)是空的,Docker会自动将容器内的这些文件复制到卷中。因此,使用卷映射时,容器内原始目录中的文件会被保留并复制到宿主机的卷中。
目录挂载
- 宿主机目录内容不变:使用目录挂载时,第一次进行这个操作,Docker不会自动将容器内目录中的文件复制到宿主机指定的挂载点。相反,容器启动后,挂载的宿主机目录将完全替代容器内对应的目录内容。这意味着,如果宿主机目录是空的或内容不同步,那么容器内本来定义的文件会被宿主机目录所替代,从而导致这些文件的缺失。
当目录挂载成功之后,对容器内部文件的修改会同步到宿主机上的对应文件,而不是覆盖容器内部的文件。这意味着,容器内部和宿主机之间的文件是双向同步的,修改任意一方的文件都会反映到另一方。
目录挂载后:
- 容器内部的文件与宿主机上的文件是直接关联的。可以将容器内的目录看作是宿主机目录的“镜像”,但它实际上并不是副本,而是直接指向宿主机目录的一个映射。
修改容器内部文件:
- 当在容器内部修改挂载目录中的文件时,这些修改会立即同步到宿主机上的对应文件。宿主机的文件内容会被更新为容器内最新的修改。
修改宿主机上的文件:
- 同样,如果在宿主机上编辑挂载目录中的文件,这些更改会立即反映到容器内部。
F.1.3. 挂载的用途
- 数据持久化:将容器内的数据持久化到宿主机,以便在容器销毁后仍然保留数据。
- 配置共享:通过挂载宿主机的配置文件或目录到多个容器中,便于管理和共享配置。
- 开发环境:在开发环境中,将代码目录挂载到容器内,以便在不重新构建镜像的情况下实时调试代码。
对比点 | 卷映射(Volume) | 目录挂载(Bind Mount) |
---|---|---|
存储位置 | Docker 管理的目录,位于宿主机的 /var/lib/docker/volumes/ 目录下 | 用户指定的宿主机目录,必须为绝对路径 |
用途和场景 | 适用于数据持久化、数据库存储,便于管理和迁移 | 适用于开发环境中的代码共享、配置文件同步等场景 |
安全性 | 权限和访问控制由 Docker 管理,通常更安全 | 容器直接访问宿主机文件系统,存在潜在安全风险 |
可移植性 | 独立于宿主机文件系统结构,具备较好的可移植性 | 依赖宿主机的具体路径结构,可移植性较差 |
性能 | Docker 优化的存储性能,适合高 I/O 操作 | 受限于宿主机文件系统性能,简单共享场景下表现较好 |
容器间共享 | 可以在多个容器之间轻松共享卷 | 需要手动管理路径和权限,支持共享但操作复杂 |
适用场景 | 生产环境下的数据持久化和多容器数据共享 | 开发环境中的快速文件共享和配置文件管理 |
操作复杂度 | 简单,Docker 自动管理 | 需要用户手动指定路径,操作复杂度稍高 |
G. 网络
Docker 网络是用于管理 Docker 容器之间通信的方式。Docker 提供了几种不同类型的网络模式,适用于不同的应用场景。了解 Docker 的网络模式有助于更好地配置和管理容器化应用。
G.1. Docker 的网络模式
Bridge 网络(默认网络模式)
- 描述:Bridge 网络是 Docker 默认的网络模式,创建一个容器时,如果没有指定网络模式,它会自动连接到一个桥接网络(如
bridge
)。容器之间通过内部虚拟网络接口进行通信,并且宿主机与容器之间也可以通过网络通信。 - 适用场景:适合大多数应用程序,尤其是在需要多个容器进行通信但不暴露于外部网络的情况下。
- 示例:
1
docker run -d --name myapp --network bridge nginx
- 描述:Bridge 网络是 Docker 默认的网络模式,创建一个容器时,如果没有指定网络模式,它会自动连接到一个桥接网络(如
Host 网络
- 描述:Host 网络模式下,容器不会获得独立的网络命名空间,而是直接使用宿主机的网络栈。这意味着容器可以直接访问宿主机的网络接口,性能最佳,但容器与宿主机之间没有网络隔离。
- 适用场景:适用于需要高性能网络通信,或者需要容器直接暴露宿主机端口的场景。
- 示例:
1
docker run -d --network host nginx
- 注意事项:容器中的端口将与宿主机冲突,如果宿主机上某个端口已经被占用,容器将无法使用该端口。
None 网络
- 描述:None 网络模式下,容器不会连接到任何网络。容器启动后没有网络接口,除了 localhost 之外无法与其他容器或宿主机通信。
- 适用场景:适合不需要网络通信的应用或需要完全自主管理网络设置的情况。
- 示例:
1
docker run -d --network none nginx
Overlay 网络
- 描述:Overlay 网络用于 Docker Swarm 或 Docker Enterprise 下的多主机容器通信。它允许跨多台宿主机的容器相互通信,就像它们在同一网络中一样。
- 适用场景:适用于分布式应用程序或跨多个宿主机的服务集群。
- 示例:
1 2
docker network create -d overlay my-overlay docker run -d --network my-overlay nginx
Macvlan 网络
- 描述:Macvlan 网络模式为每个容器分配一个独立的 MAC 地址,并使其像物理设备一样直接连接到宿主机网络。每个容器在宿主机网络中都有自己的 IP 地址。
- 适用场景:适用于需要直接连接到物理网络的场景,如在企业环境中使用特定网络拓扑时。
- 示例:
1 2
docker network create -d macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 -o parent=eth0 my-macvlan docker run -d --network my-macvlan nginx
G.2. 常见的网络命令
列出所有网络:
1
docker network ls
列出当前 Docker 环境中所有可用的网络。
查看网络详细信息:
1
docker network inspect <network_name>
显示指定网络的详细信息,包括连接到该网络的容器。
创建新的网络:
1
docker network create --driver <driver_name> <network_name>
创建一个新的自定义网络,可以选择不同的驱动程序(如
bridge
、overlay
)。删除网络:
1
docker network rm <network_name>
删除指定的网络。注意,不能删除正在使用中的网络。
将容器连接到网络:
1
docker network connect <network_name> <container_name>
将已经运行的容器连接到指定网络。
将容器从网络中断开:
1
docker network disconnect <network_name> <container_name>
将已经运行的容器从指定网络中断开。
H. 环境变量
在 Docker 中,环境变量(Environment Variables)是配置容器运行时行为的一种常见方式。环境变量可以用于设置应用程序的配置、控制容器的行为、以及传递敏感信息如密码、令牌等。使用环境变量的方式既灵活又安全,尤其是在不修改镜像的情况下对容器进行配置时非常有用。
H.1.1. 设置环境变量的方式
使用
-e
或--env
选项:- 在启动容器时,可以通过
-e
或--env
选项来传递环境变量。 - 用法:
docker run -e <VAR_NAME>=<value> <image>
- 示例:这个命令启动一个 MySQL 容器,并设置
1
docker run -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:latest
MYSQL_ROOT_PASSWORD
环境变量为my-secret-pw
。
- 在启动容器时,可以通过
使用
--env-file
选项:- 如果有大量的环境变量,您可以将它们写入一个文件,并通过
--env-file
选项传递给容器。 - 用法:
docker run --env-file <file> <image>
- 示例:
- 首先,创建一个
.env
文件:1 2
MYSQL_ROOT_PASSWORD=my-secret-pw MYSQL_DATABASE=mydatabase
- 然后,在启动容器时使用这个文件:这会将
1
docker run --env-file ./myenv.list -d mysql:latest
.env
文件中的变量传递给容器。
- 首先,创建一个
- 如果有大量的环境变量,您可以将它们写入一个文件,并通过
Dockerfile 中的环境变量:
- 您可以在 Dockerfile 中使用
ENV
指令来设置环境变量,这些变量在容器构建时被定义,并在运行时可用。 - 示例:在这个 Dockerfile 中,
1 2 3 4 5
FROM ubuntu:latest ENV APP_HOME=/usr/src/app WORKDIR $APP_HOME COPY . . CMD ["python", "app.py"]
APP_HOME
被设置为/usr/src/app
,并且在容器内的WORKDIR
指令和CMD
指令中使用了这个环境变量。
- 您可以在 Dockerfile 中使用
通过
docker-compose
配置环境变量:- 在
docker-compose.yml
文件中,您可以通过environment
字段设置环境变量,或通过env_file
字段加载一个环境变量文件。 - 示例:这个
1 2 3 4 5 6 7 8 9
version: '3.8' services: db: image: mysql:latest environment: MYSQL_ROOT_PASSWORD: my-secret-pw MYSQL_DATABASE: mydatabase env_file: - ./myenv.list
docker-compose.yml
文件中,db
服务会加载环境变量,并将myenv.list
文件中的变量添加到环境中。
- 在
H.1.2. 查看容器中的环境变量
使用
docker exec
命令查看:- 运行容器后,您可以使用
docker exec
进入容器并查看环境变量。 - 示例:这会列出当前容器中的所有环境变量。
1
docker exec -it <container_id> env
- 运行容器后,您可以使用
使用
docker inspect
查看环境变量:- 您也可以使用
docker inspect
命令查看特定容器的环境变量配置。 - 示例:
1
docker inspect <container_id> | grep -i env
- 您也可以使用
H.1.3. 环境变量的最佳实践
敏感信息的处理:
- 避免在 Dockerfile 中硬编码敏感信息,如数据库密码、API 密钥等。使用环境变量传递这些信息更安全,并且可以通过
.env
文件将其与代码分离。
- 避免在 Dockerfile 中硬编码敏感信息,如数据库密码、API 密钥等。使用环境变量传递这些信息更安全,并且可以通过
使用
--env-file
管理大量环境变量:- 如果环境变量较多,建议使用
--env-file
来管理,这样可以避免命令行过于复杂,并使配置文件更加易于维护和版本控制。
- 如果环境变量较多,建议使用
使用
docker-compose
进行环境配置:- 对于复杂的多容器应用,使用
docker-compose
来统一管理环境变量,有助于保持配置的一致性和简洁性。
- 对于复杂的多容器应用,使用
环境变量与默认值:
- 在 Dockerfile 中设置默认环境变量值,可以在没有提供环境变量时,仍然能保证应用程序的正常运行。例如:
1
ENV APP_PORT=8080
- 在 Dockerfile 中设置默认环境变量值,可以在没有提供环境变量时,仍然能保证应用程序的正常运行。例如:
I. 流程
J. docker 的分层存储机制
Docker 的分层存储机制是其镜像和容器管理的一项核心功能,它通过使用分层文件系统来提高存储效率和资源利用率。
J.1. 镜像的分层存储
- 镜像层(Image Layers): Docker 镜像由一系列只读层组成,每一层都是在前一层的基础上添加的。每个层代表了镜像在构建过程中所执行的一条指令(如
RUN apt-get install
或COPY . /app
)。 - 层的共享: 不同镜像可以共享相同的层。例如,如果两个镜像都基于同一个基础镜像,那么它们会共享这个基础层,而不需要重复存储,从而节省磁盘空间。
- 层的不可变性: 镜像中的每一层都是不可变的,一旦创建就不能修改。如果要更新镜像中的某一部分内容,Docker 会创建一个新的层,将其叠加到已有的镜像之上。
Responsive Image
J.2. 容器的分层存储
- 容器层(Container Layer): 当容器从镜像启动时,Docker 会为该容器创建一个可写层,称为容器层。这一层位于镜像的只读层之上,容器对文件系统的所有写操作(如添加、修改文件)都会发生在这个层上。
- 写时复制(Copy-on-Write): 如果容器需要修改一个文件,这个文件会从只读层复制到可写层中,然后在可写层进行修改。原始的只读层保持不变,这样可以支持多个容器共享同一个镜像层。
- 容器的存储空间: 当容器被删除时,其对应的可写层也会被删除,但镜像的只读层仍然保留,可以用于创建新的容器。
Responsive Image
多个容器的架构,基础部分都有镜像,容器不同的部分存储在不同的Thin R/W layer。
J.3. 优势
- 高效的存储使用: 由于 Docker 的分层机制和共享机制,不同的镜像可以共享相同的层,从而减少了冗余数据的存储。
- 快速构建和启动: Docker 可以重用已存在的镜像层,从而加快镜像的构建和容器的启动速度。
- 灵活的更新机制: 通过添加新的层而不是修改现有层,Docker 允许灵活地更新镜像,而不会影响基于旧层的容器。
J.4. 文件系统的实现
- 联合文件系统(Union File System): Docker 的分层存储通常是通过联合文件系统(如 OverlayFS、AUFS、btrfs、ZFS)来实现的。这些文件系统可以将多个目录或分区组合成一个单一的虚拟文件系统,支持分层的文件系统结构。
Docker 的分层存储机制使其能够高效地管理镜像和容器,提供了灵活、节省资源的容器化方案。
K. 总结
L. Footnotes
两个命令本身并没有不同,
docker images
:这是 Docker 早期版本中使用的命令,现在仍然可用。docker images
:这是 Docker 早期版本中使用的命令,现在仍然可用。 ↩︎