0%

transformer快速入门

统计语言模型

NNLM 模型

alt text
NNLM 模型首先从词表C中查询得到前面N-1个词语对应的词向量,然后将这些词向量拼接后输入到带有激活函数的隐藏层中,通过Softmax函数预测当前词语的概率。特别地,包含所有词向量的词表矩阵C也是模型的参数,需要通过学习获得。因此 NNLM 模型不仅能够能够根据上文预测当前词语,同时还能够给出所有词语的词向量(Word Embedding)。

word2vec

alt text
CBOW (Continuous Bag-of-Words)使用周围的词语w(t-2),w(t-1),w(t+1),w(t+2)来预测当前词w(t)。而 Skip-gram 则正好相反,它使用当前词w(t)来预测它的周围词语。
与严格按照统计语言模型结构设计的 NNLM 模型不同,Word2Vec 模型在结构上更加自由,训练目标也更多地是为获得词向量服务。特别是同时通过上文和下文来预测当前词语的 CBOW 训练方法打破了语言模型“只通过上文来预测当前词”的固定思维,为后续一系列神经网络语言模型的发展奠定了基础。

word2vec最大的问题是无法解决一词多义问题。后来自然语言处理的标准流程就是先将 Word2Vec 模型提供的词向量作为模型的输入,然后通过 LSTM、CNN 等模型结合上下文对句子中的词语重新进行编码,以获得包含上下文信息的词语表示。

ELMo 模型

ELMo 模型(Embeddings from Language Models)更好地解决多义词问题。与 Word2Vec 模型只能提供静态词向量不同,ELMo 模型会根据上下文动态地调整词语的词向量。
ELMo 模型首先对语言模型进行预训练,使得模型掌握编码文本的能力;然后在实际使用时,对于输入文本中的每一个词语,都提取模型各层中对应的词向量拼接起来作为新的词向量。ELMo 模型采用双层双向 LSTM 作为编码器,如图 1-10 所示,从两个方向编码词语的上下文信息,相当于将编码层直接封装到了语言模型中。
alt text
训练完成后 ELMo 模型不仅学习到了词向量,还训练好了一个双层双向的 LSTM 编码器。对于输入文本中的词语,可以从第一层 LSTM 中得到包含句法信息的词向量,从第二层 LSTM 中得到包含语义信息的词向量,最终通过加权求和得到每一个词语最终的词向量。

BERT模型

BERT 模型采用和 GPT 模型类似的两阶段框架,首先对语言模型进行预训练,然后通过微调来完成下游任务。但是,BERT 不仅像 GPT 模型一样采用 Transformer 作为编码器,而且采用了类似 ELMo 模型的双向语言模型结构。由于 BERT 模型采用双向语言模型结构,因而无法直接用于生成文本。
alt text

Transformer模型

Transformer模型按模型结构将它们大致分为三类:

  • 纯 Encoder 模型(例如 BERT),又称自编码 (auto-encoding) Transformer 模型;
  • 纯 Decoder 模型(例如 GPT),又称自回归 (auto-regressive) Transformer 模型;
  • Encoder-Decoder 模型(例如 BART、T5),又称 Seq2Seq (sequence-to-sequence) Transformer 模型。

Transformer 模型本质上是预训练语言模型,大都采用自监督学习 (Self-supervised learning) 的方式在大量生语料上进行训练,训练这些 Transformer 模型完全不需要人工标注数据。
例如下面两个常用的预训练任务:

  • 基于句子的前n个词来预测下一个词,因为输出依赖于过去和当前的输入,因此该任务被称为因果语言建模 (causal language modeling);
  • 基于上下文(周围的词语)来预测句子中被遮盖掉的词语 (masked word),因此该任务被称为遮盖语言建模 (masked language modeling)。

结构

标准的 Transformer 模型主要由两个模块构成:
Encoder(左边):负责理解输入文本,为每个输入构造对应的语义表示(语义特征);
Decoder(右边):负责生成输出,使用 Encoder 输出的语义表示结合其他输入来生成目标序列。
alt text

这两个模块可以根据任务的需求而单独使用:

  • 纯 Encoder 模型:适用于只需要理解输入语义的任务,例如句子分类、命名实体识别;
  • 纯 Decoder 模型:适用于生成式任务,例如文本生成;
  • Encoder-Decoder 模型或 Seq2Seq 模型:适用于需要基于输入的生成式任务,例如翻译、摘要。

