KubernetesのPod間通信にCalicoを使ってみる

**22/07/24 : Calicoのv3.23.3での設定手順に従って記事を更新**

## 概要

複数のノード（マシン）でKubernetesによるコンテナオーケストレーションをする際に、異なるノードに配置されたPodをつなぐためのネットワーク構築が必要となる。Pod間のネットワークを構築するツールはFlannelをはじめとして多くあるが、今回Calicoを使ってみたら結構便利だったのでご紹介する。

例えばVirtualBoxのVMがノードに含まれていたりすると、ネットワークインターフェースが特殊なために、マニュアルや書籍に書かれている通りにやっても上手く行かないことが多い。Flannelのサンプルyamlファイルを適切に修正することでホストOSのネットワークが複雑な場合でも対応はできるのだが、ネットワークインターフェース名を追記したり、ノードにラベルをつけてラベル毎の条件をマニフェストに反映したりする必要がある。マニフェストの書き方に関する知識もそれなりに必要で、一年ほど前にKubernetes初心者だった筆者はかなり手こずった…。

しかし改めて同じことをCalicoでやってみたら、サンプルのマニフェストを２行ほど修正するだけで、Pod間通信をあっさりと実現できたので、ここで共有する。

※といいつつも補足すると、Flannelの設定で苦労したのは2019年頃の話。現状のFlannelのドキュメント（下記）を見ると、Calicoと同じ感じで設定しやすくなっている可能性が高い。

https://github.com/flannel-io/flannel



## 動作確認バージョン

* Kubernetes : v1.24.3
* Calico : v3.23.3

## 試した構成

下図のように、WindowsのVirtualBox上にLinuxの仮想マシンをインストールしており、ブリッジ接続でVirtualBoxの外のネットワークからも直接アクセス可能な状態になっている。さらにもう一つ、同じネットワーク上にLinuxOSのPCが繋がっている状態。

![network.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370519/19cde80a-bfad-6042-900a-cfd2d2c36606.png)

上の構成において、VirtualBoxのLinux仮想マシンとLinuxPCにそれぞれKubernetesをインストールして、MasterノードとWorkerノード、およびPod間ネットワークが下図の状態になるようにしたい。

![network2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370519/d1788a54-cc65-c4e7-035d-613920ed0acb.png)

ちなみにVirtualBoxのLinux仮想マシンについては、ネットワークインターフェースが下記のように複数あり、後でノードのネットワークを構築する際に注意が必要。

```
$ ip a
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    ...
    inet 10.0.2.15/24 brd 10.0.2.255 ...
    ...
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    ...
    inet 192.168.1.3/24 brd 192.168.1.255 ...
    ...
```

## 必要なツール

* Docker等のコンテナ構築ツール
* kubeadm（Kubernetes環境を簡単に構築するためのツール）
    * 基本的には[公式サイト](https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/install-kubeadm/)の手順にしたがってインストールすればOK。
    * ただしVirtualBoxの仮想マシンに関しては、何も設定しないとKubernetesのノードをeth0のIPアドレス(10.0.2.15/24)と関連づけてしまうので、``/etc/default/kubelet``(CentOS、RHEL、Fedoraでは``/etc/sysconfig/kubelet``)を作成して、下記のようにeth1のIPアドレス(192.168.1.3/24)をノードIPとするように指定する必要あり。今回に限らず、ネットワークインターフェースが複数あるマシンには意図しないIPアドレスにノードが紐付けられてしまう可能性があるので（おそらく優先順位が最も高いネットワークのIPとなる？）、念のため以下を設定していた方が良いはず。

    ```txt:/etc/default/kubelet
    KUBELET_EXTRA_ARGS=--node-ip=192.168.1.3
    ```

## Kubernetesノードの作成

* マスターノード（VirtualBoxの仮想マシン）で``kubeadm init``コマンドを実行
    * 引数にPod間ネットワークのサブネット``192.168.2.0/24``が必要（今回の構成例での設定値であり、各ネットワーク構成に応じて任意の値を指定可能）。また、KubernetesのAPIサーバのアドレスとして``192.168.1.3``（VirtualBoxの仮想マシンのIPアドレス）を指定する必要があることに注意。

    ```
    $ sudo kubeadm init \
      --pod-network-cidr=192.168.2.0/24 \
      --apiserver-advertise-address=192.168.1.3
    ```

    * 最後に下記のようなメッセージが出てきたら成功

    ```
    ...
    To start using your cluster, you need to run the following as a regular user:
    
      mkdir -p $HOME/.kube
      sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
      sudo chown $(id -u):$(id -g) $HOME/.kube/config
    
    You should now deploy a pod network to the cluster.
    Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
      https://kubernetes.io/docs/concepts/cluster-administration/addons/
    
    Then you can join any number of worker nodes by running the following on each as root:
    
    kubeadm join 192.168.1.3:6443 --token xxxxxxxx... \
        --discovery-token-ca-cert-hash sha256:yyyyyyyy...
    ```

    * 出力結果にも書かれているように、下記コマンドで設定ファイルをユーザディレクトリにコピーして、Kubernetesのクラスターにユーザ権限でアクセスできるようにする。

    ```
    $ mkdir -p $HOME/.kube
    $ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    $ sudo chown $(id -u):$(id -g) $HOME/.kube/config
    ```

    * 余談だが、マスターノードにもPodなどのリソースを作れるようにするには、下記コマンドで``master:NoSchedule``のtaint(痕跡？)を外してあげる必要あり。
    
    ```
    $ kubectl taint nodes $(hostname) node-role.kubernetes.io/master:NoSchedule-
    ```

