容器云Kubernetes

《Kuberbetes权威指南》笔记:Kubernetes基础

2018-01-04  本文已影响439人  行书以鉴

《kubernetes权威指南》是本不可多得的好书,这里记录一下自己的读书笔记以及按照书中搭建的源代码。

kubernetes官方文档:https://kubernetes.io/docs/home/
kubernetes中文手册:https://www.kubernetes.org.cn/docs
kubernetes中文文档:https://linfan1.gitbooks.io/kubernetes-chinese-docs/content

What is Kubernetes?

英文原文:
Kubernetes is an open-source platform designed to automate deploying, scaling, and operating application containers.
With Kubernetes, you are able to quickly and efficiently respond to customer demand:
Deploy your applications quickly and predictably.
Scale your applications on the fly.
Roll out new features seamlessly.
Limit hardware usage to required resources only.
Our goal is to foster an ecosystem of components and tools that relieve the burden of running applications in public and private clouds.


官方定义的说法是Kubernetes一个用于容器集群的自动化部署、扩容以及运维的开源平台。使用Kubernetes,你可以快速高效地响应客户需求:

个人把k8s比喻成一艘装满集装箱的帆船,可以装满很多的集装箱。

在集群的管理方面,kubernetes将集群中的机器划分为一个master节点和一群工作节点nodes.

  • master上运行与集群管理相关的一组进程:kube-apiserver,kube-controller-manager,kube-scheduler
  • node上运行与kubectl,kube-proxy等服务进程
  • etcd 作为高性能存储服务

etcd 的学习可以参考 gitbook 上面某大神的一本书 一 etcd3学习笔记


二话不说 书本上从一个简单的例子开始实践一下

单机部署一个mysql+myweb(tomcat)应用栈
技术准备:Centos7 OSX

systemctl stop firewalld
systemctl disable firewalld
yum update -y
yum install etcd kubernetes -y

此时安装好的etcd和kubernetes版本为:

kubectl :v1.5.2
docker : v1.12.6
etcd : v3.2.9

Options的内容加上:OPTIONS='--selinux-enabled=false --insecure-registry gcr.io'
把--admission_control参数的ServiceAccount参数删除
systemctl start etcd
systemctl start docker 
systemctl start kube-apiserver
systemctl start kube-controller-manager
systemctl start kube-scheduler
systemctl start kubelet
systemctl start kube-proxy
准备工作

1.换源

curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://3b13d85e.m.daocloud.io
systemctl restart docker 

2.提前下载好所需标准镜像

#mysql镜像
docker pull daocloud.io/library/mysql:latest
#tomcat镜像
docker pull kubeguide/tomcat-app:v2

启用mysql容器服务

1.定义一个RC文件,命名为mysql-rc.yaml,编辑:

apiVersion: v1
kind: ReplicationController
metadata:
  name: mysql
spec:
  replicas: 1
  #定义RC标签选择
  selector:
    app: mysql
  #定义Pod模板
  template:
    metadata:
      labels: 
        app: mysql
    spec:
      #Pod内容器定义
      containers:
        - name: mysql
          image:  daocloud.io/library/mysql
          ports: 
          - containerPort: 3306
          env:
          - name: MYSQL_ROOT_PASSWORD
            value: "123456"
            

创建好文件后,发布到k8s集群中

[root@taroballs ~]# kubectl create -f mysql-rc.yaml 
replicationcontroller "mysql" created

查看刚刚创建的RC

[root@kubecon ~]# kubectl get rc
NAME      DESIRED   CURRENT   READY     AGE
mysql     1         1         1         11s

查看Pod的创建情况

[root@kubecon ~]# kubectl get pods
NAME          READY     STATUS    RESTARTS   AGE
mysql-bh44q   1/1       Running   0          16s

2.定义一个Service,文件命名为mysql-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
    - port: 3306
  selector:
    app: mysql

保存后同样发布到k8s集群中

[root@taroballs ~]# kubectl create -f mysql-svc.yaml 
service "mysql" created

查看创建好的Service

[root@kubecon ~]# kubectl get svc
NAME         CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
kubernetes   10.254.0.1     <none>        443/TCP    13h
mysql        10.254.248.1   <none>        3306/TCP   5s
#请注意 mysql服务被分配到了一个10.254.248.1的ClusterIP地址,此地址为虚地址,这样其他的Pod就可以通过Service的ClusterIP+端口号6379来访问它了

