本文发布于Cylon的收藏册,转载请著名原文链接~
Kubernetes集群的构成
Master Node (Control plane)
Master 是整个 Kubernetes 集群构成的基础,它负责整个集群的管理,例如处理集群的状态;组件包含 API Server, Controller manager, Scheduller, Etcd
API server
API 服务器是 Master 的统一前端入口,负责集群内其他组件的 协调 与 通信。该组件用于定义集群的状态。可以通过命令行, HTTP API, 第三方托管平台(dashboard, Rancker, Kuboard等)与 Kubernetes API 进行交互。
Scheduler
调度程序 Scheduler 负责根据可用资源来决定如何去部署容器,部署到哪里?确保所有 Pod(容器组)都分配给某一组节点。
Controller Manager
Controller manager,又分为Controller 和 Manager,Controller的组要作用是用于协调各种控制器(Deployment, Daemonset…),这些控制器可确保在节点发生故障时采取适当的措施。而 Manager 则管理的众多Controller;更一般地说,CM 负责随时将集群的当前状态调整到所需状态(Kubernetes设计基石)。
etcd
etcd 是控制平面内的一个组件,他提供了 Kubernetes 资源的存储,并为集群内组件提供了 Watch 的功能,这将意味着,etcd 在 kubernetes 集群中作为存储与分布式协调的功能。
Worker nodes
每个集群中至少需要存在一个工作节点,但是通常会有大量的节点;而工作节点包括的组件不限于 Kubelet, Kube-proxy, CNI Plugin。
Kubelet
kubelet是工作节点中管理运行时的组件,负责整个Pod (容器组)进程的生命周期
Kube-proxy
Kube-proxy 为整个集群内提供了 service 的功能,如果这个组件无法正常工作,那么整个集群内的网络通信将不能正常,因为 service 是作为集群内服务的访问入口,包含 Kubernetes API service。
CNI
CNI (Container Network Interface) 是 Kubernetes 集群中提供跨节点通信的一个组件,通常来说,它是一个独立于容器运行时(Docker等)的网络插件规范,旨在实现容器之间和容器与宿主机之间的网络连接。
一个 CNI 基本的功能就是去管理网络设备的生命周期,例如,生成网卡,添加IP地址,注销网卡,而构成这些网络功能的则有操作系统来提供的,例如网络隧道(VxLAN),而还存在一种情况就是三层网络,这时CNI会作为一个路由器来分发路由。除此之外,CNI还提供了更多的功能,例如数据包加密,网络策略,服务网格,网络加速,IPAM等功能
通过二进制安装Kubernetes cluster
硬件配置推荐
在选择节点数量时,需要考虑集群的用途,通常情况下建议至少使用 3 个节点 (APIServer),对于控制平面其他组件来说,通常最少为两个即可,因为HA架构中工作节点总是需要一个即可
要运行 apiserver 和 etcd,您需要一台具有 2 个内核和 2GB RAM 的 机器,用于中小型集群,更大规模集群可能需要更多的核心。工作节点必须有足够的资源来托管您的应用程序,并且可以有不同的配置。
etcd 硬件配置推荐
Kuberentes 中工作节点需要启动一个或多个 etcd 实例,通常官方推荐运行奇数个 etcd 实例,因为一个 3 实例时有两个活跃就具备了集群的 quorum ,同理五个实例时存在3个实例活跃即可,7=>4 等,通常集群规模不大于9,否则存在同步时的延迟。
集群的部署模式
- 单独托管的 etcd 集群
- master节点同台主机上托管 etcd 集群
- 通过 kubeadm 生成的 etcd 集群
在这里我们使用二进制方式最小化部署,即 单节点的 etcd 集群,与同时为 control plane 与 worker 一体托管在单台物理/虚拟机之上的 Kubernetes 集群
题外话:kubeadm 和 二进制究竟有什么区别?
实际上 kubeadm 和 二进制本质上并没有什么区别,如果非要说存在区别的话,那么就是其 Procss Manager 不同,
- 一个是由 systemd/system v 或其他操作系统维护的1 id的进程进行维护;
- kubeadm 部署的 kubelet 将 由
kubelet
进程自己来监控,当pod
崩溃时重启该pod
,kubelete
也无法对他们进行健康检查。静态 pod 始终绑定在某一个kubelet
,并且始终运行在同一个节点上,可以通过在 master 节点上查看/etc/kubernetes/manifests
目录
配置证书
通常情况下,大家都知道安装 kubernetes 集群需要证书,但是不知道为什么需要证书,这里需要了解 Kubernetes 认证机制,也就是“用户”在kubernetes集群中被称为什么
Kubernetes 中用户被分为几类:
- X.509 证书
- 静态 Token 文件
- Bootstrap Tokens
- Service Account Tokens
kubernetes 使用了客户端证书方式进行认证,这是作为外部用户的一种方式,这些证书会被默认生成对应的内部用户,所以需要准备证书文件,在本文中一切准备文件都是由脚本生成,不做基础的配置讲解。
对于 X.509 证书,需要注意有几点
- kubernetes 中,对于证书的用户,CN就是用户名,O 就是组织。
- 对于证书认证来说,客户端证书,也就是说客户端必须在可信任列表中,也就是 subject_name 中允许的
- 对于 Kubernetes 组件来说,通常情况下需要包含 Kubernetes API service 的所有名称(短域名+完整域名)
- kubernetes
- kubernetes.default
- kubernetes.default.svc
- kubernetes.default.svc.cluster
- kubernetes.default.svc.cluster.local
如果你希望使用IP,而不是域名,也可以对证书中sub_name增加对应域名
对于组件间认证,目前 Kubernetes 中基本上组件间认证都有 kubeconfig 完成,而 kubelet,kube-controller-manager 这两个组件,由于提供的功能不同,可能存在其他的认证方式;例如 kubelet 所作的事情是 “监听Pod资源进行部署” 那么这个时候与 APIServer 通讯使用了 kubeconfig,在例如 kubelet 要被 APIServer 签发时,此时认证是使用的 bootstrap token 进行的,而这个kubeconfig 就是 签发后生成的内容,本质上来说,kubelet 没有客户端证书,只有token,而例如 kube-scheduler 是有客户端证书,但是需要生产 kubeconfig 文件
签发kubelet
给 kubelet 签发证书主要由两部分组成,一种是 kube-controller-manager 自动签发,一种是 kubectl certifcate approve
手动签发,这里就有必要知道 kubelet 的认证的流程:
- kubelet启动时会找 kubeconfig ,如果没有进入下一部
- 没有 kubeconfig,会使用 bootstraps 进行认证,此时会被颁发客户端证书
- 在通过后,kubelet 会根据签发证书 生成 –kubeconfig 指定的 kubeconfig 文件
- 下次后,kubelet 会根据这个 kubeconfig 同 Kubernetes API 进行 验证
了解了 kubelet 的签发过程,就明白在二进制部署时,为什么需要做一个 clusterrolebinding ?
因为Kubernetes API 为 kubelet 的 bootstraps-token 使用的是用户 system:node-bootstrapper
,所以要想让这个用户可以访问 API 资源,那么就需要为这个用户绑定上集群角色(用户/组),这就需要执行一个命令
$ kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers
此时再去查看证书颁发,这时因为有权限访问 API 资源了, 所以获得了证书的被签发
$ kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr-a-MREQ1IybB0U5M8RP5FasSjckQOZiCoCYlf8ipDwx8 5m11s system:bootstrapper Pending
kube-dns 的部署
coredns部署
$ mkdir coredns && cd coredns
$ wget https://raw.githubusercontent.com/coredns/deployment/master/kubernetes/coredns.yaml.sed
$ wget https://raw.githubusercontent.com/coredns/deployment/master/kubernetes/deploy.sh
$ bash deploy.sh -i 10.96.0.10 -r "10.96.0.0/12" -s -t coredns.yaml.sed |kubectl apply -f -
开启IPVS
内核开启IPVS功能否则降级
cat > /etc/sysconfig/modules/ipvs.modules << EOF
#!/bin/bash
ipvs_mods_dir="/usr/lib/modules/$(uname -r)/kernel/net/netfilter/ipvs"
for i in \$(ls \$ipvs_mods_dir | grep -o "^[^.]*"); do
/sbin/modinfo -F filename \$i >/dev/null
if [ \$? -eq 0 ]; then
/sbin/modprobe -- \$i
fi
done
EOF
or
cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
ipvs_modules="ip_vs ip_vs_lc ip_vs_wlc ip_vs_rr ip_vs_wrr ip_vs_lblc ip_vs_lblcr ip_vs_dh ip_vs_sh ip_vs_fo ip_vs_nq ip_vs_sed ip_vs_ftp nf_conntrack_ipv4"
for kernel_module in \${ipvs_modules}; do
/sbin/modinfo -F filename \${kernel_module} > /dev/null 2>&1
if [ $? -eq 0 ]; then
/sbin/modprobe \${kernel_module}
fi
done
EOF
chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep ip_vs
Troubleshooting
apiserver报错 没有kubeappserver用户
Failed at step USER spawning No such process
Mar 2 04:54:56 node02 systemd: controller-manager.service: main process exited, code=exited, status=1/FAILURE
Mar 2 04:54:56 node02 kube-controller-manager: Get http://127.0.0.1:8443/api/v1/namespaces/kube-system/configmaps/extension-apiserver-authentication: net/http: HTTP/1.x transport connection broken: malformed HTTP response "\x15\x03\x01\x00\x02\x02"
Mar 2 04:54:56 node02 systemd: Unit controller-manager.service entered failed state.
可能是启动单元文件有问题,手动启动后正常
Mar 2 09:44:51 node02 kube-controller-manager: W0302 09:44:51.672404 39926 client_config.go:554] error creating inClusterConfig, falling
back to default config: unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined
node “xxxx” not found
如下面问题,可能原因为 kubelet 正式还未签发
Nov 14 16:31:03 master01 kubelet: E1114 16:31:03.677280 8587 kubelet.go:2270] node "master01" not found
380 19810 kubelet.go:2292] node "master-machine" not found
May 12 23:45:11 master-machine kubelet: E0512 23:45:11.415099 19810 kubelet.go:2292] node "master-machine" not found
May 12 23:45:11 master-machine kubelet: E0512 23:45:11.460097 19810 certificate_manager.go:434] Failed while requesting a signed certificate from the master: cannot create certificate signing request: certificatesigningrequests.certificates.k8s.io is forbidden: User "system:anonymous" cannot create resource "certificatesigningrequests" in API group "certificates.k8s.io" at the cluster scope
User “system:anonymous” cannot list resource xxx
原因为:kubelet 正式还未签发
k8s.io/client-go/informers/factory.go:135: Failed to list *v1.CSIDriver: csidrivers.storage.k8s.io is forbidden: User "system:anonymous" cannot list resource "csidrivers" in API group "storage.k8s.io" at the cluster scope
问题:
"master01" is forbidden: User "system:anonymous"
原因:用户未授权
解决 :
kubectl create clusterrolebinding kube-apiserver:kubelet-apis --clusterrole=system:kubelet-api-admin --user kubernetes-master
&&kubectl certificate approve
master01 kubelet: E1114 17:16:33.581116 8559 kubelet.go:2270] node "master01" not found
failed to ensure node lease exists, will retry in 6.4s, error: leases.coordination.k8s.io "master01" is forbidden: User "system:anonymous" cannot get resource "leases" in API group "coordination.k8s.io" in the namespace "kube-node-lease"
容器内 dial tcp 10.96.0.1:443: connect: connection timed out
问题1: flannela访问
dial tcp 10.96.0.1:443: connect: connection timed out
问题2:
open /run/flannel/subnet.env: no such file or directory
问题3:
Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
解决:检查kube-proxy组件
Nov 14 17:30:04 master01 kubelet: E1114 17:30:04.097261 1160 kubelet.go:2190] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
$ kubectl logs kube-flannel-ds-dvsv7 -n kube-system
....
E1114 23:22:20.984238 1 main.go:243] Failed to create SubnetManager: error retrieving pod spec for 'kube-system/kube-flannel-ds-dvsv7': Get "https://10.96.0.1:443/api/v1/namespaces/kube-system/pods/kube-flannel-ds-dvsv7": dial tcp 10.96.0.1:443: connect: connection timed out
$
kubectl get csr 显示No Resources Found的解决记录
问题:kubectl get csr 显示No Resources Found的解决记录
解决:需要先将 bootstrap token 文件中的 kubelet-bootstrap 用户赋予 system:node-bootstrapper 角色,然后 kubelet 才有权限创建认证请求
授予KUBE-APISERVER对KUBELET API的访问权限
kubelet启动启动时,--kubeletconfig
使用参数对应的文件是否存在 如果不存在--bootstrap-kubeconfig
指定的kubeconfig文件向kube-apiserver发送CSR请求
kube-apiserver 收到 CSR 请求后,对其中的 token 进行认证,认证通过后将请求的用户设置为system:bootstrap:<Token ID>
,组设置为 ,system:bootstrappers
此操作称为Bootstrap Token Auth
。
默认这个用户和组没有创建CSR的权限,kubelet没有启动,错误日志如下:
Unable to register node "master-machine" with API server: nodes is forbidden: User "system:anonymous" cannot create resource "nodes" in API group "" at the cluster scope
解决方法是:创建一个集群角色绑定,绑定一个组:system:bootstrapper
和一个clusterrole system:node-bootstrapper
$ kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers
测试授权
kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers
system:serviceaccount:default:default
kubectl create clusterrolebinding firewalld-default --clusterrole=system:aggregate-to-admin --user=system:serviceaccount:default:default
system:aggregate-to-admin
kube flannel cant get cidr although podcidr available on node
E0729 06:56:09.253632 1 main.go:330] Error registering network: failed to acquire lease: node "master-machine" pod cidr not assigned
I0729 06:56:09.253682 1 main.go:447] Stopping shutdownHandler...
W0729 06:56:09.253809 1 reflector.go:436] github.com/flannel-io/flannel/subnet/kube/kube.go:403: watch of *v1.Node ended with: an error on the server ("unable to decode an event from the watch stream: context canceled") has prevented the request from succeeding
参考:kube flannel cant get cidr although podcidr available on node 原因为 CIDR无法分配
mvcc: required revision has been compacted
kube-apiserver: W1120 17:18:20.199950 70454 watcher.go:207] watch chan error: etcdserver: mvcc: required revision has been compacted
这里是etcd返回的错误,被apiserver视为警告,在注释中有这么一句话
If the context is “context.Background/TODO”, returned “WatchChan” will not be closed and block until event is triggered, except when server returns a non-recoverable error (e.g. ErrCompacted).
如果这个上下文返回WatchChan将在下次事件被触发前不会被关闭或阻塞,除非服务器返回一个ErrCompacted (不可恢复)
对于etcd 对 Revision 有如下说明
etcd对每个kv的revision 都会保留一个压缩周期的值,例如每5分钟收集一次最新revision,当压缩周期达到时,将从历史记录中后去最后一个修订版本,例如为100,此时会压缩,压缩成功会重置计数器,并以最新的revision和新的历史记录进行开始,压缩失败将在5分钟后重试--auto-compaction-retention=10
是配置压缩周期的 每多少个小时键值存储运行定期压缩。
如果最新的revision已被修订,etcd返回一个 ErrCompacted
表示已修订,此时表示为不可恢复状态
返回错误时表示这个watch被关闭,为了优雅的关闭chan,kubernetes会对这个watch错误进行返回,而 ErrCompacted
本质上不算错误
wch := wc.watcher.client.Watch(wc.ctx, wc.key, opts...)
for wres := range wch {
if wres.Err() != nil {
err := wres.Err()
// If there is an error on server (e.g. compaction), the channel will return it before closed.
logWatchChannelErr(err)
wc.sendError(err)
return
}
...
https://etcd.io/docs/v3.5/op-guide/maintenance/#auto-compaction
本文发布于Cylon的收藏册,转载请著名原文链接~
链接:https://www.oomkill.com/2019/01/kubernetes-install-with-binary-files/
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」 许可协议进行许可。