0%

离职系列 第八篇
离职系列,想想这几年在公司的成长,在这做个记录。

此篇是因为遇到了太多环境类问题,从LMT建立的数据统计,这类问题占比已经超过了我处理问题的60%左右,占所有现场问题的20%左右以上,所以抽了个时间把可以在前置检查中避免的问题都梳理出来,试着写了个脚本,并一次次到现场验证,最终有了以下版本。该脚本在产品没有出根解之前,直接交给了TAC,让其与一线一起前置处理,避免过多的流转到LMT。

检查脚本,

   
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
#!/bin/bash

# 环境前置检查脚本(用于提前感知问题,减少安装失败、升级失败等问题出现)
# by hht
# 上传后,chmod +x 授予执行权限,然后./pre_check.sh 直接执行

# 输出错误到文件
exec 2>/tmp/pre_test_error.log
# ANSI颜色代码
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # 恢复默认颜色
YELLOW='\033[0;33m'
YELLOW_BG='\033[43m'
BLACK='\033[30m' # 黑色字体
none='\e[0m'
BLUE="\e[0;94m"
_red_bg() { echo -e "\e[41m$@${none}"; }
is_err=$(_red_bg 异常!)

warn() {
echo -e "\n${YELLOW_BG}${BLACK}警告!${NC} $@\n"
}
err() {
echo -e "\n$@ $is_err\n"
}

# 函数来检验IP地址的有效性
is_valid_ip() {
local ip="$1"
local ip_regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$"

if [[ $ip =~ $ip_regex ]]; then
return 0
else
return 1
fi
}

# 声明账号
server1_user="root"
server2_user="root"

echo -e "\n${GREEN}------------------------------------前置检查------------------------------------${NC}"
echo
# 输入服务器IP地址,进行校验
while true; do
echo "请输入master服务器的IP地址:"
read server1_ip

if is_valid_ip "$server1_ip"; then
break
else
warn "无效的IP地址,请重新输入。"
fi
done

while true; do
echo "请输入worker服务器的IP地址:"
read server2_ip
if is_valid_ip "$server2_ip"; then
break
else
warn "无效的IP地址,请重新输入。"
fi
done
# 测试Ping
echo -e "\n${BLUE}ping测试:${NC}\n"
ping_check(){
local server1_ip=$1
local server2_ip=$2
ping -c 3 $server1_ip > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo -e "$server1_ip 可以Ping通。${GREEN}正常${NC}"
else
err "$server1_ip 无法Ping通。"
fi

ping -c 3 $server2_ip > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo -e "$server2_ip 可以Ping通。${GREEN}正常${NC}"
else
err "$server2_ip 无法Ping通。" && exit 1
fi

}
ping_check $server1_ip $server2_ip
echo -e "\n------------------------------------------------------------------------------"
# SSH测试
echo -e "\n${BLUE}SSH测试:${NC}\n"
ssh_check(){
local server1_ip=$1
local server2_ip=$2
if sshpass timeout 10s ssh -o StrictHostKeyChecking=no root@$server1_ip echo "SSH test" 2>/dev/null; then
echo -e "Success: SSH from $server1_ip to $server2_ip connected.${GREEN}正常${NC}"
else
err "SSH from $server1_ip to $server2_ip failed"
fi
}

ssh_check $server1_ip $server2_ip
echo -e "\n------------------------------------------------------------------------------"
# 时间一致性检查
echo -e "\n${BLUE}时间一致性检查:${NC}\n"
date_check(){
local server1_ip=$1
local server2_ip=$2
# 时间一致性检查
server1_time=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$server1_ip date +%s)
server2_time=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$server2_ip date +%s)

if [ $server1_time -eq $server2_time ]; then
echo -e "两台服务器的时间完全一致。${GREEN}正常${NC}"
else
# 使用 date 命令将时间戳转换为日期和时间
formatted1_time=$(date -d "@$server1_time")
formatted2_time=$(date -d "@$server2_time")
err "两台服务器的时间不一致。"
echo -e "${server1_ip}时间为:${formatted1_time}"
echo -e "${server2_ip}时间为:${formatted2_time}"
fi
}

date_check $server1_ip $server2_ip
echo -e "\n------------------------------------------------------------------------------"

# 添加检查防火墙是否开启
echo -e "\n${BLUE}检查防火墙:${NC}\n"
check_firewall_status() {
local server_ip=$1

# 使用 ssh 连接到服务器并查看 firewalld 服务的状态
firewall_status=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "root@$server_ip" "systemctl is-active firewalld")

if [ "$firewall_status" = "active" ]; then
warn "$server_ip 防火墙已开启。请根据http://172.17.160.32:18090/x/cYA0CQ检查端口"
else
echo "$server_ip 防火墙未开启。"
fi
}

# 调用这个函数并传入服务器 IP 和用户名
check_firewall_status $server1_ip
check_firewall_status $server2_ip
echo -e "\n------------------------------------------------------------------------------"

# DNS配置检查
echo -e "\n${BLUE}DNS检查:${NC}\n"
dns_check(){
local server1_user=$1
local server2_user=$1
local server1_ip=$2
local server2_ip=$3
# DNS配置检查 - 验证是否一致
server1_dns=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server1_user@$server1_ip cat /etc/resolv.conf)
server2_dns=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server2_user@$server2_ip cat /etc/resolv.conf)

if [ "$server1_dns" == "$server2_dns" ]; then
echo -e "两台服务器的DNS配置一致。${GREEN}正常${NC}"
else
warn "两台服务器的DNS配置不一致。请判断是否影响集群。"
fi


# DNS配置检查 - 验证是否存在多行nameserver记录
server1_nameserver_count=$(echo "$server1_dns" | grep -c '^nameserver')
server2_nameserver_count=$(echo "$server2_dns" | grep -c '^nameserver')

if [ $server1_nameserver_count -eq 1 ] && [ $server2_nameserver_count -eq 1 ]; then
echo -e "两台服务器的DNS配置中只存在一行nameserver记录。${GREEN}正常${NC}"
else
if [ $server1_nameserver_count -ne 1 ]; then
warn "第一台服务器($server1_ip)的DNS配置存在多行nameserver记录。请判断是否影响集群。"
fi

if [ $server2_nameserver_count -ne 1 ]; then
warn "第二台服务器($server2_ip)的DNS配置存在多行nameserver记录。请判断是否影响集群。"
fi
fi
}

dns_check $server1_user $server1_ip $server2_ip
echo -e "\n------------------------------------------------------------------------------"
# 挂载检查
echo -e "\n${BLUE}数据盘挂载检查:${NC}\n"
mount_check(){
local server1_user=$1
local server2_user=$1
local server1_ip=$2
local server2_ip=$3
server1_mount=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server1_user@$server1_ip mount | grep /opt/local-path-provisioner)
server2_mount=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server2_user@$server2_ip mount | grep /opt/local-path-provisioner)

