Docker 是容器化技术的标杆,通过“打包应用及依赖到轻量级容器”,实现“一次构建、到处运行”,彻底解决“开发环境能跑、生产环境崩了”的痛点。本文从核心概念到实战操作,详解 Docker 镜像、容器、仓库三大基础,再深入 Dockerfile 构建、容器网络与存储配置,覆盖企业级运维高频场景,适配搜索引擎检索需求(如 Dockerfile 编写、Docker 容器网络配置、镜像仓库搭建、数据卷挂载),适合零基础入门到进阶实战。

一、Docker 核心认知:先搞懂“镜像/容器/仓库”是什么

Docker 生态的核心是“三要素”——镜像、容器、仓库,三者构成 Docker 应用部署的完整流程,先理清概念再动手,避免踩坑。

1. 核心概念拆解(类比理解更直观)

概念本质说明类比场景核心作用
镜像(Image)只读的“应用模板”,包含运行应用所需的代码、依赖、环境变量、配置文件(如 CentOS 系统+Nginx 服务)操作系统安装光盘、虚拟机模板作为容器的“创建基础”,不能直接运行
容器(Container)镜像的“可运行实例”,是独立的运行环境(基于镜像创建,可启动、停止、删除),具备隔离性启动后的虚拟机、独立的沙箱实际运行应用的载体,一个镜像可创建多个容器
仓库(Repository)存储镜像的“远程服务器”,类似代码仓库 Git,分为公有仓库(如 Docker Hub)和私有仓库软件下载站(如官网、应用商店)镜像的上传、下载、共享中心

2. Docker 核心优势(为什么要用 Docker?)

  • 环境一致:打包应用及所有依赖(如 Python 3.8、Nginx 1.20),避免“开发机用 Ubuntu、生产机用 CentOS”的兼容性问题;
  • 轻量级:容器共享宿主机内核,启动时间秒级(比虚拟机快 10 倍+),占用资源少(一台物理机可运行上百个容器);
  • 隔离性:容器间相互隔离(文件、网络、进程独立),一个容器故障不影响其他容器;
  • 可移植性:支持 Windows、Linux、Mac、云服务器等所有平台,打包后直接部署,无需修改配置;
  • 高效部署:从镜像启动容器仅需 1 条命令,替代传统“安装依赖→配置环境→启动服务”的复杂流程。

3. Docker 架构原理(简单理解)

  • 宿主机(Host):运行 Docker 的物理机/虚拟机;
  • Docker 引擎(Docker Engine):核心组件,包含守护进程(dockerd)、CLI 命令行工具,负责镜像管理、容器调度;
  • 容器(Container):基于镜像创建的运行实例,通过 Docker 引擎与宿主机内核交互,实现资源隔离。

二、Docker 环境准备:安装与基础配置(CentOS 7+)

先完成 Docker 安装,确保环境可用,后续所有实战基于此环境。

1. 安装 Docker(官方推荐步骤)

# 1. 卸载旧版本(若已安装)
yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine

# 2. 安装依赖工具
yum install -y yum-utils device-mapper-persistent-data lvm2

# 3. 配置 Docker 官方 yum 源(国内推荐阿里云镜像源,速度更快)
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 4. 安装 Docker CE(社区版,免费)
yum install -y docker-ce docker-ce-cli containerd.io

# 5. 启动 Docker 并设置开机自启
systemctl start docker
systemctl enable docker

# 6. 验证安装成功(输出版本信息即正常)
docker --version
docker info  # 查看 Docker 详细信息(包含镜像仓库、容器数量等)

2. 配置镜像加速(国内必备,解决拉取镜像慢)

默认 Docker Hub 位于国外,拉取镜像慢,配置阿里云/网易云加速:

# 1. 创建 Docker 配置目录
mkdir -p /etc/docker

# 2. 编写加速配置文件
tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"]  # 替换为你的阿里云加速地址(免费申请)
}
EOF

# 3. 重启 Docker 生效
systemctl daemon-reload
systemctl restart docker

# 4. 验证加速是否生效(输出配置的镜像源即成功)
docker info | grep "Registry Mirrors"

三、Docker 基础操作:镜像与容器管理(高频命令)

镜像和容器是日常操作的核心,掌握以下命令可解决 80% 的基础需求。

1. 镜像管理(拉取/查看/删除/构建)

# 1. 拉取镜像(从仓库下载,格式:docker pull 镜像名:标签,标签默认latest)
docker pull nginx  # 拉取最新版 Nginx 镜像
docker pull centos:7  # 拉取 CentOS 7 镜像(指定标签)
docker pull mysql:8.0  # 拉取 MySQL 8.0 镜像

