k8s实践系列的相关配置都在github:GitHub - huangguisu/k8s

一、部署架构规划和环境准备


1、部署方式选择

目前有三种安装方式
第一种是yum安装
使用yum安装,好处是简单,缺点就是要获取最新版需要跟你学yum源,而且所有软件的依赖又不能自己指定,尤其是系统版本比较,使用yum源安装的kubernetes的版本也会受到限制。

第一种是二进制安装
使用二进制文件安装,好处是可以安装任意版本的kubernetes,坏处是配置比较复杂。虽然二进制安装包安装的方式比较费劲,但是二进制便于熟悉各个组件,对学习k8s是很有好处的,因此我们的教程里面全都是二进制安装。

第三种是kubeadm安装
kubeadm是Kubernetes官方提供的用于快速安装Kubernetes集群的工具,将k8s的各个组件都安装在docker之上,以容器的方式运行。伴随Kubernetes每个版本的发布都会同步更新,kubeadm会对集群配置方面的一些实践做调整,通过实验kubeadm可以学习到Kubernetes官方在集群配置上一些新的最佳实践。

有多个部署方案:

可选方案方案来源易难度高可用部署方式
kubeadm官方适中自定义容器
kubespray官方简单生产级容器
kubernetes-the-hard-way社区困难自定义二进制

2、下载版本

Releases · kubernetes/kubernetes · GitHub
从上边的网址中选择相应的版本,从 CHANGELOG页面 下载二进制文件,本文以1.14.1版本为例

https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.14.md#v1142

选择Service Binaries中的kubernetes-server-linux-amd64.tar.gz 

该文件已经包含了 K8S所需要的全部组件,无需单独下载Client等组件。 

解压后,在kubernetes/server/bin 路径下包含一些必须的组件:

设置环境变量方便直接使用kubernetes的命令:

export K8S_BIN=/mnt/app/kubernetes/server/bin/
export PATH="$K8S_BIN:$PATH"

3、docker环境:

cri前世今生

在 Kubernetes1.5 之前 Docker 作为第一个容器运行时,Kubelet 通过内嵌 dockershim操作容器API,在 v1.6.0 后, Kubernetes开始默认启用 CRI(容器运行时接口)。

为了在Kubernetes等平台上更好的兼容,Docker团队也推出了CRI(Container Runtime Interface,容器运行时接口)。

简单讲CRI就是容器运行时接口(Container Runtime Interface,CRI)。

1、Container runtime

      容器运行时(Container Runtime)是负责管理和执行容器的组件。它负责将容器镜像转化为在主机上运行的实际容器进程,提供镜像管理、容器的生命周期管理、资源隔离、文件系统、网络配置等功能。

2、dockershim:

      在 Kubernetes v1.24 及更早版本中,我们使用docker作为容器引擎在k8s上使用时,依赖一个dockershim的内置k8s组件;

3、Containerd和cri-dockerd:

     在V1.24起的版本的 kubelet 就彻底移除了dockershim,改为默认使用Containerd了,当然也使用 cri-dockerd适配器来将Docker Engine与 Kubernetes 集成。现在Kubernetes官方文档介绍的都是containerd和cri-o作为容器运行时。

4、docker 和 containerd区别

1、docker 由 docker-client ,dockerd,containerd,docker-shim,runc组成,所以containerd是docker的基础组件之一

2、从k8s的角度看,可以选择 containerd 或 docker 作为运行时组件:其中 containerd 调用链更短,组件更少,更稳定,占用节点资源更少。所以k8s后来的版本开始默认使用 containerd 。

3、containerd 相比于docker , 多了 namespace 概念,每个 image 和 container 都会在各自的namespace下可见。

Containerd

containerd是容器虚拟化技术,从docker中剥离出来,形成开放容器接口(OCI)标准的一部分。

在 2016 年 12 月 14 日,Docker 公司宣布将 containerd 从 Docker 中分离,由开源社区独立发展和运营。Containerd 完全可以单独运行并管理容器,而 Containerd 的主要职责是镜像管理和容器执行。同时,Containerd 提供了 containerd-shim 接口封装层,向下继续对接 runC 项目,使得容器引擎 Docker Daemon 可以独立升级。

Containerd 可以在宿主机中管理完整的容器生命周期:容器镜像的传输和存储、容器的执行和管

理、存储和网络等。总结一下,它主要负责干以下事情

• 管理容器的生命周期(从创建容器到销毁容器)
• 拉取/推送容器镜像
• 存储管理(管理镜像及容器数据的存储)
• 调用 runC 运行容器(与 runC 等容器运行时交互)
• 管理容器网络接口及网络

containerd 是一个来自 Docker 的高级容器运行时,并实现了 CRI 规范。所以 Docker 自己在内部使用 containerd,当你安装 Docker 时也会安装 containerd。

containerd 通过其 CRI 插件实现了 Kubernetes 容器运行时接口(CRI),它可以管理容器的整个生命周期,包括从镜像的传输、存储到容器的执行、监控再到网络。

cri-dockerd

非要用docker engine,就需要安装第三方工具cri-dockerd作为CRI中间层。

cri-dockerd就是以docker作为容器引擎而提供的容器运行时接口,即我们想要用docker作为k8s的容器运行引擎,我们需要先部署好cri-dockerd。  用cri-dockerd来与kubelet交互,然后再由cri-dockerd和docker api交互,使我们在k8s能够正常使用docker作为容器引擎;

docker 作为 k8s 容器运行时,调用关系为:kubelet --> dockershim (在 kubelet 进程中) --> dockerd --> containerd

