全世界最簡單的kubernetes高可用安裝工具sealos!

English docs

概述與設計原則

sealos旨在做一個簡單幹淨輕量級穩定的kubernetes安裝工具,能很好的支持高可用安裝。 其實把一個東西做的功能強大並不難,但是做到極簡且靈活可擴展就比較難。
所以在實現時就必須要遵循這些原則。

sealos特性與優勢:

  • 支持離線安裝,工具與資源包(二進制程序 配置文件 鏡像 yaml文件等)分離,這樣不同版本替換不同離線包即可
  • 證書延期
  • 使用簡單
  • 支持自定義配置
  • 內核負載,極其穩定,因爲簡單所以排查問題也極其簡單
爲什麼不使用ansile

1.0版本確實是用ansible實現,但是用戶還是需要先裝ansible,裝ansible有需要裝python和一些依賴等,爲了不讓用戶那麼麻煩把ansible放到了容器裏供用戶使用。如果不想配置免密鑰使用用戶名密碼時又需要ssh-pass等,總之不能讓我滿意,不是我想的極簡。

所以我想就來一個二進制文件工具,沒有任何依賴,文件分發與遠程命令都通過調用sdk實現所以不依賴其它任何東西,總算讓我這個有潔癖的人滿意了。

爲什麼不用keepalived haproxy

haproxy用static pod跑沒有太大問題還算好管理,keepalived現在大部分開源ansible腳本都用yum 或者apt等裝,這樣非常的不可控,有如下劣勢:

  • 源不一致可能導致版本不一致,版本不一直連配置文件都不一樣,我曾經檢測腳本不生效一直找不到原因,後來才知道是版本原因
  • 系統原因安裝不上,依賴庫問題某些環境就直接裝不上了
  • 看了網上很多安裝腳本,很多檢測腳本與權重調節方式都不對,直接去檢測haproxy進程在不在,其實是應該去檢測apiserver是不是healthz的,api掛了即使haproxy在集羣也會不正常了,就是僞高可用了。
  • 管理不方便,通過prometheus對集羣進行監控,是能直接監控到static pod的但是用systemd跑又需要單獨設置監控,且重啓啥的還需要單獨拉起。不如kubelet統一管理來的乾淨簡潔。
  • 我們還出現過keepalived把CPU佔滿的情況。

所以爲了解決這個問題,我把keepalived跑在了容器中(社區提供的鏡像基本是不可用的) 改造中間也是發生過很多問題,最終好在解決了。

總而言之,累覺不愛,所以在想能不能甩開haproxy和keepalived做出更簡單更可靠的方案出來,還真找到了。。。

本地負載爲什麼不使用envoy或者nginx

我們通過本地負載解決高可用問題

解釋一下本地負載,就是在每個node節點上都啓動一個負載均衡,上游就是三個master,負載方式有很多 ipvs envoy nginx等,我們最終使用內核ipvs

如果使用envoy等需要在每個節點上都跑一個進程,消耗更多資源,這是我不希望的。ipvs實際也多跑了一個進程lvscare,但是lvscare只是負責管理ipvs規則,和kube-proxy類似,真正的流量還是從很穩定的內核走的,不需要再把包走到用戶態中去處理。

實現上有個問題會讓使用envoy等變得非常尷尬,就是join時如果負載均衡沒有建立那是會卡住的,kubelet就不會起,所以爲此你需要先把envory起起來,意味着你又不能用static pod去管理它,同上面keepalived宿主機部署一樣的問題,用static pod就會相互依賴,邏輯死鎖,雞說要先有蛋,蛋說要先有雞,最後誰都沒有。

使用ipvs就不一樣,我可以在join之前先把ipvs規則建立好,再去join就可以join進去了,然後對規則進行守護即可。一旦apiserver不可訪問了,會自動清理掉所有node上對應的ipvs規則, master恢復正常時添加回來。

爲什麼要定製kubeadm

首先是由於kubeadm把證書時間寫死了,所以需要定製把它改成99年,雖然大部分人可以自己去籤個新證書,但是我們還是不想再依賴個別的工具,就直接改源碼了。

