在中国大陆本地部署 Kubernetes 集群

适用于 Kubernetes 1.12

基于众所周知的原因,在中国大陆是无法访问 k8s.gcr.io。这意味着我们不能直接按照官方教程创建集群。

接下来我将演示在不借助 VPN 等资源的前提下,如何使用 kubeadm 在本地搭建一个 Single Master 集群。

本文大纲

安装

以下基于 Ubuntu 16.04 为例,安装 docker-ce, kubeadm kubelet kubectl 程序包。

这里使用阿里云镜像站的 APT 源,YUM 源的信息请参考该网站的说明。

安装 kubeadm kubelet kubectl

apt-get update && apt-get install -y apt-transport-https ca-certificates curl
curl -s https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF

apt-get update
apt-get install -y kubelet=1.12.5-00 kubeadm=1.12.5-00 kubectl=1.12.5-00
apt-mark hold kubelet kubeadm kubectl

安装并配置 docker-ce

apt-get update && apt-get install -y apt-transport-https ca-certificates curl
curl -s http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/docker-ce.list
deb [arch=amd64] https://mirror.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu xenial stable
EOF

apt-get update
apt-get install -y docker-ce=18.06.2~ce~3-0~ubuntu

# 使用国内镜像
cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "registry-mirrors": ["https://registry.docker-cn.com"]
}
EOF

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

systemctl daemon-reload
systemctl restart docker

初始化

Kubernetes 集群的初始化可以分为三个步骤

Master

kubeadm 1.12 使用的是 v1alpha3 API,这里的定义包含了 InitConfigurationClusterConfiguration 两部分。

apiVersion: kubeadm.k8s.io/v1alpha3
kind: InitConfiguration
nodeRegistration:
  kubeletExtraArgs:
    # 从 Aliyun Registry 拉取基础镜像
    pod-infra-container-image: registry.aliyuncs.com/google_containers/pause-amd64:3.1
---
apiVersion: kubeadm.k8s.io/v1alpha3
kind: ClusterConfiguration
# 从 Aliyun Registry 拉取 Control Plane 镜像
imageRepository: registry.aliyuncs.com/google_containers
# 使用确定的 Kubernetes 版本,避免初始化时从 https://dl.k8s.io/release/stable-1.12.txt 读取
kubernetesVersion: v1.12.5
networking:
  # 如使用 flannel 组件应增加如下配置
  podSubnet: 10.244.0.0/16
  serviceSubnet: 10.96.0.0/12

在 Master 上,将上述内容保存名为 init.yml 文件,运行如下命令开始初始化。kubeadm 将自动下载镜像,以 Static Pod 方式运行 Control Plane。

kubeadm init --config init.yml

初始化完成后,界面上会输出一条 kubeadm join 命令,将其记录到本地,稍后会用于 Node 加入集群。命令示例如下

kubeadm join --token <token> <master-ip>:<master-port> --discovery-token-ca-cert-hash sha256:<hash>

访问集群

可以参照 kubeadm 初始化后输出的说明,增加使用 kubectl 访问 Kubernetes 集群所需的配置。

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

这时可以使用 kubectl -n kube-system get pods 查看到已部署的 Control Plane Pod。

Node

Node 加入集群应使用如下配置,诸如 <token> 应使用前一阶段界面输出的参数值替换。

apiVersion: kubeadm.k8s.io/v1alpha3
kind: JoinConfiguration
discoveryTokenAPIServers:
- <master-ip>:<master-port>
discoveryTokenCACertHashes:
- sha256:<hash>
nodeRegistration:
  kubeletExtraArgs:
    pod-infra-container-image: registry.aliyuncs.com/google_containers/pause-amd64:3.1
token: <token>

在 Node 上,将上述内容保存名为 join.yml 的文件,运行如下命令加入集群。

kubeadm join --config join.yml

运行成功后,我们可以在 Master 上使用 kubectl get nodes 查看到已加入集群的 Node。

Pod Network

