下一代提示词工程语言POML简明教程

传统的提示词工程通常涉及编写自由文本,随着应用的发展,提示词文本会变得越来越复杂。 从而引出:提示词难以维护、难以进行版本控制、在不同场景下难以重用,几乎不可能进行系统化测试等一系列问题。 如何解决这些问题呢? Microsoft给出了一个工程化的答案:POML! 文章和 Colab 配合,学习效果更佳: https://colab.research.google.com/drive/1RrZyqB16XMvsFBjir90m-NCXE35kWFdy?usp=sharing 下一代提示词工程语言POML简明教程 一、简介 POML通过引入一种结构化方法,使用类似于 HTML 的格式编写提示词内容; 用户无需编写纯文本提示词,而是可以使用 <role>、<task> 和 <example> 等语义组件来组织提示词意图,从而带来更好的 LLM 性能和更便捷的提示词维护。 同时,POML具有类似CSS的样式系统,将内容与定义表示分离; (一)核心架构 POML采用三层架构运行,分离关注点并支持灵活的提示词开发: 该架构通过几个阶段处理POML文件: 解析:将类似XML的语法转换为结构化的中间表示; 处理:应用样式表、解析模板并集成外部数据; 生成:以各种格式生成最终的优化提示词; 这种分离使开发者能够在不改变核心逻辑的情况下修改表示样式,无缝集成外部数据源,并在项目中保持一致的提示词结构。 (二)主要特性 1、结构化标记系统 POML使用类似HTML的语法和语义组件,使提示词更具可读性和可维护性。 主要组件包括: <role>:定义LLM应采用的角色或身份; <task>:指定LLM需要完成的任务; <example>:提供少样本学习示例; <output-format>:控制预期的响应格式; <hint>:提供额外的上下文或约束; 例如: <poml> <role>You are a patient teacher explaining concepts to a 10-year-old.</role> <task>Explain the concept of photosynthesis using the provided image as a reference.</task> <img src="photosynthesis.png" alt="Diagram of photosynthesis" /> <output-format> Keep the explanation simple, engaging, and under 100 words. Start with "Hey there, future scientist!". </output-format></poml> 2、外部数据集成 POML通过专用组件来集成外部数据: <document>:嵌入文本文件、PDF或Word文档; <table>:集成电子表格或CSV文件中的结构化数据 <img>:包含带有替代文本的图像,用于支持视觉的模型 <audio>:处理多模态应用的音频文件; 例如: <hint captionStyle="header" caption="Background Knowledge"> <Document src="assets/tom_and_jerry.docx"/></hint><example> <input> <img src="assets/tom_cat.jpg" alt="The image contains the Tom cat character." syntax="multimedia" /> </input> <output> <Document src="assets/tom_introduction.txt"/> </output></example> 3、解耦的表示样式 POML具有类似CSS的样式系统,将内容与表示分离。 例如: <stylesheet> role { verbosity: concise; format: markdown; } task { emphasis: strong; }</stylesheet> 这允许开发者修改详细程度、输出格式和强调等样式方面,而无需改变核心提示词逻辑,显著降低调整提示词时格式漂移的风险。 4、模板引擎 POML包含强大的模板引擎,用于动态提示词生成: 变量:{ { variable_name } } 循环:<for each="item in items">...</for> 条件:<if condition="variable > 0">...</if> 定义:<let name="variable" value="expression" /> 这支持创建数据驱动的提示词,能够适应不同的上下文和输入。 (三)开发生态 POML提供全面的开发工具包,提高生产力: 1、VSCode扩展 Visual Studio Code扩展提供: 语法高亮和语言支持 上下文感知的自动补全 实时预览功能 与LLM提供商的集成测试 错误诊断和验证 可重用组件的提示词库 2、多语言SDK POML为Python和TypeScript/JavaScript提供SDK: Python SDK: from poml import load, render# Load and render a POML fileprompt = load("example.poml")result = render(prompt, variables={"topic": "photosynthesis"}) TypeScript SDK: import { loadPoml, renderPoml } from 'pomljs';// Load and render a POML fileconst prompt = await loadPoml('example.poml');const result = await renderPoml(prompt, { topic: 'photosynthesis' }); 二、基本使用 (一)安装 Node.js (via npm): npm install pomljs Python (via pip): pip install poml (二)第一个案例 1、编写POML文件 编写一个名为 example.poml 的文件,内容如下: example.poml <poml> <role>You are a patient teacher(named {{teacher_name}}) explaining concepts to a 10-year-old.</role> <task>Explain the concept of photosynthesis using the provided image as a reference.</task> <input> <img src="photosynthesis.jpg" alt="Diagram of photosynthesis" syntax="multimedia"/> </input> <output-format> Keep the explanation simple, engaging, and under 100 words. Start with "Hey there, future scientist!". </output-format></poml> 示例定义了: LLM 的角色和任务,包含一张图片作为上下文,并指定了所需的输出格式。 同时,包含了一个变量 teacher_name; 编写完成后,如果你安装了 Visual Studio Code poml 插件,则可以进行预览: 2、解析并渲染POML 借助 POML 工具包,此提示词可以轻松渲染为灵活的格式,并可通过 LLM 进行测试。 例如在 Python 中: from poml import poml# Process a POML file# result = poml("example.poml")# Process with context variablesresult = poml("example.poml", context={"teacher_name": "Jasonkay"})print(f"Process with context variables: {result}")# Get OpenAI chat format(Within the higher version)# messages = poml("example.poml", format="openai_chat")# print(f"Get OpenAI chat format: {messages}") poml 函数接受以下参数: markup:POML 内容(字符串或文件路径) context:可选的模板注入数据 stylesheet:可选的样式自定义 format:输出格式(”dict”、”openai_chat”、”langchain”、”pydantic” 或 “raw”) 执行代码后,输出结果为: Process with context variables: [{'speaker': 'system', 'content': '# Role\n\nYou are a patient teacher(named Jasonkay) explaining concepts to a 10-year-old.\n\n# Task\n\nExplain the concept of photosynthesis using the provided image as a reference.'}, {'speaker': 'human', 'content': [{'type': 'image/webp', 'base64': 'UklGRg......', 'alt': 'Diagram of photosynthesis'}, '# Output Format\n\nKeep the explanation simple, engaging, and under 100 words. Start with "Hey there, future scientist!".']}] 可以看到,输出的内容将内容进行了渲染! 3、与LLM系统集成(Gemini) 最后,将我们的提示词和外部 LLM 系统相结合! 由于目前最新的 POML SDK 还不支持使用 format 参数来渲染 openai_chat 类型的 Prompt; 因此,这里使用 Gemini API 来发送图片! 使用下面的 poml 文件来渲染: example.poml <poml> <role>You are a patient teacher(named {{teacher_name}}) explaining concepts to a 10-year-old.</role> <task>Explain the concept of photosynthesis using the provided image as a reference.</task> <output-format> Keep the explanation simple, engaging, and under 100 words. Start with "Hey there, future scientist!". </output-format></poml> 首先安装 Gemini SDK: pip install -U google-genai 要运行下面的代码,你需要创建一个 Gemini 的 API Key: https://aistudio.google.com/app/apikey 随后,将下面的 YOUR_API_KEY 替换为你生成的 Key! from google import genaifrom poml import pomlfrom google.genai import typesGEMINI_API_KEY="YOUR_API_KEY"client = genai.Client(api_key=GEMINI_API_KEY)# Read the picturewith open('photosynthesis.jpg', 'rb') as f: image_bytes = f.read()# Render the POML fileresult = poml("example.poml", context={"teacher_name": "Jasonkay"}, chat=False)# print(f"Process with context variables: {result}")response = client.models.generate_content( model="gemini-2.5-flash", contents=[ result, types.Part.from_bytes( data=image_bytes, mime_type='image/jpeg', ), ])print(response.text) 最后,执行即可输出内容: Look at our amazing plant friend! Just like you need food, plants need to eat too! This image shows how they do it, a process called **photosynthesis**. Plants use sunlight from the sun, and "drink" water through their roots. They also breathe in a gas called CO2 (carbon dioxide) from the air, shown by the blue arrow going in. Using these, they make their own sugary food to grow! As a super cool bonus, they release O2 (oxygen) for us to breathe, shown by the blue arrow going out. Amazing, right? (三)使用样式 现在,让我们为上面的例子增加相关的样式,来优化的 Prompt 配置! example-2.poml <poml> <role>You are a patient teacher(named {{teacher_name}}) explaining concepts to a 10-year-old.</role> <task>Explain the concept of photosynthesis using the provided image as a reference.</task> <output-format> <list listStyle="dash"> <item className="explanation">Keep the explanation simple, engaging, and under 100 words.</item> <item className="greeting"> Start with "Hey there, future scientist!". </item> </list> </output-format></poml><stylesheet> { ".explanation": { "syntax": "json" }, "list" : { "whiteSpace": "trim" } }</stylesheet> 渲染结果如下: # RoleYou are a patient teacher(named ) explaining concepts to a 10-year-old.# TaskExplain the concept of photosynthesis using the provided image as a reference.# Output Format```json"Keep the explanation simple, engaging, and under 100 words."```- Start with "Hey there, future scientist!". 更多内容可以参考官方文档: https://microsoft.github.io/poml/latest/language/standalone/#stylesheet 三、深入学习 在完成了基础学习之后,可以继续阅读下面的内容: 更多官方的案例 官方文档 POML语法结构 POML中间表示 API参考 外部系统集成 自定义组件 进行更加深度的学习! 附录 文章和 Colab 配合,学习效果更佳: https://colab.research.google.com/drive/1RrZyqB16XMvsFBjir90m-NCXE35kWFdy?usp=sharing 参考文章: https://github.com/microsoft/poml https://zread.ai/microsoft/poml https://microsoft.github.io/poml/latest/ https://ai.google.dev/gemini-api/docs/image-understanding

2025/8/20
articleCard.readMore

开了一个新的专门学习日语的博客

之前一直都在这里发一些学习日语相关的内容。但是感觉这些可以单独开一个新的网站来总结; 最近比较有时间,就开了一个新的坑; 博客主题用的是:hexo-theme-anzhiyu,真的很酷! 也是花了半个小时,使用hexo,从零搭建了一个博客; 之前没有好好总结,这里就又简单记录了一下搭建的过程,如果你还没有博客,可以跟着我一步一步来搭建一个GithubPages博客! 新博客地址: https://jasonkayzk.github.io/jp/ 源代码: https://github.com/JasonkayZK/jp https://github.com/anzhiyu-c/hexo-theme-anzhiyu 开了一个新的专门学习日语的博客 一、安装Node.js&Hexo 可以在官网下载Node: https://nodejs.org/zh-cn/download 我这里使用的是 fnm 作为版本管理工具; 配置国内源: # 国内 淘宝 镜像源 npm config set registry https://registry.npmmirror.com/ 参考: npm、yarn、pnpm 设置最新国内镜像源(附官方镜像源和最新阿里源),以及 nrm 的使用教程【2025】 也可以把 yarn 安装了: npm install -g yarn# 国内 淘宝 镜像源yarn config set registry https://registry.npmmirror.com/ 随后下载 Hexo-cli 命令行工具: npm install hexo-cli -g 参考: https://hexo.io/zh-cn/ 二、初始化Hexo项目 直接通过命令行初始化项目 jp: # hexo init jpINFO Cloning hexo-starter https://github.com/hexojs/hexo-starter.gitINFO Install dependencies......INFO Start blogging with Hexo! 安装依赖并测试: cd jpnpm ihexo s # 本地调试 此时访问: http://localhost:4000/ 你就能看到你的博客了! 参考文档: https://hexo.io/zh-cn/ 三、更换主题 默认的主题不太好看,可以在 github 上搜索 hexo 的相关主题(一般为 hexo- 开头); 以 hexo-theme-anzhiyu 为例,文档: https://docs.anheyu.com/ 首先在你项目的根目录将主题 clone 下来: git clone -b main https://github.com/anzhiyu-c/hexo-theme-anzhiyu.git themes/anzhiyu 随后打开 Hexo 根目录下的 config.yml, 找到以下配置项,把主题改为anzhiyu # Extensions## Plugins: https://hexo.io/plugins/## Themes: https://hexo.io/themes/theme: anzhiyu 然后安装 pug 和 stylus 渲染插件: npm install hexo-renderer-pug hexo-renderer-stylus --save 再次执行: hexo s 此时再次访问: http://localhost:4000/ 你就能看到你新的主题的博客了! 四、自定义配置 拉下来的新的主题,很多内容大概率都不符合你的预期(比如:title、分类等等); 此时你可以参考文档进行个性化的配置: https://docs.anheyu.com/page/front-matter.html 需要注意的是: 在根目录下存在 _config.yml、各自主题下也有这个文件,优先级为:根目录 > 各自主题配置! 五、撰写新的文章 通过: hexo new xxx 即可创建一篇新的博文! 实际上就是在 source/_posts/ 目录下创建了一个新的*.md文件而已! 可以增加 Post Front-matter 来对文章进行配置! 文章内容就按照 markdown 的格式去写就可以了! 六、发布到Github 本地调试无误后,修改根目录下的 _config.yml: # Deployment## Docs: https://hexo.io/docs/one-command-deploymentdeploy: type: 'git' repo: git@github.com:jasonkayzk/jp.git # 替换为你的仓库地址 branch: main # 部署分支 安装插件: npm install hexo-deployer-git --save 随后,在发布时,通常情况下: 首先,通过 hexo g 生成静态资源; 然后使用 hexo d 即可部署! 参考: https://hexo.io/docs/one-command-deployment 但是这种方式每次都要在本地生成静态资源,效率太低! 1、使用GitHub Actions自动部署 可以通过 Github Actions,每次 push 代码之后自动提交! 在 .github/workflows/ 目录下创建: deploy.yml name: Build & Deploy Blogon: workflow_dispatch: push: branches: - devjobs: build: runs-on: ubuntu-latest strategy: matrix: node_version: [18] steps: - name: Checkout source uses: actions/checkout@v3 with: ref: dev - name: Cache dependencies uses: actions/cache@v3 with: path: | node_modules public key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- - name: Use Node.js ${{ matrix.node_version }} uses: actions/setup-node@v3 with: version: ${{ matrix.node_version }} - name: Add SSH key uses: webfactory/ssh-agent@v0.7.0 with: ssh-private-key: ${{ secrets.hexo_deploy_pri }} - name: Setup hexo run: | git config --global user.email "jasonkayzk@gmail.com" git config --global user.name "jasonkayzk" npm install hexo-cli -g npm install - name: Hexo deploy env: GIT_SSH_COMMAND: ssh -o StrictHostKeyChecking=no run: | hexo clean hexo g hexo d 随后在仓库的 Secrets and variables 设置中,添加 Repository secrets: key为:HEXO_DEPLOY_PRI; 值为:你连接 Github 的私钥!(注意是私钥!) 提交到 dev 分支后即可自动部署! 2、使用新分支保存笔记 同时,生成的内容可能会覆盖原本的笔记(hexo d 会强行覆盖部署分支!): 我们只需要 checkout 到另外的一个分支去编写博客,然后部署到 main 分支即可! 例如: git checkout -b devgit push 附录 新博客地址: https://jasonkayzk.github.io/jp/ 源代码: https://github.com/JasonkayZK/jp https://github.com/anzhiyu-c/hexo-theme-anzhiyu

2025/7/30
articleCard.readMore

一、并行编程导论与CUDA入门