alt text

Attention

注意力层的作用就是让模型在处理文本时,将注意力只放在某些词语上。

例如要将英文“You like this course”翻译为法语,由于法语中“like”的变位方式因主语而异,因此需要同时关注相邻的词语“You”。同样地,在翻译“this”时还需要注意“course”,因为“this”的法语翻译会根据相关名词的极性而变化。对于复杂的句子,要正确翻译某个词语,甚至需要关注离这个词很远的词。

同样的概念也适用于其他 NLP 任务:虽然词语本身就有语义,但是其深受上下文的影响,同一个词语出现在不同上下文中可能会有完全不同的语义(例如“我买了一个苹果”和“我买了一个苹果手机”中的“苹果”)。

NLP神经网络模型的本质就是对输入文本进行编码,常规的做法是首先对句子进行分词,然后将每个词语 (token) 都转化为对应的词向量 (token embeddings),这样文本就转换为一个由词语向量组成的矩阵X=(x1,x2,…,xn),其中 xi就表示第i个词语的词向量,维度为d,故 X∈Rn*d。

在 Transformer 模型提出之前,对 token 序列 X 的常规编码方式是通过循环网络 (RNNs) 和卷积网络 (CNNs)。

  • RNN(例如 LSTM)的方案很简单,每一个词语 xt 对应的编码结果 yt通过递归地计算得到:yt=f(y(t-1),xt)。
    RNN 的序列建模方式虽然与人类阅读类似,但是递归的结构导致其无法并行计算,因此速度较慢。而且 RNN 本质是一个马尔科夫决策过程,难以学习到全局的结构信息;
  • CNN 则通过滑动窗口基于局部上下文来编码文本,例如核尺寸为 3 的卷积操作就是使用每一个词自身以及前一个和后一个词来生成嵌入式表示:yt=f(x(t-1),xt,x(t+1))。
    CNN 能够并行地计算,因此速度很快,但是由于是通过窗口来进行编码,所以更侧重于捕获局部信息,难以建模长距离的语义依赖。

Google《Attention is All You Need》提供了第三个方案:直接使用 Attention 机制编码整个文本。相比 RNN 要逐步递归才能获得全局信息(因此一般使用双向 RNN),而 CNN 实际只能获取局部信息,需要通过层叠来增大感受野,Attention 机制一步到位获取了全局信息:yt=f(xt,A,B)
其中A,B是另外的词语序列(矩阵),如果取A=B=X就称为 Self-Attention,即直接将xt与自身序列中的每个词语进行比较,最后算出yt。

Scaled Dot-product Attention

Scaled Dot-product Attention是最常见的attention实现
alt text

Scaled Dot-product Attention 共包含 2 个主要步骤:

  1. 计算注意力权重:使用某种相似度函数度量每一个 query 向量和所有 key 向量之间的关联程度。对于长度为 m 的 Query 序列和长度为 n 的 Key 序列,该步骤会生成一个尺寸为 m*n 的注意力分数矩阵。
    特别地,Scaled Dot-product Attention 使用点积作为相似度函数,这样相似的 queries 和 keys 会具有较大的点积。
    由于点积可以产生任意大的数字,这会破坏训练过程的稳定性。因此注意力分数还需要乘以一个缩放因子来标准化它们的方差,然后用一个 softmax 标准化。这样就得到了最终的注意力权重 w(ij),表示第 i 个 query 向量与第 j 个 key 向量之间的关联程度。
  2. 更新 token embeddings:将权重 w(ij) 与对应的 value 向量 v1,…,vn 相乘以获得第 i 个 query 向量更新后的语义表示 。

Multi-head Attention

关于BBR的介绍参见 BBR (很久以前写过详细文档,后来丢了,只找到了一小部分……)

在一些比较老的OpenVZ的vps(宿主机内核低于4.9版本)上,是无法开启bbr的。tcp的拥塞控制,在网络链路存在严重丢包时,会将拥塞窗口乘性减少,所以进行文件传输时,速度会越来越慢。以下介绍一下我面临scp速度慢的问题的解决办法

haproxy-lkl代理ssh端口

市面上有很多代替BBR的方案,大多都大差不差,都是使用lkl(linux kernal library)内核库绕过内核协议处理网络数据,再使用端口代理,实现在某几个端口的传输层能达到bbr的效果。

