Docker开发工作流程-Flask和Postgres指南

金瑞
2023-12-01

by Timothy Ko

蒂莫西·高(Timothy Ko)

Docker开发工作流程-Flask和Postgres指南 (Docker Development WorkFlow — a guide with Flask and Postgres)

Docker, one of the latest crazes, is an amazing and powerful tool for packing, shipping, and running applications. However, understanding and setting up Docker for your specific application can take a bit of time. Since the internet is filled with conceptual guides, I won’t be going too deep conceptually about Containers. Instead, I’ll be explaining what each line I write means and how you can apply that to your specific application and configuration.

Docker是最新的疯子之一,它是用于打包,运输和运行应用程序的出色而强大的工具。 但是,了解和设置特定应用程序的Docker可能需要一些时间。 由于互联网上充斥着概念性指南,因此我不会对容器进行概念上的深入研究。 相反,我将解释我写的每一行的含义以及如何将其应用于特定的应用程序和配置。

为什么选择Docker? (Why Docker?)

I am part of a student-run non-profit called Hack4Impact at UIUC, where we develop technical projects for non-profit organizations to help them further their missions. Each semester, we have multiple project teams of 5–7 student software developers, with a variety of skill levels including students who have only finished their first college-level computer science course.

我是UIUC学生管理的非营利组织Hack4Impact的一员,我们在该组织为非营利组织开发技术项目,以帮助他们进一步履行使命。 每个学期,我们有5-7个学生软件开发人员的多个项目团队,其技能水平各不相同,其中包括仅完成了第一门大学水平计算机科学课程的学生。

Since many non-profits often asked for web applications, I curated a Flask Boilerplate to allow teams to quickly get their backend REST API services up and running. Common utility functions, application structure, database wrappers, and connections are all provided along with documentation for setup, best coding practices, and steps for Heroku deployment.

由于许多非营利组织经常要求使用Web应用程序,因此我精心策划了Flask Boilerplate,以使团队能够快速启动并运行其后端REST API服务。 提供了通用实用程序功能,应用程序结构,数据库包装程序和连接,以及用于设置,最佳编码实践和Heroku部署步骤的文档。

开发环境和依赖关系问题 (Issues with Development Environment and Dependencies)

However, since we onboard new student software developers every semester, teams would spend a lot of time configuring and troubleshooting environment issues. We would often have multiple members developing on different Operating Systems and ran into a myriad of problems(Windows, I’m pointing at you). Although many of those problems were trivial, such as starting up the correct PostgreSQL database version with the right user/password, it wasted time that could’ve been put into the product itself.

但是,由于我们每学期都要招收新的学生软件开发人员,因此团队将花费大量时间来配置和解决环境问题。 我们经常会有多个成员在不同的操作系统上进行开发,并遇到许多问题(Windows,我指的是您)。 尽管其中许多问题都是微不足道的,例如使用正确的用户名/密码来启动正确的PostgreSQL数据库版本,但浪费的时间本可以放在产品本身中。

In addition to that, I only wrote documentation for MacOS users with only bash instructions (I have a Mac), and essentially left Windows and Linux users out to dry. I could’ve spun up some Virtual Machines and documented the setup again for each OS, but why would I do that if there’s Docker?

除此之外,我只使用bash指令(我有Mac)为MacOS用户编写文档,并且基本上使Windows和Linux用户干to。 我本可以启动一些虚拟机,并再次记录每个操作系统的设置,但是如果有Docker,为什么还要这么做呢?

输入Docker (Enter Docker)

With Docker, the entire application can be isolated in containers that can be ported from machine to machine. This allows for consistent environments and dependencies. Thus, you can “build once, run anywhere,” and developers will now be able to install just one thing — Docker — and run a couple commands to get the application running. Newcomers will be able to rapidly begin developing without worrying about their environment. Nonprofits will also be able to quickly make changes in the future.

使用Docker,可以将整个应用程序隔离在可以从机器移植到机器的容器中。 这允许一致的环境和依赖性。 因此,您可以“构建一次,在任何地方运行”,开发人员现在将仅能安装件事Docker,并运行几个命令来使应用程序运行。 新移民将能够Swift开始发展,而不必担心他们的环境。 非营利组织也将能够在未来Swift做出改变。

