使用容器進行 Python 開發

先決條件

完成容器化 Python 應用程式

概述

在本節中,您將學習如何為容器化應用程式設定開發環境。這包括

  • 新增本地資料庫並持久化資料
  • 配置 Compose 在您編輯和儲存程式碼時自動更新正在執行的 Compose 服務

獲取示例應用程式

你需要克隆一個新的倉庫來獲取一個包含連線資料庫邏輯的示例應用程式。

  1. 切換到你想要克隆倉庫的目錄,然後執行以下命令。

    $ git clone https://github.com/estebanx64/python-docker-dev-example
    
  2. 在克隆的倉庫目錄中,手動建立 Docker 資產或執行 `docker init` 來建立必要的 Docker 資產。

    在克隆的倉庫目錄中,執行 `docker init`。請參考以下示例,回答 `docker init` 的提示。

    $ docker init
    Welcome to the Docker Init CLI!
    
    This utility will walk you through creating the following files with sensible defaults for your project:
      - .dockerignore
      - Dockerfile
      - compose.yaml
      - README.Docker.md
    
    Let's get started!
    
    ? What application platform does your project use? Python
    ? What version of Python do you want to use? 3.12
    ? What port do you want your app to listen on? 8001
    ? What is the command to run your app? python3 -m uvicorn app:app --host=0.0.0.0 --port=8001
    

    建立一個名為 `.gitignore` 的檔案,其內容如下。

    .gitignore
    # Byte-compiled / optimized / DLL files
    __pycache__/
    *.py[cod]
    *$py.class
    
    # C extensions
    *.so
    
    # Distribution / packaging
    .Python
    build/
    develop-eggs/
    dist/
    downloads/
    eggs/
    .eggs/
    lib/
    lib64/
    parts/
    sdist/
    var/
    wheels/
    share/python-wheels/
    *.egg-info/
    .installed.cfg
    *.egg
    MANIFEST
    
    # Unit test / coverage reports
    htmlcov/
    .tox/
    .nox/
    .coverage
    .coverage.*
    .cache
    nosetests.xml
    coverage.xml
    *.cover
    *.py,cover
    .hypothesis/
    .pytest_cache/
    cover/
    
    # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
    __pypackages__/
    
    # Environments
    .env
    .venv
    env/
    venv/
    ENV/
    env.bak/
    venv.bak/

    如果您沒有安裝 Docker Desktop 或更喜歡手動建立資產,您可以在專案目錄中建立以下檔案。

    建立一個名為 Dockerfile 的檔案,內容如下。

    Dockerfile
    # syntax=docker/dockerfile:1
    
    # Comments are provided throughout this file to help you get started.
    # If you need more help, visit the Dockerfile reference guide at
    # https://docs.docker.net.tw/go/dockerfile-reference/
    
    # Want to help us make this template better? Share your feedback here: https://   forms.gle/ybq9Krt8jtBL3iCk7
    
    ARG PYTHON_VERSION=3.12
    FROM python:${PYTHON_VERSION}-slim
    
    # Prevents Python from writing pyc files.
    ENV PYTHONDONTWRITEBYTECODE=1
    
    # Keeps Python from buffering stdout and stderr to avoid situations where
    # the application crashes without emitting any logs due to buffering.
    ENV PYTHONUNBUFFERED=1
    
    WORKDIR /app
    
    # Create a non-privileged user that the app will run under.
    # See https://docs.docker.net.tw/go/dockerfile-user-best-practices/
    ARG UID=10001
    RUN adduser \
        --disabled-password \
        --gecos "" \
        --home "/nonexistent" \
        --shell "/sbin/nologin" \
        --no-create-home \
        --uid "${UID}" \
        appuser
    
    # Download dependencies as a separate step to take advantage of Docker's    caching.
    # Leverage a cache mount to /root/.cache/pip to speed up subsequent builds.
    # Leverage a bind mount to requirements.txt to avoid having to copy them into
    # into this layer.
    RUN --mount=type=cache,target=/root/.cache/pip \
        --mount=type=bind,source=requirements.txt,target=requirements.txt \
        python -m pip install -r requirements.txt
    
    # Switch to the non-privileged user to run the application.
    USER appuser
    
    # Copy the source code into the container.
    COPY . .
    
    # Expose the port that the application listens on.
    EXPOSE 8001
    
    # Run the application.
    CMD ["python3", "-m", "uvicorn", "app:app", "--host=0.0.0.0", "--port=8001"]

    建立一個名為 compose.yaml 的檔案,內容如下。

    compose.yaml
    # Comments are provided throughout this file to help you get started.
    # If you need more help, visit the Docker Compose reference guide at
    # https://docs.docker.net.tw/go/compose-spec-reference/
    
    # Here the instructions define your application as a service called "server".
    # This service is built from the Dockerfile in the current directory.
    # You can add other services your application may depend on here, such as a
    # database or a cache. For examples, see the Awesome Compose repository:
    # https://github.com/docker/awesome-compose
    services:
      server:
        build:
          context: .
        ports:
          - 8001:8001
    # The commented out section below is an example of how to define a PostgreSQL
    # database that your application can use. `depends_on` tells Docker Compose to
    # start the database before your application. The `db-data` volume persists the
    # database data between container restarts. The `db-password` secret is used
    # to set the database password. You must create `db/password.txt` and add
    # a password of your choosing to it before running `docker compose up`.
    #     depends_on:
    #       db:
    #         condition: service_healthy
    #   db:
    #     image: postgres
    #     restart: always
    #     user: postgres
    #     secrets:
    #       - db-password
    #     volumes:
    #       - db-data:/var/lib/postgresql/data
    #     environment:
    #       - POSTGRES_DB=example
    #       - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    #     expose:
    #       - 5432
    #     healthcheck:
    #       test: [ "CMD", "pg_isready" ]
    #       interval: 10s
    #       timeout: 5s
    #       retries: 5
    # volumes:
    #   db-data:
    # secrets:
    #   db-password:
    #     file: db/password.txt

    建立一個名為 .dockerignore 的檔案,內容如下。

    .dockerignore
    # Include any files or directories that you don't want to be copied to your
    # container here (e.g., local build artifacts, temporary files, etc.).
    #
    # For more help, visit the .dockerignore file reference guide at
    # https://docs.docker.net.tw/go/build-context-dockerignore/
    
    **/.DS_Store
    **/__pycache__
    **/.venv
    **/.classpath
    **/.dockerignore
    **/.env
    **/.git
    **/.gitignore
    **/.project
    **/.settings
    **/.toolstarget
    **/.vs
    **/.vscode
    **/*.*proj.user
    **/*.dbmdl
    **/*.jfm
    **/bin
    **/charts
    **/docker-compose*
    **/compose.y*ml
    **/Dockerfile*
    **/node_modules
    **/npm-debug.log
    **/obj
    **/secrets.dev.yaml
    **/values.dev.yaml
    LICENSE
    README.md

    建立一個名為 `.gitignore` 的檔案,其內容如下。

    .gitignore
    # Byte-compiled / optimized / DLL files
    __pycache__/
    *.py[cod]
    *$py.class
    
    # C extensions
    *.so
    
    # Distribution / packaging
    .Python
    build/
    develop-eggs/
    dist/
    downloads/
    eggs/
    .eggs/
    lib/
    lib64/
    parts/
    sdist/
    var/
    wheels/
    share/python-wheels/
    *.egg-info/
    .installed.cfg
    *.egg
    MANIFEST
    
    # Unit test / coverage reports
    htmlcov/
    .tox/
    .nox/
    .coverage
    .coverage.*
    .cache
    nosetests.xml
    coverage.xml
    *.cover
    *.py,cover
    .hypothesis/
    .pytest_cache/
    cover/
    
    # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
    __pypackages__/
    
    # Environments
    .env
    .venv
    env/
    venv/
    ENV/
    env.bak/
    venv.bak/

