【Python】- 下载docker镜像
直接使用python运行代码:py .\install.py 镜像:版本号,如下图所示,即可开始下载对应的镜像,下载完会在当前目录生成tar文件
代码如下:
import os import sys import gzip from io import BytesIO import json import hashlib import shutil import requests import tarfile import urllib3 urllib3.disable_warnings() if len(sys.argv) != 2 : print('Usage:\n\tdocker_pull.py [registry/][repository/]image[:tag|@digest]\n') exit(1) repo = 'library' tag = 'latest' imgparts = sys.argv[1].split('/') try: img, tag = imgparts[-1].split('@') except ValueError: try: img, tag = imgparts[-1].split(':') except ValueError: img = imgparts[-1] if len(imgparts) > 1 and ('.' in imgparts[0] or ':' in imgparts[0]): registry = imgparts[0] repo = '/'.join(imgparts[1:-1]) else: registry = 'registry-1.docker.io' if len(imgparts[:-1]) != 0: repo = '/'.join(imgparts[:-1]) else: repo = 'library' repository = '{}/{}'.format(repo, img) auth_url = 'https://auth.docker.io/token' reg_service = 'registry.docker.io' resp = requests.get('https://{}/v2/'.format(registry), verify=False) if resp.status_code == 401: auth_url = resp.headers['WWW-Authenticate'].split('"')[1] try: reg_service = resp.headers['WWW-Authenticate'].split('"')[3] except IndexError: reg_service = "" def get_auth_head(mtype): resp = requests.get('{}?service={}&scope=repository:{}:pull'.format(auth_url, reg_service, repository), verify=False) access_token = resp.json()['token'] return {'Authorization': 'Bearer ' + access_token, 'Accept': mtype} def progress_bar(ublob, nb_traits): sys.stdout.write('\r' + ublob[7:19] + ': Downloading [') for i in range(0, nb_traits): sys.stdout.write('>' if i == nb_traits - 1 else '=') for i in range(0, 49 - nb_traits): sys.stdout.write(' ') sys.stdout.write(']') sys.stdout.flush() # Step 1: Get manifest auth_head = get_auth_head('application/vnd.docker.distribution.manifest.v2+json') resp = requests.get('https://{}/v2/{}/manifests/{}'.format(registry, repository, tag), headers=auth_head, verify=False) # Step 2: If not valid manifest, maybe it's a manifest list if (resp.status_code != 200) or ('layers' not in resp.json()): print('[*] Falling back to manifest list...') auth_head = get_auth_head('application/vnd.docker.distribution.manifest.list.v2+json') resp = requests.get('https://{}/v2/{}/manifests/{}'.format(registry, repository, tag), headers=auth_head, verify=False) if (resp.status_code != 200): print('[-] Cannot fetch manifest list for {} [HTTP {}]'.format(repository, resp.status_code)) print(resp.content) exit(1) manifest_list = resp.json() matched = False for manifest in manifest_list.get('manifests', []): platform = manifest.get('platform', {}) if platform.get('architecture') == 'amd64' and platform.get('os') == 'linux': digest = manifest['digest'] print(f"[+] Found amd64/linux manifest: {digest}") auth_head = get_auth_head('application/vnd.docker.distribution.manifest.v2+json') resp = requests.get( 'https://{}/v2/{}/manifests/{}'.format(registry, repository, digest), headers=auth_head, verify=False ) matched = True break if not matched or 'layers' not in resp.json(): print('[-] Could not find matching architecture or manifest is invalid') exit(1) # Step 3: Now we are sure we have a proper manifest with layers layers = resp.json()['layers'] imgdir = 'tmp_{}_{}'.format(img, tag.replace(':', '@')) os.mkdir(imgdir) print('Creating image structure in: ' + imgdir) config = resp.json()['config']['digest'] confresp = requests.get('https://{}/v2/{}/blobs/{}'.format(registry, repository, config), headers=auth_head, verify=False) file = open('{}/{}.json'.format(imgdir, config[7:]), 'wb') file.write(confresp.content) file.close() content = [{ 'Config': config[7:] + '.json', 'RepoTags': [], 'Layers': [] }] if len(imgparts[:-1]) != 0: content[0]['RepoTags'].append('/'.join(imgparts[:-1]) + '/' + img + ':' + tag) else: content[0]['RepoTags'].append(img + ':' + tag) empty_json = '{"created":"1970-01-01T00:00:00Z","container_config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false, "StdinOnce":false,"Env":null,"Cmd":null,"Image":"", "Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null}}' parentid = '' for layer in layers: ublob = layer['digest'] fake_layerid = hashlib.sha256((parentid + '\n' + ublob + '\n').encode('utf-8')).hexdigest() layerdir = imgdir + '/' + fake_layerid os.mkdir(layerdir) file = open(layerdir + '/VERSION', 'w') file.write('1.0') file.close() sys.stdout.write(ublob[7:19] + ': Downloading...') sys.stdout.flush() auth_head = get_auth_head('application/vnd.docker.distribution.manifest.v2+json') bresp = requests.get('https://{}/v2/{}/blobs/{}'.format(registry, repository, ublob), headers=auth_head, stream=True, verify=False) if bresp.status_code != 200 and 'urls' in layer: bresp = requests.get(layer['urls'][0], headers=auth_head, stream=True, verify=False) if bresp.status_code != 200: print('\rERROR: Cannot download layer {} [HTTP {}]'.format(ublob[7:19], bresp.status_code)) print(bresp.content) exit(1) bresp.raise_for_status() unit = int(bresp.headers['Content-Length']) / 50 acc = 0 nb_traits = 0 progress_bar(ublob, nb_traits) with open(layerdir + '/layer_gzip.tar', "wb") as file: for chunk in bresp.iter_content(chunk_size=8192): if chunk: file.write(chunk) acc += 8192 if acc > unit: nb_traits += 1 progress_bar(ublob, nb_traits) acc = 0 sys.stdout.write("\r{}: Extracting...{}".format(ublob[7:19], " " * 50)) sys.stdout.flush() with open(layerdir + '/layer.tar', "wb") as file: unzLayer = gzip.open(layerdir + '/layer_gzip.tar', 'rb') shutil.copyfileobj(unzLayer, file) unzLayer.close() os.remove(layerdir + '/layer_gzip.tar') print("\r{}: Pull complete [{}]".format(ublob[7:19], bresp.headers['Content-Length'])) content[0]['Layers'].append(fake_layerid + '/layer.tar') file = open(layerdir + '/json', 'w') if layers[-1]['digest'] == layer['digest']: json_obj = json.loads(confresp.content) json_obj.pop('history', None) json_obj.pop('rootfs', None) else: json_obj = json.loads(empty_json) json_obj['id'] = fake_layerid if parentid: json_obj['parent'] = parentid parentid = json_obj['id'] file.write(json.dumps(json_obj)) file.close() file = open(imgdir + '/manifest.json', 'w') file.write(json.dumps(content)) file.close() if len(imgparts[:-1]) != 0: content = {'/'.join(imgparts[:-1]) + '/' + img: {tag: fake_layerid}} else: content = {img: {tag: fake_layerid}} file = open(imgdir + '/repositories', 'w') file.write(json.dumps(content)) file.close() docker_tar = repo.replace('/', '_') + '_' + img + '.tar' sys.stdout.write("Creating archive...") sys.stdout.flush() tar = tarfile.open(docker_tar, "w") tar.add(imgdir, arcname=os.path.sep) tar.close() shutil.rmtree(imgdir) print('\rDocker image pulled: ' + docker_tar)