#!/bin/bash # =================================================== # 脚本功能: # 1. 配置 systemd-networkd 网络桥接与 VLAN # 2. 配置 k3s agent 节点并启动服务 # 3. 可通过配置文件 bushu.conf 管理所有参数 # =================================================== set -euo pipefail # ----------------------- # 检查 root 权限 # ----------------------- if [[ $EUID -ne 0 ]]; then echo "请使用 root 用户运行此脚本" exit 1 fi # ----------------------- # 引入配置文件 # ----------------------- CONFIG_FILE="./bushu.conf" if [[ -f "$CONFIG_FILE" ]]; then source "$CONFIG_FILE" else echo "配置文件 $CONFIG_FILE 不存在,请先创建!" exit 1 fi # ----------------------- # 网络配置部分 # ----------------------- echo "=== 开始 cloud 网桥配置 ===" # 备份现有网络配置 mkdir -p /etc/systemd/network/backup cp /etc/systemd/network/*.network /etc/systemd/network/backup/ 2>/dev/null || true cp /etc/systemd/network/*.netdev /etc/systemd/network/backup/ 2>/dev/null || true echo "网络配置备份已完成" # 创建 cloud bridge cat > /etc/systemd/network/00-cloud.netdev << EOF [NetDev] Name=cloud Kind=bridge MACAddress=$CLOUD_MAC [Bridge] VLANFiltering=yes EOF # 配置 cloud 网络 cat > /etc/systemd/network/00-cloud.network << EOF [Match] Name=cloud [Network] Address=$CLOUD_IP Address=$CLOUD_SECOND_IP Gateway=$CLOUD_GATEWAY DNS=$CLOUD_DNS ConfigureWithoutCarrier=yes [Link] RequiredForOnline=no-carrier EOF cat > /etc/systemd/network/00-$CLOUD_IFACE.network << EOF [Match] Name=$CLOUD_IFACE [Network] Bridge=cloud DHCP=no # VLAN=cloud-monitor [BridgeVLAN] VLAN=1-4094 [BridgeVLAN] VLAN=1 PVID=1 EgressUntagged=1 EOF echo "cloud 网桥配置已完成" # ----------------------- echo "=== 开始 yewu 网桥配置 ===" # 创建 yewu bridge cat > /etc/systemd/network/00-yewu.netdev << EOF [NetDev] Name=yewu Kind=bridge MACAddress=$YEWU_MAC [Bridge] VLANFiltering=yes EOF # 配置 yewu 网络 cat > /etc/systemd/network/00-yewu.network << EOF [Match] Name=yewu [Network] Address=$YEWU_IP Gateway=$YEWU_GATEWAY DNS=$YEWU_DNS ConfigureWithoutCarrier=yes [Link] RequiredForOnline=no-carrier EOF # 用户网卡接入 yewu bridge cat > /etc/systemd/network/00-$YEWU_IFACE.network << EOF [Match] Name=$YEWU_IFACE [Network] Bridge=yewu DHCP=no [BridgeVLAN] VLAN=1-4094 [BridgeVLAN] VLAN=1 PVID=1 EgressUntagged=1 EOF echo "yewu 网桥配置已完成" # 使用 networkctl reload 应用配置 echo "正在重新加载网络配置..." networkctl reload echo "网络配置已生效" # =================================================== # 新增部分:monitor VLAN 子接口配置 # =================================================== echo "=== 开始配置 monitor VLAN 子接口 ===" : "${MONITOR_VLAN_ID:?请在 bushu.conf 中配置 MONITOR_VLAN_ID}" : "${MONITOR_PHY_IFACE:?请在 bushu.conf 中配置 MONITOR_PHY_IFACE}" : "${MONITOR_ALIAS:=lsa}" MONITOR_VLAN_IFACE="${MONITOR_PHY_IFACE}.${MONITOR_VLAN_ID}" echo "monitor 物理网卡: ${MONITOR_PHY_IFACE}" echo "monitor VLAN ID: ${MONITOR_VLAN_ID}" echo "monitor VLAN 子接口: ${MONITOR_VLAN_IFACE}" echo "monitor VLAN alias: ${MONITOR_ALIAS}" modprobe 8021q || true if ip link show "${MONITOR_VLAN_IFACE}" >/dev/null 2>&1; then echo "VLAN 子接口 ${MONITOR_VLAN_IFACE} 已存在,跳过创建" else echo "正在创建 VLAN 子接口 ${MONITOR_VLAN_IFACE} ..." ip link add link "${MONITOR_PHY_IFACE}" name "${MONITOR_VLAN_IFACE}" type vlan id "${MONITOR_VLAN_ID}" fi ip link set dev "${MONITOR_VLAN_IFACE}" alias "${MONITOR_ALIAS}" ip link set dev "${MONITOR_VLAN_IFACE}" up echo "monitor VLAN 子接口 ${MONITOR_VLAN_IFACE} 当前已生效" # ----------------------- # 新增部分:写入 rc.local,保证重启后自动创建 # ----------------------- echo "=== 写入 rc.local,配置 monitor VLAN 开机自动创建 ===" if [[ ! -f /etc/rc.local ]]; then cat > /etc/rc.local << 'EOF' #!/bin/bash exit 0 EOF chmod +x /etc/rc.local fi sed -i '/# BEGIN MONITOR VLAN/,/# END MONITOR VLAN/d' /etc/rc.local sed -i "/^exit 0/i\\ # BEGIN MONITOR VLAN\\ modprobe 8021q || true\\ while ! ip link show ${MONITOR_PHY_IFACE} >/dev/null 2>&1; do\\ sleep 1\\ done\\ ip link show ${MONITOR_VLAN_IFACE} >/dev/null 2>&1 || ip link add link ${MONITOR_PHY_IFACE} name ${MONITOR_VLAN_IFACE} type vlan id ${MONITOR_VLAN_ID}\\ ip link set dev ${MONITOR_VLAN_IFACE} alias ${MONITOR_ALIAS}\\ ip link set dev ${MONITOR_VLAN_IFACE} up\\ # END MONITOR VLAN\\ " /etc/rc.local chmod +x /etc/rc.local systemctl enable rc-local 2>/dev/null || true systemctl enable rc-local.service 2>/dev/null || true echo "rc.local 已写入 monitor VLAN 配置" # ----------------------- # k3s 配置部分 # ----------------------- echo "=== k3s 安装配置 ===" echo "=== 下载 K3s 二进制到 /usr/bin/ ===" curl -L -o /usr/bin/k3s "https://file.piicloud.com/f/acfd6bc97c7e42de8647/?dl=1" chmod +x /usr/bin/k3s ls -lh /usr/bin/k3s echo "=== 检查/创建 K3s agent 镜像目录 ===" if [ ! -d "/var/lib/rancher/agent/images" ]; then mkdir -p /var/lib/rancher/agent/images echo "目录已创建" else echo "目录已存在,跳过创建" fi # 下载镜像 [ -f "/var/lib/rancher/agent/images/base.tar" ] || \ curl -L -o /var/lib/rancher/agent/images/base.tar "https://file.piicloud.com/f/40e4c1cc37564dd8900d/?dl=1" [ -f "/var/lib/rancher/agent/images/expend.tar" ] || \ curl -L -o /var/lib/rancher/agent/images/expend.tar "https://file.piicloud.com/f/c6cf4a7811d344ca9bf6/?dl=1" chown root:root /var/lib/rancher/agent/images/*.tar chmod 644 /var/lib/rancher/agent/images/*.tar ls -lh /var/lib/rancher/agent/images/ # 创建 systemd 服务文件 cat > /etc/systemd/system/k3s.service << EOF [Unit] Description=Lightweight Kubernetes Documentation=https://k3s.io Wants=network-online.target After=network-online.target [Install] WantedBy=multi-user.target [Service] Type=notify EnvironmentFile=-/etc/default/%N EnvironmentFile=-/etc/sysconfig/%N EnvironmentFile=-/etc/systemd/system/k3s.service.env KillMode=process Delegate=yes User=root LimitNOFILE=1048576 LimitNPROC=infinity LimitCORE=infinity TasksMax=infinity TimeoutStartSec=0 Restart=always RestartSec=5s ExecStartPre=-/sbin/modprobe br_netfilter ExecStartPre=-/sbin/modprobe overlay ExecStart=/usr/bin/k3s server EOF # 创建 override 配置 mkdir -p /etc/systemd/system/k3s.service.d cat > /etc/systemd/system/k3s.service.d/override.conf << EOF [Service] ExecStart= ExecStart=/usr/bin/k3s agent \ --server https://$MASTER_IP:6443 \ --token $K3S_TOKEN \ --node-name $NODE_NAME \ --node-ip $NODE_IP \ --node-external-ip $NODE_EXT_IP \ --kubelet-arg max-pods=120 EOF # 创建 registries.yaml mkdir -p /etc/rancher/k3s/ cat > /etc/rancher/k3s/registries.yaml << EOF configs: harbor.tdology.com: auth: password: Y_TSq6qDJBZC}538 username: admin mirrors: docker.io: endpoint: - http://localhost:30500 - https://harbor.tdology.com EOF # ----------------------- # NVMe 分区与格式化 # ----------------------- # 检查磁盘是否存在 if [[ ! -b /dev/nvme0n1 ]]; then echo "错误:/dev/nvme0n1 不存在" exit 1 fi # 如果分区不存在,则创建 if [[ ! -b /dev/nvme0n1p1 ]]; then echo "/dev/nvme0n1p1 不存在,开始创建分区..." if lsblk -nr /dev/nvme0n1 | awk '{print $6}' | grep -q '^part$'; then echo "检测到已有分区,但没有 nvme0n1p1,请手动检查" exit 1 fi parted -s /dev/nvme0n1 mklabel gpt parted -s /dev/nvme0n1 mkpart primary 0% 100% partprobe /dev/nvme0n1 sleep 2 if [[ ! -b /dev/nvme0n1p1 ]]; then echo "错误:分区创建失败" exit 1 fi echo "分区创建成功:/dev/nvme0n1p1" else echo "/dev/nvme0n1p1 已存在,跳过创建分区" fi # 如果没有文件系统,则格式化 if ! blkid /dev/nvme0n1p1 >/dev/null 2>&1; then echo "/dev/nvme0n1p1 没有文件系统,开始格式化为 ext4..." mkfs.ext4 -F /dev/nvme0n1p1 else echo "/dev/nvme0n1p1 已有文件系统,跳过格式化" fi # ----------------------- # NVMe 挂载与绑定挂载 # ----------------------- # 确保主挂载点存在 mkdir -p /mnt/nvme # 挂载 /mnt/nvme if ! mountpoint -q /mnt/nvme; then echo "挂载 /dev/nvme0n1p1 到 /mnt/nvme ..." mount /dev/nvme0n1p1 /mnt/nvme else echo "/mnt/nvme 已经挂载,跳过挂载" fi # ✅ 确保源目录存在(关键修复点) mkdir -p /mnt/nvme/rancher mkdir -p /mnt/nvme/longhorn # 确保绑定挂载目标目录存在 mkdir -p /var/lib/rancher mkdir -p /var/lib/longhorn # 挂载 /var/lib/rancher if ! mountpoint -q /var/lib/rancher; then echo "绑定挂载 /mnt/nvme/rancher 到 /var/lib/rancher ..." mount --bind /mnt/nvme/rancher /var/lib/rancher else echo "/var/lib/rancher 已经挂载,跳过绑定挂载" fi # 挂载 /var/lib/longhorn if ! mountpoint -q /var/lib/longhorn; then echo "绑定挂载 /mnt/nvme/longhorn 到 /var/lib/longhorn ..." mount --bind /mnt/nvme/longhorn /var/lib/longhorn else echo "/var/lib/longhorn 已经挂载,跳过绑定挂载" fi # ----------------------- # 更新 /etc/fstab # ----------------------- NVME_UUID=$(blkid -s UUID -o value /dev/nvme0n1p1) NVME_FSTYPE=$(blkid -s TYPE -o value /dev/nvme0n1p1) if [[ -z "$NVME_UUID" || -z "$NVME_FSTYPE" ]]; then echo "错误:无法获取 UUID 或文件系统类型,停止写入 fstab" exit 1 fi # 主挂载(安全) grep -qE "[[:space:]]/mnt/nvme[[:space:]]" /etc/fstab || \ echo "UUID=${NVME_UUID} /mnt/nvme ${NVME_FSTYPE} defaults,nofail,x-systemd.device-timeout=5 0 2" >> /etc/fstab # bind mount(安全) grep -qE "[[:space:]]/var/lib/rancher[[:space:]]" /etc/fstab || \ echo "/mnt/nvme/rancher /var/lib/rancher none bind,nofail,x-systemd.requires-mounts-for=/mnt/nvme 0 0" >> /etc/fstab grep -qE "[[:space:]]/var/lib/longhorn[[:space:]]" /etc/fstab || \ echo "/mnt/nvme/longhorn /var/lib/longhorn none bind,nofail,x-systemd.requires-mounts-for=/mnt/nvme 0 0" >> /etc/fstab # ----------------------- # 验证 # ----------------------- echo "测试 fstab..." mount -a echo "显示挂载状态" findmnt /mnt/nvme findmnt /var/lib/rancher findmnt /var/lib/longhorn # ----------------------- # 时间同步 # ----------------------- echo "=== 时间同步配置 ===" : "${ENABLE_CHRONY:=true}" : "${ENABLE_GPTP:=false}" if [[ "${ENABLE_CHRONY}" == "true" && "${ENABLE_GPTP}" == "true" ]]; then echo "错误:ENABLE_CHRONY 和 ENABLE_GPTP 不能同时为 true" exit 1 fi echo "=== 从 RTC 恢复系统时间 ===" hwclock -s 2>/dev/null || hwclock --hctosys 2>/dev/null || true date if [[ "${ENABLE_CHRONY}" == "true" || "${ENABLE_GPTP}" == "true" ]]; then echo "安装时间同步相关组件..." apt update apt install -y chrony linuxptp fi if [[ "${ENABLE_CHRONY}" == "true" ]]; then echo "=== 启用 chrony 时间同步 ===" systemctl disable --now gptp.service 2>/dev/null || true systemctl disable --now phc2sys.service 2>/dev/null || true pkill ptp4l 2>/dev/null || true pkill phc2sys 2>/dev/null || true cp /etc/chrony/chrony.conf /etc/chrony/chrony.conf.bak 2>/dev/null || true cat > /etc/chrony/chrony.conf << EOF # 由 bushu.sh 自动生成 EOF for server in "${CHRONY_SERVERS[@]}"; do echo "server ${server} iburst" >> /etc/chrony/chrony.conf done cat >> /etc/chrony/chrony.conf << EOF driftfile /var/lib/chrony/chrony.drift makestep 1.0 -1 rtcsync logdir /var/log/chrony EOF systemctl enable --now chrony chronyc -a makestep || true sleep 3 chronyc sources -v || true chronyc tracking || true elif [[ "${ENABLE_GPTP}" == "true" ]]; then echo "=== 启用 gPTP 时间同步 ===" systemctl disable --now chrony 2>/dev/null || true : "${GPTP_IFACE:?请在 bushu.conf 中配置 GPTP_IFACE}" mkdir -p /usr/local/bin mkdir -p /etc/linuxptp cat > /etc/linuxptp/gptp.cfg << EOF # 由 bushu.sh 自动生成 # gPTP / IEEE 802.1AS 配置 [global] gmCapable 0 priority1 248 priority2 248 logAnnounceInterval 0 logSyncInterval -3 syncReceiptTimeout 3 neighborPropDelayThresh 800 min_neighbor_prop_delay -20000000 assume_two_step 1 path_trace_enabled 1 follow_up_info 1 transportSpecific 0x1 ptp_dst_mac 01:80:C2:00:00:0E network_transport L2 delay_mechanism P2P time_stamping hardware EOF cat > /usr/local/bin/start-gptp.sh << 'EOF' #!/bin/bash set -euo pipefail GPTP_IFACE="${1:?Usage: start-gptp.sh }" while ! ip link show "${GPTP_IFACE}" >/dev/null 2>&1; do echo "等待网卡 ${GPTP_IFACE} 出现..." sleep 1 done ip link set dev "${GPTP_IFACE}" up if ethtool -T "${GPTP_IFACE}" 2>/dev/null | grep -q "hardware-transmit"; then TS_MODE="hardware" else echo "警告:${GPTP_IFACE} 不支持硬件时间戳,切换为 software timestamp" TS_MODE="software" sed -i 's/^time_stamping.*/time_stamping software/' /etc/linuxptp/gptp.cfg fi echo "gPTP timestamp mode: ${TS_MODE}" exec /usr/sbin/ptp4l \ -i "${GPTP_IFACE}" \ -f /etc/linuxptp/gptp.cfg \ -m EOF chmod +x /usr/local/bin/start-gptp.sh cat > /etc/systemd/system/gptp.service << EOF [Unit] Description=gPTP IEEE 802.1AS Service After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/local/bin/start-gptp.sh ${GPTP_IFACE} Restart=always RestartSec=5 User=root [Install] WantedBy=multi-user.target EOF cat > /etc/systemd/system/phc2sys.service << EOF [Unit] Description=Synchronize system clock from gPTP PHC After=gptp.service Requires=gptp.service [Service] Type=simple ExecStartPre=/bin/sleep 10 ExecStart=/usr/sbin/phc2sys -s ${GPTP_IFACE} -c CLOCK_REALTIME -O 0 -m Restart=always RestartSec=5 User=root [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now gptp.service systemctl enable --now phc2sys.service else echo "=== 未启用时间同步 ===" systemctl disable --now chrony 2>/dev/null || true systemctl disable --now gptp.service 2>/dev/null || true systemctl disable --now phc2sys.service 2>/dev/null || true fi echo "=== 检查系统时间 ===" date timedatectl || true YEAR=$(date +%Y) if (( YEAR < 2024 )); then echo "错误:系统时间异常,当前时间:$(date)" echo "拒绝继续执行,避免 apt/curl/k3s TLS 失败" exit 1 fi # ----------------------- # 域名解析 # ----------------------- echo "=== 配置 /etc/hosts ===" for entry in "${HOSTS_ENTRIES[@]}"; do grep -qF "$entry" /etc/hosts || echo "$entry" >> /etc/hosts done # ----------------------- # 启动 k3s 服务 # ----------------------- systemctl daemon-reload systemctl enable k3s systemctl start k3s echo "k3s 配置完成"