参考了ovz架构安装bbr内核一文,很方便,一建安装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
恭喜!BBR 安装完成并成功启动

已加速的端口: 2222

你可以通过修改文件:
/usr/local/haproxy-lkl/etc/port-rules

来配置需要加速的端口或端口范围。

请使用 systemctl {start|stop|restart} haproxy-lkl
来 {开启|关闭|重启} 服务

服务已自动加入开机启动,请放心使用。

如果这个脚本帮到了你,你可以请作者喝瓶可乐:
https://blog.kuoruan.com/donate

享受加速的快感吧!

但也有缺点,这个端口代理只能影响外部访问该端口的,不能影响由内向外的。我尝试过打开某个本地端口,再通过以下代码将文件scp到外面,但是并没有用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 指定本地端口
# 没有用,haproxy-lkl并监听由本地端口发起的连接
def transfer_and_delete_file_using_key_and_local_port(local_file_path, remote_host, remote_user, private_key_path,
remote_file_path, local_port):
private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
local_host = "0.0.0.0" # 表示从任意本地网络接口连接

# 创建 SSH 客户端对象
ssh = paramiko.SSHClient()
# 自动添加主机密钥
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((local_host, local_port))
sock.connect((remote_host, 22))
transport = paramiko.Transport(sock)
try:
transport.start_client()
transport.auth_publickey(remote_user, private_key)
sftp = paramiko.SFTPClient.from_transport(transport)
# 检查远程文件路径的目录是否存在,如果不存在则创建
remote_dir = os.path.dirname(remote_file_path)
try:
sftp.listdir(remote_dir)
except IOError:
sftp.mkdir(remote_dir)
sftp.put(local_file_path, remote_file_path)
sftp.close()
logging.info(f"local file scp finish, {local_file_path}")
# 删除本地文件
os.remove(local_file_path)
logging.info(f"local file deleted: {local_file_path}")
except Exception as e:
traceback.print_exc()
logging.error(f"exception on {local_file_path} with error {e}")
finally:
# 关闭 Transport 连接
transport.close()

http请求呼叫远端pull

