k8s 入門教程和實戰

在本文中,咱們從技術細節上對kubernetes進行簡單運用介紹,利用一些yaml腳本層面上實例告訴你們kubernetes基本概念。Kubernetes以及它呈現出的編程範式值得你去使用和整合到本身的技術棧中。php

你們若有興趣,能夠關注由達觀數據出品的更多經典好文datagrand達觀數據html

kubernetes簡單介紹

kubernetes起源

Kubernetes最初認爲是谷歌開源的容器集羣管理系統,是Google多年大規模容器管理技術Borg或Omega的開源版本。準確來講的話,kubernetes更是一個全新的平臺,一個全新的
平臺管理工具,它是專門爲job和service設計。徹底開放,2014年6月開始接受公開的commit,任何人均可以提供意見。因爲kubernetes簡化了開發、運維和管理負荷,愈來愈多
的企業開始在生產環境使用,所以kubernetes獲得了迅速的發展。node

kubernetes功能

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

kubernetes核心組件

Kubernetes主要由如下幾個核心組件組成:mysql

  • etcd保存了整個集羣的狀態;
  • apiserver提供了資源操做的惟一入口,並提供認證、受權、訪問控制、API註冊和發現等機制;
  • controller manager負責維護集羣的狀態,好比故障檢測、自動擴展、滾動更新等;
  • scheduler負責資源的調度,按照預約的調度策略將Pod調度到相應的機器上;
  • kubelet負責維護容器的生命週期,同時也負責Volume(CVI)和網絡(CNI)的管理;
  • Container runtime負責鏡像管理以及Pod和容器的真正運行(CRI);
  • kube-proxy負責爲Service提供cluster內部的服務發現和負載均衡

kubernetes環境部署

若是隻是爲了瞭解kubernetes,可使用minikube的方式進行單機安裝,minikube 實際就是本地建立了一個虛擬機,裏面運行了kubernetes 的一些必要的環境,至關於 k8s 的服務環境,建立 pod,service,deployment等都是在裏面進行建立和管理。
在本文中,我使用kubeadm方式安裝kubernetes 1.10.0,具體kubernetes部署步驟:nginx

請注意:上述環境只是測試環境,生產環境部署大同小異。git

kubernetes基本概念

Container

Container(容器)是一種便攜式、輕量級的操做系統級虛擬化技術。它使用 namespace 隔離不一樣的軟件運行環境,並經過鏡像自包含軟件的運行環境,
從而使得容器能夠很方便的在任何地方運行。因爲容器體積小且啓動快,所以能夠在每一個容器鏡像中打包一個應用程序。這種一對一的應用鏡像關係擁有不少好處。使用容器,
不須要與外部的基礎架構環境綁定, 由於每個應用程序都不須要外部依賴,更不須要與外部的基礎架構環境依賴。完美解決了從開發到生產環境的一致性問題。
容器一樣比虛擬機更加透明,這有助於監測和管理。尤爲是容器進程的生命週期由基礎設施管理,而不是由容器內的進程對外隱藏時更是如此。最後,
每一個應用程序用容器封裝,管理容器部署就等同於管理應用程序部署。
在 Kubernetes 必需要使用 Pod 來管理容器,每一個 Pod 能夠包含一個或多個容器。web

Pod

關於Pod的概念主要有如下幾點:

  • Pod是kubernetes中你能夠建立和部署的最小也是最簡的單位。一個Pod表明着集羣中運行的一個進程;
  • 在Kubrenetes集羣中Pod的使用方式;
  • Pod中如何管理多個容器

理解Pod:

上面已經說了「Pod是kubernetes中你能夠建立和部署的最小也是最簡的單位。一個Pod表明着集羣中運行的一個進程。」Pod中封裝着應用的容器(有的狀況下是好幾個容器),
存儲、獨立的網絡IP,管理容器如何運行的策略選項。Pod表明着部署的一個單位:kubernetes中應用的一個實例,可能由一個或者多個容器組合在一塊兒共享資源。
請注意:Docker是kubernetes中最經常使用的容器運行時,可是Pod也支持其餘容器運行時。redis

Kubrenetes集羣中Pod的兩種使用方式:

(1)一個Pod中運行一個容器
「每一個Pod中一個容器」的模式是最多見的用法;在這種使用方式中,你能夠把Pod想象成是單個容器的封裝,kuberentes管理的是Pod而不是直接管理容器。sql

實戰:建立一個nginx容器
apiVersion: v1
kind: Pod
metadata:
  name: nginx-test
  labels:
    app: web
