• PA真人视讯

    PA真人视讯动态

    PA真人视讯愿与业内同行分享 助力各企业在大数据浪潮来临之际一起破浪前行

    PA真人视讯数据:kubernetes简介和实战

    在本文中,我们从技术细节上对kubernetes进行简单运用介绍,利用一些yaml脚本层面上实例告诉大家kubernetes基本概念。Kubernetes以及它呈现出的编程范式值得你去使用和整合到自己的技术栈中。

    kubernetes简单介绍
    1

    kubernetes起源

     

    Kubernetes最初认为是谷歌开源的容器集群管理系统,是Google多年大规模容器管理技术BorgOmega的开源版本。准确来说的话,kubernetes更是一个全新的平台,一个全新的平台管理工具,它是专门为jobservice设计。完全开放,20146月开始接受公开的commit,任何人都可以给予意见。由于kubernetes简化了开发、运维和管理负荷,越来越多的企业开始在生产环境使用,因此kubernetes得到了迅速的开展。

    2

    kubernetes功能

    • 基于容器的应用部署、维护和滚动升级

    • 负载均衡和服务发现

    • 跨机器和跨地区的集群调度

    • 自动伸缩

    • 无状态服务和有状态服务

    • 广泛的Volume支持

    • 插件机制保证扩展性

    3

    kubernetes核心组件

     

    Kubernetes主要由以下几个核心组件组成:

     

    • etcd保存了整个集群的状态;

    • apiserver给予了资源操作的唯一入口,并给予认证、授权、访问控制、API注册和发现等机制; 

    • controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等; 

    • scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上; 

    • kubelet负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理; 

    • Container runtime负责镜像管理以及Pod和容器的真正运行(CRI); 

    • kube-proxy负责为Service给予cluster内部的服务发现和负载均衡

    kubernetes环境部署

    如果只是为了分析kubernetes,可以使用minikube的方式进行单机安装,minikube实际就是本地创建了一个虚拟机,里面运行了kubernetes的一些必要的环境,相当于k8s的服务环境,创建pod,service,deployment等都是在里面进行创建和管理。在本文中,我使用kubeadm方式安装kubernetes 1.10.0,具体kubernetes部署步骤:

     

    • 使用kubeadm方式安装kubernetes 1.10.0

    • Kubernetes集群添加/删除Node

    • Kubernetes Dashboard1.8.3部署

    • k8s原生的集群监控方案(Heapster+InfluxDB+Grafana)

     

    请注意:上述环境只是测试环境,生产环境部署大同小异。

     

    kubernetes基本概念

    1

    Container

    Container(容器)是一种便携式、轻量级的操作系统级虚拟化技术。它使用namespace隔离不同的软件运行环境,并顺利获得镜像自包含软件的运行环境,从而使得容器可以很方便的在任何地方运行。由于容器体积小且启动快,因此可以在每个容器镜像中打包一个应用程序。这种一对一的应用镜像关系拥有很多好处。

     

    使用容器,不需要与外部的基础架构环境绑定,因为每一个应用程序都不需要外部依赖,更不需要与外部的基础架构环境依赖,完美解决了从开发到生产环境的一致性问题。容器同样比虚拟机更加透明,这有助于监测和管理。尤其是容器进程的生命周期由基础设施管理,而不是由容器内的进程对外隐藏时更是如此。最后,每个应用程序用容器封装,管理容器部署就等同于管理应用程序部署。在Kubernetes必须要使用Pod来管理容器,每个Pod可以包含一个或多个容器。

     

    2

    Pod

    关于Pod的概念主要有以下几点:

     

    • Podkubernetes中你可以创建和部署的最小也是最简的单位。一个Pod代表着集群中运行的一个进程;

    • Kubrenetes集群中Pod的使用方式;

    • Pod中如何管理多个容器

     

    理解Pod:

     

    上面已经说了“Podkubernetes中你可以创建和部署的最小也是最简的单位。一个Pod代表着集群中运行的一个进程。Pod中封装着应用的容器(有的情况下是好几个容器),存储、独立的网络IP,管理容器如何运行的策略选项。

     

    Pod代表着部署的一个单位:kubernetes中应用的一个实例,可能由一个或者多个容器组合在一起共享资源。

     

    请注意:Dockerkubernetes中最常用的容器运行时,但是Pod也支持其他容器运行时。

     

    Kubrenetes集群中Pod的两种使用方式:

     

    (1)一个Pod中运行一个容器“每个Pod中一个容器的模式是最常见的用法;在这种使用方式中,你可以把Pod想象成是单个容器的封装,kuberentes管理的是Pod而不是直接管理容器

     

    实战:创建一个nginx容器

     

    apiVersion: v1
    kind: Pod
    metadata:

      name:nginx-test
      labels:
        app: web
    spec:
      containers:
      – name:front-end
        image:nginx:1.7.9
        ports:
        -containerPort: 80
     
    创建Pod:
    kubectl create -f ./pod1-deployment
    查看Pod:
    kubectl get po
    查看Pod详细情况:
    kubectl describe po nginx-test

    进入到Pod(容器)内部:

    kubectl exec -it nginx-test  /bin/bash

    (2)在一个Pod中同时运行多个容器

     

    说明:在一个Pod中同时运行多个容器是一种比较高级的用法。只有当你的容器需要紧密配合协作的时候才考虑用这种模式。

     

    一个Pod中也可以同时封装几个需要紧密耦合互相协作的容器,它们之间共享资源。这些在同一个Pod中的容器可以互相协作成为一个service单位——一个容器共享文件,另一个“sidecar”容器来更新这些文件。Pod将这些容器的存储资源作为一个实体来管理。

     

    实战:在一个pod里放置两个容器:nginxredis

     

     
    apiVersion: v1
    kind: Pod
    metadata:

      name: rss-site
      labels:
        app: web
    spec:
      containers:
      – name:front-end
        image:nginx:1.7.9
        ports:
        -containerPort: 80
      – name:rss-reader
        image: redis
        ports:
        -containerPort: 88
     

    创建Pod:
    kubectl create -f ./test-deployment
    查看pod
    kubectl get po
    查看Pod详细情况
    kubectl describe po rss-site
    进入front-end内部:
    kubectl exec -it rss-site  -c front-end /bin/bash
    进入rss-reade内部:
    kubectl exec -it rss-site  -c rss-reader /bin/bash

    以上是关于Pod的简单介绍。

     

    3
    Node

    NodePod真正运行的主机,可以物理机,也可以是虚拟机。为了管理Pod,每个Node节点上至少要运行container runtime(比如docker或者rkt)、kubeletkube-proxy服务。

    4

    Namespace

     

     

    Namespace对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。常见的pods, services, replication controllersdeployments等都是属于某一个namespace的(默认是default),而node, persistentVolumes等则不属于任何namespace

     

    5

    Deployment

     

    我们既然有Pod了,为什么还要使用Deployment呢?这是因为实际工作中,我们很少会直接在kubernetes中创建单个Pod。因为Pod的生命周期是短暂的,用后即焚的实体。DeploymentPodReplicaSet给予了一个声明式定义(declarative)方法,用来替代以前的ReplicationController来方便的管理应用。你只需要在Deployment中描述想要的目标状态是什么,Deploymentcontroller就会帮你将PodReplicaSet的实际状态改变到你的目标状态。你可以定义一个全新的Deployment来创建ReplicaSet或者删除已有的Deployment并创建一个新的来替换。

     

    什么是复制控制器(ReplicationController,RC

     

    RCK8s集群中最早的保证Pod高可用的API对象。顺利获得监控运行中的Pod来保证集群中运行指定数目的Pod副本。指定的数目可以是多个也可以是1个;少于指定数目,RC就会启动运行新的Pod副本;多于指定数目,RC就会杀死多余的Pod副本。即使在指定数目为1的情况下,顺利获得RC运行Pod也比直接运行Pod更明智,因为RC也可以发挥它高可用的能力,保证永远有1Pod在运行。RCK8s较早期的技术概念,只适用于长期伺服型的业务类型,比如控制小机器人给予高可用的Web服务。

     

    什么是副本集(Replica Set,RS

     

    RS是新一代RC,给予同样的高可用能力,区别主要在于RS后来居上,能支持更多种类的匹配模式。副本集对象一般不单独使用,而是作为Deployment的理想状态参数使用。

     

    Deployment典型的应用场景

     

    • 定义Deployment来创建PodReplicaSet

    • 滚动升级和回滚应用;如果当前状态不稳定,回滚到之前的Deployment revision。每次回滚都会更新Deploymentrevision

    • 扩容和缩容,扩容Deployment以满足更高的负载

    • 暂停和继续Deployment,暂停Deployment来应用PodTemplateSpec的多个修复,然后恢复上线

     

    实战Deployment

     

    比如,我们这里定义一个简单的nginx应用:

    apiVersion:extensions/v1beta1
    kind: Deployment
    metadata:

        name:nginx-test
        namespace:test
    spec:
        replicas: 3
        template:
           metadata:
               labels:
                   app: nginx
            spec:
               containers:
                  -name: nginx
                   image: nginx:1.7.9
                    ports:
                   – containerPort: 80 
    创建deploy
    kubectl create -f ./nginx-deployment
    查看deploy
    kubectl get deploy –namespace=test
    查看rs(副本集)
    kubectl get rs –namespace=test
    查看pods(容器组)
    kubectl get po –namespace=test 

    关于Deployment的应用还有很多,如:扩容、缩容、滚动升级、回滚应用等,这里由于篇幅的问题不再一一介绍。

     5 

    Label

     

    Label是识别Kubernetes对象的标签,以key/value的方式附加到对象上(key最长不能超过63字节,value可以为空,也可以是不超过253字节的字符串)。

     

    Label不给予唯一性,并且实际上经常是很多对象(如Pods)都使用相同的label来标志具体的应用。Label定义好后其他对象可以使用Label Selector来选择一组相同label的对象(比如ReplicaSetServicelabel来选择一组Pod)。Label Selector支持以下几种方式:

    • 等式,如app=nginxenv!=production

    • 集合,如env in(production, qa)

    • 多个label(它们之间是AND关系),如app=nginx,env=test

     

    6

    Service Account

     

    Service account作用

     

    Service account是为了方便Pod里面的进程调用Kubernetes API或其他外部服务。

     

    Serviceaccount使用场景

     

    运行在pod里的进程需要调用Kubernetes API以及非Kubernetes API的其它服务。Service Account它并不是给kubernetes集群的用户使用的,而是给pod里面的进程使用的,它为pod给予必要的身份认证。

     

    User account区别

     

    • User account是为人设计的,而service account则是为了Pod中的进程

    • User account是跨namespace的,而service account则是仅局限它所在的namespace

    实战命名空间

    apiVersion: v1
    kind: Namespace
    metadata:

      name:datagrand
      labels:
        name: test 
    创建namespace:test
    kubectl create -f ./test.yaml 
    查看命名空间testsa
    kubectl get sa -n test
    查看命名空间test生成的default
    kubectl get sa default -o yaml -n test
    我们可以创建Deployment时,使用这个test命名空间了,如上例Deployment实战。   

    Service Account鉴权

     

    Service Account为服务给予了一种方便的认知机制,但它不关心授权的问题。可以配合RBAC来为Service Account鉴权:

    • 配置–authorization-mode=RBAC–runtime-config=rbac.authorization.k8s.io/v1alpha1

    • 配置–authorization-rbac-super-user=admin

    • 定义Role、ClusterRole、RoleBindingClusterRoleBinding

     

    实战鉴权

     

    我们在Kubernetes Dashboard1.8.3部署中,碰到首次登入出现访问权限报错的问题,原因就是ServiceAccount的创建问题。

     
    apiVersion:rbac.authorization.k8s.io/v1beta1
    kind: ClusterRoleBinding
    metadata:

          name:kubernetes-dashboard
          labels:
             k8s-app: kubernetes-dashboard
    roleRef:
          apiGroup:rbac.authorization.k8s.io
          kind:ClusterRole
          name:cluster-admin
    subjects:
        – kind:ServiceAccount
          name:kubernetes-dashboard
          namespace:kube-system  
    7

    Secret

     

    Secret介绍

     

    Secret解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中。Secret可以以Volume或者环境变量的方式使用。

     

    Secret类型

    • Opaque(default):任意字符串,base64编码格式的Secret,用来存储密码、密钥等

    • kubernetes.io/service-account-token:作用于ServiceAccount,就是kubernetesService Account中所说的。即用拜访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod/run/secrets/kubernetes.io/serviceaccount目录中

    • kubernetes.io/dockercfg:作用于Docker registry,用户下载docker镜像认证使用。用来存储私有docker registry的认证信息

     

    实战Opaque Secret类型

     
    Opaque类型的数据是一个map类型,要求valuebase64编码格式:
    创建admin账户
    echo -n “admin” | base64
    YWRtaW4=
    echo -n “1f2d1e2e67df” | base64
    MWYyZDFlMmU2N2Rm 
     
    创建secret.yaml
    cat >> secrets.yml << EOF
    apiVersion: v1
    kind: Secret
    metadata:

        name:mysecret
    type: Opaque
    data:

        password:MWYyZDFlMmU2N2Rm
        username:YWRtaW4=  
     
    创建secret
    kubectl create -f secrets.yml  
    查看secret运行状态
    kubectl get secret –all-namespaces   

    Secret使用方式

     

    • Volume方式

    • 以环境变量方式

     

    实战Secret使用Volume方式

     
    apiVersion: v1
    kind: Pod
    metadata:

      name: mypod
      labels:
        name: wtf
    spec:
      volumes:
      – name:secrets
        secret:
         secretName: mysecret
      containers:
      – image:nginx:1.7.9
        name: nginx
       volumeMounts:
        – name:secrets
          mountPath:”/etc/secrets”
          readOnly:true
        ports:
        – name: cp
         containerPort: 5432
          hostPort:5432   

    说明:这样就可以顺利获得文件的方式挂载到容器内,在/etc/secrets目录下回生成这个文件。

     

    实战Secret使用环境变量方式

     
    apiVersion:extensions/v1beta1
    kind: Deployment
    metadata:

      name:wordpress-deployment
    spec:
      replicas: 2
      template:
        metadata:
          labels:
            app:wordpress
        spec:
         containers:
          – name:”wordpress”
            image:”wordpress:latest”
            ports:
            -containerPort: 80
            env:
            – name:WORDPRESS_DB_USER
             valueFrom:
               secretKeyRef:
                 name: mysecret
                 key: username
            – name:WORDPRESS_DB_PASSWORD
             valueFrom:
               secretKeyRef:
                 name: mysecret
                 key: password
     
    查看Pod运行状态
    kubectl get po
    NAME                                                         READY     STATUS   RESTARTS     AGE
    wordpress-deployment-6b569fbb7d-8qcpg   1/1      Running   0                   2m
    wordpress-deployment-6b569fbb7d-xwwkg   1/1      Running   0                 2m    
     
    进入容器内部查看环境变量
    kubectl exec -itwordpress-deployment-694f4c79b4-cpsxw /bin/bash
    root@wordpress-deployment-694f4c79b4-cpsxw:/var/www/html#env
    WORDPRESS_DB_USER=admin
    WORDPRESS_DB_PASSWORD=1f2d1e2e67df   
    8

    ConfigMap

    Configure说明

     

    ConfigMaps允许你将配置文件、命令行参数或环境变量中读取的配置信息与docker image分离,以保持集装箱化应用程序的便携性。即ConfigMapAPI给我们给予了向容器中注入配置信息的机制。

     

    理解ConfigMapsPods 

     

    ConfigMap API资源用来保存key-value pair配置数据,这个数据可以在pods里使用,或者被用来为像controller一样的系统组件存储配置数据。虽然ConfigMapSecrets类似,但是ConfigMap更方便的处理不含敏感信息的字符串。

     

    注意:ConfigMaps不是属性配置文件的替代品。ConfigMaps只是作为多个properties文件的引用。你可以把它理解为Linux系统中的/etc目录,专门用来存储配置文件的目录。

     

    实战创建ConfigMap

     
    kind: ConfigMap
    apiVersion: v1
    metadata:

     creationTimestamp: 2016-02-18T19:14:38Z
      name:example-config
      namespace:default
      data:
       example.property.1: hello
       example.property.2: world
       example.property.file: |-
         property.1=value-1
         property.2=value-2
         property.3=value-3

    data一栏包括了配置数据,ConfigMap可以被用来保存单个属性,也可以用来保存一个配置文件。配置数据可以顺利获得很多种方式在Pods里被使用。ConfigMaps可以被用来:

     

    • 设置环境变量的值

    • 在容器里设置命令行参数

    • 在数据卷里面创建config文件

    9

    Volum

     

    为什么需要Volume

     

    容器磁盘上文件的生命周期是短暂的,这就使得在容器中运行重要应用时出现一些问题。比如,当容器崩溃时,kubelet会重启它,但是容器中的文件将丢失容器以干净的状态(镜像最初的状态)重新启动。

     

    其次,在Pod中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes中的Volume抽象就很好的解决了这些问题。

     

    Volume背景

     

    Docker中也有一个volume的概念,尽管它稍微宽松一些,管理也很少。在Docker中,卷就像是磁盘或是另一个容器中的一个目录。它的生命周期不受管理,直到最近才有了local-disk-backed卷。Docker现在给予了卷驱动程序,但是功能还非常有限(例如Docker1.7只允许每个容器使用一个卷驱动,并且无法给卷传递参数)。

     

    Kubernetes中的卷有明确的寿命——与封装它的Pod相同。所以,卷的生命比Pod中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当Pod不再存在时,卷也将不复存在。也许更重要的是,Kubernetes支持多种类型的卷,Pod可以同时使用任意数量的卷。要使用卷,需要为pod指定为卷(spec.volumes字段)以及将它挂载到容器的位置(spec.containers.volumeMounts字段)。

     

    Volume类型

     

    Kubernetes支持以下类型的卷:

     
    awsElasticBlockStore、azureDisk、azureFile、cephfs、csi、downwardAPI、emptyDir、fc (fibre channel)、flocker、gcePersistentDisk、gitRepo、glusterfs、hostPath、iscsi、local、nfs、persistentVolumeClaim、projected、portworxVolume、quobyte、rbd、scaleIO、secret、storageos、vsphereVolume

    K8S的存储系统分类

     

    K8S的存储系统从基础到高级大致分为三个层次:普通Volume,Persistent Volume和动态存储供应。

     

    普通Volume

    最简单的普通Volume是单节点Volume。它和Docker的存储卷类似,使用的是Pod所在K8S节点的本地目录。

     

    persistent volume

     

    它和普通Volume的区别是什么呢?

     

    普通Volume和使用它的Pod之间是一种静态绑定关系,在定义Pod的文件里,同时定义了它使用的VolumeVolumePod的附属品,我们无法单独创建一个Volume,因为它不是一个独立的K8S资源对象。而Persistent Volume简称PV是一个K8S资源对象,所以我们可以单独创建一个PV它不和Pod直接发生关系,而是顺利获得Persistent Volume Claim,简称PVC来实现动态绑定。Pod定义里指定的是PVC,然后PVC会根据Pod的要求去自动绑定合适的PVPod使用。

     

    PV的访问模式有三种:

    • ReadWriteOnce:是最基本的方式,可读可写,但只支持被单个Pod挂载

    • ReadOnlyMany:可以以只读的方式被多个Pod挂载

    • ReadWriteMany:这种存储可以以读写的方式被多个Pod共享。比较常用的是NFS

     

    一般来说,PVPVC的生命周期分为5个阶段:

     

    • Provisioning,即PV的创建,可以直接创建PV(静态方式),也可以使用StorageClass动态创建

    • Binding,将PV分配给PVC

    • Using,Pod顺利获得PVC使用该Volume

    • Releasing,Pod释放Volume并删除PVC

    • Reclaiming,回收PV,可以保留PV以便下次使用,也可以直接从云存储中删除

     

    根据这5个阶段,Volume的状态有以下4种:

    • Available:可用

    • Bound:已经分配给PVC

    • Released:PVC解绑但还未执行回收策略

    • Failed:发生错误

     

    变成ReleasedPV会根据定义的回收策略做相应的回收工作。有三种回收策略:

    • Retain就是保留现场,K8S什么也不做,等待用户手动去处理PV里的数据,处理完后,再手动删除PV

    • Delete K8S会自动删除该PV及里面的数据

    • Recycle K8S会将PV里的数据删除,然后把PV的状态变成Available,又可以被新的PVC绑定使用

    在实际使用场景里,PV的创建和使用通常不是同一个人。这里有一个典型的应用场景:管理员创建一个PV池,开发人员创建PodPVC,PVC里定义了Pod所需存储的大小和访问模式,然后PVC会到PV池里自动匹配最合适的PVPod使用。

     

    前面在介绍PV的生命周期时,提到PV的供给有两种方式,静态和动态。其中动态方式是顺利获得StorageClass来完成的,这是一种新的存储供应方式。

     

    使用StorageClass有什么好处呢?除了由存储系统动态创建,节省了管理员的时间,还有一个好处是可以封装不同类型的存储供PVC选用。在StorageClass出现以前,PVC绑定一个PV只能根据两个条件,一个是存储的大小,另一个是访问模式。在StorageClass出现后,等于增加了一个绑定维度。

     

    比如这里就有两个StorageClass,它们都是用谷歌的存储系统,但是一个使用的是普通磁盘,我们把这个StorageClass命名为slow。另一个使用的是SSD,我们把它命名为fast。在PVC里除了常规的大小、访问模式的要求外,还顺利获得annotation指定了Storage Class的名字为fast,这样这个PVC就会绑定一个SSD,而不会绑定一个普通的磁盘。限于篇幅问题,这里简单说一下emptyDir、nfs、PVPVC

     

    实战–emptyDir

     

    emptyDir说明

     

    EmptyDir类型的volume创建于pod被调度到某个宿主机上的时候,而同一个pod内的容器都能读写EmptyDir中的同一个文件。一旦这个pod离开了这个宿主机,EmptyDir中的数据就会被永久删除。所以现在EmptyDir类型的volume主要用作临时空间,比如Web服务器写日志或者tmp文件需要的临时目录。

     

    apiVersion: v1
    kind: Pod
    metadata:

    labels:
    name:test-emptypath
    role: master
    name: test-emptypath
    spec:
    containers:
    – name:test-emptypath
    image:nginx:1.7.9
    volumeMounts:
    – name:log-storage
    mountPath:/tmp/
    volumes:
    – name:log-storage
    emptyDir: {}

    实战使用共享卷的标准多容器Pod

     
    apiVersion: v1
    kind: Pod
    metadata:

      name:datagrand
    spec:
      containers:
      – name: test1
        image:nginx:1.7.9
       volumeMounts:
        – name:log-storage
          mountPath:/usr/share/nginx/html
      – name: test2
        image:centos
       volumeMounts:
        – name:log-storage
          mountPath:/html
        command:[“/bin/sh”,”-c”]
        args:
        – whiletrue;do
          data>> /html/index.html;
          sleep 1;
          done
      volumes:
        – name:log-storage
          emptyDir:{}  

    简单解释下上面的内容:在这个例子中,我们定义了一个名为HTML的卷。它的类型是emptyDir,这意味着当一个Pod被分配到一个节点时,卷先被创建,并只要Pod在节点上运行时,这个卷仍存在。正如名字所说,它最初是空的。

     

    第一容器运行nginx的服务器并将共享卷挂载到目录/ usr /share/ nginx /html。第二容器使用centos的镜像,并将共享卷挂载到目录/HTML。每一秒,第二容器添加当前日期和时间到index.html文件中,它位于共享卷。当用户发出一个HTTP请求到Pod,nginx的服务器读取该文件并将其传递给响应请求的用户。

     

    实战–NFS

     

    NFS卷说明

     

    nfs卷允许将现有的NFS(网络文件系统)共享挂载到你的容器中。不像emptyDir,当删除Pod时,nfs卷的内容被保留,卷仅仅是被卸载。这意味着NFS卷可以预填充数据,并且可以在pod之间“切换数据。NFS可以被多个写入者同时挂载。

     

    NFS卷使用注意

     

    • 请先部署好自己的NFS服务

    • 在使用共享之前,必须运行自己的NFS服务器并运行共享

     

    实战pod内的文件共享

     
    apiVersion: v1
    kind: Pod
    metadata:

      name:nginx-test
      labels:
        app: nginx
    spec:
      containers:
      – name: nginx
        image:nginx:1.7.9
        ports:
        -containerPort: 80
         volumeMounts:
        #Mount thepath to the container
          – mountPath:”/tmp/”
            name:pv0003
      volumes:
      – name: pv0003
        nfs:
        #fixed:This ip is the addressof the nfs server
          server:192.168.246.169
        #fixed:This path is sharedexternally by the nfs server
          path:”/data”   

    实战PVPVC

     

    nfs作为k8s的网络存储驱动,可以满足持久存储业务的需求,支持多节点读写。下面是两个Pod同时使用一个持久性volume实例。

     
    #创建PV
    apiVersion: v1
    kind: PersistentVolume
    metadata:

      name: nfs-pv
    spec:
      capacity:
        storage: 4Gi
      accessModes:
        -ReadWriteMany
      nfs:
        server:192.168.246.168  ##NFS服务器的ip地址
        path:”/data”  ##NFS服务器上的共享目录
    #创建PVC
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:

      name: nfs-pvc
    spec:
      accessModes:
        – ReadWriteMany
     storageClassName: “”
      resources:
        requests:
          storage:3Gi   
    #创建Deployment
    apiVersion: apps/v1
    kind: Deployment
    metadata:

      name:nginx-deployment
      labels:
        app: nginx
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app:nginx
        spec:
         containers:
          – name:nginx
            image:nginx:1.7.9
           volumeMounts:
              -mountPath: “/wtf”
               name: datadir
          volumes:
          – name:datadir
           persistentVolumeClaim:
             claimName: nfs-pvc    -containerPort: 80
    10

    Service

     

    Kubernetes Pod是有生命周期的,它们可以被创建,也可以被销毁,然而一旦被销毁生命就永远结束。顺利获得ReplicaSets能够动态地创建和销毁Pod(例如,需要进行扩缩容,或者执行滚动升级)。每个Pod都会获取它自己的IP地址,即使这些IP地址不总是稳定可依赖的。这会导致一个问题:在Kubernetes集群中,如果一组Pod(称为backend)为其它Pod(称为frontend)给予服务,那么那些frontend该如何发现,并连接到这组Pod中的哪些backend呢?

     

    什么是Service

     

    Kubernetes Service定义了这样一种抽象:一个Pod的逻辑分组,一种可以访问它们的策略——通常称为微服务。这一组Pod能够被Service访问到,通常是顺利获得Label Selector(查看下面分析,为什么可能需要没有selectorService)实现的。

     

    举个例子,考虑一个图片处理backend,它运行了3个副本。这些副本是可互换的—— frontend不需要关心它们调用了哪个backend副本。然而组成这一组backend程序的Pod实际上可能会发生变化,frontend客户端不应该也没必要知道,而且也不需要跟踪这一组backend的状态。Service定义的抽象能够解耦这种关联。

     

    Kubernetes集群中的应用,Kubernetes给予了简单的Endpoints API,只要Service中的一组Pod发生变更,应用程序就会被更新。对非Kubernetes集群中的应用,Kubernetes给予了基于VIP的网桥的方式访问Service,再由Service重定向到backend Pod

     

    定义Service

     

    selector的单端口Service

     

    一个ServiceKubernetes中是一个REST对象,和Pod类似。像所有的REST对象一样,Service定义可以基于POST方式,请求apiserver创建新的实例。例如,假定有一组Pod,它们对外暴露了9376端口,同时还被打上“app=MyApp”标签。

     
    kind: Service
    apiVersion: v1
    metadata:

      name:my-service
    spec:
      selector:
        app: MyApp
      ports:
        – protocol:TCP
          port: 80
          targetPort: 9376  

    上述配置将创建一个名称为“my-service”Service对象,它会将请求代理到使用TCP端口9376,并且具有标签“app=MyApp”Pod上。这个Service将被指派一个IP地址(通常称为“Cluster IP”),它会被服务的代理使用(见下面)。该Serviceselector将会持续评估,处理结果将被POST到一个名称为“my-service”Endpoints对象上。

     

    需要注意的是,Service能够将一个接收端口映射到任意的targetPort默认情况下,targetPort将被设置为与port字段相同的值。可能更有趣的是,targetPort可以是一个字符串,引用了backend Pod的一个端口的名称。但是,实际指派给该端口名称的端口号,在每个backend Pod中可能并不相同。对于部署和设计Service,这种方式会给予更大的灵活性。例如,可以在backend软件下一个版本中,修改Pod暴露的端口,并不会中断客户端的调用。

     

    Kubernetes Service能够支持TCPUDP协议,默认TCP协议。

     

    selector的多端口Service

     

    很多Service需要暴露多个端口。对于这种情况,Kubernetes支持在Service对象中定义多个端口。当使用多个端口时,必须给出所有的端口的名称,这样Endpoint就不会产生歧义,例如:

     
    kind: Service
    apiVersion: v1
    metadata:

      name:my-service
    spec:
        selector:
          app: MyApp
        ports:
          – name:http
           protocol: TCP
            port: 80
           targetPort: 9376
          – name:http
           protocol: TCP
            port:443
           targetPort: 9377  
    没有selectorService

     

    Service抽象了该如何访问Kubernetes Pod,但也能够抽象其它类型的backend,例如:

     

    • 希望在生产环境中使用外部的数据库集群,但测试环境使用自己的数据库。

    • 希望服务指向另一个Namespace中或其它集群中的服务。

    • 正在将工作负载转移到Kubernetes集群,和运行在Kubernetes集群之外的backend

     

    根据以上的应用场景,我们都能够定义没有selectorService,如下:

     
    kind: Service
    apiVersion: v1
    metadata:

      name:my-service
    spec:
      ports:
        – protocol:TCP
          port: 80
         targetPort: 9376  

    由于这个Service没有selector,就不会创建相关的Endpoints对象。可以手动将Service映射到指定的Endpoints:

     
    kind: Endpoints
    apiVersion: v1
    metadata:

      name:my-service
    subsets:
      – addresses:
          – ip:10.0.0.3  ##Endpoint IP = PodIP +ContainerPort
        ports:
          – port:9376  

    注意:Endpoint IP地址不能是loopback(127.0.0.0/8)、link-local(169.254.0.0/16)、或者link-local多播(224.0.0.0/24)。访问没有selectorService,与有selectorService的原理相同。请求将被路由到用户定义的Endpoint(该示例中为10.0.0.3:9376)。

     

    发布服务——服务类型

     

    对一些应用(如Frontend)的某些部分,可能希望顺利获得外部(Kubernetes集群外部)IP地址暴露ServiceKubernetes ServiceTypes允许指定一个需要的类型的Service,默认是ClusterIP类型。Type的取值以及行为如下:

     

    • ClusterIP顺利获得集群的内部IP暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的ServiceType

    • NodePort顺利获得每个Node上的IP和静态端口(NodePort)暴露服务。NodePort服务会路由到ClusterIP服务,这个ClusterIP服务会自动创建。顺利获得请求:,可以从集群的外部访问一个NodePort服务。

    • LoadBalancer使用云给予商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort服务和ClusterIP服务。

    • ExternalName顺利获得返回CNAME和它的值,可以将服务映射到externalName字段的内容(例如,foo.bar.example.com)。没有任何类型代理被创建,这只有Kubernetes 1.7或更高版本的kube-dns才支持。

     
    NodePort类型

     

    如果设置type的值为“NodePort”,Kubernetes master将从给定的配置范围内(默认:30000-32767)分配端口,每个Node将从该端口(每个Node上的同一端口)代理到Service。该端口将顺利获得Servicespec.ports[*].nodePort字段被指定。如果需要指定的端口号,可以配置nodePort的值,系统将分配这个端口,否则调用API将会失败(比如,需要关心端口冲突的可能性)。

     

     

    kubernetes实战–edusoho平台创建

    1

    文件目录结构

     
    # pwd
    /data
    # tree -L 3
    .
    ├── mysql
    │   ├── conf
    │   │   └── my.cnf
    │   └── data
    │      ├── auto.cnf
    │      ├── edusoho
    │      ├── ibdata1
    │      ├── ib_logfile0
    │      ├── ib_logfile1
    │      ├── mysql
    │      └── performance_schema
    ├── nginx
    │   ├── conf
    │   │   └── nginx.conf
    │   ├── edusoho
    │   │   ├── api
    │   │   ├── app
    │   │   ├── bootstrap
    │   │   ├── plugins
    │   │   ├── src
    │   │   ├── vendor
    │   │   ├── vendor_user
    │   │   └── web
    │   └── log
    │      └── error.log
    ├── php
    │   ├── log
    │   │   └── php-fpm.log
    │   ├── php-fpm.conf
    │   ├── php.ini
    │   └── www.conf
    2

    Podyaml文件

     

    apiVersion: v1
    kind: Pod
    metadata:
      name:lamp-edusoho
      labels:
        app:lamp-edusoho
    restartPolicy: Always
    spec:
      containers:
      – name: nginx
        abels:
          app:lamp-nginx
        image:dockerhub.hiwan88.com/global/nginx:v1
        ports:
        -containerPort: 80
       volumeMounts:
          – name:datadir
           mountPath: “/var/log/nginx/error.log”
            subPath:./nginx/log/error.log
          – name:datadir
           mountPath: “/etc/nginx/nginx.conf”
            subPath:./nginx/conf/nginx.conf
          – name:datadir
           mountPath: “/usr/share/nginx/html”
            subPath:./nginx/edusoho
      – name: php
        image:dockerhub.hiwan88.com/global/php:v1
        ports:
        -containerPort: 9000
       volumeMounts:
          -mountPath: /usr/local/php/etc/php-fpm.conf
            name:datadir
            subPath:./php/php-fpm.conf
          -mountPath: /usr/local/php/etc/php-fpm.d/www.conf
            name:datadir
            subPath:./php/www.conf
          -mountPath: /usr/local/php/etc/php.ini
            name:datadir
            subPath:./php/php.ini
          -mountPath: /usr/local/php/var/log/php-fpm.log
            name:datadir
            subPath:./php/log/php-fpm.log
          -mountPath: /usr/share/nginx/html
            name:datadir
            subPath:./nginx/edusoho
      – name: mysql
        image:dockerhub.hiwan88.com/global/mysql:5.6
        ports:
        -containerPort: 3306
        env:
          – name:MYSQL_ROOT_PASSWORD
            value:”123456″
          – name:MYSQL_DATABASE
            value:”edusoho”
          – name:MYSQL_USER
            value:”edusoho”
          – name:MYSQL_PASSWORD
            value:”edusoho”
        args:[‘–character-set-server=utf8’]
       volumeMounts:
          – name:datadir
           mountPath: “/var/lib/mysql”
            subPath:./mysql/data
          – name:datadir
           mountPath: “/etc/my.cnf”
            subPath:./mysql/conf/my.cnf
      volumes:
      – name:datadir
        persistentVolumeClaim:
          claimName:nfs-pvc
    3

    PVyaml文件

     

    apiVersion: v1
    kind: PersistentVolume
    metadata:

    name: nfs-pv
    spec:
    capacity:
    storage: 4Gi
    accessModes:
    -ReadWriteMany
    nfs:
    server:192.168.246.168 
    ##NFS服务器的ip地址
    path:”/data” 
    ##NFS服务器上的共享目录

    4

     

    PVCyaml文件

     
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:

      name: nfs-pvc
    spec:
      accessModes:
        -ReadWriteMany
     storageClassName: “”
      resources:
        requests:
          storage:3Gi
    5

    Serviceyaml文件

     

    apiVersion: v1
    kind: Service
    metadata:

      name: edusoho
      labels:
        app: edusoho
    spec:
      type: NodePort
      ports:
      – port: 80
        nodePort:32756
      selector:
        app:lamp-edusoho
    6

    命令汇总

     

    查看Pod
    kubectl get po -o wide
    查看Service
    kubectl get svc
    进入容器内部某个应用,如这里的nginx
    kubectl exec -it lamp-edusoho -c nginx /bin/bash
    7

     

    访问安装Edusoho平台

    http://192.168.246.168:32756/install/start-install.php


    说明:这里的192.168.246.168kubernetesnode节点IP,32756Service中定义的nodePort

     

    参考文档
    • kubernetes概念–Service:http://kubernetes.io/docs/concepts/services-networking/service/

    • kubernetes官网教程:http://kubernetes.io/docs/tutorials/

    • Kubernetes中的Persistent Volume解析:http://jimmysong.io/posts/kubernetes-persistent-volume/

    A

    BOUT

    关于作者

    吴腾飞:PA真人视讯数据运维工程师 ,负责PA真人视讯数据系统平台和应用业务的快速部署、监控、优化及维护,设计并研发自动化运维工具和平台,数据库的日常维护。

    对自动化运维,Docker容器、虚拟化技术和容器编排kubernetes相关领域有浓厚兴趣。