SDK - Containers - Returning image name with digest (#1768)

* SDK - Containers - Returning image name with digest

Image building functions now return image name with digest: image_repo@sha256:digest

Fixes https://github.com/kubeflow/pipelines/issues/1715

* Added comments
This commit is contained in:
Alexey Volkov 2019-08-09 17:49:13 -07:00 committed by Kubernetes Prow Robot
parent 69ca3c7e4b
commit 17e0efe51d
4 changed files with 37 additions and 9 deletions

View File

@ -324,13 +324,13 @@ class ComponentBuilder(object):
# Prepare build files
logging.info('Generate build files.')
self._container_builder.build(local_build_dir, self._arc_docker_filename, self._target_image, timeout)
return self._container_builder.build(local_build_dir, self._arc_docker_filename, self._target_image, timeout)
def build_image_from_dockerfile(self, docker_filename, timeout):
""" build_image_from_dockerfile builds an image based on the dockerfile """
with tempfile.TemporaryDirectory() as local_build_dir:
self._prepare_files(local_build_dir, docker_filename)
self._container_builder.build(local_build_dir, self._arc_docker_filename, self._target_image, timeout)
return self._container_builder.build(local_build_dir, self._arc_docker_filename, self._target_image, timeout)
def _configure_logger(logger):
""" _configure_logger configures the logger such that the info level logs
@ -440,11 +440,11 @@ def build_python_component(component_func, target_image, base_image=None, depend
' and push the image to ' +
target_image)
builder = ComponentBuilder(gcs_staging=staging_gcs_path, target_image=target_image, namespace=namespace)
builder.build_image_from_func(component_func,
image_name_with_digest = builder.build_image_from_func(component_func,
base_image=base_image, timeout=timeout,
python_version=python_version, dependency=dependency)
logging.info('Build component complete.')
return _generate_pythonop(component_func, target_image, target_component_file)
return _generate_pythonop(component_func, image_name_with_digest, target_component_file)
def build_docker_image(staging_gcs_path, target_image, dockerfile_path, timeout=600, namespace='kubeflow'):
""" build_docker_image automatically builds a container image based on the specification in the dockerfile and
@ -459,5 +459,6 @@ def build_docker_image(staging_gcs_path, target_image, dockerfile_path, timeout=
"""
_configure_logger(logging.getLogger())
builder = ComponentBuilder(gcs_staging=staging_gcs_path, target_image=target_image, namespace=namespace)
builder.build_image_from_dockerfile(docker_filename=dockerfile_path, timeout=timeout)
image_name_with_digest = builder.build_image_from_dockerfile(docker_filename=dockerfile_path, timeout=timeout)
logging.info('Build image complete.')
return image_name_with_digest

View File

@ -63,7 +63,9 @@ class ContainerBuilder(object):
'args': ['--cache=true',
'--dockerfile=' + docker_filename,
'--context=' + context,
'--destination=' + target_image],
'--destination=' + target_image,
'--digest-file=/dev/termination-log', # This is suggested by the Kaniko devs as a way to return the image digest from Kaniko Pod. See https://github.com/GoogleContainerTools/kaniko#--digest-file
],
'image': 'gcr.io/kaniko-project/executor@sha256:78d44ec4e9cb5545d7f85c1924695c89503ded86a59f92c7ae658afa3cff5400',
'env': [{
'name': 'GOOGLE_APPLICATION_CREDENTIALS',
@ -116,8 +118,19 @@ class ContainerBuilder(object):
logging.info('Start a kaniko job for build.')
from ._k8s_helper import K8sHelper
k8s_helper = K8sHelper()
k8s_helper.run_job(kaniko_spec, timeout)
result_pod_obj = k8s_helper.run_job(kaniko_spec, timeout)
logging.info('Kaniko job complete.')
# Clean up
GCSHelper.remove_gcs_blob(context)
# Returning image name with digest
(image_repo, _, image_tag) = target_image.partition(':')
# When Kaniko build completes successfully, the termination message is the hash digest of the newly built image. Otherwise it's empty. See https://github.com/GoogleContainerTools/kaniko#--digest-file https://kubernetes.io/docs/tasks/debug-application-cluster/determine-reason-pod-failure/#customizing-the-termination-message
termination_message = [status.state.terminated.message for status in result_pod_obj.status.container_statuses if status.name == 'kaniko'][0] # Note: Using status.state instead of status.last_state since last_state entries can still be None
image_digest = termination_message
if not image_digest.startswith('sha256:'):
raise RuntimeError("Kaniko returned invalid image digest: {}".format(image_digest))
strict_image_name = image_repo + '@' + image_digest
logging.info('Built and pushed image: {}.'.format(strict_image_name))
return strict_image_name

View File

@ -108,9 +108,20 @@ class K8sHelper(object):
return False
return api_response
def _read_pod_status(self, pod_name, namespace):
try:
# Using read_namespaced_pod due to the following error: "pods \"kaniko-p2phh\" is forbidden: User \"system:serviceaccount:kubeflow:jupyter-notebook\" cannot get pods/status in the namespace \"kubeflow\""
#api_response = self._corev1.read_namespaced_pod_status(pod_name, namespace)
api_response = self._corev1.read_namespaced_pod(pod_name, namespace)
except k8s_client.rest.ApiException as e:
logging.exception('Exception when calling CoreV1Api->read_namespaced_pod_status: {}\n'.format(str(e)))
return False
return api_response
def run_job(self, yaml_spec, timeout=600):
""" run_job runs a kubernetes job and clean up afterwards """
pod_name, succ = self._create_k8s_job(yaml_spec)
namespace = yaml_spec['metadata']['namespace']
if not succ:
return False
# timeout in seconds
@ -119,8 +130,9 @@ class K8sHelper(object):
logging.info('Kubernetes job failed.')
print(self._read_pod_log(pod_name, yaml_spec))
return False
status_obj = self._read_pod_status(pod_name, namespace)
self._delete_k8s_job(pod_name, yaml_spec)
return succ
return status_obj
@staticmethod
def sanitize_k8s_name(name):

View File

@ -29,7 +29,9 @@ spec:
args: ["--cache=true",
"--dockerfile=dockerfile",
"--context=gs://mlpipeline/kaniko_build.tar.gz",
"--destination=gcr.io/mlpipeline/kaniko_image:latest"]
"--destination=gcr.io/mlpipeline/kaniko_image:latest",
"--digest-file=/dev/termination-log",
]
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /secret/gcp-credentials/user-gcp-sa.json