Kubernetes — 從0到1:搭建一個完整的Kubernetes集群


 准備工作

 首先,准備機器。最直接的辦法,自然是到公有雲上申請幾個虛擬機。當然,如果條件允許的話,拿幾台本地的物理服務器來組集群是最好不過了。這些機器只要滿足如下幾個條件即可:

  • 滿足安裝 Docker 項目所需的要求,比如 64 位的 Linux 操作系統、3.10 及以上的內核版本;
  • x86+或者+ARM+架構均可;
  • 機器之間網絡互通,這是將來容器之間網絡互通的前提;
  • 有外網訪問權限(科學),因為需要拉取鏡像;
  • 能夠訪問到gcr.io、quay.io這兩個 docker registry,因為有小部分鏡像需要在這里拉取;
  • 單機可用資源建議 2 核 CPU、8 GB 內存或以上,再小的話問題也不大,但是能調度的 Pod 數量就比較有限了;
  • 30 GB 或以上的可用磁盤空間,這主要是留給 Docker 鏡像和日志文件用的

 安裝 kubeadm 和 Docker

《Kubernetes 一鍵部署利器:kubeadm》它的一鍵安裝非常方便,我們只需要添加 kubeadm 的源,然后直接使用 apt-get 安裝即可,具體流程如下所示:

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -

把官方源列表加入到k8s節點本地源列表配置目錄中

vi  /etc/apt/sources.list.d/kubernetes.list

輸入以下內容

deb http://apt.kubernetes.io/ kubernetes-xenial main

更新包索引 

apt-get update

安裝docker 這里我用的是docker 17.03.3-ce版本

添加Docker的官方GPG密鑰:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

添加官方源倉庫  

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu  $(lsb_release -cs) stable"

更新包索引

apt-get update

安裝17.03版本

apt-get install docker-ce=17.03.3~ce-0~ubuntu-xenial

安裝 kubelet kubctl kubeadm

apt install kubelet=1.11.1-00
apt install kubectl=1.11.1-00
apt install kubeadm=1.11.1-00

  

部署 Kubernetes 的 Master 節點

kubeadm 可以一鍵部署 Master 節點。不過,在本篇文章中既然要部署一個“完整”的 Kubernetes 集群,那我們不妨稍微提高一下難度:通過配置文件來開啟一些實驗性功能。

所以,這里我編寫了一個給 kubeadm 用的 YAML 文件(名叫:kubeadm.yaml):

apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
controllerManagerExtraArgs:
  horizontal-pod-autoscaler-use-rest-clients: "true"
  horizontal-pod-autoscaler-sync-period: "10s"
  node-monitor-grace-period: "10s"
apiServerExtraArgs:
  runtime-config: "api/all=true"
kubernetesVersion: "v1.11.1"

 

 這個配置中,我給+kube-controller-manager+設置了

horizontal-pod-autoscaler-use-rest-clients: "true"

  

 這意味着,將來部署的 kube-controller-manager 能夠使用自定義資源(Custom Metrics)進行自動水平擴展。

其中,“stable-1.11”就是 kubeadm 幫我們部署的 Kubernetes 版本號,即:Kubernetes release 1.11 最新的穩定版,在我的環境下,它是 v1.11.1。你也可以直接指定這個版本,比如:kubernetesVersion:“v1.11.1”。然后,我們只需要執行一句指令:

kubeadm init --config kubeadm.yaml

  

就可以完成 Kubernetes+Master 的部署了,這個過程只需要幾分鍾。部署完成后,kubeadm 會生成一行指令

kubeadm join 209.250.244.253:6443 --token 4srvlc.6ug9t7ikb3ao0m06 --discovery-token-ca-cert-hash sha256:b71d09fee04a19c53787b44bd18df39b50256de6beeee90f3acc2665948d18b8

 這個 kubeadm join 命令,就是用來給這個 Master 節點添加更多工作節點(Worker)的命令。我們在后面部署 Worker 節點的時候馬上會用到它,所以找一個地方把這條命令記錄下來。此外,kubeadm 還會提示我們第一次使用 Kubernetes 集群所需要的配置命令

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

 而需要這些配置命令的原因是:Kubernetes 集群默認需要加密方式訪問。所以,這幾條命令,就是將剛剛部署生成的 Kubernetes 集群的安全配置文件,保存到當前用戶的.kube 目錄下,kubectl 默認會使用這個目錄下的授權信息訪問 Kubernetes 集群。

