PVE安装指南

换源 https://skyao.net/learning-pve/docs/installation/source/#pve9 下载镜像 https://www.proxmox.com/en/downloads 内存检查 Proxmox / Linux 安装阶段对内存稳定性非常敏感。内存配置失误会导致安装时进程卡住。 确保内存条不是奇数条(最好是) 确保BIOS设置的内存条为默认频率(一定要) 进入 BIOS,把 EXPO 关闭 关闭Wait for F1 If Error 防止 BIOS 提示按 F1 继续,增加启动时间。 路径:Boot → Wait for “F1” If Error → Disabled 断电自启 正常关机不会触发来电自启动,必须意外断电才行 进入 BIOS F7进入Advance mode(高级模式) 高级电源管理(APM) 将Restore AC Power Loss(断电恢复后电源状态)选项设置为Power On(电源开启) F10保存 注意:禁用 USB 上电唤醒(华硕 B650/B850 主板专属问题) - Disabled(默认,关机后 USB 和一些设备仍带电) - Enabled (S4+S5)(完全断电) - Enabled (S5)(半断电) Advanced → APM Configuration → ErP Ready -> Enabled (S4+S5)(完全断电) ...

January 9, 2026 · 1 min

PVE设置指南

网络 修改IP 通过webui修改ip,结果服务器起不来,最后重装PVE。 所以修改ip还是去/etc/network/interfaces修改吧。 重启后的提示语还是显示原来的https://旧IP:8006。这个要是以后忘了就容易出问题。可以更改/etc/hosts文件,也可以在web管理页面主机处修改,修改完记得保存。此操作修改完需要重启才可刷新显示提示语。 换源 PVE 9.0 基于 Debian 13,除了换 Debian 的软件源以外,还需要编辑企业源、Ceph 源、无订阅源以及 CT 模板源。 Debian 软件源 Debian 13 软件源变更为 DEB822 格式 /etc/apt/sources.list.d/debian.sources ,不再是传统格式 /etc/apt/sources.list 与常规的 Debian 13 一样,将 /etc/apt/sources.list.d/debian.sources 中默认源全部删除,将其替换为清华源 mv /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list.d/debian.sources.bak && cat > /etc/apt/sources.list.d/debian.sources <<'EOF' Types: deb URIs: https://mirrors.tuna.tsinghua.edu.cn/debian Suites: trixie trixie-updates trixie-backports Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: https://security.debian.org/debian-security Suites: trixie-security Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF 企业源 将 PVE 的企业源 /etc/apt/sources.list.d/pve-enterprise.sources 注释掉(也可以直接删除) ...

January 9, 2026 · 1 min

PVE迁移虚拟机

记录从PVE intel 上迁移vm到PVE AMD上。 注意AMD一定要进入 BIOS,把 EXPO 关闭。否则连网络传输大文件都有可能出错。 1. 更改VM处理器类型 改为兼容性更高的x86-64-v2。 kvm64比较老,不推荐。 2. 备份VM 备份文件为 vzdump-qemu-102-2025_12_05-11_50_02.vma.zst 3. 传输备份文件 将备份文件夹传输到另一台PVE rsync -avP /var/lib/vz/dump/vzdump-qemu-102-2025_12_05-11_50_02.vma.zst [email protected]:/var/lib/vz/dump/ 4. 校验文件 分别在原PVE和目标PVE执行,检查两边是否一致。如果不一致考虑是开启了EXPO等超频技术或者其他兼容性问题。 sha256sum /var/lib/vz/dump/vzdump-qemu-102-2025_12_05-11_50_02.vma.zst 5. 恢复VM 方式一: 在webui操作 在webui恢复可能会提示vm-102-cloudinit已经存在: lvcreate 'pve/vm-104-cloudinit' error: Logical Volume "vm-104-cloudinit" already exists in volume group "pve" 处理办法: 移除对应的已存在的cloudinit 可以尝试使用命令行恢复。 方式二: 执行脚本 qmrestore /var/lib/vz/dump/vzdump-qemu-102-2025_12_05-11_50_02.vma.zst 102 \ --storage local-lvm --unique 1 说明: 102:指定VMID –storage local-lvm:明确指定放到现在的 LVM-Thin(data)上,视情况更改 –unique 1:如果备份里有 cloud-init/disk 的旧 volume ID,会强制用新的 volume 名,避免名字冲突。 6. 配置新IP 配置新的IP和网关 ...

