【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)

浙公网安备 33010602011771号