雲端架構部署救星!介紹 Terraform 是如何大幅改善雲端基礎建設的建立流程

  1. 過去在建立基礎架構時有許多痛點
  2. Terraform 是什麼?
  3. Terraform 如何運作
  4. Terraform 基本檔案結構
    1. providers.tf
    2. main.tf
      1. BigQuery Dataset
      2. BigQuery Tables
      3. Pub/Sub Topic
      4. Pub/Sub Subscriptions
    3. variables.tf
    4. terraform.tfvars
    5. outputs.tf
  5. 總結
    1. Terraform 注意事項與缺點
    2. Terraform 帶來的好處
  6. 後記
  7. 參考資料

過去在建立基礎架構時有許多痛點

當我們想要在雲端上建立各種服務時,通常都是透過 UI 的方式進行,但當架構越來越複雜,甚至還需要整組搬遷的時候,就會遇到很多困難,例如重新設定後可能剛好忘記 region 是設在哪、這個專案是用哪一個 Docker image,或這台機器的規格到底是幾個 CPU 多少記憶體。

整體來說,傳統建立基礎架構時會遇到以下的痛點

  • UI 操作過程繁雜
  • 大量人工操作,易出錯且難以維持一致性,增加除錯時間
  • 難以自動化,導致部署效率低
  • 無法版本控管、追蹤變更,增加操作風險
  • 無法重複利用,缺乏模組化設計,每次建立的成本都很高

所以我們過去在建立基礎架構時,常常需要花費大量時間與人力,效率太差,甚至可能會有許多都是重複性高的操作,而 Terraform 就是可以解決這些痛點的重要工具。

Terraform 是什麼?

Terraform 是一種「基礎架構即程式碼」工具 (Infrastructure as Code, Iac) ,讓我們使用人類易讀的設定檔中定義雲端和地端資源,並可對這些檔案進行版本控管、可重複使用和分享共用,讓所有基礎架構能在配置和管理上達成一致。

Terraform 如何運作

Terraform 主要是透過 Providers 來跟特定的雲端 API 做溝通,例如 GCP providers, AWS providers 等

在使用 Terraform 時主要有三個步驟:

閱讀更多»

如何使用 Python 套件管理工具「uv」取代 pip 來加速 Docker Image 的建立

  1. 前言
  2. 什麼是 uv?
  3. uv 的使用方法
    1. 本機
    2. Dockerfile
  4. 實測檔案
    1. Dockerfile Using pip
    2. Dockerfile Using uv
    3. requirements.txt
  5. Linux Ubuntu 上進行 docker build
    1. 使用 pip = 87.6 秒
    2. 使用 uv = 33.3 秒
  6. GCP Cloud Shell 上進行 docker build
    1. 使用 pip = 20.1 秒
    2. 使用 uv = 11.7 秒
  7. 總結

前言

在 Build Docker Image 的時候,通常最花時間的都是安裝套件,為了突破這個效能瓶頸,這次決定嘗試使用 uv 來取代 pip 進行 Python 套件的安裝。

什麼是 uv?

uv 是一個以 Rust 撰寫的 Python 套件管理工具,號稱比 pip 還要快 10-100 倍,國外也有相關實測,像是 Streamlit 在 2024 年 7 月就有發 blog 表示他們用 uv 取代 pip 後速度提升了 55%(如圖 1)。

(2025/05更新)
近期發現開始有開源專案的官網文件也已經預設是使用 uv 來安裝,例如 Prefect(如圖 2)。

之前我也有在本機實測過,確實有非常顯著地提升了效能,而這次我打算將 uv 運用在 Docker 上,讓 Docker image 的建立時間能大幅縮減。

圖 1:Streamlit 用 uv 取代 pip 後速度提升了 55%
圖 2:開源專案 Prefect 的官網也已經預設是使用 uv 來安裝

uv 的使用方法

閱讀更多»

【MongoDB 最佳實踐規範】❶ Data Modeling 篇: 資料的儲存該如何設計

  1. MongoDB 和一般關聯式資料庫 (RDBMS) 的差異
  2. MongoDB Best Practices 最佳實踐規範
  3. Data Modeling
    1. 1. 會一起被使用的數據就放在一起
    2. 2. 避免儲存較大的陣列
    3. 3. 某些情況下,建議將數據放在不同文件
    4. 4. 跨文件存放相同的數據可以提高效能
  4. 參考資料

MongoDB 和一般關聯式資料庫 (RDBMS) 的差異

MongoDB 屬於一種非關聯式資料庫,與關聯式資料庫的差異其實網路上已經有非常多的資料,個人覺得主要是 Schema 的彈性不同:MongoDB 可以不用事先定義資料欄位和型態,但像是 MySQL 就得先 Create Table 並定義固定的 schema,若後續有想要更改或是新增欄位就會比較麻煩。

所以如果你的資料隨時間變動的可能性較大、具有較高不確定性的話,MongoDB 就會是很好的選擇!

關於差異也可以參考 AWS 所做的表格

MongoDBMySQL
資料模型將資料儲存在 JSON 文件中
然後將其組織到集合中
將資料儲存在資料欄和資料列中
資料儲存採用表格式和關聯式方式
可擴展性使用複寫和碎片化來水平擴展使用垂直擴展和僅供讀取複本來大規模提升效能
查詢語言使用 MongoDB 查詢語言使用 SQL
效能擅長 INSERT 或 UPDATE 大量記錄SELECT 大量記錄時,MySQL 的速度更快
靈活性沒有結構描述,這點提供更多靈活性
使其能處理非結構化、半結構化和結構化資料
具有剛性結構描述,能有效處理結構化資料
安全性使用 Kerberos、X.509 和 LDAP 憑證來驗證使用者使用內建身分驗證方法
MongoDB vs. MySQL