spec:
  containers:
  - name: front-end
    image: nginx:1.7.9
    ports:
    - containerPort: 80
建立Pod:
kubectl create -f ./pod1-deployment\
查看Pod:
kubectl get po
查看Pod詳細狀況:
kubectl describe po nginx-test
進入到Pod(容器)內部:
kubectl exec -it nginx-test  /bin/bash

(2)在一個Pod中同時運行多個容器
說明:在一個Pod中同時運行多個容器是一種比較高級的用法。只有當你的容器須要緊密配合協做的時候才考慮用這種模式。
一個Pod中也能夠同時封裝幾個須要緊密耦合互相協做的容器,它們之間共享資源。這些在同一個Pod中的容器能夠互相協做成爲一個service單位——一個容器共享文件,
另外一個「sidecar」容器來更新這些文件。Pod將這些容器的存儲資源做爲一個實體來管理。docker

實戰:在一個pod裏放置兩個容器:nginx與redis
apiVersion: v1
kind: Pod
metadata:
  name: rss-site
  labels:
    app: web
spec:
  containers:
  - name: front-end
    image: nginx:1.7.9
    ports:
    - containerPort: 80
  - name: rss-reader
    image: redis
    ports:
    - containerPort: 88
建立Pod:
kubectl create -f ./test-deployment
查看pod
kubectl get po
查看Pod詳細狀況
kubectl describe po rss-site 
進入front-end內部:
kubectl exec -it rss-site  -c front-end /bin/bash 
進入rss-reade內部:
kubectl exec -it rss-site  -c rss-reader  /bin/bash

以上是關於Pod的簡單介紹,如需瞭解更多,請參考Pod

Node

Node 是 Pod 真正運行的主機,能夠物理機,也能夠是虛擬機。爲了管理 Pod,每一個 Node 節點上至少要運行 container runtime(好比 docker 或者 rkt)、kubelet 和 kube-proxy 服務。

Namespace

Namespace 是對一組資源和對象的抽象集合,好比能夠用來將系統內部的對象劃分爲不一樣的項目組或用戶組。常見的 pods, services, replication controllers 和 deployments 等都是屬於
某一個 namespace 的(默認是 default),而 node, persistentVolumes 等則不屬於任何 namespace。

Deployment

咱們既然有Pod了,爲何還要使用Deployment呢?這是由於實際工做中,咱們不多會直接在kubernetes中建立單個Pod。由於Pod的生命週期是短暫的,用後即焚的實體。
Deployment 爲 Pod 和 ReplicaSet 提供了一個聲明式定義(declarative)方法,用來替代之前的ReplicationController 來方便的管理應用。
你只須要在 Deployment 中描述想要的目標狀態是什麼,Deployment controller 就會幫你將 Pod 和ReplicaSet 的實際狀態改變到你的目標狀態。你能夠定義一個全新的 Deployment 來建立
ReplicaSet 或者刪除已有的 Deployment 並建立一個新的來替換。

什麼是複製控制器(Replication Controller,RC)

RC是K8s集羣中最先的保證Pod高可用的API對象。經過監控運行中的Pod來保證集羣中運行指定數目的Pod副本。指定的數目能夠是多個也能夠是1個;少於指定數目,RC就會啓動運行新的Pod副本;
多於指定數目,RC就會殺死多餘的Pod副本。即便在指定數目爲1的狀況下,經過RC運行Pod也比直接運行Pod更明智,由於RC也能夠發揮它高可用的能力,保證永遠有1個Pod在運行。RC是K8s較早期
的技術概念,只適用於長期伺服型的業務類型,好比控制小機器人提供高可用的Web服務。

什麼是副本集(Replica Set,RS)

RS是新一代RC,提供一樣的高可用能力,區別主要在於RS後來居上,能支持更多種類的匹配模式。副本集對象通常不單獨使用,而是做爲Deployment的理想狀態參數使用。

Deployment典型的應用場景

  • 定義Deployment來建立Pod和ReplicaSet
  • 滾動升級和回滾應用;若是當前狀態不穩定,回滾到以前的Deployment revision。每次回滾都會更新Deployment的revision
  • 擴容和縮容,擴容Deployment以知足更高的負載
  • 暫停和繼續Deployment,暫停Deployment來應用PodTemplateSpec的多個修復,而後恢復上線

實戰Deployment

好比,咱們這裏定義一個簡單的nginx應用:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
    name: nginx-test
    namespace: test
spec:
    replicas: 3
    template:
        metadata:
            labels:
                app: nginx
        spec:
            containers:
              - name: nginx
                image: nginx:1.7.9
                ports:
                - containerPort: 80
