接触Kubernetes:探索Kubernetes内部的网络和DNS
Kubernetesに触れる の続き。KubernetesのNetwork関連の話題について。
Networkについて
Pod間の通信
Kubernetesクラスタをkubeadmなどを使って構築しようとすると、以下のようなコマンドを実行することになる。
kubectl apply -n kube-system -f \
"https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 |tr -d '\n')"
この作業で、Kubernetes上でプライベートネットワークが作られる。Weave Netはデフォルトでは10.32.0.0/12というセグメントでネットワークを作る。このセグメントの指定は変更可能である。ホストネットワークと被ってはいけないという制約があるので、ホストネットワークが例えば10.0.0.0/8である場合は、明示的に別のセグメントを指定する必要がある。
这次我们选择了应用Weave Net,但还有其他选择,如Flannel和Calico。请参考kubeadm的官方网站。
Podを2つデプロイしてみる。以下はcentos7のContainerイメージを動かしてsleep 3600を実行したPodがデプロイされている様子。
[node1 ~]$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
cent-1 1/1 Running 0 17s 10.44.0.1 node3 <none>
cent-2 1/1 Running 0 13s 10.36.0.1 node2 <none>
当查看Pod时,我们可以看到IP地址分配在10.32.0.0/12的范围内。这样,在Kubernetes中,每个Pod都被分配一个IP地址。
色々なところからpingしてみる。今回は、node1がMasterでnode2, node3, node4がNodesである。
从Nodenode2向Podcent-1发送ping请求。
[node2 ~]$ ping 10.44.0.1
PING 10.44.0.1 (10.44.0.1) 56(84) bytes of data.
64 bytes from 10.44.0.1: icmp_seq=1 ttl=64 time=1.45 ms
64 bytes from 10.44.0.1: icmp_seq=2 ttl=64 time=0.769 ms
^C
--- 10.44.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.769/1.110/1.452/0.343 ms
Podcent-1向Podcent-2发送ping请求。
[node1 ~]$ kubectl exec -it cent-1 /bin/sh
sh-4.2# ping 10.36.0.1
PING 10.36.0.1 (10.36.0.1) 56(84) bytes of data.
64 bytes from 10.36.0.1: icmp_seq=1 ttl=64 time=2.54 ms
64 bytes from 10.36.0.1: icmp_seq=2 ttl=64 time=0.781 ms
^C
--- 10.36.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.781/1.663/2.545/0.882 ms
このように、Kubernetesの中ではクラスタ固有のプライベートネットワークがある。この仕組みによって、Kubernetesクラスタ毎に、ホストのネットワークに依存しない、独立したネットワーク環境が実現できる。
Pod中容器之间的通信
假设在Pod内部有多个容器的情况下。
先に説明したように、Kubernetes上ではPodに一つのIPが割り当てられる。
在Pod中的容器之间如何进行通信?
进行”yum install tcpdump net-tools”操作。
apiVersion: v1
kind: Pod
metadata:
name: cent-cent
labels:
app: myapp
spec:
containers:
- name: cent7-1
image: centos:7
command: ['/bin/sh', '-c', '/usr/bin/yum install tcpdump net-tools -y && sleep 3600']
- name: cent7-2
image: centos:7
command: ['/bin/sh', '-c', '/usr/bin/yum install tcpdump net-tools -y && sleep 3600']
当使用ifconfig进行确认时,发现具有相同的IP地址。
[node1 ~]$ kubectl exec -it cent-cent -c cent7-1 /usr/sbin/ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 65535
inet 10.47.0.1 netmask 255.240.0.0 broadcast 10.47.255.255
ether da:aa:b4:3f:6b:87 txqueuelen 0 (Ethernet)
RX packets 14553 bytes 21243468 (20.2 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2958 bytes 210548 (205.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[node1 ~]$ kubectl exec -it cent-cent -c cent7-2 /usr/sbin/ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 65535
inet 10.47.0.1 netmask 255.240.0.0 broadcast 10.47.255.255
ether da:aa:b4:3f:6b:87 txqueuelen 0 (Ethernet)
RX packets 14553 bytes 21243468 (20.2 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2958 bytes 210548 (205.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ためしに別のノードからpingしてtcpdumpしてみる。
[node1 ~]$ kubectl exec -it cent-cent -c cent7-1 /bin/sh
sh-4.2# tcpdump -i eth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
08:30:37.554918 IP 10.36.0.0 > cent-cent: ICMP echo request, id 38327, seq 39, length 64
08:30:37.554953 IP cent-cent > 10.36.0.0: ICMP echo reply, id 38327, seq 39, length 64
08:30:38.554826 IP 10.36.0.0 > cent-cent: ICMP echo request, id 38327, seq 40, length 64
08:30:38.554899 IP cent-cent > 10.36.0.0: ICMP echo reply, id 38327, seq 40, length 64
[node1 ~]$ kubectl exec -it cent-cent -c cent7-2 /bin/sh
sh-4.2# tcpdump -i eth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
08:31:03.570873 IP 10.36.0.0 > cent-cent: ICMP echo request, id 38327, seq 65, length 64
08:31:03.570908 IP cent-cent > 10.36.0.0: ICMP echo reply, id 38327, seq 65, length 64
08:31:04.570800 IP 10.36.0.0 > cent-cent: ICMP echo request, id 38327, seq 66, length 64
08:31:04.570836 IP cent-cent > 10.36.0.0: ICMP echo reply, id 38327, seq 66, length 64
どちらもきてる。
VMの比喩を引きずってるとわかりにくい現象であるが、ここからもわかるように、podはIPを共有しているため、このPodへpingすると、それぞれのContainerにあたる。したがってこの内のContainer一にアクセスするには、ポートでわけるしかない。実際、Pod内のContainerが同じポートをバインドすることはできない。例えばPodに二つのNginxを立てようとして80番ポートをそれぞれのContainerがバインドしようとした場合、エラーとなってPodは起動できない。
关于DNS
各种各样的命名解决方法
以前にも紹介したが、ClusterIPはこれはロードバランシングの機能を提供してくれる。今回はこれの名前解決について。
Kubernetes提供了DNS服务,并为ClusterIP分配了可解析的名称。
用与上一次相同的nginx示例来思考。
[node1 ~]$ k get pods
NAME READY STATUS RESTARTS AGE
my-nginx-b685dc965-4hc7w 1/1 Running 0 1m
my-nginx-b685dc965-7527g 1/1 Running 0 1m
my-nginx-b685dc965-mkv9l 1/1 Running 0 1m
[node1 ~]$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1h
my-nginx-svc ClusterIP 10.110.220.5 <none> 8080/TCP 1m
每个Pod都将10.96.0.10指定为其nameserver。
[node1 ~]$ k exec -it my-nginx-b685dc965-4hc7w /bin/sh
# cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
当我查询名为my-nginx-svc.default.svc.cluster.local的名称时,会返回一个IP地址。返回的结果将是ClusterIP的IP地址。因此,要从Pod中以随机方式访问某个集群(例如,用于发送REST API等用途),只需向自定义的my-nginx-svc名称发送请求即可。
[node1 ~]$ dig my-nginx-svc.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.9.4-RedHat-9.9.4-73.el7_6 <<>> my-nginx-svc.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21647
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;my-nginx-svc.default.svc.cluster.local. IN A
;; ANSWER SECTION:
my-nginx-svc.default.svc.cluster.local. 1 IN A 10.110.220.5
;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sat Mar 09 09:05:23 UTC 2019
;; MSG SIZE rcvd: 121
上記の例ではAレコードを返した。これをCNAMEレコードとして扱うこともできる。そのためにはPodでデプロイしていたものを、StatefulSetとしてデプロイしておく必要がある。StatefulSetとは、Kubernetes上でStatefulな役割を持つContainerを扱いたいときに使うリソース。たとえばディスクへのアクセスが必要なDBやKafkaをデプロイしたい場合はこれを利用する。
---
kind: Service
metadata:
name: my-nginx-svc
labels:
app: nginx
spec:
type: ClusterIP
clusterIP: None
ports:
- port: 8080
targetPort: 80
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-nginx
labels:
app: nginx
spec:
serviceName: my-nginx-svc
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
これを適用するとServiceとStatefulSetがデプロイされる。まずServiceにはClusterIPがついていない。一方、それぞれのPodの名前が-0、-1、… といった、連番になっている。
[node1 ~]$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-nginx-0 1/1 Running 0 2m
my-nginx-1 1/1 Running 0 1m
my-nginx-2 1/1 Running 0 1m
[node1 ~]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1h
my-nginx-svc ClusterIP None <none> 8080/TCP 2m
ここでmy-nginx-svcの名前を引いてみるとCNAMEでかえってくることがわかる。また、この状態ならばそれぞれのPodに対しても名前解決して通信をすることができる。
[node1 ~]$ dig my-nginx-svc.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.9.4-RedHat-9.9.4-73.el7_6 <<>> my-nginx-svc.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9330
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;my-nginx-svc.default.svc.cluster.local. IN A
;; ANSWER SECTION:
my-nginx-svc.default.svc.cluster.local. 5 IN A 10.36.0.1
my-nginx-svc.default.svc.cluster.local. 5 IN A 10.44.0.1
my-nginx-svc.default.svc.cluster.local. 5 IN A 10.47.0.1
;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sat Mar 09 09:19:23 UTC 2019
;; MSG SIZE rcvd: 229
[node1 ~]$ dig my-nginx-0.my-nginx-svc.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.9.4-RedHat-9.9.4-73.el7_6 <<>> my-nginx-0.my-nginx-svc.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8834
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;my-nginx-0.my-nginx-svc.default.svc.cluster.local. IN A
;; ANSWER SECTION:
my-nginx-0.my-nginx-svc.default.svc.cluster.local. 5 IN A 10.44.0.1
;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sat Mar 09 09:21:35 UTC 2019
;; MSG SIZE rcvd: 143
DNSについての最後に、Kubernetesクラスタ内から外部への通信の際の名前解決について。
如果创建以下这样的服务,还可以包装外部资源的名称。
kind: Service
apiVersion: v1
metadata:
name: google-externalname
namespace: default
spec:
type: ExternalName
externalName: google.com
当查询名称时,通过CNAME返回了google.com。
[node1 ~]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
google-externalname ExternalName <none> google.com <none> 5s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2h
my-nginx-svc ClusterIP None <none> 8080/TCP 16m
[node1 ~]$ dig google-externalname.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.9.4-RedHat-9.9.4-73.el7_6 <<>> google-externalname.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20827
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;google-externalname.default.svc.cluster.local. IN A
;; ANSWER SECTION:
google-externalname.default.svc.cluster.local. 5 IN CNAME google.com.
google.com. 5 IN A 172.217.168.206
;; Query time: 10 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sat Mar 09 09:31:56 UTC 2019
;; MSG SIZE rcvd: 169
什么让你感到高兴?
以往我们需要自行管理IP和终端点。特别是在生产环境和分段环境中,如果终端点不同,需要在应用程序的配置文件中写入各自终端点的主机名,然后在运行时进行分配和应用。
一方、上の仕組みならIPやエンドポイントの名前の管理をする必要がそもそもなくなる。Kubernetesでこの機能が一番感動した。
来自外部的访问 de
当在公开网站等时需要从外部访问时,可以选择使用负载均衡器或Ingress来实现。不做过多详细介绍。
这些差别在这里有详细解释。
- Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what?
それぞれ以下のような特徴がある。
-
- LoadBalancer
L4のロードバランシング
Ingress
L7のロードバランシング
パスベースでのルーティング
SSL/TSLの終端になる。
実装はNginxなど。
我本来想写书的,但后来放弃了。
-
- NetworkPolicy
- Kafka Cluster(Zookeeperも含む) をStatefulSetでk8s上にデプロイしてみる。
请参照以下材料。
- AKSでNetworkPolicyを有効にする: https://docs.microsoft.com/ja-jp/azure/aks/use-network-policies#code-try-0