二進制方式部署Kubernetes 1.6.0集群(開啟TLS)


本節內容:

  • Kubernetes簡介
  • 環境信息
  • 創建TLS加密通信的證書和密鑰
  • 下載和配置 kubectl(kubecontrol) 命令行工具
  • 創建 kubeconfig 文件
  • 創建高可用 etcd 集群
  • 部署 kubernetes master節點  
  • 部署kubernetes node節點
  • 安裝和配置 kube-dns 插件

 

一、Kubernetes簡介

Kubernetes是谷歌開源的容器集群管理系統,是Google多年大規模容器管理技術Borg的 開源版本,主要功能包括:

  • 基於容器的應用部署、維護和滾動升級
  • 負載均衡和服務發現
  • 跨機器和跨地區的集群調度
  • 自動伸縮
  • 無狀態服務和有狀態服務
  • 廣泛的Volume支持
  • 插件機制保證擴展性

之前嘗試使用kubeadm自動化部署集群,使用yum去安裝kubeadm等工具,但是不翻牆的情況下,這種方式在國內幾乎是不可能安裝成功的。於是改為采用二進制文件部署Kubernetes集群,同時開啟了集群的TLS安全認證。本篇實踐是參照opsnull的文章《創建 kubernetes 各組件 TLS 加密通信的證書和秘鑰》,結合公司的實際情況進行部署的。

 

二、環境信息

主機名 操作系統版本 IP地址 角色 安裝軟件
node1 CentOS 7.0 172.16.7.151 Kubernetes Master、Node etcd 3.2.7、kube-apiserver、kube-scheduler、kube-controller-manager、kubelet、kube-proxy、etcd 3.2.7、flannel 0.7.1、docker 1.12.6
node2 CentOS 7.0 172.16.7.152 Kubernetes Node kubelet、kube-proxy、flannel 0.7.1、etcd 3.2.7、docker 1.12.6
node3 CentOS 7.0 172.16.7.153 Kubernetes Node kubelet、kube-proxy、flannel 0.7.1、etcd 3.2.7、docker 1.12.6
spark32 CentOS 7.0 172.16.206.32 Harbor docker-ce 17.06.1、docker-compose 1.15.0、harbor-online-installer-v1.1.2.tar

 

spark32主機是harbor私有鏡像倉庫,關於harbor的安裝部署見之前的博客《企業級Docker Registry——Harbor搭建和使用》。

 

三、創建TLS加密通信的證書和密鑰

kubernetes各組件需要使用TLS證書對通信進行加密,這里我使用CloudFlare的PKI工具集 cfssl 來生成CA和其它證書。

生成的CA證書和密鑰文件如下:

  • ca-key.pem
  • ca.pem
  • kubernetes-key.pem
  • kubernetes.pem
  • kube-proxy.pem
  • kube-proxy-key.pem
  • admin.pem
  • admin-key.pem

各組件使用證書的情況如下:

  • etcd:使用ca.pem、kubernetes-key.pem、kubernetes.pem;
  • kube-apiserver:使用ca.pem、kubernetes-key.pem、kubernetes.pem;
  • kubelet:使用ca.pem;
  • kube-proxy:使用ca.pem、kube-proxy-key.pem、kube-proxy.pem;
  • kubectl:使用ca.pem、admin-key.pem、admin.pem

kube-controller、kube-scheduler當前需要和kube-apiserver部署在同一台機器上且使用非安全端口通信,故不需要證書。

 

1. 安裝CFSSL

有兩種方式安裝,一是二進制包安裝,二是使用go命令安裝。

(1)方式一:二進制包安裝

# wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
# chmod +x cfssl_linux-amd64
# mv cfssl_linux-amd64 /root/local/bin/cfssl
        
# wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
# chmod +x cfssljson_linux-amd64
# mv cfssljson_linux-amd64 /root/local/bin/cfssljson

# wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
# chmod +x cfssl-certinfo_linux-amd64
# mv cfssl-certinfo_linux-amd64 /root/local/bin/cfssl-certinfo

# export PATH=/root/local/bin:$PATH
二進制包安裝CFSSL

(2)方式二:使用go命令安裝

# 下載地址:https://golang.org/dl/
[root@node1 ~]# cd /usr/local/
[root@node1 local]# wget https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz
[root@node1 local]# tar zxf go1.9.linux-amd64.tar.gz
[root@node1 local]# vim /etc/profile
# Go
export GO_HOME=/usr/local/go
export PATH=$GO_HOME/bin:$PATH 
[root@node1 local]# source /etc/profile
# 查看版本信息
[root@node1 local]# go version
go version go1.9 linux/amd64
安裝go(需要go 1.6+)
[root@node1 local]# go get -u github.com/cloudflare/cfssl/cmd/...
[root@node1 local]# ls /root/go/bin/
cfssl  cfssl-bundle  cfssl-certinfo  cfssljson  cfssl-newkey  cfssl-scan  mkbundle  multirootca
[root@node1 local]# mv /root/go/bin/* /usr/local/bin/
安裝cfssl

 

2. 創建CA

(1)創建 CA 配置文件

[root@node1 local]# mkdir /opt/ssl
[root@node1 local]# cd /opt/ssl/
[root@node1 ssl]# cfssl print-defaults config > config.json
[root@node1 ssl]# cfssl print-defaults csr > csr.json
[root@node1 ssl]# vim ca-config.json 
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "8760h"
      }
    }
  }
}
創建CA配置文件

部分字段說明:

  • ca-config.json:可以定義多個 profiles,分別指定不同的過期時間、使用場景等參數;后續在簽名證書時使用某個 profile;
  • signing:表示該證書可用於簽名其它證書;生成的 ca.pem 證書中 CA=TRUE;
  • server auth:表示client可以用該 CA 對server提供的證書進行驗證;
  • client auth:表示server可以用該 CA 對client提供的證書進行驗證。

 

(2)創建 CA 證書簽名請求

[root@node1 ssl]# vim ca-csr.json
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
創建CA證書簽名請求

部分字段說明:

  • “CN”:Common Name,kube-apiserver 從證書中提取該字段作為請求的用戶名 (User Name);瀏覽器使用該字段驗證網站是否合法;
  • “O”:Organization,kube-apiserver 從證書中提取該字段作為請求用戶所屬的組 (Group);

 

(3)生成 CA 證書和私鑰

[root@node1 ssl]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca
2017/09/10 04:22:13 [INFO] generating a new CA key and certificate from CSR
2017/09/10 04:22:13 [INFO] generate received request
2017/09/10 04:22:13 [INFO] received CSR
2017/09/10 04:22:13 [INFO] generating key: rsa-2048
2017/09/10 04:22:13 [INFO] encoded CSR
2017/09/10 04:22:13 [INFO] signed certificate with serial number 348968532213237181927470194452366329323573808966
[root@node1 ssl]# ls ca*
ca-config.json  ca.csr  ca-csr.json  ca-key.pem  ca.pem
生成CA證書和私鑰

 

3. 創建 Kubernetes 證書

(1)創建 kubernetes 證書簽名請求

[root@node1 ssl]# vim kubernetes-csr.json
{
    "CN": "kubernetes",
    "hosts": [
      "127.0.0.1",
      "172.16.7.151",
      "172.16.7.152",
      "172.16.7.153",
      "172.16.206.32",
      "10.254.0.1",
      "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"
        }
    ]
}
創建kubernetes證書簽名請求

部分字段說明:

  • 如果 hosts 字段不為空則需要指定授權使用該證書的 IP 或域名列表,由於該證書后續被 etcd 集群和 kubernetes master 集群使用,所以上面分別指定了 etcd 集群、kubernetes master 集群的主機 IP;
  • 還需要添加kube-apiserver注冊的名為 kubernetes 服務的 IP(一般是 kue-apiserver 指定的 service-cluster-ip-range 網段的第一個IP,如 10.254.0.1)

 

(2)生成 kubernetes 證書和私鑰

