Docker 架构采用客户端 - 服务器(C/S)模型,由多个核心组件协同工作,实现容器的创建、运行、分发和管理。
主要包含以下组件:

1.docker images
功能:列出本地镜像
语法:docker images 【选项】 【仓库名称【:tag】】
该命令还有别名:docker image ls,docker image list
选项:
2.docker image inspect
功能:查看具体一个镜像的信息
语法:docker image inspect image:tag|imageid
3.docker tag
功能:标记本地镜像(重命名),将其归入某一仓库中
语法:docker tag source_image[:tag] target_image[:tag]
4.docker run
功能:创建一个容器并运行一个目录
语法:docker run 【选项】 image 【命令】
别名:docker run container
当你执行
docker run centos:7后容器立即退出,是因为 容器内没有持续运行的进程,这是 Docker 容器的核心特性决定的:Docker 容器的生命周期与容器内的 主进程(PID 1) 绑定:
简单说:容器需要一个 "永不结束" 的主进程才能保持运行状态,否则会随主进程退出而终止。
CentOS 7 镜像的默认行为是启动 /bin/bash 作为主进程,但:
-it),bash 会因为没有终端输入而立即退出
exit 或 Ctrl+D 退出 bash,主进程结束,容器也会停止
要让 CentOS 7 容器持续运行,可以:
1,交互模式运行(适合临时操作):docker run -it centos:7 /bin/bash
此时会进入容器的交互式终端,退出终端后容器会停止。
2,后台运行并保持进程(适合长期运行):docker run -d centos:7 tail -f /dev/null
用 tail -f 命令保持主进程持续运行,容器会在后台一直运行。
--link选项用于在两个容器之间建立单向链接,使一个容器可以通过别名访问另一个容器(无需知道对方的 IP 地址)。这是早期 Docker 中实现容器间通信的常用方式,不过目前更推荐使用网络(network) 来管理容器通信。基本语法 :docker run --name 容器名 --link 目标容器名:别名 镜像名
链接后,当前容器的 /etc/hosts 文件会自动添加一条记录,将 “别名” 映射到目标容器的 IP 地址。
5.docker ps
功能:列出正在运行的容器
语法:docker ps 选项
6.docker history
功能:查看镜像历史
语法:docker history 选项 image/imageid
-H,--human:大小和日期采用人能读懂的格式 展现
--no-trunc:显示全部信息,不要隔断
-q,--quiet:只显示镜像id信息
7.docker image prune
功能:删除不使用的镜像
语法:docker image prune 选项
scp(Secure Copy)是一个基于 SSH 协议的文件传输命令,用于在本地主机和远程主机之间安全地复制文件或目录。它使用与 SSH 相同的加密机制,确保数据传输的安全性。
scp基本语法
# 本地文件复制到远程
scp [选项] 本地文件路径 远程用户@远程IP:远程目标路径
# 远程文件复制到本地
scp [选项] 远程用户@远程IP:远程文件路径 本地目标路径
# 复制目录(需加 -r 选项)
scp -r [选项] 本地目录路径 远程用户@远程IP:远程目标路径
scp -r [选项] 远程用户@远程IP:远程目录路径 本地目标路径Docker 镜像是 Docker 技术的核心组成部分,可以理解为一个包含应用程序及其所有运行依赖的只读模板,是容器运行的基础。本质上是由多层文件系统叠加而成的特殊文件集合。
一个完整的 Docker 镜像包含运行应用所需的全部要素:
可以简单理解为:
镜像 = 类(Class),容器 = 实例(Instance)。
镜像定义了应用的 “模板”,容器则是这个模板的 “运行状态”,一个镜像可以生成多个容器,容器的生命周期独立于镜像。
总结来说,Docker 镜像通过标准化打包和分层设计,解决了 “环境一致性”“依赖管理”“快速分发” 等传统部署难题,是实现 “一次构建,到处运行” 的核心保障。
Docker 镜像作为容器技术的核心组件,解决了传统软件部署和运行中的一系列痛点问题,主要体现在以下几个方面:
nginx:1.21、mysql:8.0)。迭代时只需构建新镜像并打上标签,回滚时直接使用旧标签的镜像即可。此外,镜像的分层结构允许查看每一层的变更记录,便于追溯版本差异。
.tar.gz、.exe),部署时需手动传输安装包并执行复杂的安装脚本,耗时且易出错,尤其在大规模集群部署时效率极低。
docker run 命令即可基于镜像启动容器,实现 “一次构建,到处运行”,显著简化大规模部署流程。
Docker 容器是基于 Docker 镜像运行的可执行实例,是镜像的动态表现形式。它包含了应用程序运行所需的完整环境(代码、运行时、依赖、配置等),并通过操作系统级虚拟化技术实现了资源隔离和独立运行,是 Docker 技术中用于实际运行应用的核心载体。
容器的生命周期是容器可能处于的状态。
created:初建状态,running:运行状态,stopped:停止状态,paused:暂停状态,deleted:删除状态

