跳到主要內容

Web Day 29 環境變數與設定管理

Web Day 29 環境變數與設定管理

執行需求:CPU 可跑。今天是「前端整合」這個區塊的最後一篇,也是「上線」這個區塊的第一篇。我們要做的是:把硬編碼的設定(Web Day 27 的 SECRET = "dev-secret-change-me"、Web Day 25 的 BASE = "http://127.0.0.1:8000")抽離出來,改用 pydantic-settings 2.x 管理。完成後,你的程式碼裡不會再有任何「裸字串」當作設定值——所有可調整的參數都來自環境變數或 .env 檔案,可以依 dev / staging / prod 切換。

引言

前面二十八天,我們為了「讓範例能跑」寫了不少 hardcoded 字串:API URL、JWT secret、log 層級、CORS 白名單。在本機開發這樣沒問題,但程式碼要上線時就會爆掉——正式環境的 URL 不同、secret 不一樣、CORS 白名單更嚴格。我們需要一個系統性的做法:把「程式碼」與「設定」分開,把設定外部化到環境變數或設定檔。

這篇文章會用 pydantic-settings 2.x(2025 年 7 月的主流版本)做完整的設定管理。我們會建立一個 Settings class 把所有設定欄位化、用 .env 檔案做本機預設值、用環境變數做正式覆寫、用多重環境(dev/staging/prod)做切換。我們也會涵蓋「敏感資訊不進版控」、「測試時用隔離設定」、「啟動時驗證設定完整性」這些工程細節。pydantic-settings 2.x 的好處是「型別檢查 + 預設值 + 驗證」三件事一次做完,比 os.environ + 手動轉型乾淨很多。

為什麼要外部化設定

十二因子應用(12-Factor App)方法論把「設定」列為第三個原則:在環境中儲存設定。這聽起來抽象,但實際意義很明確——同一份程式碼要在不同環境跑(開發、測試、正式),不能因為「換個 URL」就要改程式碼。違反這個原則的典型症狀:

  • if DEBUG: BASE_URL = "http://localhost" else: BASE_URL = "https://api.example.com"——把環境判斷寫進商業邏輯。
  • 把 secret 直接寫在 main.py 然後 commit 到 Git,結果 secret 永遠公開。
  • 不同工程師本機用不同設定,「在我電腦可以跑」變成日常工作模式。

把設定外部化解決這三個問題:環境變數決定行為、敏感資訊不入版控、.env.example 列出需要的所有 key(不含真實值)、每位工程師複製一份 .env 改自己的值。

來源 適合的設定 不適合
環境變數 正式部署、CI/CD、敏感資訊 需要預設值的本機開發
.env 檔案 本機開發、團隊共享的非敏感設定 正式部署(不進容器)
設定檔(YAML/JSON) 複雜的結構化設定 簡單的 key-value
密鑰管理服務(Vault、AWS Secrets Manager) 高度敏感的資訊 一般設定

用 pydantic-settings 建立 Settings 類別

pydantic-settings 2.x 是 Pydantic 的官方延伸套件,把環境變數讀取、型別轉換、驗證整合進單一類別。我們建立一個 Settings class:

# config.py
# 集中管理所有設定(pydantic-settings 2.x)
from typing import Literal

from pydantic import Field, PostgresDsn, RedisDsn, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """應用設定:從環境變數或 .env 檔案讀取"""

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=False,
        extra="ignore",
    )

    # 環境識別
    environment: Literal["dev", "staging", "prod"] = "dev"
    debug: bool = False

    # 應用基本
    app_name: str = "預約管理 API"
    host: str = "127.0.0.1"
    port: int = 8000
    workers: int = 1

    # 認證(敏感)
    jwt_secret: str = Field(min_length=32)
    jwt_algorithm: str = "HS256"
    access_token_ttl_seconds: int = 15 * 60
    refresh_token_ttl_seconds: int = 7 * 24 * 60 * 60

    # 資料庫
    database_url: str = "sqlite:///./app.db"
    database_echo: bool = False

    # CORS
    cors_allowed_origins: list[str] = Field(default_factory=list)

    # Logging
    log_level: str = "INFO"

    @field_validator("cors_allowed_origins", mode="before")
    @classmethod
    def split_csv(cls, v):
        # 環境變數通常是逗號分隔字串,這裡自動轉成 list
        if isinstance(v, str):
            return [s.strip() for s in v.split(",") if s.strip()]
        return v

    @field_validator("workers")
    @classmethod
    def workers_at_least_one(cls, v: int) -> int:
        if v < 1:
            raise ValueError("workers 至少 1")
        return v

    @field_validator("jwt_secret")
    @classmethod
    def jwt_secret_not_dev_in_prod(cls, v: str, info) -> str:
        env = info.data.get("environment", "dev")
        if env == "prod" and v.startswith("dev-") or v == "change-me":
            raise ValueError("正式環境禁止使用 dev 開頭或 change-me 的 JWT secret")
        return v


# module-level singleton
settings = Settings()

這個 Settings class 展示 pydantic-settings 2.x 的核心能力。BaseSettings 子類別自動從 .env 與環境變數讀值(環境變數優先)。Field(min_length=32) 確保 JWT secret 長度足夠;Literal["dev", "staging", "prod"] 限制 environment 只能是這三個值、輸入錯就拋驗證錯誤。cors_allowed_origins: list[str] 是 list 欄位,預期用逗號分隔字串(CORS_ALLOWED_ORIGINS="http://localhost:3000,http://localhost:8000"),@field_validator(mode="before") 自動 split。

最關鍵的是 jwt_secret_not_dev_in_prod 這個自訂驗證器:正式環境禁止使用 dev 開頭或 "change-me" 的 secret。這是「啟動時把關」的好習慣——不要等到 secret 真的被攻擊者拿來簽 token 才發現設定錯了。這類驗證可以在 staging 就抓到,比正式出事好太多。

把設定整合進應用

把所有 hardcoded 值換成 settings.xxx:

# main.py
# 把 Web Day 25–28 的硬編碼值換成 settings 引用
import logging

import uvicorn
from fastapi import FastAPI, WebSocket
from fastapi.middleware.cors import CORSMiddleware

from config import settings


def create_app() -> FastAPI:
    app = FastAPI(
        title=settings.app_name,
        debug=settings.debug,
    )

    # CORS:從設定讀白名單
    if settings.cors_allowed_origins:
        app.add_middleware(
            CORSMiddleware,
            allow_origins=settings.cors_allowed_origins,
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )

    # Logging:依設定調整層級
    logging.basicConfig(
        level=settings.log_level,
        format="%(asctime)s %(levelname)s %(name)s: %(message)s",
    )

    return app


app = create_app()


if __name__ == "__main__":
    uvicorn.run(
        "main:app",
        host=settings.host,
        port=settings.port,
        workers=settings.workers,
        reload=settings.debug,
    )

這份檔案展示「設定如何驅動行為」。reload=settings.debug 在開發時自動重啟、正式環境關掉;workers=settings.workers 在生產多開幾個 process;CORS 白名單從設定讀,正式環境只允許正式網域。create_app() 是工廠函式,方便測試時建立不同設定的實例(Web Day 16 已示範過)。

分環境設定檔

實務上我們會為不同環境準備不同的預設值。最常見的做法是用 .env.dev、.env.staging、.env.prod 三個檔案,啟動時用 ENV_FILE 環境變數指定:

# config.py 修訂:支援多環境
from pydantic_settings import BaseSettings, SettingsConfigDict
import os


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        # 優先讀 ENV_FILE,其次讀 .env
        env_file=os.environ.get("ENV_FILE", ".env"),
        env_file_encoding="utf-8",
        case_sensitive=False,
        extra="ignore",
    )

    environment: str = "dev"
    # ... 其他欄位同上


settings = Settings()

啟動時指定環境:

# 開發(本機)
ENV_FILE=.env.dev python main.py
# 正式(容器)
ENV_FILE=.env.prod gunicorn main:app --workers 4

.env.dev 範例:

# .env.dev
ENVIRONMENT=dev
DEBUG=true
JWT_SECRET=dev-secret-please-change-32chars-min
DATABASE_URL=sqlite:///./dev.db
CORS_ALLOWED_ORIGINS=http://localhost:3000
LOG_LEVEL=DEBUG

.env.prod 範例:

# .env.prod
ENVIRONMENT=prod
DEBUG=false
JWT_SECRET=CHANGE_ME_TO_RANDOM_64_CHARS_FROM_VAULT
DATABASE_URL=postgresql://app:secret@db.internal:5432/booking
CORS_ALLOWED_ORIGINS=https://booking.example.com
LOG_LEVEL=WARNING
WORKERS=4

