接触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
广告
将在 10 秒后关闭
bannerAds