序章

アプリケーションを本番環境にデプロイする場合、バージョン情報やその他のメタデータを使用してバイナリをビルドすると、ビルドを長期にわたって追跡するのに役立つ識別情報が追加されるため、監視、ロギング、およびデバッグのプロセスが改善されます。 このバージョン情報には、ビルド時間、バイナリをビルドするマシンまたはユーザー、ビルド対象のバージョン管理システム(VCS)コミットIDなどの非常に動的なデータが含まれることがよくあります。 これらの値は絶えず変化するため、このデータをソースコードに直接コーディングし、新しいビルドが発生する前に変更すると、面倒でエラーが発生しやすくなります。ソースファイルは移動でき、変数/定数は開発中にファイルを切り替える可能性があります。ビルドプロセスを中断します。

Goでこれを解決する1つの方法は、 -ldflags とともに go build ソースコードを変更することなく、ビルド時に動的情報をバイナリに挿入するコマンド。 この旗では、 ld linker の略で、コンパイルされたソースコードのさまざまな部分を最終的なバイナリにリンクするプログラムです。 ldflags、は、リンカーフラグを表します。 これは、基になるGoツールチェーンリンカー cmd / link にフラグを渡すため、これと呼ばれます。これにより、ビルド時にコマンドラインからインポートされたパッケージの値を変更できます。

このチュートリアルでは、 -ldflags バージョン情報を画面に出力するサンプルアプリケーションを使用して、ビルド時に変数の値を変更し、独自の動的情報をバイナリに導入します。

前提条件

この記事の例に従うには、次のものが必要です。

サンプルアプリケーションの構築

使用する前に ldflags 動的データを導入するには、最初に情報を挿入するアプリケーションが必要です。 このステップでは、このアプリケーションを作成します。このアプリケーションは、この段階では静的なバージョン情報のみを出力します。 それでは、そのアプリケーションを作成しましょう。

あなたの中で src ディレクトリ、アプリケーションにちなんで名付けられたディレクトリを作成します。 このチュートリアルでは、アプリケーション名を使用します app:

  1. mkdir app

作業ディレクトリを次のフォルダに変更します。

  1. cd app

次に、選択したテキストエディタを使用して、プログラムのエントリポイントを作成します。 main.go:

  1. nano main.go

次に、次のコンテンツを追加して、アプリケーションにバージョン情報を印刷させます。

app / main.go
package main

import (
	"fmt"
)

var Version = "development"

func main() {
	fmt.Println("Version:\t", Version)
}

の内部 main() 関数、あなたは宣言しました Version 変数、文字列を出力します Version:、続いてタブ文字、 \t、次に宣言された変数。

この時点で、変数 Version と定義されている development、このアプリのデフォルトバージョンになります。 後で、この値をセマンティックバージョニング形式に従って配置された公式バージョン番号に変更します。

ファイルを保存して終了します。 これが完了したら、アプリケーションをビルドして実行し、正しいバージョンが出力されることを確認します。

  1. go build
  2. ./app

次の出力が表示されます。

  1. Output
    Version: development

これで、デフォルトのバージョン情報を出力するアプリケーションができましたが、ビルド時に現在のバージョン情報を渡す方法はまだありません。 次のステップでは、 -ldflagsgo build この問題を解決するために。

使用する ldflagsgo build

前に述べたように、 ldflags リンカーフラグの略で、Goツールチェーンの基になるリンカーにフラグを渡すために使用されます。 これは、次の構文に従って機能します。

  1. go build -ldflags="-flag"

この例では、 flag 根底にある go tool link の一部として実行されるコマンド go build. このコマンドは、に渡されるコンテンツを二重引用符で囲みます。 ldflags その中の文字、またはコマンドラインが私たちが望むもの以外のものとして解釈する可能性のある文字を壊さないようにするため。 ここから、多くの異なるリンクフラグを渡すことができます。 このチュートリアルでは、 -X リンク時に変数に情報を書き込むためのフラグと、それに続く変数への packageパスとその新しい値:

  1. go build -ldflags="-X 'package_path.variable_name=new_value'"

引用符の中には、 -X オプションと、変更する変数とその新しい値を表すキーと値のペア。 The . 文字はパッケージパスと変数名を区切り、キーと値のペアで文字が壊れないように一重引用符が使用されます。

交換するには Version サンプルアプリケーションの変数の場合、最後のコマンドブロックの構文を使用して、新しい値を渡し、新しいバイナリを作成します。

  1. go build -ldflags="-X 'main.Version=v1.0.0'"

このコマンドでは、 main のパッケージパスです Version この変数は main.go ファイル。 Version 書き込み先の変数であり、 v1.0.0 新しい値です。

使用するには ldflags、変更する値は存在し、タイプのパッケージレベル変数である必要があります string. この変数は、エクスポートまたは非エクスポートのいずれかです。 値を const または、関数呼び出しの結果によってその値が設定されます。 幸運、 Version これらの要件のすべてに適合します:それはすでに変数として宣言されています main.go ファイル、および現在の値(development)および目的の値(v1.0.0)は両方とも文字列です。

あなたの新しい app バイナリがビルドされたら、アプリケーションを実行します。

  1. ./app

次の出力が表示されます。

  1. Output
    Version: v1.0.0

