【问题标题】:How to upload file outside Docker container in Flask app如何在 Flask 应用程序中将文件上传到 Docker 容器外
【发布时间】:2021-12-30 10:51:47
【问题描述】:

我是 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

DockerFile

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 中的脚本更改为这个(我从这个question 尝试过这个),是的,它不起作用:

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

【问题讨论】:

  • 您不需要-v 选项来覆盖应用程序代码,也不需要--network host 选项来禁用Docker 网络。您的 Dockerfile 中的目录似乎与您最初的描述不匹配,这也有点令人困惑。您究竟在运行什么命令来“检查项目结构”?应用程序是否运行?如果您将文件上传到其中,它们会在哪里结束?
  • 嗯,不同文件系统结构的原因在于您的 Dockerfile 内部:您复制当前目录的所有内容(我猜应该是目录 myApp,因为那是您的 Dockerfile 所在的位置)进入您的 docker 映像/容器的目录 code (如果尚不存在,则会创建它)。结果是/code/controller。顺便说一句,我不确定你为什么要在这里使用音量。
  • @DavidMaze 就是这样。该应用程序确实运行,但是当我上传文件时,我无法跟踪它。我不是在这里制作 Docker 脚本的人,我只是要求创建一个 API 来上传和下载文件。项目文件夹结构与我之前描述的完全一样,但是由于项目运行在 Docker 容器上,当我检查我制作的 Controller API 的结构文件夹时,它返回为/code/controller,这与我描述的文件夹结构不同之前。
  • @Ibnu 卷基本上只是位于主机系统上的文件或目录,您在启动之前将其放入容器中。这样,您将拥有应用程序的开发映像,您可以在其中始终将应用程序文件的当前状态挂载到容器中,而不是拥有应用程序的固定版本的映像,其中包含应用程序的相应文件图像已经。我只是在这里没有看到很大的优势,因为据我所知,您无法在不停止容器的情况下更改音量,因此在更新应用程序文件时必须始终重新启动它。
  • @Ibnu 我没有使用 gitlab CI 的经验,但是如果您可以访问 gitlab CI 服务器,您可以创建一个目录,其中包含您想要“上传”开箱即用的所有文件。然后,在 .gitlab-ci.yaml 中,将此目录的一个卷添加到您的 docker 部署中(在容器内的 /uploads 中)。现在容器始终包含这些文件,因此从现在开始,这些文件在每个部署中都可用。当然,您可以随时添加更多文件。

标签: docker flask docker-compose flask-restful


【解决方案1】:

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


从您的工作文件夹结构来看,您的应用似乎正在尝试写入../../uploads 文件夹,目录结构提高了 2 级。因此我们可以在容器中创建一个文件夹结构来适应这种情况。

Dockerfile 中进行以下更改:

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

我们将应用程序 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

【讨论】:

    【解决方案2】:

    您在 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

    【讨论】:

    • 感谢您的回答,我想我明白您对我的情况的意思。如何在容器内创建文件夹?我认为我无权访问提供给我的服务器凭据中的 open /code。我只能访问我之前描述的项目文件夹,而不是容器位置。
    • @IbnuAgielsAlthur 这需要您提供更多信息;这个容器在哪里运行,谁拥有/运行它,你在你的主机上有哪些资源的访问权限以及你如何访问这个主机。如果您使用自己的主目录并在容器内运行您拥有的所有权利的容器。如果所有东西都由其他人运行,那么您只需要访问主机并上传其中的文件夹,而您不需要访问容器。同样重要的是,您的代码尝试将这些上传的文件存储在容器中的位置。
    • @IbnuAgielsAlthur 容器可以被宿主机中的运行用户访问,所以如果你运行它,你就是它的 root 用户。但即便如此,在命令中运行此“绑定安装”文件夹时,您也不需要访问容器。您所需要的只是在您的烧瓶代码中精细定义的上传文件夹,并将该文件夹包含在容器中以及您的主机中的挂载文件夹。如果由其他人运行,那么您只需要访问他们在运行命令中设置的主机端上传文件夹即可读取其中的文件。
    • @IbnuAgielsAlthur 最后一件事:dockerfile 中的/root/.local 只是复制在compile-image 中的应用程序构建过程中创建的一些运行脚本,并且您的应用程序需要从build-image 运行。您可以看到它已添加到系统的 PATH 中。但是您的应用程序被复制到 /code 文件夹中并在其中运行。这可能会清除您脑海中的一些事情。至于上传文件夹的位置,请检查应用程序的代码和设置文件并绑定该文件夹
    • 关于访问,我可以访问项目部署的位置,位于 /home/myProjects/myDocker/myApp 中,而我的服务器凭据不是 root,这意味着我无法访问 /root/.local,很可能是 Docker在 myApp 在/root/.local/code/myDocker/myApp/ 中运行,我知道从将脚本运行到 myApp 到从 Docker 容器的根目录映射所有可用文件夹,尽管我不知道我的假设是否正确。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-24
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多