随着人工智能的发展,科学计算(尤其是矩阵/张量计算)越来越重要;因此,基于CUDA的张量编程也越来越重要。 在上一篇笔记中翻译了《An Even Easier Introduction to CUDA》,但是感觉作者写的不是很好; 这里重新写了一篇。同时,也作为CUDA和并行编程的开篇。 源代码: https://github.com/JasonkayZK/high-performance-computing-learn/blob/main/cuda/1_introduction_to_parallel_programming_and_cuda.ipynb 一、并行编程导论与CUDA入门 温馨提示:本文章配合 Colab 一同执行学习效果更佳: https://colab.research.google.com/github/JasonkayZK/high-performance-computing-learn/blob/main/cuda/1_introduction_to_parallel_programming_and_cuda.ipynb (一)、CUDA编程概述 1、什么是CUDA CUDA 是 NVIDIA 开发的并行计算平台和编程模型; 具有以下特点: C/C++ 语法; SIMT(Single Instruction Multiple Threads)模式:一个指令会被多个线程同时执行! 需要与CPU协作:CPU负责整理结果、处理逻辑; 自动调度:根据设定的执行参数,自动调度; 2、CUDA 运算硬件单元 (1)SM 单元 下面是一个 GPU 硬件单元: 每个核心中包含了多个 SM(Stream Multi-processor),任务在 SM 中处理; SM 中包含了: CUDA Core/SP:进行并行的加减法等计算; Tensor Core:张量计算 …… (2)CPU与GPU协作 CPU 与 GPU 协同工作的流程如下: 首先,习惯上将: CPU 所在端称为:Host 端,对应内存为 RAM; GPU 所在称为:Device 端,对应内存为 Global Memory(通常对应 GPU RAM,显存); 通常,Global Memory 在其范围和生命周期中是全局的! 也就是说,每个在thread block grid 中的 thread 都可以访问Global Memory,并且生命周期与程序的执行时间一样长! 更多内容: https://modal.com/gpu-glossary/device-software/global-memory CUDA 程序执行时主要分为以下几个步骤: CPU 准备(CPU Prepare):在主机端(Host ,包含 CPU 和 RAM 主存 ),CPU 负责初始化数据、设置计算参数等准备工作,为后续在 GPU 上的运算任务做铺垫,确定要处理的数据和运算逻辑; CPU 传输数据至 GPU(CPU Transfers Data to GPU):通过总线(Bus),CPU 把主存(RAM)中准备好的数据传输到 GPU 端的全局内存(Global Memory ,GM),因为 GPU 运算需要的数据要先存放到其可访问的内存空间; 从 GM 读数据(Read Data from GM):GPU(如 NVIDIA A100 )从自身的全局内存中读取需要参与运算的数据,将数据加载到运算单元可处理的位置; 运算(Compute):NVIDIA A100 等 GPU 设备利用自身的并行运算核心,对读取的数据执行 CUDA 核函数定义的运算操作,发挥 GPU 并行计算优势,高效处理大规模数据计算任务; 写回 GM(Write Back to GM):运算完成后,GPU 将运算结果写回到全局内存中,暂存运算产出的数据; GPU 传输数据至 CPU(GPU Transfers Data to CPU):再次通过总线,GPU 把全局内存中存储的运算结果传输回主机端的主存(RAM),供 CPU 进一步处理(如数据展示、后续其他主机端逻辑运算等 ),完成一次 CUDA 编程的计算流程; CUDA 这种流程实现了 CPU 与 GPU 协同,让 GPU 承担并行计算 heavy - lifting ,提升计算密集型任务效率,广泛用于深度学习训练推理、科学计算等场景! (二)、CUDA运算示例:加法 1、CPU加法 add_cpu.cpp #include <cmath>#include <iostream>#include <vector>// Step 2: Define add functionvoid add_cpu(std::vector<float> &c, const std::vector<float> &a, const std::vector<float> &b) { // CPU use loop to calculate for (size_t i = 0; i < a.size(); i++) { c[i] = a[i] + b[i]; }}int main() { // Step 1: Prepare & initialize data constexpr size_t N = 1 << 20; // ~1M elements // Initialize data const std::vector<float> a(N, 1); const std::vector<float> b(N, 2); std::vector<float> c(N, 0); // Step 3: Call the cpu addition function add_cpu(c, a, b); // Step 4: Check for errors (all values should be 3.0f) float maxError = 0.0f; for (int i = 0; i < N; i++) { maxError = fmax(maxError, fabs(c[i] - 3.0f)); } std::cout << "Max error: " << maxError << std::endl;} 主要分为以下几个步骤: 准备和初始化数据; 定义加法函数 靠循环来进行所有的元素加法 调用函数 验证结果 2、修改为GPU加法(重点!) 分为以下几个步骤: 修改文件名为 *.cu:例如add_cuda.cu(表示 CUDA 程序) 准备和初始化数据(CPU):使用 vector 等进行 Host 端的 RAM 分配; 数据传输到 GPU:使用 cudaMalloc 分配显存、使用 cudaMemcpy 复制数据等; GPU 从 GM 中读取并计算后写回(调用核(kernel)函数计算): 修改核函数声明: 修改调用方式: 将 GPU 数据传输回 CPU: 验证结果 下面分别来看; (1)修改文件名为 *.cu CUDA 规定其文件扩展名为 *.cu,语法和 C++ 类似! (2)准备和初始化数据(CPU) 这步比较简单: add_cuda.cu // Step 1: Prepare & initialize dataconstexpr size_t N = 1 << 20; // ~1M elementsconstexpr size_t size_bytes = sizeof(float) * N;// Initialize dataconst std::vector<float> h_a(N, 1);const std::vector<float> h_b(N, 2);std::vector<float> h_c(N, 0); 此时在 Host 端的 RAM 分配内存; (3)数据传输到 GPU 数据传输到 GPU 使用 CUDA 提供的函数: 使用 cudaMalloc 分配显存; 使用 cudaMemcpy 复制数据; add_cuda.cu float *d_a, *d_b, *d_c;CUDA_CHECK(cudaMalloc(&d_a, size_bytes));CUDA_CHECK(cudaMalloc(&d_b, size_bytes));CUDA_CHECK(cudaMalloc(&d_c, size_bytes));CUDA_CHECK(cudaMemcpy(d_a, h_a.data(), size_bytes, cudaMemcpyHostToDevice));CUDA_CHECK(cudaMemcpy(d_b, h_b.data(), size_bytes, cudaMemcpyHostToDevice));CUDA_CHECK(cudaMemcpy(d_c, h_c.data(), size_bytes, cudaMemcpyHostToDevice)); 这里使用了: CUDA_CHECK 宏进行校验; cudaMemcpyHostToDevice 指定数据流方向; CUDA_CHECK 宏定义如下: #define CUDA_CHECK(call) \{ \ cudaError_t err = call; \ if (err != cudaSuccess) { \ std::cerr << "CUDA Error at " << __FILE__ << ":" << __LINE__ \ << " - " << cudaGetErrorString(err) << std::endl; \ } \} (4:补)CUDA层级结构 i.线程层级结构 在 CPU 中,使用循环进行执行; 而在 GPU 中,使用的是 SIMT,即:一条命令会同时被多个线程执行! 此时需要指挥每个线程:组织结构和编号! 在 CUDA 中,包含: Grid: Block: Thread: 如下图: 其中:每一个 Grid 中包含多个已编号的 Block,而每一个 Block 中包含多个已编号的 Thread! 同时,每个 Block 中包含的线程数是一样的! 一共有:0~N-1个Thread(假设每个 Block 包含 N 个 Thread); ii.线程索引计算方法 在 CUDA 中: 每个线程都有 独一无二 的编号索引(idx); idx = BlockID * BlockSize + ThreadID; 如下图: (4)编写和调用核函数 相对于 CPU 中使用循环的方式执行,在 GPU 中主要使用的是:多线程并行; 步骤如下: 定义 block 的数量和大小来指挥线程、进行/并行计算; 定义 GPU 上的加法函数(核函数); 结合定义的信息调用 GPU 加法函数; 层级结构定义: // Set up kernel configurationdim3 block_dim(256);dim3 grid_dim((N + block_dim.x - 1) / block_dim.x); 核函数定义: template<typename T>__global__ void add_kernel(T *c, const T *a, const T *b, int n) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx < n) { c[idx] = a[idx] + b[idx]; }} 只能通过指针的方式传递! 因为像是 vector 等数据结构,都是在 Host 端定义的,并不能在 Global Memory 中分配! 核函数调用: // Call cuda add kerneladd_kernel<<<grid_dim, block_dim>>>(d_c, d_a, d_b, N); 其中: dim3:CUDA 表示线程层级结构的类型(包括:x、y、z 三个维度); <<<>>>:传递线程层级信息给核函数; 核函数(kernel):设备侧的入口函数; __global__:表示这是个核函数; blockldx:表示 block 的编号,第几个 block; blockDim:表示 block 的大小,一个 block 多少个线程; threadldx:表示 thread 的编号,表示第几个 thread; (5)将 GPU 数据传输回 CPU 同样,使用 cudaMemcpy: CUDA_CHECK(cudaMemcpy(h_c.data(), d_c, size_bytes, cudaMemcpyDeviceToHost)); (6)验证结果,释放内存 验证结果使用已经复制到 h_c 中的数据; 释放内存使用 cudaFree: add_cuda.cu float maxError = 0.0f;for (int i = 0; i < N; i++) { maxError = fmax(maxError, fabs(h_c[i] - 3.0f));}std::cout << "Max error: " << maxError << std::endl;if (d_a) { CUDA_CHECK(cudaFree(d_a));}if (d_b) { CUDA_CHECK(cudaFree(d_b));}if (d_c) { CUDA_CHECK(cudaFree(d_c));} 3、编译&运行CUDA程序 需要使用 NVCC(NIVIDEA CUDA Compiler) 的编译器来编译程序; NVCC 是 CUDA Toolkit 的一部分: https://developer.nvidia.com/cuda-toolkit (1)编译流程 如下图所示: 流程如下: 每个 cu:Host 代码与 Device 代码分离(部分在CPU执行、部分在GPU执行) 每个虚拟架构:Device 代码编译出 fatbin Host 端使用系统的 C++ 编译器(如 g++) 链接(device,host) 最终获得可使用 GPU 的可执行二进制文件 补:GPU虚拟架构 NVIDIA 不同年代生产的GPU可能有不同的架构,如下图所示: 以 A100 为例,A100 为 Ampere 架构;同时,各个架构间有区别; 因此提出:Compute Capability (CC) 类似版本,表示能支持的功能和指令集合 A100 (Ampere 架构)是:cc8.0 虽然 A100 举例,但从 CUDA 编程的角度目前各种架构没有本质区别! 正因为如此,所以说CUDA是一个编程平台 同时,在编译时也可以指定架构编译选项: -arch:指定虚拟架构,PTX生成目标。决定代码中可使用的CUDA 功能; -code:指定实际架构,生成针对特定 GPU 硬件的二进制机器码(cubin); (2)编译命令 通过: nvcc add_cuda.cu -o add_cuda 即可编译! 运行: ./add_cuda (三)、GPU性能测试 可以通过: nvidia-smi 观察 GPU 利用率! 1、并行加法性能对比 分别对比: CPU; <<<1,1>>>; <<<256,256>>>; GPU 满载; 代码如下: add_cuda_profiling.cu #include <cmath>#include <iostream>#include <vector>#define CUDA_CHECK(call) \{ \ cudaError_t err = call; \ if (err != cudaSuccess) { \ std::cerr << "CUDA Error at " << __FILE__ << ":" << __LINE__ \ << " - " << cudaGetErrorString(err) << std::endl; \ } \}// Step 3: Define add kerneltemplate<typename T>__global__ void add_kernel(T *c, const T *a, const T *b, const size_t n, const size_t step) { int idx = blockIdx.x * blockDim.x + threadIdx.x + step; if (idx < n) { c[idx] = a[idx] + b[idx]; }}template<typename T>void vector_add(T *c, const T *a, const T *b, size_t n, const dim3& grid_dim, const dim3& block_dim) { size_t step = grid_dim.x * block_dim.x; for (size_t i = 0; i < n; i += step) { add_kernel<<<grid_dim, block_dim>>>(c, a, b, n, i); }}int main() { // Step 1: Prepare & initialize data constexpr size_t N = 1 << 20; // ~1M elements constexpr size_t size_bytes = sizeof(float) * N; // Initialize data const std::vector<float> h_a(N, 1); const std::vector<float> h_b(N, 2); std::vector<float> h_c(N, 0); // Step 2: Allocate device memory & transfer to global memory float *d_a, *d_b, *d_c; CUDA_CHECK(cudaMalloc(&d_a, size_bytes)); CUDA_CHECK(cudaMalloc(&d_b, size_bytes)); CUDA_CHECK(cudaMalloc(&d_c, size_bytes)); CUDA_CHECK(cudaMemcpy(d_a, h_a.data(), size_bytes, cudaMemcpyHostToDevice)); CUDA_CHECK(cudaMemcpy(d_b, h_b.data(), size_bytes, cudaMemcpyHostToDevice)); CUDA_CHECK(cudaMemcpy(d_c, h_c.data(), size_bytes, cudaMemcpyHostToDevice)); // Step 4: Call the cpu addition function // Set up kernel configuration dim3 block_dim(1); dim3 grid_dim(1); // Call cuda add kernel vector_add(d_c, d_a, d_b, N, block_dim, grid_dim); // Step 5: Transfer data from global mem to host mem CUDA_CHECK(cudaMemcpy(h_c.data(), d_c, size_bytes, cudaMemcpyDeviceToHost)); // Step 6: Check for errors (all values should be 3.0f) float sumError = 0.0f; for (int i = 0; i < N; i++) { sumError += fabs(h_c[i] - 3.0f); } std::cout << "Sum error: " << sumError << std::endl; if (d_a) { CUDA_CHECK(cudaFree(d_a)); } if (d_b) { CUDA_CHECK(cudaFree(d_b)); } if (d_c) { CUDA_CHECK(cudaFree(d_c)); }} 可以修改其中的: dim3 block_dim(1); dim3 grid_dim(1); 不同情况下的性能如下: 可以使用 Nsight Systems(nsys,NVIDIA系统级性能分析工具)来分析; 启动 profiling: nsys profile -t cuda,nvtx,osrt -o add_cuda_profiling -f true ./add_cuda_profiling 解析并统计性能信息: nsys stats add_cuda_profiling.nsys-rep ** OS Runtime Summary (osrt_sum): Time (%) Total Time (ns) Num Calls Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Name -------- --------------- --------- ------------- ------------- ---------- ----------- ------------- ---------------------- 56.2 7,592,724,284 84 90,389,574.8 100,130,776.0 2,330 370,626,986 45,049,255.4 poll 42.4 5,736,493,727 26 220,634,374.1 189,702,756.5 41,077,614 752,975,386 124,762,585.8 sem_wait 1.2 164,252,099 543 302,490.1 13,509.0 529 111,402,991 4,818,716.4 ioctl 0.1 14,968,499 38 393,907.9 131,267.0 135 5,539,804 890,642.6 pthread_rwlock_wrlock ...... ** CUDA API Summary (cuda_api_sum): Time (%) Total Time (ns) Num Calls Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Name -------- --------------- --------- ------------ ----------- -------- ----------- ------------- ---------------------- 96.9 6,504,565,162 1,048,576 6,203.2 5,159.0 2,928 37,814,020 99,097.6 cudaLaunchKernel 3.0 203,141,797 3 67,713,932.3 103,908.0 73,162 202,964,727 117,130,625.1 cudaMalloc 0.1 4,017,591 4 1,004,397.8 1,012,632.0 941,545 1,050,782 45,652.8 cudaMemcpy 0.0 524,788 3 174,929.3 136,182.0 122,785 265,821 78,999.0 cudaFree 0.0 2,584 1 2,584.0 2,584.0 2,584 2,584 0.0 cuModuleGetLoadingMode...... 各个类型 API Summary 分析结果如下: 可以看到:<<<1,1>>> cudaLaunchKernel 占比非常高这是由于: 核函数调用有开销,在外面多次循环调用开销巨大! 因此,需要进行优化! 2、将循环放入核函数(Grid-strided loop) 由于在循环中频繁的调用核函数具有巨大的性能开销,因此可以将循环放入核函数中: template<typename T>__global__ void add_kernel_inner_loop(T *c, const T *a, const T *b, const size_t n, const size_t step) { int idx = blockIdx.x * blockDim.x + threadIdx.x; for (size_t i = idx; i < n; i += step) { if (i < n) { c[i] = a[i] + b[i]; } }}template<typename T>void vector_add(T *c, const T *a, const T *b, size_t n, const dim3& grid_dim, const dim3& block_dim) { size_t step = grid_dim.x * block_dim.x; add_kernel_inner_loop<<<grid_dim, block_dim>>>(c, a, b, n, step);} 分析后结果如下图: 同时使用 nsys 分析: ** CUDA API Summary (cuda_api_sum): Time (%) Total Time (ns) Num Calls Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Name -------- --------------- --------- ------------ ----------- -------- ----------- ------------- ---------------------- 55.4 204,935,456 3 68,311,818.7 104,741.0 79,097 204,751,618 118,160,333.0 cudaMalloc 44.4 164,057,041 4 41,014,260.3 1,000,521.5 926,775 161,129,223 80,076,651.2 cudaMemcpy 0.2 653,441 3 217,813.7 204,732.0 194,409 254,300 32,016.9 cudaFree 0.1 264,055 1 264,055.0 264,055.0 264,055 264,055 0.0 cudaLaunchKernel 0.0 2,429 1 2,429.0 2,429.0 2,429 2,429 0.0 cuModuleGetLoadingMode 可以看到 cudaLaunchKernel 的确少了很多! 这说明: 核函数的发射数量减少,因此总体执行时间降低! 3、CUDA并行加法性能评估(加速比) 指标: 加速比 = Tcpu / Tgpu 其中: T_cpu 是任务在 CPU 上的执行时间; T_gpu 是任务在 GPU 上的执行时间; 理想加速比与实际加速比 理想加速比:当任务完全并行化且没有任何开销时,加速比等于处理器核心数之比。例如,一个具有 1000 个 CUDA 核心的 GPU 理论上可以实现 1000 倍的加速(相对于单核 CPU)。 实际加速比:由于以下因素,实际加速比通常远低于理想值: 任务中存在无法并行化的串行部分 数据在 CPU 和 GPU 之间的传输开销 线程同步和内存访问延迟 算法在 GPU 架构上的效率低下 为什么<<<1,1>>> 比 CPU慢? 这是由于,单个 GPU 的核心实际上要比 CPU 的能力要弱! 实际上,GPU 是由于干活的人多,所以快! 4、CUDA并行加法性能评估(总耗时) 实际上观察 nsys 的输出结果: ** CUDA GPU Kernel Summary (cuda_gpu_kern_sum): Time (%) Total Time (ns) Instances Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Name -------- --------------- --------- ------------- ------------- ----------- ----------- ----------- --------------------------------------------------------------------------------------------- 100.0 160,054,287 1 160,054,287.0 160,054,287.0 160,054,287 160,054,287 0.0 void add_kernel_inner_loop<float>(T1 *, const T1 *, const T1 *, unsigned long, unsigned long)Processing [add_cuda_profiling2.sqlite] with [/usr/local/cuda-12.1/nsight-systems-2023.1.2/host-linux-x64/reports/cuda_gpu_mem_time_sum.py]... ** CUDA GPU MemOps Summary (by Time) (cuda_gpu_mem_time_sum): Time (%) Total Time (ns) Count Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Operation -------- --------------- ----- --------- --------- -------- -------- ----------- ------------------ 78.4 2,318,310 3 772,770.0 763,159.0 761,400 793,751 18,191.4 [CUDA memcpy HtoD] 21.6 640,473 1 640,473.0 640,473.0 640,473 640,473 0.0 [CUDA memcpy DtoH] 总体的耗时应当是三个部分: 总耗时 = TH2D + Tkernel + TD2H 并且,对于 <<<256,256>>> 来说:HtoD 和 DtoH 的耗时会远大于 kernel 的运行时间! 这就说明,来回的移动和复制数据比计算更消耗时间! 能否对这一部分进行优化呢? 后面的文章中会讲解! (四)、设备信息 对于 GPU 而言:越多的线程 => 越大的并行度 => 越好的性能 GPU 最大可以启动的线程数可以参考: 官网查询硬件:https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#features-and-technical-specifications 代码动态获取(cudaDeivceProp):https://docs.nvidia.com/cuda/cuda-runtime-api/index.html 也可以参考: https://www.nvidia.cn/docs/IO/51635/NVIDIA_CUDA_Programming_Guide_1.1_chs.pdf 1、cudaDeivceProp 重点的几个参数: maxGridSize(int[3]):x、y、z三个方向分别最多可支持的 block 数; maxBlockSize(int[3]):每个 Block中x、y、z三个方向分别最多可支持的线程数; maxThreadsPerBlock(int):每个 block 中最多可有的线程数 其中:Blocksize 需同时满足这两组条件:maxBlockSize、maxThreadsPerBlock: x、y、z加起来不超过:maxThreadsPerBlock; x、y、z各个方向不超过:maxBlockSize; 2、CUDA版本 查看 CUDA 版本使用: # CUDA版本nvcc --versionnvcc: NVIDIA (R) Cuda compiler driverCopyright (c) 2005-2023 NVIDIA CorporationBuilt on Tue_Feb__7_19:32:13_PST_2023Cuda compilation tools, release 12.1, V12.1.66Build cuda_12.1.r12.1/compiler.32415258_0 可以看到 CUDA 为 12.1! 而 nvidia-smi 命令输出的是:驱动支持的的最高版本,而非实际正在使用的版本! Tue Jul 29 09:30:09 2025 +-----------------------------------------------------------------------------------------+| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 ||-----------------------------------------+------------------------+----------------------+| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC || Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. || | | MIG M. ||=========================================+========================+======================|| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 || N/A 38C P8 10W / 70W | 0MiB / 15360MiB | 0% Default || | | N/A |+-----------------------------------------+------------------------+----------------------++-----------------------------------------------------------------------------------------+| Processes: || GPU GI CI PID Type Process name GPU Memory || ID ID Usage ||=========================================================================================|| No running processes found |+-----------------------------------------------------------------------------------------+ 可以看到,最高支持 12.4! 后记 更加详细的内容,可以看: https://qiankunli.github.io/2025/03/22/cuda.html 附录 源代码: https://github.com/JasonkayZK/high-performance-computing-learn/blob/main/cuda/1_introduction_to_parallel_programming_and_cuda.ipynb 参考文章: https://beta.infinitensor.com/camp/summer2025/stage/1/course/cuda-programming https://qiankunli.github.io/2025/03/22/cuda.html#simd%E5%92%8Csimt https://www.nvidia.cn/docs/IO/51635/NVIDIA_CUDA_Programming_Guide_1.1_chs.pdf

