1. 程式人生 > 實用技巧 >K8S學習筆記記錄

K8S學習筆記記錄

1.k8s概述
2.k8s安裝單機與叢集
3.pod
4.Replication Controller(RC)/Replica Set(RS)
5.Deployment
6.service
7.NodePort
8.service
9.Volume

1.k8s概述

k8s(Kubernetes)是容器叢集管理系統,可以實現容器叢集的自動化部署、自動化擴縮容、維護等功能。k8s能提供一個以“容器為中心的基礎架構”,k8s的一個核心特點就是能夠自主的管理容器來保證雲平臺中的容器按照使用者的期望狀態執行著。k8s著重於不間斷的服務狀態,即容器掛掉後自動重啟,始終保持使用者期望的叢集數執行。

通過k8s可以實現的功能:

  1. 快速部署應用
  2. 快速擴充套件應用
  3. 無縫對接新的應用功能
  4. 節省資源,優化硬體資源的使用

k8s的特點:

  • 可移植:支援公有云,私有云,混合雲,多重雲
  • 可擴充套件:模組化,外掛化,可掛載,可組合
  • 自動化:自動部署,自動重啟,自動複製,自動伸縮/擴充套件

k8s的核心元件:

  • etcd:儲存了整個叢集的狀態
  • apiserver:提供了資源操作的唯一入口,並提供認證、授權、訪問控制、API註冊和發現等機制
  • controller manager:負責維護叢集的狀態,比如故障檢測、自動擴充套件、滾動更新等
  • scheduler:負責資源的排程,按照預定的排程策略將pod排程到相應的機器上
  • kubelet
    :負責維護容器的生命週期,同時也負責Volume和網路的管理
  • container runtime:負責映象管理以及pod和容器的真正執行(CRI)
  • kube-proxy:負責為service提供cluster內部的服務發現和負載均衡

2.k8s安裝單機與叢集

這裡筆者使用的是三臺虛擬機器(2G記憶體,2核),一主兩從。

1.安裝並更新依賴

[root@localhost ~]# yum -y update
[root@localhost ~]# yum install -y conntrack ipvsadm ipset jq sysstat curl iptables libseccomp

2.安裝docker並設定docker倉庫,配置映象加速器等。關於docker的歡迎檢視筆者的上一篇部落格,這裡就不再說明了:

https://www.cnblogs.com/pluto-charon/p/14118514.html

3.修改host檔案,設定主從,保證叢集中所有的機器的網路彼此能相互連線

[root@localhost ~]# yum install -y homanamectl
# 設定節點名稱,主節點設定成m,從1設定為w1,從2設定為w2
[root@localhost ~]# hostnamectl set-hostname m
[root@localhost ~]# vi /etc/hosts
# 在這裡我將153設定成主節點,將154設定成work1節點,將155設定成work2節點
192.168.189.153 m
192.168.189.154 w1
192.168.189.155 w2
# 測試,ping 節點名稱,如果能名稱,則說明已經設定成功了
[root@localhost ~]# ping w2
PING w2 (192.168.189.155) 56(84) bytes of data.
64 bytes from w2 (192.168.189.155): icmp_seq=1 ttl=64 time=0.689 ms
64 bytes from w2 (192.168.189.155): icmp_seq=2 ttl=64 time=0.559 ms
64 bytes from w2 (192.168.189.155): icmp_seq=3 ttl=64 time=0.686 ms

4.系統基礎前提配置

# 關閉防火牆
[root@localhost ~]# systemctl stop firewalld && systemctl disable firewalld
# 關閉selinux
[root@localhost ~]# setenforce 0
[root@localhost ~]# sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
# 關閉swap,在linux下swap的作用類似於windows下的虛擬記憶體,關閉是為了更好的使用記憶體
[root@localhost ~]# swapoff -a
[root@localhost ~]# sed -i '/swap/s/^\(.*\)$/#\1/g' /etc/fstab
# 配置iptables的ACCEPT規則
[root@localhost ~]# iptables -F && iptables -X && iptables -F -t nat && iptables -X -t nat && iptables -P FORWARD ACCEPT
# 設定系統引數
[root@localhost ~]# cat <<EOF >  /etc/sysctl.d/k8s.conf
                        net.bridge.bridge-nf-call-ip6tables = 1
                        net.bridge.bridge-nf-call-iptables = 1
                        EOF