在完成前面两步后,通过 kubectl get nodes 我们可以看到 Master 和 Node 状态是 NotReady,这是因为缺少 Pod Network 附加组件

现在部署的 Pod 不会被分配 IP,会维持在 ContainerCreating 状态。比如通过 kubectl -n kube-system get pods -l k8s-app=kube-dns 查看到的 CoreDNS Pod。

可以选择任意一款支持 CNI 的网络组件,这里使用 flannel 作为示例,在 Master 上运行如下命令。

# 内容拷贝自 https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# 只是从 Aliyun Registry 加载 flannel 镜像
kubectl apply -f https://git.io/fxNfD

待网络组件安装完成后,再次运行 kubectl -n kube-system get pods -l k8s-app=kube-dns 可以看到 CoreDNS Pod 已经变为 Running 状态。

测试

在全部初始化完成后,我们可以做一些测试,确保各个功能模块都是正确运行的。

验证 kube-apiserver, kube-controller-manager, kube-scheduler, pod network 正常

# 部署一个 Nginx Deployment,包含两个 Pod
# https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
kubectl create deployment nginx --image=nginx:alpine
kubectl scale deployment nginx --replicas=2

# 待启动后,两个 Nginx Pod 应该是 Running 状态,并且各自分配有 10.244 开头的集群内 IP
kubectl get pods -l app=nginx -o wide
# 结果示例
# NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE                   
# nginx-65d5c4f7cc-d88nq   1/1     Running   0          3s    10.244.1.7   izuf6ex1vq9wpui8cgkiekz
# nginx-65d5c4f7cc-q8vlq   1/1     Running   0          3s    10.244.2.3   izuf6efhmsjefx1lfbu3qaz

验证 kube-proxy 正常

# 以 NodePort 方式对外提供服务
# https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/
kubectl expose deployment nginx --port=80 --type=NodePort

# Nginx 服务应该得到一个 10.96 开头的集群内 IP,以及集群外可访问的 Port
kubectl get services nginx
# 结果示例
# NAME    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
# nginx   NodePort   10.98.222.155   <none>        80:32630/TCP   14s

# 可以通过任意 NodeIP:Port 在集群外部访问这个服务
curl http://node-ip:32630

验证 dns, pod network 正常

# 启动一个 Busybox 部署,并进入其内部
# 如果没有出现提示符,按下回车键
kubectl run -it curl --image=radial/busyboxplus:curl

# 输入命令 nslookup nginx 应可以正确解析出集群内的 IP,证明 DNS 服务正常
[ root@curl-5cc7b478b6-7zxlq:/ ]$ nslookup nginx
# 结果示例
# Server:    10.96.0.10
# Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
#
# Name:      nginx
# Address 1: 10.98.222.155 nginx.default.svc.cluster.local

# 输入命令 curl nginx 应可以正确返回 Nginx 首页,证明 kube-proxy 正常
[ root@curl-5cc7b478b6-7zxlq:/ ]$ curl http://nginx/

# 如果部署有多个 Node,应针对每个 Nginx Pod 的集群内 IP 进行 curl,证明跨 Node 的 Pod Network 正常
# 这里是比较容易出问题的故障点,请参考网络组件相关文档进行排查
[ root@curl-5cc7b478b6-7zxlq:/ ]$ curl http://10.244.1.7/
[ root@curl-5cc7b478b6-7zxlq:/ ]$ curl http://10.244.2.3/

结语

至此,我们搭建了一个 Kubernetes 集群,接下来可以参考官方文档部署其他服务了。

附记

本文默认从 registry.aliyuncs.com 拉取 Docker Image,该地址可在阿里云之外访问。

也可以根据自身所属区域,使用 registry.<region-id>.aliyuncs.com 来替代,比如北京区域可使用 registry.cn-beijing.aliyuncs.com

如果是在阿里云上 VPC 网络内搭建,可以使用 registry-vpc.<region-id>.aliyuncs.com 内网地址。