容器化 Flask 应用程序

1.准备 Flask 应用程序代码和 Dockerfile

1.创建一个项目目录 flask-postgres

bash 复制代码
[root@host1 ~]# mkdir -p flask-postgres && cd flask-postgres
[root@host1 flask-postgres]# 

2.在该项目目录下准备一个名为 app.py 的源代码文件

bash 复制代码
# 创建数据库密码文件目录
[root@host1 flask-postgres]# mkdir db

[root@host1 flask-postgres]# vi app.py
[root@host1 flask-postgres]# cat app.py
import json
from flask import Flask
import psycopg2
import os

app = Flask(__name__)

# 读取数据库密码(支持文件/环境变量两种方式)
if 'POSTGRES_PASSWORD_FILE' in os.environ:
    with open(os.environ['POSTGRES_PASSWORD_FILE'], 'r') as f:
        password = f.read().strip()
else:
    password = os.environ['POSTGRES_PASSWORD']

# 根路由:返回 Hello, Flask!
@app.route('/')
def hello_world():
    return 'Hello, Flask!'

# 查询数据库数据的路由
@app.route('/widgets')
def get_widgets():
    with psycopg2.connect(host="db", user="postgres", password=password, database="example") as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT * FROM widgets")
            row_headers = [x[0] for x in cur.description]
            results = cur.fetchall()
        conn.close()
    json_data = [dict(zip(row_headers, result)) for result in results]
    return json.dumps(json_data)

# 初始化数据库的路由(创建库和表)
@app.route('/initdb')
def db_init():
    # 先创建数据库
    conn = psycopg2.connect(host="db", user="postgres", password=password)
    conn.set_session(autocommit=True)
    with conn.cursor() as cur:
        cur.execute("DROP DATABASE IF EXISTS example")
        cur.execute("CREATE DATABASE example")
    conn.close()
    # 再创建表
    with psycopg2.connect(host="db", user="postgres", password=password, database="example") as conn:
        with conn.cursor() as cur:
            cur.execute("DROP TABLE IF EXISTS widgets")
            cur.execute("CREATE TABLE widgets (name VARCHAR(255), description VARCHAR(255))")
        conn.close()
    return 'init database'

if __name__ == "__main__":
    app.run(host="0.0.0.0")

3.准备用于安装 Python 依赖包的 requirements.txt 文件

bash 复制代码
[root@host1 flask-postgres]# vi requirements.txt
[root@host1 flask-postgres]# cat requirements.txt
blinker==1.6.2
click==8.1.6
colorama==0.4.6
Flask==2.3.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
psycopg2-binary==2.9.6
Werkzeug==2.3.6

4.准备用于构建镜像的 Dockerfile

bash 复制代码
[root@host1 flask-postgres]# vi Dockerfile
[root@host1 flask-postgres]# cat Dockerfile
# syntax=docker/dockerfile:1
ARG PYTHON_VERSION=3.11.4
FROM python:${PYTHON_VERSION}-slim as base

# 禁用 .pyc 文件生成 + 实时输出日志
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

# 创建非特权用户
ARG UID=1001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/bin/false" \
    --no-create-home \
    --uid "${UID}" \
    appuser

# 缓存依赖下载(加速构建)
RUN --mount=type=cache,target=/root/.cache/pip \
    --mount=type=bind,source=requirements.txt,target=requirements.txt \
    python -m pip install -r requirements.txt

# 切换到非特权用户
USER appuser

# 复制源代码到容器
COPY . .

# 暴露 Flask 端口
EXPOSE 5000

# 启动 Flask 应用
CMD python -m flask run --host=0.0.0.0

2.在 Compose 文件中定义服务

1.在项目目录下创建一个名为db的新目录,并在该目录中创建一个包含数据库密码的名为password.txt 的文件。为简化实验,将该文件内容(数据库密码)设置为"abel23"

bash 复制代码
[root@host1 flask-postgres]# cd db
[root@host1 db]# cd
[root@host1 ~]# cd flask-postgres/db
[root@host1 db]# vi password.txt
[root@host1 db]# cat password.txt
abc123

2.在项目目录下创建一个名为 compose.yaml 的文件来定义所需的服务和配套内容

bash 复制代码
[root@host1 flask-postgres]# cd db
[root@host1 db]# cd
[root@host1 ~]# cd flask-postgres/db
[root@host1 db]# vi password.txt
[root@host1 db]# cat password.txt
abc123
[root@host1 db]# cd flask-postgres
-bash: cd: flask-postgres: 没有那个文件或目录
[root@host1 db]# cd
[root@host1 ~]# cd flask-postgres
[root@host1 flask-postgres]# vi compose.yaml
[root@host1 flask-postgres]# cat compose.yaml
services:
  # Flask 应用服务
  server:
    build: .  # 从当前目录构建镜像
    ports:
      - "8000:5000"  # 主机8000端口 → 容器5000端口
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db-password  # 从secret读密码
    depends_on:
      db:
        condition: service_healthy  # 等数据库"健康"后再启动
    secrets:
      - db-password  # 引用密码secret

  # PostgreSQL 数据库服务
  db:
    image: postgres  # 官方PostgreSQL镜像
    restart: always  # 异常时自动重启
    user: postgres
    secrets:
      - db-password  # 给数据库提供密码secret
    volumes:
      - db-data:/var/lib/postgresql/data  # 持久化数据
    environment:
      POSTGRES_DB: example  # 初始化的数据库名
      POSTGRES_PASSWORD_FILE: /run/secrets/db-password  # 从secret读密码
    expose:
      - "5432"  # 暴露数据库内部端口
    healthcheck:  # 健康检查(确保数据库就绪)
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5

secrets:
  db-password:
    file: db/password.txt  # 关联本地密码文件

volumes:
  db-data:  # 持久化数据库数据的卷

3.查看项目目录结构,确认已经准备好如下目录和文件

bash 复制代码
[root@host1 flask-postgres]# tree .
.
├── app.py
├── compose.yaml
├── db
│   └── password.txt
├── Dockerfile
└── requirements.txt

1 directory, 5 files

3.构建镜像并运行应用程序

1.执行以下命令构建镜像并运行应用程序。-build 选项表示在启动容器之前构建镜像

bash 复制代码
[root@host1 flask-postgres]# docker compose up --build
[+] Building 273.6s (14/14) FINISHED                                                                 
 => [internal] load local bake definitions                                                      0.0s
 => => reading from stdin 507B                                                                  0.0s
 => [internal] load build definition from Dockerfile                                            0.0s
 => => transferring dockerfile: 901B                                                            0.0s
 => resolve image config for docker-image://docker.io/docker/dockerfile:1                       3.0s
 => CACHED docker-image://docker.io/docker/dockerfile:1@sha256:dabfc0969b935b2080555ace70ee69a  0.0s
 => [internal] load metadata for docker.io/library/python:3.11.4-slim                           4.7s
 => [internal] load .dockerignore                                                               0.0s
 => => transferring context: 2B                                                                 0.0s
 => [base 1/5] FROM docker.io/library/python:3.11.4-slim@sha256:17d62d681d9ecef20aae6c6605e9c  16.9s
 => => resolve docker.io/library/python:3.11.4-slim@sha256:17d62d681d9ecef20aae6c6605e9cf83b0b  0.0s
 => => sha256:17d62d681d9ecef20aae6c6605e9cf83b0ba3dc247013e2f43e1b5a045ad4901 1.65kB / 1.65kB  0.0s
 => => sha256:0275089b5b654bb33931fc239a447db9fdd1628bc9d1482788754785d6d9e464 1.37kB / 1.37kB  0.0s
 => => sha256:596e0d6b34dfaa7ed330941075bcd38b376b3eba8e5b63a1da38bf04fe08bdd3 6.92kB / 6.92kB  0.0s
 => => sha256:52d2b7f179e32b4cbd579ee3c4958027988f9a8274850ab0c7c24661e3adaa 29.12MB / 29.12MB  6.7s
 => => sha256:2b8a9a2240c1224b34f6aafbc3310f9a3fe65bd6893050906d02e89fc8326aa9 3.50MB / 3.50MB  4.1s
 => => sha256:051d6521462a7eb4ca0374e97701d6eec68eb51b118d3ef5d002798b498fb1 17.86MB / 17.86MB  5.9s
 => => sha256:fce84b1f897c621e9474bd4d5a49e2e22fa35e248e78e754010d34ec3d2d28cd 245B / 245B      5.3s
 => => sha256:46233543d8c2dc599bdb9d522180ca9e14cad4ac2017a5dc481660bfa4aa3ed9 3.38MB / 3.38MB  6.7s
 => => extracting sha256:52d2b7f179e32b4cbd579ee3c4958027988f9a8274850ab0c7c24661e3adaac5       4.6s
 => => extracting sha256:2b8a9a2240c1224b34f6aafbc3310f9a3fe65bd6893050906d02e89fc8326aa9       0.5s
 => => extracting sha256:051d6521462a7eb4ca0374e97701d6eec68eb51b118d3ef5d002798b498fb12e       3.5s
 => => extracting sha256:fce84b1f897c621e9474bd4d5a49e2e22fa35e248e78e754010d34ec3d2d28cd       0.0s
 => => extracting sha256:46233543d8c2dc599bdb9d522180ca9e14cad4ac2017a5dc481660bfa4aa3ed9       1.0s
 => [internal] load build context                                                               0.0s
 => => transferring context: 4.45kB                                                             0.0s
 => [base 2/5] WORKDIR /app                                                                    17.8s
 => [base 3/5] RUN adduser     --disabled-password     --gecos ""     --home "/nonexistent"     0.8s
 => [base 4/5] RUN --mount=type=cache,target=/root/.cache/pip     --mount=type=bind,source=r  229.4s 
 => [base 5/5] COPY . .                                                                         0.0s 
 => exporting to image                                                                          0.2s 
 => => exporting layers                                                                         0.2s 
 => => writing image sha256:c16de8f60dae3801bb185412c3242cf46e8c30413465600b81ef633b37b57d48    0.0s 
 => => naming to docker.io/library/flask-postgres-server                                        0.0s 
 => resolving provenance for metadata file                                                      0.0s
[+] Running 5/5
 ✔ flask-postgres-server              Built                                                     0.0s 
 ✔ Network flask-postgres_default     Created                                                   0.1s 
 ✔ Volume "flask-postgres_db-data"    Created                                                   0.0s 
 ✔ Container flask-postgres-db-1      Created                                                   0.1s 
 ✔ Container flask-postgres-server-1  Created                                                   0.0s 
Attaching to db-1, server-1
db-1  | The files belonging to this database system will be owned by user "postgres".
db-1  | This user must also own the server process.
db-1  | 
db-1  | The database cluster will be initialized with locale "en_US.utf8".
db-1  | The default database encoding has accordingly been set to "UTF8".
db-1  | The default text search configuration will be set to "english".
db-1  | 
db-1  | Data page checksums are disabled.
db-1  | 
db-1  | fixing permissions on existing directory /var/lib/postgresql/data ... ok
db-1  | creating subdirectories ... ok
db-1  | selecting dynamic shared memory implementation ... posix
db-1  | selecting default "max_connections" ... 100
db-1  | selecting default "shared_buffers" ... 128MB
db-1  | selecting default time zone ... Etc/UTC
db-1  | creating configuration files ... ok
db-1  | running bootstrap script ... ok
db-1  | performing post-bootstrap initialization ... ok
db-1  | syncing data to disk ... ok
db-1  | 
db-1  | 
db-1  | Success. You can now start the database server using:
db-1  | 
db-1  |     pg_ctl -D /var/lib/postgresql/data -l logfile start
db-1  | 
db-1  | initdb: warning: enabling "trust" authentication for local connections
db-1  | initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb.
db-1  | waiting for server to start....2025-09-24 03:53:30.862 UTC [35] LOG:  starting PostgreSQL 17.6 (Debian 17.6-1.pgdg13+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
db-1  | 2025-09-24 03:53:30.864 UTC [35] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db-1  | 2025-09-24 03:53:30.872 UTC [38] LOG:  database system was shut down at 2025-09-24 03:53:30 UTC
db-1  | 2025-09-24 03:53:30.882 UTC [35] LOG:  database system is ready to accept connections
db-1  |  done
db-1  | server started
db-1  | CREATE DATABASE
db-1  | 
db-1  | 
db-1  | /usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*
db-1  | 
db-1  | waiting for server to shut down....2025-09-24 03:53:31.533 UTC [35] LOG:  received fast shutdown request
db-1  | 2025-09-24 03:53:31.534 UTC [35] LOG:  aborting any active transactions
db-1  | 2025-09-24 03:53:31.540 UTC [35] LOG:  background worker "logical replication launcher" (PID 41) exited with exit code 1
db-1  | 2025-09-24 03:53:31.541 UTC [36] LOG:  shutting down
db-1  | 2025-09-24 03:53:31.543 UTC [36] LOG:  checkpoint starting: shutdown immediate
db-1  | 2025-09-24 03:53:31.598 UTC [36] LOG:  checkpoint complete: wrote 925 buffers (5.6%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.048 s, sync=0.004 s, total=0.057 s; sync files=301, longest=0.002 s, average=0.001 s; distance=4256 kB, estimate=4256 kB; lsn=0/1915960, redo lsn=0/1915960
db-1  | 2025-09-24 03:53:31.608 UTC [35] LOG:  database system is shut down
db-1  |  done
db-1  | server stopped
db-1  | 
db-1  | PostgreSQL init process complete; ready for start up.
db-1  | 
db-1  | 2025-09-24 03:53:31.695 UTC [1] LOG:  starting PostgreSQL 17.6 (Debian 17.6-1.pgdg13+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
db-1  | 2025-09-24 03:53:31.697 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
db-1  | 2025-09-24 03:53:31.697 UTC [1] LOG:  listening on IPv6 address "::", port 5432
db-1  | 2025-09-24 03:53:31.699 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db-1  | 2025-09-24 03:53:31.704 UTC [51] LOG:  database system was shut down at 2025-09-24 03:53:31 UTC
db-1  | 2025-09-24 03:53:31.712 UTC [1] LOG:  database system is ready to accept connections
server-1  |  * Debug mode: off
server-1  | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
server-1  |  * Running on all addresses (0.0.0.0)
server-1  |  * Running on http://127.0.0.1:5000
server-1  |  * Running on http://172.20.0.3:5000
server-1  | Press CTRL+C to quit

2.打开另一个终端窗口,执行以下命令初始化数据库

bash 复制代码
[root@host1 ~]# curl http://localhost:8000/initdb
init database

3.执行以下 curl 命令访问 Flask 应用程序测试其容器化效果,结果表明Flask 应用程序容器化成功

bash 复制代码
[root@host1 ~]# curl http://localhost:8000
Hello, Flask!

4.回原终端窗口,按Ctrl+C组合键停止应用程序

!!!部分修改过程:

bash 复制代码
[root@host1 ~]# cd flask-postgres
[root@host1 flask-postgres]# cat requirements.txt
blinker==1.6.2
click==8.1.6
colorama==0.4.6
Flask==2.3.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
psycopg2-binary==2.9.6
Werkzeug==2.3.6
[root@host1 flask-postgres]# vi requirements.txt
[root@host1 flask-postgres]# cat requirements.txt
blinker==1.6.2
click==8.1.6
colorama==0.4.6
Flask==2.3.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
psycopg2-binary==2.9.9
Werkzeug==2.3.6
bash 复制代码
[root@host1 ~]# mkdir flask-postgres && cd flask-postgres
mkdir: 无法创建目录 "flask-postgres": 文件已存在
[root@host1 ~]# cd flask-postgres
[root@host1 flask-postgres]# vi app.py
[root@host1 flask-postgres]# cat app.py
import json
from flask import Flask
import psycopg2
import os

app = Flask(__name__)

# 从环境变量或密钥文件中获取 PostgreSQL 密码
if 'POSTGRES_PASSWORD_FILE' in os.environ:
    with open(os.environ['POSTGRES_PASSWORD_FILE'], 'r') as f:
        password = f.read().strip()
else:
    password = os.environ['POSTGRES_PASSWORD']

# 根路由:测试服务可用性
@app.route('/')
def hello_world():
    return 'Hello, Flask!'

# /widgets 路由:查询数据库中 widgets 表的数据
@app.route('/widgets')
def get_widgets():
    with psycopg2.connect(host="db", user="postgres", password=password, database="example") as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT * FROM widgets")
            row_headers = [x[0] for x in cur.description]
            results = cur.fetchall()
        conn.close()
    json_data = [dict(zip(row_headers, result)) for result in results]
    return json.dumps(json_data)

# /initdb 路由:初始化数据库(创建库和表)
@app.route('/initdb')
def db_init():
    # 先连接默认库,创建 example 数据库
    conn = psycopg2.connect(host="db", user="postgres", password=password)
    conn.set_session(autocommit=True)
    with conn.cursor() as cur:
        cur.execute("DROP DATABASE IF EXISTS example")
        cur.execute("CREATE DATABASE example")
    conn.close()

    # 连接 example 数据库,创建 widgets 表
    with psycopg2.connect(host="db", user="postgres", password=password, database="example") as conn:
        with conn.cursor() as cur:
            cur.execute("DROP TABLE IF EXISTS widgets")
            cur.execute("CREATE TABLE widgets (name VARCHAR(255), description VARCHAR(255))")
        conn.close()
    return 'init database'

if __name__ == "__main__":
    app.run(host="0.0.0.0")
[root@host1 flask-postgres]# cat requirements.txt
blinker==1.6.2
click==8.1.6
colorama==0.4.6
Flask==2.3.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
psycopg2-binary==2.9.9
Werkzeug==2.3.6
[root@host1 flask-postgres]# vi requirements.txt
[root@host1 flask-postgres]# cat requirements.txt
blinker==1.6.2
click==8.1.6
colorama==0.4.6
Flask==2.3.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
psycopg2-binary==2.9.6
Werkzeug==2.3.6
[root@host1 flask-postgres]# cat Dockerfile
# syntax=docker/dockerfile:1
ARG PYTHON_VERSION=3.11.4
FROM python:${PYTHON_VERSION}-slim as base

# 禁用 .pyc 文件生成 + 实时输出日志
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

# 创建非特权用户
ARG UID=1001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/bin/false" \
    --no-create-home \
    --uid "${UID}" \
    appuser

# 缓存依赖下载(加速构建)
RUN --mount=type=cache,target=/root/.cache/pip \
    --mount=type=bind,source=requirements.txt,target=requirements.txt \
    python -m pip install -r requirements.txt

# 切换到非特权用户
USER appuser

# 复制源代码到容器
COPY . .

# 暴露 Flask 端口
EXPOSE 5000

# 启动 Flask 应用
CMD python -m flask run --host=0.0.0.0
[root@host1 flask-postgres]# vi Dockerfile
[root@host1 flask-postgres]# mkdir db
mkdir: 无法创建目录 "db": 文件已存在
[root@host1 flask-postgres]# echo "abc123" > db/password.txt
[root@host1 flask-postgres]# cat compose.yaml
services:
  # Flask 应用服务
  server:
    build: .  # 从当前目录构建镜像
    ports:
      - "8000:5000"  # 主机8000端口 → 容器5000端口
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db-password  # 从secret读密码
    depends_on:
      db:
        condition: service_healthy  # 等数据库"健康"后再启动
    secrets:
      - db-password  # 引用密码secret

  # PostgreSQL 数据库服务
  db:
    image: postgres  # 官方PostgreSQL镜像
    restart: always  # 异常时自动重启
    user: postgres
    secrets:
      - db-password  # 给数据库提供密码secret
    volumes:
      - db-data:/var/lib/postgresql/data  # 持久化数据
    environment:
      POSTGRES_DB: example  # 初始化的数据库名
      POSTGRES_PASSWORD_FILE: /run/secrets/db-password  # 从secret读密码
    expose:
      - "5432"  # 暴露数据库内部端口
    healthcheck:  # 健康检查(确保数据库就绪)
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5

secrets:
  db-password:
    file: db/password.txt  # 关联本地密码文件

volumes:
  db-data:  # 持久化数据库数据的卷
[root@host1 flask-postgres]# vi compose.yaml
[root@host1 flask-postgres]# cat compose.yaml
services:
  # Flask 服务
  server:
    build:
      context: .  # 从当前目录构建镜像
    ports:
      - "8000:5000"  # 宿主机 8000 端口映射到容器 5000 端口
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password  # 从密钥文件读密码
    depends_on:
      db:
        condition: service_healthy  # 等待 PostgreSQL 健康检查通过后再启动
    secrets:
      - db-password  # 挂载数据库密码密钥

  # PostgreSQL 服务
  db:
    image: postgres  # 使用官方 PostgreSQL 镜像
    restart: always  # 容器退出时自动重启
    user: postgres   # 使用 postgres 用户运行
    secrets:
      - db-password  # 挂载数据库密码密钥
    volumes:
      - db-data:/var/lib/postgresql/data  # 持久化数据库数据
    environment:
      - POSTGRES_DB=example        # 自动创建 example 数据库
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password  # 从密钥文件读密码
    expose:
      - 5432  # 暴露数据库端口(仅内部网络可见)
    healthcheck:  # 健康检查(确保数据库就绪)
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5

# 定义密钥(从本地文件加载)
secrets:
  db-password:
    file: db/password.txt

# 定义数据卷(持久化数据库)
volumes:
  db-data:
[root@host1 flask-postgres]# tree .
.
├── app.py
├── compose.yaml
├── db
│   └── password.txt
├── Dockerfile
├── requirements.txt
└── test_db.py

1 directory, 6 files
[root@host1 flask-postgres]# docker compose up --build
[+] Building 3.7s (12/12) FINISHED                                                                   
 => [internal] load local bake definitions                                                      0.0s
 => => reading from stdin 507B                                                                  0.0s
 => [internal] load build definition from Dockerfile                                            0.0s
 => => transferring dockerfile: 1.10kB                                                          0.0s
 => WARN: FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 3)                  0.0s
 => [internal] load metadata for docker.io/library/python:3.11.4-slim                           3.3s
 => [internal] load .dockerignore                                                               0.0s
 => => transferring context: 2B                                                                 0.0s
 => [base 1/5] FROM docker.io/library/python:3.11.4-slim@sha256:17d62d681d9ecef20aae6c6605e9cf  0.0s
 => [internal] load build context                                                               0.0s
 => => transferring context: 5.34kB                                                             0.0s
 => CACHED [base 2/5] WORKDIR /app                                                              0.0s
 => CACHED [base 3/5] RUN adduser     --disabled-password     --gecos ""     --home "/nonexist  0.0s
 => CACHED [base 4/5] RUN --mount=type=cache,target=/root/.cache/pip     --mount=type=bind,sou  0.0s
 => [base 5/5] COPY . .                                                                         0.0s
 => exporting to image                                                                          0.0s
 => => exporting layers                                                                         0.0s
 => => writing image sha256:14a87d62855044676976c1710eb5333dcf6365e6cba287e71ee0feb45534a3f5    0.0s
 => => naming to docker.io/library/flask-postgres-server                                        0.0s
 => resolving provenance for metadata file                                                      0.0s
[+] Running 3/3
 ✔ flask-postgres-server              Built                                                     0.0s 
 ✔ Container flask-postgres-db-1      Running                                                   0.0s 
 ✔ Container flask-postgres-server-1  Recreated                                                10.4s 
Attaching to db-1, server-1
server-1  |  * Debug mode: off
server-1  | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
server-1  |  * Running on all addresses (0.0.0.0)
server-1  |  * Running on http://127.0.0.1:5000
server-1  |  * Running on http://172.20.0.3:5000
server-1  | Press CTRL+C to quit
bash 复制代码
[root@host1 flask-postgres]# tree .
.
├── app.py
├── compose.yaml
├── db
│   └── password.txt
├── Dockerfile
├── requirements.txt
└── test_db.py

1 directory, 6 files
[root@host1 flask-postgres]# docker compose up --build
[+] Building 3.7s (12/12) FINISHED                                                                   
 => [internal] load local bake definitions                                                      0.0s
 => => reading from stdin 507B                                                                  0.0s
 => [internal] load build definition from Dockerfile                                            0.0s
 => => transferring dockerfile: 1.10kB                                                          0.0s
 => WARN: FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 3)                  0.0s
 => [internal] load metadata for docker.io/library/python:3.11.4-slim                           3.3s
 => [internal] load .dockerignore                                                               0.0s
 => => transferring context: 2B                                                                 0.0s
 => [base 1/5] FROM docker.io/library/python:3.11.4-slim@sha256:17d62d681d9ecef20aae6c6605e9cf  0.0s
 => [internal] load build context                                                               0.0s
 => => transferring context: 5.34kB                                                             0.0s
 => CACHED [base 2/5] WORKDIR /app                                                              0.0s
 => CACHED [base 3/5] RUN adduser     --disabled-password     --gecos ""     --home "/nonexist  0.0s
 => CACHED [base 4/5] RUN --mount=type=cache,target=/root/.cache/pip     --mount=type=bind,sou  0.0s
 => [base 5/5] COPY . .                                                                         0.0s
 => exporting to image                                                                          0.0s
 => => exporting layers                                                                         0.0s
 => => writing image sha256:14a87d62855044676976c1710eb5333dcf6365e6cba287e71ee0feb45534a3f5    0.0s
 => => naming to docker.io/library/flask-postgres-server                                        0.0s
 => resolving provenance for metadata file                                                      0.0s
[+] Running 3/3
 ✔ flask-postgres-server              Built                                                     0.0s 
 ✔ Container flask-postgres-db-1      Running                                                   0.0s 
 ✔ Container flask-postgres-server-1  Recreated                                                10.4s 
Attaching to db-1, server-1
server-1  |  * Debug mode: off
server-1  | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
server-1  |  * Running on all addresses (0.0.0.0)
server-1  |  * Running on http://127.0.0.1:5000
server-1  |  * Running on http://172.20.0.3:5000
server-1  | Press CTRL+C to quit
db-1      | 2025-09-24 05:02:37.739 UTC [14] LOG:  checkpoint starting: immediate force wait
db-1      | 2025-09-24 05:02:37.746 UTC [14] LOG:  checkpoint complete: wrote 1 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.002 s, sync=0.002 s, total=0.008 s; sync files=1, longest=0.002 s, average=0.002 s; distance=1 kB, estimate=3850 kB; lsn=0/2E66F18, redo lsn=0/2E66EC0
server-1  | [2025-09-24 05:02:37,881] ERROR in app: Exception on /initdb [GET]
server-1  | Traceback (most recent call last):
server-1  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 2190, in wsgi_app
server-1  |     response = self.full_dispatch_request()
server-1  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
server-1  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1486, in full_dispatch_request
server-1  |     rv = self.handle_user_exception(e)
server-1  |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
server-1  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1484, in full_dispatch_request
server-1  |     rv = self.dispatch_request()
server-1  |          ^^^^^^^^^^^^^^^^^^^^^^^
server-1  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1469, in dispatch_request
server-1  |     return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
server-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
server-1  |   File "/app/app.py", line 44, in db_init
server-1  |     with psycopg2.connect(host="db", user="postgres", password=password, database="example") as conn:
server-1  | psycopg2.InterfaceError: connection already closed
server-1  | 172.20.0.1 - - [24/Sep/2025 05:02:37] "GET /initdb HTTP/1.1" 500 -
Gracefully Stopping... press Ctrl+C again to force
 Container flask-postgres-server-1  Stopping
 Container flask-postgres-server-1  Stopped
 Container flask-postgres-db-1  Stopping
server-1 exited with code 137
 Container flask-postgres-db-1  Stopped
db-1 exited with code 137

[root@host1 flask-postgres]# curl http://localhost:8000/initdb
curl: (7) Failed to connect to localhost port 8000: 拒绝连接
[root@host1 flask-postgres]# ^C
[root@host1 flask-postgres]# docker logs flask-postgres-server-1
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://172.20.0.3:5000
Press CTRL+C to quit
[2025-09-24 05:02:37,881] ERROR in app: Exception on /initdb [GET]
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 2190, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1486, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1484, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1469, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/app.py", line 44, in db_init
    with psycopg2.connect(host="db", user="postgres", password=password, database="example") as conn:
psycopg2.InterfaceError: connection already closed
172.20.0.1 - - [24/Sep/2025 05:02:37] "GET /initdb HTTP/1.1" 500 -
[root@host1 flask-postgres]# cat app.py
import json
from flask import Flask
import psycopg2
import os

app = Flask(__name__)

# 从环境变量或密钥文件中获取 PostgreSQL 密码
if 'POSTGRES_PASSWORD_FILE' in os.environ:
    with open(os.environ['POSTGRES_PASSWORD_FILE'], 'r') as f:
        password = f.read().strip()
else:
    password = os.environ['POSTGRES_PASSWORD']

# 根路由:测试服务可用性
@app.route('/')
def hello_world():
    return 'Hello, Flask!'

# /widgets 路由:查询数据库中 widgets 表的数据
@app.route('/widgets')
def get_widgets():
    with psycopg2.connect(host="db", user="postgres", password=password, database="example") as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT * FROM widgets")
            row_headers = [x[0] for x in cur.description]
            results = cur.fetchall()
        conn.close()
    json_data = [dict(zip(row_headers, result)) for result in results]
    return json.dumps(json_data)

# /initdb 路由:初始化数据库(创建库和表)
@app.route('/initdb')
def db_init():
    # 先连接默认库,创建 example 数据库
    conn = psycopg2.connect(host="db", user="postgres", password=password)
    conn.set_session(autocommit=True)
    with conn.cursor() as cur:
        cur.execute("DROP DATABASE IF EXISTS example")
        cur.execute("CREATE DATABASE example")
    conn.close()

    # 连接 example 数据库,创建 widgets 表
    with psycopg2.connect(host="db", user="postgres", password=password, database="example") as conn:
        with conn.cursor() as cur:
            cur.execute("DROP TABLE IF EXISTS widgets")
            cur.execute("CREATE TABLE widgets (name VARCHAR(255), description VARCHAR(255))")
        conn.close()
    return 'init database'

if __name__ == "__main__":
    app.run(host="0.0.0.0")
[root@host1 flask-postgres]# vi app.py
[root@host1 flask-postgres]# cat app.py
import json
from flask import Flask
import psycopg2
import os
import time  

app = Flask(__name__)

if 'POSTGRES_PASSWORD_FILE' in os.environ:
    with open(os.environ['POSTGRES_PASSWORD_FILE'], 'r') as f:
        password = f.read().strip()
else:
    password = os.environ['POSTGRES_PASSWORD']

@app.route('/')
def hello_world():
    return 'Hello, Flask!'

@app.route('/widgets')
def get_widgets():
    with psycopg2.connect(
        host="db",
        user="postgres",
        password=password,
        database="example",
        connect_timeout=10,
        sslmode='disable'
    ) as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT * FROM widgets")
            row_headers = [x[0] for x in cur.description]
            results = cur.fetchall()
    json_data = [dict(zip(row_headers, result)) for result in results]
    return json.dumps(json_data)

@app.route('/initdb')
def db_init():
    with psycopg2.connect(
        host="db",
        user="postgres",
        password=password,
        database="postgres",  
        connect_timeout=10,
        sslmode='disable'
    ) as conn:
        conn.set_session(autocommit=True)  
        with conn.cursor() as cur:
            cur.execute("DROP DATABASE IF EXISTS example")
            cur.execute("CREATE DATABASE example")
    time.sleep(2)

    with psycopg2.connect(
        host="db",
        user="postgres",
        password=password,
        database="example",
        connect_timeout=10,
        sslmode='disable'
    ) as conn:
        with conn.cursor() as cur:
            cur.execute("DROP TABLE IF EXISTS widgets")
            cur.execute("CREATE TABLE widgets (name VARCHAR(255), description VARCHAR(255))")

    return 'init database'  

if __name__ == "__main__":
    app.run(host="0.0.0.0")
[root@host1 flask-postgres]# cd flask-postgres
-bash: cd: flask-postgres: 没有那个文件或目录
[root@host1 flask-postgres]# docker compose down
[+] Running 3/3
 ✔ Container flask-postgres-server-1  Removed                                                   0.0s 
 ✔ Container flask-postgres-db-1      Removed                                                   0.0s 
 ✔ Network flask-postgres_default     Removed                                                   0.2s 
[root@host1 flask-postgres]# docker compose up --build -d
[+] Building 1.1s (12/12) FINISHED                                                                   
 => [internal] load local bake definitions                                                      0.0s
 => => reading from stdin 507B                                                                  0.0s
 => [internal] load build definition from Dockerfile                                            0.0s
 => => transferring dockerfile: 1.10kB                                                          0.0s
 => WARN: FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 3)                  0.0s
 => [internal] load metadata for docker.io/library/python:3.11.4-slim                           0.8s
 => [internal] load .dockerignore                                                               0.0s
 => => transferring context: 2B                                                                 0.0s
 => [base 1/5] FROM docker.io/library/python:3.11.4-slim@sha256:17d62d681d9ecef20aae6c6605e9cf  0.0s
 => [internal] load build context                                                               0.0s
 => => transferring context: 2.43kB                                                             0.0s
 => CACHED [base 2/5] WORKDIR /app                                                              0.0s
 => CACHED [base 3/5] RUN adduser     --disabled-password     --gecos ""     --home "/nonexist  0.0s
 => CACHED [base 4/5] RUN --mount=type=cache,target=/root/.cache/pip     --mount=type=bind,sou  0.0s
 => [base 5/5] COPY . .                                                                         0.0s
 => exporting to image                                                                          0.0s
 => => exporting layers                                                                         0.0s
 => => writing image sha256:c3772d63f2533e31ce60970b53616a26d57009b6ac939de0a21b90d7b7861c2d    0.0s
 => => naming to docker.io/library/flask-postgres-server                                        0.0s
 => resolving provenance for metadata file                                                      0.0s
[+] Running 4/4
 ✔ flask-postgres-server              Built                                                     0.0s 
 ✔ Network flask-postgres_default     Created                                                   0.1s 
 ✔ Container flask-postgres-db-1      Healthy                                                  11.0s 
 ✔ Container flask-postgres-server-1  Started                                                  11.4s 
[root@host1 flask-postgres]# docker compose ps
NAME                      IMAGE                   COMMAND                   SERVICE   CREATED          STATUS                    PORTS
flask-postgres-db-1       postgres                "docker-entrypoint.s..."   db        14 seconds ago   Up 13 seconds (healthy)   5432/tcp
flask-postgres-server-1   flask-postgres-server   "python -m flask run..."   server    14 seconds ago   Up 2 seconds              0.0.0.0:8000->5000/tcp, [::]:8000->5000/tcp
[root@host1 flask-postgres]# docker exec -u root -it flask-postgres-db-1 bash
root@7040b811a7e3:/# grep -E "host|local" /var/lib/postgresql/data/pg_hba.conf | grep -v "#"
local   all             all                                     trust
host    all             all             127.0.0.1/32            trust
host    all             all             ::1/128                 trust
local   replication     all                                     trust
host    replication     all             127.0.0.1/32            trust
host    replication     all             ::1/128                 trust
host all all all md5
root@7040b811a7e3:/# sed -i 's/trust/md5/g' /var/lib/postgresql/data/pg_hba.conf
root@7040b811a7e3:/# grep -E "host|local" /var/lib/postgresql/data/pg_hba.conf | grep -v "#"
local   all             all                                     md5
host    all             all             127.0.0.1/32            md5
host    all             all             ::1/128                 md5
local   replication     all                                     md5
host    replication     all             127.0.0.1/32            md5
host    replication     all             ::1/128                 md5
host all all all md5
root@7040b811a7e3:/# su - postgres
postgres@7040b811a7e3:~$ /usr/lib/postgresql/17/bin/pg_ctl -D /var/lib/postgresql/data restart
waiting for server to shut down....[root@host1 flask-postgres]# 
[root@host1 flask-postgres]# docker compose restart server
[+] Restarting 1/1
 ✔ Container flask-postgres-server-1  Started                                                  10.8s 
[root@host1 flask-postgres]# docker compose ps
NAME                      IMAGE                   COMMAND                   SERVICE   CREATED          STATUS                        PORTS
flask-postgres-db-1       postgres                "docker-entrypoint.s..."   db        35 minutes ago   Up About a minute (healthy)   5432/tcp
flask-postgres-server-1   flask-postgres-server   "python -m flask run..."   server    35 minutes ago   Up About a minute             0.0.0.0:8000->5000/tcp, [::]:8000->5000/tcp
[root@host1 flask-postgres]# cd
[root@host1 ~]# vi flask-postgres/app.py
[root@host1 ~]# vi flask-postgres/app.py
[root@host1 ~]# cat flask-postgres/app.py
import json
from flask import Flask
import psycopg2
import os
import time  

app = Flask(__name__)

if 'POSTGRES_PASSWORD_FILE' in os.environ:
    with open(os.environ['POSTGRES_PASSWORD_FILE'], 'r') as f:
        password = f.read().strip()
else:
    password = os.environ['POSTGRES_PASSWORD']

@app.route('/')
def hello_world():
    return 'Hello, Flask!'

@app.route('/widgets')
def get_widgets():
    with psycopg2.connect(
        host="db",
        user="postgres",
        password=password,
        database="example",
        connect_timeout=10,
        sslmode='disable'
    ) as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT * FROM widgets")
            row_headers = [x[0] for x in cur.description]
            results = cur.fetchall()
    json_data = [dict(zip(row_headers, result)) for result in results]
    return json.dumps(json_data)

@app.route('/initdb')
def db_init():
    # 阶段1:创建 example 数据库(关键:手动管理连接,强制 autocommit)
    conn = None  # 初始化连接变量
    try:
        # 连接默认库(postgres),直接设置 autocommit=True,不开启事务
        conn = psycopg2.connect(
            host="db",
            user="postgres",
            password=password,
            database="postgres",
            connect_timeout=10,
            sslmode='disable'
        )
        conn.autocommit = True  # 强制开启自动提交,彻底关闭事务
        cur = conn.cursor()
        cur.execute("DROP DATABASE IF EXISTS example")
        cur.execute("CREATE DATABASE example")
        cur.close()  # 关闭游标
    finally:
        if conn:
            conn.close()  # 确保连接关闭

    # 阶段2:等待新库元数据同步(不变)
    time.sleep(2)

    # 阶段3:创建 widgets 表(表操作可在事务内,用 with 没问题)
    with psycopg2.connect(
        host="db",
        user="postgres",
        password=password,
        database="example",
        connect_timeout=10,
        sslmode='disable'
    ) as conn:
        with conn.cursor() as cur:
            cur.execute("DROP TABLE IF EXISTS widgets")
            cur.execute("CREATE TABLE widgets (name VARCHAR(255), description VARCHAR(255))")

    return 'init database' 

if __name__ == "__main__":
    app.run(host="0.0.0.0")
[root@host1 ~]# cd flask-postgres
[root@host1 flask-postgres]# docker compose down
[+] Running 3/3
 ✔ Container flask-postgres-server-1  Removed                                                  10.3s 
 ✔ Container flask-postgres-db-1      Removed                                                   0.2s 
 ✔ Network flask-postgres_default     Removed                                                   0.2s 
[root@host1 flask-postgres]# docker compose up --build -d
[+] Building 2.9s (12/12) FINISHED                                                                   
 => [internal] load local bake definitions                                                      0.0s
 => => reading from stdin 507B                                                                  0.0s
 => [internal] load build definition from Dockerfile                                            0.0s
 => => transferring dockerfile: 1.10kB                                                          0.0s
 => WARN: FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 3)                  0.0s
 => [internal] load metadata for docker.io/library/python:3.11.4-slim                           2.6s
 => [internal] load .dockerignore                                                               0.0s
 => => transferring context: 2B                                                                 0.0s
 => [base 1/5] FROM docker.io/library/python:3.11.4-slim@sha256:17d62d681d9ecef20aae6c6605e9cf  0.0s
 => [internal] load build context                                                               0.0s
 => => transferring context: 2.96kB                                                             0.0s
 => CACHED [base 2/5] WORKDIR /app                                                              0.0s
 => CACHED [base 3/5] RUN adduser     --disabled-password     --gecos ""     --home "/nonexist  0.0s
 => CACHED [base 4/5] RUN --mount=type=cache,target=/root/.cache/pip     --mount=type=bind,sou  0.0s
 => [base 5/5] COPY . .                                                                         0.0s
 => exporting to image                                                                          0.0s
 => => exporting layers                                                                         0.0s
 => => writing image sha256:f4f5195583f1bed7475ff9da158ba9018d378dd9378d06c0ba0c63f0bb286097    0.0s
 => => naming to docker.io/library/flask-postgres-server                                        0.0s
 => resolving provenance for metadata file                                                      0.0s
[+] Running 4/4
 ✔ flask-postgres-server              Built                                                     0.0s 
 ✔ Network flask-postgres_default     Created                                                   0.1s 
 ✔ Container flask-postgres-db-1      Healthy                                                  11.0s 
 ✔ Container flask-postgres-server-1  Started                                                  11.4s 
[root@host1 flask-postgres]# docker compose ps
NAME                      IMAGE                   COMMAND                   SERVICE   CREATED          STATUS                    PORTS
flask-postgres-db-1       postgres                "docker-entrypoint.s..."   db        34 seconds ago   Up 33 seconds (healthy)   5432/tcp
flask-postgres-server-1   flask-postgres-server   "python -m flask run..."   server    33 seconds ago   Up 22 seconds             0.0.0.0:8000->5000/tcp, [::]:8000->5000/tcp
[root@host1 flask-postgres]# 
bash 复制代码
[root@host1 ~]# curl http://localhost:8000/initdb
init database
相关推荐
MediaTea2 小时前
Jupyter Notebook:基于 Web 的交互式编程环境
前端·ide·人工智能·python·jupyter
阿_旭2 小时前
基于深度学习的CT扫描图像肝脏肿瘤智能检测与分析系统【python源码+Pyqt5界面+数据集+训练代码】
人工智能·python·深度学习·肝脏肿瘤分割
鹏大师运维3 小时前
麒麟系统中修改 WPS 默认新建文件格式的方法
linux·操作系统·wps·docx·麒麟·word文档·excel文档
belldeep3 小时前
python:Django 和 Vue.js 技术栈解析
vue.js·python·django
yuriy.wang3 小时前
Spring IOC源码篇六 核心方法obtainFreshBeanFactory.parseCustomElement
java·后端·spring
蓝桉~MLGT4 小时前
Python学习历程——基础语法(print打印、变量、运算)
开发语言·python·学习
Lin_Aries_04214 小时前
通过配置 GitLab 自动触发项目自动化构建与部署
运维·docker·容器·自动化·云计算·gitlab
尘埃不入你眼眸4 小时前
Docker操作命令
运维·docker·容器
GottdesKrieges4 小时前
OceanBase主备库日志传输服务
linux·oceanbase