containerd 作为 k8s 容器运行时,调用关系为:kubelet --> cri plugin(在 containerd 进程中) --> containerd
 

5、ctr 和 crictl 命令

ctr 是 containerd 的一个客户端工具。安装了 containerd 服务后就可以操作 ctr 命令。ctr 界面 与 Docker CLI 不兼容,看起来不那么用户友好,它的主要受众是测试守护进程的容器开发人员。ctr 也非常适合学习的能力低级别的使用人员,因为 ctr + containerd 是更接近实际的容器比 docker + dockerd。

crictl 是 CRI 兼容的容器运行时命令行接口,可以使用它来检查和调试 k8s 节点上的容器运行时和应用程序。安装了 k8s 后,命令行才会有 crictl 命令。

3、节点规划和环境准备

k8s至少需要一个master和一个node才能组成一个可用集群。我们将搭建一个master节点和2个node节点。

我们有三台服务器,规划如下,最好然后修改hosts
192.168.10.21 k8s-master
192.168.10.22 k8s-node1
192.168.10.23 k8s-node2

1、修改 /etc/hosts 文件,添加域名解析,方便后续使用

cat <<EOF >>/etc/hosts
192.168.10.21  k8s-master
192.168.10.22  k8s-node1
192.168.10.23  k8s-node1
192.168.10.21 node1.etcd.k8-cluster.com
192.168.10.22 node2.etcd.k8-cluster.com
192.168.10.23 node3.etcd.k8-cluster.com
EOF

2.关闭防火墙、selinux和swap。

systemctl stop firewalld
systemctl disable firewalld
setenforce 0
sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
#关闭swap
swapoff -a
#要永久禁掉swap分区,/etc/fstab文件注释掉swap那一行
sed -i 's/.*swap.*/#&/' /etc/fstab

5、准备证书:

具体说明:k8s实践(8)--ssl安全认证配置

相关证书放在:/mnt/app/kubernetes/ssl/

mkdir /mnt/app/kubernetes/ssl/

cat <<EOF >> /mnt/app/kubernetes/ssl/masterssl.cnf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster.local
DNS.5 = master.k8s.tulingapi.com
DNS.6 = k8s-master
IP.4 = 127.0.0.1
IP.1 = 192.168.10.21
IP.2 = 192.168.0.1
IP.3 = 10.0.0.1
IP.4 = 172.17.0.1
IP.4 = 172.16.0.1
IP.4 = 172.18.0.1
EOF

#根证书
openssl genrsa -out ca-private.pem 2048
openssl req -x509 -new -nodes -key ca-private.pem -days 3650 -out ca-public.pem -subj "/CN=kube-ca"

#apiserver证书
openssl genrsa -out apiserver-private.pem 2048
openssl req -new -key apiserver-private.pem  -out apiserver.csr -subj "/CN=k8s-master" -config masterssl.cnf
openssl x509 -req -in apiserver.csr  -CA ca-public.pem  -CAkey ca-private.pem -CAcreateserial -out apiserver-public.pem  -days 3650 -extensions v3_req -extfile masterssl.cnf

#设置kube-controller-manager的客户端证书
openssl genrsa -out manager-client-private.pem  2048
openssl req -new -key  manager-client-private.pem  -subj "/CN=k8s-controller"  -out  manager-client.csr
openssl x509 -req  -in  manager-client.csr  -CA ca-public.pem  -CAkey ca-private.pem  -CAcreateserial -out  manager-client-public.pem  -days 5000

#node节点客户端双向证书生成
openssl genrsa -out kubelet-private.pem 2048
openssl req -new -key kubelet-private.pem   -out kubelet.csr -subj "/CN=kubelet-key"
openssl x509 -req -in kubelet.csr  -CA ca-public.pem  -CAkey ca-private.pem  -CAcreateserial -out kubelet-public.pem -days 3650

 二、高可用Kubernetes Master节点安装


kubernetes master 节点包含的组件。

  • kube-apiserver
  • kube-scheduler
  • kube-controller-manager

kube-schedulerkube-controller-manager 和 kube-apiserver 三者的功能紧密相关,因此建议这三个组件部署在同一台机器上。同时只能有一个 kube-schedulerkube-controller-manager 进程处于工作状态,如果运行多个,则需要通过选举产生一个 leader;

Kubernetes 的服务都是通过直接运行二进制文件加启动参数完成,为了便于管理,常见的做法:

1、可以使用nohup运行二进制文件来实现后台运行。

2、将Kubernetes服务进程配置成系统服。

3、使用supervisor来管理,我们这里都使用这个方式。

2.1、Kuber-apiserver


2.1.1 Kubernetes API Server概述

Kubernetes API Server的核心功能是提供了Kubernetes各类资源对象(如Pod、RC、Service等)的增、删、改、查及Watch等HTTP Rest接口,成为集群内各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心。除此之外,他还有以下的特性:

  1. 是集群管理的API入口;
  2. 是资源配额控制的入口;
  3. 提供完备的集群安全机制;

2.1.2 启动Kube-apiserver 进程服务

Kubernetes API Server通过一个名为Kube-apiserver的进程提供服务,该进程运行在Master节点上。在默认情况下,kube-apiserver进程在本机的8080端口(对应参数--insecure-port)提供REST服务。

简单使用nohup启动,由于我本地端口8080被占用,修改kube-apiserver端口为9090

nohup /mnt/app/kubernetes/server/bin/kube-apiserver --insecure-bind-address=0.0.0.0 --insecure-port=9090 --etcd-servers=http://node1.etcd.k8-cluster.com:2379 --service-cluster-ip-range=192.168.10.0/16   --admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota --logtostderr=false --log-dir=/mnt/logs/k8s-kube-apiserver/ --v=2 &