這兩個檔案的差別在於正式環境把所有「便利開發」的設定關掉(DEBUG=false、LOG_LEVEL=WARNING),並把敏感資訊指向正式資源(資料庫連線、生產網域)。注意 JWT_SECRET=CHANGE_ME... 是 placeholder——正式部署前要從 Vault 或密鑰管理服務注入真實值,不會出現在 .env.prod 裡。

敏感資訊的管理

JWT secret、資料庫密碼、API 金鑰這些敏感資訊不該放在 .env.prod 檔案(檔案可能被 commit、可能被誤傳)。正確的做法是用密鑰管理服務:

服務 特色 適合規模
環境變數(手動設定) 簡單、無外部依賴 小型專案
Docker secrets / k8s secrets 容器編排整合 中型團隊
HashiCorp Vault 完整 audit log、動態 secret 大型 / 合規需求
雲端密鑰管理(AWS Secrets Manager / GCP Secret Manager) 雲原生整合 雲端部署

對大多數小團隊,環境變數搭配 .env.example(不含真實值)就夠了。我們的 .env.example:

# .env.example:列出所有 key,不含真實值
# 複製成 .env 後填入真實值
ENVIRONMENT=dev
DEBUG=true
HOST=127.0.0.1
PORT=8000
WORKERS=1

# 認證:dev 可以用 dev- 開頭;prod 必須是真實亂數
JWT_SECRET=please-generate-with-openssl-rand-base64-48
JWT_ALGORITHM=HS256

# 資料庫
DATABASE_URL=sqlite:///./app.db
DATABASE_ECHO=false

# CORS:逗號分隔
CORS_ALLOWED_ORIGINS=http://localhost:3000

# Logging
LOG_LEVEL=INFO

這個檔案 commit 到 Git,所有工程師看得到「需要哪些 key」、但看不到值。新工程師複製成 .env、填自己的值。CI/CD 通常在 pipeline 設定真實環境變數、不依賴 .env 檔案。

產生安全 JWT secret 的指令:

# 產生 48 bytes 隨機 base64 字串
openssl rand -base64 48
# 輸出(範例):7HjK9mNqP2vR3sT4uV5wX6yZ1aB0cD8eF9gH0iJ1kL2mN3oP4qR=

用 pytest 驗證設定

設定錯誤往往在啟動時才發現,但這時已經來不及。我們用 pytest 在啟動前驗證設定:

# test_config.py
# 設定驗證測試
import pytest
from pydantic import ValidationError

from config import Settings


def test_default_settings_load(tmp_path, monkeypatch):
    # 沒有 .env 時應用預設值
    monkeypatch.chdir(tmp_path)
    s = Settings()
    assert s.environment == "dev"
    assert s.jwt_algorithm == "HS256"


def test_reads_env_file(tmp_path, monkeypatch):
    env_file = tmp_path / ".env"
    env_file.write_text(
        "ENVIRONMENT=staging\n"
        "JWT_SECRET=test-secret-with-at-least-32-chars\n"
        "DATABASE_URL=sqlite:///./staging.db\n"
    )
    monkeypatch.chdir(tmp_path)
    s = Settings()
    assert s.environment == "staging"
    assert s.database_url == "sqlite:///./staging.db"


def test_env_var_overrides_file(tmp_path, monkeypatch):
    # 環境變數優先於 .env 檔案
    env_file = tmp_path / ".env"
    env_file.write_text("ENVIRONMENT=dev\n")
    monkeypatch.chdir(tmp_path)
    monkeypatch.setenv("ENVIRONMENT", "prod")
    monkeypatch.setenv(
        "JWT_SECRET", "a-real-64-char-secret-not-starting-with-dev-or-change"
    )
    s = Settings()
    assert s.environment == "prod"


def test_jwt_secret_min_length():
    with pytest.raises(ValidationError):
        Settings(JWT_SECRET="too-short")


def test_environment_literal():
    with pytest.raises(ValidationError):
        Settings(ENVIRONMENT="unknown", JWT_SECRET="x" * 40)


def test_prod_rejects_dev_secret():
    with pytest.raises(ValidationError):
        Settings(
            ENVIRONMENT="prod",
            JWT_SECRET="dev-this-is-not-allowed-in-production",
        )


