用 Python 實現(xiàn) Docker 鏡像批量推送(帶進(jìn)度條)
背景
在本地調(diào)試或搭建漏洞復(fù)現(xiàn)環(huán)境時,我們經(jīng)常會使用到各種公開的 Docker 鏡像(如 vulhub/、jboss/、portainer/ 等)。但出于以下原因,你可能需要將這些鏡像遷移到自己的 Docker Hub 倉庫:
- 防止官方倉庫刪除或無法訪問
- 在隔離環(huán)境中部署
- 長期保存當(dāng)前使用的鏡像版本
- 配合自己的
docker-compose.yml統(tǒng)一管理
手動一個個 docker tag + docker push 很繁瑣,尤其當(dāng)鏡像數(shù)量多、體積大時。因此我寫了一個 Python 腳本,它可以:
- ? 自動讀取當(dāng)前所有容器(包括停止的)使用的鏡像
- ? 去除原始倉庫前綴(如
xxx/),打上自己的倉庫標(biāo)簽 - ? 帶 實時進(jìn)度條(基于
tqdm)顯示每一層的上傳進(jìn)度 - ? 增量上傳:已存在的層會自動跳過,不會重復(fù)消耗流量
腳本代碼
將以下代碼保存為 push_images_with_progress.py:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import docker
import sys
from tqdm import tqdm
# ========== 配置 ==========
DOCKER_USERNAME = "rockmelodies" # 你的 Docker Hub 用戶名
# =========================
client = docker.from_env()
def get_unique_images_from_all_containers():
"""獲取所有容器(包括停止的)使用的鏡像名(唯一)"""
containers = client.containers.list(all=True)
images = set()
for c in containers:
# 鏡像名格式:repository:tag
image_full = c.image.tags[0] if c.image.tags else f"{c.image.id[:12]}"
images.add(image_full)
return images
def short_image_name(full_name):
"""從 vulhub/nginx:1.11.13 提取 nginx:1.11.13"""
# 去掉第一個 / 之前的所有內(nèi)容
if '/' in full_name:
return full_name.split('/', 1)[-1]
return full_name
def tag_and_push(image_full):
"""打標(biāo)簽并推送,顯示進(jìn)度條"""
short = short_image_name(image_full)
new_tag = f"{DOCKER_USERNAME}/{short}"
print(f"\n?? 處理鏡像: {image_full}")
print(f" → 新標(biāo)簽: {new_tag}")
# 1. 拉取原始鏡像(確保本地存在)
try:
client.images.get(image_full)
except docker.errors.ImageNotFound:
print(f" ?? 本地未找到 {image_full},嘗試?yán)?..")
client.images.pull(image_full)
# 2. 打標(biāo)簽
img = client.images.get(image_full)
img.tag(new_tag)
# 3. 推送(帶進(jìn)度條)
print(" ?? 開始推送...")
layers_progress = {}
for line in client.images.push(new_tag, stream=True, decode=True):
if 'id' in line and 'progressDetail' in line:
layer_id = line['id']
progress = line['progressDetail']
total = progress.get('total')
current = progress.get('current')
if total and current:
if layer_id not in layers_progress:
layers_progress[layer_id] = tqdm(
total=total, unit='B', unit_scale=True,
desc=f" 層 {layer_id[:12]}", leave=False
)
layers_progress[layer_id].update(current - layers_progress[layer_id].n)
if current >= total:
layers_progress[layer_id].close()
del layers_progress[layer_id]
elif 'status' in line:
status = line['status']
if 'already exists' in status.lower():
tqdm.write(f" ?? {status}")
print(f" ? 推送完成: {new_tag}\n")
def main():
print("?? 獲取所有容器使用的鏡像...")
images = get_unique_images_from_all_containers()
if not images:
print("? 沒有找到任何容器鏡像。")
sys.exit(1)
print(f"?? 找到 {len(images)} 個唯一鏡像:")
for img in images:
print(f" - {img}")
for img in images:
try:
tag_and_push(img)
except Exception as e:
print(f"? 推送失敗 {img}: {e}")
print("\n?? 所有鏡像處理完畢!")
if __name__ == "__main__":
main()使用步驟
1. 安裝依賴
pip install docker tqdm
如果提示 pip 未找到,請先確保 Python 環(huán)境正常(必要時使用 python -m ensurepip --upgrade)。
2. 登錄 Docker Hub
docker login
輸入你的 Docker ID 和 個人訪問令牌(推薦) 或密碼。
3. 修改腳本中的用戶名
將 DOCKER_USERNAME = "rockmelodies" 改為你自己的 Docker Hub 用戶名。
4. 運行腳本
python3 push_images_with_progress.py
運行效果示例
?? 獲取所有容器使用的鏡像...
?? 找到 11 個唯一鏡像:
- vulhub/comfyui:3.39.1-with-manager
- vulhub/webmin:1.910
...
?? 處理鏡像: vulhub/comfyui:3.39.1-with-manager
→ 新標(biāo)簽: rockmelodies/comfyui:3.39.1-with-manager
?? 開始推送...
層 a1b2c3d4e5f6: 45%|████▌ | 45.2M/100M [00:05<00:06, 8.2MB/s]
?? Layer already exists
? 推送完成: rockmelodies/comfyui:3.39.1-with-manager
?? 所有鏡像處理完畢!核心優(yōu)勢
?? 增量上傳(不會重復(fù)推送)
- 腳本使用
docker.images.push(stream=True)獲取推送過程的實時輸出。 - Docker 底層采用分層存儲,如果遠(yuǎn)程倉庫已有某層(如之前推送過),會顯示
Layer already exists并直接跳過,不會重新傳輸數(shù)據(jù)。 - 因此即使多次運行腳本,也不會浪費流量和時間。
?? 精確進(jìn)度條
- 基于
tqdm,為每一個正在上傳的鏡像層繪制獨立的進(jìn)度條。 - 實時顯示已傳輸大小、總大小、傳輸速度。
- 當(dāng)層傳輸完成后進(jìn)度條自動關(guān)閉。
?? 自動提取鏡像列表
- 調(diào)用 Docker API 獲取所有容器(
all=True包括已停止的)。 - 自動去重,同一個鏡像不會重復(fù)處理。
- 無需手動維護(hù)鏡像列表,適合動態(tài)環(huán)境。
??? 智能打標(biāo)簽
- 自動去除原始倉庫前綴(如
vulhub/、jboss/、portainer/)。 - 例如:
vulhub/nginx:1.11.13→你的用戶名/nginx:1.11.13 - 簡潔且符合 Docker Hub 命名規(guī)范。
常見問題
Q1:推送時提示denied: requested access to the resource is denied
A:沒有登錄或登錄憑據(jù)無效。執(zhí)行 docker login 并確認(rèn)用戶名/令牌正確。
Q2:腳本運行時卡住不動?
A:大型鏡像的上傳需要時間,進(jìn)度條可能會短暫沒有更新,實際仍在傳輸。請耐心等待。如果長時間無響應(yīng),檢查網(wǎng)絡(luò)連接。
Q3:我不想推送所有鏡像,只想推送某幾個怎么辦?
A:可以修改 get_unique_images_from_all_containers() 函數(shù),增加過濾邏輯;或直接在腳本中定義一個白名單列表。
Q4:推送完成后如何確認(rèn)鏡像在 Docker Hub 上?
A:登錄 https://hub.docker.com/u/你的用戶名 查看倉庫列表。也可以執(zhí)行 docker pull 你的用戶名/鏡像名:標(biāo)簽 測試。
擴(kuò)展想法
- 可以添加
--dry-run模式,只顯示將要推送的鏡像而不實際執(zhí)行。 - 支持多個目標(biāo)倉庫(如同時推送到阿里云鏡像倉庫)。
- 增加失敗重試機(jī)制和日志記錄。
總結(jié)
這個 Python 腳本讓你可以用一條命令將本地所有運行過的 Docker 鏡像完整遷移到自己的 Docker Hub 倉庫,并實時看到推送進(jìn)度。而且由于 Docker 分層的特性,重復(fù)執(zhí)行也不會造成資源浪費。
如果你也在管理多個漏洞復(fù)現(xiàn)環(huán)境或自定義鏡像,不妨試試這個腳本,讓鏡像遷移變得輕松又透明。
到此這篇關(guān)于用 Python 實現(xiàn) Docker 鏡像批量推送(帶進(jìn)度條)的文章就介紹到這了,更多相關(guān)Python Docker 鏡像批量推送內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
10張動圖學(xué)會python循環(huán)與遞歸問題
今天為大家整理了十張動圖GIFS,有助于認(rèn)識循環(huán)、遞歸、二分檢索等概念的具體運行情況。代碼實例以Python語言編寫,非常不錯,感興趣的朋友跟隨小編一起學(xué)習(xí)吧2021-02-02
Python從Manim中提取表格/坐標(biāo)系并轉(zhuǎn)GIF的四種高效方案
在數(shù)據(jù)可視化和數(shù)學(xué)動畫創(chuàng)作中,我們經(jīng)常需要將 Manim 動畫中的表格、坐標(biāo)系等核心元素單獨導(dǎo)出為 GIF,本文整理了四種高效方案,每種方案僅提供核心代碼,聚焦關(guān)鍵實現(xiàn)邏輯,需要的朋友可以參考下2025-08-08
Python OpenCV基于霍夫圈變換算法檢測圖像中的圓形
這篇文章主要介紹了通過霍夫圈變換算法檢測圖像中的圓形,文中用到的函數(shù)為cv2.HoughCircles(),該函數(shù)可以很好地檢測圓心。感興趣的小伙伴可以了解一下2021-12-12
python將圖片轉(zhuǎn)base64,實現(xiàn)前端顯示
今天小編就為大家分享一篇python將圖片轉(zhuǎn)base64,實現(xiàn)前端顯示,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01
python中windows鏈接linux執(zhí)行命令并獲取執(zhí)行狀態(tài)的問題小結(jié)
這篇文章主要介紹了python中windows鏈接linux執(zhí)行命令并獲取執(zhí)行狀態(tài),由于工具是pyqt寫的所以牽扯到用python鏈接linux的問題,這里記錄一下一些碰到的問題,需要的朋友可以參考下2022-11-11
python讀取json文件并將數(shù)據(jù)插入到mongodb的方法
這篇文章主要介紹了python讀取json文件并將數(shù)據(jù)插入到mongodb的方法,實例分析了Python操作json及mongodb數(shù)據(jù)庫的技巧,需要的朋友可以參考下2015-03-03
基于MATLAB和Python實現(xiàn)MFCC特征參數(shù)提取
這篇文章主要介紹了基于MATLAB和Python實現(xiàn)MFCC特征參數(shù)提取,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08

