【AWS】Ansible & KanikoでECRにDockerイメージをpushする

 Kubernetesクラスタ上でDockerイメージをビルド&プッシュするためのツール「Kaniko」について、仕様を調査したのでメモ。

Kanikoとは

 実体はKubernetesクラスタ上でDockerイメージをビルド、プッシュを行うためのOSSのDockerイメージです。
Googleが開発しているようですが、公式サポートはしていないようです。
 READMEを見ると各クラウド環境のレジストリへのプッシュにも対応しており、使いやすそうな印象です。

github.com

前提条件

 前回使用したEC2、ECR、ソースコードをそのまま使用します。
Ansibleで直接ECRにプッシュしていた箇所を、Kanikoポッドでプッシュするように変更してみます。

tm200.hatenablog.com


 KanikoポッドがECRにイメージをプッシュするには、稼働環境(EC2/EKS)にIAMロールを付与するか、IAMロールが付与されたユーザーのクレデンシャルをコンテナに渡す必要があります。
今回は前回と同じユーザーを使用するため、クレデンシャルのSecretを作成しました。

$ k create secret generic aws-secret --from-file=.aws/credentials -n ansible-test


 AWS ECR周りの設定は、この辺りに詳細が記載されています。

ディレクトリ構成

.
├─ deploy-image.yml     実行するAnsible playbook
├─ Dockerfile           プッシュするイメージのDockerfile
├─ Jenkinsfile          イメージに含めるJenkinsfile
├─ inventory
│  └─ hosts
└─ roles
   └─ k8s-kaniko
       └─ tasks
             ├─ kaniko.yml    Kanikoポッドを立ち上げる為のマニフェスト
             └─ main.yml      Ansibleのロール

実装

 記載のないファイルの内容は前回と同様です。

deploy-image.yml

 roleを含めるだけのplaybookです。

---
- name: k8s kaniko Test
  hosts: local
  roles:
    - k8s-kaniko
roles/k8s-kaniko/tasks/main.yml

 DockerfileJenkinsfileをKanikoコンテナに渡す必要があるため、ConfigMapにしてからVolume経由でコンテナに渡す想定です。
 1つのConfigMapに纏めようとしたのですが、yamlのパースエラーが起きるので2つに分けています。
 改行+インデントがうまく設定できるようなコマンドを含めれば解消できそうですが、今回はそこまでの作りこみはしません。

---

- name: "Build docker image in k8s cluster"
  debug:
    msg: "Build docker image in k8s cluster"

- name: Install boto3
  pip:
    name: boto3
    state: present

- name: Slurp Dockerfile
  slurp:
    path: Dockerfile
  register: cat_Dockerfile

- name: Slurp Jenkinsfile
  slurp:
    path: Jenkinsfile
  register: cat_Jenkinsfile

- name: Create config-map in Dockerfile
  k8s:
    kubeconfig: ~/.kube/config
    state: present
    resource_definition:
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: kaniko-test-cm-docker
        namespace: ansible-test
      data:
        Dockerfile: "{{ cat_Dockerfile.content | b64decode }}"

- name: Create config-map in Jenkinsfile
  k8s:
    kubeconfig: ~/.kube/config
    state: present
    resource_definition:
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: kaniko-test-cm-jenkins
        namespace: ansible-test
      data:
        Jenkinsfile: "{{ cat_Jenkinsfile.content | b64decode }}"

- name: Create repository
  ecs_ecr:
    name: "{{ image_name }}"
    aws_access_key: "{{ key_id }}"
    aws_secret_key: "{{ access_key }}"
    region: "{{ region }}"
  register: ecr_repo

- name: Push docker image
  vars:
    imagename: "{{ ecr_repo.repository.repositoryUri }}:{{ image_version }}"
    namespace: ansible-test
  k8s:
    definition: '{{ item }}'
    kubeconfig: ~/.kube/config
    state: present
  loop:
    - "{{ lookup('template', 'kaniko.yml') | from_yaml_all | list }}"
kaniko.yml

 Kanikoポッドを立ち上げる為のマニフェストファイルです。
使い方の詳細はGitHubのREADMEに記載されています。

apiVersion: v1
kind: Pod
metadata:
  name: kaniko
  namespace: {{namespace}}
spec:
  restartPolicy: Never
  containers:
    - name: kaniko
      image: gcr.io/kaniko-project/executor:latest
      args:
        - "--dockerfile=./Dockerfile"
        - "--context=/kaniko-test-cm"
        - "--destination={{ imagename }}"
      volumeMounts:
        - name: aws-secret
          mountPath: /root/.aws
          readOnly: true
        - name: kaniko-test-cm
          mountPath: /kaniko-test-cm
          readOnly: true

  volumes:
    - name: aws-secret
      secret:
        secretName: aws-secret
    - name: kaniko-test-cm
      projected:
        sources:
        - configMap:
            name: kaniko-test-cm-docker
        - configMap:
            name: kaniko-test-cm-jenkins

playbook実行

 正常終了することを確認。

$ ansible-playbook deploy-image.yml -i inventory/hosts

PLAY [k8s kaniko Test] *************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Build docker image in k8s cluster] ******************************************************************
ok: [localhost] => {
    "msg": "Build docker image in k8s cluster"
}

TASK [k8s-kaniko : Install boto3] **************************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Slurp Dockerfile] ***********************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Slurp Jenkinsfile] **********************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Create config-map in Dockerfile] ********************************************************************
ok: [localhost]

TASK [k8s-kaniko : Create config-map in Jenkinsfile] *******************************************************************
ok: [localhost]

TASK [k8s-kaniko : Create repository] **********************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Push docker image] **********************************************************************************
changed: [localhost] => (item=[{u'kind': u'Pod', u'spec': {u'restartPolicy': u'Never', u'volumes': [{u'secret': {u'secretName': u'aws-secret'}, u'name': u'aws-secret'}, {u'name': u'kaniko-test-cm', u'projected': {u'sources': [{u'configMap': {u'name': u'kaniko-test-cm-docker'}}, {u'configMap': {u'name': u'kaniko-test-cm-jenkins'}}]}}], u'containers': [{u'image': u'gcr.io/kaniko-project/executor:latest', u'args': [u'--dockerfile=./Dockerfile', u'--context=/kaniko-test-cm', u'--destination=xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/jenkins-test/jenkins-test:v1.0.0'], u'volumeMounts': [{u'readOnly': True, u'mountPath': u'/root/.aws', u'name': u'aws-secret'}, {u'readOnly': True, u'mountPath': u'/kaniko-test-cm', u'name': u'kaniko-test-cm'}], u'name': u'kaniko'}]}, u'apiVersion': u'v1', u'metadata': {u'namespace': u'ansible-test', u'name': u'kaniko'}}])

PLAY RECAP *************************************************************************************************************
localhost                  : ok=9    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


 コンソールからECRにイメージがプッシュされたことが確認できます。

注意

 issuesプルリクから察するに、メンテナンスはあまり活発に行われていないように思います。
 また、最近のissuesの中にはマルチステージビルドに失敗する等、クリティカルな内容の物も見受けられます。
 もし技術選定に関われる立場にあるなら、できる限りKanikoは使用しない設計に倒した方が無難でしょう。