Kubernetes Cookbook 编程指南 中文版教程
创建时间:2018-12-07  访问量:3673  7  0

Kubernetes Cookbook 编程指南 中文版教程

服务(Service)的使用

网络服务是一个能接收请求并提供解决方案的应用。客户端通过网络连接访问服务。他们不需要知道服务本身的架构或服务是如何运行的。客户端唯一需要验证的就是服务的端点是否是可连接的,然后遵循它的使用规则解决问题。Kubernetes服务与之有类似的概念。在达到它们所需的功能之前是不需要知道每一个Pod的。对于Kubernetes 系统外的组件,他们仅仅是通过暴露的端口访问Kubernetes服务来与相关运行的Pod进行通信。它们不需要关注容器的IP和端口。因此,我们实现零停机时间来更新容器应用程序是没有任何压力的。 

上面的图片显示了服务的基本架构并实现的如下所示的概念:

  • 与副本控制器类似,服务指向的Pod都需要包含服务Selector标签。用另一句话说,服务选择Pod是通过它们的标签进行的。

  • 发送到服务请求负载将分发给四个Pods。

  • 副本控制器保证运行的Pod的数量在一个期望状态。它监示服务相关的Pod,保证在Pod崩溃时有其它Pod能够接替服务的职责。

本节,你将学习如何创建与Pod一起工作的服务。

开始

在申请服务之前,先验证系统中的所有Node节点中是否都运行了kube-proxy进程。kube-proxy守护进程主要负责Node中的网络代理。它帮助在每个Node节点上映射服务设置如IP或端口。为了检查是否启用了kube-proxy进程,你可以查看此守护进程的状态或在Node节点上通过指定的名称查看运行中的进程:

// check the status of service kube-proxy
# service kube-proxy status
or
// Check processes on each node, and focus on kube-proxy
// grep "kube-proxy" or "hyperkube proxy"
# ps aux | grep "kube-proxy"

为了在接下来的章节中进行演示,你也可以在Master节点中安装一个私有网络。与网络设置相关的守护进程是flanneld和kube-proxy。对于在单台机器中操作与验证是非常容易的。另外,请验证Node节点中的Kubernetes服务,黙认情况下,已经有一个内部网络。

如何去做...

我们可以通过CLI或一个配置文件来定义或创建一个新的Kubernetes服务。这里我们将解释如何通过命令行来部署服务。在以下各种情形的命令中使用了expose和describe子命令。本节我们使用的Kubernetes版本是1.1.3。对于文件格式的创建。更详细的讨论请参考第3章操作配置文件中的玩转容器这一节。

当创建服务的时候,我们需要关心两个配置:一个是标签(label),另一个是端口(port)。正如下面的图片所显示的,服务和pod都有它们自己的键值对标签和端口。确保这些设置都使用了正确的选项标记:

为了创建如上所示的这个服务,运行以下命令:

# kubectl expose pod <POD_NAME> --labels="Name=Amy-log-service" --selector="App=LogParser,Owner=Amy" --port=8080 --target-port=80

expose子命令中的--labels选项标记使用键值对标记了服务。它用于标记服务。为了为服务定义Selector选择器,使用--selector选项标记。如果服务没有设置selector选择器,那么这个selector选择器将与资源的labels标签相同。在上面的图片中,selector选择器中还有一个附加的label标签是Version=1.0。

为了暴露服务的端口,我们在expose子命令中使用--port选项标记来发送一个端口号。如果没有指定要分配的端口,那么服务将使用容器的端口作为其暴露的端口。另一方面,--target-port选项标记指出了服务的容器端口。当目标端口(target-port)与容器暴露的端口不同时,用户将得到一个空的响应。同时,如果我们仅分配了服务端口,那黙认的目标端口将与服务端口相同。以上面的图片为例,假设我们没有使用--target-port选项标记,那么请求流将指向容器端口8080,这将会得到一个拒绝连接的错误。

为不同的资源创建服务

你可以为一个Pod,一个副本控制器(Replication Controller)和一个Kubernetes系统之外的端点附加一个服务,或其它服务。在下一页,我们将向你一步一步展示这些。服务的创建格试:kubectl expose RESOURCE_TYPE RESOURCE_NAME [TAGS] 或者 kubectl expose -f CONFIGURATION_FILE [TAGS]。简而言之,资源类型为Pod,副本控制器和服务都支持expose子命令。因此,配置文件也遵循这样的类型限制。

为一个Pod创建一个服务

由服务封装的pod需要包含label标签,因为服务要实现这个功能就需要基于selector选择器这样一个必要条件:

// Create a pod, and add labels to it for the selector of service.
# kubectl run nginx-pod --image=nginx --port=80 --restart="Never" --labels="app=nginx"
pod "nginx-pod" created
# kubectl expose pod nginx-pod --port=8000 --target-port=80 --name="service-pod"
service "service-pod" exposed