创建Tomcat应用栈

1.同样,定义一个RC文件,命名为myweb-rc.yaml,后面会讲到如何编写不用担心:

apiVersion: v1
kind: ReplicationController
metadata:
  name: myweb
spec:
# Pod的数量
  replicas: 1
# spec.selector与spec.template.metadata.labels,这两个字段必须相同,否则下一步创建RC会失败。
  selector:
    app: myweb
  template:
    metadata:
      labels:
        app: myweb
#   容器组的定义
    spec:
      containers:
#       容器名称
        - name: myweb
#         容器对应的镜像
          image: kubeguide/tomcat-app:v2
          ports:
#         在8080端口上启动容器进程,PodIP与容器端口组成Endpoint,代表着一个服务进程对外通信的地址
          - containerPort: 8080
          env:
#此处如果在未安装域名解析的情况下,会无法将mysql对应的IP解析到env环境变量中,因此先注释掉!
#          - name: MYSQL_SERVICE_HOST
#            value: 'mysql'
          - name: MYSQL_SERVICE_PORT
            value: '3306'

把它发布到集群当中:

[root@kubecon ~]# kubectl create -f tomcat-rc.yaml 
replicationcontroller "myweb" created

查看RC是否存在

[root@kubecon ~]# kubectl get rc
NAME      DESIRED   CURRENT   READY     AGE
mysql     1         1         1         1h
myweb     1         1         1         15s

查看Pod是否运行

[root@kubecon ~]# kubectl get pods
NAME          READY     STATUS    RESTARTS   AGE
mysql-bh44q   1/1       Running   0          1h
myweb-dctwr   1/1       Running   0          19s

2.定义一个Service文件,命名为myweb-svc.yaml

apiVersion: v1
kind: Service
metadata: 
  name: myweb
spec:
  type: NodePort
  ports:
    - port: 8080
      nodePort: 30001
  selector:
    app: myweb

同样的,把它发布到集群中

[root@kubecon ~]# kubectl create -f tomcat-svc.yaml 
service "myweb" created

查看是否存在这个Service

[root@kubecon ~]# kubectl get svc
NAME         CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes   10.254.0.1       <none>        443/TCP          15h
mysql        10.254.248.1     <none>        3306/TCP         1h
myweb        10.254.134.126   <nodes>       8080:30001/TCP   4s

如果都能完成的话 就到了激动人心的时刻了,打开浏览器访问http://yourip:30001

View on http://yourip:30001/demo/

如果看到这个界面说明你成功了


尝试一下插入一条数据



术语:

Master

Node

[root@kubecon ~]# kubectl get nodes
NAME        STATUS    AGE
127.0.0.1   Ready     15h
#由于笔者只有一台ecs >-<.

查看某个node的详细信息:

[root@kubecon ~]# kubectl describe node 127.0.0.1
Name:           127.0.0.1
Role:           
Labels:         beta.kubernetes.io/arch=amd64
            beta.kubernetes.io/os=linux
            kubernetes.io/hostname=127.0.0.1
Taints:         <none>
CreationTimestamp:  Wed, 03 Jan 2018 22:12:05 +0800
Phase:          
Conditions:
  Type          Status  LastHeartbeatTime           LastTransitionTime          Reason              Message
  ----          ------  -----------------           ------------------          ------              -------
  OutOfDisk         False   Thu, 04 Jan 2018 13:24:24 +0800     Wed, 03 Jan 2018 22:12:05 +0800     KubeletHasSufficientDisk    kubelet has sufficient disk space available
  MemoryPressure    False   Thu, 04 Jan 2018 13:24:24 +0800     Wed, 03 Jan 2018 22:12:05 +0800     KubeletHasSufficientMemory  kubelet has sufficient memory available
  DiskPressure      False   Thu, 04 Jan 2018 13:24:24 +0800     Wed, 03 Jan 2018 22:12:05 +0800     KubeletHasNoDiskPressure    kubelet has no disk pressure
  Ready         True    Thu, 04 Jan 2018 13:24:24 +0800     Wed, 03 Jan 2018 22:12:05 +0800     KubeletReady            kubelet is posting ready status
