kubernetes基本概念和术语

在Kubernetes中,几乎所有的概念,包括Master、Node、Pod、Label、Namespace、Volume等都可以看作是一种“资源对象”。
从这个角度上来说,Kubernetes是一个高度自动化的资源控制系统它通过对比etcd中保存的“资源期望状态”和当前环境的“资源实际状态”
以此来实现自动控制和自动纠错的功能。

1.Master

Master是Kubernetes集群的控制节点,每个kubernetes集群至少有一个Master节点,
它负责整个集群的控制和管理,几乎所有的kubectl的命令都是同时Master节点来执行的。
Master节点也可以参与实际任务的执行,但是并不建议这样做,因为master节点必须保证高可用,
一旦master节点宕机,那么整个集群都会处于停滞状态。生产环境建议使用3台独立的服务器作为Master节点。

Master节点的主要组件包括:APIServer、Controller-Manager、Scheduler,还有kubelet、kubectl、etcd等组件。
这些组件会以进程的形式展开。
  APIServer:整个集群的唯一入口,也是连接etcd的唯一入口,所有对资源对象进行的操作都必须通过这个组件来展开。
    所有组件的操作也必须通过APIServer这个组件来实现。
  Controller-Manager:所有资源对象的自动化控制中心,比如自动启动和删除容器来维护容器的数量。
  Scheduler:负责资源调度,也就是将Pod分配到具体的Node上。

2.Node

在Kubernetes集群中,除了master节点之外就是Node节点,当然Etcd集群除外。
Node节点是Kubernetes的工作负载节点,每个Node节点都会被Master分配一些工作任务,
当Node节点宕机的时候其上的工作任务会被转移到其它节点上去。

Node节点可以在运行期间动态的加入到Kubernetes集群中。
在默认情况下,Node节点上的kubelet组件会自动注册到master节点,
一旦被纳入到集群的范围,Kubelet就会定期向master节点汇报自身的情况。比如CPU内存的使用情况。
如果master在一定时间之内没有收到Node节点的报备信息,Node节点就会被标记为不可用
随后master节点就会进行“工作负载转移”的自动化流程。

root@VM-16-6-ubuntu:~/test# kubectl get nodes
NAME             STATUS    ROLES     AGE       VERSION
vm-0-3-ubuntu    Ready     <none>    2d        v1.10.2
vm-16-6-ubuntu   Ready     master    2d        v1.10.2
vm-16-8-ubuntu   Ready     <none>    2d        v1.10.2

查看某一个Node的详细信息

root@VM-16-6-ubuntu:~# kubectl describe node/vm-0-3-ubuntu
Name:               vm-0-3-ubuntu  #主机名称
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64  #标签
                    beta.kubernetes.io/os=linux
                    kubernetes.io/hostname=vm-0-3-ubuntu
Annotations:        node.alpha.kubernetes.io/ttl=0  #注解
                    volumes.kubernetes.io/controller-managed-attach-detach=true
CreationTimestamp:  Wed, 19 Jun 2019 11:59:12 +0800  #创建时间
Taints:             <none>
Unschedulable:      false
Conditions:
  Type                 Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----                 ------  -----------------                 ------------------                ------                       -------
  NetworkUnavailable   False   Wed, 19 Jun 2019 11:59:38 +0800   Wed, 19 Jun 2019 11:59:38 +0800   WeaveIsUp                    Weave pod has set this
  OutOfDisk            False   Sun, 23 Jun 2019 19:24:09 +0800   Sat, 22 Jun 2019 04:16:58 +0800   KubeletHasSufficientDisk     kubelet has sufficient disk space available
  MemoryPressure       False   Sun, 23 Jun 2019 19:24:09 +0800   Sat, 22 Jun 2019 04:16:58 +0800   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure         False   Sun, 23 Jun 2019 19:24:09 +0800   Sat, 22 Jun 2019 04:16:58 +0800   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure          False   Sun, 23 Jun 2019 19:24:09 +0800   Wed, 19 Jun 2019 11:59:12 +0800   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready                True    Sun, 23 Jun 2019 19:24:09 +0800   Sat, 22 Jun 2019 04:16:58 +0800   KubeletReady                 kubelet is posting ready status. AppArmor enabled
Addresses:
  InternalIP:  172.27.0.3  #主机地址与主机名
  Hostname:    vm-0-3-ubuntu
Capacity(容量):  #描述Node可用的系统资源
 cpu:                1  #可用的CPU核数
 ephemeral-storage:  51474044Ki
 hugepages-2Mi:      0
 memory:             884964Ki  #可用内存数量
 pods:               110  #最大可调度的Pod数量
Allocatable(可分配的容量):  #系统剩余可分配的资源
 cpu:                1
 ephemeral-storage:  47438478872
 hugepages-2Mi:      0
 memory:             782564Ki
 pods:               110
System Info:  #系统信息
 Machine ID:                 c5f311a0edd169fa9fb1933c58105309
 System UUID:                5F71CCD1-781D-4FC3-BC67-630A2127EC53  #系统UUID
 Boot ID:                    2e3bcb7a-95dd-47c5-81ac-1ffad0bd9ba6
 Kernel Version:             4.4.0-130-generic  #Linux内核版本
 OS Image:                   Ubuntu 16.04.1 LTS  #系统版本
 Operating System:           linux
 Architecture:               amd64
 Container Runtime Version:  docker://17.3.2
 Kubelet Version:            v1.10.2  #kubelet核kube-proxy信息
 Kube-Proxy Version:         v1.10.2
ExternalID:                  vm-0-3-ubuntu  #正在运行的Pod列表概要信息
Non-terminated Pods:         (3 in total)
  Namespace                  Name                                  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------                  ----                                  ------------  ----------  ---------------  -------------
  default                    deployment-example-9956dd665-hzcbf    0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-system                kube-proxy-hc9bc                      0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-system                weave-net-cshgn                       20m (2%)      0 (0%)      0 (0%)           0 (0%)
Allocated resources:  #已分配的资源使用概要信息
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ------------  ----------  ---------------  -------------
  20m (2%)      0 (0%)      0 (0%)           0 (0%)
Events:         <none>  #Node相关的Event信息。

3.Replication Controller

RC是kubernetes的核心概念之一,它定义了一个期望的场景。
即声明某种pod的数量在任意时刻都符合某个预期值。RC的定义至少包括以下几个部分:
  Pod的数量,即replicas
  用于删选pod的Label Selector,即selector
  用于生成pod的模板,即template

下面是一个定义RC的简单示例:

apiVersion: v1
kind: ReplicationController  #资源对象的类型
metadata:  #rc的元数据
  name: mysql
spec:  #rc的相关属性定义
  replicas: 1  #pod数量
  selector:
    app: mysql  #给pod定义标签
  template:  #根据此模板创建pod
    metadata:
      labels:
        app: mysql  #pod的标签,对应于selector的标签
    spec:  #pod的相关属性
      containers:  #容器的相关属性定义,属性值是一个数组
      - name: mysql  #容器名
        image: mysql  #使用的镜像名称
        ports:
        - containerPort: 3306  #端口号,可能会有多个端口,使用数组作为值
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "123456"

当我们定义了一个RC并提交到系统之后,Master节点的Controller-Manager就会得到通知,并定期巡检目标Pod的数量
如果目标数量不等于期望值,那么就会执行相应的增加或删除操作,这就实现了应用集群的高可用性。
通过kubectl scale和kubectl apply可以修改pod的副本数量。
当我们需要更新一个服务的时候,可以使用滚动升级,就是当前容器逐个停止,新的服务容器逐个启动,不改变pod的副本数量。

在1.2版本之后,Replica Set和Deployment这个两个资源对象逐渐取代了RC
Replica Set是RC的升级版,基本上可以替代RC,只是功能更加强大,
比如支持基于集合的Label Selector,RC只支持等式的Selector。
Replica Set主要被Deployment这个更高层次的对象所使用,从而形成一整套Pod创建、删除、更新的编排机制。
当我们使用deployment的时候,无需关心和维护Replica Set。

Replica Set和deployment实现了自动扩容和伸缩的功能。

4.Deployment

Deployment是kubernetes1.2版本中为了更好的解决Pod的编排问题而引入的概念。
Deployment内部是使用Replica Set来实现的,之前说过Replica Set是对RC的一次升级,唯一的区别是支持基于集合的Select Label。

Deployment与RC的相似度非常高,最大的升级是随时知道当前Pod部署的进度
一个Pod的创建、调度、绑定节点及在目标Node上启动对应的容器这一过程需要一定的时间

