The Go Blog

2019年のGoモジュール

Russ Cox
2018年12月19日

なんて一年だ!

2018年はGoエコシステムにとって素晴らしい年でした。特にパッケージ管理に力を入れていました。2月には、パッケージ管理をGoツールチェーンに直接統合する方法についてコミュニティ全体で議論を開始し、8月には、Go 1.11でGoモジュールと呼ばれるその機能の最初の概略実装を提供しました。Goモジュールへの移行は、Go 1以来、Goエコシステムにとって最も広範囲にわたる変更となるでしょう。エコシステム全体(コード、ユーザー、ツールなど)をGOPATHからモジュールに変換するには、多くの異なる分野での作業が必要になります。モジュールシステムは、Goエコシステムに優れた認証とビルド速度を提供することにも役立つでしょう。

この投稿は、Goチームが2019年にモジュールに関して計画していることのプレビューです。

リリース

2018年8月にリリースされたGo 1.11では、モジュールの暫定的なサポートが導入されました。現時点では、モジュールサポートは従来のGOPATHベースのメカニズムと並行して維持されています。goコマンドは、GOPATH/src以外のディレクトリツリーで実行され、そのルートにgo.modファイルがある場合、デフォルトでモジュールモードになります。この設定は、一時的な環境変数$GO111MODULEonまたはoffに設定することで上書きできます。デフォルトの動作はautoモードです。Goコミュニティ全体でモジュールの採用が大幅に進んでおり、モジュールを改善するための多くの有用な提案やバグ報告もいただいています。

2019年2月に予定されているGo 1.12では、モジュールサポートが改良されますが、デフォルトでは引き続きautoモードのままです。多くのバグ修正やその他の小さな改善に加えて、Go 1.12で最も重要な変更は、go run x.gogo get rsc.io/2fa@v1.1.0のようなコマンドが、明示的なgo.modファイルなしでGO111MODULE=onモードで動作できるようになったことです。

2019年8月に予定されているGo 1.13では、モジュールモードをデフォルトで有効にし(つまり、デフォルトをautoからonに変更し)、GOPATHモードを非推奨にすることを目指しています。そのためには、より優れたツールサポートと、オープンソースモジュールエコシステムへのより良いサポートに取り組んでいます。

ツールとIDE統合

GOPATHが導入されてからの8年間で、GoソースコードがGOPATHに保存されていることを前提とするツールが驚くほど多く作成されてきました。モジュールへの移行には、その前提を持つすべてのコードを更新する必要があります。私たちは、特定のターゲットのGoソースコードに関する情報を見つけてロードする操作を抽象化する新しいパッケージ、golang.org/x/tools/go/packagesを設計しました。この新しいパッケージは、GOPATHとモジュールモードの両方に自動的に適応し、Bazelが使用するようなツール固有のコードレイアウトにも拡張可能です。Goコミュニティ全体でツール作成者と協力して、彼らがツールでgolang.org/x/tools/go/packagesを採用するのを支援しています。

この取り組みの一環として、gocode、godef、go-outlineのような様々なソースコードクエリツールを、コマンドラインから使用でき、最新のIDEで使われる言語サーバープロトコルもサポートする単一のツールに統合する作業も行っています。

モジュールへの移行とパッケージロードの変更は、Goプログラム解析にも大きな変更を促しました。モジュールをサポートするためにgo vetを再設計する一環として、Goプログラムのインクリメンタル解析のための汎用フレームワークを導入しました。このフレームワークでは、アナライザーは一度に1つのパッケージに対して呼び出されます。このフレームワークでは、あるパッケージの解析結果が、そのパッケージをインポートする他のパッケージの解析で利用可能になります。例えば、go vetによるlogパッケージの解析では、log.Printffmt.Printfラッパーであるという事実を特定し、記録します。これにより、go vetは、log.Printfを呼び出す他のパッケージでprintfスタイルのフォーマット文字列をチェックできます。このフレームワークは、開発者が早期にバグを発見し、コードをよりよく理解するのに役立つ、多くの新しい高度なプログラム解析ツールを可能にするはずです。

モジュールインデックス

go getの元の設計の最も重要な部分の1つは、それが**分散型**であったことです。当時も今も、誰でも自分のコードを任意のサーバーに公開できるべきだと信じています。これは、PerlのCPAN、JavaのMaven、NodeのNPMのような集中型レジストリとは対照的です。go getインポートスペースの先頭にドメイン名を置くことで、既存の分散システムを再利用し、どの名前を誰が使えるかを決めるという問題を改めて解決する必要がなくなりました。また、企業はプライベートサーバー上のコードをパブリックサーバーからのコードと一緒にインポートできるようになりました。Goモジュールに移行するにあたっても、この分散化を維持することが重要です。

Goの依存関係の分散化には多くの利点がありましたが、いくつかの重大な欠点ももたらしました。1つ目は、公開されているすべてのGoパッケージを見つけるのが難しすぎることです。パッケージに関する情報を提供したいすべてのサイトは、独自のクロールを行うか、ユーザーが特定のパッケージについて尋ねるまで待ってからフェッチする必要があります。

