懒惰是万恶之源,虽然小编对“五一”四天小长假也是没有一点抵抗力,乘自己还有一点仅存意识没有被“五一”侵蚀,赶紧把文章更了。
  这是小编对kubernetes的第一篇文章,这篇文章介绍了以下内容:

  • kubernetes介绍
  • kubernetes 作用
  • kubernetes的简单案例实践与分析
  • kubernetes基础概念详细说明
  • 部署中可能遇到的问题解决方案

1. Kubernetes介绍

  Kubernetes是一个全新的基于容器技术的分布式架构领先方案,与2015年被Google高调的正式开源。一经开源便迅速称霸容器技术领域。
  Kubernetes是一个开放的平台。不局限与任何一种语言,不论是C、java、Python都可以毫无阻碍的映射Kubernetes的service,并通过标准的TCP协议进行交互。
  Kubernetes是一个完备的分布式系统支撑平台。具备完备的集群管理能力,内建负载均衡、强大的故障发现和自我修复能力,并提供了完备的管理工具,如开发、部署测试、运维监控等等。总之,Kubernetes是一个一站式的完备的分布式系统开发和支撑平台。

2. Kubernetes的优势

  第一点就是跟docker相仿,部署简单,大大节省了运维的成本,pod之间的资源是相互隔离的,不会各自的系统环境影响其他服务,只要轻松的修改image,并部署相应的service和deployment就可以实现服务部署应用、服务滚动升级等等。

  其次,Kubernetes全面拥抱微服务架构。将一个巨大的单体应用,分解成多个微服务,每一个微服务后又有多个实例,并且内嵌了负载均衡。让我们直接应用微服务解决复杂业务系统的架构问题。

  然后,就是我们可以随时随地的整体“搬迁”到公有云上。举个例子,后台的某个namespace下运行着各种应用的管控程序,此时我们只需要在web界面中,创建自己的项目,并在项目中部署自己的应用,后台就会根据我们的资源模板,在相应的namespace中运行我们部署的应用,这里由于使用的namespace可以做到完全的资源隔离。使部署更简单,让开发人员的重点都放在业务之上。

  最后,Kubernetes有超强的横向扩展能力,学习过hadoop的生态的朋友们一定知道,hdfs好处之一就是部署在廉价的机器上,并实现了冗余备份,更是支持横向的扩展,这些优点对于后起之秀的Kubernetes当然是统统继承下来,并且与之完善。Kubernetes可以做到不用修改代码就能平滑扩招到拥有上百个node的大规模集群。

老版本的架构

kubernetes中数据下载_操作系统


新版本的架构

kubernetes中数据下载_kubernetes中数据下载_02

3. Kubernetes案例演示

  在演示之前,由于大家可能对yaml语法有写模糊,小编这里先介绍一下yaml的基本语法,有助于大家理解下面案例的配置文件。

(1)yaml的语法介绍

  ① Yaml的语法规则
    - 大小写敏感
    - 使用缩进表示层级关系
    - 缩进时不允许使用 Tab 键,只允许使用空格
    - 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
    - “#” 表示注释
    - 在 yaml 里面,连续的项目(如:数组元素、集合元素)通过减号“-”来表示,map 结构 里面的键值对(key/value)用冒号“:”来分割。
  ② Yaml支持的三种数据结构
   1. 对象:对象的一组键值对,使用冒号结构表示。

#案例演示
animal: pets
hash:
  name: Steve 
  foo: bar
或者:hash: { name: Steve, foo: bar }

   2. 数组:一组连词线开头的行,构成一个数组。

#案例演示
- Cat 
- Dog 
- Goldfish
或者:[ 'Cat', 'Dog', 'Goldfish' ]

   3. 复合结构:对象和数组可以结合使用,形成复合结构。

#案例演示
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.30

  说了这么多的概念,相比大家对Kubernetes也是有所动心,那么小编这就带大家简单的部署一下Kubernetes,感受一下他的强大之处。
这里我们使用一个MySQL+Tomcat实现一个简单的web应用的部署。

(2)案例演示

  • 单机版的kubernetes集群搭建
#单机版的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;
  • 编写相应的yaml文件
#①  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
  • 创建相应的kubernetes资源对象
