从Docker到Kubernetes——Kubernetes设计解读之ReplicationController、Service

Kubernetes的设计解读

replication controller 设计解读

k8s第二个重要的概念就是replication controller,它决定了一个pod有多少同时运行的副本,并保证这些副本的期望状态与当前状态一致

replication controller在设计上依然体现出了“旁路控制”的思想,在k8s中并没有像Cloud Foundry那样设置一个专门的健康检查组件,而是为每个pod“外挂”了一个控制器进程,从而避免健康检查组件成为性能的瓶颈;即使这个控制器进程失效,容器仍然可以正常运行。

根据上一章的实践,我们大概知道了pod有这么几个状态:

  • Pending:pod已经被创建,但是pod中有一个或多个容器未启动,可能是因为下载镜像的网络传输慢或者pod的调度时间,或者pod没有找到绑定的工作节点等等。
  • Running:pod被绑定到工作节点上且pod中的所有容器均被创建。最重要的是pod中至少有一个容器处于运行、启动或者重启状态。
  • Succeeded:专指pod所有容器均成功正常退出,且不会发生重启。
  • Failed:pod内所有容器均退出且至少有一个容器退出发生错误。
  • Unknown:因为未知原因无法获取pod状态。

pod的状态转移与pod的重启策略息息相关,由于其复杂性在此不表。只需记住,replication controller 会识别pod某状态而实现重启策略。

replication controller的描述文件

与pod对象类似,k8s也可以使用一份JSON格式的资源配置文件来定义一个replication controller对象。主要由3方面组成:

  • 一个用于创建pod的pod template
  • 一个期望副本数(replicas)
  • 一个用于选择被控制pod集合的label selector
apiVersion: v1
kind: ReplicationController  //ReplicationController类型
metadata:
   name: nginx               //pod名字
spec:
   replicas: 2               //2个副本
   selector:                 
     app: nginx              //通过这个标签找到生成的pod
   template:                 //定义pod模板,在这里不需要定义pod名字,就算定义创建的时候也不会采用这个名字而是.metadata.generateName+5位随机数。
     metadata:
      labels:                //定义标签
       app: nginx            //key:v   这里必须和selector中定义的KV一样
     spec:
      containers:            //rc 的容器重启策略必须是Always(总是重启),这样才能保证容器的副本数正确
       - image: nginx
         name: nginx
         ports:  
         - containerPort: 80

replication controller 对pod数量和健康状况的监控是通过副本选择器(replica selector或者label selector的一种)来实现的。replica selector 定义了replication controller和它控制的pod之间的松耦合关系。这与pod和Docker容器之间强耦合的关系相反。因为可以修改pod的labels将一个pod从replication controller的控制中集中移除。

replication controller的典型场景:

  • 重调度:不管是想运行多少个pod副本,replication controller都能保证指定数目的pod正常运行。一旦当前的宿主机节点异常崩溃或者pod停止运行,k8s就会进行相应的重调度。
  • 弹性伸缩:顾名思义,修改relicas来达到pod数目的弹性伸缩。
  • 滚动更新(灰度发布):replication controller 被设计成为通过逐个替换pod的方式来进行副本的增删操作。实现起来很简单:创建新的replication controller ,并按时间逐步将新的replication controller副本数+1,旧的replication controller副本数-1即可。

replication controller 使用示例

{
        "apiVersion":"v1",
        "kind":"ReplicationController",
        "metadata":{
            "name":"redis-controller",
            "labels":{
                        "name":"redis"
                }
        },
        "spec":{
                "replicas":1,
                "selector":{
                        "name":"redis"
                },
                "template":{
                        "metadata":{
                                "labels":{
                                        "name":"redis"
                                }
                        },
                        "spec":{
                                "containers":[{
                                                "name":"redis",
                                                "image":"redis:latest",
                                                "ports":[{
                                                                "containerPort":6379,
                                                                "hostPort":6380
                                                        }]
                                        }]
                        }
                }
        }
}

使用资源配置文件配合kubectl create命令创建一个replication controller对象:

[root@master ~]# kubectl create -f redis-controller.json 
replicationcontroller/redis-controller created

使用kubectl get查询replication controller的基本信息并查看这个controller自动创建的pod。

