K8S架构
-
master:
APISERVER:所有服务访问统一入口,负责处理接受请求的工作 ControllerManager:控制器管理器,维持副本期望数目 Scheduler:调度器,负责接收任务,选择合适的节点进行分配任务 ETCD:键值对数据库,储存K8S集群所有重要信息(持久化)
-
node:
Kubelet:直接跟容器引擎交互实现容器的生命周期管理 Kube-proxy:负责写入规则至IPTABLES,IPVS实现服务映射访问的;负责为Service提供cluster内部的服务发现和负载均衡 Container:容器进行时
-
其他插件:
COREDNS:可以为集群中的SVC创建一个域名IP的对应关系解析 DASHBOARD:给K8S集群提供一个B/S结构访问体系
查看当前k8s支持的API版本
kubectl api-versions
POD
容器组(Pod)是Kubernetes创建或部署的最小单位 。Pod是一组容器(可包含一个或多个应用程序容器),以及共享存储(卷 Volumes)、IP 地址和有关如何运行容器的信息。
网络通讯方式
-
Pod内部通信
同一个Pod共享同一个网络命令空间,共享同一个Linux协议栈。
Pod内部容器之间直接通过localhost**:**端口号的方式进行通信 ,但是它们不能在同一端口上同时开启服务,否则会有端口冲突,这就是共享网络栈的意思 。 Pod中还有一个比较特殊的叫pause的容器,这个容器运行的唯一目的是为Pod建立共享的veth0网络接口 ,其他容器共用pause的网络栈,共用存储卷。 **总结**:即pod内部容器是通过共享一个虚拟网卡相互通信的,可以直接通过localhost相互访问,而这个虚拟网卡是通过一个特殊的容器pause创建的。
-
相同节点的Pod通信
docker0是一个虚拟网桥,可以简单理解为一个虚拟交换机,它是支持该节点上的Pod之间进行IP寻址和互通的设备。Pod的IP是由docker0网桥分配的,例如上图docker0网桥的IP是172.17.0.1,它给第一个Pod分配IP为172.17.0.2。如果该节点上再启一个Pod2,那么相应的分配IP为172.17.0.3,如果再启动Pod可依次类推。因为这些Pods都连在同一个网桥上,在同一个网段内,它们可以进行IP寻址和互通
总结: Pod1与Pod2在同一个机器,由docker0的网桥直接转发请求至Pod2,不需要经过Flannel
-
不同节点的Pod通信
Pod1与Pod2不在同一个主机,Pod的地址是与docker0在同一个网段的,但docker0网段与宿主机网卡是两个完全不同的IP网段,并且不同Node之前的通信只能通过宿主机的物理网卡进行。经Pod的IP和所在Node的IP关联起来,通过这个关联让Pod可以相互访问。需要经过Flannel
Pod至Service的网络:目前基于性能考虑,全部为LVS维护和转发
-
Pod到外网
Pod向外网发送请求,查找路由表,转发数据包到宿主机的网卡,宿主网卡完成路由选择后,iptables执行Masquerade,把源IP更改为宿主网卡的IP,然后向外网发送请求
Pod的生命周期
-
启动pause容器
负责网络和数据卷初始化,并共享给里面的所有容器使用。
-
启动init容器
负责pod的初始化容器,执行完后退出。正常退出完后,才能开始启动main容器
-
启动主容器
-
启动就绪探针
探针是由kubelet对容器执行的定期诊断
Readiness Probe同样是周期性的检测Pod,然后根据响应来判断Pod是否就绪
Exec:Probe执行容器中的命令并检查命令退出的状态码,如果状态码为0则说明已经就绪。
HTTP GET:往容器的IP:Port发送HTTP GET请求,如果Probe收到2xx或3xx,说明已经就绪。
TCP Socket:尝试与容器建立TCP连接,如果能建立连接说明已经就绪。
Readiness Probe的工作原理
通过Endpoints就可以实现Readiness Probe的效果,当Pod还未就绪时,将Pod的IP:Port从Endpoints中删除,Pod就绪后再加入到Endpoints中
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:alpine
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
readinessProbe: # Readiness Probe
exec: # 定义 ls /ready 命令
command:
- ls
- /ready
readinessProbe: # readinessProbe
httpGet: # HTTP GET定义
path: /read
port: 80
readinessProbe: # readinessProbe
tcpSocket: # TCP Socket定义
port: 80
initialDelaySeconds:1 # 延迟1S查询
periodSeconds:3 # 查询3S一次
timeoutSeconds:10 # 超时时间
imagePullSecrets:
- name: default-secret
-
启动存活探针
指示容器是否正在运行。如果存活探针失败,则kubelet会杀死容器,并且容器将受到重启策略的影响。
-
启动,停止动作
容器启动前动作,和停止后动作
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:alpine
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
lifecycle:
postStart:
exec:
command:["/bin/sh","-c","echo xxxx >> xx.txt"]
prestop:
exec:
command:["/bin/sh","-c","echo xxxx >> xx.txt"]
Pod的编排与调度(控制器完成)
-
无状态负载(Deployment)
Kubernetes提供了Controller(控制器)来管理Pod,Controller可以创建和管理多个Pod,提供副本管理、滚动升级和自愈能力,其中最为常用的就是Deployment 。
apiVersion: apps/v1 # 注意这里与Pod的区别,Deployment是apps/v1而不是v1
kind: Deployment # 资源类型为Deployment
metadata:
name: nginx # Deployment的名称
spec:
replicas: 2 # Pod的数量,Deployment会确保一直有2个Pod运行
selector: # Label Selector
matchLabels:
app: nginx
template: # Pod的定义,用于创建Pod,也称为Pod template
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:latest
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
扩容:
kubectl scale deployment xxxx --replicas=10
-
有状态负载(StatefulSet)
每个Pod都有自己单独的状态,或者存储。
可以看看有状态负载(StatefulSet)_云容器引擎 CCE_Kubernetes基础知识_Pod的编排与调度_华为云
-
普通任务(Job)和定时任务(CronJob)
Job和CronJob是负责批量处理短暂的一次性任务
Job:是Kubernetes用来控制批处理型任务的资源对象。Job管理的Pod根据用户的设置把任务成功完成就自动退出(Pod自动删除)。
CronJob:是基于时间的Job,就类似于Linux系统的crontab文件中的一行,在指定的时间周期运行指定的Job。
Job类型 | 说明 | 使用示例 |
---|---|---|
一次性Job | 创建一个Pod直至其成功结束 | 数据库迁移 |
固定结束次数的Job | 依次创建一个Pod运行直至completions个成功结束 | 处理工作队列的Pod |
固定结束次数的并行Job | 依次创建多个Pod运行直至completions个成功结束 | 多个Pod同时处理工作队列 |
并行Job | 创建一个或多个Pod直至有一个成功结束 | 多个Pod同时处理工作队列 |
创建Job
apiVersion: batch/v1 # API版本 包含批处理、任务处理对象
kind: Job
metadata:
name: pi-with-timeout
spec:
completions: 50 # 运行的次数,即Job结束需要成功运行的Pod个数
parallelism: 5 # 并行运行Pod的数量,默认为1
backoffLimit: 5 # 表示失败Pod的重试最大次数,超过这个次数不会继续重试。
activeDeadlineSeconds: 10 # 表示Pod超期时间,一旦达到这个时间,Job及其所有的Pod都会停止。
template: # Pod定义
spec:
containers:
- name: pi
image: perl
command:
- perl
- "-Mbignum=bpi"
- "-wle"
- print bpi(2000)
restartPolicy: Never
创建CronJob
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cronjob-example
spec:
schedule: "0,15,30,45 * * * *" # 定时相关配置
jobTemplate: # Job的定义
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: main
image: pi
-
守护进程集(DaemonSet)
DaemonSet 确保全部(或者一些)Node上运行一个Pod的副本。当有node加入集群时,也会为他们新增一个Pod。当有Node移除,Pod也会被回收。删除DaemonSet 将会删除它创建的所有Pod。
使用DaemonSet的一些典型用法:
在每个Node上运行日志收集daemon,例如fluenth。
在每个Node上运行监控daemon,例如Prometheus
创建DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-daemonset
labels:
app: nginx-daemonset
spec:
selector:
matchLabels:
app: nginx-daemonset
template:
metadata:
labels:
app: nginx-daemonset
spec:
nodeSelector: # 节点选择,当节点拥有daemon=need时才在节点上创建Pod
daemon: need
containers:
- name: nginx-daemonset
image: nginx:alpine
resources:
limits:
cpu: 250m
memory: 512Mi
requests:
cpu: 250m
memory: 512Mi
imagePullSecrets:
- name: default-secret
打标签
kubectl label node xx.xx.xx.xx daemon=need
修改标签
kubectl label node xx.xx.xx.xx daemon=no --overwrite
-
亲和与反亲和调度
Service服务
原理说明
-
apiserver用户通过kubeclt命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中
-
每个节点上都有一个叫做kube-proxy的进程,这个进程通过etcd中数据负责感知service,pod的变化,并将变化的信息写入本地的ipvs规则中
-
ipvs使用NAT等技术将虚拟ip的流量转至endpoint中
解决直接访问Pod的问题
apiVersion: v1
kind: Service
metadata:
name: nginx # Service的名称
spec:
selector: # Label Selector,选择包含app=nginx标签的Pod
app: nginx
ports:
- name: service0
targetPort: 80 # Pod的端口
port: 8080 # Service对外暴露的端口
protocol: TCP # 转发协议类型,支持TCP和UDP
type: ClusterIP # Service的类型
说明:创建一个名为“nginx”的Service,通过selector选择到标签“app:nginx”的Pod,目标Pod的端口为80,Service对外暴露的端口为8080。
访问服务只需要通过“服务名称:对外暴露的端口”接口,对应本例即“nginx:8080”。这样,在其他Pod中,只需要通过“nginx:8080”就可以访问到“nginx”关联的Pod。
Service是如何做到服务发现的
$ kubectl describe svc nginx
Name: nginx
......
Endpoints: 172.16.2.132:80,172.16.3.6:80,172.16.3.7:80
......
可以看到一个Endpoints,Endpoints同样也是Kubernetes的一种资源对象,可以查询得到。Kubernetes正是通过Endpoints监控到Pod的IP,从而让Service能够发现Pod。
$ kubectl get endpoints
NAME ENDPOINTS AGE
nginx 172.16.2.132:80,172.16.3.6:80,172.16.3.7:80 5h48m
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-869759589d-dnknn 1/1 Running 0 5h40m 172.16.3.7 192.168.0.212
nginx-869759589d-fcxhh 1/1 Running 0 5h40m 172.16.3.6 192.168.0.212
nginx-869759589d-r69kh 1/1 Running 0 5h40m 172.16.2.132 192.168.0.94
Service的类型
Service的类型除了ClusterIP还有NodePort、LoadBalancer和Headless Service
ClusterIP:用于在集群内部互相访问的场景,通过ClusterIP访问Service
NodePort:用于从集群外部访问的场景,通过节点上的端口访问Service
LoadBalancer:用于从集群外部访问的场景,其实是NodePort的扩展,通过一个特定的LoadBalancer访问Service,这个LoadBalancer将请求转发到节点的NodePort,而外部只需要访问LoadBalancer
Headless Service:用于Pod间的互相发现,该类型的Service并不会分配单独的ClusterIP, 而且集群也不会为它们进行负载均衡和路由
yaml常用字段说明
参数名 | 字段类型 | 说明 |
---|---|---|
apiversion | string | 指K8S API的版本,可以用kubectl api-version命令查询 |
kind | string | 资源类型,如pod |
metadata | object | 元数据对象,固定值就写metadata |
metadata.name | string | 元数据对象的名字,比如命名Pod的名字 |
metadata.namespace | string | 元数据对象的命名空间 |
Spec | object | 详细定义对象,固定值就写Spec |
Spec.containers[] | list | Spec对象的容器列表定义,是个列表 |
Spec.containers[].name | string | 定义容器的名字 |
Spec.containers[].image | string | 定义容器的镜像 |
常见错误
address already in use:
level=warning msg="Error starting load balancer: listen tcp 127.0.0.1:6444: bind: address already in use"
原因:
agent服务器同时启动了K3S-master服务,导致agent一直挂载不上master。
解决方案:
netstat -tulnp | grep LISTEN #查看监控端口 journalctl -xeu k3s-agent.service #查看服务日志 service k3s stop #关闭K3S-master服务
kubectl logs 查询日志报错:
proxyconnect tcp: proxy error from 127.0.0.1:6443 while dialing proxy.xxx.com:8080, code 503: 503 Service Unavailable
原因:
k3s配置代理,没有把自身配置到no_proxy中。
解决方案:
vim /etc/systemd/system/k3s.service.env
NO_PROXY=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
K3S配置代理:
如果你运行 K3s 的环境中只通过 HTTP 代理进行外部连接,你可以在 K3s 的 systemd 服务上配置代理。K3s 将使用这些代理设置,并向下传递到嵌入式 containerd 和 kubelet。
K3s 安装脚本会自动使用当前 shell 中的 HTTP_PROXY
、HTTPS_PROXY
和 NO_PROXY
,以及 CONTAINERD_HTTP_PROXY
、CONTAINERD_HTTPS_PROXY
和 CONTAINERD_NO_PROXY
变量(如果存在),并将它们写入 systemd 服务的环境文件,通常是:
-
/etc/systemd/system/k3s.service.env
-
/etc/systemd/system/k3s-agent.service.env
你也可以通过编辑这些文件来配置代理。
K3s 会自动将集群内部 Pod 和 Service IP 范围以及集群 DNS 域添加到 NO_PROXY
条目列表中。你需要确保 Kubernetes 节点本身使用的 IP 地址范围(即节点的公共和私有 IP)包含在 NO_PROXY
列表中,或者可以通过代理访问节点。
HTTP_PROXY=http://your-proxy.example.com:8888 HTTPS_PROXY=http://your-proxy.example.com:8888 NO_PROXY=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
如果你想在不影响 K3s 和 Kubelet 的情况下为 containerd 配置代理,你可以在变量前加上 CONTAINERD_
:
CONTAINERD_HTTP_PROXY=http://your-proxy.example.com:8888 CONTAINERD_HTTPS_PROXY=http://your-proxy.example.com:8888 CONTAINERD_NO_PROXY=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
K3S-agent下载镜像失败:
rpc error: code = DeadlineExceeded desc = failed to pull and unpack image
原因:
k3s-agent上没有配置代理,或者没有配置加速器。注意一定要master配置好后再启动agent
解决方案:
vim /var/log/syslog #查看系统日志
vim /etc/systemd/system/k3s-agent.service.env #配置agent上代理
#NO_PROXY="10.169.0.0/24,10.169.0.0/16"
#HTTP_PROXY="http://******@proxy.xxx.com:8080"
#HTTPS_PROXY="http://******@proxy.xxx.com:8080"
CONTAINERD_HTTP_PROXY=http://******@proxy.xxx.com:8080
CONTAINERD_HTTPS_PROXY=http://******@proxy.xxx.com:8080
CONTAINERD_NO_PROXY=10.0.0.0/24,100.0.0.0/24,10.169.0.0/16,10.169.0.0/24,172.19.90.131/24
vim /var/lib/rancher/k3s/agent/etc/containerd/config.toml #配置agent加速器
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://fogjl973.mirror.aliyuncs.com", "https://registry-1.docker.io"]
cat >> /etc/rancher/k3s/registries.yaml <<EOF #配置master加速器
mirrors:
"docker.io":
endpoint:
- "https://fogjl973.mirror.aliyuncs.com"
- "https://registry-1.docker.io"
EOF
systemctl restart k3s
service k3s-agent restart
systemctl restart containerd.service