建立deploy
kubectl create -f ./nginx-deployment
查看deploy
kubectl get deploy --namespace=test
查看rs(副本集)
kubectl get rs --namespace=test
查看pods(容器組)
kubectl get po --namespace=test

關於Deployment的應用還有不少,如:擴容、縮容、滾動升級、回滾應用等,這裏因爲篇幅的問題再也不一一介紹,詳見Deployment的應用

Label

Label 是識別 Kubernetes 對象的標籤,以 key/value 的方式附加到對象上(key 最長不能超過 63 字節,value 能夠爲空,也能夠是不超過 253 字節的字符串)。
Label 不提供惟一性,而且實際上常常是不少對象(如 Pods)都使用相同的 label 來標誌具體的應用。
Label 定義好後其餘對象可使用 Label Selector 來選擇一組相同 label 的對象(好比 ReplicaSet 和 Service 用 label 來選擇一組 Pod)。Label Selector 支持如下幾種方式:

  • 等式,如 app=nginx 和 env!=production
  • 集合,如 env in (production, qa)
  • 多個 label(它們之間是 AND 關係),如 app=nginx,env=test

Service Account

Service account做用

Service account是爲了方便Pod裏面的進程調用Kubernetes API或其餘外部服務。

Service account使用場景

運行在pod裏的進程須要調用Kubernetes API以及非Kubernetes API的其它服務。Service Account它並非給kubernetes集羣的用戶使用的,而是給pod裏面的進程使用的,它爲pod提供必要的身份認證。

與User account區別

  • User account是爲人設計的,而service account則是爲了Pod中的進程
  • User account是跨namespace的,而service account則是僅侷限它所在的namespace

實戰命名空間

apiVersion: v1
kind: Namespace
metadata:
  name: datagrand
  labels:
    name: test
建立namespace:test
kubectl create -f ./test.yaml  
查看命名空間test的sa
kubectl get sa -n test
查看命名空間test生成的default
kubectl get sa default -o yaml -n test
咱們能夠建立Deployment時,使用這個test命名空間了,如上例Deployment實戰。

Service Account鑑權

Service Account爲服務提供了一種方便的認知機制,但它不關心受權的問題。能夠配合RBAC來爲Service Account鑑權:

  • 配置--authorization-mode=RBAC和--runtime-config=rbac.authorization.k
    8s.io/v1alpha1
  • 配置--authorization-rbac-super-user=admin
  • 定義Role、ClusterRole、RoleBinding或ClusterRoleBinding

實戰鑑權

咱們在Kubernetes Dashboard1.8.3部署中,碰到首次登入出現訪問權限報錯的問題,緣由就是ServiceAccount的建立問題。

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
      name: kubernetes-dashboard
      labels:
          k8s-app: kubernetes-dashboard
roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: cluster-admin
subjects:
    - kind: ServiceAccount
      name: kubernetes-dashboard
      namespace: kube-system

Service Account介紹請參考博文

Secret

Secret介紹

Secret解決了密碼、token、密鑰等敏感數據的配置問題,而不須要把這些敏感數據暴露到鏡像或者Pod Spec中。Secret能夠以Volume或者環境變量的方式使用。

Secret類型

  • Opaque(default):任意字符串,base64編碼格式的Secret,用來存儲密碼、密鑰等
  • kubernetes.io/service-account-token:做用於ServiceAccount,就是kubernetes的Service Account中所說的。 即用來訪問Kubernetes API,由Kubernetes自動建立,而且會自動掛載到Pod的/run/secrets/kubernetes.io/serviceaccount目錄中
  • kubernetes.io/dockercfg: 做用於Docker registry,用戶下載docker鏡像認證使用。用來存儲私有docker registry的認證信息

實戰Opaque Secret類型

Opaque類型的數據是一個map類型,要求value是base64編碼格式:
建立admin帳戶
echo -n "admin" | base64
YWRtaW4=
echo -n "1f2d1e2e67df" | base64
MWYyZDFlMmU2N2Rm
建立secret.yaml
cat >> secrets.yml << EOF
apiVersion: v1
kind: Secret
metadata:
    name: mysecret
type: Opaque
data:
    password: MWYyZDFlMmU2N2Rm
    username: YWRtaW4=
建立secret
kubectl create -f secrets.yml
查看secret運行狀態
kubectl get secret --all-namespaces

Secret使用方式

  • 以Volume方式
  • 以環境變量方式

實戰Secret使用Volume方式

apiVersion: v1
kind: Pod
metadata:
  name: mypod
  labels:
    name: wtf