if [ -n "$server1_mount" ] && [ -n "$server2_mount" ]; then
echo -e "两台服务器都正确挂载了/opt/local-path-provisioner目录。${GREEN}正常${NC}"
else
# echo -e "两台服务器中有一台或两台未正确挂载/opt/local-path-provisioner目录。${RED}异常${NC}"
if [ -z "$server1_mount" ]; then
err "第一台服务器($server1_ip)未正确挂载/opt/local-path-provisioner目录。"
fi

if [ -z "$server2_mount" ]; then
err "第二台服务器($server2_ip)未正确挂载/opt/local-path-provisioner目录。"
fi
fi
}

mount_check $server1_user $server1_ip $server2_ip
echo -e "\n------------------------------------------------------------------------------"
# 使用Telnet检查SFTP服务是否联通
# check_sftp() {
# local server_ip=$1
# echo -e "\n-----------使用Telnet检查SFTP服务是否联通-------------"
# # # 使用Telnet连接到SSH端口(默认是22)
# # # 尝试连接SFTP服务器
# # sftp -oPort=22 $server_ip <<EOF 2>/dev/null
# # quit
# # EOF

# # # Check the exit status of the SFTP command
# # if [ $? -eq 0 ]; then
# # echo "SFTP on $IP is working normally"
# # else
# # echo "SFTP on $IP is not working!"
# # fi

# # 使用SSH命令测试SFTP服务
# ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null sftpuser@$server_ip sftp exit > /dev/null 2>&1

# # 检查SSH命令的退出状态码
# if [ $? -eq 0 ]; then
# echo "SFTP服务正常,可以连接到主机 $server_ip 的SFTP服务。"
# else
# err "SFTP服务异常,无法连接到主机 $server_ip 的SFTP服务。"
# fi
# }
# # 调用函数来进行Telnet检查
# check_sftp $server_ip

# 检查master SSH服务状态和配置
echo -e "\n${BLUE}master SSH服务状态和配置检查:${NC}\n"
check_sshd_config() {

local server_ip=$1
local server_user=$2

# 使用 ssh 连接到服务器并执行命令检查SSH服务状态
ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "systemctl is-active sshd" > /dev/null 2>&1

if [ $? -eq 0 ]; then
# 获取SSH配置文件内容
sshd_config_content=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "cat /etc/ssh/sshd_config")

# 检查配置文件内容是否包含指定的三行数据
if echo "$sshd_config_content" | grep -q -E "Subsystem\s+sftp\s+internal-sftp" && echo "$sshd_config_content" | grep -q "Match User sftpuser" && echo "$sshd_config_content" | grep -q "ChrootDirectory /opt/ftpfile/sftp/sftpuser/"; then
echo -e "SSH配置正常。${GREEN}正常${NC}"
else
warn "请检查sshd_config文件,是否正确配置sftp。"
fi
else
warn "无法连接服务器或检查SSH服务状态。"
fi
}

# 调用函数执行检查
check_sshd_config $server1_ip $server1_user
echo -e "\n------------------------------------------------------------------------------"
# sftp_with_password() {
# echo -e "\n-----------检查master的sftp连通性-------------"
# local server_ip=$1
# local server_password=$2

# # 使用 expect 来自动输入密码
# expect -c "
# spawn sftp -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null sftpuser@$server_ip
# expect {
# \"password:\" {
# send \"$server_password\n\"
# exp_continue
# }
# eof
# }
# "

# if [ $? -eq 0 ]; then
# echo -e "$server_ip SFTP连接成功。${GREEN}正常${NC}"
# else
# warn "无法连接$server_ip 的SFTP服务。"
# fi
# }

# # 调用函数来进行SFTP连接检查
# sftp_with_password $server1_ip "FYktvR1w2upoOb"

# 检查目录权限是否为777
# check_directory_permission() {
# local server_ip=$1
# local directory_path=$2
# echo -e "\n-----------检查master的目录${directory_path}权限-------------"

# # 使用 ssh 连接到服务器并执行 stat 命令获取目录权限信息
# ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null sftpuser@$server_ip "stat -c %a $directory_path" > /dev/null 2>&1

# if [ $? -eq 0 ]; then
# # 获取目录权限
# directory_permission=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null sftpuser@$server_ip "stat -c %a $directory_path")

# if [ "$directory_permission" -eq 777 ]; then
# echo -e "$directory_path 目录权限为777。${GREEN}正常${NC}"
# else
# warn "$directory_path 目录权限不是777。"
# fi
# else
# warn "无法连接$server_ip 服务器或获取目录权限。"
# fi
# }
echo -e "\n${BLUE}检查master的sftp目录权限:${NC}\n"
check_user() {
ssh $1 "id $2 >/dev/null 2>&1"
if [ $? -eq 0 ]; then
echo -e "$2用户在$1上存在。${GREEN}正常${NC}"
else
err "$2用户在$1上不存在"
fi
}

check_dir_perm() {
ssh $1 "if [ \`stat -c %a $2\` -eq $3 ]; then echo -e '$2目录为$3权限。${GREEN}正常${NC}'; else err '$2目录不为$3权限'; fi"
}

# 调用函数来检查服务器的目录权限
check_user $server1_ip "sftpuser"

check_dir_perm $server1_ip "/opt" 755

check_dir_perm $server1_ip "/opt/ftpfile/sftp/sftpuser" 755

echo -e "\n------------------------------------------------------------------------------"
# check_directory_permission $server1_ip "/opt"
# check_directory_permission $server1_ip "/opt/ftpfile/sftp/sftpuser"

# 检查主机名与/etc/hosts文件的一致性
echo -e "\n${BLUE}检查主机名的一致性:${NC}\n"
check_hostname_and_hosts() {

local server_ip=$1
local server_user=$2
# 使用 ssh 连接到服务器并获取主机名
server_hostname=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "hostname")

if [ $? -eq 0 ]; then
# 获取 /etc/hosts 文件内容
hosts_file_content=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "cat /etc/hosts")

# 检查主机名与 /etc/hosts 内是否一致
if echo "$hosts_file_content" | grep -q -E "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s+$server_hostname\s*$"; then
echo -e "$server_ip 主机名与/etc/hosts文件一致。${GREEN}正常${NC}"
else
warn "$server_ip 主机名与/etc/hosts文件不一致。"
fi
else
warn "无法连接$server_ip 服务器或获取主机名。"
fi
}

# 检查主机名与Kubernetes节点名是否一致
check_hostname_and_k8s_node() {

local server_ip=$1
local server_user=$2
local num=$3
# 使用 ssh 连接到服务器并获取Kubernetes节点名
k8s_node_name=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "kubectl get node -o jsonpath='{.items[$num].metadata.name}'")

if [ $? -eq 0 ]; then
# 获取主机名
server_hostname=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "hostname")

# 检查主机名与Kubernetes节点名是否一致
if [ "$server_hostname" = "$k8s_node_name" ]; then
echo -e "$server_ip 主机名与Kubernetes节点名一致。${GREEN}正常${NC}"
else
warn "$server_ip 主机名与Kubernetes节点名不一致。"
fi
else
warn "无法连接$server_ip 服务器或获取Kubernetes节点名。"
fi
}

