You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Docker容器内运行Docker-Compose的卷挂载问题与AIO方案咨询

问题:All-In-One Docker镜像内部Compose服务卷挂载失败

我要把应用打包成All-In-One Docker镜像,采用容器内运行Docker-Compose的方案。镜像能正常运行,但内部Compose服务无法通过文件路径挂载卷:挂载的文件夹和nginx.conf在内部容器中找不到,已确认这些文件/目录已复制或创建到容器内。

错误信息

Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/app/icons/Logo.png" to rootfs at "/usr/share/nginx/html/Logo.png": mount /app/icons/Logo.png:/usr/share/nginx/html/Logo.png (via /proc/self/fd/6), flags: 0x5000: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type

相关配置

Dockerfile

FROM docker:23.0.1-cli-alpine3.17

# install docker-compose
RUN apk add --no-cache docker-compose

# create directories
WORKDIR /app
RUN mkdir -p /app/icons
RUN mkdir -p /app/database
RUN mkdir -p /app/files

COPY docker-compose.yml .
COPY nginx.conf ./nginx.conf
COPY ./icons ./icons

ENV DOCKER_HOST=unix:///var/run/docker.sock

# default vars being reset at image start
ENV MYSQL_DATABASE=""
ENV MYSQL_USER=""
ENV MYSQL_PASSWORD=""
ENV MYSQL_ROOT_PASSWORD=""

EXPOSE 80 3306

# Standard command: start docker-compose
CMD ["docker-compose", "up"]

内部docker-compose.yml

version: '3.8'

services:
  aio_reverse_proxy:
    image: nginx:alpine
    container_name: aio_reverse_proxy
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - aio_backend
      - aio_frontend

  ff_admin_aio_frontend:
    image: frontend:latest
    container_name: aio_frontend
    restart: unless-stopped
    ports:
      - "80"
    volumes:
      - ./icons/Logo.png:/usr/share/nginx/html/Logo.png

  aio_backend:
    image: backend:latest
    container_name: aio_backend
    restart: unless-stopped
    ports:
      - "5000"
    environment:
      - DB_TYPE=mysql
      - DB_HOST=aio_database
      - DB_PORT=3306
      - DB_NAME=${MYSQL_DATABASE}
      - DB_USERNAME=${MYSQL_USER}
      - DB_PASSWORD=${MYSQL_PASSWORD}
    volumes:
      - ./files:/app/files
    networks:
      - aio_internal
    depends_on:
      - aio_database

  aio_database:
    image: mariadb:11.2
    container_name: aio_database
    restart: unless-stopped
    ports:
      - "3306:3306"
    environment:
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
    volumes:
      - ./database:/var/lib/mysql
    networks:
      - aio_internal

networks:
  aio_internal:

nginx.conf

events { }