spec:
  volumes:
  - name: secrets
    secret:
      secretName: mysecret
  containers:
  - image: nginx:1.7.9
    name: nginx
    volumeMounts:
    - name: secrets
      mountPath: "/etc/secrets"
      readOnly: true
    ports:
    - name: cp
      containerPort: 5432
      hostPort: 5432

說明:這樣就能夠經過文件的方式掛載到容器內,在/etc/secrets目錄下回
生成這個文件。

實戰Secret使用環境變量方式

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: wordpress-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: "wordpress"
        image: "wordpress:latest"
        ports:
        - containerPort: 80
        env:
        - name: WORDPRESS_DB_USER
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: username
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: password
查看Pod運行狀態
kubectl get po
NAME                                    READY     STATUS    RESTARTS   AGE
wordpress-deployment-6b569fbb7d-8qcpg   1/1       Running   0          2m
wordpress-deployment-6b569fbb7d-xwwkg   1/1       Running   0          2m
進入容器內部查看環境變量
kubectl exec -it wordpress-deployment-694f4c79b4-cpsxw  /bin/bash
root@wordpress-deployment-694f4c79b4-cpsxw:/var/www/html# env
WORDPRESS_DB_USER=admin
WORDPRESS_DB_PASSWORD=1f2d1e2e67df

想要了解更多,請詳見Kubernetes的Secret

ConfigMap

Configure 說明

ConfigMaps容許你將配置文件、命令行參數或環境變量中讀取的配置信息與docker image分離,以保持集裝箱化應用程序的便攜性。即ConfigMap API給咱們提供了向容器中注入配置信息的機制。

理解ConfigMaps 和 Pods

ConfigMap API資源用來保存key-value pair配置數據,這個數據能夠在pods裏使用,或者被用來爲像controller同樣的系統組件存儲配置數據。雖然ConfigMap跟Secrets相似,可是ConfigMap更方便的處理不含敏感信息的字符串。
注意:ConfigMaps不是屬性配置文件的替代品。ConfigMaps只是做爲多個properties文件的引用。你能夠把它理解爲Linux系統中的/etc目錄,專門用來存儲配置文件的目錄。

實戰建立ConfigMap

kind: ConfigMap
apiVersion: v1
metadata:
  creationTimestamp: 2016-02-18×××9:14:38Z
  name: example-config
  namespace: default
  data:
    example.property.1: hello
    example.property.2: world
    example.property.file: |-
      property.1=value-1
      property.2=value-2
      property.3=value-3

data一欄包括了配置數據,ConfigMap能夠被用來保存單個屬性,也能夠用來保存一個配置文件。 配置數據能夠經過不少種方式在Pods裏被使用。ConfigMaps能夠被用來:

  • 設置環境變量的值
  • 在容器裏設置命令行參數
  • 在數據卷裏面建立config文件

更多ConfigMap實戰,詳見ConfigMap - Kubernetes

Volume

爲何須要Volume

容器磁盤上文件的生命週期是短暫的,這就使得在容器中運行重要應用時出現一些問題。好比,當容器崩潰時,kubelet會重啓它,可是容器中的文件將丟失--容器以乾淨的狀態
(鏡像最初的狀態)從新啓動。其次,在 Pod 中同時運行多個容器時,這些容器之間一般須要共享文件。Kubernetes 中的 Volume 抽象就很好的解決了這些問題。

Volume背景

Docker 中也有一個 volume 的概念,儘管它稍微寬鬆一些,管理也不多。在 Docker 中,卷就像是磁盤或是另外一個容器中的一個目錄。它的生命週期不受管理,直到最近纔有了 local-disk-backed 卷。Docker 如今提供了卷驅動程序,可是功能還很是有限(例如Docker1.7只容許每一個容器使用一個卷驅動,而且沒法給卷傳遞參數)。
Kubernetes 中的卷有明確的壽命——與封裝它的 Pod 相同。因此,卷的生命比 Pod 中的全部容器都長,當這個容器重啓時數據仍然得以保存。固然,當 Pod 再也不存在時,卷也將不復存在。也許更重要的是,Kubernetes 支持多種類型的卷,Pod 能夠同時使用任意數量的卷。
要使用卷,須要爲 pod 指定爲卷(spec.volumes 字段)以及將它掛載到容器的位置(spec.containers.volumeMounts 字段)。

Volume類型

Kubernetes 支持如下類型的卷:

awsElasticBlockStore、azureDisk、azureFile、cephfs、csi、downwardAPI、emptyDir、fc (fibre channel)、flocker、gcePersistentDisk、gitRepo、glusterfs、hostPath、iscsi、local、nfs、persistentVolumeClaim、projected、portworxVolume、quobyte、rbd、scaleIO、secret、storageos、vsphereVolume等

