7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Kubernetesに触れる: Kubernetes内のネットワークとDNS

Last updated at Posted at 2019-03-13

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を見てみると、10.32.0.0/12の中にあるIPが割り当てられている。このように、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内部のContainer間の通信

翻って、Pod内部に複数のContainerがある場合を考える。

先に説明したように、Kubernetes上ではPodに一つのIPが割り当てられる。

ではPod内のContainer間の通信はどのように行われるか?

yum install tcpdump net-toolsをする。

cent-cent.yml
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.10nameserverとして指定している。

[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をデプロイしたい場合はこれを利用する。

nginx-headless.yaml
---
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クラスタ内から外部への通信の際の名前解決について。

以下のようなServiceを作れば、外部のリソースの名前もラップできる。

google-external.yaml
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やエンドポイントの管理を自分たちで行う必要があった。特にProduction環境やStaging環境などでエンドポイントが異なる場合に、アプリケーションのコンフィグファイルにそれぞれのエンドポイントのホストネームを記述して、実行時に適用仕分けたりだとか。

一方、上の仕組みならIPやエンドポイントの名前の管理をする必要がそもそもなくなる。Kubernetesでこの機能が一番感動した。

外部からのアクセス

Webサイトを公開するときなど、外部からアクセスする際は、選択しとしてLoadBalancerかIngressがある。あまり詳細はしない。

これらの違いはここに詳しい。

それぞれ以下のような特徴がある。

  • LoadBalancer
    • L4のロードバランシング
  • Ingress
    • L7のロードバランシング
    • パスベースでのルーティング
    • SSL/TSLの終端になる。
    • 実装はNginxなど。

書こうと思ったけどやめたこと

  • NetworkPolicy
  • Kafka Cluster(Zookeeperも含む) をStatefulSetでk8s上にデプロイしてみる。

参考

7
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?