返回文章列表

Docker Codeship 建置釋出執行流程

本文闡述如何利用 Codeship 和 Docker 建立 CI/CD 流程,並以 Sonyflake ID 生成器為例,示範如何在 Docker Swarm 環境中佈署無狀態應用程式,同時探討如何結合 Redis 和 Twemproxy 提升後端服務的可用性和擴充套件性,並詳細解析 Dockerfile

Web 開發 DevOps

現代軟體開發強調建置、釋出、執行階段的嚴格區分,以提升效率和可靠性。本文以 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最佳實踐
  1. 使用官方基礎映像:盡量使用官方維護的基礎映像,如alpine:3.5,以確保安全性和穩定性。

  2. 最小化層數:減少Dockerfile中的指令數量可以減少映像的層數,從而減小映像大小並提高建置效率。

  3. 利用快取:合理安排指令順序,將不常變化的指令放在前面,以充分利用Docker的快取機制,加速建置過程。

  4. 安全性考量:避免以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"]

內容解密:

  1. FROM alpine:3.5:使用Alpine Linux 3.5作為基礎映像,Alpine Linux因其小巧、安全而廣受歡迎。

  2. MAINTAINER Tit Petric <[email protected]>:指定Docker映像的維護者資訊。

  3. ARG GITVERSION=developmentARG GITTAG=development:定義建置引數,分別代表Git提交版本和標籤,預設值為development

  4. ENV GITVERSION=${GITVERSION} GITTAG=${GITTAG}:將建置引數轉換為環境變數,供容器內應用程式使用。

  5. ADD ./build/sonyflake /sonyflake:將本地已編譯的Sonyflake二進位制檔案新增到映像中的根目錄。

  6. 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)。

  1. 取得最新標籤:透過git describe --tags git rev-list –tags –max-count=1``取得最新的標籤,用於計算與當前HEAD之間的變更日誌。

  2. 計算變更日誌:使用git log命令生成從上一個標籤到當前HEAD的變更日誌,以簡潔的一行格式顯示每個提交記錄。

  3. 建立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 的原則,可以採取以下措施:

  1. 關閉磁碟持久化:僅將資料儲存在 RAM 中,以避免資料持久化的複雜性。
  2. 資料分片:將資料分散到多個 Redis 例項中,以提高容錯能力。當一個例項故障時,只會損失部分資料。
  3. 使用快取模式:如果 Redis 僅用作快取,那麼即使例項被銷毀,也不會影回應用程式的核心功能。

在 Docker Swarm 中實作 Redis 高用性

在 Docker Swarm 環境中,可以透過以下方式提高 Redis 的可用性:

  1. 使用 Docker Swarm 的服務發現機制:Swarm 會自動將請求路由到正在執行的 Redis 例項。
  2. 連線到本地 Docker 主機的 Redis 例項:這樣可以減少網路跳躍次數,提高連線的可靠性。
  3. 使用浮動 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 的負載平衡和容錯移轉機制,確保服務的高用性。

佈署步驟

  1. 建構 Sonyflake 的 Docker 映像。
  2. 將服務佈署到 Docker Swarm,並暴露必要的埠(例如埠 80)。
  3. 利用 Swarm 的內建負載平衡,將請求分發到多個 Sonyflake 例項。