2025/7/29
articleCard.readMore

一些免费的GPU资源

在学习AI时,经常需要用到GPU资源;而有些时候我们手头并没有老黄的显卡,或者显卡已经不支持进行人工智能的学习了; 本文总结了一些常用的GPU资源; 同时,后续也会在博客更新一些和并行计算、人工智能相关的内容,敬请期待! 一些免费的GPU资源 一、Google Colab(推荐) 网址: https://colab.research.google.com/notebooks/ 特点: 提供 NVIDIA T4/P100/V100/A100(具体型号随机分配); 免费用户每天最多可使用 12 小时(可能因资源调度而中断); 可升级至 Colab Pro(9.99 美元/月)或 Colab Pro+(49.99 美元/月),享受更长运行时间和优惠优先级; 集成 Jupyter Notebook 环境,适合深度学习和机器学习任务 二、Kaggle 网址: https://www.kaggle.com/ 特点: GPU小时数:每周30小时。 GPU:提供Tesla P100,与Google Colab的T4相当。 使用质量:非常好,很少断连。 CPU和内存:提供四个CPU和29GB RAM。 易用性:易用,有类似笔记本的界面。 存储:无持久性存储。 三、Paperspace Gradient 网址: https://www.paperspace.com/ 特点: GPU小时数:无具体限制,质量不佳。 存储:有持久性存储,数据不会丢失。 GPU:提供M4000 GPU,质量低于Google Colab的T4。 四、其他 1、AWS Sagemaker Studio Lab 特点: GPU小时数:每天4小时,CPU小时数12小时。 GPU:提供T4 GPU,与Google Colab相同。 使用质量:非常好,很少断连。 易用性:需要在网站上注册。 存储:有持久性存储。 2、Lightning AI 网址: https://lightning.ai/ 特点: GPU小时数:每月22小时。 CPU:提供一个Studio,4个CPU完全免费。 使用质量:非常好,连接稳定。 易用性:非常好,提供VS Code界面。 3、百度 AI Studio 网址: https://aistudio.baidu.com/index 特点: 免费提供 GPU 运行环境,支持常见型号(如 T4、P40 等,具体配置可能随时调整); 集成 PaddlePaddle 以及 TensorFlow、PyTorch 等主流深度学习框架; 类似 Jupyter Notebook 的在线编程环境,适合快速上手、学习和实验; 4、云平台注册赠费 Google Cloud Free Tier 新用户可获得 300 美元免费试用额度(90 天内有效),体验包括 GPU 实例在内的众多云服务 用户可自行创建 NVIDIA T4/V100/A100 GPU 实例(需手动配置) 适用于大规模机器学习和 AI 训练 注册时需绑定信用卡,但试用期间不会产生扣费 Microsoft Azure Free Tier 新用户可获得 200 美元免费试用额度(30 天内有效),支持 GPU 虚拟机(如 NC/ND 系列) 适用于 AI 深度学习训练和企业级应用开发 需要信用卡验证,但试用期间不收费 AWS Free Tier(Amazon Web Services) 免费层主要提供 750 小时 t2.micro 实例(不含 GPU),部分新用户可额外申请 GPU 实例(如 p3/g4 系列) 提供 Amazon SageMaker 平台,支持机器学习项目的快速部署 注册需绑定信用卡,确保试用过程不产生费用 其他资源: https://www.reddit.com/r/KoboldAI/comments/13taldr/google_colabs_possible_alternatives/ https://deepnote.com/compare/alternatives/colab 附录 参考文章: https://zhuanlan.zhihu.com/p/1906295351289308808 https://www.bilibili.com/read/cv34465418/ https://www.reddit.com/r/KoboldAI/comments/13taldr/google_colabs_possible_alternatives/ https://deepnote.com/compare/alternatives/colab

2025/7/24
articleCard.readMore

debian12部署kubernetes-1.28集群

