亿琪软件-开源博客 - 产品
type: channel
link: https://www.yiqisoft.cn/blogs/category/Product/
items: Dockerfile 编写经验：优化大小与效率
items: 使用 OpenVINO Model Zoo 下载和转换模型
items: 基于 C 语言的线程安全的图像缓冲队列
items: Linux运维笔记：修复 ThinLinc 会话数据库损坏问题
items: C 语言应用开发中的注意事项
items: EdgeX Foundry 开发设备服务的几个关键点
items: 分享-GCC 手动编译问题 之 链接顺序
items: 分享-如何在 K3S 集群上部署自定义 DNS，方便内部使用。
items: 分享- Nginx 代理后，CORS 跨域问题解决思路
items: 在 ubuntu22.04 中 安装 EdgeX 并运行 device-sdk-c sample

Dockerfile 编写经验：优化大小与效率
type: item
date: 2025-04-29T06:05:41+00:00
description: Dockerfile 编写经验：优化大小与效率 Dockerfile 通用的技巧总结 1. 使用多阶段构建 2. 最小化层数 3. 彻底清理 4. 选择合适的基镜像 5. 仅安装必要的依赖 6. 利用构建缓存 常见陷阱 总结 Dockerfile 编写经验：优化大小与效率 创建高效的 Docker 镜像对于加速构建、减少存储需求和提升部署性能至关重要。在实践中，通过不断改进 Dockerfile，可以总结出编写优化 Dockerfile 的一些关键技巧。 Dockerfile 通用的技巧总结 1. 使用多阶段构建 利用多阶段构建分离构建和运行时环境，仅将必要的产物（二进制文件、库、配置）复制到最终阶段，这通过排除构建工具和中间文件保持运行时镜像精简。 示例：仅从构建阶段复制编译后的二进制文件和特定资源 WORKDIR /device-openvino # Copy binaries and resources from the builder stage COPY --from=builder /device-openvino/device-openvino-object-classification-c /device-openvino/device-openvino-object-classification-c # Copy the CSDK library (custom dependency built by build_deps.sh) COPY --from=builder /opt/iotech/iot/1.5/lib /opt/iotech/iot/1.5/lib COPY --from=builder /device-openvino/deps/device-sdk-c/build/release/_CPack_Packages/Linux/TGZ/csdk-0.0.0 /device-openvino/csdk # copy device service configuration file COPY res/configuration.yaml /device-openvino/res/configuration.yaml COPY res/profiles /device-openvino/res/profiles COPY res/devices /device-openvino/res/devices 2. 最小化层数 将相关命令（例如，apt-get update && apt-get install ）合并到单个 RUN 指令中，以减少层数。每个 RUN 都会创建一个新层，增加镜像大小。 使用&&和\保持可读性，同时串联命令。 # Configure the image source and install runtime dependencies RUN sed -i 's/archive.ubuntu.com/mirror.nju.edu.cn/g' /etc/apt/sources.list && \ sed -i 's/security.ubuntu.com/mirror.nju.edu.cn/g' /etc/apt/sources.list && \ apt-get update && apt-get install -y curl gnupg && \ # Configure OpenVINO repository curl -fsSL https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | apt-key add - && \ echo "deb https://apt.repos.intel.com/openvino/2025 ubuntu22 main" | tee /etc/apt/sources.list.d/intel-openvino-2025.list && \ # Configure Intel GPU repository curl -fsSL https://repositories.intel.com/gpu/intel-graphics.key | \ gpg --yes --dearmor --output /usr/share/keyrings/intel-graphics.gpg && \ echo "deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy unified…

