本節內容:
- 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
(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

[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/
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-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" } ] }
部分字段說明:
- “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
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" } ] }
部分字段說明:
- 如果 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
或者直接在命令行上指定相關參數:
# 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" } ] }
說明:
- 后續 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
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" } ] }
說明:
- 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
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 ...
【說明】:
- 確認 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", ...
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
【說明】:
- 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".
【說明】:
- --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".
【說明】:
- 設置集群參數和客戶端認證參數時 --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/
六、創建高可用 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

[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

[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
【說明】:
- 指定 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
結果最后一行為 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 ...
- 方式二:從 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
將二進制文件拷貝到指定路徑:
[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
上面的配置文件中用到的/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"
該配置文件同時被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"
【說明】:
- --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配置文件/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"
【說明】:
- --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
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配置文件/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"
【說明】:
- --address 值必須為 127.0.0.1,因為當前 kube-apiserver 期望 scheduler 和 controller-manager 在同一台機器。
(2)啟動 kube-scheduler

# systemctl daemon-reload # systemctl enable kube-scheduler # systemctl start 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"}
八、部署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配置文件:

# 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"
【說明】:
- 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
啟動完成后,會在/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
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
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"
【注意】:將配置文件中的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
(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
(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"
【說明】:
- --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
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>
瀏覽器輸入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
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
[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命令操作。