我干脆就不push了,让远端pull。这里面只有一个问题,就是网络链路不稳定容易导致('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))错误

解决方案是pull接口幂等+重试

本地:

1
2
3
4
5
6
7
8
9
10
11
def ask_remote_scp_local_file_and_remote():
while True:
try:
resp = session.post("http://xxx", json=req,timeout=3600)
if resp:
break
except Exception as e:
logging.error("ask remote scp fail," + e.__str__())
if resp.status_code == 200 and resp.text == 'success':
os.remove(file_path)
return

远端:
使用python-redis-lock包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def scp_from_remote_using_key(local_file_path, remote_host, remote_user, private_key_path, remote_file_path, port):
# 创建 SSH 客户端
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# 加载私钥
private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
# 连接到远程服务器
ssh.connect(remote_host, port=port, username=remote_user, pkey=private_key)
# 使用 sftp 传输文件
sftp = ssh.open_sftp()
# 检查远程文件路径的目录是否存在,如果不存在则创建
local_dir = os.path.dirname(remote_file_path)
try:
os.listdir(local_dir)
except IOError:
os.mkdir(local_dir)

sftp.get(remote_file_path, local_file_path)
sftp.close()
logging.info(f"remote file scp finish, {remote_file_path}")
except Exception as e:
logging.error(f"exception on {remote_file_path} with error {e}")
finally:
# 关闭 SSH 连接
ssh.close()
def lock_and_scp(local_file_path, remote_host, remote_user, private_key_path, remote_file_path, port):
m2 = hashlib.md5()
m2.update(remote_file_path.encode('utf-8'))
md5 = m2.hexdigest()
lock = redis_lock.Lock(redis_connect, "lock_" + md5, expire=600)

if lock.acquire(blocking=True):
if redis_connect.get("finished_" + md5) is None:
scp_from_remote_using_key(local_file_path, remote_host, remote_user, private_key_path, remote_file_path,
port)
redis_connect.set("finished_" + md5, "1")
redis_connect.expire("finished_" + md5, 600)
lock.release()

起因

在truenas的nextcloud升级后,可能是由于容器拉取混乱,k3s里的nextcloud空间也乱了。陆续出现了几个问题:

  1. 原本只有3个pod,结果多了nextcloud-postgres-ncnextcloud-nc两个deployment,而且都是比我设置版本低的,读的同一个映射路径(直接删掉多余的deploy)
  2. 在nextcloud-postgres容器中出现了2025-01-01T00:10:27.747127138+08:00 2024-12-31 16:10:27.747 UTC [169] FATAL: role "postgres" does not exist报错。虽然没找到这个错误的起因,但还是尝试解决了一下。
  3. app_api插件更新卡住不动(在将/html/apps/app_api目录先移出目录在移进后就莫名其妙好了)

网上说解决第2个问题的方法都语焉不详。postgres应该是默认的超级管理员用户,现在超级管理员直接没了,所以很多操作实际上是执行不了的。一下说一下我的解决办法

创建临时pg容器

在原有容器中,postgres进程是不能关的,关了后容器会重建,尝试设置了一下pod的restartPolicy为Never也没用。因此创建了一个临时容器来处理,新容器删掉了探针,加了一个tail -f /dev/null保持容器不关闭。

配置postgres.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
apiVersion: v1
kind: Pod
metadata:
name: postgres-13.1
labels:
app: postgres
namespace: default
spec:
restartPolicy: Never
containers:
- name: postgres
image: postgres:13.1
command: ["tail", "-f", "/dev/null"]
ports:
- containerPort: 5432
name: postgres
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
volumes:
- name: postgres-data
hostPath:
path: /xxxx # 宿主机挂载到容器的pg根目录
type: DirectoryOrCreate
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard

接着启动容器

1
2
k3s kubectl apply -f postgres2.yaml
k3s kubectl delete pod postgres-13.1 -n default

进入single模式,增加postgres用户

1
2
3
4
5
6
7
8
pg_ctl stop -D /var/lib/postgresql/data/

postgres --single -D /var/lib/postgresql/data/
>CREATE ROLE postgres SUPERUSER LOGIN CREATEDB CREATEROLE INHERIT REPLICATION BYPASSRLS;

pg_ctl start -D /var/lib/postgresql/data/

k3s kubectl delete pod postgres-13.1 -n default

总指南《保姆级教程!将 Vim 打造一个 IDE (Python 篇)》
系统:Linux version 5.4.0-198-generic (buildd@lcy02-amd64-109) (gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2))

安装 vim 9.1

安装python3.8及以上的python

YouCompleteMe插件必须要python3.8+编译的vim

1
2
3
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get update
sudo apt-get install python3.8

安装vim9.1

YouCompleteMe插件必须要最新的vim版本才能支持,网上的添加非官方库add-apt-repository ppa:jonathonf/vim的方法也只能装9.0版本的,因此需要手动编译
The Vim repository at GitHub
How To Install Vim with Python3 Support

1
2
3
4
5
6
7
8
sudo apt install -y python3-distutils python3-dev 
sudo apt install libncurses-dev
./configure --enable-python3interp \
--with-python3-config-dir=/usr/lib/python3.8/config-* \
--with-python3-command=/usr/bin/python3 \
--without-x
make
sudo make install

安装插件

安装插件管理工具vim-plug

1
2
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

插件配置

在.vimrc中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
" 插件开始的位置
call plug#begin('~/.vim/plugged')

" 代码自动完成,安装完插件还需要额外配置才可以使用
Plug 'ycm-core/YouCompleteMe'

" 用来提供一个导航目录的侧边栏
Plug 'scrooloose/nerdtree'
" 设置NerdTree
map <F3> :NERDTreeMirror<CR>
map <F3> :NERDTreeToggle<CR>
" 可以使 nerdtree 的 tab 更加友好些
Plug 'jistr/vim-nerdtree-tabs'

" 可以在导航目录中看到 git 版本信息
" Plug 'Xuyuanp/nerdtree-git-plugin'

" 查看当前代码文件中的变量和函数列表的插件,
" 可以切换和跳转到代码中对应的变量和函数的位置
" 大纲式导航, Go 需要 https://github.com/jstemmer/gotags 支持
Plug 'preservim/tagbar'
nmap <F9> :TagbarToggle<CR>

" 自动补全括号的插件,包括小括号,中括号,以及花括号
Plug 'jiangmiao/auto-pairs'

" Vim状态栏插件,包括显示行号,列号,文件类型,文件名,以及Git状态
Plug 'vim-airline/vim-airline'