5.安裝kubeadm,kubelet和kubectl

# 配置yum源
[root@localhost ~]# cat <<EOF > /etc/yum.repos.d/kubernetes.repo
			[kubernetes]
			name=Kubernetes
			baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
			enabled=1
			gpgcheck=0
			repo_gpgcheck=0
			gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
       		http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
			EOF
#檢視kubeadm的版本
[root@localhost ~]# yum list kubeadm --showduplicates | sort -r
# 安裝kubeadm,kubelet和kubectl,為了防止有坑,建議大家按照這樣的順便安裝
[root@localhost ~]# yum install -y kubelet-1.14.0-0
[root@localhost ~]# yum install -y kubeadm-1.14.0-0 kubelet-1.14.0-0 kubectl-1.14.0-0
# 如果直接安裝k8s可能會報錯,因為docker中的cgroup和k8s的cgroup可能不一致,所以將docker和k8s設定同一個cgroup
[root@localhost ~]# vi /etc/docker/daemon.json 
# 複製進去即可
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "registry-mirrors": ["https://fz0dvm3j.mirror.aliyuncs.com"]
}
# 重啟docker
[root@localhost ~]# systemctl restart docker
[root@localhost ~]# sed -i "s/cgroup-driver=systemd/cgroup-driver=cgroupfs/g" /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
# 下面這個報錯,才沒有問題。這句話的涵義是檢測k8s的cgroup是否是system,如果是就修改,沒有就報錯
sed:無法讀取 /etc/systemd/system/kubelet.service.d/10-kubeadm.conf:沒有那個檔案或目錄
# 開啟k8s
[root@localhost ~]# systemctl enable kubelet && systemctl start kubelet

6.使用docker拉去k8s需要的映象

# 檢視docker hub上kubeadm所需要的映象
[root@localhost ~]# kubeadm config images list 
# 解決國內訪問慢的問題,使用阿里雲的映象並拉取映象
#!/bin/bash

set -e

KUBE_VERSION=v1.14.0
KUBE_PAUSE_VERSION=3.1
ETCD_VERSION=3.3.10
CORE_DNS_VERSION=1.3.1

GCR_URL=k8s.gcr.io
ALIYUN_URL=registry.cn-hangzhou.aliyuncs.com/google_containers

images=(kube-proxy:${KUBE_VERSION}
kube-scheduler:${KUBE_VERSION}
kube-controller-manager:${KUBE_VERSION}
kube-apiserver:${KUBE_VERSION}
pause:${KUBE_PAUSE_VERSION}
etcd:${ETCD_VERSION}
coredns:${CORE_DNS_VERSION})

for imageName in ${images[@]} ; do
  docker pull $ALIYUN_URL/$imageName
  docker tag  $ALIYUN_URL/$imageName $GCR_URL/$imageName
  docker rmi $ALIYUN_URL/$imageName
done	

#拉取完成後,使用docker images檢視映象是否拉去成功

7.初始化

kubeadm init流程:
1.進行一系列的檢查,確保這臺機器能夠部署k8s
2.生成k8s對外提供服務所需要的各種證書可對應的目錄
目錄在:/etc/kubernetes/pki
3.為其他元件生成kube-ApiServer所需要的配置檔案
4.為Master元件生成Pod配置檔案
5.生成etcd這樣的yaml檔案
6.master容器啟動之後,kubeadm會通過檢查localhost:6443/healthz這個master元件的健康檢查URL,等待master元件完全執行起來
7.為叢集生成一個bootstrap token
8.將master節點的重要資訊,通過ConfigMap得方式保留在etcd中,供後續部署node節點使用
9.安裝預設外掛,kubernetes預設kube-haproxy和DNS兩個預設安裝(必須安裝)