其次就是做本地負載時修改kubeadm代碼是最方便的,因爲在join時我們需要做兩個事,第一join之前先創建好ipvs規則,第二創建static pod,如果這塊不去定製kubeadm就把報靜態pod目錄已存在的錯誤,忽略這個錯誤很不優雅。 而且kubeadm中已經提供了一些很好用的sdk供我們去實現這個功能。

且這樣做之後最核心的功能都集成到kubeadm中了,sealos就單單變成分發和執行上層命令的輕量級工具了,增加節點時我們也就可以直接用kubeadm了

使用教程

安裝依賴

安裝教程

多master HA:

sealos init --master 192.168.0.2 \
    --master 192.168.0.3 \
    --master 192.168.0.4 \
    --node 192.168.0.5 \
    --user root \
    --passwd your-server-password \
    --version v1.14.1 \
    --pkg-url /root/kube1.14.1.tar.gz     

或者單master多node:

sealos init --master 192.168.0.2 \
    --node 192.168.0.5 \
    --user root \
    --passwd your-server-password \
    --version v1.14.1 \
    --pkg-url /root/kube1.14.1.tar.gz 

使用免密鑰或者密鑰對:

sealos init --master 172.16.198.83 \
    --node 172.16.198.84 \
    --pkg-url https://sealyun.oss-cn-beijing.aliyuncs.com/free/kube1.15.0.tar.gz \
    --pk /root/kubernetes.pem # this is your ssh private key file \
    --version v1.15.0
--master   master服務器地址列表
--node     node服務器地址列表
--user     服務器ssh用戶名
--passwd   服務器ssh用戶密碼
--pkg-url  離線包位置,可以放在本地目錄,也可以放在一個http服務器上,sealos會wget到安裝目標機
--version  kubernetes版本
--pk       ssh私鑰地址,配置免密鑰默認就是/root/.ssh/id_rsa

Other flags:

 --kubeadm-config string   kubeadm-config.yaml kubeadm配置文件,可自定義kubeadm配置文件
 --vip string              virtual ip (default "10.103.97.2") 本地負載時虛擬ip,不推薦修改,集羣外不可訪問

檢查安裝是否正常:

[root@iZj6cdqfqw4o4o9tc0q44rZ ~]# kubectl get node
NAME                      STATUS   ROLES    AGE     VERSION
izj6cdqfqw4o4o9tc0q44rz   Ready    master   2m25s   v1.14.1
izj6cdqfqw4o4o9tc0q44sz   Ready    master   119s    v1.14.1
izj6cdqfqw4o4o9tc0q44tz   Ready    master   63s     v1.14.1
izj6cdqfqw4o4o9tc0q44uz   Ready    <none>   38s     v1.14.1
[root@iZj6cdqfqw4o4o9tc0q44rZ ~]# kubectl get pod --all-namespaces
NAMESPACE     NAME                                              READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-5cbcccc885-9n2p8          1/1     Running   0          3m1s
kube-system   calico-node-656zn                                 1/1     Running   0          93s
kube-system   calico-node-bv5hn                                 1/1     Running   0          2m54s
kube-system   calico-node-f2vmd                                 1/1     Running   0          3m1s
kube-system   calico-node-tbd5l                                 1/1     Running   0          118s
kube-system   coredns-fb8b8dccf-8bnkv                           1/1     Running   0          3m1s
kube-system   coredns-fb8b8dccf-spq7r                           1/1     Running   0          3m1s
kube-system   etcd-izj6cdqfqw4o4o9tc0q44rz                      1/1     Running   0          2m25s
kube-system   etcd-izj6cdqfqw4o4o9tc0q44sz                      1/1     Running   0          2m53s
kube-system   etcd-izj6cdqfqw4o4o9tc0q44tz                      1/1     Running   0          118s
kube-system   kube-apiserver-izj6cdqfqw4o4o9tc0q44rz            1/1     Running   0          2m15s
kube-system   kube-apiserver-izj6cdqfqw4o4o9tc0q44sz            1/1     Running   0          2m54s
kube-system   kube-apiserver-izj6cdqfqw4o4o9tc0q44tz            1/1     Running   1          47s
kube-system   kube-controller-manager-izj6cdqfqw4o4o9tc0q44rz   1/1     Running   1          2m43s
kube-system   kube-controller-manager-izj6cdqfqw4o4o9tc0q44sz   1/1     Running   0          2m54s
kube-system   kube-controller-manager-izj6cdqfqw4o4o9tc0q44tz   1/1     Running   0          63s
kube-system   kube-proxy-b9b9z                                  1/1     Running   0          2m54s
kube-system   kube-proxy-nf66n                                  1/1     Running   0          3m1s
kube-system   kube-proxy-q2bqp                                  1/1     Running   0          118s
kube-system   kube-proxy-s5g2k                                  1/1     Running   0          93s
kube-system   kube-scheduler-izj6cdqfqw4o4o9tc0q44rz            1/1     Running   1          2m43s
kube-system   kube-scheduler-izj6cdqfqw4o4o9tc0q44sz            1/1     Running   0          2m54s
kube-system   kube-scheduler-izj6cdqfqw4o4o9tc0q44tz            1/1     Running   0          61s
kube-system   kube-sealyun-lvscare-izj6cdqfqw4o4o9tc0q44uz      1/1     Running   0          86s

