懒惰是万恶之源,虽然小编对“五一”四天小长假也是没有一点抵抗力,乘本身还有一点仅存意识没有被“五一”侵蚀,赶忙把文章更了。
这是小编对kubernetes的第一篇文章,这篇文章介绍了如下内容:前端
Kubernetes是一个全新的基于容器技术的分布式架构领先方案,与2015年被Google高调的正式开源。一经开源便迅速称霸容器技术领域。
Kubernetes是一个开放的平台。不局限与任何一种语言,不管是C、java、Python均可以毫无阻碍的映射Kubernetes的service,并经过标准的TCP协议进行交互。
Kubernetes是一个完备的分布式系统支撑平台。具有完备的集群管理能力,内建负载均衡、强大的故障发现和自我修复能力,并提供了完备的管理工具,如开发、部署测试、运维监控等等。总之,Kubernetes是一个一站式的完备的分布式系统开发和支撑平台。java
第一点就是跟docker相仿,部署简单,大大节省了运维的成本,pod之间的资源是相互隔离的,不会各自的系统环境影响其余服务,只要轻松的修改image,并部署相应的service和deployment就能够实现服务部署应用、服务滚动升级等等。
其次,Kubernetes全面拥抱微服务架构。将一个巨大的单体应用,分解成多个微服务,每个微服务后又有多个实例,而且内嵌了负载均衡。让咱们直接应用微服务解决复杂业务系统的架构问题。
而后,就是咱们能够随时随地的总体“搬迁”到公有云上。举个例子,后台的某个namespace下运行着各类应用的管控程序,此时咱们只须要在web界面中,建立本身的项目,并在项目中部署本身的应用,后台就会根据咱们的资源模板,在相应的namespace中运行咱们部署的应用,这里因为使用的namespace能够作到彻底的资源隔离。使部署更简单,让开发人员的重点都放在业务之上。
最后,Kubernetes有超强的横向扩展能力,学习过hadoop的生态的朋友们必定知道,hdfs好处之一就是部署在廉价的机器上,并实现了冗余备份,更是支持横向的扩展,这些优势对于后起之秀的Kubernetes固然是通通继承下来,而且与之完善。Kubernetes能够作到不用修改代码就能平滑扩招到拥有上百个node的大规模集群。
老版本的架构:
新版本的架构:node
在演示以前,因为你们可能对yaml语法有写模糊,小编这里先介绍一下yaml的基本语法,有助于你们理解下面案例的配置文件。mysql
① Yaml的语法规则:
- 大小写敏感
- 使用缩进表示层级关系
- 缩进时不容许使用 Tab 键,只容许使用空格
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐便可
- “#” 表示注释
- 在 yaml 里面,连续的项目(如:数组元素、集合元素)经过减号“-”来表示,map 结构 里面的键值对(key/value)用冒号“:”来分割。
② Yaml支持的三种数据结构:
1. 对象:对象的一组键值对,使用冒号结构表示。linux
#案例演示 animal: pets hash: name: Steve foo: bar 或者:hash: { name: Steve, foo: bar }
2. 数组:一组连词线开头的行,构成一个数组。web
#案例演示 - Cat - Dog - Goldfish 或者:[ 'Cat', 'Dog', 'Goldfish' ]
3. 复合结构:对象和数组能够结合使用,造成复合结构。redis
#案例演示 bat: website: baidu: http://www.baidu.com qq: http://www.qq.com ali: - http://www.taobao.com - http://www.tmall.com ceo: yanhongli: 李彦宏 huatengma: 马化腾 yunma: 马云
4. 纯量:纯量是最基本的、不可再分的值。如:字符串、布尔值、整数、浮点数、Null、时间、日期
#案例演示
number: 12.30sql
说了这么多的概念,相比你们对Kubernetes也是有所动心,那么小编这就带你们简单的部署一下Kubernetes,感觉一下他的强大之处。
这里咱们使用一个MySQL+Tomcat实现一个简单的web应用的部署。docker
#单机版的k8s安装 [root@zy ~]# systemctl disable firewalld [root@zy ~]# systemctl stop firewalld [root@zy ~]#yum install -y etcd kubernetes #按顺序启动如下服务 [root@zy ~]#systemctl start etcd [root@zy ~]#systemctl start docker [root@zy ~]#systemctl start kube-apiserver [root@zy ~]#systemctl start kube-controller-manager [root@zy ~]#systemctl start kube-scheduler [root@zy ~]#systemctl start kubelet [root@zy ~]#systemctl start kube-proxy #批量启动服务 for i in etcd docker kube-apiserver kube-controller-manager kube-scheduler kubelet kube-proxy ;do systemctl start $i ;done;
#① MySQL服务的RC(mysql-rc.yaml) apiVersion: v1 kind: ReplicationController #副本控制器RC metadata: name: mysql #RC的名称,全局惟一 spec: replicas: 1 #pod 期待的副本数 selector: app: mysql #符合目标的pod拥有此标签 template: #根据下面的模板建立pod的副本 metadata: labels: #pod副本拥有的标签,对应RC的selector app: mysql spec: #pod内容器的定义部分 containers: - name: mysql #容器名称 image: mysql #镜像名称 ports: #容器内应用监听的端口号 - containerPort: 3306 env: #容器内容的环境变量 - name: MYSQL_ROOT_PASSWORD value: "123456"
#② MySQL的service(mysql-svc.yaml) apiVersion: v1 kind: Service metadata: name: mysql spec: ports: - port: 3306 selector: app: mysql
#③ Tomcat服务的RC(myweb-rc.yaml) apiVersion: v1 kind: ReplicationController #副本控制器RC metadata: name: myweb #RC的名称,全局惟一 spec: replicas: 2 #pod 期待的副本数 selector: app: myweb #符合目标的pod拥有此标签 template: #根据下面的模板建立pod的副本 metadata: labels: #pod副本拥有的标签,对应RC的selector app: myweb spec: #pod内容器的定义部分 containers: - name: myweb #容器名称 image: kubeguide/tomcat-app:v1 #镜像名称 ports: #容器内应用监听的端口号 - containerPort: 8080
#④ Tomcat服务的service(myweb-svc.yaml) apiVersion: v1 kind: Service metadata: name: myweb spec: type: NodePort ports: - port: 8080 nodePort: 30001 selector: app: myweb
#执行如下命令 [root@zy ~]#kubelet create -f ./mysql-rc.yaml [root@zy ~]#kubelet create -f ./mysql-svc.yaml [root@zy ~]#kubelet create -f ./myweb-rc.yaml [root@zy ~]#kubelet create -f ./ myweb-svc.yaml 注意:这里可能会涉及到image的下载,须要联网。
验证是否部署成功:
#查看集群中的RC资源对象
[root@zy ~]# kubectl get rc 数据库
[root@zy ~]# kubectl get svc #查看集群中的service资源对象
#访问pod中的Tomcat服务: [root@zy ~]# curl 192.168.130.130:30001
到此在单机版的kubernetes集群中,咱们就部署成功一个Tomcat+MySQL服务。
经过上面的案例你们也对Kubernetes有必定的了解,而且能简单的实际的操做一下Kubernetes,可是可能对其中的细节并非很了解,就像什么是“rc”,为何就能经过service将容器中的服务映射到宿主机中供外部访问等等,接下来小编就给你们介绍Kubernetes几个重中之重的基础概念。
说道master你们必定不会陌生,分布式集群中没有老大怎么能行,群龙无首那岂不天下大乱。Kubernetes中的master就是集群的控制节点,基本上Kubernetes全部的控制命令都会发给它,他来负责具体的执行,若是它一旦宕机,集群内部的全部容器应用的管理都将失效。为了不被斩首,最好设置多个master造成高可用。
这里介绍一下master中的一组进程:
- Kube-apiserver:提供了HTTP Rest接口的关键服务进程,是Kubernetes集群中增、删、改、查等操做的惟一入口。若是它启动失败,那么Kubernetes的全部操做均将无效。
- Kube-controller-manager:Kube-controller-manager:
- Kube-scheduler:负责资源调度的进程,当咱们部署应用,建立pod时,这个进程就会根据每一个node的资源状况,将pod安排的指定的node中运行。
- etcd:Kubernetes里的全部资源对象的数据所有保存在etcd中,他就是一个Kubernetes的数据库。好比,建立相应的pod时,就会在etcd中生成一个记录,若是后期这个pod被删除,一样的存在etcd中这个记录也会被删除。
Kubernetes集群中除了master,其余的机器就是node,也就是所谓的从节点。Node是Kubernetes集群的工做负载节点,每一个node都会被master分配必定的工做负载。就像hdfs同样,若是有从节点宕机,master就会将此节点上的工做(对于hdfs是副本)转义到其余的可用的node上,以保证咱们设置的replica个数。
这里介绍一下node中的一组进程:
- Kubelet:负责node节点上的pod的建立、启停、同时与master保持通讯。
- Kube-proxy:实现service的通讯与负载均衡机制的重要组件。
- Docker:负责node中的容器建立和管理。
固然上面,说到了Kubernetes集群能够横向扩展,也就是node能够动态的添加,当node节点上安装了相应的配置,默认状况下kubelet会自动的向master注册本身,kubelet会向master汇报自身状况(资源、系统…),这样master会发现node并以后为其分配工做负载。固然这种相似心跳机制的通讯,有助于master计算各个node的资源,更好的进行过负载,也有助于master对node的管理,当有node“失联”时,master也会很快的将其node上的工做负载转移到其余机器上,有利于集群的正常工做。
pod是Kubernetes最基本最重要的概念,pod也是Kubernetes集群的负责均衡的最小单位。
上面的图就是一个pod中的结构,这里不论pod中运行多少container,都有一个pause container,它的做用就是表明这个容器组的状态,为pod中的每个container共享IP和数据目录。
Kubernetes为每个pod分配了一个Pod IP,这个Pod IP被pod中的全部容器共享,默认的Kubernetes集群中任意的两个pod之间能够直接的TCP通讯。
默认的当pod中有某个容器中止时,Kubernetes会自动检测到这个问题并重启这个pod(重启pod中全部的container)。当前前面也说了,当node宕机时,会将工做负载移动到其余的node上,这个里的工做负载就是pod。
当时小编学习k8s的label也是半知半解,k8s的中文官档把label解释的太过官方,小编我愣是读了3遍没有看懂到底label强大在哪里。容小编我先介绍一下label,label是一个key=value的键值对,其中key与value由用户自定义。Label能够附加在各类资源对象上,例如:node、pod、service、RC等。一个资源对象又能够定义多个label,同一个label也能够被添加到任意数量的资源对象上,label一般在资源对象定义时建立,同时也能够在资源对象建立后动态添加。
是否是听了上面一堆向绕口令式的解释,有种想打人的冲突。年轻人,不要心浮气躁,其实label很简单,学过JavaScript小伙伴必定知道选择器,有什么类选择器、id选择器。其实label就和JavaScript的标签同样,当咱们给某一个资源对象用label打上标签的时候,能够在其余的资源对象中经过“selector”来选择相应的标签,这样就能准确的定位当拥有相应标签的资源对象了。
Label selector有两种类型:
- 基于等式: name=redis-slave #匹配具备name=redis-slave标签的资源对象
- 基于集合:env!=production #匹配不具备env=production标签的资源对象
Label selector在k8s集群中的重要使用场景:
- Kube-controller进程经过资源对象RC上定义的label selector来筛选要监控的pod副本的数量,从而实现pod副本的数量始终符合预期设定的全自动控制流程
- Kube-proxy进程经过service的label selector来选择对应的pod,自动创建起每个service到对应pod的请求转发路由表,从而实现了service的智能复杂均衡。
- 经过对某些node定义特定的label,而且在pod定义文件中使用nodeselector这种标签地调度,kube-scheduler进程能够实现pod“定向调度”的特性。
总之,使用label能够对对象建立多组标签,label和label selector共同构成了k8s系统中最核心的应用模型,使得被管理对象可以被精确地分组管理。
RC实际上是一个定义了指望的场景,即声明某种pod的副本数量在任意时刻都符合某个预期的值,因此RC的定义包括如下几部分:
- pod的期待的副本数
- 用于筛选目标pod的label selector
- 当pod的副本数小于预期数量,用于建立新的pod的pod模板
如上图所示,RC就控制着,label为name=app的pod保持两个副本,若是有pod出现问题,RC会在相应的其余node上,根据RC中的template启动pod,若是pod的副本数对于期待的值,RC会马上终结掉多余数量的pod,这一切都是自动化的。固然若是要下线pod,能够先将RC中期待的副本数设置为0,以后再删除RC便可。
在k8s v1.2是RC升级成为了一个新的概念---replica set,其实两者的区别就是,RC使用的是基于等式的label selector,而RS使用的是基于集合的label selector。
RC的特性:
- 经过RC能够实现pod的建立以及副本数的自动控制
- 经过改变RC中定义的pod的副本数,能够实现pod的扩容和缩容
- 经过改变RC中template的image版本,能够实现pod的滚动升级(优雅)
deployment也是kubernetes v1.2引入的概念,它的宗旨在于更好的解决pod的编排问题。Deployment内部是使用了replica set来实现,其类似度与replication controller有90%以上。Deployment相对于RC最大的优点就是,咱们能够随时知道当前pod部署的进度,这是由于咱们期待系统启动N个pod的目标状态,是一个连续变化的过程,而deployment能够实时的跟踪这个过程,这至关有助于排查错误。
Deployment的典型使用场景:
- 建立deployment对象来生成RS,并完成pod副本的建立过程
- 建立deployment对象来生成RS,并完成pod副本的建立过程
- 当deployment不稳定时,能够回滚到先前的一个deployment版本
- 暂停deployment以便于一次性修改多个podtemplatespec的配置项,以后再恢复deployment进行新的发布。
在k8s中,pod的管理对象RC、deployment、daemonset和job都是无状态的服务。可是不少时候咱们须要有状态的服务,好比MySQL、akka、zookeeper集群,他们的节点都有固定的身份、集群规模比较固定,集群中的每一个节点都是有状态的一般会持久化数据到磁盘、若是磁盘损坏则集群的某个节点将没法正常运行。若是使用RC/deployment来控制这些pod,那么咱们发现这是不知足要求的。由于,pod的名称是随机产生的,pod的IP是在运行期间才肯定的,并且咱们须要pod在失败重启后,仍然能挂载集群中的数据共享。
根据以上的问题,kubernetes从v1.4引入了petSet,并在v1.5正式更名为statuefulSet。而它的特性是:
- statuefulSet里的每个pod都有稳定的、惟一的网络表示(经过某种DNS实现),能够发现集群中的其余成员
- statuefulSet里的每个pod都有稳定的、惟一的网络表示(经过某种DNS实现),能够发现集群中的其余成员
- statuefulSet中的pod采用的是稳定的持久化存储卷,经过PV/PVC来实现。删除pod时默认不会删除与statuefulSet相关的存储卷。
service也是kubernetes里最核心的资源对象之一,kubernetes的每个service其实就是微服务架构中的一个“微服务”,由下图所示:
Service定义了一个服务的访问入口,前端的frontend pod经过这个入口地址访问其背后的一组由pod副本组成的集群实例,service与其后端的pod经过label selector无缝对接。可是咱们想到了,虽然客户端访问的是service,可是最终访问的仍是对应的service下的pod中的应用程序,因为pod在失败重启后,他的IP地址是动态变化的,那么咱们如何肯定咱们该如何访问服务。这里service设计了一种巧妙的方法,service一旦建立,集群会自动为它分配一个可用的clusterIP,并且在service的整个生命周期中,这个clusterIP不会改变,咱们只要使用最基本的TCP网络通讯,就能够访问具体的service服务,那以后service与pod的通讯,是经过每一个node节点上的kube-proxy,它负责把对service的请求转发到后端的某个pod中。这样咱们就能够经过一个固定的IP去访问服务啦。固然最终咱们访问的仍然是宿主机的某个端口,咱们能够在定义service的yaml文件中,配置相应的宿主机的端口映射来实现外部访问集群内部的服务。
apiVersion: v1 kind: Service metadata: name: myweb spec: type: NodePort #这里就设置了service到宿主机端口获得映射 ports: - port: 8080 nodePort: 30001 selector: app: myweb
volume是pod中可以被多个容器访问的共享目录。K8s中的volume跟docker中的volume比较类似,可是二者不能等价。K8s中的volume定义在pod上,而后被一个pod例的多个容器挂载到具体的文件目录下,其次,kubernetes中的volume与pod的生命周期相同,但与容器的生命周期不相关,当容器终止或者重启时,volume中的数据不会丢失。
Volume的使用也比较简单,咱们先在pod中声明一个volume,而后在容器里引用该volume并mount到容器的目录中便可。例:
template: metadata: labels: app: app-demo tier: frontend spec: volume: - name: datavol emptyDir: {} containers: - name: tomcat-deom image: tomcat volumeMounts: - mountPath: /mydata-data name: datavol
这里就是使用了一个emptyDir 类型的volume,挂载到container中的/mydata-data下。
这里介绍一下volume的常见的类型:
- emptyDir:临时的空目录,当pod在node中建立时自动分配,当pod从node中移除时,其emptyDir分配的目录中的数据也将被删除
- hostPath:在pod上挂载宿主机的文件或者目录。
- gecPersistenDisk:表示使用谷歌公有云提供的永久磁盘存放volume的数据。
- nfs:表示使用nfs网络文件系统提供的共享目录存储数据,固然想用这种方式,还须要额外的部署一个nfs服务。
volume是定义在pod上的,属于“计算资源”的一部分,而实际上,“网络存储”是相对独立与“计算资源”而存在的一种实体资源。相似于网盘,而kubernetes中的persistent volume 和与之关联的persistent volume claim ,也起到了相似的做用。
PV的特色:
- PV只能是网络存储,不属于任何一个node,可是能够在任意一个node中访问
- PV并非定义在pod上的,而是独立于pod以外定义的
例:
#定义一个PV apiVersion: v1 kind: PersistentVolume metadata: name: pv003 spec: capacity: storage: 5Gi accessModes: - ReadWriteOnce nfs: path: /somedata server: 172.17.0.2
#若是pod想使用上面的PV,就须要定义一个PVC kind: PersistentVolumeCliam apiVersion: v1 metadata: name: myclaim spec: accessModes: - ReadWriteOnce resources: requests: storage: 8Gi #而后在pod的volume总引用上述的PVC便可 spec: volume: - name: mypd PersistentVolumeCliam: cliamName: myclaim
最后介绍一下PV的状态:
Availale:空闲状态
Bound:已经绑定到某个PVC上
Released:对应的PVC已经删除,可是资源尚未被集群回收
Failed:PV自动回收失败
namespace是kubernetes系统中实现多租户的资源隔离。Namespace经过将集群内部的资源对象“分配”到不一样的namespce中,造成逻辑上分组的不一样项目、小组或者用户组,便于不一样的分组在共享使用整个集群的资源的同时还能被分别管理。
咱们能够在定义资源对象是,在他的metadata中指定他属于的namespace:
apiVersion: v1 kind: PersistentVolume metadata: name: pv003 namespace: xxxx
同时在不一样的namespace中,还能限定不一样租户能占用的资源,若有CPU使用量、内存等等。
annotation与label相似,也使用key/value键值对的形式进行定义。不一样的是,label具备严格的命名规则,他定义的是k8s对象的元数据,而且同于label selector。而annotation则是用户任意定义的“附加信息” ,以便于外部工具的查找。
大致来讲,annotation定义以下信息:
- build信息、release信息、dicker镜像地址等等
- 日志库、监控库、分析库等等资源库的地址信息
- 程序调试工具信息,例如:工具名、版本号
- 团队信息,如:联系电话、负责人名称、网址等
[root@zy ~]# yum install -y etcd kubernetes
缘由:机器上安装了docker,由于安装kubernetes会自动安装docker。
解决:卸载docker
[root@zy ~]# yum list installed | grep docke [root@zy ~]# yum remove -y docker-ce.x86_64 [root@zy ~]# rm -rf /var/lib/docker
[root@zy yaml_file]# kubectl create -f mysql-rc.yaml
缘由:k8s的认证问题。
[root@zy yaml_file]#systemctl restart kube-apiserver
缘由:查看pod的建立的报错信息,发现是pod-infrastructure镜像下载失败,致使pod启动失败。
解决:
pod-infrastructure镜像下载配置:
[root@zy yaml_file]# vim /etc/kubernetes/kubelet
发现:
发现国内是没法经过这个连接下载到pod-infrastructure镜像。
首先咱们:
[root@zy yaml_file]# docker search pod-infrastructure
下载咱们须要的镜像:
[root@zy yaml_file]# docker pull docker.io/w564791/pod-infrastructure
而后将下载的镜像推送到本身的私有仓库:
[root@zy yaml_file]#docker tag docker.io/w564791/pod-infrastructure 127.0.0.1:5000/ pod-infrastructure:v1.0 [root@zy yaml_file]#docker push 127.0.0.1:5000/ pod-infrastructure:v1.0
修改拉取的pod-infrastructure镜像的位置:
[root@zy yaml_file]# vim /etc/kubernetes/kubelet: 改成: KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=127.0.0.1:5000/ pod-infrastructure:v1.0"
最后重启集群:
#!/bin/bash
for SERVICES in kube-apiserver kube-controller-manager kube-scheduler; do
systemctl restart $SERVICES
done
systemctl restart kubelet
缘由:环境是CentOS Linux release 7.6.1810 (Core),可是因为内核的版本和docker存在兼容性问题。
解决:升级内核:
#查看内核版本 [root@zy yaml_file]# uname -r #升级内核 #导入key [root@zy yaml_file]# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org #安装elrepo的yum源 [root@zy yaml_file]# rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm #安装内核在yum的ELRepo源中,有mainline颁布的,能够这样安装: [root@zy yaml_file]# yum --enablerepo=elrepo-kernel install kernel-ml-devel kernel-ml -y #重启Linux [root@zy yaml_file]#reboot