# 初始化master節點,然後讓work節點加入192.168.189.153這個要換成自己的ip,如果init出了問題,在下一次init之前,需要先kubeadm reset
kubeadm init --kubernetes-version=1.14.0 --apiserver-advertise-address=192.168.189.153 --pod-network-cidr=10.10.0.0/16 --ignore-preflight-errors=Swap
#注意最後有這樣的一句話,最好能儲存一下,後面有需要用得到
Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.189.153:6443 --token kfm4nl.6ptjcww2fl1rs7si \
    --discovery-token-ca-cert-hash sha256:9c134702ad78527df11429aa501d3db289b6c9272778429f10ed88ace7846830 

# 檢視是否kube是否建立成功,如下所示,有.kube表示已經建立成功
[root@m ~]# ls -a
.  ..  anaconda-ks.cfg  .bash_history  .bash_logout  .bash_profile  .bashrc  .cshrc  .kube  .pki  .tcshrc
# 建立檔案並授權
[root@m ~]# mkdir -p $HOME/.kube
[root@m ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@m ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config

檢視系統裡中pod的情況,如下圖可以看到有兩個元件並沒有開啟,因為coredns解析需要網路外掛,這個網路外掛是為了在叢集中能夠通訊,在這裡筆者使用的是calico3.9。

[root@m ~]# kubectl get pods -n kube-system

# 下載檔案
[root@m ~]# yum install -y wget
[root@m ~]# wget https://docs.projectcalico.org/v3.9/manifests/calico.yaml
# 下載完成後,apply
[root@m ~]# kubectl apply -f calico.yaml 
# 檢視calico中需要使用的映象,然後使用docker pull 拉取這些映象
[root@m ~]# cat calico.yaml | grep image
          image: calico/cni:v3.9.6
          image: calico/cni:v3.9.6
          image: calico/pod2daemon-flexvol:v3.9.6
          image: calico/node:v3.9.6
          image: calico/kube-controllers:v3.9.6
# 拉去完成後,檢視狀態
[root@m ~]# kubectl get pods -n kube-system -w

還記得上面初始化成功後,有一句話最好儲存一下嗎?其實如果沒儲存,使用下面這行命令也可以檢視的,如果想讓其他工作節點加入,將下面的join命令複製到其他的虛擬機器上執行即可。

[root@m ~]# kubeadm token create --print-join-command
kubeadm join 192.168.189.153:6443 --token ej3ta0.6smn0227gqafj95d     --discovery-token-ca-cert-hash sha256:9c134702ad78527df11429aa501d3db289b6c9272778429f10ed88ace7846830 

執行完成後,在主節點上檢視k8s節點的資訊

[root@m ~]# kubectl get nodes
NAME   STATUS     ROLES    AGE   VERSION
m      Ready      master   91m   v1.14.0
w1     NotReady   <none>   40s   v1.14.0
w2     NotReady   <none>   30s   v1.14.0

到這裡,叢集就配置完成了。

3.pod

pod是k8s建立或部署的最小/最簡單的基本單元,一個pod代表叢集上正在執行的一個程序。一個pod封裝一個應用容器(也可以有多個容器),儲存資源,一個獨立的網路IP以及管理控制容器執行方式的策略選項,pod代表部署的一個單位:k8s中單個應用的例項,它可能有單個容器或多個容器共享組成的資源。

pod的兩種主要使用方式:

  • pod中執行一個容器,在這種情況下,可以將pod視為單個封裝的容器,到那時k8s是直接管理pod而不是容器
  • pods中執行多個需要一起工作的容器,pod可以封裝緊密耦合的應用,他們需要由多個容器組成,他們之間能夠共享資源,這些容器可以形成一個單一的內部service單位。

docker是k8s pod中最常用的容器執行時,但pod也能支援其他的容器執行時。

在這裡我們使用pod建立一個nginx:

# 建立一個pod_nginx_rs.yaml檔案
[root@m ~]# cat > pod_nginx_rs.yaml <<EOF
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx
  labels:
    tier: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      name: nginx
      labels:
        tier: frontend
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# 建立pod,建立的過程需要一點時間,需要拉取映象等
[root@m ~]# kubectl apply -f pod_nginx_rs.yaml 
# 檢視nginx的詳情資訊
[root@m ~]# kubectl get pods -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP               NODE   NOMINATED NODE   READINESS GATES
nginx-8kvlj   1/1     Running   0          10m   192.168.80.193   w2     <none>           <none>
nginx-jkvwh   1/1     Running   0          10m   192.168.190.66   w1     <none>           <none>
nginx-r9rj2   1/1     Running   0          10m   192.168.190.65   w1     <none>           <none>
# curl ip,訪問nginx
[root@m ~]# curl 192.168.80.193
# 檢視nginx是在哪臺伺服器上
[root@m ~]# kubectl describe pod nginx-jkvwh

# 在從機上檢視該機器建立了幾個pod
[root@w1 ~]# docker ps|grep nginx

pod終止流程:

  1. 使用者傳送一個命令來刪除pod,預設的優雅退出時間為30s

  2. API伺服器中的pod更新時間,超過該時間則認為pod死亡

  3. 在客戶端命令裡面,pod顯示為“Terminating(退出中)”的狀態

  4. 與第三步同時,當kubelet看到pod被標記為退出中的時候,開始關閉pod

    i.如果pod定義了一個停止前的鉤子,其會在pod內部被呼叫,如果鉤子在優雅退出時間段超時仍在執行,第二步會以一個很小的優雅時間段被呼叫

    ii.程序被髮送term的訊號

  5. 與第三步同時,pod從service的列表中被刪除,不在被認為是執行著的pod的一部分,緩慢關閉的pod可以繼續對外服務,但負載均衡器將他們輪流移除

  6. 當優雅退出時間超時了,任何pod中正在執行的程序都會被SIGKILL訊號殺死

  7. kubelet會完成pod的刪除,將優雅退出的時間設定為0(表示立即刪除)。pod從API中刪除,不在對客戶端可見

4.Replication Controller(RC)/Replica Set(RS)

Replication Controller 保證了在所有時間內,都有特定數量的Pod副本正在執行並保證其可用性,如果太多了,Replication Controller就殺死幾個,如果太少了,Replication Controller會新建幾個。Replication Controller 就像一個程序管理器,監管著不同node上的多個pod,而不是單單監控一個node上的pod,Replication Controller 會委派本地容器來啟動一些節點上服務(Kubelet ,Docker)。

由Replication Controller監控的Pod的數量是由一個叫 label selector(標籤選擇器)決定的,label selector在Replication Controller和被控制的pod建立了一個鬆散耦合的關係,與pod相比,pod與他們的定義檔案關係緊密。

# 建立replicaset_nginx.yaml檔案
[root@m ~]# cat > relicaset_nginx.yaml <<EOF
apiVersion: v1
# 表示新建物件的型別
kind: ReplicationController
metadata:
  name: nginx
spec:
  # 副本的數量,外部容器可以通過修改replicas的值來實現pod數量的變化 
  replicas: 3
  selector:
    app: nginx
    # 用於定義pod的模板,如pod的名稱等
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

刪除一個pod,會發現,馬上又建立了一個

[root@m ~]# kubectl delete pod nginx-927f9

擴縮容:

我上面是建立的3個pod,現在將起擴充到5個

[root@m ~]# kubectl scale rc nginx --replicas=5
# 可以看到有兩個正在建立
[root@m ~]# kubectl get pods
NAME          READY   STATUS              RESTARTS   AGE
nginx-927f9   1/1     Running             0          5m18s
nginx-9sb48   0/1     ContainerCreating   0          10s
nginx-fgpxk   1/1     Running             0          5m18s
nginx-sn4gx   1/1     Running             0          5m18s
nginx-x6jtx   0/1     ContainerCreating   0          10s

如果都不想要了,那麼必須是要刪除yaml檔案才行。

[root@m ~]# kubectl delete -f relicaset_nginx.yaml

ReplicaSet是下一代複本控制器。ReplicaSet和 Replication Controller之間的唯一區別是現在的選擇器支援。Replication Controller只支援基於等式的selector(env=dev或environment!=qa),但ReplicaSet還支援新的,基於集合的selector。

5.deployment

deployment為pod和ReplicaSet提供了一個宣告式更新定義的方法。只需要在Deployment中描述你想要的目標狀態是什麼,Deployment controller就會幫你將Pod和Replica Set的實際狀態改變到你的目標狀態。

典型的應用場景包括:

  • 定義Deployment來建立pod和ReplicaSet
  • 滾動升級和回滾應用(可以在任何時間點更新應用的時候保證某些例項依然可以正常執行來防止應用 down 掉,當新部署的 Pod 啟動並可以處理流量之後,才會去殺掉舊的 Pod。)
  • 擴縮容
  • 暫停和繼續Deployment

下面的滾動更新的例子:

# 建立deployment.yaml檔案
[root@m ~]# cat > deployment_nginx.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
EOF

# 建立pod
[root@m ~]# kubectl apply -f deployment_nginx.yaml 
# 檢視
[root@m ~]# kubectl get deployment
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   0/3     3            0           21s
[root@m ~]# kubectl get deployment -o wide
NAME               READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES        SELECTOR
nginx-deployment   1/3     3            1           81s   nginx        nginx:1.7.9   app=nginx
# 如上所示,現在的nginx的版本是1.7.9,現在將版本更新,nginx-deployment為yaml檔案中的metadata的name
[root@m ~]# kubectl set image deployment nginx-deployment nginx=1.9.1
deployment.extensions/nginx-deployment image updated
# 再次檢視,可以看到已經更新成了1.9.1了
[root@m ~]# kubectl get deployment -o wide
NAME               READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES   SELECTOR
nginx-deployment   1/3     1            1           5m    nginx        1.9.1    app=nginx

6.service

由於Pod和Service是k8s叢集範圍內的虛擬概念,所以叢集外的客戶端系統無法通過Pod的IP地址或者Service的虛擬IP地址和虛擬埠號訪問到它們。為了讓外部客戶端可以訪問這些服務,可以將Pod或Service的埠號對映到宿主機,以使得客戶端應用能夠通過物理機訪問容器應用。

Service 能夠支援 TCP 和 UDP 協議,預設 TCP 協議。

對一些應用(如 Frontend)的某些部分,可能希望通過外部(Kubernetes 叢集外部)IP 地址暴露 Service。

ServiceTypes 允許指定一個需要的型別的 Service,預設是 ClusterIP 型別。

Type 的取值以及行為如下:

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

Service還有一種資源叫HostPort:直接將容器的埠與所排程的節點上的埠路由,這樣使用者就可以直接通過宿主機的IP來訪問Pod了。

HostPort和NodePort區別:HostPort只會在一臺物理主機上開啟埠,而NodePort在所有主機上都會開啟

k8s預設的對映埠是30000-32767,如果需要對映其他埠,需修改kube-apiserver.yaml

vi /etc/kubernetes/manifests/kube-apiserver.yaml
# 找到這一行--service-cluster-ip-range ,在其下新增一個如下內容
- --service-node-port-range=1-65535
# 重啟k8s
systemctl daemon-reload && systemctl restart kubelet

7. NodePort

如果設定 type 的值為 "NodePort",k8smaster 將從給定的配置範圍內(預設:30000-32767)分配埠,每個 Node 將從該埠(每個 Node 上的同一埠)代理到 Service。該埠將通過 Service 的 spec.ports[*].nodePort 欄位被指定。

如果需要指定的埠號,可以配置 nodePort 的值,系統將分配這個埠,否則呼叫 API 將會失敗(比如,需要關心埠衝突的可能性)。

# 建立一個 Deployment
[root@m ~]# cat > whoami-deployment.yaml  <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami-deployment
  labels:
    app: whoami
spec:
  replicas: 3
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
      - name: whoami
        image: jwilder/whoami
        ports:
        - containerPort: 8000
EOF

[root@m ~]# kubectl apply -f whoami-deployment.yaml 
# 建立成功後,檢視當前service的狀況
[root@m ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2d1h
# 暴露一個service
[root@m ~]# kubectl expose deployment whoami-deployment
service/whoami-deployment exposed
# 再次檢視,新增了一個service
[root@m ~]# kubectl get svc
NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes          ClusterIP   10.96.0.1       <none>        443/TCP    2d1h
whoami-deployment   ClusterIP   10.99.211.118   <none>        8000/TCP   2s
# 測試    在任意一個節點上使用 10.99.211.118:8000訪問,發現是能訪問通的
[root@w2 ~]# curl 10.99.211.118:8000
I'm whoami-deployment-678b64444d-s2m2m

上面的這種方式只能在容器內部訪問,要想在容器外部訪問,要做如下的配置:

# 刪除上面建立的yaml檔案
[root@m ~]# kubectl delete -f whoami-deployment.yaml 
# 刪除service
[root@m ~]# kubectl delete svc whoami-deployment
# 建立一個 Deployment
[root@m ~]# cat > whoami-deployment.yaml  <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami-deployment
  labels:
    app: whoami
spec:
  replicas: 3
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
      - name: whoami
        image: jwilder/whoami
        ports:
        - containerPort: 8000
EOF

[root@m ~]# kubectl apply -f whoami-deployment.yaml 
deployment.apps/whoami-deployment created
# 建立成功後,檢視當前service的狀況
[root@m ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2d1h
# 暴露一個service
[root@m ~]# kubectl expose deployment whoami-deployment --type=NodePort
service/whoami-deployment exposed
# 再次檢視,新增了一個service
[root@m ~]# kubectl get svc
NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes          ClusterIP   10.96.0.1       <none>        443/TCP    2d1h
whoami-deployment   NodePort    10.99.86.219   <none>        8000:31669/TCP   26s

在虛擬機器內部使用8000埠,可以訪問:

[root@m ~]# curl 10.99.86.219:8000
I'm whoami-deployment-678b64444d-2nspn

[root@m ~]# lsof -i tcp:31669
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
kube-prox 2292 root   10u  IPv6  79783      0t0  TCP *:31669 (LISTEN)

在瀏覽器上使用工作節點的ip+暴露的埠也訪問:

8.Ingress

如果想讓外界訪問,我們可以使用service.type=NodePort這樣的方式,但是生產環境不推薦使用這種方式。NodePort 的缺點是一個埠只能掛載一個 Service。所以生產環境一般使用Nginx Ingress Controller執行在一個合適的節點上,並使用hostport暴露出一個埠。

ingress就可以配置提供外部可訪問的URL,負載均衡,SSL終結,基於名稱的虛擬主機等。使用者通過POST Ingress資源到API server的方式來請求ingress。Ingress 控制器通常負責通過負載均衡來實現Ingress ,儘管它可以配置邊緣路由器火其他前端來幫助處理流量。Ingress 不會公開任意埠或協議,將HTTP和HTTPS以外的服務公開到Internet時,通常使用Service.Type=NodePort或Service.Type=LoadBalancer型別的服務。

# 部署Ingress Controller  
[root@m ~]#  vi mandatory.yaml
# 這個檔案是從官網上下載的
# 也可以使用命令下載  wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---

kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-configuration
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
kind: ConfigMap
apiVersion: v1
metadata:
  name: tcp-services
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
kind: ConfigMap
apiVersion: v1
metadata:
  name: udp-services
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-ingress-serviceaccount
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: nginx-ingress-clusterrole
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - endpoints
      - nodes
      - pods
      - secrets
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
  - apiGroups:
      - "extensions"
      - "networking.k8s.io"
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - "extensions"
      - "networking.k8s.io"
    resources:
      - ingresses/status
    verbs:
      - update

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: nginx-ingress-role
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - pods
      - secrets
      - namespaces
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - configmaps
    resourceNames:
      # Defaults to "<election-id>-<ingress-class>"
      # Here: "<ingress-controller-leader>-<nginx>"
      # This has to be adapted if you change either parameter
      # when launching the nginx-ingress-controller.
      - "ingress-controller-leader-nginx"
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - get

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: nginx-ingress-role-nisa-binding
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: nginx-ingress-role
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: nginx-ingress-clusterrole-nisa-binding
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: nginx-ingress-clusterrole
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      # wait up to five minutes for the drain of connections
      terminationGracePeriodSeconds: 300
      serviceAccountName: nginx-ingress-serviceaccount
      # 為true表示Pod中執行的應用程式可以直接使用node節點的埠,這樣node節點主機所在網路的其他主機,都可以通過該埠訪問到此應用程式。
      hostNetwork: true
      nodeSelector:
        name: ingress
        kubernetes.io/os: linux
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
          securityContext:
            allowPrivilegeEscalation: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            # www-data -> 33
            runAsUser: 33
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
            - name: https
              containerPort: 443
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown

---

[root@m ~]# kubectl apply -f mandatory.yaml 
# 建立tomcat-service檔案
[root@m ~]# vi tomcat-service.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deployment
  labels:
    app: tomcat
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tomcat
  template:
    metadata:
      labels:
        app: tomcat
    spec:
      containers:
      - name: tomcat
        image: tomcat
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  ports:
  - port: 80   
    protocol: TCP
    # 因為service的埠與tomcat的埠不一致,所以需要指定tomcat的埠
    targetPort: 8080
  selector:
    app: tomcat

[root@m ~]# kubectl apply -f tomcat-service.yaml 
deployment.apps/tomcat-deployment created
service/tomcat-service created
# 檢視服務
[root@m ~]# kubectl get svc
NAME                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes          ClusterIP   10.96.0.1        <none>        443/TCP          4d1h
tomcat-service      ClusterIP   10.104.189.196   <none>        80/TCP           2m21s
# 檢視tomcat的pod是執行在 w1 上的
[root@m ~]# kubectl get pods -o wide
NAME                                 READY   STATUS             RESTARTS   AGE     IP               NODE   NOMINATED NODE   READINESS GATES
tomcat-deployment-6b9d6f8547-t67nw   1/1     Running            0          6m29s   192.168.190.94   w1     <none>           <none>
# service和pod都準備好後,在容器內部訪問,這個時候訪問,肯定會是一個404的html,因為docker拉取tomcat映象,拉取得是最簡單的tomcat映象
[root@m ~]# curl 192.168.190.94:8080
# 進入tomcat容器
[root@m ~]# kubectl exec -it tomcat-deployment-6b9d6f8547-t67nw /bin/bash
# 將webapps.dist 檔案的內容拷貝進webapp檔案內
root@tomcat-deployment-6b9d6f8547-t67nw:/usr/local/tomcat# cp -r webapps.dist/* webapps
# 檢視內容是否已經複製進去了
root@tomcat-deployment-6b9d6f8547-t67nw:/usr/local/tomcat# cd webapps
root@tomcat-deployment-6b9d6f8547-t67nw:/usr/local/tomcat/webapps# ls
ROOT  docs  examples  host-manager  manager
# 退出再次訪問,就會發現可以訪問
root@tomcat-deployment-6b9d6f8547-t67nw:/usr/local/tomcat/webapps# exit

因為tomcat-service是執行在w1上的,所以希望將nginx也執行在w1上面,原來這個檔案是隨機部署在任意節點上,現在使用標籤選擇器將檔案指定部署在w1上。

[root@m ~]# kubectl label node w1 name=ingress
node/w1 labeled

檢視節點資訊會發現給w1打上了ingress的標籤

[root@m ~]# kubectl get node --show-labels

上面配置的mandatory.yaml中配置nodeSeletor,這樣就能保證Ingress Controller執行在w1節點上。

# 檢視ingress-nginx
[root@m ~]# kubectl get pods -n ingress-nginx -o wide
NAME                                        READY   STATUS              RESTARTS   AGE    IP                NODE   NOMINATED NODE   READINESS GATES
nginx-ingress-controller-7c66dcdd6c-8dcvk   0/1     ContainerCreating   0          104s   192.168.189.154   w1     <none>           <none>

# 如果處理問題可以檢視詳情
[root@m ~]# kubectl describe pod nginx-ingress-controller-7c66dcdd6c-5cmhp -n ingress-nginx
# 出了問題,可以刪掉檔案重新拉取
[root@m ~]# kubectl delete -f mandatory.yaml 
[root@m ~]# kubectl apply -f mandatory.yaml 

當上面這些都完成後,在w1節點上,會開放出HTTP對應的80埠和HTTPS對應的443埠。需要配置ingress的規則:

[root@m ~]# vi ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-ingress
spec:
  rules:
  - host: tomcat.charon.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 80
[root@m ~]# kubectl apply -f ingress.yaml
ingress.extensions/nginx-ingress created
# 檢視
[root@m ~]# kubectl get ingress
NAME            HOSTS               ADDRESS   PORTS   AGE
nginx-ingress   tomcat.charon.com             80      5s

我在ingress.yaml檔案中配置了域名是tomcat.charon.com,所以需要在windows裡面配置域名。

目錄:C:\Windows\System32\drivers\etc 的hosts檔案

新增內容,因為我們要去w1上訪問,所以配置的ip應該是w1的ip:192.168.189.154 tomcat.charon.com

然後再瀏覽器上輸入域名即可訪問

9.Volume

預設情況下容器中的磁碟檔案是非持久化的,對於執行在容器中的應用來說面臨兩個問題,第一:當容器掛掉kubelet將重啟啟動它時,檔案將會丟失;第二:當Pod中同時執行多個容器,容器之間需要共享檔案時。k8s的Volume解決了這兩個問題。

對於上面的示例,我們修改了tomcat的webapps的檔案,如果重啟pod之後,會發現訪問依然會報404的錯誤。

# 安裝nfs伺服器
# w1節點上安裝,因為tomcat就是安裝在w1的
[root@m ~]# yum -y install nfs-utils rpcbind
# 其他節點安裝
[root@w1 ~]# yum -y install nfs-utils 
[root@w2 ~]# yum -y install nfs-utils 
# w1節點上建立檔案
[root@w1 app]# cd /
[root@w1 /]# mkdir -p /app/tomcat_data
#  w1節點上授權
[root@m ~]# chmod -R 777 /app/tomcat_data
# w1節點上修改檔案
[root@w1 ~]# vi /etc/exports
/app/tomcat_data 192.168.189.154(rw,no_root_squash,subtree_check,fsid=0)
#  w1節點上重新掛載
[root@m tomcat_data]# exportfs -r
# w1節點上啟動
[root@w1 ~]# systemctl start rpcbind && systemctl enable rpcbind
[root@w1 ~]# systemctl start nfs && systemctl enable nfs
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
# 其他節點上啟動
[root@w1 ~]# systemctl start nfs
# w1節點上檢視
[root@m tomcat_data]# showmount -e 
Export list for w1:
/app/tomcat_data 192.168.189.154
# 這一步好像不是必須的,我沒有加這一步也可以
[root@w1 ~]#  mount -t nfs 192.168.189.154:/app/tomcat_data /app/tomcat_data

安裝和配置完nfs伺服器後(一定要注意,pod在那個節點伺服器上,對應的檔案應該也在那個伺服器上),在主節點做如下配置:

# 建立 persistent-volume.yaml
[root@m ~]# vi persistent-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: tomcat-data
spec:
  capacity:
    storage: 2Gi 
  accessModes:
  - ReadWriteMany 
  nfs: 
    path: /app/tomcat_data
    server: 192.168.189.154
  persistentVolumeReclaimPolicy: Recycle 

---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: tomcat-data
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 2Gi
      
[root@m ~]# kubectl apply -f persistent-volume.yaml 
persistentvolume/tomcat-data created
persistentvolumeclaim/tomcat-data created
# 修改tomcat-service.yaml(位置如下圖)
[root@m ~]# vi tomcat-service.yaml 
	    volumeMounts: 
        - mountPath: /usr/local/tomcat/webapps
          name: tomcat-data
     volumes:
     - name: tomcat-data
       persistentVolumeClaim:
        claimName: tomcat-data
 # 重啟pod
 [root@m ~]# kubectl replace --force -f tomcat-service.yaml 
deployment.apps "tomcat-deployment" deleted
service "tomcat-service" deleted
deployment.apps/tomcat-deployment replaced
service/tomcat-service replaced

再次在瀏覽器上訪問,出現tomcat的首頁:

在w1節點上訪問掛載的目錄可以發現,tomcat的webapps目錄下的檔案全部都在裡面:

[root@w1 ~]# cd /app/tomcat_data/
[root@w1 tomcat_data]# ls
docs  examples  host-manager  manager  ROOT

到這裡persistentVolume就配置好了。

參考文件:

http://docs.kubernetes.org.cn/227.html#Kubernetes

https://www.kubernetes.org.cn/k8s

https://blog.csdn.net/ljx1528/article/details/103399826