Docker 从 systemd 托管容器、overlay2 存储驱动到 Compose 多容器实战

文章目录

  • [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 接管容器生命周期,实现开机自启、异常自动重启。

关键点:

  1. ExecStartPre=-:行首-代表命令执行失败不阻断后续流程,用于幂等清理残留同名容器;

  2. ExecStart直接执行docker run,不要加‑d后台参数,让 systemd 直接捕获容器主进程;

  3. ExecStop执行docker stop优雅关闭容器;

  4. Requires=docker.service:依赖 docker 服务,docker 未启动该服务不会运行;

  5. 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

默认识别文件名优先级:

  1. compose.yaml / compose.yml
  2. docker‑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 全部资源。

总结

  1. systemd 托管 docker 容器:不能带‑d,systemd 接管前台进程,ExecStartPre做清理,实现脱离 docker ‑‑restart的自启;
  2. overlay2 分层存储:lower 为镜像只读层,upper 是容器读写层,宿主机不能直接访问 lower 层 diff 目录;容器 init 层存放 hostname、hosts 等特殊文件;
  3. compose 核心:service 定义容器,network 实现容器间 dns 通信,volume/bind 挂载实现持久;.env统一管理环境变量;depends_on只控制启动顺序,不等业务就绪;
  4. 图形工具:Portainer‑CE 适合日常管理,DockerUI 只适合学习,生产慎用。
相关推荐
新*1 小时前
ES 7.17 三节点 Docker 集群接入 IK 自定义词典:一次从丢插件到热更新的完整排障
运维·elasticsearch·docker·中文分词·ik分词
小白的码BUG之路2 小时前
Jenkins -- gitee+docker+harbor+k8s 部署项目
docker·gitee·jenkins
AlfredZhao2 小时前
Docker Compose 平滑切换环境变量文件,容器配置不残留
docker
duansamve18 小时前
Python3+Flask+PostgreSQL18+Vue3项目生产环境用docker如何部署?
python·docker·flask
那明眸似水1 天前
云原生项目三:Docker秒级启动NextCloud个人网盘存储办公系统
linux·运维·docker·云原生·容器
知已难寻1 天前
Docker 入门:容器技术与 Docker 基础
java·运维·服务器·docker·云原生·容器
xbzb2 天前
镜像到底存在哪:/var/lib/docker 的四级映射与 daemon.json 配置
docker·容器·json·镜像·daemon.json·四级
雾时之林2 天前
国内Docker 镜像加速器配置
云原生·eureka
方便面不加香菜2 天前
Docker Volume -- 管理卷Volume详解
运维·docker·容器