1,docker logs
功能:查看容器日志
语法:docker logs 【选项】 container
参数:
-f,--follow:跟踪日志输出
--since:显示某个开始时间的所有日志。
-t,--timestamps:显示时间戳
-n,--tail:仅列出最新的n条日志
2,docker exec
功能:在容器中执行命令
语法:docker exec 【选项】container command 【args】
参数:
-d:在后台运行
-i:进行交互
-t:分配一个伪终端
-e:设置环境变量
-u,--user:指定用户
-w:指定工作目录
3,docker stop
功能 :停止运行的容器
4,docker restart
功能:重启容器
5,docker kill
功能:强制退出容器
6,docker top
功能:查看容器中的 进程信息
7,docker stats
功能:查看容器资源的使用情况,包括CPU,内存,网路I/O等
8,docker cp
功能:在容器和宿主机之间拷贝文件
9,docker commit
功能:从容器创建一个新的镜像
语法:docker commit 选项 container image[:tag]
-a:提交镜像作者
-c:通过dockerfile指令来创建镜像,可以修改启动指令
-m:提交时的文字说明
-p:在commit时,将容器暂停掉
10,docker export
功能:将容器转为tar文件
语法:docker export 选项 container
-o:写入到文件
11,docker import
功能:从归档文件中创建镜像
语法:docker import 【选项】 file image[:tag]
12.docker update
docker update 是 Docker 命令行工具中用于动态更新容器配置的命令
语法:docker update [OPTIONS] CONTAINER [CONTAINER...]
常用选项
--cpus:设置容器可以使用的 CPU 核心数(例如 --cpus 0.5 表示使用半个核心)
--cpu-shares:设置 CPU 共享权重(相对优先级)
--memory 或 -m:设置容器可以使用的最大内存量(例如 -m 512m)
--memory-swap:设置容器可以使用的内存 + 交换分区总量
--restart:更改容器的重启策略(例如 --restart always)
适用于将 Docker 镜像在不同主机、仓库之间迁移。 1. 在源主机保存镜像为 tar 文件 docker save -o 镜像名.tar 镜像名:标签 # 例:docker save -o nginx.tar nginx:latest 2. 通过 scp/ftp 等工具将 tar 文件传输到目标主机 scp 镜像名.tar 目标用户@目标IP:/路径 # 例:scp nginx.tar user@192.168.1.100:/tmp 3. 在目标主机加载镜像 docker load -i 镜像名.tar
attached模式
容器运行语句示例:docker run --name mytest1 -p 8100:80 nginx:1.24.0(不加-d,-it选项)
通过上述方式创建容器,就是attached模式,这样容器会在前台运行。
detached模式
在容器运行语句中加上-d选项,就是detached模式,表示在后台运行。
使用docker attach 容器名,可以 让一个detached模式的容器转化为attached模式。
interactive模式
创建一个interactive(交互 )模式的容器,需要带上-it选项。
创建运行容器并进入到交互模式:docker run -it --name mytest1 -p 8100:80 nginx:1.24.0 bash
针对一个已经运行的容器进入交互模式:
docker run --name mytest5 -d nginx:1.24.0
docker exec -it mytest5 bash
在 Docker 中,存储卷(Volumes) 是用于持久化存储容器数据的机制,它将容器内的数据与容器本身的生命周期解耦,确保容器删除或重建后数据不会丢失。存储卷本质上是宿主机文件系统中的一段特殊目录,由 Docker 管理,与容器内的指定路径形成映射。
/var/lib/docker/volumes/<卷名>/_data),具体路径由 Docker 自动分配。
Dockerfile 的 VOLUME 指令定义)
/home/user/data)。
/etc/hosts、日志配置)到容器。
1. 容器的 “临时性” 与数据的 “持久性” 矛盾
/var/lib/mysql 目录即可解决。
2.容器间的数据共享需求
/etc/config)。
3. 宿主机与容器的数据交互需求
总结
存储卷的核心价值是解决容器与数据的生命周期分离问题,同时满足数据持久化、跨容器共享、宿主机交互、镜像精简、跨环境兼容等需求。无论是数据库、日志、配置文件还是用户数据,只要需要长期保留或多组件共享,就必须使用存储卷。
简单来说:没有存储卷,容器就只能作为 “无状态服务” 运行;有了存储卷,容器才能可靠地处理 “有状态数据”。
1.创建卷
# 创建命名卷(最常用)
docker volume create <卷名称>
//如果不指定卷名称,docker会随机生成
# 示例:创建一个名为 db_data 的卷
docker volume create db_data2.查看卷列表
# 列出所有卷
docker volume ls
# 过滤查看(例如只看本地卷)
docker volume ls --filter driver=local3.查看卷详情
# 查看指定卷的详细信息(包括存储路径、驱动等)
docker volume inspect <卷名称>
# 示例:查看 db_data 卷的信息
docker volume inspect db_data
输出结果中会显示卷在宿主机的实际路径(如 /var/lib/docker/volumes/db_data/_data)。4.删除卷
# 删除指定卷(需确保卷未被任何容器使用)
docker volume rm <卷名称>
# 示例:删除 db_data 卷
docker volume rm db_data5.清理未使用的卷
# 删除所有未被容器引用的卷(谨慎操作)
docker volume prune
# 强制删除(无需确认)
docker volume prune -f1.创建管理卷
-v:创建管理卷,并启动容器
# 运行容器时挂载卷
docker run -d --name <容器名> -v <卷名称>:<容器内目录> <镜像名>
# 示例:将 db_data 卷挂载到 MySQL 容器的数据目录
docker run -d --name mysql_db -v db_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0-mount:创建管理卷,并启动容器
# 运行容器时挂载卷
docker run -d --name <容器名> --mount src=卷名称,dst=容器内目录 <镜像名>
# 示例:将 db_data 卷挂载到 MySQL 容器的数据目录
docker run -d --name mysql_db --mount src=db_data,dst=/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0卷的共享
# 容器1挂载卷
docker run -d --name app1 -v shared_data:/data nginx
# 容器2挂载同一个卷(实现数据共享)
docker run -d --name app2 -v shared_data:/data nginx2.创建绑定卷
-v:创建绑定卷
语法:docker run -v name:directory[:options] ...
第一个参数name为宿主机的一个目录,这和管理卷是不一样的,如果是管理卷,表示卷的名称。
第二个参数directory:映射到容器中的目录
第三个参数options:选项,比如ro表示readonly
如果指定的宿主机的目录不存在,会建立该目录--mount:创建绑定卷
语法:docker run --mount type=bind,src= ,dst= ...
需要指明类型为bind
src:宿主机目录
dst:容器内目录
同样也可以指定ro选项
如果指定的宿主机的目录不存在,会报错如果宿主机指定的目录和容器指定的目录下都存在文件,以宿主机的目录为准,容器目录的文件会同步与宿主机目录下的文件一致。
3.创建临时卷
临时卷tmpfs的局限性:不能在容器之间共享,只能在linux系统上运行
--tmpfs创建:
语法:docker run --tmpfs 容器内目录 ...
--mount创建:
语法:docker run --mount type=tmpfs,dst=容器内目录 ....
参数:tmpfs-size:卷的大小(以字节为单位),默认是无限制的
容器的本质是 “被隔离的进程”,它拥有独立的文件系统、进程空间、用户空间,但网络不能完全隔离(否则容器就是一个 “孤岛”,无法发挥作用)。
具体来说,Docker 需要网络的核心原因包括:
1.docker network create
功能:创建自定义网络
语法:docker network create 【选项】 NETWORK
参数:
-d,--driver:网络驱动
--gateway:网关地址
--subnet:表示网段的CIDR格式的子网
--ipv:启用ipv6
2.docker netowrk inspect
功能:查看网络详情
语法:docker netowrk inspect 【选项】NETWORK
参数:
-f,--format:指定输出格式(JSON/table)
3.docker network connect
功能:将容器连接到网络,可以按名称或ID连接容器。一旦连接,容器可以与网络中的其他容器通信。
语法:docker network connect 【选项】 NETWORK CONTAINER
参数:
--ip:指定ip地址
--ipv6:指定ipv6地址
4.docker network disconnect
功能:断开网络
语法:docker network disconnect 【选项】 NETWORK CONTAINER
参数:
-f:强制断开
5.docker network prune
功能:删除不使用的网络
6.docker network rm
功能:删除一个或多个网络
语法:docker network rm NETWORK
7.docker network ls
功能:展示出 创建 的网络
参数:
-f,--filter:指定过滤条件
--format:指定格式
--no-trunc:不截断
-q,--quiet:只显示id
Docker 通过不同的网络驱动提供多种网络类型,默认包含 5 种常用驱动,用户也可安装第三方驱动。
docker0的默认网桥(自定义 bridge 网络会创建独立网桥),容器通过 veth pair 接入网桥,获得私有 IP(如172.17.x.x)。
bridge 桥接模式可以参考下图:

