Kubernetes学习指北之多节点集群(Multi Node Cluster)

Kubernetes学习指北之多节点集群(Multi Node Cluster) 一、 多节点集群(Multi Node Cluster)说明 真实生产环境的缩影,可以模拟调度、高可用、跨节点通信 特点 Master 节点负责管理集群 多个 Worker 节点运行实际应用 Pod Master 负责调度、管理和监控 高可用性可扩展 可以增加 Worker 节点提升容量 Master 可做 HA(高可用)部署 真实调度场景 Kubernetes 会根据资源和策略,将 Pod 调度到不同节点 网络复杂 Pod 网络跨节点,需要 CNI 插件(Flannel、Calico 等)支持 适用场景 生产环境部署微服务 测试调度策略、Pod 分布、Service 负载均衡 实战演练集群管理和高可用 优点 模拟真实生产环境 可以横向扩展容器数量 Master 故障可配置高可用 缺点 配置复杂,需要多台虚拟机或服务器 网络和存储管理比单节点复杂 资源占用高 二、 准备环境和工具 这里使用centos7环境 说明 sudo -i 建议用root账号操作 每台节点都需要执行的操作,包括 master 和 worker:系统初始化、安装和配置容器运行时(containerd)、镜像源替换等操作 这里不再赘述,参照另一篇:Kubernetes学习指北之单节点集群 三、节点准备 在每台节点上安装: # 安装 kubeadm、kubelet、kubectl yum install -y kubelet kubeadm kubectl # 启动 kubelet systemctl enable --now kubelet # 关闭 swap(K8s 要求) swapoff -a sed -i '/swap/d' /etc/fstab # 配置 sysctl cat <<EOF | sudo tee /etc/sysctl....

2025年05月01日 · 3 min · Leanku

选择开源许可证

为什么需要开源许可证 把代码发布到 GitHub,并不意味着其他人就自动获得了自由使用、修改和分发代码的权利。 开源许可证的作用,就是明确告诉其他人:这个项目可以怎么使用、能不能修改、能不能商业使用、修改后是否需要公开源码,以及分发时需要保留哪些声明。 Choose a License 将开源许可证按照限制程度进行了整理,从 GNU AGPLv3、GNU GPLv3、GNU LGPLv3,到 MPL 2.0、Apache License 2.0、MIT License,再到基本没有额外条件的许可证,形成了一个从强 Copyleft 到宽松许可的连续范围。 因此,选择许可证真正需要考虑的不是「哪个许可证最好」,而是: 你希望别人如何使用你的代码? 如果希望别人可以自由使用,包括商业项目和闭源项目,通常应该选择宽松许可证;如果希望衍生作品继续保持开源,则应该考虑 Copyleft 许可证。 开源许可证主要解决什么问题 选择许可证之前,需要先理解几个核心问题。 商业使用 商业使用是很多开发者首先关注的问题。 主流开源许可证通常允许商业使用,包括 MIT、Apache License 2.0、GPL、LGPL、MPL 和 AGPL。区别并不在于「能不能赚钱」,而在于商业使用过程中需要履行什么义务。 例如: MIT:可以用于商业软件,也可以制作闭源商业软件。 Apache 2.0:可以用于商业软件,并提供明确的专利授权。 GPL:可以用于商业软件,但分发衍生作品时需要遵守 GPL 的开源要求。 AGPL:可以用于商业软件,同时对网络服务场景增加了更强的源码公开要求。 因此,「允许商业使用」并不等于「可以随便闭源」。 修改和再分发 大多数主流许可证都允许修改代码,但对修改后的代码如何发布存在明显区别。 宽松许可证通常允许: 开源项目 ↓ 修改 ↓ 加入商业项目 ↓ 发布闭源版本 而 Copyleft 许可证则通常要求符合条件的衍生作品继续采用相同或兼容的许可证,并提供对应源码。 是否必须公开源码 这是选择许可证时最重要的区别之一。 可以简单理解为: MIT / Apache 2.0 ↓ 允许闭源 ↓ 商业软件可以不公开源码 GPL ↓ Copyleft ↓ 符合条件的衍生作品需要开放源码 LGPL / MPL ↓ 弱 Copyleft ↓ 只对特定范围的修改要求开放 AGPL ↓ 强 Copyleft ↓ 进一步覆盖网络服务场景 需要注意的是,具体的源码公开义务取决于许可证定义的触发条件,不能简单理解成「用了 GPL 就必须把整个公司所有代码开源」。...