Docker also has many other benefits, such as its portable and resource-efficient nature (compared to Virtual Machines), and how you can painlessly set up Continuous Integration and rapidly deploy your application.

Docker还具有许多其他好处,例如其可移植性和资源高效性(与虚拟机相比),以及如何轻松设置持续集成并快速部署应用程序。

Docker核心组件的简要概述 (A Brief Overview of Docker Core Components)

There are many resources online that will explain Docker better than I can, so I won’t go over them in too much detail. Here’s an awesome blog post on its concepts, and another one on Docker specifically. I will, however, go over some of the Core Components of Docker that are required to understand the rest of this blog post.

在线上有许多资源可以比我更好地解释Docker,因此我不会对它们进行过多的介绍。 这是一篇很棒的博客文章,介绍了它的概念, 另外一篇是关于Docker的。 但是,我将介绍一些Docker核心组件,它们是理解本博文其余部分所必需的。

Docker映像 (Docker Images)

Docker images are read-only templates that describe a Docker Container. They include specific instructions written in a Dockerfile that defines the application and its dependencies. Think of them as a snapshot of your application at a certain time. You will get images when you docker build.

Docker映像是描述Docker容器的只读模板。 它们包括用Dockerfile编写的特定指令,该指令定义了应用程序及其依赖项。 将它们视为特定时间的应用程序快照。 当您docker build时,您将获得图像。

Docker容器 (Docker Containers)

Docker Containers are instances of Docker images. They include the operating system, application code, runtime, system tools, system libraries, and so on. You are able to connect multiple Docker Containers together, such as a having a Node.js application in one container that is connected to a Redis database container. You will run a Docker Container with docker start.

Docker容器是Docker映像的实例。 它们包括操作系统,应用程序代码,运行时,系统工具,系统库等。 您可以将多个Docker容器连接在一起,例如将一个Node.js应用程序放在一个连接到Redis数据库容器的容器中。 您将使用docker start运行Docker容器。

Docker注册表 (Docker Registries)

A Docker Registry is a place for you to store and distribute Docker images. We will be using Docker Images as our base images from DockerHub, a free registry hosted by Docker itself.

Docker Registry是一个用于存储和分发Docker映像的地方。 我们将使用Docker Images作为DockerHub的基础映像,DockerHub是Docker本身托管的免费注册表。

Docker撰写 (Docker Compose)

Docker Compose is a tool that allows you to build and start multiple Docker Images at once. Instead of running the same multiple commands every time you want to start your application, you can do them all in one command — once you provide a specific configuration.

Docker Compose是一个工具,可让您一次构建和启动多个Docker映像。 您不必在每次启动应用程序时都运行相同的多个命令,而是可以在提供特定配置后在一个命令中全部执行这些命令。

Flask和Postgres的Docker示例 (Docker example with Flask and Postgres)

With all the Docker components in mind, let’s get into setting up a Docker Development environment with Flask Application using Postgres as its data store. For the remainder of this blog post, I will be referencing Flask Boilerplate, the repository I mentioned earlier for Hack4Impact.

考虑到所有Docker组件,让我们开始使用Flask Application将Postgres作为其数据存储来设置Docker开发环境。 对于本博文的其余部分,我将引用Flask Boilerplate ,这是我之前提到的Hack4Impact的存储库。

In this configuration, we will use Docker to build two Images:

在此配置中,我们将使用Docker构建两个镜像:

  • app — the Flask Application served in port 5000

    app — Flask Application在端口5000中提供

  • postgres — the Postgres Database served in port 5432

    postgres —端口5432中提供的Postgres数据库

When you look at the top directory, there are three files that define this configuration:

当您查看顶层目录时,有三个文件定义了此配置:

  • Dockerfile — a script composed of instructions to setup the app containers. Each command is automatic and is successively performed. This file will be located in the directory where you run the app(python manage.py runserver or python app.py or npm start are some examples). In our case, it is in the top directory(where manage.py is located). A Dockerfile accepts Docker Instructions.

    Dockerfile —一个脚本,由设置app容器的指令组成。 每个命令都是自动的,并且会连续执行。 该文件将位于您运行应用程序的目录中( python manage.py runserverpython app.pynpm start是一些示例)。 在我们的例子中,它位于顶层目录( manage.py所在的目录)中。 Dockerfile接受Docker指令

  • .dockerignore — specifies which files not to include in the Container. It is just like .gitignore but for the Docker Containers. This file is paired with the Dockerfile.

    .dockerignore-指定不包含在容器中的文件。 就像.gitignore一样,但用于Docker容器。 该文件与Dockerfile配对。

  • docker-compose.yml — Configuration file for Docker Compose. This will allow us to build both app and postgres images at once, define volumes and state that app depends on postgres, and set required environmental variables.

    docker-compose.yml — Docker Compose的配置文件。 这将使我们能够同时构建apppostgres图像,定义卷并声明app依赖于postgres ,并设置所需的环境变量。

Note: There’s only one Dockerfile for two images because we will be taking an official Docker Postgres image from DockerHub! You can include your own Postgres Image by writing your own Dockerfile for it, but there’s no point.

注意:两个映像只有一个Dockerfile,因为我们将从DockerHub获取一个官方的Docker Postgres映像! 您可以通过为其编写自己的Dockerfile来包含自己的Postgres Image,但这没有意义。

Docker文件 (Dockerfile)

Just to clarify again, this Dockerfile is for the app container. As an overview, here is the entire Dockerfile—it essentially gets a base image, copies the application over, installs dependencies, and sets a specific environment variable.

再次说明一下,此Dockerfile用于app容器。 作为概述,这是整个Dockerfile,它实际上是获取基本映像,复制应用程序,安装依赖项并设置特定的环境变量。

FROM python:3.6
LABEL maintainer "Timothy Ko <tk2@illinois.edu>"
RUN apt-get update
RUN mkdir /app
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir -r requirements.txt
ENV FLASK_ENV="docker"
EXPOSE 5000

Because this Flask Application uses Python 3.6, we want an environment that supports it and already has it installed. Fortunately, DockerHub has an official image that’s installed on top of Ubuntu. In one line, we will have a base Ubuntu image with Python 3.6, virtualenv, and pip. There are tons of images on DockerHub, but if you would like to start off with a fresh Ubuntu image and build on top of it, you could do that.

由于此Flask应用程序使用Python 3.6,因此我们需要一个支持它并且已经安装了它的环境。 幸运的是, DockerHub的官方映像已安装在Ubuntu之上。 在一行中,我们将获得一个基本的Ubuntu映像,其中包含Python 3.6,virtualenv和pip。 DockerHub上有很多映像,但是如果您想从一个新的Ubuntu映像开始并在其之上构建,则可以这样做。

FROM python:3.6

I then note that I’m the maintainer.

然后,我注意到我是维护者。

LABEL maintainer "Timothy Ko <tk2@illinois.edu>"

Now it’s time to add the Flask application to the image. For simplicity, I decided to copy the application under the /app directory on our Docker Image.

现在是时候将Flask应用程序添加到图像了。 为简单起见,我决定将应用程序复制到Docker Image的/app目录下。

RUN mkdir /app
COPY . /app
WORKDIR /app

WORKDIR is essentially a cd in bash, and COPY copies a certain directory to the provided directory in an image. ADD is another command that does the same thing as COPY , but it also allows you to add a repository from a URL. Thus, if you want to clone your git repository instead of copying it from your local repository (for staging and production purposes), you can use that. COPY, however, should be used most of the time unless you have a URL. Every time you use RUN, COPY, FROM, or CMD, you create a new layer in your docker image, which affects the way Docker stores and caches images. For more information on best practices and layering, see Dockerfile Best Practices.

WORKDIR本质上是bash中的cdCOPY将某个目录复制到映像中提供的目录中。 ADD是另一个与COPY相同的命令,但它也允许您从URL添加存储库。 因此,如果您想克隆git存储库而不是从本地存储库复制它(用于登台和生产目的),则可以使用它。 但是,除非有URL,否则大部分时间应使用COPY 。 每次使用RUNCOPYFROMCMD ,都会在Docker映像中创建一个新层,这会影响Docker存储和缓存映像的方式。 有关最佳实践和分层的更多信息,请参阅Dockerfile最佳实践