私たちは、Goエコシステムに投入されるパッケージの公開ログを提供する新しいサービス、Go Module Indexに取り組んでいます。godoc.orgやgoreportcard.comのようなサイトは、新しいパッケージを見つけるためのコードをそれぞれが独立して実装する代わりに、このログを監視して新しいエントリを見つけることができるようになります。また、このサービスを使ってシンプルなクエリでパッケージを検索できるようにし、goimportsがまだローカルシステムにダウンロードされていないパッケージのインポートを追加できるようにしたいと考えています。

モジュール認証

今日、go getは、コードをダウンロードするために正しいサーバーと通信していることを確認するために、接続レベルの認証(HTTPSまたはSSH)に依存しています。コード自体には追加のチェックがないため、HTTPSまたはSSHメカニズムが何らかの方法で侵害された場合、中間者攻撃の可能性が残ります。分散化は、ビルドのためのコードが多くの異なるサーバーからフェッチされることを意味し、これは、ビルドが多くのシステムが正しいコードを提供することに依存することを意味します。

Goモジュールの設計は、各モジュールにgo.sumファイルを格納することでコード認証を改善します。このファイルは、モジュールの各依存関係の予想されるファイルツリーの暗号化ハッシュをリストします。モジュールを使用する場合、goコマンドはgo.sumを使用して、ビルドで使用する前に依存関係が予想されるバージョンとビット単位で同一であることを検証します。しかし、go.sumファイルには、そのモジュールが使用する特定の依存関係のハッシュしかリストされていません。新しい依存関係を追加したり、go get -uで依存関係を更新したりする場合、go.sumに対応するエントリがないため、ダウンロードされたビットの直接認証は行われません。

公開されているモジュールについては、モジュールインデックスログを追跡し、新しいモジュールをダウンロードし、「モジュールMのバージョンVはファイルツリーハッシュHを持つ」という形式のステートメントを暗号的に署名する**公証人**と呼ぶサービスを運用する予定です。公証人サービスは、これらの公証されたハッシュすべてを照会可能な、Certificate Transparencyスタイルの改ざん防止ログに公開し、誰でも公証人が正しく機能していることを検証できるようにします。このログは、go getが依存関係を追加または更新する際にモジュールを認証するために使用できる、公開されたグローバルなgo.sumファイルとして機能します。

Go 1.13から、goコマンドがgo.sumにまだない公開されているモジュールの公証されたハッシュをチェックすることを目指しています。

モジュールミラー

分散型のgo getは複数のオリジンサーバーからコードをフェッチするため、コードのフェッチは最も遅く、最も信頼性の低いサーバーと同じくらい高速で信頼性が高いにすぎません。モジュール以前に利用できた唯一の防御策は、依存関係を自分のリポジトリにベンダー化することでした。ベンダー化は引き続きサポートされますが、すでに使用しているモジュールだけでなく、すべてのモジュールで機能し、依存関係をそれを使用するすべてのリポジトリに複製する必要がないソリューションを望んでいます。

Goモジュールの設計では、モジュールプロキシという考え方が導入されています。これは、goコマンドがオリジンサーバーの代わりにモジュールを要求するサーバーです。重要なプロキシの一種は**モジュールミラー**であり、オリジンサーバーからモジュールをフェッチし、将来のリクエストのためにそれらをキャッシュすることでモジュールへのリクエストに応答します。適切に運営されているミラーは、一部のオリジンサーバーがダウンしても、高速で信頼性が高いはずです。2019年には、公開されているモジュール向けのミラーサービスを開始する予定です。GoCenterやAthensなどの他のプロジェクトもミラーサービスを計画しています。(企業は独自の内部ミラーを運用するための複数の選択肢を持つと予想していますが、この投稿ではパブリックミラーに焦点を当てています。)

ミラーの潜在的な問題の1つは、それらが正確に中間者サーバーであるため、攻撃の自然な標的となることです。Go開発者は、ミラーがオリジンサーバーと同じビットを提供していることをある程度保証する必要があります。前のセクションで説明した公証プロセスは、まさにこの懸念に対処しており、オリジンサーバーを使用したダウンロードと同様に、ミラーを使用したダウンロードにも適用されます。ミラー自体は信頼される必要はありません。

Go 1.13から、Googleが運営するモジュールミラーがデフォルトでgoコマンドで使用できるようになることを目指しています。代替ミラーを使用するか、まったくミラーを使用しないかの設定は簡単です。

モジュールの発見

最後に、モジュールインデックスによってgodoc.orgのようなサイトを構築しやすくなると先ほど述べました。2019年の私たちの作業の一部は、利用可能なモジュールを発見し、特定のモジュールに依存するかどうかを決定する必要がある開発者にとって、godoc.orgをより有用にするための大規模な改修です。

全体像

この図は、Goモジュールのソースコードがこの投稿のデザインを通じてどのように移動するかを示しています。

以前は、Goソースコードのすべてのコンシューマー(goコマンドやgodoc.orgなどのサイト)は、各コードホストから直接コードをフェッチしていました。今では、高速で信頼性の高いミラーからキャッシュされたコードをフェッチしながら、ダウンロードされたビットが正しいことを認証できます。また、インデックスサービスにより、ミラー、godoc.org、およびその他の同様のサイトが、毎日Goエコシステムに追加される素晴らしい新しいコードすべてに簡単に追いつくことができます。

私たちは2019年のGoモジュールの未来に興奮しており、皆さんもそうであることを願っています。明けましておめでとうございます!

次の記事:Go 1.12がリリースされました
前の記事:Go 2、いざ行かん!
ブログインデックス