def test_cors_csv_parsing(tmp_path, monkeypatch):
    env_file = tmp_path / ".env"
    env_file.write_text(
        "JWT_SECRET=test-secret-with-at-least-32-chars\n"
        "CORS_ALLOWED_ORIGINS=http://a.com,http://b.com\n"
    )
    monkeypatch.chdir(tmp_path)
    s = Settings()
    assert s.cors_allowed_origins == ["http://a.com", "http://b.com"]


def test_workers_must_be_positive():
    with pytest.raises(ValidationError):
        Settings(WORKERS=0, JWT_SECRET="x" * 40)

八個測試覆蓋了設定系統的所有關鍵路徑:預設值、讀檔案、環境變數優先、長度驗證、Literal 限制、prod 拒絕 dev secret、CSV 解析、正整數。tmp_path 是 pytest 內建的暫存目錄 fixture;monkeypatch.chdir(tmp_path) 把當前目錄切到暫存目錄,這樣 Settings() 找不到 .env 檔案時就會用預設值或環境變數。monkeypatch.setenv(...) 在測試結束後自動清除環境變數,避免污染其他測試。

關鍵的 test_prod_rejects_dev_secret 展示了「正式環境拒絕 dev secret」的驗證——這個測試保證未來就算改了 jwt_secret_not_dev_in_prod 的邏輯,只要不小心改壞,CI 會立刻失敗。

用 FastAPI Depends 注入設定

另一個好做法是把 Settings 透過 FastAPI 的 Depends 注入,而不是直接 import singleton:

# dependencies.py
# 把設定包成 dependency,方便測試時覆寫
from functools import lru_cache

from fastapi import Depends

from config import Settings


@lru_cache
def get_settings() -> Settings:
    # lru_cache 確保整個 process 只讀一次設定
    return Settings()


# 在 endpoint 裡用:
#   def endpoint(settings: Settings = Depends(get_settings)):
#       secret = settings.jwt_secret

lru_cache 確保 Settings() 只實例化一次(讀環境變數很貴)。在測試裡可以用 app.dependency_overrides[get_settings] = lambda: test_settings 替換,這是 FastAPI 官方推薦的測試模式。

常用設定模式速查

把幾個常見的設定模式整理成速查表,方便日後套用:

模式 寫法 用途
布林值 debug: bool = False DEBUG、feature flag
整數(含範圍) port: int = Field(default=8000, ge=1, le=65535) port、timeout、TTL
字串列舉 env: Literal["dev","prod"] 環境識別
list 從 CSV origins: list[str] + field_validator(split_csv) CORS 白名單
SecretStr secret: SecretStr 密碼、API 金鑰
選填設定群組 redis: RedisSettings | None = None 分模組的子設定

SecretStr 是 Pydantic 內建的「不會印到 log」的密碼型別。把它用在 secret 欄位可以避免不小心把密碼印到 traceback:

# config.py:用 SecretStr 保護敏感資訊
from pydantic import SecretStr


class Settings(BaseSettings):
    # ...
    jwt_secret: SecretStr = Field(min_length=32)
    database_password: SecretStr | None = None

存取 SecretStr 時用 settings.jwt_secret.get_secret_value() 才能拿到明文;印 settings.jwt_secret 只會看到 **********,不會洩漏。

常見錯誤與踩雷

第一個常見踩雷:.env 進了版控。.gitignore 一定要加 .env;commit 之前 git status 確認沒有 .env。如果不小心 commit 了,secret 立刻撤銷、重新產生、推到所有機器。

第二個常見踩雷:JWT_SECRET=change-me 推到正式環境。正式部署的 checklist 一定要有「檢查每個 secret 不是 placeholder」。我們的 jwt_secret_not_dev_in_prod 驗證器就是擋這個——如果忘了換 secret、應用會在啟動時崩潰,而不是被攻擊者發現。

第三個常見踩雷:把不同環境的 .env 都 commit。每個環境的敏感資訊不同,commit 任何一份都會洩漏。建議:.env 都不進版控、只有 .env.example 進;正式部署用雲端密鑰管理。

第四個常見踩雷:在測試裡用真的 Settings()。測試本來就要隔離,用真設定可能會碰到「測試環境沒設某個 env var、應用崩潰」。對應策略:在 conftest.py 用 monkeypatch.setenv(...) 設定測試專用的環境變數,或用 Settings(...) 直接傳值。

第五個常見踩雷:Settings() 在 import 時就讀取。pydantic-settings 在物件建立時就讀環境變數,如果這時環境變數不完整(例如部分 CI 環境沒設),整個應用 import 就會崩潰。對應策略:在 main.py 內呼叫 Settings()、不要在 module 層級;或在 model_config 設 validate_default=False。

效能與實務提醒

Settings() 物件很輕量,但讀取 .env 檔案有檔案 I/O 成本。我們用 @lru_cache 確保只讀一次,避免每個請求都重新讀檔。在 100 RPS 的應用下,這可以省下可觀的時間。

另一個提醒:pydantic-settings 2.x 預設不會讀 .env.local(常見慣例是 dev 個人設定)。我們可以在 env_file 設定成 [".env.local", ".env"](優先讀 local),讓每個工程師用自己的 .env.local 覆蓋預設值。

最後一個細節:Settings 的設定變動需要重啟應用才生效。如果你想做「不重啟熱更新設定」(例如調 log level 不重啟),可以讀環境變數 + 自己寫 reload 機制、或用專門的設定中心(etcd、Consul)。對大多數應用,重啟就夠了——Web Day 30 的 Docker 化會讓重啟成本接近零。

啟動時檢查設定完整性的範本

我們在前面的 jwt_secret_not_dev_in_prod 驗證器展示了「啟動時驗證」的好處。再擴充幾個常見的檢查,把整套啟動檢查寫進 main.py:

# startup_checks.py
# 啟動時做完整的健康檢查
import sys

from config import settings


def check_database_url() -> list[str]:
    errors = []
    if not settings.database_url:
        errors.append("DATABASE_URL 不能為空")
    return errors


def check_cors_in_prod() -> list[str]:
    errors = []
    if settings.environment == "prod":
        if not settings.cors_allowed_origins:
            errors.append("正式環境必須設定 CORS 白名單")
        if "http://" in " ".join(settings.cors_allowed_origins):
            errors.append("正式環境禁止 http:// 的 CORS origin")
    return errors


def check_workers_in_prod() -> list[str]:
    errors = []
    if settings.environment == "prod" and settings.workers < 2:
        errors.append("正式環境 WORKERS 至少 2")
    return errors


def run_all_checks() -> None:
    all_errors = []
    all_errors.extend(check_database_url())
    all_errors.extend(check_cors_in_prod())
    all_errors.extend(check_workers_in_prod())

    if all_errors:
        print("啟動檢查失敗:")
        for e in all_errors:
            print(f"  - {e}")
        sys.exit(1)

    print(f"啟動檢查通過:{settings.environment} 環境")

在 main.py 開頭呼叫 run_all_checks(),啟動時就把問題抓出來。這比「應用跑起來才 500」好太多。Web Day 34 會把這套邏輯擴充成正式的健康檢查端點(/health),給監控系統與負載平衡器用。

把設定管理納入 CI/CD

設定管理不只在程式裡,也要延伸到 CI/CD。GitHub Actions(Web Day 32 詳細展開)的 workflow 應該把 .env 與 secret 分開處理:

  • 非敏感設定:放在 .env.staging 檔案、commit 到 repo、CI 直接讀。
  • 敏感設定:放在 GitHub Actions 的 Settings → Secrets,CI 用 secrets.JWT_SECRET 引用,不進 log。

GitHub Actions 的 secret 在日誌中會被自動遮罩(***),但仍要避免「印出整個環境變數」這種行為——遮罩只對「字串完全等於 secret 名稱」有效,部分遮罩(例如只印 secret 的前 4 字元)會洩漏。

多服務共享設定的設計

「預約管理系統」貫穿專案會有多個服務:FastAPI(後端)、Next.js(前端)、PostgreSQL(資料庫)、Redis(快取)、Caddy(反向代理)。每個服務都有自己的設定,但會共享一些「跨服務」的設定(例如資料庫 URL)。常見做法是把共用設定抽到一個 config/common.env 檔案,每個服務用 env_file 引用:

# docker-compose.yml(Web Day 41 完整版)
services:
  api:
    env_file:
      - config/common.env
      - config/api.env
    environment:
      - DATABASE_URL=postgresql://app:secret@db:5432/booking

  web:
    env_file:
      - config/common.env
      - config/web.env

  db:
    env_file:
      - config/db.env