bridge网络对应的网桥就是docker0


容器间可通过 IP 或名称(自定义 bridge 网络支持)通信,与外部通信需通过宿主机的 NAT 转换。
# 创建自定义bridge网络
docker network create my-bridge
# 启动容器并接入该网络
docker run -d --name app --network my-bridge nginx
#如果不指定网络,会接入默认的bridge网络
# 容器的80端口直接映射到宿主机80端口
docker run -d --network host nginx Docker Container 共享其他容器的网络环境,则至少这两个容器之间不存在网络隔离,而这两个容器又与宿主机以及除此之外其他的容器存在网络隔离。

示例:
#创建容器1
docker run -dit --name container1 busybox
# 使用容器1的网络创建容器2
docker run -dit --name container2 --network container:container1 busybox# 容器无网络连接
docker run -d --network none busybox Docker Compose 是 Docker 官方提供的一个工具,用于定义和运行多容器 Docker 应用程序。它允许你通过一个 YAML 格式的配置文件(通常命名为 docker-compose.yml)来声明式地配置应用程序所需的所有服务(容器),然后使用单一条命令即可创建和启动所有服务。
当你的应用需要多个容器协同工作时(比如 WordPress 需要 WordPress 容器和 MySQL 容器配合),手动逐个启动容器并配置它们之间的网络连接会很繁琐。Docker Compose 可以:
docker compose up -d)
docker compose down)
1.顶级参数
version 指定 Compose 文件格式版本(需与 Docker 版本兼容),推荐使用 '3.8'(较新且兼容性好)。 示例:version: '3.8'
2. services 服务配置(核心)
每个服务对应一个容器,是配置的核心部分,常见参数:
示例:
ports:
- "8080:80" # 主机 8080 映射到容器 80
- "443:443" # 映射 HTTPS 端口services:
app: #服务名
networks:
- my-network # 加入自定义网络
networks:
my-network: # 定义网络volumes 数据卷挂载,用于持久化数据或共享文件(主机路径:容器路径)。 示例:
volumes:
- ./html:/var/www/html # 主机目录挂载到容器
- data-volume:/var/lib/mysql # 使用命名卷(需在顶级 volumes 定义)
volumes:
data-volume: # 定义命名卷environment:
- MYSQL_ROOT_PASSWORD=root123
- DEBUG=false
# 或字典形式
environment:
WORDPRESS_DB_HOST: dbdepends_on:
- db # 依赖 db 服务
- redis:
condition: service_healthy # 等待 redis 健康检查通过healthcheck:
test: mysql -uroot -proot -e 'select 1;'
interval: 10s
timeout: 5s
retries: 101. 启动服务
docker compose up
功能:创建并启动所有服务(根据 docker-compose.yml 配置)
常用选项:
-d:后台运行容器( detached 模式),如 docker compose up -d
--build:启动前重新构建服务的镜像,如 docker compose up --build2.停止并删除服务
docker compose down
功能:停止并删除所有容器、网络,默认保留数据卷(volumes)
常用选项:
-v:同时删除数据卷,如 docker-compose down -v
--rmi all:删除所有相关镜像3.查看服务状态
docker compose ps
功能:列出所有正在运行的服务容器,显示容器名称、状态、端口映射等信息4.查看服务日志
docker compose logs
功能:查看所有服务的日志输出
常用选项:
-f:实时跟踪日志(类似 tail -f),如 docker compose logs -f
可指定单个服务名,只看该服务日志,如 docker compose logs -f wordpress5. 启动 / 停止 / 重启服务
# 启动已存在的服务(不重新创建)
docker compose start
# 停止运行中的服务(不删除容器)
docker compose stop
# 重启服务
docker compose restartdocker compose restart db 只重启数据库服务
WordPress 是一款开源的内容管理系统(CMS),主要用于快速搭建和管理网站,无论是个人博客、企业官网、电商平台还是新闻门户等,都可以通过它实现。
1,编写docker-compose.yml文件
services:
wordpress:
image: wordpress
restart: always
ports:
- 8050:80
environment:
//要连接的数据库的ip地址,即配置的db服务,docker会自动解析,将容器名转化为对应的ip
WORDPRESS_DB_HOST: db
//连接数据库时的用户名
WORDPRESS_DB_USER: mywordpressuser
//用户名对应的密码
WORDPRESS_DB_PASSWORD: mywordpresspass
//要使用的数据库的名称
WORDPRESS_DB_NAME: wordpress
volumes:
- ./wordpress/:/var/www/html
depends_on:
db:
condition: service_healthy
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress
MYSQL_USER: mywordpressuser
MYSQL_PASSWORD: mywordpresspass
volumes:
- ./mysqllib/:/var/lib/mysql
healthcheck:
test: mysql -uroot -proot -e 'select 1;'
interval: 10s
timeout: 5s
retries: 102,启动服务
docker compose up -d 
3,访问站点
如果没有下载Wordpress,会出现下载页面,之后重新登录即可。

