🐁

いつ go build したサーバーなのか外から確認したいときってありますよね?

2023/03/02に公開

はじめに

今動いているサーバーがいつ作成されたものなのか外から確認したいときってありますよね?
たぶんあるはずです。
なかったとしてもなんとなくビルドしたタイミングを外から確認できたら便利なときが来る気がするので作成方法を調べてみました。

go build で version を含める

こちらの方の記事を参考したら速攻で作成できました。ありがとうございます。

https://devlights.hatenablog.com/entry/2021/03/02/110912

私の方でもこちらの情報を元にサーバーを起動するコードを書いてみます。
てっとり早く作りたかったので実装は以下のようになります。

package main

import (
	"fmt"
	"log"
	"net/http"
)

var version string

func Version(w http.ResponseWriter, r *http.Request) {
	_, _ = w.Write([]byte(version))
	log.Println(r.URL.Path)
}

func main() {
	http.HandleFunc("/version", Version)
	fmt.Printf("Server Version: %s\n", version)
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
}

起動時と/versionへのアクセス時にbuild時に含めた値を参照するようにしています。

ローカル環境で起動して動作確認してみます。go run main.goでは確認できなかったので以下のコマンドをMakefileに記述しています。

export
VERSION := local

.PHONY: build
build:
	@go build -ldflags "-X main.version=local" -o main

.PHONY: run
run:
	@make build
	@./main
make run
Server Version: local
2023/0x/0x 00:00:00 /version
curl http://localhost:8080/version
local

起動ログおよびcurlによる取得にて環境変数で設定したversion値localが設定されていることを確認できました。

ko に設定する

私はGoアプリをビルドするときkoを使っています。

koについては以下の記事を参考していただけると幸いです。

https://ymotongpoo.hatenablog.com/entry/2021/12/22/090000

koでビルドしたコンテナにも任意のversion値を設定したいので調整していきます。
koのコンテナビルドには.ko.yamlファイルを用意し設定を記載する必要があります。

builds:
  - id: go-build-include-version
    dir: .
    main: ./
    env:
      - GOOS=linux
      - GOARCH=amd64
      - CGO_ENABLED=0
    ldflags:
      - -s -w
      - -X main.version=local

こちらのファイルを用意して以下のコマンドにてローカル環境で動作確認をしてみます。
起動コマンドは以下となります。(Makefileだとうまく動かせなかったので直接実行)

docker run --rm -p 8080:8080 $(ko publish --local .)
2023/0x/0x 00:00:00 Using base distroless.dev/static:latest@sha256:eae79cd3ef653a6233fa1f87d3643cb3ff279eebf78fd8ff83d1a44255a06628 for gobuildincludeversion
2023/0x/0x 00:00:00 Using build config go-build-include-version for gobuildincludeversion
2023/0x/0x 00:00:00 Building gobuildincludeversion for linux/amd64
2023/0x/0x 00:00:00 Loading ko.local/gobuildincludeversion-31abc168f0c3cbc7d0b2bce8e9fea564:7082117446b97c197618deadea1665d9f1acd1e06cf710f41cdadc9194def205
2023/0x/0x 00:00:00 Loaded ko.local/gobuildincludeversion-31abc168f0c3cbc7d0b2bce8e9fea564:7082117446b97c197618deadea1665d9f1acd1e06cf710f41cdadc9194def205
2023/0x/0x 00:00:00 Adding tag latest
2023/0x/0x 00:00:00 Added tag latest
Server Version: local

koを用いてもversion値localを含めてコンテナをビルド・起動できることが確認できました。

動的にversionを指定する

koを用いてもversion値を含めることが確認できましたが、いちいち.ko.yamlを編集・コミットしていくのは手間です。
そこで動的に.ko.yamlを作成することを考えます。

私の持ち手で使えそうなツールとしてenvsubstを知っていたので利用します。

builds:
  - id: go-build-include-version
    dir: .
    main: ./
    env:
      - GOOS=linux
      - GOARCH=amd64
      - CGO_ENABLED=0
    ldflags:
      - -s -w
      - -X main.version=${VERSION}

動的に指定したい値を${VERSION}としファイル名を.ko.yaml.templateとして保存します。
そして以下のコマンドをMakefileに記載し実行します。

export
VERSION := local

.PHONY: ko
ko:
	@envsubst '$$VERSION' < .ko.yaml.template > .ko.yaml
make ko

結果として以下のような.ko.yamlが出力されます。

builds:
  - id: go-build-include-version
    dir: .
    main: ./
    env:
      - GOOS=linux
      - GOARCH=amd64
      - CGO_ENABLED=0
    ldflags:
      - -s -w
      - -X main.version=local

これで任意の値を.ko.yamlに設定することができました!

動作確認をCIで行う

より実践的な使い方でGitHubActionsにて動的に設定をしてみようと思います。
最終的な成果物はこのようになりました。