# 调用函数来检查服务器的主机名与/etc/hosts文件一致性
check_hostname_and_hosts $server1_ip $server1_user
check_hostname_and_hosts $server2_ip $server2_user

# 调用函数来检查服务器的主机名与Kubernetes节点名一致性
check_hostname_and_k8s_node $server1_ip $server1_user 0
check_hostname_and_k8s_node $server2_ip $server2_user 1
echo -e "\n------------------------------------------------------------------------------"

# 磁盘写入速度测试(带缓存)
# disk_speed_test() {
# # local server_ip=$1
# # local server_user=$2
# # local test_file="/tmp/disk_speed_test_file"
# # # 进行写入测试
# # write_speed=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "dd if=/dev/zero of=${test_file} bs=8k count=128 conv=fsync oflag=direct" 2>&1 | tail -n 1)
# # echo -e "${server_ip} 写入速度: $write_speed"
# # # 进行读取测试
# # read_speed=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "sudo dd if=${test_file} of=/dev/null bs=8k count=128 conv=fsync iflag=direct" 2>&1 | tail -n 1)
# # echo -e "${server_ip} 读取速度: $read_speed"

# # # 删除测试文件
# # ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "rm $test_file"
# }

# 测试磁盘读速度的函数
disk_speed_test() {
local server_ip=$1
local test_duration=30 # 测试持续时间(秒)

echo "开始测试 $server_ip 过程会持续30s..."

local time0=$(date "+%s")
cat /dev/null > disk_res

while ((($(date "+%s") - time0) <= test_duration)); do
disk_info=$(ssh "$server_ip" 'dd if=/dev/zero of=output.file bs=8k count=128 conv=fsync 2>&1 1>/dev/null')
io_res=$(echo "$disk_info" | grep --only-matching -E '[0-9.]+ ([MGk]?B|bytes)/[s(ec)?|秒]')
echo "$io_res" >> disk_res
done

local count=$(cat disk_res | wc -l)
local sum=$(cat disk_res | xargs -n2 | awk '{ if ($2 == "kB/秒" || $2 == "kB/s") a+=($1/1024); else a+=$1 } END{printf("%.2f", a)}')
local average_speed=$(awk 'BEGIN{printf "%.2f\n", '$sum'/'$count'}')

echo -e "平均速度 $server_ip: ${RED}$average_speed MB/s${NC}。推荐值:${GREEN}>=200m/s${NC}"
}



# 磁盘写入速度测试(不带缓存)

# disk_speed_test_no_cache() {
# local server_ip=$1
# local server_user=$2
# local test_file="/tmp/disk_speed_test_file"
# # 禁用磁盘缓存
# ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "hdparm -W0 /dev/mapper/centos-root" 2>/dev/null
# # 进行写入测试
# write_speed=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "dd if=/dev/zero of=${test_file} bs=8k count=128 conv=fsync oflag=direct" 2>&1 | tail -n 1)
# echo -e "${server_ip} 写入速度: $write_speed"
# # 进行读取测试
# read_speed=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "sudo dd if=${test_file} of=/dev/null bs=8k count=128 conv=fsync iflag=direct" 2>&1 | tail -n 1)
# echo -e "${server_ip} 读取速度: $read_speed"

# # 删除测试文件
# ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "rm $test_file"
# # 启用磁盘缓存
# ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $server_user@$server_ip "hdparm -W1 /dev/mapper/centos-root" 2>/dev/null
# }

# 测试磁盘写入速度的函数
disk_speed_test_no_cache() {
local server_ip="$1"
local test_duration=30 # 测试持续时间(秒)

echo "开始测试 $server_ip 过程会持续30s..."

local time0=$(date "+%s")
cat /dev/null > disk_res

while ((($(date "+%s") - time0) <= test_duration)); do
disk_info=$(ssh "$server_ip" 'dd if=/dev/zero of=output.file bs=8k count=128 oflag=direct,nonblock conv=fsync 2>&1 1>/dev/null')
io_res=$(echo "$disk_info" | grep --only-matching -E '[0-9.]+ ([MGk]?B|bytes)/[s(ec)?|秒]')
echo "$io_res" >> disk_res
done

local count=$(cat disk_res | wc -l)
local sum=$(cat disk_res | xargs -n2 | awk '{ if ($2 == "kB/秒" || $2 == "kB/s") a+=($1/1024); else a+=$1 } END{printf("%.2f", a)}')
local average_speed=$(awk 'BEGIN{printf "%.2f\n", '$sum'/'$count'}')

echo -e "平均速度 $server_ip: ${RED} $average_speed MB/s ${NC}。推荐值:${GREEN}>=50m/s${NC}"
}


# 检查 CPU 是否支持 AVX
echo -e "\n${BLUE}检查 CPU 是否支持 AVX:${NC}\n"
check_avx_support() {
local server_ip=$1
local avx_check=$(ssh -q -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$server_ip "grep -o 'avx' /proc/cpuinfo")

if [ -n "$avx_check" ]; then
echo -e "$server_ip CPU 支持 AVX (Advanced Vector Extensions)。${GREEN}正常${NC}"
else
warn "$server_ip CPU 不支持 AVX (Advanced Vector Extensions)。"
fi
}

# 调用函数来检查 AVX 支持
check_avx_support $server1_ip
check_avx_support $server2_ip

# 定义要加载镜像的目录列表
load_images() {
image_directories=("/opt/xx/images/product/insight" "/opt/xx/images/product/insight/patch")

# 遍历目录并加载镜像
for directory in "${image_directories[@]}"; do
if [ -d "$directory" ]; then
cd "$directory"
echo "进入目录: $directory"
for file in $(ls . | grep .tgz); do
echo "加载镜像: $file"
docker load < "$file"
done
else
echo "目录 $directory 不存在。"
fi
done
}


# 检查磁盘性能
echo -e "\n${BLUE}磁盘测试:${NC}\n"
# read -p "是否要检查磁盘? (y/n): " confirm
echo "是否要检查磁盘? (y/n):"
read confirm
if [ "$confirm" = "y" ]; then
echo -e "\n${BLUE}磁盘写入速度测试(不带缓存):${NC}\n"
disk_speed_test_no_cache $server1_ip
disk_speed_test_no_cache $server2_ip
echo -e "\n${BLUE}磁盘写入速度测试(带缓存):${NC}\n"
disk_speed_test $server1_ip
disk_speed_test $server2_ip
else
echo "取消磁盘检查。"
fi

# 镜像丢的时候使用
echo -e "\n${BLUE}镜像加载:${NC}\n"
echo "是否要加载 Docker 镜像? (y/n):"
read confirm
# read -p "是否要加载 Docker 镜像? (y/n): " confirm
if [ "$confirm" = "y" ]; then
load_images
else
echo "取消加载 Docker 镜像。"
fi



离职系列 第七篇
离职系列,想想这几年在公司的成长,在这做个记录。此篇主要谈谈LMT时,处理的两次OOM问题。

为啥是两次?因为都很有代表性,一次可以算是三方库使用不当,一次是程序自身的问题。

第一次

JPA使用不当,….待续

第二次

应用频繁重启,….待续

