简介
- Kubernetes(通常称为k8s)是一个以容器为中心的基础架构,可以实现在物理集群或虚拟机集群上调度和运行容器,提供容器自动部署、扩展和管理的开源平台.满足了应用程序在生产环境中的一些通用需求:应用实例副本、水平自动扩展、命名与发现、负载均衡、滚动升级、资源监控等
特点
- 可移植:支持公有云,私有云,混合云、多重云
- 可扩展:模块化、插件化、可挂载、可组合
- 自动化:自动部署、自动重启、自动复制、自动伸缩/扩展
- 快速部署应用,快速扩展应用
- 无缝对接新的应用功能
- 节省资源,优化硬件资源的使用
核心组件
- Kubernetes遵循master-slave architecture.Kubernetes的组件可分为管理单个的node组件和控制平面的一部分组件
- Kubernetes Master是集群的主要控制单元,用于管理其工作负载并指导整个系统的通信.Kubernetes控制平面由各自的进程组成,每个组件都可以在单个主节点上运行,也可以支持high-architecture clusters的多个主节点上运行
组件 | 说明 |
---|---|
etcd | 保存了整个集群的状态 |
apiserver | 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制 |
controller manager | 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等 |
scheduler | 负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上 |
kubelet | 负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理 |
Container runtime | 负责镜像管理以及Pod和容器的真正运行(CRI) |
kube-proxy | 负责为Service提供cluster内部的服务发现和负载均衡 |
- Add-ons
组件 | 说明 |
---|---|
kube-dns | 负责为整个集群提供DNS服务 |
Ingress Controller | 为服务提供外网入口 |
Heapster | 提供资源监控 |
Dashboard | 提供GUI |
Federation | 提供跨可用区的集群 |
Fluentd-elasticsearch | 提供集群日志采集、存储与查询 |
部署Kubernetes
架构
角色 | IP | 主机名 |
---|---|---|
master | 10.0.0.111/172.16.12.111 | k8s_master |
node01 | 10.0.0.112/172.16.12.112 | k8s_node01 |
node02 | 10.0.0.113/172.16.12.113 | k8s_node02 |
master节点
- 在master设置hosts解析
[root@k8s_master ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.0.0.11 k8s_master
10.0.0.12 k8s_node01
10.0.0.13 k8s_node02
- 安装软件包
[root@k8s_master ~]# yum install etcd docker kubernetes flannel -y
[root@k8s_node01 ~]# yum install docker kubernetes flannel -y
[root@k8s_node02 ~]# yum install docker kubernetes flannel -y
- 修改配置
- yum安装的etcd默认配置文件在/etc/etcd/etcd.conf
[root@k8s_master ~]# grep -Ev "^$|#" /etc/etcd/etcd.conf
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_NAME="default"
ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.111:2379"
- 启动etcd
[root@k8s_master ~]# systemctl start etcd
[root@k8s_master ~]# systemctl enable etcd
- 测试etcd
[root@k8s_master ~]# etcdctl -C http://10.0.0.111:2379 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://10.0.0.111:2379
cluster is healthy
- 配置并启动kubernetes
# /etc/kubernetes/apiserver配置文件内容
[root@k8s_master ~]# grep -Ev "^$|#" /etc/kubernetes/apiserver
KUBE_API_ADDRESS="--insecure-bind-address=0.0.0.0"
KUBE_API_PORT="--port=8080"
KUBE_ETCD_SERVERS="--etcd-servers=http://10.0.0.111:2379"
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.254.0.0/16"
KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota"
KUBE_API_ARGS=""
# /etc/kubernetes/config配置文件内容
[root@k8s_master ~]# grep -Ev "^$|#" /etc/kubernetes/config
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=0"
KUBE_ALLOW_PRIV="--allow-privileged=false"
KUBE_MASTER="--master=http://10.0.0.111:8080"
- 启动服务
[root@k8s_master ~]# systemctl start kube-apiserver.service
[root@k8s_master ~]# systemctl enable kube-apiserver.service
[root@k8s_master ~]# systemctl start kube-controller-manager.service
[root@k8s_master ~]# systemctl enable kube-controller-manager.service
[root@k8s_master ~]# systemctl start kube-scheduler.service
[root@k8s_master ~]# systemctl enable kube-scheduler.service
node节点
- 部署配置node
# node01 /etc/kubernetes/config配置文件内容
[root@k8s_node01 ~]# grep -Ev "^$|#" /etc/kubernetes/config
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=0"
KUBE_ALLOW_PRIV="--allow-privileged=false"
KUBE_MASTER="--master=http://10.0.0.111:8080"
# node01 /etc/kubernetes/kubelet配置文件内容
[root@k8s_node01 ~]# grep -Ev "^$|#" /etc/kubernetes/kubelet
KUBELET_ADDRESS="--address=0.0.0.0"
KUBELET_HOSTNAME="--hostname-override=10.0.0.112"
KUBELET_API_SERVER="--api-servers=http://10.0.0.111:8080"
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest"
KUBELET_ARGS=""
# node02 /etc/kubernetes/config配置文件内容
[root@k8s_node02 ~]# grep -Ev "^$|#" /etc/kubernetes/config
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=0"
KUBE_ALLOW_PRIV="--allow-privileged=false"
KUBE_MASTER="--master=http://10.0.0.111:8080"
# node02 /etc/kubernetes/kubelet配置文件内容
[root@k8s_node02 ~]# grep -Ev "^$|#" /etc/kubernetes/kubelet
KUBELET_ADDRESS="--address=0.0.0.0"
KUBELET_HOSTNAME="--hostname-override=10.0.0.113"
KUBELET_API_SERVER="--api-servers=http://10.0.0.111:8080"
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest"
KUBELET_ARGS=""
- 启动
[root@k8s_node01 ~]# systemctl start kubelet.service
[root@k8s_node01 ~]# systemctl enable kubelet.service
[root@k8s_node01 ~]# systemctl start kube-proxy.service
[root@k8s_node01 ~]# systemctl enable kube-proxy.service
[root@k8s_node02 ~]# systemctl start kubelet.service
[root@k8s_node02 ~]# systemctl enable kubelet.service
[root@k8s_node02 ~]# systemctl start kube-proxy.service
[root@k8s_node02 ~]# systemctl enable kube-proxy.service
- 在master上查看集群中节点及节点状态
[root@k8s_master ~]# kubectl -s http://10.0.0.111:8080 get node
NAME STATUS AGE
10.0.0.112 Ready 1m
10.0.0.113 Ready 2m
[root@k8s_master ~]# kubectl get nodes
NAME STATUS AGE
10.0.0.112 Ready 2m
10.0.0.113 Ready 2m
创建覆盖网络--Flannel
- 配置Flannel(所有节点操作)
[root@k8s_node02 ~]# grep "^[a-Z]" /etc/sysconfig/flanneld
FLANNEL_ETCD_ENDPOINTS="http://10.0.0.111:2379"
FLANNEL_ETCD_PREFIX="/atomic.io/network"
- 配置etcd中关于Flannel的key
- Flannel使用Etcd进行配置,来保证多个Flannel实例之间的配置一致性,所以需要在etcd上进行配置
- /atomic.io/network/config与/etc/sysconfig/flanneld中的FLANNEL_ETCD_PREFIX="/atomic.io/network"是相对应的
# 操作创建网络
[root@k8s_master ~]# vim /etc/sysconfig/flanneld
[root@k8s_master ~]# etcdctl mk /atomic.io/network/config '{"Network":"172.16.0.0/16"}'
{"Network":"172.16.0.0/16"}
# master节点操作
[root@k8s_master ~]# systemctl enable flanneld.service
[root@k8s_master ~]# systemctl start flanneld.service
[root@k8s_master ~]# systemctl restart docker
[root@k8s_master ~]# systemctl restart kube-apiserver.service
[root@k8s_master ~]# systemctl restart kube-controller-manager.service
[root@k8s_master ~]# systemctl restart kube-scheduler.service
# node节点操作
[root@k8s_node01 ~]# systemctl enable flanneld.service
[root@k8s_node01 ~]# systemctl start flanneld.service
[root@k8s_node01 ~]# systemctl restart docker
[root@k8s_node01 ~]# systemctl restart kubelet.service
[root@k8s_node01 ~]# systemctl restart kube-proxy.service
Kubernetes其他安装方法
kubuadm
minikube
Ansible部署:https://github.com/easzlab/kubeasz
编译安装
Kubernetes核心概念
Pod
- Pod是Kubernetes的基本操作单元,也是应用运行的载体.整个Kubernetes系统都是围绕着Pod展开的,比如如何部署运行Pod、如何保证Pod的数量、如何访问Pod等
基本操作
操作 | 指令 |
---|---|
创建 | kubectl create -f xxx.yaml |
查询 | kubectl get yourPodName;kubectl describe pod youPodNAme |
删除 | kubectl delete pod yourPodName |
更新 | kubectl replace /path/to/yourPodName |
Pod与容器
- 在Docker中,容器是最小的处理单元,增删改查的对象是容器,容器是一种虚拟化技术,容器之间是隔离的,隔离是基于Linux Namespace实现的.而在kubernetes中,Pod包含一个或者多个相关的容器,Pod可以认为是容器的一种延伸,一个Pod也是一个隔离体,而Pod内部包含的一组容器又是共享的(包括PID、Network、IPC、UTS).除此之外,Pod中的容器可以访问共同的数据卷来实现文件系统的共享
镜像
- 在kubernetes中,镜像的下载策略为
- Always:每次都下载最新的镜像
- Never:只使用本地镜像,从不下载
- IfNotPresent:只有当本地没有的时候才下载镜像
- Pod被分配到Node之后会根据镜像下载策略进行下载镜像,可以根据自身集群的特点来决定采用何种下载策略.无论何种策略,都要确保Node上有正确的镜像可用
其他设置
- 通过yaml文件,可以在Pod中设置:
- 启动命令:spec-->containers-->command
- 环境变量:spec-->containers-->env-->name/value
- 端口桥接:spec-->containers-->ports-->containerPort/Protocol/hostIP/hostPort(使用hostPort时需要注意端口冲突的问题,不过kubernetes在调度Pod的时候回检查宿主机端口是否冲突,比如当两个Pod均要求绑定宿主机的80端口,kubernetes会将这两个Pod分别调度到不同的机器上)
- Host网络:一些特殊场景下,容器必须要以host方式进行网络设置(如接收物理机网络才能够接收到的组播流),在Pod中也支持host网络的设置,spec-->hostNetwork=true
- 数据持久化:spec-->containers-->volumeMounts-->mountPath
- 重启策略:当Pod中的容器终止退出后,重启容器的策略.这里的Pod重启,实际上的做法是容器的重建,之前容器中的数据将会丢失,如果需要持久化数据,那么需要使用数据卷进行持久化设置.Pod支持三种重启策略
- Always(默认策略,当容器终止退出后,总是重启容器)
- OnFailure(当容器终止且异常退出时,重启)
- Never(从不重启)
Replication Controller
- Replication Controller(RC)是Kubernetes的另一个核心概念,应用托管在Kubernetes之后,Kubernetes需要保证应用能够持续运行,这是RC的工作内容,它会确保任何时间Kubernetes中都有指定数量的Pod在运行.在此基础上,RC还提供了一些更高级的特性,比如滚动升级、升级回滚等
RC与Pod的关联
- RC与Pod的关联是通过Label来实现的.Label机制是Kubernetes中的一个重要设计,通过Label进行对象的弱关联,可以灵活地进行分类和选择.对于Pod,需要设置其自身的Label来进行标识,Label是一些了的key/value对,在Pod-->metadata-->labeks中进行设置
- Lable的定义时任意的,但是Label必须具有可标识性,比如设置Pod的应用名称和版本号等.另外Label是不具有唯一性的,为了更准确的标识一个Pod,应该为Pod设置多个维度的Label
"release":"stable","release":"canary"
"environment":"dev","environment":"prod"
- 当在RC的yaml文件中定义了该RC的selector中的label为app:myweb,那么这个RC就会去关注Pod-->metadata-->labeks中label为app:myweb的Pod.修改了对应Pod的Label,就会使Pod脱离RC的控制.同样,在RC运行正常的时候,若试图继续创建同样Label的Pod,是创建不出来的.因为RC认为副本数已经正常了,再多起的话会被RC删掉
弹性伸缩
- 弹性伸缩是指适应负载变化,以弹性可伸缩的方式提供资源.反映到Kubernetes中,指的是可根据负载的高低动态调整Pod的副本数量.调整Pod的副本数是通过修改RC中Pod的副本来实现的
# 扩容Pod的副本数目到10
kubectl scale relicationcontroller youRcName --replicas=10
# 缩容Pod的副本数目到1
kubectl scale relicationcontroller youRcName --replicas=1
滚动升级
- 滚动升级是一种平滑过渡的升级方式,通过逐步替换的策略,保证整体系统的稳定,在初始升级的时候就可以及时发现、调整问题,以保证问题影响度不会扩大.Kubernetes中滚动升级的命令为:
kubectl rolling-update my-rcName-v1 -f my-rcName-v2-rc.yaml --update-period=10s
- 升级开始后,首先依据提供的定义文件创建v2版本的RC,然后每隔10s(--update-period=10s)逐步增加v2版本的Pod副本数,逐步减少v1版本Pod的副本数.升级完成之后,删除v1版本的RC,保留v2版本的RC,实现滚动升级
- 升级过程中,发生了错误中途退出时,可以选择继续升级.Kubernetes能够智能的判断升级中断之前的状态,然后紧接着继续执行升级,也可以进行回退
kubectl rolling-update my-rcName-v1 -f my-rcName-v2-rc.yaml --update-period=10s --rollback
- 回退的方式实际就是升级的逆操作,逐步增加v1版本的Pod副本数,逐步减少v2版本Pod的副本数
新一代副本控制器replica set
- replica set可以被认为是升级版的Replication Controller.replica set是用于保证与label selector匹配的pod数量维持在期望状态.区别在于,replica set引入了对基于自己的selector查询条件,而Replication Controller仅支持基于值相等的selector条件查询
Service
- Kubernetes中的核心要素Service提供了一套简化的服务代理和发现机制,天然适应微服务架构
原理
在Kubernetes中,在受到RC调控的时候,Pod副本是变化的,对于虚拟IP也是变化的,比如发生迁移或者伸缩的时候.这对于Pod的访问者来说是不可接受的.Kubernetes中的Service是一种抽象概念,它定义了一个Pod逻辑集合以及访问它们的策略,Service同Pod的关联同样是基于Label来完成的.Service的目标是提供一种桥梁,它会为访问者提供一个固定访问地址,用于在访问时重定向到相应的后端,这使得非Kubernetes原生应用程序,在无须为Kubernetes编写特定代码的前提下,轻松访问后端
Service同RC一样,都是通过Label来关联Pod.当在Service的yaml文件中定义该Service的selector中的lable为app:myweb,那么这个Service会将Pod-->metadata-->labeks中的label为app:myweb的Pod作为分发请求的后端.当Pod发生变化时(增加、减少、重建等),Service会及时更新.这样,Service就可以作为Pod的访问入口,起到代理服务器的作用,而对于访问者来说,通过Service进行访问,无需直接感知Pod
注意的是,Kubernetes分配给Service的固定IP是一个虚拟IP,并不是一个真实的IP,在外部是无法寻址的.真实的系统实现上,kubernetes是通过kube-proxy组件来实现的虚拟IP路由及转发.实现了kubernetes层级的虚拟转发网络
Service内部负载均衡
- 当Service的Endpoints包含多个IP的时候,及服务器代理存在多个后端,将进行请求的负载均衡.默认的负载均衡策略是轮询或者随机(由kube-proxy的模式决定).同时,Service上通过设置Service-->spec-->sessionAffinity=ClientIP,来实现基于源IP地址的会话保持
发布Service
- Service的虚拟IP是由Kubernetes虚拟出来的内部网络,外部是无法寻址到的.有些服务需要被外部访问到,例如Web前端.这时候需要加一层网络转发,即外网到内网的转发,即外网到内网的转发.Kubernetes提供了NodePort、LoabBalancer、Ingress三种方式
- NodePort:Kubernetes会在每一个Node上暴露一个端口:nodeport,外部网络可以通过(任一node)[NodeIP]:[NodePort]访问到后端的Service
- LoadBalance:在NodePort基础上,Kubernetes可以请求底层云平台创建一个负载均衡器,将每个Node作为后端,进行服务分发.该模式需要底层云平台(例如GCE)支持
- Ingress:是一种HTTP方式的路由转发机制,由Ingress Controller和HTTP代理服务器组合而成.Ingress Controller实时监控Kubernetes API,实时更新HTTP代理服务器的转发规则.HTTP代理服务器有GCE Load-Balancer、Nginx、HaProxy等
Servicede自发性机制
- Kubernetes中有一个重要的服务自发现特性.一旦一个Service被创建,该Service的Service IP和Service port等信息都可以被注入到Pod中供它们使用.Kubernetes主要支持两种Service发现机制:环境变量和DNS
- 环境变量方式:
- Kubernetes创建Pod时会自动添加所有可用的Service环境变量到该Pod中,如有需要,这些环境变量就被注入Pod内的容器里,需要注意的是,环境变量的注入只发生在Pod创建时,且不会被自动更新.这个特点暗含了Service和访问该Service的Pod的创建时间的先后顺序,即任何想要访问Service的Pod都需要在Service已经存在后创建,否则与Service相关的环境变量就无法注入该Pod的容器中,这样先创建的容器就无法发现后创建的Service
- DNS方式:
- DNS服务器使用Kubernetes的watchAPI,不间断的监测新的Service的创建并为每个Service新建一个DNS记录.如果DNS在整个集群范围内都可用,那么所有的Pod都能够自动解析Service的域名
多个Service避免地址和端口冲突
- Kubernetes为每个Service分配一个唯一的ClusterIP,当时用ClusterIP:port的组合访问一个Service的时候,不管port是什么,这个组合一定不会发生重复.另一方面,kube-proxy为每个Service真正打开的是一个不会重复的随机端口,用户在Service描述文件中指定的访问端口会被映射到这个随机端口上
Deployment
- Kubernetes提供了一种更加简单的更新RC和Pod的机制,Deployment.通过在Deployment中描述期望的集群状态,Deployment Controller会将现在的集群状态在一个可控的速度下逐步更新成所期望的集群状态,Deployment主要功能是保证Pod的数量和健康,90%的功能与Replication Controller一致,具备了Replication Controller之外的新特性
- 事件和状态查看:可以查看Deployment的升级详细进度和状态
- 回滚:当升级Pod镜像或者相关参数的时候发现问题,可以使用回滚操作回滚到上一个稳定的版本或者指定的版本
- 版本记录:每一次对Deployment的操作,都能保存下来,给予后续可能的回滚使用
- 暂停和启动:对于每一次升级,都能够随时暂停和启动
- 多种升级方案:Recreate--删除所有已存在的Pod,重新创建新的;RollingUpdate--滚动升级,逐步替换的策略,同时滚动升级时,支持更多的附加参数,例如设置最大不可用Pod数量,最小升级间隔时间等
滚动升级
- 相比于RC,Deployment直接使用kubectl edit Deployment/DeploymentName或者kubectl set方法就可以直接升级(原理是Pod的template发生变化,例如更新label、更新镜像版本等操作会触发Deployment的滚动升级)
Deployment命令
kubectl describe deployments # 查询详细信息,获取进度升级
kubectl rollout pause deployment/nginx-deployment2 # 暂停升级
kubectl rollout resume deployment/nginx-deployment2 # 继续升级
kubectl rollout undo deployment/nginx-deployment2 # 升级回滚
kubectl scale deployment nginx-deployment --replicas # 弹性伸缩Pod数量
多重升级
- 关于多重升级,举例,当创建了一个nginx1.7的Deployment,要求副本数量为5之后,Deployment Controller会逐步的将5个1.7的Pod启动起来,当启动到3个的时候,又发出更新Deployment中Nginx到1.9的命令;这时Deployment Controller会立即将已启动的3个1.7Pod杀掉,然后逐步启动1.9的Pod.Deployment Controller不会等到1.7的Pod都启动完成之后,再次杀掉1.9,启动1.9
Volume
- 在Docker的设计实现中,容器中的数据是临时的,即当容器被销毁时,其中的数据会丢失.如果需要持久化数据,需要使用Docker数据卷挂载宿主机上的文件或者目录到容器中.在Kubernetes中,当Pod重建的时候,数据是会丢失的,Kubernetes也是通过数据卷挂载来提供Pod数据的持久化.Kubernetes数据卷是对Docker数据卷的扩展,Kubernetes数据卷是Pod级别的,可以用来实现Pod中容器的文件共享.
- Kubernetes支持的数据卷类型
- EmptyDir
- HostPath
- GCE Persistent Disk
- AWS Elastic Block Store
- NFS
- iSCSI
- Flocker
- GlusterFS
- RBD
- Git repo
- Secret
- Persistent Volume Claim
Persistent Volume和Persistent Volume Claim
- Kubernetes提供了Persistent Volume和Persistent Volume Claim机制,这是存储消费模式.Persistent Volume是由系统管理员配置创建的一个数据卷(HostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、iSCSI、GlusterFS、RBD),它代表了一类存储插件实现,对于普通用户来说,通过Persistent Volume Claim可请求并获得合适的Persistent Volume,而无须感知后端的存储实现.Persistent Volume和Persistent Volume Claim的关系类似于Pod和Node,Pod消费Node资源,Persistent Volume Claim则消费Persistent Volume资源.Persistent Volume和Persistent Volume Claim相互关联,有着完整的生命周期管理
- 准备:系统管理员创建或规划一批Persistent Volume
- 绑定:用户通过创建Persistent Volume Claim来声明存储请求,Kubernetes发现有存储请求的时候,就去查找符合条件的Persistent Volume(最小满足策略).找到合适的就绑定上,找不到就一直处于等待状态
- 使用:创建Pod的时候使用Persistent Volume Claim
- 释放:当用户删除绑定在Persistent Volume上的Persistent Volume Claim时,Persistent Volume进入释放状态,此时Persistent Volume中还残留着上一个Persistent Volume Claim的数据,状态还不可用
- 回收:Persistent Volume需要回收才能再次使用.回收策略可以是人工的也可以是Kubernetes自动进行清理(仅支持NFS和HostPath)