Azure Container Registryの認証

Azure Container Registry(ACR)へのアクセスで使用する認証情報の取り扱いについて

ACRにdocker pushする場合と、Kubernetesでpullする場合のそれぞれについて、認証情報をどう扱うかをまとめておきます。

以前同じことをやったことがあったのですが、手順がすぐに思い出せなかったので、こちらにまとめておくことにしました。 なお、以下の実行環境はUbuntu 16.04 LTSで、ACRのレジストリは作成済みの前提です(レジストリ作成手順はチュートリアル参照)。

また、Azure Container Service(AKS)の場合、以下の「k8sへのsecret登録&YAMLへのimagePullSecretsの指定」をしなくても、 ロールの割り当て(az role assignment create・・・)を行うことでpullできるようになるようです。AKSのチュートリアルでも、その手順で記載されています。

docker pushする場合

Azure CLIがインストールされていない場合、まずインストールします。そのうえで、以下のようにコマンドを実行すると、ローカルにあるコンテナイメージをACRにpushできます(レジストリ名=workreg、タグ=kazusato/demoapp:v1の場合です)。

$ az login
$ sudo az acr login -n workreg
$ sudo docker push kazusato/demoapp:v1

az acr loginコマンドを実行すると、~/.docker/config.jsonに認証情報が書き込まれ、docker push時にそれが使われるようです。sudoで実行した場合でも/rootの下ではなく、sudo実行ユーザーのホームディレクトリ配下に作成されました。

詳しく調べていませんが、az acr loginコマンドもsudoなしで実行するとdocker関連のパーミッションでエラー(Got permission denied while trying to connect to the Docker daemon・・・)になりました。

pushできたかの確認

きちんとpushできたかは、

$ az acr respository show-tags -n workreg --repository kazusato/demoapp

を実行すると、

[
  "v1"
]

のようにACRに登録されているイメージのバージョンが返ってきますので、さきほどpushしたものが含まれているかで確認できます。

Kubernetesでpullする場合

Kubernetes(k8s)では、secret(というk8sのリソース)に認証情報を登録しておき、Pod生成に使うコンテナ情報にimagePullSecretsとして指定することで、認証が必要なコンテナレジストリにアクセスできるようになります。

この際、k8sからはpullしかしない(pushすることはない)ため、読み取り専用の権限があれば十分です。そこで、読み取り専用の ユーザー(サービスプリンシパルと呼ぶ←これはAzureの概念)を作り、その認証情報をSecretとして登録するという手順をとります。 この手順については、Azureのドキュメント サービス プリンシパルによる Azure Container Registry 認証 に記載があるのですが、複数のコマンドが入り混じってわかりにくかったので、少し整理してみました。

ACR読み取り専用サービスプリンシパルの作成

まず、

$ az acr show -n workreg --query id --output tsv

を実行し、レジストリIDを取得します(以下のような文字列が返ってきます。なお、「myresgrp」は、このレジストリの所属するリソースグループ名です)。

/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myresgrp/providers/Microsoft.ContainerRegistry/registries/workreg

次に、

az ad sp create-for-rbac --name sp-workreg-reader --scopes /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myresgrp/providers/Microsoft.ContainerRegistry/registries/workreg --role reader

を実行します。すると、

{
  "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "displayName": "sp-workreg-reader",
  "name": "http://sp-workreg-reader",
  "password": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

のような文字列が返ってきます。この中の「appId」と「password」を、secret作成に使います。

kubectlでのsecretの作成

登録するsecret名を「workreg-reader-secret」とする場合、

$ kubectl create secret docker-registry workreg-reader-secret \
  --docker-server workreg.azurecr.io \
  --docker-email myname@example.com \
  --docker-username xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
  --docker-password xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

を実行することで、secretの登録ができます。 ここで、「--docker-username」に指定する値は、「az ad sp ・・・」コマンドで返ってきたJSONのうち、 「appId」の値であることに注意してください。名前が紛らわしいですが、「name」や「displayName」では ありません(私はここで勘違いして、しばらくはまりました)。

正常に登録できると、

secret "workreg-reader-secret" created

のように返ってきます。

imagePullSecretsの指定

ここからの手順はAzure固有の要素はなく、 k8s一般のものになります。なお、ここでは直接Podを作成する場合で記載しますが、 Deploymentを作成する場合のテンプレート情報として指定する場合も同様です。

Podのコンテナ情報として、Pod定義のYAMLに以下のように指定します。

apiVersion: v1
kind: Pod
metadata:
  name: demoapp
spec:
  containers:
  - name: demoapp
    image: workreg.azurecr.io/kazusato/demoapp:v1
  imagePullSecrets:
  - name: workreg-reader-secret

このファイルをdemoapp.yamlとした場合、

$ kubectl apply -f demoapp.yaml

を実行すると、Podが登録できます。このとき、さきほど指定したsecretを使ってレジストリにアクセスしています。 Podが正しく登録できると、

$ kubectl get po

に対して、

NAME         READY     STATUS    RESTARTS   AGE
demoapp      1/1       Running   0          11m

のようにステータスが「Running」で返ってきます(kubectl applyした直後は「 ContainerCreating」かと思いますので、コンテナが起動するまでしばらく待ってください)。

(おまけ)secretが正しく登録されていない場合の挙動

どこかで設定を失敗した場合など、正しくコンテナイメージをpullできないと、Podの登録に失敗してエラーになります。 私もはまりましたので、切り分けの材料として、secretが正しく登録されていない場合の挙動を記載しておきます。

imagePullSecretsに指定したsecretが存在しない場合と、secretはあるが認証情報を誤って指定した場合を試してみました。 いずれの場合も、以下のようにステータスが「ErrImagePull」となりました。

NAME         READY     STATUS         RESTARTS   AGE
demoapp      0/1       ErrImagePull   0          4s

より詳細には、

kubectl describe po demoapp

を実行することで、情報が取得できます。表示最下部のEvents欄を見ると、Messageが 「Failed to pull image・・・(中略)・・・unauthorized: authentication required」 となっている行が存在すると思います。