由于暑假到了以及天气原因,学校的k8s集群暂时关闭了,但是目前还是有使用k8s的需求,花了2个小时又重新搭了一下; 由于国内网络的问题,导致github包、镜像都很难拉下来,因此本文的内容更适合国内需求环境。 源代码: https://github.com/JasonkayZK/kubernetes-learn debian12部署kubernetes-1.28集群 零、前置工作 0、环境校验 该部分内容来自于 K8S 官方文档: 一台兼容的 Linux 主机。Kubernetes 项目为基于 Debian 和 Red Hat 的 Linux 发行版以及一些不提供包管理器的发行版提供通用的指令。 每台机器 2 GB 或更多的 RAM(如果少于这个数字将会影响你应用的运行内存)。 CPU 2 核心及以上。 集群中的所有机器的网络彼此均能相互连接(公网和内网都可以)。 节点之中不可以有重复的主机名、MAC 地址或 product_uuid。 1、准备虚拟机 IP AddressHostnameCPUMemoryStorageOS ReleaseRole 192.168.117.200k14C8G100GBDebian 12Master 192.168.117.201k24C8G100GBDebian 12Worker 192.168.117.202k34C8G100GBDebian 12Worker 虚拟机安装、配置部分不再赘述了! 主要包括下面几个方面: 配置软件源; 配置静态IP; 配置 hosts 解析; 配置 SSH 免密登录; 安装必要工具:net-tools、wget、curl、htop等; 参考: 《从零开始搭建大数据镜像-1》 2、卸载docker(如有) 新版本的 k8s 和 docker 底层都依赖 containerd 容易造成冲突,直接卸载docker: sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extrassudo rm -rf /var/lib/dockersudo rm -rf /var/lib/containerdsudo rm -rf /etc/docker 3、设置系统时区和时间同步 使用阿里云的时钟源: timedatectl set-timezone Asia/Shanghai# 安装 chronyapt-get install -y chrony# 修改为阿里的时钟源sed -i "s/pool 2.debian.pool.ntp.org iburst/server ntp.aliyun.com iburst/g" /etc/chrony/chrony.conf# 启用并立即启动 chrony 服务systemctl restart chronysystemctl enable chrony# 查看与 chrony 服务器同步的时间源chronyc sources 4、安装ipvs工具 在 Kubernetes 中,ipset 和 ipvsadm 的用途: ipset 主要用于支持 Service 的负载均衡和网络策略。它可以帮助实现高性能的数据包过滤和转发,以及对 IP 地址和端口进行快速匹配。 ipvsadm 主要用于配置和管理 IPVS 负载均衡器,以实现 Service 的负载均衡。 执行: apt-get install -y ipset ipvsadm 5、关闭服务 关闭 swap、防火墙等: # 关闭所有已激活的 swap 分区swapoff -a# 禁用系统启动时自动挂载 swap 分区sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab# 停止 AppArmor 服务systemctl stop apparmor.service# 禁用 AppArmor 服务systemctl disable apparmor.service# 禁用 Uncomplicated Firewall(ufw)ufw disable# 停止 ufw 服务systemctl stop ufw.service# 禁用 ufw 服务systemctl disable ufw.service 6、内核优化 创建一个名为 kubernetes.conf 的内核配置文件,并写入以下配置内容: cat > /etc/sysctl.d/kubernetes.conf << EOF# 允许 IPv6 转发请求通过iptables进行处理(如果禁用防火墙或不是iptables,则该配置无效)net.bridge.bridge-nf-call-ip6tables = 1# 允许 IPv4 转发请求通过iptables进行处理(如果禁用防火墙或不是iptables,则该配置无效)net.bridge.bridge-nf-call-iptables = 1# 启用IPv4数据包的转发功能net.ipv4.ip_forward = 1# 禁用发送 ICMP 重定向消息net.ipv4.conf.all.send_redirects = 0net.ipv4.conf.default.send_redirects = 0# 提高 TCP 连接跟踪的最大数量net.netfilter.nf_conntrack_max = 1000000# 提高连接追踪表的超时时间net.netfilter.nf_conntrack_tcp_timeout_established = 86400# 提高监听队列大小net.core.somaxconn = 1024# 防止 SYN 攻击net.ipv4.tcp_syncookies = 1net.ipv4.tcp_max_syn_backlog = 2048net.ipv4.tcp_synack_retries = 2# 提高文件描述符限制fs.file-max = 65536# 设置虚拟内存交换(swap)的使用策略为0,减少对磁盘的频繁读写vm.swappiness = 0EOF 加载或启动内核模块 br_netfilter,该模块提供了网络桥接所需的网络过滤功能 modprobe br_netfilter 查看是否已成功加载模块: lsmod | grep br_netfilter 将读取该文件中的参数设置,并将其应用到系统的当前运行状态中: sysctl -p /etc/sysctl.d/kubernetes.conf 参考: Linux操作系统-内核优化 7、内核模块配置 将自定义在系统引导时自动加载的内核模块: # 将自定义在系统引导时自动加载的内核模块cat > /etc/modules-load.d/kubernetes.conf << EOF# /etc/modules-load.d/kubernetes.conf# Linux 网桥支持br_netfilter# IPVS 加载均衡器ip_vsip_vs_rrip_vs_wrrip_vs_sh# IPv4 连接跟踪nf_conntrack_ipv4# IP 表规则ip_tablesEOF 添加可执行权限: chmod a+x /etc/modules-load.d/kubernetes.conf 8、安装containerd运行时 以下指令适用于 Kubernetes 1.28! (1)安装 安装 containerd: # cri-containerd 比 containerd 多了 runcwget https://github.com/containerd/containerd/releases/download/v1.7.21/cri-containerd-1.7.21-linux-amd64.tar.gztar xf cri-containerd-1.7.21-linux-amd64.tar.gz -C /# 创建目录,该目录用于存放 containerd 配置文件mkdir /etc/containerd# 创建一个默认的 containerd 配置文件containerd config default > /etc/containerd/config.toml# 修改配置文件中使用的沙箱镜像版本sed -i 's#registry.k8s.io/pause:3.8#registry.aliyuncs.com/google_containers/pause:3.9#' /etc/containerd/config.toml# 设置容器运行时(containerd + CRI)在创建容器时使用 Systemd Cgroups 驱动sed -i '/SystemdCgroup/s/false/true/' /etc/containerd/config.toml# 修改存储目录# mkdir /data1/containerd# sed -i 's#root = "/var/lib/containerd"#root = "/data1/containerd"#' /etc/containerd/config.toml (2)配置脚本 配置启动脚本: /lib/systemd/system/containerd.service [Unit]Description=containerd container runtimeDocumentation=https://containerd.ioAfter=network.target local-fs.target[Service]ExecStartPre=-/sbin/modprobe overlayExecStart=/usr/local/bin/containerdType=notifyDelegate=yesKillMode=processRestart=alwaysRestartSec=5LimitNPROC=infinityLimitCORE=infinityTasksMax=infinityOOMScoreAdjust=-999[Install]WantedBy=multi-user.target 执行配置: # 启用并立即启动 containerd 服务systemctl enable --now containerd.service# 检查 containerd 服务的当前状态systemctl status containerd.service# 检查 containerd crictl runc 的版本containerd --versioncrictl --versionrunc --versioncrictl config runtime-endpoint unix:///run/containerd/containerd.sock 一、安装组件 更新 apt 包索引并安装使用 Kubernetes apt 仓库所需要的包: sudo apt-get update# apt-transport-https 可能是一个虚拟包(dummy package);如果是的话,你可以跳过安装这个包sudo apt-get install -y apt-transport-https ca-certificates curl gpg#下载用于 Kubernetes 软件包仓库的公共签名密钥。所有仓库都使用相同的签名密钥,因此你可以忽略URL中的版本:curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg#添加 Kubernetes apt 仓库。 请注意,此仓库仅包含适用于 Kubernetes 1.28 的软件包; 对于其他 Kubernetes 次要版本,则需要更改 URL 中的 Kubernetes 次要版本以匹配你所需的次要版本 (你还应该检查正在阅读的安装文档是否为你计划安装的 Kubernetes 版本的文档)。# 此操作会覆盖 /etc/apt/sources.list.d/kubernetes.list 中现存的所有配置。echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list#更新 apt 包索引,安装 kubelet、kubeadm 和 kubectl,并锁定其版本:sudo apt-get updatesudo apt-get install -y kubelet=1.28.13-1.1 kubeadm=1.28.13-1.1 kubectl=1.28.13-1.1#锁定版本sudo apt-mark hold kubelet kubeadm kubectl#说明:在 Debian 12 和 Ubuntu 22.04 之前的早期版本中,默认情况下不存在 /etc/apt/keyrings 目录; 你可以通过运行 sudo mkdir -m 755 /etc/apt/keyrings 来创建它。 配置 kubelet 开机启动: systemctl enable kubelet 二、初始化集群 1、初始化集群(Master节点执行) 本小节在 master 节点执行! 生成配置文件: kubeadm config print init-defaults > kubeadm.yaml 配置文件如下: kubeadm.yaml apiVersion: kubeadm.k8s.io/v1beta3bootstrapTokens:- groups: - system:bootstrappers:kubeadm:default-node-token token: abcdef.0123456789abcdef ttl: 24h0m0s usages: - signing - authenticationkind: InitConfiguration#localAPIEndpoint:# advertiseAddress: 192.168.2.232# bindPort: 6443nodeRegistration: criSocket: unix:///run/containerd/containerd.sock imagePullPolicy: IfNotPresent# name: node taints: null---apiServer: timeoutForControlPlane: 4m0sapiVersion: kubeadm.k8s.io/v1beta3certificatesDir: /etc/kubernetes/pkiclusterName: kubernetescontrollerManager: {}dns: {}etcd: local: dataDir: /var/lib/etcd# 指定阿里云镜像以及k8s版本imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containerskind: ClusterConfigurationkubernetesVersion: 1.28.13# 新增controlPlaneEndpoint: 192.168.117.200:6443 # 修改为masterIP!networking: dnsDomain: cluster.local serviceSubnet: 10.254.0.0/16 podSubnet: 10.255.0.0/16 # 指定pod网段scheduler: {}# 新增如下:---apiVersion: kubelet.config.k8s.io/v1beta1kind: KubeletConfigurationcgroupDriver: systemd---apiVersion: kubeproxy.config.k8s.io/v1alpha1kind: KubeProxyConfigurationmode: ipvs 验证镜像仓配置是否生效。 kubeadm config images list --config=kubeadm.yaml 提前拉取镜像。 kubeadm config images pull --config=kubeadm.yaml 查看镜像是否下载。 crictl images 开始初始化。 kubeadm init --config=kubeadm.yaml 安装完会有加入集群的相关指令: You should now deploy a pod network to the cluster.Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/You can now join any number of control-plane nodes by copying certificate authoritiesand service account keys on each node and then running the following as root: kubeadm join 192.168.117.200:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:91a2398cbadf3967950dc6900e7411d5319e82ad30e139a1163896f9a8c61234 \ --control-plane Then you can join any number of worker nodes by running the following on each as root:kubeadm join 192.168.117.200:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:91a2398cbadf3967950dc6900e7411d5319e82ad30e139a1163896f9a8c61234 2、Worker加入集群(Worker节点) 此小节在 worker 节点执行! 执行加入脚本: kubeadm join 192.168.117.200:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:91a2398cbadf3967950dc6900e7411d5319e82ad30e139a1163896f9a8c61234 等待后即可加入! 3、Master节点污点(可选) 默认情况下 Master 节点为 control-plane,无法部署服务; 可以通过执行: # 查看污点kubectl describe node k1 |grep TaintsTaints: node-role.kubernetes.io/control-plane:NoSchedule# 删除污点kubectl taint node k1 node-role.kubernetes.io/control-plane:NoSchedule- 启用 master 节点调度! 补:其他节点使用kubectl 其他节点默认是无法直接使用 kubectl 管理集群的,我们只需要将配置文件复制到其他节点即可! 方法一:拷贝master节点的/etc/kubernetes/admin.conf 到nodes节点中的同样的目录/etc/kubernetes/ ,然后再配置环境变量 [root@k8s-node1 qq-5201351]# scp k8s-master:/etc/kubernetes/admin.conf /etc/kubernetes/admin.conf 然后再配置环境变量: echo 'export KUBECONFIG=/etc/kubernetes/admin.conf' >> ~/.bash_profilesource ~/.bash_profile 方法二:拷贝master节点的/etc/kubernetes/admin.conf 到nodes节点的$HOME/.kube目录,并且命名为config 因为默认是没有 $HOME/.kube 目录的,先进行创建: mkdir -p $HOME/.kube scp k8s-master:/etc/kubernetes/admin.conf $HOME/.kube/config 三、网络插件 1、安装Calico k8s 部署完成后还不能使用,需要配置网络插件,从而为 Pod 分配 IP,打通网络等等。 Calico是 目前开源的最成熟的纯三层网络框架之一, 是一种广泛采用、久经考验的开源网络和网络安全解决方案,适用于 Kubernetes、虚拟机和裸机工作负载。 Calico 为云原生应用提供两大服务:工作负载之间的网络连接和工作负载之间的网络安全策略。 Calico 访问链接:projectcalico.docs.tigera.io/about/ 在这里使用 calico 来做为集群的网络插件,官网提供2种安装方式: operator 的方式修改镜像比较麻烦,这里不使用; 通过yaml配置文件的方式; curl https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico.yaml -O 配置: 修改 CALICO_IPV4POOL_CIDR 为我们的网段(本文为:10.254.0.0/16) 修改 CALICO_IPV4POOL_IPIP 为 Always 启用 ipip 协议; - # - name: CALICO_IPV4POOL_CIDR- # value: "192.168.0.0/16"+ - name: CALICO_IPV4POOL_CIDR+ value: "10.254.0.0/16"# Enable IPIP+ - name: CALICO_IPV4POOL_IPIP+ value: "Always" 修改镜像地址: 搜索 image: 将镜像修改: - image: docker.io/calico/cni:v3.28.1+ image: registry.cn-hangzhou.aliyuncs.com/jasonkay/cni:v3.28.1- image: docker.io/calico/node:v3.28.1+ image: registry.cn-hangzhou.aliyuncs.com/jasonkay/node:v3.28.1- image: docker.io/calico/kube-controllers:v3.28.1+ image: registry.cn-hangzhou.aliyuncs.com/jasonkay/kube-controllers:v3.28.1 即,将:docker.io/calico 替换为 registry.cn-hangzhou.aliyuncs.com/jasonkay (我在阿里云上同步的镜像)! 随后执行: kubectl apply -f calico.yaml 等待部署完成即可! 2、验证 验证 coredns dns 转发是否正常: # 安装dns工具apt install -y dnsutils# 获取dns ip地址kubectl get svc -n kube-systemNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkube-dns ClusterIP 10.254.0.10 <none> 53/UDP,53/TCP,9153/TCP 15h# 测试能够解析dig -t a www.baidu.com @10.254.0.10 四、部署应用测试 部署 nginx 进行测试: nginx-deploy.yaml apiVersion: apps/v1kind: Deploymentmetadata: name: nginx-deployment labels: app: nginxspec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: registry.cn-hangzhou.aliyuncs.com/jasonkay/nginx:latest ports: - containerPort: 80---apiVersion: v1kind: Servicemetadata: name: nginx-servicespec: type: NodePort selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 nodePort: 31080 应用: kubectl apply -f nginx-deploy.yaml 然后访问 k8s-ip:31080,能够正常访问 Nginx! 五、安装Helm Helm 是 Kubernetes 的一个包管理工具,类似于 Linux 的 Apt 或 Yum; 这个工具能帮助开发者和系统管理员更方便地管理在 Kubernetes 集群上部署、更新、卸载应用。 Helm 中的三个主要概念: 概念描述 Chart在 Kubernetes 集群上部署应用所需的所有资源定义的包 Release在 Kubernetes 集群上部署的 Chart 的实例 RepositoryChart 的存储位置,类似软件仓库,用于分发和分享 Chart 安装脚本: 1. 添加 Helm 的官方 GPG keyroot@k8s-master:~# curl https://baltocdn.com/helm/signing.asc | gpg --dearmor -o /usr/share/keyrings/helm-keyring.gpg2. 添加 Helm 的官方 APT 仓库root@k8s-master:~# echo "deb [signed-by=/usr/share/keyrings/helm-keyring.gpg] https://baltocdn.com/helm/stable/debian/ all main" | tee /etc/apt/sources.list.d/helm-stable-debian.list3. 更新 apt 源root@k8s-master:~# apt-get update4. 安装 Helmroot@k8s-master:~# apt-get install -y helm5. 检查 Helm 是否已正确安装root@k8s-master:~# helm versionversion.BuildInfo{Version:"v3.13.3", GitCommit:"c8b948945e52abba22ff885446a1486cb5fd3474", GitTreeState:"clean", GoVersion:"go1.20.11"} 六、安装面板KubeSphere 官方提供的面板不太好用,这里推荐使用 KubeSphere; 配置下载区域: export KKZONE=cn 安装也很简单,使用 helm 即可,而且支持国内: helm upgrade --install -n kubesphere-system \ --create-namespace ks-core \ https://charts.kubesphere.com.cn/main/ks-core-1.1.4.tgz \ --debug --wait \ --set global.imageRegistry=swr.cn-southwest-2.myhuaweicloud.com/ks \ --set extension.imageRegistry=swr.cn-southwest-2.myhuaweicloud.com/ks 等待所有 Pod 就绪后,安装完成,显示: NOTES:Thank you for choosing KubeSphere Helm Chart.Please be patient and wait for several seconds for the KubeSphere deployment to complete.1. Wait for Deployment Completion Confirm that all KubeSphere components are running by executing the following command: kubectl get pods -n kubesphere-system2. Access the KubeSphere Console Once the deployment is complete, you can access the KubeSphere console using the following URL: http://192.168.6.10:308803. Login to KubeSphere Console Use the following credentials to log in: Account: admin Password: P@88w0rdNOTE: It is highly recommended to change the default password immediately after the first login.For additional information and details, please visit https://kubesphere.io. 执行以下命令检查 Pod 状态。 kubectl get pods -n kubesphere-system 当 Pod 状态都为 Running 时,使用默认的账户和密码 (admin/P@88w0rd) 通过 <NodeIP>:30880 访问 KubeSphere Web 控制台! 七、工具推荐 1、kubectx 推荐安装 kubectx,可以切换k8s上下文(管理多个集群); 并且 kubectx 自带了另一个工具:kubens,可以方便切换默认的 namespace; 安装: apt install -y kubectx 2、nerdctl nerdctl 可以提供在宿主机上类 docker 的操作(操作 containerd),可以提升用户体验: cd /tmpwget https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-1.7.6-linux-amd64.tar.gztar xf nerdctl-1.7.6-linux-amd64.tar.gzmv nerdctl /usr/sbin 附录 参考文章: https://isekiro.com/kubernetes%E5%9F%BA%E7%A1%80-debian-%E9%83%A8%E7%BD%B2k8s-1.28/ https://juejin.cn/post/7300419978486169641 https://www.cnblogs.com/5201351/p/17407406.html https://kubesphere.io/zh/docs/v4.1/02-quickstart/01-install-kubesphere/ 源代码: https://github.com/JasonkayZK/kubernetes-learn

2025/7/21
articleCard.readMore

分享两个服务器实用脚本:xsync和xcall

如果同时需要维护多台服务器,可能会需要在多台服务器之间同步文件、执行命令。 本文介绍了两个简单的脚本实现这一功能! 源代码: https://gist.github.com/JasonkayZK/e8bbe840d4b4d9d0ed15d4385e1c0a07 https://gist.github.com/JasonkayZK/6847064a739bd08232e2da938d5e34ef 分享两个服务器实用脚本:xsync和xcall 文件同步:xsync 1、前置依赖 xsync 依赖于 rsync 工具,可以通过 yum 或者 apt 简单的安装: apt或yum install -y rsync 此外,还需要配置 SSH 无密码登录! 2、编写脚本 脚本内容: xsync #!/bin/bash# Dependency:# 1. rsync: yum/apt install -y rsync# 2. password-less SSH login## 0. Define server listservers=("server-1" "server-2" "server-3")# 1. check param numif [ $# -lt 1 ]; then echo "Not Enough Arguement!" exit 1fi# 2. traverse all machinesfor host in "${servers[@]}"; do echo "==================== $host ====================" # 3. traverse dir for each file for file in "$@"; do # 4. check file exist if [ -e "$file" ]; then # 5. get parent dir pdir=$(cd -P "$(dirname "$file")" && pwd) # 6. get file name fname=$(basename "$file") ssh "$host" "mkdir -p $pdir" rsync -av "$pdir/$fname" "$host:$pdir" else echo "$file does not exist!" fi donedone 使用时,上面的文件中 servers 数组中的配置,为你服务器集群! 3、使用 增加可执行权限、并将文件放在 PATH 下; 然后直接使用,例如: xsync ~/.bashrc 命令执行:xcall 和 xsync 类似,编写: xcall #!/bin/bash# Dependency: password-less SSH login## Define server array (easily extensible)servers=( "server-1" "server-2" "server-3")# Check if command arguments are providedif [ $# -eq 0 ]; then echo "Error: Please provide a command to execute" >&2 exit 1fi# Execute command across all serversfor server in "${servers[@]}"; do echo "--------- $server ----------" # Execute remote command and handle errors if ssh "$server" "$*"; then echo "✓ Command executed successfully" else echo "✗ Command failed on server: $server" >&2 # Uncomment below line to exit script on first failure # exit 1 fidone 使用也是类似: 增加可执行权限、并将文件放在 PATH 下; 然后直接使用,例如: xcall ls 附录 源代码: https://gist.github.com/JasonkayZK/e8bbe840d4b4d9d0ed15d4385e1c0a07 https://gist.github.com/JasonkayZK/6847064a739bd08232e2da938d5e34ef

2025/7/21
articleCard.readMore

RSS订阅工具Folo使用

之前一直使用的是Inoreader,也早就听说并且下载了Folo,但是一直没有时间切换; 今天有时间切换到了Folo,聊一下感受! 源代码: https://github.com/RSSNext/Folo RSS订阅工具Folo使用 零、Folo介绍 Follow 是一款新兴且创新的 RSS 订阅工具; 除了具备传统 RSS 订阅器的所有基本功能外,Follow 还提供了一些额外的特色功能。 类似于 Inoreader,可以订阅 RSS,查看属于自己独一无二的信息流; 如果你还不了解 RSS,可以简单认为类似于将网页内容转为微信公众号订阅! 一、使用方法 1、下载&注册 Web 版: https://app.follow.is/ Mac、Win、Android: https://github.com/RSSNext/Folo/releases 2、使用 添加订阅即可! 添加方法: (1)搜索:输入关键词进行搜索; (2)RSS订阅:输入RSS URL进行订阅; (3)RSSHub订阅; (4)RSS3 (5)UID:可以直接搜索订阅正在使用Follow的用户,输入对方的UID(在个人资料里设置的唯一标识)即可订阅; (6)通过导入OPML文件(如Inoreader)订阅; 强烈建议配合RSSHub使用(网页插件,可以探索当前页面的RSS源): https://docs.rsshub.app/ 二、从Inoreader迁移 如果你根据 Folo 提供的: 打开 https://www.inoreader.com/preferences/content切换到 "SYSTEM FOLDERS" 标签。点击 "Newsfeed" 右侧的 "OPML" 按钮。 会要求你开通会员才能导出; 可以在设置的 账号 中,选择 导出和备份,此时会导出 xml 格式的文件! 随后,在导入时选择 所有文件,即可导入! 目前最多支持500个订阅,比 Inoreader 的150 个多很多了! 三、RSS所有者认证 认证自己的订阅源。 1、获取认证码 你在 Follow 中订阅自己的博客,再在 Follow 中右键点击订阅源,申请 Claim,便可以获取到认证码,例如: This message is used to verify that this feed (feedId:57983956538829824) belongs to me (userId:81890745999218688). Join me in enjoying the next generation information browser https://folo.is. 2、验证认证码 在你的博客,写入一篇文章,贴入认证码并发布;然后在 Follow 中再次申请 Claim 即可认证。 认证过后,认证码的信息便可以删除;此处予以修改、保留,方便其他读者认证时参考使用。 当然,你也可以通过一个 Telegram 频道,来进行 Follow 的认证。 后记 更多的内容可以阅读: 《使用 Follow 的第 50 天:RSS 迎来又一春?》 后续也会在博客分享一些我的订阅源,敬请期待~ 附录 源代码: https://github.com/RSSNext/Folo RSS相关服务: RSSHub Telegram RSS / JSON generator RSS-proxy yarb (Yet Another Rss Bot) ALL-about-RSS 各种转 RSS 服务 diff.blog Kill the Newsletter 参考文章: https://zhuanlan.zhihu.com/p/781349659 https://yinji.org/5317.html

2025/7/15
articleCard.readMore

多平台消息推送工具ntfy使用

