Merge pull request #780 from bogdando/downloads

Add download_always_pull check and sha256 for docker images
This commit is contained in:
Bogdan Dobrelya 2016-12-21 11:02:57 +01:00 committed by GitHub
commit cf4f2b4f14
5 changed files with 113 additions and 22 deletions

42
docs/downloads.md Normal file
View file

@ -0,0 +1,42 @@
Downloading binaries and containers
===================================
Kargo supports several download/upload modes. The default is:
* Each node downloads binaries and container images on its own, which is
``download_run_once: False``.
* For K8s apps, pull policy is ``k8s_image_pull_policy: IfNotPresent``.
* For system managed containers, like kubelet or etcd, pull policy is
``download_always_pull: False``, which is pull if only the wanted repo and
tag/sha256 digest differs from that the host has.
There is also a "pull once, push many" mode as well:
* Override the ``download_run_once: True`` to download container images only once
then push to cluster nodes in batches. The default delegate node
for pushing images is the first `kube-master`.
* If your ansible runner node (aka the admin node) have password-less sudo and
docker enabled, you may want to define the ``download_localhost: True``, which
makes that node a delegate for pushing images while running the deployment with
ansible. This maybe the case if cluster nodes cannot access each over via ssh
or you want to use local docker images as a cache for multiple clusters.
Container images and binary files are described by the vars like ``foo_version``,
``foo_download_url``, ``foo_checksum`` for binaries and ``foo_image_repo``,
``foo_image_tag`` or optional ``foo_digest_checksum`` for containers.
Container images may be defined by its repo and tag, for example:
`andyshinn/dnsmasq:2.72`. Or by repo and tag and sha256 digest:
`andyshinn/dnsmasq@sha256:7c883354f6ea9876d176fe1d30132515478b2859d6fc0cbf9223ffdc09168193`.
Note, the sha256 digest and the image tag must be both specified and correspond
to each other. The given example above is represented by the following vars:
```
dnsmasq_digest_checksum: 7c883354f6ea9876d176fe1d30132515478b2859d6fc0cbf9223ffdc09168193
dnsmasq_image_repo: andyshinn/dnsmasq
dnsmasq_image_tag: '2.72'
```
The full list of available vars may be found in the download's ansible role defaults.
Those also allow to specify custom urls and local repositories for binaries and container
images as well. See also the DNS stack docs for the related intranet configuration,
so the hosts can resolve those urls and repos.

View file

@ -8,14 +8,8 @@ For a large scaled deployments, consider the following configuration changes:
* Override containers' `foo_image_repo` vars to point to intranet registry. * Override containers' `foo_image_repo` vars to point to intranet registry.
* Override the ``download_run_once: true`` to download container images only once * Override the ``download_run_once: true`` and/or ``download_localhost: true``.
then push to cluster nodes in batches. The default delegate node See download modes for details.
for pushing images is the first kube-master. Note, if you have passwordless sudo
and docker enabled on the separate admin node, you may want to define the
``download_localhost: true``, which makes that node a delegate for pushing images
while running the deployment with ansible. This maybe the case if cluster nodes
cannot access each over via ssh or you want to use local docker images as a cache
for multiple clusters.
* Adjust the `retry_stagger` global var as appropriate. It should provide sane * Adjust the `retry_stagger` global var as appropriate. It should provide sane
load on a delegate (the first K8s master node) then retrying failed load on a delegate (the first K8s master node) then retrying failed

View file

