如何在 Flask 应用程序中上传 Docker 容器外的文件

How to upload file outside Docker container in Flask app

我是 Docker 的新手。我在 Docker 中包含的 Flask 应用程序中上传文件并访问它时遇到问题。

假设我有一个采用这种结构的项目:

/home
| /myProjects
  | /myDockers
  | | /myApp
  | | | /controller
  | | | ...
  | | | wsgi.py
  | | | .gitlab-ci.yml
  | | | DockerFile
  | | | gunicorn.sh
  | /uploads

我的 Flask 应用程序在 /MyApp 中,而我想将文件上传到 /uploads。该应用已部署在 Docker 上,配置如下:

.gitlab-ci.yml:

stages:
  - build
  - deploy

build-image:
  stage: build
  only:
    refs:
      - dev
  script:
    - cd /home/myProjects/myDocker/myApp/
    - git checkout dev
    - git pull origin dev
    - docker container rm -f myApp
    - docker image rm -f myApp
    - docker build -t myApp -f 20021_DockerfileLmsGeneralRepositoryService .

deploy-container:
  stage: deploy
  only:
    refs:
      - dev
  script:
    - docker run -d --network host -e DATABASE_URL=$DATABASE_URL -e REDIS_HOST=$REDIS_HOST --name myApp myApp

Docker文件:

FROM python:3.8.7-slim-buster AS compile-image
RUN apt-get update && apt-get install -y --no-install-recommends build-essential gcc libpq-dev && apt-get install -y apt-utils

COPY ./requirements.txt .
RUN pip install --upgrade --user -r requirements.txt
RUN pip install injector


COPY . /code

FROM python:3.8.7-slim-buster AS build-image
COPY --from=compile-image /code/ /code
COPY --from=compile-image /root/.local /root/.local

# Make sure scripts in .local are usable:
ENV PATH=/root/.local/bin:$PATH
WORKDIR /code

RUN chmod a+x gunicorn.sh
ENTRYPOINT ["sh","./gunicorn.sh"]

gunicorn.sh:

#!/bin/sh
gunicorn wsgi:application -w 2 --threads 2 --preload -b 0.0.0.0:20021

我已经在我的 Flask 应用程序中创建了一个 API 来上传文件并在本地 (Windows) PC 上成功,但是当我将它部署到开发服务器时,文件夹的结构是与我的预期不同。当我使用脚本检查项目结构时,它返回:

"/code/controller"

和服务器中项目的结构不一样,所以问学长,他说我应该了解Docker中的Volume,因为项目包含在Docker容器中,但我从来没有用过Docker。

我也已经尝试将 .gitlab-ci.yml 中的脚本更改为这个(我从这个 尝试过)是的,它没有不工作:

- docker run -d --network host -e DATABASE_URL=$DATABASE_URL -e REDIS_HOST=$REDIS_HOST --name myApp -v /home/myProjects/:/root/.local/code myApp

您在 CI 文件中使用 cd /home/myProjects/myDocker/myApp/,在 docker 构建文件中使用 COPY . /code

这意味着“无论您在主机的 /home/myProjects/myDocker/myApp/ 文件夹中看到什么,都将它们复制到容器的 /code 文件夹中”。因此你的 ...myApp/* 变成 /code/*

但你的痛苦不仅仅如此。从应用程序的角度来看,您正在将上传的文件写入 ../../uploads 但您的容器中没有这样的文件夹。另请注意,您的主机中有 2 个父级别,您的应用程序正在寻找这样的路径,并且无论如何都不会在容器内找到,因为您的项目距离根级别只有 1 级。

在后一种情况下,更改您的代码以从环境中读取上传文件夹路径 (os.environ?),将默认设置为 ../../uploads 用于您的开发,在您的容器中创建一个文件夹,例如/uploads,使用 ENV 在 docker 文件中添加它的路径,然后使用 volume 命令映射主机和容器上传文件夹路径,可能 -v home/myProjects/uploads:/uploads


编辑:

我写了一个简单的应用程序来执行上面最后一段。请访问此地址:https://github.com/yilmazdurmaz/basicbindmount

在我们谈话之后,我可以看到另一个更简单的解决方案。我的其他答案的部分内容仍然适用。


从您的工作文件夹结构来看,您的应用程序似乎尝试写入 ../../uploads 文件夹,在目录结构中向上 2 层。因此我们可以在容器中创建一个文件夹结构来适应这个。

Dockerfile 中进行以下更改:

  • COPY --from=compile-image /code/ /code 编辑为 COPY --from=compile-image /code/ /code/app
  • WORKDIR /code 编辑为 WORKDIR /code/app
  • after FROM .. AS build-image 的任何地方添加 RUN mkdir /uploadsbefore ENTRYPOINT

我们将 app 2 层复制到容器深处,并将上传文件夹添加到根目录中。

现在 运行 将您的 运行 命令更改为:

docker run -d --network host -e DATABASE_URL=$DATABASE_URL -e REDIS_HOST=$REDIS_HOST --name myApp -v /home/myProjects/uploads:/uploads myApp

我们将主机中的上传文件夹绑定到容器中的上传文件夹中。您可以将绑定设置为 -v path_to_your_uploads_folder_in_host:/uploads