ntfy是一个开源的多平台消息推送工具,可以通过HTTP请求发送通知到手机或桌面。 本文总结了ntfy的安装、配置和使用方法。 官方仓库: https://github.com/binwiederhier/ntfy ntfy使用 一、简介 ntfy(发音为“notify”)是一个简单的基于HTTP的发布-订阅通知服务。通过ntfy,可以使用PUT/POST请求从任何计算机的脚本发送通知到手机或桌面,而无需注册或支付费用。 同时ntfy是开源的,可以轻松自托管。 ntfy提供免费的公共实例:ntfy.sh,也有Android和iOS应用可用; 主要特点: 简单易用:通过curl等工具发送通知。 自托管:可以部署自己的实例。 多平台:支持Android、iOS、Web等。 访问控制:支持用户认证和ACL。 二、使用 1、发送通知 使用curl: curl -d "Hello from ntfy" ntfy.sh/mytopic 2、订阅主题 在APP或Web订阅主题接收通知。 三、自部署 参考: https://blog.xiaoz.org/archives/20400 https://k1r.in/posts/notify-ntfy/ 附录 参考: https://github.com/binwiederhier/ntfy https://k1r.in/posts/notify-ntfy/ https://blog.7theaven.top/2023/06/11/%E4%BD%BF%E7%94%A8-ntfy-%E8%87%AA%E5%BB%BA%E6%B6%88%E6%81%AF%E6%8E%A8%E9%80%81%E6%9C%8D%E5%8A%A1/#toc-head-1 https://blog.xiaoz.org/archives/20400

2025/7/15
articleCard.readMore

AppleScript介绍与简单实战

AppleScript是macOS内置的脚本语言,可以自动化操作应用程序。本文介绍了AppleScript的基本概念、语法和一个简单实战示例。 AppleScript介绍与简单实战 一、什么是AppleScript AppleScript是苹果公司推出的一种脚本语言,内置于macOS中,可以直接操作和控制macOS及其应用程序。它是一个实现macOS自动化的强大工具,AppleScript的前身是HyperCard所使用的脚本语言HyperTalk。 与其他脚本语言如Python和JavaScript相比,AppleScript最显著的特点是能够控制其他macOS上的应用程序。通过AppleScript,我们可以完成一些繁琐重复的工作。其语法简单,接近自然语言,就像在和系统对话一样。此外,系统提供了语法查询字典,方便查询语法。 二、基础语法 按照惯例,用AppleScript写一个Hello World: display dialog "Hello, world!" 命令行执行: osascript -e 'display dialog "Hello, world!"' 运行后,系统会弹出“Hello, world!”的弹窗。 下面介绍几种常用语法: 1、告诉应用做某事 AppleScript的语法接近自然语言,例如: tell application "Safari" activate open location "https://qq.com/"end tell 这告诉Safari启动并打开指定网址。 2、设置变量 set qq to "https://qq.com/"tell application "Safari" activate open location qqend tell 3、条件语句 if num > 2 then // ...else // ...end if 4、循环语句 repeat with num in {1, 2, 3} display dialog "hello, world"end repeat 5、模拟点击和输入 可以使用click命令模拟点击,或keystroke输入文本。 三、简单实战:命令完成后显示通知 执行以下命令可以展示一条通知: osascript -e 'display notification "The command finished" with title "Success"' 在.zshrc中定义一个函数: function notifyResult () { if [ $? -eq 0 ]; then osascript -e 'display notification "The command finished" with title "Success"' else osascript -e 'display notification "The command failed" with title "Failed"' fi} 运行长时间命令时: some_program; notifyResult 执行完后会收到通知是否执行成功! 可以设置通知音效: MacOS: notify when the terminal command is finished 四、一些常用脚本 1、通过参数发送系统通知 源代码: https://gist.github.com/JasonkayZK/c32cc4af8582b12d785619f6c999999f 代码如下: sys-notify #!/bin/bash# 帮助函数show_help() { echo "Usage: $0 [Option] [message]" echo "Show system notification" echo echo "Option:" echo " -h, --help Show help info" echo " -t, --title TITLE Set notification title (Default: Notify)" exit 0}# 默认值title="Notify"# 解析命令行参数while [[ $# -gt 0 ]]; do case "$1" in -h|--help) show_help ;; -t|--title) if [[ -z "$2" ]]; then echo "错误: --title 需要一个参数" >&2 exit 1 fi title="$2" shift 2 ;; *) # 第一个非选项参数视为消息内容 message="$1" shift break ;; esacdone# 如果没有提供消息内容,则从标准输入读取if [[ -z "$message" ]]; then if [[ -t 0 ]]; then echo "错误: 没有提供消息内容" >&2 show_help exit 1 else message=$(cat) fifi# 发送通知case "$(uname -s)" in Darwin) # macOS 使用 AppleScript osascript -e "display notification \"$message\" with title \"$title\"" ;; Linux) # Linux 使用 notify-send (libnotify) if command -v notify-send &>/dev/null; then notify-send "$title" "$message" else echo "错误: 未找到 notify-send 命令。请安装 libnotify 包。" >&2 exit 1 fi ;; *) echo "错误: 不支持的操作系统" >&2 exit 1 ;;esac 准备: 编写 sys-notify; 增加可执行权限 chmod +x sys-notify; 加入到 PATH 环境变量; 使用方法: 基本用法:sys-notify "操作已完成" 自定义标题:sys-notify -t "操作结果" "操作已完成" 从标准输入读取内容:echo "操作已完成" | sys-notify -t "操作结果" 2、命令完成后展示结果 源代码: https://gist.github.com/JasonkayZK/d5c4e3adadf3478ded5896becf01f08c function notifyResult () { if [ $? -eq 0 ]; then osascript -e 'display notification "The command finished" with title "Success"' else osascript -e 'display notification "The command failed" with title "Failed"' fi} 在运行某些需要比较长时间的程序时,执行以下命令: some_program; notifyResult 五、后记 AppleScript是一个简单而强大的工具,能自动化macOS操作; 更多内容可参考附录链接; 附录 源代码: https://gist.github.com/JasonkayZK/c32cc4af8582b12d785619f6c999999f https://gist.github.com/JasonkayZK/d5c4e3adadf3478ded5896becf01f08c 官方文档: https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html 参考文章: https://zhuanlan.zhihu.com/p/149893274 https://ameow.xyz/archives/display-notification-after-command-finishes-macos

2025/7/15
articleCard.readMore

uv使用

uv 是一个用 Rust 编写的、速度极快的 Python 包和项目管理工具,由 ruff 的作者开发。它的目标是成为 pip、venv、virtualenv 和 pip-tools 等工具的直接替代品,提供一个统一、快速且易于使用的体验。 本文总结了如何安装和使用 uv。 官方仓库: https://github.com/astral-sh/uv uv使用 一、简介 如果你对 Python 的包管理生态有所了解,你可能用过 pip 来安装包,用 venv 或 virtualenv 来创建虚拟环境,用 pip-tools 来锁定依赖。这些工具各司其职,但组合使用起来有时会显得有些繁琐。 uv 的出现就是为了解决这个问题。它将包安装、虚拟环境管理和依赖解析等功能集成到了一个单一的命令行工具中,并且性能极高。 主要特点: 极速: uv 利用了先进的依赖解析算法和全局缓存,安装和解析包的速度比 pip 快得多。 一体化: 无需在 pip 和 venv 之间切换,uv 提供了统一的命令来处理大多数包和环境管理任务。 直接替代: uv 的命令设计与 pip 非常相似(例如 uv pip install),使得迁移成本非常低。 依赖锁定: 内置了类似 pip-tools 的功能,可以从 pyproject.toml 或 requirements.in 文件编译生成锁定的 requirements.txt 文件。 二、安装 uv 提供了多种安装方式: macOS 和 Linux 使用 curl: curl -LsSf https://astral.sh/uv/install.sh | sh Windows 使用 PowerShell: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 其他方式 也可以通过 pip、brew 等包管理器安装: # 通过 pippip install uv# 通过 Homebrewbrew install uv 安装完成后,可以通过 uv --version 来验证是否成功。 三、基本使用 uv 的使用非常直观,特别是对于熟悉 pip 和 venv 的用户。 1、管理python版本 使用如下命令,显示出当前环境中所有可用的python版本(包括已经安装的和可以安装的) uv python list 如果需要安装其他版本的python,使用如下命令 uv python install 3.12 除了标准python之外,还可以安装其他的Python实现,比如PyPy实现的python: uv python install pypy@3.10 查找某个python版本的路径: ❯ uv python find 3.10/Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10 四、单个脚本使用 参考: https://github.com/astral-sh/uv?tab=readme-ov-file#scripts uv 可以管理单文件脚本的依赖项和环境; 例如:创建一个新脚本并添加内联元数据来声明其依赖项: $ echo 'import requests; print(requests.get("https://astral.sh"))' > example.py$ uv add --script example.py requestsUpdated `example.py` 然后,在隔离的虚拟环境中运行脚本: $ uv run example.pyReading inline script metadata from: example.pyInstalled 5 packages in 12ms<Response [200]> 五、项目中使用 1、选用python版本 在具体的某个项目中,进入项目目录,使用如下命令指定选用的python版本: uv python pin 版本号 这个命令会在指定目录下创建一个.python-version文件,内容如下: ❯ uv python pin 3.10 Pinned `.python-version` to `3.10`❯ cat .python-version 3.10 注意:这里选用的python版本只和uv管理的虚拟环境有关系,和我们全局的python、python3命令都没有关系! 2、创建虚拟环境 创建项目有两种方式,第一种方式,先创建好项目目录,然后设置python版本并初始化uv虚拟环境: uv python pin 3.10uv init # 初始化 执行了uv init之后,会在当前目录下创建几个文件,同时也会在当前目录下执行git init创建出一个新的git仓库来: ❯ uv python pin 3.13Pinned `.python-version` to `3.13`❯ uv initInitialized project `test-code`❯ lsREADME.md main.py pyproject.toml 另外一个方式是在init之后添加一个项目名,会自动创建项目文件夹 uv init 项目名 如果需要指定特定python版本,建议使用第一种方式来创建项目; 否则还需要手动修改pyproject.toml配置文件里面需要的python版本。 3、添加依赖 uv add 依赖项 比如添加requests库: uv add requests 还可以指定具体版本: uv add requests==版本号 执行了这个命令后,会在当前目录下创建.venv虚拟环境目录(在vscode里面可以选择这个目录作为虚拟环境,否则代码解析会有问题); 并添加我们要的依赖项,同时会新增一个uv.lock文件,用于存放依赖项版本相关的信息。 pyproject.toml文件中的dependencies字段也会包含需要的依赖项: ❯ uv add requestsUsing CPython 3.13.1 interpreter at: /opt/homebrew/opt/python@3.13/bin/python3.13Creating virtual environment at: .venvResolved 6 packages in 13.85sPrepared 5 packages in 5.55sInstalled 5 packages in 13ms + certifi==2025.1.31 + charset-normalizer==3.4.1 + idna==3.10 + requests==2.32.3 + urllib3==2.4.0 而且,从这个输出中也能看到,它自动使用了.python-version指定的3.13版本的python; 和当前全局配置下的python3指向什么版本没有关系(全局python3指向的是3.10版本); 4、运行程序 依赖添加好后,就可以使用uv来运行python程序了 uv run 程序文件名 [命令行参数] uv会自动按照我们的配置来运行程序,无序我们手动维护依赖项,也不需要手动去source各式各样的虚拟环境! 5、其他机器中配置环境 在其他机器拉取代码后,也需要配置虚拟环境、下载项目依赖; 如果使用 uv 进行管理只需要一步: uv sync 即可同步环境,随后使用: uv run 程序文件名 [命令行参数] 即可运行! 六、uvx命令 随着uv下载的还有一个uvx命令,和 pipx 类似; 使用 uvx 命令可以执行或安装一些使用 Python 编写的命令行工具; 参考: https://github.com/astral-sh/uv?tab=readme-ov-file#tools 1、临时使用命令 例如,在一个临时环境中执行一个工具: ➜ blog git:(save) ✗ uvx pycowsay 'hello world!'Installed 1 package in 6ms/Users/zk/.cache/uv/archive-v0/C-520Z4yR2_MYfgN_xvWl/lib/python3.12/site-packages/pycowsay/main.py:23: SyntaxWarning: invalid escape sequence '\ ' """ ------------< hello world! > ------------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || 可以看到,工具会被临时下载到 ~/.cache/uv 中! 2、全局安装工具 也可以全局安装一个工具: $ uv tool install ruffResolved 1 package in 6msInstalled 1 package in 2ms + ruff==0.5.0Installed 1 executable: ruff$ ruff --versionruff 0.5.0 参考: tools documentation uvx命令本质上是uv tool run命令的别名: uvx python main.py# 等价于uv run main.py# 等价于uv tool run main.py 实际例子,如下这两个命令是等价的: ❯ uvx --directory ~/data/code/python/test_code python main.pyHello from test-code!❯ uv tool run --directory ~/data/code/python/test_code python main.pyHello from test-code! 七、配置 1、依赖镜像源配置 参考: https://blog.csdn.net/qq_41472205/article/details/145686414 (1)项目级 uv下载第三方库本质上也是通过pypi源下载的,所以在国内网络环境中默认链接速度会很慢,可以在项目目录的pyproject.toml中添加如下内容来使用清华源: [[tool.uv.index]]url = "https://pypi.tuna.tsinghua.edu.cn/simple"default = true (2)命令行级 运行uv add命令的时候也可以指定镜像源 uv add --default-index https://pypi.tuna.tsinghua.edu.cn/simple requests (3)全局 uv也提供了全局的配置项,可以通过环境变量UV_DEFAULT_INDEX配置镜像源 export UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple 注意:全局的配置项优先级低于pyproject.toml中配置的镜像源; 附录 官方仓库: https://github.com/astral-sh/uv 官方文档: https://docs.astral.sh/uv/ 参考文章: https://blog.csdn.net/muxuen/article/details/147544307

2025/7/14
articleCard.readMore

gemini-cli使用

