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


免責聲明!

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



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