zhangguanzhang's Blog

Kubernetes v1.11.x HA全手动苦工安装教学

字数统计: 13.2k阅读时长: 65 min
2018/08/03

管理组件采用staticPod或者daemonSet形式跑的,宿主机os能跑docker应该本篇教程能大多适用
安装完成仅供学习和实验

本次安裝的版本:

  • Kubernetes v1.11.1
  • CNI v0.7.1
  • Etcd v3.3.9
  • Flannel v0.10.0 或者 Calico v3.1.3
  • Docker CE latest version(18.06)

** 不建议用docker 18.05 , docker CE 18.05有bind mount的bug **

本次部署的网络信息:

  • Cluster IP CIDR: 10.244.0.0/16
  • Service Cluster IP CIDR: 10.96.0.0/12
  • Service DNS IP: 10.96.0.10
  • DNS DN: cluster.local
  • Kubernetes API VIP: 192.168.126.110
  • Kubernetes Ingress VIP: 192.168.126.109
  • 如果单台master的话Kubernetes API VIP写master的ip即可,单台就别搞啥HA了
  • 单台master的所有复制到其他mster的操作都忽略即可

节点信息
本教学将以下列节点数与规格来进行部署Kubernetes集群,系统可采用Ubuntu 16.xCentOS 7.x

IP Hostname CPU Memory
192.168.126.111 K8S-M1 1 4G
192.168.126.112 K8S-M2 1 4G
192.168.126.113 K8S-M3 1 4G
192.168.126.114 K8S-N1 1 4G
另外VIP为192.168.126.110,由所有master节点后面的keepalived+haproxy的staticPod来选择VIP的归属保持高可用
  • 所有操作全部用root使用者进行(方便用),以SRE来说不推荐。
  • 高可用一般建议奇数台,我使用3台master来做高可用

事前准备

  • 所有机器彼此网路互通,并且k8s-m1SSH登入其他节点为passwdless。

  • 所有防火墙与SELinux 已关闭。如CentOS:

    1
    2
    3
    4
    systemctl disable --now firewalld
    systemctl disable --now NetworkManager
    setenforce 0
    sed -ri '/^[^#]*SELINUX=/s#=.+$#=disabled#' /etc/selinux/config
  • 所有机器需要设定/etc/hosts解析到所有集群主机。

    1
    2
    3
    4
    5
    6
    ...
    192.168.126.111 k8s-m1
    192.168.126.112 k8s-m2
    192.168.126.113 k8s-m3
    192.168.126.114 k8s-n1

  • 所有机器需要安装Docker CE 版本的容器引擎:

    1
    curl -fsSL "https://get.docker.com/" | bash -s -- --mirror Aliyun
  • 所有机器配置加速源:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    mkdir -p /etc/docker/
    cat>/etc/docker/daemon.json<<EOF
    {
    "registry-mirrors": ["https://fz5yth0r.mirror.aliyuncs.com"],
    "log-driver": "json-file",
    "log-opts": {
    "max-size": "100m",
    "max-file": "3"
    }
    }
    EOF
  • 所有机器安装ipvs(1.11后使用ipvs,性能甩iptables几条街)

    1
    yum install ipvsadm -y
  • 所有机器选择需要开机加载的内核模块,以下是 ipvs 模式需要加载的模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    $ :> /etc/modules-load.d/ipvs.conf
    $ module=(
    ip_vs
    ip_vs_lc
    ip_vs_wlc
    ip_vs_rr
    ip_vs_wrr
    ip_vs_lblc
    ip_vs_lblcr
    ip_vs_dh
    ip_vs_sh
    ip_vs_fo
    ip_vs_nq
    ip_vs_sed
    ip_vs_ftp
    )
    $ for kernel_module in ${module[@]};do
    /sbin/modinfo -F filename $kernel_module |& grep -qv ERROR && echo $kernel_module >> /etc/modules-load.d/ipvs.conf || :
    done
    $ systemctl enable --now systemd-modules-load.service
  • 设置docker开机启动,CentOS安装完成后docker需要手动设置docker命令补全:
1
2
$ systemctl enable --now docker
$ yum install -y epel-release bash-completion && cp /usr/share/bash-completion/completions/docker /etc/bash_completion.d/
  • 所有机器需要设定/etc/sysctl.d/k8s.conf的系统参数。
    1
    2
    3
    4
    5
    6
    7
    8
    $ cat <<EOF > /etc/sysctl.d/k8s.conf
    net.ipv4.ip_forward = 1
    net.bridge.bridge-nf-call-ip6tables = 1
    net.bridge.bridge-nf-call-iptables = 1
    fs.may_detach_mounts = 1
    EOF

    $ sysctl --system
  • Kubernetes v1.8+要求关闭系统Swap,若不关闭则需要修改kubelet设定参数,在所有机器使用以下指令关闭swap并注释掉/etc/fstab中swap的行:
    1
    2
    $ swapoff -a && sysctl -w vm.swappiness=0
    $ sed -ri '/^[^#]*swap/s@^@#@' /etc/fstab
  • 执行到此确保getenforce的值是Disabled,如果不是请重启

  • 所有机器下载Kubernetes二进制执行档:

无越墙工具的,我已把1.11.1的kubectl和kubelet上传到我的七牛云了,使用下面下载

1
2
3
4
5
$ wget http://ols7lqkih.bkt.clouddn.com/kubernetes-linux-amd64-v1.11.1.tar.tar.gz
$ 自行下载解压后放到/usr/local/bin目录下并且赋予可执行权限
$ node 请忽略下载 kubectl


最新版的kubectl使用以下命令来下载可获得最新版本,如果要最新的自行工具下载

1
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl

要下载特定版本,请将上面curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt这部分替换为特定版本。

例如下面1.11.1直链地址

1
2
3
4
5
6
7
8
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
chmod +x /usr/local/bin/kubelet

# node 请忽略下载 kubectl

wget "${KUBE_URL}/kubectl" -O /usr/local/bin/kubectl
chmod +x /usr/local/bin/kubectl
  • 所有机器下载Kubernetes CNI 二进制执行档:(centos命令报错的话建议直接下载后解压到目录里)
    1
    2
    3
    4
    mkdir -p /opt/cni/bin && cd /opt/cni/bin
    export CNI_URL="https://github.com/containernetworking/plugins/releases/download"
    wget -qO- "${CNI_URL}/v0.7.1/cni-plugins-amd64-v0.7.1.tgz" | tar -zx

  • k8s-m1需要安裝CFSSL工具,这将会用來建立 TLS Certificates。
    1
    2
    3
    4
    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 /usr/local/bin/cfssljson

    建立集群CA keys 与Certificates

    在这个部分,将需要产生多个元件的Certificates,这包含Etcd、Kubernetes 元件等,并且每个集群都会有一个根数位凭证认证机构(Root Certificate Authority)被用在认证API Server 与Kubelet 端的凭证。
    • PS这边要注意CA JSON档的CN(Common Name)O(Organization)等内容是会影响Kubernetes元件认证的。

首先在k8s-m1通过git获取部署要用到的文件,并export下VIP为自己的VIP(根据自己的ip来)

1
2
git clone https://github.com/zhangguanzhang/k8s-manual-files.git ~/k8s-manual-files
cd ~/k8s-manual-files/pki

根据自己环境声明用到的变量(主要是ip,路径最好别改)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export VIP=192.168.126.110
export INGRESS_VIP=192.168.126.109
export KUBE_APISERVER=https://${VIP}:6443
export K8S_DIR=/etc/kubernetes
export PKI_DIR=${K8S_DIR}/pki
export ETCD_SSL=/etc/etcd/ssl
export MANIFESTS_DIR=/etc/kubernetes/manifests/
# 声明集群成员信息
declare -A MasterArray otherMaster NodeArray
MasterArray=(['k8s-m1']=192.168.126.111 ['k8s-m2']=192.168.126.112 ['k8s-m3']=192.168.126.113)
otherMaster=(['k8s-m2']=192.168.126.112 ['k8s-m3']=192.168.126.113)
NodeArray=(['k8s-n1']=192.168.126.114 ['k8s-n2']=192.168.126.115 ['k8s-n3']=192.168.126.116)


Etcd CA

首先在k8s-m1建立/etc/etcd/ssl文件夹

1
mkdir -p ${ETCD_SSL}

从CSR json文件ca-config.jsonetcd-ca-csr.json生成etcd的CA keys与Certificate:

1
cfssl gencert -initca etcd-ca-csr.json | cfssljson -bare ${ETCD_SSL}/etcd-ca

生成Etcd证书:

1
2
3
4
5
6
7
cfssl gencert \
-ca=${ETCD_SSL}/etcd-ca.pem \
-ca-key=${ETCD_SSL}/etcd-ca-key.pem \
-config=ca-config.json \
-hostname=127.0.0.1,$(xargs -n1<<<${MasterArray[@]} | sort | paste -d, -s -) \
-profile=kubernetes \
etcd-csr.json | cfssljson -bare ${ETCD_SSL}/etcd
  • -hostname需修改成所有masters节点的IP

