Kubernetes集羣安裝(本身搭過,已搭好)

在該文檔的基礎上從新部署了最新的v1.8.2版本,實現了kube-apiserver的高可用、traefik ingress 的部署、在kubernetes上安裝docker的私有倉庫harbor、容器化kubernetes部分組建、使用阿里雲日誌服務收集日誌。html

部署完成後,你將理解系統各組件的交互原理,進而能快速解決實際問題,因此本文檔主要適合於那些有必定kubernetes基礎,想經過一步步部署的方式來學習和了解系統配置、運行原理的人。前端

本系列系文檔適用於 CentOS 7Ubuntu 16.04 及以上版本系統,因爲啓用了 TLS 雙向認證、RBAC 受權等嚴格的安全機制,建議從頭開始部署,不然可能會認證、受權等失敗!node

有人問我爲何這麼長的文章不分拆成幾篇文章啊?這樣閱讀起來也方便啊,然而在我本身學習的過程當中,這種整個一篇文章把一件事情從頭至尾講清楚的形式是最好的,能給讀者提供一種沉浸式的學習體驗,閱讀完整個文章後有種酣暢淋漓的感受,因此我選擇這種一篇文章的形式。linux

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

集羣環境變量

後面的嗯部署將會使用到的全局變量,定義以下(根據本身的機器、網絡修改):nginx

# 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-apigit

其中192.168.1.137爲master01 的IP,暫時使用該IP 來作apiserver 的負載地址github

若是你使用的是阿里雲的ECS 服務,強烈建議你先將上述節點的安全組配置成容許全部訪問,否則在安裝過程當中會遇到各類訪問不了的問題,待集羣配置成功之後再根據須要添加安全限制。web

2. 建立CA 證書和密鑰

kubernetes 系統各個組件須要使用TLS證書對通訊進行加密,這裏咱們使用CloudFlare的PKI 工具集cfssl 來生成Certificate Authority(CA) 證書和密鑰文件, CA 是自簽名的證書,用來簽名後續建立的其餘TLS 證書。docker

安裝 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文件中。shell

建立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"
        }
    ]
}

 

  • CNCommon Name,kube-apiserver 從證書中提取該字段做爲請求的用戶名(User Name);瀏覽器使用該字段驗證網站是否合法;
  • OOrganization,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個節點,分別命名爲etcd01etcd02etcd03:

  • 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=5
LimitNOFILE=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.509032ms
https://192.168.1.138:2379 is healthy: successfully committed proposal: took = 1.639228ms
https://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 將Group system:masters與Role cluster-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:masterskube-apiserver 預約義的 RoleBinding cluster-admin 將 Group system:masters 與 Role cluster-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 agent
After=network.target
After=network-online.target
Wants=network-online.target
After=etcd.service
Before=docker.service
[Service]
Type=notify
ExecStart=/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/docker
Restart=on-failure
[Install]
WantedBy=multi-user.target
RequiredBy=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-2四、172.30.30.0-2四、172.30.19.0-2四、172.30.41.0-2四、172.30.83.0-24。

6. 部署master 節點

kubernetes master 節點包含的組件有:

  • kube-apiserver
  • kube-scheduler
  • kube-controller-manager

目前這3個組件須要部署到同一臺機器上:(後面再部署高可用的master)

  • kube-schedulerkube-controller-manager 和 kube-apiserver 三者的功能緊密相關;
  • 同時只能有一個 kube-schedulerkube-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/kubernetes
After=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=6
Restart=on-failure
RestartSec=5
Type=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=2
Restart=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 Scheduler
Documentation=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=2
Restart=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端口
  • --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 高可用

按照上面的方式在master01master02機器上安裝kube-apiserverkube-controller-managerkube-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 check

frontend 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 statshaproxy 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 = 1
net.ipv4.ip_nonlocal_bind = 1

# 驗證並生效
$ sysctl -p
# 驗證是否生效
$ cat /proc/sys/net/ipv4/ip_forward
1

 

安裝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_haproxy
    }
}

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 addr
1: 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 forever
2: 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-managerkube-scheduler–master參數中的8080端口去掉了,而後分別重啓這兩個組件便可。

驗證apiserver:關閉master01 節點上的kube-apiserver 進程,而後查看虛擬ip是否漂移到了master02 節點。

而後咱們就能夠將第一步在/etc/hosts裏面設置的域名對應的IP 更改成咱們的虛擬IP了

master01 與master 02 節點都須要安裝keepalived 和haproxy,實際上咱們虛擬IP的自身檢測應該是檢測haproxy,腳本你們能夠自行更改

kube-apiserver hakube-apiserver ha

這樣咱們就實現了接入層apiserver 的高可用了,一個部分是多活的apiserver 服務,另外一個部分是一主一備的haproxy 服務。

kube-controller-manager 和kube-scheduler 的高可用

Kubernetes 的管理層服務包括kube-schedulerkube-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節點的全局惟一性。總體方案以下圖所示:

imgimg