#执行以下命令
[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资源对象

kubernetes中数据下载_网络_03

#访问pod中的Tomcat服务:
[root@zy ~]# curl 192.168.130.130:30001

kubernetes中数据下载_kubernetes中数据下载_04


到此在单机版的kubernetes集群中,我们就部署成功一个Tomcat+MySQL服务。

4. Kubernetes的基础概念

  通过上面的案例大家也对Kubernetes有一定的了解,并且能简单的实际的操作一下Kubernetes,但是可能对其中的细节并不是很了解,就像什么是“rc”,为什么就能通过service将容器中的服务映射到宿主机中供外部访问等等,接下来小编就给大家介绍Kubernetes几个重中之重的基础概念。

(1) Master

  说道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中这个记录也会被删除。

(2) Node

  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上的工作负载转移到其他机器上,有利于集群的正常工作。

(3) pod

  pod是Kubernetes最基本最重要的概念,pod也是Kubernetes集群的负责均衡的最小单位。

kubernetes中数据下载_网络_05


  上面的图就是一个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。

kubernetes中数据下载_操作系统_06

(4) Label

  当时小编学习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“定向调度”的特性。

kubernetes中数据下载_mysql_07


   总之,使用label可以对对象创建多组标签,label和label selector共同构成了k8s系统中最核心的应用模型,使得被管理对象能够被精确地分组管理。

(5) replication controller—RC

  RC其实是一个定义了期望的场景,即声明某种pod的副本数量在任意时刻都符合某个预期的值,所以RC的定义包括以下几部分:

   - pod的期待的副本数

   - 用于筛选目标pod的label selector

   - 当pod的副本数小于预期数量,用于创建新的pod的pod模板

kubernetes中数据下载_kubernetes中数据下载_08


  如上图所示,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的滚动升级(优雅)

(6) deployment

  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进行新的发布。

(7) statefulSet

  在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相关的存储卷。

(8) service

  service也是kubernetes里最核心的资源对象之一,kubernetes的每一个service其实就是微服务架构中的一个“微服务”,由下图所示:

kubernetes中数据下载_网络_09

  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

(9) volume

  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服务。

(10) persistent volume---PV

  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自动回收失败

(11) namespace

  namespace是kubernetes系统中实现多租户的资源隔离。Namespace通过将集群内部的资源对象“分配”到不同的namespce中,形成逻辑上分组的不同项目、小组或者用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
  我们可以在定义资源对象是,在他的metadata中指定他属于的namespace:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
  namespace: xxxx

  同时在不同的namespace中,还能限定不同租户能占用的资源,如有CPU使用量、内存等等。

(12) annotation

  annotation与label类似,也使用key/value键值对的形式进行定义。不同的是,label具有严格的命名规则,他定义的是k8s对象的元数据,并且同于label selector。而annotation则是用户任意定义的“附加信息” ,以便于外部工具的查找。
  大体来说,annotation定义如下信息:
   - build信息、release信息、dicker镜像地址等等
   - 日志库、监控库、分析库等等资源库的地址信息
   - 程序调试工具信息,例如:工具名、版本号
   - 团队信息,如:联系电话、负责人名称、网址等

5. 部署中可能遇到的问题解决方案

(1)安装k8s报错:

[root@zy ~]# yum install -y etcd kubernetes

kubernetes中数据下载_网络_10


原因:机器上安装了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

(2)创建rc报错:

kubernetes中数据下载_kubernetes中数据下载_11

[root@zy yaml_file]# kubectl create -f mysql-rc.yaml

原因:k8s的认证问题。

kubernetes中数据下载_kubernetes中数据下载_12

[root@zy yaml_file]#systemctl  restart  kube-apiserver

(3)Error syncing pod, skipping: failed to "StartContainer" for "POD" with Image

原因:查看pod的创建的报错信息,发现是pod-infrastructure镜像下载失败,导致pod启动失败。
解决
pod-infrastructure镜像下载配置:

[root@zy yaml_file]# vim /etc/kubernetes/kubelet

发现:

kubernetes中数据下载_kubernetes中数据下载_13


发现国内是无法通过这个链接下载到pod-infrastructure镜像。

首先我们:

[root@zy yaml_file]# docker search pod-infrastructure

kubernetes中数据下载_mysql_14


下载我们需要的镜像:

[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

(4)运行docker容器时:报错oci runtime error: exec failed: container_linux.go

原因:环境是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

转载于:https://blog.51cto.com/14048416/2387054