name: version
run-name: ${{ github.ref_name }} by @${{ github.actor }} [${{ github.workflow }}]
on:
  workflow_dispatch:
jobs:
  version:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      # 含めるバージョンを環境変数に設定する
      # 今回は最新のコミットハッシュを設定する
      - name: Create version
        run: |
          version=$(git show --format='%H' --no-patch)
          echo "VERSION=${version}" >> $GITHUB_ENV
      # envsubstを使うためのインストールコマンド
      - name: Install gettext
        run: sudo apt-get install gettext
      - name: Set up go
        uses: actions/setup-go@v3
        with:
          go-version-file: go.mod
      # ツールのバージョン管理ツール
      # なくても大丈夫です。
      - name: Setup aqua
        uses: aquaproj/aqua-installer@v2.0.2
        with:
          aqua_version: v1.33.0
      # aquaを用いてkoをインストールします。
      # go install github.com/google/ko@latest でも問題ないです。
      - name: Install ko
        run: ${AQUA_ROOT_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua}/bin/aqua install -t ko
      # .ko.yamlファイルを作成します。
      - name: Create .ko.yaml
        env:
          VERSION: ${{ env.VERSION }}
        run: envsubst '$$VERSION' < .ko.yaml.template > .ko.yaml
      # koを用いてコンテナビルドおよび起動時にversionが指定されているか確認します。
      - name: Build ko container
        run: |
          docker run --rm -p 8080:8080 $(ko publish --local .) &
          echo PID=$! >> $GITHUB_ENV
          sleep 120
      # 念の為。
      - name: Shutdown
        if: always()
        run: kill ${{ env.PID }}

こちらのCIを実行して以下のログが出力されました。

Run docker run --rm -p 8080:8080 $(ko publish --local .) &
  docker run --rm -p 8080:8080 $(ko publish --local .) &
  echo PID=$! >> $GITHUB_ENV
  sleep 120
  shell: /usr/bin/bash -e {0}
  env:
    VERSION: 87bada15034ea4b1e9f23e35ba3d632db56f268c
time="2023-0x-0xT00:00:00Z" level=info msg="download and unarchive the package" aqua_version=1.33.0 env=linux/amd64 exe_name=ko exe_path=/home/runner/.local/share/aquaproj-aqua/pkgs/github_release/github.com/ko-build/ko/v0.12.0/ko_0.12.0_Linux_x86_64.tar.gz/ko package=ko-build/ko package_name=ko-build/ko package_version=v0.12.0 program=aqua registry=standard
2023/0x/0x 00:00:00 Using base distroless.dev/static:latest@sha256:eae79cd3ef653a6233fa1f87d3643cb3ff279eebf78fd8ff83d1a44255a06628 for gobuildincludeversion
2023/0x/0x 00:00:00 Using build config go-build-include-version for gobuildincludeversion
2023/0x/0x 00:00:00 Building gobuildincludeversion for linux/amd64
2023/0x/0x 00:00:00 Loading ko.local/gobuildincludeversion-31abc168f0c3cbc7d0b2bce8e9fea564:64800cc9ddfa45f0e9e69e13ce22a45ce2336d09f51b8238e891f4c53cc587ca
2023/0x/0x 00:00:00 Loaded ko.local/gobuildincludeversion-31abc168f0c3cbc7d0b2bce8e9fea564:64800cc9ddfa45f0e9e69e13ce22a45ce2336d09f51b8238e891f4c53cc587ca
2023/0x/0x 00:00:00 Adding tag latest
2023/0x/0x 00:00:00 Added tag latest
Server Version: 87bada15034ea4b1e9f23e35ba3d632db56f268c

無事サーバーにversion値87bada15034ea4b1e9f23e35ba3d632db56f268cが設定されて起動できていることが確認できました!
本格的に使う場合はGitHubActionsに以下のようなjobを追加してビルドしたコンテナをデプロイしてください。

      - name: Docker login
        run: ko login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }} index.docker.io
      - name: Build and push container
        run: |
          KO_DOCKER_REPO=${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPOSITORY }} \
          SOURCE_DATE_EPOCH=$(date +%s) \
          ko build --sbom=none --bare --tags=${{ env.VERSION }},latest ./ --platform=linux/amd64

環境・ツールバージョン

❯ go version
go version go1.19.6 darwin/arm64

golangci-lintが通らず1.20.1に上げられない...

❯ aqua version
aqua version 1.33.0
❯ ko version
v0.12.0

おわりに

今回作成したものは以下のリポジトリに置いておきます。

https://github.com/takokun778/go-build-include-version

無事version値を返すGoサーバーコンテナを作成することができました。
ただ、今回はmain指定であったので他のファイルおよびパッケージの場合はどうするのかもう少し確認する必要がありそうです。

Discussion