分享-OpenWrt 双 wan 出口实现负载均衡和带宽叠加（每个链接单独出口）
情况说明 OpenWrt 安装 网络链接 有线网络 无线网络 mwan3(Multi WAN load balancing/failover) 配置 Interface Member Policy Rule 总结 单出口 双出口 情况说明 当你的网络运营商（公司IT，楼宇等）针对单独一个 IP 或设备限制了流量，比如 5MB 带宽，你可以通过以下方法实现同时使用两个外网出口（有线网络+Wi-Fi），这样总的带宽就有 5MB+5MB=10MB。但是，单个链接，还是只能有 5MB 限制。 OpenWrt 安装 opkg update opkg install mwan3 luci-app-mwan3 网络链接 有线网络 Gateway Metri: 10 Network > Interfaces WAN > Edit Advanced Settings Use default gateway: enabled Use gateway metric: 10 Save & Apply 无线网络 Gateway Metri: 20 Network > Interfaces WWAN > Edit Advanced Settings Use default gateway: enabled Use gateway metric: 20 Save & Apply mwan3(Multi WAN load balancing/failover) 配置 通过 UI 配置比较简单一些，进入 OpenWrt UI，Network -> MultiWAN Manager Interface 创建两个 interface，跟你的 Network Interfaces 里名称一样，类似如下： Member 新建两个 Member，保持 Metric 相等： Policy 把两个 Member 选中 Rule 保留两个，https 一定要保留，因为不能随便切换线路。 总结 这样的话，通过两个出口，两个连接，就可以实现带宽叠加，达到更高的下载速度。 单出口 双出口
在 kubeflow 自定义 namespace 创建的 inferenceService 无法访问，出现 403 错误，RBAC: access denied
奇怪的问题 根据 inferenceService pod 来指定策略 奇怪的问题 不知道为什么，只有增加这一条规则，才能访问。试了很多方法，只有这个奏效。 apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: allow-all namespace: yiqisoft spec: rules: - {} 根据 inferenceService pod 来指定策略 apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: allow-ovms namespace: yiqisoft spec: selector: matchLabels: serving.kserve.io/inferenceservice: ovms rules: - {} --- apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: allow-sklearn-iris namespace: yiqisoft spec: selector: matchLabels: serving.kserve.io/inferenceservice: sklearn-iris rules: - {} ``
Linux运维笔记：修复 ThinLinc 会话数据库损坏问题 ThinLinc 在多用户 Linux 工作站中的重要性 问题发现：会话数据库损坏 解决： 1. 备份数据 2.删除损坏的数据库文件 3.重启服务 4.重新检查 5.验证修复 总结 Linux运维笔记：修复 ThinLinc 会话数据库损坏问题 ThinLinc 是一个优秀的远程桌面解决方案，它让用户能够方便地访问工作站，运行复杂计算任务或进行数据分析。然而，最近我们遇到一个问题：ThinLinc 客户端无法成功连接至工作站。排查发现是 ThinLinc 的核心服务 vsmserver.service 启动失败，导致用户无法登录。经过进一步排查，发现问题源于会话数据库损坏，并成功解决了它。 ThinLinc 在多用户 Linux 工作站中的重要性 实验室的工作站通常需要配置高性能硬件，运行专业软件，但用户分布在不同地点。ThinLinc 通过虚拟会话管理（VSM）提供流畅的远程访问体验，让用户无需物理接触工作站即可完成工作。VSM 服务器（vsmserver.service）是 ThinLinc 的核心组件，负责协调用户会话。如果它出现故障，整个远程访问系统就会瘫痪。 最近，我们注意到用户反馈无法连接到工作站。运行 systemctl list-units --type=service 检查后，发现 vsmserver.service 状态为 loaded failed failed，详细情况如下所示： root@ccccs-Z790-UD-AX:/home/ccccs# systemctl list-units --type=service ... vsmagent.service loaded active running ThinLinc VSM agent vsmserver.service loaded failed failed ThinLinc VSM server whoopsie.service loaded active running crash report submission daemon ... 此时通过 systemctl 命令尝试重启该服务，发现仍不能成功启动... 问题发现：会话数据库损坏 首先查看了系统日志： sudo journalctl -u vsmserver.service 日志显示服务在启动后立即退出，状态码为 status=1/FAILURE，但没有具体错误详情。接着转向 ThinLinc 的专用日志文件： sudo cat /var/log/vsmserver.log 2025-04-14 12:26:52 INFO vsmserver: VSM Server version 4.15.0 build 3358 started 2025-04-14 12:26:52 ERROR vsmserver: Error loading session database: Error unpacking /var/lib/vsm/sessions (invalid load key, '\x00'.) 2025-04-14 12:26:52 ERROR vsmserver: Session database needs manual recovery. 2025-04-14 12:26:52 ERROR vsmserver: Exiting 发现是 /var/lib/vsm/sessions 文件损坏，导致 VSM 服务器无法加载会话数据库并退出。 这个错误提示 invalid load key, '\x00' 通常表示文件内容不完整或被意外修改，可能是磁盘问题、异常关机或软件 bug 导致。(工作站会偶尔发生断电或人为重启) 解决： 1. 备份数据 sudo cp -r /var/lib/vsm/sessions /var/lib/vsm/sessions.bak 2.删除损坏的数据库文件 ThinLinc 文档指出，损坏的 /var/lib/vsm/sessions 文件可以安全删除，服务器会在下次启动时自动重新创建。我们执行： sudo rm /var/lib/vsm/sessions 注意：删除会话数据库会清除当前会话记录，可能导致用户丢失未保存的会话状态。在我们的场景中，实验室用户主要运行短期任务，影响较小，但也要确保此时每人连接。 3.重启服务 sudo systemctl start vsmserver.service 4.重新检查 systemctl list-units --type=service # 启动成功 vsmserver.service loaded active running ThinLinc VSM server 5.验证修复 让用户通过他们本地 ThinLinc 客户端登录工作站，确认远程桌面功能完全恢复。 总结 定期备份：虽然 ThinLinc 的会话数据库可以重建，但备份 /var/lib/vsm/ 文件夹能降低风险。可每周自动备份。 监控磁盘健康：数据库损坏可能与磁盘错误有关。运行 df -h /var/lib/vsm 和 fsck 检查文件系统是个好习惯。 检查日志：journalctl 和 /var/log/vsmserver.log 是诊断 ThinLinc 问题的关键。熟悉这些工具能大大加快故障排除。 远程桌面系统的稳定性对用户体验至关重要。通过系统化的诊断和谨慎的操作，大多数问题都可以迎刃而解。
在 ubuntu22.04 中 安装 EdgeX 并运行 device-sdk-c sample
在 ubuntu22.04 中 安装 EdgeX 并运行 device-sdk-c sample 1.克隆源码-EdgeX 2.安装工具 3.编译运行-EdgeX 4.克隆源码-SDK-C 5.添加第三方依赖库 6.编译-C SDK 7.编译运行 random 设备服务示例 总结 在 ubuntu22.04 中 安装 EdgeX 并运行 device-sdk-c sample EdgeX Foundry 作为一个开源的边缘计算框架，为开发者提供了一个灵活、可扩展的平台，用于构建和管理边缘设备及服务。然而，对于初次接触 EdgeX 的用户来说，从零开始安装并运行其相关组件（如 device-sdk-c 示例）可能会面临不少挑战。 无论是环境的配置、依赖的安装，还是编译与调试的过程，每一步都需要细致的操作和清晰的思路。本文将以 Ubuntu 22.04 为基础平台，详细记录如何从克隆 EdgeX 源码到成功运行 device-sdk-c 的 sample 示例的全过程。希望通过这篇博客，不仅能为新手提供一份实用的操作指南，也能让有经验的开发者在遇到问题时找到解决思路。 1.克隆源码-EdgeX 在开始安装 EdgeX 之前，我们需要先获取其源码，而 GitHub 是获取最新版本的最佳途径。为了让后续操作更加有条理，我们首先在用户主目录下创建一个专属的工作目录，用于存放 EdgeX 相关的文件。以下是具体操作： 打开终端，输入以下命令创建一个名为 edgex 的文件夹，并进入该目录： mkdir edgex && cd edgex 接下来，我们需要从 GitHub 上克隆 EdgeX 的 Compose 文件仓库，这是运行 EdgeX 服务的核心配置文件集合。执行以下命令： git clone https://github.com/edgexfoundry/edgex-compose.git cd edgex-compose 结果显示如下： root@ubuntu:/home/ubuntu# mkdir edgex && cd edgex root@ubuntu:/home/ubuntu/edgex# git clone https://github.com/edgexfoundry/edgex-compose.git Cloning into 'edgex-compose'... remote: Enumerating objects: 5826, done. remote: Counting objects: 100% (3563/3563), done. remote: Compressing objects: 100% (319/319), done. remote: Total 5826 (delta 3504), reused 3250 (delta 3244), pack-reused 2263 (from 3) Receiving objects: 100% (5826/5826), 1.59 MiB | 1.76 MiB/s, done. Resolving deltas: 100% (4949/4949), done. root@ubuntu:/home/ubuntu/edgex# ls edgex-compose 输出信息显示了克隆进度，最终 ls 命令确认目录中已存在 edgex-compose 文件夹，说明这一步成功完成。 2.安装工具 EdgeX 的部署离不开 Docker，因为它通过容器化技术简化了服务的管理和运行。因此，我们需要先在 Ubuntu 系统中安装 Docker。以下是详细的安装步骤： 使用官方脚本安装 Docker，并指定阿里云镜像源以加速下载： curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh --mirror Aliyun docker -v 第一条命令通过 curl 下载安装脚本并保存为 get-docker.sh 文件；第二条命令执行脚本，--mirror Aliyun 参数指定使用阿里云镜像源，避免默认源下载缓慢；最后通过 docker -v 检查版本，确保安装成功。安装过程的输出类似： root@ubuntu:/home/ubuntu# curl -fsSL https://get.docker.com -o get-docker.sh root@ubuntu:/home/ubuntu# sh get-docker.sh --mirror Aliyun # Executing docker install script, commit: 4c94a56999e10efcf48c5b8e3f6afea464f9108e ... Client: Docker Engine - Community Version: 28.0.1 API version: 1.48 ... root@ubuntu:/home/ubuntu# docker -v Docker version 28.0.1, build 068a01e 如果下载速度过慢，可能是镜像源的问题。这时可以调整 Ubuntu 的软件源以提高效率。首先检查系统版本： root@ubuntu:/home/ubuntu# more /etc/os-release PRETTY_NAME="Ubuntu 22.04.5 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04.5 LTS (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-poli cy" UBUNTU_CODENAME=jammy 确认版本为 jammy 后，访问南京大学镜像站（https://mirror.nju.edu.cn/mirrorz-help/ubuntu/）, 获取对应源配置并编辑 /etc/apt/sources.list： # vim /etc/apt/sources.list deb https://mirror.nju.edu.cn/ubuntu/ jammy main restricted universe multiverse deb https://mirror.nju.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse deb https://mirror.nju.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse 启动 docker 并查看状态： root@ubuntu:/home/ubuntu# systemctl restart docker root@ubuntu:/home/ubuntu# systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset> Active: active (running) since Thu 2025-03-13 12:21:05 CST; 6s ago TriggeredBy: ● docker.socket Docs: https://docs.docker.com Main PID: 28525 (dockerd) Tasks: 10 Memory: 22.1M CPU: 1.228s CGroup: /system.slice/docker.service └─28525 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/con> Active: active (running) 表示 Docker 已正常运行，为后续 EdgeX 部署奠定了基础。 3.编译运行-EdgeX 有了 Docker 支持后，我们可以开始部署 EdgeX 服务。EdgeX 使用 Docker Compose 管理多个容器，因此需要进入 compose-builder 目录执行构建和运行命令： 进入目录并运行无安全模式的 EdgeX： cd /home/ubuntu/edgex/edgex-compose/compose-builder/ make run no-secty echo MQTT_VERBOSE= MQTT_VERBOSE= docker compose -p edgex -f docker-compose-base.yml -f add-postgres.yml -f add-mqtt-messagebus.yml -f add-mqtt-broker-mosquitto.yml convert --output docker-compose.yml.gen if [ "0" = "0" ]; then rm -rf ./gen_ext_compose; fi echo "# Generated with: Docker Compose version v2.33.1" > docker-compose.yml cat docker-compose.yml.gen >> docker-compose.yml rm docker-compose.yml.gen docker compose -p edgex up -d [+] Running 12/12 ✔ Container edgex-ui-go Running 0.0s ✔ Container edgex-postgres Running 0.0s ✔ Container edgex-mqtt-broker Running 0.0s ✔ Container edgex-core-keeper Running 0.0s ✔ Container edgex-core-metadata Running 0.0s ✔ Container edgex-core-common-config-bootstrapper Running 0.0s ✔ Container edgex-app-rules-engine Running 0.0s ✔ Container edgex-support-scheduler Running 0.0s ✔ Container edgex-core-data Running 0.0s ✔ Container edgex-kuiper Running 0.0s ✔ Container edgex-support-notifications Running 0.0s ✔ Container edgex-core-command Started 0.4s 这表明所有容器已启动，包括 UI、数据库和核心服务等。 查看容器运行状态，显示如下，每个容器显示 Up 状态，说明 EdgeX 已成功运行： docker compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS edgex-app-rules-engine nexus3.edgexfoundry.org:10004/app-service-configurable:latest "/app-service-config…" app-rules-engine 7 minutes ago Up 7 minutes 48095/tcp, 127.0.0.1:59701->59701/tcp edgex-core-command nexus3.edgexfoundry.org:10004/core-command:latest "/core-command --reg…" core-command 7 minutes ago Up 5 minutes 127.0.0.1:59882->59882/tcp edgex-core-common-config-bootstrapper nexus3.edgexfoundry.org:10004/core-common-config-bootstrapper:latest "entrypoint.sh /core…" core-common-config-bootstrapper 7 minutes ago Up 7 minutes edgex-core-data nexus3.edgexfoundry.org:10004/core-data:latest "/core-data --regist…" core-data 7 minutes ago Up 7 minutes 127.0.0.1:59880->59880/tcp edgex-core-keeper nexus3.edgexfoundry.org:10004/core-keeper:latest "/core-keeper" core-keeper 7 minutes ago Up 7 minutes 127.0.0.1:59890->59890/tcp edgex-core-metadata nexus3.edgexfoundry.org:10004/core-metadata:latest "/core-metadata ... 4.克隆源码-SDK-C 为了运行 device-sdk-c 的示例，我们还需要获取其源码。device-sdk-c 包含了丰富的 C 语言示例，是开发自定义设备服务的好起点： 在 /edgex 目录下克隆仓库： git clone https://github.com/edgexfoundry/device-sdk-c.git root@ubuntu:/home/ubuntu/edgex/edgex-compose/compose-builder# cd ../.. root@ubuntu:/home/ubuntu/edgex# ls edgex-compose root@ubuntu:/home/ubuntu/edgex# git clone https://github.com/edgexfoundry/device-sdk-c.git Cloning into 'device-sdk-c'... remote: Enumerating objects: 5641, done. remote: Counting objects: 100% (527/527), done. remote: Compressing objects: 100% (213/213), done. remote: Total 5641 (delta 419), reused 337 (delta 307), pack-reused 5114 (from 4) Receiving objects: 100% (5641/5641), 1.93 MiB | 1.70 MiB/s, done. Resolving deltas: 100% (3210/3210), done. 5.添加第三方依赖库 编译 device-sdk-c 需要一系列依赖库，参考官方 README（https://github.com/edgexfoundry/device-sdk-c/blob/main/README.md） A Linux build host A version of GCC supporting C11. CMake version 3 or greater and make. Development libraries and headers for: curl (version 7.56 or later) microhttpd (version 0.9) libyaml (version 0.1.6 or later) libcbor (version 0.5) paho (version 1.3.x) libuuid (from util-linux v2.x) IOTech's C utilities package (version 1.5) apt-get install libcurl4-openssl-dev libmicrohttpd-dev libyaml-dev libcbor-dev libpaho-mqtt-dev apt-get install lsb-release apt-transport-https curl gnupg curl -fsSL https://iotech.jfrog.io/artifactory/api/gpg/key/public | gpg --dearmor -o /usr/share/keyrings/iotech.gpg echo "deb [signed-by=/usr/share/keyrings/iotech.gpg] https://iotech.jfrog.io/iotech/debian-release $(lsb_release -cs) main" | tee -a /etc/apt/sources.list.d/iotech.list apt-get update apt-get install iotech-iot-1.5-dev apt install uuid-dev 6.编译-C SDK 在C SDK 顶层目录下, 运行 make，最终结果显示如下： root@ubuntu:/home/ubuntu/edgex/device-sdk-c# make ... [100%] Built target device-file 7.编译运行 random 设备服务示例 编译前的准备工作，参考https://github.com/edgexfoundry/device-sdk-c/blob/main/src/c/examples/random/README.md 此示例设备服务模拟了一个具有两个随机数生成器和一个可读取和写入状态的开关的设备。先配置环境变量，注意自己的路径： export CSDK_DIR=/home/logan/edgex/device-sdk-c/build/release/_CPack_Packages/Linux/TGZ/csdk-0.0.0 export LD_LIBRARY_PATH=$CSDK_DIR/lib:/opt/iotech/iot/1.5/lib 涉及头文件和库文件的环境变量很重要，并且运行程序的时候也需要用到。 在/random 目录下执行编译命令： gcc -I$CSDK_DIR/include -I/opt/iotech/iot/1.5/include -L$CSDK_DIR/lib -L/opt/iotech/iot/1.5/lib -o device-random device-random.c -lcsdk -liot 将编译好的可执行文件 device-random 复制到以下源文件目录，因为源文件目录提供了/res 里面包含预设的配置文件： cp device-sdk-c/build/release/c/examples/random/device-random device-sdk-c/src/c/examples/random/ cd /home/ubuntu/edgex/device-sdk-c/src/c/examples/random/ 执行以下命令：-o 表示覆盖默认配置，-cp 指定配置提供者 ./device-random -o -cp=keeper.http://localhost:59890 如果遇到报错，按以下方法解决： 安全 Token 报错：设置环境变量禁用安全服务： root@ubuntu:/home/ubuntu/edgex/device-sdk-c/src/c/examples/random# ./device-random -o -cp=keeper.http://localhost:59890 level=INFO ts=2025-03-13T06:44:41Z app=device-random msg="iot_threadpool_alloc (threads: 8 max_jobs: 0 default_priority: -1 affinity: -1)" level=INFO ts=2025-03-13T06:44:41Z app=device-random msg="iot_scheduler_alloc (priority: -1 affinity: -1)" level=ERROR ts=2025-03-13T06:44:41Z app=device-random msg="vault: Token file /tmp/edgex/secrets/device-random/secrets-token.json doesn't parse as JSON" Error: 4: Configuration is invalid 解决：需要关闭Edges安全服务： 参考https://docs.edgexfoundry.org/4.0/microservices/configuration/CommonEnvironmentVariables/#edgex_security_secret_store export EDGEX_SECURITY_SECRET_STORE="false" Curl 解析失败：编辑 /etc/hosts： level=DEBUG ts=2025-03-13T06:45:41Z app=device-random msg="mqtt: subscribing to edgex/configs/edgex/v4/core-common-config-bootstrapper/all-services/#" level=DEBUG ts=2025-03-13T06:45:41Z app=device-random msg="mqtt: subscribing to edgex/configs/edgex/v4/core-common-config-bootstrapper/device-services/#" level=ERROR ts=2025-03-13T06:45:42Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:43Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:44Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:45Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:46Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:47Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" 解决：在/etc/hosts 中添加127.0.0.1 edgex-core-metadata： /etc/hosts 127.0.0.1 localhost 127.0.0.1 edgex-core-metadata # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters ff02::3 ip6-allhosts 健康检查间隔缺失：修改 res/configuration.yml： level=DEBUG ts=2025-03-13T06:50:39Z app=device-random msg="HTTP response: 404" level=DEBUG ts=2025-03-13T06:50:39Z app=device-random msg="HTTP response: 400" level=ERROR ts=2025-03-13T06:50:39Z app=device-random msg="Register service failed: {"apiVersion":"v3","message":"AddRegistrationRequest.Registration.HealthCheck.Interval field is required","statusCode":400} level=ERROR ts=2025-03-13T06:50:39Z app=device-random msg="Unable to register service in registry" Error: 21: HTTP request failed level=DEBUG ts=2025-03-13T06:50:39Z app=device-random msg="Scheduler thread stopping" 解决：在res/configration.yml 中添加 HealthCheckInterval: 10s： Writable: LogLevel: DEBUG Service: Host: localhost Port: 59999 StartupMsg: Example random device service started HealthCheckInterval: 10s ​ 或者在/etc/hosts 中添加127.0.0.1 edgex-mqtt-broker,然后在执行命令 ./device-random -o -cp=keeper.http://localhost:59890 中去掉 -o： /etc/hosts 127.0.0.1 localhost 127.0.0.1 edgex-core-metadata 127.0.0.1 edgex-mqtt-broker # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters ff02::3 ip6-allhosts 如果程序运行成功，将显示如下： ... level=INFO ts=2025-03-13T07:36:45Z app=device-random correlation-id=c26ef1f7-755c-4952-8091-5c7c436e039b msg="AutoEvent: RandomDevice1/SensorOne" level=INFO ts=2025-03-13T07:36:50Z app=device-random correlation-id=dc222997-3113-465b-9c5c-26829ce24901 msg="AutoEvent: RandomDevice1/SensorTwo" level=INFO ts=2025-03-13T07:36:55Z app=device-random correlation-id=049d560d-896a-4e5a-8101-f3364c2767f1 msg="AutoEvent: RandomDevice1/SensorOne" 打开浏览器进入：localhost:4000 可发现一个设备服务，可成功读取随机数，如下： 总结 通过以上步骤，我们成功地在 Ubuntu 22.04 上完成了 EdgeX 的安装，并运行了 device-sdk-c 的 random 设备服务示例。从源码的克隆到 Docker 环境的搭建，再到依赖库的安装和配置文件的调整，每一个环节都环环相扣，考验着我们的耐心和解决问题的能力。 过程中可能遇到的报错，如安全性 token 问题、Curl 解析失败或健康检查间隔配置缺失，也提醒我们在操作复杂系统时，细节的重要性不容忽视。 最终，当我们在浏览器中看到 localhost:4000 界面上显示的设备服务和随机数据时，那份成就感无疑是对所有努力的最好回报。这不仅是一个技术实现的过程，更是一次对 EdgeX 架构和边缘计算理念的深入学习之旅。未来，我们还可以基于此基础，进一步扩展功能、优化性能，让边缘计算的潜力在更多场景中得以释放！
在 ubuntu22.04 中 安装 OpenVINO 并运行 Hello Classification C Sample
在 ubuntu22.04 中 安装 OpenVINO 并运行 Hello Classification C Sample 1.克隆源码 2.安装依赖 和 编译 3.安装项目 4.配置环境变量 5.编译 Sample 总结 在 ubuntu22.04 中 安装 OpenVINO 并运行 Hello Classification C Sample 从GitHub上获取一个项目的源码并成功运行其Demo，虽然看似简单，但实际上涉及多个步骤和注意事项。这个过程不仅需要对Git的基本操作有所了解，还需要根据项目的具体需求进行环境配置、依赖安装和构建工具的使用。以下是一个相对通用的流程，以OpenVINO项目的编译、安装、运行为例，详细描述从克隆源码到运行Demo的各个步骤。 1.克隆源码 在 root 下创建文件夹目录 mkdir devel && cd devel mkdir openvino && cd 克隆新版本源码 git clone -b 2025.0.0 https://github.com/openvinotoolkit/openvino.git 注意版本号2025.0.0 2.安装依赖 和 编译 编译流程中，前2~5点参考的https://github.com/openvinotoolkit/openvino/blob/2025.0.0/docs/dev/build_linux.md 编译和构建软件之前，安装必要的开发工具和库： sudo apt install build-essential -y OpenVINO 包含一些子模块，还需要更新，国内推荐使用gitee：（时间稍长 须作等待） cd openvino chmod +x scripts/submodule_update_with_gitee.sh ./scripts/submodule_update_with_gitee.sh 使用项目根目录脚本安装依赖项： sudo ./install_build_dependencies.sh 编译的准备工作： mkdir build && cd build OpenVINO Runtime使用基于CMake的构建系统。在创建的构建目录中，运行cmake获取项目依赖关系并创建Unix makefile，然后运行make构建项目： cmake -DCMAKE_BUILD_TYPE=Release .. cmake --build . --parallel 4 （ 通过 nproc 命令查询到 CPU 核心数为 4 ）编译时间漫长，成功后显示如下： 3.安装项目 目的: 将编译结果安装到系统指定位置。 在 build 文件下执行安装命令： sudo cmake --install . 文件会被安装到 /usr/local 如果只是运行 sample，可以跳过此步，直接使用构建目录中的文件。但建议规范安装好完整项目，从 /usr/local/sample 下获取 sample 源码和编译文件。 此时注意到：/usr/local/include 为 openvino 规范的头文件路径，/usr/local/lib/intel64/ 为规范的库文件路径。在 install 之前，二者的路径为/源码/src/bindings/c/include 以及/bin/intel64/Release ，使用它们并不规范！ 4.配置环境变量 目的: 告诉系统的动态链接器等（ld.so）在运行时查找共享库（.so 文件）的额外路径。在 OpenVINO 的例子中，/usr/local/lib/intel64/ 包含编译后的动态库（如 libopenvino.so），程序运行时需要这些库。如果这个路径没有被正确设置，运行程序时可能会遇到错误。openvino 提供了配置脚本 /usr/local/setupvars.sh ，执行 source 命令启动它： source /usr/local/setupvars.sh 注意： 不能直接执行 ./usr/local/setupvars.sh 。因为./setupvars.sh 会在一个新的子 Shell 进程中执行脚本。脚本中设置的环境变量只在子进程中生效，子进程结束后，这些修改不会传递回父 Shell（即你的当前终端）。 OpenVINO 的 setupvars.sh 脚本也是临时的，除非你每次打开终端都运行它，否则需要手动永久设置。 永久设置：（设置需要用到的环境变量） vim ~/.bashrc # .bashrc末尾添加 export LD_LIBRARY_PATH=/usr/local/runtime/3rdparty/tbb/lib:/usr/local/runtime/lib/intel64:$LD_LIBRARY_PATH export PYTHONPATH=/usr/local/python:/usr/local/python/python3:$PYTHONPATH 5.编译 Sample 参考：https://docs.openvino.ai/2025/get-started/learn-openvino/openvino-samples/get-started-demos.html#build-samples 在 <INSTALL_DIR>/samples/c 下执行 source build_samples.sh 命令，如下所示： 根据编译结果的提示，找到二进制可执行文件路径/home/ubuntu/openvino_c_samples_build/intel64/Release 准备好模型文件 alexnet.xml 和 alexnet.bin 之后，准备一张 png 图片 运行程序，结果如下即为运行成功： 总结 本文描述了从 GitHub 获取 OpenVINO 源码并运行Demo的过程，也可简化为以下几个核心步骤： 克隆源码：使用git clone下载项目到本地。 阅读文档：查看README.md，了解项目的安装、依赖和构建说明。 安装依赖：根据项目需求，安装必要的工具和库。 构建项目：使用构建工具（如CMake、Make）编译源码。 配置环境：设置环境变量或路径，确保项目正常运行。 运行Demo：执行Demo，验证项目功能。 每个项目的具体步骤可能会有所不同，但遵循这个通用流程可以帮助你快速上手大多数开源项目。无论是学习新技术、参与开源社区，还是将开源项目应用于实际工作中，掌握这些基本操作都是非常重要的。
端口转发与反向代理实现本机连接远程内网
端口转发与反向代理实现本机连接远程内网 在日常工作中，特别是对网络管理、系统运维、开发调试等任务而言，有时需要通过 SSH 来访问远程内网资源。为了实现这一目标，可以利用 端口转发 和 反向代理 技术。通过 SSH 的端口转发功能，可以把本地的流量转发到远程网络，进而访问远程内网的资源。本文将介绍如何通过 SSH 端口转发和反向代理技术来实现本机连接远程内网。 1. 本机连接远程内网：本地端口转发 首先，假设我们有一台远程服务器 120.120.120.120，我们希望通过本机访问该服务器内网中的某个服务（例如，位于 127.0.0.1:1000 的服务）。通过设置 本地端口转发（Local Port Forwarding），可以将本机的流量转发到远程服务器上，然后访问该服务。 命令： ssh -L 1000:127.0.0.1:1000 120.120.120.120 参数解析： -L 1000:127.0.0.1:1000： 1000（本地端口）：指定本机的端口，用于监听。 127.0.0.1:1000（远程目标地址和端口）：表示远程服务器内部的目标地址和端口，这里的 127.0.0.1 表示远程服务器上的本地回环地址，1000 是远程服务器的端口。 本地计算机会监听本地端口 1000，然后把流量转发到远程服务器的 127.0.0.1:1000。 120.120.120.120： 远程服务器的 IP 地址，表示 SSH 的目标服务器。 工作原理： 当你在本地计算机上访问 127.0.0.1:1000 时，流量会被转发到远程服务器 120.120.120.120 的 127.0.0.1:1000 地址。 这意味着，本地计算机就像直接连接到了远程服务器内网中的服务一样，通过 SSH 隧道实现了端口转发。 2. 远程服务器连接本地服务器：动态端口转发 假设远程服务器 50.50.50.50 上有一个服务需要通过 SSH 隧道来访问本地的资源。可以通过 动态端口转发（Dynamic Port Forwarding） 技术，配置一个 SOCKS 代理，从而将远程服务器的数据流量通过 SSH 隧道转发到本地计算机，进而访问内网资源。 命令： ssh -D 2000 50.50.50.50 参数解析： -D 2000： 2000（本地端口）：指定本地计算机上用于监听的端口，此端口将充当 SOCKS 代理服务器。 该命令会在本地启动一个 SOCKS 代理服务器，监听端口 2000。 通过该代理服务器，所有从本地发送到端口 2000 的流量将被发送到远程服务器 50.50.50.50，然后通过该服务器转发到目标地址。 50.50.50.50： 远程服务器的 IP 地址，表示 SSH 的目标服务器。 工作原理： 本地计算机会在 2000 端口上启动一个 SOCKS 代理，所有流量会通过这个代理发送到远程服务器 50.50.50.50。 远程服务器通过 SSH 隧道将流量发送到最终目标，例如内网服务。 这样一来，本地计算机就能够通过远程服务器来访问内网资源。 3. 端口转发与反向代理的结合 通过端口转发和反向代理的结合，可以实现本地计算机通过远程服务器访问内网资源，也可以通过远程服务器反向访问本地内网资源。两者结合，形成了一条完整的 SSH 隧道链路，解决了很多需要穿越防火墙和 NAT 网络的场景。 常见的应用场景： 远程桌面访问：通过端口转发，可以将本地端口映射到远程服务器上的 RDP 服务端口（如 3389），使得本地计算机能够远程控制内网机器。 内网穿透：当本地计算机位于 NAT 网络后时，通过 SSH 端口转发可以实现内网穿透，访问远程内网服务。 网络调试与监控：开发和运维人员可以通过端口转发访问远程服务，进行调试和监控。 绕过防火墙：通过 SSH 隧道，可以将流量加密并绕过防火墙的限制，实现安全通信。 4. 总结 本文介绍了通过 端口转发 和 动态代理 技术，实现本机与远程内网的连接。通过 SSH 的端口转发功能，可以将本地的流量转发到远程服务器，再由远程服务器转发到目标内网服务。而动态端口转发则充当 SOCKS 代理，允许本地计算机通过远程服务器访问内网资源。这两种技术常常用于实现远程访问、内网穿透、远程调试等场景。掌握这些技巧，对于解决网络访问限制和提高工作效率具有重要意义。
使用 FRP 将本地 SSH 服务暴露到远程主机 FRP（Fast Reverse Proxy）是一个高性能的反向代理应用，可以帮助我们穿透 NAT 和防火墙，将本地的服务暴露到公网上。在本篇博客中，我们将讲解如何使用 FRP 将本地的 SSH 服务暴露到远程主机，实现安全的远程访问。 1. 安装 FRP 首先，你需要从 FRP 的 GitHub 页面 下载适合你系统的版本。假设你已经成功解压 FRP 文件，并准备好 frpc（客户端）和 frps（服务器端）的可执行文件。 2. FRP 配置 FRP 配置分为两部分：客户端配置 (frpc.toml) 和服务器端配置 (frps.toml)。 FRP 服务器端配置 (frps.toml) 在远程主机上配置 FRP 服务器。frps.toml 配置文件内容如下： # frps.toml bindPort = port1 FRP 服务器将监听 port1 端口，用于 FRP 客户端与 FRP 服务器之间的控制和数据传输。 客户端通过 FRP 服务器的port1端口与服务器建立连接，所有的代理服务请求和管理通信都会通过这个端口来进行。这是 FRP 服务器暴露的端口，客户端需要连接到此端口来请求访问服务。 在 frps.toml 配置中设置了 bindPort = port1，这意味着 FRP 服务器在 port1 端口监听来自客户端的连接请求。 在 frpc.toml 配置中，客户端设置 serverPort = port1，表示客户端连接到服务器的 port1 端口进行通信。 FRP 客户端配置 (frpc.toml) # frpc.toml serverAddr = "127.0.0.1" # FRP 服务器地址，服务器和客户端在同一台机器上 serverPort = port1 # FRP 服务器端口，确保与 frps.toml 中的配置一致 proxies name = "test-tcp" # 为该代理指定一个名称 type = "tcp" # 使用 TCP 协议进行代理 localIP = "127.0.0.1" # 本地机器的 IP 地址 localPort = 22 # 本地 SSH 服务的端口，默认 SSH 端口为 22 remotePort = port2 # 远程服务器上的端口port2，将会映射到本地的 22 端口 用于暴露本地服务（如 SSH）到远程主机。 客户端的本地服务（如 SSH 服务，默认是 22 端口）通过 FRP 被映射到远程服务器上的某个端口（如 port2 端口）。此端口是客户端的本地服务经过 FRP 隧道转发到远程服务器后的端口。 在 frpc.toml 配置文件中，localPort = 22 表示客户端本地服务（如 SSH）运行在 22 端口。 remotePort = port2 表示远程服务器上的端口号，用于通过 FRP 将本地 22 端口的流量转发到远程服务器的 port2 端口。 3. 启动 FRP 服务 在远程服务器上启动 FRP 服务器端： ./frps -c ./frps.toml 在本地机器上启动 FRP 客户端： ./frpc -c ./frpc.toml 4. 验证连接 当 FRP 客户端和服务器都成功启动后，本地机器上的 SSH 服务（通常运行在 22 端口）将被暴露到远程服务器的 port2 端口。你可以通过以下命令进行连接测试： ssh -p port2 username@remote_host 5. 查看端口状态 你可以在远程服务器或本地机器上通过 netstat 命令查看端口是否成功监听。 在 Linux 上使用以下命令查看 port2 端口的状态： netstat -na | grep port2 tcp 0 0 0.0.0.0:port2 0.0.0.0:* LISTEN 6. 总结 通过以上步骤，你可以使用 FRP 将本地的 SSH 服务暴露到远程服务器。这样，即使在 NAT 或防火墙后面，你也可以通过 FRP 安全地访问本地服务。FRP 提供了高效、灵活的端口转发机制，适合多种网络环境和使用场景。 为了避免每次ssh登录输入密码，可以配置公私钥。 PS: 如何配置公私钥 生成密钥对 ssh-keygen -t rsa -b 2048 -C "your_email@example.com" 秘钥添加到远程服务器 生成的公钥 (~/.ssh/id_rsa.pub) 添加到目标服务器的 ~/.ssh/authorized_keys 文件中。 ssh-copy-id user@remote_host # 公钥直接复制到远程服务器 使用私钥连接或者直接连接 ssh -i ~/.ssh/id_rsa user@remote_host ssh user@remote_host
openssl genrsa -out mydomain.key 2048 openssl req -new -key mydomain.key -out mydomain.csr openssl x509 -req -days 3650 -in mydomain.csr -signkey mydomain.key -out mydomain.crt [req] default_bits = 2048 distinguished_name = req_distinguished_name req_extensions = req_ext prompt = no [req_distinguished_name] C = CN ST = SH L = Shanghai O = YIQISOFT OU = Tech CN = harbor.yiqisoft.cn [req_ext] subjectAltName = @alt_names [alt_names] DNS.1 = harbor.yiqisoft.cn openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout mydomain.key -out mydomain.crt -config openssl.cnf
准备工作 硬件 软件 基础配置 数据库配置 Docker 数据库 PostgreSQL MySQL 部署 K3S 管理集群 主机名 K3S 核心安装 自动安装(推荐) 手动二进制文件安装(不推荐) Master / Worker 手动加入(如有必要) 安装 Rancher 集群管理工具和 Harbor Registry 仓库 准备工作 安装 Kubernetes 包管理工具 Helm 安装证书管理器 cert-manager 安装 Rancher 安装 Harbor 使用 Ceph（推荐） 使用 NFS 部署 pv 和 pvc 部署 Harbor Helm 批量上传所需 image 到 Harbor 部署 K3S 业务集群 所有的 Master 和 Worker 都加入业务集群 Rancher 中创建一个新的集群 Master 加入集群(初始化业务集群) Worker 加入业务集群 (部署/更新) Harbor hub CA 证书到所有的 Master 和 Worker (手动操作)导出 Harbor hub HTTPS 证书 (自动部署)Harbor Hub CA 证书 新建一个 Secret 新建一个 DaemonSet 基于 K3S 部署业务应用(边缘计算框架) EdgeX Foundry Base 部署辅助应用：流媒体，推理服务器，NVR 部署 OpenVINO AI 应用 总结 准备工作 硬件 主 K3S Master 节点：3台, master101, master102, master103 业务 K3S Master 节点：3台，master151, master152, master153 Worker 节点：N台，随意, worker01, worker02, ... 软件 基础配置 操作系统：Ubuntu Server 22.04 最小化安装系统，包含：openssh-server, iputils-ping, net-tools, vim PRETTY_NAME="Ubuntu 22.04.5 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04.5 LTS (Jammy Jellyfish)" 管理集群 主机名 IP 其他 database 192.168.198.100 数据库+NFS存储，如有需要做 HA master101 192.168.198.101 管理 K3S master master102 192.168.198.102 管理 K3S master master103 192.168.198.103 管理 K3S master 业务集群 主机名 IP 其他 master151 192.168.123.151 业务 K3S master + worker master152 192.168.123.152 业务 K3S master + worker master153 192.168.123.153 业务 K3S master + worker worker161 192.168.123.161 业务 worker worker162 192.168.123.162 业务 worker 数据库配置 Docker 准备 docker 环境 curl -fsSL https://get.docker.com | sh - 验证 docker docker images REPOSITORY TAG IMAGE ID CREATED SIZE 设置一个 Registry 镜像 cat /etc/docker/daemon.json { "insecure-registries" : [ "0.0.0.0/0" ], "registry-mirrors": [ "https://xxx.mirror.swr.myhuaweicloud.com" ] } 重启 docker systemctl restart docker 数据库 PostgreSQL 创建 compose.yml services: postgres: image: postgres:alpine container_name: postgres restart: unless-stopped environment: POSTGRES_USER: k3s POSTGRES_PASSWORD: talent POSTGRES_DB: k3s ports: - "5432:5432" volumes: - ./data:/var/lib/postgresql/data - ./init-db.sh:/docker-entrypoint-initdb.d/init-db.sh 创建脚本 init-db.sh #!/bin/bash set -e psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname=postgres <<-EOSQL CREATE DATABASE harbor; EOSQL 启动 PostgreSQL 数据库容器 docker compose up -d 验证数据库 docker exec -it postgres psql -U k3s -c '\l' List of databases Name | Owner | Encoding | Locale Provider | Collate | Ctype | Locale | ICU Rules | Access privileges -----------+-------+----------+-----------------+------------+------------+--------+-----------+------------------- harbor | k3s | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | k3s | k3s | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | ... (5 rows) MySQL 因为 Rancher 只支持 MySQL 不支持 PostgreSQL 创建 compose.yml services: mysql: image: mysql:latest container_name: mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: talent MYSQL_DATABASE: rancher MYSQL_USER: rancher MYSQL_PASSWORD: talent ports: - "3306:3306" volumes: - ./data:/var/lib/mysql - ./init:/docker-entrypoint-initdb.d 创建脚本 init/permissions.sql GRANT ALL PRIVILEGES ON *.* TO 'rancher'@'%' IDENTIFIED BY 'talent'; FLUSH PRIVILEGES; 启动 MySQL 数据库容器 docker compose up -d 验证数据库 docker exec -it mysql mysql -u rancher -p'talent' -e "SHOW DATABASES;" +--------------------+ | Database | +--------------------+ | rancher | +--------------------+ 部署 K3S 管理集群 本章节内容在管理 K3S 主机 上执行 主机名 所有的 Master 配置相同的 /etc/hosts127.0.0.1 localhost 127.0.1.1 master101 # CHAMGEME 192.168.198.100 database 192.168.198.101 master101 192.168.198.102 master102 192.168.198.103 master103 K3S 核心安装 自动安装(推荐) 可以连接互联网的情况下 默认安装最新版本，这里是: v1.30.5+k3s1，注意：k3s:talent 是你自己的数据库用户名密码，使用一样的 token curl -sfL https://get.k3s.io | sh -s - server \ --datastore-endpoint="postgres://k3s:talent@database:5432/k3s?sslmode=disable" --token K10c71232f051944de58ab058871ac7a85b42cbdee5c6df94deb6bb493c79b15a92::server:3d6c2d90cec2e63db66bde0e6f24013d 验证安装 kubectl get nodes NAME STATUS ROLES AGE VERSION master101 Ready control-plane,master 43s v1.30.5+k3s1 其他 Master 安装方式一样。在任何一台 Master 上确认安装完成 kubectl get nodes NAME STATUS ROLES AGE VERSION master101 Ready control-plane,master 117s v1.30.5+k3s1 master102 Ready control-plane,master 118s v1.30.5+k3s1 master103 Ready control-plane,master 29s v1.30.5+k3s1 手动二进制文件安装(不推荐) 下载二进制文件 wget https://github.com/k3s-io/k3s/releases/download/vX.X.X/k3s -O /usr/local/bin/k3s chmod +x /usr/local/bin/k3s 初始化 k3s server --cluster-init 设置自启动脚本 所有 Master 机器上一样的配置 cat <<EOF | sudo tee /etc/systemd/system/k3s.service [Unit] Description=Lightweight Kubernetes Documentation=https://k3s.io After=network.target [Service] ExecStart=/usr/local/bin/k3s server --datastore-endpoint="postgres://k3s:talent@database:5432/k3s?sslmode=disable" --token K10c71232f051944de58ab058871ac7a85b42cbdee5c6df94deb6bb493c79b15a92::server:3d6c2d90cec2e63db66bde0e6f24013d Restart=always RestartSec=5s [Install] WantedBy=multi-user.target EOF 更新自启动设置 systemctl daemon-reload systemctl enable k3s systemctl start k3s 查看集群状态 任何一台 Master 都可以 /usr/local/bin/k3s kubectl get nodes 结果 NAME STATUS ROLES AGE VERSION master101 Ready control-plane,master 10m v1.30.4+k3s1 master102 Ready control-plane,master 10m v1.30.4+k3s1 master103 Ready control-plane,master 81s v1.30.4+k3s1 Master / Worker 手动加入(如有必要) 获取集群 token cat /var/lib/rancher/k3s/server/node-token Master 直接加入集群（不建议） k3s server --server https://master101:6443 --token <token> 或修改 /etc/systemd/system/k3s.service Worker加入（可选） 替换<node-token> 为你的token cat <<EOF | sudo tee /etc/systemd/system/k3s-agent.service [Unit] Description=Lightweight Kubernetes Documentation=https://k3s.io After=network.target [Service] ExecStart=/usr/local/bin/k3s agent --server https://master101:6443 --token <node-token> Restart=always RestartSec=5s [Install] WantedBy=multi-user.target EOF 更新自启动设置 systemctl daemon-reload systemctl enable k3s-agent systemctl start k3s-agent 不管怎么操作，基础三个主机的 K3S 集群要跑起来，保证基础工作完成。 安装 Rancher 集群管理工具和 Harbor Registry 仓库 准备工作 安装 Kubernetes 包管理工具 Helm 选择一个 Master 或全部 Master，安装 Helm curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash - 设置环境变量 加入到 /etc/profile 自启动 echo "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >> /etc/profile && source /etc/profile 安装证书管理器 cert-manager helm 脚本安装 kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.3/cert-manager.crds.yaml helm repo add jetstack https://charts.jetstack.io helm repo update helm install cert-manager jetstack/cert-manager --namespace cert-manager \ --create-namespace --version v1.15.3 验证安装成功 kubectl get pods --namespace cert-manager NAME READY STATUS RESTARTS AGE cert-manager-9647b459d-9jwpj 1/1 Running 0 2m47s cert-manager-cainjector-5d8798687c-66r95 1/1 Running 0 2m47s cert-manager-webhook-c77744d75-k22h5 1/1 Running 0 2m47s 设置自签名 Issuer 创建 issuer-cert.yaml, 这里创建两个： rancher.yiqisoft.com hub.yiqisoft.com, 后面访问集群业务就是通过域名来通信 apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: selfsigned-issuer spec: selfSigned: {} --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: rancher-cert namespace: cattle-system spec: secretName: rancher-cert-secret issuerRef: name: selfsigned-issuer kind: ClusterIssuer commonName: rancher.yiqisoft.com duration: 315360000s renewBefore: 240h dnsNames: - rancher.yiqisoft.com --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: hub-cert namespace: harbor spec: secretName: hub-cert-secret issuerRef: name: selfsigned-issuer kind: ClusterIssuer commonName: hub.yiqisoft.com duration: 315360000s renewBefore: 240h dnsNames: - hub.yiqisoft.com 安装自定义 Certificate 先创建两个 namespace，再安装脚本 kubectl create namespace harbor kubectl create namespace cattle-system kubectl apply -f issuer-cert.yaml 结果输出 clusterissuer.cert-manager.io/selfsigned-issuer created certificate.cert-manager.io/rancher-cert created certificate.cert-manager.io/hub-cert created 安装 Rancher 增加 Rancher Helm 仓库 helm repo add rancher-latest https://releases.rancher.com/server-charts/latest helm repo update 安装 Rancher 这个过程比较漫长，需要从互联网下载 helm install rancher rancher-latest/rancher \ --namespace cattle-system \ --set hostname=rancher.yiqisoft.com \ --set replicas=3 \ --set extraEnv[0].name=CATTLE_DATABASE_ENDPOINT \ --set extraEnv[0].value=database:3306 \ --set extraEnv[1].name=CATTLE_DATABASE_USER \ --set extraEnv[1].value=rancher \ --set extraEnv[2].name=CATTLE_DATABASE_PASSWORD \ --set extraEnv[2].value=talent \ --set extraEnv[3].name=CATTLE_DATABASE_NAME \ --set extraEnv[3].value=rancher 验证安装成功 kubectl get pods --namespace cattle-system 结果 NAME READY STATUS RESTARTS AGE rancher-567cc5c6ff-bzx42 1/1 Running 0 65m rancher-567cc5c6ff-mgvns 1/1 Running 0 86m rancher-567cc5c6ff-rbgjn 1/1 Running 0 86m rancher-webhook-77b49b9ff9-mp2dm 1/1 Running 0 142m 安装 Harbor Harbor 作为内部 docker hub 使用 使用 Ceph（推荐） 准备一个 Ceph 集群用于存储（这里省略）。 使用 NFS 简单实用，但是效率不高，安全性一般。 在一个文件服务器上部署 NFS，这里用 database 主机apt update apt install nfs-kernel-server -y mkdir -p /opt/nfs_share chown nobody:nogroup /opt/nfs_share chmod 777 /opt/nfs_share echo "/opt/nfs_share *(rw,sync,no_subtree_check)" >> /etc/exports exportfs -ra systemctl restart nfs-kernel-server 部署 pv 和 pvc 编辑 pv-pvc.yaml apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv spec: capacity: storage: 30Gi accessModes: - ReadWriteMany nfs: path: /opt/nfs_share server: database --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi volumeName: nfs-pv storageClassName: "" 应用到 namespace harbor kubectl apply -f pv-pvc.yaml -n harbor 结果 persistentvolume/nfs-pv created persistentvolumeclaim/nfs-pvc created 查看结果，nfs-pvc 必须是 Bound kubectl get pvc -n harbor 结果 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE nfs-pvc Bound nfs-pv 30Gi RWX <unset> 92s 查看 nfs-pv 必须是 Bound kubectl get pv -n harbor 结果 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE nfs-pv 30Gi RWX Retain Bound harbor/nfs-pvc <unset> 97s 部署 Harbor Helm 安装 NFS 依赖 所有的 Master 上需要安装 nfs-common，并验证（可选），可以访问 /tmp/nfs 目录 apt install nfs-common -y mkdir /tmp/nfs mount -t nfs database:/opt/nfs_share /tmp/nfs ll /tmp/nfs 增加 Harbor Helm 仓库 helm repo add harbor https://helm.goharbor.io helm repo update 导出默认 values，修改 helm show values harbor/harbor > harbor-values.yml # 内容修改如下 expose: ingress: hosts: core: hub.yiqisoft.cn externalURL: https://hub.yiqisoft.cn persistentVolumeClaim: registry:/exter existingClaim: "nfs-pvc" storageClass: "-" database: type: external external: host: "192.168.198.100" port: "5432" username: "k3s" password: "talent" coreDatabase: "harbor" harborAdminPassword: "Harbor12345" 安装 Harbor，需要通过互联网下载所有的镜像，时间根据网络带宽决定 helm install harbor harbor/harbor -f harbor-values.yml -n harbor 确认 Harbor 是否安装成功 kubectl get pods -n harbor 都显示 READY 即可 NAME READY STATUS RESTARTS AGE harbor-core-666cf84bc6-vq8mp 1/1 Running 0 85s harbor-jobservice-c68bc669-6twbp 1/1 Running 0 85s harbor-portal-d6c99f896-cq9fx 1/1 Running 0 85s harbor-redis-0 1/1 Running 0 84s harbor-registry-5984c768c8-4xnrg 2/2 Running 0 84s harbor-trivy-0 1/1 Running 0 84s 验证 Harbor 工作状态 curl -k https://hub.yiqisoft.com/v2/ -u admin:Harbor12345 提示 {} 证明成功 {} 以下在 database 主机上运行 登录 Harbor docker login hub.yiqisoft.com -u admin -p Harbor12345 登录成功 Login Succeeded 上传已经存在的 PostgreSQL image 测试 docker tag postgres:alpine hub.yiqisoft.com/library/postgres:alpine docker push hub.yiqisoft.com/library/postgres:alpine 结果显示 The push refers to repository [hub.yiqisoft.com/library/postgres] 284f9fec9bcb: Pushed 07188499987f: Pushed ... 63ca1fbb43ae: Pushed alpine: digest: sha256:f0407919d966a86e9***c81641801e43c29b size: 2402 批量上传所需 image 到 Harbor 在准备好 docker image 的机器上运行 导出需要的 image docker images --format "{{.Repository}}:{{.Tag}}" |grep yiqisoft.com > images.txt 新建 /etc/docker/daemon.json，跳过安全证书 { "insecure-registries" : [ "0.0.0.0/0" ] } 重启 docker systemctl restart docker 新建一个脚本 push-images.sh #!/bin/bash # Check if the correct number of arguments is provided if [ $# -ne 1 ]; then echo "Usage: $0 <path_to_images_file>" exit 1 fi # Assign the first argument to the file variable file=$1 # Check if the file exists if [ ! -f "$file" ]; then echo "File not found: $file" exit 1 fi while read image; do echo "Pushing image: $image" docker push $image if [ $? -eq 0 ]; then echo "Successfully pushed $image" else echo "Failed to push $image" fi done < "$file" 登录 Harbor 并上传: push image docker login hub.yiqisoft.com -u admin -p Harbor12345 bash push-images.sh images.txt 查看 Harbor Registry 仓库结果 curl -k https://hub.yiqisoft.com/v2/_catalog -u admin:Harbor12345 |json_pp 结果显示 { "repositories" : [ "edgexfoundry/app-service-gateway", "edgexfoundry/consul", "edgexfoundry/core-command", "edgexfoundry/core-common-config-bootstrapper", "edgexfoundry/core-data", "edgexfoundry/core-metadata", "edgexfoundry/device-modbus", "edgexfoundry/device-onvif-camera", "edgexfoundry/device-openvino-face-recognition", "edgexfoundry/device-openvino-object-detection", "edgexfoundry/edgex-ui", "edgexfoundry/redis", "edgexfoundry/support-notifications", "edgexfoundry/support-scheduler", "library/hello-world", "library/postgres", "rancher/system-agent", "yiqisoft/media_server", "yiqisoft/model_server", "yiqisoft/nginx", "yiqisoft/nvr" ] } 部署 K3S 业务集群 以下操作注意：全部在新建的业务集群中操作 所有的 Master 和 Worker 都加入业务集群 Rancher 中创建一个新的集群 Rancher UI 操作（省略）, Cluster Name: yiqisoft, Kubernetes Version: 1.30.4+k3s1 Master 加入集群(初始化业务集群) 通过新集群的 Registration 加入，可以自定义一个 namespace。需要几个 Master 根据自己业务需要来定。 导入 hub CA echo | openssl s_client -showcerts -connect hub.yiqisoft.com:443 2>/dev/null | \ openssl x509 -outform PEM > /etc/ssl/certs/hub.yiqisoft.com.crt 分别在 master151-master153 主机上执行，主机名不能重复 curl --insecure -fL https://rancher.yiqisoft.com/system-agent-install.sh | \ sudo sh -s - --server https://rancher.yiqisoft.com --label 'cattle.io/os=linux' \ --token xft4fsrjgkn4rzqxrgrb4mztkcxtz4nhpwm58t84q54lvpsscdgd72 \ --ca-checksum 30d78ad0ece6355c40a4b3764ba02dfc96388a9c20f6b4e2bffb131cf7402c1f \ --etcd --controlplane --worker 查看业务集群 node kubectl get nodes 确认 Master 都在线 NAME STATUS ROLES AGE VERSION master151 Ready control-plane,etcd,master,worker 32m v1.30.4+k3s1 master152 Ready control-plane,etcd,master,worker 3h39m v1.30.4+k3s1 master153 Ready control-plane,etcd,master,worker 23m v1.30.4+k3s1 Worker 加入业务集群 首先通过 curl 直接输出 hub 证书到主机证书目录 containerd 拉取 image 时需要信任此证书 CA echo | openssl s_client -showcerts -connect hub.yiqisoft.com:443 2>/dev/null | \ openssl x509 -outform PEM > /etc/ssl/certs/hub.yiqisoft.com.crt 需要等 Master 加入成功后（至少一个成功注册），Worker 主机再次通过 Registration 加入，只需要选择 Worker 即可，需要定义一个 namespace label， 比如： namespace=worker01，以后可单独部署到独立应用 到 namespace 作为隔离使用。 在所有业务 Worker 上执行，同样注意主机名不能重复 curl --insecure -fL https://rancher.yiqisoft.com/system-agent-install.sh | \ sudo sh -s - \ --server https://rancher.yiqisoft.com \ --label 'cattle.io/os=linux' \ --token xft4fsrjgkn4rzqxrgrb4mztkcxtz4nhpwm58t84q54lvpsscdgd72 \ --ca-checksum 30d78ad0ece6355c40a4b3764ba02dfc96388a9c20f6b4e2bffb131cf7402c1f \ --worker \ --label namespace=worker161 验证 Worker 节点 kubectl get nodes -A 结果正常 NAME STATUS ROLES AGE VERSION master151 Ready control-plane,etcd,master,worker 74m v1.30.4+k3s1 master152 Ready control-plane,etcd,master,worker 4h21m v1.30.4+k3s1 master153 Ready control-plane,etcd,master,worker 65m v1.30.4+k3s1 worker161 Ready worker 25m v1.30.4+k3s1 (部署/更新) Harbor hub CA 证书到所有的 Master 和 Worker (手动操作)导出 Harbor hub HTTPS 证书 增加自签名证书到 ca-certificates 所有的 Master 和 Worker 上执行（不太可行）echo | openssl s_client -showcerts -connect hub.yiqisoft.com:443 2>/dev/null | \ openssl x509 -outform PEM > /etc/ssl/certs/hub.yiqisoft.com.crt (自动部署)Harbor Hub CA 证书 新建一个 Secret 在业务集群中，把 Harbor 证书导入集群 kube-system kubectl create secret generic hub-ca --from-file=ca.crt=/hub.yiqisoft.com.crt -n kube-system 查看 kubectl get secret -A |grep hub-ca 结果 NAMESPACE NAME TYPE DATA AGE kube-system hub-ca kubernetes.io/tls 2 101m 新建一个 DaemonSet 新建一个部署文件: ca-distributor.yaml，用于分发 hub 的证书，非常重要，否则无法从私有 Registry 拉取镜像。 apiVersion: apps/v1 kind: DaemonSet metadata: name: ca-distributor namespace: kube-system spec: selector: matchLabels: name: ca-distributor template: metadata: labels: name: ca-distributor spec: containers: - name: ca-distributor image: busybox:latest command: - /bin/sh - -c - "cp /etc/ca-certificates/tls.crt /etc/ssl/certs/hub.yiqisoft.cn.crt && echo 'Certificate Updated' && sleep 3600" volumeMounts: - name: ca-cert mountPath: /etc/ca-certificates - name: ssl-certs mountPath: /etc/ssl/certs volumes: - name: ca-cert secret: secretName: hub-ca - name: ssl-certs hostPath: path: /etc/ssl/certs type: Directory restartPolicy: Always 部署 yaml kubectl apply -f ca-distributor.yaml 验证结果,确保所有的节点都执行成功。 kubectl get pods -A |grep ca-distributor kube-system ca-distributor-ckhgh 1/1 Running 0 100s kube-system ca-distributor-hqzwt 1/1 Running 0 101s kube-system ca-distributor-lj72n. 1/1 Running 0 101s kube-system ca-distributor-mpp7m 1/1 Running 0 101s 在 Worker 上拉取镜像 image k3s ctr i pull hub.yiqisoft.com/library/hello-world:latest 验证结果 hub.yiqisoft.cn/library/hello-world:latest: resolved |++++++++++++++++++++++++++++++++++++++| manifest-sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7: done |++++++++++++++++++++++++++++++++++++++| config-sha256:d2c94e258dcb3c5ac2798d32e1249e42ef01cba4841c2234249495f87264ac5a: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:c1ec31eb59444d78df06a974d155e597c894ab4cda84f08294145e845394988e: done |++++++++++++++++++++++++++++++++++++++| elapsed: 0.6 s total: 524.0 (871.0 B/s) unpacking linux/amd64 sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7... done: 54.873311ms 基于 K3S 部署业务应用(边缘计算框架) 以下所有部署都是通过 Helm Chart 工具进行，需要自行编写。 EdgeX Foundry Base 部署基本框架结果kubectl get pods -n worker161 NAME READY STATUS RESTARTS AGE edgex-core-command-tspw7 1/1 Running 0 5m41s edgex-core-common-config-bootstrapper-92q25 1/1 Running 0 5m25s edgex-core-consul-8lqxz 1/1 Running 0 6m4s edgex-core-data-nv26d 1/1 Running 0 6m23s edgex-core-metadata-l6c49 1/1 Running 0 5m3s edgex-device-onvif-camera-5tt4w 1/1 Running 0 3m26s edgex-mqtt-broker-j2jdr 1/1 Running 0 81s edgex-redis-rf4v7 1/1 Running 0 4m50s edgex-support-notifications-ws6l5 1/1 Running 0 4m28s edgex-support-scheduler-drx2p 1/1 Running 0 3m56s edgex-ui-v55vm 1/1 Running 0 3m45s 部署辅助应用：流媒体，推理服务器，NVR 部署辅助应用 业务 pod 之间有依赖关系，需等待其他 pod 起来才能工作，会不断的重试kubectl get pods -n worker161 |grep -v edgex 结果 NAME READY STATUS RESTARTS AGE media-server-wtjxk 1/1 Running 1 (5m29s ago) 13m model-server-9fpb2 1/1 Running 0 89s nvr-gqlst 1/1 Running 2 (5m29s ago) 11m 部署 OpenVINO AI 应用 部署基于 OpenVINO 推理框架的 AI 应用 podkubectl get pods -n worker161 |grep openvino 结果 NAME READY STATUS RESTARTS AGE edgex-device-openvino-face-recognition-bgch4 1/1 Running 0 5m43s edgex-device-openvino-object-detection-tbvwk 1/1 Running 0 7m21s 总结 通过本部署方案，可以管理基于 EdgeX 的边缘计算方案，EdgeX 具备边缘自治，即使脱离 K3S 集群，也照样能正常工作。 同时，此方案可以管理成千上万个边缘设备，之间互不干扰，适合大型项目部署。 云端配置相对较为复杂，但是边缘端就轻松很多，只需要手动注册到 K3S 集群，其他都可以通过 K8S API 进行部署和管理。 对比 K8S 而言，K3S 非常适合在边缘端工作，占用资源少，管理较为方便。 其实，最终结果就是：K3S 适合管理边缘容器分发。
下载 docker deb 文件 #!/bin/bash # Check if running as root if [ "$EUID" -ne 0 ]; then echo "Please run as root" exit fi # Check if correct number of arguments provided if [ "$#" -ne 2 ]; then echo "Usage: $0 <architecture> <distro>" exit fi ARCHITECTURE=$1 DISTRO=$2 # Current directory CURRENT_DIR=$(pwd) # URL of the HTML page with the list of Docker files HTML_URL="https://download.docker.com/linux/ubuntu/dists/${DISTRO}/pool/stable/${ARCHITECTURE}/" # Fetch the list of available packages PACKAGE_LIST=$(curl -s "$HTML_URL" | grep -oP '(?<=href=")[^"]+(?=")' | grep -oP '.*\.deb' | sort -Vr) # Function to download a package with a progress bar download_package() { PACKAGE=$1 FILENAME="${PACKAGE}" URL="${HTML_URL}${FILENAME}" echo "Downloading $FILENAME..." curl -L --progress-bar "$URL" -o "$CURRENT_DIR/$FILENAME" } # Download the latest package for each type TYPES=("containerd.io" "docker-ce" "docker-ce-cli" "docker-buildx-plugin" "docker-compose-plugin") for TYPE in "${TYPES[@]}"; do PACKAGE=$(echo "$PACKAGE_LIST" | grep "$TYPE" | head -n 1) if [ -n "$PACKAGE" ]; then download_package "$PACKAGE" fi done echo "Docker packages downloaded successfully!" 运行下载 bash docker-image.sh amd64 noble