Skip to content

Commit 3d90dec

Browse files
authored
Docker load ydbd_slice (#12797)
1 parent 6e5868b commit 3d90dec

File tree

3 files changed

+108
-12
lines changed

3 files changed

+108
-12
lines changed

ydb/tools/ydbd_slice/__init__.py

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -857,26 +857,36 @@ def _run(args):
857857

858858
#
859859
# docker and kube scenarios
860-
def build_and_push_docker_image(build_args, docker_package, build_ydbd, image, force_rebuild):
860+
def build_docker_image(build_args, docker_package, build_ydbd, image, force_rebuild):
861861
if docker_package is None:
862862
docker_package = docker.DOCKER_IMAGE_YDBD_PACKAGE_SPEC
863863

864864
logger.debug(f'using docker package spec: {docker_package}')
865865

866866
image_details = docker.docker_inspect(image)
867+
output_path = docker.get_image_output_path(image)
867868

868869
if image_details is None:
869870
logger.debug('ydb image %s is not present on host, building', image)
870871
root = arcadia_root()
871872
ya_package_docker(root, build_args, docker_package, image)
873+
docker.docker_image_save(image, output_path, True)
872874
elif force_rebuild:
873875
logger.debug('ydb image %s is already present on host, rebuilding', image)
874876
root = arcadia_root()
875877
ya_package_docker(root, build_args, docker_package, image)
878+
docker.docker_image_save(image, output_path, True)
876879
else:
877880
logger.debug('ydb image %s is already present on host, using existing image', image)
881+
docker.docker_image_save(image, output_path, False)
878882

879-
docker.docker_push(image)
883+
884+
def push_docker_image(image):
885+
image_details = docker.docker_inspect(image)
886+
if image_details is not None:
887+
docker.docker_push(image)
888+
else:
889+
logger.error('ydb image %s is not present on host, skip', image)
880890

881891

882892
def add_arguments_docker_build_with_remainder(mode, add_force_rebuild=False):
@@ -908,13 +918,24 @@ def add_arguments_docker_build_with_remainder(mode, add_force_rebuild=False):
908918
)
909919

910920

921+
def add_arguments_docker_push_with_remainder(mode):
922+
group = mode.add_argument_group('docker push options')
923+
group.add_argument(
924+
'-i', '--image',
925+
help='Optional: docker image name and tag to push. Conflicts with "-t" argument.',
926+
)
927+
group.add_argument(
928+
'-t', '--tag',
929+
help='Optional: docker image tag to push. Conflicts with "-i" argument. Default is {user}-latest.',
930+
)
931+
932+
911933
def add_docker_build_mode(modes):
912934
def _run(args):
913935
logger.debug("starting docker-build cmd with args '%s'", args)
914936
try:
915937
image = docker.get_image_from_args(args)
916-
build_and_push_docker_image(args.build_args, args.docker_package, False, image, force_rebuild=True)
917-
938+
build_docker_image(args.build_args, args.docker_package, False, image, True)
918939
logger.info('docker-build finished')
919940
except RuntimeError as e:
920941
logger.error(e.args[0])
@@ -929,6 +950,26 @@ def _run(args):
929950
mode.set_defaults(handler=_run)
930951

931952

953+
def add_docker_push_mode(modes):
954+
def _run(args):
955+
logger.debug("starting docker-push cmd with args '%s'", args)
956+
try:
957+
image = docker.get_image_from_args(args)
958+
push_docker_image(image)
959+
logger.info('docker-push finished')
960+
except RuntimeError as e:
961+
logger.error(e.args[0])
962+
sys.exit(1)
963+
964+
mode = modes.add_parser(
965+
"docker-push",
966+
parents=[],
967+
description="Push YDB docker image."
968+
)
969+
add_arguments_docker_push_with_remainder(mode)
970+
mode.set_defaults(handler=_run)
971+
972+
932973
def add_kube_generate_mode(modes):
933974
def _run(args):
934975
logger.debug("starting kube-generate cmd with args '%s'", args)
@@ -989,11 +1030,11 @@ def _run(args):
9891030
try:
9901031
image = docker.get_image_from_args(args)
9911032
if not args.use_prebuilt_image:
992-
build_and_push_docker_image(args.build_args, args.docker_package, False, image, force_rebuild=args.force_rebuild)
1033+
build_docker_image(args.build_args, args.docker_package, False, image, args.force_rebuild)
9931034