4,发布一篇文章

通过 “快照方式” 制作 Docker 镜像,通常指的是基于运行中的容器状态创建镜像(即对容器当前的文件系统状态做 “快照” 并打包为镜像)。
docker commit
功能:从容器创建一个新的镜像
语法:docker commit 选项 container image[:tag]
-a:提交镜像作者
-c:通过dockerfile指令来创建镜像,可以修改启动指令
-m:提交时的文字说明
-p:在commit时,将容器暂停掉示例:
# 启动一个 Ubuntu 容器并进入交互模式
docker run -it --name my-ubuntu ubuntu:22.04 /bin/bash
# 在容器内执行修改操作(安装 curl 并创建一个测试文件)
apt update && apt install -y curlecho "快照测试文件" > /root/test.txt
# 完成后退出容器
exit
#使用docker commit 将上述容器的修改 “快照” 为新镜像:
docker commit -m "安装了 curl 并添加 test.txt" -a "yourname" my-ubuntu ubuntu-with-curl:v1
#验证新镜像
# 查看镜像列表,确认新镜像存在
docker images | grep ubuntu-with-curl
# 用新镜像启动容器,检查修改是否生效
docker run -it --rm ubuntu-with-curl:v1 /bin/bash
# 在新容器内验证:curl 已安装,test.txt 存在
curl --version
cat /root/test.txt # 应输出 "快照测试文件"制作 Docker 镜像是将应用程序及其依赖打包成标准化容器的过程,核心是通过 Dockerfile 定义构建规则,最终生成可在任何支持 Docker 的环境中运行的镜像。
基础概念
FROM:指定基础镜像(如ubuntu-20.04),必须是 Dockerfile 第一条指令,推荐使用官方精简镜像(如 alpine 版本)
COPY/ADD:复制本地文件到镜像,COPY 仅复制文件,ADD 支持解压和 URL 下载。
RUN:执行命令(如安装依赖、修改配置),建议合并命令减少镜像层。
EXPOSE:声明端口(不实际映射,需在 docker run 时用 -p 映射)。
CMD/ENTRYPOINT:定义容器启动命令(CMD 可被 docker run 后的参数覆盖,ENTRYPOINT 不可)。
LABEL:为镜像添加元数据,格式为key=value。
ENV:为镜像添加环境变量,并可被Dockerfile文件中位于其后面的指令(如COPY,ADD,ENV)使用。
WORKDIR:为Dockerfile中所有的RUN,CMD,ENTRYPOINT等指令设定工作目录。
VOLUME:给镜像创建一个存储卷。如果在docker run的时候没有指定存储卷,就会使用默认的管理卷。
ONBUILD:用于在Dockerfile中定义一个触发器。它定义的操作不会在当前镜像构建时执行,而是在以当前镜像为基础镜像(即被其他 Dockerfile 的 FROM 指令引用)构建新镜像时,才会触发执行。
HEALTHCHECK:告诉docker如何检测容器是否在正常工作。
示例:在ubuntu22.04容器中,部署一个nginx服务
#在ubuntu22.04容器中,部署一个nginx服务
#Dockerfile文件内容如下
FROM ubuntu:22.04 as build1 #指定基础镜像
LABEL version="1.0" desc="create by xg" #添加镜像版本和描述信息
ENV ROOTDIR=/data/web/html/
COPY --chown=news:news ./index.html ${ROOTDIR}
ONBUILD echo "onbuild trigger" > /data/tmp.txt
ENV Test=1
WORKDIR /data/src
ADD ./nginx-1.24.0.tar.gz .
RUN apt-get update -y && apt install -y build-essential libpcre3 libpcre3-dev zlib1g-dev
RUN cd nginx-1.24.0 && ./configure && make && make install
COPY ./nginx.conf /usr/local/nginx/conf/
#nginx.conf配置文件内容如下
#nginx访问的首页内容原来在/html下,修改为/data/web/html目录
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /data/web/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}在项目目录执行 docker build 命令,格式:
docker build -t <镜像名称>:<标签> .
-t:指定镜像名称和标签(如 my-nginx:v1)。
.:表示构建上下文为当前目录(Docker 会读取该目录下的文件)。其他参数:
--build-arg=[] :设置镜像创建时的变量;
-f :指定要使用的 Dockerfile 路径;
--label=[] :设置镜像使用的元数据;
--no-cache :创建镜像的过程不使用缓存;
--pull :尝试去更新镜像的新版本;
--quiet, -q :安静模式,成功后只输出镜像 ID;
--network: 默认 default。在构建期间设置 RUN 指令的网络模式
1.善用.dockerignore 文件
使用它可以标记在执行 docker build 时忽略的路径和文件,避免发送不必要的数据内容,从而加快整个镜像创建过程。
2.镜像的多阶段构建
通过多步骤创建,可以将编译和运行等过程分开,保证最终生成的镜像只包括运行应用所需要的最小化环境。当然,用户也可以通过分别构造编译镜像和运行镜像来达到类似的结果,但这种方式需要维护多个 Dockerfile。
3.合理使用缓存
将频繁变化的指令(如 COPY 代码)放在 Dockerfile 末尾,利用 Docker 层缓存(未变化的指令可复用缓存)。
示例:
# 优化前(每次改代码都会重新安装依赖)
COPY . .
RUN npm install
# 优化后(依赖不变时复用缓存)
RUN npm install
COPY . . # 代码变化不影响依赖安装层4.基础镜像尽量使用官方镜像,并选择体积较小镜像
容器的核心是应用,大的平台微服务可能几十上百个。选择过大的父镜像(如 Ubuntu系统镜像)会造成最终生成应用镜像的臃肿,推荐选用瘦身过的应用镜像(如node:slim),或者较为小巧的系统镜像(如 alpine、busybox 或 debian);
5.减少镜像层数
如果希望所生成镜像的层数尽量少,则要尽量合并 RUN、ADD 和 COPY 指令。通常情况下,多个 RUN 指令可以合并为一条 RUN 指令;如 apt get update&&apt install 尽量写到一行
6.精简镜像用途
尽量让每个镜像的用途都比较集中单一,避免构造大而复杂、多功能的镜像;
7.减少外部源的干扰
如果确实要从外部引入数据,需要指定持久的地址,并带版本信息等,让他人可以复用而不出错。
8.减少不必要的包安装
只安装需要的包,不要安装无用的包,减少镜像体积。
demo.c
int main()
{
printf("hello world\n");
return 0;
}编写Dockerfile
FROM centos:7
#更换国内软件源(这里使用的是中科大软件源)
RUN sed -i.bak \
-e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \
/etc/yum.repos.d/CentOS-Base.repo
#更新yum缓存
RUN yum makecache
#指定容器工作目录
WORKDIR /src
#将宿主机上的.c文件拷贝到容器中
COPY ./demo.c .
#容器内下载gcc
RUN yum install -y gcc
#编译完成后,删除
RUN gcc demo.c -o demo && rm -f demo.c && yum remove -y gcc
#设置容器启动命令
CMD ["/src/demo"]制作镜像,启动容器
docker build -t xg:1.0 .
docker run -it --rm xg:1.0ENTRYPOINT:定义容器启动时的固定命令或程序(不可被 docker run 命令行参数直接覆盖,相当于 “主程序”)。
CMD:定义容器启动时的默认参数(可被 docker run 命令行参数覆盖,相当于给 ENTRYPOINT 传递的默认参数)。
结合使用时的行为(推荐方式)
ENTRYPOINT 定义固定命令,CMD 定义默认参数(可被 docker run 参数覆盖),这是最灵活的用法。
示例Dockerfile:
FROM alpine
ENTRYPOINT ["echo", "Hello"] # 固定命令部分
CMD ["Docker"] # 默认参数(可被覆盖)启动容器(无参数):ENTRYPOINT + CMD 组合执行
docker run 镜像名 # 输出:Hello Docker(默认参数生效)启动容器(带参数,覆盖 CMD):
docker run 镜像名 Nginx # 输出:Hello Nginx(参数 "Nginx" 替代原 CMD)将全部组件及其依赖库的编译、测试、打包等流程封装进一个 docker 镜像中。但是这种方式存在一些问题, 比如 Dockefile 特别长,可维护性降低;镜像的层次多,体积大,部署时间长等问题。
将每个阶段分散到多个 Dockerfile。一个 Dockerfile 负责将项目及其依赖库编译测试打包好后,然后将运行文件拷贝到运行环境中,这种方式需要我们编写多个Dockerfile 以及一些自动化脚本才能将其两个阶段自动整合起来。
为了解决以上的两个问题,Docker 17.05 版本开始支持多镜像阶段构建。只需要编写一个 Dockerfile 即可解决上述问题。
Dockerfile示例:
FROM centos:7 as buildstage1
RUN sed -i.bak \
-e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \
/etc/yum.repos.d/CentOS-Base.repo
RUN yum makecache
WORKDIR /src
COPY ./demo.c .
RUN yum install -y gcc
RUN gcc demo.c -o demo && rm -f demo.c && yum remove -y gcc
CMD ["/src/demo"]
FROM centos:7
COPY --from=buildstage1 /src/demo /src
CMD ["/src/demo"]1.准备工作
mkdir mysqlcluster
cd mysqlcluster
touch dokcer-compose.yml
mkdir master
cd master
touch Dockerfile
touch master.sql
cd ..
mkdir slave
cd slave
touch Dockerfile
touch slave.sql目录结构如下图:
mysqlcluster/
├── docker-compose.yml
├── master
│ ├── Dockerfile
│ └── master.sql
└── slave
├── Dockerfile
└── slave.sql
2.主节点和从节点Dockerfile的编写
主节点Dockerfile
FROM mysql:5.7 #指明基础镜像
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime #设置时区
COPY ./master/master.sql /docker-entrypoint-initdb.d
#MySQL镜像启动时会自动执行 /docker-entrypoint-initdb.d 目录下的.sql.sql.gz或.sh文件 从节点Dockerfile
FROM mysql:5.7
RUN ln /usr/share/zoneinfo/Asia/Shanghai /etc/loacltime
COPY ./slave/slave.sql /docker-entrypoint-initdb.d 3.主节点和从节点数据库配置信息
主节点:master.sql文件
//创建一个用户名是 root、允许从任何 IP 地址(% 通配符)连接的用户,并设置该用户的密码为root
CREATE USER 'root'@'%' IDENTIFIED By 'root';
//授予root用户在所有数据库(*.*)上的两个权限:
//replication slave:允许作为从库连接主库进行数据复制
//replication client:允许查询主从复制的状态信息(show master status或show slave staus命令)
grant replication slave,replication client on *.* to 'root'@'%';
//刷新权限使其生效
flush privileges;从节点:master.sql文件
//在从库中记录主库的连接信息,为后续的数据同步做准备
change master to master_host='mysql-master',master_user='root',master_password='root',master_port='3306';
//启动从库复制进程
start slave;4.docker-compose.yml的编写
services:
mysql-master:
build:
context: ./
dockerfile: ./master/Dockerfile
image: mysqlmaster:v1.0
restart: always
container_name: mysql-master
volumes:
- ./mastervarlib:/var/lib/mysql
ports:
- 9306:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=1',
'--log-bin=master-bin',
'--binlog-ignore-db=mysql',
'--binlog_cache_size=256M',
'--binlog_format=mixed',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
mysql-slave:
build:
context: ./
dockerfile: ./slave/Dockerfile
image: mysqlslave:v1.0
restart: always
container_name: mysql-slave
volumes:
- ./slavevarlib:/var/lib/mysql
ports:
- 9307:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=2',
'--relay_log=slave-relay',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
depends_on:
- mysql-master
mysql-slave2:
build:
context: ./
dockerfile: ./slave/Dockerfile
image: mysqlslave:v1.0
restart: always
container_name: mysql-slave2
volumes:
- ./slavevarlib2:/var/lib/mysql
ports:
- 9308:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=3',
'--relay_log=slave-relay',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
depends_on:
- mysql-master5.构建镜像
docker compose build6.启动服务
docker compose up -d
#查看启动的服务
docker compose ps
#确保服务的状态都是up
7.检查同步状态
进入到一个容器后,连接上数据库,输入show master status或show slave staus命令.
示例:

8.检查数据是否可以同步
在主数据库mysql-master中新建表,插入数据,然后在从数据库mysql-slave和mysql-slave1中查看数据是否存在。
1.ADD 与 COPY 的区别
ADD:不仅能够将构建命令所在的主机本地的文件或目录,而且能够将远程 URL所对应的文件或目录,作为资源复制到镜像文件系统。所以,可以认为 ADD 是增强版的 COPY,支持将远程 URL 的资源加入到镜像的文件系统。
COPY:COPY 指令能够将构建命令所在的主机本地的文件或目录,复制到镜像文件系统。
2.CMD 与 EntryPoint 的区别
ENTRYPOINT 容器启动后执行的命令,让容器执行表现的像一个可执行程序一样,与 CMD 的 区 别 是 不 可 以 被 docker run 覆 盖 , 会 把 docker run 后 面 的参 数 当 作 传 递 给 ENTRYPOINT 指令的参数。
Dockerfile 中只能指定一个 ENTRYPOINT,如果指定了很多,只 有 最 后 一 个 有效 。 docker run 命 令 的
-entrypoint 参 数 可 以 把 指 定 的 参 数 继 续 传 递 给ENTRYPOINT。
组合使用 ENTRYPOINT 和 CMD, ENTRYPOINT 指定默认的运行命令, CMD指定默认的运行参数。
3.什么是空悬镜像(dangling )
REPOSITORY 和 TAG 标签 均为<none> 的镜像被称为虚悬镜像,一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的。
造成虚悬镜像的原因:
原因一:
原本有镜像名和标签的镜像,发布了新版本后,重新 docker pull *** 时,旧的镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消;
原因二:
docker build 同样可以导致这种现象。比如用 dockerfile1 构建了个镜像 tnone1:v1,又用另外一个 Dockerfile2 构建了一个镜像 tnone1:v1,这样之前的那个镜像就会变成空悬镜像。
可以用下面的命令专门显示这类镜像
docker image ls -f dangling=true4.中间层镜像是什么?
当使用 docker build 构建镜像时,Docker 会对 Dockerfile 中的每一条指令(如 RUN、COPY、ADD 等)生成一个对应的镜像层,这些层就是中间层镜像。
中间层镜像通常没有 REPOSITORY 和 TAG 标签(显示为 <none>:<none>)。
缓存作用:
中间层镜像是 Docker 构建缓存的核心:
Docker 镜像是容器技术的核心基础,它采用了分层存储和联合文件系统(UnionFS) 等技术.
其核心原理可以概括为:一个镜像由多个只读层(Layer)组成,每层包含文件系统的增量变化,通过联合文件系统合并为一个统一的只读文件系统,作为容器运行的基础。
Docker 镜像的每一层都是只读的,容器运行时会添加一个可写层。
Docker 镜像并非一个单一的文件,而是由一系列只读层(Layer) 叠加而成的集合。每个层对应镜像构建过程中的一条指令(如 FROM、RUN、COPY 等),记录该步骤对文件系统的修改(新增、修改或删除的文件)。
例如,一个简单的镜像构建过程:
FROM ubuntu:20.04 # 基础层(来自 ubuntu:20.04 镜像的所有层)
RUN apt-get update # 新增层 1:记录 apt-get update 产生的文件变化
RUN apt-get install -y nginx # 新增层 2:记录安装 Nginx 产生的文件变化
COPY index.html /var/www/html # 新增层 3:记录复制 index.html 产生的变化构建后,这个镜像包含:
UnionFS(Union File System,联合文件系统)是一种分层、轻量级的文件系统,它的核心功能是将多个独立的目录(文件系统)“合并” 成一个统一的虚拟文件系统,对外呈现为单一的目录结构。用户或应用程序操作这个虚拟目录时,系统会自动协调底层多个目录的读写逻辑。
写时复制的完整流程示例:
假设场景:UnionFS 堆叠了两层目录,lowerdir(只读层,存放原始文件)和 upperdir(可写层,初始为空),合并后呈现为统一视图 merged。当用户尝试修改 lowerdir 中的文件时,CoW 流程如下:
初始状态:文件仅存在于只读层
当用户执行修改操作(如 echo "world" >> merged/data.txt)时:
系统执行 “复制” 操作:
复制完成后,系统在 upperdir 中对复制后的文件执行用户的修改操作:
修改完成后,UnionFS 的虚拟视图 merged 会优先显示可写层的文件:
overlay2 是 Docker 目前默认的存储驱动(storage driver)。
镜像层(只读):layer1 → layer2 → layer3(lowerdir 组合)
↑ ↑ ↑
└───────┼───────┘
↓
容器可写层: upperdir
↑
└─── 合并为 merged(容器内 /)
读文件操作
当容器读取一个文件(如 /etc/hosts)时:
写文件操作(触发写时复制 CoW)
当容器修改一个来自镜像层(lowerdir)的文件时,overlay2 会触发写时复制:
删除文件操作
删除文件时,overlay2 不会真正删除镜像层的文件,而是通过 “标记” 实现逻辑删除:
1.创建一个目录fs,在该目录中创建文件系统的工作目录
mkdir fs
mkdir upper lower work merged
fs
├── lower
├── merged
├── upper
└── work2.准备一些文件
echo "in lower" > lower/in_lower.txt
echo "in upper" > upper/in_upper.txt
echo "In both. from lower" > lower/in_both.txt
echo "In both. from upper" > upper/in_both.txt
fs
├── lower
│ ├── in_both.txt
│ └── in_lower.txt
├── merged
├── upper
│ ├── in_both.txt
│ └── in_upper.txt
└── work
此时merged目录下是空的3.创建文件系统并完成挂载
#命令
mount -t overlay overlay -o lowerdir=./lower,upperdir=./upper,workdir=./work ./merged
#查看是否完成了挂载
df -h
#取消挂载
umount 挂在点或设备文件4.再次查看目录结构
#命令
tree
fs
├── lower
│ ├── in_both.txt
│ └── in_lower.txt
├── merged
│ ├── in_both.txt
│ ├── in_lower.txt
│ └── in_upper.txt
├── upper
│ ├── in_both.txt
│ └── in_upper.txt
└── work
└── work发现merged命令下自动生成3个文件,可以看到upper,lower中的文件都在,merged 目录其实就是用户看到的目录,用户的实际文件操作在这里进行。
#同时查看merged/in_both.txt的内容
cat merged/in_both.txt
结果是:In both. from upper
可以证明这个文件是upper中的,upper目录中的in_both.txt覆盖了lower目录中的in_both.txt.5.修改lower中的文件
echo "in lower! after edit!" > merged/in_lower.txt
#再次查看目录结构
tree
fs
├── lower
│ ├── in_both.txt
│ └── in_lower.txt
├── merged
│ ├── in_both.txt
│ ├── in_lower.txt
│ └── in_upper.txt
├── upper
│ ├── in_both.txt
│ ├── in_lower.txt
│ └── in_upper.txt
└── work
└── work可以看到 in_lower.txt 在 upper 里面生成了,发生了Cow(写时复制),底层lower目录中的文件是不变的。
原理:Docker 卷本质是宿主机上的一个目录(或文件),通过--bind类似的机制挂载到容器中。
容器读写卷时,数据实际保存在宿主机的卷目录中,与容器生命周期分离。
mount --bind 是 Linux 系统中一种特殊的挂载方式,用于将一个目录或文件 "绑定" 到另一个目录位置,实现两个路径指向同一个数据的效果。这种方式类似创建了一个 "硬链接",但适用于目录,且更灵活。
#基本语法
mount --bind 源路径 目标路径当容器进程被创建之后,尽管开启了 Mount Namespace,但是在它执行 chroot(chroot 就是可以改变某进程的根目录,使这个程序不能访问目录之外的其他目录,这个跟我们在一个容器中是很相似的)之前,容器进程一直可以看到宿主机上的整个文件系统。
所以,在执行 chroot 之前,把 Volume 指定的宿主机目录(比如 /home 目录),挂载到指定的容器目录(比如 /test 目录)在宿主机上对应的目录(即 /var/lib/docker/aufs/mnt/[可读写层 ID]/test)上,这个 Volume 的挂载工作就完成了。
宿主机与容器的目录绑定(Bind Mount),本质是 容器运行时的动态路径映射:容器访问 容器目录 时,Linux 内核通过 VFS 层直接将操作转发到宿主机的 宿主机目录,但这个映射关系仅存在于容器运行期间,不会对容器自身的文件系统(可写层)产生任何修改 —— 容器的 容器目录 本身只是一个 “逻辑挂载点”,没有实际数据(数据全在宿主机目录中)。
Docker 存储卷(Volume)是独立于容器可写层的外部存储,其数据存储在宿主机的特定目录(通常是 /var/lib/docker/volumes/),而非容器自身的文件系统层中。 容器与卷的关系是 “挂载” 关系,卷中的数据本质上不属于容器文件系统的一部分。 docker commit 的作用是将容器的可写层(即容器运行过程中对镜像只读层的修改) 打包为一个新的镜像层。 由于卷的数据不在容器的可写层中,因此 commit 操作无法捕获卷中的内容。
在计算机网络中,tun 与 tap 是操作系统内核中的虚拟网络设备。不同于普通靠硬件网络适配器实现的设备,这些虚拟的网络设备全部用软件实现,并向运行于操作系统上的软件提供与硬件的网络设备完全相同的功能
tap/tun 虚拟了一套网络接口,这套接口和物理的接口无任何区别,可以配置 IP,可以路由流量,不同的是,它的流量只在主机内流通。
tun 和 tap 是两个相对独立的虚拟网络设备,其中 tap 模拟了以太网设备,操作二层数据包(以太帧),tun 则模拟了网络层设备,操作三层数据包(IP 报文)。