Deployment和Replica Set以及RC的定义非常类似。下面是一个简单的示例:

apiVersion: extensions/v1beta1
kind: Deployment  #资源对象的类型
metadata:  
  name: frontend  #这个对象的名称,通过kubectl get deployment可以获取
spec:  #deployment与RC一样,至少由replicas、selector、template组成
  replicas: 1
  selector:  #deployment使用Replica Set,因此支持基于集合的Selector Label
    matchLabels:
      tier: frontend
    matchExpressions:
      - {key: tier, operator: In, values: [frontend]}
  template:
    metadata:
      labels:
        app: app-demo
        tier: frontend  #这个需要与Selector对应
    spec:
      containers:  #容器的一些属性定义
      - name: tomcat-demo
        image: tomcat
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

创建deployment:

[root@VM_16_6_centos chapter01]# kubectl create -f tomcat-deployment.yaml
deployment "frontend" created

查看deployment:

[root@VM_16_6_centos chapter01]# kubectl get deployment
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend   1         1         1            1           5s

关于名词解释:
  DESIRED:Pod的期望数量
  CURRENT:Pod的当前数量,Pod的数量会逐步等于DESIRED的值。
  UP-TO-DATE:最新版本的Pod数量,在版本升级中,已经进行升级了的Pod数量
  AVAILABLE:当前集群中可用的Pod的数量。
如果你感觉Deployment与RC并没有什么区别,那么可以查看以下RC。

[root@VM_16_6_centos chapter01]# kubectl get rc
NAME      DESIRED   CURRENT   READY     AGE
mysql     3         3         3         9h

虽然告诉你了期望值、当前值和已经可用的值以及运行时间,但是并没有解决服务在滚动升级过程的推进过程
这就是deployment与RC最大的不同之处。

deployment内部是调用Replica Set来实现的,因此也会自动创建:

[root@VM_16_6_centos chapter01]# kubectl get rs
NAME                 DESIRED   CURRENT   READY     AGE
frontend-141477217   

查看已经创建的Pod:

[root@VM_16_6_centos chapter01]# kubectl get pods|grep frontend
frontend-141477217-94ltm   1/1       Running   0          6m

创建了一个Pod。

查看容器:

[root@VM_16_6_centos chapter01]# docker ps|grep front
0f2f83d4b8d9        tomcat                                                       "catalina.sh run"        9 minutes ago       Up 9 minutes                            k8s_tomcat-demo.b9a602ba_frontend-141477217-94ltm_default_2d3f9e82-9cc0-11e9-a763-52540095a842_eb119b9f
160304a783ba        registry.access.redhat.com/rhel7/pod-infrastructure:latest   "/pod"                   9 minutes ago       Up 9 minutes                            k8s_POD.24f70ba9_frontend-141477217-94ltm_default_2d3f9e82-9cc0-11e9-a763-52540095a842_506a66db

每个pod都会创建一个Pause容器用于共享网络和存储卷。

5.pod

Pod是Kubernetes的核心概念之一,Pod是kubernetes的最小管理单元
每个Pod在创建的时候默认都会创建一个pause容器,这个容器用来共享网络和文件
除了pause容器之外,每个Pod还包含一个或多个紧密相关的业务容器。
需要注意的是Pod存储的并不是一个或多个相同的容器,而是业务紧密相关的一组容器。
这样Pause容器就非常好的解决了一个pod内多个容器之间的通信和文件共享的问题。
如果是Pod存储的是相同类型的容器,这些容器之间是不需要进行通信,更多的是和外部的通信。
Kubernetes会为每一个Pod分配一个IP。Pod中所有的容器共享这一个IP。
Kubernetes在设计之初,就要求不同Node上的容器能够直接进行通信,kubernetes的网络方案有非常多种,之后会有说明。

Kubernetes中Pod一般分为普通Pod和静态Pod(static pod).
静态Pod使用来为了维护集群而创建,比如你修改了集群的配置文件,静态pod会进行定期检查,这样就会自动更新配置。
静态Pod并不会存储在etcd中,而是存放在某个Node的一个文件中,并且只在此Node上启动运行。
普通Node一旦创建之后就会被存储到etcd中,然后被scheduler调度到某个具体的Node上进行绑定,
然后该Node上的kubelet组件就会进行容器的创建工作。