# 2. 查看本地镜像(列出所有已下载的镜像)
docker images  # 简洁显示
docker images -a  # 显示所有镜像(包含中间层镜像)

# 3. 搜索镜像(从 Docker Hub 搜索可用镜像)
docker search redis  # 搜索 Redis 镜像(显示星级、描述、是否官方)

# 4. 删除镜像(格式:docker rmi 镜像ID/镜像名:标签)
docker rmi nginx:latest  # 通过镜像名+标签删除
docker rmi -f 镜像ID  # 强制删除(镜像被容器使用时需加 -f)
docker rmi -f $(docker images -q)  # 批量删除所有镜像(谨慎使用)

# 5. 保存/加载镜像(离线传输镜像)
docker save -o nginx.tar nginx:latest  # 保存镜像为本地文件
docker load -i nginx.tar  # 从本地文件加载镜像

2. 容器管理(创建/启动/停止/进入/删除)

容器基于镜像创建,操作流程:拉取镜像 → 创建容器 → 启动容器 → 操作容器。

(1)容器创建与启动
# 1. 基础创建并启动容器(格式:docker run [参数] 镜像名 命令)
# 示例1:启动 Nginx 容器(前台运行,按Ctrl+C停止)
docker run nginx

# 示例2:后台运行 Nginx 容器(-d 后台模式,--name 指定容器名)
docker run -d --name nginx-test -p 80:80 nginx
# 参数说明:
# -d:后台运行( detached )
# --name:给容器命名(便于后续操作)
# -p 80:80:端口映射(宿主机80端口 → 容器80端口,外部通过宿主机IP:80访问)

# 示例3:启动 CentOS 容器并进入交互模式(-it 交互终端)
docker run -it --name centos-test centos:7 /bin/bash
# 参数说明:
# -i:保持标准输入打开
# -t:分配伪终端(终端交互)
# /bin/bash:容器启动后执行的命令(进入 Bash 终端)
(2)容器状态管理
# 1. 查看容器状态(运行中/停止的容器)
docker ps  # 查看运行中的容器
docker ps -a  # 查看所有容器(包含停止的)
docker ps -q  # 只显示运行中容器的ID

# 2. 启动/停止/重启容器
docker start nginx-test  # 启动已停止的容器
docker stop nginx-test   # 优雅停止容器(发送 SIGTERM 信号)
docker kill nginx-test   # 强制停止容器(类似拔电源,慎用)
docker restart nginx-test  # 重启容器

# 3. 进入运行中的容器(两种方式)
# 方式1:exec(推荐,不影响容器运行,退出后容器继续存活)
docker exec -it nginx-test /bin/bash
# 方式2:attach(进入容器的主进程终端,退出后容器停止,不推荐)
docker attach nginx-test

# 4. 删除容器(需先停止容器,否则加 -f 强制删除)
docker stop nginx-test && docker rm nginx-test
docker rm -f centos-test  # 强制删除运行中的容器
docker rm -f $(docker ps -a -q)  # 批量删除所有容器(谨慎使用)

# 5. 查看容器日志(后台运行容器的输出,如 Nginx 访问日志)
docker logs nginx-test  # 查看所有日志
docker logs -f nginx-test  # 实时跟踪日志(类似 tail -f)
docker logs --tail 100 nginx-test  # 查看最后100行日志
(3)实战示例:Nginx 容器部署
# 1. 拉取 Nginx 镜像
docker pull nginx

# 2. 启动 Nginx 容器(后台运行+端口映射+自动重启)
docker run -d --name nginx-prod -p 80:80 --restart=always nginx
# --restart=always:容器退出时自动重启(生产环境必备)

# 3. 测试访问(宿主机IP:80,如 192.168.1.100:80,显示 Nginx 默认页面即成功)
curl http://192.168.1.100

# 4. 修改 Nginx 配置(进入容器编辑配置文件)
docker exec -it nginx-prod /bin/bash
vi /etc/nginx/nginx.conf  # 编辑配置
nginx -t  # 检查配置语法
nginx -s reload  # 重启 Nginx 生效

四、Dockerfile 实战:自定义镜像(核心技能)

官方镜像往往无法满足个性化需求(如预装软件、配置自定义参数),通过 Dockerfile 可手动构建专属镜像,是 Docker 进阶的核心。

1. Dockerfile 核心语法(常用指令)

Dockerfile 是文本文件,包含一系列指令,按顺序执行构建镜像,常用指令如下:

