使用 k8s cronjob 自动更新 aws ecr credentiails 认证

1. Before

aws ecr 使用都需要 credentials认证,才可以 pull/push镜像,即使通过 docker login -u AWS -p https://.dkr.ecr..amazonaws.com.cn 的方式登陆 ecr,获得的token认证也只有 12h的有效期,对于CICD的要求来说无疑是一大障碍,因此下文采用 k8s cronjob/ secrets 的方式来保持token的更新及应用部署过程中对私用镜像的获取。

2. 制作一个具有awsclikubectldocker image

此处Dockerfile 中使用的 base imagecentos7 ,想用小些的镜像可自行下载,alpine 即可

[root@aws-172-20-20-101 aws-kubectl]# cat Dockerfile 
FROM centos:centos7
MAINTAINER xx.xx 

WORKDIR /opt/cronjob
RUN yum install -y awscli 
RUN mkdir ~/.aws &&\
      mkdir ./pki
# 若想使用 docker login 功能,则需安装 docker-cli,docker-cli rpm 包需要提前下载好
#    mkdir ./pki &&\
#    mkdir ./docker
#COPY docker-ce-cli-18.09.5-3.el7.x86_64.rpm  ./docker/
#RUN  rpm -i ./docker/docker-ce-cli-18.09.5-3.el7.x86_64.rpm 
#COPY credentials config   ~/.aws/ # 将 aws 的认证信息复制到 ~/.aws/ 目录下
COPY ca.crt admin.crt admin.key admin.kubeconfig ./pki/ # kubectl 访问 k8s 集群需要用到的证书,build 镜像前将证书都放在上下文同一路径下
COPY kubectl /usr/local/bin
ENV PATH=/root/.local/bin:/usr/local/bin:$PATH
ENV KUBECONFIG=/opt/cronjob/pki/admin.kubeconfig

这里 kubectl 使用的证书格式如下,可根据自己环境使用方式做调整

[root@aws-172-20-20-101 aws-kubectl]# cat admin.kubeconfig 
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /opt/cronjob/pki/ca.crt
    server: https://xxxxx.com.cn:443
  name: default-cluster
contexts:
- context:
    cluster: default-cluster
    user: default-user
  name: default-context
current-context: default-context
kind: Config
preferences: {}
users:
- name: default-user
  user:
    client-certificate: /opt/cronjob/pki/admin.crt
    client-key: /opt/cronjob/pki/admin.key

build 镜像,并 push 到远端仓库,镜像名称 tag 自己定义

[root@aws-172-20-20-101 aws-kubectl]# docker build -t xxx.dkr.ecr.xxx.amazonaws.com.cn/k8s-mirror:aws-kubectl-1.7 .

3. 创建 k8s cronjob

cronjob需要在每个用到的 namespace 下都创建一个,同名不同 namespace即可,yaml中添加了注释,复制使用时记得把注释删掉

[root@aws-172-20-20-28 aws-kubectl]# cat aws-kubectl-centos.yaml 
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  annotations:
  name: ecr-cred-helper
#  namespace: kube-system
spec:
  concurrencyPolicy: Allow
  failedJobsHistoryLimit: 1
  jobTemplate:
    metadata:
      creationTimestamp: null
    spec:
      template:
        metadata:
          creationTimestamp: null
        spec:
          containers:
          - command:
            - /bin/sh
            - -c
            - |-
              ACCOUNT=$your_aws_account_id
              REGION=$your_aws_region
              SECRET_NAME=${REGION}-ecr-registry # secret name,可自定义
              TOKEN=`aws ecr get-login --region ${REGION} --registry-ids ${ACCOUNT} | cut -d ' ' -f6` # 通过 awscli 命令行实时获取能访问 aws ecr 的 password
              echo `aws sts get-caller-identity` # 查看获取的认证信息是否正确,上线使用时可删此行
              echo `aws --version`  # 查看 awscli 的版本号
              echo "ENV variables setup done."
              /usr/local/bin/kubectl delete secret --ignore-not-found $SECRET_NAME # 删除旧的 secret
              /usr/local/bin/kubectl create secret docker-registry $SECRET_NAME \  # 创建新的 secret
              --docker-server=xxxx.dkr.ecr.xxxx.amazonaws.com.cn \ # aws ecr server
              --docker-username=AWS \
              --docker-password="${TOKEN}" \
              --docker-email="[email protected]" # 注册的邮箱
              echo "Secret created by name. $SECRET_NAME"
              /usr/local/bin/kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "$SECRET_NAME"}]}'  # 将 secret patch 到 default token 中
              echo "All done."
            env:
            - name: AWS_DEFAULT_REGION
              value: xxxxx   # aws 的 region name
            - name: AWS_SECRET_ACCESS_KEY
              value: 8nOxxxxxxxxjnpf5fZlV0MxxxxxxXs+  # access_key
            - name: AWS_ACCESS_KEY_ID
              value: AxxxxxxGK22xxxxx  # access_id
            - name: KUBERNETES_SERVICE_HOST
              value: xxxxxx.com.cn  # apiserver 访问入口
            - name: KUBERNETES_SERVICE_PORT
              value: "443"
            image: xxxxx.dkr.ecr.xxxxx.amazonaws.com.cn/k8s-mirror:aws-kubectl-1.7 # 刚才编译好的镜像
            imagePullPolicy: IfNotPresent
            name: ecr-cred-helper
            resources: {}
            securityContext:
              capabilities: {}
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
          nodeSelector:
            node-role.kubernetes.io/master: "true"
          tolerations:
          - key: "node-role.kubernetes.io/master"
            value:
          dnsPolicy: Default
          hostNetwork: true
          restartPolicy: Never
          schedulerName: default-scheduler
          securityContext: {}
          terminationGracePeriodSeconds: 30
  schedule: "* */6 * * *" # 表示每 6 小时刷新一次,测试时可调整,参考 linux crontab
  successfulJobsHistoryLimit: 3
  suspend: false