9941035
manifests = kube_handlers.get_all_manifests(args.path)
9951036
kube_handlers.manifests_ydb_set_image(args.path, manifests, image)
996-
kube_handlers.slice_install(args.path, manifests, args.wait_ready, args.dynamic_config_type)
1037+
kube_handlers.slice_install(args.path, manifests, args.wait_ready, args.dynamic_config_type, image, args.use_prebuilt_image)
9971038

9981039
logger.info('kube-install finished')
9991040
except RuntimeError as e:
@@ -1036,12 +1077,12 @@ def _run(args):
10361077
try:
10371078
image = docker.get_image_from_args(args)
10381079
if not args.use_prebuilt_image:
1039-
build_and_push_docker_image(args.build_args, args.docker_package, False, image, force_rebuild=args.force_rebuild)
1080+
build_docker_image(args.build_args, args.docker_package, False, image, args.force_rebuild)
10401081

10411082
manifests = kube_handlers.get_all_manifests(args.path)
10421083
manifests = kube_handlers.manifests_ydb_filter_components(args.path, manifests, args.components)
10431084
kube_handlers.manifests_ydb_set_image(args.path, manifests, image)
1044-
kube_handlers.slice_update(args.path, manifests, args.wait_ready, args.dynamic_config_type)
1085+
kube_handlers.slice_update(args.path, manifests, args.wait_ready, args.dynamic_config_type, image, args.use_prebuilt_image)
10451086

10461087
logger.info('kube-update finished')
10471088
except RuntimeError as e:
@@ -1368,6 +1409,7 @@ def main(walle_provider=None):
13681409
add_sample_config_mode(modes)
13691410

13701411
add_docker_build_mode(modes)
1412+
add_docker_push_mode(modes)
13711413
add_kube_generate_mode(modes)
13721414
add_kube_install_mode(modes)
13731415
add_kube_update_mode(modes)

ydb/tools/ydbd_slice/kube/docker.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ def get_image_from_args(args):
4343
return "%s:%s" % (DOCKER_IMAGE_FULL_NAME, tag)
4444

4545

