序章
アプリケーションを本番環境にデプロイする場合、バージョン情報やその他のメタデータを使用してバイナリをビルドすると、ビルドを長期にわたって追跡するのに役立つ識別情報が追加されるため、監視、ロギング、およびデバッグのプロセスが改善されます。 このバージョン情報には、ビルド時間、バイナリをビルドするマシンまたはユーザー、ビルド対象のバージョン管理システム(VCS)コミットIDなどの非常に動的なデータが含まれることがよくあります。 これらの値は絶えず変化するため、このデータをソースコードに直接コーディングし、新しいビルドが発生する前に変更すると、面倒でエラーが発生しやすくなります。ソースファイルは移動でき、変数/定数は開発中にファイルを切り替える可能性があります。ビルドプロセスを中断します。
Goでこれを解決する1つの方法は、 -ldflags
とともに go build
ソースコードを変更することなく、ビルド時に動的情報をバイナリに挿入するコマンド。 この旗では、 ld
linker の略で、コンパイルされたソースコードのさまざまな部分を最終的なバイナリにリンクするプログラムです。 ldflags
、は、リンカーフラグを表します。 これは、基になるGoツールチェーンリンカー cmd / link にフラグを渡すため、これと呼ばれます。これにより、ビルド時にコマンドラインからインポートされたパッケージの値を変更できます。
このチュートリアルでは、 -ldflags
バージョン情報を画面に出力するサンプルアプリケーションを使用して、ビルド時に変数の値を変更し、独自の動的情報をバイナリに導入します。
前提条件
この記事の例に従うには、次のものが必要です。
- Goのインストール方法とローカルプログラミング環境のセットアップに従ってセットアップされたGoワークスペース。
サンプルアプリケーションの構築
使用する前に ldflags
動的データを導入するには、最初に情報を挿入するアプリケーションが必要です。 このステップでは、このアプリケーションを作成します。このアプリケーションは、この段階では静的なバージョン情報のみを出力します。 それでは、そのアプリケーションを作成しましょう。
あなたの中で src
ディレクトリ、アプリケーションにちなんで名付けられたディレクトリを作成します。 このチュートリアルでは、アプリケーション名を使用します app
:
- mkdir app
作業ディレクトリを次のフォルダに変更します。
- cd app
次に、選択したテキストエディタを使用して、プログラムのエントリポイントを作成します。 main.go
:
- nano main.go
次に、次のコンテンツを追加して、アプリケーションにバージョン情報を印刷させます。
package main
import (
"fmt"
)
var Version = "development"
func main() {
fmt.Println("Version:\t", Version)
}
の内部 main()
関数、あなたは宣言しました Version
変数、文字列を出力します Version:
、続いてタブ文字、 \t
、次に宣言された変数。
この時点で、変数 Version
と定義されている development
、このアプリのデフォルトバージョンになります。 後で、この値をセマンティックバージョニング形式に従って配置された公式バージョン番号に変更します。
ファイルを保存して終了します。 これが完了したら、アプリケーションをビルドして実行し、正しいバージョンが出力されることを確認します。
- go build
- ./app
次の出力が表示されます。
- OutputVersion: development
これで、デフォルトのバージョン情報を出力するアプリケーションができましたが、ビルド時に現在のバージョン情報を渡す方法はまだありません。 次のステップでは、 -ldflags
と go build
この問題を解決するために。
使用する ldflags
と go build
前に述べたように、 ldflags
リンカーフラグの略で、Goツールチェーンの基になるリンカーにフラグを渡すために使用されます。 これは、次の構文に従って機能します。
- go build -ldflags="-flag"
この例では、 flag
根底にある go tool link
の一部として実行されるコマンド go build
. このコマンドは、に渡されるコンテンツを二重引用符で囲みます。 ldflags
その中の文字、またはコマンドラインが私たちが望むもの以外のものとして解釈する可能性のある文字を壊さないようにするため。 ここから、多くの異なるリンクフラグを渡すことができます。 このチュートリアルでは、 -X
リンク時に変数に情報を書き込むためのフラグと、それに続く変数への packageパスとその新しい値:
- go build -ldflags="-X 'package_path.variable_name=new_value'"
引用符の中には、 -X
オプションと、変更する変数とその新しい値を表すキーと値のペア。 The .
文字はパッケージパスと変数名を区切り、キーと値のペアで文字が壊れないように一重引用符が使用されます。
交換するには Version
サンプルアプリケーションの変数の場合、最後のコマンドブロックの構文を使用して、新しい値を渡し、新しいバイナリを作成します。
- 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
バイナリがビルドされたら、アプリケーションを実行します。
- ./app
次の出力が表示されます。
- OutputVersion: v1.0.0
使用する -ldflags
、あなたは首尾よく変更しました Version
からの変数 development
に v1.0.0
.
これで、 string
ビルド時の単純なアプリケーション内の変数。 使用する ldflags
、コマンドラインのみを使用して、バージョンの詳細やライセンス情報などを、配布可能なバイナリに埋め込むことができます。
この例では、変更した変数は main
プログラム、パス名を決定する難しさを軽減します。 ただし、これらの変数へのパスを見つけるのがより複雑な場合があります。 次のステップでは、サブパッケージの変数に値を書き込んで、より複雑なパッケージパスを決定するための最良の方法を示します。
サブパッケージ変数のターゲティング
前のセクションでは、 Version
アプリケーションの最上位パッケージにあった変数。 しかし、これは常に当てはまるわけではありません。 多くの場合、これらの変数を別のパッケージに配置する方が実用的です。 main
インポート可能なパッケージではありません。 サンプルアプリケーションでこれをシミュレートするには、新しいサブパッケージを作成します。 app/build
、バイナリがビルドされた時刻とビルドコマンドを発行したユーザーの名前に関する情報が格納されます。
新しいサブパッケージを追加するには、最初にプロジェクトに新しいディレクトリを追加します。 build
:
- mkdir -p build
次に、という名前の新しいファイルを作成します build.go
新しい変数を保持するには:
- nano build/build.go
テキストエディタで、次の新しい変数を追加します Time
と User
:
package build
var Time string
var User string
The Time
変数は、バイナリが作成された時刻の文字列表現を保持します。 The User
変数は、バイナリを作成したユーザーの名前を保持します。 これらの2つの変数には常に値があるため、これらの変数をデフォルト値で初期化する必要はありません。 Version
.
ファイルを保存して終了します。
次に、開く main.go
これらの変数をアプリケーションに追加するには:
- nano 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.Time
と build.User
あなたが印刷したのと同じ方法で Version
.
ファイルを保存して、テキストエディタを終了します。
次に、これらの変数をターゲットにするには ldflags
、インポートパスを使用できます app/build
に続く .User
また .Time
、インポートパスをすでに知っているので。 ただし、変数へのパスが明確でない、より複雑な状況をシミュレートするために、代わりに nm
Goツールチェーンのコマンド。
The go tool nm
コマンドは、特定の実行可能ファイル、オブジェクトファイル、またはアーカイブに関連するシンボルを出力します。 この場合、シンボルは、定義またはインポートされた変数や関数など、コード内のオブジェクトを参照します。 でシンボルテーブルを生成することによって nm
と使用 grep
変数を検索するには、そのパスに関する情報をすばやく見つけることができます。
注: nm
パッケージ名にASCII以外の文字が含まれている場合、または "
また %
それはツール自体の制限であるため、文字。
このコマンドを使用するには、最初に次のバイナリをビルドします app
:
- go build
今 app
構築されている、ポイント nm
ツールを使って出力を検索します。
- 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.Time
と app/build.User
.
パスがわかったので、今度は変更して、アプリケーションを再度ビルドします Version
, User
、 と Time
ビルド時に。 これを行うには、複数を渡します -X
フラグ -ldflags
:
- 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
現在の日付を一覧表示するコマンド。
実行可能ファイルがビルドされたら、プログラムを実行します。
- ./app
このコマンドをUnixシステムで実行すると、次のような出力が生成されます。
OutputVersion: v1.0.0
build.Time: Fri Oct 4 19:49:19 UTC 2019
build.User: sammy
これで、問題を解決するときに本番環境で重要な支援を提供できるバージョン管理とビルド情報を含むバイナリができました。
結論
このチュートリアルでは、正しく適用した場合に、どのように ldflags
ビルド時に貴重な情報をバイナリに注入するための強力なツールになります。 このようにして、ソースコードに変更を加えることなく、機能フラグ、環境情報、バージョン情報などを制御できます。 追加することにより ldflags
現在のビルドワークフローに合わせて、Goの自己完結型のバイナリ配布形式のメリットを最大化できます。
Goプログラミング言語の詳細については、Goシリーズのコーディング方法をご覧ください。 バージョン管理のその他のソリューションをお探しの場合は、Gitの使用方法リファレンスガイドをお試しください。