config/common.env 放共用設定(如 ENVIRONMENT=staging),每個服務自己的 .env 放專屬設定(如資料庫密碼、API 金鑰)。Docker Compose 的 env_file 支援多檔案、後面的會覆蓋前面的,這讓設定組合有彈性。

敏感資訊(如資料庫密碼)通常用 Docker secrets(Swarm 模式)或 Kubernetes secrets 注入,不放進 .env 檔案。今天的範例先用 env_file 簡化、貫穿專案 Day 41 會正式用 Docker Compose 的完整機制。

設定演進的策略

應用成長後,設定會越來越多,Settings class 會膨脹。建議的演進策略:

  1. 第一階段:所有設定放在單一 Settings class(今天示範的方式)。
  2. 第二階段:把相關設定組成巢狀結構,例如 DatabaseSettings、AuthSettings、LoggingSettings,每個獨立可驗證。
  3. 第三階段:把巢狀結構拆成獨立的設定檔(如 config/database.py、config/auth.py),每個檔案用各自的 pydantic model。

貫穿專案「預約管理系統」會走到第二階段:DatabaseSettings 處理連線池與遷移、AuthSettings 處理 JWT 與 OAuth、LoggingSettings 處理 log 輸出。今天先建立基礎結構,未來章節會逐步擴充。

回顧與銜接

今天結束後,我們的應用有了完整的「設定管理」能力:所有設定欄位化、型別化、驗證化;環境變數與 .env 分層;敏感資訊不進版控;正式環境有啟動檢查。這些能力讓 Web Day 30 的 Docker 化、Day 31 的 PostgreSQL 上線、Day 32 的 CI/CD 都有乾淨的基礎。沒有設定管理就上線,會發生「為什麼正式環境連不到資料庫」這種痛苦問題——把所有設定外部化、變動只需改環境變數,是把「我電腦能跑」變成「正式環境能跑」的工程紀律。

明天我們進入 Docker 化,把這個能跨環境跑的應用打包成容器、部署到任何支援 Docker 的平台。Web Day 30 標示為「需 Docker」,沒有 Docker 的讀者可以先讀全文、之後再補做;有了今天建立的設定管理,明天的工作會非常順暢。

小結

今天把「設定管理」這個上線前的關卡打通。我們用 pydantic-settings 2.x 建立 Settings 類別,所有設定欄位化、型別化、驗證化;用 .env 檔案做本機預設值、用環境變數做正式覆寫;用驗證器擋「prod 用 dev secret」這類錯誤;用 pytest 保護設定不被改壞。整合進 FastAPI 後,main.py 不再有任何 hardcoded 字串,整個應用可以用同一份程式碼在 dev/staging/prod 跑。明天(Web Day 30)我們進入 Docker 化:把這個能跨環境跑的應用打包成容器,讓部署從「改檔案」變成「拉新 image」。

結語

今天的重點是「設定外部化是上線的基本功」。我們用 pydantic-settings 2.x 把所有設定欄位化、用環境變數與 .env 分層管理、用驗證器擋 prod 用 dev secret 的錯誤、用 pytest 保護設定邏輯。讀完這篇你應該能回答:為什麼 Settings 要驗證?.env 與環境變數的優先順序?怎麼防止 secret 進版控?正式部署要怎麼注入 secret?

明天,我們進入 Docker 化。Docker Compose 會讓「部署一個完整應用(API + 資料庫 + 反向代理)」變成「拉 image、設定環境變數、啟動」三個步驟。Web Day 30 標示為「需 Docker」,沒裝 Docker 的讀者可以先用本機 uvicorn + 本機 SQLite 跟著做、之後再回頭補 Docker。準備好迎接容器化時代了嗎?我們出發。

延伸資源

  • pydantic-settings 2.x 官方文件(2025):https://docs.pydantic.dev/latest/concepts/pydantic_settings/,BaseSettings、SettingsConfigDict、Sources 的完整用法。
  • 十二因子應用(2025):https://12factor.net/config,設定外部化的經典方法論。
  • openssl rand 用法(2025):https://www.openssl.org/docs/manmaster/man1/rand.html,產生高品質亂數作為 secret。
  • OWASP Secrets Management Cheat Sheet(2024):https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html,secret 不進版控、不在 log、不在錯誤訊息的具體做法。
  • FastAPI Settings and Environment Variables(0.116,2025):https://fastapi.tiangolo.com/advanced/settings/,FastAPI 對 pydantic-settings 的整合慣例。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...