k8s基本使用入门-了解Service
# 1,引入。
现在准备了两个 pod 的 yaml 文件。
pod_nginx.yml
[root@master services]$cat pod_nginx.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx
ports:
- name: nginx-port
containerPort: 80
2
3
4
5
6
7
8
9
10
11
12
13
14
pod_busybox.yml
[root@master services]$cat pod_busybox.yml
apiVersion: v1
kind: Pod
metadata:
name: busybox-pod
labels:
app: busybox
spec:
containers:
- name: busybox-container
image: busybox
command:
- sleep
- "360000"
2
3
4
5
6
7
8
9
10
11
12
13
14
启动。
kubectl create -f pod_nginx.yml
kubectl create -f pod_busybox.yml
2
然后看一下两个 pod 的 ip:
[root@master services]$kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
busybox-pod 1/1 Running 0 15m 10.244.1.58 node
nginx-pod 1/1 Running 0 15m 10.244.1.59 node
2
3
4
此时,这两个生成的 ip,在集群当中任一节点里,都是可以畅通访问的。
详细的介绍,可以看官方的文档介绍:https://kubernetes.io/docs/concepts/cluster-administration/networking/
# 2,缘由。
现在可以思考一个问题:为什么不直接通过 pod 来作为 k8s 的管理单位进行管理呢?
- 当我们使用 ReplicaSet 或者 ReplicationController 做水平扩展 scale 的时候,Pod 会在这个过程中被 terminated,随着这个更替,Pod 的 ip 等的也都在变幻。
- 当我们使用 Deployment 的时候,我们去更新 Docker Image Version,旧的 Pods 会被 terminated,然后新的 Pods 创建,这个过程中,同样会发生 Pod 的 ip 改变等问题。从而难于对其进行访问。
# 3,service 概念。
之于如上所提问题,那么就引入了 service 这个概念。
k8s 分配给 Service 一个固定 IP,这是一个虚拟 IP(也称为 ClusterIP),并不是一个真实存在的 IP,而是由 k8s 虚拟出来的。虚拟 IP 的范围通过 k8s API Server 的启动参数 –service-cluster-ip-range=19.254.0.0/16 配置;
虚拟 IP 属于 k8s 内部的虚拟网络,外部是寻址不到的。在 k8s 系统中,实际上是由 k8s Proxy 组件负责实现虚拟 IP 路由和转发的,所以 k8s Node 中都必须运行了 k8s Proxy,从而在容器覆盖网络之上又实现了 k8s 层级的虚拟转发网络。
服务代理:
在逻辑层面上,Service 被认为是真实应用的抽象,每一个 Service 关联着一系列的 Pod。在物理层面上,Service 有事真实应用的代理服务器,对外表现为一个单一访问入口,通过 k8s Proxy 转发请求到 Service 关联的 Pod。
Service 同样是根据 Label Selector 来刷选 Pod 进行关联的,实际上 k8s 在 Service 和 Pod 之间通过 Endpoint 衔接,Endpoints 同 Service 关联的 Pod;相对应,可以认为是 Service 的服务代理后端,k8s 会根据 Service 关联到 Pod 的 PodIP 信息组合成一个 Endpoints。
4,service 几种类型。
# 1,ClusterIP 方式。
kubernetes 默认就是这种方式,是集群内部访问的方式,外部的话,是无法访问的。
spec:
clusterIP: 10.0.0.1
ports:
- name: http
2
3
4
# 2,NodePort 方式
NodePort 方式主要通过节点 IP 加端口的形式暴露端口,是最常见也常用的方式,比较推荐。
apiVersion: v1
kind: Service
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"creationTimestamp":"2017-05-24T06:38:16Z","labels":{"kubernetes.io/name":"heapster","plugin":"heapster"},"name":"heapster","namespace":"kube-system","resourceVersion":"173906247","selfLink":"/api/v1/namespaces/kube-system/services/heapster","uid":"91470fbb-404b-11e7-ba41-5254eec04736"},"spec":{"clusterIP":"10.3.129.117","externalTrafficPolicy":"Cluster","ports":[{"nodePort":30003,"port":80,"protocol":"TCP","targetPort":8082}],"selector":{"k8s-app":"heapster"},"sessionAffinity":"None","type":"NodePort"},"status":{"loadBalancer":{}}}
creationTimestamp: 2018-07-25T03:22:01Z
labels:
kubernetes.io/name: heapster
plugin: heapster
name: heapster
namespace: kube-system
resourceVersion: "789489505"
selfLink: /api/v1/namespaces/kube-system/services/heapster
uid: e56886a2-8fb9-11e8-acd2-5254171bf8db
spec:
clusterIP: 10.3.129.117
externalTrafficPolicy: Cluster
ports:
- nodePort: 30003
port: 80
protocol: TCP
targetPort: 8082
selector:
k8s-app: heapster
sessionAffinity: None
type: NodePort
status:
loadBalancer: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 3,LoadBalancer 方式
这种方式主要是利用其他第三方的 LB 暴露服务的,谷歌或者亚马逊的 LB 等等
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
loadBalancerIP: 78.11.24.19
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 146.148.47.155
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4,ExternalName 方式
这种方式主要是通过 CNAME 实现的,在 kubernetes 1.7.x 以及以上才支持这种方式,例如
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
2
3
4
5
6
7
8
访问时,kube-dns 服务直接解析到指定的 Cnamemy.database.example.com 这个域名上。
# 5,service 学习示例。
# 1,cluster IP。
首先准备一个 deployment 的 yaml 文件:
[root@master deployment]$cat deployment_python_http.yaml
apiVersion: extensions/v1bete1
kind: Deployment
metadata:
name: service-test
spec:
replicas: 2
selector:
matchLabels:
app: service_test_pod
template:
metadata:
labels:
app: service_test_pod
spec:
containers:
- name: simple-http
image: python:2.7
imagePullPolicy: IfNotPresent
command: ["/bin/bash"]
args: ["-c","echo \"<p>Hello from $(hostname)<p>\" > index.html; python -m SimpleHTTPServer 8080"]
ports:
- name: http
containerPort: 8080
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
创建之前,可以先在 node 节点将镜像 pull 下来,以免等会儿创建的时候比较慢。
[root@master deployment]$kubectl create -f deployment_python_http.yaml
deployment.extensions "service-test" created
[root@master deployment]$kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
service-test 2 2 2 2 16s
[root@master deployment]$kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox-pod 1/1 Running 0 2h
nginx-pod 1/1 Running 0 2h
service-test-7bd7775475-gsmqn 1/1 Running 0 47s
service-test-7bd7775475-mrd8d 1/1 Running 0 47s
2
3
4
5
6
7
8
9
10
11
先在分别访问一下这两个 pod:
[root@master deployment]$curl 10.244.1.60:8080
<p>Hello from service-test-7bd7775475-gsmqn<p>
[root@master deployment]$curl 10.244.1.61:8080
<p>Hello from service-test-7bd7775475-mrd8d<p>
2
3
4
看到返回了两个不同的结果,之前有对这个名称做过分析,那么现在将请求的返回数据掐在 service 阶段,从而让访问统一,让返回值负载均衡。
[root@master deployment]$kubectl expose deployment service-test
service "service-test" exposed
[root@master deployment]$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
service-test ClusterIP 10.100.53.40 <none> 8080/TCP 5s
[root@master deployment]$curl 10.100.53.40:8080
<p>Hello from service-test-7bd7775475-mrd8d<p>
[root@master deployment]$curl 10.100.53.40:8080
<p>Hello from service-test-7bd7775475-gsmqn<p>
[root@master deployment]$curl 10.100.53.40:8080
<p>Hello from service-test-7bd7775475-gsmqn<p>
[root@master deployment]$curl 10.100.53.40:8080
<p>Hello from service-test-7bd7775475-gsmqn<p>
[root@master deployment]$curl 10.100.53.40:8080
<p>Hello from service-test-7bd7775475-mrd8d<p>
[root@master deployment]$curl 10.100.53.40:8080
<p>Hello from service-test-7bd7775475-mrd8d<p>
[root@master deployment]$curl 10.100.53.40:8080
<p>Hello from service-test-7bd7775475-mrd8d<p>
[root@master deployment]$curl 10.100.53.40:8080
<p>Hello from service-test-7bd7775475-gsmqn<p>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
看上去权重貌似是 3。
实验快速更新部署。
现在同时开启两个窗口,一个窗口一直 curl 刚刚的 CLUSTER-IP。
while true; do curl 10.100.53.40:8080 && sleep 1; done
<p>Hello from service-test-7bd7775475-c6cfp<p>
<p>Hello from service-test-7bd7775475-bmlk4<p>
<p>Hello from service-test-7bd7775475-c6cfp<p>
<p>Hello from service-test-7bd7775475-bmlk4<p>
2
3
4
5
6
那么现在模拟应用升级,可以通过直接编译 yaml 文件来实现。
[root@master deployment]$kubectl edit deployment service-test
在刚刚的echo处更改一下输出:
添加一个new version然后保存退出:
deployment.extensions "service-test" edited
2
3
4
5
不用动,可以看到下边的输出已经自动的变化了,基本上零停顿。
# 2,NodePort。
了解之前,先将之前所有的环境都清理一波,还原成一个干净的环境。
1,通过指令创建 NodePort。
准备有 nginx 的一个 yaml 文件:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx
ports:
- name: nginx-port
containerPort: 80
2
3
4
5
6
7
8
9
10
11
12
13
启动这个常规的 pod:
[root@master services]$kubectl create -f pod_nginx.yml
pod "nginx-pod" created
[root@master services]$kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-pod 0/1 ContainerCreating 0 5s
[root@master services]$kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-pod 1/1 Running 0 48m 10.244.1.72 node
2
3
4
5
6
7
8
现在将其配置为 NodePort 的形式,使用如下指令:
[root@master services]$kubectl expose pod nginx-pod --type=NodePort
service "nginx-pod" exposed
[root@master services]$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
nginx-pod NodePort 10.101.193.19 <none> 80:30338/TCP 18s
2
3
4
5
6
7
这个时候可以看到类型变成 NodePort 了,而且后边也详细标出了端口的映射关系,是将 pod 的 80 端口映射到 node 的 30338 上。
那么现在就可以通过这种方式就可以访问了:
master:
node:
2,通过 yaml 形式创建 NodePort。
现在介绍另外一种情况,介绍之前,先将刚刚的 service 删除。
[root@master services]$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
nginx-pod NodePort 10.101.193.19 <none> 80:30338/TCP 8m
[root@master services]$kubectl delete svc nginx-pod
service "nginx-pod" deleted
[root@master services]$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
2
3
4
5
6
7
8
9
然后添加一个文件:
[root@master services]$more service_nginx.yml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
ports:
- port: 30022
nodePort: 30022
targetPort: nginx-port
protocol: TCP
selector:
app: nginx
type: NodePort
2
3
4
5
6
7
8
9
10
11
12
13
14
此文件内容是基于刚刚还存在的 pod 而成立的,selector,就是检索刚刚创建的 nginx 的 pod,然后直接将端口类型设置为 NodePort,端口为 30022。
然后创建之:
[root@master services]$kubectl create -f service_nginx.yml
service "nginx-service" created
[root@master services]$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
nginx-service NodePort 10.96.139.29 <none> 30022:30022/TCP 4s
2
3
4
5
6
访问之: