Go 语言中的 Slice 类型

Slice(切片)是 Go 语言内置的可变长度序列,属于引用类型,基于底层数组封装实现,解决数组长度固定、数组传参完整拷贝开销大的问题,是 Go 开发最常用的数据结构之一。 1. Slice 的基础特性 可变长度:长度不固定,可通过 append 动态追加元素。 底层数组共享:切片本身不存储实际元素,元素存放在底层数组;多个切片可以指向同一个底层数组,修改会互相影响。 len 与 cap 分离:len代表切片当前可用元素个数;cap代表底层数组允许的最大容量。 零值特性:切片零值为 nil,nil slice len=0、cap=0;nil slice 和空切片 []int{} 不相等。 引用语义:切片变量存储的是结构体头信息,赋值、传参拷贝的是切片头,不是底层数组。 不可直接比较:切片不能使用 == 判断两个切片内容是否相等,仅允许和 nil 做相等判断。 友情提示 上述特性与 Slice 的底层实现紧密关联,建议结合第 4 节"Slice 底层实现原理"章节阅读,以获得更深入的理解。 2. Slice 的四种初始化方式 切片必须初始化,nil切片可以读取、append,但不能直接对索引位置赋值。 2.1 声明未初始化(nil slice) 仅定义变量,底层数组指针为 nil,len=0、cap=0。 // 声明nil切片,未分配底层数组 var a []string fmt.Println(a == nil) // true fmt.Println(len(a), cap(a)) // 0 0 // a[0] = "hello" // 未初始化的切片直接赋值会引发 panic // panic: index out of range 2.2 字面量初始化(空切片/带初始值) 直接初始化,分配底层数组,支持空切片和预设元素。 ...

Go 语言中的 Map 类型