http {
    server {
        listen 80;

        # redirect of /api/* to the backend
        location /api/ {
            proxy_pass http://aio_backend:5000/;  # internal backend
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }

        # redirect other requests to frontend
        location / {
            proxy_pass http://aio_frontend:80/;  # internal frontend
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
}

AIO镜像使用配置

services:
  aio:
    image: aio:latest
    container_name: aio
    ports:
      - "80:80"
    environment:
      - MYSQL_DATABASE=1234
      - MYSQL_USER=1234
      - MYSQL_PASSWORD=1234
      - MYSQL_ROOT_PASSWORD=1234
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./files:/app/files
      - ./database:/app/database
      - ./icons/Logo.png:/app/icons/Logo.png # optional to replace default logo

我试过多种路径(相对路径./files、绝对路径/app/files),也尝试启动无需挂载的内部容器,在镜像内创建挂载目录,但都找不到创建的目录。需要可行的挂载路径,或者其他多服务AIO容器的实现方案。


解决方案

核心原因分析

容器内运行Docker Compose时,Compose解析的卷路径是宿主机的路径,而非AIO容器内部的路径。因为你挂载了宿主机的/var/run/docker.sock,内部Compose实际调用的是宿主机的Docker引擎,所以所有卷路径都会被解析为宿主机上的路径,和AIO容器内部的/app目录无关。

方案1:修正嵌套Docker的卷路径配置

步骤1:传递宿主机路径环境变量

修改AIO镜像的启动配置,把宿主机的挂载路径通过环境变量传递给AIO容器:

services:
  aio:
    image: aio:latest
    container_name: aio
    ports:
      - "80:80"
    environment:
      - MYSQL_DATABASE=1234
      - MYSQL_USER=1234
      - MYSQL_PASSWORD=1234
      - MYSQL_ROOT_PASSWORD=1234
      # 传递宿主机的绝对路径
      - HOST_FILES_DIR=${PWD}/files
      - HOST_DATABASE_DIR=${PWD}/database
      - HOST_LOGO_PATH=${PWD}/icons/Logo.png
      - HOST_NGINX_CONF=${PWD}/nginx.conf
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./files:/app/files
      - ./database:/app/database
      - ./icons/Logo.png:/app/icons/Logo.png
      # 把AIO镜像内的nginx.conf复制到宿主机(可选,或者提前在宿主机准备该文件)
      - ./nginx.conf:/app/nginx.conf

步骤2:修改内部docker-compose.yml使用环境变量

将内部Compose的卷路径替换为宿主机路径的环境变量:

version: '3.8'

services:
  aio_reverse_proxy:
    image: nginx:alpine
    container_name: aio_reverse_proxy
    ports:
      - "80:80"
    volumes:
      - ${HOST_NGINX_CONF}:/etc/nginx/nginx.conf:ro
    depends_on:
      - aio_backend
      - aio_frontend

  ff_admin_aio_frontend:
    image: frontend:latest
    container_name: aio_frontend
    restart: unless-stopped
    ports:
      - "80"
    volumes:
      - ${HOST_LOGO_PATH}:/usr/share/nginx/html/Logo.png

  aio_backend:
    image: backend:latest
    container_name: aio_backend
    restart: unless-stopped
    ports:
      - "5000"
    environment:
      - DB_TYPE=mysql
      - DB_HOST=aio_database
      - DB_PORT=3306
      - DB_NAME=${MYSQL_DATABASE}
      - DB_USERNAME=${MYSQL_USER}
      - DB_PASSWORD=${MYSQL_PASSWORD}
    volumes:
      - ${HOST_FILES_DIR}:/app/files
    networks:
      - aio_internal
    depends_on:
      - aio_database

  aio_database:
    image: mariadb:11.2
    container_name: aio_database
    restart: unless-stopped
    ports:
      - "3306:3306"
    environment:
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
    volumes:
      - ${HOST_DATABASE_DIR}:/var/lib/mysql
    networks:
      - aio_internal

networks:
  aio_internal:

注意事项

  • 宿主机必须提前存在nginx.conficons/Logo.png等文件/目录,否则会出现挂载失败
  • 此方案本质是通过宿主机做中转,嵌套Docker的复杂度较高,不推荐用于生产环境

方案2:真正的单容器多服务方案(推荐)

放弃嵌套Docker的思路,用进程管理工具(如supervisord)在单个容器内运行所有服务,彻底规避路径解析问题。

步骤1:编写Dockerfile

FROM alpine:3.17

# 安装所有服务依赖
RUN apk add --no-cache nginx mariadb mariadb-client python3 py3-pip supervisor

# 创建必要目录
RUN mkdir -p /app /app/files /app/database /run/mysqld /var/log/supervisor /usr/share/nginx/html

# 复制应用文件和配置
COPY nginx.conf /etc/nginx/nginx.conf
COPY ./backend /app/backend
COPY ./frontend/* /usr/share/nginx/html/
COPY supervisord.conf /etc/supervisord.conf

# 设置权限
RUN chown -R mysql:mysql /run/mysqld /app/database
RUN chmod -R 755 /app/files

# 安装后端依赖(如果需要)
RUN pip3 install -r /app/backend/requirements.txt

# 暴露端口
EXPOSE 80 3306

# 启动supervisord管理所有进程
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

步骤2:编写supervisord.conf

[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log
pidfile=/run/supervisord.pid

# 管理MariaDB进程
[program:mariadb]
command=/usr/bin/mysqld_safe --datadir=/app/database
user=mysql
stdout_logfile=/var/log/supervisor/mariadb.log
stderr_logfile=/var/log/supervisor/mariadb.err.log
autorestart=true
startsecs=10

# 管理后端进程
[program:backend]
command=python3 /app/backend/main.py
user=root
stdout_logfile=/var/log/supervisor/backend.log
stderr_logfile=/var/log/supervisor/backend.err.log
autorestart=true
environment=DB_TYPE=mysql,DB_HOST=localhost,DB_PORT=3306,DB_NAME=%(ENV_MYSQL_DATABASE)s,DB_USERNAME=%(ENV_MYSQL_USER)s,DB_PASSWORD=%(ENV_MYSQL_PASSWORD)s
startsecs=5
startretries=3

# 管理Nginx进程
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
user=root
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx.err.log
autorestart=true

步骤3:启动AIO容器的配置

services:
  aio:
    image: aio:latest
    container_name: aio
    ports:
      - "80:80"
      - "3306:3306"
    environment:
      - MYSQL_DATABASE=1234
      - MYSQL_USER=1234
      - MYSQL_PASSWORD=1234
      - MYSQL_ROOT_PASSWORD=1234
    volumes:
      - ./files:/app/files
      - ./database:/app/database
      - ./icons/Logo.png:/usr/share/nginx/html/Logo.png

优势

  • 无需嵌套Docker,路径解析直接针对容器内部,逻辑清晰
  • 资源占用更低,避免宿主机Docker引擎的额外开销
  • 符合Docker单进程设计的变通方案,生产环境更稳定

内容的提问来源于stack exchange,提问作者JK Effects

火山引擎 最新活动