有一段时间没有写博客了,这两年AI的发展日新月异,真是可怕。最近终于把学校的一摊子事忙完了,接下来还有个创新大赛、八月份又要去广州参加国培,不可谓不忙; 本文介绍了如何使用 Google 最新出的 gemini-cli: https://github.com/google-gemini/gemini-cli gemini-cli使用 一、简介 目前大家可能用的都是 Cursor 或者 VSCode 里的插件(如 Cline)、阿里的通义灵码,以及腾讯的 CodeBuddy 这类界面化的 AI 编码交互方式。 而 gemini-cli 是一个让你可以在命令行使用和上面功能类似的命令行工具! 功能特点: AI 编程伙伴: 你可以直接在终端里问它编程问题、让它帮你写代码、解释错误信息或者优化现有代码。 多功能助手: 不仅仅是代码,你还可以向它提问各种问题,甚至可以发图片(比如错误截图)给它看,让它帮你分析。 简化工作: 对于开发者和技术人员来说,这意味着不用离开熟悉的终端界面,就能快速获得 AI 的帮助,从而提高工作效率。 开源免费: 它是开源的,并且在预览期间为个人开发者提供了非常慷慨的免费使用额度。 二、安装 直接使用 npm 全局安装即可: npm install -g @google/gemini-cli 三、使用 直接在命令行输入: gemini 即可打开: ➜ blog git:(save) ✗ gemini ███ █████████ ██████████ ██████ ██████ █████ ██████ █████ █████░░░███ ███░░░░░███░░███░░░░░█░░██████ ██████ ░░███ ░░██████ ░░███ ░░███ ░░░███ ███ ░░░ ░███ █ ░ ░███░█████░███ ░███ ░███░███ ░███ ░███ ░░░███ ░███ ░██████ ░███░░███ ░███ ░███ ░███░░███░███ ░███ ███░ ░███ █████ ░███░░█ ░███ ░░░ ░███ ░███ ░███ ░░██████ ░███ ███░ ░░███ ░░███ ░███ ░ █ ░███ ░███ ░███ ░███ ░░█████ ░███ ███░ ░░█████████ ██████████ █████ █████ █████ █████ ░░█████ █████░░░ ░░░░░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░Tips for getting started:1. Ask questions, edit files, or run commands.2. Be specific for the best results.3. Create GEMINI.md files to customize your interactions with Gemini.4. /help for more information.╭─────────────────────────────────────────────────────────────────────────────────────────────╮│ > Type your message or @path/to/file │╰─────────────────────────────────────────────────────────────────────────────────────────────╯~/workspace/blog (save*) no sandbox (see /docs) gemini-2.5-pro (100% context left) 首次运行时,它会引导完成几个设置步骤: 选择主题风格: 为界面选择一个喜欢的颜色主题。 授权登录: 它会提示您通过 Google 账户进行登录授权。这通常会生成一个链接,需要在浏览器中打开并授权。 授权后,就可以享受免费的调用额度(预览版期间每天有 1000 次请求)。 四、配置API登陆 如果你没有配置 API,每次运行 gemini-cli 都要重新登陆一次,可以配置 API 修改登录方式; 1、访问 Google AI Studio 在浏览器中打开网址: https://aistudio.google.com/app/apikey 2、创建或获取密钥 如果尚未登录,请使用您的 Google 账户登录。 点击 “Create API key” (创建 API 密钥) 按钮。 系统会生成一个新的 API 密钥。这是一个很长的字符串。 3、复制并妥善保管 立即点击密钥旁边的复制,复制 key; 4、引入环境变量 配置: export GEMINI_API_KEY="YOUR_API_KEY" 将你复制的内容替换 YOUR_API_KEY; 有关 Google Gemini API 的使用也可以看这篇文章: Google Gemini API 接口调用教程,图文讲解 5、生效 运行 source ~/.zshrc 或重启终端; 随后在登录选项中选择 API Key 的方式即可! 五、其他说明 启动 gemini 后,引用本地文件 可以使用 @ 来选择文件; 更多内容可以通过 /help 获取: Basics: ││ Add context: Use @ to specify files for context (e.g., @src/myFile.ts) to target specific ││ files or folders. ││ Shell mode: Execute shell commands via ! (e.g., !npm run start) or use natural language ││ (e.g. start server). ││ ││ Commands: ││ /clear - clear the screen and conversation history ││ /help - for help on gemini-cli ││ /memory - Commands for interacting with memory. ││ show - Show the current memory contents. ││ add - Add content to the memory. ││ refresh - Refresh the memory from the source. ││ /theme - change the theme ││ /docs - open full Gemini CLI documentation in your browser ││ /auth - change the auth method ││ /editor - set external editor preference ││ /privacy - display the privacy notice ││ /stats - check session stats. Usage: /stats [model|tools] ││ /mcp - list configured MCP servers and tools ││ /extensions - list active extensions ││ /tools - list available Gemini CLI tools ││ /about - show version info ││ /bug - submit a bug report ││ /chat - Manage conversation history. Usage: /chat <list|save|resume> <tag> ││ /quit - exit the cli ││ /compress - Compresses the context by replacing it with a summary. ││ ! - shell command ││ ││ Keyboard Shortcuts: ││ Enter - Send message ││ Ctrl+J - New line ││ Up/Down - Cycle through your prompt history ││ Alt+Left/Right - Jump through words in the input ││ Shift+Tab - Toggle auto-accepting edits ││ Ctrl+Y - Toggle YOLO mode ││ Esc - Cancel operation ││ Ctrl+C - Quit application 官方也提供了大量的经典案例: https://github.com/google-gemini/gemini-cli?tab=readme-ov-file#popular-tasks 六、后记 实际上和这位博主一样,我最开始也不喜欢使用命令行: 由于先入为主的一些体验,我一直习惯于使用像 Cursor 或者 VSCode 里的插件(如 Cline)、阿里的通义灵码,以及腾讯的 CodeBuddy 这类界面化的 AI 编码交互方式。相比之下,对于 Claude cli、Gemini cli 这类基于终端、命令行的交互方式,我一直提不起兴趣。 直到最近,我体验了几次 Claude cli 的编码功能,这才让我对以往的成见有了一些改变。过去我之所以更习惯插件式的交互方式,是因为它们的操作更为直观。无论是选择代码段、文件,还是插入图片,都非常直接简单。因此,我一直认为这种交互方式就是最优雅的 AI 编码方式。 正因为这种先入为主的心态,我始终对 Cli 这类交互方式有些抵触。我一直不太能理解,在编辑器里写代码,却要用终端来进行 AI 编码,这到底是一种怎样的交互逻辑?总觉得这种方式很割裂,与 AI 的交互以及 AI 的编码过程变成了一种黑盒,很不直观。这也是我迟迟没有尝试这种编码方式的原因。 但是在安装并且体验了之后,发现确实要方便很多! 他最后的总结也是我的体验以及一些个人感想: 这让我意识到,很多时候限制我们接受新事物的,并不是事物本身的缺点。那些所谓的缺点,或许只是我们自己想象出来的。真正阻碍我们发展的,往往是自己内心深处那些根深蒂固的成见。我们习惯于依赖以往成功的经验,固守在自己的舒适区,对不熟悉的方式和交互逻辑产生抵触情绪。而正是这种抵触,让我们与更美好的事物始终形同陌路。 这次 Claude cli 的体验给了我一个提醒:应该始终保持开放的心态,勇于尝试那些看似不符合直觉的新方法或新工具。也许这样能够带来许多意想不到的效率提升和全新的体验,要善于打破自己的成见,才能真正拥抱进步。 附录 参考: https://www.v2ex.com/t/1144487 https://wiki.eryajf.net/pages/d89910/

2025/7/14
articleCard.readMore

跑步一年多的一些总结和感想

不知不觉跑步已经锻炼了有一年时间了,从最开始200米都跑不下来,到现在跑完半马,变化还是巨大的。 本文记录一些跑步的感想。 跑步一年多的一些总结和感想 新的开始 契机 最开始让我跑步的原因是当时刚刚告别程序员这个行业,体检的时候发现自己居然接近三高了: 血压偏高 血脂偏高 尿酸偏高 并且体重接近 90kg! 要知道上大学的时候,我还是易瘦体质,体重还没超过70kg! 正巧当时还没有确定好工作,空余时间比较多,加上膝盖已经出现了问题,所以就打算锻炼身体。 其实在此之前,在深圳、福州就已经开始断断续续的跑步了! 但是没有真正的把跑步当作一件事情认真对待,只是简单的跑一跑。 真正认真对待这件事还是回家之后,想跑一跑步,突然发现自己身体素质差的要命! 当时刚从南方回家,大概在十一月份,因为北方已经开始降温了,天气比较冷: 基本上遇到冷空气就要打喷嚏,并且几乎每个月都要感冒,身体差的不行; 再加上医院的体检结果也是非常吓人,很多指标都偏高! 但是实际上压死骆驼的最后一根稻草是: 我去运动场上试跑了一下,发现自己连200米都坚持不下来! 这个对我来说印象十分深刻: 我至今都还能记得,自己跑了200米就开始大口大口喘气的感觉! 跑步训练-v0.1 由于没有经验,最开始在跑步的时候是没有什么规划的,也不知道训练方法之类的; 基本上就是来到体育场,也没有热身,能跑多快跑多快; 这是最早的一张图(当时用的APP还是小米运动): 基本上跑个400米就不行了。 而且因为没有热身,基本上跑完之后,第二天大腿、小腿还会疼; 跑步训练-v1.0 实际上即使没有任何训练计划,跑了几天之后发现自己体力也是有明显进步的; 可能是因为原本的身体基础实在是太差、段位太低,所以提升的比较快! 虽说是明显的进步,实际上也就是能坚持跑个1公里左右; 于是在这个时候开始准备一些更长的训练: 这个阶段我的跑步策略是: 先走一圈热身 然后再跑一公里 然后再走一圈 然后再跑一公里 因为一开始根本是无法坚持跑完3公里的路程的,所以基本上只能分成3组,每组大概是1公里的样子; 中间穿插一小段热身缓和; 这个阶段大概持续了半年左右: 2023年11月-12月 2024年2月-7月 跑步训练-v2.0 上面的分阶段跑步大概持续了大半年左右,一直到了2024年的8月份; 这个时候训练从原来的3组1公里,慢慢变成了:3组1.5公里、4组1.5公里、一组5公里等等; 所以在这个阶段,慢慢开始考虑,是不是可以不再分组了,而是一口气跑下来5公里或者更长呢? 于是从8月份开始,开始尝试5公里的跑步; 最开始的训练是非常痛苦的,因为5公里的长度对于我来说已经是非常长了,所以基本上是跑跑停停: 也是从这一阶段开始,我的跑步APP也从小米运动转到了 Keep; 同时也开始真正记录一些跑步数据、并且真正开始看和跑步相关的知识; 也许是之前训练了很长的时间,所以在这一阶段,5公里已经开始能跑出比较不错的成绩了! 当时也是迷上了一部动漫: 《風が強く吹いている》 实际上这本书在我本科上学期间就已经读过了,非常推荐! 同时,也是在知乎上看到了这么一幅图: 所以开始追求跑步的配速! 同时也在 Github 搭建了: running-page 感谢 yihong0618!❤️ 进一步激发了我的跑步热情! 跑步训练-v3.0 经历了5公里洗礼之后,再继续跑步的时候,就想慢慢探索自己能否跑更长的距离; 也是这一时期,感觉自己可以开始试着准备准备半程马拉松了! 于是开始挑战10km这个距离; 上面的这次跑步实际上只是为了看看自己1个小时究竟能跑多远! 说是挑战10公里,实际上要比我的第一个5公里要简单很多了! 但是长距离带来了另外的问题,就是: 自己的脚底开始磨出水泡! 然后也开始慢慢意识到: 即使跑步速度不快的情况下,时间或者距离足够长,实际上会将你的鞋底摩擦、衣服摩擦造成的影响给无限放大,导致出现短跑不会遇到的问题! 也是这一阶段开始,自己开始注意在运动前保护自己: 跑前拉伸、热身1-2公里 注意检查自己的鞋是否存在磨脚等情况 跑步的状态等 跑步训练-v4.0 4.0 版本实际上也就是目前的训练版本; 在今年的2月份,买了 Garmin 255 的手表,专门用来记录跑步! 同时,在跑步训练上也更加专注长距离的跑步,基本上每次训练都是10公里的量! 同时也是完成了半马挑战! 虽然成绩不是很好,哈哈。 也是这几天,买了更加专业的跑鞋(李宁飞电6C); 跑步装备越来越专业,实际上只是为了激励自己能更好的坚持跑下去! 不过我个人觉得,实际上更重要的是要能坚持迈开腿跑下去!装备并没有那么重要! 当然,后面也会挑战更多的半马; 上面年由于工作比较忙,所以也是希望下半年有机会参加郑开的半马! 一些收获和感想 1、身体健康 最大的收获当然是:自己的身体健康! 坚持跑步以来,到目前为止,自己体检时的各项身体指标已经全部正常,并且处于中间位置; 高血压、高血脂、高尿酸全部不见了; 体重也从90公斤减到了70公斤! 目前为止,生病感冒等现象也没发生过了! 2、精神状态 整个人的精神面貌也好了不少; 锻炼给人的感觉应该是要消耗大量的体力、导致身体疲惫。 但是实际上,锻炼之后只是当时比较累,事后反而会让你精力充沛,这是比较反直觉的! 3、其他 除了以上这些之外,实际上自己对于距离、速度的掌控也变得精确。 同时对于长跑也不再那么恐惧,目前已经参加了: 学校10公里微马拉松; 集团2次马拉松:团体赛、个人赛; 同时觉得也更能掌控自己的身体! 跑步时我在做什么 实际上这来自于知乎的一个问题: 《你跑步的时候在想什么?》 那要问跑步时我在做什么,实际上答案当然是:跑步! 这里说的是除了跑步之外,我在做什么,包括: 大脑在想什么; 耳朵在听什么; 眼睛在看什么; …… 大脑在想什么 实际上脑子里在想的,和知乎上有些人写的差不多; 更多的是在思考: 好痛苦啊?! 现在跑了多久了? 怎么还有5公里?! 速度怎么这么慢? …… 我在跑步的时候是思考不了其他事情的,我也很佩服那些大脑是多核处理器的人; 能在跑步的同时思考一些其他时间,我是不行; 这个过程对于我来说有点类似于冥想,只不过我是在匀速运动中冥想,而有的人是静止状态。 耳朵在听什么 大部分情况下都是在听自己的一些歌单; 关于这个歌单,后文会给大家介绍; 当然还有一些跑步的大牛会听一些播客; 不过和上面的原因一样,我感觉我听这些根本进不去脑子; 而且如果我听播客,我跑步的时候可能会很别扭。 所以我一般就是听歌曲; 眼睛在看什么 我基本上都是在运动场跑步,所以一般情况下都不会是我自己; 在跑步的时候,可以观察一下其他也在跑步的朋友,他的: 跑步姿势; 跑步速度; 跑了多久; …… 然后感叹:我靠他怎么这么厉害、我跟不上他了; 不过相信我,如果你能在运动场跑个一个小时以上的时间,基本上至少能熬走2-3波来锻炼的人! 一些相关的推荐 这里给大家分享一些我目前用到的一些工具等等; 歌单 我平时听的歌曲是一些节奏比较强烈的歌,尤其是鼓点最好能配上你的步频节奏那就更好了! 除此之外,我个人常听的一些歌是: 原神战斗bgm 例如: 虽然我已经好久都没玩原神了,但是听这些歌曲给我一种我自己正在游戏世界里奔跑游荡的感觉! 除此之外,你可以试试一些播客~ APP 一、跑步记录 跑步记录的 APP 我这里推荐几个; 1、Keep 可以在 Keep 里面设置自定义的训练内容,比如 5公里、10公里、15公里等; 然后在每一公里处都设置播报,当然你的速度快了或者慢了,Keep 也都有提示; 总体来说还是比较方便的; 2、Garmin Connect 由于我目前用的是佳明的手表,所以一般情况下都是使用手表记录跑步,然后通过 Connect 同步到云端; 当然,如果你有其他的手表,比如:Coros、Apple Watch、华为、小米 等等,也是可以的! 二、听歌 我一般使用的是 QQ 音乐,因为充了会员,歌曲还是比较多的; 除此之外,也推荐大家使用汽水音乐,他是字节推出的一个听歌APP; 使用汽水音乐有一个好处就是: 他的交互模式类似于抖音,上划就能下一曲了,比其他APP切歌方便的多! 同时,他的歌曲推荐也还可以! 如果你的手表包含了歌曲功能,也是可以的! 网站 一、RunningPage(强烈推荐) 这个是一个可以自行部署并生成你的跑步记录的网站: https://github.com/yihong0618/running_page 支持许多的手表设备和平台; 包括 Keep! 下面的这个网站就是我搭建的: https://jasonkayzk.github.io/running_page/ 搭建完成后,每天会自动获取你的运动数据,并且更新内容,非常的酷! 二、Garmin Connect 这个是佳明手表提供的平台: 同时,你也可以分享你的个人资料,比如下面是我个人的资料: https://connect.garmin.cn/modern/profile/1fbb8b4a-ba06-4c4a-bd9b-fc29524ec46f 佳明平台是分中国区和国际区的,数据是不同步的! 你可以使用下面这个仓库同步: https://github.com/gooin/dailysync-rev 三、Strava Strave 是一个跑步运动的平台,许多的大牛都在这个平台上; 国内可能无法访问! 你可以在这个平台关注一些你喜欢的跑者、运动员,看他们每天的训练内容、计划等等! 下面是我的个人网站: https://www.strava.com/athletes/145491210 Strava 中可以配置从其他平台同步数据(比如:佳明)! 当然,只能同步国际区的佳明数据,可以通过上面介绍的同步国际区的方式同步数据1 博主 这里我比较关注的有: 黑影儿TV:跑过100km! yihong0618:坚持跑步好多年的大佬,RunningPage的作者 其他: https://www.reddit.com/r/AdvancedRunning/comments/1aiugyx/running_influencers/?tl=zh-hans 装备 手表/手环:佳明、高驰、华为、小米都可以 手机 一双好的运动鞋 动漫书籍 动漫、电影、书籍都推荐:强风吹拂: 《風が強く吹いている》 后记 最后,说了这么多,实际上:一切的理论、装备都不如一份坚持重要。 我最近参加了几个业余组的比赛就发现: 比赛刚开始的时候,大家都是兴致勃勃,但是超过一公里之后的每一段距离,都有一部分选手选择停下来; 但是对于长跑来说: 重要的实际上并不是最开始或者最后的一公里,而是在开始前确定一个恒定的速度,然后按照这个恒定的速度一直坚持跑下去,像机器一样、节奏一直恒定的运行。 最后,也祝各位读者能够身体健康,每天突破自己的 PB!🎉

2025/5/4
articleCard.readMore

通过GithubActions拉取并推送Docker镜像到国内云

自从DockerHub在国内被墙之后,Docker镜像在国内的拉取一直是一个问题。目前有许多解决方案,比如:使用公开的镜像站、或者通过Cloudflare自建镜像站等等。但是都存在访问不稳定、配置麻烦等问题。 实际上,Github提供的Actions服务器就是在海外,可以通过Actions拉取Docker镜像,并推送到国内的云厂商,实现稳定的访问! tech-shrimp/docker_image_pusher 库就实现了这个功能! 通过GithubActions拉取并推送Docker镜像到国内云 使用方法 首先 fork 项目: https://github.com/tech-shrimp/docker_image_pusher 到本地,然后基本上根据 README 文档来即可; 需要在 fork 后的仓库的配置中配置 Secrets: ALIYUN_NAME_SPACE:你创建的阿里云的命名空间 ALIYUN_REGISTRY:阿里云的地址 ALIYUN_REGISTRY_USER:阿里云的用户名 ALIYUN_REGISTRY_PASSWORD:阿里云的密码 配置完成后直接修改 images.txt 文件即可直接拉取镜像并推送到阿里云的容器仓库! 附录 源码仓库: https://github.com/tech-shrimp/docker_image_pusher