使用 OpenVINO Model Zoo 下载和转换模型
type: item
date: 2025-04-21T02:15:00+00:00
description: 使用 OpenVINO Model Zoo 下载和转换模型 前提条件 1. 安装 pip 及 OpenVINO 开发包 2. 安装 Protocol Buffers 3. 验证安装 4. 检查 Model Zoo 5. 下载并转换 AlexNet 模型 结论 使用 OpenVINO Model Zoo 下载和转换模型 OpenVINO 是由英特尔开发的一个开源工具包，用于优化和部署深度学习模型。它特别适合在英特尔硬件上运行推理，但也支持其他平台。OpenVINO 的一个关键功能是 Model Zoo，这是一个包含预训练深度学习模型的集合，涵盖了对象检测、分类、图像分割等多种任务。 笔者将详细介绍在 Linux 系统上使用 OpenVINO Model Zoo 的步骤。我们将涵盖安装必要工具、探索可用模型、下载并转换 AlexNet 模型的完整过程。这些步骤基于 OpenVINO 2023.3 版本，并使用命令行工具完成。 前提条件 在开始之前，请确保您的 Linux 系统满足以下要求： Python 3 和 pip：需要 Python 3 以及 pip 来管理 Python 包。 OpenVINO 开发工具：这些工具是与 Model Zoo 交互的必需品。 互联网连接：用于下载工具和模型。 以下是我们将使用的关键命令： 命令 描述 apt install python3-pip 安装 Python 3 的 pip 包管理器 pip install openvino-dev==2023.3 安装 OpenVINO 开发工具（2023.3 版本） pip install protobuf==3.20.1 安装与 OpenVINO 兼容的 Protocol Buffers pip list 列出已安装的 Python 包以验证安装 omz_downloader -h 显示 omz_downloader 工具的帮助信息 omz_downloader --print_all 列出 Model Zoo 中的所有可用模型 omz_downloader --name alexnet 下载 AlexNet 模型 omz_converter --name alexnet 将 AlexNet 模型转换为 OpenVINO IR 格式 1. 安装 pip 及 OpenVINO 开发包 如果您的系统尚未安装 Python 3 的 pip，可以通过以下命令安装： apt install python3-pip 此命令确保可以安装和管理 Python 包。OpenVINO 开发包包含与 Model Zoo 交互所需的工具，例如 omz_downloader 和 omz_converter。安装 2023.3 版本的开发包： pip install openvino-dev==2023.3 确保版本号与您使用的 OpenVINO 版本匹配，以避免兼容性问题。 2. 安装 Protocol Buffers 某些模型可能需要 Protocol Buffers（protobuf）来处理模型文件，例如转换格式等。安装与 OpenVINO 2023.3 兼容的版本： pip install protobuf==3.20.1 此步骤确保您可以处理需要 protobuf 的模型。 3. 验证安装 为了确认所有工具都已正确安装，运行以下命令列出已安装的 Python 包： pip list 在输出中查找以下包，如果这些包出现在列表中，说明安装成功： 包名 版本 openvino-dev 2023.3 protobuf 3.20.1 4. 检查 Model Zoo OpenVINO Model Zoo 包含超过 200 个预训练模型，涵盖了对象检测、分类、图像分割、手写识别、文本到语音、姿势估计等多种任务。这些模型分为两类： 英特尔模型：已转换为 OpenVINO 格式。 公共模型：需要使用 OpenVINO 模型转换 API 进行转换。 要了解 omz_downloader 工具的用法，运行： omz_downloader -h usage: omz_downloader [-h] [--name PAT[,PAT...]] [--list FILE.LST] [--all] [--print_all] [--precisions PREC[,PREC...]] [-o DIR] [--cache_dir DIR] [--num_attempts N] [--progress_format {text,json}] [-j N] options: -h, --help show this help message and exit --name PAT[,PAT...] d…

