kubernetes 简称k8s, 因为k和s 中间有'ubernete' 8个单词,所以简称k8s。是一个开源的,用于管理云平台中多个主机上的容器化的应用, k8s 的目标是让部署容器化的应用简单并且高效,k8s 提供了应用部署、规划、更新、维护的一种机制。是google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

它有三个关键的组件:kubelet, kubeadm, kubectl

其中:

kubeadm : 是用来搞集群的,比如初始化 master 节点,添加 work 节

kubelet : 简单说就是master 派到node 节点的代表,管理本机容器的相关操作。负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理

kubectl : 是执行各种查询和控制类操作的

这里再写几个其他的概念:

etcd:存储系统,用于保存集群相关的数据,比如说状态数据、pod数据等,注意pod可以分为2类,一种是普通的pod,这种pod会将自己的相关信息存储到etcd,这样管理节点就可以知道有哪些pod,还有一种是静态pod它们会将自己的信息存储到自己所在的节点的存储文件里,这样一来管理节点就不知道它们的存在,所以他们的启动停止等操作,就只能靠人工去相应的节点上去手动操作了。

apiserver:资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制,以restful 方式,交给etcd 存储

scheduler:节点调度,选择node 节点应用部署;按照预定的调度策略将Pod调度到相应的机器上

controller manager:负责维护集群的状态,比如故障检测、自动扩展、滚动更新等,一个资源对应一个控制器

kube-proxy:网络上的代理,包括负载均衡等操作。负责为Service提供cluster内部的服务发现和负载均衡

pod:k8s部署的最小单元,一个pod 可以包含多个容器,也就是一组容器的集合;容器内部共享网络;pod 的生命周期是短暂的,重新部署之后是一个新的pod,pod是K8s集群中所有业务类型的基础,可以看作运行在K8s集群中的小机器人,不同类型的业务就需要不同类型的小机器人去执行。目前K8s中的业务主要可以分为长期伺服型(long-running)、批处理型(batch)、节点后台支撑型(node-daemon)和有状态应用型(stateful application);分别对应的小机器人控制器为Deployment、Job、DaemonSet和PetSet

Replication Controller :RC是K8s集群中最早的保证Pod高可用的API对象。通过监控运行中的Pod来保证集群中运行指定数目的Pod副本。指定的数目可以是多个也可以是1个;少于指定数目,RC就会启动运行新的Pod副本;多于指定数目,RC就会杀死多余的Pod

Service:定义一组pod 的访问规则。每个Service会对应一个集群内部有效的虚拟IP,集群内部通过虚拟IP访问一个服务。

ok,接下来,看下k8s部署集群的公共操作(不管是部署master节点,还是work节点,都需要先完成这些操作)

1、关闭selinux

     vi /etc/selinux/config  # 永久关闭

     将参数 SELINUX 改为 disabled 即可

     

2、注释掉交换分区

     vi /etc/fstab

     将交换分区那一行注掉 

    执行swapoff -a 暂时关闭这个文件

3、修改网卡配置(由于集群中肯定存在流量转发的问题,所以需要修改网卡)

      vi /etc/sysctl.conf

      将如下三行搞进去:

net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1

      

      保存退出后,执行 sysctl -p 来应用它

      

 4、启用内核模块

       vi /etc/sysconfig/modules/ipvs.modules

       添加如下内容:

modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4

       完成后,其并不会立刻生效,需要执行一下命令,让其临时生效:

modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4

5、关闭防火墙

systemctl stop firewalld
systemctl disable firewalld
systemctl status firewalld

6、配置 hosts

     编辑源的配置文件

     vi /etc/yum.repos.d/kubernetes.repo

     将如下代码添加进去:

[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg

  

安装 kubelet, kubeadm, kubectl:

  yum install -y kubelet kubeadm kubectl

启动 kubelet 服务:

systemctl enable kubelet
systemctl start kubelet
systemctl status kubelet    # 这里会有255的错,这个错属于正常,后面会解决

 执行命令 journalctl -xe 可以查看具体报错信息

后续会创建这个配置文件,所以暂时不管。 

安装 docker,可以去官网上看下流程:

https://docs.docker.com/engine/install/

这里以cetos为例:

1、先卸载以前的版本,如果之前没安过,直接跳过

 sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

2、Set up the repository

sudo yum install -y yum-utils
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

3、下载docker

sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin

4、启动docker

sudo systemctl start docker

5、查看状态

systemctl status docker

 docker version

接下来修改docker配置:

1、查看docker驱动

docker info | grep -i cgroup

 k8s默认的docker驱动为systemd,所以需要修改驱动

2、修改驱动

vi /etc/docker/daemon.json

将下面的东西添加进去:

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

完成以后重启docker服务:

systemctl restart docker

再看驱动: docker info | grep -i cgroup

接下来需要下载k8s所需要的一些镜像文件:

1、查看需要下载哪些镜像:

      kubeadm config images list

2、下载这些镜像,由于网络原因,我们需要用docker去找docker hub中等效的镜像,将其下载下来,完成后使用docker tag 命令将docker下载下的镜像名称换成上面所需要的k8s的镜像名称,然后再删除docker名称的那些原始镜像。

https://hub.docker.com/

在上面搜索所需同名镜像:

执行命令: docker pull kubeimage/kube-apiserver-amd64

完成后,查看镜像: docker images

 然后修改tag: docker tag xxx xxx 再次查看: docker images 

删除多余镜像,并查看结果:

然后,这就可以了,剩下的镜像,按照流程替换即可。

以上就是所有集群化配置的公共内容了。 

Master 节点的配置

假如是做测试,在pc上搞三个虚拟机做为节点的话,上面的操作可以在一个虚拟机上进行,完成后复制2份即可,但是会有一个问题,复制的虚拟机的 ip 地址也是完全相同的,所以需要将后面的复制品的 IP 进行更改。

这里分享一个改虚拟机 ip 的 方法:

首先:

ip addr    # 查看 ip

 所以这里就需要改2个地方的 ip

vi /etc/sysconfig/network-scripts/ifcfg-enp0s3

vi /etc/sysconfig/network-scripts/ifcfg-enp0s8

 换掉这个值就行,同一个机子,这个值应该是统一的。

注意,完事后记着重启网络服务:systemctl restart network

另外,对于后面的2个虚拟机还需要设定其主机名称,方法为:

cat  /etc/hostname     #  查看主机名

hostnamectl  set-hostname   xxx     # 修改主机名

这里再提一嘴,往往可以 ping 主机名   就可以找到具体的 ip,那是因为在配置文件里已经有了相应的映射配置,所以才可以ping通,这里添加一个修改这个映射的方法:

vi /etc/hosts     # 在这修改,将新的 ip 与 主机名映射添加进去即可

 master节点初始化之前还需要对 pod-network-cidr 这个参数做一些准备工作,就是选择合适的插件。

https://kubernetes.io/zh/docs/concepts/cluster-administration/addons/

可以在这个网站上查看,都有哪些网络插件可用。

然后,选择你想用的插件,点中去查看,例如:

 进去一后往下拉,看这里:

 将这个url在浏览器中打开,搜 network,就可以找到网络取值范围:

 然后将这个值给到,master节点的 pod-network-cidr 这个参数。所以说这个参数不是随便给的,也是精挑细选,选出来的。

然后就可以执行master节点的创建工作了。

kubeadm init --apiserver-advertise-address=192.168.13.103 --kubernetes-version v1.18.0 --pod-network-cidr=10.244.0.0/16

其中参数:

--pod-network-cidr 指pod的网络范围,不同的插件对应的范围不同,所以在给值的时候要注意

--kubernetes-version 就是k8s的版本默认是最新版,当然如果用的不是最新版,就最好指定一下

--apiserver-advertise-address 这是master机子所在节点的 ip 地址

如果在初始化过程中,失败了,那么肯定先找失败的原因,解决掉以后,需要重新还原配置,命令为:kubeadm reset

 执行完命令后,这里会有个这个文件,在这里可以修改这个文件,因为我们有将主机名和ip绑定,所以,可以将这个文件中的 ip 地址换成主机名,然后,就可以通过主机名访问各个服务了。

编辑这个文件:

将它换掉:

 

 然后,按照执行完初始化master的命令中的提示,继续即可。

然后查看下命名空间:kubectl get pods --all-namespaces 

 这里会发现有2个pod处于等待态。这是由于初始化参数 pod-network-cidr 我们给了参数,但是并没有进行相应的配置。还记着上面截图中,我们圈出来的命令么,这里,我们执行一下,将插件的配置文件使用起来。

kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

执行完成之后,会发现这里多了一个pod

 然后也可以通过命令去查看这个pod到底什么情况:

kubectl describe pods xxx(pod-name) -n xxx(namespece)

现在再看,它就好了

 然后,master节点就ok了,接下来,我们需要生成一个将work节点添加到master节点的命令。

kubectl token create --print-join-command

 work 节点的配置

 基础的相关配置和上面差不多,都需要改 ip,改主机名,改 hosts 映射,完成后,就只需要执行,在master节点上创建的 join-command 命令即可,就是上面那个,复制粘贴到work节点就完事了。

在master节点看下结果,一切ok

服务部署:

其实,服务的部署,只有一个命令就可以了,重点是得有部署服务的 yml 文件,这属于k8s的yml文件。

类似于这样的:

 

 然后,基于这个配置文件,就可以直接使用命令部署了。

kubectl apply -f  xxx.yml

直接就可以完成部署了。

接下来,分析下k8s.yml文件。

apiVersion:是当下构建内容的版本

kind:是指,你想干啥,比如,Namespace就是创建命名空间,Deployment就是部署服务

Namespace:命名空间,它的作用就是资源的隔离。

资源对象的隔离:Service,Deployment,Pod

资源配额的隔离:Cpu,Memory

这里注意,namespace的资源隔离只是在名称方面的隔离,它并不会隔离 service_ip, pod_ip,这有些像 /etc/hosts里的设定,它的隔离相当于更改了这里映射的名称,但并不能改ip本身,所以它只是隔离了类似主机名称,却不会产生物理隔离。

 metadata:元数据,就是对当下kind的整体的说明,其中的 name 参数是指给这个 kind 起的名称,namespace 是指这个pod属于哪个命名空间。注意,如果这里没给namespace,那么就是用的默认的default命名空间。比如,当下这个name对应的kind是deployment,那么它的变动,就会引起deploy的变化,简单来说就是建立了新的deploy。

 resources:资源管理。节点上kubelet会收集资源信息(内存大小,多少核等信息)然后上报给apiService,然后,apiService就会根据容器这里的资源需求将其分配到相应的节点上。就比如有个节点内存只有1G,但是有一个pod运行的容器需要10G内存,那么,这个时候,显然apiServer就是不可能将这个pod部署到这个节点上的。这里注意,resources,下有2个参数,requests,limits,前者是说,这个容器希望被分配多少资源,后者是说,这个容器最多消耗多少资源。memory,是内存,单位 Mi (就是传统的M),Gi (传统的G),cpu就是内核,如果不给单位,默认就是核,比如 cpu:1,就是cpu 1核,单位 m,1核 = 1000m。上面的配置解读的意思就是:运行这个容器,必须得有100M内存,0.1核的资源,同时这个容器最多占用资源100M,0.2核。这里需要注意,requests == limits 是最合理的安排,也是最稳定的。requests < limits, 这种设定意义不大,比如,设定 request.memory = 1G,而 limit.memory = 100G,那么就会产生一个问题,这个容器的运行过程占用内存波动太大了,到底它占多少内容,没人知道,这样的东西,就风险太大了。同时 resource 这个参数最好是给上,否则,在node上资源不够用的时候,第一个干掉的就是这种没任何设定的。另外,这里的值最好不要乱设置,资源不足时,cpu会按比例将资源分给各个容器,但是内存不会,资源不足时,占内存大的进程会被干掉,当然容器是不会被停掉的,但是容器内运行的占内存大的服务就会被停掉了。

当然也可以不在deploy这里定义容器的resource,转而通过一个专门的配置文件去控制这些资源。根据命令:

kubectl create -f xxx.yml -n xxx_namespace   # 这里注意 limit.yml 必须依赖命名空间创建,这样以来就相当于命名空间本身有了相关的限制,所以,最后部署于这个命名空间之中的pod也就同样需要遵守这些限制

kubectl describe limits -n xxx_namespace   # 查看 xx 命名空间的限制

 这也是一种资源配额的设定。

 只不过,这两种限定资源的启动方式和之前的不同:

kubectl apply -f  xxx.yml  -n  xxx_namespace  # 一样需要依赖命名空间

注意这种配置,属于配置的是总额,比如左侧截图的配置,就是说pods最多运行4个,需求cpu总量占用最多2核,需求内存总量占用最多4G,容器占用cup总量最多波动到4核,内存波动到8G。

扯一个机制:

pod驱逐-eviction :通常情况,kubelet会监控node的情况,并将相关信息返回给apiService,那么apiService就会根据一些策略将一些pod给干掉。

--eviction-soft=memory.available<1.5Gi    # 当可用内存小于1.5G时,准备执行pod驱逐策略,这时候不是立刻执行pod驱逐策略

--eviction-soft-grace-period=memory.available=1m30s # 当可用内存小于1.5G这样的情况持续存在了1分30秒的时候,执行pod驱逐策略

--eviction-hard=memory.available<100Mi,nodefs.available<1Gi,nidefs.inodesFree<5% # 当内存小于100M或者磁盘小于1G或者磁盘i节点小于5%的时候,立刻执行pod驱逐策略。

对于磁盘紧缺的情况:

1、删除死掉的pod,容器

2、删除没用的镜像

3、按优先级,资源占用的情况驱逐pod

内存紧缺的情况:

1、驱逐不可靠的pod    # 就是没有设置 resource参数的pod

2、驱除基本可靠的pod   # 通常就是resource参数中 limit > request 的那些pod

3、驱除可靠的pod  # 通常就是resource参数中 limit == request 的那些pod

label :

它可以贴到 pod,Deployment,Service,Node,Resource 等等上

而且同一个对象,可以有多个标签,不同的对象,可以有同样的标签。

selector:选择器

matchLabels:匹配 pod 的标签,app就是一个标签的key,对应的就是标签的值。

这里注意,当matchLabels 和 matchExpressions  同时存在的时候是且的关系,就是都要满足。

另外,还有一点,selector一旦创建就不可以更改,如果要改,那就得把原先的删掉,重新构建。

kubectl delete -f xxx.yml   # 先删

kubectl create -f xxx.yml  # 再建

同时对于标签的用法,也可以用作过滤:

kubectl get pods -l xx_key=xx_value -n xx_namespace  # 这就是根据标签来过滤pod

上述命令中 -l 参数中用逗号分隔是‘且’的意思。

kubectl label node xxx_node  xx_key=xx_value  # 直接给节点打标签

在template中可以添加节点选择器nodeSelector,这东西下面给的还是 label,只是这个 label 是属于node的label,意思就是说,这个创建pod的模板要求,只有拥有 disktype=ssd 标签的node,才能去承载当下所创建的pod。

replicas:副本数,就是部署多少个这样的pod

template:模板,就是根据这个模板的配置,将这个pod创建出来。

注意这里也有个metadata,它下面就必须有个labels标签项,且标签的内容,必须有上面selector里的标签。上面已经说了,选择器selector会选择出具有标签 app=web-demo的pod,最后将之创建在命名空间为dev的空间里,所以模板这里在构建创建pod的规则里,就需要体现这一点,就是创建的pod必须有标签app=web-demo。

spec:规范,它描述了你对这个对象所期望的状态,也就是目标。

containers:容器,name就是定义容器的名称,image就是定义容器的镜像从哪里获取,ports就是容器内部的端口,往往docker-compose.yml都会设置,容器的端口和主机端口的映射关系的。

这里的原理是这样的,上面的配置里不是创建pod的么,同时牵扯将这些pod部署到node上,完事后,就可以访问pod上面的服务了,那么这里的Service的配置里就设定了端口的映射关系,当访问80端口的时候,就会映射到容器8080的端口,那么假如现在有多个pod,每一个pod都部署了8080端口的容器服务,那么问题来了,当从浏览器访问服务的时候,具体访问的是哪个呢?这里的selector参数就很有用了,它会去过滤出有labels有app=web-demo的pod,然后去访问,那么加入有多个pod都有这个标签呢?那就轮询访问,浏览器每刷新一次就换一个pod访问。

 ingress它是一种插件,也是一种模式,添加这个插件以后,在集群里,不管这个节点有没有部署你要访问的服务,它偶可以访问,如果你访问的节点刚好没部署相关服务,那么它会将你的请求转发给有相关服务的节点,然后就可以正常访问了。而且也指定了服务的名称,所以,这三个配置在一起也真是环环相扣了。

健康检查,首先,健康检查是针对容器服务的一种检查,所以它的存在是在容器项之下,和容器的其他配置平级。这种是搞shell脚本的方式检查。

livenessProbe:存活探针,该参数用于配置健康检查。

exec:执行命令,command

initialDelaySeconds:初始延迟时间,由于健康检查是针对于容器服务的检查,所以起码得等容器先起来以后,才需要进行检查,那么这个参数设定的时间就是给起容器的时间。

periodSeconds:每隔多长时间检查一次,显然健康检查不是个一次性操作。

failureThreshold:失败的门槛,当健康检查,连续检查2次,都是失败的,那么就认定健康检查失败

successThreshold:只要有一次成功,就任务健康检查成功

timeoutSeconds:执行健康检查命令的超时时间。

健康检查失败后,容器会被杀掉,然后重建。

这是第二种配置健康检查的方式,这种是属于访问http的方式:

path:就是健康检查时,所访问的http路径

port:是容器的真实端口

scheme:类型就是HTTP

注意,这个路径的检查,它只认200,比如返回个301重定向之类的,它会认定失败的。

这是第三种健康检查的方式,根据端口有没有被监听来检查。

这里还有一种参数,readinessProbe,检查程序是否准备就绪,可以访问了,如果可以正常访问了,就可以将其挂到负载均衡上去了。其他参数的意思同上,只是换了个领头的。

对于web服务,这样的配置就比较友好了,以检查端口是否被监听,来确定服务是否正常,如果不正常,就面临容器被杀,重新建容器的结果,然后再通过检查服务的http界面是否可以访问,如果可以访问,那就说明服务准备就绪,可以挂到负载均衡上,让用户访问了。 

affinity:亲和性,它和容器时平级的

nodeAffinity:节点亲和性

requiredDuring....Execution:必须满足如下条件才可以被调度

nodeSelectorTerms:节点的选择策略

matchExpressions:匹配 label 的表达式,key就是 label 的key,value 就是 label 对应的值

这里注意,如果有多个匹配表达式,同时关系是‘且’的关系,那么就可matchExpressions,保持同级就可以。如果是‘或’的关系,就定义多个nodeSelectorTerms,每个 nodeSelectorTerms 都是平级写就可以。

perferreDuring....Execution:最好满足下面的条件

weight:权重

perference:最好满足

如果有多个条件,可以做多个 weight 标签,每个 weight 都是平级关系。

还是亲和性,不过这次是 pod 的亲和性。就是说,必须和具有哪些标签的pod在一起运行,更想跟具有哪些标签的pod在一起运行,运行在 topologyKey 对应的节点的标签的节点上。

topologyKey :它限定了节点的范围,就是该节点必须具备 label = topologyKey对应的值。

 podAntiAffinity:不想跟具备哪些标签的pod运行在具备xx标签的node上

将label的值改为当下pod的label,这样设定以后,就相当于不愿意和自己运行在具有xx标签的节点上。通常这时候的replicas的值一般是不等于1的。通常这样的做法就是不希望同一个服务运行在一起。

反之,把 pod 亲和性从 podAntiAffinity 改成 podAffinity ,就成了希望同一个服务运行在一起。

下一个概念,污点(taint),污点是设定在node上的,然后就是需要在pod上明确设定容忍,如果没有设定容忍,那么,pod 就肯定不会去这些有污点的 node 上的。应用场景就是,通常有一些特殊的节点,他不希望有不相干的pod到他这运行,只有特殊的pod才被允许在这些节点上运行,那么这时候,就可以在这些节点上添加污点,然后给特殊pod上添加容忍,这样就可以规避其他不相干的pod调度到这些特殊node上了。

kubectl taint nodes xxx_node xx_key=xx_value:NoSchedule # 给节点xx打污点 key=value,效果是NoSchedule(不允许pod调度到这)

这个yml中和容器平级的 tolerations 就是容忍。

key就是node上污点的key,value就是污点key对应的值,effect,就是与污点对应的效果。

注意,这里的容忍,只是说对pod添加了容忍,就是说,node有污点pod可以接受,但是并不说pod就必须只能去有污点的node。如果副本数比较多的话,pod也是有可能部署到没有污点的node上的。

strategy:部署策略,type为Recreate

 

 这个部署策略是说,当deploy文件发生变化以后,我们重新执行命令:

kubectl apply -f xx.yml 

之后,会重新部署服务,这时部署服务的策略就是 Recreate,它的特点就是,停止原实例,然后创建新的实例。不管有几个副本,都是一样的。

同样是部署策略 strategy,只是这次是滚动升级。

maxSurge:每次多启动的实例的百分比个数,比如:共4个实例,每次多启动 4*25% 个实例

maxUnavailable:启动实例后,不可用个数。比如:共4个实例,升级完成后,最多可以有 4*25% 个实例是有问题的。

注意,这两个参数可以直接给int,不带%号。另外,对于滚动升级的模式,如果不配,其实默认也是这个策略,且默认的值,和上面截图一致。

kubectl rollout pause deploy xx_pod_name -n xx_namespace   # 暂停xx_pod的滚动升级

kubectl rollout resume deploy xx_pod_name -n xx_namespace # 恢复

kubectl rollout undo deploy xx_pod_name -n xx_namespace  # 回滚升级

蓝绿部署,就是比如之前是按照之前的部署方式,然后新建一个部署,然后等所有新的部署都ok以后,在通过切换service的selector,让其将流量转到新部署的pod上。

 

 这是新搞的deploy.yml文件,这个文件不含service和ingress的内容。

 然后启动它:

kubectl apply -f xx.yml

完成后,就会有新的pod被创建。创建完成后,去改之前的 service 

 

这里 selector 的标签切换成新的 pod 就可以实现导流了。

注意,这时候,新的 pod,和老的 pod 都是在的,如果新的pod运行有问题,那么就可以通过改 service的selector,迅速的将流量切回老的 pod。

 

跟蓝绿部署不同的是,如果将版本号删掉,那么就成了金丝雀部署。

金丝雀部署的特点就是,所有的版本都是存在的,访问的过程就是轮询的状态,这就造成了有些人访问的时候,访问的是老版本,有些人就访问的新版本。

Logo

开源、云原生的融合云平台

更多推荐