@ -14,6 +14,9 @@ download_compress: 9
# in the download_run_once mode. # in the download_run_once mode.
download_localhost: False download_localhost: False
# Always pull images if set to True. Otherwise check by the repo's tag/digest.
download_always_pull: False
# Versions # Versions
etcd_version: v3.0.6 etcd_version: v3.0.6
#TODO(mattymo): Move calico versions to roles/network_plugins/calico/defaults #TODO(mattymo): Move calico versions to roles/network_plugins/calico/defaults
@ -85,16 +88,19 @@ downloads:
container: true container: true
repo: "{{ netcheck_server_img_repo }}" repo: "{{ netcheck_server_img_repo }}"
tag: "{{ netcheck_tag }}" tag: "{{ netcheck_tag }}"
sha256: "{{ netcheck_server_digest_checksum|default(None) }}"
enabled: "{{ deploy_netchecker|bool }}" enabled: "{{ deploy_netchecker|bool }}"
netcheck_agent: netcheck_agent:
container: true container: true
repo: "{{ netcheck_agent_img_repo }}" repo: "{{ netcheck_agent_img_repo }}"
tag: "{{ netcheck_tag }}" tag: "{{ netcheck_tag }}"
sha256: "{{ netcheck_agent_digest_checksum|default(None) }}"
enabled: "{{ deploy_netchecker|bool }}" enabled: "{{ deploy_netchecker|bool }}"
netcheck_kubectl: netcheck_kubectl:
container: true container: true
repo: "{{ netcheck_kubectl_img_repo }}" repo: "{{ netcheck_kubectl_img_repo }}"
tag: "{{ netcheck_kubectl_tag }}" tag: "{{ netcheck_kubectl_tag }}"
sha256: "{{ netcheck_kubectl_digest_checksum|default(None) }}"
enabled: "{{ deploy_netchecker|bool }}" enabled: "{{ deploy_netchecker|bool }}"
weave: weave:
dest: weave/bin/weave dest: weave/bin/weave
@ -108,7 +114,8 @@ downloads:
etcd: etcd:
version: "{{etcd_version}}" version: "{{etcd_version}}"
dest: "etcd/etcd-{{ etcd_version }}-linux-amd64.tar.gz" dest: "etcd/etcd-{{ etcd_version }}-linux-amd64.tar.gz"
sha256: "{{ etcd_checksum }}" sha256: >-
{%- if etcd_deployment_type == 'docker' -%}{{etcd_digest_checksum|default(None)}}{%- else -%}{{etcd_checksum}}{%- endif -%}
source_url: "{{ etcd_download_url }}" source_url: "{{ etcd_download_url }}"
url: "{{ etcd_download_url }}" url: "{{ etcd_download_url }}"
unarchive: true unarchive: true
@ -121,64 +128,78 @@ downloads:
container: true container: true
repo: "{{ hyperkube_image_repo }}" repo: "{{ hyperkube_image_repo }}"
tag: "{{ hyperkube_image_tag }}" tag: "{{ hyperkube_image_tag }}"
sha256: "{{ hyperkube_digest_checksum|default(None) }}"
flannel: flannel:
container: true container: true
repo: "{{ flannel_image_repo }}" repo: "{{ flannel_image_repo }}"
tag: "{{ flannel_image_tag }}" tag: "{{ flannel_image_tag }}"
sha256: "{{ flannel_digest_checksum|default(None) }}"
enabled: "{{ kube_network_plugin == 'flannel' or kube_network_plugin == 'canal' }}" enabled: "{{ kube_network_plugin == 'flannel' or kube_network_plugin == 'canal' }}"
calicoctl: calicoctl:
container: true container: true
repo: "{{ calicoctl_image_repo }}" repo: "{{ calicoctl_image_repo }}"
tag: "{{ calicoctl_image_tag }}" tag: "{{ calicoctl_image_tag }}"
sha256: "{{ calicoctl_digest_checksum|default(None) }}"
enabled: "{{ kube_network_plugin == 'calico' or kube_network_plugin == 'canal' }}" enabled: "{{ kube_network_plugin == 'calico' or kube_network_plugin == 'canal' }}"
calico_node: calico_node:
container: true container: true
repo: "{{ calico_node_image_repo }}" repo: "{{ calico_node_image_repo }}"
tag: "{{ calico_node_image_tag }}" tag: "{{ calico_node_image_tag }}"
sha256: "{{ calico_node_digest_checksum|default(None) }}"
enabled: "{{ kube_network_plugin == 'calico' or kube_network_plugin == 'canal' }}" enabled: "{{ kube_network_plugin == 'calico' or kube_network_plugin == 'canal' }}"
calico_cni: calico_cni:
container: true container: true
repo: "{{ calico_cni_image_repo }}" repo: "{{ calico_cni_image_repo }}"
tag: "{{ calico_cni_image_tag }}" tag: "{{ calico_cni_image_tag }}"
sha256: "{{ calico_cni_digest_checksum|default(None) }}"
enabled: "{{ kube_network_plugin == 'calico' or kube_network_plugin == 'canal' }}" enabled: "{{ kube_network_plugin == 'calico' or kube_network_plugin == 'canal' }}"
calico_policy: calico_policy:
container: true container: true
repo: "{{ calico_policy_image_repo }}" repo: "{{ calico_policy_image_repo }}"
tag: "{{ calico_policy_image_tag }}" tag: "{{ calico_policy_image_tag }}"
sha256: "{{ calico_policy_digest_checksum|default(None) }}"
enabled: "{{ kube_network_plugin == 'canal' }}" enabled: "{{ kube_network_plugin == 'canal' }}"
calico_rr: calico_rr:
container: true container: true
repo: "{{ calico_rr_image_repo }}" repo: "{{ calico_rr_image_repo }}"
tag: "{{ calico_rr_image_tag }}" tag: "{{ calico_rr_image_tag }}"
sha256: "{{ calico_rr_digest_checksum|default(None) }}"
enabled: "{{ peer_with_calico_rr is defined and peer_with_calico_rr}} and kube_network_plugin == 'calico'" enabled: "{{ peer_with_calico_rr is defined and peer_with_calico_rr}} and kube_network_plugin == 'calico'"
pod_infra: pod_infra:
container: true container: true
repo: "{{ pod_infra_image_repo }}" repo: "{{ pod_infra_image_repo }}"
tag: "{{ pod_infra_image_tag }}" tag: "{{ pod_infra_image_tag }}"
sha256: "{{ pod_infra_digest_checksum|default(None) }}"
nginx: nginx:
container: true container: true
repo: "{{ nginx_image_repo }}" repo: "{{ nginx_image_repo }}"
tag: "{{ nginx_image_tag }}" tag: "{{ nginx_image_tag }}"
sha256: "{{ nginx_digest_checksum|default(None) }}"
dnsmasq: dnsmasq:
container: true container: true
repo: "{{ dnsmasq_image_repo }}" repo: "{{ dnsmasq_image_repo }}"
tag: "{{ dnsmasq_image_tag }}" tag: "{{ dnsmasq_image_tag }}"
sha256: "{{ dnsmasq_digest_checksum|default(None) }}"
kubednsmasq: kubednsmasq:
container: true container: true
repo: "{{ kubednsmasq_image_repo }}" repo: "{{ kubednsmasq_image_repo }}"
tag: "{{ kubednsmasq_image_tag }}" tag: "{{ kubednsmasq_image_tag }}"
sha256: "{{ kubednsmasq_digest_checksum|default(None) }}"
kubedns: kubedns:
container: true container: true
repo: "{{ kubedns_image_repo }}" repo: "{{ kubedns_image_repo }}"
tag: "{{ kubedns_image_tag }}" tag: "{{ kubedns_image_tag }}"
sha256: "{{ kubedns_digest_checksum|default(None) }}"
testbox: testbox:
container: true container: true
repo: "{{ test_image_repo }}" repo: "{{ test_image_repo }}"
tag: "{{ test_image_tag }}" tag: "{{ test_image_tag }}"
sha256: "{{ testbox_digest_checksum|default(None) }}"
exechealthz: exechealthz:
container: true container: true
repo: "{{ exechealthz_image_repo }}" repo: "{{ exechealthz_image_repo }}"
tag: "{{ exechealthz_image_tag }}" tag: "{{ exechealthz_image_tag }}"
sha256: "{{ exechealthz_digest_checksum|default(None) }}"
download: download:
container: "{{ file.container|default('false') }}" container: "{{ file.container|default('false') }}"

View file

@ -39,11 +39,6 @@
mode: "{{ download.mode|default(omit) }}" mode: "{{ download.mode|default(omit) }}"
when: "{{ download.enabled|bool and not download.container|bool and (download.unarchive is not defined or download.unarchive == False) }}" when: "{{ download.enabled|bool and not download.container|bool and (download.unarchive is not defined or download.unarchive == False) }}"
- name: pulling...
debug:
msg: "{{ download.repo }}:{{ download.tag }}"
when: "{{ download.enabled|bool and download.container|bool }}"
- set_fact: - set_fact:
download_delegate: "{% if download_localhost %}localhost{% else %}{{groups['kube-master'][0]}}{% endif %}" download_delegate: "{% if download_localhost %}localhost{% else %}{{groups['kube-master'][0]}}{% endif %}"
tags: facts tags: facts
@ -70,29 +65,41 @@
when: "{{ download_run_once|bool and download.enabled|bool and download.container|bool and download_delegate == 'localhost' }}" when: "{{ download_run_once|bool and download.enabled|bool and download.container|bool and download_delegate == 'localhost' }}"
tags: localhost tags: localhost
- name: Make download decision if pull is required by tag or sha256
include: set_docker_image_facts.yml
when: "{{ download.enabled|bool and download.container|bool }}"
delegate_to: "{{ download_delegate if download_run_once|bool else inventory_hostname }}"
run_once: "{{ download_run_once|bool }}"
tags: facts
- name: pulling...
debug:
msg: "{{ pull_args }}"
when: "{{ download.enabled|bool and download.container|bool }}"
#NOTE(bogdando) this brings no docker-py deps for nodes #NOTE(bogdando) this brings no docker-py deps for nodes
- name: Download containers - name: Download containers if pull is required or told to always pull
command: "/usr/bin/docker pull {{ download.repo }}:{{ download.tag }}" command: "/usr/bin/docker pull {{ pull_args }}"
register: pull_task_result register: pull_task_result
until: pull_task_result|success until: pull_task_result|success
retries: 4 retries: 4
delay: "{{ retry_stagger | random + 3 }}" delay: "{{ retry_stagger | random + 3 }}"
when: "{{ download.enabled|bool and download.container|bool }}" when: "{{ download.enabled|bool and download.container|bool and pull_required|bool|default(download_always_pull) }}"
delegate_to: "{{ download_delegate if download_run_once|bool else inventory_hostname }}" delegate_to: "{{ download_delegate if download_run_once|bool else inventory_hostname }}"
run_once: "{{ download_run_once|bool }}" run_once: "{{ download_run_once|bool }}"
- set_fact: - set_fact:
fname: "{{local_release_dir}}/containers/{{download.repo|regex_replace('/|\0|:', '_')}}:{{download.tag|regex_replace('/|\0|:', '_')}}.tar" fname: "{{local_release_dir}}/containers/{{download.repo|regex_replace('/|\0|:', '_')}}:{{download.tag|default(download.sha256)|regex_replace('/|\0|:', '_')}}.tar"
tags: facts tags: facts
- name: "Set default value for 'container_changed' to false" - name: "Set default value for 'container_changed' to false"
set_fact: set_fact:
container_changed: false container_changed: "{{pull_required|bool|default(false)}}"
- name: "Update the 'container_changed' fact" - name: "Update the 'container_changed' fact"
set_fact: set_fact:
container_changed: "{{ not 'up to date' in pull_task_result.stdout }}" container_changed: "{{ pull_required|bool|default(false) or not 'up to date' in pull_task_result.stdout }}"
when: "{{ download.enabled|bool and download.container|bool }}" when: "{{ download.enabled|bool and download.container|bool and pull_required|bool|default(download_always_pull) }}"
delegate_to: "{{ download_delegate if download_run_once|bool else inventory_hostname }}" delegate_to: "{{ download_delegate if download_run_once|bool else inventory_hostname }}"
run_once: "{{ download_run_once|bool }}" run_once: "{{ download_run_once|bool }}"
tags: facts tags: facts
@ -108,7 +115,7 @@
tags: facts tags: facts
- name: Download | save container images - name: Download | save container images
shell: docker save "{{ download.repo }}:{{ download.tag }}" | gzip -{{ download_compress }} > "{{ fname }}" shell: docker save "{{ pull_args }}" | gzip -{{ download_compress }} > "{{ fname }}"
delegate_to: "{{ download_delegate }}" delegate_to: "{{ download_delegate }}"
register: saved register: saved
run_once: true run_once: true

