当数据库在容器中运行时,如何使用 alembic 自动生成和应用迁移?

How to autogenerate and apply migrations with alembic when the database runs in a container?

我发现在容器化环境中处理数据库迁移的工作流程令人困惑。我有一个带有附加数据库的网站 API。 API 运行 在一个容器中,数据库在另一个容器中。项目文件结构如下

.
├── docker-compose.yml
├── Dockerfile
└── app
|    ├── __init__.py
|    ├── database
|    |    ├── alembic/
|    |    ├── __init__.py
|    |    ├── db.py
|    |    └── models.py
|    ├── other
|    ├── source
|    └── files
├── other
└── files

为了让 API 容器能够访问数据库,ini 文件中的 sqlalchemy.url 设置为:

postgresql://{username}:{password}@db:5432/{database}

然而,当我想进行迁移时,例如添加一个 table 列,我将更改 app/database/models.py 中的模型,将目录更改为 app/database 和 运行 alembic revision --autogenerate -m "Description"。这是问题发生的地方,我得到错误:

sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) could not translate host name "db" to address: Name or service not known

如果我将主机名更改为 localhost,它可以工作,但是 docker-compose 会中断,因为它必须引用容器名称。

这个工作流程好像不太对。人们如何在使用容器的项目中使用数据库?

docker-compose.yml 文件如下所示:

version: "3"

services:
  db:
    image: postgres
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=username
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=database

  app:
    build: .
    command: bash -c "cd app/database && alembic upgrade head && cd ../.. && python app/main.py"
    volumes:
      - .:/code
    ports:
      - "5000:5000"
    depends_on:
      - db

由于您的数据库已发布 ports:,您可以直接从主机访问它。主机上容器外的应用运行和Compose设置中的同一个应用运行是不同的环境,用环境变量来指定是合适的。不要在您的应用程序中对数据库位置进行硬编码。

如果您可以使用 standard PostgreSQL environment variables,那么指定它就相当容易了。

# To run migrations:
cd app/database
PGUSER=username PGPASSWORD=password PGDATABASE=database \
  alembic revision --autogenerate -m "Description"
# (assumes default PGHOST=localhost)
# To run the application:
version: '3.8'
services:
  db: { ... }
  app:
    build: .
    environment:
      PGHOST: db
      PGUSER: username
      PGPASSWORD: password
      PGDATABASE: database
    ports:
      - "5000:5000"
    depends_on:
      - db

您似乎缺少连接到两个容器的网络,因此 DNS 可以解析为数据库。因此,将相同的网络添加到两个容器中,如下所示:

services:
  db:
    image: postgres
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=username
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=database
    networks:
      - app-network    
  app:
    build: .
    command: bash -c "cd app/database && alembic upgrade head && cd ../.. && python app/main.py"
    volumes:
      - .:/code
    ports:
      - "5000:5000"
    depends_on:
      - db
    networks:
      - app-network
networks:
  app-network:

附带说明一下,您已将端口映射到本地主机上的 5432。有一个特殊的 docker DNS 指向容器所在的本地主机 运行,因此您可以在应用程序容器内使用 host.docker.internal,它将解析为您的本地主机。