简单示例:

apiVersion: v1
kind: Pod
metadata:
  name: myweb
  labels:
    name: myweb
spec:
  containers:
  - name: myweb1
    image: kubeguide/tomcat-app:v1
    ports:
    - containerPort: 8010
    env:
    - name: MYSQL_SERVICE_HOST
      value: 'mysql'
    - name: MYSQL_SERVICE_PORT
      value: '3307'

创建pod:

[root@VM_16_6_centos chapter01]# kubectl create -f myweb-pod.yaml 
pod "myweb" created

查看Pod:

[root@VM_16_6_centos chapter01]# kubectl get pods|grep myweb
myweb                      1/1       Running   0          4m

通过查看Pod的详细信息,可以了解该Pod的所有Event,包括其整个生命周期中所进行的操作。
从下面的Event中我们可以看出,经历了pull、create和start这个三个过程。
当然还可以查看到一些容器的详细信息。

[root@VM_16_6_centos chapter01]# kubectl describe pods/myweb
Name:        myweb
Namespace:    default
Node:        127.0.0.1/127.0.0.1
Start Time:    Tue, 02 Jul 2019 20:43:33 +0800
Labels:        name=myweb
Status:        Running
IP:        172.17.0.6
Controllers:    <none>
Containers:
  myweb1:
    Container ID:    docker://a482b4d1c1a9fd7a5cd643fc5ce3f20c5797966ff58a5e7b136d8b599cc75c55
    Image:        kubeguide/tomcat-app:v1
    Image ID:        docker-pullable://docker.io/kubeguide/tomcat-app@sha256:7a9193c2e5c6c74b4ad49a8abbf75373d4ab76c8f8db87672dc526b96ac69ac4
    Port:        8010/TCP
    State:        Running
      Started:        Tue, 02 Jul 2019 20:43:33 +0800
    Ready:        True
    Restart Count:    0
    Volume Mounts:    <none>
    Environment Variables:
      MYSQL_SERVICE_HOST:    mysql
      MYSQL_SERVICE_PORT:    3307
Conditions:
  Type        Status
  Initialized     True 
  Ready     True 
  PodScheduled     True 
No volumes.
QoS Class:    BestEffort
Tolerations:    <none>
Events:
  FirstSeen    LastSeen    Count    From            SubObjectPath        Type        Reason            Message
  ---------    --------    -----    ----            -------------        --------    ------            -------
  48s        48s        1    {default-scheduler }                Normal        Scheduled        Successfully assigned myweb to 127.0.0.1
  48s        48s        2    {kubelet 127.0.0.1}                Warning        MissingClusterDNS    kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. Falling back to DNSDefault policy.
  48s        48s        1    {kubelet 127.0.0.1}    spec.containers{myweb1}    Normal        Pulled            Container image "kubeguide/tomcat-app:v1" already present on machine
  48s        48s        1    {kubelet 127.0.0.1}    spec.containers{myweb1}    Normal        Created            Created container with docker id a482b4d1c1a9; Security:[seccomp=unconfined]
  48s        48s        1    {kubelet 127.0.0.1}    spec.containers{myweb1}    Normal        Started            Started container with docker id a482b4d1c1a9

Kubernetes中的所有资源对象都可以采用yaml或者JSON格式的文件来进行定义和描述。
当然在Pod里面还可以进行资源配额的限制。

6.Label

Label同样是Kubernetes中非常核心的一个概念。Label是以键值对的形式进行定义的。
Label可以附加到各种资源对象上,比如Node、Service、Pod、RC等。
一个资源对象可以绑定任意多个Label,一个Label也可以绑定到任意个资源对象上去。
Label通常在资源对象定义时确定,也可以在资源对象创建之后动态添加或者删除。
我们可以给指定的资源对象绑定一个或者多个不同的Label来实现多维度的资源分组。

Label对象之所非常重要,这是因为Label是不同资源对象之间进行关联、查询和筛选的方式。
之前说过RC只支持基于等式的筛选,而RS支持基于集合形式的筛选。
RC的定义方式:

replicas: 1
selector:
app: mysql
template:
metadata:
  labels:
    app: mysql

RS的定义方式:

selector:
  mathLabels:
    app: myweb
  matchExpressions:
    - {key: tier, operator: In, values: [frontend]}
    - {key: environment, operator: NotIn, Value: [dev]}

