Web 應用程式 Docker 化

首先,我們需要撰寫一個 Dockerfile,用於建構 Web 應用程式的 Docker 映像檔。以下是一個簡化的 Dockerfile 範例:

FROM python:3

ENV PYTHONUNBUFFERED 1

RUN mkdir /opt/code
RUN mkdir /opt/requirements
WORKDIR /opt/code

ADD requirements /opt/requirements
RUN pip install -r /opt/requirements/prod.txt

這個 Dockerfile 使用官方的 python:3 映像檔作為基礎,並安裝了生產環境所需的套件。requirements/prod.txt 檔案包含了所有必要的 Python 套件,例如 Flask、SQLAlchemy、psycopg2、pymongo 和 gunicorn。

  Flask
SQLAlchemy
psycopg2
pymongo
gunicorn

Nginx 設定

為了反向代理 Web 應用程式,我們需要設定 Nginx。以下是一個基本的 Nginx 設定檔:

worker_processes 1;

events { worker_connections 1024; }

http {
    sendfile on;

    upstream app {
        server web:8000;
    }

    server {
        listen 8080;

        location / {
            proxy_pass http://app;
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $server_name;
        }
    }
}

這個 Nginx 設定將監聽 8080 埠,並將所有請求反向代理到執行在 8000 埠的 Web 應用程式。

使用 Docker Compose 管理

為了簡化容器的管理,我們使用 Docker Compose。以下是一個 docker-compose.yml 檔案的範例:

version: '3.8'
services:
  web:
    build: ./docker/web
    ports:
      - "8000:8000"
  nginx:
    build: ./docker/nginx
    ports:
      - "8080:8080"
    depends_on:
      - web
  db:
    image: postgres
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: your_user
      POSTGRES_PASSWORD: your_password
      POSTGRES_DB: your_db

程式碼調整與資料函式庫整合

為了讓應用程式連線到 Postgres 資料函式庫,我們需要修改程式碼。以下是一個使用 SQLAlchemy 連線 Postgres 資料函式庫的範例:

import os
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# 從環境變數中讀取資料函式庫連線資訊
db_user = os.environ.get("POSTGRES_USER")
db_password = os.environ.get("POSTGRES_PASSWORD")
db_host = os.environ.get("POSTGRES_HOSTNAME")
db_port = os.environ.get("POSTGRES_PORT")
db_name = os.environ.get("APPLICATION_DB")

# 建立資料函式庫連線字串
db_url = f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}"

# 建立資料函式庫引擎
engine = create_engine(db_url)

# 建立資料函式庫 Session
Session = sessionmaker(bind=engine)
session = Session()

# ... 後續資料函式庫操作 ...

內容解密:這段程式碼示範瞭如何使用 SQLAlchemy 連線 Postgres 資料函式庫。首先,它從環境變數中讀取資料函式庫連線資訊,然後建立資料函式庫連線字串。接著,它使用 create_engine 函式建立資料函式庫引擎,並使用 sessionmaker 函式建立資料函式庫 Session。最後,可以使用 session 物件進行資料函式庫操作。

透過 Docker、Nginx 和 Postgres,我們可以輕鬆地建構一個可立即投入生產的 Web 應用程式。容器化技術讓應用程式的佈署和管理更加便捷,同時也提高了應用程式的可移植性和可擴充套件性。

  graph LR
    C[C]
    A[Client] --> B(Nginx)
    B --> C{Web Application}
    C --> D(PostgreSQL)

上圖展示了系統的架構,客戶端請求透過 Nginx 反向代理到 Web 應用程式,Web 應用程式再與 PostgreSQL 資料函式庫互動。

  
在建構生產級系統時,資料函式庫遷移是至關重要的一環。本文將引導您使用 Alembic,一個專為 SQLAlchemy 設計的資料函式庫遷移工具,搭配 PostgreSQL 資料函式庫和 Docker 容器化佈署,實作穩健與可控的資料函式庫版本管理。

## 驗證資料函式庫連線

首先,我們需要驗證與資料函式庫的連線。透過執行以下指令,登入資料函式庫容器並連線到 PostgreSQL:

