# 使用 Kind 构建离线集群安装包

[Kind（Kubernetes in Docker）](https://github.com/kubernetes-sigs/kind) 是一个 Kubernetes 孵化项目，它使用 Docker 为节点进行 Kubernetes 的快速部署，可以方便的搭建 Kubernetes 集群，虽然简单，但也是通过 [Kubernetes 一致性认证](https://landscape.cncf.io/selected=kind)的安装器，因此用来做测试还是比较可靠的。

按照社区的一贯德行，安装过程虽然只要有个 Docker 就可以了，但是对网络有严重的依赖。在 Workshop 之类的场合，会受到很大限制。这种情况下，有一个离线包就会非常方便了。

## 获取安装文件

本文会从一个新安装的 CentOS Mini 服务器为例，逐个步骤展示构建离线 Kind 安装包的过程。最后再安装一个

> 这里假设这个服务器有“优秀”的互联网连接。

### Docker 离线包

首先我们要为 Docker 的离线安装准备好 RPM 包，新建一个目录 `rpms`，用来下载安装包：

~~~shell
$ yum install --downloadonly --downloaddir=./rpms \
    yum-utils device-mapper-persistent-data lvm2
~~~

执行完毕之后，会在 rpms 目录中找到 Docker 的依赖包，接下来获取 Docker 的 RPM。

~~~shell
$ yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
$ yum --downloadonly --downloaddir=./rpms \
    install docker-ce docker-ce-cli containerd.io  
~~~

如此就获得了 Docker 自身以及所有的依赖 RPM 包。可以使用 rpm 直接安装。并启动 Docker 服务。

### Kubeadm 和 Kubectl

根据[官方文档](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#installing-runtime)：

~~~shell
$ 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

$ yum install --downloadonly --downloaddir=./rpms \
kubectl kubeadm --disableexcludes=kubernetes
~~~

这里做了一点修改：不再安装 kubelet，也不启动服务，仅用于获取安装包。

### 获取镜像

使用 rpm 安装 kubeadm，开始获取安装所需的所有镜像：

~~~shell
$ kubeadm config images list --kubernetes-version=v1.15.3
k8s.gcr.io/kube-apiserver:v1.15.3
k8s.gcr.io/kube-controller-manager:v1.15.3
k8s.gcr.io/kube-scheduler:v1.15.3
k8s.gcr.io/kube-proxy:v1.15.3
k8s.gcr.io/pause:3.1
k8s.gcr.io/etcd:3.3.10
k8s.gcr.io/coredns:1.3.1
~~~

拿到列表了就可以逐个拉取处理镜像，这里给个小脚本：

~~~bash
#!/bin/sh
for image in `kubeadm config images list --kubernetes-version=$1`
do
  image_name=`echo ${image} | sed "s/k8s.gcr.io.//g"`
  docker pull "${image}"
  docker tag "${image}" "${image_name}"
  file_name="${image_name}.tgz"
  docker save "${image_name}" | gzip > "${file_name}"
  docker rmi "${image}"
  docker rmi "${image_name}"
done
~~~

这样我们就有了所有 Kubeadm 所需的镜像。另外我们还需要两个辅助镜像：`kindest/node:v1.15.3` 以及 `registry:2.7.1`。

### CFSSL 和 Kind

~~~shell
$ wget https://github.com/kubernetes-sigs/kind/releases/download/v0.5.1/kind-linux-amd6
$ wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
$ wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
~~~

这样我们已经获得了所有安装所需的文件。可以进入离线步骤了。

## 安装

### RPM 安装

把前面获得的 Kubectl 安装起来。

### 签发证书

使用 CFSSL 生成证书，首先生成 CA。

`ca-csr.json`：

~~~json
{
    "CN": "Kubernetes CA",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "ca": {
       "expiry": "87600h"
    }
}
~~~

`cfssl gencert -initca ca-csr.json | cfssljson -bare ca` 生成 CA 证书。

把证书加入信任列表：

~~~shell
$ update-ca-trust enable
$ cp ca.pem /etc/pki/ca-trust/source/anchors/
$ update-ca-trust extract
~~~

更新之后，需要重启 Docker 服务，让 Docker 也信任这一 CA。

然后是生成镜像库所需的服务器证书：

`config.json`：

~~~json
{
    "signing": {
        "default": {
            "expiry": "87600h"
        },
        "profiles": {
            "server": {
                "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            },
            "user": {
                "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "client auth"
                ]
            }
        }
    }
}
~~~

`server.json`：

~~~json
{
    "CN": "Registry",
    "hosts": [
      "127.0.0.1","10.211.55.10"
    ]
}
~~~

接下来签发证书：

~~~shell
$ cfssl gencert -ca ca.pem -ca-key ca-key.pem -config=config.json \
     -profile=server server.json | cfssljson -bare registry
~~~

> 这里的 IP 地址要跟本地地址对应。

### 启动镜像库

使用 `docker load -i`，载入了 registry 镜像，把仓库运行起来：

~~~bash
#!/bin/sh
docker run -d \
   --name="registry2" \
   -v "$(pwd)/storage":/var/lib/registry \
   -p 5000:5000 \
  -v "$(pwd)/certs":/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.pem \
  -e REGISTRY_HTTP_TLS_KEY=/certs/registry-key.pem \
   --restart=always \
   registry:2.7.1
~~~

如此就在 5000 端口启动了一个镜像库。

### 推送镜像到私库

把前面的所有镜像都载入 Docker，Tag 之后推入私库，最后在私库里就有了一系列的镜像，例如 `10.211.55.10:5000/pause:3.1`。

### 安装集群

为了能够使用私库的镜像，要对 Kind 进行一点配置：

~~~yaml
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
kubeadmConfigPatches:
- |
  apiVersion: kubeadm.k8s.io/v1beta2
  kind: ClusterConfiguration
  metadata:
    name: config
  networking:
    serviceSubnet: 10.0.0.0/16
  imageRepository: 10.211.55.10:5000
  nodeRegistration:
    kubeletExtraArgs:
      pod-infra-container-image: 10.211.55.10:5000/pause:3.1
- |
  apiVersion: kubeadm.k8s.io/v1beta2
  kind: InitConfiguration
  metadata:
    name: config
  networking:
    serviceSubnet: 10.0.0.0/16
  imageRepository: 10.211.55.10:5000
nodes:
- role: control-plane
~~~

~~~shell
$ kind create cluster --image kindest/node:v1.15.3
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.15.3) 🖼
 ✓ Preparing nodes 📦
 ✓ Creating kubeadm config 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Cluster creation complete. You can now use the cluster with:

export KUBECONFIG="$(kind get kubeconfig-path --name="kind")"
~~~

如此这般，Kind 集群就启动了。我们可以测试一下：

~~~shell
$ export KUBECONFIG="$(kind get kubeconfig-path --name="kind")"
$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                         READY   STATUS    RESTARTS   AGE
kube-system   coredns-5c98db65d4-lkg92                     1/1     Running   0          72m
kube-system   coredns-5c98db65d4-rhv7g                     1/1     Running   0          72m
kube-system   etcd-kind-control-plane                      1/1     Running   0          71m
kube-system   kindnet-892gh                                1/1     Running   1          72m
kube-system   kube-apiserver-kind-control-plane            1/1     Running   0          71m
kube-system   kube-controller-manager-kind-control-plane   1/1     Running   0          71m
kube-system   kube-proxy-nd4ml                             1/1     Running   0          72m
kube-system   kube-scheduler-kind-control-plane            1/1     Running   0          71m
~~~

## 离线环境下部署镜像

Kind 提供了一个 load 命令，可以把镜像载入所有“节点”，例如：

~~~shell
$ kind load docker-image 10.211.55.10:5000/registry:2.7.1
$ kubectl run registry --image=10.211.55.10:5000/registry:2.7.1 --replicas=2
$ kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/registry created
$ kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
registry-6b7648c4f4-g8zxr   1/1     Running   0          24s
~~~

## 后记

很明显，这些步骤是可以自动化的，只需要分发一个安装包，就能够很方便的快速部署、删除一整套的 Kubernetes，可以用随需部署的方式完成环境的搭建和删除。
