diff --git a/learning/k8s-intermediate/service/service-details.md b/learning/k8s-intermediate/service/service-details.md
index 0090ab5..0894857 100644
--- a/learning/k8s-intermediate/service/service-details.md
+++ b/learning/k8s-intermediate/service/service-details.md
@@ -307,6 +307,73 @@ DNS 的配置方式取决于该 Service 是否配置了 selector:
* 对 ExternalName 类型的 Service,返回 CNAME 记录
* 对于其他类型的 Service,返回与 Service 同名的 `Endpoints` 的 A 记录
+## 虚拟 IP 的实现
+
+如果只是想要正确使用 Service,不急于理解 Service 的实现细节,您无需阅读本章节。
+
+### 避免冲突
+
+Kubernetes 的一个设计哲学是:尽量避免非人为错误产生的可能性。就设计 Service 而言,Kubernetes 应该将您选择的端口号与其他人选择的端口号隔离开。为此,Kubernetes 为每一个 Service 分配一个该 Service 专属的 IP 地址。
+
+为了确保每个 Service 都有一个唯一的 IP 地址,kubernetes 在创建 Service 之前,先更新 etcd 中的一个全局分配表,如果更新失败(例如 IP 地址已被其他 Service 占用),则 Service 不能成功创建。
+
+Kubernetes 使用一个后台控制器检查该全局分配表中的 IP 地址的分配是否仍然有效,并且自动清理不再被 Service 使用的 IP 地址。
+
+### Service 的 IP 地址
+
+Pod 的 IP 地址路由到一个确定的目标,然而 Service 的 IP 地址则不同,通常背后并不对应一个唯一的目标。 kube-proxy 使用 iptables (Linux 中的报文处理逻辑)来定义虚拟 IP 地址。当客户端连接到该虚拟 IP 地址时,它们的网络请求将自动发送到一个合适的 Endpoint。Service 对应的环境变量和 DNS 实际上反应的是 Service 的虚拟 IP 地址(和端口)。
+
+#### Userspace
+
+以上面提到的图像处理程序为例。当后端 Service 被创建时,Kubernetes master 为其分配一个虚拟 IP 地址(假设是 10.0.0.1),并假设 Service 的端口是 1234。集群中所有的 kube-proxy 都实时监听者 Service 的创建和删除。Service 创建后,kube-proxy 将打开一个新的随机端口,并设定 iptables 的转发规则(以便将该 Service 虚拟 IP 的网络请求全都转发到这个新的随机端口上),并且 kube-proxy 将开始接受该端口上的连接。
+
+当一个客户端连接到该 Service 的虚拟 IP 地址时,iptables 的规则被触发,并且将网络报文重定向到 kube-proxy 自己的随机端口上。kube-proxy 接收到请求后,选择一个后端 Pod,再将请求以代理的形式转发到该后端 Pod。
+
+这意味着 Service 可以选择任意端口号,而无需担心端口冲突。客户端可以直接连接到一个 IP:port,无需关心最终在使用哪个 Pod 提供服务。
+
+#### iptables
+
+仍然以上面提到的图像处理程序为例。当后端 Service 被创建时,Kubernetes master 为其分配一个虚拟 IP 地址(假设是 10.0.0.1),并假设 Service 的端口是 1234。集群中所有的 kube-proxy 都实时监听者 Service 的创建和删除。Service 创建后,kube-proxy 设定了一系列的 iptables 规则(这些规则可将虚拟 IP 地址映射到 per-Service 的规则)。per-Service 规则进一步链接到 per-Endpoint 规则,并最终将网络请求重定向(使用 destination-NAT)到后端 Pod。
+
+当一个客户端连接到该 Service 的虚拟 IP 地址时,iptables 的规则被触发。一个后端 Pod 将被选中(基于 session affinity 或者随机选择),且网络报文被重定向到该后端 Pod。与 userspace proxy 不同,网络报文不再被复制到 userspace,kube-proxy 也无需处理这些报文,且报文被直接转发到后端 Pod。
+
+在使用 node-port 或 load-balancer 类型的 Service 时,以上的代理处理过程是相同的。
+
+#### IPVS
+
+在一个大型集群中(例如,存在 10000 个 Service)iptables 的操作将显著变慢。IPVS 的设计是基于 in-kernel hash table 执行负载均衡。因此,使用 IPVS 的 kube-proxy 在 Service 数量较多的情况下仍然能够保持好的性能。同时,基于 IPVS 的 kube-proxy 可以使用更复杂的负载均衡算法(最少连接数、基于地址的、基于权重的等)
+
## 支持的传输协议
-未完,待续,【2019年9月18日 22:33】
+#### TCP
+
+默认值。任何类型的 Service 都支持 TCP 协议。
+
+#### UDP
+
+大多数 Service 都支持 UDP 协议。对于 LoadBalancer 类型的 Service,是否支持 UDP 取决于云供应商是否支持该特性。
+
+#### HTTP
+
+如果您的云服务商支持,您可以使用 LoadBalancer 类型的 Service 设定一个 Kubernetes 外部的 HTTP/HTTPS 反向代理,将请求转发到 Service 的 Endpoints。
+
+::: tip
+使用 Ingress
+:::
+
+#### Proxy Protocol
+
+如果您的云服务上支持(例如 AWS),您可以使用 LoadBalancer 类型的 Service 设定一个 Kubernetes 外部的负载均衡器,并将连接已 PROXY 协议转发到 Service 的 Endpoints。
+
+负载均衡器将先发送描述该 incoming 连接的字节串,如下所示:
+
+```
+PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n
+
+```
+
+然后在发送来自于客户端的数据
+
+#### SCTP
+
+尚处于 `alpha` 阶段,暂不推荐使用。如需了解,请参考 [SCTP](https://kubernetes.io/docs/concepts/services-networking/service/#sctp)
diff --git a/learning/k8s-intermediate/service/service-types.md b/learning/k8s-intermediate/service/service-types.md
index 594f112..17c2ebf 100644
--- a/learning/k8s-intermediate/service/service-types.md
+++ b/learning/k8s-intermediate/service/service-types.md
@@ -3,7 +3,11 @@ layout: LearningLayout
description: Kubernetes 中发布 Service 的方式,ServiceType
---
-# Service 类型
+# 发布 Service
+
+Kubernetes Service 支持的不同访问方式。
+
+## Service 类型
Kubernetes 中可以通过不同方式发布 Service,通过 `ServiceType` 字段指定,该字段的默认值是 `ClusterIP`,可选值有:
@@ -31,10 +35,104 @@ Kubernetes 中可以通过不同方式发布 Service,通过 `ServiceType` 字
## ClusterIP
-未完,待续,【2019年9月18日 22:56】
+ClusterIP 是 ServiceType 的默认值。在 [Iptables 代理模式](service-details.html#iptables-代理模式) 中,详细讲述了 ClusterIP 类型 Service 的工作原理。
## NodePort
+对于 `NodePort` 类型的 Service,Kubernetes 为其分配一个节点端口(对于同一 Service,在每个节点上的节点端口都相同),该端口的范围在初始化 apiserver 时可通过参数 `--service-node-port-range` 指定(默认是:30000-32767)。节点将该端口上的网络请求转发到对应的 Service 上。可通过 Service 的 `.spec.ports[*].nodePort` 字段查看该 Service 分配到的节点端口号。
+
+在启动 kube-proxy 时使用参数 `--nodeport-address` 可指定阶段端口可以绑定的 IP 地址段。该参数接收以逗号分隔的 CIDR 作为参数值(例如:10.0.0.0/8,192.0.2.0/25),kube-proxy 将查找本机符合该 CIDR 的 IP 地址,并将节点端口绑定到符合的 IP 地址上。
+
+例如,
+* 如果启动 kube-proxy 时指定了参数 `--nodeport-address=127.0.0.0/8`,则 kube-proxy 只将阶段端口绑定到 loopback 地址上。
+* `--nodeport-address` 的默认值是一个空列表。则 kube-proxy 将节点端口绑定到该节点所有的网络 IP 地址上。
+
+您可以通过 `nodePort` 字段指定节点端口号(必须在 `--service-node-port-range` 指定的范围内)。Kubernetes 在创建 Service 时将使用该节点端口,如果该端口已被占用,则创建 Service 将不能成功。在这种情况下,您必须自己规划好端口使用,以避免端口冲突。
+
+使用 NodePort,您可以:
+* 根据自己的需要配置负载均衡器
+* 配置 Kubernetes / 非 Kubernetes 的混合环境
+* 直接暴露一到多个节点的 IP 地址,以便客户端可访问 Kubernetes 中的 Service
+
+NodePort 类型的 Service 可通过如下方式访问:
+* 在集群内部通过 $(ClusterIP): $(Port) 访问
+* 在集群外部通过 $(NodeIP): $(NodePort) 访问
+
## LoadBalancer
+在支持外部负载均衡器的云环境中(例如 GCE、AWS、Azure 等),将 `.spec.type` 字段设置为 `LoadBalancer`,Kubernetes 将为该Service 自动创建一个负载均衡器。负载均衡器的创建操作异步完成,您可能要稍等片刻才能真正完成创建,负载均衡器的信息将被回写到 Service 的 `.status.loadBalancer` 字段。如下所示:
+
+``` yaml
+apiVersion: v1
+kind: Service
+metadata:
+ name: my-service
+spec:
+ selector:
+ app: MyApp
+ ports:
+ - protocol: TCP
+ port: 80
+ targetPort: 9376
+ clusterIP: 10.0.171.239
+ loadBalancerIP: 78.11.24.19
+ type: LoadBalancer
+status:
+ loadBalancer:
+ ingress:
+ - ip: 146.148.47.155
+```
+
+发送到外部负载均衡器的网络请求就像被转发到 Kubernetes 中的后端 Pod 上。负载均衡的实现细节由各云服务上确定。
+
+由于 Kuboard 不限定 Kubernetes 是运行在裸机上、私有云上或者是公有云上,因此 Kuboard 暂不支持 LoadBalancer 类型的 Service。关于更多 LoadBalancer Service 相关的描述,请参考 [Type LoadBalancer](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) 和您所使用的云供应商的文档
+
## ExternalName
+
+ExternalName 类型的 Service 映射到一个外部的 DNS name,而不是一个 pod label selector。可通过 `spec.externalName` 字段指定外部 DNS name。
+
+下面的例子中,名称空间 `prod` 中的 Service `my-service` 将映射到 `my.database.example.com`:
+
+``` yaml
+apiVersion: v1
+kind: Service
+metadata:
+ name: my-service
+ namespace: prod
+spec:
+ type: ExternalName
+ externalName: my.database.example.com
+```
+
+执行 `nslookup my-service.prod.svc.cluster.local` 指令时,集群的 DNS 服务将返回一个 `CNAME` 记录,其对应的值为 `my.database.example.com`。访问 `my-service` 与访问其他类型的 Service 相比,网络请求的转发发生在 DNS level,而不是使用 proxy。如果您在后续想要将 `my.database.example.com` 对应的数据库迁移到集群内部来,您可以按如下步骤进行:
+1. 在 Kubernetes 中部署数据库(并启动数据库的 Pod)
+2. 为 Service 添加合适的 selector 和 endpoint
+3. 修改 Service 的类型
+
+::: tip 注意事项
+* ExternalName 可以接受一个 IPv4 地址型的字符串作为 `.spec.externalName` 的值,但是这个字符串将被认为是一个由数字组成的 DNS name,而不是一个 IP 地址。
+* 如果要 hardcode 一个 IP 地址,请考虑使用 [headless Service](./service-details.html#headless-services)
+:::
+
+## External IP
+
+如果有外部 IP 路由到 Kubernetes 集群的一个或多个节点,Kubernetes Service 可以通过这些 `externalIPs` 进行访问。`externalIP` 需要由集群管理员在 Kubernetes 之外配置。
+
+在 Service 的定义中, `externalIPs` 可以和任何类型的 `.spec.type` 一通使用。在下面的例子中,客户端可通过 `80.11.12.10:80` (externalIP:port) 访问`my-service`
+
+``` yaml
+apiVersion: v1
+kind: Service
+metadata:
+ name: my-service
+spec:
+ selector:
+ app: MyApp
+ ports:
+ - name: http
+ protocol: TCP
+ port: 80
+ targetPort: 9376
+ externalIPs:
+ - 80.11.12.10
+```