创建单一控制平面(单master)集群¶
备注
请注意本章节是部署单个Master节点方法,仅适合测试环境。生产环境需要部署高可用集群,请参考 高可用(HA)Kubernetes集群 架构,并直接按照 堆叠型管控/etcd节点高可用集群 或 扩展etcd节点高可用集群 部署步骤直接部署,不用执行本章节操作步骤。
初始化控制平面节点(control-plane node)¶
备注
本段落为背景说明,实际操作命令只需要执行下一段落 初始化命令执行
管控平面节点(control-plane node)是指控制平面组件运行的主机节点,包括 etcd
(集群数据库) 和 API 服务器(kubectl命令行工具通讯的服务器组件)。
规划并选择一个pod network add-on,请注意所选择CNI是否需要的任何参数传递给kubeadm初始化
我这里选择 Flannel 作为CNI,以便采用精简的网络配置和获得较好的性能。根据Flannel CNI的安装要求,是需要想 kubeadm init
传递参数 --pod-network-cidr=10.244.0.0/16
。
备注
必须安装一个 pod network add-on 这样你的pod才能彼此通讯。
在部署任何应用程序之前需要部署网络,并且网络安装之前不能启动 CoreDNS。 kubeadm 只支持 Container Network Interface (CNI) 网络(并且不支持kubenet)。
由于有多种CNI容器网络接口,对比选择很重要,可以参考 Choosing a CNI Network Provider for Kubernetes 和 Kubernetes 网络模型
Kubernetes网络插件(CNI)基准测试的最新结果 一文对比测试了不同CNI的性能、易用性和高级加密特性,推荐如下:
服务器硬件资源有限并且不需要安全功能,推荐 Flannel :最精简和易用的CNI,兼容x86和ARM,自动检测MTU,无需配置。
如果需要加密网络则使用WeaveNet,但是徐亚注意MTU设置,并且性能较差(加密的代价)
其他常用场景推荐 Calico ,是广泛用于Kubernetes部署工具(Kops, Kubespray, Rancher等)的CNI,在资源消耗、性能和安全性有较好平衡。需要注意ConfigMap中设置MTU以适配巨型帧。
备注
kubeadm默认设置了一个较为安全的集群并且使用 RBAC ,所以需要确保你选择的网络框架支持 RBAC 。
使用的 Pod network 必须不覆盖任何主机网络,否则会导致故障。如果你发现网络插件的Pod neetowrk和物理主机网络冲突,则需要选择合适的CIDR,并且采用 kubeadm init
的 --pod-network-cidr
来指定
(可选)从v1.14开始,kubeadm可以自动检测Linux所使用的container runtime。如果使用不同的container runtime,则指定
--cri-socket
参数传递给kubeadm init
(可选)除非指定,否则kubeadm将使用默认网关相关的网络接口来公告master的IP。如果要使用不同的网络接口,则传递
--apiserver-advertise-address=<ip-address>
参数给kubeadm init
(可选)可运行
kubeadm config images pull
使得kubeadm init
验证到gcr.io
仓库的网络连接
初始化命令执行¶
备注
部署Kubernetes的服务需要能够解析所安装服务器的域名,通常这个工作由DNS来提供,建议在局域网内部部署一个DNSmasq服务。在初始阶段,也可以在每个主机上部署 /etc/hosts
提供静态地址解析(例如,我在Host物理主机上的 /etc/hosts
就包含了这个测试集群所有主机的域名解析,可以分发到各个虚拟机上提供基本解析):
pscp.pssh -h kube /etc/hosts /tmp/hosts
pssh -ih kube 'sudo cp /tmp/hosts /etc/hosts'
kubeadm init --pod-network-cidr=10.244.0.0/16
备注
上述 kubeadm init
要求主机已经正确安装了kubelet,否则会出现报错:
[kubelet-check] It seems like the kubelet isn't running or healthy.
[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get http://localhost:10248/healthz: dial tcp 127.0.0.1:10248: connect: connection refused.
Unfortunately, an error has occurred:
timed out waiting for the condition
可通过以下方法检查 kubelet
问题:
systemctl status kubelet
journalctl -xeu kubelet
集群初始化过程¶
在执行了 kubeadm init
指令之后,可以看到执行了一系列操作
下载镜像:
[preflight] Pulling images required for setting up a Kubernetes cluster ... [preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
kubeadm会把kubelet运行环境写入配置文件,然后启动
kubelet
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Activating the kubelet service
创建Kubernetes证书:
[certs] Using certificateDir folder "/etc/kubernetes/pki" [certs] Generating "front-proxy-ca" certificate and key [certs] Generating "front-proxy-client" certificate and key [certs] Generating "etcd/ca" certificate and key [certs] Generating "etcd/server" certificate and key [certs] etcd/server serving cert is signed for DNS names [kubemaster-1 localhost] and IPs [192.168.101.81 127.0.0.1 ::1] [certs] Generating "etcd/peer" certificate and key [certs] etcd/peer serving cert is signed for DNS names [kubemaster-1 localhost] and IPs [192.168.101.81 127.0.0.1 ::1] [certs] Generating "etcd/healthcheck-client" certificate and key [certs] Generating "apiserver-etcd-client" certificate and key [certs] Generating "ca" certificate and key [certs] Generating "apiserver-kubelet-client" certificate and key [certs] Generating "apiserver" certificate and key [certs] apiserver serving cert is signed for DNS names [kubemaster-1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.101.81] [certs] Generating "sa" key and public key
创建Kubernetes配置:
[kubeconfig] Using kubeconfig folder "/etc/kubernetes" [kubeconfig] Writing "admin.conf" kubeconfig file [kubeconfig] Writing "kubelet.conf" kubeconfig file [kubeconfig] Writing "controller-manager.conf" kubeconfig file [kubeconfig] Writing "scheduler.conf" kubeconfig file
启动Kubernetes control plane控制平面(管控三大件):
[control-plane] Using manifest folder "/etc/kubernetes/manifests" [control-plane] Creating static Pod manifest for "kube-apiserver" [control-plane] Creating static Pod manifest for "kube-controller-manager" [control-plane] Creating static Pod manifest for "kube-scheduler"
创建etcd,然后等待control plane的三大组件正常运行(需要依赖etcd服务):
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests" [wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s [apiclient] All control plane components are healthy after 23.506119 seconds
在集群的
kube-system
名字空间存储kubeadm-config
配置映射:[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
同样客户端kubelet的配置映射也需要存储到
kube-system
中:[kubelet] Creating a ConfigMap "kubelet-config-1.15" in namespace kube-system with the configuration for the kubelets in the cluster
跳过了上传certs:
[upload-certs] Skipping phase. Please see --upload-certs
由于control plane已经就绪,现在把第一个master节点
kubemaster-1
标记为master节点:[mark-control-plane] Marking the node kubemaster-1 as control-plane by adding the label "node-role.kubernetes.io/master=''" [mark-control-plane] Marking the node kubemaster-1 as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
配置集群的RBAC角色:
[bootstrap-token] Using token: fm9k1o.vhxmun0sriombljn [bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles [bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials [bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token [bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster [bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
最后执行基础addon:
[addons] Applied essential addon: CoreDNS [addons] Applied essential addon: kube-proxy
最后提示Kubernetes control plane初始化成功:
Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join <master-ip>:<master-port> --token <token> \ --discovery-token-ca-cert-hash sha256:<hash>
备注
请注意,上述 kubeadm init
输出信息是我在测试虚拟机集群的演示输出,隐去了token信息。
警告! 这里输出的token信息和安全紧密相关,请勿将token信息泄漏。否则获得token的任何人都可以在集群添加授权节点,并且可以展示,创建和删除节点。
准备管理员工作环境¶
kubectl
可以用非root用户身份执行,即按照上述 kubeadm init
输出准备管理员工作环境:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
当然,如果你使用 root
用户身份就可以直接访问Kubernetes管理配置文件,就只需要设置环境变量:
export KUBECONFIG=/etc/kubernetes/admin.conf
安装Pod网络插件¶
在Kubernetes集群中,必须安装一个po network add-on 才能使得pods彼此通讯。
备注
必须在所有应用部署之前部署好网络。并且,CoreDNS只有在网络安装以后才能启动。Kubeadm只支持基于容器网络接口(Container Network Interface, CNI)的网络(并且不支持kubenet)。
注意,kubeadm设置了一个默认较为安全的集群并且增强使用RBAC,所以需要确保你选择的网络框架支持RBAC。
并且需要注意的是,Pod network必须不覆盖任何主机网络(host network),否则会导致问题。
备注
上文我参考 Kubernetes网络插件(CNI)基准测试的最新结果 一文选择了 Flannel 网络,所以这里记录的是安装 Flannel 网络插件的实践记录。
安装 Flannel 网络插件之前,请先检查以下设置必须是 1 ,这可以是的IPv4流量被传递给iptables:
cat /proc/sys/net/bridge/bridge-nf-call-iptables
如果值不是1,则执行 sysctl net.bridge.bridge-nf-call-iptables=1
修改,并参考前文设置内核sysctl。
允许报所有主机的网络允许 UDP端口 8285 到 8472 流量
由于我采用CentOS 7操作系统,需要设置 firewalld ,方法如下:
firewall-cmd --zone=public --add-port=8285-8472/udp --permanent
对于我在 Kubernetes部署服务器 使用pssh,则执行命令如下:
pssh -ih kube 'sudo firewall-cmd --zone=public --add-port=8285-8472/udp --permanent'
安装套件:
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
输出提示信息:
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds-amd64 created
daemonset.apps/kube-flannel-ds-arm64 created
daemonset.apps/kube-flannel-ds-arm created
daemonset.apps/kube-flannel-ds-ppc64le created
daemonset.apps/kube-flannel-ds-s390x created
一旦安装网络成功,可以通过检查 CoreDNS pod是否运行来确认:
kubectl get pods --all-namespaces
kube-flannel pod无法启动排查¶
执行安装了 kube-flannel 之后,遇到coredns的pod始终创建中的问题:
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-5c98db65d4-shg69 0/1 ContainerCreating 0 14h
kube-system coredns-5c98db65d4-x5lz4 0/1 ContainerCreating 0 14h
检查节点无法running原因:
kubectl describe pod coredns-5c98db65d4-shg69 -n kube-system
错误原因如下:
Warning FailedCreatePodSandBox 2m41s (x12436 over 8h) kubelet, kubemaster-1 (combined from similar events): Failed create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "f1dbd3dc5c66b15165ec2d183d75a5c50deb4d642bf00b55d161cc081f3d779b" network for pod "coredns-5c98db65d4-shg69": NetworkPlugin cni failed to set up pod "coredns-5c98db65d4-shg69_kube-system" network: open /run/flannel/subnet.env: no such file or directory
检查发现 kube-flannel
不断出现Crash现象:
kube-system kube-flannel-ds-amd64-vv6pv 0/1 CrashLoopBackOff 96 8h
但是 kubectl describe pod kube-flannel-ds-amd64-vv6pv -n kube-system
仅显示:
Warning BackOff 72s (x2169 over 8h) kubelet, kubemaster-1 Back-off restarting failed container
检查pod kube-flannel-ds-amd64-vv6pv日志:
kubectl logs -n kube-system kube-flannel-ds-amd64-vv6pv
输出显示:
E0802 02:17:32.961097 1 main.go:241] Failed to create SubnetManager: error retrieving pod spec for 'kube-system/kube-flannel-ds-amd64-vv6pv': Get https://10.96.0.1:443/api/v1/namespaces/kube-system/pods/kube-flannel-ds-amd64-vv6pv: dial tcp 10.96.0.1:443: i/o timeout
我比较疑惑,为何需要访问 https://10.96.0.1:443
,想到最初初始化阶段 kubeadm init
确实输出有提示创建接口:
single_master_k8s.rst: [certs] apiserver serving cert is signed for DNS names [kubemaster-1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.101.81]
备注
IP 192.168.101.81
是我在部署 Kubernetes 时启用了 OpenConnect VPN 使用的 tun0
接口上IP地址,在关闭VPN之后,我通过 修改Kubernetes Master IP 重新初始化了Kubernetes集群,所以 /etc/kubernetes
配置目录中所有apiserver访问入口IP地址都修订为 192.168.122.11
。
10.96.0.1
地址是kubernetes的内部集群IP地(在Kubernetes集群内部有一个服务CIRD网络,子网网段是 10.96.0.0/12
,分配的第一个地址就是 10.96.0.1
,这个地址是集群的Master服务求第一个IP)。这里访问端口 443
是Kubernetes的 Kubernetes仪表盘 服务的端口。这说明在Kubernetes集群中缺少了Dashboard pod运行。
创建Dashboard:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml
此时发现新创建的Dashboard也是因为Flannel的网络插件无法启动:
kubectl describe pod kubernetes-dashboard-7d75c474bb-x5ntd -n kube-system
显示:
Warning FailedCreatePodSandBox 88s (x4 over 97s) kubelet, kubemaster-1 (combined from similar events): Failed create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "7465e1499f916318d6079f25b5230a36d4c55d2ae2284bfd0cb2cf6d81d4bc18" network for pod "kubernetes-dashboard-7d75c474bb-x5ntd": NetworkPlugin cni failed to set up pod "kubernetes-dashboard-7d75c474bb-x5ntd_kube-system" network: open /run/flannel/subnet.env: no such file or directory
参考 Flannel (NetworkPlugin cni) error: /run/flannel/subnet.env: no such file or directory #70202 和 Kubernetes 报错:”open /run/flannel/subnet.env: no such file or directory” ,看起来Node主机上缺少 /run/flannel/subnet.env
,有人建议可以手工创建该文件,内容如下:
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
但是我参考 kube-dns ContainerCreating /run/flannel/subnet.env no such file #36575 其中提到了和我类似的情况 (修改过Master IP Changing master IP address #338 ) 。我发现是我 修改Kubernetes Master IP 再次 kubeadm init
时遗忘了传递参数 --pod-network-cidr=10.244.0.0/16
导致上述错误。
再次按照正确方法重新初始化,修复后就可以正常启动所有 pod
$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-5c98db65d4-shg69 1/1 Running 51 28h
kube-system coredns-5c98db65d4-x5lz4 1/1 Running 51 28h
kube-system etcd-kubemaster-1 1/1 Running 2 25h
kube-system kube-apiserver-kubemaster-1 1/1 Running 3 27h
kube-system kube-controller-manager-kubemaster-1 1/1 Running 0 4m11s
kube-system kube-flannel-ds-amd64-vv6pv 1/1 Running 160 14h
kube-system kube-proxy-qtbs9 1/1 Running 2 28h
kube-system kube-scheduler-kubemaster-1 1/1 Running 4 28h
kube-system kubernetes-dashboard-7d75c474bb-x5ntd 1/1 Running 51 4h14m
Kubernetes Dashboard¶
如上文所述,在KVM虚拟机 Kubernetes部署服务器 ,其虚拟机网络默认是NAT网络。所以对于远程访问虚拟机端口需要映射才行。对于Kubernetes集群,内部服务网络子网网段是 10.96.0.0/12
,需要使用 kubectl proxy
建立代理才可以在外部访问。
综上所述,对于远程的管理主机桌面,如果要访问虚拟机集群组建的Kubernetes Dashboard,先采用ssh端口映射命令登陆到 worker4
(上面运行KVM虚拟机)物理主机上,再从物理服务器到KVM虚拟机 kubemaster-1
的端口映射,再通过 kubectl proxy
映射到Kubernetes集群内部Dashboard:
ssh -L 8001:127.0.0.1:8001 worker4
ssh -L 8001:127.0.0.1:8001 kubemaster-1
登陆到 kubemaster-1
虚拟机之后,执行以下命令启用kubernetes代理:
kubectl proxy
现在就可以在自己个人电脑上使用浏览器访问: http://127.0.0.1:8001/api/v1/namespaces/kube-system/services/http:kubernetes-dashboard:/proxy/ 查看集群的Dashboard。
备注
这里还有一个页面报错提示待解决:
Error: 'net/http: HTTP/1.x transport connection broken: malformed HTTP response "\x15\x03\x01\x00\x02\x02"'
Trying to reach: 'http://10.244.0.7:8443/'
管控平面节点隔离¶
默认情况下,由于安全原因,集群不会在管控平面节点(control-plane node)上调度pod(schedule pods),即管控平面节点不会运行业务相关pod(调度器不会调度pod到管控服务器)。
如果你希望在管控平面节点上运行schedule pods,例如,作为开发工作使用的单主机Kubernetes集群,或者测试集群为了节约服务器资源,想让管控服务器也能够分担一部分业务pod,可以运行以下命令:
kubectl taint nodes --all node-role.kubernetes.io/master-
此时输出类似:
node "test-01" untainted
taint "node-role.kubernetes.io/master:" not found
taint "node-role.kubernetes.io/master:" not found
上述命令将从集群的所有节点上移除 node-role.kubernetes.io/master
taint,包括control-plane节点。这意味着调度器可以调度pods到任何node上,也包括管控平面节点,例如 kubemaster-1
服务器。
集群加入节点¶
按照之前 kubeadm 部署,我们已经在集群所有节点都部署了 docker kubeadm kubectl kubelet
软件包(对于 kubenode 节点,可以不部署 kubectl
)。
添加节点到Kubernetes集群的命令在 kubeadm init
时输出提示中就有:
kubeadm join --token <token> <master-ip>:<master-port> --discovery-token-ca-cert-hash sha256:<hash>
如果你当时记录了提示信息,可以直接复制使用(执行时候请先登陆到node节点上,并切换到root身份)。
添加节点的标准方法是先创建一个临时token,然后使用这个24小时有效的token结合pki的sha256值添加服务器节点
默认情况下,token有效期是24小时:
kubeadm token create
如果token过期,可以通过运行上述命令再创建一个token,上述命令输出类似:
5didvk.d09sbcov8ph2amjw
此时你再次使用 kubeadm token list
检查可以看到上述命令创建的新token,有效期24小时,并且可以通过以下命令获得control-plane的命令链有关 --discovery-token-ca-cert-hash
内容:
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'
输出内容类似:
8cb2de97839780a412b93877f8507ad6c94f73add17d5d7058e91741c9d5ec78
则结合新生成的token和sha256字符串,就可以添加新的节点到集群:
kubeadm join 192.168.122.11:6443 --token 5didvk.d09sbcov8ph2amjw --discovery-token-ca-cert-hash \
sha256:8cb2de97839780a412b93877f8507ad6c94f73add17d5d7058e91741c9d5ec78
备注
上述方法分两步命令执行,并且使用 openssl
原始命令比较繁琐(虽然更直接和底层),所以 kubeadm
还提供了一个直接打印输出最终 join
命令的方法:
kubeadm token create --print-join-command
此时会提示你正确的添加命令方法(包含token和sha256哈希值)。这样只需要复制粘贴就可以添加节点。
从非control-plane节点管理集群¶
将control-plane节点的 /etc/kubernetes/admin.conf
配置文件分发到安装了 kubectl
工具的主机上,则该主机就具有管理集群的权限:
scp root@<master ip>:/etc/kubernetes/admin.conf .
kubectl --kubeconfig ./admin.conf get nodes
备注
请注意 admin.conf
文件包含了集群的超级用户首选,所以需要谨慎使用。对于普通用户,建议生成一个唯一信任证书,然后再将这个证书加入白名单权限(whitelist privileges):
kubeadm alpha kubeconfig user --client-name <CN>
上述命令输出的kubeconfig文件到标准输出,可以保存为一个文件分发给用户。然后通过 kubectl create (cluster)rolebind
命令添加白名单权限。
本地主机代理API Server¶
如果想在集群之外连接API Server,可以使用 kubectl proxy
scp root@<master ip>:/etc/kubernetes/admin.conf .
kubectl --kubeconfig ./admin.conf proxy
拆除节点(Tear down)¶
要去除节点(消除所有kubeadm的操作),需要先清理节点(drain the node),并确保节点完全清理干净之后才可以关闭节点。
执行以下命令:
kubectl drain <node name> --delete-local-data --force --ignore-daemonsets
kubectl delete node <node name>
然后在节点移除之后,再reset所有kubeadmin的已经安装状态:
kubeadm reset
注意,这个reset国晨并不会清理掉iptables规则或者IPVS表,如果需要reset iptables,需要手工执行:
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
如果需要reset IPVS表,必须运行以下命令:
ipvsadm -C