オンプレミスのCentOS7上にKubernetes、Docker基盤を構築

# 記事の概要

オンプレミス（物理サーバあるいはVMWarePlayerなどの仮想マシン）上にKubernetes、Docker基盤を、手動（デプロイツール、サービスを使用しない）で構築する手順について記載します。

構築する目的としては、「Kubernetesが何をしているのか、その動作を理解する初めの1歩を踏み出すこと」です。

# 前提条件、環境

使用する環境は、「OS : CentOS7」、「ハードウェア：物理マシン、または、VMWare等の仮想マシン」です。

詳細な前提条件は[Kubernetes公式サイト](https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/install-kubeadm/)の「始める前に」に記載されております。

実際に使用した環境は以下になります。

* マスターノード（1台）
    * ハードウェア：デスクトップパソコン
    * メモリ：4GB
    * プロセッサ：2コア
    * ハードウェア：100GB

* ワーカーノード（1台）
    * ハードウェア：VMWarePlayer14
    * メモリ：2GB
    * プロセッサ：2コア
    * ハードウェア：20GB

* Kubernetesバージョン：1.17.3

# 導入方法

## マスターノードの設定

### 基本設定

設定項目  | 設定値
------------- | -------------
ホスト名  | master
IPアドレス  | 192.168.0.200
ゲートウェイ | 192.168.0.1

### swapの無効化

メモリ領域が不足した際に、HDD領域に仮想メモリを作って、そこを使用するようにするのがスワップ。
これを有効にしていると、メモリ不足には対応できるが、メモリよりもI/Oの遅いHDD領域を使用することでKubernetesの動作が遅くなる問題があり、無効化が必須になった。
一時的に無効化したあとで、恒久的に無効化する設定を行う。

> 一時的に無効化する。

    swapoff -a

> 恒久的に無効化する。

    vi /etc/fstab
    以下の行をコメントアウト
    # /dev/mapper/cl-swap     swap                    swap       defaults        0 0

### Dockerインストール

Dockerはsystemdで実行するようにする。

**※cgroupsでも実行することが可能だが、Docker cgroupsドライバーとk8s cgroupsドライバーが異なり、k8sが正常に動作しないことがあるため、systemdを使う。**

* 必要なパッケージのインストール

```shell-session
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
```
* Dockerのリポジトリを指定

```shell-session
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
```

* Dockerのインストール

```shell-session

1-1 バージョン指定なし
sudo yum install docker-ce docker-ce-cli containerd.io

1-2 バージョン指定あり
バージョン確認
yum list docker-ce --showduplicates | sort -r

バージョン指定してインストール
sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io

2 systemd経由で起動されるように設定
vi /etc/docker/daemon.json
----
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}  
----

mkdir -p /etc/systemd/system/docker.service.d

systemctl daemon-reload

3 自動起動有効化

systemctl start docker

```

**※動作確認**

```shell-session
systemctl start docker
docker run hello-world

----
Hello from Docker!  ←これが表示されていればOK! 
This message shows that your installation appears to be working correctly.
...
```

### kubernetesのインストール

#### ネットワーク設定

トラフィックがiptablesを迂回して、正しくないルートを通るという問題があるため、明示的にiptablesを有効にしておく必要がある。

```shell-session
vi /etc/sysctl.d/k8s.conf

----
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
----

sysctl --system
```

ホスト名でアクセス出来るようにする。

```shell-session
vi /etc/hosts
----
192.168.0.200 master
192.168.0.201 node
----
```

#### SELinuxの無効化

コンテナがホストファイルシステムにアクセスするために、SELinuxを無効にする。
※kubeletがSELinuxをサポートするまでは必要。

```shell-session
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
```

#### yumのリポジトリを追加

```shell-session
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
```

#### インストール

```shell-session
yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

自動起動有効化
systemctl enable --now kubelet
```

#### daemonの設定変更

dockerをsystemdで動かしているため、それをkubeletに伝える必要がある。

※デフォルトではkubeletは「cgroupfs」でdockerを動かそうとするため。

```shell-session
sudo vi /etc/sysconfig/kubelet

----
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd
----

設定反映のためにkubeletを再起動する。

systemctl daemon-reload
systemctl restart kubelet
```

#### ポートの開放

以下のポートをLinuxのfirewalldで許可する。