2025年04月13日 · 4 min · Leanku

GitHub 项目版本管理规范

GitHub 项目版本管理规范 Git 标签(Tags)和 发布(Releases) 在 GitHub 项目中定义和管理版本,通常结合 Git 标签(Tags)、发布(Releases) 和 语义化版本(SemVer) 来实现。以下是具体方法和最佳实践: 1. 使用 Git 标签(Tags)定义版本 (1)创建版本标签 # 创建轻量标签 git tag v1.0.0 # 创建附注标签(推荐,包含提交信息和签名) git tag -a v1.0.0 -m "Release version 1.0.0" # 推送标签到 GitHub git push origin v1.0.0 (2)语义化版本(SemVer) 版本号格式:MAJOR.MINOR.PATCH MAJOR:不兼容的 API 变更 MINOR:向后兼容的功能新增 PATCH:向后兼容的问题修复 示例: v1.0.0:初始稳定版本 v1.2.3:第 1 个大版本,第 2 次功能更新,第 3 次补丁 2. 通过 GitHub Releases 管理版本 (1)手动创建 Release 进入 GitHub 仓库 → Releases → Draft a new release...

2025年04月11日 · 2 min · Leanku

GitHub 项目版本管理规范

GitHub 项目版本管理规范 Git 标签(Tags)和 发布(Releases) 在 GitHub 项目中定义和管理版本,通常结合 Git 标签(Tags)、发布(Releases) 和 语义化版本(SemVer) 来实现。以下是具体方法和最佳实践: 1. 使用 Git 标签(Tags)定义版本 (1)创建版本标签 # 创建轻量标签 git tag v1.0.0 # 创建附注标签(推荐,包含提交信息和签名) git tag -a v1.0.0 -m "Release version 1.0.0" # 推送标签到 GitHub git push origin v1.0.0 (2)语义化版本(SemVer) 版本号格式:MAJOR.MINOR.PATCH MAJOR:不兼容的 API 变更 MINOR:向后兼容的功能新增 PATCH:向后兼容的问题修复 示例: v1.0.0:初始稳定版本 v1.2.3:第 1 个大版本,第 2 次功能更新,第 3 次补丁 2. 通过 GitHub Releases 管理版本 (1)手动创建 Release 进入 GitHub 仓库 → Releases → Draft a new release...

2025年04月11日 · 2 min · Leanku

Markdown 编写规范

1. 文件规范 1.1 文件扩展名 Markdown 文件必须使用 .md 扩展名。 正确: docker-installation.md docker-compose.md mysql-index.md 错误: docker-installation.markdown docker-installation.txt 1.2 文件名 文件名统一使用小写字母。 多个单词之间使用 - 分隔,不使用空格、下划线或驼峰命名。 推荐: docker-installation.md docker-image-optimization.md mysql-deep-pagination.md 不推荐: DockerInstallation.md docker_installation.md docker installation.md 1.3 文件编码 Markdown 文件必须使用 UTF-8 编码。 2. 标题规范 2.1 文档标题 按照 FEX 规范,文档标题可以使用 Setext 风格: Markdown 编写规范 ================ 如果项目使用 Hugo 等现代 Markdown 工具链,也可以使用: # Markdown 编写规范 但是,如果严格遵循 FEX Team 原规范,则推荐使用 Setext 风格作为文档标题。 对于 Hugo 技术文章,如果 Front Matter 已经提供 title,正文通常不再重复一级标题。 例如:...

2025年04月03日 · 6 min · Leanku

PHP + Redis实现订单超时