清理

sealos clean \
    --master 192.168.0.2 \
    --master 192.168.0.3 \
    --master 192.168.0.4 \
    --node 192.168.0.5 \
    --user root \
    --passwd your-server-password

增加節點

獲取 join command, 在master上執行:

kubeadm token create --print-join-command

可以使用super kubeadm, 但是join時需要增加一個--master 參數:

cd kube/shell && init.sh
echo "10.103.97.2 apiserver.cluster.local" >> /etc/hosts   # using vip
kubeadm join 10.103.97.2:6443 --token 9vr73a.a8uxyaju799qwdjv \
    --master 10.103.97.100:6443 \
    --master 10.103.97.101:6443 \
    --master 10.103.97.102:6443 \
    --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866

也可以用sealos join命令:

sealos join 
    --master 192.168.0.2 \
    --master 192.168.0.3 \
    --master 192.168.0.4 \
    --vip 10.103.97.2 \       
    --node 192.168.0.5 \            
    --user root \             
    --passwd your-server-password \
    --pkg-url /root/kube1.15.0.tar.gz 

使用自定義kubeadm配置文件

比如我們需要在證書里加入 sealyun.com:

先獲取配置文件模板:

sealos config -t kubeadm >>  kubeadm-config.yaml.tmpl

修改kubeadm-config.yaml.tmpl,文件即可, 編輯增加 sealyun.com, 注意其它部分不用動,sealos會自動填充模板裏面的內容:

apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
kubernetesVersion: {{.Version}}
controlPlaneEndpoint: "apiserver.cluster.local:6443"
networking:
  podSubnet: 100.64.0.0/10
apiServer:
        certSANs:
        - sealyun.com # this is what I added
        - 127.0.0.1
        - apiserver.cluster.local
        {{range .Masters -}}
        - {{.}}
        {{end -}}
        - {{.VIP}}
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"
ipvs:
        excludeCIDRs: 
        - "{{.VIP}}/32"

使用 --kubeadm-config 指定配置文件模板即可:

sealos init --kubeadm-config kubeadm-config.yaml.tmpl \
    --master 192.168.0.2 \
    --master 192.168.0.3 \
    --master 192.168.0.4 \
    --node 192.168.0.5 \
    --user root \
    --passwd your-server-password \
    --version v1.14.1 \
    --pkg-url /root/kube1.14.1.tar.gz 

版本升級

本教程以1.14版本升級到1.15爲例,其它版本原理大差不差,懂了這個其它的參考官方教程即可

升級過程

  1. 升級kubeadm,所有節點導入鏡像
  2. 升級控制節點
  3. 升級master(控制節點)上的kubelet
  4. 升級其它master(控制節點)
  5. 升級node
  6. 驗證集羣狀態

升級kubeadm

把離線包拷貝到所有節點執行 cd kube/shell && sh init.sh
這裏會把kubeadm kubectl kubelet bin文件都更新掉,而且會導入高版本鏡像

升級控制節點

kubeadm upgrade plan
kubeadm upgrade apply v1.15.0