Label Selector在Kubernetes中重要使用场景有以下几处:
  kube-controller进程通过资源对象RC上定义的Selector Label来查找pod,然后与replicas的数量对比,以此维护期望值。
  kube-proxy通过Service的Selector Label来选择对应的Pod,自动建立起每个Service到对应Pod的请求转发路由表,实现自动负载均衡的功能。

7.Horizontal Pod Autoscaler

通过kubectl scale或者kubectl apply或者kubectl edit就可以实现来对pod扩容和缩容。
但是这种手动操作的方式显然并不是很多人所期待的。
更多人希望系统能够根据当前负载的变化情况自动触发水平扩展或缩容的行为,希望这个过程完全是自动化、智能化的。

Kubernetes在1.1版本中首次发布了Horizontal Pod Autoscaling(Pod横向自动扩容,简称HPA)。
在1.2版本中HPA被升级为稳定版本(apiVersion:autoscaling/v1),但是依然保留了旧版本(apiVersion:extensions/v1betal)。
从1.6版本开始,对根据应用自定义指标进行自动扩容和缩容的功能进行增强,API版本为autoscaling/v2alphal。

每一个yaml或者json文件都会指定apiVersion,其实这是指定调用那个APIService的接口来处理这个任务。
因为可能对于同一种该资源类型可能会有不同的处理方式。

HPA与之前的RC、Deployment一样也属于一种kubernetes资源对象。
通过追踪分析RC控制的所有目标Pod的负载变化情况,来确定是否需要针对性的调整目标pod的副本数,这是HPA的实现的原理。
HPA可以有以下两种方式来衡量Pod负载的度量指标:
  CPUUtilizationPercentage

  应用程序自定义的度量指标,比如服务在每秒内的相关请求数(TPS或QPS)。

CPUUtilizationPercentage是一个算术平均值,即目标Pod所有副本自身的CPU利用率的平均值
一个Pod自身的CPU利用率是该Pod当前CPU的使用量除以它的Pod Request的值。
比如某个时刻CPUUtilizationPercentage的值超过了预设值,则意味着当前的Pod的数量不足以支撑接下来的请求,此时则需要进行动态扩容。
当请求高峰时段过去后,Pod的CPU利用率又会降下来,此时对应的Pod副本数又会自动降低到一个合理的水平。

CPUUtilizationPercentage计算过程中使用到的Pod的CPU使用通常是1min内的平均值。目前通过查询Heapster扩展组件来得到这个值。
如果你没有定义Pod的Request的值,则无法使用CPUUtilizationPercentage来实现横向自动扩容的能力。

下面是一个HPA的简单示例:

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  maxReplicas: 10  #扩容或缩容Pod数量的约束
  minReplicas: 1
  scaleTargetRef:
    kind: Deployment
    name: php-apache
  targetCPUUtilizationPercentage: 90  #当CPU的占用率超过90%的时候自动触发扩容行为

8.StatefulSet

在kubernetes中,Pod的管理对象RC、Deployment、DaemonSet和Job都是面向无状态的服务,
因为Pod是非持久性的,随时可能被删除,是无法长期存储有状态服务的数据的。

在现实业务中,最常用到的有状态的服务是数据库类的服务,比如MySQL集群、MongoDB集群。
这些应用集群有一些共同点:
  每个节点都有固定的ID,通过这个ID,集群中的成员可以相互发现并通信。
  集群的规模比较固定,集群的规模不能随意变动。
  集群的每个节点都有状态,通常会将持久化的数据永久存储。
  如果某个节点无法正常访问,那么集群的功能将会受到一定的影响。

如果你使用RC/Deployment来控制Pod,那么Pod的名字是随机产生的,IP也不是固定的,
这样你根本无法给每一个Pod分配一个固定的IP。

Kubernetes为了兼顾这种有状态的服务,在1.4版本中引入了PetSet这个新的资源对象。
在1.5版本中将其变更为StatefulSet,StatefulSet从本质上来说是RC/Deployment的一个变种,只是改变了一些特性:
StatefulSet控制的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其它成员。
StatefulSet控制的Pod的启动顺序是受控制的。
StatefulSet里的Pod采用稳定的持久化存储卷,通过PV/PVC来实现,删除pod时默认不会删除与StatefulSet相关的存储卷(保证数据安全)

StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service配合使用,
即在每个StatefulSet的定义中要声明它属于哪一个Headless Service。
Headless Service与普通Service的区别在于它没有Cluster IP,
如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。
StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例创建了一个DNS域名。

9.Service

(1)概论

Service也是Kubernetes里面最核心的概念之一,Kubernetes里的每个Service其实就是微服务架构中的一个微服务。
之前我们所说的RC、Deployment、Pod其实都是为Service来服务的。
下面是对Pod、RC和Service的关系说明图:

Service定义了一个服务的访问入口地址,前端Pod通过这个入口访问Service下面的一组Pod集群实例,
Service与Pod集群之间通过Label Selector来实现关联。
RC用来保证Service的服务能力和服务质量永远保持在预期的标准。

下面是Kubernetes所提供的微服务网格架构:

通过分析、识别并建模系统中的所有服务为微服务,最终我们的系统由多个提供不同业务能力而又彼此独立的微服务单元所组成。
服务之间通过TCP/IP进行通信,从而形成了我们强大而又灵活的服务网格,拥有了强大的分布式能力、弹性扩展能力、容错能力。

每个Pod都会被分配一个单独的IP,与container的Port来组成Endpoint来被客户端访问。
多个Pod组成一个集群,那么客户端是如何来进行访问的了?一般是通过负载均衡来进行转发。
在Kubernetes中,每个Node上都有一个kube-proxy组件来实现自动的负载均衡,
kube-proxy负责将对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡和会话保持机制。

在Kubernetes中,Service不是共用一个负载均衡器的IP地址,而是每个Service分配一个全局唯一的虚拟IP地址。
这个IP被称为Cluster IP,这样每个服务就变成了具备唯一IP地址的“通信节点”,服务的调用就变成了最基础的TCP网络通信问题。

Pod的EndPoint会随着Pod的销毁和重建而发生改变,因为Pod的IP地址会在这个过程中发生改变。
而Service一旦被创建之后,就会被分配一个唯一的Cluster IP,在整个Service的生命周期内,Cluster IP都不会发生改变。
许多时候,我们可以直接将Service暴漏出去,这样访问Service的名称就可以访问到对应的服务。

下面是一个Service创建的简单实例:

apiVersion: v1
kind: Service  #定义资源对象的类型
metadata:
  name: tomcat-service  #该service的名称
spec:
  ports:
  - port: 8091  #对外的服务端口号
  selector:
    tier: frontend  #通过这个Lable筛选Pod

创建Service:

[root@VM_16_6_centos chapter01]# kubectl create -f tomcat-service.yaml 
service "tomcat-service" created

端口已经被暴漏出来了,而且还分配了一个ClusterIP:

[root@VM_16_6_centos chapter01]# kubectl get service
NAME             CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes       10.254.0.1       <none>        443/TCP          6d
mysql            10.254.40.50     <none>        3306/TCP         5d
myweb            10.254.167.209   <nodes>       8080:30001/TCP   5d
tomcat-service   10.254.165.154   <none>        8091/TCP         12s

查看该Service的详细信息:

[root@VM_16_6_centos chapter01]# kubectl get svc tomcat-service -o yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: 2019-07-03T11:41:44Z
  name: tomcat-service
  namespace: default
  resourceVersion: "633043"
  selfLink: /api/v1/namespaces/default/services/tomcat-service
  uid: 887daf77-9d87-11e9-895d-52540095a842
spec:
  clusterIP: 10.254.165.154
  ports:
  - port: 8091
    protocol: TCP
    targetPort: 8091
  selector:
    tier: frontend
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

targetport属性用来确定提供该服务的容器所暴露的端口号,而port则定义了Service的虚拟端口号,
如果没有定义targetPort,则默认targetPort与Port相同。

(2)Kuberbetes的服务发现机制

在Kubernetes中,每个Service都有一个唯一的Cluster IP和唯一的名字,名字是用户自己定义的。
在实际中,我们不可能通过IP来定位Service对象,更多的是通过name,那么Service 的Cluster IP与name是如何关联和定义的了?
早期的时候,kubernetes通在每个Pod启动的时候,自动注入环境变量,通过访问变量就可以找到对应的Cluster IP。
后来Kubernetes通过Add-On增值包的方式引入DNS系统,将服务名作为DNS的域名,这样就可以通过服务名来建立通信了。

