現代軟體開發強調建置、釋出、執行階段的嚴格區分,以提升效率和可靠性。本文以 Sonyflake ID 生成器專案為例,說明如何使用 Codeship 和 Docker 建立 CI/CD 流程,並將應用程式佈署至 Docker Swarm。Codeship 的 codeship-services.yml 定義了 Docker 建置服務,codeship-steps.yml 規劃了準備、Go 程式建置、Docker 映像建置和釋出等步驟。codeship-build.sh 指令碼呼叫 Makefile 中的目標執行建置,而 codeship-release.sh 則負責 Docker Hub 登入和 GitHub 釋出的建立。Makefile 定義了 Go 程式編譯和 Docker 映像建置的規則,Dockerfile 則根據 Alpine Linux 建立最小化映像。同時,文章也探討瞭如何透過 Git 提交雜湊和語義化版本控制進行映像標籤管理,以及利用 github-release 工具自動產生變更日誌。
建置、釋出、執行 - 嚴格區分建置與執行階段
在現代軟體開發流程中,將建置(build)、釋出(release)和執行(run)三個階段嚴格區分是至關重要的。這種區分不僅能提高開發效率,還能確保軟體產品的品質和可靠性。本文將探討如何透過使用 Codeship 和 Docker 等工具來實作這一目標。
使用 Codeship 和 Docker 實作 CI/CD
Codeship 是一款流行的持續整合和持續佈署(CI/CD)工具,它能夠與 GitHub 等版本控制系統無縫整合,自動觸發建置、測試和佈署流程。Docker 則是一種容器化技術,能夠將應用程式及其依賴封裝成一個可移植的容器,從而實作環境的一致性和可預測性。
設定 Codeship 服務
首先,我們需要在 codeship-services.yml 檔案中定義我們的服務。這裡,我們定義了一個名為 sonyflake 的服務,它使用 Dockerfile.build 進行建置,並將原始碼掛載到容器中。
sonyflake:
build:
dockerfile: Dockerfile.build
add_docker: true
encrypted_env_file: .env.encrypted
volumes:
- ./:/go/src/app
設定 Codeship 建置步驟
接下來,我們需要在 codeship-steps.yml 檔案中定義建置步驟。這裡,我們定義了四個步驟:準備(prepare)、建置 Go 程式(build go)、建置 Docker 映像(build docker)和釋出(release)。
- type: serial
tag: master
service: sonyflake
steps:
- name: 'prepare'
command: ./codeship-build.sh prepare
- name: 'build go'
command: ./codeship-build.sh build-go
- name: 'build docker'
command: ./codeship-build.sh build-docker
- name: 'release'
command: ./codeship-release.sh
建置指令碼
codeship-build.sh 指令碼負責呼叫 Makefile 中的目標,進行實際的建置工作。
#!/bin/bash
set -e
## Get git commit ID
CI_COMMIT_ID=${CI_COMMIT_ID:-$(git rev-list HEAD --max-count=1)}
CI_COMMIT_ID_SHORT=${CI_COMMIT_ID:0:7}
## Get latest tag ID
CI_TAG_ID=$(git tag | tail -n 1)
if [ -z "${CI_TAG_ID}" ]; then
CI_TAG_ID="v0.0.0";
fi
CI_TAG_AUTO="${CI_TAG_ID}"
if [ -f "build/.date" ]; then
CI_TAG_AUTO="$(echo ${CI_TAG_ID} | awk -F'.' '{print $1 "." $2}').$(<build/.date)"
fi
make -e CI_TAG_ID=${CI_TAG_ID} \
-e CI_TAG_AUTO=${CI_TAG_AUTO} \
-e CI_COMMIT_ID=${CI_COMMIT_ID} \
-e CI_COMMIT_ID_SHORT=${CI_COMMIT_ID_SHORT} "$@"
Makefile
Makefile 中定義了建置 Go 程式、建置 Docker 映像等目標。
all:
@echo 'Usage: make <prepare|build-go|build-docker>'
build-go: build/sonyflake build/sonyflake.exe
@echo "Build finished"
build/sonyflake:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o build/sonyflake main.go
cd build && tar -zcvf sonyflake_linux_64bit.tgz sonyflake && cd ..
build/sonyflake.exe:
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o build/sonyflake.exe main.go
cd build && tar -zcvf sonyflake_windows_64bit.tgz sonyflake.exe && cd ..
build-docker:
docker build --rm -t titpetric/sonyflake --build-arg GITVERSION=${CI_COMMIT_ID} --build-arg GITTAG=${CI_TAG_AUTO} .
釋出指令碼
codeship-release.sh 指令碼負責登入 Docker Hub、建立 GitHub 釋出等工作。
#!/bin/bash
set -e
## Get git commit ID
CI_COMMIT_ID=${CI_COMMIT_ID:-$(git rev-list HEAD --max-count=1)}
CI_COMMIT_ID_SHORT=${CI_COMMIT_ID:0:7}
## Get latest tag ID
CI_TAG_ID=$(git tag | tail -n 1)
if [ -z "${CI_TAG_ID}" ]; then
CI_TAG_ID="v0.0.0";
fi
CI_TAG_AUTO="$(echo ${CI_TAG_ID} | awk -F'.' '{print $1 "." $2}').$(<build/.date)"
## Login to docker hub on release action
if [ ! -f "/root/.docker/config.json" ]; then
docker login -u $DOCKER_REGISTRY_USERNAME -p $DOCKER_REGISTRY_PASSWORD
fi
function github_release {
TAG="$1"
NAME="$2"
latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)
comparison="$latest_tag..HEAD"
if [ -z "$latest_tag" ]; then
comparison="";
fi
changelog=$(git log $comparison --oneline --no-merges)
echo "Creating release $1: $2"
github-release release \
--user titpetric \
--repo sonyflake \
--tag "$1" \
--name "$2" \
--description "$changelog"
}
詳細解析Dockerfile與映像標籤策略
在軟體開發的持續整合與持續佈署(CI/CD)流程中,Dockerfile扮演著至關重要的角色。它定義了Docker映像的建置過程,確保了開發環境與生產環境的一致性。本文將探討Dockerfile的最佳實踐以及映像標籤策略。
Dockerfile最佳實踐
使用官方基礎映像:盡量使用官方維護的基礎映像,如
alpine:3.5,以確保安全性和穩定性。最小化層數:減少Dockerfile中的指令數量可以減少映像的層數,從而減小映像大小並提高建置效率。
利用快取:合理安排指令順序,將不常變化的指令放在前面,以充分利用Docker的快取機制,加速建置過程。
安全性考量:避免以root使用者執行應用程式,使用非特權使用者以降低安全風險。
Dockerfile範例解析
以下是一個範例Dockerfile,用於建置Sonyflake專案的Docker映像:
FROM alpine:3.5
MAINTAINER Tit Petric <[email protected]>
ARG GITVERSION=development
ARG GITTAG=development
ENV GITVERSION=${GITVERSION} GITTAG=${GITTAG}
ADD ./build/sonyflake /sonyflake
ENTRYPOINT ["/sonyflake"]
內容解密:
FROM alpine:3.5:使用Alpine Linux 3.5作為基礎映像,Alpine Linux因其小巧、安全而廣受歡迎。MAINTAINER Tit Petric <[email protected]>:指定Docker映像的維護者資訊。ARG GITVERSION=development和ARG GITTAG=development:定義建置引數,分別代表Git提交版本和標籤,預設值為development。ENV GITVERSION=${GITVERSION} GITTAG=${GITTAG}:將建置引數轉換為環境變數,供容器內應用程式使用。ADD ./build/sonyflake /sonyflake:將本地已編譯的Sonyflake二進位制檔案新增到映像中的根目錄。ENTRYPOINT ["/sonyflake"]:指定容器啟動時執行的命令,即執行Sonyflake應用程式。
映像標籤策略
映像標籤是Docker映像版本管理的關鍵。透過為映像打上不同的標籤,可以方便地追蹤和管理不同版本的映像。
使用Git提交雜湊作為標籤:將Git提交雜湊值作為Docker映像的標籤,可以精確地對應到特定的程式碼提交,從而實作可重現的建置。
結合語義化版本控制:採用語義化版本控制(Semantic Versioning),可以清晰地表達映像的版本資訊,便於依賴管理和升級。
程式碼管理與變更日誌
在軟體開發過程中,變更日誌(Changelog)的管理至關重要。透過記錄每次釋出的變更內容,可以幫助開發者、使用者瞭解軟體的演進歷程。
在上述codeship-release.sh指令碼中,使用了github-release工具建立GitHub釋出,並自動生成變更日誌。這種做法不僅簡化了釋出流程,也提高了變更日誌的準確性和時效性。
function github_release {
TAG="$1"
NAME="$2"
latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)
comparison="$latest_tag..HEAD"
if [ -z "$latest_tag" ]; then
comparison="";
fi
changelog=$(git log $comparison --oneline --no-merges)
echo "Creating release $1: $2"
github-release release \
--user titpetric \
--repo sonyflake \
--tag "$1" \
--name "$2" \
--description "$changelog"
}
詳細解析github_release函式作用與邏輯
github_release函式是用於建立GitHub釋出並生成變更日誌的關鍵部分。該函式接受兩個引數:標籤(TAG)和名稱(NAME)。
取得最新標籤:透過
git describe --tagsgit rev-list –tags –max-count=1``取得最新的標籤,用於計算與當前HEAD之間的變更日誌。計算變更日誌:使用
git log命令生成從上一個標籤到當前HEAD的變更日誌,以簡潔的一行格式顯示每個提交記錄。建立GitHub釋出:呼叫
github-release工具,建立新的GitHub釋出,包含指定的標籤、名稱和變更日誌作為描述。
建置、釋出、執行 - 嚴格區分建置和執行階段
在現代化的軟體開發流程中,將建置(build)、釋出(release)和執行(run)三個階段嚴格區分是至關重要的。以下是一個示範如何將應用程式釋出到 GitHub 和 Docker Hub 的範例。
釋出到 GitHub
首先,我們定義了一個名為 github_upload 的函式,用於將檔案上傳到 GitHub Release。
function github_upload {
echo "Uploading $2 to $1"
github-release upload \
--user titpetric \
--repo sonyflake \
--tag "$1" \
--name "$(basename $2)" \
--file "$2"
}
內容解密:
github_upload函式接受兩個引數:要上傳的檔案所屬的標籤名稱,以及檔案路徑。- 使用
github-release工具進行上傳操作。 --user和--repo指定了 GitHub 使用者和倉函式庫名稱。--tag指定了 Release 的標籤。--name和--file指定了要上傳的檔案名稱和路徑。
接下來,我們建立一個新的 Release,並將建置好的檔案上傳。
github_release ${CI_TAG_AUTO} "$(date)"
FILES=$(find build -type f | grep tgz$)
for FILE in $FILES; do
github_upload ${CI_TAG_AUTO} "$FILE"
done
內容解密:
github_release用於建立新的 Release,標籤為${CI_TAG_AUTO}。- 透過
find命令查詢build目錄下所有的.tgz檔案。 - 使用迴圈將這些檔案逐一上傳到 GitHub Release。
釋出到 Docker Hub
最後,我們將 Docker 映像標記並推播到 Docker Hub。
docker tag titpetric/sonyflake titpetric/sonyflake:${CI_COMMIT_ID_SHORT}
docker push titpetric/sonyflake:${CI_COMMIT_ID_SHORT}
docker push titpetric/sonyflake:latest
內容解密:
- 使用
docker tag將映像標記為特定的提交 ID 和latest。 - 使用
docker push將標記後的映像推播到 Docker Hub。 - 這樣可以確保
latest標籤始終指向最新的映像,而特定提交 ID 的標籤可以用於鎖定特定版本。
檢查容器中的環境資訊
在容器中,我們可以檢查環境變數來確認當前執行的版本。
# docker run --rm --entrypoint=env titpetric/sonyflake | grep GIT
GITVERSION=e22a185d64838209ee3f62faf322fa0e8add3f70
GITTAG=v1.0.170402-2022
內容解密:
- 使用
docker run命令以env為入口點執行容器,輸出環境變數。 - 透過
grep命令篩選出包含GIT的環境變數。 - 這樣可以確認容器中執行的版本資訊。
VI. 程式 - 以一個或多個無狀態程式執行應用程式
12 Factor App 的原則之一是應用程式應該是無狀態的,不應在本地儲存狀態。這意味著應用程式不應依賴本地快取或記憶體中的值。
資料快取
資料快取是一種儲存形式,需要應用程式載入/儲存經常使用或計算昂貴的資料集。這影響了啟動速度,因為應用程式需要時間來載入資料。
Share-nothing 架構
在可擴充套件和分散式系統中,Share-nothing(SN)架構是一種常見的模式。這意味著應用程式之間不分享記憶體或磁碟儲存,也不用依靠程式間通訊來交換資料。
建構高用性的無狀態應用程式:Redis 與 Docker Swarm 的實踐
在現代的雲端原生應用程式開發中,遵循 12 Factor App 的原則已經成為業界的最佳實踐。其中,將應用程式設計為無狀態(stateless)是實作高用性和可擴充套件性的關鍵。本文將探討如何在 Docker Swarm 環境中使用 Redis 作為後端服務,並確保應用程式的無狀態特性。
Redis 在 Docker Swarm 中的佈署挑戰
Redis 是一個流行的記憶體資料函式庫,常被用作快取或訊息佇列。然而,在 Docker Swarm 環境中佈署 Redis 面臨著一些挑戰。首先,Redis 的資料持久化需求與無狀態應用的原則相矛盾。其次,在 Swarm 模式下,Redis 例項可能會跨多個主機執行,這增加了網路連線的不確定性。
將 Redis 轉化為符合 12 Factor App 的後端服務
為了使 Redis 更符合 12 Factor App 的原則,可以採取以下措施:
- 關閉磁碟持久化:僅將資料儲存在 RAM 中,以避免資料持久化的複雜性。
- 資料分片:將資料分散到多個 Redis 例項中,以提高容錯能力。當一個例項故障時,只會損失部分資料。
- 使用快取模式:如果 Redis 僅用作快取,那麼即使例項被銷毀,也不會影回應用程式的核心功能。
在 Docker Swarm 中實作 Redis 高用性
在 Docker Swarm 環境中,可以透過以下方式提高 Redis 的可用性:
- 使用 Docker Swarm 的服務發現機制:Swarm 會自動將請求路由到正在執行的 Redis 例項。
- 連線到本地 Docker 主機的 Redis 例項:這樣可以減少網路跳躍次數,提高連線的可靠性。
- 使用浮動 IP:組態 IP 容錯移轉機制,確保即使某個主機離線,也能保持對 Redis 服務的連線。
使用 Twemproxy 提高 Redis 的可擴充套件性
Twemproxy(也稱為 nutcracker)是一個代理服務,可以將請求分發到多個 Redis 例項,並實作資料分片。這樣可以更有效地利用可用 RAM,並提高系統的整體效能。
設定 Twemproxy 的好處
- 提供與 Redis 相同的介面,易於整合。
- 自動將資料分片到多個 Redis 例項,提高容錯能力和效能。
無狀態應用的實踐:Sonyflake ID 生成器
Sonyflake 是 Twitter Snowflake 的一個實作,用於生成唯一的 64 位元 ID。它結合了時間戳、序列號和機器 ID,能夠在分散式環境中高效生成唯一 ID。
Sonyflake 的特點
- 無狀態:每個例項可以獨立生成 ID,無需協調。
- 高效能:單個例項每秒可生成約 25,600 個 ID。
- 唯一性:透過結合時間戳、序列號和機器 ID,確保生成的 ID 唯一。
在 Docker Swarm 中佈署 Sonyflake
將 Sonyflake 佈署為 Docker Swarm 服務,可以利用 Swarm 的負載平衡和容錯移轉機制,確保服務的高用性。
佈署步驟
- 建構 Sonyflake 的 Docker 映像。
- 將服務佈署到 Docker Swarm,並暴露必要的埠(例如埠 80)。
- 利用 Swarm 的內建負載平衡,將請求分發到多個 Sonyflake 例項。