1.概述
Docker是一种优秀的开源的容器化平台。用于部署、运行应用程序,它通过将应用及其依赖打包成轻量级、可移植的容器,实现高效一致的运行效果,简单来说,Docker就是一种轻量级的虚拟技术。
2.核心概念
2.1.容器(Container)
容器是独立运行的进程单元,包含应用程序代码、运行时环境、系统工具和依赖库。
1.每个容器相互隔离,共享主机操作系统内核
2.启动速度快,资源占用低
2.2.镜像(Image)
镜像是容器启动的静态模板,包含创建容器所需的分层文件系统
1.通过Dockerfile定义构建步骤(例如指定基础镜像,安装依赖)
2.支持版本管理和共享(如Docker hub共享仓库)
2.3.仓库(Registry)
用于仓储和分发镜像的平台(如Docker Hub、私有仓库)
3.核心功能
3.1.容器化技术
- 应用隔离:将应用及其依赖打包到独立的容器中
- 环境一致性:保证开发、测试、生产环境的一致性
- 快速部署:秒级启动和停止容器
3.2.镜像管理
- 分层存储:镜像采用分层结构,共享基础层,节省存储空间
- 版本控制:支持镜像的版本管理和回滚
- 镜像仓库:Docker Hub等平台提供镜像共享和分发
3.3.资源管理
- 资源限制:可以限制容器的CPU、内存、磁盘IO等资源
- 网络管理:提供多种网络模式(bridge、host等)
- 存储管理:支持数据卷,绑定挂载等持久化存储方案
4.容器化&虚拟化
- Infrastructure:基础设施,可以是个人电脑、主机服务器
- Host Operating system:运行的主操作系统,例如windows、mac、Linux
- Hypervisor:虚拟机管理器,可以虚拟出多个硬件,然后再安装操作系统,再部署应用
- Guest OS:从操作系统,有Hypervisor虚拟出来
- BINS/LIBS:二进制文件(Binaries)/库文件(Libraries),例如应用程序相关的编译后的可以执行的二进制文件,提供给应用程序调用的同样功能的库文件
- Docker Daemon:Docker守护进程,代替了虚拟机的Hypervisor,docker守护进程本质是运行在主操作系统上的一个进程,直接与操作系统进行交互,这也是Docker 容器比较快的原因。
虽然虚拟机比较笨重但是也不是没有优势,可以讲服务器比作一个码头,拥有各种设备和场地(服务器硬件资源),服务器虚拟化类似在码头上有好多个仓库,仓库可以完全独立,每个仓库有各自的管理人员,Docker容器类似仓库里面的集装箱,每个集装箱各自独立,灵巧,方便迁移,但是隔离性比较差,多个集装箱统一由一个管理者进行管理,所有集装箱信息就暴露给一个管理人员,而仓库则可以有各自的用户,例如云服务提供者,通过虚拟出一个独立的虚拟机,有自己的用户,即租户管理自己的虚拟机。
# Docker 架构
+-------------------------------------------------------+
| Application A |
| Application B |
| Application C |
+-------------------------------------------------------+
| Docker Engine |
+-------------------------------------------------------+
| Host Operating System |
+-------------------------------------------------------+
| Physical Hardware |
+-------------------------------------------------------+
# 虚拟机架构
+-----------+ +-----------+ +-----------+
| App A | | App B | | App C |
+-----------+ +-----------+ +-----------+
| Guest OS | | Guest OS | | Guest OS |
+-----------+ +-----------+ +-----------+
| Hypervisor |
+-------------------------------------------------------+
| Host Operating System |
+-------------------------------------------------------+
| Physical Hardware |
+-------------------------------------------------------+
4.1.详细对比
特性 | Docker (容器) | 虚拟机 (VM) |
---|---|---|
启动速度 | 秒级启动 | 分钟级启动 |
性能 | 接近原生性能 | 有性能损耗(5-15%) |
资源占用 | 轻量(MB级别) | 重量(GB级别) |
隔离性 | 进程级别隔离 | 完整的系统级别隔离 |
安全性 | 相对较低(共享内核) | 较高(完全隔离) |
镜像大小 | 通常为 MB 级别 | 通常为 GB 级别 |
部署密度 | 高(单机可运行数百容器) | 低(单机通常10-20个VM) |
兼容性 | 依赖宿主机内核 | 任何支持虚拟化的系统 |
迁移性 | 跨平台有限制 | 跨平台性好 |
系统开销 | 低(无额外OS开销) | 高(每个VM需要完整OS) |
4.1.技术实现对比
技术层面 | Docker | 虚拟机 |
---|---|---|
虚拟化类型 | 操作系统级虚拟化 | 硬件级虚拟化 |
虚拟化技术 | Namespaces, Cgroups | Hypervisor (KVM, VMware, Hyper-V) |
Guest OS | 不需要,共享主机内核 | 每个VM需要完整的Guest OS |
资源分配 | 动态分配,按需使用 | 预先分配固定资源 |
镜像构建 | Dockerfile,分层构建 | 模板或快照,整体构建 |
5.Docker使用场景
使用Docker容器开发、测试、部署服务:因为Docker本身非常轻量化,所以本地开发人员可以构建、运行并分享Docker容器。容器可以在开发环境中创建,然后再提交到测试,最终进入生产环境。
创建隔离的运行环境:在很多企业应用中,同一服务的不同版本可能服务于不同的用户,那么使用Docker非常容易创建不同的生成环境来运行不同的服务。
搭建测试环境:由于Docker的轻量化,所以开发者很容易利用Docker在本地搭建测试环境,用来测试程序在不用系统下的兼容性;甚至搭建集群的部署测试。
构建多用户的平台即服务(PaaS)基础设施。
提供软件即服务(SaaS)应用程序。
高性能、超大规模的宿主机部署。
5.1.Docker组成
5.2.Docker 客户端 / 守护进程
Docker是C/S架构的程序:Docker客户端向Docker服务器端,也就是Docker的守护进程发出请求,守护进程处理完所有的请求工作并返回结果。Docker 客户端对服务器端的访问既可以是本地也可以通过远程来访问。
5.3.Docker Image 镜像
镜像是Docker容器的基石,容器基于镜像启动和运行。镜像就好比容器的源代码,保存了用于启动容器的各种条件。
Docker的镜像是一个层叠的只读文件系统,最低端是一个引导文件系统(即bootfs),第二层是root文件系统(即rootfs),它位于bootfs之上,可以是一种或多种操作系统,比如Ubuntu或者CentOS。在Docker中,root文件系统永远只能是只读状态,并且docker运用联合加载技术又会在root文件系统之上加载更多的只读文件系统,联合加载指的是一次加载多个文件系统,但是在外面看起来只能看到一个文件系统,联合加载会将各层文件系统叠加到一起,这样最终的文件系统会包含所有的底层文件和目录,docker将这样的文件系统称为镜像。
5.4.Docker Container 容器
容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
当一个容器启动时,Docker会在该镜像的最顶层加载一个读写文件系统,也就是一个可写的文件层,我们在Docker运行的程序,就是在这个层中进行执行的,当Docker第一次启动一个容器时,初始的读写层是空的,当文件系统发生变化时,这些变化都会应用到这一层上,比如像修改一个文件,该文件首先会从读写层下面的只读层复制到该读写层,该文件的只读版本依然存在,但是已经被读写层中的该文件副本所隐藏,这就是Docker的一个重要技术:写时复制(copy on write)。每个只读镜像层都是只读的,永远不会变化,当创建一个新容器时,Docker会构建出一个镜像栈,
5.5.Docker Registry 仓库
Registry仓库是集中存放Docker镜像文件的场所。人们有时候会把Repository仓库和Registry仓库混为一谈,并不严格区分。实际上,Registry仓库上往往存放着多个Repository仓库,每个Repository仓库中又包含了多个Docker镜像,每个Docker镜像有不同的标签(tag)。
Registry仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。 国内的公开仓库包括 Docker Pool 等,可以提供大陆用户更稳定快速的访问。
6.Dockerfile
Dockerfile 是用于构建 Docker 镜像的文本文件,包含一系列指令。每个指令在镜像中创建一个新层,最终形成可运行的容器环境。
6.1.Dockerfile 是什么
- 文本文件: 纯文本文件,通常命名为 Dockerfile (首字母大写,无扩展名是惯例)。
- 指令集合: 包含一系列按顺序执行的指令 (INSTRUCTION arguments)。
- 构建蓝图: 描述了如何从基础组件(通常是基础镜像)一步步组装成一个新的、可运行的镜像。
- 自动化构建: 执行 docker build 命令时,Docker 引擎读取 Dockerfile 并执行其中的指令,最终生成一个镜像。
- 可重复性 & 版本控制: Dockerfile 本身可以被版本控制(如 Git),确保镜像构建过程透明、可审计、可重复。
6.2.Dockerfile 的核心组成部分
指令 | 目的 | 说明 | 示例 | 最佳实践 |
FROM | 基础镜像 | 必须是第一个指令,指定构建新镜像所属的现有镜像 | FROM ubuntu:22.04 FROM python:3.9-slim-buster |
使用官方、轻量级、具体版本的基础镜像。 |
RUN | 执行命令 | 在构建期间,在新镜像的文件系统层中执行命令 | RUN apt-get update && apt-get install -y nginx RUN pip install -r requirements.txt |
合并命令减少层数;清理缓存;命令失败时应返回非零。 |
COPY | 复制文件 | 从构建上下文复制文件或目录到镜像指定路径 | COPY ./app /usr/src/app COPY requirements.txt . |
优先使用 COPY ;明确复制目标目录。 |
ADD | 复制与解压 | 类似COPY,单增加了功能;自动解压本地.tar文件,支持URL源 | ADD ./app.tar.gz /opt/ ADD https://example.com/file /tmp/ |
除非需要解压或 URL 下载,否则优先用 COPY 。 |
CMD | 容器默认命令 | 知道容器启动运行的默认命令及参数每个Dockerfile只能有一个CMD可被docker run覆盖 | CMD ["nginx", "-g", "daemon off;"] CMD ["python", "app.py"] |
使用exec形式避免shell进程;仅指定主进程 |
ENTRYPOINT | 容器入口点 | 定义容器启动时可执行程序,CMD作为参数传递给ENTRYPOINT,可被 docker run --entrypoint 覆盖 | ENTRYPOINT ["python"] CMD ["app.py"] |
常与CMD配合使用;优先exec形式;用于固定入口 |
WORKDIR | 设置工作目录 | 设置后续指令(RUN、CMD)的工作目录 | WORKDIR /app RUN pwd # 输出 /app |
为清晰路径设置,替代 RUN cd ... ;用绝对路径。 |
EXPOSE | 申明端口 | 文档性说明容器在运行时监听哪些网络端口,不实际发布端口 | EXPOSE 80 EXPOSE 8000/tcp |
说明服务监听的端口;真实端口映射在 docker run 时指定。 |
ENV | 设置环境变量 | 设置环境变量,可以在构建和运行期间使用 | ENV PYTHONUNBUFFERED=1 ENV APP_HOME=/app |
配置服务行为;使用变量简化 Dockerfile。 |
ARG | 构建时变量 | 定义仅在构建过程中有效的变量,可用docker build --build -arg 传入 | ARG VERSION=latest RUN apt-get install package=$VERSION |
用于参数化构建;不同默认值于不同阶段。 |
VOLUME | 定义匿名卷 | 在镜像中创建一个挂载点,用于关联外部存储 | VOLUME /var/lib/mysql |
声明需要持久化或共享数据的目录。 |
USER | 切换用户 | 设置后续指令(RUN、CMD)运行时的用户及用户组 | USER www-data USER 1000:1000 |
避免 root 用户运行应用;提高安全性。 |
LABEL | 添加元数据 | 为镜像添加描述性元数据,如维护者信息,许可证等 | LABEL maintainer="dev@example.com" LABEL version="1.0" |
记录镜像相关信息。 |
ONBUILD | 延迟指令 | 将指令标记为延迟执行,当该镜像被作为另一个构建的基础时才触发。 | ONBUILD COPY . /app/src |
用于构建基础镜像。 |
7.最佳实践
- 精简基础镜像: 使用 alpine、slim 等小型基础镜像减少攻击面和最终镜像大小。
- 合并 RUN 指令: 将多个 RUN 命令(特别是 apt-get update && install && clean)合并为一个,减少层数并清理包管理器缓存。
- 合理利用缓存: 将更改频率低的指令(如安装核心依赖)放前面,更改频率高的指令(如复制应用代码)放后面。
- 使用 .dockerignore 文件: 明确忽略构建上下文中的不必要文件。
- 优先使用 COPY 而非 ADD: 除非明确需要解压缩或从 URL 下载,否则用更简单直接的 COPY。
- 指定版本标签: FROM 和依赖安装时使用明确版本,避免因 latest 标签更新导致的意外行为。
- 减少镜像层数: 虽然层本身开销不大,但过多小层不利于管理,合并相关操作有益。
- 设置非 root 用户: 在 RUN 用户级步骤后使用 USER,避免容器以 root 权限运行应用,提高安全性。
- 使用多阶段构建: 分离构建环境和运行环境,大幅减小最终生产镜像体积(见示例)。
- 明确暴露端口: 使用 EXPOSE 声明容器监听的端口。
- 优先 exec 格式: 对 CMD 和 ENTRYPOINT 使用 JSON 数组格式 ["executable", "param1", "param2"] (exec 格式),避免不必要的 shell 进程并确保信号正确处理。
- 添加元数据: 使用 LABEL 提供镜像信息。
- 设置工作目录: 用 WORKDIR 替代 RUN cd ... && ...,使用绝对路径。
- 考虑安全性: 定期更新基础镜像和依赖包修复漏洞;避免在 Dockerfile 或镜像中存储硬编码的机密信息,使用 ARG 或 secrets(在运行时通过环境变量或卷传入)。
- 包含健康检查: 对关键服务使用 HEALTHCHECK 帮助编排系统管理容器状态。