Addresses:      127.0.0.1,127.0.0.1,127.0.0.1
Capacity:
 alpha.kubernetes.io/nvidia-gpu:    0
 cpu:                   1
 memory:                1883496Ki
 pods:                  110
Allocatable:
 alpha.kubernetes.io/nvidia-gpu:    0
 cpu:                   1
 memory:                1883496Ki
 pods:                  110
System Info:
 Machine ID:            963c2c41b08343f7b063dddac6b2e486
 System UUID:           82E60F76-EF44-4B56-8865-9B667778593F
 Boot ID:           a48df7fa-e8ea-4300-8323-437c83f20321
 Kernel Version:        3.10.0-693.11.1.el7.x86_64
 OS Image:          CentOS Linux 7 (Core)
 Operating System:      linux
 Architecture:          amd64
 Container Runtime Version: docker://1.12.6
 Kubelet Version:       v1.5.2
 Kube-Proxy Version:        v1.5.2
ExternalID:         127.0.0.1
Non-terminated Pods:        (2 in total)
  Namespace         Name            CPU Requests    CPU Limits  Memory Requests Memory Limits
  ---------         ----            ------------    ----------  --------------- -------------
  default           mysql-bh44q     0 (0%)      0 (0%)      0 (0%)      0 (0%)
  default           myweb-dctwr     0 (0%)      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
  ------------  ----------  --------------- -------------
  0 (0%)    0 (0%)      0 (0%)      0 (0%)
Events:
  FirstSeen LastSeen    Count   From            SubObjectPath   Type        Reason          Message
  --------- --------    -----   ----            -------------   --------    ------          -------
  13m       13m     2   {kubelet 127.0.0.1}     Warning     MissingClusterDNS   kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-1ndjf_default(984cd373-f10d-11e7-952b-00163e04f964)". Falling back to DNSDefault policy.
  11m       11m     2   {kubelet 127.0.0.1}     Warning     MissingClusterDNS   kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-vblfl_default(f8b588aa-f10d-11e7-952b-00163e04f964)". Falling back to DNSDefault policy.
  10m       10m     2   {kubelet 127.0.0.1}     Warning     MissingClusterDNS   kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-dctwr_default(07cd7332-f10e-11e7-952b-00163e04f964)". Falling back to DNSDefault policy.

Pod:

Kubernetes中,所有的容器都运行在pod中,一个pod来容纳一个单独的容器,或者多个合作的容器。

为什么要引入Pod容器?
1.以“容器组”为单位整体能够简单对业务进行封装管理以及判断
2.Pod里多个业务共享Pause容器的IP以及Volume使得管理极其方便

Pod特性:

普通Pod及静态Pod

差别 普通Pod 静态Pod
存放位置 一创建就存在etcd 放在某个具体node的具体文件中
使用方式 被Master调度到某node上进行绑定 只在此具体文件所在的node上启动运行

EndPoint的概念
PodIP+containerPort--->EndPoint
Pod的IP加上容器开启的端口,代表着一个服务进程与外界通信的地址

资源限制
每个Pod可以设置限额的计算机资源有CPU和Memory;

如果一个容器失败,它会被Kubernetes的node上的kubectl自动重启
简单来说,Pod就是集装箱中的一个一个小的房间

Event
Event :是一个事件记录,记录了事件最早产生的时间、最后重复时间、重复次数、发起者、类型,以及导致此事件的原因等信息。Event通常关联到具体资源对象上,式排查故障的重要参考信息;

Pod错误排查
当我们发现某个Pod迟迟无法创建时,我们可以使用

kubectl describe pod xxxx

来查看它的描述信息.用来定位问题的范围~

[root@kubecon ~]# kubectl get pods
NAME          READY     STATUS              RESTARTS   AGE
mysql-bwnlk   0/1       ContainerCreating   0          1m


pod服务一直处于 ContainerCreating状态,即出现了错误
[root@kubecon ~]# kubectl describe po  mysql-bwnlk
Name:       mysql-bwnlk
Namespace:  default
Node:       127.0.0.1/127.0.0.1
Start Time: Thu, 04 Jan 2018 10:20:45 +0800
Labels:     app=mysql
Status:     Pending
IP:     
Controllers:    ReplicationController/mysql
Containers:
  mysql:
    Container ID:   
    Image:      daocloud.io/library/mysql
    Image ID:       
    Port:       3306/TCP
    State:      Waiting
      Reason:       ContainerCreating
    Ready:      False
    Restart Count:  0
    Volume Mounts:  <none>
    Environment Variables:
      MYSQL_ROOT_PASSWORD:  123456