(3)外部系统访问Service的问题

在Kubernetes中,最常见到的IP有三种:
  Node IP:Node节点的IP,这是一个真实存在的物理IP,通过这个IP就可以直接访问到Node这个服务器。
  Pod IP:Pod的IP地址,每个Pod的IP其实是Pause容器的IP,这个一个容器IP,
      这是容器在创建的过程中,docker0虚拟网桥随机分配的一个IP。这个IP是可以被访问的,可以ping通,可以看作是一个虚拟IP。
  Cluster IP:Service的IP,更像一个“虚假”的IP,虚拟IP至少还能够使用,而Cluster IP不能直接使用。
      Cluster IP仅仅作用于Service这个对象,由Kubernetes管理和分配。
      Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备通信的能力,
并且Kubernetes属于集群内的封闭空间,集群之外的节点如果要访问这个通信端口,则需要进行额外的操作。
Cluster IP属于集群内部的地址,无法在集群外部直接使用这个地址。
在微服务中,外部或内部应用会直接访问Service的服务,那么客户该如何来进行访问了?
采用NodePort是解决上述问题的最直接、最有效、最常用的做法。
NodePort的实现方式是在Kubernetes集群里的每个Node上为需要外部访问的Service开启一个对应的TCP监听端口。

我们可以通过一个完整的例子来展示:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      tier: frontend
    matchExpressions:
      - {key: tier, operator: In, values: [frontend]}
  template:
    metadata:
      labels:
        app: app-demo
        tier: frontend
    spec:
      containers:
      - name: tomcat-demo
        image: tomcat
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

我们创建了一个叫做frontend的deployment:

[root@VM_16_6_centos chapter01]# kubectl get deployment
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend   1         1         1            1           18m

创建了一个pod,这里是随机命名:

[root@VM_16_6_centos chapter01]# kubectl get pod
NAME                       READY     STATUS    RESTARTS   AGE
frontend-141477217-pm1m9   1/1       Running   0          19m

然后启动了一个叫做tomcat-demo的容器:

[root@VM_16_6_centos chapter01]# docker ps
CONTAINER ID        IMAGE                                                        COMMAND             CREATED             STATUS              PORTS               NAMES
bbce24a9ea39        tomcat                                                       "catalina.sh run"   19 minutes ago      Up 19 minutes                           k8s_tomcat-demo.b9a602ba_frontend-141477217-pm1m9_default_1308260d-9d91-11e9-895d-52540095a842_b0445ecc
e2b768eea56c        registry.access.redhat.com/rhel7/pod-infrastructure:latest   "/pod"              19 minutes ago      Up 19 minutes                           k8s_POD.24f70ba9_frontend-141477217-pm1m9_default_1308260d-9d91-11e9-895d-52540095a842_8687e42c

frontend这个deployment负责保证pod保持在期望的状态。

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  type: NodePort
  ports:
  - port: 8080
    nodePort: 31002
  selector:
    tier: frontend

现在我们又创建了一个service对象:

[root@VM_16_6_centos chapter01]# kubectl get svc
NAME             CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes       10.254.0.1      <none>        443/TCP          6d
tomcat-service   10.254.38.147   <nodes>       8080:31002/TCP   20m

Nodeport为tomcat-service启动了31002这个外部的监听端口,自然而然的将其访问引入到了容器内部。
之后我们就可以访问这个Tomcat服务了。

10.Volume

Kubernetes中的Volume与容器中的Volume在概念和用途上面比较类似,但是还是有差异之处。
Kubernetes中的Volume被定义在Pod上,然后被pod中的一个或者多个容器挂载到相应的目录下面。
Volume的生命周期与Pod的生命周期相同,与容器的生命周期无关。

下面是volume的简单实例:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: deployment-example
spec:
  volumes:  #在pod中定义了一个volumes
    - name: detavol  #name和emptyDir都是定义的该volume的属性
      emptyDir: {}
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.10
        volumeMounts:  #将volumes挂载到容器中
          - mountPath: /mydata-data  #挂载到那个目录
            name: detavol  #挂载哪一个volume

Volume的使用比较简单,只需要在Pod中定义,然后在容器中进行引用就可以了。

Pod中的多个容器通过共享一个volume,就是实现容器间文件共享的问题,
这样就可以将容器中数据保存到一个目录,最后实现永久性存储。
不仅如此,通过Volume还可以实现容器配置文件集中化配置和管理,这个功能通过ConfigMap来进行实现。

Volume常见类型:

(1)emptyDir

emptyDir类型的volume是Pod分配到Node时创建的。
它的初始内容空,并且无须指定宿主机上对应的文件,当Pod从Node上移除时,emptyDir中的数据会永久被删除。

(2)hostPath

hostPath为在Pod上挂载宿主机上的文件文件或者目录,常用于日志文件存储或者直接访问docker的文件系统

(3)gcePersistentDisk

使用谷歌公有云提供的永久磁盘(PD)存放Volume数据,PD上的内容会被永久保存,当Pod被删除时,PD只是被卸载,但不会被删除。

(4)awsElasticBlockStore

与.gcePersistentDisk类似使用亚马逊提供的永久存储保存数据

volumes:
  - name: test-volume
    awsElasticBlockStore:
      volumeID: aws://<availability-zone>/<volume-id>
      fsType: ext4

(5)NFS

使用NFS网络文件系统提供的共享目录存储数据,需要在系统中部署一个NFS Server。

volumes:
  - name: nfs
    nfs:
      #改为你的NFS服务器地址,该volume绑定在某一个Pod上面,作用对象是该Pod上的一个或多个容器
      server: nfs-server.localhost
      path: “/”

(6)其它类型的Volume

还有iscsi、flocker、glusterfs、rbd、gitRepo、secret

11.Persistent Volume

Volume是定义在Pod上的,属于“计算资源”的一部分。
实际上,“网络存储”是相对于“计算资源”而存在的一种实体资源。

PV可以理解成Kubernetes集群中的某个网络存储中对应的一块存储,它与Volume类似,但又有所区别:
  PV只能是网络存储,不属于任何Node,但可以在每个Node上访问。
  PV并不是定义在Pod上,而是独立于Pod之外定义

PV的作用对象是Node,当然就可以作用Pod。
下面是一个NFS类型PV的一个yaml定义文件,声明了需要5Gi的存储空间:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003  #PV的名称属性
spec:
  capacity:  #PV的需求
    storage: 5Gi
  accessModes:  #模式
    - ReadWriteOnce
  nfs:  #nfs的路径和地址
    path: /somepath
    server: 172.17.0.2

PV常见的accessModes类型:
  ReadWriteOnce:读写权限,只能被单个Node挂载
  ReadOnlyMany:只读权限,允许被多个Node挂载
  ReadWriteMany:读写权限,允许被多个Node挂载

如果某个Pod想申请某种类型的PV,则首先需要定义一个PersistentVolumeClaim(PVC)对象:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: myclaim
spec:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 8Gi

然后在Pod的volumes中引用上述PVC即可:

volumes:
  - name: mypd
    persistenVolumeClaim:
      claimName: myclaim

PVC是针对于Pod来准备的,因为PV能够被很多的对象使用,只有pod在使用的时候才需要先定义PVC。
volume是在Pod内应用,而PV是外部的网络存储资源。

PV有以下几种状态:
  Available:空闲状态
  Bound:已经绑定到某个PVC上
  Released:对应的PVC已经删除,但资源还没有被集群回收
  File:PV自动回收失败

12.Namespace

Namespace是Kubernetes系统中非常重要的核心概念,
Namespace在很多情况下用于实现多用户的资源隔离。
Namespace通过将集群内部的资源对象“分配”到不同namespace中,形成逻辑上分组的不同项目、小组或用户组,
便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。

[root@VM_16_6_centos ~]# kubectl get namespaces
NAME          STATUS    AGE
default       Active    7d
kube-system   Active    7d

集群启动之后,会创建一个名为“default”的Namespace,如果没有指定Namespace,那么创建的资源自将自动归属到default下面。

13.Annotation

Annotation与Label类似,也使用key/value键值对的形式进行定义。
不同的是Label具有严格的命名规范,它定义的是Kubernetes对象的元数据(Metadata),并且用于Label Selector。
而Annotation则是用户任意定义的“附加”信息,以便于外部工具进行查找,
很多时候,Kubernetes的模块自身会通过Annotation的方式标记资源对象的一些特殊信息。

原文地址:https://www.cnblogs.com/yangmingxianshen/p/12635690.html