PHP + Redis实现订单超时 针对秒杀场景下的订单超时未支付自动取消,这是一个典型需求。由于秒杀需要快速释放被占用的库存,所以超时机制的设计非常关键。 一、核心思路 订单创建时:记录超时时间(如 30 分钟),将订单 ID 推入 Redis 的 Sorted Set(有序集合),score 为超时时间戳。 定时轮询:启动一个 PHP 常驻进程,每隔 1-2 秒轮询 Sorted Set,取出已超时的订单 ID。 超时处理:更新 MySQL 订单状态为“已取消”,并恢复 Redis 中的秒杀库存(如果业务要求释放库存)。 无需额外组件:纯 Redis + PHP,不依赖 RabbitMQ 的延迟队列或 RocketMQ。 二、详细设计 1. 订单表增加字段 ALTER TABLE orders ADD COLUMN status TINYINT DEFAULT 0 COMMENT '0-待支付 1-已支付 2-已取消 3-已超时'; ALTER TABLE orders ADD COLUMN expire_time DATETIME COMMENT '订单超时时间'; 2. 创建订单时:加入超时监控 在秒杀成功、订单写入 MySQL 后(或写入 Redis 队列时),同时将订单 ID 加入 Redis Sorted Set:...

2025年03月28日 · 4 min · Leanku

PHP 实现 Redis 高并发解决方案

PHP 实现 Redis 高并发解决方案 使用 PHP 实现 Redis 在缓存加速、分布式锁和队列场景中的应用。 首先确保已安装 PHP Redis 扩展 一、缓存加速实现 1. 基本缓存操作 <?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379); // 设置缓存 function setCache($key, $value, $expire = 3600) { global $redis; $serialized = serialize($value); return $redis->setex($key, $expire, $serialized); } // 获取缓存 function getCache($key) { global $redis; $serialized = $redis->get($key); return $serialized ? unserialize($serialized) : false; } // 删除缓存 function deleteCache($key) { global $redis; return $redis->del($key); } // 示例:用户数据缓存 function getUser($userId) { $cacheKey = "user:{$userId}"; $user = getCache($cacheKey); if ($user === false) { // 模拟数据库查询 $user = [ 'id' => $userId, 'name' => 'User ' ....

2025年03月28日 · 5 min · Leanku

内存溢出

内存溢出 一、什么是内存溢出 内存溢出(Memory Overflow / Out of Memory, OOM)指程序在申请内存时,没有足够的空间供其使用。在 PHP 中,通常表现为: 脚本超出 memory_limit 设置(默认 128M),抛出 Fatal error: Allowed memory size of X bytes exhausted 系统级 OOM:PHP 进程消耗了操作系统分配的所有内存,被内核强制杀死(常见于常驻进程如 Swoole、Workerman) 二、如何排查内存溢出? 1. 查看错误日志 PHP 错误日志中会直接记录内存耗尽的位置和当前内存使用量。 系统日志(/var/log/messages 或 journalctl -xe)可看到是否因 OOM Killer 杀死了进程。 2. 使用内存分析工具 Xdebug 的追踪功能 xdebug.profiler_enable = 1 xdebug.profiler_output_dir = /tmp 生成 cachegrind 文件,用 QCacheGrind 或 WebGrind 分析,查看哪个函数/行消耗了大量内存。 Xdebug 的垃圾回收统计 xdebug_debug_zstd('your_var'); // 显示变量内存占用(需扩展) 使用 memory_get_usage() / memory_get_peak_usage() 在代码关键位置打印内存,定位峰值点: echo memory_get_usage() . " bytes\n"; // 执行一段代码 echo memory_get_peak_usage() ....

2025年03月28日 · 2 min · Leanku

线程安全