离职系列 第七篇
离职系列,想想这几年在公司的成长,在这做个记录。上一篇服务可用性定位问题常用命令,针对服务可用性常用命令进行了说明,这一篇主要是讲实践案例。

因为我在LMT除了日常管理工作外,额外向领导请求了承担部分现场问题处理,挑的比较陌生且有挑战性的“服务可用性的问题”,想着在实践中学习,并且这部分问题是最麻烦的没有什么标准,往往又是最紧急的,我担起来一是减少了组员上报成本,另一个我更方便直接找到对应的负责人进行沟通,提高效率。所以这块积累的经验更多。

案例分享

简单分享几个案例,一个主要对外(TAC),一个对内(组内研发)。两个文档因为对象不同,使用场景不同,稍有差异。

整理案例,一是为了组内共同学习,二是提供给TAC团队,让其能更多的拦截一些一线提出的较简单的现场问题。

案例1(TAC)

案例2(TAC)

案例3(研发)

离职系列 第六篇
离职系列,想想这几年在公司的成长,在这做个记录。此篇主要谈谈LMT时期。

因为现场故障直接影响客户口碑、人力成本等事业部的重要指标,所以事业部领导要求针对现场故障处理需要有单独的奖惩制度,让整个事业部重视起来,所以我整理了对应的内容用于事业部邮件同步。

事故的定义

故障处理或产品自身服务中断造成客户损失、恶劣影响。(一线、客户投诉且影响产品口碑的)。

事故处理

奖惩制度

这块就比较个性化了,主要看重视程度,需要根据公司规章制度和领导的要求做调整。

离职系列 第五篇
离职系列,想想这几年在公司的成长,在这做个记录。上一篇现场故障定位指南,主要讲的方法论,这篇主要对服务可用性的几个场景总结下相应的命令。

以下命令主要针对现场经常出现的安装失败、升级失败、补丁失败、服务不断重启、服务不可用几个场景:

  1. 安装失败,通常就是现场环境问题,比如服务器的磁盘性能不达标、网络通信问题、服务器DNS配置错误、集群IP段不可用
  2. 升级失败,通常和服务器的资源紧张有关(内存、磁盘、CPU等)
  3. 服务不断重启,通常是基础组件问题如redis异常、应用pod自身程序的bug如OOM、k8s组件问题如etcd重启
  4. 服务不可用,通常就是集群出了问题,比如磁盘满了导致镜像丢失