" Shorthand notation; fetches https://github.com/junegunn/vim-easy-align
" 可以快速对齐的插件
Plug 'junegunn/vim-easy-align'

" 可以在文档中显示 git 信息
Plug 'airblade/vim-gitgutter'

" markdown 插件
Plug 'iamcco/mathjax-support-for-mkdp'
Plug 'iamcco/markdown-preview.vim'

" 下面两个插件要配合使用,可以自动生成代码块
Plug 'SirVer/ultisnips'
Plug 'honza/vim-snippets'

" go 主要插件
Plug 'fatih/vim-go', { 'tag': '*' }

" go 中的代码追踪,输入 gd 就可以自动跳转
Plug 'dgryski/vim-godef'

" 可以在 vim 中使用 tab 补全
"Plug 'vim-scripts/SuperTab'

" 可以在 vim 中自动完成
"Plug 'Shougo/neocomplete.vim'


" 插件结束的位置,插件全部放在此行上面
call plug#end()

安装插件

打开 vim 输入 :PlugInstall

安装YouCompleteMe

YouCompleteMe需要单独安装

1
2
cd ~/.vim/plugged/YouCompleteMe
python3 install.py

安装 ctags

tagbar需要单独安装ctags

1
sudo apt install ctags

插件使用

切换窗口

CTRL-w j
光标切换到下一个窗口。
CTRL-w k
光标切换到上一个窗口。
CTRL-w h
光标切换到左边窗口。如果左边没有窗口,保持在当前窗口不变。
CTRL-w l
光标切换到右边窗口。
CTRL-w w
在各个窗口之间来回切换。每输入一次,切换一个窗口。不停输入,可以遍历所有窗口。在只有两个窗口时很方便来回切换。

YouComplete 代码自动补全

自动补全代码,使用方法和普通IDE一样

NerdTree 文件目录

.vimrc中添加

1
2
map <F3> :NERDTreeMirror<CR>
map <F3> :NERDTreeToggle<CR>

设置F3为打开文件目录的快捷键

基本操作快捷键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
?: 快速帮助文档
o: 打开一个目录或者打开文件,创建的是buffer,也可以用来打开书签
go: 打开一个文件,但是光标仍然留在NERDTree,创建的是buffer
t: 打开一个文件,创建的是Tab,对书签同样生效
T: 打开一个文件,但是光标仍然留在NERDTree,创建的是Tab,对书签同样生效
i: 水平分割创建文件的窗口,创建的是buffer
gi: 水平分割创建文件的窗口,但是光标仍然留在NERDTree
s: 垂直分割创建文件的窗口,创建的是buffer
gs: 和gi,go类似
x: 收起当前打开的目录
X: 收起所有打开的目录
e: 以文件管理的方式打开选中的目录
D: 删除书签
P: 大写,跳转到当前根路径
p: 小写,跳转到光标所在的上一级路径
K: 跳转到第一个子路径
J: 跳转到最后一个子路径
<C-j>和<C-k>: 在同级目录和文件间移动,忽略子目录和子文件
C: 将根路径设置为光标所在的目录
u: 设置上级目录为根路径
U: 设置上级目录为跟路径,但是维持原来目录打开的状态
r: 刷新光标所在的目录
R: 刷新当前根路径
I: 显示或者不显示隐藏文件
f: 打开和关闭文件过滤器
q: 关闭NERDTree
A: 全屏显示NERDTree,或者关闭全屏

tagbar 大纲式导航

.vimrc中添加nmap <F9> :TagbarToggle<CR> 绑定F9为打开关闭的快捷键
上下移动(jk), 选中回车后会跳转

vimgrep 文件内查找

在 Vim 命令行模式下输入 :h vimgrep 可以看到 vimgrep 的使用文档

命令

1
2
:vim[grep][!] /{pattern}/[g][j] {file} ...
:vim[grep][!] {pattern} {file} ...

含义

  • vim 可作为 vimgrep 的缩写
  • ! 可紧随 vimgrep 之后,表示强制执行该命令
  • 索引的关键字 pattern 放在了两个 “/” 中间,并且支持正则表达式
  • g, j 可选。 如果添加 g,将显示重复行, 如果添加 j,vim 将不会自动跳转到第一个匹配的行(可能是别的文件)
  • file 可以是正则文件名,也可以是多个确定的文件名

