Edited at

Go Modulesに対応しつつ、Dockerコンテナの中でプライベートリポジトリを go get する


TL;DR

Dockerコンテナ x module aware mode x プライベートリポジトリ の組み合わせで go get できないことにお困りの場合は、 gitconfig 書き換えや ssh agent forwarding などをしなくてもGOPROXY を使うとモジュール利用者が何も設定することなくコンテナ内で go get できるようになるよという話


1. はじめに

弊社では長らくGoのモジュール管理に glide を使っていましたが、Go ModulesGo1.13 で正式にサポートされることもあり乗り換えようとしました。

社内には多くの内製ライブラリがあり、それらをプライベートリポジトリで管理していました。 glide で管理していた際は、 glide.yaml に以下のように x-oauth-basic をつけて対応していました。

- package: github.com/knocknote/private_repo

repo: https://${basic_auth_token}:x-oauth-basic@github.com/knocknote/private_repo.git

また、開発にはDockerを利用しており、Goの実行環境は Dockerコンテナ上にあります。

これらの前提の上で、Go1.12 x GO111MODULE=on な環境に移行することが目的です。


しかし、 go get はプライベートリポジトリに対応していないため、普通にやると gitconfig をいじって x-oauth-basic をつけてアクセスするようにしたり、 go get を利用するときに内部的に SSH でアクセスできるように変更する必要があります。

ですが、モジュールの依存解決をしたいがためにこれらの設定をチームメンバ全員に強いるのも嫌ですし、何も設定することなく Docker コンテナ内で go get できるようにしたいところです。

そこで、GOPROXY に目をつけました。 GOPROXY=http://localhost:6000 な具合に GOPROXY を設定しておくと、 go get する際に http://localhost:6000 を通してモジュールをダウンロードしにいくような挙動になります。これによって、例えば http://localhost:6000 で立ち上げているサーバーからモジュールを取得しに行くときには必ず x-oauth-basic でトークンをつけてアクセスしにいくみたいなことができるようになる可能性があります。

今回、この GOPROXY を利用した方法でやりたいことが実現できたため、そのアプローチの紹介をしたいと思います。

また、前段として今まで似たようなことをしたいときに利用したであろう解決パターンについても触れたいと思います。


2. よくある(と思っている)解決方法


2.1 gitconfig 書き換えパターン


2.1.1 gitconfig 書き換えで x-oauth-basic を常につけるようにする

git config --global url."https://${token}:x-oauth-basic@github.com/".insteadOf "https://github.com/"


2.1.2 gitconfig 書き換えで https でアクセスする代わりに SSH を利用する

.gitconfig に以下を記述

[url "git@github.com:"]

insteadOf = https://github.com/


2.2 あらかじめ git clone パターン

はじめからプライベートリポジトリを git clone しておいて、それらを git-submodule なりで紐づけておくようなやり方です。


弊社では glide でモジュール管理をしていた際、バージョン固定などの観点から vendor 配下をバージョン管理するようにし、プライベートリポジトリもそこに含めていました。また、vendorsubmodule としてアプリケーションコードのリポジトリにマウントしておき、ライブラリを利用する側としては git submodule update してやれば簡単に更新できる状態で運用していました。

module aware mode では vendor 廃止の方向に動いているため、この方法をとりずらくなっていますが、

例えば go.modreplace directive に

replace (

github.com/knocknote/private_repo => ./vendor/github.com/knocknote/private_repo
)

と書けば同様のことができます。


3. GOPROXY を使ったやり方

GOPROXY を利用する場合に便利なプロジェクトとして goproxyio/goproxy があります。これはまさにやりたいことそのままで、 go get をプロキシできるようになるのですが、プライベートリポジトリには対応していません。

そこで、 簡単なパッチを当てたバージョンを knocknote/goproxy に作成しました。やっていることは -basicAuthToken オプションを足して、指定されている場合は goproxy 内部で(というよりかは modfetch の内部で) git ls-remote https://github.com/knocknote/private-repo を実行するときに、 git ls-remote https://${basic_auth_token}:x-oauth-basic@github.com/knocknote/private-repo を代わりに行うようにするというものです。

こちらは ビルド済みのものを DockerHub で公開しているため ( https://hub.docker.com/r/knocknote/goproxy )、以下のように docker-compose.yml に書くだけでプライベートリポジトリが取得できるようになるはずです。

services:

module_proxy:
image: knocknote/goproxy:latest
ports:
- "8002:8002"
entrypoint: /goproxy -cacheDir=/go -basicAuthToken=xxxxx -listen=0.0.0.0:8002

app:
environment:
GO111MODULE: "on"
GOPROXY: "http://module_proxy:8002"
(略)

この設定を一度書いてしまえば、使う側としては何も設定することなく、プライベートリポジトリだとしても go get でとってくることができます! :)


4. まとめ

GOPROXY を使ったプライベートリポジトリの取得方法を紹介してみました。同じような悩みをかかえていた方の参考になれば嬉しいです。まだ昨日今日でガッと作った仕組みですが、Go Modules に移行できる兆しが見えてきた気がします。

また、もっと良い方法でやっているよとか、これだとこのケースに対応するのがしんどいのでこうしたほうが良いといった情報があれば教えていただけると嬉しいです!