2025/1/30
articleCard.readMore

使用AnythingLLM+SillconFlow+Milvus快速搭建个人云端知识库

最近DeepSeek如火如荼,由于目前我也有自己搭建个人知识库的需求,因此结合最新的一些技术搭建了一下。主要是包括: AnythingLLM平台 SillconFlow提供免费的大模型API支持 私有部署的Milvus向量数据库供多个人使用 源代码: https://github.com/JasonkayZK/docker-repo/blob/master/anything-llm.sh https://github.com/JasonkayZK/docker-repo/tree/milvus-standalone 使用AnythingLLM+SillconFlow+Milvus快速搭建个人云端知识库 环境搭建部分完全使用 Docker: 首先部署 Milvus、然后部署 AnythingLLM 平台,最后注册 SillconFlow 并对平台进行配置。 部署Milvus 部署 向量数据库 milvus 的部署比较简单,直接通过 docker-compose 部署即可: version: '3.5'services: etcd: container_name: milvus-etcd image: quay.io/coreos/etcd:v3.5.16 restart: unless-stopped environment: - ETCD_AUTO_COMPACTION_MODE=revision - ETCD_AUTO_COMPACTION_RETENTION=1000 - ETCD_QUOTA_BACKEND_BYTES=4294967296 - ETCD_SNAPSHOT_COUNT=50000 volumes: - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd healthcheck: test: ["CMD", "etcdctl", "endpoint", "health"] interval: 30s timeout: 20s retries: 3 minio: container_name: milvus-minio image: registry.cn-hangzhou.aliyuncs.com/jasonkay/minio:RELEASE.2023-03-20T20-16-18Z restart: unless-stopped environment: MINIO_ACCESS_KEY: minioadmin MINIO_SECRET_KEY: minioadmin ports: - "9001:9001" - "9000:9000" volumes: - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data command: minio server /minio_data --console-address ":9001" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 standalone: container_name: milvus-standalone image: registry.cn-hangzhou.aliyuncs.com/jasonkay/milvus:v2.5.4 restart: unless-stopped command: ["milvus", "run", "standalone"] security_opt: - seccomp:unconfined environment: ETCD_ENDPOINTS: etcd:2379 MINIO_ADDRESS: minio:9000 volumes: - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"] interval: 30s start_period: 90s timeout: 20s retries: 3 ports: - "19530:19530" - "9091:9091" depends_on: - "etcd" - "minio"networks: default: name: milvus 镜像使用的是我在阿里云上的,国内也可以直接使用; 使用: docker-compose up -d 部署即可; 配置密码 部署成功后,Milvus 的默认账号、密码为: rootMilvus 可以通过在部署的时候提供配置文件来配置账号密码,也可以部署后通过 Python 脚本部署; 例如: from pymilvus import MilvusClientclient = MilvusClient( uri='http://localhost:19530', # replace with your own Milvus server address token="root:Milvus")client.update_password('root', 'Milvus', '<your-new-password>', using='default')print(client.list_users()) 参考: https://www.milvus-io.com/adminGuide/authenticate 备注 在启动 standalone 类型的服务后, Web 界面会有 etcd unhealthy 的报错。 这个是正常现象,忽略即可; 参考: milvus-io/milvus#39417 注册SiliconFlow SiliconFlow 提供了各种大模型的 API,甚至有许多免费的版本; 可以通过下面的链接注册: https://cloud.siliconflow.cn/i/zA7ywKdU 链接包含我的邀请码,注册后可额外获得 2000w 的 token 额度。 注册成功后,可以在 API密钥 生成密钥; 保存,后面会用到! 部署AnythingLLM 部署 可以通过 Docker 直接部署 AnythingLLM: export STORAGE_LOCATION=$HOME/workspace/anythingllm && \mkdir -p $STORAGE_LOCATION && \touch "$STORAGE_LOCATION/.env" && \chmod -R 0777 $STORAGE_LOCATION && \docker run -d -p 3001:3001 \--restart=unless-stopped \--name my-anything-llm \--cap-add SYS_ADMIN \-v ${STORAGE_LOCATION}:/app/server/storage \-v ${STORAGE_LOCATION}/.env:/app/server/.env \-e STORAGE_DIR="/app/server/storage" \registry.cn-hangzhou.aliyuncs.com/jasonkay/anythingllm:latest 这里需要注意: Docker挂载目录需要给权限 0777,否则可能会报错无权限(尤其是root用户操作): https://github.com/Mintplex-Labs/anything-llm/issues/2564 配置 在配置 LLM 提供商 时,需要选择:Generic OpenAI 才能使用我们在 SiliconFlow 中的模型,随后配置: Base URL:https://api.siliconflow.cn API Key:你上面生成的API密钥 Chat Model Name:可在 https://cloud.siliconflow.cn/models 查找免费的模型(例如:Qwen/Qwen2.5-7B-Instruct) 在配置向量数据库时,可以直接使用 AnythingLLM 中内嵌的 LanceDB,此时无需配置; 当然,也可以使用我们上面已经部署好的 Milvus 数据库; 此时,只需要配置: Milvus DB Address:http://172.17.0.1 (可以使用 Docker 的gateway,也可以用其他的) Username、Password:使用你配置的或者默认的即可! 加载模型 上面配置完成后,即可正常使用对话功能; 但是,如果想要通过上传文档、图片等形成个人知识库,则还需要对数据进行向量化等操作,此时需要加载模型; 在没有挂梯子的情况下,在上传文档时会报错:fetch-failed-on-upload: https://docs.anythingllm.com/fetch-failed-on-upload#windows-visual-c-redistributable https://github.com/Mintplex-Labs/anything-llm/issues/821#issuecomment-1968382359 此时大概率是一位模型无法下载; 可以通过手动下载并加载的方式来解决: 参考: https://github.com/JasonkayZK/docker-repo/issues/4 首先,在 <your-anythingllm-path>/models/ 目录下创建 Xenova 目录; 随后下载模型压缩包,并上传到这个目录下: all-MiniLM-L6-v2.zip 最后解压缩即可: unzip all-MiniLM-L6-v2.zip 解压缩完成后效果: root@VM-12-16-debian:~/workspace/anythingllm/models/Xenova# tree ..└── all-MiniLM-L6-v2 ├── config.json ├── onnx │ └── model_quantized.onnx ├── tokenizer_config.json └── tokenizer.json2 directories, 4 files 功能测试 全部部署完成后,可以创建新的工作区,并且在工作区中创建 thread 进行新的对话; 同时在工作区中也可以上传文档,在对话时,AnythingLLM 会首先根据你的问题,在向量数据库中匹配,随后发给大模型作为 prompt 来生成对应回答! 结语 除了使用 Docker 来部署 AnythingLLM 之外,也提供了桌面的版本; Desktop 版本和 Web 版的配置非常类似,这里不再赘述! 附录 源代码: https://github.com/JasonkayZK/docker-repo/blob/master/anything-llm.sh https://github.com/JasonkayZK/docker-repo/tree/milvus-standalone

2025/1/29
articleCard.readMore

【顶】さよなら2024、こんにちは2025!

又是新的一年了,最近放寒假,终于能抽出时间来总结一下2024年、再展望一下2025年! 【顶】さよなら2024、こんにちは2025! さよなら2024! 前言 2024年对我个人来说,是转变极大的一年。 在这一年,我彻底放弃了程序员的职业,转而回家考取体制内,最终在八月份成为了一名家乡大专的计算机专业的老师。 除了职业上的转变,实际上也包含了心态上的转变。 在2024年的上半年,实际上花了很长时间继续去做一些技术上的突破,比如: 学习了一些数据存储的格式,例如:bamboo; 学习了分布式系统相关的时钟逻辑,也用 rust 实现了一个:merkle-trie-clock; 学习了 CRDTs 相关的内容,用 rust 写了一个简单的 CRDTs 的demo; 看了数据库内核的相关实现,打算尝试去写一个基于 CRDTs 理论的分布式数据库; …… 但是,后面发现对于技术的热情实际上是有所下降的。 与其说是对于技术的热情有所下降,不如说是更多是想分享知识,而非单纯的研究技术; 当然,除了研究技术之外,上半年也准备了一下考试。 别忘了,我是学电气工程出身的! 为了准备相关的考试,又重新复习了一下电气工程的相关书,包括: 电网招聘考试书 注册电气工程师 原来本科的书 行测等 …… 最后也是通过笔试第二、面试第一成功选择了学校的岗位。 有些人可能会奇怪,电气工程专业为啥当上了计算机的老师; 实际上我在去学校报道的时候,正好碰到了计算机学院的院长,然后就被拉到了计算机学院😁,巧了! 学校生活 八月份入职之前,其实提前准备了一些教师相关的东西,比如: 如何备课 如何上好一门课 上课期间站姿、手势、仪态等等 但实际上刚入职的老师会被要求担任辅导员; 辅导员 作为初入学校的「しろうと」来说,辅导员的挑战实际上是有的。 对于2024年新入学的学生来说,大致是在06年出生,和我相差大概10岁左右。 但是实际上,辅导员算是一个半管理岗位,为啥说是“半”呢? 因为,每天一睁眼,几十号人的吃喝拉撒基本上都要你管! 但是实际上会有些紧张,这个紧张一方面是:和一群「歳下」 的孩子们接触: 如何和这些同学接触? 如何融入学生? 进一步,如何管理这些学生?(达成共识?解决冲突?) 实际上,这些工作和程序员的工作思路是完全不同的事情! 另一方面是:如何正确的引导这些学生? 思想上 三观上 我自以为,老师和其他相关的职业有一个很大的不同在于: 一个好的老师真的可以拯救学生,而一个不负责任的老师真的也能毁掉无数个学生 我自认为自己还算是一个做事负责任的人吧! 实际上你的言行真的会影响到很多学生、尤其是刚刚成年的学生未来的三观。 但是对于如何做,实际上当时我是毫无头绪的! 但是当时我给自己定下了一个最低的底线: 一定不要成为自己最讨厌的那种老师! 现在看来,这个底线实际上也并不低! 首次接触学生 我第一次接触学生实际上是在新生报道之前。 因为我之前是程序员的缘故,接触过不少云计算相关的内容,所以我的导师王老师让我来带一带竞赛的学生; 实际上这个决定让我首先小范围的接触了几位同学; 对我后面工作的开展获益匪浅; 所以,在还没开学之前就了解了目前这个年龄段的学生脑子里想的是什么,学生的层次、一些学校的规则制度、运行模式等等。 同时也能够小范围的先锻炼锻炼自己的表达能力… 关于竞赛的事情,后文再继续聊~ 迎新活动 当新生辅导员的第一件事实际上是为全班的新生建群。 好在我带的班学生都比较积极,基本上一天之内就建好了! 到了24级正式开学的时候,实际上今年一共招了将近900号学生,要对这些学生进行信息采集、确认; 当时在学院楼外面坐了整整两天,说话说的嗓子都要哑了,最后也是顺利的把这些熊孩子给安置好了。 第一次带军训 迎新结束之后,马不停蹄的就是学生的军训; 比起我当年的军训,个人觉得00后的军训强度要低太多了; 可能是身体素质整体都下滑,军训还没开始,刚刚站了十几分钟就已经有晕倒的了。 我带的班,军训的两个星期也是请假无数……; 总而言之,身体素质在00后这一代真的堪忧……。 第一次班会 在军训期间,要开一个班会,和班里的同学都见见面。 这也是我第一次以辅导员的身份站在讲台上; 其实刚开始还是有些紧张的,毕竟下面坐着几十个完全不认识的熊孩子; 需要做的实际上是提前准备班会的内容,可以先记在笔记本上,然后带过去讲就行; 后面发现,站上去之后讲的挺自然的,就不紧张了… 当然,现在在上面时间长了,早就没什么感觉了… 第一次出差 在九月底还没到十月份的时候,接到了通知要带几个学生去开封参加比赛,这也是我第一次带学生外出比赛; 比较好的是,来回的路途是由市政府安排的专车接送,所以不需要提前订票了; 但是当时的酒店我们希望能拼房,所以出来不小的问题,就是和我们拼房的人提前3天走了,导致我们无房可拼; 不过后面也是找到了开封市的人社局给解决了; 本次比赛的成绩可以说是非常惨淡:参与了3个赛项,无一获奖! 实际上一个比较关键的原因是:竞赛的学生基本上都是靠自己自学,而没有老师带! 这些学生会浪费大量的时间在寻找学习材料和学习路径上,这是很可怕的事情! 这也让我感受到了,学校的竞赛之路,任重而道远啊! 一些其他的碎碎念 除了上面的事情之外,实际上还有很多其他的事情,下面就简单列出一部分: 学生宿舍值班 学生日常请假管理 学生心理问题排查 班级活动组织 …… 最后想再说一下: 我最开始以为,来到大专的学生大多数是对学习毫无兴趣的学生; 但是后面接触之后我发现,实际上这里大多数学生都来自农村、乡镇(至少我班里只有4、5个是城市户口); 他们先天就没有特别好的教育环境。 以我带的竞赛的一个学生为例:这个学生白天的时候要去干农活、晚上在家做完饭之后,才能抽出时间来学习云计算; 我当时听完之后感觉到挺不可思议的。 当时我还戏称自己是“上山下乡”…… 教学 竞赛培训 作为一个老师而言,其实最基本的一个工作就是讲课; 分享的经验实际上在之前当程序员的时候是有的,当时有大量的技术分享会,而且我也参与过一次线上的直播分享; 但是实际上对于这些学生的水平,我是拿不准的,不知道这些学生对于知识的掌握程度,这个是很致命的! 好在有个学生掌握的还可以,但是没有老师带,实际上还是挺吃力的,一直在自己部署 OpenStack,对于 Docker、K8S 都不知道是什么; 我也是从 Github、git、一路讲到 Docker! 实际上,如果真正感兴趣、用心学的学生,他的学习能力是惊人的! 最后也是拿了省二等和国家三等奖的成绩。 实际上这个成绩只是个开始,后面还要继续努力! 第一次试讲 关于这个试讲,这里说一下。 新入职的老师是不会直接参与讲课的,只有通过了:教研室、系部、学校,三个阶段的试讲才能上课。 试讲的内容是自己选择一门最拿手的课程,然后进行无生试讲,大概 15 分钟左右。 我的试讲内容实际上是云计算,讲的 Docker。 由于没有什么经验,同时备课准备的实际上不是很充分,所以课讲的稀烂…… 甚至讲课的状态是我最讨厌的念 PPT 的方式……! 当时主任给我提了很多很实用的整改意见,我这里大概总结了几条: PPT 的内容应该少文章多图片,这样比较生动; 讲课的时候应该多举一些生活中的例子,而不只是念ppt的内容; 在讲的过程中,声音应当抑扬顿挫,有节奏,类似于唱歌; …… 这里非常感谢主任给我提的这些意见,让我在后面找到了讲课的方向,慢慢摸索出了如何讲课! 实际上还有另外一个问题,就是:当时我刚来的时候就是给参与竞赛的学生讲,但是这些学生实际上水平、学习热情是远远高于一般的学生的! 实际上这也导致了,后面在真正上课的时候,一开始上课的内容实际上学生并不能很好的掌握(甚至部分学生别崩溃)! 后面,由于第一次试讲讲的并不是很好,所以后面又陆陆续续讲了几次试讲,进步其实是很明显的! 新生分享、社团分享 在第二次试讲之前,为了锻炼一下自己的讲课能力,参与了一些其他课程的讲授。 主要是几次的新生分享: 第一次是给24级新来的计算机专业的新生讲解目前一些新的主流技术,比如:云计算、大数据、人工智能相关的内容; 第二次是给学校的本科学生讲解:大学时间规划、考研等相关的内容; 相比于第一次分享,第二次是受到学校的另一个学院的院长邀请,形式上也比较正式,是在学校图书馆一楼的报告厅,大概有几百号学生参与; 实际上准备的并不是很充分,但是不知道是不是因为讲的都是自己的经历,反而没什么可紧张的,讲的过程也很顺利。 实际上,在台上看到下面坐着的许多学生心里还是几多感慨: 大概十年前,我和他们是一样的迷茫,不知道自己何去何从,不知道自己要干什么…… 所以当时,上台之前,我想做的实际上就是分享自己的经历,包括好的和坏的。让这些学生有所借鉴,能走出属于这些孩子自己的一条路! 同时,由于我是学校计算机社团的指导老师,在后面也给社团讲了一次课(大概2个小时的时间)。 主要是分享(或者说科普)一些计算机相关的内容。 来参与的学生实际上并不只是计算机专业的学生,但是除了基础的 Python 之外,我还是讲了一些其他的内容。 比如: 人工智能的基本使用方法; Github 网站 一些写代码的工具 …… 基本上每次分享,我都要讲上面的一些内容; 尤其是 Github,我觉得如果从事计算机专业,这个网站是必须要知道的! 但是国内很多高校的学生,甚至到毕业都不知道这个网站! 个人觉得,学会并使用 Github 是从事计算机行业最基本的一项内容! 第二次试讲 第二次试讲来的比较匆忙,从通知到试讲实际上就间隔了两三天时间。 并且由于和之前第一次试讲间隔了太长的时间,实际上之前试讲的内容都忘的差不多了。 好在 “瘦死的骆驼比马大”! 所以最后还是顺利的拿下了。 当然,当时院长也提出了不少的意见,比如: 讲课过程中,动作太多,一直来回走 PPT 的标题不统一 …… 紧急代课! 十一月底的时候,由于有一位老师提前走了,他这学期有两门课需要我临时带,所以强度突然上来! 当时是周五的晚上通知我,下周一就要上课了! 由于是第一次正式上课,很多规矩都还不清楚,而且当时是临时代课,还不知道之前的内容。 所以花了一些时间将要做的按照先后顺序事情归了一下类: 添加原来的老师、班委,询问课程进度 拿之前老师的教学资料 提前备课 询问上课的流程(包括如何签到、课前的点名、下课的内容等等) …… 比较巧的是,我带的其中有一门课,在还没开学的时候,提前备过课,所以可以直接拿过来讲。 所以第一节课实际上马马虎虎,没有讲什么具体的操作内容,主要是对大数据的技术做了一个科普。 但是后面的课程实际上是出了一些小状况的: 1、由于这些学生之前的基础没有打好,所以导致我在讲课的时候,班里一大半的学生根本不知道如何实操! 实际上,使用到的都是一些非常讲的的 Shell 命令! 对于我之前带的竞赛的学生来说是非常简单的,但是对于来上课的普通学生来说,感觉比登天还难! 所以后面又先带着“复习”了一下 Linux 的内容。 2、另外一个状况是:在机房里面,实际上是没有部署相应的软件的,所以学生没办法跟着一起练习! 所以,我专门抽了一个下午的时间,做了一个 VMWare 的镜像给到学生,让学生们可以直接通过这个镜像练习! 最开始我做的是一个无图形界面的系统,但是发现这些学生不会用…… 所以没办法,又做了一个 Desktop 版本的…… 不管怎么样,最后也是把课、期末考试、教学材料跌跌撞撞的走完了! 虽然比较生涩,但是确实把: 如何上课 如何设计课程 如何设计期末考试 如何整理教学材料 …… 在高强度之下快速的摸清楚了! 第三次试讲 第三次试讲是由学校来组织的,来参与的都是新老师,但是还是收获满满。 我由于之前的很多经历,准备的比较充分,所以没什么可紧张的。 但是,在听其他老师试讲的过程中,实际上发现自己和他们还是有非常大的差距的! 尤其是有几个音乐专业的老师,讲的生动形象,我和他们比起来可以说是相形见绌…… 并且,我在试讲的时候,写板书写了一分多钟,被评委给吐槽了…… 一些工作上的总结 一些心得和分享 上面是半个学期的一些工作内容。实际上,除了上面列出的一些工作之外,还有很多其他的工作没有列出来。 可以说,虽然是一个新的老师,但是工作还是非常充实的! 下面列一些我觉得这学期工作的时候,比较实用的一些技巧和心得。 待办清单(小米云服务) 这个是我目前为止,基本上离不开的一个东西! 因为辅导员的事情实际上是非常多的,基本上每次开会都要安排十几件事情! 如果没有个待办清单,很多事情都会忘掉! 我之前尝试过一些其他的 TODO 应用,但是感觉还没有小米自带的好用。 小米的待办清单在 Windows 上有 MIUI+ 可以同步,但是我用的是 Mac,没有这个软件。 但是有一个 小米云服务,所以可以曲线救国,用这个! 这里面唯一一个我用的功能就是待办清单! 顺便吐槽一下,MIUI+ 说了好多年适配 Mac 了,到现在还没有做出来…… 和学生打交道 有的人可能会对辅导员这个职业比较抗拒,但是实际上我倒是觉得这是为数不多比较暖心的工作了… 可能是因为我和这些孩子们的年龄差距不大,所以并没有那么多代沟,所以沟通起来还是比较轻松的。 而且也都经历过学生时代,对于这些学生心里是怎么想的、比较反感的东西实际上是一清二楚的。 当时给自己定的目标就是: 从群众中来,到群众中去! 学生们的事情尽量身体力行的去做! 不要成为自己讨厌的那一类辅导员! 基本上每周六晚上我值班的时候,都会和班里的学生打打篮球。 然后时不时的在班级群里吹吹牛啥的,也挺欢乐。 但是确实,有的事情确实是没有办法,dddd…… 讲课 对于讲课来说,是一个老师的基本功。 一个感受比较深的点就是: 一定要备课! 一定要备课! 一定要备课! 不备课的课和备过课的课上起来是完全不一样的! 备课的时候,主要是记一下这节课的主要大纲,有哪些重点难点什么的。 然后就是提前准备一些和课程有关的例子,如果不熟悉可以提前先打印出来,然后用笔标注一下。 认真备课之后,讲出来的东西可能才是你想要表达的; 如果不备课,虽然很多东西都是你用到的很基础的内容,当时实际上有的时候也很难讲出来! 我刚来学校的时候,主任和我分享的一句话就是: 备课实际上是备学生,你要了解你上课的班的学生的整体状态! 需要继续加强的地方 首先就是做事方面: 很多事情不懂得推脱,基本上只要有能帮忙的地方,都会去揽活来做,把自己搞得比较累。 有些时候如果自己比较忙,实际上是可以拒绝的! 另外就是讲课方面: 基本上现在很多课程都还需要很长的时间备课,同时讲课的能力方面实际上还有很大的进步空间。 心态方面: 很多事情在做的时候,还是会比较急,心态容易出问题。 这可能在未来的工作中还需要进一步沉淀沉淀! こんにちは2025! 聊完了已经过去的 2024 年,下面是我对于 2025 年的一些规划: 证书: 高校教师资格证 + 普通话证 通过软考中级(软件设计师) 日语N2证书 雅思证书(待定) 跑步: 年跑量 1000km 参加一次半马 旅游: 去一次日本旅游 购物: 40系 N 卡 佳明 255 跑表 内容可能不是很多,但是是一些目前我能够想到的东西; 如果后面有新加入的内容,也会再加进来! 最后,祝愿各位在新的一年都能顺顺利利,无限进步! 今年も、よろしくお願いします!

