分类目录归档:技术笔记

k8s 使用 pv-migrate 迁移 pvc

TL;DR

安装

wget https://github.com/utkuozdemir/pv-migrate/releases/download/v1.7.1/pv-migrate_v1.7.1_linux_x86_64.tar.gz
tar -xvf pv-migrate_v1.7.1_linux_x86_64.tar.gz
mv pv-migrate /usr/local/bin

用法

pv-migrate migrate \
  --source-namespace default \
  --dest-namespace default \
  localpv-vol csi-lvmpv

🚀 Starting migration
💭 Will attempt 3 strategies: mnt2, svc, lbsvc
🚁 Attempting strategy: mnt2
📂 Copying data... 100% |██████████████████████████████| (3.4 GB/s)     
📂 Copying data...   0% |                              |  [0s:0s]🧹 Cleaning up
📂 Copying data... 100% |██████████████████████████████|         
✨ Cleanup done
✅ Migration succeeded

References

k3s 部署 kube-prometheus-stack 监控栈

TL;DR

$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
$ helm repo update
$ helm show values prometheus-community/kube-prometheus-stack
$ helm show values prometheus-community/kube-prometheus-stack > values.yaml
# Edit values.yaml
$ helm install prometheus-community prometheus-community/kube-prometheus-stack  --namespace monitoring -f values.yaml --create-namespace
# update values.yaml
$ helm upgrade --install prometheus-community prometheus-community/kube-prometheus-stack  --namespace monitoring -f values.yaml

References

helm operation 中断锁死问题解决

两种方案:

解决办法

方法一,卸载重装

helm uninstall <release name> -n <namespace>

方法二,回滚

This error can happen for few reasons, but it most commonly occurs when there is an interruption during the upgrade/install process as you already mentioned.
发生此错误的原因有很多,但最常见的原因是升级/安装过程中出现中断,正如您之前提到的。

To fix this one may need to, first rollback to another version, then reinstall or helm upgrade again.
要修复这个问题,可能需要先回滚到另一个版本,然后重新安装或再次进行 helm upgrade。

Try below command to list
尝试以下命令列出

helm ls --namespace 
<namespace>

but you may note that when running that command ,it may not show any columns with information
但你可能会注意到,运行该命令时,它可能不会显示任何包含信息的列

Try to check the history of the previous deployment
尝试检查上次部署的历史记录

helm history 
<release> --namespace <namespace>

This provides with information mostly like the original installation was never completed successfully and is pending state something like STATUS: pending-upgrade state.
这提供的信息大多类似于原始安装从未成功完成并且处于待处理状态,例如状态:待处理升级状态。

To escape from this state, use the rollback command:
要退出此状态,请使用 rollback 命令:

helm rollback 
<release> <revision> --namespace <namespace>

revision is optional, but you should try to provide it.
修订是可选的,但您应该尝试提供它。

You may then try to issue your original command again to upgrade or reinstall.
然后您可以尝试再次发出原始命令来升级或重新安装。

References

将当前系统做成 docker 镜像

TL; DR

  • Step1: 将当前系统打包

系统进行格式转化并压缩,执行如下命令,生成一个镜像的centos7-base.tar的文件

# 示例打包命令
$ tar --numeric-owner --exclude=/proc --exclude=/sys -zcvpf /data/centos7-svr.tar.gz /

$ tar --numeric-owner \
--exclude=/proc --exclude=/sys \
--exclude=/var/lib/vz \
--exclude=/root \
--exclude=/media \
--exclude=~ \
-zcvpf ~/hci6-ci-base.tar.gz /

$ tar -cvpf /home/buildrpm.tar --directory=/ --exclude=proc --exclude=sys --exclude=dev --exclude=run /
# --numeric-owner:执行所属
# --exclude:排除那些文件或者目录
# -zcvf :打包压缩 p保持文件的绝对路径
  • Step2: 导入镜像并运行

