Service 对象
为什么需要 service
每个 pod 都有自己的 ip 地址,但是在 Deployment 中,在同一时刻运行的 pod 集合可能与稍后运行该应用程序的 pod 集合不同。这导致了一个问题,如果一组 Pod(称为后端)为集群内的其他 pod (称为前端)提供功能,那么前端如何找出并跟踪要连接的 ip 地址,以便前端可以使用后端部分呢?
Service 概念
将运行在一组 pods 上的应用程序公开为网络服务的抽象方法。
使用 kubernetes 服务无需修改应用程序即可使用通用的服务发现机制。kubernetes 为 pods 提供自己的 ip 地址,并为一组 pod 提供相同的 DNS 名,并且可以在它们之间进行负载均衡。
定义 service
service 在 kubernetes 中是一个 REST 对象,和 pod 类似。像所有的 REST 对象一样,service 定义可以基于 POST 方式,请求 API server 创建新的实例。
创建一个 service
vim service.yaml
apiVersion: v1
kind: Service
metadata:
# service 的名称,将来会被用作请求的域名来被集群的内部或外部对象使用
name: my-service
spec:
# 匹配 pod
selector:
app: MyApp
ports:
- protocol: TCP
# service 的端口
port: 80
# 容器的端口
targetPort: 9376
kubectl create -f service.yaml
查看 service 信息
kubectl get svc
可以看到:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 47h
my-service ClusterIP 10.1.22.99 <none> 80/TCP 74s
用 service 暴露 pod 服务地址
创建一个 Deployment,包含两个 nginx pod ,具有一个容器端口80:
vim nginx-deplyment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
# 筛选
selector:
# 根据标签匹配
matchLabels:
# 标签
run: my-nginx
# pod 的数量
replicas: 2
# 模板
template:
metadata:
# 标签
labels:
run: my-nginx
spec:
# 容器
containers:
- name: my-nginx
image: registry.cn-beijing.aliyuncs.com/qingfeng666/nginx:latest
ports:
- containerPort: 80
kubectl create -f nginx-deplyment.yaml
kubectl get po
可以看到:
my-nginx-76d78c9795-5jzjg 1/1 Running 0 2m13s
my-nginx-76d78c9795-frcmq 1/1 Running 0 2m13s
查看 pod 的 ip 地址
kubectl get pods -l run=my-nginx -o yaml | grep podIP
上面的参数
-
-l
表示:label
-
-o
表示:output
可以看到:
podIP: 10.244.1.44
podIP: 10.244.2.5
不直接访问 pod ,通过 service 访问 pod。创建 service :
vim nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
kubectl create -f nginx-service.yaml
kubectl get svc
查看 service 的描述信息
kubectl describe svc my-nginx
可以看到:
Name: my-nginx
Namespace: default
Labels: run=my-nginx
Annotations: <none>
Selector: run=my-nginx
Type: ClusterIP
IP: 10.1.12.12
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.44:80,10.244.2.5:80
Session Affinity: None
Events: <none>
curl 10.1.12.12:80
可以看到:
Welcome to nginx!
集群内 pod 通信机制
Kubernetes 支持两种基本的服务发现模式:环境变量和 DNS
环境变量
当 pod 运行在 node 上,kubelet 会为每个活跃的 service 添加一组环境变量。它同时支持 Docker Links,简单的 {SVCNAME}_SERVICE_HOST
和 {SVCNAME}_SERVICE_PORT
变量。这里 service 的名称需要大写,横线被转换成下划线。
举个例子,一个名称是 my-nginx
的 service 暴露了 TCP 端口 80,同时给它分配了 Cluster IP 地址 10.1.180.155 ,这个 service 生成了如下环境变量:
MY_NGINX_PORT_80_TCP_PORT=80
MY_NGINX_PORT_80_TCP_PROTO=tcp
MY_NGINX_PORT_80_TCP_ADDR=10.1.180.155
Service 要比 pod 先创建,才能把环境变量写入 pod
创建 service:
kubectl create -f nginx-service.yaml
创建 Deployment:
kubectl create -f nginx-deplyment.yaml
查看 pod :
kubectl get po
进入 pod:
kubectl exec -it my-nginx-76d78c9795-5jzjg sh
打印环境变量:
printenv
# printenv |grep MY_NGINX
MY_NGINX_PORT=tcp://10.1.99.100:80
MY_NGINX_SERVICE_PORT=80
MY_NGINX_PORT_80_TCP_ADDR=10.1.99.100
MY_NGINX_PORT_80_TCP_PORT=80
MY_NGINX_PORT_80_TCP_PROTO=tcp
MY_NGINX_PORT_80_TCP=tcp://10.1.99.100:80
MY_NGINX_SERVICE_HOST=10.1.99.100
DNS
可以通过使用附加组件为 kubernetes 集群设置 DNS 服务
支持集群的 DNS 服务器(例如 CoreDNS)监视 kubetnetes API 中的新服务,并为每个服务创建一组 DNS 记录。如果在整个集群中都启用了 DNS ,则所有 pod 都应该能够通过其 DNS 名称自动解析服务。
举个例子, 如果在 kubernetes 命名空间 my-ns
中有一个名为 my-service
的新服务,则控制节点和 DNS 服务共同为 my-service.my-ns
创建 DNS 纪录。my-ns
命名空间中的 pod 应该能够通过对 my-service
进行名称查找来找到它。(my-service.my-ns
也可以)
其他命名空间中的 pod 必须将名称限定为 my-service.my-ns
。这些名称将解析为为服务分配的集群 ip 。
Service 创建 DNS 纪录
Kubernetes DNS 在集群上调度 DNS Pod 和服务,并配置 kubelet 以告知各个容器使用 DNS 服务的 IP 来解析 DNS 名称。
创建一个 service 和两个 pod:
vim dns.yaml
apiVersion: v1
kind: Service
metadata:
name: default-subdomain
spec:
selector:
name: busybox
clusterIP: None
ports:
- name: foo
port: 1234
targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
name: busybox1
labels:
name: busybox
spec:
hostname: busybox-1
subdomain: default-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox
---
apiVersion: v1
kind: Pod
metadata:
name: busybox2
labels:
name: busybox
spec:
hostname: busybox-2
subdomain: default-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox
hostname
表示 pod 的主机名
subdomain
表示 pod 的子域名
kubectl create -f dns.yaml
可以看到:
service/default-subdomain created
pod/busybox1 created
pod/busybox2 created
查看 pod
kubectl get po
可以看到:
NAME READY STATUS RESTARTS AGE
busybox1 1/1 Running 0 2m14s
busybox2 1/1 Running 0 2m14s
进入 pod 查看域名:
[root@node1 ~]# kubectl exec -it busybox2 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # hostname
busybox-2
/ # hostname -f
busybox-2.default-subdomain.default.svc.cluster.local
/ # exit
[root@node1 ~]#
从集群外部访问 service
从集群外部访问 service 的方法:
-
ClusterIP
仅仅使用一个集群内部的 IP 地址--这是默认值。选择这个值意味着这个服务你只想在集群内部被访问到。
-
NodePort
在集群内部 IP 的基础上,在集群的每一个节点的端口上开放这个服务。你可以在任意 NodePort 地址上访问到这个服务。
-
LoadBalancer
在使用一个集群内部地址和在 NodePort 上开放一个 service 的基础上,还可以向云提供者申请一个负载均衡器,将流量转发到已经以 NodePort 的形式开放的 service 上。
创建 nginx pod 和 NodePort 类型的 service
vim nodeport.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
---
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx-deployment
spec:
ports:
- port: 80
name: nginx-service80
protocol: TCP
targetPort: 80
nodePort: 30001
selector:
app: nginx
type: NodePort
创建
kubectl create -f nodeport.yaml
可以看到
deployment.apps/nginx-deployment created
service/nginx-deployment created
kubectl get all
可以看到
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-6799fc88d8-lh9m5 1/1 Running 0 104s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 6d5h
service/nginx-deployment NodePort 10.1.238.159 <none> 80:30001/TCP 104s
通过浏览器访问:192.168.190.132:30001
(ip 是节点的 )
可以看到:
Welcome to nginx!
访问的流程:
节点 ip:30001 > service:80 > pod:80
Ingress
Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是:HTTP
Ingress 公开了从集群外部到集群内部的 HTTP 和 HTTPS 路由,流量路由由 Ingress 资源上定义的规则控制
创建一个 Ingress 资源
vim minimal-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
- 可选的
host
。上面的例子中没有指定 host,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。如果提供了 host ,则规则(rules)适用于该 host 。 - 路径列表
paths
。每个路径都有一个由 serviceName 和 servicePort 定义的关联后端。在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。 - 后端
backend
。是 service 文档中所述的服务和端口名称的组合。与规则的 host 和 paths 匹配的 Ingress 的 HTTP (和 HTTPS)请求将发送到列出的 backend 。
安装 Nginx Ingress 控制器
vim ingress-nginx-controller.yaml
apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: tcp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: udp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: nginx-ingress-clusterrole
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- nodes
- pods
- secrets
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- "extensions"
- "networking.k8s.io"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- "extensions"
- "networking.k8s.io"
resources:
- ingresses/status
verbs:
- update
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: nginx-ingress-role
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- pods
- secrets
- namespaces
verbs:
- get
- apiGroups:
- ""
resources:
- configmaps
resourceNames:
- "ingress-controller-leader-nginx"
verbs:
- get
- update
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: nginx-ingress-role-nisa-binding
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: nginx-ingress-role
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: nginx-ingress-clusterrole-nisa-binding
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-clusterrole
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
kind: Service
apiVersion: v1
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
clusterIP: 10.1.211.240
externalTrafficPolicy: Cluster
ports:
- name: http
nodePort: 31686
port: 80
protocol: TCP
targetPort: http
- name: https
nodePort: 30036
port: 443
protocol: TCP
targetPort: https
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
sessionAffinity: None
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
spec:
terminationGracePeriodSeconds: 300
serviceAccountName: nginx-ingress-serviceaccount
containers:
- name: nginx-ingress-controller
image: registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.26.1
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --publish-service=$(POD_NAMESPACE)/ingress-nginx
- --annotations-prefix=nginx.ingress.kubernetes.io
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 33
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
---
创建
kubectl apply -f ingress-nginx-controller.yaml
查看,指定 namespace
kubectl get all -n ingress-nginx
可以看到
NAME READY STATUS RESTARTS AGE
pod/nginx-ingress-controller-95598b695-4j5nw 1/1 Running 0 84s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx NodePort 10.1.211.240 <none> 80:31686/TCP,443:30036/TCP 84s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-ingress-controller 1/1 1 1 84s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-ingress-controller-95598b695 1 1 1 84s
检查部署版本
POD_NAMESPACE=ingress-nginx
POD_NAME=$(kubectl get pods -n $POD_NAMESPACE -l app.kubernetes.io/name=ingress-nginx --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --version
可以看到
NGINX Ingress controller
Release: 0.26.1
Build: git-2de5a893a
Repository: https://github.com/kubernetes/ingress-nginx
nginx version: openresty/1.15.8.2
Nginx Ingress 控制器实践
部署一个 hello world 服务
-
创建一个 Deployment
kubectl create deployment web --image=registry.cn-beijing.aliyuncs.com/qingfeng666/hello-app:1.0
可以看到:
deployment.apps/web created
-
把 Deployment 暴露出来
kubectl expose deployment web --type=NodePort --port=8080
expose
这里会创建一个 NodePort 类型的 service ,端口是 8080可以看到:
service/web exposed
查看 service
kubectl get svc
可以看到:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE web NodePort 10.1.163.170 <none> 8080:31886/TCP 93s
-
使用节点 ip 加端口 31886 访问
curl node1:31886
可以看到:
Hello, world! Version: 1.0.0 Hostname: web-6db77f5fdb-kh7ks
创建一个 Ingress 资源
vim example-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: hello-world.info
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 8080
创建
kubectl apply -f example-ingress.yaml
可以看到:
ingress.networking.k8s.io/example-ingress created
查看 Ingress
kubectl get ing
可以看到
NAME CLASS HOSTS ADDRESS PORTS AGE
example-ingress <none> hello-world.info 10.1.211.240 80 42s
添加域名
vim /etc/hosts
添加
10.1.211.240 hello-world.info
验证 Ingress 能够转发请求
curl hello-world.info
可以看到
Hello, world!
Version: 1.0.0
Hostname: web-6db77f5fdb-kh7ks