* ワーカーノード（LinuxOSの物理マシン）で``kubeadm join``コマンドを実行
    * マスターノードで``kubeadm join``した時に出力されたコマンドを素直に実行する。

    ```
    $ sudo kubeadm join 192.168.1.3:6443 --token xxxxxxxx... \
        --discovery-token-ca-cert-hash sha256:yyyyyyyy...
    ```

    * マスターノード(VirtualBoxの仮想マシン)で、以下のように２つのノードが表示されたらOK（以下の出力は全設定が既に完了してから出力したものなので、実際にこの時点で確認したらSTATUSがReadyにはなっていなかったかも…）

    ```
    $ kubectl get node
    NAME         STATUS   ROLES    AGE    VERSION
    virtualbox   Ready    master   3d1h   v1.24.3
    linux-pc     Ready    <none>   3d1h   v1.24.3
    ```

## Calicoをデプロイ

ようやく本題のCalicoの話。
下記サイトの「Install Calico」の手順に従って進めていく。

https://projectcalico.docs.tigera.io/getting-started/kubernetes/quickstart

まずは以下のコマンドで、Calicoのオペレーターやカスタム定義をインストールする。

```
$ kubectl create -f https://projectcalico.docs.tigera.io/manifests/tigera-operator.yaml
```

続いて、Calicoのリソースをデプロイするためのマニフェストをダウンロード

```
$ wget https://projectcalico.docs.tigera.io/manifests/custom-resources.yaml
```

ダウンロードした``custom-resources.yaml``で、spec.calicoNetwork.ipPoolsのパラメータの一つ、``cidr:``を``192.168.2.0/24``（``kubeadm init`` で指定した引数``--pod-network-cidr``と同じもの）に変更。（公式サイトの手順の場合、PodネットワークのCIDRが``192.168.0.0/16``であることが前提になっているので注意！）

```yaml:custom-resources.yaml
# This section includes base Calico installation configuration.
# For more information, see: https://projectcalico.docs.tigera.io/v3.23/reference/installation/api#operator.tigera.io/v1.Installation
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  # Configures Calico networking.
  calicoNetwork:
    # Note: The ipPools section cannot be modified post-install.
    ipPools:
    - blockSize: 26
      cidr: 192.168.0.0/16 # ここを「192.168.2.0/24」に変更！
      encapsulation: VXLANCrossSubnet
      natOutgoing: Enabled
      nodeSelector: all()
...
```

Calicoのリソースを下記コマンドで作成

```
$ kubectl apply -f custom-resources.yaml
```

数分程度待って、Calicoのリソースが動いていることを確認。新たなネームスペースcalico-systemにPodがデプロイされる。

```
$ kubectl get pod -n calico-system -o wide
NAME                                      READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
calico-kube-controllers-657d56796-d5mgd   1/1     Running   0          15m   192.168.2.197   linux-pc     <none>           <none>
calico-node-5nk2v                         1/1     Running   0          15m   192.168.1.2     linux-pc     <none>           <none>
calico-node-9zx4f                         1/1     Running   0          15m   192.168.1.3     virtualbox   <none>           <none>
calico-typha-5fb5c59b98-pwdl2             1/1     Running   0          15m   192.168.1.3     linux-pc     <none>           <none>
```

念のため、kube-systemのPodの動作状況も確認

```
$ kubectl get pods -n kube-system -o wide
NAME                                 READY   STATUS    RESTARTS   AGE    IP             NODE          NOMINATED NODE   READINESS GATES
coredns-f9fd979d6-7rrvh              1/1     Running   0          22m    192.168.2.2    linux-pc     <none>           <none>
coredns-f9fd979d6-tlb82              1/1     Running   0          22m    192.168.2.3    linux-pc     <none>           <none>
etcd-kube-master                     1/1     Running   0          22m    192.168.1.3    virtualbox   <none>           <none>
kube-apiserver-virtualbox            1/1     Running   0          22m    192.168.1.3    virtualbox   <none>           <none>
kube-controller-manager-virtualbox   1/1     Running   0          22m    192.168.1.3    virtualbox   <none>           <none>
kube-proxy-dtcsk                     1/1     Running   0          19m    192.168.1.4    linux-pc     <none>           <none>
kube-proxy-htk75                     1/1     Running   0          22m    192.168.1.3    virtualbox   <none>           <none>
kube-scheduler-virtualbox            1/1     Running   0          22m    192.168.1.3    virtualbox   <none>           <none>
```

## Pod間ネットワークの疎通確認

試しに、nginxのコンテナをレプリカ3の冗長構成で動かしてみる

```
$ kubectl create deployment nginx --image=nginx --replicas=3
```

起動後しばらくして確認すると、事前に定義したPodネットワーク``192.168.2.0/24``内で動いていることを確認

```
$ kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP              NODE          NOMINATED NODE   READINESS GATES
nginx-6799fc88d8-8jjl9   1/1     Running   0          11s   192.168.2.194   virtualbox    <none>           <none>
nginx-6799fc88d8-fzg2h   1/1     Running   0          11s   192.168.2.131   linux-pc      <none>           <none>
nginx-6799fc88d8-kj44h   1/1     Running   0          11s   192.168.2.132   linux-pc      <none>           <none>
```

あるPodからcurlを実行し、他のPodのwebサーバにアクセスできることを確認！

```
$ kubectl exec nginx-6799fc88d8-8jjl9 -- curl 192.168.2.131
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
(以後の出力省略)
```