本文主要是记录我个人近期学习k8s群集的内容
由于是站在初学者的角度来编写的,难免会存在许多的问题
如果你发现了错误或者需要改进的地方,也欢迎你指出
前情提要
由于我是使用别人部署好的群集进行实战,所以本文不会有教你如何部署群集的过程
并且只是从受限制的管理员权限的角度进行操作,还请注意和作为群集所有者进行操作群集的区别
获取kubectl和oidc插件
安装kubectl
官方文档:https://kubernetes.io/zh-cn/docs/tasks/tools/
windows来这边:https://kubernetes.io/zh-cn/docs/tasks/tools/install-kubectl-windows/
不过你会发现它让你用curl,Chocolatey、Scoop 或 winget 安装,其实不用管它也行
把二进制文件下过来就好了(curl那步的命令里有文件地址)
https://dl.k8s.io/release/v1.30.0/bin/windows/amd64/kubectl.exe
编写本文时的地址如上
安装OIDC插件
https://github.com/int128/kubelogin
直接去release里下载
windows在本文编写时最新的地址为https://github.com/int128/kubelogin/releases/download/v1.28.1/kubelogin_windows_amd64.zip
注意
kubectl的版本号最多只能和群集差异一个小版本号,不管更新还是更旧,否则会无法通讯
配置kube和登录群集
配置kube config
通常路径~/.kube/config
windows下为"C:\Users\用户名\.kube\config"
config是一个纯文本的yaml文件且无后缀名,具体内容你应该向群集管理员取得
如果需要指定namespace,那么请参考以下位置
contexts:
- context:
cluster: cluster-name
user: your-username-oidc
namespace: 在这里填写命名空间
name: 群集名称
配置环境变量
随便找个地方,放置你取得的两个exe
然后将解压得到的kubelogin插件,重命名为kubectl-oidc_login.exe
写入环境变量PATH
接下来执行kubectl get pod
你应该会看到弹出认证界面在浏览器中,完成验证即可继续操作,此时已经完成了登录
概念
Pod
Pod是我们通常情况下操作和管理群集的最小单位
容器运行在pod中,一个pod中可能有多个相同服务的容器在运行,用于均衡负载和容灾
PV、PVC
全称为PersistentVolume、PersistentVolumeClaim,也就是永久卷和永久卷声明
可以简单的理解为永久存储卷,和docker那边的持久化存储是类似的玩意
在全程手动分配的情况下可能会出现先定义pv,在定义pvc的情况
实际上大多数情况下我们只需要直接编写pvc即可,告诉主控(我想要个卷),然后让群集自己分配一个出来,不用在乎pv是如何被定义的
Service(svc)、Ingress
用于暴露服务给外面,否则容器只能在容器间相互通讯
具体差异看下文
常用命令:获取容器状态
获取pod
kubectl get pod
返回
NAME READY STATUS RESTARTS AGE
python-pod 1/1 Running 0 3h17m
获取Service(svc)
kubectl get svc
返回
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
python-service ClusterIP 10.1.2.120 <none> 30000/TCP 3h19m
此处举例的是没有外部ip的情况
获取节点(Node)
你可以使用以下命令获取集群中所有节点的 IP 地址:
kubectl get nodes -o wide
返回
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
node1 Ready master 12d v1.18.8 192.168.1.10 <none> Ubuntu 20.04.1 LTS 5.4.0-42-generic docker://19.3.12
node2 Ready <none> 12d v1.18.8 192.168.1.11 <none> Ubuntu 20.04.1 LTS 5.4.0-42-generic docker://19.3.12
注意,受限的管理员账户可能无法执行此命令
常用命令:通过YAML创建/删除容器
此部分内容可参考docker-compose up -d和docker-compose down
创建(应用配置)
kubectl apply -f 配置文件
删除(停止容器)
kubectl delete -f 配置文件
需要注意的是,k8s没有实际意义上提供停止和重启容器的命令,需要实现类似效果你只能通过创建和删除容器
pvc、pod、service等服务的创建和删除通常也使用yaml直接进行,这样便于维护
常用命令:进入pod容器内部操作
注意,前提是这个pod在正常运行
并且和docker一样,一旦入口进程结束工作,那么整个容器都会停止运行
然后群集可能尝试重启它,注意不要搞出无限重启,然后退出的死循环,否则会非常糟糕
kubectl exec -it pod名称 -n 命名空间 -- /bin/bash
当然,也可能会遇到容器镜像里连个bash都不剩的情况,那么可以试试sh之类的
kubectl exec -it pod名称 -n 命名空间 -- /bin/sh
常用命令:从本地向群集复制文件
通常在首次部署pod时会用到它,将文件传入到pv中
kubectl cp 本地路径 命名空间/pod名称:pod内的绝对路径
注意,执行的前提也是有一个正常工作的pod且已被挂载了对应的pv,不能不经过pod直接把文件丢进pv里
常用命令:删除指定服务
-n 参数用于指定命名空间,通常来说它是可选的
删除 PersistentVolumeClaim(PVC)
kubectl delete pvc pvc名称 -n 命名空间
删除 Pod
kubectl delete pod pod名称 -n 命名空间
删除 Service
kubectl delete svc service名称 -n 命名空间
删除 Ingress
kubectl delete ingress ingress名称 -n 命名空间
将端口暴露给外部
TL;DR
- NodePort 适合简单的开发和测试环境,或者需要快速验证服务的场景。(对于测试环境,也可以通过Lens将端口映射回到本地)
- LoadBalancer 适合在云环境中部署的生产级服务,尤其是需要自动负载均衡和固定外部 IP 地址的场景。
- Ingress 适合复杂的 HTTP/HTTPS 服务和多域名场景,提供细粒度的流量控制和更高的安全性。
在 Kubernetes 中,NodePort、LoadBalancer 和 Ingress 是三种将集群内部服务暴露给外部访问的方式。它们之间的主要区别如下:
NodePort
工作原理:
- NodePort 服务在每个节点的某个固定端口(通常是 30000-32767 范围内的端口)上开放服务。
- 该端口将流量转发到集群内的相应服务。
- 用户可以通过
NodeIP:NodePort
访问服务。
优点:
- 简单易用,配置相对简单。
- 不需要额外的负载均衡器。
缺点:
- 需要手动指定或查询 NodePort 端口号。
- 每个节点都需要开放相应的端口,安全性和管理性较差。
- 不适合大规模的生产环境,因为无法自动进行负载均衡。
LoadBalancer
工作原理:
- LoadBalancer 服务会创建一个外部负载均衡器(例如 AWS ELB、GCP LB),并将流量转发到集群内的服务。
- 负载均衡器会自动分配一个外部 IP 地址,用户可以通过该 IP 地址访问服务。
优点:
- 自动进行负载均衡,适合大规模生产环境。
- 外部用户可以通过固定的 IP 地址访问服务。
缺点:
- 依赖云服务提供商提供的负载均衡器,因此需要支付额外费用。
- 配置和管理相对复杂。
Ingress
工作原理:
- Ingress 是一种 API 对象,管理集群内的 HTTP 和 HTTPS 路由。
- 它通过定义路由规则,将外部流量转发到集群内的服务。
- 需要一个 Ingress Controller 来实现具体的负载均衡和路由功能(例如 Nginx Ingress Controller)。
优点:
- 可以根据域名、路径等进行细粒度的流量控制和路由。
- 支持 TLS/SSL 终结,提供更高的安全性。
- 适合复杂的 HTTP/HTTPS 服务和多域名场景。
缺点:
- 需要部署和配置 Ingress Controller,初始配置相对复杂。
- 主要用于 HTTP/HTTPS 流量,对于其他协议支持有限。
补充:CLUSTER-IP是什么?
CLUSTER-IP
是 Kubernetes 中 Service 的内部 IP 地址,用于在集群内不同 Pod 之间通信。CLUSTER-IP
是一个虚拟 IP 地址,分配给每个 Service,确保集群内的 Pod 可以通过该 IP 地址访问相应的服务,而不需要直接知道服务背后的具体 Pod IP 地址。
具体用途
- 集群内部通信:
CLUSTER-IP
主要用于集群内部的服务发现和通信。集群内的其他 Pod 可以通过该 IP 地址访问服务,而无需了解具体的 Pod IP 地址。 - 负载均衡:Kubernetes Service 会将流量均匀分配到后端的多个 Pod 上,实现负载均衡。
Ingress类似平时使用的CDN回源的情况,依靠host名称来区分服务和流量指向,因此也只能用于穿透web服务
如果需要穿透标准TCP服务,那么你需要使用NodePort或LoadBalancer,当然他们也需要外部具有ipv4/v6公网地址才行,否则用了也是白用
Ingress在配置合理的情况下,不需要节点具有外部公网地址也可以使用(家里云也行)
注意
这三者只能由群集所有者进行配置,作为操作者你只能使用他们来暴露服务
例如Ingress,在外部回源回到群集服务中时,目标地址也需要群集所有者来告诉你
实战:创建一个使用Python官方镜像的pod来运行负载
注意,请自行修改name和namespace以符合你的需求
先起一个PV用于存放程序
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: python-pvc
namespace: moeworld
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
此处的storage: 1Gi代表这是个1G空间的pv,需要多少空间自己改数字即可
启动Pod并暴露服务
# python-pod-service.yaml
apiVersion: v1
kind: Pod
metadata:
name: python-pod
namespace: moeworld
labels:
app: python-app
spec:
containers:
- name: python-container
image: python:3.11
ports:
- containerPort: 35640
volumeMounts:
- mountPath: /data
name: python-data
command: ["/bin/bash", "-c", "tail -f /dev/null"]
volumes:
- name: python-data
persistentVolumeClaim:
claimName: python-pvc
---
apiVersion: v1
kind: Service
metadata:
name: python-service
namespace: moeworld
spec:
selector:
app: python-app
ports:
- protocol: TCP
port: 35640
targetPort: 35640
type: ClusterIP
这份配置文件我们使用了一个没有实际负载的入口命令用于保活(以便于我们把文件先复制进pv)
并使用ClusterIP将端口暴露了出去
command: ["/bin/bash", "-c", "tail -f /dev/null"]
这里就是容器启动后,自动执行的命令,我们需要在把程序放入pv后,然后删除pod,修改command并把真正的启动命令写到这里,重启pod
至此,这个容器应该已经能够正常工作了
如果需要使用Ingress来回源
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: python-ingress
namespace: moeworld
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
rules:
- host: 你的域名(不要带http/https)
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: python-service
port:
number: 35640
port: number: 35640 这里即为指定的端口号
遇到过的问题
The Service "***" is invalid: * spec.ports[0].name: Required value * spec.ports[1].name: Required value
在 Kubernetes 中,Service 的端口配置需要包含名称。名称字段是必需的
它在内部用于区分多个端口配置。我们需要为每个端口配置添加一个名称
Pod反复重启,但是就是启动不了
可能是由于pod配置文件里缺少了入口命令command,当入口进程结束,中控会认为这个容器已经挂了,会尝试重启(但是没有入口命令就永远都不可能收到入口进程在运行的信号)