CentOS7.5安装kubernetes 1.11.x版本多主高可用

kubernetes1.11.1部署

10.0.40.214 master1 (etcd)
10.0.40.215 master2 (etcd)
10.0.40.216 master3 (etcd)
10.0.40.217 node1
10.0.40.218 node2

192.168.63.160 k8s-m1
192.168.63.161 k8s-m2
192.168.63.162 k8s-m3
192.168.63.163 k8s-g1

前言

系统版本均为Centos.7.4.x86_64,系统建议使用最新版本. ubuntu 16更佳。
k8s-m1为主要操作机器,建议在k8s-m1上设置免秘密登录其他机器。
3台master会有3个api server,端口6443,这里使用负载均衡配置lb,负载均衡vip为192.168.63.110,端口6443.
建议提前配置好负载均衡。配置LB的过程这里不在赘述。

# 在k8s-m1上操作
$ ssh-keygen -t rsa
$ for NODE in k8s-m2 k8s-m3 k8s-g1; do
    echo "--- $NODE ---"
    ssh-copy-id -i ~/.ssh/id_rsa.pub ${NODE}
  done

本文参考的文章

https://jicki.me/kubernetes/2018/08/10/kubernetes-1.11.2.html
https://www.kubernetes.org.cn/4291.html
https://www.kubernetes.org.cn/4256.html
https://www.cnblogs.com/zhenyuyaodidiao/p/6237019.html

系统基础环境设置

关闭selinux和firewalld

$ systemctl stop firewalld && systemctl disable firewalld
$ sed -i 's/SELINUX=permissive/SELINUX=disabled/' /etc/sysconfig/selinux
$ setenforce 0

安装ipvs

在k8s1.11中,proxy新增加了ipvs模式,相比于iptable模式,ipvs会直接将规则写入内核。而且比iptable更加好管理,毕竟大量的iptables规则开起来还是很头疼的。

yum -y install ipset ipvsadm conntrack-tools.x86_64

安装docker

建议安装docker 17.03版本或者更低版本,官方不建议安装更高版本.

$ yum -y install \
https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/docker-ce-17.03.2.ce-1.el7.centos.x86_64.rpm \
https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm
# 启动docker
$ systemctl enable docker && systemctl restart docker

配置系统参数

# 关闭Swap,在/etc/fstab里去掉swap挂载
$ swapoff -a && sysctl -w vm.swappiness=0
# 开启forward
$ iptables -P FORWARD ACCEPT # 这条千万要记得执行
# 配置转发, 以下内容写入/etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness=0
# 写入后执行如下命令
$ sysctl -p /etc/sysctl.d/k8s.conf
# 配置ipvs内核模块, 重启需要重新执行,建议写入启动脚本
$ modprobe ip_vs
$ modprobe ip_vs_rr
$ modprobe ip_vs_wrr
$ modprobe ip_vs_sh
$ modprobe nf_conntrack_ipv4

所有节点下载kubectl和kubelet二进制文件

# 下载kubelet
$ export KUBE_URL=https://storage.googleapis.com/kubernetes-release/release/v1.11.1/bin/linux/amd64
$ wget ${KUBE_URL}/kubelet -O /usr/local/bin/kubelet
# 下载kubectl 
# node节点可以不安装kebectl
$ wget ${KUBE_URL}/kubectl -O /usr/local/bin/kubectl
$ chmod +x /usr/local/bin/kube*

所有节点下载Kubernetes CNI 网络二进制文件

$ mkdir -p /opt/cni/bin && cd /opt/cni/bin
$ wget https://github.com/containernetworking/plugins/releases/download/v0.7.1/cni-plugins-amd64-v0.7.1.tgz
$ tar xf cni-plugins-amd64-v0.7.1.tgz
$ rm -f cni-plugins-amd64-v0.7.1.tgz

k8s-m1节点安装cfssl工具,用来建立 CA ,并产生TLS 认证

为了集群内部通信安全, 建议采用证书加密通信

$ export CFSSL_URL=https://pkg.cfssl.org/R1.2
$ wget ${CFSSL_URL}/cfssl_linux-amd64 -O /usr/local/bin/cfssl
$ wget ${CFSSL_URL}/cfssljson_linux-amd64 -O /usr/local/bin/cfssljson
$ chmod +x /usr/local/bin/cfssl*

在所有master节点安装etcd

$ yum install -y etcd
$ systemctl enable etcd

建立 CA 与产生 TLS 凭证

本节将会透过 CFSSL 工具来产生不同元件的凭证,如 Etcd、Kubernetes API Server 等等,其中各元件都会有一个根数位凭证认证机构(Root Certificate Authority)被用在元件之间的认证。

要注意 CA JSON 档中的CN(Common Name)O(Organization)等内容是会影响 Kubernetes 元件认证的。