46+
def get_image_output_path(image):
47+
if ":" in image:
48+
image_name, tag = image.split(":")
49+
else:
50+
image_name, tag = image, "latest"
51+
image_base_name = image_name.split("/")[-1]
52+
53+
return f"{image_base_name}.{tag}.tar"
54+
55+
4656
def docker_tag(old, new):
4757
proc = subprocess.Popen(['docker', 'tag', old, new], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
4858
try:
@@ -70,6 +80,20 @@ def docker_inspect(obj):
7080
return json.loads(stdout)
7181

7282

83+
def docker_image_save(image, output_path, overwrite):
84+
if os.path.exists(output_path) and not overwrite:
85+
logger.info(f"Compressed file '{output_path}' already exists, using existing archive")
86+
return
87+
88+
logger.info(f"execute command 'docker save image' for '{image}' to output path '{output_path}'")
89+
proc = subprocess.Popen(['docker', 'image', 'save', image, '-o', output_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
90+
_, stderr = proc.communicate()
91+
if proc.returncode != 0:
92+
raise RuntimeError("docker image save: failed with code %d, error: %s" % (proc.returncode, stderr))
93+
94+
logger.info(f"Docker image '{image}' saved and compressed successfully to '{output_path}'")
95+
96+
7397
def docker_push(image):
7498
proc = subprocess.Popen(['docker', 'push', image], text=True)
7599
proc.communicate()

ydb/tools/ydbd_slice/kube/handlers.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from kubernetes.client import Configuration
88

99
from ydb.tools.ydbd_slice import nodes
10-
from ydb.tools.ydbd_slice.kube import api, kubectl, yaml, generate, cms, dynconfig
10+
from ydb.tools.ydbd_slice.kube import api, kubectl, yaml, generate, cms, dynconfig, docker
1111

1212

1313
logger = logging.getLogger(__name__)
@@ -114,7 +114,7 @@ def update_image(data, image):
114114
data['spec'].pop('version')
115115
image_data = data['spec'].setdefault('image', {})
116116
image_data['name'] = image
117-
image_data['pullPolicy'] = 'Always'
117+
image_data['pullPolicy'] = 'IfNotPresent'
118118

119119

120120
def update_manifest(path, data):
@@ -184,8 +184,34 @@ def manifests_ydb_filter_components(project_path, manifests, update_components):
184184
return result
185185

186186

187+
def slice_docker_load(api_client, project_path, manifests, image, use_prebuilt_image):
188+
if use_prebuilt_image:
189+
logger.info('arg use_prebuilt_image found, nothing to load.')
190+
return
191+
192+
node_list = get_nodes(api_client, project_path, manifests)
193+
if len(node_list) == 0:
194+
logger.info('no nodes found, nothing to load.')
195+
return
196+
197+
built_image_path = docker.get_image_output_path(image)
198+
remote_path = f"/Berkanavt/kikimr/{os.path.basename(built_image_path)}"
199+
200+
image_details = docker.docker_inspect(image)
201+
local_digest = image_details[0]['Id']
202+
203+
node_list = nodes.Nodes(node_list)
204+
node_list.copy(built_image_path, remote_path)
205+
206+
cmd = 'remote_digest=$(sudo crictl inspecti -o json {_image} | jq -r ".status.id"); if [[ "{_local_digest}" != "$remote_digest" ]]; then sudo ctr image import {_remote_path}; fi'.format(
207+
_image=image, _local_digest=local_digest, _remote_path=remote_path
208+
)
209+
node_list.execute_async(cmd)
210+
187211
#
188212
# macro level nodeclaim functions
213+
214+
189215
def slice_namespace_apply(api_client, project_path, manifests):
190216
for (path, _, kind, _, _, data) in manifests:
191217
if kind != 'namespace':
@@ -269,6 +295,8 @@ def wait_for_storage(api_client, project_path, manifests):
269295

270296

271297
# macro level ydb functions
298+
299+
272300
def slice_ydb_apply(api_client, project_path, manifests, dynamic_config_type):
273301
# process storages first
274302
for (path, api_version, kind, namespace, name, data) in manifests:
@@ -462,22 +490,24 @@ def slice_generate(project_path, user, slice_name, template, template_vars):
462490
sys.exit(f'Slice template {template} not implemented.')
463491

464492

465-
def slice_install(project_path, manifests, wait_ready, dynamic_config_type):
493+
def slice_install(project_path, manifests, wait_ready, dynamic_config_type, image, use_prebuilt_image):
466494
with api.ApiClient() as api_client:
467495
slice_namespace_apply(api_client, project_path, manifests)
468496
slice_nodeclaim_apply(api_client, project_path, manifests)
469497
slice_nodeclaim_wait_ready(api_client, project_path, manifests)
470498
slice_ydb_delete(api_client, project_path, manifests)
471499
slice_ydb_storage_wait_pods_deleted(api_client, project_path, manifests)
472500
slice_nodeclaim_format(api_client, project_path, manifests)
501+
slice_docker_load(api_client, project_path, manifests, image, use_prebuilt_image)
473502
slice_ydb_apply(api_client, project_path, manifests, dynamic_config_type)
474503
slice_ydb_wait_ready(api_client, project_path, manifests, wait_ready)
475504

476505

477-
def slice_update(project_path, manifests, wait_ready, dynamic_config_type):
506+
def slice_update(project_path, manifests, wait_ready, dynamic_config_type, image, use_prebuilt_image):
478507
with api.ApiClient() as api_client:
479508
slice_nodeclaim_apply(api_client, project_path, manifests)
480509
slice_nodeclaim_wait_ready(api_client, project_path, manifests)
510+
slice_docker_load(api_client, project_path, manifests, image, use_prebuilt_image)
481511
slice_ydb_apply(api_client, project_path, manifests, dynamic_config_type)
482512
slice_ydb_restart(api_client, project_path, manifests)
483513
slice_ydb_wait_ready(api_client, project_path, manifests, wait_ready)

0 commit comments

Comments
 (0)