Kubernetes资源名称的缩写

当使用CLI来管理资源时,你可以键入它们名称的缩写来代替完整的名称,这样可以减小时间并辟免一些输入错误。

资源类型 缩写别名
Componentstatuses cs
Events ev
Endpoints ep
Horizontalpodautoscalar hpa
Limitranges limits
Nodes no
Namespaces ns
Pods ps
Persistentvolumes pv
Persistentvolumesclaims pvc
Resourcequotas qotas
Replicationcontrollers rc
Services svc
Ingress ing
// "svc" is the abbreviation of service
# kubectl get svc service-pod
NAME CLUSTER_IP EXTERNAL_IP PORT(S) SELECTOR AGE
service-pod 192.168.195.195 <none> 8000/TCP app=nginx 11s

 正如你所看到的在这些命令中,我们使用暴露的端口8000来打开一个服务。我们为什么需要指定容器端口,因为这个服务黙认不能将8000作为容器的端口。为了验证服务是否可以工作,在内网环境(安装了Kubernetes集群CIDR)中使用下面的命令。

// accessing by services CLUSTER_IP and PORT
# curl 192.168.195.195:8000

为副本控制器创建一个服务并添加一个外部的IP

副本控制器是服务的理想资源类型。对于由副本控制器监视的pod, Kubernetes系统有一个控制器管理器来查看它们的生命周期。它还有助于通过将现有服务绑定到另一个副本控制器来更新程序的版本或状态:

// Create a replication controller with container port 80 exposed
# kubectl run nginx-rc --image=nginx --port=80 --replicas=2
replicationcontroller "nginx-rc" created
# kubectl expose rc nginx-rc --name="service-rc" --external-ip="<USER_SPECIFIED_IP>"
service "service-rc" exposed

在这种情况下,我们可以为服务提供另外一个IP地址,这个IP地址不要求是集群网络内部的IP。子expose的--external-ip选项标记可以实现静态IP需求。注意,用户指定的IP地址是要可以连通的,例如,使用Master节点的公共IP:

// EXTERNAL_IP has Value shown on
# kubectl get svc service-rc
NAME CLUSTER_IP EXTERNAL_IP PORT(S) SELECTOR AGE
service-rc 192.168.126.5 <USER_SPECIFIED_IP> 80/TCP run=nginx-rc 4s

现在,你可以通过192.168.126.5:80或者<USER_SPECIFIED_IP>:80来验证服务:

// Take a look of service in details
# kubectl describe svc service-rc
Name: service-rc
Namespace: default
Labels: run=nginx-rc
Selector: run=nginx-rc
Type: ClusterIP
IP: 192.168.126.5
Port: <unnamed> 80/TCP
Endpoints: 192.168.45.3:80,192.168.47.2:80
Session Affinity: None
No events.

你将发现服务的label标签和selector选择器副本控制器黙认的。另外,有多个端点,这些都是副本控制器的副本,对于处理服务的请求都是可用的。

为一个端点创建一个no-selector无选择器的服务

首先,你应该有一个具有IP地址的端点。例如,我们可以实例中生成单个容器,它位于Kubernetes系统之外,但仍然可以被连接:

// Create an nginx server on another instance with IP address <FOREIGN_IP>
# docker run -d -p 80:80 nginx
2a17909eca39a543ca46213839fc5f47c4b5c78083f0b067b2df334013f62002
# docker ps
CONTAINER ID IMAGE COMMAND
CREATED STATUS PORTS
NAMES
2a17909eca39 nginx "nginx -g
'daemon off" 21 seconds ago Up 20 seconds 0.0.0.0:80->80/
tcp, 443/tcp goofy_brown

然后,在Master节点中,我们可以通过使用配置文件来创建一个Kubernetes端点。端点名称为service-foreign-ep。我们可以在模板中配置多个IP地址和端口:

# cat nginx-ep.json
{
	"kind": "Endpoints",
	"apiVersion": "v1",
    "metadata": {
    	"name": "service-foreign-ep"
    },
    "subsets": [
        {
            "addresses": [
            	{ "ip": "<FOREIGN_IP>" }
            ],
            "ports": [
            	{ "port": 80 }
            ]
         }
     ]
}
# kubectl create -f nginx-ep.json
endpoints "service-foreign-ep" created
# kubectl get ep service-foreign-ep
NAME ENDPOINTS AGE
service-foreign-ep <FOREIGN_IP>:80 16s

正如前面部分提到的,我们可以使用expose子命令为资源配置模板启动服务。然而,CLI不支持在文件格式中暴露一个端点:

// Give it a try!
# kubectl expose -f nginx-ep.json
error: invalid resource provided: Endpoints, only a replication
controller, service or pod is accepted

因此,我们可以通过配置文件创建一个服务:

# cat service-ep.json
{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
    	"name": "service-foreign-ep"
    },
    "spec": {
        "ports": [
            {
                "protocol": "TCP",
                "port": 80,
                "targetPort" : 80
            }
        ]
    }
}

所有最重要的就是这里没有在模板中定义selector选择器。因为端点不在Kubernetes系统中,所以这是非常合理的。端点和服务之间的关系是通过资源名建立的。正如你看到的,服务名称必须与端点名称相同:

# kubectl create -f service-ep.json
service "service-foreign-ep" created
// Check the details in service
# kubectl describe svc service-foreign-ep
Name: service-ep
Namespace: default
Labels: <none>
Selector: <none>
Type: ClusterIP
IP: 192.168.234.21
Port: <unnamed> 80/TCP
Endpoints: <FOREIGN_IP>:80
Session Affinity: None
No events.

最终,为外部端点创建了这个no-selector服务。使用<FOREIGN_IP>:80验证结果。

基于另一个服务创建具有会话关联(Session Affinity)的服务

通过expose子命令,我们也可以将一个服务的设置复制到另一个服务:

// Check the service we created for replication controller in previous
section
# kubectl describe svc service-rc
Name: service-rc
Namespace: default
Labels: run=nginx-rc
Selector: run=nginx-rc
Type: ClusterIP
IP: 192.168.126.5
Port: <unnamed> 80/TCP
Endpoints: 192.168.45.3:80,192.168.47.2:80
Session Affinity: None
No events.
//Create a new service with different name and service port
# kubectl expose svc service-rc --port=8080 --target-port=80 --name=service-2nd --session-affinity="ClientIP"
service "service-2nd" exposed

重置名为service-2nd的新服务,服务端口8080,启用Session Affinity:

# kubectl describe svc service-2nd
Name: service-2nd
Namespace: default
Labels: run=nginx-rc
Selector: run=nginx-rc
Type: ClusterIP
IP: 192.168.129.65
Port: <unnamed> 8080/TCP
Endpoints: 192.168.45.3:80,192.168.47.2:80
Session Affinity: ClientIP
No events.

目录,ClientIP是--session-affinity选项标记的唯一设置值。当启用了对ClientIP的会话关联,而不是轮循时,应该将服务发送到哪个端点的请求将由ClientIP决定。例如,如果客户端发送CIDR范围192.168.45.0/24的请求到服务service-2nd,它们将被传送到端点为192.168.45.7:80上。

创建不同类型的服务

有三种类型的服务:ClusterIP、NodePort和LoadBalancer:

 

默认情况下,每个服务都创建为ClusterIP类型。 ClusterIP类型的服务将随机分配一个内部IP地址。 对于NodePort类型,它涵盖了ClusterIP的功能,还允许用户使用相同的端口公开每个Node节点上的服务。 LoadBalancer位于其他两种类型的顶部。 LoadBalancer服务将在内部和节点上公开。 除此之外,如果您的云提供商支持外部负载平衡服务器,您可以将负载均衡器IP绑定到服务,这将成为另一个暴露点。

创建一个NodePort类型的服务

接下来,我们将向你展示如何创建一个NodePort服务。expose子命令中的--type选项标记可以帮助你定义服务类型:

// Create a service with type NodePort, attaching to the replication controller we created before
# kubectl expose rc nginx-rc --name=service-nodeport --type="NodePort" service "service-nodeport" exposed
# kubectl describe svc service-nodeport
Name: service-nodeport
Namespace: default
Labels: run=nginx-rc
Selector: run=nginx-rc
Type: NodePort
IP: 192.168.57.90
Port: <unnamed> 80/TCP
NodePort: <unnamed> 31841/TCP
Endpoints: 192.168.45.3:80,192.168.47.2:80
Session Affinity: None
No events.

在前一种情况下,在Node节点上暴露的网络端口31841是系统随机分配; 默认端口范围是30000到32767。请注意,端口在系统中的每个节点上都是公开的,因此可以通过<NODE_IP>:31841访问服务,例如,通过节点的域名,如kube-node1:31841。

删除一个服务

如果您想停止服务,可以简单地使用子命令delete:

# kubectl delete svc <SERVICE_NAME>
service "<SERVICE_NAME>" deleted

它是如何工作的

Kubernetes系统中执行服务环境的主要角色是flanneld和kube-proxy。守护进程flanneld通过从预先配制的地址空间中分配子网租约,并将网络配制存储在etcd中,而kube-proxy则指导服务和pod的端点,从而构建集群网络。

还可以参考

为更好的使用各项服务,我们亦建议大家阅读以下章节:

  • 副本控制器(replication controller)的使用

  • 标签(labels)和选择器(selectors)的使用

  • 第3章玩转容器章,配置文件的使用

  • 第5章构建一个持续交付管线,由整体转到微服务