应用程序通过 tun 设备对外发送数据包后,tun 设备,便会把数据包通过字符设备发送给 VPN 程序,VPN 收到数据包,会修改后再重新封装成新报文,譬如数据包原本是发送给 A 地址的,VPN 把整个包进行加密,然后作为报文体,封装到另一个发送给 B 地址的新数据包当中。然后通过协议栈发送到物理网卡发送出去。
基础命令:
1.添加网卡
# 创建 tap
ip tuntap add dev <name> mode tap
# 创建 tun
ip tuntap add dev <name> mode tun2.删除网卡
# 删除 tap
ip tuntap del dev <name> mode tap
# 删除 tun
ip tuntap del dev <name> mode tun3.激活网卡
ip link set name up4.设置 ip并查看虚拟网卡
ip addr add 10.5.0.1/24 dev <name>
ifconfigLinux 支持网络名空间隔离的同时,也提供了专门的虚拟以太网(Virtual Ethernet,习惯简写做 veth)让两个隔离的网络名称空间之间可以互相通信。
直接把 veth 比喻成是虚拟网卡其实并不十分准确,如果要和物理设备类比,它应该相当于由交叉网线连接的一对物理网卡。形象化的理解如下:

veth 实际上不是一个设备,而是一对设备,因而也常被称作 veth pair。要使用 veth,必须在两个独立的网络名称空间中进行才有意义,因为 veth pair 是一端连着协议栈,另一端彼此相连的,在 veth 设备的其中一端输入数据,这些数据就会从设备的另外一端原样不变地流出.

