Linux OS 的 Init 和 Systemd
什么是 Init 守护进程Init守护进程是Linux内核执行的第一个进程,它的进程ID(PID)始终是1,它的目的是初始化、管理和跟踪系统服务和守护进程,也就是说init deamon(Init 守护进程)是系统上所有进程的父进程。
什么是 InitInit(也被称为System V init或者SysVinit),它是一个初始化守护进程,创建于 1980 年。它定义了6个运行级别(系统状态)并将所有的系统服务映射到这些运行级别。这使得所有服务都会以预定义的顺序启动,只有当顺序中的当前脚本执行完成或者卡主超时时才会执行下一个脚本。除了执行超时导致无法预期的等待之外,串行的启动服务也会使得系统初始化过程效率低下且相对较慢。
什么是 SystemdSystemd(system daemon)是现代系统使用的init守护进程,它并行的启动系统服务,从而消除不必要的延迟并加快初始化进程。systemd通过使用Unit Dependencies来定义一个服务是否想要/需要依赖其他服务才能运行,使用Unit Order来定义一个服务是否需要在它之前/之后启动。可以使用syst ...
使用 Kubeadm 部署 Kubernetes 集群(基于 Containerd 运行时)
基础知识CRI、Containerd、OCI、runcCRICRI(Container Runtime Interface)是Kubernetes定义的接口,定义了如何操作容器和镜像的统一规范。Kubernetes 尽量不关心您使用哪个容器运行时。 它只需要能够向它发送指令——为Pod创建容器、终止它们等等。 这就是CRI的用武之地。CRI 是对现在或将来可能存在的任何类型的容器运行时的抽象。 所以 CRI 让 Kubernetes 更容易使用不同的容器运行时。 CRI API描述了Kubernetes如何与任何运行时交互,而不是Kubernetes项目需要单独添加对每个运行时的支持。 只要给定的容器运行时实现了CRI API,运行时就可以随心所欲地创建和启动容器。
Containerdcontainerd是一个来自Docker的高级容器运行时,并实现了CRI规范。它是从Docker项目中分离出来,之后containerd被捐赠给云原生计算基金会(CNCF)为容器社区提供创建新容器解决方案的基础。
所以Docker自己在内部使用containerd,当你安装Docker时也会安装co ...
Kubernetes 集群部署之网段划分
Kubernetes 集群部署会涉及到如下三个网段
宿主机网段
准备安装 Kubernetes 集群的服务器的内网网段
POD 网段
Kubernetes 中部署的 POD 的网段
Service 网段
Kubernetes 中部署的 Service 的网段
需要注意的的是在部署Kubernetes的时候,这三个网段之间不能有任何的交叉。比如,如果宿主机的网段是172.16.168.135/12,那么Service或Pod的网段就不能在172.16.0.1~172.31.255.254之间。
所以一般的推荐是,三个网段的第一位就不要重复,比如宿主机的网段如果是172开头,那么Service和Pod就不要用172打头的网段,这样不近可以避免网段冲突,还可以减去计算的步骤。
A 类地址子网掩码最小为8B 类地址子网掩码最小为16C 类地址子网掩码最小为24
为了方便POD数量的扩展,应该给POD设置B类或A类地址,因为C类最多只有254个可用IP。
使用 Fluentd + ElasticSearch 搭建 docker stdout 日志解决方案
整体架构如下图,容器产生的 stdout 日志通过 Fluentd 分发到 ElasticSearch 的不同索引,然后通过 Kibana 进行可视化查询、搜索等。
部署ElasticSearch & Kibana笔者使用的是docker-compose方式部署的 ES 集群和 Kibana,参考文件如下:
目录结构12345>ls -aldrwxr-xr-x 2 root root 4096 9月 18 14:45 .drwxr-xr-x 7 root root 4096 9月 15 14:50 ..-rw-r--r-- 1 root root 7956 9月 16 14:18 docker-compose.yaml-rw-r--r-- 1 root root 776 9月 16 13:43 .env
.env 配置文件内容123456789101112131415161718192021222324252627282930# 此处为 elastic 用户设置密码,至少6个字符ELASTIC_PASSWORD=# 此处设置kibana密码KIBANA_PA ...
使用 Docker Compose 实现滚动更新部署
最近在写一个Golang的微服务项目,受限于测试和生产环境交付的时候都无法使用到Kubernetes,所以准备使用Docker Compose部署,记录一下实现方式。
思路
变更容器编排文件docker-compose.yaml
获取当前启动的容器id,标识为旧容器
更新并调度服务比当前容器数量多的指定数量
杀掉标识的旧容器
清理杀掉的旧容器
重新调度服务容器数量为期望数量
shell 脚本实现
备注:此脚本需在 docker-compose.yaml 编排文件所在目录执行。或者自己改脚本,cd 进目标目录执行
12345678910111213141516171819202122# 1. 更新 docker-compose.yaml,有很多选择,你可以手动更新,也可以使用 ci 自动使用新的编排文件覆盖。总之就是把当前目录下的编排文件变成你期望的最新的,有可能是只改了镜像,或者其他# 取到使用当前目录下的编排文件启动的服务容器idprevious_container=`docker-compose ps -q`# 将容器数量调度到指定值,此值需要大于当前服务的容器数量docker ...
使用 log-pilot + elk 搭建 docker stdout 日志解决方案
最近在写一个Golang的微服务项目,受限于企业内部网络等各种问题,日志无法上云只好自建日志收集管理平台了,记录一下实现方式。
整个环境从服务到elk到log-pilot均使用docker-compose部署
log-pilot 介绍log-Pilot 是一个阿里云开源的一款智能容器日志采集工具,它不仅能够高效便捷地将容器日志采集输出到多种存储日志后端,同时还能够动态地发现和采集容器内部的日志文件。log-pilot 通过声明式配置实现强大的容器事件管理,可同时获取容器标准输出和内部文件日志,解决了动态伸缩问题,此外,log-pilot 具有自动发现机制,CheckPoint 及句柄保持的机制,自动日志数据打标,有效应对动态配置、日志重复和丢失以及日志源标记等问题。
log-Pilot代码仓库
log-pilot 特性
一个单独的 log 进程收集机器上所有容器的日志。不需要为每个容器启动一个 log 进程。
支持文件日志和 stdout
docker log dirver 亦或 logspout 只能处理 stdout,log-pilot 不仅支持收集 stdout 日志,还可以 ...
搭建基于 Kubernetes 生产可用的日志和监控系统
前言本篇文章涉及很多 Kubernetes 基础知识,包括但不限于以下资源对象:
Namespace
Pod
Deployment
Service
Headless Service
Volume
PersistenVolume
PersistenVolumeClaim
StatefulSet
DaemonSet
Ingress
Role
ClusterRole
如果这些东西你还很陌生,请转战Kubernetes中文官方文档
基础设施安装工具1yum install bind-utils -y
内网免密登录1ssh-keygen
master 节点的 pub key 加入各节点的 auth key
初始化集群为了节省时间使用了 sealos 工具来搭建 K8S 集群,如果你想了解使用 Kubeadm 部署 K8S 集群可以参考笔者的另一篇文章Kubeadm 安装 Kubernetes V1.22.2 踩坑手记
工具12wget -c https://sealyun-home.oss-cn-beijing.aliyuncs.com/sealos/latest/se ...
浅析 Golang 内存机制(内存分配、GC、内存逃逸)
内存分配
Go 程序在启动时,会向操作系统申请一定区域的内存,分为堆(Heap)和栈(Stack)。
堆(Heap)
一般来讲是人为手动进行管理,手动申请、分配和释放。所涉及的内存大小并不定,一版会存放比较大的对象。分配相对较慢涉及到的指令动作相对较多。
在 Go 语言中,堆内存由程序申请分配,由 GC(Garbage Collection)负责回收
栈(Stack)
由编译器进行管理,自动申请、分配、释放。一般不会太大,我们常见的函数参数、局部变量等等都会放在栈上。
在 Go 语言中,栈内存会随着函数的调用而进行分配,随着函数的调用结束而回收
性能上,栈内存的使用和回收会更快一些,尽管 Golang 的 GC 很高效,但过程中还会在标记准备阶段和标记结束阶段进行 STW,因此,会优先使用栈内存进行分配(因为栈内存更高效,不需要 GC,所以会尽可能将内存分配到栈上),在某些特殊场景下可能会发生内存逃逸到堆上
GC(Garbage Collection)标记清除法在 Go 1.3 之前的版本使用的是标记清除法(mark and sweep),大致流程如下:
暂停程序业 ...
浅读 Go 优秀开源项目源码—Gin框架
Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。它是一个类似于 martini 但拥有更好性能的 API 框架, 由于 httprouter,速度提高了近 40 倍。
Gin 是目前 Go 里面使用最广泛的框架之一了,弄清楚 Gin 框架的原理,有助于我们更好的使用 Gin。通过浅读 Gin 框架源码,大致总结一下它的一些核心实现。
从一个 Demo 入手下面这段代码便是使用 Gin 编写一个简单的只有一个接口的 Go server 端程序,详细使用方式参考官方文档:Gin Web Frameword
123456789101112131415161718192021222324252627package mainimport ( "fmt" "log" "time" "github.com/gin-gonic/gin")func main() { r := gin.Default() // 注册路由 r.GET(" ...
基于 Consul 实现 gRPC 服务注册与发现
什么是服务注册与发现,为什么需要服务注册发现
服务注册
服务进程在注册中心注册自己的元数据,一般包括 Host 和 Port,有时候还有身份验证信息,协议,版本号以及运行环境等信息。
服务发现
客户端进程向服务注册中心发起查询,来获取依赖的服务的信息,然后向其发送请求。服务发现一个重要的作用是提供给客户端进程一个可用的服务列表。
简单的说,当服务A需要依赖服务B时,服务发现中间件需要告诉服务A哪些地址是服务B的可用地址,这就是服务注册发现需要解决的问题。
服务注册
客户端注册
服务自身负责注册与注销工作,当服务启动后注册线程向注册中心注册,当服务停止时注销自己。
代理注册
当服务启动后以某种方式通知代理服务,代理服务向注册中心发起注册工作。
健康检测
被动检测
服务主动向注册中心发送心跳消息,时间间隔可以自定义。注册中心如果在指定周期内未收到服务节点的心跳消息,则将其从该服务可用节点列表中移除
主动检测
服务注册中心指定时间间隔内向所有列表中的服务节点发送心跳检测,如果指定周期内未成功则主动移除该节点。
服务发现
客户端发现
客户端向注册中心发起请求查询 ...