文章目录
- [Docker 从 systemd 托管容器、overlay2 存储驱动到 Compose 多容器实战](#Docker 从 systemd 托管容器、overlay2 存储驱动到 Compose 多容器实战)
-
- [一、systemd 将容器配置为系统服务](#一、systemd 将容器配置为系统服务)
- [二、Docker 存储驱动 overlay2](#二、Docker 存储驱动 overlay2)
-
- [2.1 存储驱动基础概念](#2.1 存储驱动基础概念)
- [2.2 overlay2 目录字段含义](#2.2 overlay2 目录字段含义)
- [2.3 镜像分层构建实践](#2.3 镜像分层构建实践)
- [三、Docker Compose 多容器编排](#三、Docker Compose 多容器编排)
-
- [3.1 Compose 简介](#3.1 Compose 简介)
- [3.2 实战 1:Wordpress 示例](#3.2 实战 1:Wordpress 示例)
- [3.3 实战 2:ECShop 商城项目(bind 挂载、自定义网络、.env 环境变量)](#3.3 实战 2:ECShop 商城项目(bind 挂载、自定义网络、.env 环境变量))
-
- [步骤 1:准备宿主机目录、源码、nginx 配置](#步骤 1:准备宿主机目录、源码、nginx 配置)
- [步骤 2:创建 compose 项目目录,编写.env 与 docker‑compose.yml](#步骤 2:创建 compose 项目目录,编写.env 与 docker‑compose.yml)
- [四、Docker 图形化管理工具](#四、Docker 图形化管理工具)
-
- [4.1 DockerUI(仅学习测试,项目停止维护)](#4.1 DockerUI(仅学习测试,项目停止维护))
- [4.2 Portainer‑CE(社区免费版,主流图形管理工具)](#4.2 Portainer‑CE(社区免费版,主流图形管理工具))
- 总结
Docker 从 systemd 托管容器、overlay2 存储驱动到 Compose 多容器实战
摘要:本文涵盖 systemd 托管 docker 容器(不用
--restart=always)、overlay2 存储驱动底层原理、docker‑compose 多容器编排实战(WordPress、ECShop 商城项目)、DockerUI 与 Portainer 图形化管理工具,完整保留全部实操代码,附带原理讲解,适合学习复习。
一、systemd 将容器配置为系统服务
场景:不依赖 docker 自身
--restart=always重启策略,交给 systemd 接管容器生命周期,实现开机自启、异常自动重启。关键点:
-
ExecStartPre=-:行首-代表命令执行失败不阻断后续流程,用于幂等清理残留同名容器; -
ExecStart直接执行docker run,不要加‑d后台参数,让 systemd 直接捕获容器主进程; -
ExecStop执行docker stop优雅关闭容器; -
Requires=docker.service:依赖 docker 服务,docker 未启动该服务不会运行; -
Restart=on‑failure:只有异常退出才重启,由 systemd 负责重启,不是 docker 守护进程。/etc/systemd/system/container-web.service
[Unit]
Description=Container web (nginx)
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service
[Service]服务类型simple,直接管控ExecStart启动的主进程
Type=simple
启动前清理残留web容器,- 表示失败不报错阻断
ExecStartPre=-/usr/bin/docker rm -f web
前台运行容器,去掉-d,systemd接管进程,日志输出到journald
ExecStart=/usr/bin/docker run
--name web
--hostname web
-v /www:/usr/share/nginx/html
nginx:1.24优雅停机,10秒超时强制停止
ExecStop=/usr/bin/docker stop -t 10 web
容器异常退出自动重启,间隔5秒
Restart=on-failure
RestartSec=5
[Install]多用户模式开机自启,对应systemctl enable
WantedBy=multi-user.target
生效配置
systemctl daemon-reload
设置开机自启
systemctl enable --now container-web.service
查看状态
systemctl status container-web.service
二、Docker 存储驱动 overlay2
2.1 存储驱动基础概念
容器镜像采用分层联合文件系统,存储驱动负责镜像层、容器读写层管理。
docker ps -s输出说明:-
前面数值:容器可写层实际占用磁盘大小;
-
virtual:镜像 + 容器可写层总虚拟大小。
[root@docker ~]# docker container ps -s
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
e8c6a5c0c697 busybox "sh" 3 minutes ago Up 3 minutes busybox 15B (virtual 4.28MB)
-
主流存储驱动对比:
表格
| 驱动 | 说明 |
|---|---|
| overlay2 | 现代 Linux 默认,联合文件系统,生产首选 |
| aufs | 旧版本 docker 使用,现已淘汰 |
| devicemapper | 性能差,老 CentOS 旧方案 |
| btrfs/zfs | 需要底层文件系统支持,适合快照场景 |
| vfs | 性能极差,仅用于测试 |
查看当前存储驱动:
[root@docker ~]# docker info |grep 'Storage Driver'
Storage Driver: overlay2
docker 数据根目录默认:/var/lib/docker
2.2 overlay2 目录字段含义
overlay2 包含多种关键目录:
diff:保存本层实际文件数据;link:层的短链接 id;lower:记录下层镜像层(只读基础层列表);merged:多层合并之后的完整视图,只有容器运行时有效;work:overlay 内部工作目录,用于 copy‑on‑write 写时复制;
⚠️ lower 镜像只读层 diff 目录宿主机不能直接 ls/tree 遍历,会报打开目录错误,属于内核 overlayfs 隔离机制,不是故障。
2.3 镜像分层构建实践
编写 Dockerfile 构建带 ssh 服务的 ubi8 镜像
FROM hub.laoma.cloud/library/ubi8:latest
MAINTAINER admin@laoma.cloud
RUN yum install -y openssh-server
RUN /usr/libexec/openssh/sshd-keygen ecdsa
RUN sed -i '/UseDNS/cUseDNS no' /etc/ssh/sshd_config
RUN echo "root:redhat" | chpasswd
CMD /usr/sbin/sshd -D
# 构建镜像
[root@docker ~]# docker build -f dockerfile . -t ubi8:sshd
# 查看镜像分层历史
[root@docker ~]# docker image history ubi8:sshd
# 导出镜像备份
[root@docker ~]# docker save ubi8:sshd -o ssh.tar
# 清理docker环境用于实验
[root@docker ~]# systemctl stop docker
[root@docker ~]# rm -fr /var/lib/docker/
# 确认overlay内核模块加载
[root@docker ~]# lsmod | grep overlay
[root@docker ~]# modprobe overlay
如果需要手动指定存储驱动,修改 docker service 单元:
[root@docker ~]# cp /usr/lib/systemd/system/docker.service /etc/systemd/system
[root@docker ~]# vim /etc/systemd/system/docker.service
# ExecStart参数追加 --storage-driver=overlay2
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --storage-driver=overlay2
[root@docker ~]# systemctl daemon-reload
[root@docker ~]# systemctl start docker
[root@docker ~]# docker info |grep -i storage
导入镜像观察 overlay2 层生成
[root@docker ~]# docker load -i ~/ssh.tar
[root@docker ~]# docker image ls
# 查看/var/lib/docker/overlay2下生成各个镜像层目录
[root@docker ~]# ls /var/lib/docker/overlay2/
创建容器后会额外生成容器读写层xxx-init与容器 upper 层:
-
-init层:存放容器初始化生成文件/etc/hosts /etc/resolv.conf /etc/hostname; -
upper 层 diff:容器运行时所有修改写入到此目录。
启动容器
[root@docker overlay2]# docker container run -d --name ssh ubi8:sshd
在容器内部新建文件
[root@docker overlay2]# docker container exec ssh touch /laoma
在宿主机upper层diff中可以看到新建的laoma文件
[root@docker overlay2]# tree -L 2 a20fb63788177f7435d6338e93b0b31293412f76258fb9e0ba46d0c717defcfa/diff
容器的
/etc/hostname、/etc/hosts、resolv.conf不在 overlay2 读写层,存放在/var/lib/docker/containers/<容器ID>/目录下。
三、Docker Compose 多容器编排
3.1 Compose 简介
Compose 用于定义、运行多容器应用,通过 yaml 文件描述 services、networks、volumes 资源。
- Compose v1:python 编写,已经停止维护;
- Compose v2:go 实现,集成到 docker cli,命令为
docker compose(注意中间空格,不是短横线)。
查看插件是否安装:
[root@docker ~]# rpm -q docker-compose-plugin
docker-compose-plugin-2.27.0-1.el8.x86_64
默认识别文件名优先级:
compose.yaml/compose.ymldocker‑compose.yaml/docker‑compose.yml
常用核心命令:
docker compose up -d # 创建并后台启动服务
docker compose ps # 查看容器状态
docker compose logs -f # 实时查看日志
docker compose down # 停止删除容器、网络,保留volume数据卷
docker compose config # 校验并输出解析后的yaml配置
docker compose exec # 在运行容器内部执行命令
docker compose ls # 列出compose项目
3.2 实战 1:Wordpress 示例
新建 wordpress 目录,编写docker‑compose.yml,使用 docker 管理命名 volume 持久数据。
[root@docker ~]# mkdir -p wordpress && cd wordpress
services:
db:
image: mysql:latest
volumes:
# docker托管命名volume,数据由docker管理
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: redhat
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
expose:
- 3306
wordpress:
image: wordpress:latest
volumes:
- wp_data:/var/www/html
ports:
- 80:80
restart: always
environment:
# db为mysql服务名,compose内部dns解析直接访问服务名
- WORDPRESS_DB_HOST=db
- WORDPRESS_DB_USER=wordpress
- WORDPRESS_DB_PASSWORD=wordpress
- WORDPRESS_DB_NAME=wordpress
volumes:
# 声明命名volume,compose自动创建
db_data:
wp_data:
启动与验证:
[root@docker wordpress]# docker compose up -d
# 查看项目列表
[root@docker wordpress]# docker compose ls
# 查看网络、volume
[root@docker wordpress]# docker network ls
[root@docker wordpress]# docker volume ls
# down只会删除容器网络,volume不会自动删除
[root@docker wordpress]# docker compose down
# 清理无用volume
[root@docker wordpress]# docker volume prune -a
3.3 实战 2:ECShop 商城项目(bind 挂载、自定义网络、.env 环境变量)
业务组件:mysql5.7 数据库 + php‑fpm7.2 + nginx;使用 bind 挂载宿主机目录,自定义 bridge 网络,使用.env 文件统一管理环境变量,加入健康检查。
步骤 1:准备宿主机目录、源码、nginx 配置
[root@docker ~]# mkdir /ecshop/mysql /ecshop/nginx/{conf,html,log}
# 下载ecshop源码包
[root@docker ~]# wget http://192.168.45.200/class/course-materials/softwares/stage01/ECShop_V4.1.20_UTF8.zip
[root@docker ~]# unzip ECShop_V4.1.20_UTF8.zip
# 拷贝源码到宿主机站点目录
[root@docker ~]# cp -a ECShop_V4.1.20_UTF8_release20250416/source/ecshop/* /ecshop/nginx/html/
[root@docker ~]# chmod -R 777 /ecshop/nginx/html/
# 写入nginx配置文件
[root@docker ~]# cat > /ecshop/nginx/conf/nginx.conf <<'EOF'
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name shop.laoma.cloud; # 替换为实际域名
root /usr/share/nginx/html;
index index.php index.html;
location ~ \.php$ {
# compose网络内直接用服务名ecshop‑php访问php‑fpm
fastcgi_pass ecshop-php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* \.(jpg|jpeg|png|gif|css|js)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
}
}
EOF
步骤 2:创建 compose 项目目录,编写.env 与 docker‑compose.yml
[root@docker ~]# mkdir ecshop && cd ecshop
.env 文件,统一管理变量,yaml 通过${变量名}引用:
[root@docker ecshop]# cat > .env <<'EOF'
# MySQL
MYSQL_ROOT_PASSWORD=Laoma@123
MYSQL_DATABASE=ecshop
MYSQL_USER=ecshop
MYSQL_PASSWORD=ECShop@123
MYSQL_PORT=3306
# Nginx
NGINX_PORT=80
# PHP
PHP_PORT=80
# 镜像版本
NGINX_IMG=hub.laoma.cloud/library/nginx:1.24
PHP_IMG=hub.laoma.cloud/library/php:7.2-fpm-ecshop
MYSQL_IMG=hub.laoma.cloud/library/mysql:5.7
EOF
docker‑compose.yml
[root@docker ecshop]# cat > docker-compose.yml <<'EOF'
services:
ecshop-mysql:
image: ${MYSQL_IMG}
container_name: ecshop-mysql
volumes:
# bind挂载宿主机目录,持久mysql数据
- type: bind
source: /ecshop/mysql
target: /var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
networks:
- ecshop
# 健康检查,确认mysql服务可用
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 3
start_period: 20s
ecshop-php:
image: ${PHP_IMG}
container_name: ecshop-php
volumes:
- type: bind
source: /ecshop/nginx/html
target: /usr/share/nginx/html
restart: always
networks:
- ecshop
healthcheck:
test: ["CMD", "pgrep", "php-fpm"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
ecshop-nginx:
image: ${NGINX_IMG}
container_name: ecshop-nginx
volumes:
- type: bind
source: /ecshop/nginx/conf/nginx.conf
target: /etc/nginx/nginx.conf
- type: bind
source: /ecshop/nginx/log
target: /var/log/nginx
- type: bind
source: /ecshop/nginx/html
target: /usr/share/nginx/html
restart: always
networks:
- ecshop
ports:
- "${NGINX_PORT}:80"
# 依赖,优先启动mysql、php,只是启动顺序,不等待业务就绪
depends_on:
- ecshop-mysql
- ecshop-php
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
# 自定义bridge网络,指定子网网段
networks:
ecshop:
name: ecshop
driver: bridge
ipam:
config:
- subnet: 172.28.5.0/24
ip_range: 172.28.5.0/24
gateway: 172.28.5.1
driver_opts:
com.docker.network.bridge.name: ecshop
EOF
启动项目
[root@docker ecshop]# docker compose up -d
# 查看容器状态
[root@docker ecshop]# docker ps --format '{{ .Names }}\t{{ .Ports }}\t{{ .Status }}'
注意:
depends_on仅仅控制容器启动顺序,不会等待 mysql 数据库完全就绪;生产环境建议增加健康检查或者应用层重试逻辑。
四、Docker 图形化管理工具
4.1 DockerUI(仅学习测试,项目停止维护)
-
轻量 web 界面,管理镜像、容器、网络、卷;
-
通过挂载
/var/run/docker.sock和 docker 引擎通信。[root@docker ~]# docker run -d
--name dockerui
--restart always
-v /var/run/docker.sock:/var/run/docker.sock
-p 8999:8999
joinsunsoft/docker.ui
访问地址:http://服务器IP:8999,默认账号:ginghan / 123456


4.2 Portainer‑CE(社区免费版,主流图形管理工具)
# 安装 Portainer
[root@docker ~]# docker run -d \
--name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /portainer_data:/data \
-p 9000:9000 -p 9443:9443 \
portainer/portainer-ce:latest
9000:http 访问端口;9443:https 加密访问端口;/var/run/docker.sock:与 docker 引擎通信套接字;/portainer_data:持久保存 portainer 配置、账号信息。


新版本首次访问会生成 setup_token,有效期 5 分钟,超时需要重启容器重新获取 token;练习环境可以启动参数增加
--no‑setup‑token关闭 token 校验。
访问:http://IP:9000 或者 https://IP:9443,首次访问创建管理员账号,之后选择 local 本地 docker 环境连接,即可 web 界面管理 docker 全部资源。



总结
- systemd 托管 docker 容器:不能带
‑d,systemd 接管前台进程,ExecStartPre做清理,实现脱离 docker‑‑restart的自启; - overlay2 分层存储:lower 为镜像只读层,upper 是容器读写层,宿主机不能直接访问 lower 层 diff 目录;容器 init 层存放 hostname、hosts 等特殊文件;
- compose 核心:service 定义容器,network 实现容器间 dns 通信,volume/bind 挂载实现持久;
.env统一管理环境变量;depends_on只控制启动顺序,不等业务就绪; - 图形工具:Portainer‑CE 适合日常管理,DockerUI 只适合学习,生产慎用。