指令作用说明示例
FROM指定基础镜像(必须是 Dockerfile 第一行,如 centos:7、ubuntu:20.04)FROM centos:7
MAINTAINER指定镜像维护者信息(姓名+邮箱,可选)MAINTAINER zhangsan zhangsan@xxx.com
RUN构建镜像时执行的命令(如安装软件、创建目录)RUN yum install -y nginx
WORKDIR指定容器启动后的工作目录(类似 cd 命令,后续指令基于此目录执行)WORKDIR /usr/share/nginx/html
COPY将宿主机文件/目录复制到镜像中(复制本地文件)COPY index.html /usr/share/nginx/html
ADD复制文件/目录到镜像,支持 URL 下载和压缩包自动解压(比 COPY 功能强)ADD https://xxx.com/test.tar.gz /tmp
ENV设置环境变量(构建和容器运行时都生效)ENV JAVA_HOME /usr/local/jdk1.8
EXPOSE声明容器暴露的端口(仅文档说明,不实际映射,需配合 docker run -p)EXPOSE 80 443
CMD容器启动时执行的命令(一个 Dockerfile 只能有一个 CMD,多写覆盖)CMD [“nginx”, “-g”, “daemon off;”]
ENTRYPOINT容器启动时执行的命令(与 CMD 类似,但不可被 docker run 命令覆盖,优先级更高)ENTRYPOINT [“nginx”]

2. 实战:编写 Dockerfile 构建自定义 Nginx 镜像

需求:基于 CentOS 7 构建 Nginx 镜像,预装 curl 工具,自定义首页内容。

(1)创建 Dockerfile 目录及文件
# 1. 创建工作目录
mkdir -p /root/docker-nginx && cd /root/docker-nginx

# 2. 创建自定义首页文件 index.html
tee index.html <<-'EOF'
<!DOCTYPE html>
<html>
<head><title>自定义 Nginx 首页</title></head>
<body><h1>Hello Docker! 这是自定义镜像</h1></body>
</html>
EOF

# 3. 创建 Dockerfile 文件(核心)
tee Dockerfile <<-'EOF'
# 基础镜像
FROM centos:7

# 维护者信息
MAINTAINER devops <devops@xxx.com>

# 安装 Nginx 和 curl(yum 源更新+安装)
RUN yum clean all && \
    yum makecache fast && \
    yum install -y nginx curl && \
    yum clean all

# 设置工作目录
WORKDIR /usr/share/nginx/html

# 复制宿主机 index.html 到镜像
COPY index.html ./

# 声明暴露的端口
EXPOSE 80

# 容器启动时启动 Nginx(前台运行,避免容器退出)
CMD ["nginx", "-g", "daemon off;"]
EOF
(2)构建镜像(docker build 命令)
# 语法:docker build -t 镜像名:标签 构建上下文目录(. 表示当前目录)
docker build -t my-nginx:v1.0 .

# 构建过程说明:
# 1. 按 Dockerfile 指令顺序执行,每一步生成一个中间层镜像;
# 2. 若指令未修改,会复用缓存(加速构建);
# 3. 构建成功后,通过 docker images 查看镜像。

# 查看构建结果
docker images | grep my-nginx  # 显示 my-nginx:v1.0 即成功
(3)基于自定义镜像启动容器
# 启动容器(端口映射 8080:80)
docker run -d --name my-nginx-test -p 8080:80 my-nginx:v1.0

# 测试访问(输出自定义首页内容即成功)
curl http://192.168.1.100:8080

3. Dockerfile 构建避坑指南

  • 指令顺序优化:频繁修改的指令(如 COPY 本地文件)放在最后,利用缓存加速构建;
  • 减少层数:多个 RUN 指令用 && 合并(如示例中 yum 安装命令),减少中间层镜像体积;
  • 避免冗余:构建完成后清理无用文件(如 yum clean all),减小镜像大小;
  • CMD 与 ENTRYPOINT 区别:CMD 可被 docker run 命令覆盖(如 docker run my-nginx:v1.0 /bin/bash),ENTRYPOINT 不可覆盖,适合固定启动命令。

五、Docker 容器网络:实现容器间通信

容器默认是隔离的,需通过网络配置实现容器间、容器与外部的通信,Docker 提供 4 种网络模式,重点掌握桥接模式和自定义网络。

1. Docker 网络模式详解