如果不這么做的話,我們每次都需要通過 export+KUBECONFIG 環境變量告訴 kubectl 這個安全配置文件的位置。

現在,我們就可以使用 kubectl get 命令來查看當前唯一一個節點的狀態了:

kubectl get nodes

可以看到,這個 get 指令輸出的結果里,Master 節點的狀態是 NotReady,這是為什么呢?

在調試 Kubernetes 集群時,最重要的手段就是用 kubectl describe 來查看這個節點(Node)對象的詳細信息、狀態和事件(Event),我們來試一下:

kubectl describe node master

通過 kubectl describe 指令的輸出,我們可以看到 NodeNotReady 的原因在於,我們尚未部署任何網絡插件。

另外,我們還可以通過+kubectl+檢查這個節點上各個系統 Pod 的狀態,其中,kube-system 是 Kubernetes 項目預留的系統 Pod 的工作空間(Namepsace,注意它並不是 Linux Namespace,它只是 Kubernetes 划分不同工作空間的單位):

kubectl get pods -n kube-system

可以看到,CoreDNS 等依賴於網絡的 Pod 都處於 Pending 狀態,即調度失敗。這當然是符合預期的:因為這個 Master 節點的網絡尚未就緒

部署網絡插件

在 Kubernetes 項目“一切皆容器”的設計理念指導下,部署網絡插件非常簡單,只需要執行一句 kubectl apply 指令,  

apiVersion: v1
kind: List
items:
  - apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: weave-net
      labels:
        name: weave-net
      namespace: kube-system
  - apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRole
    metadata:
      name: weave-net
      labels:
        name: weave-net
    rules:
      - apiGroups:
          - ''
        resources:
          - pods
          - namespaces
          - nodes
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - extensions
        resources:
          - networkpolicies
        verbs:
          - get
          - list
          - watch
  - apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRoleBinding
    metadata:
      name: weave-net
      labels:
        name: weave-net
    roleRef:
      kind: ClusterRole
      name: weave-net
      apiGroup: rbac.authorization.k8s.io
    subjects:
      - kind: ServiceAccount
        name: weave-net
        namespace: kube-system
  - apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: Role
    metadata:
      name: weave-net-kube-peer
      namespace: kube-system
      labels:
        name: weave-net-kube-peer
    rules:
      - apiGroups:
          - ''
        resources:
          - configmaps
        resourceNames:
          - weave-net
        verbs:
          - get
          - update
      - apiGroups:
          - ''
        resources:
          - configmaps
        verbs:
          - create
  - apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: RoleBinding
    metadata:
      name: weave-net-kube-peer
      namespace: kube-system
      labels:
        name: weave-net-kube-peer
    roleRef:
      kind: Role
      name: weave-net-kube-peer
      apiGroup: rbac.authorization.k8s.io
    subjects:
      - kind: ServiceAccount
        name: weave-net
        namespace: kube-system
  - apiVersion: extensions/v1beta1
    kind: DaemonSet
    metadata:
      name: weave-net
      labels:
        name: weave-net
      namespace: kube-system
    spec:
      template:
        metadata:
          labels:
            name: weave-net
        spec:
          containers:
            - name: weave
              command:
                - /home/weave/launch.sh
              env:
                - name: HOSTNAME
                  valueFrom:
                    fieldRef:
                      apiVersion: v1
                      fieldPath: spec.nodeName
              image: 'weaveworks/weave-kube:2.5.2'
              readinessProbe:
                httpGet:
                  host: 127.0.0.1
                  path: /status
                  port: 6784
              resources:
                requests:
                  cpu: 10m
              securityContext:
                privileged: true
              volumeMounts:
                - name: weavedb
                  mountPath: /weavedb
                - name: cni-bin
                  mountPath: /host/opt
                - name: cni-bin2
                  mountPath: /host/home
                - name: cni-conf
                  mountPath: /host/etc
                - name: dbus
                  mountPath: /host/var/lib/dbus
                - name: lib-modules
                  mountPath: /lib/modules
            - name: weave-npc
              env:
                - name: HOSTNAME
                  valueFrom:
                    fieldRef:
                      apiVersion: v1
                      fieldPath: spec.nodeName
              image: 'weaveworks/weave-npc:2.5.2'
              resources:
                requests:
                  cpu: 10m
              securityContext:
                privileged: true
          hostNetwork: true
          hostPID: true
          restartPolicy: Always
          securityContext:
            seLinuxOptions: {}
          serviceAccountName: weave-net
          tolerations:
            - effect: NoSchedule
              operator: Exists
          volumes:
            - name: weavedb
              hostPath:
                path: /var/lib/weave
            - name: cni-bin
              hostPath:
                path: /opt
            - name: cni-bin2
              hostPath:
                path: /home
            - name: cni-conf
              hostPath:
                path: /etc
            - name: dbus
              hostPath:
                path: /var/lib/dbus
            - name: lib-modules
              hostPath:
                path: /lib/modules
      updateStrategy:
        type: RollingUpdate