完成后删除不必要文件,确认/etc/etcd/ssl有以下文件

1
2
3
rm -rf ${ETCD_SSL}/*.csr
ls $ETCD_SSL
etcd-ca-key.pem etcd-ca.pem etcd-key.pem etcd.pem

复制相关文件至其他Etcd节点,这边etcd跑在所有master节点上,所以etcd的证书复制到其他mster节点:

1
2
3
4
5
6
7
for NODE in "${otherMaster[@]}"; do
echo "--- $NODE ---"
ssh $NODE "mkdir -p ${ETCD_SSL}"
for FILE in etcd-ca-key.pem etcd-ca.pem etcd-key.pem etcd.pem; do
scp ${ETCD_SSL}/${FILE} ${NODE}:${ETCD_SSL}/${FILE}
done
done

Kubernetes CA

k8s-m1建立pki文件夹(192.168.126.110为VIP),并生成CA凭证。

1
2
3
4
mkdir -p ${PKI_DIR}
cfssl gencert -initca ca-csr.json | cfssljson -bare ${PKI_DIR}/ca
ls ${PKI_DIR}/ca*.pem
/etc/kubernetes/pki/ca-key.pem /etc/kubernetes/pki/ca.pem

API Server Certificate

此凭证将被用于API Server和Kubelet Client通信使用,使用下面命令生成kube-apiserver凭证:

1
2
3
4
5
6
7
8
9
10
cfssl gencert \
-ca=${PKI_DIR}/ca.pem \
-ca-key=${PKI_DIR}/ca-key.pem \
-config=ca-config.json \
-hostname=10.96.0.1,${VIP},127.0.0.1,kubernetes,kubernetes.default \
-profile=kubernetes \
apiserver-csr.json | cfssljson -bare ${PKI_DIR}/apiserver

ls ${PKI_DIR}/apiserver*.pem
/etc/kubernetes/pki/apiserver-key.pem /etc/kubernetes/pki/apiserver.pem
  • 这边-hostname10.96.0.1是Cluster IP的Kubernetes端点(默认占用第一个ip,用于给集群里的pod要调用Kubernetes的API server);
  • kubernetes.default为Kubernets DN。
  • 如果使用域名可以加上域名

Front Proxy Certificate

此凭证将被用于Authenticating Proxy的功能上,而该功能主要是提供API Aggregation的认证。F使用下面命令生成CA:

1
2
3
4
5
cfssl gencert \
-initca front-proxy-ca-csr.json | cfssljson -bare ${PKI_DIR}/front-proxy-ca

ls ${PKI_DIR}/front-proxy-ca*.pem
/etc/kubernetes/pki/front-proxy-ca-key.pem /etc/kubernetes/pki/front-proxy-ca.pem

接着生成front-proxy-client凭证(hosts的warning忽略即可):

1
2
3
4
5
6
7
8
9
cfssl gencert \
-ca=${PKI_DIR}/front-proxy-ca.pem \
-ca-key=${PKI_DIR}/front-proxy-ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
front-proxy-client-csr.json | cfssljson -bare ${PKI_DIR}/front-proxy-client

ls ${PKI_DIR}/front-proxy-client*.pem
front-proxy-client-key.pem front-proxy-client.pem

Controller Manager Certificate

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

这边通过以下命令生成 Controller Manager 凭证(hosts的warning忽略即可):

1
2
3
4
5
6
7
8
9
cfssl gencert \
-ca=${PKI_DIR}/ca.pem \
-ca-key=${PKI_DIR}/ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
manager-csr.json | cfssljson -bare ${PKI_DIR}/controller-manager

ls ${PKI_DIR}/controller-manager*.pem
controller-manager-key.pem controller-manager.pem

接着利用kubectl生成Controller Managerkubeconfig文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# controller-manager set cluster

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

# controller-manager set credentials

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

# controller-manager set context

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

# controller-manager set default context

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

Scheduler Certificate

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

这边通过以下命令生成 Scheduler 凭证(hosts的warning忽略即可):

1
2
3
4
5
6
7
8
9
cfssl gencert \
-ca=${PKI_DIR}/ca.pem \
-ca-key=${PKI_DIR}/ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
scheduler-csr.json | cfssljson -bare ${PKI_DIR}/scheduler

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

接着利用kubectl生成Schedulerkubeconfig文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# scheduler set cluster

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

# scheduler set credentials

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

# scheduler set context

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

# scheduler use default context

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

Admin Certificate

Admin 被用来绑定 RBAC Cluster Role 中 cluster-admin,当想要操作所有 Kubernetes 集群功能时,就必须利用这边生成的 kubeconfig 文件。

这边通过以下命令生成 Kubernetes Admin 凭证(hosts的warning忽略即可):

1
2
3
4
5
6
7
8
9
cfssl gencert \
-ca=${PKI_DIR}/ca.pem \
-ca-key=${PKI_DIR}/ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
admin-csr.json | cfssljson -bare ${PKI_DIR}/admin

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

接着利用kubectl生成 Admin 的kubeconfig文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# admin set cluster
kubectl config set-cluster kubernetes \
--certificate-authority=${PKI_DIR}/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=${K8S_DIR}/admin.conf

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

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

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

Master Kubelet Certificate

这边使用 Node authorizer 来让节点的 kubelet 能够存取如 services、endpoints 等 API,而使用 Node authorizer 需定义system:nodesCLusterRole(凭证的 Organization),并且包含system:node:<nodeName>的使用者名称(凭证的 Common Name)。

首先在k8s-m1节点生成所有 master 节点的 kubelet 凭证,这边通过下面腳本來生成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for NODE in "${!MasterArray[@]}"; do
echo "--- $NODE ---"
\cp kubelet-csr.json kubelet-$NODE-csr.json;
sed -i "s/\$NODE/$NODE/g" kubelet-$NODE-csr.json;
cfssl gencert \
-ca=${PKI_DIR}/ca.pem \
-ca-key=${PKI_DIR}/ca-key.pem \
-config=ca-config.json \
-hostname=$NODE \
-profile=kubernetes \
kubelet-$NODE-csr.json | cfssljson -bare ${PKI_DIR}/kubelet-$NODE;
rm -f kubelet-$NODE-csr.json
done

ls ${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
  • 这边需要依据节点修改-hostname$NODE

完成后复制kubelet凭证至所有master节点:

1
2
3
4
5
6
7
8
9
10
11
for NODE in "${!MasterArray[@]}"; do
echo "--- $NODE ${MasterArray[$NODE]} ---"
ssh ${MasterArray[$NODE]} "mkdir -p ${PKI_DIR}"
scp ${PKI_DIR}/ca.pem ${MasterArray[$NODE]}:${PKI_DIR}/ca.pem
scp ${PKI_DIR}/kubelet-$NODE-key.pem ${MasterArray[$NODE]}:${PKI_DIR}/kubelet-key.pem
scp ${PKI_DIR}/kubelet-$NODE.pem ${MasterArray[$NODE]}:${PKI_DIR}/kubelet.pem
rm -f ${PKI_DIR}/kubelet-$NODE-key.pem ${PKI_DIR}/kubelet-$NODE.pem
done



接着在k8s-m1执行以下命令给所有master产生kubelet的kubeconfig文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for NODE in "${!MasterArray[@]}"; do
echo "--- $NODE ---"
ssh ${MasterArray[$NODE]} "cd ${PKI_DIR} && \
kubectl config set-cluster kubernetes \
--certificate-authority=${PKI_DIR}/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=${K8S_DIR}/kubelet.conf && \
kubectl config set-credentials system:node:${NODE} \
--client-certificate=${PKI_DIR}/kubelet.pem \
--client-key=${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 Key

Kubernetes Controller Manager 利用 Key pair 生成与签署 Service Account 的 tokens,而这边不能通过 CA 做认证,而是建立一组公私钥来让 API Server 与 Controller Manager 使用:
k8s-m1执行以下指令

1
2
3
4
openssl genrsa -out ${PKI_DIR}/sa.key 2048
openssl rsa -in ${PKI_DIR}/sa.key -pubout -out ${PKI_DIR}/sa.pub
ls ${PKI_DIR}/sa.*
/etc/kubernetes/pki/sa.key /etc/kubernetes/pki/sa.pub

删除不必要文件
所有资讯准备完成后,就可以将一些不必要文件删除:

1
2
3
4
5
rm -f ${PKI_DIR}/*.csr \
${PKI_DIR}/scheduler*.pem \
${PKI_DIR}/controller-manager*.pem \
${PKI_DIR}/admin*.pem \
${PKI_DIR}/kubelet*.pem

复制文件至其他节点

复制凭证文件至其他master节点:

1
2
3
4
5
6
for NODE in "${!otherMaster[@]}"; do
echo "--- $NODE ---"
for FILE in $(ls ${PKI_DIR}); do
scp ${PKI_DIR}/${FILE} ${otherMaster[$NODE]}:${PKI_DIR}/${FILE}
done
done

复制Kubernetes config文件至其他master节点:

1
2
3
4
5
6
for NODE in "${!otherMaster[@]}"; do
echo "--- $NODE ---"
for FILE in admin.conf controller-manager.conf scheduler.conf; do
scp ${K8S_DIR}/${FILE} ${otherMaster[$NODE]}:${K8S_DIR}/${FILE}
done
done

Kubernetes Masters

本部分将说明如何建立与设定Kubernetes Master 角色,过程中会部署以下元件:

  • kubelet: 负责管理容器的生命周期,定期从API Server获取节点上的预期状态(如网络、存储等等配置)资源,并让对应的容器插件(CRI、CNI 等)来达成这个状态。任何 Kubernetes 节点(node)都会拥有这个
  • 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 进行沟通从而保存或读取资源状态。
  • HAProxy:提供多個 API Server 的负载均衡(Load Balance)
  • Keepalived:提供虚拟IP位址(VIP)。

部署与设定

k8s-m1节点下把部署元件的YAML文件和配置文件复制到其他master节点,这边不采用二进制执行档与Systemd来管理这些元件,全部采用Static Pod来达成。这边将文件放至/etc/kubernetes/manifests目录:

  • 信息啥的按照自己实际填写,文件改了后如果不懂我下面写的shell估计是改不回了
  • 网卡名改为各自宿主机的网卡名,下面用export interface=ens33后续的所有文件同理
  • 若cluster dns或domain有改变的话,需要修改10-kubelet.conf。
  • 单台master的话,涉及到keepalived和haproxy的修改和scp啥的不需要执行,同时把master的kube-apiserver.yml的里面的5443端口改成6443,且etcd.yml里的镜像版本改成v3.1.9,不然会报错,原因参照本贴顶部的ansible部署里的readme详解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
cd ~/k8s-manual-files/master/manifests
export MANIFESTS_DIR=/etc/kubernetes/manifests/
export interface=ens33

etcd_servers=$( xargs -n1<<<${MasterArray[@]} | sort | sed 's#^#https://#;s#$#:2379#;$s#\n##' | paste -d, -s - )

# 修改keepalived(网卡和VIP写进去,使用下面命令)

line=$( grep -Pon '\Q{{ VIP }}\E' keepalived.yml | cut -d':' -f1 )
sed -ri "${line}s#(: ).+#\1${VIP}#" keepalived.yml
line=$( grep -Pon '\Q{{ interface }}\E' keepalived.yml | cut -d':' -f1 )
sed -ri "${line}s#(: ).+#\1${interface}#" keepalived.yml

# 修改kube-apiserver(单台master修改端口为6443)

sed -ri '/--advertise-address/s#=.+#='"$VIP"'#' kube-apiserver.yml
sed -ri '/--etcd-servers/s#=.+#='"$etcd_servers"'#' kube-apiserver.yml
[ "${#MasterArray[@]}" -eq 1 ] && sed -i 's#5443#6443#' kube-apiserver.yml

# 单台master下etcd的版本为v3.1.9

[ "${#MasterArray[@]}" -eq 1 ] && sed -ri '/image:/s#[^:]+$#v3.1.9#' etcd.yml

cd ~/k8s-manual-files/master/etc

# 修改etcd的配置文件

etcd_initial_cluster=$( for i in ${!MasterArray[@]};do echo $i=https://${MasterArray[$i]}:2380; done | sort | paste -d, -s - )
sed -ri "/initial-cluster:/s#'.+'#'${etcd_initial_cluster}'#" etcd/config.yml

# 修改haproxy.cfg配置文件

paste <( seq -f' server api%g ' ${#MasterArray[@]} ) <( xargs -n1<<<${MasterArray[@]} | sort | sed 's#$#:5443 check#') > server
sed -i '$r server' haproxy/haproxy.cfg
rm -f server

# 修改encryption.yml

cd ~/k8s-manual-files/master
ENCRYPT_SECRET=$( head -c 32 /dev/urandom | base64 )
sed -ri "/secret:/s#(: ).+#\1${ENCRYPT_SECRET}#" encryption/config.yml

# 分发文件

cd ~/k8s-manual-files/master
for node in "${MasterArray[@]}"; do
echo "--- $node ---"
ssh $node "mkdir -p $MANIFESTS_DIR /etc/etcd /etc/haproxy/ /etc/systemd/system/kubelet.service.d /var/lib/kubelet /var/log/kubernetes /var/lib/etcd"
scp manifests/* $node:$MANIFESTS_DIR
[ "${#MasterArray[@]}" -eq 1 ] && ssh $node "rm -f $MANIFESTS_DIR/keepalived.yml $MANIFESTS_DIR/haproxy.yml"

scp etc/etcd/config.yml $node:/etc/etcd/config.yml
ssh $node 'sed -i "s/{HOSTNAME}/${HOSTNAME}/g" /etc/etcd/config.yml'
ssh $node "sed -i "s/{PUBLIC_IP}/${node}/g" /etc/etcd/config.yml"

scp encryption/config.yml $node:/etc/kubernetes/encryption.yml
[ "${#MasterArray[@]}" -ne 1 ] && {
scp etc/haproxy/haproxy.cfg $node:/etc/haproxy/haproxy.cfg
}
scp audit/policy.yml $node:/etc/kubernetes/audit-policy.yml
scp systemd/kubelet.service $node:/lib/systemd/system/kubelet.service
scp systemd/10-kubelet.conf $node:/etc/systemd/system/kubelet.service.d/10-kubelet.conf
done

如果机器的网卡名字不一致且使用了HA,修改每台机器的/etc/kubernetes/manifests/keepalived.yml里的网卡名

每台master机器启动kubelet 服务并设置kubectl补全脚本:

1
2
3
4
5
6
for NODE in "${MasterArray[@]}"; do
echo "--- $NODE ---"
ssh $NODE 'systemctl enable --now kubelet.service;
cp /etc/kubernetes/admin.conf ~/.kube/config;
kubectl completion bash > /etc/bash_completion.d/kubectl'
done

完成后会需要一段时间来下载映像档与启动元件,可以利用该指令来监看:

1
2
3
4
5
6
7
8
9
10
11
12
$ watch netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:10248 0.0.0.0:* LISTEN 10344/kubelet
tcp 0 0 127.0.0.1:10251 0.0.0.0:* LISTEN 11324/kube-schedule
tcp 0 0 0.0.0.0:6443 0.0.0.0:* LISTEN 11416/haproxy
tcp 0 0 127.0.0.1:10252 0.0.0.0:* LISTEN 11235/kube-controll
tcp 0 0 0.0.0.0:9090 0.0.0.0:* LISTEN 11416/haproxy
tcp6 0 0 :::2379 :::* LISTEN 10479/etcd
tcp6 0 0 :::2380 :::* LISTEN 10479/etcd
tcp6 0 0 :::10255 :::* LISTEN 10344/kubelet
tcp6 0 0 :::5443 :::* LISTEN 11295/kube-apiserve
  • 此处需要等待时间来拉取镜像,需要耐心等待
  • 若看到以上资讯表示服务正常启动,若发生问题可以用docker指令来查看。
  • 若看到关键的几个管理组件容器退出的话就说明操作错误

上面会去拉取镜像,需要一段时间,具体好没好可以下面的操作来看状态对不对

验证集群
完成后,在任意一台master节点通过简单指令验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
$ kubectl get cs
NAME STATUS MESSAGE ERROR
scheduler Healthy ok
controller-manager Healthy ok
etcd-2 Healthy {"health": "true"}
etcd-0 Healthy {"health": "true"}
etcd-1 Healthy {"health": "true"}

$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36s

[root@k8s-m1 kubernetes]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-m1 NotReady master 52s v1.11.1
k8s-m2 NotReady master 51s v1.11.1
k8s-m3 NotReady master 50s v1.11.1

$ kubectl -n kube-system get po
NAME READY STATUS RESTARTS AGE
etcd-k8s-m1 1/1 Running 0 7m
etcd-k8s-m2 1/1 Running 0 8m
etcd-k8s-m3 1/1 Running 0 7m
haproxy-k8s-m1 1/1 Running 0 7m
haproxy-k8s-m2 1/1 Running 0 8m
haproxy-k8s-m3 1/1 Running 0 8m
keepalived-k8s-m1 1/1 Running 0 8m
keepalived-k8s-m2 1/1 Running 0 7m
keepalived-k8s-m3 1/1 Running 0 7m
kube-apiserver-k8s-m1 1/1 Running 0 7m
kube-apiserver-k8s-m2 1/1 Running 0 6m
kube-apiserver-k8s-m3 1/1 Running 0 7m
kube-controller-manager-k8s-m1 1/1 Running 0 8m
kube-controller-manager-k8s-m2 1/1 Running 0 8m
kube-controller-manager-k8s-m3 1/1 Running 0 8m
kube-scheduler-k8s-m1 1/1 Running 0 8m
kube-scheduler-k8s-m2 1/1 Running 0 8m
kube-scheduler-k8s-m3 1/1 Running 0 8m

建立TLS Bootstrapping RBAC 与Secret

由于本次安装启用了TLS认证,因此每个节点的kubelet都必须使用kube-apiserver的CA的凭证后,才能与kube-apiserver进行沟通,而该过程需要手动针对每台节点单独签署凭证是一件繁琐的事情,且一旦节点增加会延伸出管理不易问题;而TLS bootstrapping目标就是解决该问题,通过让kubelet先使用一个预定低权限使用者连接到kube-apiserver,然后在对kube-apiserver申请凭证签署,当授权Token一致时,Node节点的kubelet凭证将由kube-apiserver动态签署提供。具体作法可以参考TLS BootstrappingAuthenticating with Bootstrap Tokens

后面kubectl命令只需要在任何一台master执行就行了

首先在k8s-m1建立一个变数来产生BOOTSTRAP_TOKEN,并建立bootstrap-kubelet.conf的Kubernetes config文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
export TOKEN_ID=$(openssl rand 3 -hex)
export TOKEN_SECRET=$(openssl rand 8 -hex)
export BOOTSTRAP_TOKEN=${TOKEN_ID}.${TOKEN_SECRET}

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

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

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

# bootstrap use default context
kubectl config use-context tls-bootstrap-token-user@kubernetes \
--kubeconfig=${K8S_DIR}/bootstrap-kubelet.conf
  • 若想要用手动签署凭证来进行授权的话,可以参考Certificate

接着在k8s-m1建立TLS bootstrap secret来提供自动签证使用:

1
2
3
4
5
6
7
8
cd ~/k8s-manual-files/master

# 注入变量

sed -ri "s#\{TOKEN_ID\}#${TOKEN_ID}#g" resources/bootstrap-token-Secret.yml
sed -ri "s#\{TOKEN_SECRET\}#${TOKEN_SECRET}#g" resources/bootstrap-token-Secret.yml
kubectl create -f resources/bootstrap-token-Secret.yml
secret "bootstrap-token-65a3a9" created

k8s-m1建立 TLS Bootstrap Autoapprove RBAC来自动处理 CSR:

1
2
3
4
$ kubectl apply -f resources/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

接着确认服务能够执行logs 等指令:

1
2
$ kubectl -n kube-system logs -f kube-scheduler-k8s-m2
Error from server (Forbidden): Forbidden (user=kube-apiserver, verb=get, resource=nodes, subresource=proxy) ( pods/log kube-scheduler-k8s-m2)
  • 这边会发现出现403 Forbidden问题,这是因为kube-apiserveruser并没有nodes的资源存取权限,属于正常。

为了方便管理集群,因此需要通过 kubectl logs 来查看,但由于 API 权限,故需要建立一个 RBAC Role 来获取存取权限,这边在k8s-m1节点执行下面命令创建:

1
2
3
4
5
6
7
8
9
$ kubectl apply -f resources/apiserver-to-kubelet-rbac.yml
clusterrole.rbac.authorization.k8s.io "system:kube-apiserver-to-kubelet" configured
clusterrolebinding.rbac.authorization.k8s.io "system:kube-apiserver" configured

# 测试 logs
$ kubectl -n kube-system logs -f kube-scheduler-k8s-m1
...
I0403 02:30:36.375935 1 server.go:555] Version: v1.10.0
I0403 02:30:36.378208 1 server.go:574] starting healthz server on 127.0.0.1:10251

设定master节点加上污点Taint不让(没有声明容忍该污点的)pod跑在master节点上:

1
2
3
4
$ kubectl taint nodes node-role.kubernetes.io/master="":NoSchedule --all
node "k8s-m1" tainted
node "k8s-m2" tainted
node "k8s-m3" tainted

Kubernetes Nodes
本部分将说明如何建立与设定Kubernetes Node 角色,Node 是主要执行容器实例(Pod)的工作节点。
在开始部署前,先在k8-m1将需要用到的文件复制到所有node节点上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

cd ${PKI_DIR}
for NODE in "${NodeArray[@]}"; do
echo "--- $NODE ---"
ssh ${NODE} "mkdir -p ${PKI_DIR} ${ETCD_SSL}"
# Etcd
for FILE in etcd-ca.pem etcd.pem etcd-key.pem; do
scp ${ETCD_SSL}/${FILE} ${NODE}:${ETCD_SSL}/${FILE}
done
# Kubernetes
for FILE in pki/ca.pem pki/ca-key.pem pki/front-proxy-ca.pem bootstrap-kubelet.conf ; do
scp ${K8S_DIR}/${FILE} ${NODE}:${K8S_DIR}/${FILE}
done
done

部署与设定
k8s-m1节点分发kubelet.service相关文件到每台node上去管理kubelet:

1
2
3
4
5
6
7
cd ~/k8s-manual-files/
for NODE in "${NodeArray[@]}"; do
echo "--- $NODE ---"
ssh $NODE "mkdir -p /var/lib/kubelet /var/log/kubernetes /etc/systemd/system/kubelet.service.d $MANIFESTS_DIR"
scp node/systemd/kubelet.service $NODE:/lib/systemd/system/kubelet.service
scp node/systemd/10-kubelet.conf ${NODE}:/etc/systemd/system/kubelet.service.d/10-kubelet.conf
done
  • cluster dnsdomain有改变的话,需要修改10-kubelet.conf

最后每台node节点启动kubelet 服务:

1
2
3
4
for NODE in "${NodeArray[@]}"; do
echo "--- $NODE ---"
ssh ${NODE} 'systemctl enable --now kubelet.service'
done

验证集群

完成后,在任意一台master节点并通过简单指令验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-bvz9l 11m system:node:k8s-m1 Approved,Issued
csr-jwr8k 11m system:node:k8s-m2 Approved,Issued
csr-q867w 11m system:node:k8s-m3 Approved,Issued
node-csr-Y-FGvxZWJqI-8RIK_IrpgdsvjGQVGW0E4UJOuaU8ogk 17s system:bootstrap:dca3e1 Approved,Issued
node-csr-cnX9T1xp1LdxVDc9QW43W0pYkhEigjwgceRshKuI82c 19s system:bootstrap:dca3e1 Approved,Issued
node-csr-m7SBA9RAGCnsgYWJB-u2HoB2qLSfiQZeAxWFI2WYN7Y 18s system:bootstrap:dca3e1 Approved,Issued

$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-m1 NotReady master 12m v1.11.1
k8s-m2 NotReady master 11m v1.11.1
k8s-m3 NotReady master 11m v1.11.1
k8s-n1 NotReady node 32s v1.11.1

Kubernetes Core Addons部署

当完成上面所有步骤后,接着需要部署一些插件,其中如Kubernetes DNSKubernetes Proxy等这种Addons是非常重要的。

Kubernetes Proxy

Kube-proxy是实现Service的关键插件,kube-proxy会在每台节点上执行,然后监听API Server的Service与Endpoint资源物件的改变,然后来依据变化执行iptables来实现网路的转发。这边我们会需要建议一个DaemonSet来执行,并且建立一些需要的Certificates。

k8s-m1建立Kubernetes Proxy Addon:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ cd ~/k8s-manual-files
# 注入变量
$ sed -ri "/server:/s#(: ).+#\1${KUBE_APISERVER}#" addons/kube-proxy/kube-proxy.yml
$ kubectl apply -f addons/kube-proxy/kube-proxy.yml
serviceaccount "kube-proxy" created
clusterrolebinding.rbac.authorization.k8s.io "system:kube-proxy" created
configmap "kube-proxy" created
daemonset.apps "kube-proxy" created

$ kubectl -n kube-system get po -l k8s-app=kube-proxy
NAME READY STATUS RESTARTS AGE
kube-proxy-dd2m7 1/1 Running 0 8m
kube-proxy-fwgx8 1/1 Running 0 8m
kube-proxy-kjn57 1/1 Running 0 8m
kube-proxy-vp47w 1/1 Running 0 8m
kube-proxy-xsncw 1/1 Running 0 8m

通过ipvsadm查看 proxy 规则

1
2
3
4
5
6
$ ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.96.0.1:443 rr
-> 192.168.126.110:5443 Masq 1 0 0

CoreDNS

1.11后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 中。

k8s-m1通过 kubeclt 执行下面命令來创建,并检查是否部署成功:

1
2
3
4
5
6
7
8
$ kubectl apply -f addons/coredns/coredns.yml
serviceaccount "kube-dns" created
service "kube-dns" created
deployment.extensions "kube-dns" created

$ kubectl -n kube-system get po -l k8s-app=kube-dns
NAME READY STATUS RESTARTS AGE
kube-dns-654684d656-zq5t8 0/3 Pending 0 1m

这边会发现处于Pending状态,是由于Kubernetes Pod Network还未建立完成,因此所有节点会处于NotReady状态,而造成Pod无法被排程分配到指定节点上启动,由于为了解决该问题,下节将说明如何建立Pod Network。

集群网络

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

  • 高耦合的容器到容器通信:通过 Pods 内 localhost 的來解決。
  • Pod 到 Pod 的通信:通过实现网络模型来解决。
  • Pod 到 Service 通信:由 Service objects 结合 kube-proxy 解決。
  • 外部到 Service 通信:一样由 Service objects 结合 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 集群网络。

网络部署与设定(flannel或者calico)

flannel

创建flannel

1
2
$ kubectl apply -f addons/flannel/kube-flannel.yml
...

检查是否启动

1
2
3
4
5
6
7
$ kubectl -n kube-system get po -l k8s-app=flannel
NAME READY STATUS RESTARTS AGE
kube-flannel-ds-27jwl 2/2 Running 0 59s
kube-flannel-ds-4fgv6 2/2 Running 0 59s
kube-flannel-ds-mvrt7 2/2 Running 0 59s
kube-flannel-ds-p2q9g 2/2 Running 0 59s
kube-flannel-ds-zchsz 2/2 Running 0 59s

Calico

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

由于 Calico 提供了 Kubernetes resources YAML 文娟来快速以容器方式部署网络插件至所有节点上,因此只需要在k8s-m1使用 kubeclt 执行下面指令來建立:

1
kubectl apply -f addons/calico/v3.1
  • 另外当节点超过 50 台,可以使用 Calico 的 Typha 模式来减少通过 Kubernetes datastore 造成 API Server 的负担。

可能墙的原因(情况好点的可能拉取慢)是下面状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ kubectl -n kube-system get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-node-2hdqf 0/2 ContainerCreating 0 4m
kube-system calico-node-456fh 0/2 ContainerCreating 0 4m
kube-system calico-node-jh6vd 0/2 ContainerCreating 0 4m
kube-system calico-node-sp6w9 0/2 ContainerCreating 0 4m
kube-system calicoctl-6dfc585667-24s9h 0/1 Pending 0 4m
kube-system coredns-6975654877-jjqkg 0/1 Pending 0 10m
kube-system coredns-6975654877-ztqjh 0/1 Pending 0 10m
kube-system etcd-k8s-m1 1/1 Running 0 14m
kube-system etcd-k8s-m2 1/1 Running 0 13m
kube-system etcd-k8s-m3 1/1 Running 0 14m
kube-system haproxy-k8s-m1 1/1 Running 0 13m
kube-system haproxy-k8s-m2 1/1 Running 0 14m
kube-system haproxy-k8s-m3 1/1 Running 0 14m
kube-system keepalived-k8s-m1 1/1 Running 0 14m
kube-system keepalived-k8s-m2 1/1 Running 0 14m
kube-system keepalived-k8s-m3 1/1 Running 0 14m
kube-system kube-apiserver-k8s-m1 1/1 Running 0 14m
kube-system kube-apiserver-k8s-m2 1/1 Running 2 13m
kube-system kube-apiserver-k8s-m3 1/1 Running 2 13m
kube-system kube-controller-manager-k8s-m1 1/1 Running 0 13m
kube-system kube-controller-manager-k8s-m2 1/1 Running 0 14m
kube-system kube-controller-manager-k8s-m3 1/1 Running 0 13m
kube-system kube-proxy-46hr5 1/1 Running 0 10m
kube-system kube-proxy-l42sk 1/1 Running 0 10m
kube-system kube-proxy-p2nbf 1/1 Running 0 10m
kube-system kube-proxy-q6qn9 1/1 Running 0 10m
kube-system kube-scheduler-k8s-m1 1/1 Running 0 14m
kube-system kube-scheduler-k8s-m2 1/1 Running 0 14m
kube-system kube-scheduler-k8s-m3 1/1 Running 0 14m
kube-system metrics-server-576cb6fbd5-v9d4w 0/1 Pending 0 9m

describe查看是在拉取镜像

1
2
$ kubectl describe -n kube-system pod calico-node-2hdqf
Normal Pulling 6m kubelet, k8s-n1 pulling image "quay.io/calico/node:v3.1.3"

手动pull发现拉取挺慢的,没办法手动拉等着看进度吧,没梯子的同学可以下面命令拉取

1
curl -s https://zhangguanzhang.github.io/bash/pull.sh | bash -s quay.io/calico/node:v3.1.3

calico正常是下面状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ kubectl get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-node-2hdqf 2/2 Running 0 33m
kube-system calico-node-456fh 2/2 Running 2 33m
kube-system calico-node-jh6vd 2/2 Running 0 33m
kube-system calico-node-sp6w9 2/2 Running 0 33m
kube-system calicoctl-6dfc585667-24s9h 1/1 Running 0 33m
kube-system coredns-6975654877-jjqkg 1/1 Running 0 39m
kube-system coredns-6975654877-ztqjh 1/1 Running 0 39m
kube-system etcd-k8s-m1 1/1 Running 0 42m
kube-system etcd-k8s-m2 1/1 Running 0 42m
kube-system etcd-k8s-m3 1/1 Running 0 42m
kube-system haproxy-k8s-m1 1/1 Running 0 42m
kube-system haproxy-k8s-m2 1/1 Running 0 42m
kube-system haproxy-k8s-m3 1/1 Running 0 42m
kube-system keepalived-k8s-m1 1/1 Running 0 42m
kube-system keepalived-k8s-m2 1/1 Running 0 42m
kube-system keepalived-k8s-m3 1/1 Running 0 42m
kube-system kube-apiserver-k8s-m1 1/1 Running 1 42m
kube-system kube-apiserver-k8s-m2 1/1 Running 2 42m
kube-system kube-apiserver-k8s-m3 1/1 Running 2 42m
kube-system kube-controller-manager-k8s-m1 1/1 Running 0 42m
kube-system kube-controller-manager-k8s-m2 1/1 Running 0 42m
kube-system kube-controller-manager-k8s-m3 1/1 Running 1 42m
kube-system kube-proxy-46hr5 1/1 Running 0 39m
kube-system kube-proxy-l42sk 1/1 Running 0 39m
kube-system kube-proxy-p2nbf 1/1 Running 0 39m
kube-system kube-proxy-q6qn9 1/1 Running 0 39m
kube-system kube-scheduler-k8s-m1 1/1 Running 1 42m
kube-system kube-scheduler-k8s-m2 1/1 Running 0 43m
kube-system kube-scheduler-k8s-m3 1/1 Running 0 42m
kube-system metrics-server-576cb6fbd5-v9d4w 1/1 Running 0 37m

部署后通过下面查看状态即使正常

1
2
3
4
5
6
kubectl -n kube-system get po -l k8s-app=calico-node
NAME READY STATUS RESTARTS AGE
calico-node-bv7r9 2/2 Running 4 13m
calico-node-cmh2w 2/2 Running 3 13m
calico-node-klzrz 2/2 Running 4 13m
calico-node-n4c9j 2/2 Running 4 13m

查找calicoctl的pod名字

1
2
3
kubectl -n kube-system get po -l k8s-app=calicoctl
NAME READY STATUS RESTARTS AGE
calicoctl-6b5bf7cb74-d9gv8 1/1 Running 0 30m

通过 kubectl exec calicoctl pod 执行命令来检查功能是否正常

1
2
3
4
5
6
7
8
9
10
11
12
$ kubectl -n kube-system exec calicoctl-6b5bf7cb74-d9gv8 -- calicoctl get profiles -o wide
NAME LABELS
kns.default map[]
kns.kube-public map[]
kns.kube-system map[]

$ kubectl -n kube-system exec calicoctl-6b5bf7cb74-d9gv8 -- calicoctl get node -o wide
NAME ASN IPV4 IPV6
k8s-m1 (unknown) 192.168.126.111/24
k8s-m2 (unknown) 192.168.126.112/24
k8s-m3 (unknown) 192.168.126.113/24
k8s-n1 (unknown) 10.244.3.1/24

完成后,通过检查节点是否不再是NotReady,以及 Pod 是否不再是Pending:

1
2
3
4
5
6
7
8
9
10
11
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-m1 Ready master 17m v1.11.1
k8s-m2 Ready master 16m v1.11.1
k8s-m3 Ready master 16m v1.11.1
k8s-n1 Ready node 6m v1.11.1

$ kubectl -n kube-system get po -l k8s-app=kube-dns
NAME READY STATUS RESTARTS AGE
coredns-6975654877-42vh9 1/1 Running 0 10m
coredns-6975654877-tq6hs 1/1 Running 0 10m

Metrics Server

Metrics Server 是实现了 Metrics API 的元件,其目标是取代 Heapster 作位 Pod 与 Node 提供资源的 Usage metrics,该元件会从每个 Kubernetes 节点上的 Kubelet 所公开的 Summary API 中收集 Metrics

  • Horizontal Pod Autoscaler(HPA)控制器用于实现基于CPU使用率进行自动Pod伸缩的功能。
  • HPA控制器基于Master的kube-controller-manager服务启动参数–horizontal-pod-autoscaler-sync-period定义是时长(默认30秒),周期性监控目标Pod的CPU使用率,并在满足条件时对ReplicationController或Deployment中的Pod副本数进行调整,以符合用户定义的平均Pod CPU使用率。
  • 在新版本的kubernetes中 Pod CPU使用率不在来源于heapster,而是来自于metrics-server
  • 官网原话是 The –horizontal-pod-autoscaler-use-rest-clients is true or unset. Setting this to false switches to Heapster-based autoscaling, which is deprecated.
  • yml 文件来自于github https://github.com/kubernetes-incubator/metrics-server/tree/master/deploy/1.8+
  • /etc/kubernetes/pki/front-proxy-ca.pem 文件来自于部署kubernetes集群
  • 需要对yml文件进行修改才可使用 改动自行见文件

首先在k8s-m1测试一下 kubectl top 指令:

1
2
$ kubectl top node
Error from server (NotFound): the server could not find the requested resource (get services http:heapster:)

发现 top 命令无法取得 Metrics,这表示 Kubernetes 集群没有安装 Heapster 或着 Metrics Server 来提供 Metrics API 给 top 指令取得资源使用量。

由于上述问题,我們要在k8s-m1部署 Metrics Server 元件来解決:

1
2
3
4
$ kubectl create -f addons/metric-server/metrics-server.yml
$ kubectl -n kube-system get po -l k8s-app=metrics-server
NAME READY STATUS RESTARTS AGE
pod/metrics-server-86bd9d7667-5hbn6 1/1 Running 0 1m

完成后,等待一段时间(约 30s - 1m)收集 Metrics,再次执行 kubectl top 指令查看:

1
2
3
4
5
6
7
8
9
$ kubectl get --raw /apis/metrics.k8s.io/v1beta1
{"kind":"APIResourceList","apiVersion":"v1","groupVersion":"metrics.k8s.io/v1beta1","resources":[{"name":"nodes","singularName":"","namespaced":false,"kind":"NodeMetrics","verbs":["get","list"]},{"name":"pods","singularName":"","namespaced":true,"kind":"PodMetrics","verbs":["get","list"]}]}

$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
k8s-m1 113m 2% 1080Mi 14%
k8s-m2 133m 3% 1086Mi 14%
k8s-m3 100m 2% 1029Mi 13%
k8s-n1 146m 3% 403Mi 5%

而这时若有使用 HPA 的話,就能够正确抓到 Pod 的 CPU 与 Memory 使用量了。

若想让 HPA 使用 Prometheus 的 Metrics 的话,可以阅读 Custom Metrics Server 來了解。

测试是否可以建立 Pod(到此步集群即可使用)

1
2
3
4
$ kubectl run nginx --image nginx --restart=Never --port 80
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 22s

Kubernets Extra Addons部署

本节说明如何部署一些官方常用的额外 Addons,如 Dashboard、Ingress Controller、External DNS 与 Prometheus等等。

所有 Addons 部署文件已放至k8s-manual-files中,因此在k8s-m1进入该目录,按照以下顺序建立:

1
$ cd ~/k8s-manual-files

Dashboard

Dashboard是Kubernetes社区官方开发的仪表板,有了仪表板后管理者就能够通过Web-based方式来管理Kubernetes集群,除了提升管理方便,也让资源视觉化,让人更直觉看见系统资讯的呈现结果。

k8s-m1通过kubectl来建立kubernetes dashboard即可:

1
2
3
4
5
6
7
$ kubectl apply -f ExtraAddons/dashboard
$ kubectl -n kube-system get po,svc -l k8s-app=kubernetes-dashboard
NAME READY STATUS RESTARTS AGE
kubernetes-dashboard-7d5dcdb6d9-j492l 1/1 Running 0 12s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes-dashboard ClusterIP 10.111.22.111 <none> 443/TCP 12s

这边会额外建立一个名称为anonymous-dashboard-proxy的 Cluster Role(Binding) 来让system:anonymous这个匿名使用者能够通过 API Server 来 proxy 到 Kubernetes Dashboard,而这个 RBAC 规则仅能够存取services/proxy资源,以及https:kubernetes-dashboard:资源名称:

完成后,就可以通过浏览器存取Dashboard https://{YOUR_VIP}:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/
在 1.7 版本以後的 Dashboard 將不再提供所有權限,因此需要建立一個 service account 來綁定 cluster-admin role:

1
2
3
4
$ kubectl -n kube-system create sa dashboard
$ kubectl create clusterrolebinding dashboard --clusterrole cluster-admin --serviceaccount=kube-system:dashboard
$ kubectl -n kube-system describe secrets | sed -rn '/\sdashboard-token-/,/^token/{/^token/s#\S+\s+##p}'
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtdG9rZW4tdzVocmgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYWJmMTFjYzMtZjRlYi0xMWU3LTgzYWUtMDgwMDI3NjdkOWI5Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZCJ9.Xuyq34ci7Mk8bI97o4IldDyKySOOqRXRsxVWIJkPNiVUxKT4wpQZtikNJe2mfUBBD-JvoXTzwqyeSSTsAy2CiKQhekW8QgPLYelkBPBibySjBhJpiCD38J1u7yru4P0Pww2ZQJDjIxY4vqT46ywBklReGVqY3ogtUQg-eXueBmz-o7lJYMjw8L14692OJuhBjzTRSaKW8U2MPluBVnD7M2SOekDff7KpSxgOwXHsLVQoMrVNbspUCvtIiEI1EiXkyCNRGwfnd2my3uzUABIHFhm0_RZSmGwExPbxflr8Fc6bxmuz-_jSdOtUidYkFIzvEWw2vRovPgs3MXTv59RwUw
  • 复制token,然后贴到Kubernetes dashboard。注意这边一般来说要针对不同User开启特定存取权限。

cmd-markdown-logo

Ingress Controller

Ingress 是 Kubernetes 中的一个抽象资源,其功能是通过 Web Server 的 Virtual Host 概念以域名(Domain Name)方式转发到內部 Service,这避免了使用 Service 中的 NodePort 与 LoadBalancer 类型所带來的限制(如 Port 数量上限),而实现 Ingress 功能则是通过 Ingress Controller 来达成,它会负责监听 Kubernetes API 中的 Ingress 与 Service 资源物件,并在发生资源变化时,根据资源预期的结果来设置 Web Server。另外 Ingress Controller 有许多实现可以选择:

  • Ingress NGINX: Kubernetes 官方维护的方案,也是本次安装使用的 Controller。
  • F5 BIG-IP Controller: F5 所开发的 Controller,它能够让管理员通过 CLI 或 API 让 Kubernetes 与 OpenShift 管理 F5 BIG-IP 设备。
  • Ingress Kong: 著名的开源 API Gateway 方案所维护的 Kubernetes Ingress Controller。
  • Traefik: 是一套开源的 HTTP 反向代理与负载均衡器,而它也支援了 Ingress。
  • Voyager: 一套以 HAProxy 为底的 Ingress Controller。
  • 而 Ingress Controller 的实现不只上面这些方案,还有很多可以在网络上找到这里不一一列出来了

首先在k8s-m1执行下列命令来建立 Ingress Controller,并检查是否部署正常:

1
2
3
4
5
6
7
8
9
10
11
$ sed -ri 's#\{\{ INGRESS_VIP \}\}#'"${INGRESS_VIP}"'#' ExtraAddons/ingress-controller/ingress-controller-svc.yml
$ kubectl create ns ingress-nginx
$ kubectl apply -f ExtraAddons/ingress-controller/
$ kubectl -n ingress-nginx get po,svc
NAME READY STATUS RESTARTS AGE
pod/default-http-backend-846b65fb5f-l5hrc 1/1 Running 0 2m
pod/nginx-ingress-controller-5db8d65fb-z2lf9 1/1 Running 0 2m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/default-http-backend ClusterIP 10.99.105.112 <none> 80/TCP 2m
service/ingress-nginx LoadBalancer 10.106.18.106 192.168.126.109 80:31197/TCP 2m

完成后使用curl发起http请求访问http://{your ingress vip}}:80来查看是否能连接,若可以会有以下结果。

1
2
$ curl ${INGRESS_VIP}:80
default backend - 404

确认上面步骤都沒问题后,就可以通过 kubeclt 建立简单 NGINX 来测试功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ kubectl apply -f apps/nginx/
deployment.extensions/nginx created
ingress.extensions/nginx-ingress created
service/nginx created

$ kubectl get po,svc,ing
NAME READY STATUS RESTARTS AGE
pod/nginx-966857787-78kth 1/1 Running 0 32s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d
service/nginx ClusterIP 10.104.180.119 <none> 80/TCP 32s

NAME HOSTS ADDRESS PORTS AGE
ingress.extensions/nginx-ingress nginx.k8s.local 192.168.126.109 80 33s
  • P.S. Ingress 规则也支持不同 Path 的服务转发,可以参考上面提供的官方文件来设置。

完成后通过 curl 命令来测试功能是否正常:

1
2
3
4
5
6
7
8
9
10
$ curl 192.168.126.109 -H 'Host: nginx.k8s.local'
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

# 测试其他 domain name 是否会回返回 404
$ curl 192.168.126.109 -H 'Host: nginx1.k8s.local'
default backend - 404

虽然 Ingress 能够让我們通过域名方式访问 Kubernetes 內部服务,但是若域名无法被用户解析的话,将会显示default backend - 404结果,而这经常发送在內部自建环境上,虽然可以通过修改主机/etc/hosts来解决,但不能弹性扩展,因此下节将说明如何建立一个 External DNS 与 DNS 服务器来提供自动解析 Ingress 域名。

External DNS

本部分说明如何通过 CoreDNS 自建一套DNS 服务,并利用Kubernetes ExternalDNS 同步Kubernetes 的Ingress 与Service API object 中的域名(Domain Name)来产生资源纪录(Record Resources),让使用者能够通过自建DNS 服务来导向到Kubernetes 集群里的服务

External DNS 是 Kubernetes 社区的孵化方案,被用于定期同步 Kubernetes Service 与 Ingress 资源,并依据资源內容来自动设定公有云 DNS 服务的资源记录(Record resources)。而由于部署不是公有云环境,因此需要通过 CoreDNS 提供一個內部 DNS 服务器,再由 ExternalDNS 与这个 CoreDNS 做串接。

  • CoreDNS:用来提供使用者的 DNS 解析以处理服务导向,并利用 Etcd 插件来存储与查询 DNS 资源记录(Record resources)。CoreDNS 是由 CNCF 维护的开源 DNS 方案,该方案前身是 SkyDNS,其采用了 Caddy 的一部分来开发私有服务器框架,使其能够构建一套快速灵活的 DNS,而 CoreDNS 每个功能都可以被当作成一个插件的中介软体,如 Log、Cache 等功能,甚至能夠将资源存储存至 Redis、Etcd 中。另外 CoreDNS 目前也被 Kubernetes 作为一个內部服务查询的核心元件,并慢慢取代 KubeDNS 来提供使用。
  • 由于市面上大多以 Bind9 作为 DNS,但是 Bind9 并不支持插件与 REST API 功能,虽然效率高又稳定,但是在一些场景並不灵活。
  • Etcd:用来储存 CoreDNS 资源纪录,并提供给整合的元件查询与储存使用。 Etcd 是一套分散式键值(Key/Value)储存系统,其功能类似ZooKeeper,而Etcd 在一致性演算法采用了Raft 来处理多节点高可靠性问题,Etcd 好处是支援了REST API、JSON 格式、SSL 与高效能等,而目前Etcd 被应用在Kubernetes 与Cloud Foundry 等方案中。

  • ExternalDNS:用于定期同步Kubernetes Service 与Ingress 资源,并根据Kubernetes 资源内容产生DNS 资源纪录来设定CoreDNS,架构中采用Etcd 作为两者沟通中介,一旦有资源纪录产生就储存至Etcd 中,以提供给CoreDNS作为资源纪录来确保服务辨识导向。 ExternalDNS 是 Kubernetes 社区的专案,目前被用于同步 Kubernetes 自动设定公有云 DNS 服务的资源纪录。

  • Ingress Controller:提供 Kubernetes Service 能够以 Domain Name 方式提供外部的存取。 Ingress Controller 会监听Kubernetes API Server 的Ingress 与Service 抽象资源,并依据对应资讯产生配置文件来设置到一个以NGINX 为引擎的后端,当使用者存取对应服务时,会通过NGINX 后端进入,这时会依据设定档的Domain Name 来转送给对应Kubernetes Service。

  • Kubernetes API Server:ExternalDNS 会定期抓取来自 API Server 的 Ingress 与 Service 抽象资源,并根据资源內容产生资源记录。

首先当使用者建立了一个Kubernetes Service 或Ingress(实作以同步Ingress 为主)时,会通过与API Server 与Kubernetes 集群沟通,一旦Service 或Ingress 建立完成,并正确分配Service external IP 或是Ingress address 后,ExternalDNS 会在同步期间抓取所有Namespace(或指定)中的Service 与Ingress 资源,并从Service 的metadata.annotations取出external-dns.alpha.kubernetes.io/hostname键的值,以及从Ingress 中的spec.rules取出host 值来产生DNS 资源纪录(如A record),当完成产生资源纪录后,再通过Etcd 储存该纪录来让CoreDNS 在收到查询请求时,能够依据Etcd 的纪录来辨识导向

t4 拆解不同流程步驟如下:
  1. 使用者建立一个带有 annotations 的 Service 或是 Ingress。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    apiVersion: v1
    kind: Service
    metadata:
    name: nginx
    annotations:
    external-dns.alpha.kubernetes.io/hostname: nginx.k8s.local # 将被自动注册 domain name.
    spec:
    type: NodePort
    selector:
    app: nginx
    ports:
    - protocol: TCP
    port: 80
    targetPort: 80
    ---
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
    name: nginx-ingress
    spec:
    rules:
    - host: nginx.k8s.local # 将被自动注册的 domain name.
    http:
    paths:
    - backend:
    serviceName: nginx
    servicePort: 80
  • 该示例中,若使用 Ingress 的话则不需要在 Service 塞入external-dns.alpha.kubernetes.io/hostname,且不需要使用 NodePort 与 LoadBalancer。
  1. ExternalDNS 接收到 Service 与 Ingress 抽象资源,取出将被用来注册 Domain Name 的资讯,并依据上述信息产生 DNS 资源纪录(Record resources),然后储存到 Etcd。

  2. 当使用者访问 nginx.k8s.local 时,将对 CoreDNS 提供的 DNS 服务器发送查询请求,这时 CoreDNS 会到 Etcd 找寻资源纪录来进行辨识重导向功能,若找到资源纪录回复解析结果给使用者。

  3. 这时使用者正确地被导向地址。其中若使用 Service 则要额外输入对应 Port,用 Ingress 则能够透过 DN 存取到服务,这是由于 Ingress controller 提供了一个 NGINX Proxy 后端来转至对应的内部服务。

首先在k8s-m1执行下面命令来建立 CoreDNS Server,并检查是否部署正常:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sed -ri 's#\{\{ INGRESS_VIP \}\}#'"${INGRESS_VIP}"'#' ExtraAddons/external-dns/coredns/coredns-svc-tcp.yml
$ sed -ri 's#\{\{ INGRESS_VIP \}\}#'"${INGRESS_VIP}"'#' ExtraAddons/external-dns/coredns/coredns-svc-udp.yml
$ kubectl create ns external-dns
$ kubectl create -f ExtraAddons/external-dns/coredns/
$ kubectl -n external-dns get po,svc
NAME READY STATUS RESTARTS AGE
pod/coredns-54bcfcbd5b-5grb5 1/1 Running 0 2m
pod/coredns-etcd-6c9c68fd76-n8rhj 1/1 Running 0 2m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/coredns-etcd ClusterIP 10.110.186.83 <none> 2379/TCP,2380/TCP 2m
service/coredns-tcp LoadBalancer 10.109.105.166 192.168.126.109 53:32169/TCP,9153:32150/TCP 2m
service/coredns-udp LoadBalancer 10.110.242.185 192.168.126.109 53:31210/UDP
  • 这边域名为k8s.local,可以修改文件中的coredns-cm.yml來改变。

完成后,通过 dig 工具来检查 DNS 是否正常:

1
2
3
4
5
$ dig @192.168.126.109 SOA nginx.k8s.local +noall +answer +time=2 +tries=1
...
; (1 server found)
;; global options: +cmd
k8s.local. 300 IN SOA ns.dns.k8s.local. hostmaster.k8s.local. 1531299150 7200 1800 86400 30

接着部署 ExternalDNS 来与 CoreDNS 同步资源记录:

1
2
3
4
$ kubectl apply -f ExtraAddons/external-dns/external-dns/
$ kubectl -n external-dns get po -l k8s-app=external-dns
NAME READY STATUS RESTARTS AGE
external-dns-86f67f6df8-ljnhj 1/1 Running 0 1m

完成后,通过 dig 与 nslookup 工具检查前面测试 Ingress 的 nginx 服务:

1
2
3
4
5
6
7
8
9
10
11
$ dig @192.168.126.109 A nginx.k8s.local +noall +answer +time=2 +tries=1
...
; (1 server found)
;; global options: +cmd
nginx.k8s.local. 300 IN A 192.168.126.109

$ nslookup nginx.k8s.local
Server: 192.168.126.109
Address: 192.168.126.109#53

** server can't find nginx.k8s.local: NXDOMAIN

这时会无法通过 nslookup 解析域名,这是因为测试机器并没有使用这个 DNS 服务器,可以通过修改/etc/resolv.conf来加入(不同 OS 有差异,不过都可以设置)。

设置后再次通过 nslookup 检查,会发现可以解析了,这时也就能通过 curl 来测试结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ nslookup nginx.k8s.local
Server: 192.168.126.109
Address: 192.168.126.109#53

Name: nginx.k8s.local
Address: 192.168.126.109

$ nslookup www.baidu.com
Server: 192.168.126.110
Address: 192.168.126.110#53

Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com.
Name: www.a.shifen.com
Address: 61.135.169.125
Name: www.a.shifen.com
Address: 61.135.169.121

$ curl nginx.k8s.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

Prometheus

由于 Heapster 将要被移除,因此这里选用 Prometheus 作为第三方的集群监控方案。而本次安装采用 CoreOS 开发的 Prometheus Operator 用来管理在 Kubernetes 上的 Prometheus 集群资源,更多关于 Prometheus Operator 的信息可以參考 Prometheus Operator 介绍与安装文章。

k8s-m1通过kubectl来建立Prometheus 需要的元件:

1
2
3
4
5
6
7
8
9
10
11
$ kubectl apply -f ExtraAddons/prometheus/
$ kubectl apply -f ExtraAddons/prometheus/operator/

# 这边要等 operator 起來并建立好 CRDs 才能进行
$ kubectl apply -f ExtraAddons/prometheus/alertmanater/
$ kubectl apply -f ExtraAddons/prometheus/node-exporter/
$ kubectl apply -f ExtraAddons/prometheus/kube-state-metrics/
$ kubectl apply -f ExtraAddons/prometheus/grafana/
$ kubectl apply -f ExtraAddons/prometheus/kube-service-discovery/
$ kubectl apply -f ExtraAddons/prometheus/prometheus/
$ kubectl apply -f ExtraAddons/prometheus/servicemonitor/

完成后,通过 kubectl 检查服务是否正常运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ kubectl -n monitoring get po,svc
NAME READY STATUS RESTARTS AGE
pod/alertmanager-main-0 1/2 Running 0 1m
pod/grafana-6d495c46d5-jpf6r 1/1 Running 0 43s
pod/kube-state-metrics-b84cfb86-4b8qg 4/4 Running 0 37s
pod/node-exporter-2f4lh 2/2 Running 0 59s
pod/node-exporter-7cz5s 2/2 Running 0 59s
pod/node-exporter-djdtk 2/2 Running 0 59s
pod/node-exporter-kfpzt 2/2 Running 0 59s
pod/node-exporter-qp2jf 2/2 Running 0 59s
pod/prometheus-k8s-0 3/3 Running 0 28s
pod/prometheus-k8s-1 3/3 Running 0 15s
pod/prometheus-operator-9ffd6bdd9-rvqsz 1/1 Running 0 1m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/alertmanager-main ClusterIP 10.110.188.2 <none> 9093/TCP 1m
service/alertmanager-operated ClusterIP None <none> 9093/TCP,6783/TCP 1m
service/grafana ClusterIP 10.104.147.154 <none> 3000/TCP 43s
service/kube-state-metrics ClusterIP None <none> 8443/TCP,9443/TCP 51s
service/node-exporter ClusterIP None <none> 9100/TCP 1m
service/prometheus-k8s ClusterIP 10.96.78.58 <none> 9090/TCP 28s
service/prometheus-operated ClusterIP None <none> 9090/TCP 33s
service/prometheus-operator ClusterIP 10.99.251.16 <none> 8080/TCP

NAME HOSTS ADDRESS PORTS AGE
ingress.extensions/grafana-ing grafana.monitoring.k8s.local 192.168.126.109 80 45s
ingress.extensions/prometheus-ing prometheus.monitoring.k8s.local 192.168.126.109 80 34s

确认没问题后,通过浏览器查看 prometheus.monitoring.k8s.localgrafana.monitoring.k8s.local 是否正常,若沒问题就可以看到下图结果。
prometheus
grafana

  • 另外这边也推荐用 Weave Scope 来监控容器的网络 Flow 拓扑图。

Helm Tiller Server(只有ubuntu的安装)

Helm是Kubernetes Chart的管理工具,Kubernetes Chart是一套预先组态的Kubernetes资源套件。其中Tiller Server主要负责接收来至Client的指令,并通过kube-apiserver与Kubernetes集群做沟通,根据Chart定义的内容,来产生与管理各种对应API物件的Kubernetes部署文件(又称为Release)。

首先在k8s-m1安装Helm tool:

1
2
$ wget -qO- https://kubernetes-helm.storage.googleapis.com/helm-v2.9.1-linux-amd64.tar.gz | tar -zx
$ sudo mv linux-amd64/helm /usr/local/bin/

另外在所有node机器安裝 socat:

1
$ sudo apt-get install -y socat

接着初始化 Helm(这边会安装 Tiller Server):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ kubectl -n kube-system create sa tiller
$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
$ helm init --service-account tiller
...
Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.
Happy Helming!

$ kubectl -n kube-system get po -l app=helm
NAME READY STATUS RESTARTS AGE
tiller-deploy-5f789bd9f7-tzss6 1/1 Running 0 29s

$ helm version
Client: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}

测试Helm 功能
这边部署简单Jenkins 来进行功能测试:

1
2
3
4
5
6
7
8
9
10
11
12
$ helm install --name demo --set Persistence.Enabled=false stable/jenkins
$ kubectl get po,svc -l app=demo-jenkins
NAME READY STATUS RESTARTS AGE
demo-jenkins-7bf4bfcff-q74nt 1/1 Running 0 2m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
demo-jenkins LoadBalancer 10.103.15.129 <pending> 8080:31161/TCP 2m
demo-jenkins-agent ClusterIP 10.103.160.126 <none> 50000/TCP 2m

# 取得 admin 账号的密码
$ printf $(kubectl get secret --namespace default demo-jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
r6y9FMuF2u

完成后,就可以通过浏览器存取Jenkins Web http://192.168.126.110:31161

cmd-markdown-logo
测试完成后,即可删除:

1
2
3
4
5
6
$ helm ls
NAME REVISION UPDATED STATUS CHART NAMESPACE
demo 1 Tue Apr 10 07:29:51 2018 DEPLOYED jenkins-0.14.4 default

$ helm delete demo --purge
release "demo" deleted

更多Helm Apps可以到Kubeapps Hub寻找。

测试集群高可用

SSH进入k8s-m1节点,然后关闭该节点:

1
$ sudo poweroff

接着进入到k8s-m2节点,通过kubectl来检查集群是否能够正常执行:

1
2
3
4
5
6
7
8
9
10
# 先检查 etcd 状态,可以发现 etcd-0 因為关机而中断
$ kubectl get cs
NAME STATUS MESSAGE ERROR
scheduler Healthy ok
controller-manager Healthy ok
etcd-1 Healthy {"health": "true"}
etcd-2 Healthy {"health": "true"}
etcd-0 Unhealthy Get https://192.168.126.111:2379/health: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)


CATALOG
  1. 1. 事前准备
  2. 2. 建立集群CA keys 与Certificates
    1. 2.1. 根据自己环境声明用到的变量(主要是ip,路径最好别改)
    2. 2.2. Etcd CA
    3. 2.3. Kubernetes CA
    4. 2.4. API Server Certificate
    5. 2.5. Front Proxy Certificate
    6. 2.6. Controller Manager Certificate
    7. 2.7. Scheduler Certificate
    8. 2.8. Admin Certificate
    9. 2.9. Master Kubelet Certificate
    10. 2.10. Service Account Key
    11. 2.11. 复制文件至其他节点
  3. 3. Kubernetes Masters
    1. 3.1. 部署与设定
    2. 3.2. 建立TLS Bootstrapping RBAC 与Secret
    3. 3.3. 验证集群
  4. 4. Kubernetes Core Addons部署
    1. 4.1. Kubernetes Proxy
    2. 4.2. CoreDNS
  5. 5. 集群网络
  6. 6. 网络部署与设定(flannel或者calico)
    1. 6.1. flannel
    2. 6.2. Calico
    3. 6.3. Metrics Server
    4. 6.4. 测试是否可以建立 Pod(到此步集群即可使用)
  7. 7. Kubernets Extra Addons部署
    1. 7.1. Dashboard
    2. 7.2. Ingress Controller
    3. 7.3. External DNS
    4. 7.4. Prometheus
    5. 7.5. Helm Tiller Server(只有ubuntu的安装)
  8. 8. 测试集群高可用