隨著雲原生時代的到來,建構符合 12 Factor 原則的應用程式已成為確保應用程式可擴充套件性、可維護性和可移植性的重要手段。本文將結合 Docker 的容器化能力和 Go 語言的效能優勢,探討如何實踐 12 Factor 的各個導向,包含程式碼函式倉管理、依賴管理、外部組態、後端服務、建置發布與執行、程式管理、連線埠繫結、平行處理、可棄性、開發與生產環境一致性、日誌處理以及管理程式。透過實際的程式碼片段和操作步驟,演示如何將這些原則應用於實際的開發流程中,協助開發者構建更具彈性且易於管理的雲原生應用程式。
建構符合 12 Factor 原則的 Docker 與 Go 應用程式
在現代軟體開發中,12 Factor 應用程式已成為設計和佈署可擴充套件、健壯且易於維護的應用程式的重要指導原則。本文將探討如何使用 Docker 和 Go 語言來建立符合 12 Factor 原則的應用程式。
為什麼選擇 Docker 和 Go?
Docker 提供了一個輕量級且可移植的容器化平台,使得應用程式的佈署和管理變得更加簡單和高效。Go 語言則以其簡潔、高效和併發特性而聞名,非常適合構建現代化的雲原生應用程式。
12 Factor 原則概述
12 Factor 原則是一套指導開發者構建現代化應用程式的最佳實踐,包括:
- Codebase:一個程式碼函式庫對應多個佈署環境
- Dependencies:明確宣告和隔離依賴
- Config:將組態儲存在環境變數中
- Backing services:將後端服務視為附加資源
- Build, release, run:嚴格區分構建、發布和執行階段
- Processes:將應用程式作為一個或多個無狀態程式執行
- Port binding:透過埠繫結來暴露服務
- Concurrency:透過程式模型來擴充套件
- Disposability:最大化健壯性,透過快速啟動和優雅關閉
- Dev/prod parity:保持開發、測試和生產環境的一致性
- Logs:將日誌視為事件流
- Admin processes:將管理任務作為一次性程式執行
使用 Docker 和 Go 實作 12 Factor 原則
1. Codebase
使用 Gogs 建立一個 Git 倉函式庫來管理我們的程式碼函式庫。
docker run -d --name gogs -p 10080:3000 gogs/gogs
2. Dependencies
使用 Go Modules 來管理依賴。
go mod init myapp
go mod tidy
3. Config
使用環境變數來儲存組態。
import (
"log"
"os"
)
func main() {
dbURL := os.Getenv("DATABASE_URL")
if dbURL == "" {
log.Fatal("DATABASE_URL is not set")
}
// ...
}
#### 內容解密:
這段程式碼展示瞭如何使用 os.Getenv 函式從環境變數中讀取 DATABASE_URL 的值。如果該變數未設定,程式將輸出錯誤訊息並終止。這種方式使得組態可以在不同的環境中靈活變更,而無需修改程式碼。
4. Backing services
將資料函式庫視為後端服務。
version: '3'
services:
db:
image: postgres
environment:
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mypassword
#### 內容解密:
這段 Docker Compose 組態檔案定義了一個名為 db 的服務,使用官方的 Postgres 映象,並設定了環境變數來組態資料函式庫使用者和密碼。這樣,我們可以輕鬆地在本地或生產環境中啟動和管理資料函式庫服務。
介紹
身為一位擁有近二十年程式設計經驗的開發者,我曾經在90年代開始最佳化程式碼,在2000年代發現了PHP,並陸續參與了多個大型專案的開發,同時也接觸了其他程式語言家族,如Java、Node.js,最終選擇了Go語言。我曾經為自己的內容管理產品建立了多個API,並擔任多個產品的首席開發人員,這些產品已被銷售到多個國家。我還為斯洛維尼亞國家電視台和廣播電台網站(RTV Slovenia)編寫了一個專業的API框架,該框架同時也作為軟體開發工具包使用。此外,我也曾在多個本地PHP使用者群組活動和會議上擔任演講者,並撰寫了《API Foundations in Go》一書。我的部落格(scene-si.org)每兩個月會發布新文章,值得一讀。
本文適用物件
本文適用於所有以撰寫應用程式為生的開發者。我將涵蓋軟體開發的廣泛主題,試圖建立良好的開發實踐,同時參考12 Factor App宣言。本文將涵蓋以下主題:
- I. 程式碼函式庫 - 在版本控制中追蹤一個程式碼函式庫,多次佈署
- II. 相依性 - 明確宣告和隔離相依性
- III. 組態 - 將組態儲存在環境中
- IV. 後端服務 - 將後端服務視為附加資源
- V. 建置、發布、執行 - 嚴格區分建置和執行階段
- VI. 程式 - 將應用程式執行為一個或多個無狀態程式
- VII. 連線埠繫結 - 透過連線埠繫結匯出服務
- VIII. 平行性 - 透過程式模型擴充套件
- IX. 可棄性 - 透過快速啟動和優雅關閉最大化強健性
- X. 開發/生產環境一致性 - 盡可能保持開發、預發布和生產環境的一致性
- XI. 日誌 - 將日誌視為事件流
- XII. 管理程式 - 將管理任務作為一次性程式執行
涵蓋這些概念將使您對12 Factor App的設計和試圖解決的問題有一個全面的瞭解。本文試圖提供一套最佳實踐來引導您,並透過個別章節提供實際範例。
如何學習本文
在閱讀本文的過程中,我將提供多個範例來說明開發API時的常見做法。這些範例已釋出在GitHub上。您可以跟隨本文中的範例,或者單獨閱讀每個章節,以瞭解該章節的知識。這些範例是獨立的,但通常建立在前幾章的基礎上。在使用本文時,請務必遵循「需求」一節的說明。
Linux和Docker
本文的範例依賴於Linux主機上的最新docker-engine安裝。
自有硬體
如果您有自己的硬體,建議的組態是:
- 2個CPU核心
- 2GB RAM
- 128GB磁碟(SSD)
雲端快速入門
如果您沒有自己的硬體,可以在Digital Ocean上建立虛擬伺服器。您可以使用DigitalOcean的推薦連結獲得$10的信用額度,同時也可以幫助我減少一些主機費用。在註冊後,建立一個具有執行Docker引擎的Linux例項非常簡單,只需點選幾下即可。
首先,請點選頁面頂部標頭上的綠色按鈕「Create Droplet」,然後在開啟的頁面上導航到「One-click apps」,並從列表中選擇「Docker」。
請注意,執行Docker可能會佔用大量磁碟空間。某些Docker映像檔可能會「重達」1 GB或更多。因此,建議選擇至少具有30GB磁碟空間的例項。
建立Digital Ocean Droplet
Digital Ocean提供了CLI工具doctl,它是一個Go程式,可以與其API介面互動,以允許您列出正在執行的Droplet、建立新的Droplet、關閉它們等等。如果您不需要24/7執行的例項,您可以使用API根據您的需求啟動和關閉它。這樣可以節省約一半的成本。
下載並安裝doctl,然後執行doctl auth init以對Digital Ocean API進行身份驗證。您可以從儀錶板的頂部產生令牌。
# 安裝doctl
wget https://github.com/digitalocean/doctl/releases/download/vX.Y.Z/doctl-X.Y.Z-linux-amd64.tar.gz
tar xf doctl-X.Y.Z-linux-amd64.tar.gz
sudo mv doctl /usr/local/bin/
# 初始化doctl
doctl auth init
內容解密:
上述命令用於安裝和初始化doctl。首先,我們從GitHub下載doctl的壓縮檔,然後解壓縮並將其移動到/usr/local/bin/目錄下。接著,執行doctl auth init命令以使用產生的令牌對Digital Ocean API進行身份驗證。這樣,我們就可以使用doctl來管理Digital Ocean上的資源。
程式碼範例
package main
import (
"fmt"
"log"
"github.com/digitalocean/godo"
)
func main() {
// 建立新的godo客戶端
client := godo.NewClient(nil)
// 列出所有Droplet
droplets, _, err := client.Droplets.List(ctx, nil)
if err != nil {
log.Fatal(err)
}
for _, droplet := range droplets {
fmt.Println(droplet.Name)
}
}
內容解密:
上述Go程式碼範例演示瞭如何使用godo函式庫來列出Digital Ocean上的所有Droplet。首先,我們建立了一個新的godo客戶端,然後使用Droplets.List方法來檢索Droplet列表。如果發生錯誤,我們將記錄錯誤並離開。最後,我們遍歷Droplet列表並列印每個Droplet的名稱。這個範例展示瞭如何使用godo函式庫與Digital Ocean API進行互動。
使用doctl進行DigitalOcean驗證與資源管理
在開始使用DigitalOcean的API之前,需要先進行驗證。以下指令用於初始化doctl的驗證:
doctl auth init
執行後,需要輸入DigitalOcean的access token,例如:
DigitalOcean access token: 24df63816084cb8270fbe03a7c98341a
驗證成功後,會顯示:
Validating token: OK
此後,便可自由地列出映像檔(images)並透過API建立新的droplet。
列出包含Docker的映像檔
要列出包含Docker的映像檔,可以執行以下指令:
doctl compute image list | grep docker
範例輸出可能如下:
23219707 Docker 17.03.0-ce on 14.04 docker
24232340 Docker 17.04.0-ce on 16.04 docker-16-04
內容解密:
doctl compute image list用於列出所有可用的映像檔。grep docker用於篩選出包含“docker”關鍵字的映像檔。
建立與刪除Droplet的指令碼
為了方便建立和刪除droplet,我們可以建立兩個指令碼:create.sh和destroy.sh。
建立Droplet(create.sh)
#!/bin/bash
CHECK=$(doctl compute droplet list asx --format ID --no-header | wc -l)
if [ "$CHECK" == "0" ]; then
echo "Creating ASX droplet"
doctl compute droplet create asx -v \
--image docker-16-04 \
--size 2gb \
--region ams3 \
--ssh-keys $(./ssh-key.sh)
else
echo "Droplet ASX already running"
fi
內容解密:
doctl compute droplet list asx --format ID --no-header列出名為“asx”的droplet的ID。wc -l計算輸出的行數,用於檢查droplet是否存在。- 若droplet不存在,則使用
doctl compute droplet create建立一個新的droplet。 --image docker-16-04指定使用包含Docker的映像檔。--size 2gb指定droplet的大小。--region ams3指定droplet的地區。--ssh-keys $(./ssh-key.sh)指定用於連線droplet的SSH金鑰。
刪除Droplet(destroy.sh)
#!/bin/bash
CHECK=$(doctl compute droplet list asx --format ID --no-header | wc -l)
if [ "$CHECK" == "0" ]; then
echo "Droplet ASX not started"
else
echo "Deleting droplet asx"
doctl compute droplet delete asx -f -v
fi
內容解密:
- 邏輯與
create.sh類別似,但用於檢查droplet是否存在,若存在則刪除。 doctl compute droplet delete asx -f -v強制刪除名為“asx”的droplet。
連線至Droplet
要連線至新建立的droplet,可以使用以下指令取得其公網IP:
doctl compute droplet list asx --format PublicIPv4 --no-header
然後使用SSH連線:
ssh $(doctl compute droplet list asx --format PublicIPv4 --no-header)
內容解密:
doctl compute droplet list asx --format PublicIPv4 --no-header取得droplet的公網IP。- 使用SSH連線到該IP。
Docker網路組態
在Docker中,微服務之間的通訊是透過建立自定義網路來實作的。
建立自定義橋接網路
docker network create -d bridge --subnet 172.25.0.0/24 party
內容解密:
docker network create用於建立新的網路。-d bridge指定網路驅動為橋接模式。--subnet 172.25.0.0/24指定網路的子網。party是網路的名稱。
MySQL資料函式庫例項的快速啟動
可以使用Docker快速啟動MySQL例項。
啟動MySQL例項(mysql.sh)
#!/bin/bash
NAME="mysql"
APP_DIR=$(dirname $(readlink -f $0))
mkdir -p ${APP_DIR}/data/$NAME
docker stop $NAME
docker rm $NAME
DOCKERFILE="titpetric/percona-xtrabackup"
docker run --restart=always \
-h $NAME \
--name $NAME \
--net=party \
-v ${APP_DIR}/data/$NAME/data:/var/lib/mysql \
-e MYSQL_ALLOW_EMPTY_PASSWORD="yes" \
-d $DOCKERFILE
內容解密:
- 設定MySQL例項的名稱和資料儲存路徑。
- 使用
docker run啟動MySQL容器,設定主機名、容器名稱、網路、資料卷掛載和環境變數。 --net=party將容器加入到之前建立的“party”網路中。
版本控制系統(VCS)與程式碼管理
使用版本控制系統(如Git)來管理程式碼對於協作開發和版本追蹤至關重要。
使用Git進行版本控制
建議使用GitHub、Bitbucket或自託管的GitLab、Gogs等服務來託管Git儲存函式庫。確保定期備份並有還原程式。
為公司專案選擇合適的Git託管服務
對於公司專案,Bitbucket是不錯的選擇,提供免費的私有倉函式庫和合理的價格計畫。
為社群專案選擇合適的Git託管服務
對於社群專案,GitHub是首選,提供豐富的工具和龐大的社群支援。
管理多個應用程式或微服務的程式碼函式庫
建議每個專案或微服務使用單獨的倉函式庫以保持簡單。若多個應用程式共用程式碼,應將共用程式碼分離到獨立的套件中。