KubeEdgeを構築する(再)

# はじめに
[前記事](https://qiita.com/pideoh/items/77f384788b9616c73107)では、K8sのコントロールプレーンをEKSとしてKubeEdgeの構築を試みました。結果、EKSはEKSに最適化されたAMIから構築されたEC2しか利用できず、AWS外のエッジノードはWorkerNodeとしてJoinできないことがわかりました。
今回は`kubeadm`を利用してコントロールプレーンも構築した上で、KubeEdgeの構築を試みます。
KubeEdgeの概要については[こちら](https://qiita.com/pideoh/items/175af7bc9f915fef578f)をご参照ください。

なお、[前記事](https://qiita.com/pideoh/items/77f384788b9616c73107)では、EKSのバージョン表記の問題からKubeEdgeのインストールツールである`keadm`がうまく動作しないバグがあり、そのバグがリリースされていないものの、HEADで解決されていたことから、`keadm`を自分でビルドし、構築を行う方法を取りました。
※KubeEdge 1.2.1の次のバージョンにて改修版がリリースされるものと思われます
本記事においてはピュアなコントロールプレーンを構築するため、上記の問題は発生せず、自分でビルドする必要はないのですが、せっかくなのでより新しいバイナリを、ということでこれをそのまま使い回すこととします。


# 実行環境
今回ためした実行環境は以下の通りです。

### ローカル
- macOS Catalina(10.15.3)
- zhs + prezto
- aws-cli/1.18.24 Python/3.8.2 Darwin/19.3.0 botocore/1.15.24
- Homebrew 2.2.10

### K8s Control plane & KubeEdge Master Node
K8sのコントロールプレーンとKubeEdgeマスターノードは同居の構成とします。
（KubeEdgeのマスターノードは大してメモリも食わないので！）
- Ubuntu Server 18.04 LTS (HVM), SSD Volume Type - ami-07f4cb4629342979c (64 ビット x86)
- t3.medium
- K8s 1.18.0
- kubeadm 1.18.0
- kubectl 1.18.0
- golang 1.13.8
- keadm HEAD(506b130)
- KubeEdge 1.2.1

### KubeEdge Worker Node
本来はエッジサーバやRaspberry Piなどの機器で実行するものですが、NWのつなぎこみ等手間がかかるので今回はEC2で構築し、これをEdgeNodeとして見立てます。
EC2は以下のインスタンスを立てました。HEADを見る限り、CentOSなどへの対応が進められていますが、現状はMasterNodeもEdgeNodeもUbuntuのみ対応です。

- Ubuntu Server 18.04 LTS (HVM), SSD Volume Type - ami-07f4cb4629342979c (64 ビット x86)
- t3.medium


# K8s Control plane & KubeEdge Master Nodeのセットアップ
早速セットアップしていきます。

## 諸々アップデート
おまじないですが、やっておきます。

```terminal
# apt-get update
# apt-get upgrade
```

## K8s Control plane & KubeEdge Master Nodeの構築
[公式ドキュメント](http://docs.kubeedge.io/en/latest/setup/kubeedge_install_keadm.html)をみながら構築を進めます。

### K8s Control planeのインストール
こちらも同じく[公式ドキュメント](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/)をみながらインストールを進めます。


```terminal
##初期設定
##ブリッジされたトラフィックをiptablesからフィルタリングできるように変更する
##br_netfilterのロード
# modprobe br_netfilter
##ロード確認。表示されればロードされている
# lsmod | grep br_netfilter
br_netfilter           24576  0
bridge                155648  1 br_netfilter

##設定の投入
# cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
##設定の反映
# sysctl --system
~
##設定の確認。2行表示されれば設定できている
# sysctl -a | grep -e "net.bridge.bridge-nf-call-ip6tables = 1" -e "net.bridge.bridge-nf-call-iptables = 1"
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

##Dockerのインストール
# apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
# add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
# apt-get update
# apt-get install docker-ce docker-ce-cli containerd.io

# cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF
# mkdir -p /etc/systemd/system/docker.service.d
# systemctl daemon-reload
# systemctl restart docker


##kubeadm, kubelet, kubectlのインストール
# apt-get update && apt-get install -y apt-transport-https curl
Hit:1 http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu bionic InRelease
Hit:2 http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu bionic-updates InRelease                                             
Hit:3 http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu bionic-backports InRelease                                           
Hit:4 https://download.docker.com/linux/ubuntu bionic InRelease                                                                    
Hit:5 http://security.ubuntu.com/ubuntu bionic-security InRelease                                                                  
Reading package lists... Done
Reading package lists... Done
Building dependency tree       
Reading state information... Done
curl is already the newest version (7.58.0-2ubuntu3.8).
apt-transport-https is already the newest version (1.6.12).
0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded.

# curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
# cat <<EOF | tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
deb https://apt.kubernetes.io/ kubernetes-xenial main

# apt-get update
# apt-get install -y kubelet kubeadm kubectl
# apt-mark hold kubelet kubeadm kubectl
kubelet set on hold.
kubeadm set on hold.
kubectl set on hold.

##kubeletが利用するcgroup driverの設定
##Dockerのcgroup dirverにあわせてsystemdに変更する
# echo 'KUBELET_EXTRA_ARGS=--cgroup-driver=systemd' > /etc/default/kubelet
# systemctl daemon-reload
# systemctl restart kubelet


##kubeadmをつかったK8sコントロールプレーンの構築
##--pod-network-cidrはPodのCIDRを指定するオプション。今回はCNIとしてFlannelを利用するため、
##FlannelのCIDRにあわせる必要がある。
##※Flannelが利用するCIDRを変更することも可能だが、その場合、本オプションとFlannnelをデプロイする際のyamlを書き換えてデプロイする必要がある
# kubeadm init --pod-network-cidr=10.244.0.0/16
W0408 13:57:33.184817    5951 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]
[init] Using Kubernetes version: v1.18.0
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
〜
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  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 172.31.26.4:6443 --token hogehoge \
    --discovery-token-ca-cert-hash sha256:hogehoge 


##kubectlの初期設定
# mkdir -p $HOME/.kube
# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# chown $(id -u):$(id -g) $HOME/.kube/config


##Flannnelのデプロイ
# kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds-amd64 created
daemonset.apps/kube-flannel-ds-arm64 created
daemonset.apps/kube-flannel-ds-arm created
daemonset.apps/kube-flannel-ds-ppc64le created
daemonset.apps/kube-flannel-ds-s390x created

##デプロイの確認
##動作しているPodを確認し、kube-flannnel-ds-amd64がデプロイされ、STATUSがRunningであることを確認する
# kubectl get pod -A
NAMESPACE     NAME                                     READY   STATUS    RESTARTS   AGE
kube-system   coredns-66bff467f8-6l5vx                 1/1     Running   0          2m25s
kube-system   coredns-66bff467f8-zlsx4                 1/1     Running   0          2m25s
kube-system   etcd-ip-172-31-26-4                      1/1     Running   0          2m43s
kube-system   kube-apiserver-ip-172-31-26-4            1/1     Running   0          2m43s
kube-system   kube-controller-manager-ip-172-31-26-4   1/1     Running   0          2m42s
kube-system   kube-flannel-ds-amd64-hncgd              1/1     Running   0          39s
kube-system   kube-proxy-kv52x                         1/1     Running   0          2m25s
kube-system   kube-scheduler-ip-172-31-26-4            1/1     Running   0          2m42s
```

## KubeEdgeのインストール

## keadmのビルド

```console
##golangのインストール
##インストールにはgoenvを利用し、インストール手順(https://github.com/syndbg/goenv/blob/master/INSTALL.md)に従ってインストールする
##goenvのチェックアウト
# git clone https://github.com/syndbg/goenv.git ~/.goenv

##パスの追加。
##Ubuntuはbash_profile読まないのでbashrcへパスを通す
# echo 'export GOENV_ROOT="$HOME/.goenv"' >> ~/.bashrc
# echo 'export PATH="$GOENV_ROOT/bin:$PATH"' >> ~/.bashrc
# echo 'eval "$(goenv init -)"' >> ~/.bashrc

##ターミナルの再起動

##golang 1.13系の最新版(執筆時点)の1.13.8をインストール
# goenv install  1.13.8
Downloading go1.13.8.linux-amd64.tar.gz...
-> https://dl.google.com/go/go1.13.8.linux-amd64.tar.gz
Installing Go Linux 64bit 1.13.8...
Installed Go Linux 64bit 1.13.8 to /root/.goenv/versions/1.13.8
# goenv global 1.13.8

##go versionを実行してインストールしたバージョンが表示されれば完了
# go version
go version go1.13.8 linux/amd64


##keadmのビルド
##リポジトリのクローン。ブランチはHEADを使いたいため、Masterでよい
# git clone https://github.com/kubeedge/kubeedge.git $GOPATH/src/github.com/kubeedge/kubeedge

##ビルドの実行
# cd $GOPATH/src/github.com/kubeedge/kubeedge
# make all WHAT=keadm
# #エラーが出なければOK

##以下のパスにkeadmのバイナリが作成されていれば完了
# cd _output/local/bin/
# ls -l
total 37064
-rwxr-xr-x 1 root root 37952949 Mar 19 14:10 keadm
```

## KubeEdgeのインストール
ビルドしたkeadmを実行します。最後に`CloudCore started`と出たら完了です。

```console
##keadm initを実行するとcloudcoreのバイナリダウンロードや証明書類の発行、デフォルトのコンフィグ作成などが行われる
# ./keadm init
Kubernetes version verification passed, KubeEdge installation will start...
~中略~
Certificates got generated at: /etc/kubeedge/ ca and /etc/kubeedge/ certs
certs/
certs/edge.csr
certs/edge.crt
certs/edge.key

Certificates got tared at: /etc/kubeedge/ path, Please copy it to desired edge node (at /etc/kubeedge/ path)

KubeEdge cloudcore is running, For logs visit:  /var/log/kubeedge/cloudcore.log
CloudCore started
```


# KubeEdge Worker Nodeのセットアップ

## 諸々アップデート
おまじないですが、やっておきます。

```console
# apt-get update
# apt-get upgrade
```

## Dockerのインストール
[公式ドキュメント](https://docs.docker.com/install/linux/docker-ce/ubuntu/)をみてインストールします。

```console
# apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -

##x86_64/amd64はコレ
##arm等の場合は公式を見てリポジトリのURLを判断すること
# add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

# apt-get update
# apt-get install docker-ce docker-ce-cli containerd.io
```

## KubeEdgeのインストール
MasterNodeの`/etc/kubeedge/`配下のファイル郡をWorkerNodeの`/etc/kubeedge`配下へコピーします。
また、先程ビルドした`keadm`も同様に自分のホームディレクトリや`/usr/local/bin`などにコピーします。

## クラスターへのJoin
以下のコマンドを実行すれば、EdgeNodeのバイナリ郡がダウンロードされ、KubeEdgeのMasterを経由してK8sにWorkerNodeとして登録されます。

```console
~# ./keadm join --cloudcore-ipport=192.168.10.195:10000 
# keadm join --cloudcore-ipport=172.31.26.4:10000 --interfacename=ens5 --runtimetype docker --edgenode-name edge-01
〜略〜
kubeedge-v1.2.1-linux-amd64/
kubeedge-v1.2.1-linux-amd64/edge/
kubeedge-v1.2.1-linux-amd64/edge/edgecore
kubeedge-v1.2.1-linux-amd64/cloud/
kubeedge-v1.2.1-linux-amd64/cloud/csidriver/
kubeedge-v1.2.1-linux-amd64/cloud/csidriver/csidriver
kubeedge-v1.2.1-linux-amd64/cloud/admission/
kubeedge-v1.2.1-linux-amd64/cloud/admission/admission
kubeedge-v1.2.1-linux-amd64/cloud/cloudcore/
kubeedge-v1.2.1-linux-amd64/cloud/cloudcore/cloudcore
kubeedge-v1.2.1-linux-amd64/version

KubeEdge edgecore is running, For logs visit:  /var/log/kubeedge/edgecore.log
```

# 動作確認
## WorkerNodeの登録確認
K8s Control plane & KubeEdge Master Nodeからkubectlで確認してみます。
ノードが登録され、STATUSがReadyであれば無事動作しています。

```console
# kubectl get node -o wide
NAME              STATUS   ROLES    AGE   VERSION                   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION    CONTAINER-RUNTIME
edge-01           Ready    edge     14d   v1.17.1-kubeedge-v1.2.1   172.31.19.17    <none>        Ubuntu 18.04.4 LTS   4.15.0-1065-aws   remote://19.3.8
```

## Podのデプロイ
以下のyamlでPodをデプロイしてみます。

```yaml:nginx-edge-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-edge-deployment
  labels:
    k8s-app: nginx-edge
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: nginx-edge
  template:
    metadata:
      labels:
        k8s-app: nginx-edge
    spec:
      nodeSelector:
        node-role.kubernetes.io/edge: ""
      containers:
      - name: nginx
        image: nginx:1.15.12
        ports:
        - containerPort: 80
          hostPort: 80
```

```console
# kubectl apply -f nginx-edge-deployment.yaml 
deployment.apps/nginx-edge-deployment configured
# kubectl get pod -o wide
NAME                                         READY   STATUS    RESTARTS   AGE   IP              NODE              NOMINATED NODE   READINESS GATES
nginx-edge-deployment-6747b6875c-sd4rb       1/1     Running   0          9s    172.17.0.2      edge-01           <none>           <none>
```

無事、Podが起動しています！
ただし、PoｄのIPはFlannnelのCIDRとは異なるようです。調べてみたところ、現状の仕様のようです（これは次回解明します）

WorkerNodeからも見てみます。
同様に問題なさそうです！！

```console
# docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED              STATUS              PORTS                NAMES
7a31727ab1b1        53f3fd8007f7         "nginx -g 'daemon of…"   About a minute ago   Up About a minute                        k8s_nginx_nginx-edge-deployment-6747b6875c-sd4rb_default_dbf2b7fd-dcee-4e77-9647-bd15657f57a2_0
7dbd20393fbc        kubeedge/pause:3.1   "/pause"                 About a minute ago   Up About a minute   0.0.0.0:80->80/tcp   k8s_POD_nginx-edge-deployment-6747b6875c-sd4rb_default_dbf2b7fd-dcee-4e77-9647-bd15657f57a2_0
```


# まとめ
`kubeadm`を利用しK8sのコントロールプレーンを構築し、KubeEdge1.2.1を用いてWorkerNodeをクラスターに参加させました。
また、その上でPodが動作することも確認できました。
次回は構築できたKubeEdgeを用いてK8sと同様に使える機能、使えない機能、特徴等について深堀りしていきたいと思います。