把镜像文件centos7-svr.tar.gz放到装有docker的系统上

docker import centos7-base.tar <docekr-image>
docker run -it --name <contaner-name> <docker-images> /bin/bash

参考文献

cert-manager CNAME 问题记录

在研究 cert-manager 使用 webhook 方式调用 dnspod 使用 DNS-01 方式签发 SSL 证书遇到问题,一直得到错误:

I0306 03:48:38.870605       1 controller.go:144] "syncing item" logger="cert-manager.controller"
I0306 03:48:38.870714       1 dns.go:118] "checking DNS propagation" logger="cert-manager.controller.Check" resource_name="test1-tsh1-frytea-com-1-3300738485-2689263791" resource_namespace="default" resource_kind="Challenge" resource_version="
v1" dnsName="test1.tsh1.frytea.com" type="DNS-01" resource_name="test1-tsh1-frytea-com-1-3300738485-2689263791" resource_namespace="default" resource_kind="Challenge" resource_version="v1" domain="test1.tsh1.frytea.com" nameservers=["223.5.5.5:53","8.8.8.8:53"]
I0306 03:48:38.879628       1 wait.go:94] "Updating FQDN" logger="cert-manager.controller" resource_name="test1-tsh1-frytea-com-1-3300738485-2689263791" resource_namespace="default" resource_kind="Challenge" resource_version="v1" dnsName="test
1.tsh1.frytea.com" type="DNS-01" fqdn="_acme-challenge.test1.tsh1.frytea.com." cname="tsh1.frytea.com."
I0306 03:48:38.897174       1 wait.go:145] "Looking up TXT records" logger="cert-manager.controller" resource_name="test1-tsh1-frytea-com-1-3300738485-2689263791" resource_namespace="default" resource_kind="Challenge" resource_version="v1" dns
Name="test1.tsh1.frytea.com" type="DNS-01" fqdn="tsh1.frytea.com."
E0306 03:48:38.897227       1 sync.go:208] "propagation check failed" err="DNS record for \"test1.tsh1.frytea.com\" not yet propagated" logger="cert-manager.controller" resource_name="test1-tsh1-frytea-com-1-3300738485-2689263791" resource_nam
espace="default" resource_kind="Challenge" resource_version="v1" dnsName="test1.tsh1.frytea.com" type="DNS-01"I0306 03:48:38.897688       1 controller.go:164] "finished processing work item" logger="cert-manager.controller"

我使用了以下资源:

在相关仓库找到这些 issue

发现,只要申请证书的域名能够匹配到 CNAME 记录,就会默认跟随,找不到正确的 TXT 记录,导致认证失败。 虽然 cert-manager 提供了这个参数 cnameStrategy: None ,能够在声明 ISSUE 时使用,但是似乎大部分实现的 webhook 都没有实现这个特性:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: dnspod
spec:
  acme:
    email: xxxx # 在证书过期的时候,会发邮件通知
    preferredChain: ""
    privateKeySecretRef:
      name: example-com-letsencrypt-dev-key # 用于存储ACME帐户私钥的密钥名称
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    #server: https://acme-v02.api.letsencrypt.org/directory # 生产
    solvers:
      - dns01:
          cnameStrategy: None
          webhook:
            config:
              secretId: xxxxxx
              secretKeyRef:
                key: secret-key
                name: dnspod-secret
              ttl: 600
            groupName: acme.imroc.cc
            solverName: dnspod

目前临时的解决办法,只能是 避免 cert-manager 托管域名能够解析到 CNAME 记录,等有空了研究一下 cert-managerwebhook 的实现方法,看能否解决这个问题。

References

ProxmoxVE (PVE) NAT 网络配置方法

伪装允许只有私有 IP 地址的访客使用主机 IP 地址来访问网络,以处理传出流量。每个传出数据包都会被重写 iptables ,使其看起来来自主机,响应也会相应地被重写以路由到原始发件人。

auto lo
iface lo inet loopback