使用
:vimgrep /user/g **
使用该命令可以查找当前目录下所有文件中包含 user 字符串的文件,并跳转到第一个匹配的文件,
同时所有搜索结果会显示在 Quickfix 中,使用 :cw, :copen 可以打开该列表

更多命令

1
2
3
4
5
:cnext, :cn         # 当前页下一个结果
:cprevious, :cp # 当前页上一个结果
:clist, :cl # 使用 more 打开 Quickfix 窗口
:copen, :cope, :cw # 打开 Quickfix 窗口,列出所有结果
:ccl[ose] # 关闭 Quickfix 窗口。

PVE升级

更换apt源并升级pve

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mv /etc/apt/sources.list.d/pve-enterprise.list /etc/apt/sources.list.d/pve-enterprise.list.bak
# 屏蔽订阅企业源
wget https://mirrors.ustc.edu.cn/proxmox/debian/proxmox-release-bookworm.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
echo "deb https://mirrors.ustc.edu.cn/proxmox/debian bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-no-subscription.list
# 使用Proxmox非企业版中科大源
sed -i 's|^deb http://ftp.debian.org|deb https://mirrors.ustc.edu.cn|g' /etc/apt/sources.list
sed -i 's|^deb http://security.debian.org|deb https://mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list
# 将Debian官方源替换为中科大源
echo "deb https://mirrors.ustc.edu.cn/proxmox/debian/ceph-quincy bookworm no-subscription" > /etc/apt/sources.list.d/ceph.list
# 替换Ceph源
sed -i 's|http://download.proxmox.com|https://mirrors.ustc.edu.cn/proxmox|g' /usr/share/perl5/PVE/APLInfo.pm
# 替换CT镜像下载源(重启PVE才能生效)
apt update
#更新源列表
apt upgrade
#升级软件包
apt dist-upgrade
#升级系统
apt install openvswitch-switch
#安装OVS

系统盘迁移

truenas scale

基本步骤

  1. 导出旧的配置文件
  2. 重新安装后倒入配置文件
  3. 修改网卡mac地址(可选,主要是我路由器配置了静态DHCP,懒得改路由器了,直接在pve中改的虚拟网卡)
  4. 硬盘直通。以下详细讲解

硬盘直通

硬盘直通有三种方式:

  1. 单个硬盘直通,pve只支持一个虚拟机通最多6块sata硬盘
  2. 通过pcie设备管理添加sata控制器。但这样控制器下的所有sata设备都会通进去
  3. 使用阵列卡,然后将阵列卡通过pcie通进去

我需要一块sata ssd当ubuntu系统盘,又懒得买阵列卡,所以选的第一种方法。刚好需要通6块hdd进去

1
2
3
4
5
6
7
8
9
10
11
12
root@pve:~# ls -l /dev/disk/by-id/
> total 0
lrwxrwxrwx 1 root root 9 Oct 28 23:25 ata-HGST_HUS728T8TALE6L4_VAK95U8L -> ../../sdf
root@pve:~# qm set 100 -sata0 /dev/disk/by-id/ata-HGST_HUS728T8TALE6L4_VAK95U8L
> update VM 100: -sata0 /dev/disk/by-id/ata-HGST_HUS728T8TALE6L4_VAK95U8L

qm set 100 -sata0 /dev/disk/by-id/ata-HGST_HUS728T8TALE6L4_VAK95U8L
qm set 100 -sata1 /dev/disk/by-id/ata-ST5000DM000-1FK178_W4J0YDC6
qm set 100 -sata2 /dev/disk/by-id/ata-TOSHIBA_MD04ABA400V_39KFK08HFMYB
qm set 100 -sata3 /dev/disk/by-id/ata-TOSHIBA_MD04ABA400V_39KRK24AFMYB
qm set 100 -sata4 /dev/disk/by-id/ata-TOSHIBA_MG07ACA14TE_X0F0A016F94G
qm set 100 -sata5 /dev/disk/by-id/ata-TOSHIBA_MG08ACA14TE_71W0A0RNFRVH

ubuntu

直接使用旧硬盘直通

1
qm set 101 -sata0 /dev/disk/by-id/ata-SanDisk_SSD_PLUS_240GB_19192U446110

由于旧磁盘是EFI格式,需要将BIOS设置为OVMF(UEFI),并给虚拟机添加EFI磁盘