网络模式核心原理适用场景
bridge(桥接模式)默认网络模式,Docker 自动创建 bridge 网桥,容器通过网桥获取私有 IP,实现容器间通信大多数场景(如 Web 容器+数据库容器通信)
host(主机模式)容器共享宿主机网络命名空间,容器无独立 IP,直接使用宿主机 IP 和端口需占用宿主机端口,追求性能(如高性能服务)
none(无网络模式)容器无网络配置,仅用于内部测试无需网络的离线任务(如数据处理)
container(容器模式)新容器共享已有容器的网络命名空间,与目标容器共用 IP 和端口需复用其他容器网络(如Sidecar模式)

2. 实战:容器间通信(桥接模式+自定义网络)

(1)默认桥接模式通信(基于容器名/IP)
# 1. 启动 MySQL 容器(后台运行,设置密码,命名为 mysql-db)
docker run -d --name mysql-db -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql:8.0

# 2. 启动 Web 容器(自定义 Nginx 镜像),与 MySQL 容器通信
docker run -d --name web-app -p 80:80 my-nginx:v1.0

# 3. 进入 Web 容器,测试连接 MySQL(需安装 mysql 客户端)
docker exec -it web-app /bin/bash
yum install -y mysql
mysql -h mysql-db -u root -p123456  # -h 指定容器名(Docker 自动解析为容器 IP)
# 连接成功说明容器间通信正常
(2)自定义网络(推荐,更稳定,支持域名解析)

默认桥接模式下,容器重启后 IP 可能变化,自定义网络可固定域名解析:

# 1. 创建自定义桥接网络(名称:my-network,驱动:bridge)
docker network create my-network

# 2. 启动 MySQL 容器,加入自定义网络
docker run -d --name mysql-db -e MYSQL_ROOT_PASSWORD=123456 --network my-network mysql:8.0

# 3. 启动 Web 容器,加入同一自定义网络
docker run -d --name web-app -p 80:80 --network my-network my-nginx:v1.0

# 4. 测试通信(Web 容器内可通过容器名 mysql-db 访问 MySQL,IP 变化不影响)
docker exec -it web-app curl mysql-db:3306  # 测试 MySQL 端口连通性

3. 容器端口映射(外部访问容器)

容器内服务需对外提供访问时,通过 -p--publish 映射端口:

# 格式1:-p 宿主机端口:容器端口(指定宿主机端口)
docker run -d -p 8080:80 nginx  # 外部访问 宿主机IP:8080 → 容器 80 端口

# 格式2:-p 宿主机IP:宿主机端口:容器端口(指定宿主机 IP,仅该 IP 可访问)
docker run -d -p 192.168.1.100:8080:80 nginx

# 格式3:-p 宿主机端口:容器端口/协议(指定 TCP/UDP)
docker run -d -p 53:53/udp dnsmasq  # UDP 端口映射

六、Docker 容器存储:数据持久化(数据卷/绑定挂载)

容器默认存储是临时的,容器删除后数据丢失,需通过“数据卷(Volume)”或“绑定挂载”实现数据持久化(如数据库数据、配置文件)。

1. 两种存储方式对比

存储方式本质说明适用场景
数据卷(Volume)Docker 管理的宿主机目录(/var/lib/docker/volumes/),与容器解耦,由 Docker 维护数据库数据、频繁修改的业务数据
绑定挂载(Bind Mount)直接挂载宿主机任意目录到容器,容器与宿主机目录直接关联配置文件、开发环境代码(实时同步)

2. 实战 1:数据卷(Volume)持久化 MySQL 数据

# 1. 创建数据卷(名称:mysql-data)
docker volume create mysql-data

# 2. 查看数据卷详情(路径:/var/lib/docker/volumes/mysql-data/_data)
docker volume inspect mysql-data

# 3. 启动 MySQL 容器,挂载数据卷(-v 数据卷名:容器内数据目录)
docker run -d --name mysql-db \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v mysql-data:/var/lib/mysql \  # 挂载数据卷到 MySQL 数据目录
  -p 3306:3306 \
  mysql:8.0

# 4. 测试数据持久化:
# ① 进入容器创建测试数据库
docker exec -it mysql-db mysql -u root -p123456 -e "create database test_db;"
# ② 删除容器
docker rm -f mysql-db
# ③ 重新启动容器,挂载同一数据卷
docker run -d --name mysql-db-new \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v mysql-data:/var/lib/mysql \
  -p 3306:3306 \
  mysql:8.0
# ④ 验证数据存在(test_db 仍存在,说明数据持久化成功)
docker exec -it mysql-db-new mysql -u root -p123456 -e "show databases;"

3. 实战 2:绑定挂载(Bind Mount)同步配置文件

