k8s的使用手册
一. k8s demo部署示例
说明
- 先通过一个hello world程序直观感受一下k8s
- 程序部署在宿主机,容器和k8s三种环境,对比他们的差异
- 代码大致是这个样子
@RestController
public class K8sDemoController {
@GetMapping("/hello")
public String hello(){
return "hello k8s demo.";
}
}
1. 宿主机上如何运行
- mvn编译代码打成jar包
- 执行java -jar k8s-demo.jar &
- 浏览器中输入 http://10.1.69.101:8080/hello 地址访问服务
2. Docker容器上如何运行
- mvn编译代码打成jar包
- 将jar包打成docker镜像
- 执行docker run --name k8s-demo -d -p 8080:8080 k8s-demo:0.0.1-SNAPSHOT
- 浏览器中输入 http://10.1.69.101:8080/hello 地址访问服务
3. 在k8s中如何运行
这一步大致感受一下yaml的样子,不需要关心脚本的细节,后面介绍资源时会细说。只需要有个大体的印象,部署一个k8s服务的基本流程。
- mvn编译代码打成jar包
- 将jar包打成docker镜像
- 构建一个运行服务的yaml文件
- 执行kubectl apply -f k8s-demo.yaml
- 浏览器中输入 www.k8s-demo.com/hello 地址访问服务
- yaml文件内容
# 文件名为k8s-demo.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: k8s-demo
namespace: spring-test
spec:
replicas: 3
template:
metadata:
labels:
app: k8s-demo
spec:
containers:
- name: k8s-demo
image: k8s-demo:0.0.1-SNAPSHOT
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: k8s-demo
namespace: spring-test
spec:
type: NodePort
selector:
app: k8s-demo
ports:
- protocol: TCP
port: 8888
targetPort: 8080
nodePort: 30003
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: k8s-demo
namespace: spring-test
spec:
rules:
- host: www.k8s-demo.com
http:
paths:
- path: /hello
backend:
serviceName: k8s-demo
servicePort: 8888
4. 总结三种部署方式
- 三种部署方式高清图
为了减少部署复杂度,代码并没有使用redis,只是实例图增加了redis
5. 流程越来越复杂,为什么不直接部署在宿主机?
- 容器比宿主机优势在哪?
- 可移植性:容器提供了运行应用程序的基本包装,可以在任何支持容器的云上部署
- 高效率:启动一个容器只需一个镜像,且启动时间非常短
- 隔离性:宿主机上往往安装很多服务,且各自依赖不一样。而一个容器只跑一个服务
- 版本控制:方便追溯不同版本差异,方便快速回滚,只需替换镜像版本,无需宿主机上一套复杂的流程
- 低成本:小巧轻便,不需要像宿主机或虚拟机一样占用很多资源
- k8s的出现又解决了容器的什么问题?
- 自动编排调度:大量容器剧增后,如何管理、如何调度的问题
- 分布式解决方案:节点可水平扩展,容器可方便扩缩容
- 自愈能力:故障自动发现,并进行自我修复
我们说容器实现了单个应用程序的基本包装实现可移植。上图中,宿主机部署的方式如果加上一个nginx做反向代理,就和k8s中ingress的部署方式是一样的。也就是k8s实现了整套分布式应用的可移植
二. 基本架构和原理
1. Master节点的组件
apiServer
- 提供资源操作的唯一入口,提供api注册、发现、认证、访问控制等功能
etcd - 一个key-value数据库
- 保存整个机器的状态
controller-manager
- 负责维护机器状态,比如:自动扩容、故障检查、滚动更新
- 实现集群自动化的关键组件
scheduler
- 负责资源调度
- 将未分配节点的pod调度到合适的节点上
2. Node节点的组件
kubelet
- 负责容器生命周期管理,比如:创建、删除
- 同时负责Volume,网络的管理
kube-proxy - 负责为Service提供负载均衡、服务发现
Container Runtime
- 容器运行环境
- 默认是Docker,同时还支持其他容器引擎
三. 资源对象
概述
- k8s中大部分概念,如Node,Pod,Service都可以看做一种资源对象
- 资源的描述:yaml文件或json文件
- 资源的操作:对象可以通过kubectl(或者api)执行增、删、改、查
- 资源的存储:信息在etcd中持久化
k8s通过对比资源的“实际状态”和etcd中的“期望状态”,实现自动化控制
1. Pod
- Pod是k8s中最重要最基本的资源
- pod是在容器之外又封装的一层概念
- pod是容器调度的基本单元(不是docker容器)
- 每个pod包含一个特殊的根容器:Pause容器,和一个或多个业务容器
- 每个pod有唯一的ip,pod内的容器可通过localhost通讯
为什么要新增pod这个概念?
- 一组容器作为一个单元,很难判断整体状态,以及对整体进行管控。新增业务无关的pause容器,用于管控整体
- 简化了关联容器通信和共享的问题
2. Deployment
- 实现Pod自动编排:创建、删除、扩容、缩容
- 通过replicas控制pod数量,template控制要创建的pod的模板
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: k8s-demo
namespace: spring-test
spec:
replicas: 3
template:
metadata:
labels:
app: k8s-demo
spec:
containers:
- name: k8s-demo
image: k8s-demo:0.0.1-SNAPSHOT
ports:
- containerPort: 8080
3. Service
- pod异常时,可能会被调度到另一台机器,导致pod的ip改变,使用ip访问服务不可靠
3.1概述
- k8s里最核心的资源之一,类似微服务架构中的“微服务”
- 前端应用通过入口地址访问服务,服务通过label对接到后端的pod,即使pod的ip变了
- kube-proxy负责把service请求转发到后端,并做负载均衡
- service整个生命周期内,ClusterIp不会变,对外提供的服务地址也就不会变
apiVersion: v1
kind: Service
metadata:
name: k8s-demo
namespace: spring-test
spec:
type: NodePort
selector:
app: k8s-demo
ports:
- protocol: TCP
port: 8888
targetPort: 8080
nodePort: 30003
4. Ingress
service提供了ip:port的访问方式,即工作在tcp/ip层,而http服务需要将不同的url对应到不同的后端服务,service是无法实现这一功能的。
- Ingress提供http层的负载分发功能
- Ingress可以实现不同的请求,分发到不同的后端服务
- Ingress定义后,需要结合Ingress Controller,才能形成完整的功能
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: k8s-demo
namespace: spring-test
spec:
rules:
- host: www.k8s-demo.com
http:
paths:
- path: /hello
backend:
serviceName: k8s-demo
servicePort: 8888
4.1 Ingress Controller定义
- 可以使用公有云提供的Ingress Controller
- 也可以使用google提供的Ingress Controller,以pod形式运行,功能如下:
- 监听apiserver,获取ingress的定义
- 基于ingress定义,生成nginx的配置文件的内容
- 执行nginx -s reload,重新加载配置
4.2 Ingress定义
- 创建类型为Ingress的yaml文件
- 配置spec.rules,指定hostname中url和service的对应关系
四. k8s网络模型
前面hello world程序中,对于如何访问到服务,有必要了解一下k8s的网络模型,在这之前先介绍docker的网络模型
1. Docker网络模型
docker网络模型
- docker第一次启动时,会创建虚拟网桥docker0
- 为docker0分配一个子网
- docker创建每个容器时,会创建veth设备对,一端关联到网桥上,另一端使用linux的网络命名空间技术连接到容器内,并给容器内eth0设备分配一个ip地址
2. Docker网络的局限性
- Docker网络模型没有考虑到多主机互联的网络解决方案,崇尚简单为美
- 同一机器内的容器之间可以直接通讯,但是不同机器之间的容器无法通讯
- 为了跨节点通讯,必须在主机的地址上分配端口,通过端口路由或代理到容器
- 分配和管理容器特别困难,特别是水平扩展时
3. k8s网络模型概述
3.1 k8s网络模型的原则:
- 每个pod都拥有唯一个独立的ip地址,称IP-Per-Pod模型
- 所有pod都在一个可连通的网络环境中
- 不管是否在同一个node,都可以通过ip直接通讯
- pod被看作一台独立的物理机或虚拟机
目前原生docker和kubernetes还不能打通多节点容器与容器的通讯,要支持该模型,必须依靠第三方网络插件实现,比如:flannel
3.2 设计这个原则的原因:
- 用户不需要额外考虑如何建立pod之间的连接
- 用户不需要考虑将容器端口映射到主机端口的问题
- 可以兼容过去跑在宿主机和KVM的应用
3.3 IP-Per-Pod与Docker端口映射的区别
- docker端口映射到宿主机会引入端口管理的复杂性
- docker最终被访问的ip和端口,与提供的不一致,引起配置的复杂性
4. k8s网络模型详解
k8s网络实现
4.1 容器与容器的通讯
- 同一个容器的pod直接共享同一个linux协议栈
- 就像在同一台机器上,可通过localhost访问
- 可类比一个物理机上不同应用程序的情况
4.2 pod与pod的通讯
同一Node内的pod之间通讯
- 同一Node内的pod都是通过veth连接在同一个docker0网桥上,地址段相同,所以可以直接通讯
不同Node的pod之间通讯
- docker0网段与宿主机不在同一个网段,所以不同pod之间的pod不能直接通讯
- 不同node之间通讯只能通过宿主机物理网卡
- 前面说过k8s网络模型需要不同的pod之间能通讯,所以ip不能重复,这就要求k8s部署时要规划好docker0的网段
- 同时,要记录每个pod的ip地址挂在哪个具体的node上
- 为了达到这个目的,有很多开源软件增强了docker和k8s的网络
4. 开源网络组件Flannel
4.1 实现的功能
- 协助k8s给每个Node上的docker容器分配互不冲突的ip地址
- 能在这些ip地址之间建立覆盖网络(Overlay Network),将数据传递到目标容器
4.2 底层原理
- Flannel创建名为flannel0的网桥
- flannel0网桥一端连接docker0网桥,另一端连接flanneld进程
- flanneld进程一端连接etcd,利用etcd管理分配的ip地址资源,同时监控pod地址,建立pod节点路由表
- flanneld进程一端连接docker0和物理网络,配合路由表,完成数据包投递,完成pod之间通讯
4.3 缺点
- 引入多个网络组件,带来网络时延和损耗
- 默认使用udp作为底层传输协议,具有不可靠性
作者:黄姚
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!