K8S的存儲系統分類

K8S的存儲系統從基礎到高級大體分爲三個層次:普通Volume,Persistent Volume 和動態存儲供應。

普通Volume

最簡單的普通Volume是單節點Volume。它和Docker的存儲卷相似,使用的是Pod所在K8S節點的本地目錄。

persistent volume

它和普通Volume的區別是什麼呢?
普通Volume和使用它的Pod之間是一種靜態綁定關係,在定義Pod的文件裏,同時定義了它使用的Volume。Volume 是Pod的附屬品,咱們沒法單首創建一個Volume,由於它不是一個獨立的K8S資源對象。
而Persistent Volume 簡稱PV是一個K8S資源對象,因此咱們能夠單首創建一個PV。它不和Pod直接發生關係,而是經過Persistent Volume Claim,簡稱PVC來實現動態綁定。Pod定義裏指定的是PVC,而後PVC會根據Pod的要求去自動綁定合適的PV給Pod使用。
PV的訪問模式有三種:

  • ReadWriteOnce:是最基本的方式,可讀可寫,但只支持被單個Pod掛載
  • ReadOnlyMany:能夠以只讀的方式被多個Pod掛載
  • ReadWriteMany:這種存儲能夠以讀寫的方式被多個Pod共享。比較經常使用的是NFS

通常來講,PV和PVC的生命週期分爲5個階段:

  • Provisioning,即PV的建立,能夠直接建立PV(靜態方式),也可使用StorageClass動態建立
  • Binding,將PV分配給PVC
  • Using,Pod經過PVC使用該Volume
  • Releasing,Pod釋放Volume並刪除PVC
  • Reclaiming,回收PV,能夠保留PV以便下次使用,也能夠直接從雲存儲中刪除

根據這5個階段,Volume的狀態有如下4種:

  • Available:可用
  • Bound:已經分配給PVC
  • Released:PVC解綁但還未執行回收策略
  • Failed:發生錯誤

變成Released的PV會根據定義的回收策略作相應的回收工做。有三種回收策略:

  • Retain 就是保留現場,K8S什麼也不作,等待用戶手動去處理PV裏的數據,處理完後,再手動刪除PV
  • Delete K8S會自動刪除該PV及裏面的數據
  • Recycle K8S會將PV裏的數據刪除,而後把PV的狀態變成Available,又能夠被新的PVC綁定使用

在實際使用場景裏,PV的建立和使用一般不是同一我的。這裏有一個典型的應用場景:管理員建立一個PV池,開發人員建立Pod和PVC,PVC裏定義了Pod所需存儲的大小和訪問模式,而後PVC會到PV池裏自動匹配最合適的PV給Pod使用。
前面在介紹PV的生命週期時,提到PV的供給有兩種方式,靜態和動態。其中動態方式是經過StorageClass來完成的,這是一種新的存儲供應方式。
使用StorageClass有什麼好處呢?除了由存儲系統動態建立,節省了管理員的時間,還有一個好處是能夠封裝不一樣類型的存儲供PVC選用。在StorageClass出現之前,PVC綁定一個PV只能根據兩個條件,一個是存儲的大小,另外一個是訪問模式。在StorageClass出現後,等於增長了一個綁定維度。
好比這裏就有兩個StorageClass,它們都是用谷歌的存儲系統,可是一個使用的是普通磁盤,咱們把這個StorageClass命名爲slow。另外一個使用的是SSD,咱們把它命名爲fast。
在PVC裏除了常規的大小、訪問模式的要求外,還經過annotation指定了Storage Class的名字爲fast,這樣這個PVC就會綁定一個SSD,而不會綁定一個普通的磁盤。
限於篇幅問題,我這裏簡單說一下emptyDir、nfs、PV和PVC。

實戰--emptyDir

emptyDir說明

EmptyDir類型的volume建立於pod被調度到某個宿主機上的時候,而同一個pod內的容器都能讀寫EmptyDir中的同一個文件。一旦這個pod離開了這個宿主機,EmptyDir中的數據就會被永久刪除。因此目前EmptyDir類型的volume主要用做臨時空間,好比Web服務器寫日誌或者tmp文件須要的臨時目錄。

apiVersion: v1
kind: Pod
metadata:
  labels:
    name: test-emptypath
    role: master
  name: test-emptypath
spec:
  containers:
  - name: test-emptypath
    image: nginx:1.7.9
    volumeMounts:
    - name: log-storage
      mountPath: /tmp/
  volumes:
  - name: log-storage
    emptyDir: {}