プロトコル  | 通信の向き  | ポート  | 目的  | 使用ユーザー
------------- | ------------- | ------------- | ------------- | -------------
TCP  |  Inbound  |  6443*      | Kubernetes API server   |  All
TCP  |  Inbound  |  2379-2380  | etcd server client API  |  kube-apiserver, etcd
TCP  |  Inbound  |  10250      | Kubelet API             |  Self, Control plane
TCP  |  Inbound  |  10251      | kube-scheduler          |  Self
TCP  |  Inbound  |  10252      | kube-controller-manager  |  Self

```shell-session
sudo firewall-cmd --zone public --add-port 6443/tcp --permanent
sudo firewall-cmd --zone public --add-port 2379-2380/tcp --permanent
sudo firewall-cmd --zone public --add-port 10250/tcp --permanent
sudo firewall-cmd --zone public --add-port 10251/tcp --permanent
sudo firewall-cmd --zone public --add-port 10252/tcp --permanent
sudo firewall-cmd --reload
```

### クラスターの構築

kubeadmはkubenetesクラスターを構築するのを助けてくれるもの。

**※現時点でのベストプラクティス。**

#### コントロールプレーンノードの初期化

**※コントロールプレーンノードは、etcdやapiがインストールされているノードのこと。つまりマスターノード。**

```shell-session
※kubeletサービスが動いているとエラーが発生するため、systemctl stop kubelet　でkubeletサービスを停止しておく。

sudo kubeadm init \
--apiserver-advertise-address 192.168.0.200 \
--kubernetes-version 1.17.3 \
--pod-network-cidr 10.244.0.0/16
    
    
以下のような出力をメモしておく。後でノードを追加する際に使用する。
    
kubeadm join 192.168.0.200:6443 --token zsczab.c5g3cgugsxwoyvws \
--discovery-token-ca-cert-hash sha256:1da2cb46ee192bce33c9eb7c2189e0e811daddb9f9e2e40144d6b190f04d00b5
```

#### configファイルの設定

rootユーザでkubectlを利用する場合、環境変数に、confファイルを登録する。

```shell-session
export KUBECONFIG=/etc/kubernetes/admin.conf
```

**※kubeadm init実行時の出力に記載あり。**

#### 動作確認

```shell-session
kubectl get nodes

以下のような表示が出力されることを確認する。

NAME     STATUS     ROLES    AGE     VERSION
master   NotReady   master   3m58s   v1.17.3
```

### コンテナネットワークインターフェース(CNI)の作成

#### kubernetesに展開

```shell-session
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml

sudo firewall-cmd --zone public --add-port 8285/udp --permanent
sudo firewall-cmd --zone public --add-port 8472/udp --permanent
sudo firewall-cmd --reload
```

## ワーカーノードの設定

### 基本設定

設定項目  | 設定値
------------- | -------------
ホスト名  | worker
IPアドレス  | 192.168.0.201
ゲートウェイ | 192.168.0.1

### swapの無効化

マスターノードの設定と同じ方法で実行する。

### Dockerインストール

マスターノードの設定と同じ方法で実行する。

### kubernetesのインストール

#### ネットワーク設定

マスターノードの設定と同じ方法で実行する。

#### ポートの開放

以下のポートをLinuxのfirewalldで許可する。

```shell-session
sudo firewall-cmd --zone public --add-port 10250/tcp --permanent
sudo firewall-cmd --zone public --add-port 30000-32767/tcp --permanent
sudo firewall-cmd --reload
```

#### マスターノードに登録

    kubeadm join 192.168.0.200:6443 --token zsczab.c5g3cgugsxwoyvws \
    --discovery-token-ca-cert-hash sha256:1da2cb46ee192bce33c9eb7c2189e0e811daddb9f9e2e40144d6b190f04d00b5

**※マスターノード構築時にメモしたkubeadmの出力を参照**

## 動作確認

マスターノード上で以下のコマンドを実行する。

```shell-session
kubectl get node

以下のようにmasterとnodeが表示されていればよし。
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   9d    v1.17.3
node     Ready    <none>   9d    v1.17.4
```

# 参考資料

【cgroupsではなくsystemdを使用する理由】
<https://www.ibm.com/support/knowledgecenter/ja/SSBS6K_2.1.0.3/troubleshoot/kubelet_fails.html>


【Dockerインストール方法】
<https://docs.docker.com/install/linux/docker-ce/centos/>

【k8sインストール方法】
<https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/install-kubeadm/>

【k8sクラスター構築方法】
https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/