[root@master ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
podtest                  1/1     Running   0          11m
redis-controller-vsmwz   1/1     Running   0          2m7s
[root@master ~]# kubectl get replicationController -o wide
NAME               DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES         SELECTOR
redis-controller   1         1         1       2m24s   redis        redis:latest   name=redis

这时,我们要验证replication controller是不是真的有效。使用删除pod命令:

[root@master ~]# kubectl delete pod redis-controller-vsmwz
pod "redis-controller-vsmwz" deleted
[root@master ~]# kubectl delete pod podtest
pod "podtest" deleted
[root@master ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
redis-controller-2pzsh   1/1     Running   0          21s

我们发现,删除了之后,又创建了一个新的pod。

删除副本控制器的命令:

[root@master ~]# kubectl delete replicationController redis-controller
replicationcontroller "redis-controller" deleted
[root@master ~]# kubectl get pod
NAME                     READY   STATUS        RESTARTS   AGE
redis-controller-2pzsh   0/1     Terminating   0          99s
[root@master ~]# kubectl get replicationController
No resources found in default namespace.

service的设计解读

其实service这个名字很容易引起误解,我认为应该定义成为proxy或者router更为贴切。

service主要由一个IP地址和一个label selector组成。在创建之初,每个service便被分配了一个独一无二的IP地址,该IP地址与service的生命周期相同并且不可以更改(pod的IP会随pod的生命周期的产生及消亡)。

定义一个service
和pod一样,service也是一个k8s REST对象,客户端可以通过APIServer发送一个POST请求来创建一个新的service实例。假设有一个pod集,该pod集中所有pod均被贴上labels:{“app”:“MyAPP”},且所有容器均对外暴露TCP9376端口,那么一个service的JSON文件可以这么写:

{
	"kind":"Service",
	"apiVersion":"v1",
	"metadata":{
	"name":"my-service",
	"labels":{
		"enviroment":"testing"
		}
	},
	"spec":{
		"selector":{
			"app":"MyAPP"
			},
			"ports":[{
				"protocol":"TCP",
				"port":80,
				"targetPort":9376
				}]
	}
}

该service能将外部流量转发到所有label匹配{“app”:“MyAPP”}的pod的TCP9376端口上,每个service会由系统分配一个虚拟IP地址作为service的入口IP地址(cluster IP),然后监听上述文件中的指定端口(上述的是80端口)。
而当名为"my-service"的service对象被创建后,系统就会随之创建一个同样名为my-service的Endpoints对象,该对象即保存了所有匹配label selector后端pod的IP地址和端口。

使用kubectl get services即可获取到service的IP地址信息。

NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   23h

使用service来代理遗留系统
与replication controller不同,service的selector其实是可选项。**这样做的目的是能用service来代理一些非pod或者得不到的label资源。**比如:

  • 访问k8s集群外部的一个数据库。
  • 访问其他namespace或者其他集群的service。
  • 任何其他类型的外部遗留系统。

所以定义一个没有selector属性的service对象,如下:

{
	"kind":"Service",
	"apiVersion":"v1",
	"metadata":{
		"name":"my-service"
	},
	"spec":{
			"ports":[{
				"protocol":"TCP",
				"port":80,
				"targetPort":1111
				}]
	}
}

由于该service没有selector,所以系统不会自动创建一个EndPoints对象,此时可以通过自定义一个EndPoints对象,将上述service对象映射到一个或者多个后端

{
	"kind":"Service",
	"apiVersion":"v1",
	"metadata":{
		"name":"my-service"
	},
	"subsets":[{
		"address":[{
			"IP":"1.2.3.4"
			}],
		"ports":[{
			"port":1111
		}]
	}]
}

service的使用示例

首先定义两个pod,都分别运行nginx容器。第一个80:8088,第二个80:8089(即容器内的80端口映射到宿主机的8088和8089)。
第一个:

{
	"kind":"Pod",
	"apiVersion":"v1",
	"metadata":{
	"name":"nignx-a",
	"labels":{
		"name":"service-nginx"
		}
	},
	"spec":{
		"containers":[{
			"name":"nginx",
			"image":"nginx:latest",
			"ports":[{
				"containerPort":80,
				"hostPort":8088
				}]
		}]
	}
}

第二个:

{
	"kind":"Pod",
	"apiVersion":"v1",
	"metadata":{
	"name":"nignx-b",
	"labels":{
		"name":"service-nginx"
		}
	},
	"spec":{
		"containers":[{
			"name":"nginx",
			"image":"nginx:latest",
			"ports":[{
				"containerPort":80,
				"hostPort":8089
				}]
		}]
	}
}

创建以上两个pod:

[root@master ~]# vi nginx-a.json
[root@master ~]# kubectl create -f nginx-a.json 
pod/nignx-a created
[root@master ~]# vi nginx-b.json
[root@master ~]# kubectl create -f nginx-b.json 
pod/nignx-b created

配置文件中,我们同时把pod的labels设置成为了name=service-nginx。比如用下面的查询语句查询label。

[root@master ~]# kubectl get pod -l name=service-nginx
NAME      READY   STATUS    RESTARTS   AGE
nignx-a   1/1     Running   0          2m34s
nignx-b   1/1     Running   0          2m10s

接下来定义一个service:

{
	"kind":"Service",
	"apiVersion":"v1",
	"metadata":{
		"name":"nginx-service-clusterip",
		"labels":{
			"name":"nginx-service-clusterip"
		}
	},
	"spec":{
		"selector":{
			"name":"service-nginx"
			},
		"ports":[{
			"port":80
			}]
		}
}

selector字段表明的是service选择的labels是{“name”:“service-nginx”}。
创建service并查看:

[root@master ~]# kubectl create -f NginxService.json 
service/nginxservice created
[root@master ~]# kubectl get service
NAME                      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
kubernetes                ClusterIP   10.96.0.1       <none>        443/TCP                         25h
nginx-service-clusterip   ClusterIP   10.98.112.219   <none>        80/TCP                          56s

我们可以看到 service nginxservice创建且分配到的入口是10.103.124.74,该Ip与8000端口组成该service的入口地址(cluster IP)。
可以再来查看映射关系:

[root@master ~]# kubectl get pod -o wide
NAME                READY   STATUS    RESTARTS   AGE    IP            NODE     NOMINATED NODE   READINESS GATES
nignx-a             1/1     Running   0          133m   10.244.0.14   master   <none>           <none>
nignx-b             1/1     Running   0          133m   10.244.0.15   master   <none>           <none>
sshd-test-service   1/1     Running   0          11m    10.244.0.18   master   <none>           <none>
[root@master ~]# kubectl describe service nginx-service-clusterip
Name:              nginx-service-clusterip
Namespace:         default
Labels:            name=nginxservice-clusterip
Annotations:       <none>
Selector:          name=service-nginx
Type:              ClusterIP
IP:                10.98.112.219
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.0.14:80,10.244.0.15:80
Session Affinity:  None
Events:            <none>

可以看到EndPoints分别对应了两个pod的IP+端口。
Service 通过 Cluster 内部的 IP 对外提供服务,只有 Cluster 内的节点和 Pod 可访问,这是默认的 Service 类型,前面实验中的 Service 都是 ClusterIP。

再定义一个sshd客户端pod。首先先搜索images:docker search sshd
在这里插入图片描述
这里我们决定下载bmoorman/sshd这个镜像。
所以写sshd-pod.json如下:

{
	"kind":"Pod",
	"apiVersion":"v1",
	"metadata":{
		"name":"sshd-test-service",
		"labels":{
			"user":"service-nginx",
			"name":"client-sshd"
		}
	},
	"spec":{
		"containers":[{
			"name":"client-container",
			"image":"bmoorman/sshd:latest",
			"ports":[{
				"containerPort":22,
				"hostPort":1314
			}]
		}]
	}
}

创建完成后进入pod:

[root@master ~]# kubectl create -f sshd-pod.json 
[root@master ~]# kubectl get pod
NAME                READY   STATUS    RESTARTS   AGE
nignx-a             1/1     Running   0          103m
nignx-b             1/1     Running   0          103m
sshd-test-service   1/1     Running   0          49s
[root@master ~]# kubectl exec -it sshd-test-service /bin/bash
root@sshd-test-service:/# curl 10.98.112.219:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
.....

也可以使用NodePort和LoadBalance的方式访问。在此不表。

[root@master ~]# cat nginx-service.yaml 
apiVersion: v1 
kind: Service 
metadata: 
  name: nginx-service-nodeport 
spec: 
  selector: 
    app: service-nginx 
  ports: 
    - name: http 
      port: 8000 
      protocol: TCP 
      targetPort: 80 
    - name: https 
      port: 8443 
      protocol: TCP 
      targetPort: 443 
  type: NodePort

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值