Goブログ

2019年のGoモジュール

Russ Cox
2018年12月19日

素晴らしい1年でした!

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

この記事は、2019年のモジュールに関するGoチームの計画のプレビューです。

リリース

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つのパッケージに対して呼び出されます。このフレームワークでは、1つのパッケージの分析で、最初のパッケージをインポートする他のパッケージの分析で利用できるファクトを書き出すことができます。たとえば、logパッケージgo vetの分析では、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モジュールインデックスに取り組んでいます。 godoc.orgやgoreportcard.comのようなサイトは、新しいパッケージを見つけるためのコードをそれぞれ個別に実装する代わりに、このログを監視して新しいエントリを確認できるようになります。また、このサービスでは、単純なクエリを使用してパッケージを検索できるようにし、まだローカルシステムにダウンロードされていないパッケージのインポートをgoimportsが追加できるようにしたいと考えています。

モジュール認証

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

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

公開されているモジュールの場合、モジュールインデックスログに従い、新しいモジュールをダウンロードし、「バージョンVのモジュールMにはファイルツリーハッシュHがある」という形式のステートメントに暗号署名する、公証人と呼ばれるサービスを実行する予定です。公証人サービスは、これらのすべての公証されたハッシュを、クエリ可能で、証明書の透明性スタイルの改ざん防止ログに公開するため、誰でも公証人が正しく動作していることを検証できます。このログは、go getが依存関係を追加または更新するときにモジュールを認証するために使用できる、公開されたグローバルなgo.sumファイルとして機能します。

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

モジュールミラー

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

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

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

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

モジュール検出

最後に、先ほどモジュールインデックスによってgodoc.orgのようなサイトの構築が容易になると述べました。2019年の私たちの作業の一部として、利用可能なモジュールを発見し、特定のモジュールに依存するかどうかを判断する必要がある開発者にとって、より役立つようにgodoc.orgを大幅に刷新します。

全体像

この図は、この投稿で説明した設計を通じてモジュールのソースコードがどのように移動するかを示しています。

以前は、Goのソースコードのすべての利用者(goコマンドやgodoc.orgのようなサイト)が、各コードホストから直接コードを取得していました。現在では、ダウンロードされたビットが正しいことを認証しながら、高速で信頼性の高いミラーからキャッシュされたコードを取得できます。また、インデックスサービスにより、ミラー、godoc.org、その他の同様のサイトが、Goエコシステムに日々追加されている素晴らしい新しいコードすべてに容易に対応できます。

2019年のGoモジュールの将来に興奮しています。皆様もそうであってほしいと願っています。新年おめでとうございます!

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