2025/1/17
articleCard.readMore

Excel通过身份证号列计算性别

Excel统计信息中有身份证号,可以通过身份证号直接生成性别列; Excel通过身份证号列计算性别 例如,B2 列为身份证号所在列,则可以在性别列(如D2列)填写: =IF(MOD(MID(B2, 17, 1), 2) = 1, "男", "女") 输入后回车计算结果即为性别; 可以下拉直接生成全部结果! 参考: https://jingyan.baidu.com/article/5553fa8291d46b65a2393432.html

2024/9/11
articleCard.readMore

开源的个人书籍管理系统Talebook

我平时看的都是PDF电子书籍,但是之前没有用书籍管理,所以书籍比较乱; 比较有名的书籍管理系统有Calibre,但是Talebook支持OPDS,同时能从豆瓣导入信息,比较好用; 本文介绍了如何部署和配置Talebook; 源代码: https://github.com/JasonkayZK/docker-repo/tree/talebook 开源的个人书籍管理系统Talebook 部署 项目地址如下: https://github.com/talebook/talebook DockerHub地址: https://hub.docker.com/r/talebook/talebook B站上也有UP主对Talebook做了介绍: https://www.bilibili.com/video/BV1AT411S7c3/ 部署起来也是非常简单,先把镜像拉下来,然后 docker-compose 就行: docker-compose.yml version: "3"services: talebook: container_name: talebook image: talebook/talebook:v3.8.1 volumes: - /data/talebook:/data ports: - "80:80" - "443:443" environment: - PUID=1000 - PGID=1000 - TZ=Asia/Shanghai # 调整为『SSR=ON』可开启「服务器端渲染」模式,对于搜索引擎更友好,同时更消耗服务器性能 - SSR=OFF depends_on: - douban-rs-api restart: always douban-rs-api: container_name: douban-rs-api # https://github.com/cxfksword/douban-api-rs image: ghcr.io/cxfksword/douban-api-rs:latest restart: always 配置都比较简单,目录挂载、端口映射直接根据自己的需求修改即可! 配置 基本的配置,上面的B站UP基本上都讲了; 需要注意的是: 配置豆瓣的时候,url 结尾的 / 要删除!否则会无法使用! 参考: https://github.com/talebook/talebook/issues/340#issuecomment-2097703672 其他 支持 OPDS 的 APP 推荐: 安卓:静读天下 iOS、MacOS:Yomu、KyBook 参考: https://gameapp.club/post/2022-12-25-ebooks/ 需要注意的是: 如果要使用 OPDS,需要配置: 关闭「私人图书馆」模式。 打开「允许任意下载」(访客无需注册或登录) 参考: https://github.com/talebook/talebook/blob/master/document/README.zh_CN.md#%E9%9D%99%E8%AF%BB%E5%A4%A9%E4%B8%8Bapp%E9%87%8C%E8%AE%BF%E9%97%AE%E4%B9%A6%E5%BA%93%E4%BC%9A%E5%A4%B1%E8%B4%A5%E6%80%8E%E4%B9%88%E5%8A%9E Enjoy! 附录 源代码: https://github.com/JasonkayZK/docker-repo/tree/talebook 参考: https://www.bilibili.com/video/BV1AT411S7c3/ https://blog.hsu.life/2024/01/14/%E6%89%8B%E6%9C%BA%E5%A6%82%E4%BD%95%E6%96%B9%E4%BE%BF%E7%9A%84%E4%BD%BF%E7%94%A8%E4%B9%A6%E5%BA%93/index.html https://gameapp.club/post/2022-12-25-ebooks/

2024/8/22
articleCard.readMore

2024年安装Docker的方法

阿里云的Docker源没了,本文写了在2024年如何安装Docker、配置DockerHub源; PS:没想到2024年了,还在写Docker安装的教程… 2024年安装Docker的方法 安装Docker 目前(2024年8月22日),阿里云已不再提供 Docker 源的安装(404了); 腾讯云目前还是提供的,安装教程如下: https://cloud.tencent.com/document/product/213/46000#C_XgAwZpjht292j2EOU2t Ubuntu的安装如下: sudo apt-get updatesudo apt-get install ca-certificates curlsudo install -m 0755 -d /etc/apt/keyrings# Debian系统将ubuntu改为debian!sudo curl -fsSL https://mirrors.cloud.tencent.com/docker-ce/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.ascsudo chmod a+r /etc/apt/keyrings/docker.asc# Debian系统将ubuntu改为debian!echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://mirrors.cloud.tencent.com/docker-ce/linux/ubuntu/ \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullsudo apt-get update 需要注意的是:如果是Debian系统,需要将上面的ubuntu改为debian即可! 安装: sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin Docker镜像源 众所周知,由于不可抗因素,国内是无法在 DockerHub 上 pull 镜像的,Github 的 ghcr.io 也不行; 下面的网址提供了一些目前可用的镜像站: https://taimatsu.xlog.app/china-mirror-proxy?locale=zh https://qinyang.wang/china-mirror-proxy https://github.com/cmliu/CF-Workers-docker.io 以及使用 CF 搭建镜像站的教程: https://www.lincol29.cn/cloudflaretodocker https://github.com/cmliu/CF-Workers-docker.io 附录 参考: https://cloud.tencent.com/document/product/213/46000#C_XgAwZpjht292j2EOU2t

2024/8/22
articleCard.readMore

Python项目Linter、Formatter和Github-Actions配置

关于Python项目的一些配置问题,包括: Linter Formatter Github-Actions配置; 源代码: https://github.com/JasonkayZK/python-learn Python项目Linter、Formatter和Github-Actions配置 Linter Linter 使用的是 PyCharm 自带的,符合 PEP8 规范; 也可以使用: flake8 pylint Formatter 直接用 isort 和 black 就可以了: https://github.com/psf/black https://github.com/pycqa/isort 项目配置 Pre-Commit: .pre-commit-config.yaml repos: - repo: https://github.com/psf/black rev: 23.1.0 hooks: - id: black - repo: https://github.com/pycqa/isort rev: 5.12.0 hooks: - id: isort PyProject: pyproject.toml [tool.isort]profile = "black" 依赖配置: requirements.txt -e .[all]... requirements-dev.txt -r requirements.txt# Ciblackisort Github Actions: .github/workflows/ci.yaml name: CIon: workflow_dispatch: push: pull_request:jobs: lint_and_test: runs-on: ubuntu-latest strategy: max-parallel: 4 matrix: python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} id: setup_python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: 'pip' cache-dependency-path: 'requirements-dev.txt' - name: Install dependencies run: | pip install -r requirements-dev.txt - name: Run lint uses: pre-commit/action@v2.0.0 附录 源代码: https://github.com/JasonkayZK/python-learn 参考: https://v2ex.com/t/587696 https://github.com/psf/black https://github.com/pycqa/isort

2024/8/21
articleCard.readMore

Zerotier配置内网流量转发

在上一篇文章《简单易用的内网穿透组网工具ZeroTier》中,简单介绍了组网工具Zerotier; 实际上,我们可以通过Ip Forward的方式来访问组网设备内网下的其他设备! Zerotier配置内网流量转发 首先,在这台机器上配置 IP 转发: sudo sysctl -w net.ipv4.ip_forward=1sudo sysctl -p 查看网卡配置: ip aens33: 192.168.117.0/24ztnfanm5kw: 192.168.196.220 在zerotier网站设置转发规则: 在这台流量转发机器上设置环境变量: export PHY_IFACE=ens33 # 物理网卡export ZT_IFACE=ztnfanm5kw # Zerotier虚拟网卡 添加规则到iptables: sudo iptables -t nat -A POSTROUTING -o $PHY_IFACE -j MASQUERADEsudo iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPTsudo iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT 这三条 iptables 规则用于配置网络地址转换(NAT)和数据包转发; 含义如下: 规则 1: sudo iptables -t nat -A POSTROUTING -o $PHY_IFACE -j MASQUERADE 这条规则在 nat 表的 POSTROUTING 链中添加了一条规则。-o $PHY_IFACE 表示这条规则适用于所有通过 $PHY_IFACE 这个网络接口(通常是物理接口)出去的数据包。-j MASQUERADE 指定了 NAT 操作中的伪装(masquerading)。这意味着,当数据包从 $PHY_IFACE 发送出去时,其源 IP 地址会被替换为 $PHY_IFACE 的 IP 地址。这通常用于允许内部网络通过一个公共 IP 地址进行外部通信; 规则 2: sudo iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT 这条规则在 filter 表的 FORWARD 链中添加了一条规则。-i $PHY_IFACE 表示适用于从 $PHY_IFACE 这个接口进入的数据包,-o $ZT_IFACE 表示这些数据包要转发到 $ZT_IFACE 这个接口。-m state --state RELATED,ESTABLISHED 指定了只有那些与已有连接相关或已经建立的连接的数据包才被接受(ACCEPT)。这个规则通常用于允许来自外部网络的返回流量进入内部网络,从而支持诸如 HTTP 会话等; 规则 3: sudo iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT 这条规则在 filter 表的 FORWARD 链中添加了一条规则。-i $ZT_IFACE 表示适用于从 $ZT_IFACE 这个接口进入的数据包,-o $PHY_IFACE 表示这些数据包要转发到 $PHY_IFACE 这个接口。-j ACCEPT 表示这些数据包会被接受并转发。这条规则允许来自 $ZT_IFACE 的流量经过路由器转发到 $PHY_IFACE,从而实现网络之间的数据传输; 总结: 第一条规则用于设置源地址伪装,允许内部网络设备通过一个公共 IP 地址进行外部通信; 第二条规则允许返回流量和已经建立的连接的数据包从外部网络进入内部网络; 第三条规则允许来自内部网络的数据包被转发到外部网络; 保存配置到文件: iptables-save 此时就可以用另一台加入了此Zerotier网络的机器访问内网机器的其他电脑了! 附录 文章参考: https://blog.51cto.com/u_6364219/5264985

2024/8/21
articleCard.readMore