ネットワークまわりを確認&勉強しながら、Kubernetesの環境構築をしてみた

# はじめに
* ネットワークまわりを確認&勉強しながら、Kubernetesの環境構築をしたときのメモです。
* 構築手順自体は、[Ubuntu 18.04 LTS にKubernetes環境をインストールする [Master / Worker]](https://qiita.com/soumi/items/7736ac3aabbbe4fb474a)の通りに実施したところ、サクッとできました。

# 構成概要
## 構成図
* 今回構築した環境は下記の通りです。
* AWSでEC2を3台立てて検証しました。

![k8s_nw_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92368/eb75cbc9-292e-a33a-fce2-2e315c30a703.png)

## 補足
* EC2を配置しているVPCにはIGWを関連付けている
* EC2を配置しているVPCのIPレンジとして10.1.100.0/16を設定している
* flannelのIPレンジは、デフォルトのbridege(172.17.0.0/16など)と被らないよう設定する必要がある
    * 今回は、172.21.0.0/16を指定した
* ホストに複数のNICが接続されている場合は、他のクラスタノードと通信するNICを、 kubeadm init --apiserver-advertise-address で指定する（今回は1枚）
* 起動したPodに対して、ユーザ（インターネット経由）は http://18.176.61.21:30000 などでアクセスできる

# 構築手順
## [Ctrl/Node] EC2作成
* 2CPU/メモリ2GB以上でOSが対応していれば良さそう

```bash
* セキュリティグループ作成
    * ローカルPCからのssh
    * 下記インスタンス間のtcp（プライベートIP）
* インスタンス作成
    * Ubuntu Server 18.04 LTS
    * t2.medium
```

![k8s_nw_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92368/6a6a7107-7ff7-2146-982e-d6aa1f38729d.png)


## [Ctrl/Node] Dockerをインストール

```bash
# 必要なライブラリを事前にインストール
$ sudo apt update
$ sudo apt install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common
# GPG公開鍵のインストール
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 公開鍵のフィンガープリントを確認
$ sudo apt-key fingerprint 0EBFCD88
# aptリポジトリの設定
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

# dockerインストール
$ sudo apt update
$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io

# 動作確認
$ sudo docker run --rm hello-world
$ sudo docker rmi hello-world
```

![k8s_nw_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92368/7b23335b-a3ea-7ca3-c6a2-f01def88ace0.png)


## [Ctrl/Node] kubeadmをインストール

```bash
# GPG公開鍵のインストール
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
# aptリポジトリの設定（Googleが提供しているパッケージリポジトリを登録）
$ sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"

# kubeadmインストール
$ sudo apt update
$ sudo apt install -y kubeadm

# swapをオフにする
#   これだとOS再起動の度に必要になるので、/etc/fstabファイルを編集するのがよい
$ sudo swapoff -a
```

## [Ctrl] Kubernetesをセットアップ

```bash
# コントロールプレーンのセットアップ
#   etcd、apiserver、controller-manager、schedulerといったコンテナを起動するなど、色々
#   --apiserver-advertise-address: 他ノードと接続できるネットワークインターフェイスに割り当てられているIPアドレスを指定する
#   --pod-network-cidr: KubernetesのPod（コンテナ）に割り当てるIPアドレス範囲
#   上手くいかなかった場合、 kubeadm reset を実行して設定をリセットしてから再度実行する
$ sudo kubeadm init --apiserver-advertise-address=10.1.100.11 --pod-network-cidr=172.21.0.0/16

# kubectlコマンド設定ファイルの配置
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
# ノード確認
#   ネットワーク関連の設定が完了していないため、STATUSはNotReadyでよい
$ kubectl get nodes

# flannelのセットアップ
#   コンテナ間の通信を行うための仮想ネットワーク

# flannelのためにiptablesを編集
$ sudo sysctl net.bridge.bridge-nf-call-iptables=1
# 設定ファイルの取得、修正
$ wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# net-conf.json: 以下の ”Network” の値を、kubeadm initコマンド実行時に --pod-network-cidr オプションで指定したものに変更する
$ vim kube-flannel.yml
---
"Network": "172.21.0.0/16",
---

# flannelの作成
$ kubectl apply -f kube-flannel.yml

# ノード確認
$ kubectl get nodes

# （参考）クラスタを作り直す場合、上記で作成した仮想インターフェイスを一旦削除しておく
# ip link delete cni0, ip link delete flannel.1
```

## [Node] クラスタにjoinする

```bash
# クラスタにjoinする
$ sudo kubeadm join 10.1.100.11:6443 --token xxxxxx.xxxxxxxxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxxx

# ノード確認（コントロールプレーンにて実行）
$ kubectl get nodes
```

## [Ctrl]JupyterNotebookコンテナを作成・接続
* マニュフェストファイル作成 -> 実行
    * コマンドだと、 docker run  -p 10000:8888 --name jupyter docker.io/jupyter/scipy-notebook

```yaml:notebook.yaml
# Deployment
#   アプリケーションの配布単位を管理するリソース
#   ReplicaSetを管理する（ReplicaSetの履歴を持つ）
#   ReplicaSet: 指定された数のPodを複製し、実行してくれる
apiVersion: apps/v1 # APIのバージョン情報
kind: Deployment # リソースの種類
metadata:
  name: test-notebook # リソースの名前

spec: # リソースの詳細
  selector: # ここで指定したPodを起動する
    matchLabels:
      app: notebook # Podのテンプレートを指定する（[A]）
  replicas: 1 # クラスタ内で起動するPodの数

  template: # 作成される（動かしたい）Podのテンプレート
    metadata:
      labels:
        app: notebook # PodにLabelを付ける（[A]と対応）
    spec:
      containers:
      - name: notebook # コンテナ名
        image: docker.io/jupyter/scipy-notebook # コンテナイメージの場所
        ports:
        - containerPort: 8888 # ポート番号

---
# Service
#   クラスタ内のPodに対して、クラスタ内部および外部のネットワークからアクセスするためのリソース
#   Selectorで選択したPodをUpstreamとしたLB
apiVersion: v1 # APIのバージョン情報
kind: Service # リソースの種類
metadata:
  name: test-service # リソースの名前

spec: # リソースの詳細
  type: NodePort # [ClusterIP, NodePort, LoadBalancer, ExternalName]
  selector: # ここで指定したLabelが付与されているPodに伝送する
    app: notebook # （[A]と対応）
  ports:
    - port: 8888 # ポート番号
```

```bash
# Podのデプロイ
$ kubectl create -f notebook.yaml
# ポート確認
$ kubectl get svc
# logを見てtoken確認
$ kubectl get pod
$ kubectl logs XXX
# -> http://:<30000台のポート>/ でアクセス可能
```

![k8s_nw_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92368/0049925b-3680-9c30-6db3-f51a22597eb1.png)


# メモ
## 確認に使ったコマンド
* NW

```bash
# IP確認
$ ip addr show

# NIC確認
$ ifconfig

# iptables確認
$ sudo iptables -nL

# ルートテーブル確認
$ route
```

* Kubernetes

```bash
# クラスタ確認
$ kubectl cluster-info

# ノード確認
$ kubectl get nodes
$ kubectl get nodes -o==wide
$ kubectl describe node XXX

# ポッド確認
$ kubectl get pod
$ kubectl get pod -o wide
$ kubectl get pod --show-labels
$ kubectl get pod -l env=prod
$ kubectl describe pod XXX
$ kubectl logs XXX

# サービス確認
$ kubectl get svc

# まとめて確認（Deployment, ReplicaSet, Pod, Service）
$ kubectl get all
```

## ネットワークについて
* AWS
    * グローバルIPがOSから（ifconfigで）見えない
        * グローバルIPは、コンソールのネットワークインターフェイスのプロパティとして表示されるが、実態はNATによってプライベートIPにマッピングされている。

* Docker
    * コンテナとして起動するサービスを外部に公開する場合は、コンテナホストの特定ポートに対するアクセスをコンテナに対してiptablesによりDNATすることで、コンテナに対するアクセスを実現している。

* Kubernetes
    * flannel
        * Docker単体での利用との違いは、ノードを跨ぐこと。各Podには**ノードごとに**独立したIPアドレスが割り当てられるが、ノード跨ぎの場合は課題が発生する。Docker単体ではNATを利用しているが、Pod作成の度にNATテーブルを操作するのは効率的ではないため、オーバーレイネットワークを採用している。
        * 詳細は[わかった気になるflannelの仕組み](https://qiita.com/bbrfkr/items/0b0a787f2e3c81009c28)が分かりやすい

# 参考リンク
* [Ubuntuにdockerをインストールする](https://qiita.com/tkyonezu/items/0f6da57eb2d823d2611d)
* [【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その5:Dockerのネットワークってどうなってるの？ 〜](https://tech-lab.sios.jp/archives/20179)
* [Ubuntu 18.04 LTS にKubernetes環境をインストールする [Master / Worker]](https://qiita.com/soumi/items/7736ac3aabbbe4fb474a)
* [Kubernetesネットワーク入門](https://www.netone.co.jp/knowledge-center/netone-blog/20191226-1/)
* [Kubernetesのネットワーク構成](https://tech-lab.sios.jp/archives/7857)
* [わかった気になるflannelの仕組み](https://qiita.com/bbrfkr/items/0b0a787f2e3c81009c28)
* [Kubernetes by Example](https://kubernetesbyexample.com)