每个Pod都有自己的IP地址,但是在Deployment中,在同一时刻运行的Pod集合可能与稍后运行该应用程序的Pod集合不同。
这就导致了一个问题:如果一组Pod(称为后端)为集群内其他Pod(称为前端)提供功能,那么前端如何找出并跟踪要连接的IP地址,以便前端可以使用后端服务?
将运行在一组Pods上的应用程序公开为网络服务的抽象方法
使用Kubernetes服务无需修改应用程序即可使用通用的服务发现机制。Kubernetes为Pods提供自己的IP地址,并为一组Pod提供相同的DNS,并且可以在他们之间进行负载均衡。
Service在Kubernetes中是一个REST对象,和Pod类似。像所有的REST对象一样,Service定义可以基于POST方式,请求API server创建新的实例。
例如,有一组Pod,它们对外暴露9376端口,同时还被打上app=myapp标签
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 9376
[root@master k8s]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 2d22h
my-service ClusterIP 10.104.177.192 80/TCP 7s
上述配置创建一个名为my-service的Service对象,它会将请求代理到使用tcp端口的9376,并且具有标签app=myapp的Pod上,Kubernetes为该服务分配了一个IP地址(或集群IP),改IP地址有服务代理使用。
需要注意的是,Service能够将一个接口port映射到任意的tatgetPort。默认情况下,targetPort与port字段值相同。
创建一个nginx pod,声明具有一个80的容器端口
nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
Pod 的 IP 地址:
[root@master k8s]# kubectl get pods -l run=my-nginx -o yaml | grep podIP
podIP: 10.244.2.23
podIPs:
podIP: 10.244.1.11
podIPs:
[root@master k8s]#
此时能够通过 ssh 登录到集群中的任何一个节点上,使用 curl 也能调通所有 IP 地址。 需要注意的是,容器不会使用该节点上的 80 端口,也不会使用任何特定的 NAT 规则去路由流量到 Pod 上。 这意味着可以在同一个节点上运行多个 Pod,使用相同的容器端口,并且可以从集群中任何其他的 Pod 或节点上使用 IP 的方式访问到它们。
Kubernetes Service 从逻辑上定义了运行在集群中的一组 Pod,这些 Pod 提供了相同的功能。 当每个 Service 创建时,会被分配一个唯一的 IP 地址(也称为 clusterIP)。 这个 IP 地址与一个 Service 的生命周期绑定在一起,当 Service 存在的时候它也不会改变。 可以配置 Pod 使它与 Service 进行通信,Pod 知道与 Service 通信将被自动地负载均衡到该 Service 中的某些 Pod 上。
示例: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
上述规约将创建一个 Service,对应具有标签 `run: my-nginx` 的 Pod,目标 TCP 端口 80, 并且在一个抽象的 Service 端口(`targetPort`:容器接收流量的端口;`port`:抽象的 Service 端口,可以使任何其它 Pod 访问该 Service 的端口)上暴露。
[root@master k8s]# kubectl get service my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.111.78.3 80/TCP 57s
[root@master k8s]#
正如前面所提到的,一个 Service 由一组 backend Pod 组成。这些 Pod 通过 `endpoints` 暴露出来。 Service Selector 将持续观察,结果被 POST 到一个名称为 `my-nginx` 的 Endpoint 对象上。 当 Pod 终止后,它会自动从 Endpoint 中移除,新的能够匹配上 Service Selector 的 Pod 将自动地被添加到 Endpoint 中。 检查该 Endpoint,注意到 IP 地址与在第一步创建的 Pod 是相同的。
[root@master k8s]# kubectl describe service my-nginx
Name: my-nginx
Namespace: default
Labels:
Annotations:
Selector: run=my-nginx
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.111.78.3
IPs: 10.111.78.3
Port: 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.11:80,10.244.2.23:80
Session Affinity: None
Events:
[root@master k8s]#
[root@master k8s]# kubectl get ep my-nginx
NAME ENDPOINTS AGE
my-nginx 10.244.1.11:80,10.244.2.23:80 2m14s
[root@master k8s]#
现在,能够从集群中任意节点上使用 curl 命令请求 Nginx Service `:` 。 注意 Service IP 完全是虚拟的,它从来没有走过网络。
[root@master k8s]# curl 10.111.78.3:80
Welcome to nginx!
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
For online documentation and support please refer to
nginx.org.
Commercial support is available at
nginx.com.
Thank you for using nginx.
[root@master k8s]#
Kubernetes支持两种基本的服务发现模式--环境变量和DNS
当Pod运行在Node上,kubelet会为每个活跃的Service添加一组环境变量。它同时支持Dockerlinks、简单的{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
注意:当具有需要访问服务的Pod时,并且正在使用环境变量的方法将端口和集群IP发布到客户端Pod时,必须在客户端Pod出现之前创建服务。否则,这些客户端Pod将不会设定其环境变量
可以使用附加组件为Kubernetes集群设置DNS服务
支持集群的DNS服务器(例如CoreDNS)监视Kubernetes API中的新服务,并为每个服务创建一组DNS记录。如果整个集群中都启用了DNS,则所有的Pod都应该能够通过其DNS名称自动解析服务。
例如,在Kubernetes命名空间my-ns中有一个名为my-service的服务,则控制节点和DNS服务共同为my-service.my-ns创建DNS记录。my-ns命名空间中的Pod能够通过简单的对my-service进行名称查找来找到它。
其他命名空间中的Pod必须将名称限定为my-service.my-ns,这些名称将解析为服务分配的集群IP
Kubernetes DNS在集群上调度DNS Pod和服务,并配置kubelet以告知各个容器使用DNS服务的IP来解析DNS名称
普通服务会以my-svc.my-namespace.svc.cluster-domain.example这种名称的形式被分配一个DNS A或AAAA记录,取决于服务的IP协议族,该名称会解析成对应服务的集群IP
经由Deployment或者DaemonSet所创建的所有Pods都会有如下DNS解析项与之对应:
pod-ip-address.deploymnet-name.my-namespace.svc.cluster-domain.example
Pod规约中包含一个可选的hostname字段,可以用来指定Pod的主机名,当这个字段被设置是,它将优先于Pod的名字称为该Pod的主机名、举例,给定一个hostname为my-host的Pod,该Pod的主机名将被设置成my-host
Pod规约中还有一个可选的subdomain字段,可以用来指定Pod子域名。举例,某Pod hostname=foo,subdomain=bar,在命名空间my-ns中对应的完全限定域名为foo.bar.my-ns.svc.cluster-domain.example
实例:dns.yaml
apiVersion: v1
kind: Service
metadata:
name: busybox
spec:
selector:
app: busybox
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: busybox-1
labels:
app: busybox
spec:
hostname: busybox-1
subdomain: default-subdomain
containers:
- name: busybox
image: busybox:1.28
command: ["sh", "-c", "sleep 1h"]
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: busybox-2
labels:
app: busybox
spec:
hostname: busybox-2
subdomain: default-subdomain
containers:
- name: myapp
image: busybox:1.28
command: ["sh", "-c", "sleep 1h"]
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
[root@master k8s]# kubectl exec -it busybox-1 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # hostname -f
busybox-1.default-subdomain.default.svc.cluster.local
从集群外部访问Service的方法:
1、Cluster IP
仅仅使用一个集群内部的Ip地址(默认值)。选择这个值意味着只想这个服务在集群内部才可以被访问到
2、NodePort
在集群内部IP的基础上,在集群每个节点的端口上开发这个服务。可以再任意NodePort地址上访问这个服务
3、LoadBalancer
在使用一个集群内部IP地址和在NodePort上开放一个服务外,像云提供商申请一个负载均衡器,会让流量转发到每个在节点上移NodePort形式开放的服务上。
在使用一个集群内部IP地址和在NodePort上开放一个Service的基础上,像云提供商申请一个负载均衡器,会让流量转发到每个在节点上移NodePort形式开放的Service上。
示例:
1、创建nginx pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.cn-beijing.aliyuncs.com/qingfeng666/nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
2、创建NodePort类型的Service
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30001
Ingress是对集群中服务的外部访问进行管理的api对象,典型的访问方式是http
Ingress公开了从集群外部带内部服务的http和https路由。流量路由由ingress资源上定义的规则控制
图示是一个将所有流量都发送到同一个Service的简单Ingress示例:
可以将Ingress配置为服务提供外部可访问的URL、负载均衡流量、终止SSL/TLS,以及提供基于名称的虚拟主机等能力。Ingress控制器通常负责通过负载均衡器来实现Ingress
必须具有Ingress控制器才能满足Ingress的要求,仅创建Ingress资源本身没有任何效果,需要部署Ingress控制器,例如ingress-nginx
最小的Ingress资源示例
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
Ingress资源仅支持用于转发http流量的规则
每个HTTP规则都包含以下信息:
1、可选的host。若果未指定host,则规则只适用于通过指定IP地址的所有入站http通信;如果提供了host(例如foo.bar.com),则规则适用于该host
2、路径列表paths,每个路径都有一个有serviceName和servicePod定义的关联后端。在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容
3、backend是Service服务和端口组合。与规则host和path匹配的Ingress的HTTP请求将发送到列出的backend
https://github.com/kubernetes/ingress-nginx
版本对应
下载对应版本的ingress-nginx,提取部署文件 deploy/static/provider/cloud/deploy.yaml
# 修改Service(名称:ingress-nginx-controller)的type为NodePort
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.5.1
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
externalTrafficPolicy: Local
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- appProtocol: http
name: http
port: 80
protocol: TCP
targetPort: http
- appProtocol: https
name: https
port: 443
protocol: TCP
targetPort: https
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
type: NodePort
替换国内镜像
registry.aliyuncs.com/google_containers/nginx-ingress-controller:v1.5.1
registry.aliyuncs.com/google_containers/kube-webhook-certgen:v20220916-gd32f8c343
查询 ingress-nginx 命名空间下的 deployment、pod、service 资源
get deployment,pods,service -n ingress-nginx -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.apps/ingress-nginx-controller 1/1 1 1 41s controller registry.aliyuncs.com/google_containers/nginx-ingress-controller:v1.5.1 app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/ingress-nginx-admission-create-czqlz 0/1 Completed 0 41s 10.244.2.25 node2
pod/ingress-nginx-admission-patch-9j694 0/1 Completed 2 41s 10.244.1.13 node1
pod/ingress-nginx-controller-fbd49d74b-r8cxg 1/1 Running 0 41s 10.244.1.14 node1
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/ingress-nginx-controller NodePort 10.97.172.217 80:19386/TCP,443:13175/TCP 41s app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
service/ingress-nginx-controller-admission ClusterIP 10.109.60.199 443/TCP 41s app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
[root@master k8s]#
1、创建一个Deployment
[root@master k8s]# kubectl create deployment web --image=registry.cn-beijing.aliyuncs.com/qingfeng666/hello-app:1.0
deployment.apps/web created
2、暴露Deployment
[root@master k8s]# kubectl expose deployment web --type=NodePort --port=8080
service/web exposed
[root@master k8s]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 5d23h
web NodePort 10.108.4.70 8080:18455/TCP 89s
[root@master k8s]#
访问
[root@master k8s]# curl node1:18455
Hello, world!
Version: 1.0.0
Hostname: web-5978f64c6-jk64w
3、创建Ingress资源
ingress-example.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
创建ingress
[root@master k8s]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
example-ingress nginx hello-world.info 10.97.172.217 80 3m34s
修改hosts
[root@master k8s]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
179.220.56.230 master
179.220.56.231 node1
179.220.56.232 node2
10.97.172.217 hello-world.info
访问hello-world.info
[root@master k8s]# curl hello-world.info
Hello, world!
Version: 1.0.0
Hostname: web-5978f64c6-khzk6