Now that we have our repository copied to the image, we will install all of our dependencies, which is defined in requirements.txt

现在,我们已将存储库复制到映像,我们将安装所有依赖项,这些依赖项在requirements.txt定义

RUN pip install --no-cache-dir -r requirements.txt

But say you had a Node application instead of Flask — you would instead write RUN npm install. The next step is to tell Flask to use Docker Configurations that I hardcoded into config.py. In that configuration, Flask will connect to the correct database we will set up later on. Since I had production and regular development configurations, I made it so that Flask would choose the Docker Configuration whenever the FLASK_ENV environment variable is set to docker. So, we need to set that up in our app image.

但是,说您有一个Node应用程序而不是Flask -您应该编写RUN npm install 。 下一步是告诉Flask使用我硬编码到config.py Docker配置。 在这种配置下,Flask将连接到我们稍后将设置的正确数据库。 因为我有生产和正常的发展结构,我把它使瓶会选择泊坞窗配置每当FLASK_ENV环境变量设置为docker 。 因此,我们需要在我们的app图片中进行设置。

ENV FLASK_ENV="docker"

Then, expose the port(5000) the Flask application runs on:

然后,公开运行Flask应用程序的端口(5000):

EXPOSE 5000

And that’s it! So no matter what OS you’re on, or how bad you are at following documentation instructions, your Docker image will be same as your team members’ because of this Dockerfile.

就是这样! 因此,无论您使用的是哪种操作系统,或在遵循文档说明方面有多糟糕,由于该Dockerfile,您的Docker映像都将与团队成员的映像相同。

Anytime you build your image, these following commands will be run. You can now build this image with sudo docker build -t app .. However, when you run it with sudo docker run app to start a Docker Container, the application will run into a database connection error. This is is because you haven’t provisioned a database yet.

无论何时构建映像,都将运行以下以下命令。 您现在可以使用sudo docker build -t app .构建该映像sudo docker build -t app . 。 但是,当您使用sudo docker run app运行它以启动Docker Container时,该应用程序将遇到数据库连接错误。 这是因为尚未配置数据库。

docker-compose.yml (docker-compose.yml)

Docker Compose will allow you to do that and build your app image at the same time. The entire file looks like this:

Docker Compose将允许您这样做并同时构建您的app映像。 整个文件如下所示:

version: '2.1'services:  postgres:    restart: always    image: postgres:10    environment:      - POSTGRES_USER=${POSTGRES_USER}      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}      - POSTGRES_DB=${POSTGRES_DB}    volumes:      - ./postgres-data/postgres:/var/lib/postgresql/data    ports:      - "5432:5432"  app:    restart: always    build: .    ports:      - 5000:5000    volumes:      - .:/app

For this specific repository, I decided to use version 2.1 since I was more comfortable with it and it had a few more guides and tutorials on it — yeah, that’s my only reasoning for not using version 3. With version 2, you must provide “services” or images you want to include. In our case, it is app and postgres(these are just names that you can refer to when you use docker-compose commands. You call them database and api or whatever floats your boat).

对于这个特定的存储库,我决定使用2.1版,因为我对此比较满意,并且上面有更多的指南和教程-是的,这是我不使用3版的唯一原因。对于2版,您必须提供“服务”或您要包含的图像。 在我们的例子中,它是apppostgres (这些只是使用docker-compose命令时可以引用的名称。您将它们称为databaseapi或其他任何可以浮动的名称)。

Postgres图片 (Postgres Image)

Looking at the Postgres Service, I specify that it is a postgres:10 image, which is another DockerHub Image. This image is an Ubuntu Image that has Postgres installed and will automatically start the Postgres server.

查看Postgres服务,我指定它是一个postgres:10映像,这是另一个DockerHub映像。 该映像是已安装Postgres的Ubuntu映像,它将自动启动Postgres服务器。

postgres:  restart: always  image: postgres:10  environment:    - POSTGRES_USER=${USER}    - POSTGRES_PASSWORD=${PASSWORD}    - POSTGRES_DB=${DB}  volumes:    - ./postgres-data/postgres:/var/lib/postgresql/data  ports:    - "5432:5432"

If you want a different version, just change the “10” to something else. To specify what user, password, and database you want inside Postgres, you have to define environment variables beforehand — this is implemented in the official postgres Docker image’s Dockerfile. In this case, the postgres image will inject the $USER, $PASSWORD, and $DB environment variables and make them the POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB envrionment variables inside the postgres container. Note that $USER and the other environment variables injected are environment variables specified in your own computer (more specifically the command line process you are using to run the docker-compose up command. By injecting your credentials, this allows you to not commit your credentials into a public repository.

如果要使用其他版本,只需将“ 10”更改为其他名称即可。 要指定Postgres内部所需的用户,密码和数据库,您必须事先定义环境变量-这是在正式的postgres Docker映像的Dockerfile中实现的。 在这种情况下, postgres映像将注入$USER$PASSWORD$DB环境变量,并使它们成为postgres容器POSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_DB envrionment变量。 请注意, $USER和注入的其他环境变量是您自己的计算机中指定的环境变量(更具体地说,您用于运行docker-compose up命令的命令行进程。通过注入凭据,这使您可以不提交凭据到公共存储库中。

Docker-compose will also automatically inject environment variables if you have a .env file in the same directory as your docker-compose.yml file. Here’s an example of a .env file for this scenario:

如果您在与.env docker-compose.yml文件位于同一目录中的.env文件,则Docker-compose还将自动注入环境变量。 这是此情况的.env文件的示例:

USER=testusrPASSWORD=passwordDB=testdb

Thus our PostgreSQL database will be named testdb with a user called testusr with password password.

因此,我们的PostgreSQL数据库将被命名为testdb ,用户名为testusr ,密码为password。

Our Flask application will connect to this specific database, because I wrote down its URL in the Docker Configurations I mentioned earlier.

我们的Flask应用程序将连接到这个特定的数据库,因为我在前面提到的Docker Configurations中写下了它的URL。

Every time a container is stopped and removed, the data is deleted. Thus, you must provide a persistent data storage so none of the database data is deleted. There are two ways to do it:

每次停止和删除容器时,数据都会被删除。 因此,您必须提供一个持久的数据存储,这样就不会删除任何数据库数据。 有两种方法可以做到这一点:

  • Docker Volumes

    Docker卷
  • Local Directory Mounts

    本地目录挂载

I’ve chosen to mount it locally to ./postgres-data/postgres , but it can be anywhere. The syntax is always[HOST]:[CONTAINER]. This means any data from /var/lib/postgresql/data is actually stored in ./postgres-data.

我选择将其本地安装到./postgres-data/postgres ,但是它可以在任何地方。 语法始终为[HOST]:[CONTAINER] 。 这意味着/var/lib/postgresql/data实际上都存储在./postgres-data

volumes:- ./postgres-data/postgres:/var/lib/postgresql/data

We will use the same syntax for ports:

我们将对端口使用相同的语法:

ports:- "5432:5432"

应用程式图片 (app Image)

We will then define the app image.

然后,我们将定义app图像。

app:  restart: always  build: .  ports:    - 5000:5000  volumes:     - .:/app  depends_on:    - postgres  entrypoint: ["python", "manage.py","runserver"]

We first define it to have restart: always. This means that it will restart whenever it fails. This is especially useful when we build and start these containers. app will generally start up before postgres, meaning that app will try to connect to the database and fail, since the postgres isn’t up yet. Without this property, app would just stop and that’s the end of it.

我们首先将其定义为具有restart: always 。 这意味着它将在失败时重新启动。 当我们构建和启动这些容器时,这特别有用。 app通常会在postgres之前启动,这意味着该app将尝试连接到数据库并失败,因为postgres尚未启动。 没有此属性, app将停止运行,然后就结束了。

We then define that we want this build to be the Dockerfile that is in this current directory:

然后,我们定义我们希望此构建为当前目录中的Dockerfile:

build: .

This next step is pretty important for the Flask server to restart whenever you change any code in your local repository. This is very helpful so you don’t need to rebuild your image over and over again every time to see your changes. To do this, we do the same thing we did for postgres : we state that the /app directory inside the container will be whatever is in .(the current directory). Thus, any changes in your local repo will be reflected inside the container.

每当您更改本地存储库中的任何代码时,下一步对于Flask服务器重新启动都非常重要。 这非常有帮助,因此您无需每次都一次又一次地重建图像以查看更改。 为此,我们做与postgres相同的事情:我们声明容器内的/app目录将是。(当前目录)中的内容。 因此,本地存储库中的任何更改都将反映在容器内。

volumes:  - .:/app

After this, we need to tell Docker Compose that app depends on the postgres container. Note that if you change the name of the image to something else like database, you must replace that postgres with that name.

之后,我们需要告诉Docker Compose该应用程序依赖于postgres容器。 请注意,如果将图像名称更改为其他名称,例如database ,则必须使用该名称替换该postgres

depends_on:  - postgres

Finally, we need to provide the command that is called to start our application. In our case, it’s python manage.py runserver.

最后,我们需要提供调用该命令以启动我们的应用程序。 在我们的例子中,它是python manage.py runserver

entrypoint: ["python", "manage.py","runserver"]

One caveat for Flask is that you must explicitly note which host (port) you want to run it in, and whether you want it to be in debug mode when you run it. So in manage.py, I do that with:

Flask的一个警告是,您必须明确指出要在哪个主机(端口)中运行,以及在运行时是否希望它处于调试模式。 所以在manage.py ,我这样做:

def runserver():    app.run(debug=True, host=’0.0.0.0', port=5000)

Finally, build and start your Flask app and Postgres Database using your Command Line:

最后,使用命令行构建并启动Flask应用和Postgres数据库:

docker-compose builddocker-compose up -ddocker-compose exec app python manage.py recreate_db

The last command essentially creates the database schema defined by my Flask app in Postgres.

最后一个命令实质上是创建由我在Postgres中的Flask应用程序定义的数据库架构。

And that’s it! You should be able to see the Flask application running on http://localhost:5000!

就是这样! 您应该能够看到Flask应用程序在http:// localhost:5000上运行!

Docker命令 (Docker Commands)

Remembering and finding Docker commands can be pretty frustrating in the beginning, so here’s a list of them! I’ve also written a bunch of commonly used ones in my Flask Boilerplate Docs if you want to refer to that.

回忆和寻找泊坞窗命令可以在一开始很讨厌,所以这里是他们的名单! 如果您要引用它,我还在Flask Boilerplate Docs中写了一堆常用的文档

结论 (Conclusion)

Docker truly allows teams to develop much faster with its portability and consistent environments across platforms. Although I’ve only gone through using Docker for development, Docker excels when you use it for Continuous Integration/testing and in Deployment.

Docker通过其可移植性和跨平台一致的环境,确实使团队能够更快地发展。 尽管我仅使用Docker进行开发,但是当您将Docker用于持续集成/测试和部署时,Docker表现出色。

I could add a couple more lines and have a full production setup with Nginx and Gunicorn. If I wanted to use Redis for session caching or as a queue, I could do that very quickly and everyone on my team would be able to have the same environment when they rebuilt their Docker Images.

我可以再添加几行,并使用Nginx和Gunicorn进行完整的生产设置。 如果我想使用Redis进行会话缓存或将其用作队列,我可以非常快地做到这一点,并且团队中的每个人都可以在重建Docker映像时拥有相同的环境。

Not only that, I could spin up 20 instances of the Flask Application in seconds if I wanted to. Thanks for reading! :)

不仅如此,如果我愿意,我可以在几秒钟内启动20个Flask应用程序实例。 谢谢阅读! :)

If you have any thoughts and comments, feel free to leave a comment below or email me at tk2@illinois.edu! Also, feel free to use my code or share this with your peers!

如果您有任何想法和意见,请随时在下面留言或发送电子邮件至tk2@illinois.edu给我! 另外,请随时使用我的代码或与您的同行分享!

翻译自: https://www.freecodecamp.org/news/docker-development-workflow-a-guide-with-flask-and-postgres-db1a1843044a/

 类似资料: