使用容器進行 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.11.4
    ? 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://docker-docs.dev.org.tw/go/dockerfile-reference/
    
    # Want to help us make this template better? Share your feedback here: https://   forms.gle/ybq9Krt8jtBL3iCk7
    
    ARG PYTHON_VERSION=3.11.4
    FROM python:${PYTHON_VERSION}-slim as base
    
    # 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://docker-docs.dev.org.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://docker-docs.dev.org.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://docker-docs.dev.org.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 檔案中,您需要取消註釋所有資料庫指令。 此外,您需要將資料庫密碼檔案作為環境變數新增到伺服器服務,並指定要使用的密鑰檔案。

以下是更新後的 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 在更新程式碼時自動重新建置和執行您的容器。

相關資訊

後續步驟

在下一節中,您將瞭解如何使用 GitHub Actions 設定 CI/CD 管線。