```bash
./manage.py compose exec db psql -U postgres

請注意,-U postgres 指定了連線的使用者名稱。這個使用者名稱是在 config/production.json 中透過 POSTGRES_USER 變數設定的。登入後,使用 \l 指令檢視可用的資料函式庫:

postgres=# \l

系統會列出所有可用的資料函式庫,包括系統資料函式庫 template0template1、預設資料函式庫 postgres 以及我們建立的應用程式資料函式庫 application。使用 \c 指令連線到 application 資料函式庫:

postgres=# \c application

接著,使用 \dt 指令列出資料函式庫中的表格:

application=# \dt

目前資料函式庫中還沒有任何表格,因為我們尚未將 SQLAlchemy 模型與資料函式庫同步。

資料函式庫遷移與 Alembic

為了建立與 SQLAlchemy 模型對應的資料函式庫表格,我們需要使用 Alembic 進行資料函式庫遷移。首先,將 alembic 新增到 requirements/prod.txt 中,並執行 pip install -r requirements/dev.txt 更新虛擬環境。

接著,初始化 Alembic:

alembic init migrations

這會建立一個名為 migrations 的目錄,其中包含 Alembic 的設定檔和遷移指令碼。同時,也會建立 alembic.ini 設定檔。

修改 migrations/env.py,加入以下程式碼,讓 Alembic 可以連線到資料函式庫:

import os
# ... other imports ...

config = context.config
section = config.config_ini_section

config.set_section_option(section, "POSTGRES_USER", os.environ.get("POSTGRES_USER"))
config.set_section_option(section, "POSTGRES_PASSWORD", os.environ.get("POSTGRES_PASSWORD"))
config.set_section_option(section, "POSTGRES_HOSTNAME", os.environ.get("POSTGRES_HOSTNAME"))
config.set_section_option(section, "APPLICATION_DB", os.environ.get("APPLICATION_DB"))

# ... other code ...

from rentomatic.repository.postgres_objects import Base
target_metadata = Base.metadata

內容解密:這段程式碼設定了 Alembic 的環境變數,使其能夠連線到 PostgreSQL 資料函式庫。target_metadata 變數指定了 SQLAlchemy 模型的中繼資料,以便 Alembic 自動生成遷移指令碼。

然後,修改 alembic.ini 檔案,使用新的變數:

sqlalchemy.url = postgresql://%(POSTGRES_USER)s:%(POSTGRES_PASSWORD)s@%(POSTGRES_HOSTNAME)s/%(APPLICATION_DB)s

內容解密:這行程式碼設定了 SQLAlchemy 的連線 URL,使用環境變數動態生成。

執行遷移

現在,我們可以執行 Alembic 遷移了。首先,使用以下指令自動生成遷移指令碼:

POSTGRES_USER=postgres \
POSTGRES_PASSWORD=postgres \
POSTGRES_HOSTNAME=localhost \
APPLICATION_DB=application \
alembic revision --autogenerate -m "Initial"

這會在 migrations/versions 目錄下建立一個遷移指令碼檔案。

最後,執行以下指令應用遷移到資料函式庫:

POSTGRES_USER=postgres \
POSTGRES_PASSWORD=postgres \
POSTGRES_HOSTNAME=localhost \
APPLICATION_DB=application \
alembic upgrade head

內容解密:alembic upgrade head 指令將所有未應用的遷移指令碼應用到資料函式庫,更新資料函式庫結構。

驗證遷移結果

再次連線到資料函式庫,檢查表格:

./manage.py compose exec db psql -U postgres -d application
application=# \dt

現在,您應該可以看到 alembic_versionroom 兩個表格。alembic_version 是 Alembic 用於追蹤遷移版本的表格,而 room 表格則對應我們的 SQLAlchemy 模型。

透過以上步驟,我們成功地使用 Alembic 進行了資料函式庫遷移,並在生產環境中建立了所需的資料函式庫表格。

資料函式庫遷移流程

  graph LR
    C[C]
    A[定義 SQLAlchemy 模型] --> B(初始化 Alembic);
    B --> C{自動生成遷移指令碼};
    C -- 環境變數 --> D[應用遷移到資料函式庫];
    D --> E[驗證資料函式庫表格];

內容解密:此流程圖展示了使用 Alembic 進行資料函式庫遷移的步驟,從定義 SQLAlchemy 模型到驗證資料函式庫表格。

這個流程確保了資料函式庫結構與應用程式碼同步,為生產環境的穩定執行提供了保障。未來,當模型發生變化時,只需重複上述步驟,即可輕鬆管理資料函式庫的版本更新。

透過 Alembic 的使用,我們可以有效管理資料函式庫的版本,並確保資料函式庫結構與應用程式碼同步,從而提升開發效率和系統穩定性。

  
在軟體開發中,確保資料函式庫與API的整合性至關重要。這代表資料函式庫的任何變更都應該正確地反映在API的回應中。本文將以rentomatic專案為例,示範如何驗證這項整合性。

## 檢查Alembic版本

首先,我們可以檢查Alembic的版本,確保資料函式庫遷移已正確執行:

```sql
select * from alembic_version;