命令

  1. 确认环境信息
    • 环境信息
      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
      # 操作系统版本
      cat /etc/redhat-release # CentOS版本
      cat /etc/openEuler-release # 欧拉版本
      uname -r # 内核版本
      cat /proc/version # 内核编译信息
      hostnamectl # 查看完整的系统信息

      # 系统基础信息
      df -h # 磁盘空间
      free -h # 内存使用
      top # CPU和进程状态
      netstat -ant # 网络连接
      uptime # 系统负载
      iostat -x 1 10 # 磁盘状态

      # 进程分析
      ps -ef | grep 进程名
      pstree -p 进程ID
      lsof -p 进程ID

      # 分区及挂载
      lsblk # 查看块设备
      df -Th # 查看文件系统类型和空间
      mount | grep -E "^/dev" # 查看挂载参数

      # 磁盘空间
      du -sh /* | sort -hr # 大文件目录排序
      # 时间同步状态
      chronyc sources -v
    • K8s集群状态
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      # K8s集群状态
      systemctl status kubelet # kubelet是否正常
      systemctl status docker # docker是否正常
      systemctl status NetworkManager # 网络连接工具是否正常
      kubectl cluster-info #查看集群信息
      kubectl get nodes # kubelet集群节点
      kubectl get po -A # 查看所以pod状态
      kubectl get po -A -owide # 查看所以pod的ip和所在的node
      kubectl describe node <node-name>
      kubectl get events -n <namespace> #Kubernetes 事件日志
      journalctl -u kubelet -f # 日志查看
      cat /var/log/messages | grep xx # 日志查看

      # 应用Pod状态
      kubectl get pods -n <namespace> -o wide
      ping pod_ip # 判断容器之间的联通性
      kubectl describe <pod-name> -n <namespace>
      kubectl exec -it <pod-name> -n <namespace> /bin/sh # 进入容器内部
      kubectl logs <pod-name> -n <namespace>

      # 集群资源状态
      kubectl top nodes
      kubectl top pods -n <namespace>
  • 客户网络环境限制(可用端口、防火墙策略)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 网络组件
    ip link show
    iptables -L

    # DNS配置
    cat /etc/resolv.conf
    # 网络分析
    curl url # 应用连通性
    fping -c xx -p xx 目标IP或域名 # 基础连通性
    ping <目标IP> # 基础连通性
    telnet <IP> <端口> # 端口连通性
    traceroute # 路由跟踪
    tcpdump -i any port <端口> -w dump.pcap # 抓包分析

参考

https://jimmysong.io/kubernetes-hndbaook/guide/using-kubectl.html
https://kubernetes.io/zh-cn/docs/tasks/debug/_print/
https://cheat.sh/
https://kubernetes.io/docs/reference/kubectl/cheatsheet/
https://kubernetes.io/zh/docs/reference/kubectl/
https://docs.docker.com/engine/reference/run/


离职系列 第四篇
离职系列,想想这几年在公司的成长,在这做个记录。此篇主要谈谈LMT时,简单总结了一套针对问题现场定位方法论。

前言

在客户现场环境中,我们往往面临网络隔离、工具受限、信息不完整等挑战。并且由于LMT(9人)资源有限,但是试用+付费的现场却有500左右,且数字还在不断增加,因此需要科学的方法论和充分的实践经验,下面是我针对服务异常问题整理的,该问题出现频次最高且专业性强涉及多种操作系统(欧拉、centos、麒麟),整理文档方便其它成员学习实践。成员可以结合实际情况灵活调整。

定位指南

第一阶段:问题收集与初步分析(原则上TAC或一线提供)

  1. 确认问题的基本信息

    • 问题的具体表现(错误信息、异常行为等)
    • 问题的影响范围
    • 问题的发生时间和频率
    • 问题是否可复现
  2. 建立问题基线

    • 首次发现问题的时间点
    • 相关变更的时间点(补丁、升级、断电等)
    • 现场采取的临时措施

第二阶段:快速诊断(LMT)

  1. 检查环境

    • k8s集群、组件状态、应用pod状态
    • 检查系统资源(磁盘、内存等)
  2. 检查日志信息

    • 查看集群日志、组件日志
    • 查看应用pod日志
  3. 进行初步故障假设

    • 根据已收集的信息提出可能的故障原因
    • 按照影响范围和可能性排序
    • 可通过经验+知识库等制定快速验证方案

第三阶段:深入分析(LMT+后端研发接口人)

  1. 验证假设

    • 复现问题场景
    • 收集更多证据支持或否定假设
  2. 确定初步根因

    • 总结所有收集到的证据
    • 确认问题的触发条件
    • 建立问题发生的完整链路
  3. 是否升级问题

    • 如果验证有出入或者没有更好的办法则转交问题到我
    • 我来决定是否升级问题(申请后端研发介入)

第四阶段:解决方案(LMT+后端研发接口人+TAC+一线)

  1. 制定修复方案

    • 提出短期解决方案(快速修复)
    • 设计长期解决方案(根本解决)
    • 评估方案的风险和影响并告知一线,让其与客户沟通确认
  2. 实施修复

    • 客户确认后,在测试环境验证解决方案
    • 准备回滚方案(备份数据、备份镜像等)
    • 实施修复并验证效果

注意事项

  1. 所有重要操作前先备份
  2. 收集足够的证据再行动
  3. 重要变更需要得到一线授权
  4. 保持操作记录的完整性
  5. 及时同步问题处理进展
  6. 警惕处理过程中的连锁反应

附一张简单的问题记录卡模板

离职系列 第三篇
离职系列,想想这几年在公司的成长,在这做个记录。此篇主要谈谈LMT时,整理的现场故障处理流程。

LMT团队对外最大的价值就是及时响应现场故障,有点Google SRE On-Call Engineer的感觉,但是现场问题往往较复杂或处理链条很长,所以必须要有一个相对标准且高效的流程,让各角色团队达成一致,从而能快速推进。

LMT主要职责

  1. 快速响应告警,处理生产环境中的故障,对故障分级,同时保证SLA时效。

  2. 故障排查

    1. 使用日志、指标和工具(如 fping、netdata、arthas 等)定位问题的根本原因。
    2. 执行临时修复措施(如回滚、重启服务)以恢复服务。
    3. 问题升级
  3. 事后故障复盘

    1. 在故障解决后,组织处理人撰写事后分析报告,记录问题的根本原因、影响和改进措施。
    2. 预防,推动改进措施的实施,防止类似问题再次发生。
  4. 协作与沟通

    1. 跟进每一个现场故障,与开发团队、产品团队和其他相关方协作,确保问题得到彻底解决。
    2. 在故障期间向利益相关者提供状态更新和预计故障关闭时间。
  5. 知识库

    1. 和研发团队一起沉淀文档建立知识库,提升效率的同时培养人员。

现场故障处理流程

基于上面的职责,我梳理两个版本,0.5和1.0版本现场故障处理流程,主要在于先分清每个团队的职责,然后把现场故障能快速的流转起来,不管是否为疑难杂症,都做到万事有回响。

0.5版本

0.5版本用于建队初期,时间紧任务重,人员还未完全到位的情况。彼时LMT更多的是解决简单的问题以及跟进问题,大多问题的处理还是需要寻求原研发团队的支持故称接口人模式
(故障组就是LMT)

1.0版本

1.0版本是各核心模块人员配置到位,且各团队磨合期过了后,整理的,1.0版本流程重点主要在两方面:

  1. LMT能独立解决大部分现场故障
  2. LMT没法及时解决的需要有故障的升级路径,升级后LMT转为跟进故障处理情况并反馈一线。

后续还有2.0版本,但是因为我已经不在LMT,且职责已经跟当初我建立时大相径庭所以我就不做梳理了。

最后

一定不要忘了还有 复盘与改进

  1. 复盘

    • 使用标准的文档模板,由实际故障处理人记录问题的完整过程,总结根因
    • 复盘时共创改进措施,并每项都建立跟进人和时间
  2. 预防措施

    • 举一反三,自查类似问题
    • 更新知识库
    • 加强相关人员培训
  3. 填单
    整个流程,为了各团队统一语言,所有过程记录我们要求都基于JIRA单,每个团队对应其流程节点,对JIRA单进行扭转和补充。

离职系列 第二篇
离职系列,想想这几年在公司的成长,在这做个记录。此为第二篇,我在LMT时,整理的一个现场缺陷处理流程。

LMT的诞生背景

随着客户规模的扩大和系统复杂性的提升,产品在客户现场面临的问题越来越多,如果没有专门的团队负责快速处理这些问题,而是像之前一样流到PDT团队,事业部会面临以下困境:

  1. 响应延迟:
    1. 故障发生后,没有明确的责任人或团队,响应时间过长。
    2. 多次传递信息易导致关键信息丢失或误解。
  2. 修复效率低下:
    1. 不同团队各自为战,缺乏统一协调,资源浪费严重。
    2. 处理人员对现场环境了解不足,容易导致误判或误操作。
  3. 业务影响扩大:
    1. 紧急问题未能及时解决,可能对用户体验和企业声誉造成负面影响,影响口碑。
    2. 业务中断时间延长,如升级为事故,则事业部需要追责且需要出具报告跟客户道歉。
  4. 缺乏经验积累:
    1. 故障处理缺乏记录与总结,类似问题反复发生却未能彻底解决。

LMT 的价值与必要性

LMT(Line Maintenance Team)是一支专注于现场故障处理的专业团队,其成立能够有效应对上述问题,带来以下价值:

  1. 快速响应,缩短问题解决时间
    • 通过明确的责任划分和快速响应机制,LMT会确保问题第一时间得到处理。
    • 使用标准化工具和诊断方法,快速定位问题。
    • 如遇到必须要对应的研发才能解决的问题时,LMT会对前期现场问题进行基本的定位处理,为后续研发提供输入,大大缩减研发修复问题的时间。
  2. 专业处理,提升修复效率
    • LMT团队成员筛选的都是技术经验丰富,对功能和业务场景有深入理解的研发,能对一线服务和TAC团队提供更专业的支持,减少对PDT团队的打断。
    • 对于无法远程或远程困难的现场,可提供在线技术支持。
  3. 降低业务中断风险
  • 优先处理对业务影响重大的问题,将损失降至最低。
  • 与其他团队协作,LMT可作为沟通的桥梁,推动重大问题尽快解决。
  1. 经验积累与优化
  • 故障处理经验录入知识库,形成可复用的解决方案。
  • 通过定期复盘优化流程,不断提升处理效率和稳定性。
  1. 统一管理与高效协作
  • 明确的组织架构和职责分工,解决了多部门协作中的沟通问题。
  • 借助Jira系统和实时监控平台,实现高效管理。
  • 产品补丁包打包由LMT接手,可减少Devops团队工作的同时,更贴近一线的需求。

正因如此,LMT 的成立成为企业提升运维效率、保障业务稳定性的必然选择。

最后

给你们贴一张LMT成立初期拦截现场故障的占比,你就知道单独抽出几个研发成立LMT有没有必要了。

AI 第一篇

背景

这篇文章拖了很久了。因为我之前带LMT团队,团队的主要工作内容就是处理客户现场问题,因为我要求过程留痕并且达到知识沉淀的效果,所以处理过程中产生了大量的文档,也就是很多经验都落到了文档上,比如linux系统(硬盘、系统版本、dns等)、docker、k8s、现场网络问题(防火墙、网闸等安全设备),也就是运维经常面临的问题,也有产品配置、bug等文档。

文档越来越多,但是却越来越难利用起来,我就想着把这些经验积累起来弄一个知识库,就像gpt一样,只要我输入有关问题他就能根据这些内容生成回答的内容,当然里面不仅仅是经验问答还需要有linux系统(硬盘、系统版本、dns等)、docker、k8s、现场网络问题(防火墙、网闸等安全设备)这些原始(原理)知识,还得有我们的产品知识。

这个知识库一是为了方便使用且能让我练手,二是当时公司推行创新活动,AI相关的案例也能为事业部加分。

开始

知识库的定位:小而美,因为是个人发起的前期申请到的服务器资源有限,而且功能非常单一明确,只需要满足知识库就行,不需要全能。

选型

通过一通查找对比,综合考虑,选择了Anything LLM或者Dify.AI+Ollama+Llama2小模型。

Anything LLM

alt text

考虑的其中一点

是用Anything LLM这种开箱即用的还是用LangChain这种需要自己上手写代码的
Anything LLM和Dify.AI在其架构中广泛使用了LangChain组件,尤其是:

  • 文档加载器(Document Loaders)
  • 文本分割器(Text Splitters)
  • 向量存储(Vector Stores)
  • 检索器(Retrievers)
  1. 直接使用Anything LLM这种系统的好处
    1. 意味着即使不直接编写LangChain代码,您也在间接使用LangChain的强大功能。这有几个好处:
      模块化设计 - 可以灵活替换组件(如切换向量数据库)
      经过验证的架构 - 使用业界已验证的RAG实现方式
      未来升级路径 - 如果您将来想更深入定制,可以直接使用LangChain API
  2. 坏处
    1. 没有深入的了解和实践经验

我当时其实想用LangChain的,环境都搭好了,但是因为没有系统的学习过,进度很慢,没法赶上评审节点,所以最终选择了使用Anything LLM这种简单的方式。

验证

Anything LLM+Ollama试了几种模型和Embedder,最终勉强得出一个组合Llama2:7B-chinese+bge-m3
还需要调整文档本身的内容,以及一些参数再多次尝试。

待续…

参考

https://docs.useanything.com/setup/llm-configuration/overview
https://github.com/Mintplex-Labs/anything-llm/blob/master/docker/HOW_TO_USE_DOCKER.md
https://adasci.org/anythingllm-for-local-execution-and-inferencing-of-llms-a-deep-dive/
https://itnext.io/deploy-flexible-and-custom-setups-with-anything-llm-on-kubernetes-a2b5687f2bcc
https://www.youtube.com/watch?v=4UFrVvy7VlA

AI 第二篇

背景

整理此篇最开始源于做知识库时AI应用-知识库时,当时就想整理写一篇关于AI的一些基础概念,个人扫盲专用,但是老忘。

刚好最近找工作看到了很多AI相关的JD,所以想借着找工作的空隙,补一补这块的知识。

其实我应该是比较早用chatgpt的用户,我都付费了一段时间后,我的很多同事才刚开始想方设法使用chatgpt,但是对于我来说更多时候把它作为一个效率工具或者新奇的玩具在用,关于AI的概念比较散,没有系统的深入了解过,刚好有工作中的机会让我意识到AI离我那么近,我应该好好认知它了,所以有了这篇文章,先把各种概念搞清楚。

正文

1. 人工智能的基础概念

什么是人工智能?

人工智能(Artificial Intelligence,简称AI)是指通过计算机系统模拟人类智能的技术。它让机器能够学习、推理、感知、交流,甚至模仿人类的认知能力。

AI的分类

从应用角度,AI可分为三种类型:

  1. 弱人工智能(Narrow AI):专注于解决特定问题的AI,如语音助手、图像识别等。现阶段大多数AI应用都属于此类。

  2. 通用人工智能(AGI):具备与人类相当的认知能力,能够理解、学习并应用知识解决各种问题。目前仍处于理论和研究阶段。

  3. 超级人工智能(ASI):理论上超越人类智能的AI,能在几乎所有领域超越人类最优秀的表现。

AI的核心技术基础

  1. 机器学习(Machine Learning):让计算机从数据中学习并改进的方法,而不需要被明确编程。

  2. 深度学习(Deep Learning):机器学习的一个子集,是神经网络的一种实现方法,使用多层神经网络模拟人脑结构进行学习。深度学习是一种让多层神经元可以进行有效计算的方法,大大提高了神经网络的性能。“深度学习”这个名字,就是比喻多层神经元的自主学习过程。

  3. 自然语言处理(NLP):使计算机能够理解、解释和生成人类语言。

  4. 计算机视觉(Computer Vision):让计算机能够”看到”并理解视觉信息。

2. 机器学习:AI的核心引擎

机器学习的基本概念

机器学习使计算机能够通过经验自动改进,这种”经验”通常来自数据。简单来说,传统编程是人类编写规则让计算机执行,而机器学习是计算机从数据中找出规则。

机器学习的主要类型

  1. 监督学习:使用已标记的数据训练模型,如分类和回归问题。

    • 例如:垃圾邮件分类、房价预测
  2. 无监督学习:从未标记的数据中发现模式和结构。

    • 例如:客户分群、异常检测
  3. 强化学习:通过尝试与环境互动并获得反馈来学习最佳策略。

    • 例如:游戏AI、自动驾驶决策系统

神经网络与深度学习

神经网络是机器学习的一种主要形式。它模仿人脑神经元连接方式的数学模型,由多层人工神经元组成:

  1. 输入层:接收初始数据
  2. 隐藏层:处理信息(深度学习通常有多个隐藏层)
  3. 输出层:产生最终结果

深度学习是神经网络的一种实现方法,使用的神经网络具有多个隐藏层,能够学习更复杂的特征和模式,特别适合处理非结构化数据如图像、声音和文本。

3. 现代AI模型与架构

深度学习的方法

卷积神经网络(CNN)

CNN专门设计用于处理图像数据,通过使用卷积层来检测特征(如边缘、形状等),然后通过池化层减少数据维度,最后通过全连接层进行分类。

CNN广泛应用于:

  • 图像分类与识别
  • 物体检测
  • 医学图像分析

循环神经网络(RNN)与长短期记忆网络(LSTM)

RNN能够处理序列数据,具有”记忆”功能,适合处理文本、语音等时序数据。LSTM是RNN的改进版,解决了长序列训练中的梯度消失问题。

应用领域:

  • 语音识别
  • 机器翻译
  • 文本生成

Transformer架构

Transformer通过自注意力机制处理序列数据,克服了RNN处理长序列的局限性,成为现代语言模型的基础架构。Transformer 不同于以前的方法,不再一个个处理输入的单词,而是一次性处理整个输入,对每个词分配不同的权重。这种方法直接导致了2022年ChatGPT和后来无数生成式AI模型的诞生,是神经网络和深度学习目前的主流方法。
由于基于 Transformer 的模型需要一次性处理整个输入,所以都有“上下文大小”这个指标,指的是一次可以处理的最大输入。比如,GPT-4 Turbo 的上下文是 128k 个 Token,相当于一次性读取超过300页的文本。上下文越大,模型能够考虑的信息就越多,生成的回答也就越相关和连贯,相应地,所需要的算力也就越多。

著名的Transformer模型:

  • BERT(谷歌)
  • GPT系列(OpenAI)
  • Claude系列(Anthropic)

大型语言模型(LLM)

大型语言模型(Large Language Models,简称LLM)如GPT-4、Claude和Llama是基于Transformer架构的巨型AI模型,通过大规模预训练和微调,能够理解和生成人类语言,执行各种复杂的语言任务。本质上是一种基于深度学习的人工智能系统,它能够理解、生成和处理人类语言。让我来简单解释一下:

大语言模型的本质

大语言模型是一种超大规模的神经网络,通常基于Transformer架构,它通过分析海量文本数据来”学习”语言的模式、规则和知识。想象一下,它就像一个阅读了互联网上大部分内容的”超级读者”,通过理解文字之间的关系来预测和生成文本。

工作原理

  1. 预训练:模型首先在海量文本数据上进行训练,学习词汇、语法、事实知识和一些推理能力
  2. 模式识别:它学会识别词语间的关系和上下文意义
  3. 生成文本:当你给它一个提示或问题时,它会预测最可能的后续文本

特点和能力

  • 规模巨大:现代LLM通常有数十亿到数万亿个参数(如GPT-4、Claude等)
  • 通用性强:不需要针对特定任务重新训练就能执行多种语言任务
  • 上下文理解:能够理解长文本中的上下文关系
  • 生成能力:可以创作文章、对话、代码、诗歌等多种内容
  • 少样本学习:只需几个例子就能理解新任务

常见应用

  • 聊天机器人和虚拟助手
  • 内容创作和写作辅助
  • 代码生成和编程辅助
  • 文档总结和信息提取
  • 语言翻译和知识问答

简单来说,大语言模型是一种能够”理解”并”生成”人类语言的AI系统,它通过分析海量文本数据学习语言模式,并能够处理各种语言相关的任务。正是由于它的规模和训练方式,使它具备了理解上下文、生成连贯文本、解决问题和模拟对话等能力。

4. AI模型评估与开发框架

AI模型的评价指标

  1. 准确率(Accuracy)

    • 定义:正确预测的样本数除以总样本数
    • 适用:分类问题,特别是类别平衡的情况
    • 局限:在类别不平衡情况下可能产生误导
  2. 精确率(Precision)与召回率(Recall)

    • 精确率:真正例数除以所有预测为正例的数量
    • 召回率:真正例数除以所有实际正例的数量
    • 应用:搜索引擎结果、医疗诊断等需要平衡查全率和查准率的场景
  3. F1分数

    • 定义:精确率和召回率的调和平均数
    • 特点:同时考虑精确率和召回率,对极端值敏感
    • 公式:F1 = 2 × (精确率 × 召回率) / (精确率 + 召回率)
  4. ROC曲线和AUC值

    • ROC曲线:以假正例率为横轴,真正例率为纵轴的曲线
    • AUC:ROC曲线下的面积,表示模型区分正负样本的能力
    • 数值含义:AUC为0.5表示随机猜测,越接近1表示模型越好
  5. 困惑度(Perplexity)

    • 定义:评估语言模型预测下一个词能力的指标
    • 计算:基于交叉熵,越低表示模型预测越准确
    • 应用:评估GPT、BERT等语言模型的训练效果
  6. BLEU分数

    • 用途:评估机器翻译或文本生成质量
    • 机制:比较生成文本与参考文本的n-gram重合度
    • 范围:0到1,越高表示越接近参考文本
  7. 推理速度与延迟

    • 测量:每秒处理的请求数或响应所需的时间
    • 影响因素:模型大小、硬件配置、批处理大小
    • 重要性:实时应用中的关键指标
  8. 参数量与计算复杂度

    • 参数量:模型包含的可训练参数数量,通常以亿(B)计
    • FLOPs:浮点运算数,衡量计算复杂度
    • 应用:评估模型规模和资源需求
  9. MMLU(大规模多任务语言理解)

    • 测试内容:涵盖57个科目的多选题
    • 价值:评估模型在不同领域的知识和推理能力
    • 应用:评估像GPT-4、Claude等大型语言模型
  10. 毒性与公平性评估

    • 毒性指标:评估模型产生有害内容的倾向
    • 公平性指标:评估模型对不同群体的偏见程度
    • 方法:使用特定的基准测试集和人类评估

流行的深度学习框架

  1. TensorFlow:由Google开发的开源机器学习框架

    • 特点:完整的生态系统,支持生产环境部署,TensorBoard可视化
    • 应用:大规模产品化AI服务,如语音识别、图像分类等
    • 适合:企业级应用和研究团队
  2. PyTorch:由Facebook (Meta) AI研究团队开发

    • 特点:动态计算图,直观的Python接口,更易于调试
    • 应用:学术研究,原型开发,尤其在NLP和计算机视觉领域流行
    • 适合:研究人员和快速迭代开发
  3. 其他重要框架

    • Keras:高级API,可在TensorFlow之上运行,简化开发流程
    • JAX:Google开发的用于高性能数值计算的库
    • Hugging Face Transformers:专注于预训练模型的库,尤其是NLP模型

数据与训练过程

AI模型的训练需要大量高质量数据和计算资源:

  1. 数据收集与处理:收集、清洗、标注和增强数据
  2. 模型设计:选择合适的架构和初始参数
  3. 训练过程:使用优化算法(如梯度下降)调整模型参数
  4. 验证与测试:使用独立数据集评估模型性能
  5. 部署与监控:将模型投入实际应用并持续监控性能

先进训练技术

  1. 迁移学习:利用在一个任务上训练的知识来改进另一个相关任务的性能。

  2. 微调:在预训练模型的基础上,使用特定任务的数据进行进一步训练。

  3. 对比学习:让模型学习区分相似和不同的数据样本。

  4. 强化学习与人类反馈(RLHF):使用人类反馈来指导模型行为,提高模型输出的质量和安全性。

5. AI应用领域与开发工具

alt text

这里有个我有些混淆的点,就是AI应用和AI Agent区别,单独拎出来说下:

AI应用

AI应用是指集成了AI技术的软件程序,通常为了解决特定问题或完成特定任务而设计:

  • 功能范围:通常专注于一个或几个特定功能(如图像识别应用、语音转文本工具)
  • 交互方式:用户提出请求,应用直接响应
  • 自主性:有限,主要按照预设的方式运行
  • 例子:智能照片编辑器、语音助手、自动翻译工具

AI Agent

AI Agent是一种更高级的系统,能够感知环境、自主决策并采取行动实现目标的AI系统:

  • 功能范围:能够执行多步骤任务,调用多种工具
  • 交互方式:可以理解用户意图,规划执行步骤,使用多种工具完成任务
  • 自主性:较高,能根据环境和反馈调整行动(反应能力、社交能力和主动性)
  • 例子:研究助手、客服代理、任务自动化

关键区别

  1. 自主决策能力

    • AI应用:按预定路径执行
    • AI Agent:可以自行规划和决策如何完成任务
  2. 工具使用

    • AI应用:通常使用内置功能
    • AI Agent:可以调用多种外部工具和API
  3. 状态维护

    • AI应用:每次交互可能是独立的
    • AI Agent:维护对话或任务的状态,记住上下文

在技术栈中的位置

alt text

正如之前的图表所示,AI应用处于技术栈的顶层,可以包含AI Agent作为其组成部分,也可以是更简单的AI功能实现。而AI Agent通常建立在大语言模型之上,使用特定的Agent开发平台构建。

本质上,所有的AI Agent都是AI应用,但不是所有的AI应用都是Agent。AI Agent代表了更高级、更自主的AI应用形式。

主流AI Agent和应用开发平台

  • Dify:开源的LLM应用开发平台,提供可视化界面和API接口

    • 功能:知识库管理、对话应用创建、数据标注
    • 适合:快速构建企业级AI助手和应用
  • Coze:微信团队开发的对话式AI平台

    • 功能:多场景机器人开发、无代码开发、多平台集成
    • 适合:社交媒体和聊天平台的AI助手开发
  • LangChain:开源的大型语言模型应用开发框架

    • 功能:链式处理、代理(Agent)、知识库集成、工具调用
    • 适合:开发者构建复杂的AI应用和工作流程
  • LlamaIndex:专注于数据连接和检索的框架

    • 功能:数据摄取、结构化、检索增强生成(RAG)
    • 适合:构建与私有数据交互的应用

自然语言处理应用

  • 聊天机器人与虚拟助手:如ChatGPT、Claude、Siri和小爱同学
  • 机器翻译:如谷歌翻译、DeepL
  • 文本摘要与生成:自动生成报告、文章和创意内容
  • 情感分析:分析社交媒体、评论的情感倾向

计算机视觉应用

  • 人脸识别:安防系统、手机解锁
  • 自动驾驶:感知环境、识别道路和障碍物
  • 医学影像分析:辅助诊断疾病
  • 增强现实(AR):叠加虚拟信息到真实世界

其他重要应用

  • 推荐系统:个性化电商、音乐、视频推荐
  • 金融科技:风险评估、算法交易、欺诈检测
  • 智能制造:预测性维护、质量控制
  • 科学研究:药物发现、蛋白质折叠预测、气候模拟

6. 生成式AI

什么是生成式AI?

生成式AI是能够创建新内容的人工智能系统,包括文本、图像、音频、视频等。它不仅能回答问题,还能创作各种内容。

文本生成

大型语言模型(如GPT-4、Claude等)可以:

  • 撰写文章、故事和诗歌
  • 创建对话和角色扮演
  • 编写代码和技术文档
  • 生成各种类型的商业内容

图像生成

基于扩散模型的AI系统(如DALL-E、Midjourney、Stable Diffusion)能够:

  • 根据文本描述生成高质量图像
  • 修改现有图像
  • 创建艺术作品和商业设计
  • 将草图转换为详细图像

音频与视频生成

  • AI语音合成:生成逼真的人工语音(如ElevenLabs)
  • 音乐生成:创作原创音乐(如Suno、MusicLM)
  • 视频生成:通过文本描述或图像创建视频(如Sora、Runway)

7. AI的伦理与挑战

伦理考量

  1. 偏见与公平性:模型可能继承训练数据中的偏见,导致不公平结果
  2. 隐私问题:AI系统的训练和运行可能涉及敏感个人数据
  3. 透明度与可解释性:深度学习模型通常被视为”黑盒”,难以解释决策过程
  4. 自主性与责任:当AI系统做出决策时,谁应负责任?

技术挑战

  1. 数据质量与规模:高质量训练数据的获取与处理
  2. 计算资源需求:大型模型训练需要大量计算资源
  3. 鲁棒性问题:模型在遇到分布外数据时可能表现不佳
  4. 安全与对抗性攻击:模型可能被精心设计的输入所欺骗

社会经济影响

  1. 就业变化:自动化可能改变就业结构
  2. 数字鸿沟:技术获取不平等可能加剧社会不平等
  3. 教育转型:教育系统需要适应AI时代的技能需求
  4. 信息真实性:生成式AI带来的深度伪造和虚假信息挑战

开源与AI民主化

  1. 开源模型的崛起

    • Llama系列:Meta发布的开源大型语言模型
    • Mistral AI:欧洲初创公司开发的高性能开源模型
    • Stability AI:开源图像生成模型的领先开发者
  2. 本地部署与私有化

    • 轻量级模型:适合在消费级硬件上运行的小型模型
    • 边缘计算:将AI能力部署到终端设备
    • 隐私优先:不依赖云服务的AI解决方案

应用前景

  1. 医疗健康:个性化医疗、疾病预测、药物发现加速

  2. 气候变化:优化能源使用、气候模型、环保解决方案

  3. 教育革新:个性化学习体验、智能辅导系统

  4. 太空探索:自主探测器、数据分析、任务规划

AI与人类协作

未来,最成功的场景很可能是人类与AI协作,而非完全替代:

  • 增强人类能力:AI作为工具,扩展人类的认知和创造能力
  • 互补优势:AI处理数据密集型和重复性任务,人类提供创造力、情感和道德判断
  • 共同演进:人类与AI技术相互适应,形成新的工作和生活方式

8. 如何入门AI学习

基础知识准备

  • 数学基础:线性代数、微积分、概率与统计
  • 编程技能:Python是AI领域最流行的语言
  • 计算机科学基础:算法、数据结构、计算复杂性

推荐资源

9. 结语

人工智能正在以前所未有的速度发展,从改变我们日常使用的应用程序到推动科学研究的前沿。无论你是对AI好奇的初学者,还是寻求深入了解的专业人士,了解AI的基本概念、技术和趋势都至关重要。

AI并非遥不可及的未来技术,而是已经深入我们生活的工具和伙伴。通过积极学习和理性看待,我们可以更好地利用AI的力量,同时规避潜在风险,共同创造一个技术与人文平衡发展的未来。

关键词说明

token

在自然语言处理(NLP)和大模型中,​Token(标记/词元)​ 是文本处理的基本单位,可以理解为模型“读懂”文本的最小片段。它的作用类似于人类语言中的“词语”,但具体定义更灵活,可能是一个单词、子词、标点符号,甚至单个字符。
可以看下chatgpt的Tokenizer:
alt text

B

在大模型(如GPT、LLaMA等)的命名中,”B”通常代表”Billion”(十亿),表示模型的参数量级。例如:

​13B → 130亿参数
​70B → 700亿参数
​175B → 1750亿参数(如GPT-3)

这个单位是衡量模型复杂度和规模的关键指标。参数越多,模型通常能力越强,但也需要更多计算资源和数据训练。这个参数直接影响模型的“知识容量”和学习能力,但并非绝对。模型性能还与数据质量、训练方法、架构设计等因素相关。比如咱们的DeepSeek跟chatgpt不就是一个典型的例子吗

大模型

大模型(Large Language Model, LLM)​是指参数量极大、训练数据量极广的深度学习模型,通常专指自然语言处理(NLP)领域的超大规模模型。它们的“大”不仅体现在参数规模(如千亿级),还体现在训练数据、计算资源和应用能力的突破性提升。