skaffold で image 作成時に volumes で クレデンシャルを渡そうとしてハマった
Docker image を作成する時プライベートなリポジトリをpullする、s3からなんか引っ張ってくる、などなど Dockerfile 内で認証情報などのクレデンシャルを利用した処理を行いたいことが多々あると思います。
僕はKubernetesのアプリケーションを開発する際に skaffoldというツールを利用しています。
image のビルドもskaffold と Kubernetes クラスタ内で image をビルドするため kaniko というツールを組み合わせて行なっています。
skaffold + kaniko で前述のようにクレデンシャルを利用する処理を含んだ Dockerfile からイメージを作成しようとしてハマった事を記録します。
今回は GitHub Packages に公開された npm package を取得する例を元に説明します。
npm package を取得するための認証は以下の内容を `.npmrc` ファイルとして保存する事で可能です。
//npm.pkg.github.com/:_authToken=TOKEN
kaniko を利用してimageをビルドする場合、Kubernetes クラスタ上に kaniko pod が作成され pod上でビルドを行います。
ということで kaniko pod 作成時に 上記の .npmrc を 渡してあげると良さそうです。
僕は .npmrc を secret に保存し Volumes でマウントさせる方法を試しました。
skaffold を利用する場合、諸々の設定を skaffold.yaml に書きます。
skaffold.yaml のリファレンスは以下です。
リファレンスを参考に以下のような skaffold.yaml を用意しました。
apiVersion: skaffold/v2beta10 kind: Config build: artifacts: - image: gcr.io/k8s-skaffold/example context: . kaniko: volumeMounts: - name: npmrc mountPath: "/root/" cluster: pullSecretName: kaniko-secret volumes: - name: npmrc secret: secretName: npmrc
npmrc という名前のた secret を kaniko pod の /root/ にマウントしています。
secret は以下のような感じなので kaniko-pod に /root/.npmrc がマウントされます。
Name: npmrc Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data .npmrc: 74 bytes
準備ができたので image をビルドしてみます
$ skaffold build parsing skaffold config: unable to parse config: yaml: unmarshal errors: line 8: field mountPath not found in type v1.VolumeMount line 17: field secret not found in type v1.Volume
はい。エラーが出ちゃいました。
- mountPath プロパティが v1.VolumeMount 型に定義されていない
- secret プロパティが v1.Volume に定義されていない
ということらしいです。ほんまかいな
まずは mountPath から確認です。
skaffold.yaml のスキーマの定義は以下にあります。
skaffold/config.go at master · GoogleContainerTools/skaffold · GitHub
どうやら v1.VolumeMount とは k8s.io/api/core/v1 package のに定義されている型のようです。
定義を確認しに行くと mountPath プロパティはちゃんと v1.VolumeMount に定義されています。
はて、skaffold のバグかなと思って issue を漁ったらそれっぽいやつを見つけました
volume structures (mountPath, persistentVolumeClaim) must be lower-case to avoid unmarshal error · Issue #4175 · GoogleContainerTools/skaffold · GitHub
どうやら キー名を lower-case にしないとうまくパースできないバグが含まれていたようです。
修正の PullRequest はマージされていましたが、まだリリースされいないようなだったのでとりあえず lower-case に修正して試します。
apiVersion: skaffold/v2beta10 kind: Config build: artifacts: - image: gcr.io/k8s-skaffold/example context: . kaniko: volumeMounts: - name: npmrc mountpath: "/root/" cluster: pullSecretName: kaniko-secret volumes: - name: npmrc secret: secretName: npmrc
$ skaffold build parsing skaffold config: unable to parse config: yaml: unmarshal errors: line 17: field secret not found in type v1.Volume
はい。エラーが 1つ解消されました。
続いて 「secret プロパティが v1.Volume に定義されていない」をみてみます。
type Volume struct { Name string `json:"name" protobuf:"bytes,1,opt,name=name"` VolumeSource `json:",inline" protobuf:"bytes,2,opt,name=volumeSource"` }
v1.Volume は Name と VolumeSource のプロパティがあって VolumeSource はインラインに展開されます。
VolumeSource の方には secret プロパティが含まれているのでこちらも問題なさそうに見えます。
skaffold の方の定義は以下のようになっています (関係ある箇所抜粋)
skaffold/config.go at master · GoogleContainerTools/skaffold · GitHub
type ClusterDetails struct { // Volumes defines container mounts for ConfigMap and Secret resources. Volumes []v1.Volume `yaml:"volumes,omitempty"` }
k8s.io/api/core/v1 の定義と skaffold の方の定義を見比べると skaffold の方は yaml タグを利用しているのに対し k8s.io/api/core/v1 は json タグを利用しています。
それが悪さをして VolumeSource をうまくインライン展開できていないようでした。
こちらのバグも先ほどの PullRequest で修正されているようです。
VolumeSource がインライン展開できていないだけなので以下のように yaml を修正するとエラーを回避することができます
apiVersion: skaffold/v2beta10 kind: Config build: artifacts: - image: gcr.io/k8s-skaffold/example context: . kaniko: volumeMounts: - name: npmrc mountpath: "/root/" cluster: pullSecretName: kaniko-secret volumes: - name: npmrc volumesource: secret: secretname: npmrc
volumesource や secretname も例によって lower-case になっていることに注意してください。
エラーは回避できたとはいえ、修正の PullRequest がリリースされたら、上記の yaml は逆にパースエラーになりそうな気もするので早くリリースされるのを待つばかりです。