使用する -ldflags、あなたは首尾よく変更しました Version からの変数 developmentv1.0.0.

これで、 string ビルド時の単純なアプリケーション内の変数。 使用する ldflags、コマンドラインのみを使用して、バージョンの詳細やライセンス情報などを、配布可能なバイナリに埋め込むことができます。

この例では、変更した変数は main プログラム、パス名を決定する難しさを軽減します。 ただし、これらの変数へのパスを見つけるのがより複雑な場合があります。 次のステップでは、サブパッケージの変数に値を書き込んで、より複雑なパッケージパスを決定するための最良の方法を示します。

サブパッケージ変数のターゲティング

前のセクションでは、 Version アプリケーションの最上位パッケージにあった変数。 しかし、これは常に当てはまるわけではありません。 多くの場合、これらの変数を別のパッケージに配置する方が実用的です。 main インポート可能なパッケージではありません。 サンプルアプリケーションでこれをシミュレートするには、新しいサブパッケージを作成します。 app/build、バイナリがビルドされた時刻とビルドコマンドを発行したユーザーの名前に関する情報が格納されます。

新しいサブパッケージを追加するには、最初にプロジェクトに新しいディレクトリを追加します。 build:

  1. mkdir -p build

次に、という名前の新しいファイルを作成します build.go 新しい変数を保持するには:

  1. nano build/build.go

テキストエディタで、次の新しい変数を追加します TimeUser:

app / build / build.go
package build

var Time string

var User string

The Time 変数は、バイナリが作成された時刻の文字列表現を保持します。 The User 変数は、バイナリを作成したユーザーの名前を保持します。 これらの2つの変数には常に値があるため、これらの変数をデフォルト値で初期化する必要はありません。 Version.

ファイルを保存して終了します。

次に、開く main.go これらの変数をアプリケーションに追加するには:

  1. nano main.go

の中に main.go、次の強調表示された行を追加します。

main.go
package main

import (
	"app/build"
	"fmt"
)

var Version = "development"

func main() {
	fmt.Println("Version:\t", Version)
	fmt.Println("build.Time:\t", build.Time)
	fmt.Println("build.User:\t", build.User)
}

これらの行では、最初に app/build パッケージ、次に印刷 build.Timebuild.User あなたが印刷したのと同じ方法で Version.

ファイルを保存して、テキストエディタを終了します。

次に、これらの変数をターゲットにするには ldflags、インポートパスを使用できます app/build に続く .User また .Time、インポートパスをすでに知っているので。 ただし、変数へのパスが明確でない、より複雑な状況をシミュレートするために、代わりに nm Goツールチェーンのコマンド。

The go tool nm コマンドは、特定の実行可能ファイル、オブジェクトファイル、またはアーカイブに関連するシンボルを出力します。 この場合、シンボルは、定義またはインポートされた変数や関数など、コード内のオブジェクトを参照します。 でシンボルテーブルを生成することによって nm と使用 grep 変数を検索するには、そのパスに関する情報をすばやく見つけることができます。

注: nm パッケージ名にASCII以外の文字が含まれている場合、または " また % それはツール自体の制限であるため、文字。

このコマンドを使用するには、最初に次のバイナリをビルドします app:

  1. go build

app 構築されている、ポイント nm ツールを使って出力を検索します。

  1. go tool nm ./app | grep app

実行すると、 nm ツールは大量のデータを出力します。 このため、使用した前のコマンド | 出力をにパイプします grep コマンドは、トップレベルの用語を検索しました app タイトルに。

次のような出力が表示されます。

Output
55d2c0 D app/build.Time 55d2d0 D app/build.User 4069a0 T runtime.appendIntStr 462580 T strconv.appendEscapedRune . . .

この場合、結果セットの最初の2行には、探している2つの変数へのパスが含まれています。 app/build.Timeapp/build.User.

パスがわかったので、今度は変更して、アプリケーションを再度ビルドします Version, User、 と Time ビルド時に。 これを行うには、複数を渡します -X フラグ -ldflags:

  1. go build -v -ldflags="-X 'main.Version=v1.0.0' -X 'app/build.User=$(id -u -n)' -X 'app/build.Time=$(date)'"

ここであなたは合格しました id -u -n 現在のユーザーを一覧表示するBashコマンド、および date 現在の日付を一覧表示するコマンド。

実行可能ファイルがビルドされたら、プログラムを実行します。

  1. ./app

このコマンドをUnixシステムで実行すると、次のような出力が生成されます。

Output
Version: v1.0.0 build.Time: Fri Oct 4 19:49:19 UTC 2019 build.User: sammy

これで、問題を解決するときに本番環境で重要な支援を提供できるバージョン管理とビルド情報を含むバイナリができました。

結論

このチュートリアルでは、正しく適用した場合に、どのように ldflags ビルド時に貴重な情報をバイナリに注入するための強力なツールになります。 このようにして、ソースコードに変更を加えることなく、機能フラグ、環境情報、バージョン情報などを制御できます。 追加することにより ldflags 現在のビルドワークフローに合わせて、Goの自己完結型のバイナリ配布形式のメリットを最大化できます。

Goプログラミング言語の詳細については、Goシリーズのコーディング方法をご覧ください。 バージョン管理のその他のソリューションをお探しの場合は、Gitの使用方法リファレンスガイドをお試しください。