weave-daemonset-k8s-1.6.yaml
kubectl apply -f weave-daemonset-k8s-1.6.yaml

部署完成后,我們可以通過 kubectl get 重新檢查 Pod 的狀態:  

可以看到,所有的系統 Pod 都成功啟動了,而剛剛部署的 Weave 網絡插件則在 kube-system 下面新建了一個名叫 weave-net-cmk27 的+Pod,一般來說,這些 Pod 就是容器網絡插件在每個節點上的控制組件。 Kubernetes 支持容器網絡插件,使用的是一個名叫 CNI 的通用接口,它也是當前容器網絡的事實標准,市面上的所有容器網絡開源項目都可以通過 CNI 接入 Kubernetes,比如 Flannel、Calico、Canal、Romana 等等,它們的部署方式也都是類似的“一鍵部署”。

至此,Kubernetes 的 Master 節點就部署完成了。如果你只需要一個單節點的 Kubernetes,現在你就可以使用了。不過,在默認情況下,Kubernetes 的 Master 節點是不能運行用戶 Pod 的,所以還需要額外做一個小操作。在本篇的最后部分,我會介紹到它。

 部署 Kubernetes 的 Worker 節點

 Kubernetes 的 Worker 節點跟 Master 節點幾乎是相同的,它們運行着的都是一個 kubelet 組件。唯一的區別在於,在 kubeadm init 的過程中,kubelet 啟動后,Master 節點上還會自動運行 kube-apiserver、kube-scheduler、kube-controller-manger 這三個系統 Pod。 所以,相比之下,部署 Worker 節點反而是最簡單的,只需要兩步即可完成。  

第一步,在所有 Worker 節點上執行“安裝 kubeadm 和 Docker”一節的所有步驟

第二步,執行部署 Master 節點時生成的 kubeadm join 指令:

kubeadm join 209.250.244.253:6443 --token 4srvlc.6ug9t7ikb3ao0m06 --discovery-token-ca-cert-hash sha256:b71d09fee04a19c53787b44bd18df39b50256de6beeee90f3acc2665948d18b8

在這里我想master中加入了2個Worker節點

 

通過 Taint/Toleration 調整 Master 執行 Pod 的策略

默認情況下 Master 節點是不允許運行用戶 Pod 的。而 Kubernetes 做到這一點,依靠的是 Kubernetes 的 Taint/Toleration 機制。它的原理非常簡單:一旦某個節點被加上了一個 Taint,即被“打上了污點”,那么所有 Pod 就都不能在這個節點上運行,因為 Kubernetes 的 Pod 都有“潔癖”。

除非,有個別的 Pod 聲明自己能“容忍”這個“污點”,即聲明了 Toleration,它才可以在這個節點上運行。