當集羣中的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=1
net.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 Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=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 docker
EnvironmentFile=-/run/flannel/docker
ExecStart=/usr/bin/dockerd --log-level=info $DOCKER_NETWORK_OPTIONS
ExecReload=/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=infinity
LimitNPROC=infinity
LimitCORE=infinity
# Uncomment TasksMax if your systemd version supports it.
# Only systemd 226 and above support this version.
#TasksMax=infinity
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
# kill only the docker process, not all processes in the cgroup
KillMode=process
# restart the docker process if it exits prematurely
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=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服務是否正常

安裝和配置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/kubernetes
After=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=2
Restart=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 nodes
NAME            STATUS    ROLES     AGE       VERSION
192.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 預約義的 RoleBinding system:node-proxier 將User system:kube-proxy 與 Role system: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-authorityclient-certificate 和 client-key 指向的證書文件內容寫入到生成的 kube-proxy.kubeconfig 文件中
  • kube-proxy.pem 證書中 CN 爲 system:kube-proxykube-apiserver 預約義的 RoleBinding cluster-admin 將User system:kube-proxy 與 Role system: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 Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
[Service]
WorkingDirectory=/var/lib/kube-proxy
ExecStart=/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=2
Restart=on-failure
RestartSec=5
LimitNOFILE=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 將User system:kube-proxy 與 Role system: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: v1
kind: Service
metadata:
  name: nginx-ds
  labels:
    app: nginx-ds
spec:
  type: NodePort
  selector:
    app: nginx-ds
  ports:
  - name: http
    port: 80
    targetPort: 80
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: nginx-ds
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
spec:
  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 *.base
kubedns-cm.yaml  kubedns-sa.yaml  kubedns-controller.yaml.base  kubedns-svc.yaml.base

 

系統預約義的RoleBinding

預約義的RoleBinding system:kube-dns將kube-system 命名空間的kube-dnsServiceAccount 與 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.yaml
88c88
<         - --domain=__PILLAR__DNS__DOMAIN__.
---
>         - --domain=cluster.local
128c128
<         - --server=/__PILLAR__DNS__DOMAIN__/127.0.0.1#10053
---
>         - --server=/cluster.local/127.0.0.1#10053
160,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: 80
EOF
$ 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<<EOF
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.7.9
    ports:
    - containerPort: 80
EOF
$ kubectl create -f pod-nginx.yaml
pod "nginx" created
$ kubectl exec  nginx -i -t -- /bin/bash
root@nginx:/# cat /etc/resolv.conf
nameserver 10.254.0.2
search default.svc.cluster.local. svc.cluster.local. cluster.local.
options ndots:5
root@nginx:/# ping my-nginx
PING 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 kubernetes
PING 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.local
PING 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 時會被拒絕,前端界面提示:

403403

解決辦法是:定義一個名爲dashboard 的ServiceAccount,而後將它和Cluster Role view 綁定:

$ cat > dashboard-rbac.yaml<<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: dashboard
  namespace: kube-system
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1alpha1
metadata:
  name: dashboard
subjects:
  - kind: ServiceAccount
    name: dashboard
    namespace: kube-system
roleRef:
  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.yaml
10a11
>   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-system
NAME                   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

  1. kubernetes-dashboard 服務暴露了 NodePort,可使用 http://NodeIP:nodePort 地址訪問 dashboard
  2. 經過 kube-apiserver 訪問 dashboard
  3. 經過 kubectl proxy 訪問 dashboard

dashboard uidashboard ui

因爲缺乏 Heapster 插件,當前 dashboard 不能展現 Pod、Nodes 的 CPU、內存等 metric 圖形

注意:若是你的後端apiserver是高可用的集羣模式的話,那麼Dashboardapiserver-host最好手動指定,否則,當你apiserver某個節點掛了的時候,Dashboard可能不能正常訪問,以下配置

image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.7.1
ports:
- containerPort: 9090
  protocol: TCP
args:
  - --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           2m
monitoring-grafana     1         1         1            0           2m
monitoring-influxdb    1         1         1            1           2m

 

檢查 Pods

$ kubectl get pods -n kube-system | grep -E 'heapster|monitoring'
heapster-7cf895f48f-p98tk              1/1       Running            0          2m
monitoring-grafana-c9d5cd98d-gb9xn     0/1       CrashLoopBackOff   4          2m
monitoring-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 了。

grafana uigrafana ui

heapster 正確安裝後,咱們即可以回去看咱們的dashboard 是否有圖表出現了:

dashboarddashboard

12. 安裝Ingress

Ingress其實就是從kuberenets集羣外部訪問集羣的一個入口,將外部的請求轉發到集羣內不一樣的Service 上,其實就至關於nginx、apache 等負載均衡代理服務器,再加上一個規則定義,路由信息的刷新須要靠Ingress controller來提供

Ingress controller能夠理解爲一個監聽器,經過不斷地與kube-apiserver打交道,實時的感知後端service、pod 等的變化,當獲得這些變化信息後,Ingress controller再結合Ingress的配置,更新反向代理負載均衡器,達到服務發現的做用。其實這點和服務發現工具consulconsul-template很是相似。

