Skip to main content

mirror container images

ben.wangzLess than 1 minute

mirror container images

prepare

  1. argo workflows is ready
  2. minio is ready for artifact repository
    • endpoint: minio.storage:9000

demo

  1. configure s3 artifact repository
  2. prepare images-to-mirror.yaml which contains a configmap storing a list of images to mirror
    • apiVersion: v1
      kind: ConfigMap
      metadata:
        name: images-to-mirror
      data:
        images-to-mirror.yaml: |
          - registry: docker.io
            repository: library/busybox
            tag: stable-glibc
          - registry: docker.io
            repository: library/alpine
            tag: latest
          - registry: ghcr.io
            repository: ben-wangz/blog-skopeo
            tag: main
      
      
  3. apply images-to-mirror.yaml to k8s
    • kubectl -n business-workflows apply -f images-to-mirror.yaml
      
  4. prepare registry-credentials secret
    • #REGISTRY_INSECURE=false
      #REGISTRY=REGISTRY
      #REGISTRY_USERNAME=your-username
      #REGISTRY_PASSWORD=your-password
      kubectl -n business-workflows create secret generic registry-credentials \
        --from-literal="insecure=${REGISTRY_INSECURE:-false}" \
        --from-literal="registry=${REGISTRY:-docker.io}" \
        --from-literal="username=${REGISTRY_USERNAME:-wangz2019}" \
        --from-literal="password=${REGISTRY_PASSWORD}"
      
      
  5. prepare mirror-container-images.yaml
    • with-docker
      apiVersion: argoproj.io/v1alpha1
      kind: Workflow
      metadata:
        generateName: mirror-container-images-
      spec:
        entrypoint: entry
        artifactRepositoryRef:
          configmap: artifact-repositories
          key: default-artifact-repository
        serviceAccountName: argo-workflow
        templates:
        - name: entry
          dag:
            tasks:
            - name: initialization
              template: initialization
              arguments:
                parameters:
                - name: registry-credentials-secret-name
                  value: registry-credentials
                - name: images-to-mirror-configmap-name
                  value: images-to-mirror
            - name: dind
              dependencies:
              - initialization
              template: dind
              arguments:
                parameters:
                - name: insecure-option
                  value: "{{tasks.initialization.outputs.parameters.insecure-option}}"
                - name: insecure-registry
                  value: "{{tasks.initialization.outputs.parameters.insecure-registry}}"
            - name: wait-for-dind
              dependencies:
              - dind
              template: wait-for-dind
              arguments:
                parameters:
                - name: dockerd-host
                  value: "{{tasks.dind.ip}}"
            - name: mirror
              dependencies:
              - wait-for-dind
              template: mirror
              arguments:
                parameters:
                - name: dockerd-host
                  value: "{{tasks.dind.ip}}"
                - name: repository-prefix
                  value: wangz2019/mirror_
                - name: keep-structure
                  value: "false"
                - name: string-to-replace-slash
                  value: _
                - name: registry-credentials-secret-name
                  value: registry-credentials
                - name: dry-run-push
                  value: "false"
                artifacts:
                - name: images-to-mirror
                  from: "{{tasks.initialization.outputs.artifacts.images-to-mirror}}"
        - name: initialization
          inputs:
            parameters:
            - name: registry-credentials-secret-name
            - name: images-to-mirror-configmap-name
          volumes:
          - name: images-to-mirror
            configMap:
              name: "{{inputs.parameters.images-to-mirror-configmap-name}}"
          container:
            image: docker.io/library/alpine:3.19.1
            volumeMounts:
            - name: images-to-mirror
              mountPath: /app/images-to-mirror.yaml
              subPath: images-to-mirror.yaml
              readOnly: true
            env:
            - name: REGISTRY_INSECURE
              valueFrom:
                secretKeyRef:
                  name: "{{inputs.parameters.registry-credentials-secret-name}}"
                  key: insecure
            - name: REGISTRY
              valueFrom:
                secretKeyRef:
                  name: "{{inputs.parameters.registry-credentials-secret-name}}"
                  key: registry
            command:
            - sh
            - -c
            args:
            - |
              printf "--insecure-registry" > /tmp/insecure-option
              if [ "${REGISTRY_INSECURE}" = "true" ]; then
                  echo "insecure registry ${REGISTRY}"
                  printf "%s" ${REGISTRY} > /tmp/insecure-registry
              else
                  printf "127.0.0.1" > /tmp/insecure-registry
              fi
          outputs:
            parameters:
            - name: insecure-option
              valueFrom:
                path: /tmp/insecure-option
            - name: insecure-registry
              valueFrom:
                path: /tmp/insecure-registry
            artifacts:
            - name: images-to-mirror
              path: /app/images-to-mirror.yaml
        - name: dind
          daemon: true
          inputs:
            parameters:
            - name: insecure-option
            - name: insecure-registry
          container:
            image: docker.io/library/docker:25.0.3-dind-alpine3.19
            env:
            - name: DOCKER_TLS_CERTDIR
              value: ""
            command:
            - dockerd-entrypoint.sh
            - "{{inputs.parameters.insecure-option}}"
            - "{{inputs.parameters.insecure-registry}}"
            securityContext:
              privileged: true
        - name: wait-for-dind
          inputs:
            parameters:
            - name: dockerd-host
          container:
            image: ghcr.io/ben-wangz/blog-docker-cli-with-bash:main
            env:
            - name: DOCKER_HOST
              value: "{{inputs.parameters.dockerd-host}}"
            command:
            - sh
            - -c
            args:
            - |
              until docker ps; do sleep 3; done;
        - name: mirror
          inputs:
            artifacts:
            - name: images-to-mirror
              path: /app/images.yaml
            - name: mirror-scripts
              path: /app/mirror.sh
              raw:
                data: |
                  #! /bin/bash
      
                  set -e
                  SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
      
                  IMAGE_YAML_FILE=${IMAGE_YAML_FILE:-${SCRIPT_DIR}/images.yaml}
                  KEEP_STRUCTURE=${KEEP_STRUCTURE:-true}
                  STRING_TO_REPLACE_SLASH=${STRING_TO_REPLACE_SLASH:-_}
                  REGISTRY_INSECURE=${REGISTRY_INSECURE:-true}
                  if type podman > /dev/null 2>&1; then
                      CONTAINER_MANAGER=podman
                  else
                      CONTAINER_MANAGER=docker
                  fi
                  if [ "${CONTAINER_MANAGER}" = "podman" ] && [ "${REGISTRY_INSECURE}" = "true" ]; then
                      OPTIONS="--tls-verify=${REGISTRY_INSECURE}"
                  else
                      OPTIONS=""
                  fi
                  ${CONTAINER_MANAGER} login ${OPTIONS} -u ${REGISTRY_USERNAME} -p ${REGISTRY_PASSWORD} ${REGISTRY}
                  yq eval '.[] | ("registry=" + .registry + "; repository=" + .repository + "; tag=" + .tag)'  ${IMAGE_YAML_FILE} \
                    | while read -r EXPRESION
                      do
                          eval $EXPRESION
                          SOURCE_IMAGE=${registry}/${repository}:${tag}
                          if [ "${KEEP_STRUCTURE}" = "true" ]; then
                              TARGET_IMAGE=${REGISTRY}/${REPOSITORY_PREFIX}${SOURCE_IMAGE}
                          else
                              TARGET_IMAGE=${REGISTRY}/${REPOSITORY_PREFIX}$(echo ${SOURCE_IMAGE} | sed -e "s/\//${STRING_TO_REPLACE_SLASH}/g")
                          fi
                          ${CONTAINER_MANAGER} image pull ${SOURCE_IMAGE}
                          ${CONTAINER_MANAGER} image tag ${SOURCE_IMAGE} ${TARGET_IMAGE}
                          if [ "${DRY_RUN_PUSH:-true}" = "true" ]; then
                              echo "dry run command: '${CONTAINER_MANAGER} push ${OPTIONS} ${TARGET_IMAGE}'"
                          else
                              ${CONTAINER_MANAGER} push ${OPTIONS} ${TARGET_IMAGE}
                              echo "successfully pushed ${TARGET_IMAGE} to ${REGISTRY}"
                          fi
                      done
            parameters:
            - name: dockerd-host
            - name: repository-prefix
            - name: keep-structure
            - name: string-to-replace-slash
            - name: registry-credentials-secret-name
            - name: dry-run-push
          container:
            image: ghcr.io/ben-wangz/blog-docker-cli-with-bash:main
            env:
            - name: DOCKER_HOST
              value: "{{inputs.parameters.dockerd-host}}"
            - name: REPOSITORY_PREFIX
              value: "{{inputs.parameters.repository-prefix}}"
            - name: KEEP_STRUCTURE
              value: "{{inputs.parameters.keep-structure}}"
            - name: STRING_TO_REPLACE_SLASH
              value: "{{inputs.parameters.string-to-replace-slash}}"
            - name: IMAGE_YAML_FILE
              value: "{{inputs.artifacts.images-to-mirror.path}}"
            - name: REGISTRY_INSECURE
              valueFrom:
                secretKeyRef:
                  name: "{{inputs.parameters.registry-credentials-secret-name}}"
                  key: insecure
            - name: REGISTRY
              valueFrom:
                secretKeyRef:
                  name: "{{inputs.parameters.registry-credentials-secret-name}}"
                  key: registry
            - name: REGISTRY_USERNAME
              valueFrom:
                secretKeyRef:
                  name: "{{inputs.parameters.registry-credentials-secret-name}}"
                  key: username
            - name: REGISTRY_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: "{{inputs.parameters.registry-credentials-secret-name}}"
                  key: password
            - name: DRY_RUN_PUSH
              value: "{{inputs.parameters.dry-run-push}}"
            command:
            - bash
            args:
            - /app/mirror.sh
      
      
  6. submit with argo workflow client
    • argo -n business-workflows submit mirror-container-images.yaml
      
  7. check status
    • argo -n business-workflows list
      
    • # argo -n business-workflows get mirror-container-images-2j5z2
      argo -n business-workflows get @lastest
      
    • # argo -n business-workflows logs mirror-container-images-2j5z2
      argo -n business-workflows logs @latest