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