在阿里云内搭建高可用的Kubernetes集群

本文完整地记录下了在阿里云内网构建一个高可用Kubernetes集群的过程。

硬件资源

类型 名称 备注
ECS master-001 ip: 192.168.100.1
ECS master-002 ip: 192.168.100.2
ECS master-003 ip: 192.168.100.3
ECS worker-001 ip: 192.168.100.4
SLB internal-slb ip: 192.168.200.1 内网SLB,内网流量入口
SLB public-slb ip: 100.100.100.100 公网SLB,外网流量入口
ECS haproxy-001 ip: 192.168.110.1 HAProxy集群,不部署kubernetes节点(下文详细说明)
ECS haproxy-002 ip: 192.168.110.2

既然有SLB,为什么要用HAProxy?

我发现一个很尴尬的情况:比如SLB监听master-001的80端口,然后在master-001上执行nc -v 192.168.200.1 80居然是超时,无法走通。

这样就产生一个很困惑的状况:如果我把内网SLB作为master集群入口,我在master节点上是无法访问成功的,所以经过多次尝试,不得不使用HAProxy,而且部署HAProxy的ECS还不能作为Kubernetes集群内的任何一个Node。

所以,master cluter的网络拓扑是这样的:

准备工作

安装docker-ce

我这里使用18.06版本的docker,这是目前kubernetes支持的最高版本。我们先添加阿里云的docker镜像地址:

# 先安装必要依赖
apt update -y
apt install -y apt-transport-https ca-certificates curl software-properties-common

# 添加阿里云的docker镜像地址
curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add -
add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

# 重新更新下apt
apt update -yqq

然后执行安装命令,并锁定版本号:

apt install -yqq --allow-downgrades docker-ce=18.06
apt-mark hold docker-ce

另外在v1.14.1版本中,推荐使用sytemd作为cgroupdriver,那么你可以对/etc/docker/daemon.json做以下修改,顺便还可以使用阿里云的镜像加速器

{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
   "registry-mirrors": ["https://<your acclerate address>.mirror.aliyuncs.com"]
}

安装kubeadm

为了简化Kubernetes的安装,我们选用了v1.14.1版本,所以你可以按照以下方法添加阿里云的Kubernetes镜像地址并安装kubeadm工具:

# 添加阿里云镜像
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
add-apt-repository "deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main"
apt update -yqq

# 安装kubeadm kubelet kubectl
apt install -yqq --allow-downgrades kubelet=1.14.1-00 kubeadm=1.14.1-00 kubectl=1.14.1-00
apt-mark hold kubelet kubeadm kubect

提前拉取kubernetes镜像

因为国内的网络环境,无法在kubeadm安装过程中直接拉取镜像,所以需要一些GCR镜像服务,如:http://mirror.azure.cn/help/gcr-proxy-cache.html

如何知道要下载哪些镜像:

kubeadm config images list --kubernetes-version v1.14.1

我举一个例子:

docker pull gcr.azk8s.cn/google_containers/kube-proxy:v1.14.1
docker tag gcr.azk8s.cn/google_containers/kube-proxy:v1.14.1 k8s.gcr.io/kube-proxy:v1.14.1
docker rmi gcr.azk8s.cn/google_containers/kube-proxy:v1.14.1

快速部署脚本

以上步骤比较繁琐,我这边整理了一个shell脚本,可以方便执行:<https://gist.githubusercontent.com/jacexh/4624b8a10133ab78318c06515a06f700/raw/1570e7d55e96f75331811f09e2c198bb0eae3d16/k8s-helper.sh>

以上步骤需要在kubernetes集群上的每台ECS上执行

Kubernetes集群安装

为什么不集成aliyun-cloud-provider?

Kubernetes内置了AWS等公有云的cloud-provider,可以方便申请、使用公有云上的资源,但尚未集成阿里云aliyun cloud provider

我刚开始尝试手动集成进去,但是因为各种不确定的原因,集成失败,所以放弃了这种尝试。

Master Cluter

这里略过在阿里云控制台上创建SLB监听的过程,请确保执行以下步骤前端口映射已经完成,可以使用以下命令确认:

nc -v 192.168.200.1 6443
第一个master node

我们先准备一份kubeadmcfg.yaml文件,内容如下:

apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
kubernetesVersion: v1.14.1
controlPlaneEndpoint: 192.168.200.1:6443
clusterName: kubernetes
networking:
  dnsDomain: cluster.local
  podSubnet: 10.244.0.0/16
apiServer:
  extraVolumes:
  - name: localtime
    hostPath: /etc/localtime
    mountPath: /etc/localtime
  certSANs:
    - 192.168.200.1
controllerManager:
  extraVolumes:
  - name: localtime
    hostPath: /etc/localtime
    mountPath: /etc/localtime
scheduler:
  extraVolumes:
  - hostPath: /etc/localtime
    mountPath: /etc/localtime
    name: localtime

