第一部分:为什么要用 Docker?
传统部署的痛点
在没有容器的年代,部署一个应用是件令人头疼的事:
-
环境不一致:开发环境跑得好好的,一上测试服务器就各种报错------"缺少依赖库"、"Python版本不对"、"系统库版本冲突"
-
环境冲突:同一台服务器上部署多个应用时,A应用需要Java 8,B应用需要Java 11,升级或降级JDK会互相影响
-
资源浪费:为了隔离环境,不得不为每个应用单独租用一台物理机或虚拟机,资源利用率低
-
部署繁琐:部署一套复杂的微服务架构可能需要配置十几个服务,手动操作极易出错
Docker 如何解决这些问题?
Docker通过容器(Container) 技术实现应用隔离。容器与虚拟机不同:
| 对比维度 | 虚拟机 | Docker容器 |
|---|---|---|
| 启动速度 | 分钟级(需启动完整操作系统) | 秒级(共享宿主机内核) |
| 资源开销 | GB级(每个VM包含完整OS) | MB级(只包含应用和依赖) |
| 隔离级别 | 硬件虚拟化,强隔离 | 进程级隔离(namespace + cgroups) |
| 迁移能力 | 需要导出虚拟机镜像 | 通过Docker镜像轻松迁移 |
Docker在操作系统层面实现了应用及其运行环境的标准化封装,核心解决思路是:
应用 + 运行时环境 + 配置 = Docker镜像 → 在任何能运行Docker的机器上启动为容器
第二部分:Docker 核心概念
三大基石:镜像、容器、仓库
镜像(Image) 是Docker世界的"程序安装包"。它是一个只读的静态文件,包含了运行应用所需的一切:代码、运行时、系统工具、库和设置。类似于面向对象编程中的"类"------定义了应用所需的所有环境和依赖,但本身不运行。
容器(Container) 是镜像的"运行实例"。你从同一个镜像可以启动无数个容器,每个容器之间相互隔离。类似于面向对象编程中的"对象"------是镜像的具体运行实例,拥有独立的进程空间、网络和存储。
仓库(Repository) 是存储和分发镜像的地方。Docker Hub 是最著名的公共仓库,类似代码世界的GitHub。你可以从仓库拉取(pull)镜像,也可以把自己构建的镜像推送(push)到仓库供他人使用。
镜像(静态定义) ==docker run==> 容器(动态运行)
↑ ↑
docker pull docker start/stop
↑ ↑
仓库(分发中心)
第三部分:Docker 安装与环境配置
Linux 安装(以Ubuntu为例)
# 更新软件源
sudo apt update
# 安装依赖包
sudo apt install apt-transport-https ca-certificates curl software-properties-common
# 添加Docker官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 添加Docker稳定版仓库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# 更新源并安装Docker引擎
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
# 启动Docker服务
sudo systemctl start docker
sudo systemctl enable docker # 设置开机自启
# 验证安装
docker --version # 显示Docker版本信息
# 将当前用户加入docker组(避免每次使用sudo)
sudo usermod -aG docker $USER
# 然后重新登录使权限生效
第四部分:镜像管理 ------ Docker 的基石
镜像的基本操作
# 搜索镜像(在Docker Hub上搜索)
docker search nginx
# 拉取镜像(从仓库下载到本地)
docker pull nginx:latest # 拉取最新版
docker pull nginx:1.21 # 拉取指定版本
docker pull redis:alpine # 拉取轻量级Alpine版本
# 列出本地所有镜像
docker images
# 或
docker image ls
# 查看镜像详细信息
docker inspect nginx:latest
# 删除镜像
docker rmi nginx:1.21 # 删除指定镜像
docker rmi -f nginx:1.21 # 强制删除(即使有容器依赖)
docker image prune # 清理所有未被使用的镜像(悬空镜像)
# 给镜像打标签(用于推送到自己的仓库)
docker tag nginx:latest myhub/nginx:my-version
# 推送镜像到仓库
docker push myhub/nginx:my-version
# 导出镜像为tar文件
docker save -o nginx.tar nginx:latest
# 从tar文件加载镜像
docker load -i nginx.tar
镜像的分层结构
Docker镜像由多个只读层(Layer) 叠加而成。每一层对应Dockerfile中的一条指令。这种分层结构带来两个巨大优势:
-
共享与复用:多个镜像可以共享相同的基础层(如Ubuntu基础层),节省磁盘空间
-
增量更新:构建新版本时只需重新构建变更的层,其余层使用缓存,大幅加速构建
# 查看镜像的构建历史(展示分层) docker history nginx:latest
第五部分:容器管理 ------ 镜像的生命周期
容器的完整生命周期
# 运行容器(从镜像创建并启动)
docker run nginx:latest
# 带参数运行
docker run -d --name mynginx -p 8080:80 nginx:latest
# -d:后台运行(detach模式)
# --name:为容器指定名称
# -p 8080:80:端口映射,宿主机8080端口映射到容器的80端口
# 查看正在运行的容器
docker ps
# 查看所有容器(包括已停止的)
docker ps -a
# 停止容器
docker stop mynginx
# 启动已停止的容器
docker start mynginx
# 重启容器
docker restart mynginx
# 暂停容器(冻结所有进程)
docker pause mynginx
# 恢复暂停的容器
docker unpause mynginx
# 删除容器(需先停止)
docker rm mynginx
docker rm -f mynginx # 强制删除(运行中的容器也会被删除)
# 批量删除所有停止的容器
docker container prune
# 进入运行中的容器(交互式)
docker exec -it mynginx /bin/bash
# -i:交互模式,-t:分配伪终端
# /bin/bash:在容器中执行的命令
# 查看容器日志
docker logs mynginx
docker logs -f mynginx # 实时追踪日志(类似tail -f)
# 查看容器进程
docker top mynginx
# 查看容器资源占用(CPU、内存等)
docker stats mynginx
docker run 的常用参数
| 参数 | 说明 | 示例 |
|---|---|---|
-d |
后台运行(detach) | docker run -d nginx |
--name |
指定容器名称 | --name mynginx |
-p |
端口映射:宿主机:容器 | -p 8080:80 |
-P |
随机映射端口(自动分配) | -P |
-v |
挂载卷(数据持久化) | -v /host/data:/container/data |
-e |
设置环境变量 | -e MYSQL_ROOT_PASSWORD=123456 |
--env-file |
从文件加载环境变量 | --env-file .env |
--restart |
重启策略 | --restart=always |
--network |
指定网络 | --network bridge |
-it |
交互式终端(组合) | -it /bin/bash |
第六部分:Dockerfile ------ 构建专属镜像
Dockerfile 基础指令
Dockerfile 是一个文本文件,包含构建镜像所需的所有命令。每个命令都会创建一个新的镜像层。
# 指定基础镜像(所有镜像必须从基础镜像开始)
FROM ubuntu:20.04
# 设置维护者信息
LABEL maintainer="jack@example.com"
# 设置环境变量
ENV APP_HOME /app
ENV APP_VERSION 1.0
# 在构建时执行命令(安装软件、创建目录等)
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/* # 清理缓存减小镜像体积
# 创建工作目录
WORKDIR $APP_HOME
# 复制文件到镜像中(从构建上下文复制到镜像内)
COPY requirements.txt .
COPY app.py .
# 更灵活的复制(支持通配符,保留元数据)
ADD https://example.com/file.tar.gz /tmp/ # ADD支持远程URL和解压
# 安装Python依赖
RUN pip3 install -r requirements.txt
# 声明容器运行时监听的端口(仅是文档说明,不会自动映射)
EXPOSE 5000
# 设置容器启动时执行的默认命令
CMD ["python3", "app.py"]
# 或使用ENTRYPOINT(结合CMD使用)
ENTRYPOINT ["python3"]
CMD ["app.py"]
FROM、RUN、CMD、ENTRYPOINT 的区别
这几个指令初学者容易混淆,必须彻底搞清楚:
| 指令 | 执行时机 | 作用 | 特点 |
|---|---|---|---|
| FROM | 构建时 | 指定基础镜像 | 必须作为第一条指令 |
| RUN | 构建时 | 安装软件、修改配置、创建文件 | 会创建新的镜像层,影响镜像体积 |
| CMD | 容器启动时 | 指定默认启动命令 | 可以被 docker run 命令行参数覆盖;一个Dockerfile中只能有一条CMD |
| ENTRYPOINT | 容器启动时 | 指定容器的主程序 | 不会被 docker run 命令行参数覆盖(参数会追加);更适合配置"固定执行程序"的容器 |
最佳实践组合:
# 固定ENTRYPOINT为可执行程序,CMD提供默认参数
ENTRYPOINT ["python3"]
CMD ["app.py"]
# 运行时可以覆盖CMD:
docker run myimage main.py # 实际执行:python3 main.py
构建镜像
# 构建镜像(.表示使用当前目录作为构建上下文)
docker build -t myapp:1.0 .
# 构建时指定Dockerfile路径
docker build -f /path/to/Dockerfile -t myapp:1.0 .
# 构建时不使用缓存(强制重新构建所有层)
docker build --no-cache -t myapp:1.0 .
构建上下文(Build Context)
构建时,Docker会将指定的目录(上例中的 .)打包上传给Docker守护进程。这意味着构建目录中的文件都会被发送到Docker引擎。因此:
-
不要把整个
node_modules或target目录作为构建上下文 -
使用
.dockerignore文件排除不需要的文件
.dockerignore
类似 .gitignore,放在构建目录根目录,用于排除文件:
node_modules/
*.log
.git/
.DS_Store
*.tmp
第七部分:数据管理 ------ 让容器有"记忆"
容器是临时的,删除容器后其中的数据也会丢失。要持久化数据,有三种方式:
1. 绑定挂载(Bind Mount)------开发最常用
将宿主机目录挂载到容器内,双向同步,适合开发环境的热更新。
# 将宿主机的 ./app 目录挂载到容器的 /app
docker run -v $(pwd)/app:/app myapp:1.0
# 现代写法(--mount更明确)
docker run --mount type=bind,source=$(pwd)/app,target=/app myapp:1.0
2. 数据卷(Volume)------生产环境推荐
由Docker管理的独立存储区域,与宿主机目录分离,更安全更易迁移。
# 创建数据卷
docker volume create mydata
# 查看所有数据卷
docker volume ls
# 挂载数据卷到容器
docker run -v mydata:/app/data myapp:1.0
# 查看数据卷详情
docker volume inspect mydata
# 删除数据卷
docker volume rm mydata
# 清理未使用的数据卷
docker volume prune
总结:
Docker 的核心思想是标准化交付。它把应用和它的依赖一起打包成一个可移植的单元,从此运维不再需要关心应用内部的具体实现,开发也不再需要担心环境差异。
Docker 的学习路径建议:
1.先会用 :掌握 docker run、docker ps、docker exec 等基础命令
2.再会造:学会写 Dockerfile,把自己的应用打包成镜像
3.再会管:掌握数据卷、网络、Compose,管理多容器应用
4.再会优:理解层缓存、多阶段构建、安全配置等高级技巧
5.再往上走:进入 Kubernetes 的世界------那是容器编排的下一站
容器化是云计算时代的核心技能,掌握了 Docker,你就拿到了通往现代化开发与运维世界的入场券。