内存溢出 PHP本身(无论是PHP-FPM还是Swoole)并不支持多线程。 线程安全,在PHP的世界里通常指以下两种情况,我们需要分开讨论: PHP解释器自身的线程安全:指PHP作为C语言编写的程序,当其被嵌入到多线程的Web服务器(如Apache的worker MPM)中时,多个线程能否安全地共用一个PHP解释器实例。 PHP应用代码的并发安全:指我们的业务代码在同时处理多个请求时,如何避免共享数据的冲突。这在Swoole这类常驻内存的进程中尤其重要。 下面分别介绍: 1. PHP解释器的线程安全(Zend线程安全,ZTS) 这是PHP源码编译时的一个选项(–enable-zts)。 问题背景:PHP有很多全局变量(例如存放已加载扩展列表的变量)。在单线程环境下没问题,但在多线程环境下,线程A修改这个全局变量时,线程B可能正在读取,导致崩溃。 解决方案(ZTS):PHP通过TSRM(线程安全资源管理器) 为每个线程分配了一个独立的“全局变量副本”。线程A修改自己的副本,不会影响线程B。 对开发者的影响: PHP-FPM(Nginx+PHP-FPM):使用的是多进程模型,一个进程只处理一个请求。进程间内存天然隔离,不存在线程安全问题,因此通常编译非线程安全(NTS) 版本,性能更高。 Apache + worker MPM:Apache使用多线程模型,此时PHP必须编译为ZTS版本才能稳定运行。 Swoole:Swoole本身是多进程模型。每个Worker进程是单线程的,事件循环和协程都在这个主线程中运行。因此,PHP的ZTS在Swoole环境下并非必需,Swoole官网也推荐使用NTS版本的PHP。 小结:对于绝大多数PHP开发者(使用PHP-FPM或Swoole),无需关心PHP底层的ZTS。你基本不会直接编写相关代码,这是扩展开发者和PHP本身需要解决的问题。 2. 应用代码的并发安全:Swoole常驻内存下的挑战 在传统的PHP-FPM中,每个请求结束后,所有变量都被销毁,下一个请求是“干净”的,所以不存在并发安全问题。 但在Swoole中,Worker进程常驻内存,多个协程或请求会并发地操作进程内的同一个全局变量、静态变量或资源(如Redis连接),这就会引发经典的竞态条件(Race Condition)。 典型问题场景: // 一个 Worker 进程内的全局计数器 $globalCounter = 0; // 协程A 和 协程B 同时执行这段代码 $globalCounter++; // 这不是原子操作! echo $globalCounter; 两个协程同时读取0,分别加1,然后写回。你期望输出2,但实际可能输出1。这就是并发问题。 如何保证Swoole下的并发安全? Swoole提供了几种机制来解决这个问题: 方案一:使用原子计数器(Atomic) Swoole提供的Swoole\Atomic是一个基于CPU原子操作的计数器,操作是线程/协程安全的。 $atomic = new Swoole\Atomic(0); // 在任意协程中安全地增加 $atomic->add(); // 无锁,高性能 echo $atomic->get(); 方案二:使用锁(Lock) 对于复杂的共享数据读写(不只是计数),可以使用各种锁。 协程锁(Swoole\Lock 或 Channel):推荐,只在协程级别阻塞。 $lock = new Swoole\Lock(Swoole\Lock::SPIN); // 自旋锁 $lock->lock(); // ....

2025年03月28日 · 1 min · Leanku

PHP + Redis秒杀场景

PHP + Redis秒杀场景 在电商秒杀等高并发场景中,Redis 凭借其内存操作和原子性特性,成为库存扣减的核心组件。 下面以 PHP + Redis 为例,给出一个企业级秒杀库存设计方案示例,重点解决超卖、热点 Key、重复下单和流量冲击等问题。 一、核心思路 预加载库存:秒杀活动开始前,将商品库存从 MySQL 同步到 Redis(使用 SET 或 HMSET)。 原子扣减:使用 Redis Lua 脚本(或 DECR + 校验)保证扣减与校验的原子性,杜绝超卖。 异步落库:扣减成功后,将用户 ID、商品 ID 推入消息队列(如 Redis Stream / List),后台进程消费并创建订单、扣减数据库库存。 限流 + 防重:通过令牌桶或滑动窗口限制用户访问频率,通过 Redis 记录用户抢购状态防止同一用户重复下单。 二、详细步骤与代码实现 1. 活动开始前:初始化库存 // 假设商品ID=1001,秒杀库存=100 $redis->set('seckill:stock:1001', 100); $redis->set('seckill:closed:1001', 0); // 0-未结束,1-已结束 2. 抢购接口核心:Lua 脚本原子扣减 $lua = <<<LUA local stock_key = KEYS[1] local user_key = KEYS[2] -- 用于记录已抢购用户 local user_id = ARGV[1] local closed_key = KEYS[3] -- 检查活动是否已结束 if redis....

2025年03月28日 · 4 min · Leanku