MongoDB Best Practices 最佳實踐規範

基於以上所述的一些差異,在使用 MongoDB 時的操作就會有很多要注意的地方,這次藉由官方最新發布的 MongoDB Best Practices Guide 來整理一些建議的 Tips。

預計會分成四個主題,包含

  1. Data Modeling
  2. Working With Data
  3. Optimizing Data Access Patterns
  4. Availability and Scaling

Data Modeling

數據建模要考慮的,就是最後將會如何使用這些數據、如何訪問這些數據,來決定怎麼設計資料儲存的形式。

1. 會一起被使用的數據就放在一起

把可能會一起使用的數據放在同一個 document 中,可以大幅提升查詢效能,因為等於直接省去了很多類似關聯式資料庫的 Join。

如下方的 document,user 的 alerts 是直接寫在每個 user 的資料底下,而不是像 MySQL 那樣另外寫一張叫做 alerts 的資料表,這樣在查詢的時候只要查詢 users 這個 collection 就可以直接取得各自的 alerts,完全不需要再另外 Join。

像這樣的 embedded data (subdocuments) 在 MongoDB 是比較建議的做法,因為也可以讓 UPDATE 資料能在單一一個操作中完成。

// db.users
{
     _id: "abc",
     email: "xyz@example.com",
     preferences: {
     alerts:[
         { name: "morning", frequency: "daily", time: { h: 6, m: 0 } }
     ],
     colors: { bg: "#cccccc" }
     }
}

2. 避免儲存較大的陣列

由於 MongoDB 每個 document 的最大儲存上限是 16 MB,所以像是餐廳商店的評論,資料的則數和字數都是未知的,用陣列儲存在同一個餐廳商店資料階層底下,有可能就會占用很大的儲存空間。

因此這類的數據就建議每個評論分成不同的 document,並帶有餐廳商店的 ID 做參照 (如下方範例)

// businesses
{
     _id: "100",
     name: "Bake and Go",
     addresses: [
         { street: "40 Elm", state: "NY" },
         { street: "101 Main St", state: "VT" }
     ]
}

 // businessReviews
{
     _id: "61e706e2450fac1271c9b27a",
     storeId: "100",
     rating: 5,
     comment: "Best bagels in town, a must try if you are in the area."
}
{
     _id: "61e706e2450fac1271c9c522",
     storeId: "100",
     rating: 5,
     comment: "Yummy desserts and a nice place to sit and eat."
}

3. 某些情況下,建議將數據放在不同文件

其實並非所有的 1:1 或 1:many 關聯都可以直接無腦放在同一個文件中,如果有以下狀況的話,就建議將數據放在不同的文件:

  • 在很常需要讀取的文件中,包含了部分幾乎不會用到的數據
  • 文件中部份數據會隨時間更新和成長,但另外一部分卻相對固定不變
  • 文件大小可能會超過 16 MB 上限 (如同前述的第2點)

下方以一個汽車製造商的資料庫為例,汽車車款 (models) 是陣列的形式,但由於車款的資料通常都是個別顯示 (比較不會有同時需要取用多個車款的情況),所以這樣儲存陣列的話,查詢數據後就會需要進行額外的資料處理。

這是錯誤示範:

// manufacturers
{
     _id: "200",
     name: "Swaab Automotive",
     type: "auto",
     models: [
         { name: "Swaab Model X", year: 2022, sku: "SWA-X-22Z"},
         { name: "Swaab Model Y", year: 2022, sku: "SWA-Y-22Z"},
         //...
     ]
}

因此這種情況下,就比較建議將汽車 models 另外獨立成一個 collection,並且帶有製造商 ID 做參照。

而且通常 models 資料的使用也會較獨立,也可能更新比較頻繁,所以把它們拉出來做為另一個 collection 會比較方便讀取和寫入。

正確示範如下:

// models
{
     _id: "1201",
     name: "Swaab Model X",
     year: 2022,
     sku: "SWA-X-22Z",
     manufacturer_id: "200"
}
{
     _id: "1202",
     name: "Swaab Model Y",
     year: 2022,
     sku: "SWA-Y-22Z",
     manufacturer_id: "200"
}

4. 跨文件存放相同的數據可以提高效能

為了提高效能,就要避免額外的資料 JOIN 操作,所以建議可以在不同的文件中存放相同的數據。

如下方範例,在 users, posts 兩個 collections 中都存放相同的使用者 Email 數據:

// users
{
     _id: "1",
     name: { first: "Jane", last: "Doe" }
     email: "jdoe@example.com"
}

// posts
{
     _id: "1055",
     title: "My First Post",
     text: "Hello World",
     userId: "1",
     userEmail: "jdoe@example.com"
}

雖然資料看起來會比較複雜,但 MongoDB 提供了 change streamstriggers,來確保跨文件的重複存放數據都能保持同步更新和一致性。

參考資料

2021 年的「資料工程師」有哪些常見的技能需求?

前言

近期因為轉換工作面試了不少公司的資料工程師

面試流程不外乎就是「自介」+「考試」+「問問題」

在問問題的環節經常被問到會不會使用某些工具

以下大概整理一下我面試 Data Engineer 最常被問到的幾項技術

閱讀更多»