k8s-m1上配置证书

接下来将会配置证书,是k8s集群搭建中最为繁琐的一步.务必认真操作, 配置错误集群将会无法通信.
有一部分文件需要提前准备好,可以使用我配置好的,参考于网络上的多份教程.
下载地址: 点我 , 解压后进入k8s_file/pki目录

cd ~/k8s-manual-files/pki

Etcd

k8s-m1建立/etc/etcd/ssl文件夹,并产生 Etcd CA:

$ export DIR=/etc/etcd/ssl
$ mkdir -p ${DIR}
$ cfssl gencert -initca etcd-ca-csr.json | cfssljson -bare ${DIR}/etcd-ca

接着生成Etcd凭证

$ cfssl gencert \
  -ca=${DIR}/etcd-ca.pem \
  -ca-key=${DIR}/etcd-ca-key.pem \
  -config=ca-config.json \
  -hostname=127.0.0.1,192.168.63.160,192.168.63.161,192.168.63.162 \
  -profile=kubernetes \
  etcd-csr.json | cfssljson -bare ${DIR}/etcd

-hostname需修改成所有 masters 节点。

删除不必要的文件,并检查/etc/etcd/ssl目录是否成功建立以下文件:

$ rm -rf ${DIR}/*.csr
$ chmod 777 /etc/etcd/ssl/*.pem
$ ls /etc/etcd/ssl
etcd-ca-key.pem  etcd-ca.pem  etcd-key.pem  etcd.pem

复制文件至其他 Etcd 节点,这边为所有master节点:

$ for NODE in k8s-m2 k8s-m3; do
    echo "--- $NODE ---"
    ssh ${NODE} " mkdir -p /etc/etcd/ssl"
    for FILE in etcd-ca-key.pem  etcd-ca.pem  etcd-key.pem  etcd.pem; do
      scp /etc/etcd/ssl/${FILE} ${NODE}:/etc/etcd/ssl/${FILE}
    done
    ssh ${NODE} " chmod 777 /etc/etcd/ssl/*.pem"
  done

在所有master节点修改Etcd配置文件/etc/etcd/etcd.conf
(注意其中的ip、hostname,需要跟着节点进行改变)
如:修改k8s-m1上的etcd.conf那么etcd_ip=192.168.63.160,如修改k8s-m2上的etcd.conf那么etcd_ip=192.168.63.161。

$ export etcd_ip=192.168.63.160
$ export etcd_hostname=k8s-m1
$ cat >/etc/etcd/etcd.conf<<EOF
#[Member]
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_WAL_DIR="/var/lib/etcd/wal"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001"
ETCD_NAME="$etcd_hostname"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://$etcd_ip:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://$etcd_ip:2379,http://$etcd_ip:4001"
ETCD_INITIAL_CLUSTER="k8s-m1=http://192.168.63.160:2380,k8s-m2=http://192.168.63.161:2380,k8s-m3=http://192.168.63.162:2380"
ETCD_CERT_FILE="/etc/etcd/ssl/etcd.pem"
ETCD_KEY_FILE="/etc/etcd/ssl/etcd-key.pem"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_TRUSTED_CA_FILE="/etc/etcd/ssl/etcd-ca.pem"
ETCD_AUTO_TLS="true"
ETCD_PEER_CERT_FILE="/etc/etcd/ssl/etcd.pem"
ETCD_PEER_KEY_FILE="/etc/etcd/ssl/etcd-key.pem"
ETCD_PEER_CLIENT_CERT_AUTH="true"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/ssl/etcd-ca.pem"
ETCD_PEER_AUTO_TLS="true"
EOF

在所有master节点启动etcd集群

# 3台节点都要启动, 第一台启动会卡住,不用在意,它需要至少两台才能正常工作,就行启动别的节点
$ systemctl restart etcd

测试etcd集群是否正常工作.

$ etcdctl member list
1f10dc76703272ea: name=k8s-m3 peerURLs=http://192.168.63.156:2380 clientURLs=http://192.168.63.156:2379,http://192.168.63.156:4001 isLeader=false
698d03219349202a: name=k8s-m1 peerURLs=http://192.168.63.158:2380 clientURLs=http://192.168.63.158:2379,http://192.168.63.158:4001 isLeader=true
cb03bf45a88bed80: name=k8s-m2 peerURLs=http://192.168.63.155:2380 clientURLs=http://192.168.63.155:2379,http://192.168.63.155:4001 isLeader=false

Kubernetes组件

k8s-m1生成CA证书

$ export K8S_DIR=/etc/kubernetes
$ export K8S_PKI_DIR=${K8S_DIR}/pki
$ export KUBE_APISERVER=https://192.168.63.110:6443
$ mkdir -p ${K8S_PKI_DIR}
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ${K8S_PKI_DIR}/ca
$ ls ${K8S_PKI_DIR}/ca*.pem
/etc/kubernetes/pki/ca-key.pem  /etc/kubernetes/pki/ca.pem

KUBE_APISERVER这里设定的是LB的VIP地址.

k8s-m1生成 API Server 凭证

$ cfssl gencert \
  -ca=${K8S_PKI_DIR}/ca.pem \
  -ca-key=${K8S_PKI_DIR}/ca-key.pem \
  -config=ca-config.json \
  -hostname=10.96.0.1,192.169.63.110,127.0.0.1,kubernetes.default \
  -profile=kubernetes \
  apiserver-csr.json | cfssljson -bare ${K8S_PKI_DIR}/apiserver

$ ls ${K8S_PKI_DIR}/apiserver*.pem
/etc/kubernetes/pki/apiserver-key.pem  /etc/kubernetes/pki/apiserver.pem
  • 10.96.0.1 是 Kubernetes 的 Cluster IP
  • 192.169.63.110 是 Lb 的 VIP
  • kubernetes.default 为 Kubernetes 在 default namespace 自动建立的 API service domain name。

k8s-m1生成 Front Proxy Client 凭证

此凭证将被用于 Authenticating Proxy 的功能上,而该功能主要是提供 API Aggregation 的认证。

# 首先生成CA证书
$ cfssl gencert -initca front-proxy-ca-csr.json | cfssljson -bare ${K8S_PKI_DIR}/front-proxy-ca
$ ls ${K8S_PKI_DIR}/front-proxy-ca*.pem
/etc/kubernetes/pki/front-proxy-ca-key.pem  /etc/kubernetes/pki/front-proxy-ca.pem
# 接着生成认证
$ cfssl gencert \
  -ca=${K8S_PKI_DIR}/front-proxy-ca.pem \
  -ca-key=${K8S_PKI_DIR}/front-proxy-ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  front-proxy-client-csr.json | cfssljson -bare ${K8S_PKI_DIR}/front-proxy-client

$ ls ${K8S_PKI_DIR}/front-proxy-client*.pem
/etc/kubernetes/pki/front-proxy-client-key.pem  /etc/kubernetes/pki/front-proxy-client.pem

k8s-m1生成 Controller Manager 凭证

凭证会建立system:kube-controller-manager的使用者(凭证 CN),并被绑定在 RBAC Cluster Role 中的system:kube-controller-manager来让 Controller Manager 元件能够存取需要的 API object。这边透过以下指令产生 Controller Manager 凭证:

$ cfssl gencert \
  -ca=${K8S_PKI_DIR}/ca.pem \
  -ca-key=${K8S_PKI_DIR}/ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  manager-csr.json | cfssljson -bare ${K8S_PKI_DIR}/controller-manager

$ ls ${K8S_PKI_DIR}/controller-manager*.pem
/etc/kubernetes/pki/controller-manager-key.pem  /etc/kubernetes/pki/controller-manager.pem

接着使用kubectl命令生成kubeconfig文件,并且为Controller Manager绑定system:kube-controller-manager的RBAC 角色, 让它能够有权限访问需要的api.

$ kubectl config set-cluster kubernetes \
    --certificate-authority=${K8S_PKI_DIR}/ca.pem \
    --embed-certs=true \
    --server=${KUBE_APISERVER} \
    --kubeconfig=${K8S_DIR}/controller-manager.conf

$ kubectl config set-credentials system:kube-controller-manager \
    --client-certificate=${K8S_PKI_DIR}/controller-manager.pem \
    --client-key=${K8S_PKI_DIR}/controller-manager-key.pem \
    --embed-certs=true \
    --kubeconfig=${K8S_DIR}/controller-manager.conf

$ kubectl config set-context system:kube-controller-manager@kubernetes \
    --cluster=kubernetes \
    --user=system:kube-controller-manager \
    --kubeconfig=${K8S_DIR}/controller-manager.conf

$ kubectl config use-context system:kube-controller-manager@kubernetes \
    --kubeconfig=${K8S_DIR}/controller-manager.conf

k8s-m1生成 Scheduler 凭证

凭证会建立system:kube-scheduler的使用者(凭证 CN),并被绑定在 RBAC Cluster Role 中的system:kube-scheduler来让 Scheduler 元件能够存取需要的 API object。这边透过以下指令产生 Scheduler 凭证:

$ cfssl gencert \
  -ca=${K8S_PKI_DIR}/ca.pem \
  -ca-key=${K8S_PKI_DIR}/ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  scheduler-csr.json | cfssljson -bare ${K8S_PKI_DIR}/scheduler

$ ls ${K8S_PKI_DIR}/scheduler*.pem
/etc/kubernetes/pki/scheduler-key.pem  /etc/kubernetes/pki/scheduler.pem

接着使用kubectl命令生成kubeconfig文件,并且为 Scheduler绑定system:kube-scheduler的RBAC 角色, 让它能够有权限访问需要的api.

$ kubectl config set-cluster kubernetes \
    --certificate-authority=${K8S_PKI_DIR}/ca.pem \
    --embed-certs=true \
    --server=${KUBE_APISERVER} \
    --kubeconfig=${K8S_DIR}/scheduler.conf

$ kubectl config set-credentials system:kube-scheduler \
    --client-certificate=${K8S_PKI_DIR}/scheduler.pem \
    --client-key=${K8S_PKI_DIR}/scheduler-key.pem \
    --embed-certs=true \
    --kubeconfig=${K8S_DIR}/scheduler.conf

$ kubectl config set-context system:kube-scheduler@kubernetes \
    --cluster=kubernetes \
    --user=system:kube-scheduler \
    --kubeconfig=${K8S_DIR}/scheduler.conf

$ kubectl config use-context system:kube-scheduler@kubernetes \
    --kubeconfig=${K8S_DIR}/scheduler.conf

k8s-m1生成 Admin 凭证

Admin 被用来绑定 RBAC Cluster Role 中 cluster-admin,当想要操作所有 Kubernetes 丛集功能时,就必须利用这边产生的 kubeconfig 档案。这边透过以下指令产生 Kubernetes Admin 凭证:

$ cfssl gencert \
  -ca=${K8S_PKI_DIR}/ca.pem \
  -ca-key=${K8S_PKI_DIR}/ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  admin-csr.json | cfssljson -bare ${K8S_PKI_DIR}/admin

$ ls ${K8S_PKI_DIR}/admin*.pem
/etc/kubernetes/pki/admin-key.pem  /etc/kubernetes/pki/admin.pem

接着使用kubectl命令生成kubeconfig文件,并且为Admin绑定kubernetes-admin的RBAC 角色, 因为之后我们使用kubectl管理集群时,就要使用这个配置文件.

$ kubectl config set-cluster kubernetes \
    --certificate-authority=${K8S_PKI_DIR}/ca.pem \
    --embed-certs=true \
    --server=${KUBE_APISERVER} \
    --kubeconfig=${K8S_DIR}/admin.conf

$ kubectl config set-credentials kubernetes-admin \
    --client-certificate=${K8S_PKI_DIR}/admin.pem \
    --client-key=${K8S_PKI_DIR}/admin-key.pem \
    --embed-certs=true \
    --kubeconfig=${K8S_DIR}/admin.conf

$ kubectl config set-context kubernetes-admin@kubernetes \
    --cluster=kubernetes \
    --user=kubernetes-admin \
    --kubeconfig=${K8S_DIR}/admin.conf

$ kubectl config use-context kubernetes-admin@kubernetes \
    --kubeconfig=${K8S_DIR}/admin.conf

生成Kubelet证书

因为node节点证书需要包含使用者名字.所以我们使用主机名来命名使用者.
首先在master1生成证书.

for NODE in k8s-m1 k8s-m2 k8s-m3; do
    echo "--- $NODE ---"
    cp kubelet-csr.json kubelet-$NODE-csr.json;
    sed -i "s/\$NODE/$NODE/g" kubelet-$NODE-csr.json;
    cfssl gencert \
      -ca=${K8S_PKI_DIR}/ca.pem \
      -ca-key=${K8S_PKI_DIR}/ca-key.pem \
      -config=ca-config.json \
      -hostname=$NODE \
      -profile=kubernetes \
      kubelet-$NODE-csr.json | cfssljson -bare ${K8S_PKI_DIR}/kubelet-$NODE;
    rm kubelet-$NODE-csr.json
  done

$ ls ${K8S_PKI_DIR}/kubelet*.pem
/etc/kubernetes/pki/kubelet-k8s-m1-key.pem  /etc/kubernetes/pki/kubelet-k8s-m2.pem
/etc/kubernetes/pki/kubelet-k8s-m1.pem      /etc/kubernetes/pki/kubelet-k8s-m3-key.pem
/etc/kubernetes/pki/kubelet-k8s-m2-key.pem  /etc/kubernetes/pki/kubelet-k8s-m3.pem

产生完成后,将 kubelet 凭证复制到所有master节点上:

$ for NODE in k8s-m1 k8s-m2 k8s-m3; do
    echo "--- $NODE ---"
    ssh ${NODE} "mkdir -p ${K8S_PKI_DIR}"
    scp ${K8S_PKI_DIR}/ca.pem ${NODE}:${K8S_PKI_DIR}/ca.pem
    scp ${K8S_PKI_DIR}/kubelet-$NODE-key.pem ${NODE}:${K8S_PKI_DIR}/kubelet-key.pem
    scp ${K8S_PKI_DIR}/kubelet-$NODE.pem ${NODE}:${K8S_PKI_DIR}/kubelet.pem
    rm ${K8S_PKI_DIR}/kubelet-$NODE-key.pem ${K8S_PKI_DIR}/kubelet-$NODE.pem
  done

接着利用 kubectl 来生成 kubelet 的 kubeconfig 文件,这边通过脚本来生成所有master节点的文件:

$ for NODE in k8s-m1 k8s-m2 k8s-m3; do
    echo "--- $NODE ---"
    ssh ${NODE} "cd ${K8S_PKI_DIR} && \
      kubectl config set-cluster kubernetes \
        --certificate-authority=${K8S_PKI_DIR}/ca.pem \
        --embed-certs=true \
        --server=${KUBE_APISERVER} \
        --kubeconfig=${K8S_DIR}/kubelet.conf && \
      kubectl config set-credentials system:node:${NODE} \
        --client-certificate=${K8S_PKI_DIR}/kubelet.pem \
        --client-key=${K8S_PKI_DIR}/kubelet-key.pem \
        --embed-certs=true \
        --kubeconfig=${K8S_DIR}/kubelet.conf && \
      kubectl config set-context system:node:${NODE}@kubernetes \
        --cluster=kubernetes \
        --user=system:node:${NODE} \
        --kubeconfig=${K8S_DIR}/kubelet.conf && \
      kubectl config use-context system:node:${NODE}@kubernetes \
        --kubeconfig=${K8S_DIR}/kubelet.conf"
  done

生成service Account 密钥

kubernetes Controller Manager需要使用密钥对来产生和认证Service Account token,这里是使用openssl来建立一组公私钥.

$ openssl genrsa -out ${K8S_PKI_DIR}/sa.key 2048
$ openssl rsa -in ${K8S_PKI_DIR}/sa.key -pubout -out ${K8S_PKI_DIR}/sa.pub
$ ls ${K8S_PKI_DIR}/sa.*
/etc/kubernetes/pki/sa.key  /etc/kubernetes/pki/sa.pub

完成之后删除一些不需要的文件

$ rm -rf ${K8S_PKI_DIR}/*.csr \
    ${K8S_PKI_DIR}/scheduler*.pem \
    ${K8S_PKI_DIR}/controller-manager*.pem \
    ${K8S_PKI_DIR}/admin*.pem \
    ${K8S_PKI_DIR}/kubelet*.pem

复制凭证到其他master节点

$ scp ${K8S_PKI_DIR}/*  master2:/${K8S_PKI_DIR}/
$ scp ${K8S_PKI_DIR}/*  master3:/${K8S_PKI_DIR}/

复制kubeconfig到其他master节点

$ scp ${K8S_DIR}/{admin.conf,controller-manager.conf,scheduler.conf} master2:/${K8S_DIR}/
$ scp ${K8S_DIR}/{admin.conf,controller-manager.conf,scheduler.conf} master3:/${K8S_DIR}/

配置kuberbetes matser

接下来将会部署master节点的组件,首先看一下各个组件的功能

  • kubelet:负责管理容器的生命周期,定期从 API Server 取得节点上的预期状态(如网络、储存等等配置)资源,并呼叫对应的容器接口(CRI、CNI 等)来达成这个状态。任何 Kubernetes 节点都会拥有该组件。
  • kube-apiserver:以 REST APIs 提供 Kubernetes 资源的 CRUD,如授权、认证、访问控制与 API 注册等机制。
  • kube-controller-manager:通过核心控制循环(Core Control Loop)监听 Kubernetes API 的资源来维护集群的状态,这些资源会被不同的控制器所管理,如 Replication Controller、Namespace Controller 等等。而这些控制器会处理着自动扩展、滚动更新等等功能。
  • kube-scheduler:负责将一个(或多个)容器依据排程策略分配到对应节点上让容器引擎(如 Docker)执行。而排程受到 QoS 要求、软硬件约束、亲和性(Affinity)等等规范影响。
  • Etcd:用来保存集群所有状态的 Key/Value 储存系统,所有 Kubernetes 组件会通过 API Server 来跟 Etcd 进行沟通来保存或取得资源状态。  
    以上组件除了kubelet和etcd, 其他组件将通过k8s static pod方式部署

生成各个组件的yml和配置文件

使用之前下载的文件来生成yml和一些config文件.

$ export NODES="master1 master2 master3" # 所有matser节点
$ export APISERVER_VIP="10.0.40.219"
$ ./hack/create_config.sh
master1 config created...
master2 config created...
master3 config created...

文件生成完毕后检查一下各个master节点/etc/kubernetes/目录下的文件是否都生成成功.
接下来复制kubelet systemd文件到各个matser节点.

$ export NODE=master1 # 这里是修改变量的位置
$ ssh ${NODE} "mkdir -p /var/lib/kubelet /var/log/kubernetes /var/lib/etcd /etc/systemd/system/kubelet.service.d"
$ scp master/var/lib/kubelet/config.yml ${NODE}:/var/lib/kubelet/config.yml
$ scp master/systemd/kubelet.service ${NODE}:/lib/systemd/system/kubelet.service
$ scp master/systemd/10-kubelet.conf ${NODE}:/etc/systemd/system/kubelet.service.d/10-kubelet.conf

以上shell需要根据matser节点数量执行多次.

启动kubelet服务

接下来启动master节点的kubelet服务

$ systemctl enable kubelet.service && systemctl start kubelet.service
$ ssh master1 "systemctl enable kubelet.service && systemctl start kubelet.service"
$ ssh master2 "systemctl enable kubelet.service && systemctl start kubelet.service"

通过systemctl status kubelet查看服务启动情况.确定启动成功后就可以等待下载和启动组件了.

journalctl -xefu kubelet
tail -f /var/log/messages

因为某些不可描述的原因, 镜像从k8s站点下载可能会失败.所以需要给docker配置代理服务器.

$ vim /lib/systemd/system/docker.service
# 然后在ExecStart这一行上面加入如下内容
Environment="HTTP_PROXY=http://x.x.x.x:x/" "HTTPS_PROXY=http://x.x.x.x:x/"
# 然后重启docker
systemctl restart docker

等待启动成功后,会有10251,10252,10250,6443端口启动,那么就启动成功了.

查看集群情况

$ cp /etc/kubernetes/admin.conf ~/.kube/config
$ kubectl get nodes
NAME      STATUS       ROLES     AGE       VERSION
master1   NotReady     master    7d        v1.11.1
master2   NotReady     master    7d        v1.11.1
master3   NotReady     master    7d        v1.11.1

这在阶段状态处于NotReady的英文正常,往下进行就会了解为何。

配置TLS Bootstrapping

由于本次部署采用 TLS 认证来确保 Kubernetes 集群的安全性,因此每个节点的 kubelet 都需要通过 API Server 的 CA 进行身份验证后,才能与 API Server 进行沟通,而这过程过去都是采用手动方式针对每台节点(master与node)单独签署凭证,再设定给 kubelet 使用,然而这种方式是一件繁琐的事情,因为当节点扩展到一定程度时,将会非常费时,甚至延伸初管理不易问题.
而由于上述问题,Kubernetes 实现了 TLS Bootstrapping 来解决此问题,这种做法是先让 kubelet 以一个低权限使用者(一个能存取 CSR API 的 Token)存取 API Server,接着对 API Server 提出申请凭证签署请求,并在受理后由 API Server 动态签署 kubelet 凭证提供给对应的node节点使用。具体作法请参考 TLS BootstrappingAuthenticating with Bootstrap Tokens

首先在master1生成kubeconfig

$ export TOKEN_ID=$(openssl rand 3 -hex)
$ export TOKEN_SECRET=$(openssl rand 8 -hex)
$ export BOOTSTRAP_TOKEN=${TOKEN_ID}.${TOKEN_SECRET}
$ export KUBE_APISERVER="https://10.0.40.219:6443"  # 此处改为你们自己的VIP
$ kubectl config set-cluster kubernetes \
    --certificate-authority=/etc/kubernetes/pki/ca.pem \
    --embed-certs=true \
    --server=${KUBE_APISERVER} \
    --kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf

$ kubectl config set-credentials tls-bootstrap-token-user \
    --token=${BOOTSTRAP_TOKEN} \
    --kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf

$ kubectl config set-context tls-bootstrap-token-user@kubernetes \
    --cluster=kubernetes \
    --user=tls-bootstrap-token-user \
    --kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf

$ kubectl config use-context tls-bootstrap-token-user@kubernetes \
    --kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf

接下来在matser1生成TLS Bootstrao Secret来提供自动签证.

$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-${TOKEN_ID}
  namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
  token-id: "${TOKEN_ID}"
  token-secret: "${TOKEN_SECRET}"
  usage-bootstrap-authentication: "true"
  usage-bootstrap-signing: "true"
  auth-extra-groups: system:bootstrappers:default-node-token
EOF

接下来建立RBAC来自动受理CSR请求

$ kubectl apply -f master/rbac/kubelet-bootstrap-rbac.yml
clusterrolebinding.rbac.authorization.k8s.io/kubelet-bootstrap created
clusterrolebinding.rbac.authorization.k8s.io/node-autoapprove-bootstrap created
clusterrolebinding.rbac.authorization.k8s.io/node-autoapprove-certificate-rotation created

给apiserver加权限

为apiserver建立一个 RBAC Role 来获取访问权限

$ kubectl apply -f master/rbac/apiserver-to-kubelet-rbac.yml
clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created
clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created

配置kuberbetes node

接下来会设置kubernetes node节点,这过程只需要将 PKI、Bootstrap conf 等文件复制到机器上,再用 kubelet 启动即可。
在开始部署前,在master将需要用到的文件复制到node节点上

ssh node1 "mkdir -p /etc/kubernetes/pki/"
scp /etc/kubernetes/pki/{ca.pem,ca-key.pem} node1:/etc/kubernetes/pki
scp /etc/kubernetes/bootstrap-kubelet.conf node1:/etc/kubernetes/

确认文件都复制完成后,就可以配置systemctd来启动kubelet了

$ ssh node1 "mkdir -p /var/lib/kubelet /var/log/kubernetes /var/lib/etcd /etc/systemd/system/kubelet.service.d /etc/kubernetes/manifests"
$ scp node/var/lib/kubelet/config.yml node1:/var/lib/kubelet/config.yml
$ scp node/systemd/kubelet.service node1:/lib/systemd/system/kubelet.service
$ scp node/systemd/10-kubelet.conf node1:/etc/systemd/system/kubelet.service.d/10-kubelet.conf
# 复制完成后,启动node上的kubelet服务
$ systemctl enable kubelet.service && systemctl start kubelet.service

然后查看csr会发现自动处理了请求

$ kubectl get csr
$ kubectl get node
NAME      STATUS    ROLES     AGE       VERSION
master1   NotReady     master    8d        v1.11.1
master2   NotReady     master    8d        v1.11.1
master3   NotReady     master    8d        v1.11.1
node1     NotReady     <none>    7d        v1.11.1

部署kubernetes扩展服务

当完成master与node节点的部署,并组合成一个可运作集群后,就可以开始通过 kubectl 部署 Addons,Kubernetes 官方提供了多种 Addons 来加强 Kubernetes 的各种功能,如集群 DNS 解析的kube-dns(or CoreDNS)、外部存取服务的kube-proxy与 Web-based 管理接口的dashboard等等。而其中有些 Addons 是被 Kubernetes 认定为必要的,因此本节将说明如何部署这些 Addons。

Kubernetes Proxy

kube-proxy 是实现 Kubernetes Service 资源功能的关键组件,这个组件会通过 DaemonSet 在每台节点上执行,然后监听 API Server 的 Service 与 Endpoint 资源对象的事件,并依据资源预期状态通过 iptables 或 ipvs 来实现网络转发,而本次安装采用 ipvs。

# 使用之前下载的文件安装
$ export KUBE_APISERVER="https://10.0.40.219:6443"
$ sed -i "s/\${KUBE_APISERVER}/${KUBE_APISERVER}/g"  addons/kube-proxy/*
$ kubectl -f extend/kube-proxy/

$ kubectl -n kube-system get po -l k8s-app=kube-proxy
NAME               READY     STATUS    RESTARTS   AGE
kube-proxy-2b9xm   1/1       Running   2          7d
kube-proxy-9tcwr   1/1       Running   1          7d
kube-proxy-fpqrd   1/1       Running   1          7d
kube-proxy-nxndk   1/1       Running   1          7d
kube-proxy-spv9g   1/1       Running   2          7d

CoreDNS

本节将通过 CoreDNS 取代 Kube DNS 作为集群服务发现组件,由于 Kubernetes 需要让 Pod 与 Pod 之间能够互相沟通,然而要能够沟通需要知道彼此的 IP 才行,而这种做法通常是通过 Kubernetes API 来取得达到,但是 Pod IP 会因为生命周期变化而改变,因此这种做法无法弹性使用,且还会增加 API Server 负担,基于此问题 Kubernetes 提供了 DNS 服务来作为查询,让 Pod 能够以 Service 名称作为域名来查询 IP 地址,因此用户就再不需要关切实际 Pod IP,而 DNS 也会根据 Pod 变化更新资源纪录(Record resources)。

CoreDNS 是由 CNCF 维护的开源 DNS 项目,该项目前身是 SkyDNS,其采用了 Caddy 的一部分来开发服务器框架,使其能够建构一套快速灵活的 DNS,而 CoreDNS 每个功能都可以被实作成一个插件的中间件,如 Log、Cache、Kubernetes 等功能,甚至能够将源纪录储存至 Redis、Etcd 中.

在master1通过kubeclt执行下面指令来建立,并检查是否部署成功:

$ kubectl create -f extend/coredns/

$ kubectl -n kube-system get po -l k8s-app=kube-dns
NAME                       READY     STATUS    RESTARTS   AGE
coredns-859d584858-8kj5d   1/1       Pending   14         1m
coredns-859d584858-vc5n4   1/1       Pending   14         1m

这边会发现 Pod 处于Pending状态,这是由于 Kubernetes 的集群网络没有建立,因此所有节点会处于NotReady状态,而这也导致 Kubernetes Scheduler 无法替 Pod 找到适合节点而处于Pending,为了解决这个问题,接下来将会建立 Kubernetes 集群网络。

Kubernetes 集群网络

Kubernetes 在默认情况下与 Docker 的网络有所不同。在 Kubernetes 中有四个问题是需要被解决的,分别为:

  • 高耦合的容器到容器沟通:通过 Pods 与 Localhost 的沟通来解决。

  • Pod 到 Pod 的沟通:通过实现网络模型来解决。

  • Pod 到 Service 沟通:由 Services object 结合 kube-proxy 解决。

  • 外部到 Service 沟通:一样由 Services object 结合 kube-proxy 解决。
    而 Kubernetes 对于任何网络的实现都需要满足以下基本要求(除非是有意调整的网络分段策略):

  • 所有容器能够在没有 NAT 的情况下与其他容器沟通。

  • 所有节点能够在没有 NAT 情况下与所有容器沟通(反之亦然)。

  • 容器看到的 IP 与其他人看到的 IP 是一样的。

庆幸的是 Kubernetes 已经有非常多种的网络模型以网络插件(Network Plugins)方式被实现,因此可以选用满足自己需求的网络功能来使用。另外 Kubernetes 中的网络插件有以下两种形式:

  • CNI plugins:以 appc/CNI 标准规范所实现的网络,详细可以阅读 CNI Specification。
  • Kubenet plugin:使用 CNI plugins 的 bridge 与 host-local 来实现基本的 cbr0。这通常被用在公有云服务上的 Kubernetes 集群网络。

如果了解如何选择可以阅读 Chris Love 的 Choosing a CNI Network Provider for Kubernetes 文章。

本次部署选择了 Calico 作为集群网络的使用。Calico 是一款纯 Layer 3 的网络,其好处是它整合了各种云原生平台(Docker、Mesos 与 OpenStack 等),且 Calico 不采用 vSwitch,而是在每个 Kubernetes 节点使用 vRouter 功能,并通过 Linux Kernel 既有的 L3 forwarding 功能,而当数据中心复杂度增加时,Calico 也可以利用 BGP route reflector 来达成。

# 首先吧网段改为集群网段
$ cd ~/k8s-files
$ sed -i 's/192.168.0.0\/16/10.244.0.0\/16/g' cni/calico/v3.1/calico.yaml
$ 直接使用官方提供的pod方式安装
$ kubectl -f cni/calico/v3.1/

$ kubectl -n kube-system get po -l k8s-app=calico-node
NAME                READY     STATUS    RESTARTS   AGE
calico-node-9fkcx   2/2       Running   2          7d
calico-node-fb9xl   2/2       Running   2          7d
calico-node-ffrhq   2/2       Running   5          7d
calico-node-fv8nx   2/2       Running   2          7d
calico-node-g4vps   2/2       Running   4          7d

部署完成后,可以检查集群状态,是否变为了Ready.

$ kubectl get node
NAME      STATUS    ROLES     AGE       VERSION
master1   Ready     master    8d        v1.11.1
master2   Ready     master    8d        v1.11.1
master3   Ready     master    8d        v1.11.1
node1     Ready     <none>    7d        v1.11.1

Dashboard

Dashboard 是 Kubernetes 官方开发的 Web-based 仪表板,目的是提升管理 Kubernetes 集群资源便利性,并以资源可视化方式,来让人更直觉的看到整个集群资源状态.

$ cd ~/k8s-files
$ kubectl apply -f extend/dashboard/

$ kubectl -n kube-system get po,svc -l k8s-app=kubernetes-dashboard
NAME                                       READY     STATUS    RESTARTS   AGE
pod/kubernetes-dashboard-6948bdb78-j5g7j   1/1       Running   1          7d

NAME                           TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
service/kubernetes-dashboard   NodePort   10.107.168.142   <none>        8443:30000/TCP   7d
# 我开启了svc的对外端口30000,你也可以选择关闭或者修改,文件为extend/dashboard/kubernetes-dashboard.yml

访问dashboard的url为:

https://{YOUR_VIP}:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/
# 或者随便一个节点ip加6443端口,如:
https://10.0.40.214:6443

访问需要输入token,我们可以通过如下方式获取密钥.

# 首先要创建一个dashboard的service account
$ kubectl -n kube-system create sa dashboard
$ kubectl create clusterrolebinding dashboard --clusterrole cluster-admin --serviceaccount=kube-system:dashboard
$ SECRET=$(kubectl -n kube-system get sa dashboard -o yaml | awk '/dashboard-token/ {print $3}')
$ kubectl -n kube-system describe secrets ${SECRET} | awk '/token:/{print $2}'
this is token!

至此,kuberbetes集群就搭建完毕了.

推荐阅读更多精彩内容