Docker 拉镜像报 proxyconnect tcp: EOF 的正确解法
宝塔面板上一键部署一个项目,卡在拉镜像那步: Image mysql:8.2 Error Get "https://registry-1.docker.io/v2/": proxyconnect tcp: EOF Error response from daemon: Get "https://registry-1.docker.io/v2/": proxyconnect tcp: EOF关键词是 proxyconnect tcp: EOF——Docker 通过代理连 Docker Hub 时握手就断了。不是网络挂了,是代理配置有问题。 第一步:看 Docker 是不是走代理 docker info | grep -i proxy如果看到类似: HTTP Proxy: http://127.0.0.1:7890 HTTPS Proxy: http://127.0.0.1:7890说明 Docker daemon 被配了代理。接着验证代理是不是能用: curl -x http://127.0.0.1:7890 https://registry-1.docker.io/v2/返回 {} 或认证提示:代理正常 EOF / timeout:代理进程挂了 Connection refused:代理端口没开三个典型场景系统开了 Clash/v2ray,但 Docker daemon 拿不到——Docker 不会自动继承桌面代理,Mac 上尤其容易踩。 代理协议写错——把 http://127.0.0.1:7890 写成了 https://,直接握手失败。 代理软件本身抽风或被墙——重启一下代理再试。解决办法 方案一:临时不走代理 先确认到底是不是代理的锅: unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY docker pull redis:latest拉得下来就是代理问题,不用再折腾 Docker 本身了。 方案二:配国内镜像源(推荐) 编辑 daemon 配置: sudo mkdir -p /etc/docker sudo nano /etc/docker/daemon.json写入: { "registry-mirrors": [ "https://dockerproxy.com", "https://mirror.ccs.tencentyun.com", "https://registry.docker-cn.com" ] }重启: sudo systemctl daemon-reexec sudo systemctl restart docker再拉镜像基本就通了。注意公开镜像源可用性经常变化,实在拉不下来就换一个。 方案三:正确给 Docker 配代理 如果确实需要走代理,别改环境变量,改 systemd 单元: sudo mkdir -p /etc/systemd/system/docker.service.d sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf写入: [Service] Environment="HTTP_PROXY=http://127.0.0.1:7890" Environment="HTTPS_PROXY=http://127.0.0.1:7890" Environment="NO_PROXY=localhost,127.0.0.1"然后: sudo systemctl daemon-reload sudo systemctl restart docker一句话总结 proxyconnect tcp: EOF = Docker 正在走一个 坏掉的代理。先 unset 代理确认锅在哪儿,然后要么配国内镜像源,要么正确写 systemd 代理配置。
Docker 镜像拉取失败:proxyconnect tcp EOF 与国内镜像源配置
proxyconnect tcp: EOF 这个错误不是 Docker Hub 故障,而是 Docker daemon 正在使用一个不可达的代理。 快速诊断 # 查看 Docker 是否配置了代理 docker info | grep -i proxy# 查看环境变量 env | grep -i proxy如果看到: HTTP Proxy: http://127.0.0.1:7890说明 Docker 正在走代理,需要验证代理是否可用: curl -x http://127.0.0.1:7890 https://registry-1.docker.io/v2/返回 {} 或认证信息 → 代理正常 EOF / timeout → 代理链路断了 Connection refused → 代理未启动方案一:清除代理环境变量(临时) unset http_proxy unset https_proxy unset HTTP_PROXY unset HTTPS_PROXYdocker pull redis:latest如果此时能拉成功,说明就是代理问题。 方案二:配置国内镜像源(推荐) sudo mkdir -p /etc/docker sudo nano /etc/docker/daemon.json写入: { "registry-mirrors": [ "https://mirror.ccs.tencentyun.com", "https://registry.docker-cn.com" ] }然后重启: sudo systemctl daemon-reexec sudo systemctl restart docker验证: docker info | grep -A5 "Registry Mirrors"方案三:正确配置 Docker daemon 使用代理 Docker 不会自动读取系统代理(即使浏览器能上网),需要单独配置: sudo mkdir -p /etc/systemd/system/docker.service.d sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf写入: [Service] Environment="HTTP_PROXY=http://127.0.0.1:7890" Environment="HTTPS_PROXY=http://127.0.0.1:7890" Environment="NO_PROXY=localhost,127.0.0.1"注意:代理地址必须用 http://,不能写 https://: HTTPS_PROXY=https://127.0.0.1:7890 ← 错误 HTTPS_PROXY=http://127.0.0.1:7890 ← 正确重新加载: sudo systemctl daemon-reload sudo systemctl restart docker常见坑 Docker 不继承系统代理:Clash/sing-box 开了,Docker 仍然无法自动使用,必须显式配置 /etc/systemd/system/docker.service.d/http-proxy.conf。 proxyconnect tcp: EOF 和 connection refused 的区别:EOF → 代理在监听但链路断了(软件崩溃、配置错误) refused → 代理根本没有在对应端口监听
Python 移动 / 复制目录的正确写法:shutil 全场景速查
Python 里"移动整个目录"或"把文件全复制到另一处"这类操作,全部靠 shutil。列几个最常用的模板。 移动整个目录 99% 场景直接: import shutilshutil.move(r"C:\source_dir", r"D:\target_dir")行为:目标不存在 → 直接改名/移动 目标已存在 → 源被移到 目标\源目录名 同盘 是秒移(本质是 rename) 跨盘 会退化成复制 + 删除想强制覆盖已有目标: import os, shutilsrc = r"C:\source_dir" dst = r"D:\target_dir\source_dir"if os.path.exists(dst): shutil.rmtree(dst) shutil.move(src, dst)复制整个目录(含子目录) Python 3.8+: import shutilshutil.copytree( r"C:\source_dir", r"D:\target_dir", dirs_exist_ok=True, # 目标已存在时合并写入 )会连带子目录、文件属性一起复制。 只复制当前目录里的文件(跳过子目录) import os, shutilsrc = r"C:\source_dir" dst = r"D:\target_dir" os.makedirs(dst, exist_ok=True)for name in os.listdir(src): s = os.path.join(src, name) if os.path.isfile(s): shutil.copy2(s, os.path.join(dst, name))copy2 会保留 mtime、权限,比 copy 更完整。 只复制某类文件 import os, shutilfor name in os.listdir(src): if name.endswith(".txt"): shutil.copy2( os.path.join(src, name), os.path.join(dst, name), )递归复制文件(扁平化,不保留目录结构) import os, shutilfor root, _, files in os.walk(src): for f in files: shutil.copy2(os.path.join(root, f), os.path.join(dst, f))注意同名文件会互相覆盖,要保结构就直接 copytree。 用 pathlib 版本(更现代) from pathlib import Path import shutilsrc = Path(r"D:\A\deep\folder") dst = Path(r"D:\B\deep\folder")if dst.exists(): shutil.rmtree(dst) dst.parent.mkdir(parents=True, exist_ok=True) shutil.copytree(src, dst)Path 的 parent / mkdir(parents=True) 组合特别适合处理"目标父目录还没创建"的情况。 不要用 os.rename os.rename(src, dst)跨磁盘直接报错 不能覆盖已有目录除非明确是"同盘、无冲突",否则直接上 shutil.move。 一句话总结 移动用 shutil.move,整目录复制用 shutil.copytree(dirs_exist_ok=True)。其它场景在这两个基础上按需组合。
PyInstaller 打包 GUI 程序:无 CMD、文件夹形式
用 PyInstaller 打包一个 Python GUI 程序,想要"启动不弹 CMD 黑框、也不打成单个大文件",一条命令搞定: pyinstaller -w -D main.py参数解释 -w(或 --noconsole):不开控制台窗口。适合 Tkinter、PyQt、PySide、wxPython 之类的 GUI。 -D(或 --onedir):文件夹形式打包。产物结构: dist/ └── main/ ├── main.exe ├── python3x.dll └── ...(依赖 dll、pyd、资源)跟 -F 单文件相比,-D 启动快、崩溃少、易调试,就是需要发一整个文件夹。 加图标 pyinstaller -w -D -i app.ico main.py.ico 文件建议 256×256 或多尺寸 ico。 常见错误组合 GUI 忘了 -w: pyinstaller -D main.py # 会弹黑色 CMD 窗口GUI 只想单文件反而用了 -F -w: pyinstaller -F -w main.py能跑,但每次启动都要把打包体解压到临时目录,启动慢;sys._MEIPASS 拿资源要处理路径。 用 spec 文件更可控 第一次 pyinstaller 会生成 main.spec,之后修改这个文件重新构建: pyinstaller main.specspec 里控制"无 CMD"的字段: exe = EXE( pyz, a.scripts, name='main', console=False, # 无 CMD 窗口 icon='app.ico', ... )有 -w -D 起步足够,需要精细控制 hidden imports、数据文件、加签名时再上 spec。 加静态资源 程序里读了 templates/、assets/ 之类的目录,-D 只会打二进制,不带资源,运行时找不到。加 --add-data: Windows(分隔符 ;): pyinstaller -w -D main.py ^ --add-data "templates;templates" ^ --add-data "assets;assets"macOS / Linux(分隔符 :): pyinstaller -w -D main.py \ --add-data "templates:templates" \ --add-data "assets:assets"代码里读资源要用 PyInstaller 兼容路径: import sys, osdef resource_path(rel): base = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__))) return os.path.join(base, rel)with open(resource_path("templates/index.html")) as f: ...找不到某个包 装了包但打包运行时抛 ModuleNotFoundError——PyInstaller 静态分析漏了动态导入。补 hidden-import: pyinstaller -w -D main.py --hidden-import=some_dynamic_module大量子模块可以直接 collect-all: pyinstaller -w -D main.py --collect-all fastapi --collect-all uvicorn一句话总结 pyinstaller -w -D main.py 是 GUI 程序打包最省心的一条命令。资源要 --add-data、动态导入要 --hidden-import 或 --collect-all。
Vue 项目国际化(i18n)配置:vue-i18n 基础用法
Vue 项目做多语言支持用 vue-i18n,核心是:把文字抽成 key-value 的语言文件,组件里用 $t('key') 调用。 安装 npm install vue-i18n创建语言文件 src/ ├── locales/ │ ├── zh-CN.json │ └── en-US.json// zh-CN.json { "setup": { "logout": "退出登录", "language": "语言设置", "version": "版本更新", "clearCache": "清理缓存", "darkMode": "暗黑模式" }, "ok": "确定", "cancel": "取消" }// en-US.json { "setup": { "logout": "Logout", "language": "Language", "version": "Version Update", "clearCache": "Clear Cache", "darkMode": "Dark Mode" }, "ok": "OK", "cancel": "Cancel" }配置 i18n 实例 // src/i18n/index.ts import { createI18n } from "vue-i18n"; import zhCN from "../locales/zh-CN.json"; import enUS from "../locales/en-US.json";const savedLocale = localStorage.getItem("lang") || "zh-CN";export const i18n = createI18n({ legacy: false, // 使用 Composition API 模式 locale: savedLocale, fallbackLocale: "zh-CN", messages: { "zh-CN": zhCN, "en-US": enUS, }, });// main.ts import { createApp } from "vue"; import App from "./App.vue"; import { i18n } from "./i18n";createApp(App).use(i18n).mount("#app");组件内使用 Options API <template> <button @click="handleLogout">{{ $t('setup.logout') }}</button> <span>{{ $t('ok') }}</span> </template>Composition API import { useI18n } from "vue-i18n";const { t } = useI18n(); console.log(t("setup.logout")); // "退出登录"动态切换语言 import { useI18n } from "vue-i18n";const { locale } = useI18n();function switchLanguage(lang: string) { locale.value = lang; localStorage.setItem("lang", lang); }// 切换到英文 switchLanguage("en-US");带参数的翻译 // zh-CN.json { "greeting": "你好,{name}!", "items": "共 {count} 个项目" }<template> <p>{{ $t('greeting', { name: 'Soulock' }) }}</p> <p>{{ $t('items', { count: 42 }) }}</p> </template>语言列表组件示例 <template> <div v-for="lang in languageList" :key="lang.id" @click="switchTo(lang)"> <span>{{ lang.label }}</span> <span v-if="currentLang === lang.id">✓</span> </div> </template><script setup lang="ts"> import { ref } from "vue"; import { useI18n } from "vue-i18n";const { locale } = useI18n();const languageList = [ { id: "zh-CN", label: "简体中文" }, { id: "en-US", label: "English" }, { id: "ja-JP", label: "日本語" }, ];const currentLang = ref(locale.value);function switchTo(lang: { id: string; label: string }) { locale.value = lang.id; currentLang.value = lang.id; localStorage.setItem("lang", lang.id); } </script>按需加载语言包 语言多时可以懒加载,避免全部打包: async function loadLocale(lang: string) { const messages = await import(`../locales/${lang}.json`); i18n.global.setLocaleMessage(lang, messages.default); i18n.global.locale.value = lang; }第一次切换到某语言时再加载对应文件,减小初始包体积。
