[TOC]
K8S 的角色及组件
管理节点
- Master(control plane):Kubernetes集群的控制节点,主要职责是调度,即决定应用放在哪里运行。为了实现高可用,可以运行多个Master。Master节点由三个组件组成:
apiserver
:提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制。scheduler
:集群调度器,负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上。controller-manager
:集群控制器,它运行着所有处理集群日常任务的控制器。包括了节点控制器、副本控制器、端点(endpoint)控制器以及服务账户和令牌控制器。每一个控制器都独立工作以维护其所需的状态。简单来讲,就是负责维护集群的状态,比如:故障检测、自动扩展、滚动更新等。
数据库节点
Etcd
:key/value 数据库,用于存储Kubernetes集群相关信息,包括节点信息、pod信息、service信息等,只与apiserver交互。
工作节点
- Node(Data plane):Kubernetes集群的工作节点,负责容器应用的运行,由Master管理,Node负责监控并汇报容器状态,同时根据Master的要求管理容器的生命周期。由两个组件组成:
kubelet
:负责管控docker容器,如启动、停止、监控运行状态等。它会定期从apiserver获取分配到本机的pod,并根据pod信息启动或停止相应的容器。同时,它也会接收apiserver的http请求,汇报pod的运行状态。也负责Voleme(CVI)和网络(CNI)的管理。kube-proxy
:负责为pod提供代理。它会定期从apiserver获取所有的service,并根据service信息创建代理。当某个客户pod要访问其它pod时,访问请求会经过本机的proxy做转发。同时负责为Service提供cluster内部的服务发现和负载均衡。
K8S 的整体工作流程
以 deployment 为例,分析向 k8s 提交一个 yaml 文件后的工作流程:
- 准备好一个 Deployment 的 YAML 文件,通过 kubectl 发送到 api-server;
- api-server 接收到客户端的请求,将资源内容存储到数据库 etcd 中;
- Controller 组件(包括 Scheduler、replication、endpoint )监控资源变化并做出反应;
- ReplicaSet 检查数据库变化,创建期望数量的 Pod 实例;
- Scheduler 再次检查数据库变化,发现尚未被分配到具体节点的 Pod,然后根据一组相关规则,将 Pod 分配到可以运行它们的节点上,并更新数据库记录,记录 Pod 分配情况;
- Kubelet 监控数据库变化,管理后续 Pod 生命周期,发现被分配到它所在节点上运行的那些 Pod。如果发现新 Pod,则会在该节点上运行这个新 Pod;
- Kube-proxy 运行在集群的各个主机上,管理网络通信、服务发现、负载均衡。当有数据发送到主机时,其将路由到正确的 Pod 或容器,对于主机上发出的数据,它可以基于请求地址发现远程服务器,并将数据正确路由,在某些情况下会使用轮询调度算法(RR)将请求发送到集群中的多个实例。
Kube-apiserver 的工作
当 YAML 文件被提交到 kube-apiserver 后:
- 过滤这个请求,完成一些前置性工作(比如:授权、超时、处理、审计等);
- 进入 MUX 和 Routes 流程。
MUX 和 Routes 要做的就是完成 URL 和 Handler 绑定的场所。
Handler 则是按照 /api(apis)/Groups/Version/Resource,对于 Deployment 来说则是 /api/v1/deployments(node、pid、deployment是 "")找到 Deployment 类型的定义; - kube-apiserver 会根据这个 Deployment 类型的定义,使用用户提交的 YAML 文件里的字段,创建一个 CronJob 对象。
在这个过程中,API Server 会进行一个 Convert工作,即:把用户提交的 YAML 文件转换成一个叫做 Super Version 的对象,它正式该 API 资源类型所有版本的字段全集。这样用户提交的不同版本的 YAML 文件,就都可以用这个 Super Version 对象来进行处理了。
- kube-apiserver 先后进行 Admission() 和 Validation() 操作。
Validation 负责验证这个对象的各个字段是否合法。这个被验证过的API对象,都保存在 kube-apiserver 中一个叫做 Registry 的数据结构中。
- kube-apiserver 把验证过的 API 对象转换为用户最初提交的版本,进行序列化操作,并调用 etcd 的 API 保存起来。
Controller 的工作
从 apiserver 中获取它所关心的对象。这个操作是依靠 Informer 完成的,Informer 和 API 对象一一对应。在创建 Informer 的时候,需要传递一个 networkClient,Informer 通过 networkClient 和 apiserver 建立连接。真正负责维护这个连接的,是 Informer 的 Reflector,Reflector 使用 ListAndWatch 方法获取并监听 API 对象实例的变化。
在 ListAndWatch 机制下,一旦 apiserver 有新的 API 实例被创建、删除或者更新,Reflector 都会收到"事件通知"。
这时,该事件和它所对应的 API 对象这个组合,就被称为增量(Delta),它被放进一个 Delta FIFO Queue 中。
另一方面,Informer 会从这个队列中读取增量。每拿到一个增量,Informer 就会判断这个增量里的事件类型,然后创建或者更新本地对象缓存(Store);并且 Informer 会根据这个事件类型,触发事先注册号的 ResourceEventHandler。这些 Handler 需要在创建控制器的时候注册给它对应的 Informer。
在控制器中,还有一个工作队列(WorkQueue),这个工作队列的作用就是负责同步 Informer 和控制循环之间的数据。
比如在 Handler 中注册了三个 Handler(AddFunc、UpdateFunc、DeleteFunc),然后将该事件对应的 API 对象的 key 加入到工作队列中。
后面的控制循环,则会不断从工作队列中拿到 API 对象的 key,然后到缓存中去查询对应的 API 对象,执行真正的逻辑控制。
各节点需安装的组件
- master
- kube-apiserver
- kube-controller-manager
- kube-scheduler
- etcd
- kubelet
- kube-proxy
- docker
- node
- kubelet
- kube-proxy
- docker
单 master K8S 集群部署
环境说明
部署环境: | 主机名 | IP地址 | 节点类型 | 配置 |
---|---|---|---|---|
cka01 | 192.168.6.171 | master、etcd | 最低2C、4G | |
cka02 | 192.168.6.172 | worker | 最低2C、4G | |
cka03 | 192.168.6.173 | worker | 最低2C、4G |
相关组件版本说明: | 组件 | 版本 | 说明 |
---|---|---|---|
kubernetes | 1.20.9 | 主程序 | |
docker | 19.03.15 | 容器运行时 | |
calico | 3.18.1 | 网络插件 | |
etcd | 3.4.13 | 数据库 | |
coredns | 1.7.0 | dns组件 | |
kubernetes-dashboard | v2.2.0 | web界面 |
集群安装步骤
-
基础环境准备,需要在所有节点上操作
- 配置主机名
hostnamectl set-hostname cka01 --static
- 添加 /etc/hosts (每个节点添加对自己的解析就可以了)
echo "192.168.6.171 cka01" >> /etc/hosts
- 清空防火墙
iptables -F iptables -t nat -F
- 关闭 selinux
setenforce 0 sed -i 's/SELINUX=/SELINUX=disabled/g' /etc/selinux/config
- 设置 yum 源
yum install -y wget wget -O /etc/yum.repos.d/CentOS-Base.repo https://repo.huaweicloud.com/repository/conf/CentOS-7-reg.repo yum install -y epel-release sed -i "s/#baseurl/baseurl/g" /etc/yum.repos.d/epel.repo sed -i "s/metalink/#metalink/g" /etc/yum.repos.d/epel.repo sed -i "s@https\?://download.fedoraproject.org/pub@https://repo.huaweicloud.com@g" /etc/yum.repos.d/epel.repo
-
关闭 swap
swapoff -a sed -ri 's/(.*swap.*)/#\1/' /etc/fstab // 为什么要关闭swap? // 1. 支持swap很复杂;2. 使用swap的话,性能会很大影响。 所以在 k8s 1.8 版本强制要求必须关闭swap。
-
配置内核参数
cat << EOF | sudo tee /etc/sysctl.d/k8s.conf net.ipv4.ip_forward = 1 vm.swappiness = 0 net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF // 如果两个 net.bridge 报错,可忽略 // vm.swappiness 的作用是禁止将内存中的数据交换到swap,但是并不会关掉swap。 sysctl -p /etc/sysctl.d/k8s.conf
-
加载相关内核模块
cat > /etc/sysconfig/modules/ipvs.modules << EOF #! /bin/bash modprobe -- br_netfilter modprobe -- ip_vs modprobe -- ip_vs_rr modprobe -- ip_vs_wrr modprobe -- ip_vs_sh modprobe -- nf_conntrack_ipv4 EOF chmod 755 /etc/sysconfig/modules/ipvs.modules && \ bash /etc/sysconfig/modules/ipvs.modules && \ lsmod | grep -E 'ip_vs|nf_conntrack_ipv4'
-
安装 docker
yum install -y yum-utils device-mapper-persistent-data lvm2 wget -O /etc/yum.repos.d/docker-ce.repo https://repo.huaweicloud.com/docker-ce/linux/centos/docker-ce.repo sed -i 's+download.docker.com+repo.huaweicloud.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo yum install -y docker-ce-19.03.15 mkdir /etc/docker cat > /etc/docker/daemon.json << EOF { "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "10" }, "live-restore": true, "registry-mirrors": ["https://o9x1boqy.mirror.aliyuncs.com"] } EOF systemctl enable docker --now
-
安装 kubelet、kubectl、kubeadm
cat <
/etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF yum install -y kubelet-1.20.9 kubeadm-1.20.9 kubectl-1.20.9 systemctl enable kubelet --now # kubelet作为单独服务,需要启动
- 配置主机名
-
配置 master 节点
- 生成 kubeadm-config.yaml 文件
kubeadm config print init-defaults > kubeadm-config.yaml
- 编辑 kubeadm-config.yaml 文件
apiVersion: kubeadm.k8s.io/v1beta2 bootstrapTokens: - groups: - system:bootstrappers:kubeadm:default-node-token token: abcdef.0123456789abcdef ttl: 24h0m0s usages: - signing - authentication kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.6.171 #修改ip地址 bindPort: 6443 nodeRegistration: criSocket: /var/run/dockershim.sock name: cka01 #修改主机名 taints: - effect: NoSchedule key: node-role.kubernetes.io/master --- apiServer: timeoutForControlPlane: 4m0s apiVersion: kubeadm.k8s.io/v1beta2 certificatesDir: /etc/kubernetes/pki clusterName: kubernetes controllerManager: {} dns: type: CoreDNS etcd: local: dataDir: /var/lib/etcd imageRepository: registry.aliyuncs.com/google_containers #修改拉取k8s镜像的仓库,默认的 k8s.gcr.io 是 google 的仓库,需要翻墙 kind: ClusterConfiguration kubernetesVersion: v1.20.9 #修改版本号 networking: dnsDomain: cluster.local serviceSubnet: 10.96.0.0/12 podSubnet: 10.244.0.0/16 #添加Pod使用的子网,calico插件默认使用 192.168.0.0/16 的网络,如果需要自定义网络,则calico会自动从cubeadm工具部署的已运行的k8s配置中读取pod子网。如果是其它工具部署,就需要修改 CALICO_IPV4POOL_CIDR 参数。 scheduler: {} --- apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: "ipvs"
- 关于 kubeadm-config.yaml 更多配置语法参考:https://godoc.org/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2
- 使用 kubeadm-config.yaml 配置主节点:https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/control-plane-flags/
- kube-proxy开启ipvs参考: https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/ipvs/README.md
- kubelet的配置示例参考: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/kubelet-integration/#configure-kubelets-using-kubeadm
- 根据配置的 kubeadmin-config.yaml 文件部署 master
kubeadm init --config kubeadm-config.yaml
-
配置访问集群(kubelet命令需要读取此配置文件)
- 普通用户:
mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -u) $HOME/.kube/config
- root 用户:
export KUBECONFIG=/etc/kubernetes/admin.conf
部署完成后,发现两个问题:
- 使用
kubectl get nodes
命令发现:master 节点一直处于 NotReady - 使用
kubectl get pods -n kube-system
命令发现:coredns pod 一直处于 pending
这两个问题都是因为还没有安装网络插件导致的。
- 普通用户:
- 安装网络插件
curl https://docs.projectcalico.org/manifests/calico.yaml -O kubectl apply -f calico.yaml //安装完成后,等一会,master的状态就会转变成 Ready
- 验证集群状态
kubectl get nodes
:查看节点状态kubectl get pods -n kube-system
:查看所有控制面组件的运行状态kubectl get componentstatuses|cs
:查看各组件健康状态
- 生成 kubeadm-config.yaml 文件
-
配置 worker 节点
在master节点上,当部署master成功时,会返回类似如下信息:Then you can join any number of worker nodes by running the following on each as root: kubeadm join 192.168.6.171:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:e275f28377f62b2884ee501a77fce2a6f9340aed5cc69c2453aee931203f94b0 // 在worker节点上执行上面命令,就可以将节点加入到K8S集群
-
将节点加入集群
kubeadm join 192.168.6.171:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:e275f28377f62b2884ee501a77fce2a6f9340aed5cc69c2453aee931203f94b0
- 注意:以上指令中的token有效期只有24小时,当token失效以后,可以使用
kubeadm token create --print-join-command
生成新的添加节点指令。
- 注意:以上指令中的token有效期只有24小时,当token失效以后,可以使用
-
从集群中删除节点
kubectl delete nodes cka03 //如果需要重新加入集群,还需要删除下面两个文件 // rm -f /etc/kubernetes/kubelet.conf /etc/kubernetes/pki/ca.crt && systemctl restart kubelet
-
扩展插件安装
在集群安装完成之后,正式投产还需要安装另外一些插件。
安装扩展插件,只需要在 master 节点上操作。
安装 helm
helm的github仓库地址:https://github.com/helm/helm
安装:
wget https://breezey-public.oss-cn-zhangjiakou.aliyuncs.com/softwares/linux/helm-v3.0.3-linux-amd64.tar.gz
#添加官方chart
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
安装 ingress
ingress的github地址:https://github.com/kubernetes/ingress-nginx
安装:
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/deploy.yaml
# 修改内容如下:
...
image: registry.cn-zhangjiakou.aliyuncs.com/breezey/ingress-nginx:v0.41.2
...
ports:
- name: http
containerPort: 80
protocol: TCP
hostPort: 80
- name: https
containerPort: 443
protocol: TCP
hostPort: 443
- name: webhook
containerPort: 8443
protocol: TCP
...
安装 metrics-server
metrics-server 用于在集群内部向 kube-apiserver 暴露集群指标。
metrics-server 的github地址:https://github.com/kubernetes-sigs/metrics-server
安装:
# 下载安装文件
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.4.1/components.yaml
# 修改 metrics-server-deployment.yaml 文件相关配置:
// 1. 修改镜像地址,默认镜像来自k8s.grc.io
image: registry.cn-zhangjiakou.aliyuncs.com/breezey/metrics-server:v0.4.1
// 2. 修改 metrics-server 启动参数
containers:
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
# 部署
kubectl apply -f components.yaml
# 验证
[root@cka-master metrics-server]# kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
192.168.0.93 169m 8% 1467Mi 39%
cka-node1 138m 6% 899Mi 24%
安装 dashboard
dashboard 的gitbub仓库地址:https://github.com/kubernetes/dashboard
代码仓库当中,有给出安装示例的相关部署文件,我们可以直接获取之后,直接部署即可:
wget https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended.yaml
kubectl apply -f ./recommended.yaml
默认这个部署文件当中,会单独创建一个名为 kubernetes-dashboard 的命名空间,并将 kubernetes-dashboard 部署在该命名空间下。dashboard 的镜像来自 docker hub 官方,所以可不用修改镜像地址,直接从官方获取即可。
但是在默认情况下,dashboard并不对外开放访问端口,这里简化了操作,直接使用 nodePort 的方式将其端口暴露出来,修改 service 部分的定义:
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
ports:
- port: 443
targetPort: 8443
nodePort: 32443
type: NodePort
selector:
k8s-app: kubernetes-dashboard
重新创建 service:
kubectl delete svc kubernetes-dashboard -n kubernetes-dashboard
kubectl apply -f ./recommended.yaml
此时,即可通过浏览器访问web端,端口为 32443。
可以看到出现如上画面,需要输入一个 kubeconfig 文件或者一个 token。事实上在安装 dashboard 时,已为我们默认创建好了一个 serviceaccout,为 "kubernetes-dashboard",并为其生成好了 token,可以通过如下指令获取该 sa 的 token:
kubectl describe secret -n kubernetes-dashboard $(kubectl get secret -n kubernetes-dashboard |grep kubernetes-dashboard-token | awk '{print $1}') |grep token | awk '{print $2}'
通过该 token 登录集群后,发现很多 namespace 包括一些其他的资源都没有足够的权限查看,这是因为默认我们使用的这个账户权限有限。可以通过对该 sa 授予 cluster-admin 权限来解决这个问题:
# 修改 ClusterRoleBinding 资源内容如下:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kubernetes-dashboard
重新创建 clusterrolebinding:
kubectl delete clusterrolebinding kubernetes-dashboard -n kubernetes-dashboard
kubectl apply -f ./recommended.yaml
此时,kubernetes-dashboard相关配置都已完成。
重置集群
在安装过程中,如果遇到问题,可以将集群重置,重新开始部署:
# 重置集群
kubeadm reset
# 停止 kubelet
systemctl stop kebelet
# 删除已经部署的容器
docker ps -qa | xargs docker rm -f
# 清理所有目录
rm -rf /etc/kubernetes /var/lib/kubelet /var/lib/etcd /var/lib/cni/
升级集群
注意事项:
- 使用 kubeadm 升级集群,不支持跨版本升级;
- swap 必须关闭;
- 注意数据备份。虽然 kubeadm upgrade 操作不会触碰工作负载,只会更新 kubernetes 组件,但任何时候,备份都是最佳实践;
- 节点更新完成后,其上的所有容器都会被重启;
操作升级
参考:https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/
升级 master 节点
升级 kubeadm
-
检查可用 kubeadm 版本
# ubuntu apt update apt-cache madison kubeadm # centos yum list --showduplicates kubeadm --disableexcludes=kubernetes
-
更新 kubeadm 软件包
# ubuntu apt update apt upgrade -y kubeadm=1.21.0-00 # centos yum update -y kubeadm-1.21.0-0
-
排干要更新的节点
# 这里以 master 节点为例 kubectl drain 192.168.6.171 --ignore-daemonsets
-
创建升级计划
kubeadm upgrade plan
-
按照提示执行升级
kubeadm upgrade apply v1.21.0
-
将节点重新设置为可调度
kubectl uncordon cka01
如果有多个 master 节点,升级其它 master 节点,直接执行如下操作即可:
kubeadm upgrade node
升级 kubelet 和 kubectl
# ubuntu
apt update
apt upgrade -y kubelet=1.20.4-00 kubectl=1.21.0-00
# centos
yum update -y kubelet-1.20.4-0 kubectl-1.21.0-0
重启kubelet:
systemctl daemon-reload
systemctl restart kubelet
升级 worker 节点
# 在woker节点上升级kubeadm
yum upgrade kubeadm-1.21.0-0 -y
# 在master节点上排干要升级的worker节点
kubectl drain cka02
# 在worker节点上执行升级操作
kubectl upgrade node
# 在worker节点上更新kubelet和kubectl
yum upgrade kubelet-1.21.0-0 kubectl-1.21.0-0
# 重启worker节点上的kubelet
systemctl daemon_reload
systemctl restart kubelet
# 在master节点上取消worker节点的不可调度设置
kubectl uncordon cka02