基于 C 语言的线程安全的图像缓冲队列
type: item
date: 2025-04-20T08:14:30+00:00
description: 线程安全的图像缓冲队列 1. 介绍 2. 关键代码 2.1 数据结构 2.2 入队出队核心函数 3. 内存管理注意事项 3.1 c_mat_t 和 mat_data 的生命周期 3.2 队列销毁时的内存清理： 4. 总结 线程安全的图像缓冲队列 在这篇博客中，笔者将探讨一个最近实现的基于 C 语言的线程安全图像缓冲循环队列，重点分析其内存管理的细节和注意事项。该队列用于在图像处理后入队，供后续 AI 推理时出队使用，特别适用于实时视频处理或 AI 推理流水线。以下笔者将聚焦内存管理的实现细节、潜在风险及优化建议。 1. 介绍 该图像缓冲队列是一个固定大小的线程安全循环队列，基于 OpenCV 的 Mat 结构存储图像数据，采用生产者-消费者模型。内存管理是笔者重点考虑的细节，需要确保队列在高负载场景下的稳定性和高效性： 生产者：预处理图像（如调整大小、归一化）并入队。 消费者：出队图像并送入 AI 模型进行推理。 队列的关键特性包括： 使用 POSIX 线程（ pthread ）的互斥锁和条件变量实现线程安全。 通过循环缓冲区优化内存使用。 支持图像元数据（名称、路径）和 OpenCV 图像数据的管理。 2. 关键代码 2.1 数据结构 c_mat_t：OpenCV Mat 的封装，存储图像数据及其元数据。 typedef struct c_mat { unsigned char* mat_data; int mat_data_size; int mat_width; int mat_height; int mat_channels; int mat_type; } c_mat_t; 内存管理要点：mat_data 是一个指针，指向动态分配的实际图像数据。调用者需负责分配和释放内存，队列本身不管理 mat_data 的生命周期。 ImageInfo 是存储图像元数据和 c_mat_t 指针; ImageQueue 是循环队列结构，包含 ImageInfo 数组和同步原语: typedef struct { char name[MAX_NAME_LEN]; char path[MAX_NAME_LEN]; c_mat_t *img; } ImageInfo; typedef struct { ImageInfo items[MAX_QUEUE_SIZE]; int front; int rear; int size; pthread_mutex_t mutex; pthread_cond_t not_full; pthread_cond_t not_empty; } ImageQueue; 内存管理要点：items 是一个固定大小的数组（MAX_QUEUE_SIZE = 10），在栈或全局内存中分配，无需动态分配。mutex 和条件变量由 POSIX 线程库管理。 2.2 入队出队核心函数 从该队列设计上讲：调用者手动管理每一张图片的内存创建与释放，因此初始化队列和销毁队列的设计比较简单且常规，不再赘述。 入队图像： int enqueue(ImageQueue *queue, const char *name, const char *path, const c_mat_t *img) { pthread_mutex_lock(&queue->mutex); while (queue->size == MAX_QUEUE_SIZE) { pthread_cond_wait(&queue->not_full, &queue->mutex); } queue->rear = (queue->rear + 1) % MAX_QUEUE_SIZE; strncpy(queue->items[queue->rear].name, name, MAX_NAME_LEN - 1); strncpy(queue->items[queue->rear].path, path, MAX_NAME_LEN - 1); queue->items[queue->rear].img = img; queue->size++; pthread_cond_signal(&queue->not_empty); pthread_mutex_unlock(&queue->mutex); return 0; } 内存管理： name 和 path 使用 strncpy 复制到固定大小的字符数组，MAX_NAME_LEN - 1 确保留空间给终止符，避免缓冲区溢出。 img 指针直接赋值，队列不负责分配或释放 c_mat_t 或其 mat_data，该操作由创建实际参数时负责。 注意：笔者调用前已经确保 img 和 mat_data 在入队期间保持有效，且在队列使用期…

Linux运维笔记：修复 ThinLinc 会话数据库损坏问题
type: item
date: 2025-04-14T15:53:00+00:00
description: 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 ThinLi…