注意了,跟网上很多教程不一样,这边不需要控制etcd相关内容,kubeadm会自行创建好。

然后执行以下命令:

kubeadm init --config kubeadmcfg.yaml --experimental-upload-certs

然后意外的话,kubeadm能顺利完成安装,并输出以下内容:

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
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo 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/
  
You can now join any number of the control-plane node running the following command on each as root:

  kubeadm join 192.168.200.1:6443 --token <your token> \
    --discovery-token-ca-cert-hash sha256:<your ca cert hash> \
    --experimental-control-plane --certificate-key <your ca key>

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --experimental-upload-certs" to reload certs afterward.

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.200.1:6443 --token <your token> \
    --discovery-token-ca-cert-hash sha256:<your ca cert hash>

然后根据提示,执行以下命令:

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

然后输入kubectl get nodes看看?

其余master node

使用v1.14.1以及--experimental-upload-certs的好处就是加入master集群非常的方便。

这里回头看下第一个节点的输入,里面偶遇两段kubectl join指令,其中第一段就是加入集群,也就是说,你只要在剩余的master节点上执行以下命令,就可以加入master集群了。

kubeadm join 192.168.200.1:6443 --token <your token> \
    --discovery-token-ca-cert-hash sha256:<your ca cert hash> \
    --experimental-control-plane --certificate-key <your ca key>

分别在master-002以及master-003上执行,然后输入kubectl get nodes看下?

Worker加入

在剩余的worker节点上,如worker-001,执行另外一段join 指令即可:

kubeadm join 192.168.200.1:6443 --token <your token> \
    --discovery-token-ca-cert-hash sha256:<your ca cert hash>

Kubernetes组件安装

Flannel网络组件

为了方便,我这边使用的flanner作为网络组件,在master任意一台ECS上执行:

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml

可以通过kubectl get pods —all-namespaces查看运行情况,如果安装成功的话,之前处于Pending装的core-dns等pod会转成Running状态

Nginx Ingress

首先安装参考官方文档安装nginx-controller

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml

# 安装ingress-nginx service
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/provider/baremetal/service-nodeport.yaml

因为未使用aliyun-cloud-provideringress-nginx无法使用Type: LoadBalancer,只能选用NodePort的方式,另外我考虑到在公网SLB上做HTTPS转发,因此ingress-nginx不打算暴露443端口,所以修改ingress-nginx如下:

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

HAProxy集群上尽可能把所有节点添加上去/etc/haproxy/haproxy

listen  kubernetes-ingress-http
        bind    192.168.110.1:80
        mode    tcp
        retries 3
        option  tcp-check
        balance roundrobin
        timeout client 900s
        timeout server 900s
        server  master-001 192.168.100.1:32207 check inter 2s rise 3 fall 2 downinter 5s
        server  master-002 192.168.100.2:32207 check inter 2s rise 3 fall 2 downinter 5s
        server  master-003 192.168.100.2:32207 check inter 2s rise 3 fall 2 downinter 5s
        server 	worker-001 192.168.100.4:32207 check inter 2s rise 3 fall 2 downinter 5s
  • 在内网SLB上打开TCP 80端口监听,指向到haproxy集群的80端口
  • 在公网SLB上打开TCP 80端口监听,指向到haproxy集群的80端口
  • 在公网SLB打开HTTPS监听,选用合适的证书,指向到haproxy集群的80端口

至此,nginx安装完成,未来入站流量均会走nginx的80端口

Kubernetes Dashboard

接下来是安装Kubernetes Dashboard可视化组件,不幸的是,官方还不支持v1.14版本,所以我这边尝试使用开发版本,请参考:https://github.com/kubernetes/dashboard/wiki/Installation

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard-head.yaml

然后我参考Creating sample user创建一个可登陆的token,接下来开始着手准备怎么从公网访问。

创建一个ingress配置文件:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kubernetes-dashboard-ingress
  namespace: kube-system
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  rules:
    - host: dashboard.internal.example.com
      http:
        paths:
        - backend:
            serviceName: kubernetes-dashboard-head
            servicePort: 443
    - host: dashboard.public.shouqianba.com
      http:
        paths:
        - backend:
            serviceName: kubernetes-dashboard-head
            servicePort: 443

然后执行kubectl apply -f <file>应用ingress

在这份配置中,监听两个域名:

  • dashboar.internal.example.com: 内网访问地址,你可以直接用http://dashboar.internal.example.com
  • dashboar.public.example.com外网访问地址,需要通过https://dashboar.public.shouqianba.com访问,SSL证书的管理就交给了SLB来

nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"这是nginx-controll注解,这个表明跟后端kubernetes-dashboard-head通讯时使用HTTPS,更多注解用法请参考:<https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/

至此,整个Kubernetes集群搭建完成了,整个过程挺漫长,耗时耗力。