January 9, 2026 · 1 min

Docker容器日志撑爆磁盘排查记录

问题背景 生产环境虚拟机磁盘告警,使用率达到 94%,需要排查占用空间的文件并进行清理。 环境信息 操作系统:Ubuntu(虚拟机) 磁盘容量:1.8T,已使用 1.6T 主要服务:GitLab、MySQL、Redis、Nexus3、SkyWalking 等(均运行在 Docker 中) 排查过程 第一步:定位大文件目录 首先使用 docker system df -v 查看 Docker 各组件的磁盘占用: docker system df -v 发现 Images 和 Volumes 占用正常,但 Containers 数据异常。 进一步检查 Docker 数据目录: du -sh /var/lib/docker/* 输出结果: 88K /var/lib/docker/buildkit 556G /var/lib/docker/containers 37M /var/lib/docker/image 188K /var/lib/docker/network 🔴 发现问题:/var/lib/docker/containers 目录占用了 556GB! 第二步:定位具体容器 查看每个容器的日志文件大小: for id in $(ls /var/lib/docker/containers/); do name=$(docker inspect --format '{{.Name}}' $id 2>/dev/null | tr -d '/') log_file="/var/lib/docker/containers/$id/$id-json.log" if [ -f "$log_file" ]; then size=$(ls -lh "$log_file" | awk '{print $5}') echo "$size - $name ($id)" fi done | sort -rh 输出结果: ...

January 9, 2026 · 2 min

使用GitHub Actions自动构建docker镜像并发布到DockerHub

前言 之前写了一个开源项目,每次打tag的时候都要手动构建docker image然后上传实在是太麻烦了。于是研究了下GitHub Actions,发现非常好用。 本文就来讲一下,如何借助 Github Actions 自动构建兼容多 CPU 架构的 docker 镜像并发布到 DockerHub。 配置 使用build-push-action进行实现多CPU架构镜像构建。按照官方文档可以快速使用起来,但是有几个比较容易出错的地方需要注意。 配置案例 创建配置文件.github/workflows/docker-publish.yml name: Docker # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. on: push: # Publish semver tags as releases. tags: [ '**' ] env: # Use docker.io for Docker Hub if empty REGISTRY: 'docker.io' # github.repository as <account>/<repo> IMAGE_NAME: ${{ github.repository }} jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write # This is used to complete the identity challenge # with sigstore/fulcio when running outside of PRs. id-token: write steps: - name: Checkout repository uses: actions/checkout@v4 # Install the cosign tool except on PR # https://github.com/sigstore/cosign-installer - name: Install cosign uses: sigstore/[email protected] with: cosign-release: 'v2.4.0' # Set up BuildKit Docker container builder to be able to build # multi-platform images and export cache # https://github.com/docker/setup-buildx-action - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 # Login against a Docker registry except on PR # https://github.com/docker/login-action - name: Log into registry ${{ env.REGISTRY }} uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} # Extract metadata (tags, labels) for Docker # https://github.com/docker/metadata-action - name: Extract Docker metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} # Build and push Docker image with Buildx (don't push on PR) # https://github.com/docker/build-push-action - name: Build and push Docker image id: build-and-push uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} 很多配置见名知意,对照官方文档也都能找到答案。有的版本官方案例中使用很长一串具体版本,为了方便使用,我改为了数字版本。 ...

December 9, 2025 · 2 min

使用GitHub Actions自动构建程序并发布到Release

前言 简单记录下使用使用GitHub Actions自动构建程序并发布到Release。摸索了好一阵,坑还比较多。 配置 配置主要分为构建和上传。这里使用python作为示例,为了能在其他语言复用这个模板,这里尽量配置得通用一点。 构建部分参考Building and testing Python。上传部分参考了多个开源项目,并且实践修改而来。 配置案例 创建配置文件.github/workflows/python-package.yml name: Python package on: push: tags: [ '**' ] jobs: Linux-build-amd64: name: Build Linux Amd64 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: # 必须加'' python-version: '3.10' - name: Install dependencies run: | python -m pip install --upgrade pip pip install wheel pyinstaller if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Pyinstaller run: | pyinstaller --onefile --add-data "conf.yaml.default:." --add-data "templates:templates" --name vpspeek app.py - name: Verify generated file run: | ls -l dist/ - name: Upload Linux File uses: actions/upload-artifact@v3 with: path: dist/vpspeek Create-release: permissions: write-all runs-on: ubuntu-latest needs: [ Linux-build-amd64 ] steps: - name: Download Linux File uses: actions/download-artifact@v3 with: path: dist/ - name: Move downloaded file run: | mv dist/artifact/* dist/ - name: Verify file after move run: | ls dist/ - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} draft: false prerelease: false - name: Upload Release Asset uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: dist/vpspeek asset_name: vpspeek asset_content_type: application/octet-stream 配置说明 on.push.tags: [ '**' ]每次打tag的时候都自动构建。 jobs 分为 Linux-build-amd64 和 Create-release ,方便后面对 Windows 或者其他版本系统进行扩展。 actions/setup-python@v5 的 python-version: ‘3.10’ 必须使用’’。如填3.10会被识别成3.1。 actions/download-artifact@v3 下载步骤,会在下载的目录中为每个 artifact 创建一个子目录,默认情况下这个子目录的名称会是 artifact 名称(即 artifact)。这样就导致了期望文件 vpspeek 出现在 dist/ 目录下,实际上它在 dist/artifact/ 目录中。 解决办法就是把文件mv到dist或者直接使用artifact下的文件。 GitHub不允许配置GITHUB_开头的secrets,MY_GITHUB_TOKEN。

December 9, 2025 · 2 min

Java基础集合多线程JVM

Java基础 重点记录 构造器 Constructor 是否可被 override? Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多 个构造函数的情况。 重载和重写的区别 重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。 重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变。 重写发生在运行时。因为在编译时,编译器是无法知道我们到底是调用父类的方法还是子类的方法,相反的,只有在实际运行的时候,我们才知道应该调用哪个方法。 重载发生在编译时。在编译过程中,编译器必须根据参数类型以及长度来确定到底是调用的哪个方法,这也是Java编译时多态的体现。 Java 面向对象编程三大特性: 封装 继承 多态 继承 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是⽆法访问,只是拥有。 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。 子类可以用自己的方式实现父类的方法。(以后介绍)。 多态 多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作(调用同一个方法)。 在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。 StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的? 简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串, private final char value[] ,所以 String 对象是不可变的。 StringBuffer 对方法加了同步锁synchronized或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。 StringBuffer/StringBuilder 每次都会对对象本身进行操作,而不是像String生成新的对象并改变对象引用。 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。 在 Java 中定义一个不做事且没有参数的构造方法的作用 规定:Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。 因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。 ...

January 22, 2025 · 10 min

Java9之后内码为什么由UTF-16改为UTF-8

扩展性更强 UTF-8采用可变长度设计,长度为1-6个字节。UTF-16也为可变长设计,长度为2|4字节。UTF-8编码更加灵活和可扩展,能够支持未来出现的新字符和符号。 在之前的Java(9以前)版本中,Java平台采用的是16位Unicode编码(UTF-16)来表示字符和字符串,这种编码方式使用一个或两个16位的code units表示一个Unicode字符。虽然UTF-16编码可以表示所有的Unicode字符,但它并没有完全解决Unicode字符集的扩展问题。如果将来有新的Unicode字符需要添加到字符集中,可能需要修改UTF-16编码方案,从而导致兼容性和可移植性问题。 兼容性更好 UTF-8是一种非常广泛使用的字符编码方案,被支持和应用于众多操作系统、程序和协议中。使用UTF-8作为内部编码,可以更好地与其他系统和组件进行交互,并避免出现兼容性问题。 尽管Java平台之前采用的UTF-16编码也被广泛支持和应用,但在某些情况下,如文本传输和存储等场景,UTF-8编码具有更好的兼容性和互操作性。此外,许多开发人员在编写Java程序时也会使用UTF-8编码,因此将Java平台的内部编码改为UTF-8可以方便开发人员进行文本处理和编程。 更节省空间 相比于UTF-16编码,UTF-8编码在存储英文字符和标点符号等ASCII字符时,只需要使用一个字节,而不是两个字节。这样可以在存储和传输文本数据时节省空间和带宽。在处理大量字符串时可以节省内存使用。 尤其是在存储大量英文字符和标点符号的场景下,UTF-8编码能够显著地减少存储和传输的数据量,提高系统的效率和性能。 提高性能 Java9的String的内部value数组由char数组类型改为byte数字类型。byte占用空间为char的一半,这意味着更多的数据可以缓存在CPU缓存中,从而加速字符串操作的执行速度。

January 9, 2025 · 1 min

StringPool(字符串常量池)

字符串常量池的解释 字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定哪些是要放入字符串常量池。具体来说,只有使用双引号("")创建的字符串字面量才会被认为是常量,从而被放入常量池中。 在运行时,还可以使用 String 的 intern() 方法在运行过程将字符串添加到 String Pool 中。调用该方法会判断字符串常量池是否有该字面量,如没有,会在字符串常量池创建。之后,会返回字符串常量池中的对象。 通过new创建的字符串字面量,会先将,无论是否在字符串常量池存在,都会创建新的对象且不放入字符串常量池。 String str1 = "Hello world!"; String str2 = "Hello world!"; String str3 = new String("Hello world!"); String str4 = new String("Hello world!"); String str5 = str4.intern();//运行过程中把字符串添加到String Pool,并返回String Pool中的对象 System.out.println(str1 == str2);//运行结果:true。 System.out.println(str1 == str3);//运行结果:false。 System.out.println(str3 == str4);//运行结果:false。 System.out.println(str1 == str5);//运行结果:true。 System.out.println(str4 == str5);//运行结果:false。 字符串常量池长度 在 jdk6中,StringTable的长度是固定的,就是1009的长度。所以如果常量池中的字符串过多,会产生hash冲突,导致链表变长,降低查询效率。 在jdk7中,StringTable的长度可以通过一个参数指定: -XX:StringTableSize=99991 字符串常量池的位置 不同版本的Java虚拟机(JVM)可能会采用不同的方式来实现字符串常量池,并且在不同的JVM实现中,字符串常量池的位置也可能会发生变化。 JDK 6 字符串常量池存在于运行时常量池,运行时常量池存在方法区。方法区的实现为永久代(PermGen)。 PermGen默认大小只有4m,这种设计可能会导致PermGen空间溢出(java.lang.OutOfMemoryError: PermGen space)的问题,并且在频繁加载大量类文件时容易出现性能问题。 JDK 7 为了解决这些问题,从 JDK 7 开始,字符串常量池被移动到了堆内存中。 JDK 8 还是在堆内存中。 字符串的拼接 如果定义字符串时有拼接的表达式,编译器会根据表达式的参数是否为"“直接引用来判断是否将字符串字面量放入字符串常量池。 如表达式"Hello”+" world!",编译器会将两个字符串拼接成一个新的字符串"Hello world!",然后对比这个新字符串对象和常量池中的字符串字面量"Hello world!“是否相同。由于Java字符串常量池的特性,当两个字符串的内容相同时,它们所引用的字符串对象实例也是相同的。因此,表达式"Hello”+" world!" == “Hello world!“的结果为true。 ...

January 9, 2025 · 2 min

String的value数组不可变的好处

缓存hash值 String中用value数组来存储字符串信息,用hash来缓存value的hash值。如果value不是final,每次修改后需要重新计算hash,失去了缓存的意义,影响程序运行效率。 //java9及之后采用byte[],之前是char[] private final byte[] value; private int hash; // Default to 0 public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { hash = h = isLatin1() ? StringLatin1.hashCode(value) : StringUTF16.hashCode(value); } return h; } String Pool(常量池)复用需要 String Pool可以缓存已经创建的String对象。只有 String 是不可变的,才可能使用 String Pool。 安全性 由于String对象在创建后不可被修改,因此对于String类型的参数,其不可变性可以保证参数不会被意外或恶意地改变。 在网络连接过程中,一般需要传递服务器地址、端口号、用户名、密码等各种参数。如果这些参数使用可变的String类型来表示,那么在连接过程中,这些参数可能会被改变,导致实际连接的主机不同于预期。例如,某个客户端使用一个可变的String类型来表示服务器地址,然后在连接过程中修改了该字符串,将原本应该连接的服务器地址改成了另一个地址,从而导致最终连接到了错误的服务器上。 如果使用不可变的String类型来表示这些参数,则可以避免上述问题的出现。由于String对象不可被修改,因此无论在何种情况下都可以确保连接所需的参数不会在连接过程中被更改,从而确保连接行为的正确性和稳定性。 多线程 由于String的不可变,String具备线程安全性,可以在多线程中使用。

January 9, 2025 · 1 min