部署traefik

Traefik是一款開源的反向代理與負載均衡工具。它最大的優勢是可以與常見的微服務系統直接整合,能夠實現自動化動態配置。目前支持Docker、Swarm、Mesos/Marathon、 Mesos、Kubernetes、Consul、Etcd、Zookeeper、BoltDB、Rest API等等後端模型。

traefiktraefik

建立rbac

建立文件:ingress-rbac.yaml,用於service account驗證

apiVersion: v1
kind: ServiceAccount
metadata:
  name: ingress
  namespace: kube-system
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: ingress
subjects:
  - kind: ServiceAccount
    name: ingress
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

 

DaemonSet 形式部署traefik

建立文件:traefik-daemonset.yaml,爲保證traefik 總能提供服務,在每一個節點上都部署一個traefik,因此這裏使用DaemonSet 的形式

kind: ConfigMap
apiVersion: v1
metadata:
  name: traefik-conf
  namespace: kube-system
data:
  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: DaemonSet
apiVersion: extensions/v1beta1
metadata:
  name: traefik-ingress
  namespace: kube-system
  labels:
    k8s-app: traefik-ingress
spec:
  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-confConfigMap,該配置是用來將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/v1beta1
kind: Ingress
metadata:
  name: traefik-ingress
spec:
  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: v1
kind: Service
metadata:
  name: traefik-ui
  namespace: kube-system
spec:
  selector:
    k8s-app: traefik-ingress
  ports:
  - name: web
    port: 80
    targetPort: 8080
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: traefik-ui
  namespace: kube-system
spec:
  rules:
  - host: traefik-ui.local
    http:
      paths:
      - path: /
        backend:
          serviceName: traefik-ui
          servicePort: web

 

測試

部署完成後,在本地/etc/hosts添加一條配置:

# 將下面的xx.xx.xx.xx替換成任意節點IP
xx.xx.xx.xx master03 traefik.nginx.io traefik-ui.local

配置完成後,在本地訪問:traefik-ui.local,則能夠訪問到traefikdashboard頁面:

traefik dashboardtraefik dashboard

一樣的能夠訪問traefik.nginx.io,獲得正確的結果頁面:

WX20171110-140306        WX20171110-140306

上面配置完成後,就能夠將咱們的全部節點加入到一個SLB中,而後配置相應的域名解析到SLB便可。

13. 日誌收集

參考文章kubernetes 日誌收集方案

14. 私有倉庫harbor 搭建

參考文章在kubernetes 上搭建docker 私有倉庫Harbor

15. 問題彙總

15.1 dashboard沒法顯示監控圖

dashboard 和heapster influxdb都部署完成後 dashboard依舊沒法顯示監控圖 經過排查 heapster log有超時錯誤

`
$ kubectl logs -f pods/heapster-2882613285-58d9r -n kube-system

E0630 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💯 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:55.014414 1 influxdb.go:150] Failed to create infuxdb: failed to ping InfluxDB server at "monitoring-influxdb:8086" - Get http://monitoring-influxdb:8086/ping: dial tcp: lookup monitoring-influxdb on 10.254.0.2:53: read udp 172.30.45.4:48955->10.254.0.2:53: i/o timeout

 

我是docker的systemd Unit文件忘記添加

ExecStart=/root/local/bin/dockerd --log-level=error $DOCKER_NETWORK_OPTIONS

 

後邊的$DOCKER_NETWORK_OPTIONS,致使docker0的網段跟flannel.1不一致。

15.2 kube-proxy報錯kube-proxy[2241]: E0502 15:55:13.889842 2241 conntrack.go:42] conntrack returned error: error looking for path of conntrack: exec: 「conntrack」: executable file not found in $PATH

致使現象kubedns啓動成功,運行正常,可是service 之間沒法解析,kubernetes中的DNS解析異常

解決方法CentOS中安裝conntrack-tools包後重啓kubernetes 集羣便可。

15.3 Unable to access kubernetes services: no route to host

致使現象: 在POD 內訪問集羣的某個服務的時候出現no route to host

$ curl my-nginx.nx.svc.cluster.local
curl: (7) Failed connect to my-nginx.nx.svc.cluster.local:80; No route to host

解決方法:清除全部的防火牆規則,而後重啓docker 服務

$ iptables --flush && iptables -tnat --flush
$ systemctl restart docker

 

15.4 使用NodePort 類型的服務,只能在POD 所在節點進行訪問

致使現象: 使用NodePort 類型的服務,只能在POD 所在節點進行訪問,其餘節點經過NodePort 不能正常訪問

解決方法kube-proxy 默認使用的是proxy_model就是iptables,正常狀況下是全部節點均可以經過NodePort 進行訪問的,我這裏將阿里雲的安全組限制所有去掉便可,而後根據須要進

相關文章
相關標籤/搜索