# 场景:Nginx 容器,挂载宿主机自定义配置文件和首页
# 1. 宿主机创建配置文件和首页目录
mkdir -p /root/nginx/conf /root/nginx/html
# 2. 编写自定义 Nginx 配置(/root/nginx/conf/nginx.conf)
# 3. 编写首页文件(/root/nginx/html/index.html)
# 4. 启动容器,绑定挂载宿主机目录
docker run -d --name nginx-bind \
  -p 80:80 \
  -v /root/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \  # 挂载配置文件
  -v /root/nginx/html:/usr/share/nginx/html \  # 挂载首页目录
  nginx

# 测试:修改宿主机 /root/nginx/html/index.html,访问容器地址,内容实时更新

4. 存储避坑点

  • 数据卷权限:容器内用户可能无宿主机目录权限,挂载时指定权限(如 -v 数据卷名:容器目录:rw,rw 为可读可写);
  • 绑定挂载路径:宿主机路径必须是绝对路径(如 /root/nginx,不能写 root/nginx);
  • 数据备份:数据卷备份可通过 docker cp 命令(docker cp 容器ID:容器目录 宿主机目录)。

七、Docker 仓库:私有镜像仓库搭建(企业级必备)

Docker Hub 是公有仓库,企业内部镜像需私有仓库(如 Harbor、Docker Registry),此处以简单的 Docker Registry 为例。

1. 搭建私有仓库(Docker Registry)

# 1. 拉取 Registry 镜像
docker pull registry:2

# 2. 启动私有仓库容器(挂载数据卷,持久化镜像)
docker run -d --name my-registry \
  -p 5000:5000 \
  -v registry-data:/var/lib/registry \  # 挂载数据卷,存储镜像
  --restart=always \
  registry:2

# 3. 验证仓库启动成功(访问 http://宿主机IP:5000/v2/_catalog,返回{"repositories":[]}即正常)
curl http://192.168.1.100:5000/v2/_catalog

2. 镜像上传/下载(私有仓库)

# 1. 本地镜像打标签(格式:私有仓库地址:端口/镜像名:标签)
docker tag my-nginx:v1.0 192.168.1.100:5000/my-nginx:v1.0

# 2. 上传镜像到私有仓库
docker push 192.168.1.100:5000/my-nginx:v1.0

# 3. 验证上传成功(查看仓库镜像列表)
curl http://192.168.1.100:5000/v2/_catalog  # 显示 {"repositories":["my-nginx"]}

# 4. 从私有仓库拉取镜像(其他服务器)
docker pull 192.168.1.100:5000/my-nginx:v1.0

3. 私有仓库安全配置(可选,添加认证)

默认私有仓库无认证,添加用户名密码认证:

# 1. 创建认证目录和用户(用户名:admin,密码:123456)
mkdir -p /root/registry-auth
docker run --rm --entrypoint htpasswd registry:2 -Bbn admin 123456 > /root/registry-auth/htpasswd

# 2. 重启私有仓库,启用认证
docker rm -f my-registry
docker run -d --name my-registry \
  -p 5000:5000 \
  -v registry-data:/var/lib/registry \
  -v /root/registry-auth:/auth \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  --restart=always \
  registry:2

# 3. 上传/下载前需登录私有仓库
docker login 192.168.1.100:5000  # 输入用户名 admin,密码 123456
docker push 192.168.1.100:5000/my-nginx:v1.0  # 登录后可上传

八、常见问题排查(新手必看)

1. 容器启动失败,提示“端口已被占用”

  • 原因:宿主机端口已被其他服务占用;
  • 解决:更换宿主机端口(如 -p 8081:80),或停止占用端口的服务(netstat -tulnp | grep 80 查看占用进程)。

2. 容器内无法访问外网

  • 原因:Docker 网络配置错误,或宿主机防火墙拦截;
  • 解决:检查宿主机 DNS 配置(/etc/resolv.conf),开放 Docker 网桥端口(firewall-cmd --permanent --add-masquerade)。

3. 数据卷挂载后,容器内无权限访问

  • 原因:宿主机目录/数据卷权限不足,容器内用户无读写权限;
  • 解决:修改宿主机目录权限(chmod 777 /root/nginx/html),或启动容器时指定用户(--user root)。

4. 私有仓库上传失败,提示“http: server gave HTTP response to HTTPS client”

  • 原因:Docker 默认要求私有仓库使用 HTTPS,本地仓库为 HTTP;
  • 解决:配置 Docker 允许 HTTP 仓库,编辑 /etc/docker/daemon.json,添加 "insecure-registries": ["192.168.1.100:5000"],重启 Docker。

更多推荐