auto eno1
#real IP address
iface eno1 inet static
        address  198.51.100.5/24
        gateway  198.51.100.1

auto vmbr1
#private sub network
iface vmbr0 inet static
        address  10.10.10.1/24
        bridge-ports none
        bridge-stp off
        bridge-fd 0

        post-up   echo 1 > /proc/sys/net/ipv4/ip_forward
        post-up   iptables -t nat -A POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
        post-down iptables -t nat -D POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
        post-up   iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
        post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1

其中 vmbr1 为 NAT 网桥,网桥 IP 为 10.10.10.0/24 ,该网段流量会被转换为 eno1 网卡的 IP 发出,并在收到回复保温时转换为原始 IP,实现共享外部 IP 的目标。

重载网络配置使其生效:

ifreload -a

实测立刻可以生效,VM 中配置该段 IP 并将 10.10.10.1 作为网关即可,也可根据需要配置 dnsmasq 之类的实现 DHCP 自动分配 IP。

References

Linux 备份和恢复 docker volume 脚本分享

脚本

Dump

docker-volume-dump.sh

#!/usr/bin/env bash

dump_dir=~/docker-volume-dump

if [ ! -d $dump_dir ]; then
  mkdir -p $dump_dir
fi

for volume in $(docker volume ls -q); do
  dump_file=$dump_dir/$volume.tar.gz
  if [ -f $dump_file ]; then
    ( set -x; echo rm $dump_file; )
  fi
  echo "Dump docker volume \"$volume\" to \"$dump_file"\"
  docker run --rm -v $volume:/from alpine sh -c "cd /from; tar -cf - ." | gzip > $dump_dir/$volume.tar.gz
done

Restore

docker-volume-restore.sh

#!/usr/bin/env bash

dump_dir=~/docker-volume-dump