View file

@ -0,0 +1,27 @@
---
- set_fact:
pull_by_digest: >-
{%- if download.sha256 is defined and download.sha256 != '' -%}true{%- else -%}false{%- endif -%}
- set_fact:
pull_args: >-
{%- if pull_by_digest|bool %}{{download.repo}}@sha256:{{download.sha256}}{%- else -%}{{download.repo}}:{{download.tag}}{%- endif -%}
- name: Register docker images info
shell: "{% raw %}/usr/bin/docker images -q | xargs /usr/bin/docker inspect -f '{{.RepoTags}},{{.RepoDigests}}'{% endraw %}"
register: docker_images_raw
ignore_errors: true
when: not download_always_pull|bool
- set_fact: docker_images="{{docker_images_raw.stdout|regex_replace('\[|\]|\\n]','')|regex_replace('\s',',')}}"
when: not download_always_pull|bool
- set_fact:
pull_required: >-
{%- if pull_args in docker_images.split(',') %}false{%- else -%}true{%- endif -%}
when: not download_always_pull|bool
- name: Check the local digest sha256 corresponds to the given image tag
assert:
that: "{{download.repo}}:{{download.tag}} in docker_images.split(',')"
when: not download_always_pull|bool and not pull_required|bool and pull_by_digest|bool