veth 通信不需要反复多次经过网络协议栈,这让 veth 比起 tap/tun 具有更好的性能。
veth 实现了点对点的虚拟连接,可以通过 veth 连接两个 namespace,如果我们需要将 3 个或者多个 namespace 接入同一个二层网络时,就不能只使用 veth 了。在物理网络中,如果需要连接多个主机,我们会使用网桥,或者又称为交换机。Linux 也提供了网桥的虚拟实现。
基础命令:
1.veth 操作
# 添加 veth
ip link add <veth name> type veth peer name <peer name>
#示例:
ip link add veth11 type veth peer name veth12
# 删除 veth
ip link delete <veth name>
# 查看 veth
ip link show2.命名空间操作
#添加 ns
ip netns add <name>
#删除 ns
ip netns del <name>
#执行命令
ip netns exec <name> <cmd>
#遍历 ns
ip netns list
#示例:
ip netns add ns1
ip netns add ns23.将网卡挪到不同的命名空间中
ip link set veth11 netns ns1
ip link set veth12 netns ns24.激活网卡
ip netns exec ns1 ip link set veth11 up
ip netns exec ns2 ip link set veth12 up5.给网卡分配 IP 地址
ip netns exec ns1 ip addr add 10.5.0.1/24 dev veth11
ip netns exec ns2 ip addr add 10.5.0.2/24 dev veth126.然后我们在各自的命名空间中 ping 对方,可以看到网卡是能通的
ip netns exec ns1 ping 10.5.0.2
ip netns exec ns2 ping 10.5.0.1Linux Bridge(网桥)是用纯软件实现的虚拟交换机,有着和物理交换机相同的功能.
因此我们可以把 tun/tap,veth pair 等设备绑定到网桥上,就像是把设备连接到物理交换机上一样。此外它和 veth pair、tun/tap 一样,也是一种虚拟网络设备,具有虚拟设备的所有特性,例如配置 IP,MAC 地址等。

Linux Bridge ,由 brctl 命令创建和管理。Linux Bridge 创建以后,真实的物理设备(如 eth0)抑或是虚拟的设备(veth 或者 tap)都能与 Linux Bridge 配合工作。
#brctl工具的安装:
# centos
yum install -y bridge-utils
# ubuntu
apt-get install -y bridge-utils基本命令:
1.新建一个网桥
brctl addbr <bridge>2.添加一个设备(例如 eth0)到网桥
brctl addif <bridge> eth03.显示当前存在的网桥及其所连接的网络端口
brctl show4.启动网桥
ip link set <bridge> up5.删除网桥,需要先关闭它
ip link set <bridge> down
brctl delbr <bridge>
#或者使用 ip link del 命令直接删除网桥增加 Linux Bridge 时会自动增加一个同名虚拟网卡在宿主机器上,因此我们可以通过 ip link 命令操作这个虚拟网卡,实际上也就是操作网桥,并且只有当这个虚拟网卡状态处于 up 的时候,网桥才会转发数据。
默认情况下,Docker 会创建一个名为 docker0 的网桥(也可自定义网桥),所有使用 bridge 模式的容器都会连接到该网桥,实现:
容器连接到网桥的过程
当启动一个 bridge 模式的容器时(默认模式):
桥接模式可以参考下图:

DNAT 是修改数据包的目标 IP 地址(或目标端口),通常用于外部网络访问内部网络时,将公网 IP(或端口)映射到内网私有 IP(或端口),使内网服务能被外部访问。

SNAT 是修改数据包的源 IP 地址,通常用于内部网络(如局域网)的主机访问外部网络(如互联网)时,将内部私有 IP 转换为公共 IP,实现多台内网设备共享一个公网 IP 上网。
