gcloud docker
は Docker 18.03 以降をサポートしないらしい。
概要
gcloud auth configure-docker
してやると、今までgcloud docker -- image pull
してたところをdocker image pull
でよくなる。CoreOS で Google Cloud SDK を Docker から利用しているとややこしい。
gcloud auth print-access-token
でトークンを取ってdocker login
するしかなさそう。(追記 : 2018/5/28)
docker-credential-gcloud
を自前で書いてPATH
の通った/opt/bin
に用意すると全てが綺麗に解決する。
詳細
4月くらいには気付いていたんだけど時間がなくて直せてなかったものを直した。
いつものように
gcloud docker -- image pull gcr.io/my-project/my_image:latest
などとしていたところ
WARNING: `gcloud docker` will not be supported for Docker client versions above 18.03.
Please use `gcloud auth configure-docker` to configure `docker` to use `gcloud` as a credential helper, then use `docker` as you would for non-GCR registries, e.g. `docker pull gcr.io/project-id/my-image`.
Add `--verbosity=error` to silence this warning, e.g. `gcloud docker --verbosity=error -- pull gcr.io/project-id/my-image`.
See: https://cloud.google.com/container-registry/docs/support/deprecation-notices#gcloud-docker
というエラーメッセージが出た。エラーメッセージが出るだけでまだ普通に動きはする。
察するに gcloud docker
は deprecated となるのだろう。
gcloud auth configure-docker
しろと書いてあるので素直にしてやると、
The following settings will be added to your Docker config file
located at [/Users/dnpp/.docker/config.json]:
{
"credHelpers": {
"gcr.io": "gcloud",
"us.gcr.io": "gcloud",
"eu.gcr.io": "gcloud",
"asia.gcr.io": "gcloud",
"staging-k8s.gcr.io": "gcloud"
}
}
Do you want to continue (Y/n)?
と出て、 Y
してやれば ~/.docker/config.json
に上記が追記され、素直に docker
コマンドを使えるようになる。
これにて macOS 環境で gcloud docker
を使うために無効にせざるを得なかった Docker for Mac の Securely store Docker logins in macOS keychain
オプションを有効に出来るようになった。
めでたしめでたし。
しかし話はそんなに簡単ではない。
上記の話は、
- macOS High Sierra 10.13.4
- Docker for Mac
- Google Cloud SDK を
curl https://sdk.cloud.google.com | bash
で直接インストール
の場合の話である。もうちょい詳細な環境は、
$ docker --version
Docker version 18.05.0-ce-rc1, build 33f00ce
$ which gcloud
/Users/dnpp/google-cloud-sdk/bin/gcloud
$ gcloud --version
Google Cloud SDK 200.0.0
beta 2017.09.15
bq 2.0.33
core 2018.04.30
gcloud
gsutil 4.31
こんな感じ。
手元にある MacBook Pro Retina 13-inch Late 2013 と Mac mini Late 2012 で 2 台とも同じ環境だったが、これは言われた通りにやれば上手く動く。大きな問題はない。
試してはいないが、恐らく gcloud
が実体として PATH
の通った場所に実行できる形で存在すれば他の環境でも問題はないものと思われる。
本題はここからである。
CoreOS で Google Cloud SDK を Docker から利用しているとややこしい。
Google Cloud SDK は Docker image でも配布されている。
docker image pull google/cloud-sdk:latest
そして CoreOS ではデフォルトでこちらを使うようになっている。
GCE で CoreOS のインスタンスを立ち上げると初期状態で gcloud
コマンドが使えて結構驚くのだが、実体は alias
のようだ。
alias gcloud="(docker images google/cloud-sdk || docker pull google/cloud-sdk) > /dev/null;docker run -ti --rm --net=host -v $HOME/.config:/root/.config -v /var/run/docker.sock:/var/run/docker.sock google/cloud-sdk gcloud"
実装自体は、昔調べた 2017/9 頃の話だと coreos/ignition の internal/oem/oem.go にされていた。
2018/5 にこの記事を書くにあたって調べ直したところ、 coreos/coreos-overlay の
coreos-base/oem-gce/files/files/google-cloud-sdk.sh の方に移動した様だ。
この間に、
-
-v $HOME/.config:/.config
から/root
が抜けていたバグ。 -
-v /var/run/docker.sock:/var/run/doker.sock
にあったdoker
という typo -
run
に--rm
指定がないのでdocker ps
で見たときにゴミだらけになってた問題。
などの問題が解決されており、着実に進歩していた模様。
2017/9 頃の話なのだが、 gcloud docker -- image pull
は doker
という typo を直して /var/run/docker.sock
をマウントするだけでは動かず、
/usr/bin/docker
/run/metadata
/run/torcx
をマウントするように追記する必要があった。
詳細は Ignition's "gcloud" alias doesn't work with torcx-ified docker #2112 にある。
しかし 2018/5 にこの記事を書くにあたって再び検証してみたところ /var/run/docker.sock
のみのマウントで gcloud docker -- image pull
が出来るようになっていたので、細かいところで色々な改修が行われているのだなぁという気持ちになった。
どうして動くようになったのか、余裕があれば後で追っておきたい。
本題
- CoreOS から
gcloud
を何も考えずに使えるのはalias
になっているからであり、コマンドではない。- Docker の credential helper として
"gcloud"
と設定したところで、シェルのalias
なので永遠に見付けることができない。
- Docker の credential helper として
-
alias
で設定されているgcloud
には-v $HOME/.docker:/root/.docker
の指定がない。- つまり
gcloud auth configure-docker
の結果書き出される~/.docker/config.json
がホスト側に永続化されない。
- つまり
といった問題があり、このままでは全く使えない。
失敗例
結論から言うとこれではダメだったが、とりあえず PATH
の通ってる /opt/bin
にラッパーシェルスクリプトを置けば良いのではと思ってやってみた。
CoreOS でそのような用途で使っても良いとされているのが /opt
以下で、 /opt/bin
にはデフォルトで PATH
が通っている。
/opt/bin/gcloud
#!/usr/bin/env bash
docker container run -it --rm --net=host \
-v $HOME/.config:/root/.config \
-v $HOME/.docker:/root/.docker \
-v /var/run/docker.sock:/var/run/docker.sock \
google/cloud-sdk gcloud "$@"
シェルスクリプトを書くときに $@
とか $*
とかそれらを ""
で囲ったときの違いが怖くて毎回ググってしまうが、ラッパーとして使うには多分これで良いはず。
この状態で gcloud auth configure-docker
してやると ~/.docker/config.json
に credHelpers
が追記される。
もっとも gcloud auth configure-docker
するだけなら ~/.docker/config.json
に書き込み権限があるだけで良いので、
-v $HOME/.config:/root/.config
-v $HOME/.docker:/root/.docker
とマウントして、
docker container run -it --rm \
-v $HOME/.config:/root/.config \
-v $HOME/.docker:/root/.docker \
google/cloud-sdk gcloud auth configure-docker
これでも良さそうではあった。
とりあえず
-
gcloud
ラッパースクリプトが存在している - それを
"credHelpers"
に指定している
という状況で docker image pull
をしてみたが、これが失敗する。
$ docker image pull gcr.io/my-project/my_image:latest
Error response from daemon: unauthorized:
You don't have the needed permissions to perform this operation, and you may have invalid credentials.
To authenticate your request, follow the steps in:
https://cloud.google.com/container-registry/docs/advanced-authentication
途中試行錯誤しつつ、
-
gcloud docker
と打つたびに変更される~/.docker/config.json
のアクセス権が正しいか確認したり、 - ラッパースクリプトから環境変数
$HOME
が読めるか確認したり、 -
"credHelpers"
に直接sudo docker container run ...
などと書いてみたり、 - そもそも
"credHelpers"
に指定したgcloud
が呼ばれているのか確認したのだが、
そもそも呼ばれてないことが分かった。
マジかよ。僕が勝手に思ってた "credHelpers"
の挙動と違うじゃん。
試行錯誤してる中で見付けたのだが、 ~/.docker/config.json
への書き込み権限がある状態で gcloud docker
と打つと、サブコマンドなど一切打ってなくても ~/.docker/config.json
に "auths"
が勝手に追記され、ついでにアクセス権も root:root
の 600
になり、アクセス権を正しくしてやると、それ以降は docker image pull
に成功するということも分かり、とにかく完全につらい気持ちになった。
成功例 その1 (諦めて docker login
する)
エラーメッセージにも書いてあるので、素直に 公式ドキュメント を読みにいくと docker login
する方法が書いてあった。
gcloud auth application-default print-access-token
これでトークンが取れると書いてあるが以下のエラーが出る。
ERROR: (gcloud.auth.application-default.print-access-token)
The Application Default Credentials are not available. They are available if running in Google Compute Engine.
Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials.
See https://developers.google.com/accounts/docs/application-default-credentials for more information.
色々見ていると application-default
は別に無くても良さそうなので
gcloud auth print-access-token
とすると取れた。
実際には docker container run --rm -v $HOME/.config:/root/.config google/cloud-sdk
という prefix が付いている。
GCLOUD_ACCESS_TOKEN=$(docker container run --rm \
-v $HOME/.config:/root/.config \
google/cloud-sdk \
gcloud auth print-access-token)
$ docker login -u oauth2accesstoken -p "$GCLOUD_ACCESS_TOKEN" 'https://gcr.io'
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /home/core/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Are you sure you want to proceed? [y/N]
-p
でパスワード指定をすると「やめろ!」って言われつつ [y/N]
のプロンプトが出てしまう。
言われた通りに --password-stdin
してやると二行目の「暗号化されずに保存されるぞ!」という警告は出るが、 [y/N]
のプロンプトは出ずにそのままログインが成功する。
echo "$GCLOUD_ACCESS_TOKEN" | docker login -u 'oauth2accesstoken' --password-stdin 'https://gcr.io'
-u 'oauth2accesstoken'
は現状では固定の様だ。それ以外の値を指定するとログインに失敗する。
あと gcloud docker
と打つだけで生成される ~/.docker/config.json
には "email": "not@val.id"
とあったので試しに -e 'not@val.id'
としてみたら
unknown shorthand flag: 'e' in -e
See 'docker login --help'.
と怒られる。email の時代は終わりです。
まとめるとこうなる。
gcr-login.sh
#!/usr/bin/env bash
GCLOUD_ACCESS_TOKEN=$(docker container run --rm -v $HOME/.config:/root/.config google/cloud-sdk gcloud auth print-access-token)
HOSTS=('https://gcr.io' 'https://asia.gcr.io' 'https://eu.gcr.io' 'https://us.gcr.io' 'https://staging-k8s.gcr.io')
for HOST in ${HOSTS[@]}; do
echo "$GCLOUD_ACCESS_TOKEN" | docker login -u 'oauth2accesstoken' --password-stdin "$HOST"
done
とりあえずこれで docker login
してしまえば、力技ではあるが一応問題は解決する。
成功例 その2 (正しくラップして credHelpers
に指定する)(追記:2018/5/28)
docker login
回りの 公式ドキュメント を読んでいたところ、
Keys specify the registry domain, and values specify the suffix of the program to use (i.e. everything after docker-credential-).
という記述を見付けた。
要するにこれは credHelpers
に gcloud
と指定した場合は docker-credential-gcloud
を見に行くということなのではないかと気付いた。
試しに、 .docker/config.json
に auths
の記述がなくても動いている Mac での環境で調べたらところ
$ which docker-credential-gcloud
/Users/dnpp/google-cloud-sdk/bin/docker-credential-gcloud
と出てきて、あっこれだわ、っとなった。
幸いシェルスクリプトだったので中を見ると
docker-credential-gcloud
#!/bin/sh
#
# Copyright 2017 Google Inc. All Rights Reserved.
#
# [...] (中略) 実行環境のチェックをして変数に放り込んだり export したりするコードが続いてる。
"${CLOUDSDK_ROOT_DIR}/bin/gcloud" auth docker-helper "$@"
となっていた。
つまり gcloud auth docker-helper "$@"
という内容のラッパースクリプトを /opt/bin/docker-credential-gcloud
に置けば綺麗に解決するのではないかと思い、やってみたところ見事に解決した。
docker-credential-gcloud
#!/usr/bin/env bash
docker container run --rm -i \
-v $HOME/.config:/root/.config \
google/cloud-sdk \
gcloud auth docker-helper "$@"
これを /opt/bin
に放り込んで 755
にしてやって gcloud auth configure-docker
してやれば良い。
ポイントは docker -i
でコンテナの STDIN
にアタッチしてやること。データのやり取りは標準入力経由でされている。
もしかしたら $HOME
ではなく /home/core
を直接指定してしまった方が、 sudo で実行した場合に /root
になってしまって gcloud の認証情報が取れずにコケるのを回避することが出来て便利かもしれない。
echo "https://gcr.io" | docker-credential-gcloud get
上手くいけば上記コマンドでそれっぽい JSON が返ってくるようになり、gcr.io に向けた docker image pull
などが成功するようになる。
多分これが一番綺麗なやり方だと思う。
一応環境のメモ
Container Linux by CoreOS alpha (1758.0.0)
Container Linux by CoreOS alpha (1772.0.0)
$ docker --version
Docker version 18.04.0-ce, build 3d479c0
$ docker container run -it --rm google/cloud-sdk gcloud --version
Google Cloud SDK 198.0.0
alpha 2018.04.13
app-engine-go
app-engine-java 1.9.63
app-engine-python 1.9.69
beta 2018.04.13
bigtable
bq 2.0.32
cbt
cloud-datastore-emulator 1.4.1
core 2018.04.13
datalab 20180213
gsutil 4.30
pubsub-emulator 2018.04.13
締め
そもそもの話として gcloud docker
コマンドを使うには --
と指定しなくてはいけないのだが、僕にはこれが美しくないと感じてしまう。
引数とオプションを使う側が明確に区別するためのものだが、「先頭に gcloud
と付けるだけで docker image pull
できますよ!」という触れ込みで触ったのに、一瞬で裏切られたなぁという気持ちは常にあった。
# エラー
$ gcloud docker image pull gcr.io/my-project/my_image:latest
ERROR: (gcloud.docker) unrecognized arguments:
image
pull
gcr.io/my-project/my_image:latest
# -- を付けると通る
$ gcloud docker -- image pull gcr.io/my-project/my_image:latest
あと gcloud docker -- --version
と docker --version
で出力が違ったりもする。マジかよって思った。
$ docker container run -it --rm -v $HOME/.config:/root/.config google/cloud-sdk gcloud docker -- --version
Docker version 17.12.0-ce, build c97c6d6
$ docker --version
Docker version 18.04.0-ce, build 3d479c0
中で docker コマンドを別に持ってるのか…。そうか…。
gcloud
コマンドはそこそこお行儀が悪いので Docker 内に閉じ込めておきたいという気持ちはかなり強く、 Docker image として配布されているのはとても好印象ではある。
好印象ではあるのだが、これもまたそもそもの問題として google/cloud-sdk の Docker image が 1.65 GB なので、細い回線と細いマシンで docker pull
したくないという気持ちも強い。というか Docker image の容量に関してはもうちょっと節約できるやろ、と毎回思っている。
要するに gcloud
は邪悪。
5/13 くらいに書き始めたものの、途中で仕事が忙しくなったり、事実関係の調査とか検証を重ねていった結果時間を食われてしまい、出すまで遂に一週間以上掛かってしまった。難しいですね。
今回調べられてない以下の実装について後で調べておきたいなという気持ちだけ残しておきたい。