實戰使用共享卷的標準多容器Pod

apiVersion: v1
kind: Pod
metadata:
  name: datagrand
spec:
  containers:
  - name: test1
    image: nginx:1.7.9
    volumeMounts:
    - name: log-storage
      mountPath: /usr/share/nginx/html
  - name: test2
    image: centos
    volumeMounts:
    - name: log-storage
      mountPath: /html
    command: ["/bin/sh","-c"]
    args:
    - while true;do
      data >> /html/index.html;
      sleep 1;
      done
  volumes:
    - name: log-storage
      emptyDir: {}

簡單解釋下上面的內容:
在這個例子中,咱們定義了一個名爲HTML的卷。它的類型是emptyDir,這意味着當一個Pod被分配到一個節點時,卷先被建立,並只要Pod在節點上運行時,這個卷仍存在。正如名字所說,它最初是空的。第一容器運行nginx的
服務器並將共享卷掛載到目錄/ usr /share/ nginx /html。第二容器使用centos的鏡像,並將共享卷掛載到目錄/HTML。每一秒,第二容器添加當前日期和時間到index.html文件中,它位於共享卷。當用戶發出一個HTTP請求到Pod,
nginx的服務器讀取該文件並將其傳遞給響應請求的用戶。

更多volume實戰,詳見Kubernetes部分Volume類型介紹及yaml示例--emptyDir

實戰--NFS卷

NFS卷說明

nfs 卷容許將現有的 NFS(網絡文件系統)共享掛載到你的容器中。不像 emptyDir,當刪除 Pod 時,nfs 卷的內容被保留,卷僅僅是被卸載。這意味着 NFS 卷能夠預填充數據,而且能夠在 pod 之間「切換」數據。 NFS 能夠被多個寫入者同時掛載。

NFS卷使用注意
  • 請先部署好本身的NFS服務
  • 在使用共享以前,必須運行本身的NFS服務器並運行共享

實戰pod內的文件共享

apiVersion: v1
kind: Pod
metadata:
  name: nginx-test
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.7.9
    ports:
    - containerPort: 80
      volumeMounts:
    #Mount the path to the container
      - mountPath: "/tmp/"
        name: pv0003
  volumes:
  - name: pv0003
    nfs:
    #fixed:This ip is the address of the nfs server
      server: 192.168.246.169
    #fixed:This path is shared externally by the nfs server
      path: "/data"

更多volume實戰,詳見Kubernetes部分Volume類型介紹及yaml示例--NFS(網絡數據卷)

實戰PV和PVC

nfs 做爲 k8s 的網絡存儲驅動,能夠知足持久存儲業務的需求,支持多節點讀寫。下面是兩個Pod同時使用一個持久性volume實例。

#建立PV
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    storage: 4Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.246.168  ##NFS服務器的ip地址
    path: "/data"  ##NFS服務器上的共享目錄
#建立PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: ""
  resources:
    requests:
      storage: 3Gi
#建立Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        volumeMounts:
          - mountPath: "/wtf"
            name: datadir
      volumes:
      - name: datadir
        persistentVolumeClaim:
          claimName: nfs-pvc

更多PV和PVC實戰,詳見實戰kubernetes持久性卷使用

Service

Kubernetes Pod 是有生命週期的,它們能夠被建立,也能夠被銷燬,然而一旦被銷燬生命就永遠結束。 經過 ReplicaSets 可以動態地建立和銷燬 Pod(例如,須要進行擴縮容,或者執行 滾動升級)。 每一個 Pod 都會獲取它本身的 IP 地址,即便這些 IP 地址不老是穩定可依賴的。 這會致使一個問題:在 Kubernetes 集羣中,若是一組 Pod(稱爲 backend)爲其它 Pod (稱爲 frontend)提供服務,那麼那些 frontend 該如何發現,並鏈接到這組 Pod 中的哪些 backend 呢?

什麼是Service

Kubernetes Service 定義了這樣一種抽象:一個 Pod 的邏輯分組,一種能夠訪問它們的策略 —— 一般稱爲微服務。 這一組 Pod 可以被 Service 訪問到,一般是經過 Label Selector(查看下面瞭解,爲何可能須要沒有 selector 的 Service)實現的。
舉個例子,考慮一個圖片處理 backend,它運行了3個副本。這些副本是可互換的 —— frontend 不須要關心它們調用了哪一個 backend 副本。 然而組成這一組 backend 程序的 Pod 實際上可能會發生變化,frontend 客戶端不該該也不必知道,並且也不須要跟蹤這一組 backend 的狀態。 Service 定義的抽象可以解耦這種關聯。
對 Kubernetes 集羣中的應用,Kubernetes 提供了簡單的 Endpoints API,只要 Service 中的一組 Pod 發生變動,應用程序就會被更新。 對非 Kubernetes 集羣中的應用,Kubernetes 提供了基於 VIP 的網橋的方式訪問 Service,再由 Service 重定向到 backend Pod。