for file in ~/docker-volume-dump/* ; do
  volume=$(basename $file)
  volume=${volume%%.*}
  echo "$volume"
  docker volume inspect $volume &>/dev/null
  if [ $? -eq 0 ]; then
    ( set -x; docker volume rm $volume 1>/dev/null )
  fi
  ( set -x; docker volume create $volume 1>/dev/null )
  cat $file | docker run --rm -i -v $volume:/to alpine sh -c 'tar zxvf - -C /to'
done

References

ArchLinux 休眠到交换文件

来源: ArchLinux 休眠到交换文件

Linux 使用交换分区来休眠,首先冻结所有进程并申请足够的交换内存(位于磁盘),把当前内存都存进去。 然后下次启动时,initramfs 会直接加载上次休眠时的内存状态,跳过内核的 init 过程。 因此首先需要有足够大的交换分区或交换文件;再把内核指向到休眠的分区上;最后再配置 initramfs 让它加载休眠的内存文件。 官方文档请参考 Power_management/Suspend_and_hibernate#Hibernation, 本文细述如何休眠到交换文件,并对其中一些概念和细节进行了解释。

在本文讨论的范围内, 挂起(suspend)是指冻结当前的进程,保留它们的内存,并把几乎除了内存之外的设备都断电。 休眠(hibernate)是指把挂起后的内存写入磁盘并完全关机。 锁定(lock)则只是显示一个模态的全屏软件输入正确的密码才能退出。

交换文件

安装系统 前需要创建交换分区,现在的机器普遍内存较大不太需要交换分区来扩展内存空间, 而且磁盘一般使用读写快速但读写次数有限 SSD,因此我的交换分区也很小根本不够用来休眠。

你可以通过 swapiniss 来让你的交换分区只用于休眠。

所以我们用交换文件来替代交换分区,在创建交换文件之前首先需要知道系统休眠需要多大空间。 可以从 sysfs 来看查看:

cat /sys/power/image_size

可以参考 [官方教程][https://wiki.archlinux.org/index.php/Swap#Swap_file] 来创建:

按照你需要的大小创建,bs * count 是最终文件大小
dd if=/dev/zero of=/swapfile bs=1M count=4096 status=progress
chmod 600 /swapfile
# 检查大小和权限
ls -l /swapfile

# 初始化交换文件并立即应用到系统
mkswap /swapfile
swapon /swapfile

# 在 /etc/fstab 中写入以下内容,交换文件会在重启后生效
/swapfile none swap defaults 0 0

重启后通过 swapon -s 来检查是否生效:

> sudo swapon -s
Filename    Type    Size     Used    Priority
/swapfile   file    12582908    0    -2

让内核找到交换文件

我们需要设置 resume 和 resume_offset 两个内核参数,告诉内核在挂起时把内存写入到哪里。

  • 第一个参数是交换文件所在的磁盘分区,可以用任何 fstab 中接受的名字格式。比如 resume =/dev/sda1,或者 resume = UUID = xxx。如果你在上一步中创建的交换文件和 / 在同一分区下,可以复制已有的 root 内核参数的值。
  • 第二个参数是交换文件的偏移量,就是它在分区中的什么位置。因此这个参数给交换文件用的,如果是交换分区则不需要填写。可以通过 filefrag -v /swapfile | awk '{ if($1=="0:"){print $4} }' 命令得到。

可以通过 cat /proc/cmdline 来查看当前的内核参数,但是在哪里设置取决于你的 Boot Loader。 以 rEFInd 为例,打开 /boot/refind_linux.conf 写入 resume 和 resume_offset:

"Boot with standard options" "ro root=/dev/sda3 resume=/dev/sda3 resume_offset=3192832"

重启后用 journalctldmesg 来找到写入休眠镜像的日志:

Oct 19 13:57:56 harttle.arch.mac kernal: PM: Creating hibernation image:
Oct 19 13:57:56 harttle.arch.mac kernel: PM: Need to copy 596422 pages
Oct 19 13:57:56 harttle.arch.mac kernel: PM: Normal pages needed: 596422 + 1024, available pages: 1477067

如果看到这样的错误说明设置有误,请检查你交换文件所在分区和 filefrag 给出的偏移量:

Oct 19 13:57:56 harttle.arch.mac kernal: PM: Image not found (code -22)

让 initramfs 加载休眠的内存

initramfs 是由 Boot Loader 直接加载的一个早期的用户空间,其中已经加载了一些内核模块。 它会进行设备初始化、挂载文件系统、运行磁盘检查等工作,之后再交给内核的 init 过程。 加载休眠的内存也是它的工作,但 ArchLinux 默认并未开启,需要去 /etc/mkinitcpio.conf 中添加 resume 钩子:

HOOKS=(base udev resume autodetect modconf block filesystems keyboard fsck)

注意因为 resume 参数用到了磁盘设备名称,resume 需要写在 udev 之后。 然后重新编译 initramfs(就像更新内核时一样):

默认使用当前系统的内核,如果你现在位于启动盘的系统则需要指定宿主环境上的内核版本。
mkinitcpio -p linux

至此配置工作都完成了,通过 systemctl hibernate 来休眠,再按下电源键开机来检查休眠功能是否正常。

开着盖子无法挂起

MacBook Pro 的显示器盖子开着默认会阻止挂起,可能会出现挂起屏幕变黑后立即结束挂起。 这是因为显示器盖子默认可以唤醒休眠,在 /proc/acpi/wakeup 中可以查看哪些设备可以唤醒,其中 LID0 是显示器盖子:

> cat /proc/acpi/wakeup 
Device  S-state   Status   Sysfs node
P0P2      S3    *disabled
EC    S4    *disabled  platform:PNP0C09:00
HDEF      S3    *disabled  pci:0000:00:1b.0
RP01      S3    *enabled   pci:0000:00:1c.0
RP02      S3    *enabled   pci:0000:00:1c.1
RP03      S3    *enabled   pci:0000:00:1c.2
ARPT      S4    *disabled  pci:0000:03:00.0
RP05      S3    *enabled   pci:0000:00:1c.4
RP06      S3    *enabled   pci:0000:00:1c.5
XHC1      S3    *enabled   pci:0000:00:14.0
ADP1      S4    *disabled  platform:ACPI0003:00
LID0      S4    *enabled  platform:PNP0C0D:00

我们可以 echo LID0 > /proc/acpi/wakeup 来更改它的状态,然后再试休眠。 我们希望它的状态默认就是 disabled,需要一个这样的 systemd 服务:

[Unit]
Description=Disable LID0 wakeup triggers in /proc/acpi/wakeup

[Service]
Type=oneshot
ExecStart=/bin/sh -c "echo LID0 > /proc/acpi/wakeup"
ExecStop=/bin/sh -c  "echo LID0 > /proc/acpi/wakeup"
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

把它添加到 /etc/systemd/system 并启用即可。

自动休眠

自动休眠和其他电源管理功能,由很多不同的软件和配置方式来实现。为避免混淆先介绍几个常见的软件:

systemd-logind:ArchLinux 的默认安装包含了 systemd,其中的 systemd-logind 是自动启用的。 它包含了一些非常简单的电源管理功能,比如按下电源按钮时关机、笔记本合上盖子时挂起。 因此 ArchLinux 装好之后就基本可以用了。

acpidacpid 是一个比较基础的电源管理工具,工作方式是响应 ACPI 事件,做相应的处理比如关机还是休眠。注意 acpid 只是电源管理工具,ACPI 是设备配置接口跟它没关系。

dpmsdpms 是 xorg 提供的显示器电源管理服务,用来控制显示器关闭等动作。有 standby, suspend, off 等阶段,跟 systemd 事件一样需要有人来订阅(比如 xss-lock)才能执行具体操作。可以通过 xorg.conf 的 StandbyTime, SuspendTime, OffTime 等来配置,也可以在运行时用 xset s 来配置。

tlptlp 是一个比较无脑的电源管理工具,提供类似电池模式、电源模式、性能优先这样级别的配置。

xss-lock, xidle, xautolock:这些是 X11 下的工具用来在用户无操作时执行挂起等操作,有些还会监听 ACPI 事件,这样在 suspend 时屏幕也能锁定。

无操作自动休眠

自动休眠和自动挂起需要桌面环境(DE)或者 X11 软件的支持。 如果你在用 Gnome 或 KDE 在控制面板中配置后,会把 idle 信息报告给 systemd-logind,后者接管具体操作。

如果你像 Harttle 一样没有桌面系统和登录管理器的话, 需要安装一个类似 xss-lock, xidle 这样的工具来靠 X11 事件计时, 然后调用 systemctl hibernatesystemctl syspend

先挂起后休眠

合上盖子后 systemd-logind 的默认行为是挂起,可以在 /etc/systemd/logind.conf 中把它重新设置为休眠,或先挂起再休眠:

HandleLidSwitch=suspend-then-hibernate

挂起后休眠前的时间可以在 /etc/systemd/sleep.conf 中设置:

HibernateDelaySec=15min

除了合上盖子之外,其他场景也可以直接调用 systemctl suspend-then-hibernate

电量低自动休眠

这件事情需要具体的软件来做,或者直接安装 tlp 并启动 tlp, tlp-sleep 两个 systemd 服务。 下面提供一个简单的 udev 规则,在电量小于等于 5% 时休眠:

SUBSYSTEM=="power_supply", ATTR{status}=="Discharging", ATTR{capacity}=="[0-5]", RUN+="/usr/bin/systemctl hibernate"

把它写入 /etc/udev/rules.d/99-lowbat.rules,重启即可生效。

References

Vim 下大小写敏感的搜索-替换

来源: Vim 下大小写敏感的搜索/替换

Vim 中的搜索默认是大小写敏感的,即搜索 vim 不会匹配到 Vim。 这一点跟多数编辑器/IDE 都不同,因此 Vim 的默认设置其实很不顺手。 本文来分享一些个性化的配置方法,让 Vim 下的大小写敏感/不敏感用起来更加顺手。 比如当搜索词包含大写时应用大小写敏感搜索;其他情况应用大小写不敏感搜索。

TL;DR

以搜索词为 harttle 为例(省略了最后的回车):

  • 强制大小写不敏感搜索:/harttle\c
  • 强制大小写敏感搜索:/harttle\C
  • 强制大小写不敏感替换:s/harttle\c/Harttle
  • 强制大小写敏感替换:s/harttle\C/Harttle
  • 设置为大小写敏感::set ignorecase
  • 设置为大小写不敏感::set noignorecase
  • 设置为智能模式(有大写时敏感否则不敏感)::set smartcase
  • 设置为非智能模式::set nosmartcase

大小写敏感控制字符

正如在正则表达式有类似 i 这样的开关,Vim 也有特殊字符来控制大小写敏感。 在模式末尾加 \c 表示大小写不敏感,加 \C 表示大小写敏感。 例如:

" 大小写不敏感搜索,可以匹配:vim, Vim, VIM
/vim\c
<CR>
" 大小写敏感搜索,只可以匹配:Vim
/Vim\C
<CR>
" 把出现的所有 vim, Vim, VIM 等都替换为 Vim,在写文章时会经常会用到
:%s/vim\c/Vim/g

这一语法的优先级高于下文的 ignorecase, smartcase 等选项, 所以比较万能,在远程机器上、别人的电脑上,一般用这个操作。

ignorecase/smartcase

Vim 中的 ignorecase 用于设置大小写敏感,它将在所有搜索、替换命令中生效。 在 normal 模式中 :set ignorecase 设置为不敏感;:set noignorecase 设置为敏感。 ignorecase 属于选项变量,因此也可以通过 & 来设置,例如::let &ignorecase=1。 把冒号去掉后可以直接放到 .vimrc 文件里持久生效。

更多 Vim 变量赋值和引用的细节,可参考这篇文章:Vim 中的变量赋值、引用与作用域

开启 ignorecase 之后还可以把 smartcase 也打开(后者要求前者出于开启状态), Vim 会启用智能模式:

  • 在你输入的模式中包含大写时,启用大小写敏感模式;
  • 在你输入的模式中只有小写时,启用大小写不敏感模式。

例如:

:set ignorecase
:set smartcase

" 大小写不敏感,可以匹配:vim, Vim, VIM
/vim
<CR>
" 大小写敏感,只可以匹配 Vim
/Vim
<CR>"

当前词搜索

smartcase 只对输入的模式(pattern)生效,其他不需要输入 pattern 的搜索命令不生效。 比如 在 Vim 中优雅地查找和替换 中介绍过可以用 *(向后),#(向前),g*(不切词)等命令来搜索光标所在的词搜索光标所在的词。 为了让它们好使,可以先按下 * 来搜索一次,然后按下 /(向后)再按上箭头找到上次历史(这是一个具体的 pattern)再按回车搜索。同样地,按 ?(向前)也可以。

“展开光标所在词”是存在 Vim 命令的,因此我们可以把 *, # 映射掉来自动化上面的过程:

" respect to smartcase, expand the pattern
:nnoremap * /\<<C-R>=expand('<cword>')<CR>\><CR>
:nnoremap # ?\<<C-R>=expand('<cword>')<CR>\><CR>

这样下次按下 *# 时,Vim 就会展开光标处的词,分别应用 /? 进行搜索。 这样当光标处的词有大写时就用大小写敏感搜索,全小写时就用大小写不敏感搜索。 <C-R>= 用来插入计算表达式并插入到命令里,类似我们在 使用 Vim 寄存器 中介绍的 <C-R>" 可以把匿名寄存器(上次拷贝、剪切、删除)的内容插入到命令里。

References