C 语言应用开发中的注意事项
type: item
date: 2025-04-12T15:55:44+00:00
description: C 语言应用开发中的注意事项 1.全局变量 2.源代码文件夹中 .c 模块文件的划分 3.多线程编程注意的问题 线程安全和互斥锁 线程间通信 4.深浅拷贝问题 总结 C 语言应用开发中的注意事项 C 语言作为一门经典的编程语言，广泛应用于系统编程、嵌入式开发和高性能计算等领域。然而，在进行 C 语言应用开发时，开发者需要关注一系列问题，以确保代码的正确性、性能和可维护性。 笔者在基于 C 语言开发 EdgeX Foundry 设备服务实践之中，遇到了一些问题，如全局变量的使用、.c 模块文件的划分、多线程编程注意事项、深浅拷贝问题以及编译链接时的多重定义问题。本文将对其展开讨论，并提供具体的代码示例和实用建议。 1.全局变量 全局变量是在函数外部定义的变量，其作用域从定义处开始到文件结束。它们在程序整个生命周期内存在，通常不会被轻易时放掉。可被程序中的任何函数访问。它能够方便多个函数共享数据，并减少函数参数传递的开销，但同时也不利于模块化编程和代码重用，增加调试难度。 以下展示的 global_pp 全局变量，在 EdgeX Foundry 设备服务中用于保存设备协议的详细信息，为部分关键代码： #include "device_openvino.h" #include "circular_buffer.h" /* protocols */ typedef struct { char *input_model; char *input_image_path; /* Uri */ char *device_name; /* CPU, GPU[0,1,...], NPU */ char *Score; } protocol_properties; protocol_properties *global_pp; // malloc a protocol_properties void func() { if (!global_pp) { iot_log_debug(driver->lc, "malloc a global_pp (protocol_properties)"); global_pp = (protocol_properties *)malloc(sizeof(protocol_properties)); global_pp->input_model = NULL; global_pp->input_image_path = NULL; global_pp->device_name = NULL; global_pp->Score = NULL; } } int main() { func(); return 0; } 注意事项： 尽量减少全局变量的使用，优先考虑局部变量或函数参数。 使用有意义的命名（如 g_config），避免冲突。 在多线程环境中，使用锁机制确保线程安全。 2.源代码文件夹中 .c 模块文件的划分 模块化编程将程序分解为独立的模块，每个模块负责特定功能。这种方法能提高代码的可读性、可维护性和可重用性，同时便于团队协作开发。划分 .c 文件的指导思想有以下几点： 每个 .c 文件应包含一组相关功能的实现，例如字符串处理或网络通信。 避免单个 .c 文件过大，建议控制在 1000 行以内。 使用头文件 (.h) 声明对外接口，.c 文件实现具体逻辑。 头文件用于声明函数、宏、类型和全局变量，供其他模块使用。 以下是 EdgeX Foundry 设备服务中环形 buffer 缓冲区的模块设计： //circular_buffer.h #pragma once #include <stddef.h> // 包含 size_t 的定义 // Circular buffer structure struct circular_buffer { struct infer_result* buffer; // storing data size_t capacity; // Maximum buffer capacity size_t size; // The current number of stored elements size_t head; // Head index (write location) size_t tail; // Tail index (read position) }; struct circular_buffer* create_circular_buffer(size_t capacity); ... //circular_buffer.c #include "circular_buffer.h" #include <stdlib.h> #include "edgex_comm…