[root@node1 ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes
2017/09/10 07:44:27 [INFO] generate received request
2017/09/10 07:44:27 [INFO] received CSR
2017/09/10 07:44:27 [INFO] generating key: rsa-2048
2017/09/10 07:44:27 [INFO] encoded CSR
2017/09/10 07:44:27 [INFO] signed certificate with serial number 695308968867503306176219705194671734841389082714
[root@node1 ssl]# ls kubernetes*
kubernetes.csr  kubernetes-csr.json  kubernetes-key.pem  kubernetes.pem
生成kubernetes證書和私鑰

或者直接在命令行上指定相關參數:

# echo '{"CN":"kubernetes","hosts":[""],"key":{"algo":"rsa","size":2048}}' | cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes -hostname="127.0.0.1,172.16.7.151,172.16.7.152,172.16.7.153,172.16.206.32,10.254.0.1,kubernetes,kubernetes.default" - | cfssljson -bare kubernetes

 

4. 創建 Admin 證書

(1)創建 admin 證書簽名請求

[root@node1 ssl]# vim admin-csr.json
{
  "CN": "admin",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
創建admin證書簽名請求

說明:

  • 后續 kube-apiserver 使用 RBAC 對客戶端(如 kubelet、kube-proxy、Pod)請求進行授權;
  • kube-apiserver 預定義了一些 RBAC 使用的 RoleBindings,如 cluster-admin 將 Groupsystem:masters 與 Role cluster-admin 綁定,該 Role 授予了調用kube-apiserver 的所有 API的權限;
  • OU 指定該證書的 Group 為 system:masters,kubelet 使用該證書訪問 kube-apiserver時 ,由於證書被 CA 簽名,所以認證通過,同時由於證書用戶組為經過預授權的system:masters,所以被授予訪問所有 API 的權限。

 

(2)生成 admin 證書和私鑰

[root@node1 ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin
2017/09/10 20:01:05 [INFO] generate received request
2017/09/10 20:01:05 [INFO] received CSR
2017/09/10 20:01:05 [INFO] generating key: rsa-2048
2017/09/10 20:01:05 [INFO] encoded CSR
2017/09/10 20:01:05 [INFO] signed certificate with serial number 580169825175224945071583937498159721917720511011
2017/09/10 20:01:05 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for websites. For more information see the Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
[root@node1 ssl]# ls admin*
admin.csr  admin-csr.json  admin-key.pem  admin.pem
生成admin證書和私鑰

 

5. 創建 Kube-Proxy 證書

(1)創建 kube-proxy 證書簽名請求

[root@node1 ssl]# vim kube-proxy-csr.json
{
  "CN": "system:kube-proxy",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
創建kube-proxy證書簽名請求

說明:

  • CN 指定該證書的 User 為 system:kube-proxy;
  • kube-apiserver 預定義的 RoleBinding cluster-admin 將User system:kube-proxy 與 Rolesystem:node-proxier 綁定,該 Role 授予了調用 kube-apiserver Proxy 相關 API 的權限。

 

(2)生成 kube-proxy 客戶端證書和私鑰

[root@node1 ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes  kube-proxy-csr.json | cfssljson -bare kube-proxy
2017/09/10 20:07:55 [INFO] generate received request
2017/09/10 20:07:55 [INFO] received CSR
2017/09/10 20:07:55 [INFO] generating key: rsa-2048
2017/09/10 20:07:55 [INFO] encoded CSR
2017/09/10 20:07:55 [INFO] signed certificate with serial number 655306618453852718922516297333812428130766975244
2017/09/10 20:07:55 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for websites. For more information see the Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
[root@node1 ssl]# ls kube-proxy*
kube-proxy.csr  kube-proxy-csr.json  kube-proxy-key.pem  kube-proxy.pem
生成kube-proxy客戶端證書和私鑰

 

6. 校驗證書

以校驗Kubernetes證書為例。

(1)使用openssl命令校驗證書

[root@node1 ssl]# openssl x509 -noout -text -in kubernetes.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            79:ca:bb:84:73:15:b1:db:aa:24:d7:a3:60:65:b0:55:27:a7:e8:5a
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=CN, ST=BeiJing, L=BeiJing, O=k8s, OU=System, CN=kubernetes
        Validity
            Not Before: Sep 10 11:39:00 2017 GMT
            Not After : Sep 10 11:39:00 2018 GMT
        Subject: C=CN, ST=BeiJing, L=BeiJing, O=k8s, OU=System, CN=kubernetes
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier: 
                79:48:C1:1B:81:DD:9C:75:04:EC:B6:35:26:5E:82:AA:2E:45:F6:C5
            X509v3 Subject Alternative Name: 
                DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster, DNS:kubernetes.default.svc.cluster.local, IP Address:127.0.0.1, IP Address:172.16.7.151, IP Address:172.16.7.152, IP Address:172.16.7.153, IP Address:172.16.206.32, IP Address:10.254.0.1
    ...
使用openssl命令校驗證書

【說明】:

  • 確認 Issuer 字段的內容和 ca-csr.json 一致;
  • 確認 Subject 字段的內容和 kubernetes-csr.json 一致;
  • 確認 X509v3 Subject Alternative Name 字段的內容和 kubernetes-csr.json 一致;
  • 確認 X509v3 Key Usage、Extended Key Usage 字段的內容和 ca-config.json 中 kubernetesprofile 一致。

 

(2)使用 Cfssl-Certinfo 命令校驗

[root@node1 ssl]# cfssl-certinfo -cert kubernetes.pem
{
  "subject": {
    "common_name": "kubernetes",
    "country": "CN",
    "organization": "k8s",
    "organizational_unit": "System",
    "locality": "BeiJing",
    "province": "BeiJing",
    "names": [
      "CN",
      "BeiJing",
      "BeiJing",
      "k8s",
      "System",
      "kubernetes"
    ]
  },
  "issuer": {
    "common_name": "kubernetes",
    "country": "CN",
    "organization": "k8s",
    "organizational_unit": "System",
    "locality": "BeiJing",
    "province": "BeiJing",
    "names": [
      "CN",
      "BeiJing",
      "BeiJing",
      "k8s",
      "System",
      "kubernetes"
    ]
  },
  "serial_number": "695308968867503306176219705194671734841389082714",
  "sans": [
    "kubernetes",
    "kubernetes.default",
    "kubernetes.default.svc",
    "kubernetes.default.svc.cluster",
    "kubernetes.default.svc.cluster.local",
    "127.0.0.1",
    "172.16.7.151",
    "172.16.7.152",
    "172.16.7.153",
    "172.16.206.32",
    "10.254.0.1"
  ],
  "not_before": "2017-09-10T11:39:00Z",
  "not_after": "2018-09-10T11:39:00Z",
  "sigalg": "SHA256WithRSA",
  "authority_key_id": "",
  "subject_key_id": "79:48:C1:1B:81:DD:9C:75:4:EC:B6:35:26:5E:82:AA:2E:45:F6:C5",
  ...
使用 Cfssl-Certinfo 命令校驗

 

7. 分發證書

將生成的證書和秘鑰文件(后綴名為.pem)拷貝到所有機器的 /etc/kubernetes/ssl 目錄下備用:

[root@node1 ssl]# mkdir -p /etc/kubernetes/ssl
[root@node1 ssl]# cp *.pem /etc/kubernetes/ssl
[root@node1 ssl]# scp -p *.pem root@172.16.7.152:/etc/kubernetes/ssl/
[root@node1 ssl]# scp -p *.pem root@172.16.7.153:/etc/kubernetes/ssl/
[root@node1 ssl]# scp -p *.pem root@172.16.206.32:/etc/kubernetes/ssl/
分發證書

 

四、下載和配置 kubectl(kubecontrol) 命令行工具

1. 下載kubectl

[root@node1 local]# wget https://dl.k8s.io/v1.6.0/kubernetes-client-linux-amd64.tar.gz
[root@node1 local]# tar zxf kubernetes-client-linux-amd64.tar.gz
[root@node1 local]# cp kubernetes/client/bin/kube* /usr/bin/
[root@node1 local]# chmod +x /usr/bin/kube*

 

2. 創建 kubectl kubeconfig 文件

[root@node1 local]# cd /etc/kubernetes/
[root@node1 kubernetes]# export KUBE_APISERVER="https://172.16.7.151:6443"
# 設置集群參數
[root@node1 kubernetes]# kubectl config set-cluster kubernetes \
> --certificate-authority=/etc/kubernetes/ssl/ca.pem \
> --embed-certs=true \
> --server=${KUBE_APISERVER}
Cluster "kubernetes" set.
# 設置客戶端認證參數
[root@node1 kubernetes]# kubectl config set-credentials admin \
> --client-certificate=/etc/kubernetes/ssl/admin.pem \
> --embed-certs=true \
> --client-key=/etc/kubernetes/ssl/admin-key.pem
User "admin" set.
# 設置上下文參數
[root@node1 kubernetes]# kubectl config set-context kubernetes \
> --cluster=kubernetes \
> --user=admin
Context "kubernetes" set
# 設置默認上下文
[root@node1 kubernetes]# kubectl config use-context kubernetes
Switched to context "kubernetes".
[root@node1 kubernetes]# ls ~/.kube/config 
/root/.kube/config
創建 kubectl kubeconfig 文件

【說明】:

  • admin.pem 證書 OU 字段值為 system:masters,kube-apiserver 預定義的 RoleBinding cluster-admin 將 Groupsystem:masters 與 Role cluster admin 綁定,該 Role 授予了調用kube-apiserver 相關 API 的權限;
  • 生成的 kubeconfig 被保存到 ~/.kube/config 文件。

 

五、創建 kubeconfig 文件

kubelet、kube-proxy 等 Node 機器上的進程與 Master 機器的 kube-apiserver 進程通信時需要認證和授權。

kubernetes 1.4 開始支持由 kube-apiserver 為客戶端生成 TLS 證書的 TLS Bootstrapping 功能,這樣就不需要為每個客戶端生成證書了;該功能當前僅支持為 kubelet 生成證書。


1. 創建 TLS Bootstrapping Token

(1)Token auth file

[root@node1 ssl]# export BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')
[root@node1 ssl]# cat > token.csv <<EOF
> ${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
> EOF

將token.csv發到所有機器(Master 和 Node)的 /etc/kubernetes/ 目錄。

[root@node1 ssl]# cp token.csv /etc/kubernetes/
[root@node1 ssl]# scp -p token.csv root@172.16.7.152:/etc/kubernetes/
[root@node1 ssl]# scp -p token.csv root@172.16.7.153:/etc/kubernetes/

 

(2)創建 kubelet bootstrapping kubeconfig 文件

[root@node1 ssl]# cd /etc/kubernetes
[root@node1 kubernetes]# export KUBE_APISERVER="https://172.16.7.151:6443"
# 設置集群參數
> --certificate-authority=/etc/kubernetes/ssl/ca.pem \
> --embed-certs=true \
> --server=${KUBE_APISERVER} \
> --kubeconfig=bootstrap.kubeconfig
Cluster "kubernetes" set.
# 設置客戶端認證參數
[root@node1 kubernetes]# kubectl config set-credentials kubelet-bootstrap \
> --token=${BOOTSTRAP_TOKEN} \
> --kubeconfig=bootstrap.kubeconfig
User "kubelet-bootstrap" set.
# 設置上下文參數
[root@node1 kubernetes]# kubectl config set-context default \
> --cluster=kubernetes \
> --user=kubelet-bootstrap \
> --kubeconfig=bootstrap.kubeconfig
Context "default" created.
# 設置默認上下文
[root@node1 kubernetes]# kubectl config use-context default --kubeconfig=bootstrap.kubeconfig
Switched to context "default".
創建 kubelet bootstrapping kubeconfig 文件

【說明】:

  • --embed-certs 為 true 時表示將 certificate-authority 證書寫入到生成的 bootstrap.kubeconfig 文件中;
  • 設置客戶端認證參數時沒有指定秘鑰和證書,后續由 kube-apiserver 自動生成。

 

2. 創建 kube-proxy kubeconfig 文件

[root@node1 kubernetes]# export KUBE_APISERVER="https://172.16.7.151:6443"
# 設置集群參數
[root@node1 kubernetes]# kubectl config set-cluster kubernetes \
> --certificate-authority=/etc/kubernetes/ssl/ca.pem \
> --embed-certs=true \
> --server=${KUBE_APISERVER} \
> --kubeconfig=kube-proxy.kubeconfig
Cluster "kubernetes" set.
# 設置客戶端認證參數
[root@node1 kubernetes]# 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
User "kube-proxy" set.
# 設置上下文參數
[root@node1 kubernetes]# kubectl config set-context default \
> --cluster=kubernetes \
> --user=kube-proxy \
> --kubeconfig=kube-proxy.kubeconfig
Context "default" created.
# 設置默認上下文
[root@node1 kubernetes]# kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
Switched to context "default".
創建 kube-proxy kubeconfig 文件

【說明】:

  • 設置集群參數和客戶端認證參數時 --embed-certs 都為 true,這會將 certificate-authority、client-certificate 和client-key 指向的證書文件內容寫入到生成的 kube-proxy.kubeconfig 文件中;
  • kube-proxy.pem 證書中 CN 為 system:kube-proxy,kube-apiserver 預定義的 RoleBinding cluster-admin 將Usersystem:kube-proxy 與 Role system:node-proxier 綁定,該 Role 授予了調用 kube-apiserver Proxy 相關 API 的權限。

 

3. 分發 kubeconfig 文件

將兩個 kubeconfig 文件分發到所有 Node 機器的 /etc/kubernetes/ 目錄。

[root@node1 kubernetes]# scp -p bootstrap.kubeconfig root@172.16.7.152:/etc/kubernetes/
[root@node1 kubernetes]# scp -p kube-proxy.kubeconfig root@172.16.7.152:/etc/kubernetes/
[root@node1 kubernetes]# scp -p bootstrap.kubeconfig root@172.16.7.153:/etc/kubernetes/
[root@node1 kubernetes]# scp -p kube-proxy.kubeconfig root@172.16.7.153:/etc/kubernetes/
分發 kubeconfig 文件

 

六、創建高可用 etcd 集群

etcd 是 CoreOS 團隊發起的開源項目,基於 Go 語言實現,做為一個分布式鍵值對存儲,通過分布式鎖,leader選舉和寫屏障(write barriers)來實現可靠的分布式協作。

kubernetes系統使用etcd存儲所有數據。

CoreOS官方推薦集群規模5個為宜,我這里使用了3個節點。

 

1. 安裝配置etcd集群

搭建etcd集群有3種方式,分別為Static, etcd Discovery, DNS Discovery。Discovery請參見官網。這里僅以Static方式展示一次集群搭建過程。

首先請做好3個節點的時間同步,方式有很多,請自行百度搜索。

(1)TLS 認證文件

需要為 etcd 集群創建加密通信的 TLS 證書,這里復用以前創建的 kubernetes 證書。

[root@node1 ssl]# cp ca.pem kubernetes-key.pem kubernetes.pem /etc/kubernetes/ssl

上面這步在之前做過,可以忽略不做。【注意】:kubernetes 證書的 hosts 字段列表中包含上面三台機器的 IP,否則后續證書校驗會失敗。

 

(2)下載二進制文件

到 https://github.com/coreos/etcd/releases 頁面下載最新版本的二進制文件,並上傳到/usr/local/目錄下。

[root@node1 local]# tar xf etcd-v3.2.7-linux-amd64.tar
[root@node1 local]# mv etcd-v3.2.7-linux-amd64/etcd* /usr/local/bin/

etcd集群中另外兩台機器也需要如上操作。

 

(3)創建 etcd 的 systemd unit 文件

配置文件模板如下,注意替換 ETCD_NAME 和 INTERNAL_IP 變量的值。

[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/
EnvironmentFile=-/etc/etcd/etcd.conf
ExecStart=/usr/local/bin/etcd \
  --name ${ETCD_NAME} \
  --cert-file=/etc/kubernetes/ssl/kubernetes.pem \
  --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
  --peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
  --peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
  --trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
  --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
  --initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \
  --listen-peer-urls https://${INTERNAL_IP}:2380 \
  --listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://${INTERNAL_IP}:2379 \
  --initial-cluster-token etcd-cluster-0 \
  --initial-cluster node1=https://172.16.7.151:2380,node2=https://172.16.7.152:2380,node3=https://172.16.7.153:2380 \
  --initial-cluster-state new \
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

針對上面幾個配置參數做下簡單的解釋:

  • --name:方便理解的節點名稱,默認為default,在集群中應該保持唯一,可以使用 hostname
  • --data-dir:服務運行數據保存的路徑,默認為 ${name}.etcd
  • --snapshot-count:指定有多少事務(transaction)被提交時,觸發截取快照保存到磁盤
  • --heartbeat-interval:leader 多久發送一次心跳到 followers。默認值是 100ms
  • --eletion-timeout:重新投票的超時時間,如果 follow 在該時間間隔沒有收到心跳包,會觸發重新投票,默認為 1000 ms
  • --listen-peer-urls:和同伴通信的地址,比如 http://ip:2380。如果有多個,使用逗號分隔。需要所有節點都能夠訪問,所以不要使用 localhost!
  • --listen-client-urls:對外提供服務的地址:比如 http://ip:2379,http://127.0.0.1:2379,客戶端會連接到這里和 etcd 交互
  • --advertise-client-urls:對外公告的該節點客戶端監聽地址,這個值會告訴集群中其他節點
  • --initial-advertise-peer-urls:該節點同伴監聽地址,這個值會告訴集群中其他節點
  • --initial-cluster:集群中所有節點的信息,格式為 node1=http://ip1:2380,node2=http://ip2:2380,…。注意:這里的 node1 是節點的 --name 指定的名字;后面的 ip1:2380 是 --initial-advertise-peer-urls 指定的值
  • --initial-cluster-state:新建集群的時候,這個值為new;假如已經存在的集群,這個值為 existing
  • --initial-cluster-token:創建集群的token,這個值每個集群保持唯一。這樣的話,如果你要重新創建集群,即使配置和之前一樣,也會再次生成新的集群和節點 uuid;否則會導致多個集群之間的沖突,造成未知的錯誤

所有以--init開頭的配置都是在bootstrap集群的時候才會用到,后續節點的重啟會被忽略。

 

[root@node1 local]# mkdir -p /var/lib/etcd
[root@node1 local]# cd /etc/systemd/system/
[root@node1 system]# vim etcd.service
[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/
EnvironmentFile=-/etc/etcd/etcd.conf
ExecStart=/usr/local/bin/etcd \
  --name node1 \
  --cert-file=/etc/kubernetes/ssl/kubernetes.pem \
  --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
  --peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
  --peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
  --trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
  --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
  --initial-advertise-peer-urls https://172.16.7.151:2380 \
  --listen-peer-urls https://172.16.7.151:2380 \
  --listen-client-urls https://172.16.7.151:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://172.16.7.151:2379 \
  --initial-cluster-token etcd-cluster-0 \
  --initial-cluster node1=https://172.16.7.151:2380,node2=https://172.16.7.152:2380,node3=https://172.16.7.153:2380 \
  --initial-cluster-state new \
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
node1的etcd.service
[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/
EnvironmentFile=-/etc/etcd/etcd.conf
ExecStart=/usr/local/bin/etcd \
  --name node2 \
  --cert-file=/etc/kubernetes/ssl/kubernetes.pem \
  --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
  --peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
  --peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
  --trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
  --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
  --initial-advertise-peer-urls https://172.16.7.152:2380 \
  --listen-peer-urls https://172.16.7.152:2380 \
  --listen-client-urls https://172.16.7.152:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://172.16.7.152:2379 \
  --initial-cluster-token etcd-cluster-0 \
  --initial-cluster node1=https://172.16.7.151:2380,node2=https://172.16.7.152:2380,node3=https://172.16.7.153:2380 \
  --initial-cluster-state new \
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
node2的etcd.service
[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/
EnvironmentFile=-/etc/etcd/etcd.conf
ExecStart=/usr/local/bin/etcd \
  --name node3 \
  --cert-file=/etc/kubernetes/ssl/kubernetes.pem \
  --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
  --peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
  --peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
  --trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
  --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
  --initial-advertise-peer-urls https://172.16.7.153:2380 \
  --listen-peer-urls https://172.16.7.153:2380 \
  --listen-client-urls https://172.16.7.153:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://172.16.7.153:2379 \
  --initial-cluster-token etcd-cluster-0 \
  --initial-cluster node1=https://172.16.7.151:2380,node2=https://172.16.7.152:2380,node3=https://172.16.7.153:2380 \
  --initial-cluster-state new \
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
node3的etcd.service

【說明】:

  • 指定 etcd 的工作目錄為 /var/lib/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);
  • 創建 kubernetes.pem 證書時使用的 kubernetes-csr.json 文件的 hosts 字段包含所有 etcd 節點的 INTERNAL_IP,否則證書校驗會出錯;
  • --initial-cluster-state 值為 new 時,--name 的參數值必須位於 --initial-cluster 列表中。

 

2. 啟動 etcd 服務

集群中的節點都執行以下命令:

# systemctl daemon-reload
# systemctl enable etcd
# systemctl start etcd

 

3. 驗證服務

etcdctl 是一個命令行客戶端,它能提供一些簡潔的命令,供用戶直接跟 etcd 服務打交道,而無需基於 HTTP API 方式。這在某些情況下將很方便,例如用戶對服務進行測試或者手動修改數據庫內容。我們也推薦在剛接觸 etcd 時通過 etcdctl 命令來熟悉相關的操作,這些操作跟 HTTP API 實際上是對應的。

在etcd集群任意一台機器上執行如下命令:

(1)查看集群健康狀態

[root@node1 system]# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" cluster-health            
member 31800ab6b566b2b is healthy: got healthy result from https://172.16.7.151:2379
member 9a0745d96695eec6 is healthy: got healthy result from https://172.16.7.153:2379
member e64edc68e5e81b55 is healthy: got healthy result from https://172.16.7.152:2379
cluster is healthy
查看etcd集群健康狀態

結果最后一行為 cluster is healthy 時表示集群服務正常。

 

(2)查看集群成員,並能看出哪個是leader節點

[root@node1 system]# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" member list
31800ab6b566b2b: name=node1 peerURLs=https://172.16.7.151:2380 clientURLs=https://172.16.7.151:2379 isLeader=false
9a0745d96695eec6: name=node3 peerURLs=https://172.16.7.153:2380 clientURLs=https://172.16.7.153:2379 isLeader=false
e64edc68e5e81b55: name=node2 peerURLs=https://172.16.7.152:2380 clientURLs=https://172.16.7.152:2379 isLeader=true

 

(3)刪除一個節點

# 如果你想更新一個節點的IP(peerURLS),首先你需要知道那個節點的ID
# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" member list
31800ab6b566b2b: name=node1 peerURLs=https://172.16.7.151:2380 clientURLs=https://172.16.7.151:2379 isLeader=false
9a0745d96695eec6: name=node3 peerURLs=https://172.16.7.153:2380 clientURLs=https://172.16.7.153:2379 isLeader=false
e64edc68e5e81b55: name=node2 peerURLs=https://172.16.7.152:2380 clientURLs=https://172.16.7.152:2379 isLeader=true
# 刪除一個節點
# etcdctl --endpoints "http://192.168.2.210:2379" member remove 9a0745d96695eec6

 

七、部署 kubernetes master節點

kubernetes master 節點包含的組件:

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

目前這三個組件需要部署在同一台機器上。

kube-scheduler、kube-controller-manager 和 kube-apiserver 三者的功能緊密相關;
同時只能有一個 kube-scheduler、kube-controller-manager 進程處於工作狀態,如果運行多個,則需要通過選舉產生一個 leader。

 

1. TLS 證書文件

檢查之前生成的證書。

[root@node1 kubernetes]# ls /etc/kubernetes/ssl
admin-key.pem  admin.pem  ca-key.pem  ca.pem  kube-proxy-key.pem  kube-proxy.pem  kubernetes-key.pem  kubernetes.pem

 

2. 下載二進制文件

有兩種下載方式:

  • 方式一:從 github release 頁面下載發布版 tarball,解壓后再執行下載腳本。
[root@node1 local]# cd /opt/
[root@node1 opt]# wget https://github.com/kubernetes/kubernetes/releases/download/v1.6.0/kubernetes.tar.gz
[root@node1 opt]# tar zxf kubernetes.tar.gz 
[root@node1 opt]# cd kubernetes/
[root@node1 kubernetes]# ./cluster/get-kube-binaries.sh
Kubernetes release: v1.6.0
Server: linux/amd64  (to override, set KUBERNETES_SERVER_ARCH)
Client: linux/amd64  (autodetected)

Will download kubernetes-server-linux-amd64.tar.gz from https://storage.googleapis.com/kubernetes-release/release/v1.6.0
Will download and extract kubernetes-client-linux-amd64.tar.gz from https://storage.googleapis.com/kubernetes-release/release/v1.6.0
Is this ok? [Y]/n
y
...
從github release頁面下載
  • 方式二:從 CHANGELOG頁面 下載 client 或 server tarball 文件。server 的 tarball kubernetes-server-linux-amd64.tar.gz 已經包含了 client(kubectl) 二進制文件,所以不用單獨下載kubernetes-client-linux-amd64.tar.gz文件。
wget https://dl.k8s.io/v1.6.0/kubernetes-server-linux-amd64.tar.gz
tar -xzvf kubernetes-server-linux-amd64.tar.gz
...
cd kubernetes
tar -xzvf  kubernetes-src.tar.gz
從 CHANGELOG頁面 下載 client 或 server tarball 文件

 

將二進制文件拷貝到指定路徑:

[root@node1 kubernetes]# pwd
/opt/kubernetes
[root@node1 kubernetes]# cd server/
[root@node1 server]# cp -r kubernetes/server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler,kubectl,kube-proxy,kubelet} /usr/local/bin/

 

3. 配置和啟動 kube-apiserver

(1)創建 kube-apiserver的service配置文件

在/usr/lib/systemd/system/下創建kube-apiserver.service,內容如下:

[Unit]
Description=Kubernetes API Service
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
After=etcd.service

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/apiserver
ExecStart=/usr/local/bin/kube-apiserver \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBE_ETCD_SERVERS \
            $KUBE_API_ADDRESS \
            $KUBE_API_PORT \
            $KUBELET_PORT \
            $KUBE_ALLOW_PRIV \
            $KUBE_SERVICE_ADDRESSES \
            $KUBE_ADMISSION_CONTROL \
            $KUBE_API_ARGS
Restart=on-failure
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
kube-apiserver.service

上面的配置文件中用到的/etc/kubernetes/config文件的內容為:

###
# kubernetes system config
#
# The following values are used to configure various aspects of all
# kubernetes services, including
#
#   kube-apiserver.service
#   kube-controller-manager.service
#   kube-scheduler.service
#   kubelet.service
#   kube-proxy.service
# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true"

# journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0"

# Should this cluster be allowed to run privileged docker containers
KUBE_ALLOW_PRIV="--allow-privileged=true"

# How the controller-manager, scheduler, and proxy find the apiserver
#KUBE_MASTER="--master=http://domainName:8080"
KUBE_MASTER="--master=http://172.16.7.151:8080"
config

該配置文件同時被kube-apiserver、kube-controller-manager、kube-scheduler、kubelet、kube-proxy使用。

 

創建apiserver配置文件/etc/kubernetes/apiserver:

###
## kubernetes system config
##
## The following values are used to configure the kube-apiserver
##
#
## The address on the local server to listen to.
#KUBE_API_ADDRESS="--insecure-bind-address=sz-pg-oam-docker-test-001.tendcloud.com"
KUBE_API_ADDRESS="--advertise-address=172.16.7.151 --bind-address=172.16.7.151 --insecure-bind-address=172.16.7.151"
#
## The port on the local server to listen on.
#KUBE_API_PORT="--port=8080"
#
## Port minions listen on
#KUBELET_PORT="--kubelet-port=10250"
#
## Comma separated list of nodes in the etcd cluster
KUBE_ETCD_SERVERS="--etcd-servers=https://172.16.7.151:2379,https://172.16.7.152:2379,https://172.16.7.153:2379"
#
## Address range to use for services
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.254.0.0/16"
#
## default admission control policies
KUBE_ADMISSION_CONTROL="--admission-control=ServiceAccount,NamespaceLifecycle,NamespaceExists,LimitRanger,ResourceQuota"
#
## Add your own!
KUBE_API_ARGS="--authorization-mode=RBAC --runtime-config=rbac.authorization.k8s.io/v1beta1 --kubelet-https=true --experimental-bootstrap-token-auth --token-auth-file=/etc/kubernetes/token.csv --service-node-port-range=30000-32767 --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 --enable-swagger-ui=true --apiserver-count=3 --audit-log-maxage=30 --audit-log-maxbackup=3 --audit-log-maxsize=100 --audit-log-path=/var/lib/audit.log --event-ttl=1h"
apiserver

【說明】:

  • --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;
  • runtime-config配置為rbac.authorization.k8s.io/v1beta1,表示運行時的apiVersion;
  • --service-cluster-ip-range 指定 Service Cluster IP 地址段,該地址段不能路由可達;
  • 缺省情況下 kubernetes 對象保存在 etcd /registry 路徑下,可以通過 --etcd-prefix 參數進行調整。

 

(2)啟動kube-apiserver

# systemctl daemon-reload
# systemctl enable kube-apiserver
# systemctl start kube-apiserver

啟動過程中可以觀察日志:

# tail -f /var/log/message

 

4. 配置和啟動 kube-controller-manager

(1)創建 kube-controller-manager 的service配置文件

在/usr/lib/systemd/system/下創建kube-controller-manager.service,內容如下:

Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/controller-manager
ExecStart=/usr/local/bin/kube-controller-manager \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBE_MASTER \
            $KUBE_CONTROLLER_MANAGER_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
kube-controller-manager.service

創建kube-controller-manager配置文件/etc/kubernetes/controller-manager:

# vim /etc/kubernetes/controller-manager
###
# The following values are used to configure the kubernetes controller-manager

# defaults from config and apiserver should be adequate

# Add your own!
KUBE_CONTROLLER_MANAGER_ARGS="--address=127.0.0.1 --service-cluster-ip-range=10.254.0.0/16 --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"
controller-manager

【說明】:

  • --service-cluster-ip-range 參數指定 Cluster 中 Service 的CIDR范圍,該網絡在各 Node 間必須路由不可達,必須和 kube-apiserver 中的參數一致;
  • --cluster-signing-* 指定的證書和私鑰文件用來簽名為 TLS BootStrap 創建的證書和私鑰;
  • --root-ca-file 用來對 kube-apiserver 證書進行校驗,指定該參數后,才會在Pod 容器的 ServiceAccount 中放置該 CA 證書文件;
  • --address 值必須為 127.0.0.1,因為當前 kube-apiserver 期望 scheduler 和 controller-manager 在同一台機器,否則:
# kubectl get componentstatuses
NAME                 STATUS      MESSAGE                                                                                        ERROR
scheduler            Unhealthy   Get http://127.0.0.1:10251/healthz: dial tcp 127.0.0.1:10251: getsockopt: connection refused   
controller-manager   Healthy     ok                                                                                             
etcd-2               Unhealthy   Get http://172.20.0.113:2379/health: malformed HTTP response "\x15\x03\x01\x00\x02\x02"        
etcd-0               Healthy     {"health": "true"}                                                                             
etcd-1               Healthy     {"health": "true"}

參考:https://github.com/kubernetes-incubator/bootkube/issues/64

 

(2)啟動 kube-controller-manager

# systemctl daemon-reload
# systemctl enable kube-controller-manager
# systemctl start kube-controller-manager
啟動 kube-controller-manager

 

5. 配置和啟動 kube-scheduler

(1)創建 kube-scheduler的serivce配置文件

在/usr/lib/systemd/system/下創建kube-scheduler.service,內容如下:

[Unit]
Description=Kubernetes Scheduler Plugin
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/scheduler
ExecStart=/usr/local/bin/kube-scheduler \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBE_MASTER \
            $KUBE_SCHEDULER_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
kube-scheduler.service

創建kube-scheduler配置文件/etc/kubernetes/scheduler:

# vim /etc/kubernetes/scheduler
###
# kubernetes scheduler config

# default config should be adequate

# Add your own!
KUBE_SCHEDULER_ARGS="--leader-elect=true --address=127.0.0.1"
scheduler

【說明】:

  • --address 值必須為 127.0.0.1,因為當前 kube-apiserver 期望 scheduler 和 controller-manager 在同一台機器。

 

(2)啟動 kube-scheduler

# systemctl daemon-reload
# systemctl enable kube-scheduler
# systemctl start kube-scheduler
啟動 kube-scheduler

 

6. 驗證 master 節點功能

# kubectl get componentstatuses
NAME                 STATUS    MESSAGE              ERROR
scheduler            Healthy   ok                   
controller-manager   Healthy   ok                   
etcd-0               Healthy   {"health": "true"}   
etcd-1               Healthy   {"health": "true"}   
etcd-2               Healthy   {"health": "true"}  
驗證 master 節點功能

 

八、部署kubernetes node節點

kubernetes node 節點包含如下組件:

  • Docker 1.12.6
  • Flanneld
  • kubelet
  • kube-prox

 

1. 安裝Docker

參見之前的文章《Docker鏡像和容器》。

 

2. 安裝配置Flanneld

(1)Flannel介紹

  • Flannel是CoreOS團隊針對Kubernetes設計的一個網絡規划服務,簡單來說,它的功能是讓集群中的不同節點主機創建的Docker容器都具有全集群唯一的虛擬IP地址。
  • 在默認的Docker配置中,每個節點上的Docker服務會分別負責所在節點容器的IP分配。這樣導致的一個問題是,不同節點上容器可能獲得相同的內外IP地址。
  • Flannel的設計目的就是為集群中的所有節點重新規划IP地址的使用規則,從而使得不同節點上的容器能夠獲得“同屬一個內網”且”不重復的”IP地址,並讓屬於不同節點上的容器能夠直接通過內網IP通信。
  • Flannel實質上是一種“覆蓋網絡(overlay network)”,也就是將TCP數據包裝在另一種網絡包里面進行路由轉發和通信,目前已經支持udp、vxlan、host-gw、aws-vpc、gce和alloc路由等數據轉發方式,默認的節點間數據通信方式是UDP轉發。

在Flannel的GitHub頁面有如下的一張原理圖:

  • 數據從源容器中發出后,經由所在主機的docker0虛擬網卡轉發到flannel0虛擬網卡,這是個P2P的虛擬網卡,flanneld服務監聽在網卡的另外一端。(Flannel通過ETCD服務維護了一張節點間的路由表);
  • 源主機的flanneld服務將原本的數據內容UDP封裝后根據自己的路由表投遞給目的節點的flanneld服務,數據到達后被解包,然后直接進入目的節點的flannel0虛擬網卡,然后被轉發到目的主機的docker0虛擬網卡;
  • 最后就像本機容器通信一樣由docker0路由到目標容器,這樣整個數據包的傳遞就完成了。

 

(2)安裝配置flannel

我這里使用yum安裝,安裝的版本是0.7.1。集群中的3台node都需要安裝配置flannel。

# yum install -y flannel
# rpm -ql flannel
/etc/sysconfig/flanneld
/run/flannel
/usr/bin/flanneld
/usr/bin/flanneld-start
/usr/lib/systemd/system/docker.service.d/flannel.conf
/usr/lib/systemd/system/flanneld.service
/usr/lib/tmpfiles.d/flannel.conf
/usr/libexec/flannel
/usr/libexec/flannel/mk-docker-opts.sh
...
安裝flannel

修改flannel配置文件:

# vim /etc/sysconfig/flanneld
# Flanneld configuration options  

# etcd url location.  Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="https://172.16.7.151:2379,https://172.16.7.152:2379,https://172.16.7.153:2379"

# etcd config key.  This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX="/kube-centos/network"

# Any additional options that you want to pass
#FLANNEL_OPTIONS=""
FLANNEL_OPTIONS="-etcd-cafile=/etc/kubernetes/ssl/ca.pem -etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem -etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem"
/etc/sysconfig/flanneld

【說明】: 

  • etcd的地址FLANNEL_ETCD_ENDPOINT
  • etcd查詢的目錄,包含docker的IP地址段配置。FLANNEL_ETCD_PREFIX

 

(3)在etcd中初始化flannel網絡數據

多個node上的Flanneld依賴一個etcd cluster來做集中配置服務,etcd保證了所有node上flanned所看到的配置是一致的。同時每個node上的flanned監聽etcd上的數據變化,實時感知集群中node的變化。

執行下面的命令為docker分配IP地址段:

# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" mkdir /kube-centos/network
# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" mk /kube-centos/network/config '{"Network": "172.30.0.0/16", "SubnetLen": 24, "Backend": { "Type": "vxlan" }}'
{"Network": "172.30.0.0/16", "SubnetLen": 24, "Backend": { "Type": "vxlan" }}

 

(4)啟動flannel

集群中的3台node都啟動flannel:

# systemctl daemon-reload
# systemctl start flanneld
啟動flannel

啟動完成后,會在/run/flannel/目錄下生成兩個文件,以node1為例:

# ls /run/flannel/         
docker  subnet.env
# cd /run/flannel/
[root@node1 flannel]# cat docker 
DOCKER_OPT_BIP="--bip=172.30.51.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1450"
DOCKER_NETWORK_OPTIONS=" --bip=172.30.51.1/24 --ip-masq=true --mtu=1450"
# cat subnet.env 
FLANNEL_NETWORK=172.30.0.0/16
FLANNEL_SUBNET=172.30.51.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false

現在查詢etcd中的內容可以看到:

# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" ls /kube-centos/network/subnets
/kube-centos/network/subnets/172.30.51.0-24
/kube-centos/network/subnets/172.30.29.0-24
/kube-centos/network/subnets/172.30.19.0-24

設置docker0網橋的IP地址(集群中node節點都需要設置):

# source /run/flannel/subnet.env
# ifconfig docker0 $FLANNEL_SUBNET

這樣docker0和flannel網橋會在同一個子網中,查看node1主機網卡:

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.30.51.1  netmask 255.255.255.0  broadcast 172.30.51.255
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 172.30.51.0  netmask 255.255.255.255  broadcast 0.0.0.0

重啟docker:

# systemctl restart docker

【注意】:經過測試,docker 17.06.1-ce版本重啟后,docker0網橋又會被重置為172.17.0.1,docker 1.12.6版本測試是不會有問題的。

如果想重新設置flannel,先停止flanneld,清理etcd里的數據,然后 ifconfig flannel.1 down,然后啟動flanneld,會重新生成子網,並up flannel.1網橋設備。

 

(5)測試跨主機容器通信

分別在node1和node2上啟動一個容器,然后ping對方容器的地址:

[root@node1 flannel]# docker run -i -t centos /bin/bash
[root@38be151deb71 /]# yum install net-tools -y
[root@38be151deb71 /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 172.30.51.2  netmask 255.255.255.0  broadcast 0.0.0.0

[root@node2 flannel]# docker run -i -t centos /bin/bash
[root@90e85c215fda /]# yum install net-tools -y
[root@90e85c215fda /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 172.30.29.2  netmask 255.255.255.0  broadcast 0.0.0.0
[root@90e85c215fda /]# ping 172.16.51.2  
PING 172.16.51.2 (172.16.51.2) 56(84) bytes of data.
64 bytes from 172.16.51.2: icmp_seq=1 ttl=254 time=1.00 ms
64 bytes from 172.16.51.2: icmp_seq=2 ttl=254 time=1.29 ms
測試跨主機容器通信

 

(6)補充:下載二進制包安裝flannel

從官網 https://github.com/coreos/flannel/releases 下載的flannel release 0.7.1,並將下載的文件上傳到服務器的/opt/flannel/目錄下。

# mkdir flannel
# cd flannel/
# tar xf flannel-v0.7.1-linux-amd64.tar  
# ls
flanneld  flannel-v0.7.1-linux-amd64.tar  mk-docker-opts.sh  README.md
下載flannel二進制包

mk-docker-opts.sh是用來Generate Docker daemon options based on flannel env file。

執行 ./mk-docker-opts.sh -i 將會生成如下兩個文件環境變量文件。

Flannel的文檔中有寫Docker Integration:
Docker daemon accepts --bip argument to configure the subnet of the docker0 bridge. It also accepts --mtu to set the MTU for docker0 and veth devices that it will be creating.
Because flannel writes out the acquired subnet and MTU values into a file, the script starting Docker can source in the values and pass them to Docker daemon:

source /run/flannel/subnet.env
docker daemon --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU} &

Systemd users can use EnvironmentFile directive in the .service file to pull in /run/flannel/subnet.env

 

3. 安裝和配置 kubelet

kubelet 啟動時向 kube-apiserver 發送 TLS bootstrapping 請求,需要先將 bootstrap token 文件中的 kubelet-bootstrap 用戶賦予 system:node-bootstrapper cluster 角色(role), 然后 kubelet 才能有權限創建認證請求(certificate signing requests):

# cd /etc/kubernetes
[root@node1 kubernetes]# kubectl create clusterrolebinding kubelet-bootstrap \
> --clusterrole=system:node-bootstrapper \
> --user=kubelet-bootstrap
clusterrolebinding "kubelet-bootstrap" created  

【注意】:以上這步只需要在kubernetes node集群中的一台執行一次就可以了。

【說明】:

  • --user=kubelet-bootstrap 是在 /etc/kubernetes/token.csv 文件中指定的用戶名,同時也寫入了/etc/kubernetes/bootstrap.kubeconfig 文件。

 

(1)下載最新的 kubelet 和 kube-proxy 二進制文件

這個在之前安裝kubernetes master時已經下載好了二進制文件,只需要復制到相應目錄即可。

[root@node1 kubernetes]# cd /opt/kubernetes/server/kubernetes/server/bin/
[root@node1 bin]# scp -p kubelet root@172.16.7.152:/usr/local/bin/
[root@node1 bin]# scp -p kube-proxy root@172.16.7.152:/usr/local/bin/
[root@node1 bin]# scp -p kubelet root@172.16.7.153:/usr/local/bin/
[root@node1 bin]# scp -p kube-proxy root@172.16.7.153:/usr/local/bin/

 

(2)配置kubelet

以下操作需要在集群的kubernetes node節點上都要運行,下面以node1服務器為例:

a.創建 kubelet 的service配置文件:

在/usr/lib/systemd/system/下創建文件kubelet.serivce:

[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/var/lib/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/local/bin/kubelet \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBELET_API_SERVER \
            $KUBELET_ADDRESS \
            $KUBELET_PORT \
            $KUBELET_HOSTNAME \
            $KUBE_ALLOW_PRIV \
            $KUBELET_POD_INFRA_CONTAINER \
            $KUBELET_ARGS
Restart=on-failure

[Install]
WantedBy=multi-user.target
kubelet.service

 

b.創建kubelet配置文件

創建kubelet工作目錄(必須創建,否則kubelet啟動不了):

# mkdir /var/lib/kubelet

創建kubelet配置文件:

###
## kubernetes kubelet (minion) config
#
## The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)
KUBELET_ADDRESS="--address=172.16.7.151"
#
## The port for the info server to serve on
#KUBELET_PORT="--port=10250"
#
## You may leave this blank to use the actual hostname
KUBELET_HOSTNAME="--hostname-override=172.16.7.151"
#
## location of the api-server
KUBELET_API_SERVER="--api-servers=http://172.16.7.151:8080"
#
## pod infrastructure container
#KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=sz-pg-oam-docker-hub-001.tendcloud.com/library/pod-infrastructure:rhel7"
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure"
#
## Add your own!
KUBELET_ARGS="--cgroup-driver=systemd --cluster-dns=10.254.0.2 --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig --kubeconfig=/etc/kubernetes/kubelet.kubeconfig --require-kubeconfig --cert-dir=/etc/kubernetes/ssl --cluster-domain=cluster.local. --hairpin-mode promiscuous-bridge --serialize-image-pulls=false"
/etc/kubernetes/kubelet

【注意】:將配置文件中的IP地址更改為你的每台node節點的IP地址(除了--api-servers=http://172.16.7.151:8080這個ip地址是不用改的)。

【說明】:

  • --address 不能設置為 127.0.0.1,否則后續 Pods 訪問 kubelet 的 API 接口時會失敗,因為 Pods 訪問的 127.0.0.1 指向自己而不是 kubelet;如果設置了 --hostname-override 選項,則 kube-proxy 也需要設置該選項,否則會出現找不到 Node 的情況;
  • KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure",這個是一個基礎容器,每一個Pod啟動的時候都會啟動一個這樣的容器。如果你的本地沒有這個鏡像,kubelet會連接外網把這個鏡像下載下來。最開始的時候是在Google的registry上,因此國內因為GFW都下載不了導致Pod運行不起來。現在每個版本的Kubernetes都把這個鏡像打包,你可以提前傳到自己的registry上,然后再用這個參數指定。
  • --experimental-bootstrap-kubeconfig 指向 bootstrap kubeconfig 文件,kubelet 使用該文件中的用戶名和 token 向 kube-apiserver 發送 TLS Bootstrapping 請求;
  • 管理員通過了 CSR 請求后,kubelet 自動在 --cert-dir 目錄創建證書和私鑰文件(kubelet-client.crt 和 kubelet-client.key),然后寫入 --kubeconfig 文件;
  • 建議在 --kubeconfig 配置文件中指定 kube-apiserver 地址,如果未指定 --api-servers 選項,則必須指定 --require-kubeconfig 選項后才從配置文件中讀取 kube-apiserver 的地址,否則 kubelet 啟動后將找不到 kube-apiserver (日志中提示未找到 API Server),kubectl get nodes 不會返回對應的 Node 信息;
  • --cluster-dns 指定 kubedns 的 Service IP(可以先分配,后續創建 kubedns 服務時指定該 IP),--cluster-domain 指定域名后綴,這兩個參數同時指定后才會生效。

 

(3)啟動kubelet

# systemctl daemon-reload
# systemctl enable kubelet
# systemctl start kubelet
啟動kubelet

 

(4)通過 kubelet 的 TLS 證書請求

kubelet 首次啟動時向 kube-apiserver 發送證書簽名請求,必須通過后 kubernetes 系統才會將該 Node 加入到集群。

a. 查看未授權的 CSR 請求

# kubectl get csr 
NAME        AGE       REQUESTOR           CONDITION
csr-fv3bj   49s       kubelet-bootstrap   Pending

b. 通過 CSR 請求

# kubectl certificate approve csr-fv3bj
certificatesigningrequest "csr-fv3bj" approved
[root@node1 kubernetes]# kubectl get csr
NAME        AGE       REQUESTOR           CONDITION
csr-fv3bj   42m       kubelet-bootstrap   Approved,Issued
# kubectl get nodes
NAME           STATUS    AGE       VERSION
172.16.7.151   Ready     18s       v1.6.0

c. 查看自動生成的 kubelet kubeconfig 文件和公私鑰

[root@node1 kubernetes]# ls -l /etc/kubernetes/kubelet.kubeconfig
-rw-------. 1 root root 2215 Sep 13 09:04 /etc/kubernetes/kubelet.kubeconfig
[root@node1 kubernetes]# ls -l /etc/kubernetes/ssl/kubelet*
-rw-r--r--. 1 root root 1046 Sep 13 09:04 /etc/kubernetes/ssl/kubelet-client.crt
-rw-------. 1 root root  227 Sep 13 09:02 /etc/kubernetes/ssl/kubelet-client.key
-rw-r--r--. 1 root root 1111 Sep 13 09:04 /etc/kubernetes/ssl/kubelet.crt
-rw-------. 1 root root 1675 Sep 13 09:04 /etc/kubernetes/ssl/kubelet.key

在集群中其它的kubernetes node節點上操作完成后,查看集群kubernetes node情況如下:

# kubectl get csr
NAME        AGE       REQUESTOR           CONDITION
csr-5n72m   3m        kubelet-bootstrap   Approved,Issued
csr-clwzj   16m       kubelet-bootstrap   Approved,Issued
csr-fv3bj   4h        kubelet-bootstrap   Approved,Issued
# kubectl get nodes
NAME           STATUS    AGE       VERSION
172.16.7.151   Ready     4h        v1.6.0
172.16.7.152   Ready     6m        v1.6.0
172.16.7.153   Ready     12s       v1.6.0

【問題】:切記每台node節點上的kubelet配置文件/etc/kubernetes/kubelet中的ip地址要改正確,否則會出現加入不了的情況。我在將node1節點的/etc/kubernetes/kubelet遠程復制到node2節點上,沒有修改ip,直接啟動了,配置文件中寫的ip地址是node1的ip地址,這就造成了node2節點並沒有加入進來。采取的恢復操作是:

[root@node2 ~]# systemctl stop kubelet
[root@node2 ~]# cd /etc/kubernetes
[root@node2 kubernetes]# rm -f kubelet.kubeconfig
[root@node2 kubernetes]# rm -rf ~/.kube/cache
# 修改/etc/kubernetes/kubelet中的ip地址
[root@node2 kubernetes]# vim /etc/kubernetes/kubelet
[root@node2 ~]# systemctl start kubelet

這樣,再次啟動kubelet時,kube-apiserver才收到證書簽名請求。

 

4. 配置 kube-proxy

上面第3步中已經把kube-proxy復制到了kubernetes node節點的/usr/local/bin/目錄下了,下面開始做配置。每台kubernetes node節點都需要做如下的操作。

(1)創建 kube-proxy 的service配置文件

在/usr/lib/systemd/system/目錄下創建kube-proxy.service:

[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/proxy
ExecStart=/usr/local/bin/kube-proxy \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBE_MASTER \
            $KUBE_PROXY_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
kube-proxy.service

 

(2)創建kube-proxy配置文件/etc/kubernetes/proxy

【注意】:需要修改每台kubernetes node的ip地址。以下以node1主機為例:

# vim /etc/kubernetes/proxy
###
# kubernetes proxy config

# default config should be adequate

# Add your own!
KUBE_PROXY_ARGS="--bind-address=172.16.7.151 --hostname-override=172.16.7.151 --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig --cluster-cidr=10.254.0.0/16"
/etc/kubernetes/proxy

【說明】:

  • --hostname-override 參數值必須與 kubelet 的值一致,否則 kube-proxy 啟動后會找不到該 Node,從而不會創建任何 iptables 規則;
  • 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 的權限。

 

(3)啟動 kube-proxy

# systemctl daemon-reload
# systemctl enable kube-proxy
# systemctl start kube-proxy
啟動 kube-proxy

 

5. 驗證測試

創建一個niginx的service試一下集群是否可用。

# kubectl run nginx --replicas=2 --labels="run=load-balancer-example" --image=docker.io/nginx:latest --port=80
deployment "nginx" created                   
# kubectl expose deployment nginx --type=NodePort --name=example-service     
service "example-service" exposed
# kubectl describe svc example-service
Name:                   example-service
Namespace:              default
Labels:                 run=load-balancer-example
Annotations:            <none>
Selector:               run=load-balancer-example
Type:                   NodePort
IP:                     10.254.67.61
Port:                   <unset> 80/TCP
NodePort:               <unset> 32201/TCP
Endpoints:              172.30.32.2:80,172.30.87.2:80
Session Affinity:       None
Events:                 <none>

# kubectl get all
NAME                        READY     STATUS    RESTARTS   AGE
po/nginx-1931613429-nlsj1   1/1       Running   0          5m
po/nginx-1931613429-xr7zk   1/1       Running   0          5m

NAME                  CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
svc/example-service   10.254.67.61   <nodes>       80:32201/TCP   1m
svc/kubernetes        10.254.0.1     <none>        443/TCP        5h

NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/nginx   2         2         2            2           5m

NAME                  DESIRED   CURRENT   READY     AGE
rs/nginx-1931613429   2         2         2         5m

# curl "10.254.67.61:80" 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
創建一個niginx的service試一下集群是否可用

瀏覽器輸入172.16.7.151:32201或172.16.7.152:32201或者172.16.7.153:32201都可以得到nginx的頁面。

 

查看運行的容器(在node1和node2上分別運行了一個pod):

# docker ps
CONTAINER ID        IMAGE                                                                                     COMMAND                  CREATED             STATUS              PORTS               NAMES
7d2ef8e34e43        docker.io/nginx@sha256:fc6d2ef47e674a9ffb718b7ac361ec4e421e3a0ef2c93df79abbe4e9ffb5fa08   "nginx -g 'daemon off"   40 minutes ago      Up 40 minutes                           k8s_nginx_nginx-1931613429-xr7zk_default_c628f12f-9912-11e7-9acc-005056b7609a_0
5bbb98fba623        registry.access.redhat.com/rhel7/pod-infrastructure                                       "/usr/bin/pod"           42 minutes ago      Up 42 minutes                           k8s_POD_nginx-1931613429-xr7zk_default_c628f12f-9912-11e7-9acc-005056b7609a_0

如果想刪除剛才創建的deployment:

# kubectl get deployments
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     2         2         2            0           2m
# kubectl delete deployment nginx
deployment "nginx" deleted

 

九、安裝和配置 kube-dns 插件

1. kube-dns是什么

剛才在上一步中創建了個Nginx deployment,得到了兩個運行nginx服務的Pod。待Pod運行之后查看一下它們的IP,並在k8s集群內通過podIP和containerPort來訪問Nginx服務。

獲取Pod IP:

# kubectl get pod -o yaml -l run=load-balancer-example|grep podIP 
        podIP: 172.30.32.2
        podIP: 172.30.87.2

然后在Kubernetes集群的任一節點上就可以通過podIP在k8s集群內訪問Nginx服務了。

# curl "172.30.32.2:80"

但是這樣存在幾個問題:

  • 每次收到獲取podIP太扯了,總不能每次都要手動改程序或者配置才能訪問服務吧,要怎么提前知道podIP呢?
  • Pod在運行中可能會重建,Pod的IP地址會隨着Pod的重啟而變化,並 不建議直接拿Pod的IP來交互
  • 如何在多個Pod中實現負載均衡嘞?

 

使用k8s Service就可以解決。Service為一組Pod(通過labels來選擇)提供一個統一的入口,並為它們提供負載均衡和自動服務發現。

所以緊接着就創建了個service:

# kubectl expose deployment nginx --type=NodePort --name=example-service

創建之后,仍需要獲取Service的Cluster-IP,再結合Port訪問Nginx服務。
獲取IP:

# kubectl get service example-service
NAME              CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
example-service   10.254.67.61   <nodes>       80:32201/TCP   1h

在集群內訪問Service:

# curl "10.254.67.61:80" 

而在Kubernetes cluster外面,則只能通過http://node-ip:32201來訪問。

雖然Service解決了Pod的服務發現和負載均衡問題,但存在着類似的問題:不提前知道Service的IP,還是需要改程序或配置啊。kube-dns就是用來解決上面這個問題的。

kube-dns可以解決Service的發現問題,k8s將Service的名稱當做域名注冊到kube-dns中,通過Service的名稱就可以訪問其提供的服務。也就是說其他應用能夠直接使用服務的名字,不需要關心它實際的 ip 地址,中間的轉換能夠自動完成。名字和 ip 之間的轉換就是 DNS 系統的功能。

kubu-dns 服務不是獨立的系統服務,而是一種 addon ,作為插件來安裝的,不是 kubernetes 集群必須的(但是非常推薦安裝)。可以把它看做運行在集群上的應用,只不過這個應用比較特殊而已。

 

2. 安裝配置kube-dns

官方的yaml文件目錄:https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns。

kube-dns 有兩種配置方式,在 1.3 之前使用 etcd + kube2sky + skydns 的方式,在 1.3 之后可以使用 kubedns + dnsmasq 的方式。

該插件直接使用kubernetes部署,實際上kube-dns插件只是運行在kube-system命名空間下的Pod,完全可以手動創建它。官方的配置文件中包含以下鏡像:

gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64:1.14.1
gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.14.1
gcr.io/google_containers/k8s-dns-sidecar-amd64:1.14.1

 

(1)下載yaml文件

從 https://github.com/rootsongjc/follow-me-install-kubernetes-cluster/tree/master/manifests/kubedns 下載 kubedns-cm.yaml、kubedns-sa.yaml、kubedns-controller.yaml和kubedns-svc.yaml這4個文件下來,並上傳到/opt/kube-dns/目錄下。

# mkdir /opt/kube-dns
# cd /opt/kube-dns/
# ls kubedns-*
kubedns-cm.yaml  kubedns-controller.yaml  kubedns-sa.yaml  kubedns-svc.yaml

修改kubedns-controller.yaml文件,將其中的鏡像地址改為時速雲的地址:

index.tenxcloud.com/jimmy/k8s-dns-dnsmasq-nanny-amd64:1.14.1
index.tenxcloud.com/jimmy/k8s-dns-kube-dns-amd64:1.14.1
index.tenxcloud.com/jimmy/k8s-dns-sidecar-amd64:1.14.1
  • kubeDNS:提供了原來 kube2sky + etcd + skyDNS 的功能,可以單獨對外提供 DNS 查詢服務
  • dnsmasq: 一個輕量級的 DNS 服務軟件,可以提供 DNS 緩存功能。kubeDNS 模式下,dnsmasq 在內存中預留一塊大小(默認是 1G)的地方,保存當前最常用的 DNS 查詢記錄,如果緩存中沒有要查找的記錄,它會到 kubeDNS 中查詢,並把結果緩存起來。

 

(2)系統預定義的 RoleBinding

預定義的 RoleBinding system:kube-dns 將 kube-system 命名空間的 kube-dns ServiceAccount 與 system:kube-dns Role 綁定, 該 Role 具有訪問 kube-apiserver DNS 相關 API 的權限。

[root@node1 ~]# kubectl get clusterrolebindings system:kube-dns -o yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: 2017-09-14T00:46:08Z
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-dns
  resourceVersion: "56"
  selfLink: /apis/rbac.authorization.k8s.io/v1beta1/clusterrolebindingssystem%3Akube-dns
  uid: 18fa2aff-98e6-11e7-a153-005056b7609a
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 的權限。

 

(3)配置 kube-dns ServiceAccount

無需修改。

 

(4)配置 kube-dns 服務

# diff kubedns-svc.yaml.base kubedns-svc.yaml
30c30
<   clusterIP: __PILLAR__DNS__SERVER__
---
>   clusterIP: 10.254.0.2

【說明】:

  • spec.clusterIP = 10.254.0.2,即明確指定了 kube-dns Service IP,這個 IP 需要和 kubelet 的 --cluster-dns 參數值一致。

 

(5)配置 kube-dns Deployment

# diff kubedns-controller.yaml.base kubedns-controller.yaml

【說明】:

  • 使用系統已經做了 RoleBinding 的 kube-dns ServiceAccount,該賬戶具有訪問 kube-apiserver DNS 相關 API 的權限。

 

(6)執行所有定義文件

# pwd
/opt/kube-dns
# ls
kubedns-cm.yaml  kubedns-controller.yaml  kubedns-sa.yaml  kubedns-svc.yaml
# kubectl create -f .
configmap "kube-dns" created
deployment "kube-dns" created
serviceaccount "kube-dns" created
service "kube-dns" created

在3台node節點上查看生成的kube-dns相關pod和container:

[root@node2 ~]# docker ps
CONTAINER ID        IMAGE                                                                                                                           COMMAND                  CREATED             STATUS              PORTS               NAMES
9b1dbfde7eac        index.tenxcloud.com/jimmy/k8s-dns-sidecar-amd64@sha256:947271f3e08b1fd61c4b26478f08d3a8f10bbca90d4dec067e3b33be08066970         "/sidecar --v=2 --log"   4 hours ago         Up 4 hours                              k8s_sidecar_kube-dns-351402727-6vnsj_kube-system_efb96c05-9928-11e7-9acc-005056b7609a_0
a455dc0a9b55        index.tenxcloud.com/jimmy/k8s-dns-dnsmasq-nanny-amd64@sha256:b253876345427dbd626b145897be51d87bfd535e2cd5d7d166deb97ea37701f8   "/dnsmasq-nanny -v=2 "   4 hours ago         Up 4 hours                              k8s_dnsmasq_kube-dns-351402727-6vnsj_kube-system_efb96c05-9928-11e7-9acc-005056b7609a_0
7f18c10c8d60        index.tenxcloud.com/jimmy/k8s-dns-kube-dns-amd64@sha256:94426e872d1a4a0cf88e6c5cd928a1acbe1687871ae5fe91ed751593aa6052d3        "/kube-dns --domain=c"   4 hours ago         Up 4 hours                              k8s_kubedns_kube-dns-351402727-6vnsj_kube-system_efb96c05-9928-11e7-9acc-005056b7609a_0
a6feb213296b        registry.access.redhat.com/rhel7/pod-infrastructure                                                                             "/usr/bin/pod"           4 hours ago         Up 4 hours                              k8s_POD_kube-dns-351402727-6vnsj_kube-system_efb96c05-9928-11e7-9acc-005056b7609a_0

 

3. 檢查 kube-dns 功能

上面是通過 kubectl run 來啟動了第一個Pod,但是並不支持所有的功能。使用kubectl run在設定很復雜的時候需要非常長的一條語句,敲半天也很容易出錯,也沒法保存,在碰到轉義字符的時候也經常會很抓狂,所以更多場景下會使用yaml或者json文件,而使用kubectl create或者delete就可以利用這些yaml文件。通過 kubectl create -f file.yaml 來創建資源。kubectl run 並不是直接創建一個Pod,而是先創建一個Deployment資源 (replicas=1),再由Deployment來自動創建Pod。

新建一個 Deployment:

[root@node1 kube-dns]# vim my-nginx.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: docker.io/nginx:latest
        ports:
        - containerPort: 80
my-nginx.yaml

Export 該 Deployment,生成 my-nginx 服務:

# kubectl expose deploy my-nginx 
service "my-nginx" exposed
# kubectl get services --all-namespaces |grep my-nginx
default       my-nginx          10.254.34.181   <none>        80/TCP          26s

創建另一個 Pod,查看 /etc/resolv.conf 是否包含 kubelet 配置的 --cluster-dns 和 --cluster-domain,是否能夠將服務my-nginx 解析到 Cluster IP 10.254.34.181。

[root@node1 kube-dns]# vim dns-test-busybox.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
dns-test-busybox.yaml
[root@node1 kube-dns]# kubectl create -f dns-test-busybox.yaml
pod "busybox" created
[root@node1 kube-dns]# kubectl exec -ti busybox -- nslookup kubernetes.default
Server:    10.254.0.2
Address 1: 10.254.0.2 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes.default
Address 1: 10.254.0.1 kubernetes.default.svc.cluster.local

kubectl exec -ti busybox -- ping my-nginx
PING my-nginx (10.254.34.181): 56 data bytes

kubectl exec -ti busybox -- ping kubernetes
PING kubernetes (10.254.0.1): 56 data bytes
        
kubectl exec -ti busybox -- ping kube-dns.kube-system.svc.cluster.local
PING kube-dns.kube-system.svc.cluster.local (10.254.0.2): 56 data bytes

從結果來看,service名稱可以正常解析。

另外,使用kubernetes的時候建議不要再用docker命令操作。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM