浅入Kubernetes(11):了解 Service 和 Endpoint
<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>Srevice</li><li>Service 的创建及现象</li><li>Service 定义</li><li>Endpoint slices</li><li>创建 Endpoint、Service<ul><li>Service</li><li>创建应用</li><li>创建 Endpoint</li></ul></li></ul></div><p></p><p>浅入Kubernetes(8):外网访问集群 中已经介绍过部署一个 Deployment 和 Service,本篇是它的补充,将会广泛地聊一下 Service。</p>
<p>文章地址 https://www.cnblogs.com/whuanle/p/14685430.html</p>
<h3 id="srevice">Srevice</h3>
<p>Service 是将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法。如果我们使用 Deployment 部署 pod,则可以以 Deployment 为对象创建 Service。</p>
<p>在 K8S 中,每个 Pod 都有其自己唯一的 ip 地址,而 Service 可以为多个 Pod(一组)提供相同的 DNS 名,并且可以在他们直接进行负载均衡。</p>
<p>假如有一组 nginx pod,如果 nginx 动态伸缩改变或因为某些原因 ip/端口发生改变,那么其 ip 和 端口都不是固定的,而且我们很难确定它新扩容的 pod 的地址是什么,又万一 pod 被删除,ip 不再可用。</p>
<p>又假如一组 pod 称为前端,如 web 服务,另一组 pod 称为后端,例如 mysql。那么 前端 如何查找并跟踪要连接的 ip 地址,以便前端可以使用工作负载的后端部分?</p>
<p>这真是 Service 要解决的问题。Kubernetes Service 定义了一种抽象:逻辑上的一组 Pod,一种可以访问它们的策略 —— 通常称为微服务。当使用 Service 为一组 pod (Deployment 的方式创建的)创建服务时,无论我们创建了多少个 pod 副本,这些 pod 怎么变化,前端不需要关心它们调用了哪个后端副本,而且不需要知道后端 pod 的状态也不需要跟踪 pod。Service 把前后端的这种关联抽象化,把它们解耦了。</p>
<h3 id="service-的创建及现象">Service 的创建及现象</h3>
<p>现在按照下面的命令快速创建 pod,pod 将会在各个节点中部署运行。</p>
<pre><code class="language-shell">kubectl create deployment nginx --image=nginx:latest --replicas=3
</code></pre>
<pre><code class="language-shell">kubectl expose deployment nginx --type=LoadBalancer --port=80
</code></pre>
<p>然后执行命令查看 Service:</p>
<pre><code class="language-shell">kubectl get services
</code></pre>
<p>也就是说外部访问端口是 30424。</p>
<pre><code class="language-shell">NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d6h
nginx LoadBalancer 10.101.132.236 <pending> 80:30424/TCP 39s
</code></pre>
<p>这时,我们可以通过公网和端口访问这个 Service。</p>
<p>我们可以查看此 Service 的 yaml 文件:</p>
<pre><code class="language-shell">kubectl get service nginx -o yaml
</code></pre>
<pre><code class="language-yaml">apiVersion: v1
kind: Service
metadata:
creationTimestamp: "2021-04-23T07:40:35Z"
labels:
app: nginx
name: nginx
namespace: default
resourceVersion: "369738"
uid: 8dc49805-2fc8-4407-adc0-8e31dc24fa79
spec:
clusterIP: 10.101.132.236
clusterIPs:
- 10.101.132.236
externalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- nodePort: 30424
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
sessionAffinity: None
type: LoadBalancer
status:
loadBalancer: {}
</code></pre>
<p>有了标准的 yaml 文件模板,我们可以很方便地修改并定制一个 Service。</p>
<p>我们查看通过 Deployment 创建的 pod:</p>
<pre><code class="language-shell">kubectl get pods -o wide
</code></pre>
<pre><code>NAME IP NODE NOMINATED NODE READINESS GATES
nginx-55649fd747-9fzlr 192.168.56.56 instance-2 <none> <none>
nginx-55649fd747-ckhrw 192.168.56.57 instance-2 <none> <none>
nginx-55649fd747-ldzkf 192.168.23.138 instance-1 <none> <none>
</code></pre>
<p>注:pod 在哪个节点中运行,我们是不一样的。</p>
<p>当我们通过外部网络访问时,Service 会自动提供其中一个 pod 给我们,但是这个过程较为复杂,我们这里先将表面现象。</p>
<pre><code> ------------
| |
--- 公网ip -->| pod1 |
| pod2|
| pod3|
------------
</code></pre>
<p>然后我们通过命令查看 iptables 配置:</p>
<pre><code>iptables-save
</code></pre>
<p>然后查找 random 关键字:</p>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202104/1315495-20210423222218646-145685006.png" alt="1619165794(1)" loading="lazy"></p>
<p>你可以看到有三个 <code>default/nginx</code>, 第一个 pod 被访问的机会都是 <code>0.33333...</code>,然后 2/3 的概率中,2/3 的 0.5 的概率选择第二个 pod,剩下的 1/3 概率选择第三个 pod。</p>
<p>如果要访问 pod,可以以任意部署了 nginx pod 的节点的 ip 进行访问。由于 master 不能部署 pod,所以不能通过 master 的 ip 进行访问。</p>
<p>当然,它并不是直接都是 <code>0.33333..</code> 这样的,iptables 的规则有点复杂,这里难以讲清楚,我们只需要知道 外网能够访问 Service,而 Service 通过 iptable 为我们转发流量。即使 Deployment 部署的 pod 不在同一个节点上, k8s 的 dns 服务等会正确处理的,我们不需要手动配置这些网络。</p>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202104/1315495-20210423222350767-433675767.png" alt="" loading="lazy"><br>
【图来源:k8s 官网】</p>
<h3 id="service-定义">Service 定义</h3>
<p>在上一小节中,介绍了 Service 的创建方法(<code>kubectl expose ...</code>),也介绍了其依赖的 iptables,这里将继续学习 Service 的定义方法。</p>
<p>因为之前我们是通过 Deployment 进行操作,直接为一个 deployment 中的 pod (副本)统一映射。当然我们也可以为不同的 pod 进行网络映射。</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 6666
targetPort: 80
</code></pre>
<p>这里我们使用了 <code>selector</code> 选择器,一般 pod 的 Label 都会有 <code>app</code>,表示此 pod 的名称(一般是镜像名称)。port、targetPort 分别是 pod 端口、提供给外界访问的端口。</p>
<p>当我们不通过 Deployment 或者 job 等对象处理 pod 时,可以通过 <code>selector</code> 来选择合适的 pod。</p>
<p>Service 能够将一个接收 容器或者 pod 的端口 <code>targetPort</code> 映射到任意的 port 端口,port 是外部可以访问的端口。 如果使用 <code>kubectl expose</code> 去映射端口,会默认随机提供一个 <code>30xxx</code> 端口。而使用 yaml ,默认情况下,<code>targetPort</code> 将被设置为与 <code>port</code> 字段相同的值。</p>
<h3 id="endpoint-slices">Endpoint slices</h3>
<p>”<em>端点切片(Endpoint Slices)</em> 提供了一种简单的方法来跟踪 Kubernetes 集群中的网络端点 (network endpoints)。它们为 Endpoints 提供了一种可伸缩和可拓展的替代方案。“</p>
<p>在 Kubernetes 中,<code>EndpointSlice</code> 包含对一组网络端点的引用。 指定选择器后控制面会自动为设置了 选择算符 的 Kubernetes 服务创建 Endpoint。</p>
<p>也就是说创建 Service(带选择运算符) 会自动创建 Endpiont。</p>
<p>我们查看默认命名空间的 endpoint:</p>
<pre><code class="language-shell">kubectl get endpoints
</code></pre>
<pre><code>NAME ENDPOINTS AGE
kubernetes 10.170.0.2:6443 3d7h
nginx 192.168.56.24:80,192.168.56.25:80,192.168.56.26:80 59m
</code></pre>
<p>这些都是 pod 的 ip 和端口,也就是说,通过 Endpoint 我们跟踪 Kubernetes 集群中的网络端点 (network endpoints)变得更加任意。不过这样解释是很难明白的,笔者翻了很多次资料,一点点试错才搞懂。接下来我们一步步来上手操作,然后一点点理解这些操作的含义。</p>
<h3 id="创建-endpointservice">创建 Endpoint、Service</h3>
<p>接下来我们手动创建 Service 和Endpoint 和 ,需要先创建 Service ,再创建 Endpoint (这两者创建顺序可以随意)。</p>
<h4 id="service">Service</h4>
<p>我们先删除之前创建的 service。</p>
<pre><code class="language-shell">kubectl delete service nginx
</code></pre>
<p>编写 service.yaml 文件内容如下如下:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- protocol: TCP
port: 6666
targetPort: 80
</code></pre>
<p>应用这个 Service:</p>
<pre><code class="language-shell">kubectl apply -f service.yaml
</code></pre>
<p>查看 service :</p>
<pre><code class="language-shell">kubectl get services
</code></pre>
<pre><code>NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d12h
nginx ClusterIP 10.98.113.242 <none> 6666/TCP 6s
</code></pre>
<p>由于此 Service 没有映射任何 pod 等,因此没有任何用处,但是此时已经可以给开发人员一个交待了,或者说确定下 nginx 的 Service 端口和地址。至于真正的 nginx 服务,后面再确定。创建 Service 和 Endpoint 的顺序是任意的,只是这里我们提出抽象,先约定 端口,再提供服务,所以先创建 Service。</p>
<h4 id="创建应用">创建应用</h4>
<p>我们随便找台 worker 或者 master 节点,创建一个 nginx 容器:</p>
<pre><code class="language-shell">docker run -itd -p 80:80 nginx:latest
</code></pre>
<p>为什么不用 pod,直接创建容器?因为我们处于开发阶段,如果把 nginx 改成 mysql,我们要 Debug 呢?测试自己的数据库呢?要模拟数据呢?我们在生产时再通过 Deployment 创建应用,但是此时我们可以使用自己的数据库或者本地应用。</p>
<p>官方文档说:</p>
<ul>
<li>希望在生产环境中使用外部的数据库集群,但测试环境使用自己的数据库。</li>
<li>希望服务指向另一个 名字空间(Namespace) 中或其它集群中的服务。</li>
<li>你正在将工作负载迁移到 Kubernetes。 在评估该方法时,你仅在 Kubernetes 中运行一部分后端。</li>
</ul>
<p>总之,我们创建了 Service,可以提供了抽象,至于怎么提供这个服务,我们可以使用 pod ,也可以直接使用命令执行机器上的二进制程序,也可以通过 docker 提供。而且 mysql 可能是在外部服务提供的,或者 mysql 直接部署在宿主机上,而不使用容器和 pod,我们可以通过 Endpoint 来跟踪 mysql 服务的端口。</p>
<p>然后查询这个容器的 ip,:</p>
<pre><code class="language-shell">docker inspect {容器id} | grep IPAddress
</code></pre>
<p>笔者得到的是:<code>"IPAddress": "172.17.0.2"</code>,可以试试 <code>curl 172.17.0.2</code> ,看看是否能够访问 nginx,如果没问题我们来进行下一步。</p>
<h4 id="创建-endpoint">创建 Endpoint</h4>
<p>创建一个 endpoint.yaml 文件,内容如下(注意替换ip为你容器访问ip):</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Endpoints
metadata:
name: nginx
subsets:
- addresses:
- ip: 172.17.0.2
ports:
- port: 80
</code></pre>
<p>然后应用 yaml:</p>
<pre><code class="language-shell">kubectl apply -f endpoint.yaml
</code></pre>
<p>查看 endpoint:</p>
<pre><code class="language-shell">kubectl get endpoints
# 不能填写成 endpoint
</code></pre>
<p>然后访问 Service 的 ip:</p>
<pre><code>curl 10.99.142.242:6666
</code></pre>
<p>也可以通过公网访问此 IP。</p>
<p>如果 Endpoint 需要跟踪多个 ip (多个 pod 或者容器或者应用),可以使用:</p>
<pre><code>- addresses:
- ip: 172.17.0.2
- ip: 172.17.0.3
- ip: 172.17.0.4
... ...
</code></pre>
</div>
<div id="MySignature" role="contentinfo">
痴者工良(https://whuanle.cn)<br><br>
来源:https://www.cnblogs.com/whuanle/p/14695815.html
頁:
[1]