Docker 镜像与容器的核心机制及实践
镜像:分层文件系统与可复用模板
Docker 镜像是容器运行的基础,其本质结合了"模板"和"纳戒"的特性。作为模板,镜像可以被快速复制、分发;而作为"纳戒",它依赖于 Union File System(联合文件系统) 实现多层目录的叠加挂载。每一层称为一个 layer,代表一次构建操作的增量变更。
在 Union FS 中,各层支持只读(readonly)、读写(readwrite)和写出(whiteout-able)权限,但 Docker 镜像的所有 layer 均为只读。当构建新镜像时,从基础操作系统开始逐层叠加修改,上层内容会覆盖下层同名文件,最终对外呈现为一个统一的完整文件系统视图。
镜像的优势与局限
- 优势:
- 环境一致性:屏蔽不同主机间的差异,实现"一次构建,处处运行"。
- 分层存储:多个镜像可共享相同底层 layer,显著节省磁盘空间。
- 高效传输:拉取镜像时仅下载缺失层级,提升部署速度。
- 劣势:
- 空间占用:每层依赖均需存储,累积增加磁盘消耗。
- 性能损耗:访问文件需逐层查找,存在 I/O 开销。
常用镜像管理命令
# 删除指定镜像(若被容器使用则失败)
docker rmi nginx:latest
# 强制删除正在使用的镜像
docker rmi -f nginx:latest
# 将镜像导出为 tar 包(保留所有 layer)
docker save -o nginx_backup.tar nginx:1.25.0
# 从 tar 包加载镜像
docker load -i nginx_backup.tar
# 安静模式加载(不显示进度)
docker load -q -i nginx_backup.tar
# 查看镜像构建历史(每层指令)
docker history nginx:1.25.0 --no-trunc
# 清理无用的悬空镜像
docker image prune
# 删除所有未被容器引用的镜像
docker image prune -a
实战:镜像操作与离线迁移
# 搜索并拉取轻量级镜像
docker search busybox
docker pull busybox:1.36
# 给本地镜像添加标签并推送至仓库
docker tag busybox:1.36 myrepo/mybox:v1
docker push myrepo/mybox:v1
# 登录后查看凭证信息
cat ~/.docker/config.json
# 在无网络环境迁移镜像
# 主机A:导出镜像包
docker save -o app_image.tar myapp:latest
# 主机B:导入并运行
docker load -i app_image.tar
docker run -it myapp:latest sh
容器:镜像的可执行实例
容器是镜像的运行时实体,本质上是一个受隔离限制的进程。与静态的镜像不同,容器包含一个额外的可写层,用于保存运行期间的数据变更。尽管容器拥有独立的文件系统、网络和进程空间,但它仍运行在宿主机内核之上,不具备真正的硬件虚拟化能力。
容器生命周期状态
容器的状态流转包括:创建(created)→ 运行(running)→ 暂停(paused)→ 停止(stopped)→ 删除(deleted)。用户可通过命令控制其启停、重启或强制终止。
核心容器操作命令
# 创建但不启动容器
docker create --name web_srv -p 8080:80 nginx:1.25
# 启动已创建的容器
docker start web_srv
# 停止运行中的容器(发送 SIGTERM,超时后 SIGKILL)
docker stop web_srv
# 强制立即终止容器
docker kill web_srv
# 查看容器详细信息(含配置、挂载点等)
docker inspect web_srv
# 查看容器资源使用情况(CPU、内存、IO)
docker stats web_srv
# 实时跟踪日志输出
docker logs -f web_srv
# 查看最近 10 条日志
docker logs --tail 10 web_srv
容器交互方式
有两种主要方式进入容器内部进行调试:
- docker attach:连接到正在运行的容器主进程。
使用docker attach web_srv --sig-proxy=false--sig-proxy=false可避免 Ctrl+C 导致容器退出。 - docker exec:在运行中容器内启动新进程(推荐用于调试)。
docker exec -it web_srv bash docker exec -u www-data -w /var/www html cat index.html
容器与宿主机的数据交换
# 复制文件到容器
docker cp ./local_file.txt container_name:/tmp/
# 从容器复制文件到本地
docker cp container_name:/etc/hosts ./
# 导出容器文件系统为 tar 包(丢失元数据)
docker export -o container_fs.tar container_name
# 将 tar 包导入为新镜像(相比 save 更轻量但信息不全)
docker import container_fs.tar imported_img:v1
基于容器创建新镜像
通过 docker commit 可将运行中容器的改动固化为新的镜像 layer。
# 启动容器并修改内容
docker run -d --name modifiable_nginx nginx:1.25
docker exec modifiable_nginx sh -c "echo 'Custom Page' > /usr/share/nginx/html/index.html"
# 提交变更生成新镜像
docker commit \
-a "Author <author@example.com>" \
-m "Updated homepage" \
-c 'CMD ["nginx", "-g", "daemon off;"]' \
modifiable_nginx custom_nginx:v2
# 验证新镜像功能
docker run -d --name test_v2 -p 8090:80 custom_nginx:v2
curl http://localhost:8090
资源限制与动态调整
可通过以下命令对运行中容器施加资源约束:
# 查看当前资源占用
docker stats modifiable_nginx
# 动态设置内存上限(物理 + swap)
docker update --memory 300m --memory-swap 300m modifiable_nginx
# 限制 CPU 使用配额(如:每 100ms 最多使用 50ms)
docker update --cpu-period 100000 --cpu-quota 50000 modifiable_nginx
典型应用场景示例
1. 部署 MySQL 服务
docker run -d \
--name mysql_db \
-e MYSQL_ROOT_PASSWORD=secure_password \
-p 3306:3306 \
mysql:5.7
# 进入容器执行 SQL
docker exec -it mysql_db mysql -uroot -p
2. 运行 Redis 缓存
docker run -d \
--name redis_cache \
-p 6379:6379 \
redis:7
# 测试数据读写
docker exec -it redis_cache redis-cli
> SET counter 100
> GET counter
3. 托管 Java 应用
# 准备 jar 包
mkdir -p /opt/app && cp app.jar /opt/app/
# 启动基础容器
docker run -it --name java_app -p 8080:8080 ubuntu:22.04 bash
# 容器内安装 JDK 并运行应用
apt update && apt install openjdk-8-jdk -y
java -jar /app.jar
4. 自动重启策略
# 设置容器异常退出后自动重启
docker run -d \
--name resilient_app \
--restart unless-stopped \
nginx:1.25
支持策略:no(默认)、on-failure[:max-retries]、always、unless-stopped。