定義Service

有selector的單端口Service

一個 Service 在 Kubernetes 中是一個 REST 對象,和 Pod 相似。 像全部的 REST 對象同樣, Service 定義能夠基於 POST 方式,請求 apiserver 建立新的實例。 例如,假定有一組 Pod,它們對外暴露了 9376 端口,同時還被打上 "app=MyApp" 標籤。

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

上述配置將建立一個名稱爲 「my-service」 的 Service 對象,它會將請求代理到使用 TCP 端口 9376,而且具備標籤 "app=MyApp" 的 Pod 上。 這個 Service 將被指派一個 IP 地址(一般稱爲 「Cluster IP」),它會被服務的代理使用(見下面)。 該 Service 的 selector 將會持續評估,處理結果將被 POST 到一個名稱爲 「my-service」 的 Endpoints 對象上。
須要注意的是, Service 可以將一個接收端口映射到任意的 targetPort。 默認狀況下,targetPort 將被設置爲與 port 字段相同的值。 可能更有趣的是,targetPort 能夠是一個字符串,引用了 backend Pod 的一個端口的名稱。 可是,實際指派給該端口名稱的端口號,在每一個 backend Pod 中可能並不相同。 對於部署和設計 Service ,這種方式會提供更大的靈活性。 例如,能夠在 backend 軟件下一個版本中,修改 Pod 暴露的端口,並不會中斷客戶端的調用。
Kubernetes Service 可以支持 TCP 和 UDP 協議,默認 TCP 協議。

有selector的多端口Service

不少 Service 須要暴露多個端口。對於這種狀況,Kubernetes 支持在 Service 對象中定義多個端口。 當使用多個端口時,必須給出全部的端口的名稱,這樣 Endpoint 就不會產生歧義,例如:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
    selector:
      app: MyApp
    ports:
      - name: http
        protocol: TCP
        port: 80
        targetPort: 9376
      - name: https
        protocol: TCP
        port: 443
        targetPort: 9377
沒有 selector 的 Service

Service 抽象了該如何訪問 Kubernetes Pod,但也可以抽象其它類型的 backend,例如:

  • 但願在生產環境中使用外部的數據庫集羣,但測試環境使用本身的數據庫。
  • 但願服務指向另外一個 Namespace 中或其它集羣中的服務。
  • 正在將工做負載轉移到 Kubernetes 集羣,和運行在 Kubernetes 集羣以外的 backend。

根據以上的應用場景,咱們都可以定義沒有selector的Service,以下:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

因爲這個 Service 沒有 selector,就不會建立相關的 Endpoints 對象。能夠手動將 Service 映射到指定的 Endpoints:

kind: Endpoints
apiVersion: v1
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 10.0.0.3  ##Endpoint IP = PodIP + ContainerPort
    ports:
      - port: 9376

