ECR から Docker のイメージをプルするための認証情報は aws ecr get-login-password
で作成できます。k8s ではこの認証情報を Secret として保存することで、イメージをECR からプルしてくれます。しかし、この方法で取得した認証情報は12時間しか有効ではないため、定期的に認証を取得する必要があります。
k8s が動いているホストの cron で実行してもいいのですが、色々な事情によって k8s の cronjob を使ってやることにしました。
今回はその方法を記事にしようと思います。
前提
- k8s がすでに用意されている
- docker イメージをパブリックに公開できる環境がある。または、パブリックにある docker イメージを自己判断で使用できる環境である。
- ECR にプライベートなイメージがすでに用意されている
流れ
- aws のログイン情報を Secret として登録
- aws-cli と kubectl を使用できる Docker イメージを作成する
- ServiceAccount と ClusterRoleBinding を作成する
- cronjob で定期的に ECR への認証情報を取得して Secret として保存
1. aws のログイン情報を Secret として登録
Secret を作成するために、AWS のログイン情報をファイルにまとめて起きます。
今回は .aws.credentials
という名前で作成します。
AWS_ACCOUNT_ID=123456789012
AWS_ACCESS_KEY_ID=XXXXXXXXX
AWS_SECRET_ACCESS_KEY=YYYYYYYYY
AWS_DEFAULT_REGION=ap-northeaset-1
このファイルから Secret を作成しておきます。これはこの後の3の手順で Pod 内から使用します。
今回はSecret名を aws-credentials
にしています。 Secret は名前空間を超えることはできないので注意してください。
kubectl create generic aws-credentials --from-env-file=.aws.credentials
2. aws-cli と kubectl を使用できる Docker イメージを作成する
k8s の cronjob で aws-cli と kubectl を実行するための Docker イメージを作成します。
すでに公開されている Docker イメージを使用するのであれば、このステップは飛ばすことができます。自己判断で決めてください。
イメージを自作する場合
alpine ベースの Docker イメージを作成します。
FROM alpine:3.12
RUN apk --no-cache add aws-cli wget \
&& wget https://storage.googleapis.com/kubernetes-release/release/v1.24.0/bin/linux/amd64/kubectl \
&& mv kubectl /usr/local/bin/kubectl \
&& chmod +x /usr/local/bin/kubectl \
&& apk del wget
v1.24.0
部分が kubectl のバージョンです。
バージョン部分はハードコーディングしていますが、公式には最新版をインストールする方法の記載があります。
次のステップで説明しますが、Pod 内では kubectl で使用する config ファイルが Pod 起動時(?)に用意されます。そのため、このイメージに kubectl のための config ファイルを入れる必要はありません。パブリックとして公開するため、間違えてもいれないようにしてください。
Docekrfile を作成したら、パブリックなイメージとして docker hub や ECR などにプッシュします。
docker login
docker push my-image:latest
3. ServiceAccount と ClusterRoleBinding を作成する
続いて、Pod から Secret を作成できるように権限を設定していきます。
k8s には Role Based Access Control(RBAC) という機能があります。リソースへのアクセス権限を設定した Role を付与することで、 リソースへのアクセスを許可することができます。
今回はクラスターで共通の ClusterRole を使用していきます。(クラスター全体ではなく、Namespace ごとに分離して権限を作成したい場合は Role を使用できます)
ServiceAccount の作成
まずは ServiceAccount を作成します。
# kubectl -n default create sa <ServiceAccount 名>
kubectl -n default create sa secrets-manager
ServiceAccount は 1:N の関係で ClusterRole と紐づけることができます。
ServiceAccount は Pod のマニュアルで指定します。
ClusterRole の作成
つづいて、ClusterRole を作成します。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secrets-cr # ClusterRole の名前
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create", "updated", "delete"]
rules.resource
に secrets
と記載しています。これで secret-cr が紐づいたリソースは secret に対して作成・更新・削除ができるようになります。 verbs
には他にも get
list
watch
などがあります。
ClusterRoleBinding で紐づける
最後に、作成した SecretAccount と CluterRole を紐づけます。紐付けには ClusterRoleBinding を使用します。(もし Role を作成していた場合は、RoleBinding)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: secrets-crb # ClusterRoleBinding の名前
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: secrets-cr # 紐づける ClusterRole
subjects:
- kind: ServiceAccount
name: secrets-manager # 紐づける ServiceAccount
namespace: default
ClusterRoleBinding は最後に作成しないとエラーになるのでご注意ください。
4. Secret を更新する Cronjob を作成
最後に cronjob を作成して完成です。
apiVersion: batch/v1
kind: CronJob
metadata:
name: create-aws-secret
namespace: default
spec:
schedule: "* */10 * * *" # 有効期間は12時間
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
serviceAccount: secrets-manager # 3. で作成した ServiceAccount を設定
containers:
- name: create-aws-secret
image: < 2. で作成したパブリックな Docker イメージ>
imagePullPolicy: IfNotPresent
env:
- name: SECRET_NAME
value: aws # 作成・更新する secret の名前
envFrom:
- secretRef:
name: aws-credentials # 1. で作成した aws 認証情報
command:
- /bin/sh
- -c
- |-
kubectl delete secret --ignore-not-found ${SECRET_NAME}
kubectl create secret docker-registry ${SECRET_NAME} \
--docker-server=${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com \
--docker-username=AWS --docker-password=$(aws ecr get-login-password)