Conditions:
  Type      Status
  Initialized   True 
  Ready     False 
  PodScheduled  True 
No volumes.
QoS Class:  BestEffort
Tolerations:    <none>
Events:
  FirstSeen LastSeen    Count   From            SubObjectPath   Type        Reason      Message
  --------- --------    -----   ----            -------------   --------    ------      -------
  3m        3m      1   {default-scheduler }        Normal      Scheduled   Successfully assigned mysql-bwnlk to 127.0.0.1
  3m        20s     5   {kubelet 127.0.0.1}     Warning     FailedSync  Error syncing pod, skipping: failed to "StartContainer" for "POD" with ErrImagePull: "image pull failed for registry.access.redhat.com/rhel7/pod-infrastructure:latest, this may be because there are no credentials on this request.  details: (open /etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crt: no such file or directory)"

  2m    8s  10  {kubelet 127.0.0.1}     Warning FailedSync  Error syncing pod, skipping: failed to "StartContainer" for "POD" with ImagePullBackOff: "Back-off pulling image \"registry.access.redhat.com/rhel7/pod-infrastructure:latest\""

Label

Label是一种可以附加到各种资源对象上(Pod,Node,RC,Service等),通常在资源定义时确定也可以在对象创建后动态添加的实现资源分组动态功能的对象

Label特性

Label Selector
用于查询和筛选拥有某些Labels的资源对象

类似于SQL语句
Label Selector示例:select * from pod where pod’s name=’XXX’,env=’YYY’,支持操作符有=、!=、in、not in;

Label常用场景


Replication controller副本管理器

RC是一种用来声明某种Pod的副本数量在任何时刻都符合某个预期值的场景文件。

Kubernetes的API对象用来管理Pod副本的行为的文件被称作replication controller,它用模板的形式定义了pod,然后系统根据模板实例化出一些pod(特别是由用户)

换句话说,我们通过定义一个RC文件来实现Pod的创建过程及副本数量的自动控制

RC的定义

RC特性

先来个例子实战一下

#定义文件tomcat-frontend-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    tier: frontend
  template:
    metadata:
      labels:
        app: app-demo
        tier: frontend
    spec:
      containers:
      - name: tomcat-demo
        image: daocloud.io/library/tomcat
        imagePullPolicy: IfNotPresent
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 80

将它发布到集群中

[root@kubecon ~]# kubectl create -f tomcat-frontend-rc.yaml 
replicationcontroller "frontend" created
[root@kubecon ~]# kubectl get rc
NAME       DESIRED   CURRENT   READY     AGE
frontend   1         1         1         5s
[root@kubecon ~]# kubectl get pods
NAME             READY     STATUS    RESTARTS   AGE
frontend-8cg3k   1/1       Running   0          8s
[root@kubecon ~]# kubectl get endpoints
NAME         ENDPOINTS             AGE
kubernetes   172.18.231.136:6443   6m

Replica Set

下一代的Replication Controller

差别 Replica Set Replication Controller
base 基于集合的Label Selector 基于等式的Label Selector
future 下一代的RC,现主要被Deployment运用 将被取代

RS的特性与RC特性相类似,不再赘述.


Deployment 升级版RC

与RC区别,两者相似度>90%

Deployment 拥有更加灵活强大的升级、回滚功能。在新的版本中,官方推荐使用Replica Set和Deployment来代替RC

Deployment使用场景

下面以一个例子实战下Deployment

构建Tomcat_Deployment

定义一个文件 命名为:tomcat-deployment.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: tomcat-deployment
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-dome
        image: tomcat
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

发布到Kubernetes集群