注意:Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 link-local(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。
訪問沒有 selector 的 Service,與有 selector 的 Service 的原理相同。請求將被路由到用戶定義的 Endpoint(該示例中爲 10.0.0.3:9376)。

發佈服務 —— 服務類型

對一些應用(如 Frontend)的某些部分,可能但願經過外部(Kubernetes 集羣外部)IP 地址暴露 Service。
Kubernetes ServiceTypes 容許指定一個須要的類型的 Service,默認是 ClusterIP 類型。
Type 的取值以及行爲以下:

  • ClusterIP 經過集羣的內部 IP 暴露服務,選擇該值,服務只可以在集羣內部能夠訪問,這也是默認的 ServiceType。
  • NodePort 經過每一個 Node 上的 IP 和靜態端口(NodePort)暴露服務。NodePort 服務會路由到 ClusterIP 服務,這個 ClusterIP 服務會自動建立。經過請求 <NodeIP>:<NodePort>,能夠從集羣的外部訪問一個 NodePort 服務。
  • LoadBalancer 使用雲提供商的負載均衡器,能夠向外部暴露服務。外部的負載均衡器能夠路由到 NodePort 服務和 ClusterIP 服務。
  • ExternalName 經過返回 CNAME 和它的值,能夠將服務映射到 externalName 字段的內容(例如, foo.bar.example.com)。 沒有任何類型代理被建立,這隻有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。
NodePort 類型

若是設置 type 的值爲 "NodePort",Kubernetes master 將從給定的配置範圍內(默認:30000-32767)分配端口,每一個 Node 將從該端口(每一個 Node 上的同一端口)代理到 Service。該端口將經過 Service 的 spec.ports[*].nodePort 字段被指定。
若是須要指定的端口號,能夠配置 nodePort 的值,系統將分配這個端口,不然調用 API 將會失敗(好比,須要關心端口衝突的可能性)。

kubernetes實戰--edusoho平臺建立

文件目錄結構

# pwd
/data
# tree -L 3
.
├── mysql
│   ├── conf
│   │   └── my.cnf
│   └── data
│       ├── auto.cnf
│       ├── edusoho
│       ├── ibdata1
│       ├── ib_logfile0
│       ├── ib_logfile1
│       ├── mysql
│       └── performance_schema
├── nginx
│   ├── conf
│   │   └── nginx.conf
│   ├── edusoho
│   │   ├── api
│   │   ├── app
│   │   ├── bootstrap
│   │   ├── plugins
│   │   ├── src
│   │   ├── vendor
│   │   ├── vendor_user
│   │   └── web
│   └── log
│       └── error.log
├── php
│   ├── log
│   │   └── php-fpm.log
│   ├── php-fpm.conf
│   ├── php.ini
│   └── www.conf

Pod的yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: lamp-edusoho
  labels:
    app: lamp-edusoho
restartPolicy: Always
spec:
  containers:
  - name: nginx
    abels:
      app: lamp-nginx
    image: dockerhub.datagrand.com/global/nginx:v1
    ports:
    - containerPort: 80
    volumeMounts:
      - name: datadir
        mountPath: "/var/log/nginx/error.log"
        subPath: ./nginx/log/error.log
      - name: datadir
        mountPath: "/etc/nginx/nginx.conf"
        subPath: ./nginx/conf/nginx.conf
      - name: datadir
        mountPath: "/usr/share/nginx/html"
        subPath: ./nginx/edusoho
  - name: php
    image: dockerhub.datagrand.com/global/php:v1
    ports:
    - containerPort: 9000
    volumeMounts:
      - mountPath: /usr/local/php/etc/php-fpm.conf
        name: datadir
        subPath: ./php/php-fpm.conf
      - mountPath: /usr/local/php/etc/php-fpm.d/www.conf
        name: datadir
        subPath: ./php/www.conf
      - mountPath: /usr/local/php/etc/php.ini
        name: datadir
        subPath: ./php/php.ini
      - mountPath: /usr/local/php/var/log/php-fpm.log
        name: datadir
        subPath: ./php/log/php-fpm.log
      - mountPath: /usr/share/nginx/html
        name: datadir
        subPath: ./nginx/edusoho
  - name: mysql
    image: dockerhub.datagrand.com/global/mysql:5.6
    ports:
    - containerPort: 3306
    env:
      - name: MYSQL_ROOT_PASSWORD
        value: "123456"
      - name: MYSQL_DATABASE
        value: "edusoho"
      - name: MYSQL_USER
        value: "edusoho"
      - name: MYSQL_PASSWORD
        value: "edusoho"
    args: ['--character-set-server=utf8']
    volumeMounts:
      - name: datadir
        mountPath: "/var/lib/mysql"
        subPath: ./mysql/data
      - name: datadir
        mountPath: "/etc/my.cnf"
        subPath: ./mysql/conf/my.cnf
  volumes:
  - name: datadir
    persistentVolumeClaim:
      claimName: nfs-pvc

PV的yaml文件

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    storage: 4Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.246.168  ##NFS服務器的ip地址
    path: "/data"  ##NFS服務器上的共享目錄

PVC的yaml文件

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: ""
  resources:
    requests:
      storage: 3Gi

Service的yaml文件

apiVersion: v1
kind: Service
metadata:
  name: edusoho
  labels:
    app: edusoho
spec:
  type: NodePort 
  ports:
  - port: 80
    nodePort: 32756
  selector:
    app: lamp-edusoho

命令彙總

查看Pod
kubectl get po -o wide
查看Service
kubectl get svc
進入容器內部某個應用,如這裏的nginx
kubectl exec -it lamp-edusoho -c nginx /bin/bash

訪問安裝Edusoho平臺

http://192.168.246.168:32756/install/start-install.php
說明:這裏的192.168.246.168是kubernetes的node節點IP,32756是Service中定義的nodePort。

參考文檔

相關文章
相關標籤/搜索