其中,為節點打上“污點”(Taint)的命令是(master節點默認就有Taint,所以master上默認不能運行用戶的pod

kubectl taint nodes node1 foo=bar:NoSchedule

這時,該 node1 節點上就會增加一個鍵值對格式的 Taint,即:foo=ANoSchedule。其中值里面的 NoSchedule,意味着這個 Taint 只會在調度新 Pod 時產生作用,而不會影響已經在 node1 上運行的 Pod,哪怕它們沒有 Toleration。 那么 Pod 又如何聲明 Toleration 呢?

我們只要在 Pod 的.yaml 文件中的 spec 部分,加入 tolerations 字段即可:

apiVersion: v1
kind: Pod
...
spec:
  tolerations:
  - key: "foo"
    operator: "Equal"
    value: "bar"
    effect: "NoSchedule"

這個 Toleration 的含義是,這個 Pod 能“容忍”所有鍵值對為 foo=bar 的 Taint( operator:“Equal”,“等於”操作)。

現在回到我們已經搭建的集群上來。這時,如果你通過 kubectl describe 檢查一下master 節點的 Taint 字段,就會有所發現了:

kubectl describe node master

可以看到,master  節點默認被加上了node-role.kubernetes.io/ANoSchedule這樣一個“污點”,其中“鍵”是node-role.kubernetes.io/Fmaster,而沒有提供“值”。

此時,你就需要像下面這樣用“Exists”操作符(operator:“Exists”,“存在”即可)來說明,該 Pod 能夠容忍所有以 foo 為鍵的 Taint,才能讓這個 Pod 運行在該  Master 節點上:

apiVersion: v1
kind: Pod
...
spec:
  tolerations:
  - key: "foo"
    operator: "Exists"
    effect: "NoSchedule"

  

當然,如果你就是想要一個單節點的 Kubernetes,刪除這個  Taint 才是正確的選擇:  

kubectl taint nodes --all node-role.kubernetes.io/master-

  

如上所示,我們在“node-role.kubernetes.io/master”這個鍵后面加上了一個短橫線“-”,這個格式就意味着移除所有以“node-role.kubernetes.io/master”為鍵的 Taint。

到了這一步,一個基本完整的 Kubernetes 集群就部署完畢了。是不是很簡單呢?

有了 kubeadm 這樣的原生管理工具,Kubernetes 的部署已經被大大簡化。更重要的是,像證書、授權、各個組件的配置等部署中最麻煩的操作,kubeadm 都已經幫你完成了。

接下來,我們再在這個 Kubernetes 集群上安裝一些其他的輔助插件,比如 Dashboard 和存儲插件。

部署 Dashboard 可視化插件

在 Kubernetes 社區中,有一個很受歡迎的 Dashboard 項目,它可以給用戶提供一個可視化的 Web 界面來查看當前集群的各種信息。毫不意外,它的部署也相當簡單:

wget https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.0/src/deploy/recommended/kubernetes-dashboard.yaml

為了便於本地訪問,修改yaml文件,將service改為NodePort 類型:  

部署

kubectl apply -f kubernetes-dashboard.yaml

部署完成之后,我們就可以查看 Dashboard 對應的 Pod 的狀態了:

kubectl get pods -n kube-system

需要注意的是,由於 Dashboard 是一個 Web Server,很多人經常會在自己的公有雲上無意地暴露  Dashboard 的端口,從而造成安全隱患。所以,1.7 版本之后的 Dashboard 項目部署完成后,默認只能通過 Proxy 的方式在本地訪問。具體的操作,你可以查看 Dashboard 項目的官方文檔。

而如果你想從集群外訪問這個 Dashboard 的話,就需要用到 Ingress,我會在后面的文章中專門介紹這部分內容。

部署容器存儲插件

接下來,讓我們完成這個 Kubernetes 集群的最后一塊拼圖:容器持久化存儲。

我在前面介紹容器原理時已經提到過,很多時候我們需要用數據卷(Volume)把外面宿主機上的目錄或者文件掛載進容器的 Mount Namespace 中,從而達到容器和宿主機共享這些目錄或者文件的目的。容器里的應用,也就可以在這些數據卷中新建和寫入文件。

可是,如果你在某一台機器上啟動的一個容器,顯然無法看到其他機器上的容器在它們的數據卷里寫入的文件。這是容器最典型的特征之一:無狀態。

而容器的持久化存儲,就是用來保存容器存儲狀態的重要手段:存儲插件會在容器里掛載一個基於網絡或者其他機制的遠程數據卷,使得在容器里創建的文件,實際上是保存在遠程存儲服務器上,或者以分布式的方式保存在多個節點上,而與當前宿主機沒有任何綁定關系。這樣,無論你在其他哪個宿主機上啟動新的容器,都可以請求掛載指定的持久化存儲卷,從而訪問到數據卷里保存的內容。這就是“持久化”的含義。

由於 Kubernetes 本身的松耦合設計,絕大多數存儲項目,比如 Ceph、GlusterFS、NFS 等,都可以為 Kubernetes 提供持久化存儲能力。在這次的部署實戰中,我會選擇部署一個很重要的 Kubernetes 存儲插件項目:Rook。

Rook 項目是一個基於 Ceph 的 Kubernetes 存儲插件(它后期也在加入對更多存儲實現的支持)。不過,不同於對 Ceph 的簡單封裝,Rook 在自己的實現中加入了水平擴展、遷移、災難備份、監控等大量的企業級功能,使得這個項目變成了一個完整的、生產級別可用的容器存儲插件。

得益於容器化技術,用兩條指令,Rook 就可以把復雜的 Ceph 存儲后端部署起來:

kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/operator.yaml

kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/cluster.yaml

 

查詢所有pods 的namespace

kubectl get pods --all-namespaces

查看 pod rook-ceph-operator-5496d44d7c-nw5hz 詳細信息

kubectl describe pod  rook-ceph-operator-5496d44d7c-nw5hz -n rook-ceph-system

在部署完成后,你就可以看到 Rook 項目會將自己的 Pod 放置在由它自己管理的兩個 Namespace 當中:

kubectl get pods -n rook-ceph-system

kubectl get pods -n rook-ceph

這樣,一個基於 Rook 持久化存儲集群就以容器的方式運行起來了,而接下來在 Kubernetes 項目上創建的所有 Pod 就能夠通過 Persistent Volume(PV)和 Persistent Volume Claim(PVC)的方式,在容器里掛載由 Ceph 提供的數據卷了。

而 Rook 項目,則會負責這些數據卷的生命周期管理、災難備份等運維工作。關於這些容器持久化存儲的知識,我會在后續章節中專門講解。

這時候,你可能會有個疑問:為什么我要選擇 Rook 項目呢?

其實,是因為這個項目很有前途。 如果你去研究一下 Rook 項目的實現,就會發現它巧妙地依賴了 Kubernetes  提供的編排能力,合理的使用了很多諸如 Operator、CRD 等重要的擴展特性(這些特性我都會在后面的文章中逐一講解到)。這使得 Rook 項目,成為了目前社區中基於 Kubernetes API 構建的最完善也最成熟的容器存儲插件。我相信,這樣的發展路線,很快就會得到整個社區的推崇。

其實,在很多時候,大家說的所謂“雲原生”,就是“Kubernetes 原生”的意思。而像 Rook、Istio 這樣的項目,正是貫徹這個思路的典范。在我們后面講解了聲明式 API 之后,相信你對這些項目的設計思想會有更深刻的體會。

備忘: 

查看全部節點

kubectl get pods --all-namespaces

查看pods

kubectl describe pod -n kube-system

查看具體問題

kubectl describe pod kubernetes-dashboard-767dc7d4d-mg5gw -n kube-system

查看pod日志

kubectl logs -f podname -n cotainname

 

如果初始化過程出現問題,使用如下命令重置:

kubeadm reset

根據 kubeadm 版本查看其所需鏡像  

kubeadm config images list --kubernetes-version v1.11.1

 

 

 

centos安裝步奏

systemctl stop firewalld
systemctl disable firewalld

  

官方安裝地址

https://docs.docker.com/install/linux/docker-ce/centos/

 

安裝docker

yum install -y --setopt=obsoletes=0 \
   docker-ce-17.03.2.ce-1.el7.centos.x86_64 \
   docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch

  

參考:

https://www.datayang.com/article/45

https://www.cnblogs.com/hongdada/p/9761336.html

https://www.liyang.pro/archives/134

https://blog.csdn.net/huangjun0210/column/info/32515

  

  

  

 

  

 


免責聲明!

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



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