[root@kubecon ~]# kubectl create -f tomcat-deployment.yaml 
deployment "tomcat-deployment" created
[root@kubecon ~]# kubectl get deployments
NAME                DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
tomcat-deployment   1         1         1            1           4s
[root@kubecon ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
tomcat-deployment-142787937   1         1         1         1m
#看一下这里Pod的命名以Deployment对应的Replica Set的名字为前缀;

查看Pod的水平扩展过程

[root@kubecon ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
tomcat-deployment-142787937   1         1         1         1m
[root@kubecon ~]# kubectl describe deployments
Name:           tomcat-deployment
Namespace:      default
CreationTimestamp:  Thu, 04 Jan 2018 18:48:31 +0800
Labels:         app=app-demo
            tier=frontend
Selector:       tier=frontend,tier in (frontend)
Replicas:       1 updated | 1 total | 1 available | 0 unavailable
StrategyType:       RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Conditions:
  Type      Status  Reason
  ----      ------  ------
  Available     True    MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet:  tomcat-deployment-142787937 (1/1 replicas created)
Events:
  FirstSeen LastSeen    Count   From                SubObjectPath   Type        Reason          Message
  --------- --------    -----   ----                -------------   --------    ------          -------
  1m        1m      1   {deployment-controller }            Normal      ScalingReplicaSet   Scaled up replica set tomcat-deployment-142787937 to 1

Horizontal Pod Autoscaler(HPA)智能扩容

HPA的引入

实现原理:通过追踪分析RC控制的所有目标Pod的负载变化情况来确定是否需要针对性地调节目标Pod的副本数,相当与调节器。
HPA扩容的Pod负载度量指标:

下面以一个HPA实例来实战下
构建HPA定义文件,命名为php-apache.yaml

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  maxReplicas: 10
  minReplicas: 1
  scaleTargetRef:
    kind: Deployment
    name: php-apache
  targetCPUUtilizationPercentage: 90
#这个HPA控制的目标对象为一个名叫php-apache的Deployment里的Pod副本,当这些Pod副本的CPUUtilizationPercentage超过90%时会触发自动扩容行为,扩容或缩容时必须满足的一个约束条件是Pod的副本数要介于1与10之间;

发布到集群中

[root@kubecon ~]# kubectl create -f php-apache.yaml 
horizontalpodautoscaler "php-apache" created

也可以通过简单命令行方式创建等价HPA对象

[root@kubecon ~]# kubectl get deployments
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend   1         1         1            0           22m
[root@kubecon ~]# kubectl autoscale deployment frontend --cpu-percent=90 --min=1 --max=10
deployment "frontend" autoscaled

Service

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

我们的业务系统就是由一或多个“微服务”Service构成的。相当与帆船上一个一个的集装箱

由图中我们可以看到,Kubernetes的Service定义了一个服务的访问入口地址:

问题引入:客户端访问Pod
既然每个Pod都会被分配到一个独立的IP地址,而且每个Pod都提供了一个独立的Endpoint(Pod IP+ContainerPort)以被客户端访问,那么问题来了,现在多个Pod副本组成了一个集群来提供服务,那么客户端如何来访问它们呢?

一般的做法是部署一个负载均衡器来访问它们,为这组Pod开启一个对外的服务端口如8000,并且将这些Pod的Endpoint列表加入8000端口的转发列表中,客户端可以通过负载均衡器的对外IP地址+服务端口来访问此服务。而客户端请求会被转发到哪个Pod,则有均很负载器觉得。

Kubernetes中运行在Node上的kube-proxy其实就是一个智能的软件负载均衡器,它负责把对Service的请求转发到后端的某个Pod实例上,并且在内部实现服务的负载均衡与会话保持机制。

k8s发明了一种巧妙的设计:Service不是共用一个负载均衡器的IP地址,而是每个Servcie分配一个全局唯一的虚拟IP地址,这个虚拟IP被称为Cluster IP。

这样一来 每个服务就变成了具备唯一IP地址的“通信节点”,服务调用也成了最基础的TCP通信问题。

来一个小小的例子是实战一下~

首先定义一个Service文件,命名为tomcat-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
# 服务如果想被外部调用,必须配置type
  type: NodePort
  ports:
  - port: 8080
    name: service-port
#   服务如果想被外部调用,必须配置nodePort
    nodePort: 31002
  - port: 8005
    name: shutdown-port
  selector:
    tier: frontend

发布到Kubernetes集群

[root@kubecon ~]# kubectl create -f tomcat-service.yaml 
service "tomcat-service" created

查看Endpoint列表

[root@kubecon ~]# kubectl get endpoints
NAME             ENDPOINTS                                                     AGE
kubernetes       172.18.231.136:6443                                           24m
tomcat-service   172.17.0.2:8080,172.17.0.3:8080,172.17.0.2:8005 + 1 more...   2m

查看svc列表

[root@kubecon ~]# kubectl get svc
NAME             CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
kubernetes       10.254.0.1      <none>        443/TCP                         26m
tomcat-service   10.254.80.192   <nodes>       8080:31002/TCP,8005:30541/TCP   4m
#EndPoint=podIP+containerPort

查看Service的ClusterIP可以看到

[root@kubecon ~]# kubectl get svc tomcat-service -o yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: 2018-01-04T10:51:35Z
  name: tomcat-service
  namespace: default
  resourceVersion: "1562"
  selfLink: /api/v1/namespaces/default/services/tomcat-service
  uid: 3bf83ae4-f13d-11e7-ade2-00163e04f964
spec:
  clusterIP: 10.254.80.192
  ports:
  - name: service-port
    nodePort: 31002
    port: 8080 #虚拟端口
    protocol: TCP
    targetPort: 8080 #Expose端口,提供TCP/IP接入
    #若没有指定targetPort则默认与port相同
  - name: shutdown-port
    nodePort: 30541
    port: 8005
    protocol: TCP
    targetPort: 8005
  selector:
    tier: frontend
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

注意,存在Service对应多个端口的问题
很多服务都存在多个端口的问题,比如有些业务一个端口提供业务服务,另外一个端口提供管理服务,比如 Mycat、Codis 等常见的中间件。Kubernetes Service 支持多个 Endpoint,在存在多个 Endpoint 的情况下,要求每个 Endpoint 定义一个名字来区分。下面是 Tomcat 多端口的 Service 定义样例:
要求每个EndPoint定义一个名字区分

- name: service-port   <----------------|
    nodePort: 31002                            
    port: 8080                                      
    protocol: TCP                                 
    targetPort: 8080                             
  - name: shutdown-port <---------------|
    nodePort: 30541
    port: 8005
    protocol: TCP
    targetPort: 8005

Kubernetes服务发现机制

最早时 Kubernetes 采用了 Linux 环境变量的方式解决这个问题,即每个 Service 生成一些对应的 Linux 环境变量(ENV),并在每个 Pod 的容器在启动时,自动注入这些环境变量

#下是 tomcat-service 产生的环境变量条目
TOMCAT_SERVICE_SERVICE_HOST=10.102.107.188
TOMCAT_SERVICE_SERVICE_PORT_SERVICE_PORT=8080
TOMCAT_SERVICE_SERVICE_PORT_SHUTDOWN_PORT=8005
TOMCAT_SERVICE_PORT_8080_TCP_PORT=8080

可以看到,每个 Service 的 Ip 地址及端口都是标准的命名规范的,遵循这个命名规范,就可以通过代码访问系统环境变量的方式得到所需的信息,实现服务调用。

外部系统访问 Service 问题

区别三种IP:

差别 Node IP Pod IP ClusterIP
含义 每个节点的物理网卡的 IP 地址 每个 Pod 的 IP 地址 一个虚拟的 IP地址
源自 物理主机网卡 Docker Engine 根据 docker0 网桥的 IP 地址段进行分配的,通常是一个虚拟的二层网络 Kubernetes Service”伪造“ 和定义的
作用域 服务器之间 虚拟二层网络,不同 Pod 之间通信 仅作用于 Kubernetes Service 这个对象
是否能够ping通 否,没有一个 ”实体网络对象“ 来响应。
三种IP区别

问题来了:存在有一部分服务是要提供给 Kubernetes 集群外部的应用或者用户来使用的,典型的例子就是 Web 端的服务模块,比如上面的 tomcat-service,那么用户怎么访问它?

采用 NodePort 。具体做法如下,以 tomcat-service 为例:

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  type: NodePort
  ports:
  - port: 8080
    nodePort: 31002
#nodePort: 31002 这个属性表名手动指定 tomcat-service 的 NodePort 为 31002
  selector:
    tier: frontend

NodePort 的实现方式是在 Kubernetes 集群里的每个 Node 上为需要外部访问的 Service 开启一个对应的 TCP 监听端口,外部系统只要用任意一个 Node 的 Ip 地址加具体的 NodePort 端口号即可访问此服务
查看NodePort端口是否被监听:

[root@kubecon ~]# kubectl get svc
NAME             CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
kubernetes       10.254.0.1      <none>        443/TCP                         2h
tomcat-service   10.254.80.192   <nodes>       8080:31002/TCP,8005:30541/TCP   2h
[root@kubecon ~]# netstat -lnput | grep 31002
tcp6       0      0 :::31002                :::*                    LISTEN      1608/kube-proxy    

那么问题又来了,NodePort 还没完全解决外部访问 Service 的所有问题,比如负载均衡问题:
假如集群中有 10 个 Node,则此时最好有一个负载均衡,外部的请求只需访问此负载均衡器的 IP 地址,由负载均衡器负责转发流量到后面某个 Node 的 NodePort 上


上图中的 Load Balancer 组件独立于 Kubernetes 集群之外,通常是一个硬件的负载均衡器,或者是以软件方式实现的,例如 HAProxy 或者 Nginx。
对于每个 Service,我们通常需要配置一个对应的 Load Balancer 示例来转发流量到后端的 Node 上,这的确增加了工作量及出错的概率。

Volume存储卷

差别 Docker Volume Kubernetes Volume
定义在 容器上 Pod之上
生命周期 与容器相关 与 Pod 的生命周期相同,但与容器的声明周期不相干关。当容器中止或者启动时,Volume 中的数据也不会丢失。

Volume 是 Pod 中能够被多个容器访问的共享目录。

差别 Docker Volume Kubernetes Volume
定义在 容器上 Pod之上
生命周期 与容器相关 与 Pod 的生命周期相同,但与容器的声明周期不相干关。当容器中止或者启动时,Volume 中的数据也不会丢失。

先来个例子实战下~

#先在 Pod 上声明一个 Volume,然后在容器里引用该 Volume 并 Mount 到容器里的某个目录上
#给之前的 Tomcat Pod 增加一个名字为 datavol 的 Volume,并且 Mount 到容器的 /mydata-data 目录上
apiVersion: v1
kind: ReplicationController
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    tier: frontend
  template:
    metadata:
      labels:
        app: app-demo
        tier: frontend
    spec:
      containers:
      - name: tomcat-demo
        image: daocloud.io/library/tomcat
        #add
        volumeMounts:
        - mountPath: /mydata-data
          name: datavol
        imagePullPolicy: IfNotPresent
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 80
      #add 
      volumes:
      - name: datavol
        emptyDir: {}

发布到集群中

[root@kubecon ~]# kubectl create -f tomcat-frontend-rc.yaml 
replicationcontroller "frontend" created

获取容器id查看是否挂载成功

[root@kubecon ~]# kubectl get pods
NAME             READY     STATUS    RESTARTS   AGE
frontend-c750f   1/1       Running   0          8m
[root@kubecon ~]# docker ps
CONTAINER ID        IMAGE                                                        COMMAND             CREATED             STATUS              PORTS               NAMES
cbe157bfaeab        daocloud.io/library/tomcat                                   "catalina.sh run"   8 minutes ago       Up 8 minutes                            k8s_tomcat-demo.66724613_frontend-c750f_default_3491a794-f153-11e7-ade2-00163e04f964_157daa35
c94aedb5ef0e        registry.access.redhat.com/rhel7/pod-infrastructure:latest   "/usr/bin/pod"      8 minutes ago       Up 8 minutes                            k8s_POD.a8590b41_frontend-c750f_default_3491a794-f153-11e7-ade2-00163e04f964_2080adb3
[root@kubecon ~]# docker inspect cbe
[
    {
        "Id": "cbe157bfaeab28190481a974fe6b1c1f9830584210520f664538fccaaa98ec54",
        "Created": "2018-01-04T13:28:53.583903204Z",
...................................
"Mounts": [
            {
                "Source": "/var/lib/kubelet/pods/3491a794-f153-11e7-ade2-00163e04f964/volumes/kubernetes.io~empty-dir/datavol",
                "Destination": "/mydata-data",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
................................
#可以很明显的看到/datavol挂载到了/mydata-data
#然后可以看到使用的是empty-dir

Kubernetes 支持多种类型的 Volume,例如 GlusterFS、Ceph 等先进的分布式文件存储。

常见的几种Volume类型:

EmptyDir
volumes:
    - name: data
      emptyDir: {}
HostPath

例子:

#定义pod文件busybox-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    name: busybox
spec:
  containers:
    - image: busybox
      command:
        - sleep
        - "3600"
      imagePullPolicy: IfNotPresent
      name: busybox
      volumeMounts:
        - mountPath: /busybox-data
          name: data
  volumes:
  - hostPath:
      path: /tmp/data
    name: data

docker inspect查看挂载

   "Mounts": [
            {
                "Source": "/tmp/data",
                "Destination": "/busybox-data",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
            {
                "Source": "/var/lib/kubelet/pods/7eb594fd-f157-11e7-ade2-00163e04f964/etc-hosts",
                "Destination": "/etc/hosts",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
            {
                "Source": "/var/lib/kubelet/pods/7eb594fd-f157-11e7-ade2-00163e04f964/containers/busybox/7b91bd28",
                "Destination": "/dev/termination-log",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],

不建议使用

gcePersistentDisk&awsElasticBlockStore

一个是Google Cloud Platform的一个是Aws ec2的

NFS

使用 NFS 网络文件系统提供的共享存储数据时,需要在系统中部署一个 NFS Server。定义 NFS 类型的 Volume 示例如下:


Namespace命名空间

Kubernetes 集群在启动后,会创建一个名为 “default” 的 Namespace,通过 kubectl 可以查看到:

[root@kubecon ~]# kubectl get namespace
NAME          STATUS    AGE
default       Active    3h
kube-system   Active    3h

注意,如果不特别指明 Namespace,则用户创建的 Pod、RC、Service 都将被系统创建到这个默认的名为 default 的 Namespace 中。

Namespace 的定义很简单。如果所示的 yaml 定义名为 development 的 Namespace。

小小实战下

#development-ns.yaml
apiVersion: v1 
kind: Namespace
metadata:
  name: development

一旦创建了 Namespace,在创建资源对象时就可以指定这个资源对象属于哪个 Namespace。

定义一个名为 busybox 的 Pod,放入 development 这个 Namespace 里

apiVersion: v1 
kind: Pod
metadata:
  name: busybox
  namespace: development
spec:
  containers:
    - image: busybox
      command:
        - sleep
        - "3600"
      name: bosybox

发布到集群中并查看状态

[root@kubecon ~]# kubectl apply -f development-ns.yaml 
namespace "development" created
[root@kubecon ~]# kubectl apply -f busybox-pod.yaml 
pod "busybox" created
[root@kubecon ~]# kubectl get pod
No resources found.
#因为如果不加参数,则 kubectl get 命令将仅显示属于 default namespace 的资源对象。
[root@kubecon ~]# kubectl get pod --namespace=development
NAME      READY     STATUS    RESTARTS   AGE
busybox   1/1       Running   0          13s
#在 kubectl 命令中加入 --namespace 参数来查看某个命名空间中的对象

Annotation

Annotation 与 Label 类似,不同的是 Label 具有严格的命名规则,它定义的是 Kubernetes 对象的元数据(Metadata),并且用于 Label Selector。而 Annotation 则是用户定义的 “附件” 信息,以便于外部工具进行查找.
Annotation 的应用场景:


总结:Node、Pod、Replication Controller和Service等都可以看作是一种“资源对象”,几乎所有的资源对象都可以通过Kubernetes提供的kubectl工具执行增、删、改、查等操作并将其保存在ectd中持久化存储。 *k8s相当于装满集装箱的货船,Pod相当于船上一个个的集装箱 ,docker容器就是集装箱里摆放的一件件货物,而Service相当于一堆堆贴好标签Label要运去哪儿的集装箱集合,Replication Controller相当于确定集装箱Pods应该要有多少份,deployment是改进版的RC,它通过RS实时确定各个集装箱Pod的上船进度等,NameSpace指船上存放的地方是A区(汽车类)还是B区粮米类。

好了,今天就到这了,写了一天了,希望笔记对自己以及大家都有帮助~>-<.


上一篇下一篇

猜你喜欢

热点阅读