k8s master 上创建 cronjob

[root@aws-172-20-20-28 aws-kubectl]# kubectl create -f aws-kubectl-centos.yaml 

查看创建结果,一切正常

[root@aws-172-20-20-28 aws-kubectl]# kubectl get cronjob
# 这里使用的是测试时间,每2分钟刷新一次
NAME              SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
ecr-cred-helper   */2 * * * *   False     0        76s             3h39m

# 到了设定的时间后 cronjob 会拉起一个 pod 执行命令,更新 token,执行完后是 completed 状态
[root@aws-172-20-20-28 aws-kubectl]# kubectl get pods 
NAME                               READY   STATUS             RESTARTS   AGE
ecr-cred-helper-1561107840-wlqjl   0/1     Completed          0          5m16s
ecr-cred-helper-1561107960-x6dql   0/1     Completed          0          3m16s
ecr-cred-helper-1561108080-gr6kt   0/1     Completed          0          75s

# 可通过 kubectl logs $pod-name 查看执行状态
[root@aws-172-20-20-28 aws-kubectl]# kubectl logs ecr-cred-helper-1561108080-gr6kt
{ "Account": "xxxx", "UserId": "xxxxxxxx", "Arn": "arn:aws-cn:iam::xxxxxx:user/xxxxxx" }
aws-cli/1.14.28 Python/2.7.5 Linux/4.4.180-2.el7.elrepo.x86_64 botocore/1.8.35

ENV variables setup done.
secret "xxxxxxx-ecr-registry" deleted
secret/xxxxxxx-ecr-registry created
Secret created by name. xxxxxx-ecr-registry
serviceaccount/default patched 
All done.

4. 在需要创建的statefulset/ deployment/ daemonset/ pod 中使用 imagePullSecrets

成功的话,在没有docker login的宿主机上可以直接拉取 aws ecr上的私有镜像

[root@aws-172-20-20-28 aws-kubectl]# cat test.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: image-test
spec:
  containers:
    - name: image-test-container
      image: xxxx.dkr.ecr.cn-xxx-1.amazonaws.com.cn/k8s-mirror:nginx-1.0
  imagePullSecrets:
    - name: xxxx-ecr-registry

[root@aws-172-20-20-28 aws-kubectl]#  kubectl create -f test.yaml

彩蛋

如果有如下报错,请参考 aws 官方说明

  • Error response from daemon: Get https://xxxx.dkr.ecr.xxxxx.amazonaws.com.n/v2/amazon-k8s-cni/manifests/v1.3.2: no basic auth credentials
    如果觉得官方说明没 niao 用,按下面步骤测试一下,确认生成的 secret 是正确的
  • 在 cronjob 的 yaml 中执行sh命令那里加入几个命令,将获取的信息输出,通过kubectl logs $pod-name 查看
 containers:
          - command:
            - /bin/sh
            - -c
            - |-
              ACCOUNT=xxxxx
              REGION=xxxx
              SECRET_NAME=${REGION}-ecr-registry
              TOKEN=`aws ecr get-login --region cn-xxxxx-1 --registry-ids xxxxx | cut -d ' ' -f6`
              echo `aws ecr create-repository --repository-name aws-kubectl` # 看看是否可以创建一个存储库
              echo `aws sts get-caller-identity`
              echo `aws --version`
              echo "${TOKEN}" # 将获取的 passwd 输出,查看是否正确
              echo "ENV variables setup done."

通过查看更新的 secret 确认生成的认证是否正确, authusernamepassword的base64编码

[root@aws-172-20-20-28 aws-kubectl]# kubectl get secret $your_secret_name --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode
{"auths":{"xxxxx.dkr.ecr.cn-xxxxx-1.amazonaws.com.cn":{"username":"AWS","password":"xxxxxxx","email":"[email protected]","auth":"QVdTOmV5SndZxxxxxEo5"}}}

若生成的认证 token 无误,依然有no basic auth credentials报错,使用 amazon-ecr-credential-helper工具可解决问题,纤细参考 how to use amazon-ecr-credential-helper

你可能感兴趣的:(使用 k8s cronjob 自动更新 aws ecr credentiails 认证)