一、定义一个Service¶
1.定义一个nginx-deploy.yaml文件
$ vim nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.cn-hangzhou.aliyuncs.com/zq-demo/nginx:1.14.2
ports:
- containerPort: 80
说明:
spec.template.metadata.labels这个定义创建pod的标签
template:
metadata:
labels:
app: nginx # 这里设置的标签会被附加到每个Pod上
spec.selector.matchLabels这个定义如何选择要管理的pod
selector:
matchLabels:
app: nginx # 通过此标签选择需要管理的Pod
2.部署Pod节点
$ k create -f nginx-deploy.yaml
deployment.apps/nginx-deployment created
$ k get po
NAME READY STATUS RESTARTS AGE
nginx-deployment-df755dd4-2gqrj 1/1 Running 0 3h13m
nginx-deployment-df755dd4-wrqhv 1/1 Running 0 3h13m
nginx-deployment-df755dd4-xn6v6 1/1 Running 0 3h13m
3.定义一个服务yaml文件
$ vim service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
上面参数说明:
- protocol:k8s中service支持的协议(TCP、UDP、SCTP 等协议),默认为TCP协议。
- port:k8s中service自己的端口号,这里为80
- targetPort:k8s中容器的端口号,具体由这个后端服务来决定,这里后端服务是nginx,所以为80
其中Pod 中的端口定义是有名字的,你可以在 Service 的 targetPort 属性中引用这些名称。 例如,我们可以通过以下方式将 Service 的 targetPort 绑定到 Pod 端口:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.cn-hangzhou.aliyuncs.com/zq-demo/nginx:1.14.2
ports:
- containerPort: 80
name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: http-web-svc
4.检查所有Pod是否有app=nginx这个标签
$ k get po -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-deployment-df755dd4-2gqrj 1/1 Running 0 3h31m
nginx-deployment-df755dd4-wrqhv 1/1 Running 0 3h31m
nginx-deployment-df755dd4-xn6v6 1/1 Running 0 3h31m
5.部署服务,这里注意这两个命名空间一致
$ k create -f service.yaml
service/my-service created
6.查看服务
$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ClusterIP 10.0.35.90 <none> 80/TCP 7m2s
7.进行测试
$ curl 10.0.35.90
<!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>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
8.测试Pod删除后,服务是否可以被访问
(1)删除带有app=nginx标签的Pod
$ k delete po -l app=nginx
pod "nginx-deployment-df755dd4-2gqrj" deleted
pod "nginx-deployment-df755dd4-wrqhv" deleted
pod "nginx-deployment-df755dd4-xn6v6" deleted
(2)再次查看pod的IP地址,观察到pod的IP发生改变
$ k get po -l app=nginx -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-df755dd4-262sn 1/1 Running 0 2m12s 172.18.195.55 k8s-master03 <none> <none>
nginx-deployment-df755dd4-fwzjf 1/1 Running 0 2m12s 172.17.125.57 k8s-node01 <none> <none>
nginx-deployment-df755dd4-x952n 1/1 Running 0 2m12s 172.25.244.242 k8s-master01 <none> <none>
(3)再次查看服务
$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ClusterIP 10.0.35.90 <none> 80/TCP 152m
(4)进行服务测试,观察到虽然pod的IP发生改变,但是服务仍然可以访问
$ curl 10.0.35.90
<!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>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
9.登录进名为nginx-deployment-df755dd4-262sn的Pod中,进行同命名空间和非同命名空间进行访问测试
(1)同命名空间default访问测试,观察到访问成功
$ k exec -it nginx-deployment-df755dd4-262sn -- sh
/ # wget http://my-service
Connecting to my-service (10.0.35.90:80)
index.html 100% |*********************************| 612 0:00:00 ETA
(2)非同命名空间(default命名空间和kube-system命名空间之间)访问测试,观察到访问成功
#查看kube-system命名空间下的访问
$ k get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
calico-typha ClusterIP 10.0.135.70 <none> 5473/TCP 9d
kube-dns ClusterIP 10.0.0.10 <none> 53/UDP,53/TCP,9153/TCP 9d
metrics-server ClusterIP 10.0.179.106 <none> 443/TCP 9d
#在default命名空间下测试
$ k exec -it nginx-deployment-df755dd4-262sn -- sh
/ # wget http://metrics-server.kube-system:443
Connecting to metrics-server.kube-system:443 (10.0.179.106:443)
wget: server returned error: HTTP/1.0 400 Bad Request
二、Service类型¶
Kubernetes Service Type(服务类型)主要包括以下几种:
- ClusterIP:通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。
- NodePort:在所有安装了 Kube-Proxy 的节点上打开一个端口,此端口可以代理至后端 Pod,可以通过 NodePort 从集群外部访问集群内的服务,格式NodeIP:NodePort
- LoadBalancer:使用云提供商的负载均衡器公开服务,成本较高。
- ExternalName:通过返回
CNAME记录和对应值,可以将服务映射到externalName字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。需要使用 kube-dns 1.7 及以上版本或者 CoreDNS 0.0.8 及以上版本。
2.1 NodePort 类型¶
如果你将 type 字段设置为 NodePort,则 Kubernetes 控制平面将在 --service-node-port-range 标志指定的范围内分配端口(默认值:30000-32767)。 每个节点将那个端口(每个节点上的相同端口号)代理到你的服务中。 你的服务在其 .spec.ports[*].nodePort 字段中报告已分配的端口。
如果是二进制方式安装的k8s集群,可以通过以下方式查看分配的端口号:
$ grep "service-node-port-range" /usr/lib/systemd/system/kube-apiserver.service
--service-node-port-range=30000-32767 \
对于 NodePort 服务,Kubernetes 额外分配一个端口(TCP、UDP 或 SCTP 以匹配服务的协议)。 集群中的每个节点都将自己配置为监听分配的端口并将流量转发到与该服务关联的某个就绪端点。 通过使用适当的协议(例如 TCP)和适当的端口(分配给该服务)连接到所有节点, 你将能够从集群外部使用 通过某个宿主机的 IP+端口即可访问到后端的应用。
这里如果我们想自定义nodeport暴露的端口号,可以参考如下配置
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort:
nodePort: 30007
type: NodePort
说明:
- targetPort: 如果没有指定具体端口,则默认和port设置成一样,也就是80
- spec.ports.nodePort:通过这个字段来指定nodeport外部暴露的具体端口号
下面通过示例来说明:
1.定义一个yaml文件
$ vim service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort:
type: NodePort
说明:
- targetPort: 如果没有指定具体端口,则默认和port设置成一样,也就是80
2.部署服务
$ k replace -f service.yaml
service/my-service replaced
说明:上面已经部署过相同服务了,所以替代就可以了
3.查看暴露的端口号,这里为32459。其中二进制端口号设置目录在/usr/lib/systemd/system/kube-apiserver.service
$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service NodePort 10.0.35.90 <none> 80:32459/TCP 9h
4.任选一个节点IP:32459在浏览器进行访问进行验证,这里以192.168.1.31:32459为例。成功后,会显示nginx界面

2.2 ExternalName类型¶
ExternalName Service 是 Service 的特例,它没有 Selector,也没有定义任何端口和 Endpoint, 它通过返回该外部服务的别名来提供服务。
类型为 ExternalName 的服务将服务映射到 DNS 名称,而不是典型的选择算符,例如 my-service 或者 cassandra。 你可以使用 spec.externalName 参数指定这些服务。
例如,以下 Service 定义将 prod 名称空间中的 my-service 服务映射到 my.database.example.com:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
当查找主机 my-service.prod.svc.cluster.local 时,集群 DNS 服务返回 CNAME 记录, 其值为 my.database.example.com。 访问 my-service 的方式与其他服务的方式相同,但主要区别在于重定向发生在 DNS 级别,而不是通过代理或转发。 如果以后你决定将数据库移到集群中,则可以启动其 Pod,添加适当的选择算符或端点以及更改服务的 type。
同时,可以通过 ExternalName 类型的 Service 实现跨命名空间的服务访问解耦。具体方案如下:
# 将 B 命名空间的 mysql 服务映射到 A 命名空间的 mysql.a 服务
apiVersion: v1
kind: Service
metadata:
name: mysql # B 命名空间中的服务名称
namespace: b
spec:
type: ExternalName
externalName: mysql.a.svc.cluster.local # A 命名空间的服务完整域名
# 通过 DNS 名称透明访问,B 命名空间的应用只需访问本地的服务名(如 mysql:3306),Kubernetes DNS 会自动将其解析为 A 命名空间的实际服务地址 mysql.a.svc.cluster.local
# 如果 A 命名空间的服务名称或命名空间不变(例如仅更新 Pod IP 或端口),B 命名空间的 ExternalName Service 无需任何修改
# 如果 A 命名空间的服务名称或命名空间需要变更(例如从 mysql.a 改为 mysql-new.a),只需更新 B 命名空间中对应 ExternalName 的 externalName 字段即可。
下面以详细的示例进行说明:
准备环境
# 定义deployment的类型的yaml
$ vim nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.cn-hangzhou.aliyuncs.com/zq-demo/nginx:1.14.2
ports:
- containerPort: 80
# 创建deployment
kubectl apply -f nginx-deploy.yaml
1.定义一个关于svc的yaml文件
$ vim externalname-service.yaml
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
type: ExternalName
externalName: www.baidu.com
2.创建svc
$ k create -f externalname-service.yaml
3.查看svc
$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
external-service ExternalName <none> www.baidu.com <none> 49s
4.进入名为pod当中,使用wget访问external-service服务。
$ k get po
NAME READY STATUS RESTARTS AGE
nginx-deployment-df755dd4-262sn 1/1 Running 0 8h
nginx-deployment-df755dd4-fwzjf 1/1 Running 0 8h
nginx-deployment-df755dd4-x952n 1/1 Running 0 8h
$ k exec -it nginx-deployment-df755dd4-x952n -- sh
/ # wget external-service
Connecting to external-service (182.61.200.7:80)
wget: server returned error: HTTP/1.1 403 Forbidden
说明:403报错是因为百度那边有限制,但是可以访问!