-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathinstall_seatunnel.sh
executable file
·3399 lines (2927 loc) · 120 KB
/
install_seatunnel.sh
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
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
# 确保遇到错误时立即退出
set -e
# 获取脚本执行路径
EXEC_PATH=$(cd "$(dirname "$0")" && pwd)
echo "执行路径: $EXEC_PATH"
# 记录开始时间
START_TIME=$(date +%s)
# 日志文件路径
LOG_DIR="$EXEC_PATH/seatunnel-install-log-${INSTALL_USER:-$(whoami)}"
LOG_FILE="$LOG_DIR/install.log"
# 颜色输出函数
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}
log_warning() {
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
exit 1
}
log_success() {
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}
# 最大重试次数
MAX_RETRIES=3
# SSH超时时间(秒)
SSH_TIMEOUT=10
# 安装包仓库地址映射
declare -A PACKAGE_REPOS=(
["apache"]="https://archive.apache.org/dist/seatunnel"
["aliyun"]="https://mirrors.aliyun.com/apache/seatunnel"
["huaweicloud"]="https://mirrors.huaweicloud.com/apache/seatunnel"
)
# 插件仓库地址映射
declare -A PLUGIN_REPOS=(
["apache"]="https://repo1.maven.org/maven2"
["aliyun"]="https://maven.aliyun.com/repository/public"
["huaweicloud"]="https://repo.huaweicloud.com/repository/maven"
)
# 添加错误处理函数
handle_error() {
local exit_code=$?
local line_number=$1
# 避免递归错误
if [ "${IN_ERROR_HANDLER:-0}" -eq 1 ]; then
echo "致命错误: 在错误处理过程中发生错误"
exit 1
fi
export IN_ERROR_HANDLER=1
log_error "脚本在第 $line_number 行发生错误 (退出码: $exit_code)"
# 清理并退出
cleanup
exit $exit_code
}
# 设置错误处理
trap 'handle_error ${LINENO}' ERR
# 增强的清理函数
cleanup() {
local exit_code=$?
# 避免重复清理
if [ "${IN_CLEANUP:-0}" -eq 1 ]; then
return
fi
export IN_CLEANUP=1
log_info "开始清理..."
# 清理临时文件
cleanup_temp_files
# 如果安装失败,提示用户
if [ $exit_code -ne 0 ]; then
log_warning "安装失败。如果需要重新安装,请手动删除安装目录: $SEATUNNEL_HOME"
log_warning "删除命令: sudo rm -rf $SEATUNNEL_HOME"
fi
exit $exit_code
}
# 设置清理trap
trap cleanup EXIT INT TERM
# 带重试的SSH命令执行
ssh_with_retry() {
local host=$1
local cmd=$2
local retries=0
# 确保INSTALL_USER已设置
if [ -z "$INSTALL_USER" ]; then
log_error "INSTALL_USER未设置"
return 1
fi
while [ $retries -lt $MAX_RETRIES ]; do
if timeout $SSH_TIMEOUT ssh -p $SSH_PORT "${INSTALL_USER}@${host}" "$cmd" 2>/dev/null; then
return 0
fi
retries=$((retries + 1))
log_warning "SSH到 ${INSTALL_USER}@${host} 失败,重试 $retries/$MAX_RETRIES..."
sleep 2
done
log_error "SSH到 ${INSTALL_USER}@${host} 失败,已重试 $MAX_RETRIES 次"
return 1
}
# 带重试的SCP命令执行
scp_with_retry() {
local src=$1
local host=$2
local dest=$3
local retries=0
# 确保INSTALL_USER已设置
if [ -z "$INSTALL_USER" ]; then
log_error "INSTALL_USER未设置"
return 1
fi
# 检查源文件/目录是否存在
if [ ! -e "$src" ]; then
log_error "源文件/目录不存在: $src"
return 1
fi
# 测试SSH连接
if ! timeout $SSH_TIMEOUT ssh -p $SSH_PORT -o ConnectTimeout=5 "${INSTALL_USER}@${host}" "echo >/dev/null" 2>/dev/null; then
log_error "SSH连接失败: ${INSTALL_USER}@${host}"
return 1
fi
while [ $retries -lt $MAX_RETRIES ]; do
log_info "正在分发到 ${host}..."
# 使用-q参数静默输出,仅显示错误
if timeout $SSH_TIMEOUT scp -q -r -P $SSH_PORT "$src" "${INSTALL_USER}@${host}:$dest" 2>/dev/null; then
log_info "成功分发到 ${host}"
return 0
else
local exit_code=$?
# 检查目标主机磁盘空间
local disk_space
disk_space=$(ssh -p $SSH_PORT "${INSTALL_USER}@${host}" "df -h $dest" 2>/dev/null | tail -n1 | awk '{print $4}')
log_warning "分发失败,目标目录可用空间: ${disk_space:-未知}"
fi
retries=$((retries + 1))
if [ $retries -lt $MAX_RETRIES ]; then
log_warning "分发到 ${host} 失败,重试 $retries/$MAX_RETRIES..."
sleep 2
fi
done
log_error "分发到 ${host} 失败,已重试 $MAX_RETRIES 次"
return 1
}
# 检查文件是否存在
check_file() {
if [[ ! -f "$1" ]]; then
log_error "文件不存在: $1"
fi
}
# 检查目录是否存在
check_dir() {
if [[ ! -d "$1" ]]; then
log_error "目录不存在: $1"
fi
}
# 检查端口占用
check_port() {
local host=$1
local port=$2
# 尝试多种方式检查端口
if command -v nc >/dev/null 2>&1; then
# 如果有nc命令,优先使用
if nc -z -w2 "$host" "$port" >/dev/null 2>&1; then
log_error "端口 $port 在 $host 上已被占用"
fi
elif command -v telnet >/dev/null 2>&1; then
# 如果有telnet,使用telnet
if echo quit | timeout 2 telnet "$host" "$port" >/dev/null 2>&1; then
log_error "端口 $port 在 $host 上已被占用"
fi
else
# 最后尝试/dev/tcp
if timeout 2 bash -c "echo >/dev/tcp/$host/$port" >/dev/null 2>&1; then
log_error "端口 $port 在 $host 上已被占用"
fi
fi
}
# 替换文件内容
replace_in_file() {
local search=$1
local replace=$2
local file=$3
sed -i "s|$search|$replace|g" "$file"
}
# 读取配置文件
read_config() {
local config_file="$EXEC_PATH/config.properties"
check_file "$config_file"
# 读取基础配置
SEATUNNEL_VERSION=$(grep "^SEATUNNEL_VERSION=" "$config_file" | cut -d'=' -f2)
BASE_DIR=$(grep "^BASE_DIR=" "$config_file" | cut -d'=' -f2)
SSH_PORT=$(grep "^SSH_PORT=" "$config_file" | cut -d'=' -f2)
DEPLOY_MODE=$(grep "^DEPLOY_MODE=" "$config_file" | cut -d'=' -f2)
# 设置下载目录
DOWNLOAD_DIR="${BASE_DIR}/downloads"
mkdir -p "$DOWNLOAD_DIR"
setup_permissions "$DOWNLOAD_DIR"
# 读取用户配置
INSTALL_USER=$(grep "^INSTALL_USER=" "$config_file" | cut -d'=' -f2)
INSTALL_GROUP=$(grep "^INSTALL_GROUP=" "$config_file" | cut -d'=' -f2)
# 根据部署模式读取节点配置
if [ "$DEPLOY_MODE" = "hybrid" ]; then
# 混合模式:读取所有集群节点
CLUSTER_NODES_STRING=$(grep "^CLUSTER_NODES=" "$config_file" | cut -d'=' -f2)
[[ -z "$CLUSTER_NODES_STRING" ]] && log_error "CLUSTER_NODES 未配置"
IFS=',' read -r -a ALL_NODES <<< "$CLUSTER_NODES_STRING"
else
# 分离模式:读取master和worker节点
MASTER_IPS_STRING=$(grep "^MASTER_IP=" "$config_file" | cut -d'=' -f2)
WORKER_IPS_STRING=$(grep "^WORKER_IPS=" "$config_file" | cut -d'=' -f2)
[[ -z "$MASTER_IPS_STRING" ]] && log_error "MASTER_IP 未配置"
[[ -z "$WORKER_IPS_STRING" ]] && log_error "WORKER_IPS 未配置"
# 转换为数组
IFS=',' read -r -a MASTER_IPS <<< "$MASTER_IPS_STRING"
IFS=',' read -r -a WORKER_IPS <<< "$WORKER_IPS_STRING"
ALL_NODES=("${MASTER_IPS[@]}" "${WORKER_IPS[@]}")
fi
# 设置SEATUNNEL_HOME
SEATUNNEL_HOME="$BASE_DIR/apache-seatunnel-$SEATUNNEL_VERSION"
# 验证必要的配置
[[ -z "$SEATUNNEL_VERSION" ]] && log_error "SEATUNNEL_VERSION 未配置"
[[ -z "$BASE_DIR" ]] && log_error "BASE_DIR 未配置"
# 添加用户配置验证
[[ -z "$INSTALL_USER" ]] && log_error "INSTALL_USER 未配置"
[[ -z "$INSTALL_GROUP" ]] && log_error "INSTALL_GROUP 未配置"
# 验证部署模式
if [[ "$DEPLOY_MODE" != "hybrid" && "$DEPLOY_MODE" != "separated" ]]; then
log_error "DEPLOY_MODE 必须是 hybrid:混合模式 或 separated:分离模式"
fi
# 读取JVM内存配置
HYBRID_HEAP_SIZE=$(grep "^HYBRID_HEAP_SIZE=" "$config_file" | cut -d'=' -f2)
MASTER_HEAP_SIZE=$(grep "^MASTER_HEAP_SIZE=" "$config_file" | cut -d'=' -f2)
WORKER_HEAP_SIZE=$(grep "^WORKER_HEAP_SIZE=" "$config_file" | cut -d'=' -f2)
# 验证内存配置
[[ -z "$HYBRID_HEAP_SIZE" ]] && log_error "HYBRID_HEAP_SIZE 未配置"
[[ -z "$MASTER_HEAP_SIZE" ]] && log_error "MASTER_HEAP_SIZE 未配置"
[[ -z "$WORKER_HEAP_SIZE" ]] && log_error "WORKER_HEAP_SIZE 未配置"
# 读取安装模式配置
INSTALL_MODE=$(grep "^INSTALL_MODE=" "$config_file" | cut -d'=' -f2)
[[ -z "$INSTALL_MODE" ]] && log_error "INSTALL_MODE 未配置"
# 验证安装模式
if [[ "$INSTALL_MODE" != "online" && "$INSTALL_MODE" != "offline" ]]; then
log_error "INSTALL_MODE 必须是 online:在线安装 或 offline:离线安装"
fi
# 读取安装包相关配置
if [[ "$INSTALL_MODE" == "offline" ]]; then
PACKAGE_PATH=$(grep "^PACKAGE_PATH=" "$config_file" | cut -d'=' -f2)
[[ -z "$PACKAGE_PATH" ]] && log_error "离线安装模式下 PACKAGE_PATH 未配置"
# 处理版本号变量
PACKAGE_PATH=$(echo "$PACKAGE_PATH" | sed "s/\${SEATUNNEL_VERSION}/$SEATUNNEL_VERSION/g")
# 转换为绝对路径
if [[ "$PACKAGE_PATH" != /* ]]; then
PACKAGE_PATH="$EXEC_PATH/$PACKAGE_PATH"
fi
else
# 读取安装包仓库配置
PACKAGE_REPO=$(grep "^PACKAGE_REPO=" "$config_file" | cut -d'=' -f2)
PACKAGE_REPO=${PACKAGE_REPO:-aliyun} # 默认使用aliyun源
# 验证仓库配置
if [[ "$PACKAGE_REPO" == "custom" ]]; then
CUSTOM_PACKAGE_URL=$(grep "^CUSTOM_PACKAGE_URL=" "$config_file" | cut -d'=' -f2)
[[ -z "$CUSTOM_PACKAGE_URL" ]] && log_error "使用自定义仓库(PACKAGE_REPO=custom)时必须配置 CUSTOM_PACKAGE_URL"
else
[[ -z "${PACKAGE_REPOS[$PACKAGE_REPO]}" ]] && log_error "不支持的安装包仓库: $PACKAGE_REPO"
fi
fi
# 读取连接器配置
INSTALL_CONNECTORS=$(grep "^INSTALL_CONNECTORS=" "$config_file" | cut -d'=' -f2)
INSTALL_CONNECTORS=${INSTALL_CONNECTORS:-true} # 默认安装
if [ "$INSTALL_CONNECTORS" = "true" ]; then
CONNECTORS=$(grep "^CONNECTORS=" "$config_file" | cut -d'=' -f2)
PLUGIN_REPO=$(grep "^PLUGIN_REPO=" "$config_file" | cut -d'=' -f2)
PLUGIN_REPO=${PLUGIN_REPO:-aliyun} # 默认使用aliyun
fi
# 读取检查点存储配置
CHECKPOINT_STORAGE_TYPE=$(grep "^CHECKPOINT_STORAGE_TYPE=" "$config_file" | cut -d'=' -f2)
CHECKPOINT_NAMESPACE=$(grep "^CHECKPOINT_NAMESPACE=" "$config_file" | cut -d'=' -f2)
# 根据存储类型读取相应配置
case "$CHECKPOINT_STORAGE_TYPE" in
"HDFS")
HDFS_NAMENODE_HOST=$(grep "^HDFS_NAMENODE_HOST=" "$config_file" | cut -d'=' -f2)
HDFS_NAMENODE_PORT=$(grep "^HDFS_NAMENODE_PORT=" "$config_file" | cut -d'=' -f2)
[[ -z "$HDFS_NAMENODE_HOST" ]] && log_error "HDFS模式下必须配置 HDFS_NAMENODE_HOST"
[[ -z "$HDFS_NAMENODE_PORT" ]] && log_error "HDFS模式下必须配置 HDFS_NAMENODE_PORT"
;;
"OSS"|"S3")
STORAGE_ENDPOINT=$(grep "^STORAGE_ENDPOINT=" "$config_file" | cut -d'=' -f2)
STORAGE_ACCESS_KEY=$(grep "^STORAGE_ACCESS_KEY=" "$config_file" | cut -d'=' -f2)
STORAGE_SECRET_KEY=$(grep "^STORAGE_SECRET_KEY=" "$config_file" | cut -d'=' -f2)
STORAGE_BUCKET=$(grep "^STORAGE_BUCKET=" "$config_file" | cut -d'=' -f2)
[[ -z "$STORAGE_ENDPOINT" ]] && log_error "${CHECKPOINT_STORAGE_TYPE}模式下必须配置 STORAGE_ENDPOINT"
[[ -z "$STORAGE_ACCESS_KEY" ]] && log_error "${CHECKPOINT_STORAGE_TYPE}模式下必须配置 STORAGE_ACCESS_KEY"
[[ -z "$STORAGE_SECRET_KEY" ]] && log_error "${CHECKPOINT_STORAGE_TYPE}模式下必须配置 STORAGE_SECRET_KEY"
[[ -z "$STORAGE_BUCKET" ]] && log_error "${CHECKPOINT_STORAGE_TYPE}模式下必须配置 STORAGE_BUCKET"
;;
"LOCAL_FILE")
# 本地文件模式下使用默认路径
CHECKPOINT_NAMESPACE="$SEATUNNEL_HOME/checkpoint"
;;
"")
log_error "必须配置 CHECKPOINT_STORAGE_TYPE"
;;
*)
log_error "不支持的检查点存储类型: $CHECKPOINT_STORAGE_TYPE"
;;
esac
# 读取开机自启动配置
ENABLE_AUTO_START=$(grep "^ENABLE_AUTO_START=" "$config_file" | cut -d'=' -f2)
ENABLE_AUTO_START=${ENABLE_AUTO_START:-true}
# 读取连接器配置
INSTALL_CONNECTORS=$(grep "^INSTALL_CONNECTORS=" "$config_file" | cut -d'=' -f2)
INSTALL_CONNECTORS=${INSTALL_CONNECTORS:-true} # 默认安装
if [ "$INSTALL_CONNECTORS" = "true" ]; then
CONNECTORS=$(grep "^CONNECTORS=" "$config_file" | cut -d'=' -f2)
PLUGIN_REPO=$(grep "^PLUGIN_REPO=" "$config_file" | cut -d'=' -f2)
PLUGIN_REPO=${PLUGIN_REPO:-aliyun} # 默认使用aliyun
fi
# 读取端口配置
if [ "$DEPLOY_MODE" = "hybrid" ]; then
HYBRID_PORT=$(grep "^HYBRID_PORT=" "$config_file" | cut -d'=' -f2)
HYBRID_PORT=${HYBRID_PORT:-5801} # 默认端口5801
else
MASTER_PORT=$(grep "^MASTER_PORT=" "$config_file" | cut -d'=' -f2)
WORKER_PORT=$(grep "^WORKER_PORT=" "$config_file" | cut -d'=' -f2)
MASTER_PORT=${MASTER_PORT:-5801} # 默认端口5801
WORKER_PORT=${WORKER_PORT:-5802} # 默认端口5802
fi
# 读取安全检查配置
CHECK_FIREWALL=$(grep "^CHECK_FIREWALL=" "$config_file" | cut -d'=' -f2)
CHECK_FIREWALL=${CHECK_FIREWALL:-true} # 默认为true
FIREWALL_CHECK_ACTION=$(grep "^FIREWALL_CHECK_ACTION=" "$config_file" | cut -d'=' -f2)
FIREWALL_CHECK_ACTION=${FIREWALL_CHECK_ACTION:-error} # 默认为error
}
# 检查用户配置
check_user() {
# 检查当前执行用户和配置的安装用户是否一致
local current_user=$(whoami)
if [ "$current_user" != "$INSTALL_USER" ]; then
log_error "当前执行用户($current_user)与配置文件中的安装用户($INSTALL_USER)不一致。
请执行以下操作之一:
1. 如果用户不存在,请按照以下命令创建用户:
sudo groupadd $INSTALL_GROUP
sudo useradd -m -g $INSTALL_GROUP $INSTALL_USER
sudo passwd $INSTALL_USER
## 配置sudo权限(推荐使用以下方式):
# 创建sudo权限配置文件(免密配置):
sudo tee /etc/sudoers.d/$INSTALL_USER << EOF
Defaults:$INSTALL_USER !authenticate
$INSTALL_USER ALL=(ALL:ALL) NOPASSWD: ALL
EOF
sudo chmod 440 /etc/sudoers.d/$INSTALL_USER
# 验证sudo免密是否生效:
su - $INSTALL_USER
sudo whoami # 应该显示root且不提示密码
sudo ls /root # 应该能访问root目录且不提示密码
sudo systemctl status # 应该能执行系统管理命令且不提示密码
## 多节点部署需要配置SSH免密登录:
su - $INSTALL_USER # 切换到安装用户
ssh-keygen -t rsa # 生成密钥对,一路回车即可
# 对所有节点执行以下命令(包括本机):
ssh-copy-id $INSTALL_USER@node1
ssh-copy-id $INSTALL_USER@node2
# ... 对所有节点执行
# 验证免密登录:
ssh node1 "whoami" # 应该显示用户名且无需密码
ssh node2 "whoami"
## 注意:
- node1、node2替换为实际的节点主机名或IP
- 首次SSH连接会提示确认指纹,输入yes即可
- 需要输入目标节点上$INSTALL_USER的密码
- 所有节点上都需要先创建好相同的用户和sudo权限
2. 切换到配置的安装用户:
su - $INSTALL_USER
3. 或修改配置文件中的安装用户:
vim config.properties
# 修改 INSTALL_USER=$current_user
注意:
- sudo权限配置说明:
- * -a: 追加模式,不删除已有组
- * -G: 指定附加组
- * sudo/wheel: 系统管理员组(不同系统名称可能不同)
- 建议使用sudoers.d配置sudo权限,这样:
* 不依赖系统sudo组
* 无需输入密码
* 权限更精确可控
* 便于管理和移除
- 如果使用root用户,请将配置文件中的INSTALL_USER也设置为root
- 多节点部署必须配置SSH免密登录
- 所有节点的用户名、用户组、sudo权限配置必须一致"
exit 1
fi
# 检查本地和远程节点的sudo权限
check_sudo_permission() {
local node=$1
local is_remote=$2
# 定义sudo权限验证命令
local verify_commands=(
"sudo whoami | grep -w root" # 确认可以获取root权限
"sudo ls /root &>/dev/null" # 测试访问root目录
"sudo systemctl status &>/dev/null" # 测试systemctl权限
)
# 检查sudo是否需要密码
check_sudo_nopasswd() {
local node=$1
local is_remote=$2
if [ "$is_remote" = true ]; then
# 远程节点检查
if ! ssh_with_retry "$node" "sudo -n true" 2>/dev/null; then
return 1
fi
else
# 本地节点检查
if ! sudo -n true 2>/dev/null; then
return 1
fi
fi
return 0
}
if [ "$is_remote" = true ]; then
# 远程节点检查
local sudo_ok=true
# 先检查sudo免密
if ! check_sudo_nopasswd "$node" true; then
log_error "远程节点 $node 的用户($INSTALL_USER)的sudo权限需要输入密码,请按以下步骤配置sudo免密:
1. 在节点 $node 上执行以下命令:
# 创建或编辑sudo配置文件
sudo tee /etc/sudoers.d/$INSTALL_USER << EOF
$INSTALL_USER ALL=(ALL) NOPASSWD: ALL
EOF
# 设置正确的权限
sudo chmod 440 /etc/sudoers.d/$INSTALL_USER
2. 验证sudo免密:
sudo -n true # 应该不提示输入密码
注意:
- 必须配置sudo免密,否则自动化部署可能失败
- 如果/etc/sudoers.d/$INSTALL_USER已存在,请确保包含NOPASSWD设置
- 某些系统可能需要在/etc/sudoers中启用includedir /etc/sudoers.d"
return 1
fi
for cmd in "${verify_commands[@]}"; do
if ! ssh_with_retry "$node" "$cmd"; then
sudo_ok=false
break
fi
done
if [ "$sudo_ok" = false ]; then
log_error "远程节点 $node 的用户($INSTALL_USER)没有sudo权限,请执行以下步骤:
1. 在节点 $node 上执行以下命令之一:
方式1:将用户添加到sudo组(部分系统可能是wheel组)
sudo usermod -aG sudo $INSTALL_USER # Ubuntu/Debian系统
# 或
sudo usermod -aG wheel $INSTALL_USER # CentOS/RHEL系统
方式2:创建sudo权限配置文件(推荐)
sudo tee /etc/sudoers.d/$INSTALL_USER << EOF
Defaults:$INSTALL_USER !authenticate
$INSTALL_USER ALL=(ALL:ALL) NOPASSWD: ALL
EOF
sudo chmod 440 /etc/sudoers.d/$INSTALL_USER
2. 验证sudo权限(以下命令都应该成功执行):
sudo whoami # 应该输出 root
sudo ls /root # 应该能访问root目录
sudo systemctl status # 应该能执行系统管理命令
注意:
- 建议使用方式2配置sudo权限,这样无需输入密码
- 如果使用方式1,某些系统可能使用wheel组
- 添加权限后需要重新登录生效
- 确保授予的权限足够安装和管理服务"
return 1
fi
else
# 本地节点检查
local sudo_ok=true
# 先检查sudo免密
if ! check_sudo_nopasswd "$node" false; then
log_error "当前用户($INSTALL_USER)的sudo权限需要输入密码,请按以下步骤配置sudo免密:
1. 创建或编辑sudo配置文件:
sudo tee /etc/sudoers.d/$INSTALL_USER << EOF
Defaults:$INSTALL_USER !authenticate
$INSTALL_USER ALL=(ALL:ALL) NOPASSWD: ALL
EOF
2. 设置正确的权限:
sudo chmod 440 /etc/sudoers.d/$INSTALL_USER
3. 验证sudo免密:
sudo -n true # 应该不提示输入密码
注意:
- 必须配置sudo免密,否则自动化部署可能失败
- 如果/etc/sudoers.d/$INSTALL_USER已存在,请确保包含NOPASSWD设置
- 某些系统可能需要在/etc/sudoers中启用includedir /etc/sudoers.d"
return 1
fi
for cmd in "${verify_commands[@]}"; do
if ! eval "$cmd"; then
sudo_ok=false
break
fi
done
if [ "$sudo_ok" = false ]; then
log_error "当前用户($INSTALL_USER)没有sudo权限,请执行以下步骤后重试:
1. 联系系统管理员将当前用户添加到sudo组:
sudo usermod -aG sudo $INSTALL_USER
2. 或者让管理员在/etc/sudoers.d/目录下创建配置文件(推荐):
sudo tee /etc/sudoers.d/$INSTALL_USER << EOF
Defaults:$INSTALL_USER !authenticate
$INSTALL_USER ALL=(ALL:ALL) NOPASSWD: ALL
EOF
sudo chmod 440 /etc/sudoers.d/$INSTALL_USER
3. 验证sudo权限(以下命令都应该成功执行):
sudo whoami # 应该输出 root
sudo ls /root # 应该能访问root目录
sudo systemctl status # 应该能执行系统管理命令
注意:
- 建议使用方式2配置sudo权限,这样无需输入密码
- 确保授予的权限足够安装和管理服务
- 添加权限后需要重新登录生效"
return 1
fi
fi
return 0
}
# 检查所有节点的sudo权限
if [ "$DEPLOY_MODE" = "hybrid" ]; then
for node in "${ALL_NODES[@]}"; do
if [ "$node" != "localhost" ] && [ "$node" != "$(hostname -I | awk '{print $1}')" ]; then
if ! check_sudo_permission "$node" true; then
exit 1
fi
else
if ! check_sudo_permission "$node" false; then
exit 1
fi
fi
done
else
for master in "${MASTER_IPS[@]}"; do
if [ "$master" != "localhost" ] && [ "$master" != "$(hostname -I | awk '{print $1}')" ]; then
if ! check_sudo_permission "$master" true; then
exit 1
fi
else
if ! check_sudo_permission "$master" false; then
exit 1
fi
fi
done
for worker in "${WORKER_IPS[@]}"; do
if [ "$worker" != "localhost" ] && [ "$worker" != "$(hostname -I | awk '{print $1}')" ]; then
if ! check_sudo_permission "$worker" true; then
exit 1
fi
else
if ! check_sudo_permission "$worker" false; then
exit 1
fi
fi
done
fi
# 检查指定用户是否存在
if ! id "$INSTALL_USER" >/dev/null 2>&1; then
log_error "安装用户($INSTALL_USER)不存在,请按以下步骤操作:
1. 创建用户和用户组:
sudo groupadd $INSTALL_GROUP
sudo useradd -m -g $INSTALL_GROUP $INSTALL_USER
2. 设置用户密码:
sudo passwd $INSTALL_USER
3. 配置sudo权限(选择以下任一方式):
方式1:将用户添加到sudo组(需要输入密码)
sudo usermod -aG sudo $INSTALL_USER
方式2:创建sudo权限配置文件(推荐,无需输入密码)
sudo tee /etc/sudoers.d/$INSTALL_USER << EOF
Defaults:$INSTALL_USER !authenticate
$INSTALL_USER ALL=(ALL:ALL) NOPASSWD: ALL
EOF
sudo chmod 440 /etc/sudoers.d/$INSTALL_USER
4. 切换到新用户并验证:
su - $INSTALL_USER
sudo whoami # 应该输出 root
5. 重新运行安装脚本:
./install_seatunnel.sh
注意:
- 建议使用方式2配置sudo权限,这样无需输入密码
- 如果使用方式1,需要确保系统中存在sudo组
- 某些系统中sudo组可能叫wheel组
- 添加sudo权限后需要重新登录才能生效
- 如果是多节点安装,需要在所有节点上执行上述步骤"
exit 1
fi
# 检查用户组是否存在
if ! getent group "$INSTALL_GROUP" >/dev/null; then
log_error "用户组($INSTALL_GROUP)不存在,请执行以下命令创建用户组后重试:
1. 创建用户组:
sudo groupadd $INSTALL_GROUP
2. 将用户添加到用户组:
sudo usermod -aG $INSTALL_GROUP $INSTALL_USER"
exit 1
fi
# 检查当前用户是否有权限访问安装目录
if [ ! -d "$BASE_DIR" ]; then
# 如果目录不存在,检查是否有权限创建
if ! mkdir -p "$BASE_DIR" 2>/dev/null; then
log_error "当前用户($INSTALL_USER)无法创建安装目录($BASE_DIR),请执行以下命令后重试:
sudo mkdir -p $BASE_DIR
sudo chown -R $INSTALL_USER:$INSTALL_GROUP $BASE_DIR"
exit 1
fi
elif [ ! -w "$BASE_DIR" ]; then
# 如果目录存在但没有写权限
log_error "当前用户($INSTALL_USER)没有安装目录($BASE_DIR)的写入权限,请执行以下命令后重试:
sudo chown -R $INSTALL_USER:$INSTALL_GROUP $BASE_DIR"
exit 1
fi
}
# 设置目录权限
setup_permissions() {
local dir=$1
log_info "设置目录权限: $dir"
# 获取用户home目录的绝对路径
local user_home
if command -v getent >/dev/null 2>&1; then
user_home=$(getent passwd "$INSTALL_USER" | cut -d: -f6)
else
user_home=$(eval echo ~"$INSTALL_USER")
fi
# 检查是否是用户的home目录
if [[ "$dir" == "$user_home" ]]; then
log_warning "跳过用户home目录的权限设置: $dir"
return 0
fi
# 检查目录是否存在
if [ ! -d "$dir" ]; then
log_error "目录不存在: $dir"
return 1
fi
# 设置目录权限
sudo chown -R "$INSTALL_USER:$INSTALL_GROUP" "$dir"
sudo chmod -R 755 "$dir"
}
# 指定用户执行命令
run_as_user() {
sudo -u "$INSTALL_USER" bash -c "$1"
}
# 创建目录
create_directory() {
local dir=$1
sudo mkdir -p "$dir"
}
# 临时文件管理
create_temp_file() {
local temp_dir="$LOG_DIR/temp"
local temp_file
# 创建临时目录,如果不存在)
if [ ! -d "$temp_dir" ]; then
mkdir -p "$temp_dir" || log_error "无法创建临时目录: $temp_dir"
chmod 700 "$temp_dir"
[ -n "$INSTALL_USER" ] && [ -n "$INSTALL_GROUP" ] && chown "$INSTALL_USER:$INSTALL_GROUP" "$temp_dir"
fi
# 创建临时文件
temp_file=$(mktemp "$temp_dir/temp.XXXXXX") || log_error "无法创建临时文件"
chmod 600 "$temp_file"
[ -n "$INSTALL_USER" ] && [ -n "$INSTALL_GROUP" ] && chown "$INSTALL_USER:$INSTALL_GROUP" "$temp_file"
# 添加到临时文件列表
TEMP_FILES+=("$temp_file")
echo "$temp_file"
}
# 清理临时文件
cleanup_temp_files() {
log_info "进入cleanup_temp_files函数..."
# 检查数组是否已定义
if [ -z "${TEMP_FILES+x}" ]; then
log_info "TEMP_FILES数组未定义,退出清理"
return 0
fi
log_info "当前TEMP_FILES数组大小: ${#TEMP_FILES[@]}"
local temp_dir="$LOG_DIR/temp"
# 检查是否有临时文件需要清理
if [ ${#TEMP_FILES[@]} -eq 0 ]; then
log_info "没有临时文件需要清理"
return 0
fi
log_info "清理临时文件..."
# 清理所有已记录的临时文件
for temp_file in "${TEMP_FILES[@]}"; do
if [ -f "$temp_file" ]; then
rm -f "$temp_file"
log_info "已删除临时文件: $temp_file"
fi
done
# 清理临时目录(如果为空)
if [ -d "$temp_dir" ] && [ -z "$(ls -A "$temp_dir")" ]; then
rm -rf "$temp_dir"
log_info "已删除空的临时目录: $temp_dir"
fi
}
# 在文件开头添加参数处理
# 默认使用自动选择模式
FORCE_SED=false
ONLY_INSTALL_PLUGINS=false
NO_PLUGINS=false
# 解析命令行参数
while [[ "$#" -gt 0 ]]; do
case $1 in
--force-sed) FORCE_SED=true ;;
--install-plugins) ONLY_INSTALL_PLUGINS=true ;;
--no-plugins) NO_PLUGINS=true ;;
*) log_error "未知参数: $1" ;;
esac
shift
done
# 修改check_command函数
check_command() {
local cmd=$1
if [ "$cmd" = "awk" ]; then
# 检查awk是否可用
command -v "$cmd" >/dev/null 2>&1
else
# 其他命令强制使用指定的模式
if [ "$FORCE_SED" = true ]; then
return 1
fi
command -v "$cmd" >/dev/null 2>&1
fi
}
# 更新replace_yaml_section函数
replace_yaml_section() {
local file=$1 # yaml文件路径
local section=$2 # 要替换的部分的开始标记(如 member-list:, plugin-config:)
local indent=$3 # 新内容的缩进空格数
local content=$4 # 新的内容
local temp_file
log_info "修改配置文件: $file, 替换部分: $section"
# 创建临时文件
temp_file=$(create_temp_file)
if [ "$FORCE_SED" = true ]; then
log_info "强制使用sed处理文件..."
# 获取section的缩进和完整行
local section_line
section_line=$(grep "$section" "$file")
local section_indent
section_indent=$(echo "$section_line" | sed 's/[^[:space:]].*//' | wc -c)
# 预处理内容,添加缩进
local indented_content
indented_content=$(echo "$content" | sed "s/^/$(printf '%*s' "$indent" '')/")
# 创建临时文件存储新内容
local content_file=$(create_temp_file)
echo "$indented_content" > "$content_file"
# 第一步:找section的起始行号
local start_line
start_line=$(grep -n "$section" "$file" | cut -d: -f1)
# 第二步:找到section的结束行号
local end_line
end_line=$(tail -n +$((start_line + 1)) "$file" | grep -n "^[[:space:]]\{0,$section_indent\}[^[:space:]]" | head -1 | cut -d: -f1)
end_line=$((start_line + end_line))
# 第三步:组合新文件
# 1. 复制section之前的内容
sed -n "1,${start_line}p" "$file" > "$temp_file"
# 2. 添加新内容
cat "$content_file" >> "$temp_file"
# 3. 复制section之后的内容
sed -n "$((end_line)),\$p" "$file" >> "$temp_file"
# 清理临时内容文件
rm -f "$content_file"
else
log_info "使用awk处理文件..."
# awk版本的实现
awk -v section="$section" -v base_indent="$indent" -v content="$content" '
# 计算行的缩进空格数
function get_indent(line) {
match(line, /^[[:space:]]*/)
return RLENGTH
}
# 为每行添加缩进的函数
function add_indent(str, indent, lines, i, result) {
split(str, lines, "\n")
result = ""
for (i = 1; i <= length(lines); i++) {
if (lines[i] != "") {
result = result sprintf("%*s%s\n", indent, "", lines[i])
}
}
return result
}
BEGIN {
in_section = 0
section_indent = -1
# 预处理content,添加缩进
indented_content = add_indent(content, base_indent)
}
{
current_indent = get_indent($0)
if ($0 ~ section) {
# 找到section,记录其缩进级别
section_indent = current_indent
print $0
printf "%s", indented_content
in_section = 1
next
}
if (in_section) {
# 如果当前行的缩进小于等section的缩进,说明section结束
if (current_indent <= section_indent && $0 !~ "^[[:space:]]*$") {
in_section = 0
section_indent = -1
print $0
}
} else {
print $0
}
}' "$file" > "$temp_file"
fi
# 获取文件权限,使用ls -l作为备选方案
local file_perms
if stat --version 2>/dev/null | grep -q 'GNU coreutils'; then
# GNU stat
file_perms=$(stat -c %a "$file")
else
# 其他系统,使用ls -l解析
file_perms=$(ls -l "$file" | cut -d ' ' -f1 | tr 'rwx-' '7500' | sed 's/^.\(.*\)/\1/' | tr -d '\n')
fi
# 复制新内容到原文件
cp "$temp_file" "$file"
# 恢复文件权限
chmod "$file_perms" "$file"
}
# 修改hazelcast配置文件
modify_hazelcast_config() {
local config_file=$1
local content
# 备份文件
cp "$config_file" "${config_file}.bak"
case "$config_file" in
*"hazelcast.yaml")
log_info "修改 hazelcast.yaml (集群通信配置)..."
if [ "$DEPLOY_MODE" = "hybrid" ]; then