EdgeX Foundry 开发设备服务的几个关键点
type: item
date: 2025-04-10T07:45:00+00:00
description: EdgeX Foundry 开发设备服务的几个关键点 1.设备服务工作流 2.设备服务示例模板 2.1 SDK 相关工作流 2.2 template.c 的细节 3.删除设备的处理逻辑 4.增加设备 4.1 增加设备的方式 4.2 增加设备的处理逻辑 总结 EdgeX Foundry 开发设备服务的几个关键点 EdgeX Foundry 设备服务是与传感器/设备或物联网对象 (“物”) 交互的边缘连接器，它需要与各种设备交互，将数据传输到边缘计算系统，并根据需求执行控制命令。EdgeX Foundry 通过其模块化设计和丰富的工具集，使得设备服务的开发变得更加简单高效。我们还可以使用设备服务 SDK 来创建您自己的 EdgeX 设备服务，本文重点介绍使用 C-SDK 开发设备服务源代码的几个必须明确的关键点 。 1.设备服务工作流 以 设备服务 template.c 为例，作为一个虚拟设备服务，它可以将数据发送到核心数据微服务，用户可以通过命令和控制微服务发送命令并获得响应，虚拟设备服务的这些功能在执行功能或性能测试时不需要任何实际设备就可以使用。 注意点： 虚拟设备服务对核心数据和元数据微服务具有依赖性，因为初始化过程需要检查或注册配置文件，设备和值描述符，因此，核心数据和元数据服务必须在设备服务初始化之前完全启动。 服务启动时，服务会通过从 YAML 文件加载“值描述符”,“设备服务”,“设备描述”来初始化，并登记在元数据微服务中，YAML文件默认存在，也可以自行创建。虚拟设备服务为元数据微服务提供回调API来管理设备实例。 2.设备服务示例模板 2.1 SDK 相关工作流 EdgeX Foundry 的 C-SDK 提供了一个设备服务模板（位于 examples/template.c 强烈推荐先看一遍代码），它提供了一套规范化的开发示例，包括自定义回调函数、设备服务启动关闭、内存开辟释放等，是我们初次接触设备服务开发的良好开端。设备服务工作流在上一节有了一个大致的介绍，结合官方的指导文档 servicewritersguide.md (强烈推荐先阅读一遍)，这里进一步描述设备服务内部 SDK 相关的工作流，展示如下： 从根本上说，设备服务由多个回调组成，上图中的 SDK 内部工作流均在一系列回调函数中作处理。一个设备服务只有一个设备服务实例结构体指针 Implementation 每次调用回调时，该指针都会被传递，如初始化回调函数的第一个参数 (template_driver *) impl。 /* --- template.c ---- */ /* Initialize performs protocol-specific initialization for the device * service. */ static bool template_init ( void *impl, struct iot_logger_t *lc, const iot_data_t *config ) { template_driver *driver = (template_driver *) impl; iot_log_debug (lc, "Template Init."); return true; } 2.2 template.c 的细节 一个设备服务必须要自定义好的回调函数如下：每一个回调函数的实现正如上面提到的 template_init 那样。 /* Device Callbacks */ devsdk_callbacks *templateImpls = devsdk_callbacks_init ( template_init, template_get_handler, template_put_handler, template_stop, template_create_addr, template_free_addr, template_create_resource_attr, template_free_resource_attr ); 同时设备服务 SDK 还提供了一些非必要的回调函数，以便于开发者实现更灵活的需求，在 servicewritersguide.md 的 Optional handlers 部分有提到。举一个例子，一个设备服务实例可以实现 device_added、device_updated 和 device_removed 处理程序，然后它将收到服务管理的设备范围的更改通知。如果需要特殊的初始化/关闭操作来优化设备的使用，这可能会有所帮助。只需要将该函数实现，并注册进设备服务的回调初始化函数即可： //device_added 的具体实现 static void device_a…

分享-GCC 手动编译问题 之 链接顺序
type: item
date: 2025-03-23T11:44:40+00:00
description: GCC 链接顺序问题总结 1.问题描述 2.根本原因 3.解决方法 4.注意事项 5.总结 GCC 链接顺序问题总结 1.问题描述 在编译 C/C++ 程序时，使用 GCC 链接库时可能会遇到 undefined reference to ... 的错误，即使正确指定了库（例如 -lopenvino_c）。例如，命令： gcc -I/usr/include -L/usr/lib -lopenvino_c -o main main.c 可能会报错： /usr/bin/ld: main.c:(.text+0x2a): undefined reference to `ov_get_openvino_version' 原因是 GCC 的链接顺序不符合要求。 2.根本原因 GCC 的链接器行为：GCC 使用 ld 作为链接器，ld 在解析符号时是单向扫描的，从左到右处理输入文件和库。 符号解析规则： 链接器在遇到目标文件（如 main.c 编译生成的 .o 文件）时，会记录其中未定义的符号（例如 ov_get_openvino_version）。 只有在后续扫描到的库文件中找到这些符号的定义，链接器才会解析成功。 如果库（-lXXX）出现在源文件或目标文件之前，链接器在处理库时还不知道需要哪些符号，因此不会从中提取定义，导致符号未解析。 正确的链接顺序 规则：GCC 要求源文件或目标文件在前，依赖库在后。 正确示例： gcc -I/usr/include/openvino -L/usr/lib -o main main.c -lopenvino_c ​ - main.c 在前：链接器先处理 main.c，记录未定义的符号。 ​ - -lopenvino_c 在后：链接器随后扫描 libopenvino_c.so，找到符号定义并完成链接。 3.解决方法 调整命令顺序： 始终将源文件（main.c）或目标文件（main.o）放在依赖库（-lXXX）之前。 标准格式：gcc [编译选项] 源文件 -l库名 [链接选项]。 验证符号： 使用 nm -D /path/to/lib.so | grep 函数名 检查库中是否包含所需符号，确保问题确实出在顺序而非库本身。 完整示例： gcc -I/usr/include/openvino -L/usr/lib -o main main.c -lopenvino_c 4.注意事项 多文件情况：如果有多个源文件或目标文件，确保它们都在库之前，例如： gcc -o main file1.c file2.c -lopenvino_c 库之间的依赖：如果库之间有依赖关系，也需要按从被依赖到依赖的顺序排列，例如： gcc -o main main.c -lopenvino_c -lopenvino CMake 自动处理：在使用 CMake 时，链接顺序通常由 target_link_libraries 自动管理，无需手动调整。 5.总结 GCC 的链接顺序要求是“源文件在前，依赖库在后”，这是由链接器 ld 的单向扫描机制决定的。遵循这个规则可以避免 undefined reference 错误，确保符号正确解析。

分享-如何在 K3S 集群上部署自定义 DNS，方便内部使用。
type: item
date: 2025-03-17T06:41:31+00:00
description: 自定义 DNS 配置 部署自定义 DNS 服务 暴露 DNS 服务给集群外部使用 测试 自定义 DNS 配置 自定义一些解析，custom-dns-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: custom-dns-config namespace: default data: Corefile: | .:53 { errors health # 自定义解析规则 rewrite name regex (.*)\.t\.yiqisoft\.cn t.yiqisoft.cn hosts { 192.168.1.10 example.com # 自定义域名解析 192.168.1.11 test.example.com # 更多自定义解析 } log # 可选：记录查询日志 cache 30 # 可选：缓存响应 reload } 部署自定义 DNS 服务 使用 K3S 内置的 CoreDNS 进行部署，custom-dns-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: custom-dns namespace: default spec: replicas: 1 # 可根据需要增加副本 selector: matchLabels: app: custom-dns template: metadata: labels: app: custom-dns spec: containers: - name: coredns image: rancher/mirrored-coredns-coredns:1.10.1 args: [ "-conf", "/etc/coredns/Corefile" ] volumeMounts: - name: config-volume mountPath: "/etc/coredns" readOnly: true ports: - containerPort: 53 name: dns protocol: UDP - containerPort: 53 name: dns-tcp protocol: TCP volumes: - name: config-volume configMap: name: custom-dns-config items: - key: Corefile path: Corefile 暴露 DNS 服务给集群外部使用 使用 LoadBalancer 暴露给外部， custom-dns-service.yaml apiVersion: v1 kind: Service metadata: name: custom-dns namespace: default spec: selector: app: custom-dns ports: - name: dns port: 53 targetPort: 53 protocol: UDP - name: dns-tcp port: 53 targetPort: 53 protocol: TCP type: LoadBalancer 测试 假设集群两台服务器：192.168.123.135/136 nslookup > server 192.168.123.135 Default server: 192.168.123.135 Address: 192.168.123.135#53 > example.com Server: 192.168.123.135 Address: 192.168.123.135#53 Name: example.com Address: 192.168.1.10 > > server 192.168.123.136 Default server: 192.168.123.136 Address: 192.168.123.136#53 > test.example.com Server: 192.168.123.136 Address: 192.168.123.136#53 Name: test.example.com Address: 192.168.1.11 >

分享- Nginx 代理后，CORS 跨域问题解决思路
type: item
date: 2025-03-16T05:43:00+00:00
description: 前提 浏览器提示 CORS 问题 设置相关 主网站 被动网站 前提 当我们访问网站：demo.a.com 时，会顺便访问 *.t.a.com 时，会造成 CORS 问题。 浏览器提示 CORS 问题 不用多说，CORS 问题一直存在，现在必须要要解决掉。 设置相关 假设，两个网站都是使用 Nginx 作为反向代理。 主网站 server { root /var/www/html/demo.a.com; server_name: demo.a.com; if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization'; add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } # CORS 配置 add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always; ... } 被动网站 server { root /var/www/html/t.a.com; server_name .t.a.com; location / { # 处理预检请求 if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Authorization, DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range'; add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } # CORS 配置 add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Authorization, DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range' always; add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range' always; proxy_pass http://localhost:8888; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header …

在 ubuntu22.04 中 安装 EdgeX 并运行 device-sdk-c sample
type: item
date: 2025-03-13T09:15:00+00:00
description: 在 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: 4c94a56999e10efcf48c5b8e3f6a…