Docker Compose部署Django+PostgreSQL+Gunicorn权限及启动报错修复
- 操作系统:Ubuntu 20.04 LTS
- 容器版本:Docker Desktop 20.10.16
- 部署组件:Django v3.2、PostgreSQL v14.3、Gunicorn v20.1.0,后续计划对接宿主机Nginx做反向代理,当前优先解决容器启动异常
/path/to/myprojectweb/ ├── myproj/ │ ├── myproj/ │ ├── settings.py │ ├── wsgi.py │ └── 其他项目文件 ├── Dockerfile.prod └── compose.prod.yml
镜像和容器可正常构建,但启动Django应用时失败,存在两类报错:
- 写入
/home/app/web/myproj.log文件权限不足,日志handler初始化失败 - Gunicorn启动时无法识别传入的命令行参数,容器退出码为127
已在Dockerfile.prod中对相关目录执行过chown、chmod权限配置(甚至添加了粘滞位),权限问题仍未解决。
Dockerfile.prod
# Builder Image FROM python:3.8-slim-buster as Builder WORKDIR /usr/src/app ENV PYTHONUNBUFFERED 1 ENV PYTHONDONTWEITEBYTECODE 1 RUN apt-get update && apt-get install -y \ build-essential \ libpq-dev RUN pip install --upgrade pip pipenv COPY Pipfile* ./ RUN pipenv lock --keep-outdated --requirements > requirements.txt \ && pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt # Final Image FROM python:3.8-slim-buster RUN mkdir -p /home/app /var/log/gunicorn # 创建运行应用的非root用户和用户组 RUN groupadd -r app && useradd --no-log-init -r -g app app ENV HOME=/home/app ENV APP_HOME=/home/app/web RUN mkdir $APP_HOME WORKDIR $APP_HOME RUN apt-get update && apt-get install -y libpq-dev COPY --from=builder /usr/src/app/wheels /wheels COPY --from=builder /usr/src/app/requirements.txt ./ RUN chown -R app:app $HOME RUN chown -R app:app $APP_HOME && chmod ug+rws $APP_HOME RUN chown -R app:app /var/log/gunicorn # 提前切换用户安装依赖,方便后续执行makemigrations/migrate更新第三方包 USER app RUN pip install --no-cache /wheels/* COPY . $APP_HOME
compose.prod.yml
services: db: image: postgres:14.3 restart: always container_name: myproj_postgres_db env_file: - .env volumes: - postgres_data:/var/lib/postgresql/data/ web: build: context: . dockerfile: Dockerfile.prod container_name: myproj_app restart: always command: > sh -c "python manage.py makemigrations && python manage.py migrate && gunicorn --capture-output \ --access-logfile /var/log/gunicorn/access.log \ --error-logfile /var/log/gunicorn/error.log \ --enable-stdio-inheritance \ --workers 3 \ --worker-tmp-dir /dev/shm \ --bind 0.0.0.0:8000 \ myproj.wsgi:application" ports: - 8000:8000 env_file: - .env depends_on: - db volumes: postgres_data:
执行sudo docker compose -f compose.prod.yml up --build后报错如下:
myproj_app | Traceback (most recent call last): myproj_app | File "/usr/local/lib/python3.8/logging/config.py", line 563, in configure myproj_app | handler = self.configure_handler(handlers[name]) myproj_app | File "/usr/local/lib/python3.8/logging/config.py", line 744, in configure_handler myproj_app | result = factory(**kwargs) myproj_app | File "/usr/local/lib/python3.8/logging/handlers.py", line 200, in __init__ myproj_app | BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay) myproj_app | File "/usr/local/lib/python3.8/logging/handlers.py", line 55, in __init__ myproj_app | logging.FileHandler.__init__(self, filename, mode, encoding, delay) myproj_app | File "/usr/local/lib/python3.8/logging/__init__.py", line 1147, in __init__ myproj_app | StreamHandler.__init__(self, self._open()) myproj_app | File "/usr/local/lib/python3.8/logging/__init__.py", line 1176, in _open myproj_app | return open(self.baseFilename, self.mode, encoding=self.encoding) myproj_app | PermissionError: [Errno 13] Permission denied: '/home/app/web/myproj.log' myproj_app | myproj_app | The above exception was the direct cause of the following exception: myproj_app | myproj_app | Traceback (most recent call last): myproj_app | File "manage.py", line 22, in <module> myproj_app | main() myproj_app | File "manage.py", line 18, in main myproj_app | execute_from_command_line(sys.argv) myproj_app | File "/home/app/.local/lib/python3.8/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line myproj_app | utility.execute() myproj_app | File "/home/app/.local/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute myproj_app | django.setup() myproj_app | File "/home/app/.local/lib/python3.8/site-packages/django/__init__.py", line 19, in setup myproj_app | configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) myproj_app | File "/home/app/.local/lib/python3.8/site-packages/django/utils/log.py", line 75, in configure_logging myproj_app | logging_config_func(logging_settings) myproj_app | File "/usr/local/lib/python3.8/logging/config.py", line 808, in dictConfig myproj_app | dictConfigClass(config).configure() myproj_app | File "/usr/local/lib/python3.8/logging/config.py", line 570, in configure myproj_app | raise ValueError('Unable to configure handler ') myproj_app | ValueError: Unable to configure handler 'logfile' myproj_app | sh: 3: --access-logfile: not found myproj_app | sh: 4: --error-logfile: not found myproj_app | sh: 5: --enable-stdio-inheritance: not found myproj_app | sh: 6: --workers: not found myproj_app | sh: 7: --worker-tmp-dir: not found myproj_app | sh: 8: --bind: not found myproj_app | sh: 9: myproj.wsgi:application: not found myproj_app exited with code 127
两个问题分别对应独立根因,按以下步骤修改即可解决:
1. 修复myproj.log文件权限问题
权限配置顺序错误:Dockerfile里先执行了chown app:app $APP_HOME,之后才执行COPY . $APP_HOME把本地项目代码复制到容器内。Dockerfile中USER指令仅对后续执行的RUN/CMD/ENTRYPOINT生效,COPY操作默认以root身份执行,复制进来的文件所有者为root,不会继承之前设置的目录权限,导致运行时app用户没有项目目录下文件的写入权限。
修改Dockerfile.prod,把COPY . $APP_HOME移动到权限配置命令之前,调整后对应片段如下:
# 省略前面不变的配置 RUN chown -R app:app $HOME # 先复制项目代码 COPY . $APP_HOME # 再统一给所有项目文件设置权限 RUN chown -R app:app $APP_HOME && chmod ug+rws $APP_HOME RUN chown -R app:app /var/log/gunicorn # 切换用户再装依赖 USER app RUN pip install --no-cache /wheels/*
如果不想调整COPY顺序,也可以在COPY之后补执行一次RUN chown -R app:app $APP_HOME,效果一致。
2. 修复Gunicorn参数无法识别的问题
compose文件里command块用了>折叠换行符,这种写法会把换行替换成空格,但你在gunicorn命令的每一行末尾加了\转义符,转义符加空格之后会被sh解析成命令结束标记,后续的参数会被当成独立的shell命令执行,才会报--xxx: not found的错误。
两种修改方式二选一即可:
- 方式一:去掉所有gunicorn参数末尾的
\,因为>折叠符本身就会把多行拼成单行命令,不需要额外加转义换行 - 方式二:把折叠符从
>换成|-,保留换行的同时让\正常生效
推荐用方式一,修改后command块如下:
command: > sh -c "python manage.py makemigrations && python manage.py migrate && gunicorn --capture-output --access-logfile /var/log/gunicorn/access.log --error-logfile /var/log/gunicorn/error.log --enable-stdio-inheritance --workers 3 --worker-tmp-dir /dev/shm --bind 0.0.0.0:8000 myproj.wsgi:application"
修改完成后重新执行构建启动命令即可正常拉起服务。
补充提示:生产环境不建议每次容器启动都执行
makemigrations,该操作会自动生成模型迁移文件,容易造成迁移文件冲突,建议仅保留migrate命令,迁移文件在本地开发环境生成后提交到代码仓库。
内容的提问来源于stack exchange,提问作者Homunculus Reticulli

