轉自 楊明的博客:https://blog.qikqiak.com/post/manual-install-high-available-kubernetes-cluster/
之前按照和我一步步部署 kubernetes 集羣的步驟一步一步的成功的使用二進制的方式安裝了kubernetes
集羣,在該文檔的基礎上重新部署了最新的v1.8.2
版本,實現了kube-apiserver
的高可用、traefik ingress
的部署、在kubernetes
上安裝docker
的私有倉庫harbor
、容器化kubernetes
部分組建、使用阿里雲日誌服務收集日誌。
部署完成後,你將理解系統各組件的交互原理,進而能快速解決實際問題,所以本文檔主要適合於那些有一定kubernetes
基礎,想通過一步步部署的方式來學習和了解系統配置、運行原理的人。
本系列系文檔適用於 CentOS 7
、Ubuntu 16.04
及以上版本系統,由於啓用了 TLS
雙向認證、RBAC
授權等嚴格的安全機制,建議從頭開始部署,否則可能會認證、授權等失敗!
有人問我爲什麼這麼長的文章不分拆成幾篇文章啊?這樣閱讀起來也方便啊,然而在我自己學習的過程中,這種整個一篇文章把一件事情從頭到尾講清楚的形式是最好的,能給讀者提供一種
沉浸式
的學習體驗,閱讀完整個文章後有種酣暢淋漓
的感覺,所以我選擇這種一篇文章的形式。
另外我錄製了基於1.9版本手動搭建高可用Kubernetes集羣的視頻教程,對視頻感興趣的同學可以觀看視頻:
掃描下面的二維碼(或微信搜索k8s技術圈
)關注我們的微信公衆帳號,在微信公衆帳號中回覆 加羣 即可加入到我們的 kubernetes 討論羣裏面共同學習。
1. 組件版本 && 集羣環境
組件版本
Kubernetes 1.8.2(1.9.x版本也可以,只有細微的差別)
Docker 17.10.0-ce
Etcd 3.2.9
Flanneld
TLS 認證通信(所有組件,如etcd、kubernetes master 和node)
RBAC 授權
kubelet TLS Bootstrapping
kubedns、dashboard、heapster等插件
harbor,使用nfs後端存儲
etcd 集羣 && k8s master 機器 && k8s node 機器
master01:192.168.1.137
master02:192.168.1.138
master03/node03:192.168.1.170
由於機器有限,所以我們將master03 也作爲node 節點,後續有新的機器增加即可
node01: 192.168.1.161
node02: 192.168.1.162
集羣環境變量
後面的嗯部署將會使用到的全局變量,定義如下(根據自己的機器、網絡修改):
# TLS Bootstrapping 使用的Token,可以使用命令 head -c 16 /dev/urandom | od -An -t x | tr -d ' ' 生成BOOTSTRAP_TOKEN="8981b594122ebed7596f1d3b69c78223"# 建議使用未用的網段來定義服務網段和Pod 網段# 服務網段(Service CIDR),部署前路由不可達,部署後集羣內部使用IP:Port可達SERVICE_CIDR="10.254.0.0/16"# Pod 網段(Cluster CIDR),部署前路由不可達,部署後路由可達(flanneld 保證)CLUSTER_CIDR="172.30.0.0/16"# 服務端口範圍(NodePort Range)NODE_PORT_RANGE="30000-32766"# etcd集羣服務地址列表ETCD_ENDPOINTS="https://192.168.1.137:2379,https://192.168.1.138:2379,https://192.168.1.170:2379"# flanneld 網絡配置前綴FLANNEL_ETCD_PREFIX="/kubernetes/network"# kubernetes 服務IP(預先分配,一般爲SERVICE_CIDR中的第一個IP)CLUSTER_KUBERNETES_SVC_IP="10.254.0.1"# 集羣 DNS 服務IP(從SERVICE_CIDR 中預先分配)CLUSTER_DNS_SVC_IP="10.254.0.2"# 集羣 DNS 域名CLUSTER_DNS_DOMAIN="cluster.local."# MASTER API Server 地址MASTER_URL="k8s-api.virtual.local"
將上面變量保存爲: env.sh,然後將腳本拷貝到所有機器的/usr/k8s/bin
目錄。
爲方便後面遷移,我們在集羣內定義一個域名用於訪問apiserver
,在每個節點的/etc/hosts
文件中添加記錄:192.168.1.137 k8s-api.virtual.local k8s-api
其中192.168.1.137
爲master01 的IP,暫時使用該IP 來做apiserver 的負載地址
如果你使用的是阿里雲的ECS 服務,強烈建議你先將上述節點的安全組配置成允許所有訪問,不然在安裝過程中會遇到各種訪問不了的問題,待集羣配置成功以後再根據需要添加安全限制。
2. 創建CA 證書和密鑰
kubernetes
系統各個組件需要使用TLS
證書對通信進行加密,這裏我們使用CloudFlare
的PKI 工具集cfssl 來生成Certificate Authority(CA) 證書和密鑰文件, CA 是自簽名的證書,用來簽名後續創建的其他TLS 證書。
安裝 CFSSL
$ wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64$ chmod +x cfssl_linux-amd64$ sudo mv cfssl_linux-amd64 /usr/k8s/bin/cfssl$ wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64$ chmod +x cfssljson_linux-amd64$ sudo mv cfssljson_linux-amd64 /usr/k8s/bin/cfssljson$ wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64$ chmod +x cfssl-certinfo_linux-amd64$ sudo mv cfssl-certinfo_linux-amd64 /usr/k8s/bin/cfssl-certinfo$ export PATH=/usr/k8s/bin:$PATH$ mkdir ssl && cd ssl$ cfssl print-defaults config > config.json$ cfssl print-defaults csr > csr.json
爲了方便,將/usr/k8s/bin
設置成環境變量,爲了重啓也有效,可以將上面的export PATH=/usr/k8s/bin:$PATH
添加到/etc/rc.local
文件中。
創建CA
修改上面創建的config.json
文件爲ca-config.json
:
$ cat ca-config.json { "signing": { "default": { "expiry": "87600h" }, "profiles": { "kubernetes": { "expiry": "87600h", "usages": [ "signing", "key encipherment", "server auth", "client auth" ] } } } }
config.json
:可以定義多個profiles,分別指定不同的過期時間、使用場景等參數;後續在簽名證書時使用某個profile;signing
: 表示該證書可用於簽名其它證書;生成的ca.pem 證書中CA=TRUE
;server auth
: 表示client 可以用該CA 對server 提供的證書進行校驗;client auth
: 表示server 可以用該CA 對client 提供的證書進行驗證。
修改CA 證書籤名請求爲ca-csr.json
:
$ cat ca-csr.json { "CN": "kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "L": "BeiJing", "ST": "BeiJing", "O": "k8s", "OU": "System" } ] }
CN
:Common Name
,kube-apiserver 從證書中提取該字段作爲請求的用戶名(User Name);瀏覽器使用該字段驗證網站是否合法;O
:Organization
,kube-apiserver 從證書中提取該字段作爲請求用戶所屬的組(Group);
生成CA 證書和私鑰:
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca$ ls ca*$ ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem
分發證書
將生成的CA 證書、密鑰文件、配置文件拷貝到所有機器的/etc/kubernetes/ssl
目錄下面:
$ sudo mkdir -p /etc/kubernetes/ssl $ sudo cp ca* /etc/kubernetes/ssl
3. 部署高可用etcd 集羣
kubernetes 系統使用etcd
存儲所有的數據,我們這裏部署3個節點的etcd 集羣,這3個節點直接複用kubernetes master的3個節點,分別命名爲etcd01
、etcd02
、etcd03
:
etcd01:192.168.1.137
etcd02:192.168.1.138
etcd03:192.168.1.170
定義環境變量
使用到的變量如下:
$ export NODE_NAME=etcd01 # 當前部署的機器名稱(隨便定義,只要能區分不同機器即可)$ export NODE_IP=192.168.1.137 # 當前部署的機器IP$ export NODE_IPS="192.168.1.137 192.168.1.138 192.168.1.170" # etcd 集羣所有機器 IP$ # etcd 集羣間通信的IP和端口$ export ETCD_NODES=etcd01=https://192.168.1.137:2380,etcd02=https://192.168.1.138:2380,etcd03=https://192.168.1.170:2380$ # 導入用到的其它全局變量:ETCD_ENDPOINTS、FLANNEL_ETCD_PREFIX、CLUSTER_CIDR$ source /usr/k8s/bin/env.sh
下載etcd 二進制文件
到https://github.com/coreos/etcd/releases頁面下載最新版本的二進制文件:
$ wget https://github.com/coreos/etcd/releases/download/v3.2.9/etcd-v3.2.9-linux-amd64.tar.gz$ tar -xvf etcd-v3.2.9-linux-amd64.tar.gz$ sudo mv etcd-v3.2.9-linux-amd64/etcd* /usr/k8s/bin/
創建TLS 密鑰和證書
爲了保證通信安全,客戶端(如etcdctl)與etcd 集羣、etcd 集羣之間的通信需要使用TLS 加密。
創建etcd 證書籤名請求:
$ cat > etcd-csr.json <<EOF { "CN": "etcd", "hosts": [ "127.0.0.1", "${NODE_IP}" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ] } EOF
hosts
字段指定授權使用該證書的etcd
節點IP
生成etcd
證書和私鑰:
$ cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem \ -ca-key=/etc/kubernetes/ssl/ca-key.pem \ -config=/etc/kubernetes/ssl/ca-config.json \ -profile=kubernetes etcd-csr.json | cfssljson -bare etcd$ ls etcd* etcd.csr etcd-csr.json etcd-key.pem etcd.pem$ sudo mkdir -p /etc/etcd/ssl$ sudo mv etcd*.pem /etc/etcd/ssl/
創建etcd 的systemd unit 文件
$ sudo mkdir -p /var/lib/etcd # 必須要先創建工作目錄 $ cat > etcd.service <<EOF [Unit] Description=Etcd Server After=network.target After=network-online.target Wants=network-online.target Documentation=https://github.com/coreos[Service] Type=notify WorkingDirectory=/var/lib/etcd/ ExecStart=/usr/k8s/bin/etcd \\ --name=${NODE_NAME} \\ --cert-file=/etc/etcd/ssl/etcd.pem \\ --key-file=/etc/etcd/ssl/etcd-key.pem \\ --peer-cert-file=/etc/etcd/ssl/etcd.pem \\ --peer-key-file=/etc/etcd/ssl/etcd-key.pem \\ --trusted-ca-file=/etc/kubernetes/ssl/ca.pem \\ --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \\ --initial-advertise-peer-urls=https://${NODE_IP}:2380 \\ --listen-peer-urls=https://${NODE_IP}:2380 \\ --listen-client-urls=https://${NODE_IP}:2379,http://127.0.0.1:2379 \\ --advertise-client-urls=https://${NODE_IP}:2379 \\ --initial-cluster-token=etcd-cluster-0 \\ --initial-cluster=${ETCD_NODES} \\ --initial-cluster-state=new \\ --data-dir=/var/lib/etcd Restart=on-failure RestartSec=5LimitNOFILE=65536[Install] WantedBy=multi-user.target EOF
指定
etcd
的工作目錄和數據目錄爲/var/lib/etcd
,需要在啓動服務前創建這個目錄;爲了保證通信安全,需要指定etcd 的公私鑰(cert-file和key-file)、Peers通信的公私鑰和CA 證書(peer-cert-file、peer-key-file、peer-trusted-ca-file)、客戶端的CA 證書(trusted-ca-file);
--initial-cluster-state
值爲new
時,--name
的參數值必須位於--initial-cluster
列表中;
啓動etcd 服務
$ sudo mv etcd.service /etc/systemd/system/$ sudo systemctl daemon-reload$ sudo systemctl enable etcd$ sudo systemctl start etcd$ sudo systemctl status etcd
最先啓動的etcd 進程會卡住一段時間,等待其他節點啓動加入集羣,在所有的etcd 節點重複上面的步驟,直到所有的機器etcd 服務都已經啓動。
驗證服務
部署完etcd 集羣后,在任一etcd 節點上執行下面命令:
for ip in ${NODE_IPS}; do ETCDCTL_API=3 /usr/k8s/bin/etcdctl \ --endpoints=https://${ip}:2379 \ --cacert=/etc/kubernetes/ssl/ca.pem \ --cert=/etc/etcd/ssl/etcd.pem \ --key=/etc/etcd/ssl/etcd-key.pem \ endpoint health; done
輸出如下結果:
https://192.168.1.137:2379 is healthy: successfully committed proposal: took = 1.509032mshttps://192.168.1.138:2379 is healthy: successfully committed proposal: took = 1.639228mshttps://192.168.1.170:2379 is healthy: successfully committed proposal: took = 1.4152ms
可以看到上面的信息3個節點上的etcd 均爲healthy,則表示集羣服務正常。
4. 配置kubectl 命令行工具
kubectl
默認從~/.kube/config
配置文件中獲取訪問kube-apiserver 地址、證書、用戶名等信息,需要正確配置該文件才能正常使用kubectl
命令。
需要將下載的kubectl 二進制文件和生產的~/.kube/config
配置文件拷貝到需要使用kubectl 命令的機器上。
很多童鞋說這個地方不知道在哪個節點上執行,
kubectl
只是一個和kube-apiserver
進行交互的一個命令行工具,所以你想安裝到那個節點都想,master或者node任意節點都可以,比如你先在master節點上安裝,這樣你就可以在master節點使用kubectl
命令行工具了,如果你想在node節點上使用(當然安裝的過程肯定會用到的),你就把master上面的kubectl
二進制文件和~/.kube/config
文件拷貝到對應的node節點上就行了。
環境變量
$ source /usr/k8s/bin/env.sh $ export KUBE_APISERVER="https://${MASTER_URL}:6443"
注意這裏的
KUBE_APISERVER
地址,因爲我們還沒有安裝haproxy
,所以暫時需要手動指定使用apiserver
的6443端口,等haproxy
安裝完成後就可以用使用443端口轉發到6443端口去了。
變量KUBE_APISERVER 指定kubelet 訪問的kube-apiserver 的地址,後續被寫入
~/.kube/config
配置文件
下載kubectl
$ wget https://dl.k8s.io/v1.8.2/kubernetes-client-linux-amd64.tar.gz # 如果服務器上下載不下來,可以想辦法下載到本地,然後scp上去即可$ tar -xzvf kubernetes-client-linux-amd64.tar.gz$ sudo cp kubernetes/client/bin/kube* /usr/k8s/bin/$ sudo chmod a+x /usr/k8s/bin/kube*$ export PATH=/usr/k8s/bin:$PATH
創建admin 證書
kubectl 與kube-apiserver 的安全端口通信,需要爲安全通信提供TLS 證書和密鑰。創建admin 證書籤名請求:
$ cat > admin-csr.json <<EOF{ "CN": "admin", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "system:masters", "OU": "System" } ] }EOF
後續
kube-apiserver
使用RBAC 對客戶端(如kubelet、kube-proxy、Pod)請求進行授權kube-apiserver
預定義了一些RBAC 使用的RoleBindings,如cluster-admin 將Groupsystem:masters
與Rolecluster-admin
綁定,該Role 授予了調用kube-apiserver
所有API 的權限O 指定了該證書的Group 爲
system:masters
,kubectl使用該證書訪問kube-apiserver
時,由於證書被CA 簽名,所以認證通過,同時由於證書用戶組爲經過預授權的system:masters
,所以被授予訪問所有API 的勸降hosts 屬性值爲空列表
生成admin 證書和私鑰:
$ cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem \ -ca-key=/etc/kubernetes/ssl/ca-key.pem \ -config=/etc/kubernetes/ssl/ca-config.json \ -profile=kubernetes admin-csr.json | cfssljson -bare admin$ ls admin admin.csr admin-csr.json admin-key.pem admin.pem$ sudo mv admin*.pem /etc/kubernetes/ssl/
創建kubectl kubeconfig 文件
# 設置集羣參數$ kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/ssl/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER}# 設置客戶端認證參數$ kubectl config set-credentials admin \ --client-certificate=/etc/kubernetes/ssl/admin.pem \ --embed-certs=true \ --client-key=/etc/kubernetes/ssl/admin-key.pem \ --token=${BOOTSTRAP_TOKEN}# 設置上下文參數$ kubectl config set-context kubernetes \ --cluster=kubernetes \ --user=admin# 設置默認上下文$ kubectl config use-context kubernetes
admin.pem
證書O 字段值爲system:masters
,kube-apiserver
預定義的 RoleBindingcluster-admin
將 Groupsystem:masters
與 Rolecluster-admin
綁定,該 Role 授予了調用kube-apiserver
相關 API 的權限生成的kubeconfig 被保存到
~/.kube/config
文件
分發kubeconfig 文件
將~/.kube/config
文件拷貝到運行kubectl
命令的機器的~/.kube/
目錄下去。
5. 部署Flannel 網絡
kubernetes 要求集羣內各節點能通過Pod 網段互聯互通,下面我們來使用Flannel 在所有節點上創建互聯互通的Pod 網段的步驟。
需要在所有的Node節點安裝。
環境變量
$ export NODE_IP=192.168.1.137 # 當前部署節點的IP# 導入全局變量$ source /usr/k8s/bin/env.sh
創建TLS 密鑰和證書
etcd 集羣啓用了雙向TLS 認證,所以需要爲flanneld 指定與etcd 集羣通信的CA 和密鑰。
創建flanneld 證書籤名請求:
$ cat > flanneld-csr.json <<EOF{ "CN": "flanneld", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ] }EOF
生成flanneld 證書和私鑰:
$ cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem \ -ca-key=/etc/kubernetes/ssl/ca-key.pem \ -config=/etc/kubernetes/ssl/ca-config.json \ -profile=kubernetes flanneld-csr.json | cfssljson -bare flanneld$ ls flanneld* flanneld.csr flanneld-csr.json flanneld-key.pem flanneld.pem$ sudo mkdir -p /etc/flanneld/ssl$ sudo mv flanneld*.pem /etc/flanneld/ssl
向etcd 寫入集羣Pod 網段信息
該步驟只需在第一次部署Flannel 網絡時執行,後續在其他節點上部署Flanneld 時無需再寫入該信息
$ etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/ssl/ca.pem \ --cert-file=/etc/flanneld/ssl/flanneld.pem \ --key-file=/etc/flanneld/ssl/flanneld-key.pem \ set ${FLANNEL_ETCD_PREFIX}/config '{"Network":"'${CLUSTER_CIDR}'", "SubnetLen": 24, "Backend": {"Type": "vxlan"}}'# 得到如下反饋信息{"Network":"172.30.0.0/16", "SubnetLen": 24, "Backend": {"Type": "vxlan"}}
寫入的 Pod 網段(${CLUSTER_CIDR},172.30.0.0/16) 必須與
kube-controller-manager
的--cluster-cidr
選項值一致;
安裝和配置flanneld
前往flanneld release頁面下載最新版的flanneld 二進制文件:
$ mkdir flannel$ wget https://github.com/coreos/flannel/releases/download/v0.9.0/flannel-v0.9.0-linux-amd64.tar.gz$ tar -xzvf flannel-v0.9.0-linux-amd64.tar.gz -C flannel$ sudo cp flannel/{flanneld,mk-docker-opts.sh} /usr/k8s/bin
創建flanneld的systemd unit 文件
$ cat > flanneld.service << EOF [Unit]Description=Flanneld overlay address etcd agentAfter=network.targetAfter=network-online.targetWants=network-online.targetAfter=etcd.serviceBefore=docker.service [Service]Type=notifyExecStart=/usr/k8s/bin/flanneld \\ -etcd-cafile=/etc/kubernetes/ssl/ca.pem \\ -etcd-certfile=/etc/flanneld/ssl/flanneld.pem \\ -etcd-keyfile=/etc/flanneld/ssl/flanneld-key.pem \\ -etcd-endpoints=${ETCD_ENDPOINTS} \\ -etcd-prefix=${FLANNEL_ETCD_PREFIX}ExecStartPost=/usr/k8s/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/dockerRestart=on-failure [Install]WantedBy=multi-user.targetRequiredBy=docker.service EOF
mk-docker-opts.sh
腳本將分配給flanneld 的Pod 子網網段信息寫入到/run/flannel/docker
文件中,後續docker 啓動時使用這個文件中的參數值爲 docker0 網橋flanneld 使用系統缺省路由所在的接口和其他節點通信,對於有多個網絡接口的機器(內網和公網),可以用
--iface
選項值指定通信接口(上面的 systemd unit 文件沒指定這個選項)
啓動flanneld
$ sudo cp flanneld.service /etc/systemd/system/$ sudo systemctl daemon-reload$ sudo systemctl enable flanneld$ sudo systemctl start flanneld$ systemctl status flanneld
檢查flanneld 服務
ifconfig flannel.1
檢查分配給各flanneld 的Pod 網段信息
$ # 查看集羣 Pod 網段(/16)$ etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/ssl/ca.pem \ --cert-file=/etc/flanneld/ssl/flanneld.pem \ --key-file=/etc/flanneld/ssl/flanneld-key.pem \ get ${FLANNEL_ETCD_PREFIX}/config { "Network": "172.30.0.0/16", "SubnetLen": 24, "Backend": { "Type": "vxlan" } }$ # 查看已分配的 Pod 子網段列表(/24)$ etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/ssl/ca.pem \ --cert-file=/etc/flanneld/ssl/flanneld.pem \ --key-file=/etc/flanneld/ssl/flanneld-key.pem \ ls ${FLANNEL_ETCD_PREFIX}/subnets /kubernetes/network/subnets/172.30.77.0-24$ # 查看某一 Pod 網段對應的 flanneld 進程監聽的 IP 和網絡參數$ etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/ssl/ca.pem \ --cert-file=/etc/flanneld/ssl/flanneld.pem \ --key-file=/etc/flanneld/ssl/flanneld-key.pem \ get ${FLANNEL_ETCD_PREFIX}/subnets/172.30.77.0-24{"PublicIP":"192.168.1.137","BackendType":"vxlan","BackendData":{"VtepMAC":"62:fc:03:83:1b:2b"}}
確保各節點間Pod 網段能互聯互通
在各個節點部署完Flanneld 後,查看已分配的Pod 子網段列表:
$ etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/ssl/ca.pem \ --cert-file=/etc/flanneld/ssl/flanneld.pem \ --key-file=/etc/flanneld/ssl/flanneld-key.pem \ ls ${FLANNEL_ETCD_PREFIX}/subnets /kubernetes/network/subnets/172.30.19.0-24/kubernetes/network/subnets/172.30.30.0-24/kubernetes/network/subnets/172.30.77.0-24/kubernetes/network/subnets/172.30.41.0-24/kubernetes/network/subnets/172.30.83.0-24
當前五個節點分配的 Pod 網段分別是:172.30.77.0-24、172.30.30.0-24、172.30.19.0-24、172.30.41.0-24、172.30.83.0-24。
6. 部署master 節點
kubernetes master 節點包含的組件有:
kube-apiserver
kube-scheduler
kube-controller-manager
目前這3個組件需要部署到同一臺機器上:(後面再部署高可用的master)
kube-scheduler
、kube-controller-manager
和kube-apiserver
三者的功能緊密相關;同時只能有一個
kube-scheduler
、kube-controller-manager
進程處於工作狀態,如果運行多個,則需要通過選舉產生一個 leader;
master 節點與node 節點上的Pods 通過Pod 網絡通信,所以需要在master 節點上部署Flannel 網絡。
環境變量
$ export NODE_IP=192.168.1.137 # 當前部署的master 機器IP$ source /usr/k8s/bin/env.sh
下載最新版本的二進制文件
在kubernetes changelog 頁面下載最新版本的文件:
$ wget https://dl.k8s.io/v1.8.2/kubernetes-server-linux-amd64.tar.gz$ tar -xzvf kubernetes-server-linux-amd64.tar.gz
將二進制文件拷貝到/usr/k8s/bin
目錄
$ sudo cp -r server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler} /usr/k8s/bin/
創建kubernetes 證書
創建kubernetes 證書籤名請求:
$ cat > kubernetes-csr.json <<EOF { "CN": "kubernetes", "hosts": [ "127.0.0.1", "${NODE_IP}", "${MASTER_URL}", "${CLUSTER_KUBERNETES_SVC_IP}", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster", "kubernetes.default.svc.cluster.local" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ] } EOF
如果 hosts 字段不爲空則需要指定授權使用該證書的 IP 或域名列表,所以上面分別指定了當前部署的 master 節點主機 IP 以及apiserver 負載的內部域名
還需要添加 kube-apiserver 註冊的名爲
kubernetes
的服務 IP (Service Cluster IP),一般是 kube-apiserver--service-cluster-ip-range
選項值指定的網段的第一個IP,如 “10.254.0.1”
生成kubernetes 證書和私鑰:
$ cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem \ -ca-key=/etc/kubernetes/ssl/ca-key.pem \ -config=/etc/kubernetes/ssl/ca-config.json \ -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes$ ls kubernetes* kubernetes.csr kubernetes-csr.json kubernetes-key.pem kubernetes.pem$ sudo mkdir -p /etc/kubernetes/ssl/$ sudo mv kubernetes*.pem /etc/kubernetes/ssl/
6.1 配置和啓動kube-apiserver
創建kube-apiserver 使用的客戶端token 文件
kubelet 首次啓動時向kube-apiserver 發送TLS Bootstrapping 請求,kube-apiserver 驗證請求中的token 是否與它配置的token.csv 一致,如果一致則自動爲kubelet 生成證書和密鑰。
$ # 導入的 environment.sh 文件定義了 BOOTSTRAP_TOKEN 變量$ cat > token.csv <<EOF${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"EOF$ sudo mv token.csv /etc/kubernetes/
創建kube-apiserver 的systemd unit文件
$ cat > kube-apiserver.service <<EOF [Unit] Description=Kubernetes API Server Documentation=https://github.com/GoogleCloudPlatform/kubernetesAfter=network.target [Service] ExecStart=/usr/k8s/bin/kube-apiserver \\ --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\ --advertise-address=${NODE_IP} \\ --bind-address=0.0.0.0 \\ --insecure-bind-address=${NODE_IP} \\ --authorization-mode=Node,RBAC \\ --runtime-config=rbac.authorization.k8s.io/v1alpha1 \\ --kubelet-https=true \\ --experimental-bootstrap-token-auth \\ --token-auth-file=/etc/kubernetes/token.csv \\ --service-cluster-ip-range=${SERVICE_CIDR} \\ --service-node-port-range=${NODE_PORT_RANGE} \\ --tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem \\ --tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \\ --client-ca-file=/etc/kubernetes/ssl/ca.pem \\ --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem \\ --etcd-cafile=/etc/kubernetes/ssl/ca.pem \\ --etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem \\ --etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem \\ --etcd-servers=${ETCD_ENDPOINTS} \\ --enable-swagger-ui=true \\ --allow-privileged=true \\ --apiserver-count=2 \\ --audit-log-maxage=30 \\ --audit-log-maxbackup=3 \\ --audit-log-maxsize=100 \\ --audit-log-path=/var/lib/audit.log \\ --audit-policy-file=/etc/kubernetes/audit-policy.yaml \\ --event-ttl=1h \\ --logtostderr=true \\ --v=6Restart=on-failure RestartSec=5Type=notify LimitNOFILE=65536[Install] WantedBy=multi-user.target EOF
如果你安裝的是1.9.x版本的,一定要記住上面的參數
experimental-bootstrap-token-auth
,需要替換成enable-bootstrap-token-auth
,因爲這個參數在1.9.x裏面已經廢棄掉了kube-apiserver 1.6 版本開始使用 etcd v3 API 和存儲格式
--authorization-mode=RBAC
指定在安全端口使用RBAC 授權模式,拒絕未通過授權的請求kube-scheduler、kube-controller-manager 一般和 kube-apiserver 部署在同一臺機器上,它們使用非安全端口和 kube-apiserver通信
kubelet、kube-proxy、kubectl 部署在其它 Node 節點上,如果通過安全端口訪問 kube-apiserver,則必須先通過 TLS 證書認證,再通過 RBAC 授權
kube-proxy、kubectl 通過使用證書裏指定相關的 User、Group 來達到通過 RBAC 授權的目的
如果使用了 kubelet TLS Boostrap 機制,則不能再指定
--kubelet-certificate-authority
、--kubelet-client-certificate
和--kubelet-client-key
選項,否則後續 kube-apiserver 校驗 kubelet 證書時出現 ”x509: certificate signed by unknown authority“ 錯誤--admission-control
值必須包含ServiceAccount
,否則部署集羣插件時會失敗--bind-address
不能爲127.0.0.1
--service-cluster-ip-range
指定 Service Cluster IP 地址段,該地址段不能路由可達--service-node-port-range=${NODE_PORT_RANGE}
指定 NodePort 的端口範圍缺省情況下 kubernetes 對象保存在
etcd/registry
路徑下,可以通過--etcd-prefix
參數進行調整kube-apiserver 1.8版本後需要在
--authorization-mode
參數中添加Node
,即:--authorization-mode=Node,RBAC
,否則Node 節點無法註冊注意要開啓審查日誌功能,指定
--audit-log-path
參數是不夠的,這只是指定了日誌的路徑,還需要指定一個審查日誌策略文件:--audit-policy-file
,我們也可以使用日誌收集工具收集相關的日誌進行分析。
審查日誌策略文件內容如下:(/etc/kubernetes/audit-policy.yaml)
apiVersion: audit.k8s.io/v1beta1 # This is required.kind: Policy# Don't generate audit events for all requests in RequestReceived stage.omitStages: - "RequestReceived"rules: # Log pod changes at RequestResponse level - level: RequestResponse resources: - group: "" # Resource "pods" doesn't match requests to any subresource of pods, # which is consistent with the RBAC policy. resources: ["pods"] # Log "pods/log", "pods/status" at Metadata level - level: Metadata resources: - group: "" resources: ["pods/log", "pods/status"] # Don't log requests to a configmap called "controller-leader" - level: None resources: - group: "" resources: ["configmaps"] resourceNames: ["controller-leader"] # Don't log watch requests by the "system:kube-proxy" on endpoints or services - level: None users: ["system:kube-proxy"] verbs: ["watch"] resources: - group: "" # core API group resources: ["endpoints", "services"] # Don't log authenticated requests to certain non-resource URL paths. - level: None userGroups: ["system:authenticated"] nonResourceURLs: - "/api*" # Wildcard matching. - "/version" # Log the request body of configmap changes in kube-system. - level: Request resources: - group: "" # core API group resources: ["configmaps"] # This rule only applies to resources in the "kube-system" namespace. # The empty string "" can be used to select non-namespaced resources. namespaces: ["kube-system"] # Log configmap and secret changes in all other namespaces at the Metadata level. - level: Metadata resources: - group: "" # core API group resources: ["secrets", "configmaps"] # Log all other resources in core and extensions at the Request level. - level: Request resources: - group: "" # core API group - group: "extensions" # Version of group should NOT be included. # A catch-all rule to log all other requests at the Metadata level. - level: Metadata # Long-running requests like watches that fall under this rule will not # generate an audit event in RequestReceived. omitStages: - "RequestReceived"
審查日誌的相關配置可以查看文檔瞭解:https://kubernetes.io/docs/tasks/debug-application-cluster/audit/
啓動kube-apiserver
$ sudo cp kube-apiserver.service /etc/systemd/system/$ sudo systemctl daemon-reload$ sudo systemctl enable kube-apiserver$ sudo systemctl start kube-apiserver$ sudo systemctl status kube-apiserver
6.2 配置和啓動kube-controller-manager
創建kube-controller-manager 的systemd unit 文件
$ cat > kube-controller-manager.service <<EOF [Unit] Description=Kubernetes Controller Manager Documentation=https://github.com/GoogleCloudPlatform/kubernetes[Service] ExecStart=/usr/k8s/bin/kube-controller-manager \\ --address=127.0.0.1 \\ --master=http://${MASTER_URL}:8080 \\ --allocate-node-cidrs=true \\ --service-cluster-ip-range=${SERVICE_CIDR} \\ --cluster-cidr=${CLUSTER_CIDR} \\ --cluster-name=kubernetes \\ --cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem \\ --cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem \\ --service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem \\ --root-ca-file=/etc/kubernetes/ssl/ca.pem \\ --leader-elect=true \\ --v=2Restart=on-failure RestartSec=5[Install] WantedBy=multi-user.target EOF
--address
值必須爲127.0.0.1
,因爲當前 kube-apiserver 期望 scheduler 和 controller-manager 在同一臺機器--master=http://${MASTER_URL}:8080
:使用http
(非安全端口)與 kube-apiserver 通信,需要下面的haproxy
安裝成功後才能去掉8080端口。--cluster-cidr
指定 Cluster 中 Pod 的 CIDR 範圍,該網段在各 Node 間必須路由可達(flanneld保證)--service-cluster-ip-range
參數指定 Cluster 中 Service 的CIDR範圍,該網絡在各 Node 間必須路由不可達,必須和 kube-apiserver 中的參數一致--cluster-signing-*
指定的證書和私鑰文件用來簽名爲 TLS BootStrap 創建的證書和私鑰--root-ca-file
用來對 kube-apiserver 證書進行校驗,指定該參數後,纔會在Pod 容器的 ServiceAccount 中放置該 CA 證書文件--leader-elect=true
部署多臺機器組成的 master 集羣時選舉產生一處於工作狀態的kube-controller-manager
進程
啓動kube-controller-manager
$ sudo cp kube-controller-manager.service /etc/systemd/system/$ sudo systemctl daemon-reload$ sudo systemctl enable kube-controller-manager$ sudo systemctl start kube-controller-manager$ sudo systemctl status kube-controller-manager
6.3 配置和啓動kube-scheduler
創建kube-scheduler 的systemd unit文件
$ cat > kube-scheduler.service <<EOF [Unit]Description=Kubernetes SchedulerDocumentation=https://github.com/GoogleCloudPlatform/kubernetes [Service]ExecStart=/usr/k8s/bin/kube-scheduler \\ --address=127.0.0.1 \\ --master=http://${MASTER_URL}:8080 \\ --leader-elect=true \\ --v=2Restart=on-failureRestartSec=5 [Install]WantedBy=multi-user.target EOF
--address
值必須爲127.0.0.1
,因爲當前 kube-apiserver 期望 scheduler 和 controller-manager 在同一臺機器--master=http://${MASTER_URL}:8080
:使用http
(非安全端口)與 kube-apiserver 通信,需要下面的haproxy
啓動成功後才能去掉8080端口--leader-elect=true
部署多臺機器組成的 master 集羣時選舉產生一處於工作狀態的kube-controller-manager
進程
啓動kube-scheduler
$ sudo cp kube-scheduler.service /etc/systemd/system/$ sudo systemctl daemon-reload$ sudo systemctl enable kube-scheduler$ sudo systemctl start kube-scheduler$ sudo systemctl status kube-scheduler
6.4 驗證master 節點
$ kubectl get componentstatuses NAME STATUS MESSAGE ERROR scheduler Healthy ok controller-manager Healthy ok etcd-1 Healthy {"health": "true"} etcd-2 Healthy {"health": "true"} etcd-0 Healthy {"health": "true"}
7. kube-apiserver 高可用
按照上面的方式在master01
與master02
機器上安裝kube-apiserver
、kube-controller-manager
、kube-scheduler
,但是現在我們還是手動指定訪問的6443和8080端口的,因爲我們的域名k8s-api.virtual.local
對應的master01
節點直接通過http 和https 還不能訪問,這裏我們使用haproxy
來代替請求。
明白什麼意思嗎?就是我們需要將http默認的80端口請求轉發到
apiserver
的8080端口,將https默認的443端口請求轉發到apiserver
的6443端口,所以我們這裏使用haproxy
來做請求轉發。
安裝haproxy
$ yum install -y haproxy
配置haproxy
由於集羣內部有的組建是通過非安全端口訪問apiserver 的,有的是通過安全端口訪問apiserver 的,所以我們要配置http 和https 兩種代理方式,配置文件 /etc/haproxy/haproxy.cfg
:
listen stats bind *:9000 mode http stats enable stats hide-version stats uri /stats stats refresh 30s stats realm Haproxy\ Statistics stats auth Admin:Password frontend k8s-api bind 192.168.1.137:443 mode tcp option tcplog tcp-request inspect-delay 5s tcp-request content accept if { req.ssl_hello_type 1 } default_backend k8s-api backend k8s-api mode tcp option tcplog option tcp-check balance roundrobin default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100 server k8s-api-1 192.168.1.137:6443 check server k8s-api-2 192.168.1.138:6443 checkfrontend k8s-http-api bind 192.168.1.137:80 mode tcp option tcplog default_backend k8s-http-api backend k8s-http-api mode tcp option tcplog option tcp-check balance roundrobin default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100 server k8s-http-api-1 192.168.1.137:8080 check server k8s-http-api-2 192.168.1.138:8080 check
通過上面的配置文件我們可以看出通過https
的訪問將請求轉發給apiserver 的6443端口了,http的請求轉發到了apiserver 的8080端口。
啓動haproxy
$ sudo systemctl start haproxy$ sudo systemctl enable haproxy$ sudo systemctl status haproxy
然後我們可以通過上面9000
端口監控我們的haproxy
的運行狀態(192.168.1.137:9000/stats
):
問題
上面我們的haproxy
的確可以代理我們的兩個master 上的apiserver 了,但是還不是高可用的,如果master01 這個節點down 掉了,那麼我們haproxy 就不能正常提供服務了。這裏我們可以使用兩種方法來實現高可用
方式1:使用阿里雲SLB
這種方式實際上是最省心的,在阿里雲上建一個內網的SLB,將master01 與master02 添加到SLB 機器組中,轉發80(http)和443(https)端口即可(注意下面的提示)
注意:阿里雲的負載均衡是四層TCP負責,不支持後端ECS實例既作爲Real Server又作爲客戶端向所在的負載均衡實例發送請求。因爲返回的數據包只在雲服務器內部轉發,不經過負載均衡,所以在後端ECS實例上去訪問負載均衡的服務地址是不通的。什麼意思?就是如果你要使用阿里雲的SLB的話,那麼你不能在
apiserver
節點上使用SLB(比如在apiserver 上安裝kubectl,然後將apiserver的地址設置爲SLB的負載地址使用),因爲這樣的話就可能造成迴環了,所以簡單的做法是另外用兩個新的節點做HA
實例,然後將這兩個實例添加到SLB
機器組中。
方式2:使用keepalived
KeepAlived
是一個高可用方案,通過 VIP(即虛擬 IP)和心跳檢測來實現高可用。其原理是存在一組(兩臺)服務器,分別賦予 Master、Backup 兩個角色,默認情況下Master 會綁定VIP 到自己的網卡上,對外提供服務。Master、Backup 會在一定的時間間隔向對方發送心跳數據包來檢測對方的狀態,這個時間間隔一般爲 2 秒鐘,如果Backup 發現Master 宕機,那麼Backup 會發送ARP 包到網關,把VIP 綁定到自己的網卡,此時Backup 對外提供服務,實現自動化的故障轉移,當Master 恢復的時候會重新接管服務。非常類似於路由器中的虛擬路由器冗餘協議(VRRP)
開啓路由轉發,這裏我們定義虛擬IP爲:192.168.1.139
$ vi /etc/sysctl.conf# 添加以下內容net.ipv4.ip_forward = 1net.ipv4.ip_nonlocal_bind = 1# 驗證並生效$ sysctl -p# 驗證是否生效$ cat /proc/sys/net/ipv4/ip_forward1
安裝keepalived
:
$ yum install -y keepalived
我們這裏將master01 設置爲Master,master02 設置爲Backup,修改配置:
$ vi /etc/keepalived/keepalived.conf ! Configuration File for keepalived global_defs { notification_email { } router_id kube_api } vrrp_script check_haproxy { # 自身狀態檢測 script "killall -0 haproxy" interval 3 weight 5} vrrp_instance haproxy-vip { # 使用單播通信,默認是組播通信 unicast_src_ip 192.168.1.137 unicast_peer { 192.168.1.138 } # 初始化狀態 state MASTER # 虛擬ip 綁定的網卡 (這裏根據你自己的實際情況選擇網卡) interface eth0 # 此ID 要與Backup 配置一致 virtual_router_id 51 # 默認啓動優先級,要比Backup 大點,但要控制量,保證自身狀態檢測生效 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { # 虛擬ip 地址 192.168.1.139 } track_script { check_k8s } } virtual_server 192.168.1.139 80 { delay_loop 5 lvs_sched wlc lvs_method NAT persistence_timeout 1800 protocol TCP real_server 192.168.1.137 80 { weight 1 TCP_CHECK { connect_port 80 connect_timeout 3 } } } virtual_server 192.168.1.139 443 { delay_loop 5 lvs_sched wlc lvs_method NAT persistence_timeout 1800 protocol TCP real_server 192.168.1.137 443 { weight 1 TCP_CHECK { connect_port 443 connect_timeout 3 } } }
統一的方式在master02 節點上安裝keepalived,修改配置,只需要將state 更改成BACKUP,priority更改成99,unicast_src_ip 與unicast_peer 地址修改即可。
啓動keepalived:
$ systemctl start keepalived$ systemctl enable keepalived# 查看日誌$ journalctl -f -u keepalived
驗證虛擬IP:
# 使用ifconfig -a 命令查看不到,要使用ip addr$ ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 00:16:3e:00:55:c1 brd ff:ff:ff:ff:ff:ff inet 192.168.1.137/24 brd 192.168.1.255 scope global dynamic eth0 valid_lft 31447746sec preferred_lft 31447746sec inet 192.168.1.139/24 brd 192.168.1.255 scope global secondary eth0-vip valid_lft forever preferred_lft forever
到這裏,我們就可以將上面的6443端口和8080端口去掉了,可以手動將
kubectl
生成的config
文件(~/.kube/config
)中的server 地址6443端口去掉,另外kube-controller-manager
和kube-scheduler
的–master參數中的8080端口去掉了,然後分別重啓這兩個組件即可。
驗證apiserver:關閉master01 節點上的kube-apiserver 進程,然後查看虛擬ip是否漂移到了master02 節點。
然後我們就可以將第一步在/etc/hosts
裏面設置的域名對應的IP 更改爲我們的虛擬IP了
master01 與master 02 節點都需要安裝keepalived 和haproxy,實際上我們虛擬IP的自身檢測應該是檢測haproxy,腳本大家可以自行更改
這樣我們就實現了接入層apiserver 的高可用了,一個部分是多活的apiserver 服務,另一個部分是一主一備的haproxy 服務。
kube-controller-manager 和kube-scheduler 的高可用
Kubernetes 的管理層服務包括kube-scheduler
和kube-controller-manager
。kube-scheduler和kube-controller-manager使用一主多從的高可用方案,在同一時刻只允許一個服務處以具體的任務。Kubernetes中實現了一套簡單的選主邏輯,依賴Etcd實現scheduler和controller-manager的選主功能。如果scheduler和controller-manager在啓動的時候設置了leader-elect
參數,它們在啓動後會先嚐試獲取leader節點身份,只有在獲取leader節點身份後纔可以執行具體的業務邏輯。它們分別會在Etcd中創建kube-scheduler和kube-controller-manager的endpoint,endpoint的信息中記錄了當前的leader節點信息,以及記錄的上次更新時間。leader節點會定期更新endpoint的信息,維護自己的leader身份。每個從節點的服務都會定期檢查endpoint的信息,如果endpoint的信息在時間範圍內沒有更新,它們會嘗試更新自己爲leader節點。scheduler服務以及controller-manager服務之間不會進行通信,利用Etcd的強一致性,能夠保證在分佈式高併發情況下leader節點的全局唯一性。整體方案如下圖所示:
當集羣中的leader節點服務異常後,其它節點的服務會嘗試更新自身爲leader節點,當有多個節點同時更新endpoint時,由Etcd保證只有一個服務的更新請求能夠成功。通過這種機制sheduler和controller-manager可以保證在leader節點宕機後其它的節點可以順利選主,保證服務故障後快速恢復。當集羣中的網絡出現故障時對服務的選主影響不是很大,因爲scheduler和controller-manager是依賴Etcd進行選主的,在網絡故障後,可以和Etcd通信的主機依然可以按照之前的邏輯進行選主,就算集羣被切分,Etcd也可以保證同一時刻只有一個節點的服務處於leader狀態。
8. 部署Node 節點
kubernetes Node 節點包含如下組件:
flanneld
docker
kubelet
kube-proxy
環境變量
$ source /usr/k8s/bin/env.sh $ export KUBE_APISERVER="https://${MASTER_URL}" // 如果你沒有安裝`haproxy`的話,還是需要使用6443端口的哦 $ export NODE_IP=192.168.1.170 # 當前部署的節點 IP
按照上面的步驟安裝配置好flanneld
開啓路由轉發
修改/etc/sysctl.conf
文件,添加下面的規則:
net.ipv4.ip_forward=1 net.bridge.bridge-nf-call-iptables=1net.bridge.bridge-nf-call-ip6tables=1
執行下面的命令立即生效:
$ sysctl -p
配置docker
你可以用二進制或yum install 的方式來安裝docker,然後修改docker 的systemd unit 文件:
$ cat /usr/lib/systemd/system/docker.service # 用systemctl status docker 命令可查看unit 文件路徑[Unit]Description=Docker Application Container EngineDocumentation=https://docs.docker.comAfter=network-online.target firewalld.serviceWants=network-online.target [Service]Type=notify# the default is not to use systemd for cgroups because the delegate issues still# exists and systemd currently does not support the cgroup feature set required# for containers run by dockerEnvironmentFile=-/run/flannel/dockerExecStart=/usr/bin/dockerd --log-level=info $DOCKER_NETWORK_OPTIONSExecReload=/bin/kill -s HUP $MAINPID# Having non-zero Limit*s causes performance problems due to accounting overhead# in the kernel. We recommend using cgroups to do container-local accounting.LimitNOFILE=infinityLimitNPROC=infinityLimitCORE=infinity# Uncomment TasksMax if your systemd version supports it.# Only systemd 226 and above support this version.#TasksMax=infinityTimeoutStartSec=0# set delegate yes so that systemd does not reset the cgroups of docker containersDelegate=yes# kill only the docker process, not all processes in the cgroupKillMode=process# restart the docker process if it exits prematurelyRestart=on-failureStartLimitBurst=3StartLimitInterval=60s [Install]WantedBy=multi-user.target
dockerd 運行時會調用其它 docker 命令,如 docker-proxy,所以需要將 docker 命令所在的目錄加到 PATH 環境變量中
flanneld 啓動時將網絡配置寫入到
/run/flannel/docker
文件中的變量DOCKER_NETWORK_OPTIONS
,dockerd 命令行上指定該變量值來設置 docker0 網橋參數如果指定了多個
EnvironmentFile
選項,則必須將/run/flannel/docker
放在最後(確保 docker0 使用 flanneld 生成的 bip 參數)不能關閉默認開啓的
--iptables
和--ip-masq
選項如果內核版本比較新,建議使用
overlay
存儲驅動docker 從 1.13 版本開始,可能將 iptables FORWARD chain的默認策略設置爲DROP,從而導致 ping 其它 Node 上的 Pod IP 失敗,遇到這種情況時,需要手動設置策略爲
ACCEPT
:
$ sudo iptables -P FORWARD ACCEPT
如果沒有開啓上面的路由轉發(net.ipv4.ip_forward=1
),則需要把以下命令寫入/etc/rc.local
文件中,防止節點重啓iptables FORWARD chain的默認策略又還原爲DROP(下面的開機腳本我測試了幾次都沒生效,不知道是不是方法有誤,所以最好的方式還是開啓上面的路由轉發功能,一勞永逸)
sleep 60 && /sbin/iptables -P FORWARD ACCEPT
爲了加快 pull image 的速度,可以使用國內的倉庫鏡像服務器,同時增加下載的併發數。(如果 dockerd 已經運行,則需要重啓 dockerd 生效。)
$ cat /etc/docker/daemon.json { "max-concurrent-downloads": 10 }
啓動docker
$ sudo systemctl daemon-reload$ sudo systemctl stop firewalld$ sudo systemctl disable firewalld$ sudo iptables -F && sudo iptables -X && sudo iptables -F -t nat && sudo iptables -X -t nat$ sudo systemctl enable docker$ sudo systemctl start docker
需要關閉 firewalld(centos7)/ufw(ubuntu16.04),否則可能會重複創建 iptables 規則
最好清理舊的 iptables rules 和 chains 規則
執行命令:docker version,檢查docker服務是否正常
注意如果
vim /etc/sysctl.conf
net.ipv4.ip_forward=1 net.bridge.bridge-nf-call-iptables=1 net.bridge.bridge-nf-call-ip6tables=1
sysctl -p
安裝和配置kubelet
kubelet 啓動時向kube-apiserver 發送TLS bootstrapping 請求,需要先將bootstrap token 文件中的kubelet-bootstrap 用戶賦予system:node-bootstrapper 角色,然後kubelet 纔有權限創建認證請求(certificatesigningrequests):
kubelet就是運行在Node節點上的,所以這一步安裝是在所有的Node節點上,如果你想把你的Master也當做Node節點的話,當然也可以在Master節點上安裝的。
$ kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap
--user=kubelet-bootstrap
是文件/etc/kubernetes/token.csv
中指定的用戶名,同時也寫入了文件/etc/kubernetes/bootstrap.kubeconfig
另外1.8 版本中還需要爲Node 請求創建一個RBAC 授權規則:
$ kubectl create clusterrolebinding kubelet-nodes --clusterrole=system:node --group=system:nodes
然後下載最新的kubelet 和kube-proxy 二進制文件(前面下載kubernetes 目錄下面其實也有):
$ wget https://dl.k8s.io/v1.8.2/kubernetes-server-linux-amd64.tar.gz$ tar -xzvf kubernetes-server-linux-amd64.tar.gz$ cd kubernetes$ tar -xzvf kubernetes-src.tar.gz$ sudo cp -r ./server/bin/{kube-proxy,kubelet} /usr/k8s/bin/
創建kubelet bootstapping kubeconfig 文件
$ # 設置集羣參數$ kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/ssl/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=bootstrap.kubeconfig$ # 設置客戶端認證參數$ kubectl config set-credentials kubelet-bootstrap \ --token=${BOOTSTRAP_TOKEN} \ --kubeconfig=bootstrap.kubeconfig$ # 設置上下文參數$ kubectl config set-context default \ --cluster=kubernetes \ --user=kubelet-bootstrap \ --kubeconfig=bootstrap.kubeconfig$ # 設置默認上下文$ kubectl config use-context default --kubeconfig=bootstrap.kubeconfig$ mv bootstrap.kubeconfig /etc/kubernetes/
--embed-certs
爲true
時表示將certificate-authority
證書寫入到生成的bootstrap.kubeconfig
文件中;設置 kubelet 客戶端認證參數時沒有指定祕鑰和證書,後續由
kube-apiserver
自動生成;
創建kubelet 的systemd unit 文件
$ sudo mkdir /var/lib/kubelet # 必須先創建工作目錄 $ cat > kubelet.service <<EOF [Unit] Description=Kubernetes Kubelet Documentation=https://github.com/GoogleCloudPlatform/kubernetesAfter=docker.service Requires=docker.service [Service] WorkingDirectory=/var/lib/kubelet ExecStart=/usr/k8s/bin/kubelet \\ --fail-swap-on=false \\ --cgroup-driver=cgroupfs \\ --address=${NODE_IP} \\ --hostname-override=${NODE_IP} \\ --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig \\ --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\ --require-kubeconfig \\ --cert-dir=/etc/kubernetes/ssl \\ --cluster-dns=${CLUSTER_DNS_SVC_IP} \\ --cluster-domain=${CLUSTER_DNS_DOMAIN} \\ --hairpin-mode promiscuous-bridge \\ --allow-privileged=true \\ --serialize-image-pulls=false \\ --logtostderr=true \\ --v=2Restart=on-failure RestartSec=5[Install] WantedBy=multi-user.target EOF
請仔細閱讀下面的注意事項,不然可能會啓動失敗。
--fail-swap-on
參數,這個一定要注意,Kubernetes 1.8開始要求關閉系統的Swap,如果不關閉,默認配置下kubelet將無法啓動,也可以通過kubelet的啓動參數–fail-swap-on=false
來避免該問題--cgroup-driver
參數,kubelet 用來維護主機的的 cgroups 的,默認是cgroupfs
,但是這個地方的值需要你根據docker 的配置來確定(docker info |grep cgroup
)-address
不能設置爲127.0.0.1
,否則後續 Pods 訪問 kubelet 的 API 接口時會失敗,因爲 Pods 訪問的127.0.0.1
指向自己而不是 kubelet如果設置了
--hostname-override
選項,則kube-proxy
也需要設置該選項,否則會出現找不到 Node 的情況--experimental-bootstrap-kubeconfig
指向 bootstrap kubeconfig 文件,kubelet 使用該文件中的用戶名和 token 向 kube-apiserver 發送 TLS Bootstrapping 請求管理員通過了 CSR 請求後,kubelet 自動在
--cert-dir
目錄創建證書和私鑰文件(kubelet-client.crt
和kubelet-client.key
),然後寫入--kubeconfig
文件(自動創建--kubeconfig
指定的文件)建議在
--kubeconfig
配置文件中指定kube-apiserver
地址,如果未指定--api-servers
選項,則必須指定--require-kubeconfig
選項後才從配置文件中讀取 kue-apiserver 的地址,否則 kubelet 啓動後將找不到 kube-apiserver (日誌中提示未找到 API Server),kubectl get nodes
不會返回對應的 Node 信息--cluster-dns
指定 kubedns 的 Service IP(可以先分配,後續創建 kubedns 服務時指定該 IP),--cluster-domain
指定域名後綴,這兩個參數同時指定後纔會生效
啓動kubelet
$ sudo cp kubelet.service /etc/systemd/system/kubelet.service$ sudo systemctl daemon-reload$ sudo systemctl enable kubelet$ sudo systemctl start kubelet$ systemctl status kubelet
通過kubelet 的TLS 證書請求
kubelet 首次啓動時向kube-apiserver 發送證書籤名請求,必須通過後kubernetes 系統纔會將該 Node 加入到集羣。查看未授權的CSR 請求:
$ kubectl get csr NAME AGE REQUESTOR CONDITION node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g 2m kubelet-bootstrap Pending $ kubectl get nodes No resources found.
通過CSR 請求:
$ kubectl certificate approve node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g certificatesigningrequest "node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g" approved$ kubectl get nodesNAME STATUS ROLES AGE VERSION192.168.1.170 Ready <none> 48s v1.8.1
自動生成了kubelet kubeconfig 文件和公私鑰:
$ ls -l /etc/kubernetes/kubelet.kubeconfig-rw------- 1 root root 2280 Nov 7 10:26 /etc/kubernetes/kubelet.kubeconfig$ ls -l /etc/kubernetes/ssl/kubelet*-rw-r--r-- 1 root root 1046 Nov 7 10:26 /etc/kubernetes/ssl/kubelet-client.crt-rw------- 1 root root 227 Nov 7 10:22 /etc/kubernetes/ssl/kubelet-client.key-rw-r--r-- 1 root root 1115 Nov 7 10:16 /etc/kubernetes/ssl/kubelet.crt-rw------- 1 root root 1675 Nov 7 10:16 /etc/kubernetes/ssl/kubelet.key
配置kube-proxy
創建kube-proxy 證書籤名請求:
$ cat > kube-proxy-csr.json <<EOF{ "CN": "system:kube-proxy", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ] }EOF
CN 指定該證書的 User 爲
system:kube-proxy
kube-apiserver
預定義的 RoleBindingsystem:node-proxier
將Usersystem:kube-proxy
與 Rolesystem:node-proxier
綁定,該 Role 授予了調用kube-apiserver
Proxy 相關 API 的權限hosts 屬性值爲空列表
生成kube-proxy 客戶端證書和私鑰
$ cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem \ -ca-key=/etc/kubernetes/ssl/ca-key.pem \ -config=/etc/kubernetes/ssl/ca-config.json \ -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy$ ls kube-proxy* kube-proxy.csr kube-proxy-csr.json kube-proxy-key.pem kube-proxy.pem$ sudo mv kube-proxy*.pem /etc/kubernetes/ssl/
創建kube-proxy kubeconfig 文件
$ # 設置集羣參數$ kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/ssl/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=kube-proxy.kubeconfig$ # 設置客戶端認證參數$ kubectl config set-credentials kube-proxy \ --client-certificate=/etc/kubernetes/ssl/kube-proxy.pem \ --client-key=/etc/kubernetes/ssl/kube-proxy-key.pem \ --embed-certs=true \ --kubeconfig=kube-proxy.kubeconfig$ # 設置上下文參數$ kubectl config set-context default \ --cluster=kubernetes \ --user=kube-proxy \ --kubeconfig=kube-proxy.kubeconfig$ # 設置默認上下文$ kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig$ mv kube-proxy.kubeconfig /etc/kubernetes/
設置集羣參數和客戶端認證參數時
--embed-certs
都爲true
,這會將certificate-authority
、client-certificate
和client-key
指向的證書文件內容寫入到生成的kube-proxy.kubeconfig
文件中kube-proxy.pem
證書中 CN 爲system:kube-proxy
,kube-apiserver
預定義的 RoleBindingcluster-admin
將Usersystem:kube-proxy
與 Rolesystem:node-proxier
綁定,該 Role 授予了調用kube-apiserver
Proxy 相關 API 的權限
創建kube-proxy 的systemd unit 文件
$ sudo mkdir -p /var/lib/kube-proxy # 必須先創建工作目錄$ cat > kube-proxy.service <<EOF [Unit]Description=Kubernetes Kube-Proxy ServerDocumentation=https://github.com/GoogleCloudPlatform/kubernetesAfter=network.target [Service]WorkingDirectory=/var/lib/kube-proxyExecStart=/usr/k8s/bin/kube-proxy \\ --bind-address=${NODE_IP} \\ --hostname-override=${NODE_IP} \\ --cluster-cidr=${SERVICE_CIDR} \\ --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig \\ --logtostderr=true \\ --v=2Restart=on-failureRestartSec=5LimitNOFILE=65536 [Install]WantedBy=multi-user.target EOF
--hostname-override
參數值必須與 kubelet 的值一致,否則 kube-proxy 啓動後會找不到該 Node,從而不會創建任何 iptables 規則--cluster-cidr
必須與 kube-apiserver 的--service-cluster-ip-range
選項值一致kube-proxy 根據
--cluster-cidr
判斷集羣內部和外部流量,指定--cluster-cidr
或--masquerade-all
選項後 kube-proxy 纔會對訪問 Service IP 的請求做 SNAT--kubeconfig
指定的配置文件嵌入了 kube-apiserver 的地址、用戶名、證書、祕鑰等請求和認證信息預定義的 RoleBinding
cluster-admin
將Usersystem:kube-proxy
與 Rolesystem:node-proxier
綁定,該 Role 授予了調用kube-apiserver
Proxy 相關 API 的權限
啓動kube-proxy
$ sudo cp kube-proxy.service /etc/systemd/system/$ sudo systemctl daemon-reload$ sudo systemctl enable kube-proxy$ sudo systemctl start kube-proxy$ systemctl status kube-proxy
驗證集羣功能
定義yaml 文件:(將下面內容保存爲:nginx-ds.yaml)
apiVersion: v1kind: Servicemetadata: name: nginx-ds labels: app: nginx-dsspec: type: NodePort selector: app: nginx-ds ports: - name: http port: 80 targetPort: 80---apiVersion: extensions/v1beta1kind: DaemonSetmetadata: name: nginx-ds labels: addonmanager.kubernetes.io/mode: Reconcilespec: template: metadata: labels: app: nginx-ds spec: containers: - name: my-nginx image: nginx:1.7.9 ports: - containerPort: 80
創建 Pod 和服務:
$ kubectl create -f nginx-ds.yml service "nginx-ds" created daemonset "nginx-ds" created
執行下面的命令查看Pod 和SVC:
$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE nginx-ds-f29zt 1/1 Running 0 23m 172.17.0.2 192.168.1.170 $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-ds NodePort 10.254.6.249 <none> 80:30813/TCP 24m
可以看到:
服務IP:10.254.6.249
服務端口:80
NodePort端口:30813
在所有 Node 上執行:
$ curl 10.254.6.249$ curl 192.168.1.170:30813
執行上面的命令預期都會輸出nginx 歡迎頁面內容,表示我們的Node 節點正常運行了。
9. 部署kubedns 插件
官方文件目錄:kubernetes/cluster/addons/dns
使用的文件:
$ ls *.yaml *.basekubedns-cm.yaml kubedns-sa.yaml kubedns-controller.yaml.base kubedns-svc.yaml.base
系統預定義的RoleBinding
預定義的RoleBinding system:kube-dns
將kube-system 命名空間的kube-dns
ServiceAccount 與 system:kube-dns
Role 綁定,該Role 具有訪問kube-apiserver DNS 相關的API 權限:
$ kubectl get clusterrolebindings system:kube-dns -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" creationTimestamp: 2017-11-06T10:51:59Z labels: kubernetes.io/bootstrapping: rbac-defaults name: system:kube-dns resourceVersion: "78" selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/system%3Akube-dns uid: 83a25fd9-c2e0-11e7-9646-00163e0055c1 roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:kube-dns subjects: - kind: ServiceAccount name: kube-dns namespace: kube-system
kubedns-controller.yaml
中定義的 Pods 時使用了kubedns-sa.yaml
文件定義的kube-dns
ServiceAccount,所以具有訪問 kube-apiserver DNS 相關 API 的權限;
配置kube-dns ServiceAccount
無需更改
配置kube-dns 服務
$ diff kubedns-svc.yaml.base kubedns-svc.yaml 30c30< clusterIP: __PILLAR__DNS__SERVER__ ---> clusterIP: 10.254.0.2
需要將 spec.clusterIP 設置爲集羣環境變量中變量
CLUSTER_DNS_SVC_IP
值,這個IP 需要和 kubelet 的—cluster-dns
參數值一致
配置kube-dns Deployment
$ diff kubedns-controller.yaml.base kubedns-controller.yaml88c88 < - --domain=__PILLAR__DNS__DOMAIN__. --- > - --domain=cluster.local128c128 < - --server=/__PILLAR__DNS__DOMAIN__/127.0.0.1#10053--- > - --server=/cluster.local/127.0.0.1#10053160,161c160,161< - --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.__PILLAR__DNS__DOMAIN__,5,A < - --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.__PILLAR__DNS__DOMAIN__,5,A --- > - --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.cluster.local,5,A > - --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.cluster.local,5,A
--domain
爲集羣環境變量CLUSTER_DNS_DOMAIN
的值使用系統已經做了 RoleBinding 的
kube-dns
ServiceAccount,該賬戶具有訪問 kube-apiserver DNS 相關 API 的權限
執行所有定義文件
$ pwd /home/ych/k8s-repo/kube-dns$ ls *.yaml kubedns-cm.yaml kubedns-controller.yaml kubedns-sa.yaml kubedns-svc.yaml$ kubectl create -f .
檢查kubedns 功能
新建一個Deployment
$ cat > my-nginx.yaml<<EOF apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-nginx spec: replicas: 2 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx:1.7.9 ports: - containerPort: 80EOF $ kubectl create -f my-nginx.yaml deployment "my-nginx" created
Expose 該Deployment,生成my-nginx 服務
$ kubectl expose deploy my-nginx $ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.254.0.1 <none> 443/TCP 1d my-nginx ClusterIP 10.254.32.162 <none> 80/TCP 56s
然後創建另外一個Pod,查看/etc/resolv.conf
是否包含kubelet
配置的--cluster-dns
和--cluster-domain
,是否能夠將服務my-nginx
解析到上面顯示的CLUSTER-IP 10.254.32.162
上
$ cat > pod-nginx.yaml<<EOFapiVersion: v1kind: Podmetadata: name: nginxspec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80EOF$ kubectl create -f pod-nginx.yaml pod "nginx" created$ kubectl exec nginx -i -t -- /bin/bashroot@nginx:/# cat /etc/resolv.confnameserver 10.254.0.2search default.svc.cluster.local. svc.cluster.local. cluster.local. options ndots:5root@nginx:/# ping my-nginxPING my-nginx.default.svc.cluster.local (10.254.32.162): 48 data bytes ^C--- my-nginx.default.svc.cluster.local ping statistics ---14 packets transmitted, 0 packets received, 100% packet loss root@nginx:/# ping kubernetesPING kubernetes.default.svc.cluster.local (10.254.0.1): 48 data bytes ^C--- kubernetes.default.svc.cluster.local ping statistics ---6 packets transmitted, 0 packets received, 100% packet loss root@nginx:/# ping kube-dns.kube-system.svc.cluster.localPING kube-dns.kube-system.svc.cluster.local (10.254.0.2): 48 data bytes ^C--- kube-dns.kube-system.svc.cluster.local ping statistics ---2 packets transmitted, 0 packets received, 100% packet loss
10. 部署Dashboard 插件
官方文件目錄:kubernetes/cluster/addons/dashboard
使用的文件如下:
$ ls *.yaml dashboard-controller.yaml dashboard-rbac.yaml dashboard-service.yaml
新加了
dashboard-rbac.yaml
文件,定義 dashboard 使用的 RoleBinding。
由於 kube-apiserver
啓用了 RBAC
授權,而官方源碼目錄的 dashboard-controller.yaml
沒有定義授權的 ServiceAccount,所以後續訪問 kube-apiserver
的 API 時會被拒絕,前端界面提示:
解決辦法是:定義一個名爲dashboard 的ServiceAccount,然後將它和Cluster Role view 綁定:
$ cat > dashboard-rbac.yaml<<EOF apiVersion: v1 kind: ServiceAccountmetadata: name: dashboard namespace: kube-system --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1alpha1metadata: name: dashboardsubjects: - kind: ServiceAccount name: dashboard namespace: kube-systemroleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io EOF
配置dashboard-controller
20a21> serviceAccountName: dashboard
使用名爲 dashboard 的自定義 ServiceAccount
配置dashboard-service
$ diff dashboard-service.yaml.orig dashboard-service.yaml10a11 > type: NodePort
指定端口類型爲 NodePort,這樣外界可以通過地址 nodeIP:nodePort 訪問 dashboard
執行所有定義文件
$ pwd /home/ych/k8s-repo/dashboard$ ls *.yaml dashboard-controller.yaml dashboard-rbac.yaml dashboard-service.yaml$ kubectl create -f .
檢查執行結果
查看分配的 NodePort
$ kubectl get services kubernetes-dashboard -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes-dashboard NodePort 10.254.104.90 <none> 80:31202/TCP 1m
NodePort 31202映射到dashboard pod 80端口;
檢查 controller
$ kubectl get deployment kubernetes-dashboard -n kube-systemNAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE kubernetes-dashboard 1 1 1 1 3m$ kubectl get pods -n kube-system | grep dashboard kubernetes-dashboard-6667f9b4c-4xbpz 1/1 Running 0 3m
訪問dashboard
kubernetes-dashboard 服務暴露了 NodePort,可以使用
http://NodeIP:nodePort
地址訪問 dashboard通過 kube-apiserver 訪問 dashboard
通過 kubectl proxy 訪問 dashboard
由於缺少 Heapster 插件,當前 dashboard 不能展示 Pod、Nodes 的 CPU、內存等 metric 圖形
注意:如果你的後端
apiserver
是高可用的集羣模式的話,那麼Dashboard
的apiserver-host
最好手動指定,不然,當你apiserver
某個節點掛了的時候,Dashboard
可能不能正常訪問,如下配置
image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.7.1ports:- containerPort: 9090 protocol: TCPargs: - --apiserver-host=http://<api_server_ha_addr>:8080
11. 部署Heapster 插件
到heapster release 頁面下載最新版的heapster
$ wget https://github.com/kubernetes/heapster/archive/v1.4.3.tar.gz$ tar -xzvf v1.4.3.tar.gz
部署相關文件目錄:/home/ych/k8s-repo/heapster-1.4.3/deploy/kube-config
$ ls influxdb/ && ls rbac/ grafana.yaml heapster.yaml influxdb.yaml heapster-rbac.yaml
爲方便測試訪問,將grafana.yaml
下面的服務類型設置爲type=NodePort
執行所有文件
$ kubectl create -f rbac/heapster-rbac.yaml clusterrolebinding "heapster" created $ kubectl create -f influxdb deployment "monitoring-grafana" created service "monitoring-grafana" created serviceaccount "heapster" created deployment "heapster" created service "heapster" created deployment "monitoring-influxdb" created service "monitoring-influxdb" created
檢查執行結果
檢查 Deployment
$ kubectl get deployments -n kube-system | grep -E 'heapster|monitoring'heapster 1 1 1 1 2mmonitoring-grafana 1 1 1 0 2mmonitoring-influxdb 1 1 1 1 2m
檢查 Pods
$ kubectl get pods -n kube-system | grep -E 'heapster|monitoring'heapster-7cf895f48f-p98tk 1/1 Running 0 2mmonitoring-grafana-c9d5cd98d-gb9xn 0/1 CrashLoopBackOff 4 2mmonitoring-influxdb-67f8d587dd-zqj6p 1/1 Running 0 2m
我們可以看到monitoring-grafana
的POD 是沒有執行成功的,通過查看日誌可以看到下面的錯誤信息:
Failed to parse /etc/grafana/grafana.ini, open /etc/grafana/grafana.ini: no such file or directory
要解決這個問題(heapster issues)我們需要將grafana 的鏡像版本更改成:gcr.io/google_containers/heapster-grafana-amd64:v4.0.2
,然後重新執行,即可正常。
訪問 grafana
上面我們修改grafana 的Service 爲NodePort 類型:
$ kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE monitoring-grafana NodePort 10.254.34.89 <none> 80:30191/TCP 28m
則我們就可以通過任意一個節點加上上面的30191端口就可以訪問grafana 了。
heapster 正確安裝後,我們便可以回去看我們的dashboard 是否有圖表出現了:
12. 安裝Ingress
Ingress
其實就是從kuberenets
集羣外部訪問集羣的一個入口,將外部的請求轉發到集羣內不同的Service 上,其實就相當於nginx、apache 等負載均衡代理服務器,再加上一個規則定義,路由信息的刷新需要靠Ingress controller
來提供
Ingress controller
可以理解爲一個監聽器,通過不斷地與kube-apiserver
打交道,實時的感知後端service、pod 等的變化,當得到這些變化信息後,Ingress controller
再結合Ingress
的配置,更新反向代理負載均衡器,達到服務發現的作用。其實這點和服務發現工具consul
的consul-template
非常類似。
部署traefik
Traefik是一款開源的反向代理與負載均衡工具。它最大的優點是能夠與常見的微服務系統直接整合,可以實現自動化動態配置。目前支持Docker、Swarm、Mesos/Marathon、 Mesos、Kubernetes、Consul、Etcd、Zookeeper、BoltDB、Rest API等等後端模型。
創建rbac
創建文件:ingress-rbac.yaml
,用於service account
驗證
apiVersion: v1kind: ServiceAccountmetadata: name: ingress namespace: kube-system---kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1beta1metadata: name: ingresssubjects: - kind: ServiceAccount name: ingress namespace: kube-systemroleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
DaemonSet 形式部署traefik
創建文件:traefik-daemonset.yaml
,爲保證traefik 總能提供服務,在每個節點上都部署一個traefik,所以這裏使用DaemonSet
的形式
kind: ConfigMapapiVersion: v1metadata: name: traefik-conf namespace: kube-systemdata: traefik-config: |- defaultEntryPoints = ["http","https"] [entryPoints] [entryPoints.http] address = ":80" [entryPoints.http.redirect] entryPoint = "https" [entryPoints.https] address = ":443" [entryPoints.https.tls] [[entryPoints.https.tls.certificates]] CertFile = "/ssl/ssl.crt" KeyFile = "/ssl/ssl.key"---kind: DaemonSetapiVersion: extensions/v1beta1metadata: name: traefik-ingress namespace: kube-system labels: k8s-app: traefik-ingressspec: template: metadata: labels: k8s-app: traefik-ingress name: traefik-ingress spec: terminationGracePeriodSeconds: 60 restartPolicy: Always serviceAccountName: ingress containers: - image: traefik:latest name: traefik-ingress ports: - name: http containerPort: 80 hostPort: 80 - name: https containerPort: 443 hostPort: 443 - name: admin containerPort: 8080 args: - --configFile=/etc/traefik/traefik.toml - -d - --web - --kubernetes - --logLevel=DEBUG volumeMounts: - name: traefik-config-volume mountPath: /etc/traefik - name: traefik-ssl-volume mountPath: /ssl volumes: - name: traefik-config-volume configMap: name: traefik-conf items: - key: traefik-config path: traefik.toml - name: traefik-ssl-volume secret: secretName: traefik-ssl
注意上面的yaml 文件中我們添加了一個名爲traefik-conf
的ConfigMap
,該配置是用來將http 請求強制跳轉成https,並指定https 所需CA 文件地址,這裏我們使用secret
的形式來指定CA 文件的路徑:
$ ls ssl.crt ssl.key $ kubectl create secret generic traefik-ssl --from-file=ssl.crt --from-file=ssl.key --namespace=kube-system secret "traefik-ssl" created
創建ingress
創建文件:traefik-ingress.yaml
,現在可以通過創建ingress
文件來定義請求規則了,根據自己集羣中的service 自己修改相應的serviceName
和servicePort
apiVersion: extensions/v1beta1kind: Ingressmetadata: name: traefik-ingressspec: rules: - host: traefik.nginx.io http: paths: - path: / backend: serviceName: my-nginx servicePort: 80
執行創建命令:
$ kubectl create -f ingress-rbac.yaml serviceaccount "ingress" created clusterrolebinding "ingress" created $ kubectl create -f traefik-daemonset.yaml configmap "traefik-conf" created daemonset "traefik-ingress" created $ kubectl create -f traefik-ingress.yaml ingress "traefik-ingress" created
Traefik UI
創建文件:traefik-ui.yaml
,
apiVersion: v1kind: Servicemetadata: name: traefik-ui namespace: kube-systemspec: selector: k8s-app: traefik-ingress ports: - name: web port: 80 targetPort: 8080---apiVersion: extensions/v1beta1kind: Ingressmetadata: name: traefik-ui namespace: kube-systemspec: rules: - host: traefik-ui.local http: paths: - path: / backend: serviceName: traefik-ui servicePort: web
測試
部署完成後,在本地/etc/hosts
添加一條配置:
# 將下面的xx.xx.xx.xx替換成任意節點IPxx.xx.xx.xx master03 traefik.nginx.io traefik-ui.local
配置完成後,在本地訪問:traefik-ui.local
,則可以訪問到traefik
的dashboard
頁面:
同樣的可以訪問traefik.nginx.io
,得到正確的結果頁面:
上面配置完成後,就可以將我們的所有節點加入到一個SLB
中,然後配置相應的域名解析到SLB
即可。
13. 日誌收集
14. 私有倉庫harbor 搭建
參考文章在kubernetes 上搭建docker 私有倉庫Harbor
15. 問題彙總
15.1 dashboard無法顯示監控圖
dashboard 和heapster influxdb都部署完成後 dashboard依舊無法顯示監控圖 通過排查 heapster log有超時錯誤
$ kubectl logs -f pods/heapster-2882613285-58d9r -n kube-systemE0630 17:23:47.339987 1 reflector.go:203] k8s.io/heapster/metrics/sources/kubelet/kubelet.go:342: Failed to list *api.Node: Get http://kubernetes.default/api/v1/nodes?resourceVersion=0: dial tcp: i/o timeout E0630 17:23:47.340274 1 reflector.go:203] k8s.io/heapster/metrics/heapster.go:319: Failed to list *api.Pod: Get http://kubernetes.default/api/v1/pods?resourceVersion=0: dial tcp: i/o timeout E0630 17:23:47.340498 1 reflector.go:203] k8s.io/heapster/metrics/processors/namespace_based_enricher.go:84: Failed to list *api.Namespace: Get http://kubernetes.default/api/v1/namespaces?resourceVersion=0: dial tcp: lookup kubernetes.default on 10.254.0.2:53: dial udp 10.254.0.2:53: i/o timeout E0630 17:23:47.340563 1 reflector.go:203] k8s.io/heapster/metrics/heapster.go:327: Failed to list *api.Node: Get http://kubernetes.default/api/v1/nodes?resourceVersion=0: dial tcp: lookup kubernetes.default on 10.254.0.2:53: dial udp 10.254.0.2:53: i/o timeout E0630 17:23:47.340623 1 reflector.go:203] k8s.io/heapster/metrics/processors/node_autoscaling_enricher.go