Windows10修复/扩容恢复分区
背景 主要是遇到了两个问题: 想给两台Windows虚拟机的C盘扩容,但安装程序将恢复分区直接放在了C盘挡了路 安装KB5034441时遇到了0x80070643错误(参见微软社区的帖子),这一错误与恢复分区容量不足有关 修复和扩容恢复分区的流程其实差不多,所以在此记录一下。 操作流程 S1-创建/扩容恢复分区 此流程参考微软官方的指南。请留意磁盘和分区的索引号,错误的命令会导致数据丢失。 使用管理员身份启动cmd 执行reagentc /info检查WinRE状态,留意下路径和安装磁盘;如果正在运行的话执行reagentc /disable停用WinRE 创建或扩容恢复分区 运行diskpart 执行list disk查看磁盘状况;执行select disk <index>选中系统所在磁盘;执行list part查看分区状况 如果需要扩容恢复分区的话,就需要将C盘缩小一点,然后删除并创建新的恢复分区: 先选中系统分区select part <index>,然后缩小分区大小shrink desired=250 minimum=250 接下来选中恢复分区select part <index>,并删除恢复分区delete part override 而对于我的两台VM而言,恢复分区已经不存在了,接下来便是共通的恢复分区创建步骤,MBR和GPT磁盘略有不同,可通过list disk检查GPT列中是否有星号判断(有星号则为GPT磁盘) 对GPT磁盘,执行create partition primary id=de94bba4-06d1-4d40-a16a-bfd50179d6ac然后gpt attributes =0x8000000000000001;而对MBR磁盘则执行create partition primary id=27 格式化恢复分区format quick fs=ntfs label=”Windows RE tools” 如果为MBR磁盘,追加执行set id=27 检查一下恢复分区是否成功创建list vol S2-修复恢复分区 在完成恢复分区创建/扩容后,尝试执行reagentc /enable启用WinRE,但出现如下报错: REAGENTC.EXE: 未找到 Windows RE 映像。 参考这篇帖子,有两种途径可以修复WinRE。 使用SFC(详情参考这里),执行sfc /scannow,Windows会尝试扫描并修复损坏文件。但这种方法对我的虚拟机并未生效。 从ISO安装镜像提取winre.wim用于修复(参考这篇教程) 挂载Windows安装镜像ISO到X盘(假设),使用7-zip打开X:\sources\install.wim;这里会找到若干个数字编号的目录,可以打开[1].xml查看每个编号对应的Windows版本 选择对应Windows版本的目录,对应找到X:\sources\install.wim\<index>\Windows\System32\Recovery\winre.wim文件,拷贝出来保留 将拷贝出来的winre.wim放到需要修复的系统的C:\Windows\System32\Recovery目录下 此时再次尝试reagentc /enable,若完成修复则可以启用WinRE了;如果S1中完成了对恢复分区的扩容,可以再次尝试安装Windows更新
架设内网Nexus仓库
由于Docker Hub主动退出市场,导致国内不能方便的拉取docker镜像,故计划在内网使用Nexus Repository架设私有仓库,行pull through cache之职能,将下载的镜像/包缓存下来以便多次部署供内网的多设备使用,降低对国内高校镜像站的流量使用;同时考虑到PyPI也可能发起制裁导致不能使用,也计划建立PyPI的内网仓库。 目标 架设Nexus Repository OSS 通过高校镜像站提供的HTTP代理缓存Docker Hub 同上,缓存PyPI仓库 Nexus Repository OSS部署 Nexus Repository对Java 8以上的支持并不是特别好,有挺多坑需要留意。根据官网文档部署的情况下还需要注意以下几点: Java版本选择 目前个人建议使用Java 11,因为当前LTS版本中Java 8太老而Java 17暂不支持OrientDb。我懒得专门部署一个外部数据库所以选择了Java 11,需要进行的改动最小。 # 使用Java 11以上版本的报错 java.lang.IllegalStateException: The maximum Java version for OrientDb is Java 11. Please check current Java version meets this requirement. JVM选项修改 根据文档,使用Java 9以上版本时需要注释和取消注释掉{NEXUS_HOME}\bin\nexus.vmoptions中的部分内容,理论上来说根据配置文件中的说明进行修改即可。但上述部分在随安装包附带的版本中存在缺失若干等号=和路径不正确的问题,即使按照配置文件中的说明也无法直接使用。修改后的JVM配置文件如下,主要是缺少等号导致选项不能正确解析,再次说明对Java 8以上版本何等不上心,几年前的问题在最新版本中仍未修正。 -Xms2703m -Xmx2703m -XX:MaxDirectMemorySize=2703m -XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=../sonatype-work/nexus3/log/jvm.log -XX:-OmitStackTraceInFastThrow -Djava.net.preferIPv4Stack=true -Dkaraf.home=. -Dkaraf.base=. -Dkaraf.etc=etc/karaf -Djava.util.logging.config.file=etc/karaf/java.util.logging.properties -Dkaraf.data=../sonatype-work/nexus3 -Dkaraf.log=../sonatype-work/nexus3/log -Djava.io.tmpdir=../sonatype-work/nexus3/tmp -Dkaraf.startLocalConsole=false -Djdk.tls.ephemeralDHKeySize=2048 # # additional vmoptions needed for Java9+ # --add-reads=java....
使用dnscrypt-proxy架设私有DNS
由于手头的一些设备不太方便甚至不能配置DoH,所以准备在家中的局域网里自建一个DNS代理,将DNS请求转为DoH进行查询,在保障隐私的同时也有着良好的兼容性和可维护性。 在习惯性地开始动手造轮子的同时还是调研了下,dnscrypt-proxy作为比较成熟的软件已经能满足我的需求,且配置方便灵活,资源占用也不高,所以干脆放弃造轮子,直接用现成的。 安装dnscrypt-proxy 虽然Ubuntu仓库提供了dnscrypt-proxy,但毫不意外地进行了“定制”。为了省去debug的功夫,建议按照官方wiki的Linux安装指南手动安装一遍。文档写得还是蛮详细的,这里就不赘述安装流程了,我照着步骤一遍过了。 配置dnscrypt-proxy 其实官方wiki里写得也比较详细了,这里我提几个我格外留意的配置项吧 DNS服务器 我注释掉了所有sources,只使用手动配置的static服务器。一方面是访问官方的sources列表存在困难,而第三方提供的列表很难完全信任;另一方面是家庭使用情况下,通常也只用的上那么几个固定的DNS服务器,手动配置一下反而更方便维护和排错。 我使用的是Cloudflare和NextDNS的DoH服务,手动生成了复数个使用不同IP的备选项,当无法正确解析DoH服务器的IP时至少有个兜底。通过这个工具可以计算所需的形如sdns://的DNS stamp,比如我就在默认的1.1.1.1之外给Cloudflare DoH加了cloudflare-dns.com的备选项。 dnscrypt-proxy会定期测试服务器延迟,默认采用的是lb_strategy = 'p2',从最快的2个服务器中随机选择。也是出于这一考虑我就没在dnscrypt-proxy里配置阿里云DNS作为fallback——毕竟近水楼台,响应肯定永远是最快的。 速度优化 官方wiki里提到了三种速度优化:拦截IPv6、DNS缓存和加密套件。 拦截IPv6(block_ipv6 = true)说实话我没测试出来很显著的性能提升,虽然家里现在没弄IPv6,但考虑到之后可能会用上,加之我还是希望给局域网里的所有设备完整的DNS响应,所以最后还是没配置拦截。 默认的缓存策略会在内存中缓存最近4K条查询,TTL看起来也相对合理。家用场景下更高的cache_size似乎作用不大,可惜用不完给虚拟机配的1GB内存了。 加密套件目前也相对无所谓,我用的x86虚拟机承载DNS服务,不像在树莓派或者路由器的ARM处理器上要省着用计算资源。所以按默认配置以免之后还要查兼容性问题。 Cloaking 简单来说可以类比hosts文件,我给几台局域网下的关键服务器配置了便于访问的域名,省的给每台新机器一个个改hosts。 其它 虽然dnscrypt-proxy提供了丰富的blocking、filtering、forwarding等等功能,但是我把它们大多禁用了。我需要的只是一个安全可靠的DNS服务器,它目前做好域名解析的工作就够了;至于拦截广告和阻止访问某些网站等等,这些工作我觉得可以也应该由设备/应用和它们的使用者自己决定。 应用自建DNS 各个运行桌面/服务器OS的机器都好说,配置自建的DNS服务地址作为首选DNS,备选填上阿里云DNS以防整活失败全家断网。 各个“智能”设备则只能靠路由器DHCP分发DNS了,在路由器配置里同样配置自建DNS首选,然后重启路由器让所有设备重新联网。某些设备可能比较固执,而且我也不知道这些黑箱里到底是怎么执行网络相关逻辑的,总之还是把几个“最需要它们能正常运行”的设备本身也重启了一下。 目前能测试到的DNS请求都走自建DNS了,缓存生效的时间内能立即响应,需要通过DoH请求的域名会慢一点,但实际使用起来没啥感觉。总之我已经在没有通知任何人的情况下把家里路由器上DNS改了,如果这几天家里人没反馈网络有问题的话那就当一切安好了。
SSH“钓鱼”
手头有两个临到期的服务器闲置中,正好拿来实验一下写着玩的SSH“蜜罐”。方法就是伪装成OpenSSH Server在22端口上监听,但对所有请求均返回密码错误,同时记录下此次登入使用的用户名、密码、客户端版本及源IP地址,希望能借此捞到一点密码词典,同时对收集到的数据进行一点统计分析。 蜜罐写的很挫,就暂时不公开代码了。总共钓了大概一个月的鱼,有一些有趣的统计数据还是可以分享下的。 登入尝试次数 澳大利亚:213941,日均7131次 中国大陆:102403,日均3413次 两台服务器分别位于中国大陆和澳大利亚,没有DNS记录指向它们,应该单纯就是被扫到22端口了。数量在意料之中,改端口+套防火墙还是有必要的。 用户名TOP10 澳大利亚 中国大陆 1 root (84.97%) root (71.82%) 2 admin (1.62%) admin (3.48%) 3 user (1.02%) user (2.56%) 4 ubuntu (0.73%) ubuntu (1.62%) 5 test (0.73%) test (1.43%) 6 oracle (0.51%) oracle (1.04%) 7 ftpuser (0.42%) ftpuser (0.96%) 8 usuario (0.38%) debian (0.90%) 9 debian (0.36%) test1 (0.85%) 10 test1 (0.34%) usuario (0.84%) 同样意料之中的优先想拿下root,但稍微意料之外的是出现频率这么高,跟第二名出现了巨大的断层。usuario查了一下是西班牙语的user之意。 密码TOP10 澳大利亚 中国大陆 1 123456 (1.99%) 123456 (2.09%) 2 admin (0....
Windows启动项中的“Program”
在我的Windows启动项里始终有几个名为“Program”的程序,看起来是已经卸载的软件残留,而且禁用了没什么影响。不过看着碍眼,决定处理掉它们。 在任务管理器-启动的标题栏右键,勾选“命令行”,此时能看到启动项对应的命令行。果不其然是因为Program Files忘记加引号导致被截断。令人难绷的是连微软自家的Teams安装程序都犯了这个错,很难不嘲笑微软当年决定用Program Files彰显自家文件系统终于支持空格时给自己挖了多大坑。 同样在上图也可以看到启动项是通过何种方式添加的。通过“文件夹”方式添加的话去到开始菜单的“启动”目录,找到对应快捷方式并删除即可。 C:\Users\(USERNAME)\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp 而通过“注册表”方式添加的话需要到下列注册表位置中查找对应的注册表项: HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run HKEY_CURRENT_USER\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run 找到对应注册表项后删除即可。 当然此种方法也可以用来修复忘记加引号的启动项,类似的找到启动项把引号加上即可——当然最重要的一课可能是避免在路径中使用空格等“特殊符号”。
洲际文件传输的实践
起这个标题可能稍微有点标题党的意思,但考虑到从澳洲往国内传输文件确实跨大洲甚至跨洋了,似乎也不算那么夸张。 起因是手头有一批数十GB起的文件需要传回国内的NAS(主要是我拍摄的风光照片,以及Llama的模型权重),考虑到不像国内快递物流那样方便地可以直接寄U盘,通过网络传输是目前唯一可行的方案。 目前使用的方案——SFTP中转 截至成稿时最佳的传输实践还是朴实但可靠的SFTP传输。我在东亚某地的某台VPS有100M对等带宽和10GB的存储盘,将文件分卷打包后在澳洲依次上传,然后远程回国内再依次下载解压。简单记录了一下传输速度,多个文件同时上下载时基本能跑满带宽。 传输方向 单个文件 多文件并行 澳洲上传 约7.5MB/s 约11MB/s 国内下载 约5MB/s 约9MB/s 其他注意事项: 优点 稳定,没遇到过断连的情况,多数传输一次即可成功 基本能跑满中转服务器的带宽 缺点 如果中转用的服务器存储空间有限,需要分批多次上传/下载,不做自动化的话靠人守着有点费事 上传前下载后校验Hash(反复强调,虽然偶发且概率很低,但确实会出现文件损坏的情况) 使用7z的“极速压缩”对于多数文件取得了压缩时间和压缩率上的平衡,使用“标准压缩”速度显著变慢且几乎不能取得更低的压缩率 Llama参数:压缩率约78% RAW原片:压缩率约87% JPEG导出照片:显而易见的几乎无法压缩,使用“仅存储”即可 使用过但最终放弃的方案 这里还是简单记录一下使用过但放弃了的方案,以及在我的使用场景下为什么它们存在问题。 网盘中转 最先尝试以及最早放弃的方案。百度云盘就不说了,Google网盘在国内也需要额外的神秘开销才能使用。OneDrive虽然在澳洲上传速度很快,但国内直连仅100KB/s左右的下载速度不知道要下到猴年马月去。 一句话总结:国外的网盘在国内要么没法(直接)用,要么速度奇慢。 对象存储中转 与网盘类似的问题:国外的对象存储便宜(甚至有不少免费额度),但在国内下载慢且不稳定,哪怕是套了Cloudflare也没能完全解决断连的问题;国内的对象存储更贵,而且我想尽可能利用已有的资源避免多花钱。所以这条路很快也放弃了。 反向代理 把家里某台跳板机的存储服务反向代理到东亚某地的VPS上,然后在澳洲通过反代出来的服务直接传回家。速度相当难看,最高仅有1MB/s左右。更头疼的是线路始终不稳定,传不了几GB就会断连一次,就不说是什么原因了。 HTTP中转 这里尝试过在东亚某地的VPS上搭建AList存储服务,但发现免费Cloudflare套餐限制请求大小为140MB,导致我没法通过Web控制台上传文件。于是乎还是通过SFTP上传文件,在国内用HTTP下载。问题是很显然我没法用国内特供版的Cloudflare,导致下载过程依旧避免不了的是“从国外网站下载文件”,断连问题依旧不可避。
移动WSL文件系统到指定目录
由于WSL2在NTFS上的IO性能过于捉急,还是决定把JupyterLab环境完全挪到WSL原生的文件系统下。但是WSL默认将文件系统放在系统盘,这样我额外加的NVME SSD就成了摆设,于是需要把承载WSL文件系统的虚拟硬盘文件移动到指定的位置。查询资料后,使用原生工具进行迁移的最佳实践是“导出-挪位置-再导入”,那就按这个来吧。 导出WSL环境 WSL的每个发行版环境实际上是一个最大1TB的vhdx虚拟硬盘,可以直接使用wsl导出这个文件到某个指定目录。 wsl --list --verbose wsl --export Ubuntu-22.04 X:\WSL-Backup\Ubuntu22.vhdx --vhd 重新导入WSL环境 原本的环境我没有急着删——毕竟unregister也很方便,暂时留着以免新环境不能正常使用。这里就要注意导入上面导出的环境时需要起一个不同的名字。这里有两种方式导入:重新导入到某个指定目录,或者在上述的目录原地导入、 wsl --import Ubuntu22New X:\WSL\Ubuntu22New X:\WSL-Backup\Ubuntu22.vhdx --version 2 wsl --import-in-place Ubuntu22New X:\WSL-Backup\Ubuntu22.vhdx 但是导入过程中出现以下报错: 未指定的错误 Error code: Wsl/Service/E_FAIL 说白了,原因不明,但是在这个Github issue里找到了workaround,也就是设置默认WSL版本为WSL2后重新导入。 wsl --set-default-version 2 wsl --import Ubuntu22New E:\WSL\Ubuntu22New E:\WSL-Backup\Ubuntu22.vhdx --vhd 成功后会在指定目录下产生一个ext4.vhdx文件,顾名思义就是WSL使用的ext4文件系统了。实测在原生的文件系统里性能比跨到NTFS性能好上许多,愉快地将之前的Python venv都挪进去了——这时候就体现出虚拟环境的便利了。 设定默认发行版/用户名 设置默认发行版很简单: wsl --set-default Ubuntu22New 但导入的WSL环境不支持通过wsl直接配置默认用户名,于是需要进到环境里手动修改/etc/wsl.conf文件,添加下面的配置项: [user] default=默认用户名 重启环境后(根据微软的文档,需要在登出全部终端后等待至少8s,该环境才会shutdown)再次进入该WSL发行版,就会用设定的默认用户名登入了。 题外话 - Windows Terminal 说实话,Windows终端跟WSL融合的挺好的,目前使用没遇到问题,比起在cmd或者PowerShell里使用WSL体验要好得多,之后没什么问题的话大概会继续用下去。
PyTorch DataLoader在原生Windows和WSL下的性能对比
Windows下的num_workers > 0报错 在Windows下新搭建的PyTorch环境中使用之前的某个课程项目进行测试时,发现设置DataLoader的num_workers大于0会导致模型训练卡死。由于GPU无占用,在JupyterLab日志中找到形如AttributeError: Can't get attribute xxx on <module '__main__' (built-in)>的报错,最后定位到DataLoader上。 注意到不少回答都认为Windows下应该将num_workers设为0,但并未解释原因,实际测试发现训练过程远比进行课程项目时慢,于是进一步搜索希望能够找到更好的解决方法。 根据这个帖子的讨论以及这篇博客介绍,由于Windows多进程机制与Linux存在区别,Windows下multiprocessing不支持使用fork创建进程,而是只能通过spawn创建新的进程;这一点差异就导致导入模块时会使用模块锁,避免多个进程导入同一模块时产生竞争。 解决方案也很简单,将原来项目的内部类(是一个数据集类)放到一个外部文件/模块中,并在原来的代码中导入它。此时multiprocessing便可以正确加载该模块,确保模块只被加载一次,然后在各个进程之间共享。 Windows下的DataLoader性能问题 通过上一节的改动,DataLoader已经可以多线程地加载数据集了。但留意到增加worker数量并不能提升训练速度,CUDA核心占用依然出现间断,且间断时CPU和磁盘使用增加。可以推测认为是DataLoader加载速度跟不上训练速度,GPU经常停下来等待数据加载。 不过这也好理解:Linux下通过fork创建进程的效率显著高于Windows,在这个issue下的讨论也普遍认为Windows的进程机制拖慢了PyTorch的效率。 “那么具体一点,能不能衡量一下Windows拖慢了多少训练速度呢?”由于之前干过一段时间性能测试,这个问题马上冒了出来。好消息是在上面提到的这篇博客中,作者给出了ta测试DataLoader的代码。而我对课程项目的代码做了简单修改,作为附加的测试项目,希望能测试真实训练场景下的性能。 Windows与(Subsystem) Linux下DataLoader性能对比 测试环境 CPU:6核心12线程 内存:32GB GPU:显存16GB 操作系统: Windows 10 Ubuntu 22.04(以WSL运行) 软件: Python 3.10 PyTorch 2.2 CUDA 11.8 测试项目 上述博客中的MNIST数据集仅加载测试 num_workers从0至12以2递增 仅测试加载数据集的用时 课程项目的AlexNet训练-验证5个epoch 涉及加载train、validation两个数据集 每个epoch在train集上训练,并在validation集上验证 (项目写的比较挫,其他hyperparameter就不写出来丢人了,主要只是测加载用时的影响) 测试记录 测试项目1 num_workers WSL用时(s) Windows用时(s) 0 16.22 16.68 2 8.38 14.49 4 4.67 10.71 6 4.00 10.56 8 3.67 11.36 10 3.47 12.22 12 4.06 14....
WSL安装配置JupyterLab与CUDA
由于一个下篇文章会提到的性能问题,需要在WSL重新部署一套CUDA和JupyterLab环境,简单记录一下。 WSL 安装WSL组件 较新的Windows 10/11已经预装有WSL,只需要预先在BIOS中开启VT并在Windows控制面板中启用“Windows虚拟机监控程序平台”。 可以查看支持安装的Linux发行版,不过我这里是炼丹用,还是选Ubuntu了。 wsl --list --online wsl --install -d Ubuntu-22.04 在WSL中使用代理 这里使用了ZingLix的解决方案,用ta写的脚本配置环境变量,使用宿主机的HTTP代理。以下脚本的作者是@ZingLix,我只是在此引用并使用,感谢ta分享的这个好用的脚本 2024-02-24编辑: **注意:**使用. proxy.sh set或source proxy.sh set设置代理;假如使用bash执行脚本的话,环境变量不会被应用 #!/bin/sh hostip=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }') wslip=$(hostname -I | awk '{print $1}') port=<host_proxy_port> PROXY_HTTP="http://${hostip}:${port}" set_proxy(){ export http_proxy="${PROXY_HTTP}" export HTTP_PROXY="${PROXY_HTTP}" export https_proxy="${PROXY_HTTP}" export HTTPS_proxy="${PROXY_HTTP}" } unset_proxy(){ unset http_proxy unset HTTP_PROXY unset https_proxy unset HTTPS_PROXY } test_setting(){ echo "Host ip:" ${hostip} echo "WSL ip:" ${wslip} echo "Current proxy:" $https_proxy } if [ "$1" = "set" ] then set_proxy elif [ "$1" = "unset" ] then unset_proxy elif [ "$1" = "test" ] then test_setting else echo "Unsupported arguments....
Windows安装CUDA
前置工作 升级显卡驱动 安装Visual Studio的桌面应用开发套件(选做?似乎不影响PyTorch) 安装CUDA PyTorch可能需要某个指定版本的CUDA,从官网的归档里可以下载到特定版本的安装包 一路下一步直到选择组件的步骤 选择组件时留意一下CUDA以外的组件版本号,安装包附带的其他组件可能并不是最新的,此种情况下则不覆盖安装旧版本其他组件了 勾选上CUDA,下一步 现在的CUDA安装包会自动配置好PATH,等安装完成后就基本没有需要手动操作的地方了。 验证安装 命令行执行以下命令查看安装的CUDA版本: nvcc --version 然后前往C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8\extras\demo_suite\(默认安装路径),在命令行运行deviceQuery.exe和bandwidthTest.exe。若测试结果均为PASS,则CUDA环境已经成功安装。