【Kubernetes】NodePort Service

396 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第23天,点击查看活动详情

在开发阶段或是临时应用,如果想要外部客户端访问集群服务,最常用的方式就是 NodePort Service

NodePort Service 是在 ClusterIP Service 的基础上,为 Service 在集群所有 Node 节点上绑定一个端口,外部客户端通过 nodeIP:nodePort 就可以访问服务。

在定义 YAML 文件时需要:

  • spec.type: 设置值为 NodePort
  • spec.ports.nodePort: 设置在 Node 节点上开发的端口号

修改 nginx-deployment.yaml 文件,并向其中写入如下代码:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.7.9
          ports:
            - containerPort: 80 # 指定开放 80 端口

执行创建:

# 如果提示存在先 delete
$ kubeclt create -f nginx-deployment.yaml
deployment.apps/nginx created

/home/shiyanlou 目录下新建 nginx-service-nodeport.yaml 文件,并向其中写入如下代码:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service-nodeport
spec:
  type: NodePort
  ports:
    - port: 80 # 设置 ClusterIP 对应的端口为 80
      targetPort: 80 # Pod 开放的端口为 80
      nodePort: 31000 # 设置在 Node 上开放的端口为 31000
  selector:
    app: nginx

注意:nodePort 的默认范围为 30000-32767,我们设置的时候也需要在这个范围之内。

执行创建:

$ kubectl create -f nginx-service-nodeport.yaml
service/nginx-service-nodeport created
# CLUSTER-IP 的值为 10.110.152.192PORT(S) 表示集群 IP(80 端口)和 node 端口(31000)
$ kubectl get svc
NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
nginx-service-nodeport   NodePort    10.110.152.192   <none>        80:31000/TCP   15s

那么现在外部客户端可以通过 nodeIP:nodePort 访问服务:

# 这里访问的是 kubernetes-worker 节点
shiyanlou:~/ $ curl 172.18.0.2:31000
<!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>
...

也可以在集群内部使用 clusterIP:port 访问服务:

shiyanlou:~/ $ docker exec -it kubernetes-worker bash
root@kubernetes-worker:/# curl 10.110.152.192
<!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>
...

在使用 nodePort Service 时,需要注意:

  • node 每个端口只能提供一个服务
  • 如果 node IP 地址发生变化,访问的地址也要随之发生改变

案例:客户端直接访问 Pod

服务 Service 直接被外部客户端访问,其实 Pod 也可以直接被外部客户端访问。

主要通过两种方式实现,在定义 Pod YAML 文件时设置:

  • spec.ports.hostPort: 容器级别的设置,将容器应用的端口号映射到 Node 节点上;
  • spec.hostNetwork: Pod 级别的设置,Pod 中所有容器的端口号都被直接映射到 Node 节点上;

还有一种方式是使用命令 kubectl port-forward 实现数据转发,kubectl 会主动监听本地的某个端口,然后将对于本地端口的请求转发到 Pod 容器端口上。

hostPort

hostPort 是将容器应用的端口号映射到所在 Node 节点上,通过 NodeIP + hostPort 可以直接访问容器应用。

/home/shiyanlou 目录下新建 pod-hostport.yaml 文件,并向其中写入如下代码:

apiVersion: v1
kind: Pod
metadata:
  name: pod-hostport
  labels:
    app: nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.7.9
      ports:
        - containerPort: 80
          hostPort: 8888

执行创建:

$ kubectl create -f pod-hostport.yaml
pod/pod-hostport created
$ kubectl get pods -o wide
NAME           READY   STATUS    RESTARTS   AGE     IP           NODE          NOMINATED NODE   READINESS GATES
pod-hostport   1/1     Running   0          7m14s   10.244.3.3   kubernetes-worker2   <none>           <none>

可以看到 pod-hostportkubernetes-worker2 节点上,然后可以直接通过 kubernetes-worker2 IP:hostPort 直接访问容器:

curl 172.18.0.3:8888

但是这里需要注意的是:当 Pod 被重新调度以后,所在的 Node 节点也会发生变化。

hostNetwork

hostNetwork 用于直接定义 Pod 网络,将 Pod 中所有容器的端口号直接映射到 Node 节点上。通过 spec.hostNetwork=true 设置,并且 spec.ports.containerPortspec.ports.hostPort 必须相同:

  • 当不指定 spec.ports.hostPort 时,默认 hostPortcontainerPort 相同
  • 当指定 spec.ports.hostPort 时,hostPort 必须与 containerPort 相同

f/home/shiyanlou 目录下新建 pod-hostnetwork.yaml 文件,并向其中写入如下代码:

apiVersion: v1
kind: Pod
metadata:
  name: pod-hostnetwork
  labels:
    app: nginx
spec:
  hostNetwork: true
  containers:
    - name: nginx
      image: nginx:1.7.9
      ports:
        - containerPort: 80
          hostPort: 80

执行创建:

$ kubectl create -f pod-hostnetwork.yaml
pod/pod-hostnetwork created
$ kubectl get pods -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP           NODE          NOMINATED NODE   READINESS GATES
pod-hostnetwork   1/1     Running   0          63s   172.18.0.2   kubernetes-worker   <none>           <none>

可以看到 pod-hostnetworkkubernetes-worker 节点上,并且 IP 地址也是节点的 IP 地址:172.18.0.2,然后可以直接通过 kubernetes-worker IP:hostPort 直接访问容器:

curl 172.18.0.2:80

这里由于节点是使用 docker 模拟的,在环境中不能直接访问,大家可以在自己的本地环境中尝试。

需要注意的是:启动 Pod 后它被调度分配到的节点可能都是不同的,所以想要访问 PodnodeIP 地址是不固定的,并且需要注意 Pod 的端口不能与 Node 节点的端口有冲突。

Port Forward

Port Forward 是直接将本地端口与 Pod 端口绑定,通常用于调试服务。

当客户端发送请求到 kubectl 监听的本地端口后,kubectl 会将数据发送给 apiServerapiServerkubelet 建立连接,然后通过目标 Pod 端口以及 Pod 内部指定的容器端口,最终将请求送达。

我们直接使用命令创建一个 nginx Podd` 进行查看:

$ kubectl run nginx --image nginx:1.7.9 --port=80
pod/nginx created

查看 nginx Pod 监听的端口:

$ kubectl get pods nginx --template='{{(index (index .spec.containers 0).ports 0).containerPort}}{{"\n"}}'
80

现在将本地的 3000 端口转发到 nginx Pod 的 80 端口:

$ kubectl port-forward nginx 3000:80
Forwarding from 127.0.0.1:3000 -> 80
Forwarding from [::1]:3000 -> 80

新开一个终端访问 127.0.0.1:3000 地址:

$ curl 127.0.0.1:3000
<!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>

\