這將傳回一個版本號,該版本號應該與遷移指令碼的名稱一致。

檢查資料函式庫表結構

接著,我們可以檢查room表的結構:

\d room

這將顯示room表的欄位資訊,例如idcodesizepricelongitudelatitude

檢查資料函式庫表資料

確認表結構後,我們可以檢查room表中的資料:

select * from room;

在初始狀態下,表中應該沒有任何資料。

透過API檢視資料

此時,如果我們透過瀏覽器存取http://localhost:8080/rooms,應該會看到一個成功的回應,但沒有任何資料。

新增資料到資料函式庫

為了檢視資料,我們需要向資料函式庫新增資料。為了簡化流程,我們可以直接在資料函式庫中新增資料:

INSERT INTO room(code, size, price, longitude, latitude) 
VALUES ('f853578c-fc0f-4e65-81b8-566c5dffa35a', 215, 39, -0.09998975, 51.75436293);

再次檢查資料函式庫表資料

新增資料後,再次執行SELECT * FROM room;,確認資料已成功新增。

透過API檢視新增的資料

重新整理瀏覽器中的http://localhost:8080/rooms,應該可以看到新增的房間資料。

程式碼範例

#  範例程式碼:新增房間資料的API endpoint

from flask import Flask, jsonify, request

app = Flask(__name__)

rooms = []

@app.route('/rooms', methods=['POST'])
def add_room():
    data = request.get_json()
    rooms.append(data)
    return jsonify(data), 201

@app.route('/rooms', methods=['GET'])
def get_rooms():
    return jsonify(rooms)

if __name__ == '__main__':
    app.run(debug=True)

內容解密:

這段程式碼示範了一個簡單的Flask API,用於新增和取得房間資料。add_room函式處理POST請求,將房間資料新增到rooms列表中。get_rooms函式處理GET請求,傳回所有房間資料。

API 與資料函式庫互動流程

  graph LR
    B[B]
    D[D]
    A[Client Request] --> B{API Endpoint}
    B --> C[Database Query]
    C --> D{Database Response}
    D --> B
    B --> E[API Response]
    E --> A

這個流程圖展示了客戶端請求、API、資料函式庫之間的互動流程。客戶端傳送請求到API,API查詢資料函式庫,資料函式庫傳回結果給API,最後API將結果傳回給客戶端。

透過以上步驟,我們可以驗證資料函式庫與API的整合性,確保資料函式庫操作能正確反映在API的回應中。這對於構建可靠的應用程式至關重要。

這個範例展示瞭如何透過直接操作資料函式庫和觀察API回應來驗證兩者之間的整合性。在實際專案中,建議使用更完善的測試框架,例如pytest,來編寫自動化測試,確保程式碼的品質和穩定性。此外,更進階的測試方法還包括使用mock資料函式庫或容器化技術,以隔離測試環境,避免對生產環境造成影響。 經過這篇文章的探索,我們深入瞭解了軟體架構設計的演變、核心原則以及現代實踐方法。從早期結構化程式設計到物件導向,再到今日的微服務和雲原生架構,軟體架構的發展始終圍繞著提升軟體品質、可維護性和可擴充套件性等關鍵目標。

我特別強調了在實踐中應如何權衡不同的架構模式,並根據專案的具體需求做出最佳選擇。沒有放之四海而皆準的完美架構,只有最適合特定情境的解決方案。這需要我們對各種架構模式有深入的理解,並能靈活運用到實際專案中。

此外,我也分享了在架構設計過程中的一些心得體會,例如何有效地與團隊溝通、如何管理技術債務以及如何在快速變化的技術環境中保持學習和成長。這些經驗來自於我在眾多專案中的實踐,希望能為讀者提供一些參考和啟發。

最後,我鼓勵各位讀者持續探索和學習新的架構模式和技術,並將其應用到自己的專案中。軟體架構設計是一個持續演進的領域,只有不斷學習和實踐,才能保持競爭力並創造出更優秀的軟體產品。