kubectl命令默认连接的server 是http://localhost:8080,由于改了端口号需指定server,可以通过alias 来修改:

alias kubectl=" kubectl -s http://localhost:9090"
echo  alias kubectl=" kubectl -s http://localhost:9090"  >>  ~/.bashrc

我们可以同时启动HTTPS安全端口(--secure-port=6443)来启动安全机制,加强REST API访问的安全性。

如果集群要可以发布pod,必须的使用ssl认证启动:

nohup /mnt/app/kubernetes/server/bin/kube-apiserver --insecure-bind-address=0.0.0.0 --insecure-port=9090 --etcd-servers=http://node1.etcd.k8-cluster.com:2379 --service-cluster-ip-range=192.168.10.0/16  --service-account-key-file=/mnt/app/kubernetes/ssl/apiserver-private.pem --client-ca-file=/mnt/app/kubernetes/ssl/ca-public.pem --tls-private-key-file=/mnt/app/kubernetes/ssl/apiserver-private.pem  --tls-cert-file=/mnt/app/kubernetes/ssl/apiserver-public.pem --admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota --logtostderr=false --log-dir=/mnt/logs/k8s-kube-apiserver/ --v=2 &

使用supervisor管理: https://github.com/huangguisu/k8s/tree/master/supervisor/k8s-kube-apiserver.conf

1.26.1版的启动参数稍微有变化:

/mnt/app/kubernetes/server/bin/kube-apiserver  \
--allow-privileged=true \
--anonymous-auth=true  \
--authorization-mode=Node,RBAC  \
--etcd-servers=http://node1.etcd.k8-cluster.com:2379 \
--service-cluster-ip-range=192.168.10.0/16  \
--service-account-key-file=/mnt/app/kubernetes/ssl/apiserver-private.pem  \
--service-account-signing-key-file=/mnt/app/kubernetes/ssl/apiserver-private.pem  \
--service-account-issuer=api --client-ca-file=/mnt/app/kubernetes/ssl/ca-public.pem \
--tls-private-key-file=/mnt/app/kubernetes/ssl/apiserver-private.pem  \
--tls-cert-file=/mnt/app/kubernetes/ssl/apiserver-public.pem  \
--token-auth-file=/mnt/app/kubernetes/ssl/token.csv \
--enable-bootstrap-token-auth \
--admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
--audit-log-path=/mnt/logs/k8s-kube-apiserver  \
--v=2

验证apiserver节点健康状态:

 curl  --cacert /mnt/app/kubernetes/ssl/ca-public.pem --key /mnt/app/kubernetes/ssl/apiserver-private.pem --cert /mnt/app/kubernetes/ssl/apiserver-public.pem  https://k8s-master:6443/healthz

2.1.3 配置参数说明:

通用参数:

–admission-control=”AlwaysAdmit”:集群中资源的Admission Controller的插件的有序列表,分别使用逗号分隔,AlwaysAdmit,

–admission-control-config-file=””: Admission Controller配置文件。
–advertise-address=<nil>: 广播API Server给所有集群成员的IP地址。其它集群都可以访问该IP地址,如果为空,会使用—-allow-privileged[=false]: true,表示允许特权容器。
–authorization-mode=”AlwaysAllow”: 安全端口授权插件的有序列表,分别以逗号分隔,AlwaysAllow,AlwaysDeny,ABAC–authorization-policy-file=””: 授权策略的CSV文件,使用于–authorization-mode=ABAC模式的配置。
–basic-auth-file=””: 如果配置该选项,该文件会通过HTTP基本认证允许API Server安全端口的请求。
–bind-address=0.0.0.0: 服务–read-only-port和–secure-port端口的IP地址。相关接口必须是其它集群通过CLI/web客–cert-dir=”/var/run/kubernetes”: TLS证书的目录(默认/var/run/kubernetes)。如果配置–tls-cert-file和–tls—client-ca-file=””: 如果设置,任何提交客户端证书的请求都会验证与相关客户端证书的CommonName的身份。该客户端证书是–cloud-config=””: 云提供商配置文件的路径,空表示没有该配置文件。
–cloud-provider=””: 云服务的提供商,空表示没有该提供商。
–cluster-name=”kubernetes”: 集群实例的前缀。
–cors-allowed-origins=[]: CORS的允许起源(allowed origins, 翻译待考虑)的列表,用逗号分隔。一个允许起源可以是

ETCD参数:

–etcd-config=””: ETCD客户端的配置文件,与-etcd-servers配置项互斥。
–etcd-prefix=”/registry”: ETCD中所有资源路径的前缀。
–etcd-servers=[]: ETCD服务器(http://ip:port)列表,以逗号分隔。与-etcd-config配置项互斥。
–etcd-servers-overrides=[]: 每个资源ETCD服务器覆盖文件,以逗号分隔。独立覆盖格式,group/resource#servers,服

其他参数;

–event-ttl=1h0m0s: 保留事件的时间值,默认1小时。
–experimental-keystone-url=””: 如果Passed,激活Keystone认证插件。
–external-hostname=””: 为Master生成外部URLs使用的主机名。
–google-json-key=””: 用户Google Cloud Platform Service Account JSON Key认证。
–insecure-bind-address=127.0.0.1:非安全端口(所有接口都设置为0.0.0.0)的服务IP地址。默认是本地地址。
–insecure-port=8080: 不安全且没有认证的进程访问端口,默认8080。假设防火墙规则设置该端口从集群外部禁止访问,并且在–kubelet-certificate-authority=””: 证书路径。证书授权文件。
–kubelet-client-certificate=””: TLS客户端证书文件路径。
–kubelet-client-key=””: TLS客户端秘钥文件路径。
–kubelet-https[=true]: 使用https建立Kubelet连接。
–kubelet-port=10250: Kubelet端口。
–kubelet-timeout=5s: Kubelet操作Timeout值。
–log-flush-frequency=5s: 日志缓冲秒数的最大值。
–long-running-request-regexp=”(/|^)((watch|proxy)(/|$)|(logs?|portforward|exec|attach)/?$)”: 匹配长–master-service-namespace=”default”: Namespace,该Namespace的Kubernetes主服务应该注入Pod。
–max-connection-bytes-per-sec=0: 如果非零,表示每个用户连接的最大值,字节数/秒,当前只适用于长时间运行的请求。
–max-requests-inflight=400: 给定时间内运行的请求的最大值。如果超过最大值,该请求就会被拒绝。零表示没有限制。
–min-request-timeout=1800: 这是个可选字段,表示一个请求处理的最短时间,单位是秒。在超时之前,这个请求必须是激活的–oidc-ca-file=””: 如果设置该选项,Oidc-ca-file中的相关机构会验证OpenID服务的证书。否则,会使用主机的根证书。
–oidc-client-id=””: 如果设置了oidc-issuer-url字段,该字段,OpenID连接客户端的客户ID也必须设置。
–oidc-issuer-url=””: OpenID发行的URL,只接受HTTPS协议。如果设置该字段,将被用来验证OIDC JSON Web Token(JWT)–oidc-username-claim=”sub”: 。默认值之外的那些值,可能是不唯一的,可变的。这个标志还在尝试中,详情请参考Authentication –profiling[=true]: 通过web接口进行分析 host:port/debug/pprof/。
–runtime-config=: key=value键值对集,描述运行时配置,也会回传输到apiserver。apis/键值用于打开–secure-port=6443: 用于HTTPS的认证和授权。0表示不支持HTTPS服务。
–service-account-key-file=””: 该文件包含RPM-encoded x509 RSA的私钥和公钥,用于验证ServiceAccount的Token。–service-account-lookup[=false]: true, 表示验证Service Account的Token做为Authentication一部分在ETCD中的

–service-cluster-ip-range=: CIDR标记的IP范围,从中分配IP给服务集群。该范围不能与分配给Pod节点的任何IP范围–service-node-port-range=: NodePort可见性服务的端口范围,包含范围的两端。如’30000-32767’,包含30000和32767–ssh-keyfile=””: 如果非空,使用安全SSH代理到该节点,用该秘钥文件。
–ssh-user=””: 如果非空,使用安全SSH代理到该节点,用该用户名。
–storage-versions=”extensions/v1beta1,v1″: 存储资源的版本。不同的组存储在不同的版本里面,指定格式”group1/version1,–tls-cert-file=””: 该文件包含HTTPS的x509证书。(CA证书,如果存在,连接在服务器证书之后)。如果支持HTTPS服务,且没–tls-private-key-file=””: 该文件包含x509私钥匹配项–tls-cert-file.
–token-auth-file=””: 该文件使用Token验证保护API Server的安全端口。
–watch-cache[=true]: 可以在API Server查看缓存。

2.1.4 Kubernetes API Server使用

通常我们可以通过命令行工具kubectl来与kubernetes API Server交互,他们之间的接口是REST调用。也可以使用curl命令行工具进行快速测试验证。

查看Kubernetes API的版本信息

curl k8s-master:9090/api

查看Kubernetes API Server目前支持的资源对象的种类

curl k8s-master:9090/api/v1

查看不同资源列表信息:

curl k8s-master:9090/api/v1/pods

curl k8s-master:9090/api/v1/services

curl k8s-master:9090/api/v1/replicationcontrollers

2.1.5 Kubernetes-Proxy API管理Node接口

这类接口的作用是代理REST请求,即kubernetes API Server把收到的REST请求转发到某个Node上的kubelet守护进程的REST端口上,由该kubelet进程负责响应。

Kubernetes Proxy API中管理Node的相关接口,该接口的REST路径为/api/v1/proxy/nodes/{name},其中name为节点名称或IP地址,包括以下几个具体的接口:

  • /api/v1/proxy/nodes/{name}/pods  #列出指定节点内的所有Pod的信息
  • /api/v1/proxy/nodes/{name}/stats       #列出指定节点内物理资源的统计信息
  • /api/v1/proxy/nodes/{name}/spec       #列出指定节点的概要信息

例:节点名为k8s-node-1,下面命令获取该节点上所有运行中的pod:

  • curl localhost:8080/api/v1/proxy/nodes/k8s-node-1/pods

需要说明的是,此处获取pod信息数据来自Node而非etcd数据库,所以两者可能在某些时间点会有偏差。此外如果kubelet进程在启东时包含--enable-debugging-handles=true,namekubernetes Proxy API 还会增加下面的接口:

  • /api/v1/proxy/nodes/{name}/run           #在节点上运行某个容器
  • /api/v1/proxy/nodes/{name}/exec        #在节点的某个容器中运行某条命令
  • /api/v1/proxy/nodes/{name}/attach      #在节点上attach某个容器
  • /api/v1/proxy/nodes/{name}/portForward      #实现节点上的Pod端口转发
  • /api/v1/proxy/nodes/{name}/logs          #列出节点的各类日志信息,例如tallylog、lastlog、wtmp、ppp/、rhsm、audit、tuned、和anaconda等
  • /api/v1/proxy/nodes/{name}/metrics      #列出和该节点相关的Metrics信息
  • /api/v1/proxy/nodes/{name}/runningpods        #列出节点内运行中的Pod信息
  • /api/v1/proxy/nodes/{name}/debug/pprof        #列出节点内当前Web服务的状态,包括CPU和内存的使用情况

Kubernetes Proxy API里关于Pod的相关接口,通过这些接口,我们可以访问pod里某个容器提供的服务(如Tomcat在8080提供的服务)

  • /api/v1/proxy/namespaces/{namespace}/pods/{name}/{patch:*}         #访问pod的某个服务接口
  • /api/v1/proxy/namespaces/{namespace}/pods/{name}                        #访问pod
  • /api/v1/proxy/namespaces/{namespace}/pods/{name}/proxy/{patch:*}         #访问pod的某个服务接口
  • /api/v1/proxy/namespaces/{namespace}/pods/{name}/proxy              #访问pod

在上面的4个接口中,后面两个接口的功能和前面两个完全一样,只是写法不同。

2.1.6 kube-proxy 代理接口

如果我们只想对外暴露部分REST服务,则可以在Master或其他任何节点上通过运行kube-proxy进程启动一个内部代理来实现。

运行下面的命令,我们在8001端口启动代理,并且拒绝客户端访问RC的API:

  • kubectl proxy --reject-paths=“^/api/v1/replicationcontrollers” --port=8001 --v=2

通过下面的命令进行验证:

  • curl localhost:8001/api/v1/replicationcontrollers

kubectl proxy具有很多特性,最实用的一个特性是提供简单有效的安全机制,比如采用白名单来限制非法客户端访问时,只要采用下面的参数即可:

  • --accept-host=“^localhost$,^127\\.0\\.0\\.1$,^\\[::1\\]$”

最后一种方式是通过b编程的方式调用Kubernetes API Server。具体使用场景又细分为以下两种:

第一种使用场景:运行在Pod中的用户进程调用Kubernetes API,通常用来实现分布式集群搭建的目标。Pod中的进程如何知道API Server的访问地址呢,因为Kubernetes API Server本身也是一个Service,其名字就是Kubernetes,他的clusterIP地址是ClusterIP地址池中的第一个IP,他所服务的端口是HTTPS端口443,通过kubectl get svc可以确认这一点。

第二种使用场景:开发基于Kubernetes的管理平台。比如调用Kubernetes API 来完成Pod、Service、RC等资源对象的图形化创建和管理界面,此时可以使用kubernetes及各开源社区为开发人员提供的各种语言版本的Client Library。

2.1.7 集群功能模块之间的通信

集群内各个功能模块通过API Server将信息存入etcd,当需要获取和操作这些数据时,则通过API Server提供的REST接口(用GET/LIST/WTCH方法)来实现,从而实现各模之间的信息交互;

kubelet进程与API Server的交互:每个node上的kubelet每隔一个时间周期,就会调用一次API Server的REST接口报告自身状态,API Server接收到这些信息后,将各节点信息更新到etcd中。

       此外kubelet也通过API Server的watch接口监听pod信息,如果监听到新的Pod副本被调度绑定到本节点,则执行Pod对应的容器的创建和启动逻辑;如果监听到Pod对象被删除,则删除本节点上的相应的Pod容器;如果监听到修改Pod信息,则kubelet监听到变化后,会相应的修改本节点的Pod容器。

kube-controller-manager进程与API Server的交互:kube-controller-manager中的Node Controller模块通过API Server提供的Watch接口,实时监控Node的信息并做相应的处理;

kube-scheduler与API Server交互:当scheduler通过API Server的Watch接口监听到新建Pod副本的信息后,他会检索所有符合该Pod要求的Node列表,开始执行Pod调度逻辑,调度成功后将Pod绑定到目标节点上。为了缓解集群各模块对API Server的压力,各功能模块都采用缓存的机制来缓存数据。各功能模块定时从API Server获取指定资源对象的信息(通过LIST或watch),然后将这些信息保存到本地缓存,功能模块在某些情况下不直接访问API Server,而是通过访问缓存数据来间接访问API Server

2.1.8 、 v1.26.1版构建系统服务kube-apiserver

cat <<EOF > /usr/lib/systemd/system/kube-apiserver.service
[Unit]
Description=kube-apiserver
After=network.target
After=network-online.target
Wants=network-online.target
 
[Service]
Type=notify
WorkingDirectory=/mnt/app/kubernetes/server/
# User=etcd
ExecStart=/mnt/app/kubernetes/server/bin/kube-apiserver --etcd-servers=http://node1.etcd.k8-cluster.com:2379 --service-cluster-ip-range=192.168.10.0/16  --service-account-key-file=/mnt/app/kubernetes/ssl/apiserver-private.pem  --service-account-signing-key-file=/mnt/app/kubernetes/ssl/apiserver-private.pem  --service-account-issuer=api --client-ca-file=/mnt/app/kubernetes/ssl/ca-public.pem --tls-private-key-file=/mnt/app/kubernetes/ssl/apiserver-private.pem  --tls-cert-file=/mnt/app/kubernetes/ssl/apiserver-public.pem --admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota --audit-log-path=/mnt/logs/k8s-kube-apiserver --v=2
Restart=on-failure
LimitNOFILE=65536

EOF

2.2、Kuber-controller-manager


Kube-controller-manager处理集群中常规后台任务, 如果APIServer做的是前台的工作的话,那么controller manager就是负责后台的。每一个资源都对应一个控制器。而control manager就是负责管理这些控制器的,负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;比如我们通过APIServer创建了一个Pod,当这个Pod创建成功后,APIServer的任务就算完成了。

启动Kube-controller-manager,   依赖kube-apiserver(--master=http://k8s-master:9090 ):

nohup kube-controller-manager --master=http://k8s-master:9090  --logtostderr=false --log-dir=/mnt/logs/k8s-kube-controller-manager/  --v=2 &

Kube-controller-manager主要的配置参数:

--master: 指定apiserver的URL地址

--logtostderr=false: 设置为false表示将日志写入文件,不写入则为stderr

--log-dir=/mnt/logs/k8s/  :日志目录

--v=2 :日志等级

上面这个启动命令比较简单,由于发布pod等相关资源需要安全验证,因此需要使用https。

关于Kubernetes安全:  https://guisu.blog.csdn.net/article/details/95067232   和ssl证书:https://guisu.blog.csdn.net/article/details/95459273

证书

kube-controller-manager 在如下两种情况下使用该证书:

  • kube-apiserver 的安全端口通信;

1.20版本以前 kube-controller-manager 设置为只调用当前机器的 apiserver,走127.0.0.1网卡,因此不配制SSL证书

1.20版本+ --insecure-port has been deprecated, This flag has no effect now and will be removed in v1.24

--insecure-port默认禁用已弃用的。从 v1.20 开始,该端口将被永久禁用,并在以后的版本中删除该标志。

相比于1.15版本,无法让master其他必要组件使用http方式进行访问,只能通过https方式访问

如果是使用https证书:

--kubeconfig :kubeconfig配置文件路径,在配置文件中包括Master的地址信息及必要认证信息:

apiVersion: v1
kind: Config
users:
- name: controllermanager
  user:
    client-certificate:/mnt/app/kubernetes/ssl/manager-client-public.pem
    client-key:/mnt/app/kubernetes/ssl/manager-client-private.pem
clusters:
- name: local
  cluster:
    certificate-authority: /mnt/app/kubernetes/ssl/ca-public.pem
contexts:
- context:
    cluster: local
    user: controllermanager
  name: my-context
current-context: my-context

如果是token证书(kube-apiserver进程启动时,设置了–token-auth-file=SOMEFILE可启动token认证)

制作启动引导令牌

##设置环境变量
KUBE_APISERVER="https://k8s-master:6443" # apiserver IP:PORT
TOKEN="qwe50689783b700f35d0fdabab6a1233" # 与token.csv里保持一致
 
# 生成 kubelet bootstrap kubeconfig 配置文件
kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/kubernetes/ssl/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER}  \
  --kubeconfig=bootstrap.kubeconfig
kubectl config set-credentials "kubelet-bootstrap" \
  --token=${TOKEN} \
  --kubeconfig=bootstrap.kubeconfig
kubectl config set-context default \
  --cluster=kubernetes \
  --user="kubelet-bootstrap" \
  --kubeconfig=bootstrap.kubeconfig
kubectl config use-context default --kubeconfig=bootstrap.kubeconfig

mv bootstrap.kubeconfig  /mnt/app/kubernetes/ssl/

参数--kubeconfig=/mnt/app/kubernetes/ssl/bootstrap.kubeconfig

使用supervisor管理:

https://github.com/huangguisu/k8s/tree/master/supervisor/k8s-kube-controller-manager.conf

supervisor启动是最终集群版的结果:

[program:k8s-kube-controller-manager]
[program:k8s-kube-controller-manager]
command =/mnt/app/kubernetes/server/bin/kube-controller-manager --master=https://k8s-master:6443 --cluster-cidr=10.0.0.0/16 --service-account-private-key-file=/mnt/app/kubernetes/ssl/apiserver-private.pem  --root-ca-file=/mnt/app/kubernetes/ssl/ca-public.pem  --cluster-signing-cert-file=/mnt/app/kubernetes/ssl/ca-public.pem  --cluster-signing-key-file=/mnt/app/kubernetes/ssl/ca-private.pem --kubeconfig=/mnt/app/kubernetes/ssl/kubeconfig.yaml   --logtostderr=false --log-dir=/mnt/logs/k8s-kube-controller-manager/  --v=2
process_name=%(program_name)s
numprocs=1
directory=/mnt/app/kubernetes/
user=root
stdout_logfile=/mnt/logs/%(program_name)s/server.log
stderr_logfile=/mnt/logs/%(program_name)s/error.log
serverurl=AUTO = /mnt/logs/k8s-kube-apiserver/error.log

2.3、Kuber-scheduler


schedule按照预定的调度策略将Pod调度到相应的Node节点上.  如果把scheduler看成一个黑匣子,那么它的输入是pod和由多个Node组成的列表,输出是Pod和一个Node的绑定。 kubernetes目前提供了调度算法,同样也保留了接口。用户根据自己的需求定义自己的调度算法。

启动 kube-scheduler,   依赖kube-apiserver(--master=http://k8s-master:9090 ):

nohup kube-scheduler --master=http://k8s-master:9090  --logtostderr=false --log-dir=/mnt/logs/k8s-kube-scheduler/  --v=2 &

验证Master是否安装成功

$ kubectl get componentstatuses

使用supervisor管理:

[program:k8s-kube-scheduler]
command =/mnt/app/kubernetes/server/bin/kube-scheduler --master=http://k8s-master:9090 --address=0.0.0.0  --logtostderr=false --log-dir=/mnt/logs/k8s-kube-scheduler/  --v=2
process_name=%(program_name)s
numprocs=1
directory=/mnt/app/kubernetes/
umask=022
priority=999
autostart=true
autorestart=true
startsecs=10
startretries=3
exitcodes=0,2
stopsignal=QUIT
stopwaitsecs=10
user=root
redirect_stderr=true
stdout_logfile=/mnt/logs/%(program_name)s/server.log
stdout_logfile_maxbytes=50000MB
stdout_logfile_backups=10
stdout_capture_maxbytes=0MB
stdout_events_enabled=false
stderr_logfile=/mnt/logs/%(program_name)s/error.log
stderr_logfile_maxbytes=1MB
stderr_logfile_backups=10
stderr_capture_maxbytes=1MB
stderr_events_enabled=false
;environment=JAVA_HOME="/usr/java"
serverurl=AUTO = /mnt/logs/k8s-kube-apiserver/error.log

2.4、验证master集群状态

在三个节点中,任一主机执行以下命令,都应返回集群状态信息

kubectl get cs
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok                  
scheduler            Healthy   ok                  
etcd-0               Healthy   {"health":"true"}   
etcd-1               Healthy   {"health":"true"}   
etcd-2               Healthy   {"health":"true"}  

三、Node节点


每个Node节点主要由四个模板组成:kublet, kube-proxy,docker,flanneld

kube-scheduler 也依赖kube-apiserver

在工作node节点上,先安装好docker,并且启动docker deamon.

将kubernetes-server-linux-amd64.tar.gz复制所有的node节点服务器上解压到:/mnt/app/kubernetes/

设置环境变量:

export K8S_BIN=/mnt/app/kubernetes/server/bin/
export PATH="$K8S_BIN:$PATH"

1 kubeket服务

kublet是Master在每个Node节点上面的agent,是Node节点上面最重要的模块,它负责维护和管理该Node上的所有容器,但是如果容器不是通过kubernetes创建的,它并不会管理。本质上,它负责使Pod的运行状态与期望的状态一致。

  kublet 运行在每个 worker 节点上,接收 kube-apiserver 发送的请求,管理 Pod 容器,执行交互式命令,如exec、run、logs 等;
kublet 启动时自动向 kube-apiserver 注册节点信息,内置的 cadvisor 统计和监控节点的资源使用情况;
为确保安全,本文档只开启接收 https 请求的安全端口,对请求进行认证和授权,拒绝未授权的访问(如apiserver、heapster)。

而官方推荐我们使用--config指定配置文件,并在配置文件中指定原来这些flag所配置的内容。具体内容可以查看这里Set Kubelet parameters via a config file。这也是Kubernetes为了支持动态Kubelet配置(Dynamic Kubelet Configuration)才这么做的,参考Reconfigure a Node’s Kubelet in a Live Cluster

kubelet的配置文件必须是json或yaml格式,具体可查看这里

Kubernetes 1.8开始要求关闭系统的Swap,如果不关闭,默认配置下kubelet将无法启动。 关闭系统的Swap方法如下:

swapoff -a

修改 /etc/fstab 文件,注释掉 SWAP 的自动挂载,使用free -m确认swap已经关闭。 swappiness参数调整,修改/etc/sysctl.d/k8s.conf添加下面一行:

vm.swappiness=0

在从Kubernetes 1.10开始Dynamic Kubelet Configuration特性进入beta阶段,kubelet的大多数命令行参数都改为推荐在--config指定位置的配置文件中进行配置,包括---cluster-dns--cluster-domain两个参数.

因为1.9.0在kubelet里不再使用KUBELET_API_SERVER来跟API通信,而是通过别一个yaml的配置来实现。

nohup kubelet   --kubeconfig=/mnt/app/kubernetes/conf/kubelet.yaml --hostname-override=192.68.10.37 --logtostderr=false --log-dir=/mnt/logs/k8s-kube-scheduler/  --v=2 &

使用这个:

nohup kubelet   --kubeconfig=/mnt/app/kubernetes/conf/kubelet.yaml --hostname-override=192.68.10.37 --logtostderr=false --log-dir=/mnt/logs/k8s-kubelet/  --v=2  --cgroup-driver=systemd   --runtime-cgroups=/systemd/system.slice --kubelet-cgroups=/systemd/system.slice &

/mnt/app/kubernetes/conf/kubelet.yaml:

chown -R kubelet.kubelet /mnt/app/kubernetes/conf/kubelet.yaml

apiVersion: v1
kind: Config
users:
- name: kubelet

cgroupDriver: systemd
clusters:
- name: kubernetes
  cluster:
    server: http://k8s-master:8080
contexts:
- context:
    cluster: kubernetes
    user: kubelet
  name: service-account-context
current-context: service-account-contex

cgroupDriver 默认是cgroupfs

  • Kubelet Node Allocatable用来为Kube组件和System进程预留资源,从而保证当节点出现满负荷时也能保证Kube和System进程有足够的资源。
  • 目前支持cpu, memory, ephemeral-storage三种资源预留。
  • Node Capacity是Node的所有硬件资源,kube-reserved是给kube组件预留的资源,system-reserved是给System进程预留的资源, eviction-threshold是kubelet eviction的阈值设定,allocatable才是真正scheduler调度Pod时的参考值(保证Node上所有Pods的request resource不超过Allocatable)。
  • Node Allocatable Resource = Node Capacity - Kube-reserved - system-reserved - eviction-threshold

--enforce-node-allocatable,默认为pods,要为kube组件和System进程预留资源,则需要设置为pods,kube-reserved,system-reserve。
--cgroups-per-qos,Enabling QoS and Pod level cgroups,默认开启。开启后,kubelet会将管理所有workload Pods的cgroups。
--cgroup-driver,默认为cgroupfs,另一可选项为systemd。取决于容器运行时使用的cgroup driver,kubelet与其保持一致。比如你配置docker使用systemd cgroup driver,那么kubelet也需要配置--cgroup-driver=systemd。
--kube-reserved,用于配置为kube组件(kubelet,kube-proxy,dockerd等)预留的资源量,比如—kube-reserved=cpu=1000m,memory=8Gi,ephemeral-storage=16Gi。
--kube-reserved-cgroup,如果你设置了--kube-reserved,那么请一定要设置对应的cgroup,并且该cgroup目录要事先创建好,否则kubelet将不会自动创建导致kubelet启动失败。比如设置为kube-reserved-cgroup=/kubelet.service 。
--system-reserved,用于配置为System进程预留的资源量,比如—system-reserved=cpu=500m,memory=4Gi,ephemeral-storage=4Gi。
--system-reserved-cgroup,如果你设置了--system-reserved,那么请一定要设置对应的cgroup,并且该cgroup目录要事先创建好,否则kubelet将不会自动创建导致kubelet启动失败。比如设置为system-reserved-cgroup=/system.slice。
--eviction-hard,用来配置kubelet的hard eviction条件,只支持memory和ephemeral-storage两种不可压缩资源。当出现MemoryPressure时,Scheduler不会调度新的Best-Effort QoS Pods到此节点。当出现DiskPressure时,Scheduler不会调度任何新Pods到此节点。关于Kubelet Eviction的更多解读,请参考我的相关博文。
Kubelet Node Allocatable的代码很简单,主要在pkg/kubelet/cm/node_container_manager.go,感兴趣的同学自己去走读一遍。

–hostname-override 指定 hostname,如果非空会使用这个值作为节点在集群中的标识

然后检查节点是否成功加入集群:

kubectl get cs,nodes

如果修改了apiserver端口:

 kubectl -s  http://localhost:9090 get cs,nodes

使用supervisor管理: https://github.com/huangguisu/k8s/tree/master/supervisor/k8s-kubelet.conf

2 kube-proxy服务

该模块实现了kubernetes中的服务发现和反向代理功能。kube-proxy支持TCP和UDP连接转发,默认基Round Robin算法将客户端流量转发到与service对应的一组后端pod。服务发现方面,kube-proxy使用etcd的watch机制监控集群中service和endpoint对象数据的动态变化,并且维护一个service到endpoint的映射关系,从而保证了后端pod的IP变化不会对访问者造成影响,另外,kube-proxy还支持session affinity。

nohup kube-proxy --master http://k8s-master:8080 --hostname-override=k8s-node1 --logtostderr=false --log-dir=/mnt/logs/k8s-kube-proxy --v=2 &

kubectl -s  http://localhost:9090 get nodes

使用supervisor管理: https://github.com/huangguisu/k8s/tree/master/supervisor/k8s-kube-proxy.conf

问题


1、kubelet启动:

# 看到最后一行:error: failed to run Kubelet: failed to create kubelet: misconfiguration: kubelet cgroup driver: "cgroupfs" is different from docker cgroup driver: "systemd"

kubelet的cgroup-driver与docker设置的不一致导致无法启动

这个好特么坑,docker是使用cgroup技术来限制容器的资源的。 docker的启动参数中特意有一个参数是--cgroup-driver
kubelet文件驱动默认cgroupfs, 而我们安装的docker使用的文件驱动是systemd, 造成不一致, 导致镜像无法启动。
现在有两种方式, 一种是修改docker, 另一种是修改kubelet。
我这里采用修改docker的方式
注意:

修改或创建/etc/docker/daemon.json,加入下面的内容:

{ "exec-opts": ["native.cgroupdriver=systemd"] }

或者修改docker.service
# vim /lib/systemd/system/docker.service
# 将 --exec-opt native.cgroupdriver=systemd  修改为:
#  --exec-opt native.cgroupdriver=cgroupfs
# systemctl daemon-reload
# systemctl restart docker.service
# kubelet显示正常

修改kubelet的启动参数--cgroup-driver=systemd:

kubelet   --kubeconfig=/mnt/app/kubernetes/conf/kubelet.yaml --hostname-override=192.68.10.37 --logtostderr=false --log-dir=/mnt/logs/k8s-kubelet/  --v=2  --cgroup-driver=systemd

2、kubelet启动报错:

0617 14:25:16.541259   26288 summary.go:102] Failed to get system container stats for "/system.slice/docker.service": failed to get cgroup stats for "/system.slice/docker.service": failed to get container info for "/system.slice/docker.service": unknown container "/system.slice/docker.service"

解决方案,添加启动参数:

--runtime-cgroups=/systemd/system.slice --kubelet-cgroups=/systemd/system.slice

3、 用kubectl logs <podname> -n kube-system查看日志会发现有这样的日志存在
Expected to load root CA config from /var/run/secrets/kubernetes.io/serviceaccount/ca.crt, but got err: open /var/run/secrets/kubernetes.io/serviceaccount/ca.crt: no such file or directory
这是因为kubernetes默认创建的secrets 资源不包含用于访问apiserver的根证书
这需要重新生产证书和秘钥

4、日志报错:k8s-kube-scheduler

Error from server (NotFound): the server could not find the requested resource

或者kubectl get nodes                               
error: the server doesn't have a resource type "nodes"

错误原因:

默认连接的server 是http://localhost:8080,由于改了端口号需指定server

alias kubectl=" kubectl -s http://localhost:9090"
echo  alias kubectl=" kubectl -s http://localhost:9090"  >>  ~/.bashrc



 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