Map 是 Go 语言内置的键值对(key-value)哈希集合,属于引用类型,核心作用是通过唯一键快速索引对应值,查询、插入、删除的平均时间复杂度均为O(1),是 Go 开发中最常用的数据结构之一。 1. Map 的基础特性 键唯一性:同一个 Map 中,key 唯一,重复插入会覆盖旧值。 键类型限制:key 必须是可比较类型(int、string、bool、结构体等),切片、map、函数不可作为 key。 无序性:Map 不存储键值对插入顺序,遍历顺序随机(Go 1.12+ 每次遍历顺序不一致)。 零值特性:Map 零值为 nil,nil map 无法写入数据,仅可读取。 引用类型:赋值、传参仅传递引用,多个变量指向同一个底层数据 2. Map 的四种初始化方式 Go 语言 Map 必须初始化后才能写入数据,未初始化的nil map直接写入会触发运行时 panic。以下是所有合法初始化方式。 2.1 声明未初始化(nil map) 仅定义变量,未分配内存,零值为nil,只能读、不能写。 package main import "fmt" func main() { // 声明nil map,未初始化 var m map[string]int fmt.Println(m == nil) // 输出:true // m["a"] = 1 // 报错:panic: assignment to entry in nil map } 2.2 字面量初始化(空 Map/带初始值) 直接初始化并分配内存,支持空 Map 和预设键值对,最常用。 ...

Go 语言中的字符串类型

Go 原生支持字符串类型,关键字string用于表示字符串数据类型。 1. 底层实现 Go 中的string实际上是一个struct类型,其本身并不真正存储字符串数据,仅是由一个指向底层数组的指针和字符串的长度字段组成,可以在标准库的reflect包中,找到下面代码: // $GOROOT/src/reflect/value.go // StringHeader是一个string的运行时表示 type StringHeader struct { Data uintptr Len int } 由于string类型实际上只是一个对底层存储的引用,因此实际传参的开销非常小,另外在生成string变量时,会同时存储字节长度len,所以无论字符串长度怎么变化,获取长度的时间都是O(1)。 底层实现图解: 三个必须记住的特性: 特性 含义 不可变 任何“修改”操作都会产生新字符串,原串不变 UTF-8 编码 源码中的字符串字面量默认是 UTF-8 零值是 "" 不是 nil,var s string 是空串 var s string = "Hello" // s[0] = 'h' // 编译错误:cannot assign to s[0] b := []byte(s) // 想修改就转成切片(会复制一份) 注意:len(s)返回的是字节数,不是字符数。中文、emoji 等多字节字符会让两者不相等。 2. 字面量与引号 Go 中初始化字符串变量,需要在双引号"中定义值,单引号'用于单个字符( 以及 runes )。 var Name string = "zhangsan" age := "18" var a char = 'a' s1 := "hello\nworld" // 双引号:支持 \n \t \" \\ 等转义 // 输出: // hello // world s2 := `hello\nworld` // 反引号(原生字符串):原样保留 // 输出: // hello\nworld s3 := "" // 空串,len == 0 Go 中没有也不支持三引号的写法,使用反引号来表示原生字面量。由于所见即所得,原样保留,可跨行的特点,常用于SQL/JSON/正则表达式中 ...

Go 语言中的循环控制

Go 语言只有一种循环构造方式,即 for 循环(没有 while 或 do-while),类似于 C、Java 和 C# 之类的编程语言中的 for 循环。使用分号;分隔。。它的基本语法如下: for 初始化语句; 条件表达式; 后置语句 { 循环体 } 执行顺序 初始化语句:在循环开始前执行,且只执行一次(如 i := 0)。 条件表达式:在每次迭代前求值。如果为true,进入循环体;如果为false,循环直接结束。 循环体:条件为真时执行。 后置语句:在每次循环体执行结束后执行(如 i++),然后回到第 2 步重新判断条件。 1. 基本语法 for循环的三个组件: 在第一次迭代之前执行的初始语句(可选)。 在每次迭代之前计算的条件表达式,该条件为false时,循环会停止退出。 在每次迭代结束时执行的后处理语句(可选)。 func main() { sum := 0 for i := 1; i <= 100; i++ { sum += i } fmt.Println("sum of 1..100 is", sum) } 2. 只使用表达式的 for 循环 某些编程语言中,可以使用 while 关键字编写循环,而 Go 没有 while 关键字。 但是可以改用 for 循环实现相同的效果。 package main import ( "fmt" "math/rand" "time" ) func main() { var num int64 rand.Seed(time.Now().Unix()) for num != 5 { num = rand.Int63n(15) fmt.Println(num) } } 3. 无限循环和 break 更进一步,当 for 循环连表达式都不编写的情况就是无限循环,这时想要退出循环必须使用 break 关键字。 ...

K8s MetaLB 详解

在 Kubernetes 集群中,Service 是核心资源之一,其中 LoadBalancer 类型服务是业务对外暴露的核心方式。但原生 K8s 仅为公有云集群(AWS、阿里云、腾讯云等)提供 LoadBalancer 实现,裸金属、本地机房、私有部署的 K8s 集群默认不支持 LoadBalancer 类型 Service,只能依赖 NodePort、Ingress 实现流量暴露,存在端口散乱、运维复杂、无统一入口、负载均衡能力薄弱等问题。 MetalLB 正是这样一款专为私有/裸金属K8s集群设计的开源负载均衡组件,完全开源免费、轻量无侵入,完美补齐了私有 K8s 集群无原生 LoadBalancer 的短板,是本地机房 K8s 生产集群的标配组件。 1. 项目介绍 1.1 项目定位与核心作用 MetalLB 是 Kubernetes官方认可的负载均衡解决方案,专为非云环境(裸金属服务器、虚拟机、本地私有集群)设计。其核心作用是:为集群内 LoadBalancer 类型的 Service 自动分配外部静态 IP,实现四层流量负载均衡,替代原生 NodePort 的低效暴露方式,让私有集群拥有与公有云一致的 Service 负载均衡能力。 核心价值: 统一业务暴露方式:与公有云 K8s 开发规范对齐,无需适配 NodePort 特殊逻辑。 稳定四层负载均衡:支持 TCP/UDP 协议,适配后端服务、数据库、中间件等各类业务。 简化运维:固定服务外部 IP,无需节点端口映射,流量入口统一可控。 轻量高性能:无复杂依赖,资源占用极低,不侵入 K8s 核心组件。 1.2 架构原理 MetalLB 采用控制器 + 转发节点的分布式架构,整体由两大核心组件组成,全部运行在集群内部,以 Pod 形式部署。 1.2.1 Controller(控制器组件) 全局唯一的调度组件,默认单副本部署,核心职责: 监听集群内所有 LoadBalancer 类型的 Service 资源变更。 维护 IP 地址池,为新的 LoadBalancer Service 自动分配可用外部 IP。 回收废弃 Service 的 IP 资源,避免 IP 泄露与占用。 校验 IP 分配合法性,保证全局唯一性。 1.2.2 Speaker(转发组件) 集群节点级组件,默认每个集群节点部署一个Pod(DaemonSet部署),核心职责: ...

Pod 网络故障排查

Kubernetes 的网络是一个多层虚拟网络体系,流量从客户端到后端 Pod 的路径涉及多个组件。排查网络问题时,如果"东戳一下西戳一下"没有章法,可能需要耗费大量时间。如何最快的定位网络问题的根源?记住常见的具体现象和应对办法,遵循从内到外的原则(先确认"自己没问题",再确认"中间没问题",最后确认"外面没问题")或许能够帮你节约大量的时间,。 下面按照从具体访问现象出发的思路,由浅入深展开。 1. 第一层:Pod 自身状态确认 现象:Pod 完全无法访问,任何请求都无响应 这是最基础的一层。在排查任何网络问题之前,先确认 Pod 本身是否健康运行。 1.1 检查 Pod 状态 kubectl get pods -o wide 关注以下状态: 状态 含义 可能原因 Running 正常运行 正常 Pending 等待调度 资源不足、节点亲和性不匹配 CrashLoopBackOff 反复崩溃重启 应用启动失败、配置错误 OOMKilled 内存超限被杀 resources.limits.memory 设置过低 ImagePullBackOff 镜像拉取失败 镜像名错误、仓库认证失败 ContainerCreating 容器创建中 CNI 插件分配 IP 失败、存储卷挂载失败 如果 Pod 不在 Running 状态,网络问题只是表象,根因在 Pod 本身: # 查看 Pod 详细事件 kubectl describe pod <pod-name> -n <namespace> # 查看容器日志(如果容器曾启动过) kubectl logs <pod-name> -n <namespace> # 查看前一个崩溃容器的日志 kubectl logs <pod-name> -n <namespace> --previous 1.2 确认 Pod 有 IP 地址 kubectl get pods -o wide 如果 Pod 状态是 Running 但 IP 为空,说明 CNI 插件没有成功分配 IP。此时需要检查: ...

K8s 集群升级

Kubernetes 集群的大版本(跨次要版本)升级是一项复杂且高风险的运维操作。为了确保业务连续性,必须遵循严格的规范。以下是升级的核心原则、标准流程以及可能遇到的关键问题。 1. 大版本升级的核心原则 逐级递进,严禁跨版本:Kubernetes 不支持跨多个次要版本直接升级(例如不能从 1.24 直接升级到 1.26),必须按照1.24 → 1.25 → 1.26的顺序逐版本进行。每一跳都需要完成控制面和节点的升级与验证。 组件版本兼容策略: 控制面:kube-apiserver 版本最高且需最先升级,controller-manager 和 scheduler 版本应小于或等于 apiserver。 kubelet:节点上的 kubelet 版本必须小于或等于 apiserver,且不能低于 apiserver 的前一个次要版本(例如 apiserver 为 v1.28 时,kubelet 可以是 v1.28 或 v1.27)。 先升级控制面,再升级节点:必须先完成 Master 节点(控制平面)的升级,确认稳定后再分批升级 Worker 节点。 第三方组件兼容性检查:升级前必须确认 CNI 插件(如 Calico/Cilium)、CoreDNS、kube-proxy 以及 Ingress 控制器等是否支持目标版本。通常建议先升级 CNI,CoreDNS 则与 K8s 版本强绑定,需查阅官方对应表。 2. 标准升级流程 升级前准备: 检查集群健康状态,确保所有节点为 Ready,无 CrashLoopBackOff 的 Pod。 备份关键数据:必须备份 etcd 数据、证书文件及核心配置文件,以防升级失败导致数据丢失。 检查并处理废弃 API(Deprecated APIs),高版本可能会移除旧版 API,导致 HPA 或 Ingress 等资源失效。 升级控制面:升级 kubeadm,执行 kubeadm upgrade apply,依次更新 kube-apiserver、kube-controller-manager 和 kube-scheduler。 升级工作节点: 腾空节点(Cordon & Drain),将 Pod 驱逐到其他节点。 升级节点的 kubeadm、kubelet 和 kubectl。 解除节点保护(Uncordon),使其重新加入调度。 建议在生产环境中逐个节点离线升级后再上线,避免全部同时升级。 3. 升级过程中可能遇到的问题 废弃 API 导致业务失效:高版本 K8s 会废弃旧版 API。例如升级到 1.22+ 时,networking.k8s.io/v1beta1 的 Ingress 将不再可用;升级到 1.18+ 后,HPA 的 apiVersion 必须切换为 apps/v1,否则功能会失效。 容器运行时变更:从 Kubernetes 1.24 开始,不再支持将 Docker 作为内置容器运行时。若从 1.22/1.23 升级,必须提前将节点运行时从 Docker 迁移到 containerd,否则升级会失败。 节点排水(Drain)失败与 PDB 冲突:在驱逐 Pod 时,如果配置了 Pod Disruption Budget (PDB) 且当前可用副本数达到下限,Pod 将无法被驱逐,导致节点升级超时或失败。 网络连通性中断:如果 Pod 通过 LoadBalancer 类型的 Service 访问同节点上的另一个 Pod,且 Service 的 externalTrafficPolicy 设置为 Local,在节点轮转升级后,两个 Pod 可能不再位于同一节点,导致网络不通。 自定义配置被覆盖:如果曾通过非产品化方式(如直接登录节点修改 kubelet 配置、打开 SWAP 分区等)更改过节点配置,升级过程中这些自定义配置可能会被重置或覆盖,甚至导致升级失败。 磁盘空间不足:升级过程需要下载软件包并可能产生临时文件。如果节点磁盘水位过高(建议预留至少 20% 空间),可能导致 Pod 被驱逐或升级失败。 Cgroup v1 兼容性问题:在较新的版本(如 1.36)中,kubelet 默认仅支持 Cgroup v2。如果节点操作系统仍在使用 Cgroup v1,可能会导致 kubelet 启动失败或节点 NotReady。

Kubeadm 部署高可用 K8s 集群

kubeadm 安装官方文档 0. 引言 Kubernetes 高可用架构的核心在于保障 API Server 的访问连续性与 etcd 的数据可靠性。通常通过部署多 Master 节点并前置负载均衡来实现,本方案采用 kube-vip 承担此角色。为节省资源,本文将负载均衡与 etcd 集群复用部署于 Master 节点;生产环境请务必将其独立部署,以确保集群稳定性。 本文基于 Ubuntu 24.04 环境编写。若使用其他 Linux 发行版,请根据实际情况自行调整相关命令。 1. 环境架构 1.1 角色划分 主机名 IP 角色 master01 192.168.69.11 master01、Etcd-node01、kube-vip 节点 master02 192.168.69.12 master02、Etcd-node02、kube-vip 节点 master03 192.168.69.13 master03、Etcd-node03、kube-vip 节点 worker01 192.168.69.21 worker01 worker02 192.168.69.22 worker02 worker03 192.168.69.23 worker03 虚拟IP 10.0.0.100 kube-vip 提供 1.2 拓扑图 2. 操作系统初始化 2.1 Linux 基础配置 # 关闭防火墙 ufw disable # 关闭 swap swapoff -a && sysctl -w vm.swappiness=0 sed -ri 's/.*swap.*/#&/' /etc/fstab # 配置主机名和 hosts hostnamectl set-hostname <hostname> cat >> /etc/hosts << EOF 192.168.69.11 master01.wuvikr.top master01 192.168.69.12 master02.wuvikr.top master02 192.168.69.13 master03.wuvikr.top master03 192.168.69.21 worker01.wuvikr.top worker01 192.168.69.22 worker02.wuvikr.top worker02 192.168.69.23 worker03.wuvikr.top worker03 192.168.69.100 kubeapi.wuvikr.top kubeapi EOF # 配置 limit ulimit -SHn 65535 # 持久化 limit 配置(重新登录后生效) cat >> /etc/security/limits.conf << EOF * soft nofile 65536 * hard nofile 131072 * soft nproc 65535 * hard nproc 655350 * soft memlock unlimited * hard memlock unlimited * soft core unlimited * hard core unlimited EOF 2.2 时间同步 # 同步硬件时钟到系统时间(虚拟机环境中可选) hwclock -s # 设置时区(推荐方式,一步完成) timedatectl set-timezone Asia/Shanghai # 确保时间同步服务已启用(Ubuntu 24.04 默认已启用) systemctl enable --now systemd-timesyncd # 查看时间同步状态 timedatectl status Ubuntu 24.04 内置的 systemd-timesyncd 提供轻量级 NTP 客户端功能,支持自动同步、漂移补偿和状态查询。如需更高精度的时间同步(如金融、日志审计场景),建议改用 chrony 替代 systemd-timesyncd。 ...

Terraform 状态文件管理

1. 为什么要把 State 搬到「云上」 State 文件是 Terraform 的「账本」:它记录云上到底有哪些资源、各自的属性。本地存放 State 有三个硬伤: 绑定机器:State 在 A 电脑上,B 电脑 apply 就会"失忆",容易把资源重建一遍。 无法协作:两个人同时改同一个环境,互相覆盖,状态文件乱套。 安全隐患:本地文件易丢,而且里面可能含有明文密码等敏感信息,因此也不建议上传到 gitlab 等代码托管平台。 解决办法就是 Terraform 的 Backend 机制:用阿里云 OSS 存 State(集中、加密、可回滚),用 Tablestore 提供加锁(防止多人并发操作冲突)。 2. 工作原理 每次执行 Terraform 命令,OSS Backend 都会走「加锁 → 干活 → 放锁」三步: flowchart LR A[terraform apply] --> B{从 Tablestore 拿 LockID} B -- 已存在 --> C[报错:有人在操作] B -- 不存在 --> D[生成并存入 LockID] D --> E[读写 OSS 里的 State 文件] E --> F[释放 LockID] 有锁 → 说明有人正在操作,或上次操作异常退出,直接报错 无锁 → 自动生成 LockID 存入 Tablestore,操作完成后更新 OSS 中的 State,再释放锁 3. 第一步:创建「存储 + 锁」资源 在正式使用前,要先准备一个 OSS Bucket 和一个 Tablestore 实例/表。它们本身也是云资源,所以也可以用 Terraform 自己建。 ...

Terraform 云上存量资源导入

1. 为什么要"导入"云上资源? 很多团队都有这样的经历:最开始用控制台点几下、或者用 CLI 脚本把资源建好了,等资源越攒越多、想标准化管理时,发现这些资源"游离"在 Terraform 之外。 Terraform 的管理逻辑很简单:它只认自己 state 文件里记录的资源。你在控制台创建的资源,Terraform 根本"看不见",也不会去动它们。所以要把存量资源管起来,第一步就是"导入"(import)——把云上已有的资源登记进 Terraform 的 state,再补上对应的配置代码。 导入成功后,这些资源和 Terraform 新建的资源享受完全一样的待遇:能 plan、能 apply、能 diff、能进 CI/CD。 2. 先搞清楚两个概念:config 和 state 这是最容易绕晕的地方,务必先分清: 概念 是什么 谁来写 .tf 配置(config) 声明"想要什么",是给人看的声明式代码 手写,或工具自动生成 terraform.tfstate(state) 记录"现在实际是什么",含真实属性(可能有敏感信息) 由 apply / import 自动生成 关键点: 导入只会写 state,不会自动写 config(除非你用 -generate-config-out 或 Terraformer)。 所以完整流程 = 导入进 state + 补齐 config + plan 核对一致,三步缺一不可。 state 含敏感信息,永远不要提交到 git(后面最佳实践会讲远程存储)。 整体流程可以先看这张图: flowchart TD A[盘点存量资源: 类型 / ID / 地域] --> B[环境准备: Terraform + provider + RAM 凭证] B --> C{资源量多少?} C -->|少| D[import block / terraform import] C -->|多| E[aliterraformer 批量导入] D --> F[生成 config + state] E --> F F --> G[terraform plan 核对无漂移] G --> H[清理只读属性 + 纳入 git + OSS 远程 state] 3. 三种导入方式怎么选 阿里云官方文档提供了三种方式,直接放对比表: ...