新增本地資料庫並持久化資料

您可以使用容器設定本地服務,例如資料庫。在本節中,您將更新 compose.yaml 檔案以定義資料庫服務和用於持久化資料的卷。

在克隆的倉庫目錄中,使用 IDE 或文字編輯器開啟 `compose.yaml` 檔案。 `docker init` 處理了大部分指令的建立,但你需要根據你的獨特應用程式進行更新。

在 `compose.yaml` 檔案中,你需要取消註釋所有資料庫指令。此外,你需要將資料庫密碼檔案作為環境變數新增到伺服器服務,並指定要使用的 secret 檔案。

以下是更新後的 `compose.yaml` 檔案。

services:
  server:
    build:
      context: .
    ports:
      - 8001:8001
    environment:
      - POSTGRES_SERVER=db
      - POSTGRES_USER=postgres
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
  db:
    image: postgres
    restart: always
    user: postgres
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 5432
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt
注意

要了解有關 Compose 檔案中指令的更多資訊,請參閱Compose 檔案參考

在使用 Compose 執行應用程式之前,請注意此 Compose 檔案指定了一個 `password.txt` 檔案來儲存資料庫密碼。你必須建立此檔案,因為它未包含在原始碼倉庫中。

在克隆的倉庫目錄中,建立一個名為 `db` 的新目錄,並在該目錄中建立一個名為 `password.txt` 的檔案,其中包含資料庫密碼。使用你喜歡的 IDE 或文字編輯器,將以下內容新增到 `password.txt` 檔案中。

mysecretpassword

儲存並關閉 `password.txt` 檔案。

你的 `python-docker-dev-example` 目錄現在應該包含以下內容。

├── python-docker-dev-example/
│ ├── db/
│ │ └── password.txt
│ ├── app.py
│ ├── config.py
│ ├── requirements.txt
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── Dockerfile
│ ├── README.Docker.md
│ └── README.md

現在,執行以下 `docker compose up` 命令來啟動你的應用程式。

$ docker compose up --build

現在測試你的 API 端點。開啟一個新終端,然後使用 curl 命令向伺服器發出請求。

讓我們用 POST 方法建立一個物件。

$ curl -X 'POST' \
  'https://:8001/heroes/' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "id": 1,
  "name": "my hero",
  "secret_name": "austing",
  "age": 12
}'

您應該收到以下響應

{
  "age": 12,
  "id": 1,
  "name": "my hero",
  "secret_name": "austing"
}

讓我們用下一個 curl 命令發起 GET 請求。

curl -X 'GET' \
  'https://:8001/heroes/' \
  -H 'accept: application/json'

你應該收到與上面相同的響應,因為資料庫中只有這一個物件。

{
  "age": 12,
  "id": 1,
  "name": "my hero",
  "secret_name": "austing"
}

在終端中按 ctrl+c 停止您的應用程式。

自動更新服務

使用 Compose Watch 在您編輯和儲存程式碼時自動更新正在執行的 Compose 服務。有關 Compose Watch 的更多詳細資訊,請參閱使用 Compose Watch

在 IDE 或文字編輯器中開啟 `compose.yaml` 檔案,然後新增 Compose Watch 指令。以下是更新後的 `compose.yaml` 檔案。

services:
  server:
    build:
      context: .
    ports:
      - 8001:8001
    environment:
      - POSTGRES_SERVER=db
      - POSTGRES_USER=postgres
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
    develop:
      watch:
        - action: rebuild
          path: .
  db:
    image: postgres
    restart: always
    user: postgres
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 5432
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

執行以下命令,使用 Compose Watch 執行您的應用程式。

$ docker compose watch

在終端中,透過 curl 請求應用程式以獲取響應。

$ curl https://:8001
Hello, Docker!

您對本地機器上應用程式原始檔的任何更改現在將立即反映在執行中的容器中。

在 IDE 或文字編輯器中開啟 `python-docker-dev-example/app.py`,並透過新增幾個感嘆號來更新 `Hello, Docker!` 字串。

-    return 'Hello, Docker!'
+    return 'Hello, Docker!!!'

儲存對 `app.py` 的更改,然後等待幾秒鐘,直到應用程式重新構建。再次透過 curl 請求應用程式,並驗證更新後的文字是否出現。

$ curl https://:8001
Hello, Docker!!!

在終端中按 ctrl+c 停止您的應用程式。

摘要

在本節中,你瞭解瞭如何設定 Compose 檔案以新增本地資料庫並持久化資料。你還學習瞭如何使用 Compose Watch 在更新程式碼時自動重建並執行容器。

相關資訊

後續步驟

在下一節中,你將學習如何設定 linting、格式化和型別檢查,以遵循 Python 應用程式的最佳實踐。