重啓kubelet:

systemctl restart kubelet

其實kubelet升級簡單粗暴,我們只需要把新版本的kubelet拷貝到/usr/bin下面,重啓kubelet service即可,如果程序正在使用不讓覆蓋那麼就停一下kubelet再進行拷貝,kubelet bin文件在 conf/bin 目錄下

升級其它控制節點

kubeadm upgrade apply

升級node

驅逐節點(要不要驅逐看情況, 喜歡粗暴的直接來也沒啥)

kubectl drain $NODE --ignore-daemonsets

更新kubelet配置:

kubeadm upgrade node config --kubelet-version v1.15.0

然後升級kubelet 一樣是替換二進制再重啓 kubelet service

systemctl restart kubelet

召回失去的愛情:

kubectl uncordon $NODE

驗證

kubectl get nodes

如果版本信息對的話基本就ok了

kubeadm upgrade apply 幹了啥

  1. 檢查集羣是否可升級
  2. 執行版本升級策略 哪些版本之間可以升級
  3. 確認鏡像可在
  4. 執行控制組件升級,如果失敗就回滾,其實就是apiserver controller manager scheduler 等這些容器
  5. 執行kube-dns 和kube-proxy的升級
  6. 創建新的證書文件,備份老的如果其超過180天

源碼編譯

因爲使用了netlink庫,所以推薦在容器內進行編譯

docker run --rm -v $GOPATH/src/github.com/fanux/sealos:/go/src/github.com/fanux/sealos -w /go/src/github.com/fanux/sealos -it golang:1.12.7  go build

如果使用go mod 指定通過vendor 編譯:

go build -mod vendor

原理

執行流程

  • 通過sftp或者wget把離線安裝包拷貝到目標機器上(masters和nodes)
  • 在master0上執行kubeadm init
  • 在其它master上執行kubeadm join 並設置控制面,這個過程會在其它master上起etcd並與master0的etcd組成集羣,並啓動控制組建(apiserver controller等)
  • join node節點,會在node上配置ipvs規則,配置/etc/hosts等

    有個細節是所有對apiserver進行訪問都是通過域名,因爲master上連接自己就行,node需要通過虛擬ip鏈接多個master,這個每個節點的kubelet與kube-proxy訪問apiserver的地址是不一樣的,而kubeadm又只能在配置文件中指定一個地址,所以使用一個域名但是每個節點解析不同。

使用域名的好處還有就是IP地址發生變化時僅需要修改解析即可。

本地內核負載

通過這樣的方式實現每個node上通過本地內核負載均衡訪問masters:

  +----------+                       +---------------+  virturl server: 127.0.0.1:6443
  | mater0   |<----------------------| ipvs nodes    |    real servers:
  +----------+                      |+---------------+            10.103.97.200:6443
                                    |                             10.103.97.201:6443
  +----------+                      |                             10.103.97.202:6443
  | mater1   |<---------------------+
  +----------+                      |
                                    |
  +----------+                      |
  | mater2   |<---------------------+
  +----------+

在node上起了一個lvscare的static pod去守護這個 ipvs, 一旦apiserver不可訪問了,會自動清理掉所有node上對應的ipvs規則, master恢復正常時添加回來。

所以在你的node上加了三個東西,可以直觀的看到:

cat /etc/kubernetes/manifests   # 這下面增加了lvscare的static pod
ipvsadm -Ln                     # 可以看到創建的ipvs規則
cat /etc/hosts                  # 增加了虛擬IP的地址解析

定製kubeadm

對kubeadm改動非常少,主要是證書時間延長和join命令的擴展,主要講講join命令的改造:

首先join命令增加--master參數用於指定master地址列表

flagSet.StringSliceVar(
    &locallb.LVScare.Masters, "master", []string{},
    "A list of ha masters, --master 192.168.0.2:6443  --master 192.168.0.2:6443  --master 192.168.0.2:6443",
)

這樣就可以拿到master地址列表去做ipvs了

如果不是控制節點切不是單master,那麼就創建一條ipvs規則,控制節點上不需要創建,連自己的apiserver即可:

if data.cfg.ControlPlane == nil {
            fmt.Println("This is not a control plan")
            if len(locallb.LVScare.Masters) != 0 {
                locallb.CreateLocalLB(args[0])
            }
        } 

然後再去創建lvscare static pod去守護ipvs:

if len(locallb.LVScare.Masters) != 0 {
                locallb.LVScareStaticPodToDisk("/etc/kubernetes/manifests")
            }

所以哪怕你不使用sealos,也可以直接用定製過的kubeadm去裝集羣,只是麻煩一些:

kubeadm配置文件

apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
kubernetesVersion: v1.14.0
controlPlaneEndpoint: "apiserver.cluster.local:6443" # apiserver DNS name
apiServer:
        certSANs:
        - 127.0.0.1
        - apiserver.cluster.local
        - 172.20.241.205
        - 172.20.241.206
        - 172.20.241.207
        - 172.20.241.208
        - 10.103.97.1          # virturl ip
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"
ipvs:
        excludeCIDRs: 
        - "10.103.97.1/32" # 注意不加這個kube-proxy會清理你的規則

master0 10.103.97.100 上

echo "10.103.97.100 apiserver.cluster.local" >> /etc/hosts # 解析的是master0的地址
kubeadm init --config=kubeadm-config.yaml --experimental-upload-certs  
mkdir ~/.kube && cp /etc/kubernetes/admin.conf ~/.kube/config
kubectl apply -f https://docs.projectcalico.org/v3.6/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml

master1 10.103.97.101 上

echo "10.103.97.100 apiserver.cluster.local" >> /etc/hosts #解析的是master0的地址,爲了能正常join進去
kubeadm join 10.103.97.100:6443 --token 9vr73a.a8uxyaju799qwdjv \
    --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 \
    --experimental-control-plane \
    --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07 

sed "s/10.103.97.100/10.103.97.101/g" -i /etc/hosts  # 解析再換成自己的地址,否則就都依賴master0的僞高可用了

master2 10.103.97.102 上,同master1

echo "10.103.97.100 apiserver.cluster.local" >> /etc/hosts
kubeadm join 10.103.97.100:6443 --token 9vr73a.a8uxyaju799qwdjv \
    --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 \
    --experimental-control-plane \
    --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07  

sed "s/10.103.97.100/10.103.97.101/g" -i /etc/hosts

nodes 上

join時加上--master指定master地址列表

echo "10.103.97.1 apiserver.cluster.local" >> /etc/hosts   # 需要解析成虛擬ip
kubeadm join 10.103.97.1:6443 --token 9vr73a.a8uxyaju799qwdjv \
    --master 10.103.97.100:6443 \
    --master 10.103.97.101:6443 \
    --master 10.103.97.102:6443 \
    --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866

離線包結構分析

.
├── bin  # 指定版本的bin文件,只需要這三個,其它組件跑容器裏
│   ├── kubeadm
│   ├── kubectl
│   └── kubelet
├── conf
│   ├── 10-kubeadm.conf  # 這個文件新版本沒用到,我在shell裏直接生成,這樣可以檢測cgroup driver
│   ├── dashboard
│   │   ├── dashboard-admin.yaml
│   │   └── kubernetes-dashboard.yaml
│   ├── heapster
│   │   ├── grafana.yaml
│   │   ├── heapster.yaml
│   │   ├── influxdb.yaml
│   │   └── rbac
│   │       └── heapster-rbac.yaml
│   ├── kubeadm.yaml # kubeadm的配置文件
│   ├── kubelet.service  # kubelet systemd配置文件
│   ├── net
│   │   └── calico.yaml
│   └── promethus
├── images  # 所有鏡像包
│   └── images.tar
└── shell
    ├── init.sh  # 初始化腳本
    └── master.sh # 運行master腳本

init.sh腳本中拷貝bin文件到$PATH下面,配置systemd,關閉swap防火牆等,然後導入集羣所需要的鏡像。

master.sh主要執行了kubeadm init

conf下面有有我需要的如kubeadm的配置文件,calico yaml文件等等

sealos會會調用二者。 所以大部分兼容不同版本都可以微調腳本做到。

關注我們就是關注技術乾貨


kubernetes一鍵HA

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章