# Service & Ingress ## Service >An abstract way to expose an application running on a set of Pods as a network service. With Kubernetes you don't need to modify your application to use an unfamiliar service discovery mechanism. Kubernetes gives Pods their own IP addresses and a single DNS name for a set of Pods, and can load-balance across them. 根據官方定義,`Service` 是 k8s 內定義的抽象化物件(object),看一下詳細說明: * [Service(k8s 官網)](https://kubernetes.io/zh/docs/concepts/services-networking/service/) * [Kubernetes Service 概念詳解](https://tachingchen.com/tw/blog/kubernetes-service/) * [Kubernetes Service 取路徑差異](https://tachingchen.com/tw/blog/kubernetes-service-in-detail-1/) * [Kubernetes Service 標籤對於 Service 的影響](https://tachingchen.com/tw/blog/kubernetes-service-in-detail-2/) 在 `hello world, minikube` 中,曾經下過這道指令: ```yaml= kubectl expose deployment hello-node --type=LoadBalancer --port=8080 ``` k8s 會將部署的 pod 對外暴露 ```shell= kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello-node LoadBalancer 10.108.144.78 <pending> 8080:30369/TCP 21s kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23m ``` 換句話說,k8s 幫我們建立了一個 `service` : hello-node,讓外部可以透過它來存取相關資源  >`EXTERNAL-IP`: 指定 service 為 type: LoadBalancer 時,Cloud Provider (如 GCP、AWS) 配發的 ==Public IP== 當然我們也可以用 yaml 來定義我們想要的服務: 範例:`demo-service.yaml` ```yaml= apiVersion: v1 kind: Service metadata: name: service-example spec: selector: app: web #對應到 deployment 中的 labels: app # type: NodePort #見補充 ports: - name: http port: 8080 #service 對外部開放的埠號 targetPort: 80 #實際 pod 所開放的埠號 nodePort: 30080 #(optional): 此設定只有在 spec.type 為 NodePort 或 LoadBalancer 才會存在,預設範圍:30000-32767 protocol: TCP - name: https port: 443 targetPort: 443 protocol: TCP ``` :::info Type 的取值以及行为如下: * `ClusterIP`:通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认的 ServiceType。 * `NodePort`:通过每个节点上的 IP 和静态端口(NodePort)暴露服务。 NodePort 服务会路由到自动创建的 ClusterIP 服务。 通过请求 <节点 IP>:<节点端口>,你可以从集群的外部访问一个 NodePort 服务。 * `LoadBalancer`:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。 * `ExternalName`:通过返回 CNAME 和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。 ::: ## Internal load balancer >在混合环境中,有时有必要在同一(虚拟)网络地址块内路由来自服务的流量 在水平分割 DNS 环境中,你需要两个服务才能将内部和外部流量都路由到你的端点(Endpoints 如要设置内部负载均衡器,请根据你所使用的云运营商,为服务添加以下注解之一 以 GCP 為例 ```yaml= apiVersion: v1 kind: Service metadata: name: pomelo-block-svc annotations: networking.gke.io/load-balancer-type: "Internal" spec: selector: app: pomelo-block type: LoadBalancer loadBalancerIP: 10.107.22.111 ports: - name: conn-1 port: 30001 targetPort: 30001 nodePort: 30001 protocol: TCP - name: conn-2 port: 30002 targetPort: 30002 nodePort: 30002 protocol: TCP ``` 透過上述的 service,同一個網段的外部服務將可以透過 `10.107.22.111:30001(or 30002)` 訪問到 `pomelo-block` ## Ingress Ingress 是對集群中服務的==外部訪問==進行管理的 API 物件 * [Ingress(k8s 官網)](https://kubernetes.io/zh/docs/concepts/services-networking/ingress/) * [在 Kubernetes 中實現負載平衡 - Ingress Controller](https://ithelp.ithome.com.tw/articles/10196261) * [Kubernetes 基礎教學(二)實作範例:Pod、Service、Deployment、Ingress](https://cwhu.medium.com/kubernetes-implement-ingress-deployment-tutorial-7431c5f96c3e)  範例:[將 WebSocket 伺服器公開給 Ingress](https://docs.microsoft.com/zh-tw/azure/application-gateway/ingress-controller-expose-websocket-server) ### Ingress 與 pomelo 的相性 ingress 的特色是,透過不同的 url path mapping,從前端將請求導到正確的後端服務 例如: ```shell= apiVersion: extensions/v1beta1 kind: Ingress metadata: name: demo-ingress spec: rules: - http: paths: - path: /v1/* backend: serviceName: web1 servicePort: 8080 - path: /v2/* backend: serviceName: web2 servicePort: 8080 ``` 上面的範例,前端收到的請求 `/v1/*` 會將服務引導到 `web1` 這個 service 的 8080 port, `/v2/*` 則是對應到 `web2:8080`,這邊最大的特色是,==前端服務對外只會開放 80/443 兩種 port== 這麼做的最大好處是,我們只需要對外暴露一組 ip + 80/443 的 url,就可以讓 client 透過 url path 來做各式的請求 又因為 websocket 是建立在 http 之上的應用,所以也可以透過上述的 ingress 來連接 `ws[wss]://frontend_url`,建立長連線 回頭看看我們的需求:  pomelo 因為是利用 node js 開發,所以承襲了它的特色,一個 process 對應一個 tcp port,如果我們的 connector 需要開啟 `N` 個 cluster,那就需要開放 `N` 個對應的 port 連接方式會像是: ```shell= ws://connector:3014 ws://connector:3015 ws://connector:3016 ... ``` 從這邊可以看得出來,兩種方式的差異 為了將 pomelo 套用到 ingress 上,我嘗試了各種方法,直到看了[這篇](https://github.com/kubernetes/ingress-nginx/issues/1655)  從開發者的回答可以看出,ingress 並不支援同一個 domain name 連接多個 port 當然我們也可以改成用這樣的方式來 mapping: ```yaml= apiVersion: extensions/v1beta1 kind: Ingress metadata: name: pomelo-ingress spec: rules: - host: bbcg.prod.com http: paths: - path: /conn1-port1 backend: serviceName: conn-1 servicePort: 3014 - path: /conn1-port2 backend: serviceName: conn-1 servicePort: 3015 ``` 然後就可以透過 `ws://frontend_url/conn1-port1` 來連接到 `conn-1` 的 cluster-1 但是這麼一來,將和 pomelo 的風格完全不同,包括程式碼與設定檔都需要做大規模都修改 經評估後,決定採用比較單純的 Load Balancer 的方式來對外暴露服務,並手動綁定靜態 IP ### pomelo service load balancer 建置流程 根據需求 我們要滿足可以從內網讓其他 service 可以打進來,同時也要開放 `client port` 讓客端可以透過 websocket 連上 換句話說,我們同時需要 內網/外網 `Load Balancer` k8s 的 `service api` 可以幫我們輕鬆的建置 load balancer,但是目前看的資料,他只能讓我們二選一 (內網 or 外網),因此我們需要自行建置其中一個 這邊我採取的方法是,讓 `service api` 幫我們做掉內網,外網我們再透過 `google console`ˋ自行處理 #### service yaml ```yaml= apiVersion: v1 kind: Service metadata: name: gate labels: app: gate annotations: networking.gke.io/load-balancer-type: "Internal" networking.gke.io/internal-load-balancer-subnet: <子網域> spec: selector: app: gate type: LoadBalancer loadBalancerIP: <指定的內網IP> ports: - protocol: TCP name: "client" port: 30014 targetPort: 30014 nodePort: 30014 - protocol: TCP name: "web" port: 30050 targetPort: 30050 nodePort: 30050 --- ``` 首先在 `annotations` 的地方加上 * `load-balancer-type: "Internal"` * `internal-load-balancer-subnet: <子網域>` 第一個是和 API 註明,我要一個內網 load balancer,第二個則是 cluster 所在的子網域 接下在 `spec` 的地方加上`loadBalancerIP: <指定的內網IP>`,自行指定內網的固定 IP >要注意此 IP 是否符合 [CIDR](https://zh.wikipedia.org/wiki/%E6%97%A0%E7%B1%BB%E5%88%AB%E5%9F%9F%E9%97%B4%E8%B7%AF%E7%94%B1) 規則,以及是否被佔用 設定完後,直接下 `kubectl apply` 就會啟用(或更新)  可以看到我們的 內網 load balacer 已經建置成功 再來我們要到 `google console` 去做下一段工作:[外部負載平衡器](https://hackmd.io/@rd7-edlo/Hy_psVFhO) --- 設置完成後,我們可以到**網路服務 > 外部 IP 位址**查看結果  :::info :mag_right: 檢查我們的轉送規則有沒有綁定成功 :::  ###### tags: `k8s` `container`
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up