1. 程式人生 > 實用技巧 >docker概述

docker概述

Kubernetes

第一章:網際網路架構的演變
隨著1946年世界上第一臺電子計算機的問世網路就隨之出現了,只不過當初只是為了解決多個終端之間的連線,這就是區域網的雛形。後來,隨著美國國防部高階研究計劃局(ARPA)的無線、衛星網的分組交換技術的研究問世,一不小心搞出了TCP/IP,由此出現了我們所熟知的Internet網際網路。
早期的電腦體積大,價格昂貴,一般只用在科學研究領域,後來隨著積體電路,半導體的發展,電腦逐漸出現在我們的日常生活中。由此,我們對網際網路的依賴越來越多,到現在已經成為了我們生活中的一部分,可以說網際網路極大地改變了我們的生活方式。
在網際網路剛剛進入我們生活之中的時候,架構還比較簡單,一般的只有一個WEB伺服器,提供幾個簡單的頁面。由此我們的第一代網際網路架構就產生了。這種結構有一個致命的缺陷,就是一旦web伺服器掛掉,服務就會終止。於是產生了主備和負載均衡,這種架構是第一代架構的補充,如圖:

這種架構簡單,但是可拓展性差,耦合性高。
隨著我們業務的不斷髮展,使用者量的不斷增加,第一代架構顯然無法支援我們業務的需求,於是逐漸過渡到了我們第二代網際網路架構,第二代網際網路架構的特點是分層,所以我們也稱之為分層架構。

這種架構的優點是可以橫向擴充套件,但是耦合性相對也很高。一個IP操作讀寫。
直至現在,業務越來越複雜,使用者量越來越多,那麼找到一個高效能,解耦合的架構就尤為迫不及待,所以隨之而出的先是分散式架構,緊接著出現的就是微服務架構。
分散式架構
分散式系統是由一組通過網路進行通訊、為了完成共同的任務而協調工作的計算機節點組成的系統。分散式系統的出現是為了用廉價的、普通的機器完成單個計算機無法完成的計算、儲存任務。其目的是利用更多的機器,處理更多的資料。

微服務架構


分散式,微服務的區別?

分散式:將一個大的系統劃分為多個業務模組,業務模組分別部署到不同的機器上,各個業務模組之間通過介面進行資料互動。區別分散式的方式是根據不同機器不同業務。

微服務:微服務的設計是為了不因為某個模組的升級和BUG影響現有的系統業務。微服務與分散式的細微差別是,微服務的應用不一定是分散在多個伺服器上,他也可以是同一個伺服器。

第二章:Docker
Docker簡介
Docker是一個用於開發,交付和執行應用程式的開放平臺。Docker使應用程式與基礎架構分開,從而可以快速交付軟體。藉助Docker,可以和管理應用程式相同的方式來管理基礎架構。通過利用Docker的方法來快速交付,測試和部署程式碼,大大減少編寫程式碼和在生產環境中執行程式碼之間的延遲。

Docker提供了在簡單、隔離的環境(稱為容器)中打包和執行應用程式的功能。隔離和安全性使您可以在給定主機上同時執行多個容器。容器是輕量級的,因為它們不需要虛擬機器管理程式的額外負載,而是直接在主機的核心中執行。這意味著與使用虛擬機器相比,可以在給定的硬體組合上執行更多的容器。您甚至可以在虛擬機器的主機中執行Docker容器!

Docker提供了工具和平臺來管理容器的生命週期:

使用容器開發應用程式及其支援元件。
容器成為分發和測試應用程式的單元。
準備就緒後,可以將應用程式作為容器或協調服務部署到生產環境中。無您的生產環境是本地資料中心,雲提供商還是兩者的混合,其工作原理都相同。

Docker架構

Docker引擎是具有以下主要元件的客戶端-伺服器應用程式:

伺服器是一種長期執行的程式,稱為守護程式程序(dockerd命令)。
REST API,它指定程式可以用來與守護程式進行通訊並指示其操作的介面。
命令列介面(CLI)客戶端(docker命令)。

Docker使用客戶端-伺服器架構。Docker 客戶端與Docker 守護程序進行對話,該守護程序完成了構建,執行和分發Docker容器的繁重工作。Docker客戶端和守護程式可以在同一系統上執行,也可以將Docker客戶端連線到遠端Docker守護程式。Docker客戶端和守護程式在UNIX套接字或網路介面上使用REST API進行通訊。

Image和container的區別?
Image是映象(類)
Container是容器(物件)
首先建立image映象,然後通過image映象例項化成我們所需的容器(container)。

安裝
Docker基本可以執行在任何Linux核心的主機之上。
CentOS

在阿里雲上下載docker-ce.repo
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
安裝必要的包
yum install -y yum-utils device-mapper-persistent-data lvm2
重新整理
yum makecache fast
安裝
yum -y install docker-ce
啟動
systemctl start docker && systemctl enable docker
驗證
[root@localhost ~]# docker info
Client:
 Debug Mode: false

Server:
 Containers: 15
  Running: 5
  Paused: 0
  Stopped: 10
 Images: 26
 Server Version: 18.09.9
 Storage Driver: overlay2
  Backing Filesystem: xfs
  Supports d_type: true
  Native Overlay Diff: true
 Logging Driver: json-file
 Cgroup Driver: systemd
 Plugins:
  Volume: local
  Network: bridge host macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
 runc version: 425e105d5a03fabd737a126ad93d62a9eeede87f
 init version: fec3683
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 4.18.0-80.11.2.el8_0.x86_64
 Operating System: CentOS Linux 8 (Core)
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 1.92GiB
 Name: localhost.localdomain
 ID: FLZM:42YD:NC2K:NATO:YPCA:T6IY:G647:OKPA:WJKK:Z4TQ:AN7R:TMSB
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Registry Mirrors:
  https://hjvrgh7a.mirror.aliyuncs.com/
 Live Restore Enabled: false
 Product License: Community Engine
Ubuntu
安裝必要的一些系統工具
apt-get -y install apt-transport-https ca-certificates curl software-properties-common
安裝GPG證書
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
寫入軟體源資訊
sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
更新並安裝Docker-CE
sudo apt-get -y update
sudo apt-get -y install docker-ce
新增配置
cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "registry-mirrors": ["https://hjvrgh7a.mirror.aliyuncs.com"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

要新增我們harbor倉庫需要在新增下面一行
"insecure-registries": ["10.0.0.201"],
基本使用
Docker容器中至少有一個應用程式一直執行在前臺。
建立容器
docker run -it centos /bin/sh


docker run:有兩個過程:
檢測當前使用的映象本地是否有,沒有則去遠端倉庫拉取。
將映象例項化成容器。

在docker容器中,至少要有一個應用程式執行在前臺,否則在啟動的一瞬間生命週期就結束了。

為什麼加上-it引數之後,就可以了呢?因為偽終端是執行在前臺的。
進入容器
Acctch
docker attach nginx 
使用該命令有一個問題。當多個視窗同時使用該命令進入該容器時,所有的視窗都會同步顯示。如果有一個視窗阻塞了,那麼其他視窗也無法再進行操作,當所有視窗退出時,容器結束。
Exec
docker exec -it nginx /bin/bash
這個命令相當於在容器中執行一個命令。
Nsenter
需要配合docker inspect來使用(企業當中最長用的方式之一)
nsenter --target $( docker inspect -f {{.State.Pid}} caf8813adda2 ) --mount --uts --ipc --net --pid
Docker是用golang語言開發,所以它也支援go語言的摸版語法。
SSH
在生產環境中排除了使用docker attach命令進入容器之後,相信大家第一個想到的就是ssh。在映象(或容器)中安裝SSH Server,這樣就能保證多人進入容器且相互之間不受干擾了,相信大家在當前的生產環境中(沒有使用Docker的情況)也是這樣做的。但是使用了Docker容器之後不建議使用ssh進入到Docker容器內。

總結:進入docker container中一般情況下有4種方式,最常用的是exec和nsenter這兩種。

Nsenter和exec之間的區別?
Exec是docker自帶的命令,Nsenter是Linux提供的命令。
Exec相當於在容器內執行一個命令,而Nsenter是僅僅進入容器之中而已。

埠對映
埠對映一般使用(-p/-P)
-p 指定埠對映
docker run -d -p 8800:80 nginx

-P 隨機埠對映
docker run -d -P nginx

目錄及檔案對映
docker run -v /root/html:/usr/share/nginx/html nginx
docker inspect jolly_payne
"Mounts": [
    {
        "Type": "bind",
        "Source": "/root/html",
        "Destination": "/usr/share/nginx/html",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

COPY檔案
Copy到容器外
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
Copy到容器內
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
4.5匯入和匯出
匯出
如果要匯出本地某個容器,可以使用 docker export 命令。
docker ps
docker export 3bb25e3350f9  > centos.tar


匯入
可以使用 docker import 從容器快照檔案中再匯入為映象
[root@localhost ~]# cat centos.tar | docker import - test/centos:v1.0
sha256:e1e54eebb51a5ddd8f3d4bc1ed9cb8a4ce2841a7e265bf

網路
主要解決容器與容器之間,容器與主機之間的網路互通問題。	
--link
--link 引數的格式為 --link name:alias,其中 name 是要連結的容器的名稱,alias 是這個連線的別名。
docker -d --link 容器名:連線別名 nginx
案例:
# 第一步:建立一個容器
docker run -d --name nginx nginx

# 第二步:啟動一個測試容器
docker run -it --link nginx:nginx centos

# 第三步:在測試容器中測試訪問目標容器,看是否可以正常訪問


Network
Command
Description

docker network connect
將容器連線到網路

docker network create
建立一個網路

docker network disconnect
斷開容器與網路的連線

docker network inspect
在一個或多個網路上顯示詳細資訊

docker network ls
列出網路

docker network prune
刪除所有未使用的網路

docker network rm
刪除一個或多個網路

使用 docker network create 命令建立使用者定義的網橋網路。
docker network create chenyang
使用network
docker run --name my-nginx \
  --network chenyang \
  -p 8080:80 \
  nginx:latest
若要將正在執行的容器連線到現有使用者定義的橋接網路,請使用docker network connect命令。
docker network connect chenyang my-nginx
案例:
# 第一步:建立一個名稱為chenyang的network
Docker network create chenyang

# 第二步:建立一個目標容器
Docker run -d --network chenyang --name network_nginx nginx

# 第三步:建立一個測試容器
Docker run -it --network chenyang --name network_centos centos

# 第四步:在測試容器之中測試網路連線情況

儲存卷
一般情況下,我們可以這樣理解:儲存卷就是通過檔案系統將容器之中的資料持久化儲存。
案例
# 第一步:準備好本地的目錄
Mkdir nginx

# 第二步:建立容器關聯上我們目錄
docker run -d --name v_nginx -v /root/nginx/:/usr/share/nginx/html/ -p 8081:80 nginx

# 第三步:測試檔案是否可以互通
[root@k8s-node nginx]# curl 127.0.0.1:8081
Index

Dockerfile
Dockerfile 由一行行命令語句組成,並且支援以 # 開頭的註釋行。

一般的,Dockerfile 分為四部分:基礎映象資訊、維護者資訊、映象操作指令和容器啟動時執行指令。

例如:
FROM ubuntu

MAINTAINER Alvin [email protected]

# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# Commands when creating a new container
CMD /usr/sbin/nginx

Dockerfile示意圖:

1. FROM
指明構建的新映象是來自於哪個基礎映象,例如:

FROM centos
2. MAINTAINER
指明映象維護著及其聯絡方式(一般是郵箱地址),例如:
MAINTAINER Edison Zhou <[email protected]>
RUN
構建映象時執行的Shell命令,例如:
RUN ["yum", "install", "httpd"]
RUN yum install httpd
CMD
啟動容器時執行的Shell命令,例如:
CMD ["-C", "/start.sh"] 
CMD ["/usr/sbin/sshd", "-D"] 
CMD /usr/sbin/sshd -D
ENTRYPOINT
啟動容器時執行的Shell命令,同CMD類似,只是由ENTRYPOINT啟動的程式不會被docker run命令列指定的引數所覆蓋,而且,這些命令列引數會被當作引數傳遞給ENTRYPOINT指定指定的程式,例如:
ENTRYPOINT ["/bin/bash", "-C", "/start.sh"]
ENTRYPOINT /bin/bash -C '/start.sh'
EXPOSE
宣告容器執行的服務埠,例如:
EXPOSE 80 443
ENV
設定環境內環境變數,例如:
ENV MYSQL_ROOT_PASSWORD 123456
ENV JAVA_HOME /usr/local/jdk1.8.0_45
ADD
拷貝檔案或目錄到映象中,例如:
ADD <src>...<dest>
ADD html.tar.gz /var/www/html
ADD https://xxx.com/html.tar.gz /var/www/html
COPY
拷貝檔案或目錄到映象中,用法同ADD,只是不支援自動下載和解壓,例如:
COPY ./start.sh /start.sh

10. VOLUME    
指定容器掛載點到宿主機自動生成的目錄或其他容器,例如:
VOLUME ["/var/lib/mysql"]
USER
為RUN、CMD和ENTRYPOINT執行Shell命令指定執行使用者,例如:
USER <user>[:<usergroup>]
USER <UID>[:<UID>]
USER edisonzhou
WORKDIR
為RUN、CMD、ENTRYPOINT以及COPY和AND設定工作目錄,例如:
WORKDIR /data
HEALTHCHECK
告訴Docker如何測試容器以檢查它是否仍在工作,即健康檢查,例如:
HEALTHCHECK --interval=5m --timeout=3s --retries=3 \
    CMD curl -f http:/localhost/ || exit 1
其中,一些選項的說明:
 --interval=DURATION (default: 30s):每隔多長時間探測一次,預設30
-- timeout= DURATION (default: 30s):服務響應超時時長,預設30
--start-period= DURATION (default: 0s):服務啟動多久後開始探測,預設0秒
--retries=N (default: 3):認為檢測失敗幾次為宕機,預設3次

一些返回值的說明:
0:容器成功是健康的,隨時可以使用
1:不健康的容器無法正常工作
2:保留不使用此退出程式碼
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
  CMD curl -fs http://localhost/ || exit 1
這裡我們設定了每 5 秒檢查一次(這裡為了試驗所以間隔非常短,實際應該相對較長),如果健康檢查命令超過 3 秒沒響應就視為失敗,並且使用 curl -fs http://localhost/ || exit 1 作為健康檢查命令。
使用 docker build 來構建這個映象:
docker build -t myweb:v1 .
構建好了後,我們啟動一個容器:
docker run -d --name web -p 80:80 myweb:v1
 當執行該映象後,可以通過 docker container ls 看到最初的狀態為 (health: starting):

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                            PORTS               NAMES
03e28eb00bd0        myweb:v1            "nginx -g 'daemon off"   3 seconds ago       Up 2 seconds (health: starting)   80/tcp, 443/tcp     web
在等待幾秒鐘後,再次 docker container ls,就會看到健康狀態變化為了 (healthy):
docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS               NAMES
03e28eb00bd0        myweb:v1            "nginx -g 'daemon off"   18 seconds ago      Up 16 seconds (healthy)   80/tcp, 443/tcp     web
如果健康檢查連續失敗超過了重試次數,狀態就會變為 (unhealthy)。
為了幫助排障,健康檢查命令的輸出(包括 stdout 以及 stderr)都會被儲存於健康狀態裡,可以用 docker inspect 來檢視。
docker inspect --format '{{json .State.Health}}' web | python -m json.tool
{
    "FailingStreak": 0,
    "Log": [
        {
            "End": "2016-11-25T14:35:37.940957051Z",
            "ExitCode": 0,
            "Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n    body {\n        width: 35em;\n        margin: 0 auto;\n        font-family: Tahoma, Verdana, Arial, sans-serif;\n    }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
            "Start": "2016-11-25T14:35:37.780192565Z"
        }
    ],
    "Status": "healthy"
}
ARG
在構建映象時,指定一些引數,例如:
FROM centos:6
ARG user # ARG user=root
USER $user
這時,我們在docker build時可以帶上自定義引數user了,如下所示:
docker build --build-arg user=edisonzhou Dockerfile .
總結:
Docker build -f引數的作用:指定我們Dockerfile的地址,預設是當前目錄下的Dockerfile。

備註:docker build命令後面的點是必須的。
案例
FROM centos7

MAINTAINER Alvin [email protected]

RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

RUN yum makecache

RUN yum update -y

RUN yum install python3 -y

RUN pip3 install django

COPY docker /root/docker

WORKDIR /root/docker

EXPOSE 8080

CMD ["python3", "manage.py", "runserver", "0.0.0.0:8080"]

結果:能夠自動釋出,自動部署。
Docker三劍客
docker-compose
主要是解決本地docker容器編排問題。一般是通過yaml配置檔案來使用它,這個yaml檔案裡能記錄多個容器啟動的配置資訊(映象、啟動命令、埠對映等),最後只需要執行docker-compose對應的命令就會像執行指令碼一樣地批量建立和銷燬容器。
Docker Swarm
解決多主機多個容器排程部署得問題。swarm是基於docker平臺實現的叢集技術,他可以通過幾條簡單的指令快速的建立一個docker叢集,接著在叢集的共享網路上部署應用,最終實現分散式的服務。swarm技術相當不成熟,很多配置功能都無法實現,只能說是個半成品,目前更多的是使用Kubernetes來管理叢集和排程容器。
Docker Machine
解決docker執行環境問題。docker技術是基於Linux核心的cgroup技術實現的,那麼問題來了,如果在非Linux平臺上使用docker技術需要依賴安裝Linux系統的虛擬機器。docker-machine就是docker公司官方提出的,用於在各種平臺上快速建立具有docker服務的虛擬機器的技術。
第三章:kubernetes基礎
Kubernetes 是一個可移植的、可擴充套件的開源平臺,用於管理容器化的工作負載和服務,可促進宣告式配置和自動化。Kubernetes 擁有一個龐大且快速增長的生態系統。Kubernetes 的服務、支援和工具廣泛可用。

簡介
作為Google的競爭技術優勢,Borg理所當然的被視為商業祕密隱藏起來,但當Tiwtter的工程師精心打造出屬於自己的Borg系統(Mesos)時, Google也審時度勢地推出了來源於自身技術理論的新的開源工具。

2014年6月,谷歌雲端計算專家埃裡克·布魯爾(Eric Brewer)在舊金山的釋出會為這款新的開源工具揭牌,它的名字Kubernetes,意為“舵手”或“飛行員”,這也恰好與它在容器叢集管理中的作用吻合,即作為裝載了集裝箱(Container)的眾多貨船的指揮者,負擔著全域性排程和執行監控的職責。

它是一個全新的基於容器技術的分散式架構領先方案。這個方案雖然還很新,但是它是谷歌十幾年依賴大規模應用容器技術的經驗積累和昇華的一個重要成果。實現資源管理的自動化,以及跨多個數據中心的資源利用率的最大化。

其次,如果我們的系統設計遵循了Kubernetes的設計思想,那麼傳統系統架構中那些和業務沒有多大關係的底層程式碼或功能模組,都可以立刻從我們的視線消失,我們不必再費心於負載均衡器和部署實施問題,不必再考慮引用或自己開發一個複雜的服務治理框架,不必再頭疼於服務監控和故障處理模組的開發。使用Kubernets提供的解決方案,我們僅節省了不少於30%的開發成本,同時可以將精力更加集中於業務本身,而且由於Kubernetes提供了強大的自動化機制,所以系統後期的運維難度和運維成本大幅度降低。

它是一個開發的開發平臺。沒有限定任何程式設計介面,所以不論是Java、Go、C++還是用Python編寫的服務,都可以毫無困難地對映為Kubernetes的Service,並通過標準的TCP通訊協議進行互動。此外,由於Kubernetes平臺對現有的程式語言、程式設計框架、中間價沒有任何侵入性,因此現有的系統很容器改造升級並遷移到Kubernetes平臺上。

所以說它是一個完備的分散式系統支撐平臺,Kubernetes具有完備的叢集管理能力,包括多層次的安全防護和准入機制、多租戶應用支撐能力、透明的服務註冊和服務發現機制、內建智慧負載均衡器、強大的故障發現和自我修復能力、服務滾動升級和線上擴容能力、可擴充套件的資源自動排程機制,以及多粒度的資源配額管理能力。同時,Kubernetes提供了完善的管理工具,這些工具涵蓋了包括開發、部署測試、運維監控在內的各個環節。因此Kubernetes是一個全新的基於容器技術的分散式架構解決方案,並且是一個一站式的完備的分散式系統開發和支撐平臺。

Kubernetes作為容器叢集管理工具,於2015年7月22日迭代到 v 1.0並正式對外公佈,這意味著這個開源容器編排系統可以正式在生產環境使用。與此同時,谷歌聯合Linux基金會及其他合作伙伴共同成立了CNCF基金會( Cloud Native Computing Foundation),並將Kuberentes 作為首個編入CNCF管理體系的開源專案,助力容器技術生態的發展進步。Kubernetes專案凝結了Google過去十年間在生產環境的經驗和教訓,從Borg的多工Alloc資源塊到Kubernetes的多副本Pod,從Borg的Cell叢集管理,到Kubernetes設計理念中的聯邦叢集,在Docker等高階引擎帶動容器技術興起和大眾化的同時,為容器叢集管理提供獨了到見解和新思路。
架構
Kubernetes叢集包含有節點代理kubelet和Master元件(APIs, scheduler, etc),一切都基於分散式的儲存系統。

Kubernetes部署架構圖

Kubernetes主要由以下幾個核心元件組成:

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

除了核心元件,還有一些推薦的Add-ons:
kube-dns負責為整個叢集提供DNS服務
Ingress Controller為服務提供外網入口
Heapster提供資源監控
Dashboard提供Web UI
Federation提供跨可用區的叢集
Fluentd-elasticsearch提供叢集日誌採集、儲存與查詢
建立Kubernetes叢集
一般情況下,我們部署kubernetes叢集有兩種方式:

第一種方式:元件式安裝
針對於master節點,將API Server、etcd、controller-manager、scheduler各元件進行yum install、編譯安裝或者展開安裝的方式手動直接安裝在master節點主機上,作為系統級守護程序執行。

第二種方式:採用kubeadm安裝工具安裝
每一個節點主機上包括master節點都要手動安裝並執行docker,同時也都要手動安裝並執行kubelet。如果將第一個節點初始化為master節點,在執行初始化這個步驟,其實就是通過kubeadm工具將API Server、etcd、controller-manager、scheduler各元件執行為Pod,也就是跑在docker上。而其他node節點,因已經運行了kubelet、docker元件,剩下的kube-proxy元件也是要執行在Pod上。

基礎環境配置
本次安裝我們準備了三臺CentOS 7伺服器,其中一臺Master節點,2臺Node節點。其中Master節點上部署的元件有:Etcd、Kubelet、APIService Controller、Schedule和Docker等。Node節點上主要部署Kubelet和Docker。
IP
內網IP
主機名
備註

10.0.0.50
172.16.1.50
k8s-master
Master

10.0.0.51
172.16.1.51
k8s-node1
Node

10.0.0.52
172.16.1.52
k8s-node2
Node

各節點採用主機名的方式,這種方式與IP地址相比具有較高的可擴充套件性。
修改主機名

為什麼要修改主機名?
主要原因是kubernetes排程的時候會使用主機名進行排程。

將所有節點配置Hosts

關閉不必要的軟體
$ systemctl list-unit-files | grep enabled | grep -v sshd | grep -v NetworkManager | grep -v ntp | grep -v @ | grep -v crond | awk '{print $1}' | xargs systemctl disable {} \;

安裝必要的包
$ mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
  $ curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
  $ mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup
  $ mv /etc/yum.repos.d/epel-testing.repo /etc/yum.repos.d/epel-testing.repo.backup
  $ curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
  $ yum makecache
  $ yum install wget expect vim net-tools ntp bash-completion ipvsadm ipset jq iptables conntrack sysstat libseccomp -y

關閉Selinux

$ setenforce 0
將/etc/sysconfig/selinux檔案中的SELINUX改為disabled。


關閉swap分割槽
swapoff -a && sysctl -w
同步所有機器上時間
$ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 如果安裝的時候已經設定了時區,這步可省略
  $ echo "Asia/Shanghai" > /etc/timezone
  $ ntpdate ntp.aliyun.com # 同步時間
  $ crontab -e # 加入定時任務
  	*/5 * * * * ntpdate ntp.aliyun.com 1>/dev/null
使所有節點間免密登入
  # 生成ssh key
  $ ssh-keygen -t rsa
  # 同步到其他節點
  $ for i in k-master k-node1 k-node2;do
  > expect -c "
  > spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@$i
  >         expect {
  >                 \"*yes/no*\" {send \"yes\r\"; exp_continue}
  >                 \"*password*\" {send \"root\r\"; exp_continue}
  >                 \"*Password*\" {send \"root\r\";}
  >         } "
  > done 

[root@k8s-master ~]# for i in k8s-master k8s-node1 k8s-node2 ; do 
> ssh-copy-id -i ~/.ssh/id_rsa.pub root@$i
> done

# 測試
ssh root@k8s-node
升級核心引數
  Docker overlay2需要使用kernel 4.x版本,所以我們需要升級核心。我這裡的核心使用4.18.9。CentOS 7.x 系統自帶的 3.10.x 核心存在一些 Bugs,導致執行的 Docker、Kubernetes 不穩定。
$ wget http://mirror.rc.usf.edu/compute_lock/elrepo/kernel/el7/x86_64/RPMS/kernel-ml{,-devel}-4.18.9-1.el7.elrepo.x86_64.rpm
  # 同步到其他節點
  $ for i in k-master k-node1 k-node2 ; do scp kernel-ml{,-devel}-4.18.16-1.el7.elrepo.x86_64.rpm $i:/root; done 
  $ yum localinstall -y kernel-ml*
  $ # 修改核心啟動順序
  $ grub2-set-default  0 && grub2-mkconfig -o /etc/grub2.cfg
  $ # 檢視(可執行可不執行)
  $ grubby --default-kernel
  $ reboot

修改核心引數
  $ cat > /etc/sysctl.d/kubernetes.conf <<EOF
  net.bridge.bridge-nf-call-iptables=1
  net.bridge.bridge-nf-call-ip6tables=1
  net.ipv4.ip_forward=1
  net.ipv4.tcp_tw_recycle=0
  vm.swappiness=0
  vm.overcommit_memory=1
  vm.panic_on_oom=0
  fs.inotify.max_user_instances=8192
  fs.inotify.max_user_watches=1048576
  fs.file-max=52706963
  fs.nr_open=52706963
  net.ipv6.conf.all.disable_ipv6=1
  net.netfilter.nf_conntrack_max=2310720
  EOF
  $ sysctl -p /etc/sysctl.d/kubernetes.conf
安裝IPVS
在本教程中,kube-proxy均採用ipvs模式,該模式也是新版本預設支援的代理模式,效能比iptables要高,如果伺服器未裝了ipvs,將會轉換成iptables模式。這裡我們設定開機自動載入:
$ yum install ipset ipvsadm -y
  
$ cat > /etc/sysconfig/modules/k8s.modules <<EOF
  modprobe -- ip_vs
  modprobe -- ip_vs_rr
  modprobe -- ip_vs_wrr
  modprobe -- ip_vs_sh
  modprobe -- nf_conntrack_ipv4
  modprobe -- ip_tables
  modprobe -- ip_set
  modprobe -- xt_set
  modprobe -- ipt_set
  modprobe -- ipt_rpfilter
  modprobe -- ipt_REJECT
  modprobe -- ipip
  EOF
  # 檢視是否載入,如未看到資訊可以重啟試試。
  $ chmod 755 /etc/sysconfig/modules/k8s.modules && bash /etc/sysconfig/modules/k8s.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4

3.2	搭建叢集
3.2.1.	安裝Docker
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum install docker-ce -y
mkdir /etc/docker
cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ]
}
EOF  
mkdir -p /etc/systemd/system/docker.service.d
systemctl daemon-reload
systemctl restart docker
3.2.1.	Master節點安裝Kubernetes
  $ cat <<EOF > /etc/yum.repos.d/kubernetes.repo
  [kubernetes]
  name=Kubernetes
  baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
  enabled=1
  gpgcheck=1
  repo_gpgcheck=1
  gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
  EOF
  $ setenforce 0
  $ yum install -y kubelet kubeadm kubectl
3.2.3.	Node節點上安裝kubernetes
$ cat <<EOF > /etc/yum.repos.d/kubernetes.repo
  [kubernetes]
  name=Kubernetes
  baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
  enabled=1
  gpgcheck=1
  repo_gpgcheck=1
  gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
  EOF
  $ setenforce 0
  $ yum install -y kubelet kubeadm
3.2.4.	設定開機自啟動
systemctl enable docker.service kubelet.service 
3.2.5.	初始化Kubernetes叢集Master節點
3.2.5.1.	 設定忽略Swap
vim /etc/sysconf/kubelet # 新增如下內容
  KUBELET_EXTRA_ARGS="--fail-swap-on=false"

3.2.5.2. 安裝
  kubeadm init \
  --apiserver-advertise-address=172.16.1.50 \ # 可不要(設定Master節點IP)
  --image-repository=registry.aliyuncs.com/google_containers \
  --kubernetes-version=v1.17.0 \ # 可不要(設定安裝的k8s版本)
  --service-cidr=10.96.0.0/12 \
  --pod-network-cidr=10.244.0.0/16 \
  --ignore-preflight-errors=Swap # 如果沒有Swap分割槽,可不要

# 沒特殊情況下
  kubeadm init \
  --image-repository=registry.aliyuncs.com/google_containers \
  --service-cidr=10.96.0.0/12 \
  --pod-network-cidr=10.244.0.0/16 


安裝提示執行一下命令:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
3.2.5.2.	Kubectl命令自動補全
yum install -y bash-completion
source /usr/share/bash-completion/bash_completion
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
3.2.5.3.	安裝網路外掛
#到GitHub上搜flannel網路外掛。


對於Kubernetes 1.7 以上可以執行上面命令安裝網路外掛:
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

我們可以看到網路外掛flannel正在初始化中:

我們可以通過命令檢視一下flannel詳情:

可以明顯的看出,映象拉去不下來;但是我們可以嘗試以下方式。
docker pull quay-mirror.qiniu.com/coreos/flannel:v0.11.0-amd64; docker tag quay-mirror.qiniu.com/coreos/flannel:v0.11.0-amd64 quay.io/coreos/flannel:v0.11.0-amd64
然後檢視DNS:

3.2.5.4.	將Node節點加入叢集

有時候我們難免會忘記這個token,如果忘記,我們可以建立一個,首先我們看一下我們叢集有多少個Token:

  建立方式:
[root@k-master ~]# kubeadm token create  --print-join-command  
  kubeadm join 10.0.0.50:6443 --token 038qwm.hpoxkc1f2fkgti3r     --discovery-token-ca-cert-hash sha256:edcd2c212be408f741e439abe304711ffb0adbb3bedbb1b93354bfdc3dd13b04
  # 注:上面的IP(10.0.0.50要改成172.16.1.50,因為Kubernetes在解析的時候回找主機名解析,如果使用10.0.0.50的話,很可能會報錯。如果是本地虛擬機器就修改不是就不需要。)
  加入之後,我們可以檢視一下是否成功:

Pod
K8s有很多技術概念,同時對應很多API物件,最重要的也是最基礎的是微服務Pod。Pod是在K8s叢集中執行部署應用或服務的最小單元,它是可以支援多容器的。Pod的設計理念是支援多個容器在一個Pod中共享網路地址和檔案系統,可以通過程序間通訊和檔案共享這種簡單高效的方式組合完成服務。Pod對多容器的支援是K8s最基礎的設計理念。比如你執行一個作業系統發行版的軟體倉庫,一個Nginx容器用來發布軟體,另一個容器專門用來從源倉庫做同步,這兩個容器的映象不太可能是一個團隊開發的,但是他們一塊兒工作才能提供一個微服務;這種情況下,不同的團隊各自開發構建自己的容器映象,在部署的時候組合成一個微服務對外提供服務。

Pod是K8s叢集中所有業務型別的基礎,可以看作執行在K8s叢集中的小機器人,不同型別的業務就需要不同型別的小機器人去執行。目前K8s中的業務主要可以分為長期伺服型(long-running)、批處理型(batch)、節點後臺支撐型(node-daemon)和有狀態應用型(stateful application);分別對應的小機器人控制器為Deployment、Job、DaemonSet和PetSet,本文後面會一一介紹。

4.1.	什麼是POD
Pod是Kubernetes中能夠建立和部署的最小單元,是Kubernetes叢集中的一個應用例項,總是部署在同一個節點Node上。Pod中包含了一個或多個容器,還包括了儲存、網路等各個容器共享的資源。Pod支援多種容器環境,Docker則是最流行的容器環境。

單容器Pod,最常見的應用方式。
Pod中不同容器之間的埠是不能衝突的

多容器Pod,對於多容器Pod,Kubernetes會保證所有的容器都在同一臺物理主機或虛擬主機中執行。多容器Pod是相對高階的使用方式,除非應用耦合特別嚴重,一般不推薦使用這種方式。一個Pod內的容器共享IP地址和埠範圍,容器之間可以通過 localhost 互相訪問。


Pod並不提供保證正常執行的能力,因為可能遭受Node節點的物理故障、網路分割槽等等的影響,整體的高可用是Kubernetes叢集通過在叢集內排程Node來實現的。通常情況下我們不要直接建立Pod,一般都是通過Controller來進行管理,但是瞭解Pod對於我們熟悉控制器非常有好處。
4.2. Pod帶來的好處
Pod做為一個可以獨立執行的服務單元,簡化了應用部署的難度,以更高的抽象層次為應用部署管提供了極大的方便。
Pod做為最小的應用例項可以獨立執行,因此可以方便的進行部署、水平擴充套件和收縮、方便進行排程管理與資源的分配。
Pod中的容器共享相同的資料和網路地址空間,Pod之間也進行了統一的資源管理與分配。
4.3.Pod內如何管理多個容器
Pod中可以同時執行多個程序(作為容器執行)協同工作。同一個Pod中的容器會自動的分配到同一個 node 上。同一個Pod中的容器共享資源、網路環境和依賴,它們總是被同時排程。注意在一個Pod中同時執行多個容器是一種比較高階的用法。只有當你的容器需要緊密配合協作的時候才考慮用這種模式。
4.4. Pod的永續性	
Pod在設計⽀持就不是作為持久化實體的。在排程失敗、節點故障、缺少資源或者節點維護的狀態下都會死掉會被驅逐。通常,⽤戶不需要⼿動直接建立Pod, ⽽是應該使⽤controller(例如Deployments) , 即使是在建立單個Pod的情況下。 Controller可以提供叢集級別的⾃愈功能、 複製和升級管理。Pod原語有利於:
排程程式和控制器可插拔性
支援pod級操作,無需通過控制器API代理他們
將pod生命週期與控制器生命週期分離
控制器與服務的分離,端點控制器只是監視pod
將叢集集功能與kubelet及共鞥的清晰組合
高可用性應用程式,他們可以在終止前及在刪除前更換pod
4.5. Pod的終止
因為Pod作為在叢集的節點上運⾏的程序, 所以在需要的時候能夠優雅的終⽌掉是⼗分必要的(⽐起使⽤傳送KILL訊號這種暴⼒的⽅式)。⽤戶需要能夠放鬆刪除請求, 並且知道它們何時會被終⽌, 是否被正確的刪除。 ⽤戶想終⽌程式時傳送刪除pod的請求, 在pod可以被強制刪除前會有⼀個寬限期, 會發送⼀個TERM請求到每個容器的主程序。 ⼀旦超時, 將向主程序傳送KILL訊號並從API server中刪除。 如果kubelet或者container manager在等待程序終⽌的過程中重啟, 在重啟後仍然會重試完整的寬限期。示例流程如下:
使用者傳送刪除pod的命令,預設寬限期30秒
在Pod超過該寬限期後API server就會更新Pod的狀態為dead
在客戶端命令列上顯示的Pod的狀態為為terminating;
跟上一部同時,當kubelet發現pod被標記為terminating時,開始停止pod程序:
如果在pod中定義了preStop hook,在停止pod前會被呼叫。如果寬限期後 preStop hook仍然在執行,第二部會增加2秒寬限期
向Pod中的程序傳送TERM訊號
跟第三部同時,該Pod將從該service的端點列表中刪除,不再是replication controller中的一部分,關閉的慢的pod將繼續處理load balancer轉發的流量
過了寬限期後,將向Pod中依然存在的程序發SIGKILL訊號殺掉程序。
kubelet會在APIserver中完成Pod的刪除,通過將優雅週期設定為0(立即刪除)Pod在API中消失,並且客戶端也不可見。
4.6 Pause容器
Pause容器,又叫Infra容器。我們檢查node節點的時候會發現每個node上都運行了很多的pause容器

kubernetes中的pause容器主要為每個業務容器提供以下功能:
在pod中擔任Linux名稱空間共享的基礎;
啟用pid名稱空間,開啟init程序。
共享網路。
4.7 Pod的生命週期
像單獨的容器應用一樣,Pod並不是持久執行的。Pod建立後,Kubernetes為其分配一個UID,並且通過Controller排程到Node中執行,然後Pod一直保持執行狀態直到執行正常結束或者被刪除。在Node發生故障時,Controller負責將其排程到其他的Node中。Kubernetes為Pod定義了幾種狀態,分別如下:
Pending,Pod已建立,正在等待容器建立。經常是正在下載映象,因為這一步驟最耗費時間。
Running,Pod已經繫結到某個Node並且正在執行。或者可能正在進行意外中斷後的重啟。
Succeeded,表示Pod中的容器已經正常結束並且不需要重啟。
Failed,表示Pod中的容器遇到了錯誤而終止。
Unknown,因為網路或其他原因,無法獲取Pod的狀態。
4.8 常用Pod管理命令
Pod的配置資訊中有幾個重要部分,apiVersion、kind、metadata(元資料)、spec以及status。其中apiVersion和kind是比較固定的,status是執行時的狀態,所以最重要的就是metadata和spec兩個部分。

先來看一個典型的配置檔案,命名為 first-pod.yml
apiVersion: v1kind: Podmetadata:  name: first-pod  labels:    app: bash    tir: backendspec:  containers:    - name: bash-container      image: busybox      command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 10']

檢視一下Pod的配置資訊

檢視一下Pod 的日誌

檢視執行中Pod詳細資訊

4.9. 標籤管理
Label是Kubernetes系統中另外一個核心概念。一個Label是一個key=value的鍵值對,其中key與vaue由使用者自己指定。Label可以附加到各種資源物件上,例如Node、Pod、Service、RC等,一個資源物件可以定義任意數量的Label,同一個Label也可以被新增到任意數量的資源物件上去,Label通常在資源物件定義時確定,也可以在物件建立後動態新增或者刪除。

我們可以通過指定的資源物件捆綁一個或多個不同的Label來實現多維度的資源分組管理功能,以便於靈活、方便地進行資源分配、排程、配置、部署等管理工作。例如:部署不同版本的應用到不同的環境中;或者監控和分析應用(日誌記錄、監控、告警)等。一些常用等label示例如下。
版本標籤:"release" : "stable" , "release" : "canary"
環境標籤:"environment" : "dev" , "environment" : "production"
架構標籤:"tier" : "frontend" , "tier" : "backend" , "tier" : "middleware"
分割槽標籤:"partition" : "customerA" , "partition" : "customerB"
質量管控標籤:"track" : "daily" , "track" : "weekly"
Label相當於我們熟悉的“標籤”,給某個資源物件定義一個Label,就相當於給它打了一個標籤,隨後可以通過Label Selector(標籤選擇器)查詢和篩選擁有某些Label的資源物件,Kubernetes通過這種方式實現了類似SQL的簡單又通用的物件查詢機制。

4.9.1. 根據標籤來查詢Pod

4.9.2. 增加標籤

4.9.3 可以將標籤顯示為列

4.10. 刪除Pod
刪除Pod有兩種方式:
命令列刪除
kubectl delete pods first-pod
使用yaml檔案刪除
kubectl delete -f demo.yaml 
使用標籤刪除
kubectl delete pods -l tir=backend

4.11. Pod進行健康檢查
健康檢查最簡單的方式就是檢查程序的狀態。Kubelet 不斷的詢問 Docker daemon 這個容器程序是否還在執行,如果沒有,這個容器就會被重啟。目前在所有 Kubernetes 的案例中,這種健康檢查是一直開啟的。對與 Kubernetes 中所有執行的容器都是生效的。

在pod生命週期中可以做的一些事情。主容器啟動前可以完成初始化容器,初始化容器可以有多個,他們是序列執行的,執行完成後就推出了,在主程式剛剛啟動的時候可以指定一個post start 主程式啟動開始後執行一些操作,在主程式結束前可以指定一個 pre stop 表示主程式結束前執行的一些操作。在程式啟動後可以做兩類檢測 liveness probe(存活性探測) 和 readness probe(就緒性探測)

Kubernetes利用Handler功能,可以對容器的狀況進行探測,有以下三種形式。
ExecAction:在容器中執行特定的命令。
TCPSocketAction:檢查容器埠是否可以連線。
HTTPGetAction:檢查HTTP請求狀態是否正常。

Kubelet 可以選擇是否執行在容器上執行的兩種探針執行和做出反應:

LivenessProbe探針(存活性探測):用於判斷容器是否存活,即Pod是否為running狀態,如果LivenessProbe探針探測到容器不健康,則kubelet將kill掉容器,並根據容器的重啟策略是否重啟,如果一個容器不包含LivenessProbe探針,則Kubelet認為容器的LivenessProbe探針的返回值永遠成功。

ReadinessProbe探針(就緒性探測):用於判斷容器是否正常提供服務,即容器的Ready是否為True,是否可以接收請求,如果ReadinessProbe探測失敗,則容器的Ready將為False,控制器將此Pod的Endpoint從對應的service的Endpoint列表中移除,從此不再將任何請求排程此Pod上,直到下次探測成功。(剔除此pod不參與接收請求不會將流量轉發給此Pod

Lifecycle
定義容器啟動後和終止前立即執行的動作



4.11.1. LivenessProbe探針(存活性探測)

引數解析:
failureThreshold:最少連續幾次探測失敗的次數,滿足該次數則認為fail
initialDelaySeconds:容器啟動之後開始進行存活性探測的秒數。不填立即進行
periodSeconds:執行探測的頻率(秒)。預設為10秒。最小值為1。
successThreshold:最少連續幾次探測成功的次數,滿足該次數則認為success。
timeoutSeconds:每次執行探測的超時時間

4.11.1.1. ExecAction
apiVersion: v1
kind: Pod
metadata:
  name: probe-exec
  namespace: defualt
spec:
  containers:
    - name: nginx
      image: nginx
      livenessProbe:
        exec:
          command:
            - cat
            - /tmp/health	
        initialDelaySeconds: 5
        timeoutSeconds: 1

4.11.1.2. TCPSocketAction
apiVersion: v1
kind: Pod
metadata:
  name: probe-tcp
  namespace: default
spec:
  containers:
  - name: nginx
    image: nginx
    livenessProbe:
      initialDelaySeconds: 5
      timeoutSeconds: 1
      tcpSocket:
        port: 80

4.11.1.3. HTTPGetAction
apiVersion: v1
kind: Pod
metadata:
  name: probe-http
  namespace: default
spec:
  containers:
  - name: nginx
    image: nginx
    livenessProbe:
      httpGet:
        path: /
        port: 80
        host: 127.0.0.1
        scheme: HTTP   
      initialDelaySeconds: 5
      timeoutSeconds: 1


補充:
當使用kubectl exec時一定得保證對應的conteiner是存活且正常狀態。
如果一個POD中有多個容器,可使用-c引數來指定要進入的容器。
4.11.2. ReadinessProbe探針(就緒性探測)

引數解析:
failureThreshold:最少連續幾次探測失敗的次數,滿足該次數則認為fail
initialDelaySeconds:容器啟動之後開始進行存活性探測的秒數。不填立即進行
periodSeconds:執行探測的頻率(秒)。預設為10秒。最小值為1。
successThreshold:最少連續幾次探測成功的次數,滿足該次數則認為success。
timeoutSeconds:每次執行探測的超時時間
4.11.3. 案例
kind: Pod
apiVersion: v1
metadata:
  name: readinessprobe-nginx
  namespace: default
  labels:
    provider: aliyun
    business: pms
    environmental: dev
spec:
  containers:
    - name: readinessprobe-nginx
      image: nginx
      imagePullPolicy: Always
      ports:
        - containerPort: 80
          name: http
          protocol: TCP
        - containerPort: 443
          name: https
          protocol: TCP
      readinessProbe:
        httpGet:
          port: 80
          path: /demo.html

      livenessProbe:
        httpGet:
          port: 80
          path: /index.html

      lifecycle:
        postStart:
          exec:
            command: ["touch", "/usr/share/nginx/html/demo.html"]
5.ReplicaSet
Kubernetes 中的 ReplicaSet 主要的作用是維持一組 Pod 副本的執行,它的主要作用就是保證一定數量的 Pod 能夠在叢集中正常執行,它會持續監聽這些 Pod 的執行狀態,在 Pod 發生故障重啟數量減少時重新執行新的 Pod 副本。
5.1. 建立
ReplicaSert除了常見的 apiVersion、kind 和 metadata 屬性之外,規格中總共包含三部分重要內容,也就是 Pod 副本數目 replicas、選擇器 selector 和 Pod 模板 template,這三個部分共同定義了 ReplicaSet 的規格:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: redis

同一個 ReplicaSet 會使用選擇器 selector 中的定義查詢叢集中查詢自己持有的 Pod 物件,它們會根據標籤的匹配獲取能夠獲得的 Pod。所有 ReplicaSet 物件的增刪改查都是由 ReplicationController 控制器完成的,該控制器會通過 Informer 監聽 ReplicaSet 和 Pod 的變更事件並將其加入持有的待處理佇列ReplicationController 中的 queue 其實就是一個儲存待處理 ReplicaSet 的物件池,它執行的幾個 Goroutine 會從佇列中取出最新的資料進行處理。ReplicationController 啟動的多個 Goroutine 會從佇列中取出待處理的任務,然後呼叫 syncReplicaSet 進行同步,這個方法會按照傳入的 key 從 etcd 中取出 ReplicaSet 物件,然後取出全部 Active 的 Pod。隨後執行的 ClaimPods 方法會獲取一系列 Pod 的所有權,如果當前的 Pod 與 ReplicaSet 的選擇器匹配就會建立從屬關係,否則就會釋放持有的物件,或者直接忽視無關的 Pod,建立和釋放關係的方法就是 AdoptPod 和 ReleasePod,AdoptPod 會設定目標物件的 metadata.OwnerReferences 欄位。

映象拉取規則的三種方式!
 Always:總是到遠端拉取最新映象。
 Never:不管本地有沒有都不去遠端拉取映象
 IfNotPresent:如果本地沒有,就去遠端拉取。
6.ReplicationController
ReplicationController 確保在任何時候都有特定數量的 pod 副本處於正常執行狀態。 換句話說,ReplicationController 確保一個 pod 或一組同類的 pod 總是可用的。當 pods 數量過多時,ReplicationController 會終止多餘的 pods。當 pods 數量太少時,ReplicationController 將會啟動新的 pods。 與手動建立的 pod 不同,由 ReplicationController 建立的 pods 在失敗、被刪除或被終止時會被自動替換。 例如,在中斷性維護(如核心升級)之後,您的 pod 會在節點上重新建立。 因此,即使您的應用程式只需要一個 pod,您也應該使用一個 ReplicationController。 ReplicationController 類似於程序管理器,但是 ReplicationController 不是監控單個節點上的單個程序,而是監控跨多個節點的多個 pods。

示例:
kind: ReplicationController
apiVersion: v1
metadata:
  name: nginx
  namespace: default
spec:
  replicas: 2
  selector:
    app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
              name: http

檢查ReplicationController的狀態:
kubectl describe replicationcontrollers/nginx

要以機器可讀的形式列出屬於 ReplicationController 的所有 pod,可以使用如下命令
kubectl get pods --selector=app=nginx --output=jsonpath={.items..metadata.name}
ReplicaSet 是下一代 ReplicationController ,支援新的基於集合的標籤選擇器。 它主要被 Deployment 用來作為一種編排 pod 建立、刪除及更新的機制。 請注意,我們推薦使用 Deployment 而不是直接使用 ReplicaSet,除非您需要自定義更新編排或根本不需要更新。

Deployment 是一種更高級別的 API 物件,它以類似於 kubectl rolling-update 的方式更新其底層 ReplicaSet 及其 Pod。 如果您想要這種滾動更新功能,那麼推薦使用 Deployment,因為與 kubectl rolling-update 不同,它們是宣告式的、服務端的,並且具有其它特性。
Deployment
Deployment為Pod和Replica Set(升級版的 Replication Controller)提供宣告式更新。您在Deployment物件中描述所需的狀態,然後Deployment控制器將實際狀態以受控的速率更改為所需的狀態。您可以定義部署以建立新的副本集,或刪除現有部署並在新部署中採用其所有資源。

註釋:
K8S建立的資源名稱都必須小寫,只支援英文。
K8S資源名稱中只支援中劃線。
7.1 建立
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
          ports:
            - containerPort: 80
7.1.1 命令列執行建立
[root@k-master-01 ~]# kubectl apply -f demo.yaml 
deployment.apps/nginx-deployment created
[root@k-master-01 ~]# kubectl get deployments.apps
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   0/3     0            0           1s
將kubectl標誌設定--record為true允許您將當前命令記錄在正在建立或更新的資源的註釋中。這對於將來的自省很有用。

7.1.2 檢視部署狀態
[root@k-master-01 ~]# kubectl rollout status deployment nginx-deployment 
deployment "nginx-deployment" successfully rolled out
隔幾秒鐘再次檢視
[root@k-master-01 ~]# kubectl get deployments.apps 
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3             57s
這表明Deployment已建立了所有三個副本,並且所有副本都是最新的(包含最新的Pod模板)並且可用(Pod狀態至少已為Deployment的狀態準備就緒.spec.minReadySeconds)。執行 kubectl get rs並kubectl get pods顯示建立的ReplicaSet(RS)和Pod。
[root@k-master-01 ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-85ff79dd56   3         3         3       7m9s
您可能會注意到ReplicaSet的名稱始終為<the name of the Deployment>-<hash value of the pod template>
[root@k-master-01 ~]# kubectl get pods --show-labels
NAME                                READY   STATUS    RESTARTS   AGE     LABELS
nginx-deployment-85ff79dd56-8946c   1/1     Running   0          9m25s   app=nginx,pod-template-hash=85ff79dd56
nginx-deployment-85ff79dd56-dl4js   1/1     Running   0          9m25s   app=nginx,pod-template-hash=85ff79dd56
nginx-deployment-85ff79dd56-zb26d   1/1     Running   0          9m25s   app=nginx,pod-template-hash=85ff79dd56
建立的ReplicaSet確保始終有三個nginx Pod。

Pod模板雜湊標籤

注意上面的pod標籤中示例輸出中的pod-template-hash標籤。此標籤由Deployment控制器新增到Deployment建立或採用的每個ReplicaSet中。其目的是確保部署的子副本集不重疊。它是通過對ReplicaSet的PodTemplate進行雜湊處理並將所得的雜湊值用作標籤值來計算的,該值將新增到ReplicaSet選擇器,窗格模板標籤以及ReplicaSet可能具有的任何現有Pod中。

更新
假設我們現在要更新nginx Pods以使用nginx:latest映象而不是nginx:1.16映象。
[root@k-master-01 ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.16
deployment.apps/nginx-deployment image updated
要檢視部署狀態
[root@k-master-01 ~]# kubectl rollout status deployment nginx-deployment 
deployment "nginx-deployment" successfully rolled out
部署成功後
[root@k-master-01 ~]# kubectl get deployments.apps 
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           36m
下次我們要更新這些Pod時,我們只需要再次更新Deployment的pod模板。

回滾
有時可能想回滾部署;例如,當部署不穩定時(例如崩潰迴圈)。預設情況下,所有“部署”的推出歷史記錄都保留在系統中,以便可以隨時回滾(可以通過修改修訂歷史記錄限制來更改它)。

列出歷史
[root@k-master-01 ~]# kubectl rollout history deployment nginx-deployment 
deployment.apps/nginx-deployment 
REVISION  CHANGE-CAUSE
2         kubectl apply --filename=demo.yaml --record=true
3         kubectl apply --filename=demo.yaml --record=true
4         kubectl apply --filename=demo.yaml --record=true

回滾到版本3
[root@k-master-01 ~]# kubectl rollout undo deployment nginx-deployment --to-revision=3
deployment.apps/nginx-deployment rolled back
[root@k-master-01 ~]# kubectl rollout history deployment nginx-deployment 
deployment.apps/nginx-deployment 
REVISION  CHANGE-CAUSE
2         kubectl apply --filename=demo.yaml --record=true
4         kubectl apply --filename=demo.yaml --record=true
5         kubectl apply --filename=demo.yaml --record=true

擴容
命令列中的三種方式
Patch
[root@k-master-01 ~]#  kubectl patch deploy ddos-attack -p '{"spec":{"replicas": 5}}'
deployment.apps/ddos-attack patched

# 打補丁的格式
Kubectl patch 需要修改的資源 需要修改的資源名稱 -p ‘’

Scale
[root@k-master-01 ~]# kubectl scale deployment ddos-attack --replicas=3
deployment.apps/ddos-attack scaled

# 步驟解釋
Kubectl scale 資源型別 資源名稱 修改項


修改配置檔案中的replicas

暫停和恢復
您可以在觸發一個或多個更新之前暫停部署,然後再恢復它。這樣,您可以在暫停和恢復之間應用多個修復程式,而不會觸發不必要的部署。
[root@k-master-01 ~]# kubectl rollout pause deployment/nginx-deployment
deployment.apps/nginx-deployment paused
然後更新部署的映像
[root@k-master-01 ~]# kubectl rollout history deployment nginx-deployment 
deployment.apps/nginx-deployment 
REVISION  CHANGE-CAUSE
2         kubectl apply --filename=demo.yaml --record=true
5         kubectl apply --filename=demo.yaml --record=true
6         kubectl apply --filename=demo.yaml --record=true
[root@k-master-01 ~]# kubectl set image deploy/nginx-deployment nginx=nginx:1.15
deployment.apps/nginx-deployment image updated
[root@k-master-01 ~]# kubectl rollout history deployment nginx-deployment 
deployment.apps/nginx-deployment 
REVISION  CHANGE-CAUSE
2         kubectl apply --filename=demo.yaml --record=true
5         kubectl apply --filename=demo.yaml --record=true
6         kubectl apply --filename=demo.yaml --record=true
[root@k-master-01 ~]# kubectl set resources deployment nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
deployment.apps/nginx-deployment resource requirements updated
部署在暫停之前的初始狀態將繼續其功能,但是隻要暫停部署,對部署的新更新將不會有任何效果。最後,恢復部署並觀察新的ReplicaSet以及所有新更新。
[root@k-master-01 ~]# kubectl get pods -o wide -w
NAME                                READY   STATUS              RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
nginx-deployment-5549bd4b69-6h55t   0/1     ContainerCreating   0          2s    <none>         k-node-02   <none>           <none>
nginx-deployment-5549bd4b69-dhjh5   1/1     Running             0          8s    10.240.1.182   k-node-01   <none>           <none>
nginx-deployment-85ff79dd56-6k6hp   1/1     Running             0          22m   10.240.2.195   k-node-02   <none>           <none>
nginx-deployment-85ff79dd56-snfrf   1/1     Running             0          22m   10.240.1.180   k-node-01   <none>           <none>
nginx-deployment-5549bd4b69-6h55t   1/1     Running             0          5s    10.240.2.196   k-node-02   <none>           <none>
nginx-deployment-85ff79dd56-6k6hp   1/1     Terminating         0          22m   10.240.2.195   k-node-02   <none>           <none>
nginx-deployment-5549bd4b69-6qkc5   0/1     Pending             0          0s    <none>         <none>      <none>           <none>
nginx-deployment-5549bd4b69-6qkc5   0/1     Pending             0          0s    <none>         k-node-01   <none>           <none>
nginx-deployment-5549bd4b69-6qkc5   0/1     ContainerCreating   0          0s    <none>         k-node-01   <none>           <none>
nginx-deployment-85ff79dd56-6k6hp   0/1     Terminating         0          22m   10.240.2.195   k-node-02   <none>           <none>
nginx-deployment-85ff79dd56-6k6hp   0/1     Terminating         0          22m   10.240.2.195   k-node-02   <none>           <none>
nginx-deployment-5549bd4b69-6qkc5   1/1     Running             0          6s    10.240.1.183   k-node-01   <none>           <none>
nginx-deployment-85ff79dd56-snfrf   1/1     Terminating         0          23m   10.240.1.180   k-node-01   <none>           <none>
nginx-deployment-85ff79dd56-snfrf   0/1     Terminating         0          23m   10.240.1.180   k-node-01   <none>           <none>
nginx-deployment-85ff79dd56-snfrf   0/1     Terminating         0          23m   10.240.1.180   k-node-01   <none>           <none>
nginx-deployment-85ff79dd56-6k6hp   0/1     Terminating         0          23m   10.240.2.195   k-node-02   <none>           <none>
nginx-deployment-85ff79dd56-6k6hp   0/1     Terminating         0          23m   10.240.2.195   k-node-02   <none>           <none>
nginx-deployment-85ff79dd56-snfrf   0/1     Terminating         0          23m   10.240.1.180   k-node-01   <none>           <none>
nginx-deployment-85ff79dd56-snfrf   0/1     Terminating         0          23m   10.240.1.180   k-node-01   <none>           <none>
最後觀察RC
[root@k-master-01 ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-5489dd9d9    0         0         0       27m
nginx-deployment-5549bd4b69   3         3         3       2m54s


ConfigMap
ConfigMap是用來儲存配置檔案的kubernetes資源物件,所有的配置內容都儲存在etcd中。
8.1	建立ConfigMap
建立ConfigMap的方式有4種:

8.1.1 --from-literal
[root@k-master ~]# kubectl create configmap mysql --from-literal=db.host=localhost --from-literal=db.port=3306
configmap/mysql created
[root@k-master ~]# kubectl get configmaps
NAME    DATA   AGE
mysql   2      10s
[root@k-master ~]# kubectl describe configmaps mysql
Name:         mysql
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
db.host:
----
localhost
db.port:
----
3306
Events:  <none>

8.1.2 通過指定檔案建立,即將一個配置檔案建立為一個ConfigMap --from-file=<檔案>
[root@k-master ~]# vim nginx.conf
[root@k-master ~]# kubectl create configmap nginx --from-file=nginx.conf
configmap/nginx created
[root@k-master ~]# kubectl get configmaps
NAME    DATA   AGE
mysql   2      2m55s
nginx   1      8s
[root@k-master ~]# kubectl describe configmaps nginx
Name:         nginx
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
nginx.conf:
----
#daemon off;
user  www;
worker_processes auto;

pid        /var/run/nginx.pid;
worker_rlimit_nofile 51200;

events {
    use epoll;
    worker_connections 51200;
    multi_accept on;
}

http {

}


Events:  <none>
[root@k-master ~]#

通過指定目錄建立,即將一個目錄下的所有配置檔案建立為一個ConfigMap,--from-file=<目錄>
[root@k-master ~]# kubectl create configmap conf --from-file=conf/
configmap/conf created
[root@k-master ~]# kubectl get configmaps
NAME    DATA   AGE
conf    2      9s
mysql   2      5m16s
nginx   1      2m29s
[root@k-master ~]# kubectl describe configmaps config
Error from server (NotFound): configmaps "config" not found
[root@k-master ~]# kubectl describe configmaps conf
Name:         conf
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
mysql.conf:
----
[mysqld]


nginx.conf:
----
#daemon off;
user  www;
worker_processes auto;

pid        /var/run/nginx.pid;
worker_rlimit_nofile 51200;

events {
    use epoll;
    worker_connections 51200;
    multi_accept on;
}


http {
    include       /application/nginx-1.16.1/conf/mime.types;
default_type  application/octet-stream;
}


Events:  <none>
[root@k-master ~]#
事先寫好標準的configmap的yaml檔案,然後kubectl create -f 建立
[root@k-master ~]# vim config.yaml
kind: ConfigMapapiVersion: v1metadata:  name: configmap-yaml-test  namespace: defaultdata:  db.host: "127.0.0.1"  db.port: "3306"  db.password: "123456"

[root@k-master ~]# kubectl apply -f config.yaml
configmap/yaml created
[root@k-master ~]# kubectl get configmaps
NAME    DATA   AGE
conf    2      23m
mysql   2      28m
nginx   1      25m
yaml    2      7s
[root@k-master ~]# kubectl describe configmaps yaml
Name:         yaml
Namespace:    default
Labels:       app=yaml
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","data":{"nginx_host":"127.0.0.1","nginx_port":"80, 443"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app...

Data
====
nginx_host:
----
127.0.0.1
nginx_port:
----
80, 443
Events:  <none>
8.2 使用ConfigMap
8.2.1 通過環境變數的方式,直接傳遞給pod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-config
spec:
  selector:
    matchLabels:
      app: test-config
  template:
    metadata:
      labels:
        app: test-config
    spec:
      containers:
      - name: test-config
        image: busybox
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        command:
          - "/bin/sh"
          - "-c"
          - "env"
        
        env: 
          - name: yaml
            valueFrom: 
              configMapKeyRef: 
                name: NGINX-HOST
                key: nginx_host

8.2.2 作為volume的方式掛載到pod內
kind: Deployment
apiVersion: apps/v1

metadata:
  name: configmap-test-volumes
  namespace: default
  labels:
    app: deployment-test

spec:
  replicas: 2
  selector:
    matchLabels:
      app: deployment-test-pod

  template:
    metadata:
      name: deployment-pod-test
      labels:
        app: deployment-test-pod

    spec:
      containers:
        - name: deployment-pod-nginx
          image: nginx 
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
              name: http

          volumeMounts:
            - mountPath: "/etc/nginx/nginx.conf"
              name: configmap
              readOnly: true
              subPath: nginx.conf


      volumes:
        - name: configmap
          configMap:
            name: nginx-baidu
            items:
              - key: nginx.conf
                path: nginx.conf



注:
刪除configmap後原pod不受影響;然後再刪除pod後,重啟的pod的events會報找不到cofigmap的volume,除非設定為可為空。
pod起來後再通過kubectl edit configmap …修改configmap,pod內部的配置更新有延時。
在容器內部修改掛進去的配置檔案後,過一會內容會再次被重新整理為原始configmap內容
8.3 ConfigMap的熱更新
更新 ConfigMap 後:

使用該 ConfigMap 掛載的 Env 不會同步更新
使用該 ConfigMap 掛載的 Volume 中的資料需要一段時間(實測大概10秒)才能同步更新
ENV 是在容器啟動的時候注入的,啟動之後 kubernetes 就不會再改變環境變數的值,且同一個 namespace 中的 pod 的環境變數是不斷累加的。confgimap更新後,如果是以資料夾方式掛載的,會自動將掛載的Volume更新。如果是以檔案形式掛載的,則不會自動更新。但是對多數情況的應用來說,配置檔案更新後,最簡單的辦法就是重啟Pod(殺掉再重新拉起)。如果是以資料夾形式掛載的,可以通過在容器內重啟應用的方式實現配置檔案更新生效。即便是重啟容器內的應用,也要注意configmap的更新和容器內掛載檔案的更新不是同步的,可能會有延時,因此一定要確保容器內的配置也已經更新為最新版本後再重新載入應用。
Secret
Secret與ConfigMap類似,但是用來儲存敏感資訊。它解決了密碼、token、金鑰等敏感資料的配置問題,而不需要把這些敏感資料暴露到映象或者Pod Spec中。Secret可以以Volume或者環境變數的方式使用。
9.1. Secret型別和使用
Secret有三種類型。
9.1.1. docker-registry[kubernetes.io/dockerconfigjson]:建立一個給 Docker registry 使用的 secret


9.1.2. generic[Opaque]:從本地 file, directory 或者 literal value 建立一個 secret
建立
kind: Secret
apiVersion: v1
metadata:
  name: test-secret
  namespace: default
data:
  username: root
  password: root
type: Opaque

使用
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nginx
  namespace: default
  labels:
    name: nginx

spec:
  selector:
    matchLabels:
      name: nginx
  template:
    metadata:
      labels:
        name: nginx
    spec:
      containers:
        - image: nginx
          name: nginx
          volumeMounts:
            - name: secrets
              mountPath: "/etc/secrets"
              readOnly: true
          ports:
            - containerPort: 80
              name: http
      volumes:
        - name: secrets
          secret:
            secretName: test-secret

9.1.3. tls[kubernetes.io/service-account-token]:建立一個 TLS secret。用於被serviceaccount引用。serviceaccout建立時Kubernetes會預設建立對應的secret。Pod如果使用了serviceaccount,對應的secret會自動掛載到Pod的/run/secrets/kubernetes.io/serviceaccount目錄中。
[root@k-master-01 tls]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=alvin.com"
Generating a RSA private key
........................................+++++
...................................................................................+++++
writing new private key to 'tls.key'
-----
[root@k-master-01 tls]# kubectl create secret tls tls-secret --key tls.key --cert tls.crt
secret/tls-secret created
[root@k-master-01 tls]# kubectl get secrets 
NAME                  TYPE                                  DATA   AGE
admin-token-sxjqf     kubernetes.io/service-account-token   3      17d
default-token-5mwz6   kubernetes.io/service-account-token   3      24d
tls-secret            kubernetes.io/tls                     2      5s




9.2. Secret與ConfigMap對比
相同點:
key/value的形式
屬於某個特定的namespace
可以匯出到環境變數
可以通過目錄/檔案形式掛載(支援掛載所有key和部分key)

不同點:
Secret可以被ServerAccount關聯(使用)
Secret可以儲存register的鑑權資訊,用在ImagePullSecret引數中,用於拉取私有倉庫的映象
Secret支援Base64加密
Secret分為kubernetes.io/Service Account,kubernetes.io/dockerconfigjson,Opaque三種類型,Configmap不區分型別
Secret檔案儲存在tmpfs檔案系統中,Pod刪除後Secret檔案也會對應的刪除。

DaemonSet
DaemonSet 確保全部(或者某些)節點上執行一個 Pod 的副本。當有節點加入叢集時, 也會為他們新增一個 Pod 。當有節點從叢集移除時,這些 Pod 也會被回收。刪除 DaemonSet 將會刪除它建立的所有 Pod。

建立 DaemonSet
kind: DaemonSet
apiVersion: apps/v1
metadata:
  name: test-daemonset
  namespace: default
  labels:
    app: filebeat
spec:
  selector:
    matchLabels:
      app: filebeat
  template:
    metadata:
      labels:
        app: filebeat
    spec:
      containers:
        - name: filebeat
          image: docker.elastic.co/beats/filebeat:7.5.1
          imagePullPolicy: Always
          env:
            - name: ELASTICSEARCH_HOST
              value: 106.13.81.75
            - name: ELASTICSEARCH_PORT
              value: "9200"
          ports:
            - containerPort: 9200
              name: filebeat-port
          resources:
            limits:
              memory: 200Mi
            requests:
              cpu: 100m
              memory: 100Mi

我們可以清晰的看到分別在Node1和Node2上分別建立了一個Pod。那麼我們現在刪除一個Node檢視結果:

StatefulSet
我們知道,使用Kubernetes部署無狀態的應用很方便,例如NGINX,Tomcat等,但是有時候我們需要部署像MySQL,Redis,ElasticSearch等這些有主從狀態的應用的時候,如果我們單純的去使用Deployment或DeamonSet的話,就不是那麼的方便了。於是Kubernetes推出了StatefulSet控制器來完成這個需求。
11.1 StatefulSet特點
StatefulSet適用於具有以下特點的應用

具有固定的網路標記(主機名)
具有持久化儲存
需要按順序部署和擴充套件
需要按順序終止及刪除
需要按順序滾動更新
11.2建立StatefulSet
與Deployment一樣,StatefulSet也是使用容器的Spec來建立Pod,與之不同StatefulSet建立的Pods在生命週期中會保持持久的標記(例如Pod Name)。
kind: Service
apiVersion: v1
metadata:
  name: myapp-svc
  namespace: default
  labels:
    app: myapp-svc
spec:
  ports:
    - port: 80
      name: web
  clusterIP: None
  selector:
    app: myapp-pod

---

kind: StatefulSet
apiVersion: apps/v1
metadata:
  name: myapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp-pod
  serviceName: myapp-svc
  template:
    metadata:
      labels:
        app: myapp-pod
    spec:
      containers:
        - name: myapp
          image: nginx
          ports:
            - containerPort: 80
              name: web
          volumeMounts:
            - mountPath: /usr/local/nginx/html
              name: myappdata
  volumeClaimTemplates:
    - metadata:
        name: myappdata
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 2Gi


11.3擴容/縮容
11.3.1、scale
kubectl scale statefulset myapp --replicas=3
檢視

11.3.2、patch
kubectl patch statefulsets.apps myapp -p '{"spec":{"replicas":2}}'


11.3.3、更新策略
kubectl explain sts.spec.updateStrategy.rollingUpdate.partition

這種更新策略的含義是, 若當前statefulSet的副本數為5個,則Pod名為pod-0~pod-4,那麼此時定義partition=4, 就意味著我要更新大於等於4的Pod,而只有pod-4的ID 4 是大於等於4的,所以只有pod-4會被更新,其它不會,這就是金絲雀更新。若後期發現pod-4更新後,工作一切正常,那麼就可以調整partition=0,這樣只要大於等於0的pod ID都將被更新。
首先我們將容器副本更新成5個。
kubectl scale statefulset myapp --replicas=5
kubectl get pods -o wide -w -l app=myapp-pod

將更新策略設定成4
kubectl patch statefulsets.apps myapp -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":4}}}}'

設定映象為nginx:1.9.1
kubectl set image statefulset/myapp myapp=nginx:1.9.1

設定更新策略為0
kubectl patch statefulsets.apps myapp -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'

12. Job/CronJob
Job負責處理任務,即僅執行一次的任務,它保證批處理任務的一個或多個Pod成功結束。而CronJob則就是在Job上加上了時間排程。
12.1 Job
首先用Yaml來建立一個Job:
kind: Job
apiVersion: batch/v1
metadata:
  name: test-job
spec:
  template:
    metadata:
      name: test-job
      labels:
        app: job
    spec:
      restartPolicy: Never
      containers:
        - name: busybox
          imagePullPolicy: Always
          image: busybox
          command:
            - "/bin/sh"
            - "-c"
            - "for i in 1 2 3 4 5 6 7 8 9; do echo $i; sleep 1; done"

注:Job的RestartPolicy僅支援Never和OnFailure兩種,不支援Always。Job就相當於來執行一個批處理任務,執行完就結束了,如果使用Always的話就相當於陷入了死迴圈。

12.2 CronJob
12.2.1 建立CronJob

CronJob其實就是在Job的基礎上加上了時間排程。這個實際上和我們Linux中的crontab就非常類似了。

12.2.2 併發性規則

.spec.concurrencyPolicy也是可選的。它聲明瞭 CronJob 建立的任務執行時發生重疊如何處理。spec 僅能宣告下列規則中的一種:

Allow (預設):CronJob 允許併發任務執行。
Forbid: CronJob 不允許併發任務執行;如果新任務的執行時間到了而老任務沒有執行完,CronJob 會忽略新任務的執行。
Replace:如果新任務的執行時間到了而老任務沒有執行完,CronJob 會用新任務替換當前正在執行的任務。
請注意,併發性規則僅適用於相同 CronJob 建立的任務。如果有多個 CronJob,它們相應的任務總是允許併發執行的。
13.Horizontal Pod Autoscaling(HPA)
Horizontal Pod Autoscaling可以根據CPU使用率或應用自定義metrics自動擴充套件Pod數量(支援replication controller、deployment和replica set)。基於CPU利用率自動伸縮 replication controller、deployment和 replica set 中的 pod 數量,(除了 CPU 利用率)也可以 基於其他應程式提供的度量指標custom metrics。 pod 自動縮放不適用於無法縮放的物件,比如 DaemonSets。

13.1. HPA工作機制

Pod 水平自動伸縮的實現是一個控制迴圈,由 controller manager 的 --horizontal-pod-autoscaler-sync-period 引數 指定週期(預設值為15秒)。

每個週期內,controller manager 根據每個 HorizontalPodAutoscaler 定義中指定的指標查詢資源利用率。 controller manager 可以從 resource metrics API(每個pod 資源指標)和 custom metrics API(其他指標)獲取指標。
Service
將執行在一組 Pods 上的應用程式公開為網路服務的抽象方法。使用Kubernetes,您無需修改應用程式即可使用不熟悉的服務發現機制。 Kubernetes為Pods提供自己的IP地址和一組Pod的單個DNS名稱,並且可以在它們之間進行負載均衡。

Kubernetes Pods 是有生命週期的。他們可以被建立,而且銷燬不會再啟動。 如果您使用 Deployment 來執行您的應用程式,則它可以動態建立和銷燬 Pod。每個 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一時刻執行的 Pod 集合可能與稍後執行該應用程式的 Pod 集合不同。例如有一個Pod宕機,Deployment重新啟動了一個Pod,這個Pod的名稱和IP地址跟原來的IP完全不同。那麼這導致了一個問題: 如果一個Deployment正在對外提供功能,那麼客戶端如何找出並跟蹤要連線的IP地址,以便前端使用呢?

面對這樣的問題,Kubernetes推出了Service,它通過篩選label來關聯一組Pod,並對其提供負載均衡服務。

14.1 定義Service
kind: Service
apiVersion: v1
metadata:
  name: test-service
  namespace: default
  labels:
    app: test-service
spec:
  type: ClusterIP
  selector:
    app: test-service
  ports:
    - port: 80
      targetPort: 80

14.2 VIP 和 Service 代理
在 Kubernetes 叢集中,每個 Node 執行一個 kube-proxy 程序。kube-proxy 負責為 Service 實現了一種 VIP(虛擬 IP)的形式。
14.2.1 userspace 代理模式
這種模式,kube-proxy 會監視 Kubernetes master 對 Service 物件和 Endpoints 物件的新增和移除。 對每個 Service,它會在本地 Node 上開啟一個埠(隨機選擇)。 任何連線到“代理埠”的請求,都會被代理到 Service 的backend Pods 中的某個上面(如 Endpoints 所報告的一樣)。 使用哪個 backend Pod,是 kube-proxy 基於 SessionAffinity 來確定的。

最後,它安裝 iptables 規則,捕獲到達該 Service 的 clusterIP(是虛擬 IP)和 Port 的請求,並重定向到代理埠,代理埠再代理請求到 backend Pod。

預設情況下,使用者空間模式下的kube-proxy通過迴圈演算法選擇後端。

預設的策略是,通過 round-robin 演算法來選擇 backend Pod。

14.2.2 iptables 代理模式
這種模式,kube-proxy 會監視 Kubernetes 控制節點對 Service 物件和 Endpoints 物件的新增和移除。 對每個 Service,它會安裝 iptables 規則,從而捕獲到達該 Service 的 clusterIP 和埠的請求,進而將請求重定向到 Service 的一組 backend 中的某個上面。 對於每個 Endpoints 物件,它也會安裝 iptables 規則,這個規則會選擇一個 backend 組合。

預設的策略是,kube-proxy 在 iptables 模式下隨機選擇一個 backend。

使用 iptables 處理流量具有較低的系統開銷,因為流量由 Linux netfilter 處理,而無需在使用者空間和核心空間之間切換。 這種方法也可能更可靠。

如果 kube-proxy 在 iptable s模式下執行,並且所選的第一個 Pod 沒有響應,則連線失敗。 這與使用者空間模式不同:在這種情況下,kube-proxy 將檢測到與第一個 Pod 的連線已失敗,並會自動使用其他後端 Pod 重試。

您可以使用 Pod readiness 探測器驗證後端 Pod 可以正常工作,以便 iptables 模式下的 kube-proxy 僅看到測試正常的後端。這樣做意味著您避免將流量通過 kube-proxy 傳送到已知已失敗的Pod。

14.2.3 IPVS 代理模式
在 ipvs 模式下,kube-proxy監視Kubernetes服務和端點,呼叫 netlink 介面相應地建立 IPVS 規則, 並定期將 IPVS 規則與 Kubernetes 服務和端點同步。 該控制迴圈可確保 IPVS 狀態與所需狀態匹配。 訪問服務時,IPVS 將流量定向到後端Pod之一。

IPVS代理模式基於類似於 iptables 模式的 netfilter 掛鉤函式,但是使用雜湊表作為基礎資料結構,並且在核心空間中工作。 這意味著,與 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通訊的延遲要短,並且在同步代理規則時具有更好的效能。與其他代理模式相比,IPVS 模式還支援更高的網路流量吞吐量。

IPVS提供了更多選項來平衡後端Pod的流量。 這些是:
rr:輪詢排程
lc:最小連線數
dh:目標雜湊
sh:源雜湊
sed:最短期望延遲
nq:不排隊排程


14.3 暴露內部服務

14.3.1 Nodeport
建立這種方式的Service,內部可以通過ClusterIP進行訪問,外部使用者可以通過NodeIP:NodePort的方式單獨訪問每個Node上的例項。這種方式有很多問題,直接訪問節點的地址和埠需要在客戶端記錄很多資訊,Pod發生遷移後這些資訊沒辦法動態更新,節點的防火牆及節點所在網路區域的防火牆策略配置會比較麻煩。
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30001
  selector:
    app: nginx

14.3.2 LoadBalancer
這種方式一般需要雲供應商的支援。
14.3.3 Ingress
具體請看Ingress小節。
14.4 HeadLess Service
有時不需要或不想要負載均衡,以及單獨的 Service IP。 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP)的值為 "None" 來建立 Headless Service。您可以使用headless Service 與其他服務發現機制進行介面,而不必與 Kubernetes 的實現捆綁在一起。典型應用就是Ingress。
14.4.1. 正常Service案例
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
          ports:
            - containerPort: 80
              name: http
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  ports:
    - port: 80
      targetPort: 80
  selector:
    app: nginx
---
apiVersion: v1
kind: Service
metadata:
  name: headless-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  clusterIP: None

檢視一下Service的詳情

測試服務,是由Service代理到Pod上。

14.4.2 Headless Service案例
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
          ports:
            - containerPort: 80
              name: http
---
apiVersion: v1
kind: Service
metadata:
  name: headless-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  clusterIP: None		


測試服務,可見HeadLess Service 直接訪問的是Pod

Ingress
在Kubernetes中,服務和Pod的IP地址僅可以在叢集網路內部使用,對於叢集外的應用是不可見的。為了使外部的應用能夠訪問叢集內的服務。Kubernetes提供了NodePort, LoadBalancer和IngressNginx。
Ingress的組成
Ingress Controller:將新加入的Ingress配置轉化成nginx配置,並使其生效。
Ingress服務:將Nginx配置抽象成Ingress配置,方便使用。
Ingress工作原理
ingress controller通過和kubernetes api互動,動態的去感知叢集中ingress規則變化。
然後讀取它,按照自定義的規則,去叢集當中尋找對應Service管理的Pod。
再將生成的Nginx配置寫到nginx-ingress-control的pod裡,這個Ingress controller的pod裡執行著一個Nginx服務,控制器會把生成的nginx配置寫入/etc/nginx.conf檔案中。
然後reload一下使配置生效。以此達到域名分配置和動態更新的問題。

Ingress的優點
動態配置服務
如果按照傳統方式, 當新增加一個服務時, 我們可能需要在流量入口加一個反向代理指向我們新的k8s服務. 而如果用了Ingress, 只需要配置好這個服務, 當服務啟動時, 會自動註冊到Ingress的中, 不需要而外的操作.
減少不必要的埠暴露
  配置過k8s的都清楚, 第一步是要關閉防火牆的, 主要原因是k8s的很多服務會以NodePort方式映射出去, 這樣就相當於給宿主機打了很多孔, 既不安全也不優雅. 而Ingress可以避免這個問題。

建立Ingress
NginxIngress是託管在Github之上的:https://github.com/kubernetes/ingress-nginx/,本章採用NodePort的方式來安裝。
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.29.0/deploy/static/mandatory.yaml

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.29.0/deploy/static/provider/baremetal/service-nodeport.yaml

注:如果映象拉取不下來可使用阿里雲映象海外構建的方式拉取,映象連線:
registry.cn-hangzhou.aliyuncs.com/alvinos/nginx-ingress-controller:0.29.0




安裝成功,之所以是404是因為還沒有後端服務。

使用Ingress-Nginx建立一個後端服務
kind: DeploymentapiVersion: apps/v1metadata:  name: ingress-deployment  namespace: default  labels:    release: stable    environment: dev    tier: frontend    partition: customerA    track: daily    app: deploymentspec:  replicas: 3  selector:    matchLabels:      release: stable      environment: dev      tier: frontend      partition: customerA      track: daily      app: pod  template:    metadata:      labels:        release: stable        environment: dev        tier: frontend        partition: customerA        track: daily        app: pod    spec:      containers:        - name: ingress-pod          image: nginx          imagePullPolicy: IfNotPresent          ports:            - containerPort: 80              name: http            - containerPort: 443              name: https---kind: ServiceapiVersion: v1metadata:  name: ingress-service  namespace: default  labels:    release: stable    environment: dev    tier: frontend    partition: customerA    track: daily    app: svcspec:  type: ClusterIP  selector:    release: stable    environment: dev    tier: frontend    partition: customerA    track: daily    app: pod  ports:    - port: 80      targetPort: 80      name: http    - port: 443      targetPort: 443      name: https---kind: IngressapiVersion: extensions/v1beta1metadata:  name: ingress-ingress  namespace: default  annotations:    kubernetes.io/ingress.class: "nginx"spec:  rules:    - host: www.test.com      http:        paths:          - path: /            backend:              serviceName: ingress-service              servicePort: 80


訪問成功。

基於TLS的Ingress-Nginx
如果要使用基於TLS的Ingress-Nginx的話,需要事先準備好Nginx的證書,也可以通過OpenSSL建立,建立方式如下
# 建立證書
[root@k8s-master ingress]# openssl genrsa -out tls.key 2048
Generating RSA private key, 2048 bit long modulus
.......................................+++
.............+++
e is 65537 (0x10001)
[root@k8s-master ingress]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=ShangHai/L=ShangHai/O=Ingress/CN=www.test.com
# 將證書儲存到Secret中
[root@k8s-master tls]# kubectl -n default create secret tls ingress-tls --cert=tls.crt --key=tls.key 
secret/ingress-tls created



Volume
我們知道,Pod是由容器組成的,而容器宕機或停止之後,資料就隨之丟了,那麼這也就意味著我們在做Kubernetes叢集的時候就不得不考慮儲存的問題,而儲存卷就是為了Pod儲存資料而生的。儲存卷的型別有很多,我們常用到一般有四種:emptyDir,hostPath,NFS以及雲端儲存等。

16.2. emptyDir儲存卷
emptyDir型別的volume在pod分配到node上時被建立,kubernetes會在node上自動分配 一個目錄,因此無需指定宿主機node上對應的目錄檔案。這個目錄的初始內容為空,當Pod從node上移除時,emptyDir中的資料會被永久刪除。
emptyDir Volume主要用於某些應用程式無需永久儲存的臨時目錄。
kind: Deployment
apiVersion: apps/v1
metadata:
  name: test-volume-deployment
  namespace: default
  labels:
    app: test-volume-deployment
spec:
  selector:
    matchLabels:
      app: test-volume-pod
  template:
    metadata:
      labels:
        app: test-volume-pod
    spec:
      containers:
        - name: nginx
          image: busybox
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
              name: http
            - containerPort: 443
              name: https
          volumeMounts:
            - mountPath: /data/
              name: empty
          command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
        - name: os
          imagePullPolicy: IfNotPresent
          image: busybox
          volumeMounts:
            - mountPath: /data/
              name: empty
          command: ['/bin/sh','-c','while true;do echo 'budybox' >> /data/index.html;sleep 2;done']
      volumes:
        - name: empty
          emptyDir: {}

16.3. hostPath儲存卷
hostPath型別則是對映node檔案系統中的檔案或者目錄到pod裡。在使用hostPath型別的儲存卷時,也可以設定type欄位,支援的型別有檔案、目錄、File、Socket、CharDevice和BlockDevice。

16.3.1. hostPath型別
取值
行為


空字串(預設)用於向後相容,這意味著在安裝 hostPath 卷之前不會執行任何檢查。

DirectoryOrCreate
如果在給定路徑上什麼都不存在,那麼將根據需要建立空目錄,許可權設定為 0755,具有與 Kubelet 相同的組和所有權。

Directory
在給定路徑上必須存在的目錄。

FileOrCreate
如果在給定路徑上什麼都不存在,那麼將在那裡根據需要建立空檔案,許可權設定為 0644,具有與 Kubelet 相同的組和所有權。

File
在給定路徑上必須存在的檔案。

Socket
在給定路徑上必須存在的 UNIX 套接字。

CharDevice
在給定路徑上必須存在的字元裝置。

BlockDevice
在給定路徑上必須存在的塊裝置。


16.3.2 演示,建立儲存卷,使用DirectoryOrCreate型別,node節點不存在會自動建立。
apiVersion: v1
kind: Pod
metadata:
  name: vol-hostpath
  namespace: default
spec:
  volumes:
  - name: html
    hostPath:
      path: /data/pod/volume1/
      type: DirectoryOrCreate
  containers:
  - name: myapp
    image: nginx
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html/

16.3.3 查詢驗證

16.3.4 就算pod被刪除再重建,只要node還在,儲存卷就還在。

16.4. NFS儲存卷 
nfs使得我們可以掛載已經存在的共享到我們的Pod中,和emptyDir不同的是,當Pod被刪除時,emptyDir也會被刪除。但是nfs不會被刪除,僅僅是解除掛在狀態而已,這就意味著NFS能夠允許我們提前對資料進行處理,而且這些資料可以在Pod之間相互傳遞,並且nfs可以同時被多個pod掛在並進行讀寫。
16.4.1 安裝nfs
yum install nfs-utils.x86_64 -y

Vim /etc/exports
/root/volume/data1  172.26.192.0/20(rw,no_root_squash)
# 測試
mount -t nfs 172.19.0.3:/root/volume/data1 /data/pod/volume1

[root@k8s-node02 ~]# mount
......
172.19.0.3:/root/volume/data1 on /data/pod/volume1 type nfs4 (rw,relatime,vers=4.1,rsize=131072,wsize=131072,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=192.168.56.13,local_lock=none,addr=192.168.56.14)
[root@stor01 ~]# showmount -e
Export list for 172.19.0.3:
/data/volumes 172.19.0.3/20
16.4.2建立
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nfs
  template:
    metadata:
      labels:
        app: nfs
    spec:
      containers:
        - name: nginx
          imagePullPolicy: IfNotPresent
          image: nginx
          volumeMounts:
            - name: html
              mountPath: /usr/share/nginx/html/
      volumes:
        - name: html
          nfs:
            path: /root/volume/data1
            server: 172.19.0.3

PV/PVC
PersistentVolume(PV)是叢集中已由管理員配置的一段網路儲存。 叢集中的資源就像一個節點是一個叢集資源。 PV是諸如卷之類的卷外掛,但是具有獨立於使用PV的任何單個pod的生命週期。 該API物件捕獲儲存的實現細節,即NFS,iSCSI或雲提供商特定的儲存系統。

PersistentVolumeClaim(PVC)是使用者儲存的請求。PVC的使用邏輯:在pod中定義一個儲存卷(該儲存卷型別為PVC),定義的時候直接指定大小,pvc必須與對應的pv建立關係,pvc會根據定義去pv申請,而pv是由儲存空間創建出來的。pv和pvc是kubernetes抽象出來的一種儲存資源。
17.1生命週期
Volume 的生命週期 5 個階段

Provisioning
即 PV 的建立,可以直接建立 PV(靜態方式),也可以使用 StorageClass 動態建立。

Binding
將 PV 分配給 PVC。

Using
Pod通過PVC使用該Volume。

Releasing
Pod釋放Volume並刪除PVC。

Reclaiming
回收PV,可以保留PV 以便下次使用,也可以直接從雲端儲存中刪除。

Deleting
刪除 PV 並從雲端儲存中刪除後段儲存。

Volume的4種狀態

Available
可用。

Bound
已經分配給 PVC。

Released
PVC 解綁但還未執行回收策略。

Failed
發生錯誤。

17.2 PV的訪問模式
ReadWriteOnce(RWO)
可讀可寫,但只支援被單個節點掛載。

ReadOnlyMany(ROX)
只讀,可以被多個節點掛載。

ReadWriteMany(RWX)
多路可讀可寫。這種儲存可以以讀寫的方式被多個節點共享。不是每一種儲存都支援這三種方式,像共享方式,目前支援的還比較少,比較常用的是 NFS。在 PVC 繫結 PV 時通常根據兩個條件來繫結,一個是儲存的大小,另一個就是訪問模式。


17.3 PV的回收策略
Retain
不清理, 保留 Volume(需要手動清理)

Recycle
刪除資料,即 rm -rf /thevolume/*(只有 NFS 和 HostPath 支援)

Delete
刪除儲存資源,比如刪除 AWS EBS 卷(只有 AWS EBS, GCE PD, Azure Disk 和 Cinder 支援)


17.4 建立PV
kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv001
  labels:
    app: pv001
spec:
  nfs:
    path: /root/datav1
    server: 172.31.16.9
  accessModes:
    - "ReadWriteMany"
    - "ReadWriteOnce"
  capacity:
    storage: 2Gi
---
kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv002
  labels:
    app: pv002
spec:
  nfs:
    path: /root/datav2
    server: 172.31.16.9
  accessModes:
    - "ReadWriteMany"
    - "ReadWriteOnce"
  capacity:
    storage: 5Gi
---
kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv003
  labels:
    app: pv003
spec:
  nfs:
    path: /root/datav3
    server: 172.31.16.9
  accessModes:
    - "ReadWriteMany"
    - "ReadWriteOnce"
  capacity:
    storage: 10Gi
---
kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv004
  labels:
    app: pv004
spec:
  nfs:
    path: /root/datav4
    server: 172.31.16.9
  accessModes:
    - "ReadWriteMany"
    - "ReadWriteOnce"
  capacity:
    storage: 20Gi

17.5 建立PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
  namespace: default

spec:
  accessModes:
    - "ReadWriteMany"
  resources:
    requests:
      storage: "6Gi"

---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nfs
  template:
    metadata:
      labels:
        app: nfs
    spec:
      containers:
        - name: nginx
          imagePullPolicy: IfNotPresent
          image: nginx
          volumeMounts:
            - name: html
              mountPath: /usr/share/nginx/html/
      volumes:
        - name: html
          persistentVolumeClaim:
            claimName: pvc

StorageClass
StorageClass為我們提供了一種類似儲存“類”的方法,叢集管理員能夠在一個叢集中定義各種儲存卷供應,使用者不需要了解儲存的細節和複雜性,就能夠選擇符合自己要求的儲存。說白了,就是由kubernetes自動建立符合條件的儲存。
定義儲存類
每一個儲存類都包含provisioner、parameters和reclaimPolicy這三個引數域,當一個屬於某個類的PersistentVolume需要被動態提供時,將會使用上述的引數域。

儲存類物件的名稱非常重要,使用者通過名稱類請求特定的儲存類。管理員建立儲存類物件時,會設定類的名稱和其它的引數,儲存類的物件一旦被建立,將不能被更新。管理員能夠為PVC指定一個預設的儲存類。

建立StorageClass
使用GlusterFS做自動儲存的話需要安裝Heketi服務,請參考《第五章:基於GlusterFS的Kubernetes》
# provisioner:表示儲存分配器,需要根據後端儲存的不同而變更;
# reclaimPolicy: 預設即”Delete”,刪除pvc後,相應的pv及後端的volume,brick(lvm)等一起刪除;設定為”Retain”時則保留資料,需要手工處理
# resturl:heketi API服務提供的url;
# restauthenabled:可選引數,預設值為”false”,heketi服務開啟認證時必須設定為”true”;
# restuser:可選引數,開啟認證時設定相應使用者名稱;
# secretNamespace:可選引數,開啟認證時可以設定為使用持久化儲存的namespace;
# secretName:可選引數,開啟認證時,需要將heketi服務的認證密碼儲存在secret資源中;
# clusterid:可選引數,指定叢集id,也可以是1個clusterid列表,格式為”id1,id2”;
# volumetype:可選引數,設定卷型別及其引數,如果未分配卷型別,則有分配器決定卷型別;如”volumetype: replicate:3”表示3副本的replicate卷,”volumetype: disperse:4:2”表示disperse卷,其中‘4’是資料,’2’是冗餘校驗,”volumetype: none”表示distribute卷# 

$ vim storageclass.yaml
kind: StorageClassapiVersion: storage.k8s.io/v1metadata:  name: storageclass-glusterprovisioner: kubernetes.io/glusterfsparameters:  resturl: "http://k8s-master:18080"  clusterid: "76e679836682dbd0d023847af02f5dc5"  restauthenabled: "true"  restuser: "admin"  restuserkey: "admin"  gidMin: "40000"  gidMax: "50000"  volumetype: "replicate:2"

# 建立StorageClass
$ kubectl apply -f storageclass.yaml 

# 建立PVC,看是否可以動態建立PV
kind: PersistentVolumeClaimapiVersion: v1metadata:  name: glusterfs-test-pvc  namespace: default  annotations:    volume.beta.kubernetes.io/storage-class: "glusterfs"spec:  storageClassName: glusterfs  accessModes:  - ReadWriteMany  resources:    requests:      storage: 2Gi
$ kubectl get pv
# 這裡就可以看到動態建立的PV了。


第四章:kubernetes高階
User Account
User account是為了方便使用者訪問Kubernetes叢集而設定的,它通過了一個使用者授權的機制。
# 證書存放位置
cd /etc/kubernetes/pki/
# 做一個私鑰,生成alvin.key
(umask 077; openssl genrsa -out alvin.key 2048)
# 基於私鑰生成一個證書,生成alvin.csr,CN就是使用者賬號名
openssl req -new -key alvin.key -out alvin.csr -subj "/CN=alvin"
# 簽發證書,生成alvin.crt,-days:表示證書的過期時間,x509:生成x509格式證書
openssl  x509 -req -in alvin.csr -CA ca.crt -CAkey ca.key  -CAcreateserial -out alvin.crt -days 365
# 檢視證書內容
openssl x509 -in alvin.crt -text -noout


# 把使用者賬戶資訊新增到當前叢集中,embed-certs=true隱藏證書資訊
kubectl config set-credentials alvin --client-certificate=alvin.crt --client-key=alvin.key --embed-certs=true
# 設定該使用者可以訪問kubernetes叢集
kubectl config set-context alvin@kubernetes --cluster=kubernetes --user=alvin
# 檢視配置檔案中的使用者資訊
cat ~/.kube/config 
# 切換到alvin使用者,登入k8s,可以看到alvin使用者沒有管理器許可權
kubectl config use-context alvin@kubernetes

# 測試,在default名稱空間當中可以正常獲取Pod,但是在其他名稱空間之中卻無法獲得(具體Role建立參考RBAC小結)
kubectl get pods
kubectl get pods -n kube-system

# 切回k8s管理員
kubectl config use-context kubernetes-admin@kubernetes
# 獲取其他名稱空間中的Pod
kubectl get pods -n kube-system 

Service Account
Service account是為了方便Pod裡面的程序呼叫Kubernetes API或其他外部服務而設計的。服務提供了一種方便的認證機制,但它不關心授權的問題。與User account不同
User account是為人設計的,而service account則是為Pod中的程序呼叫Kubernetes API而設計;
User account是跨namespace的,而service account則是僅侷限它所在的namespace;
每個namespace都會自動建立一個default service account
Token controller檢測service account的建立,併為它們建立secret
開啟ServiceAccount Admission Controller後
每個Pod在建立後都會自動設定spec.serviceAccount為default(除非指定了其他ServiceAccout)
驗證Pod引用的service account已經存在,否則拒絕建立
如果Pod沒有指定ImagePullSecrets,則把service account的ImagePullSecrets加到Pod中
每個container啟動後都會掛載該service account的token和ca.crt到/var/run/secrets/kubernetes.io/serviceaccount/

[root@k8s-master ~]# kubectl create serviceaccount alvin
serviceaccount/alvin created
[root@k8s-master ~]# kubectl get serviceaccounts 
NAME      SECRETS   AGE
alvin     1         9s
default   1         4d8h
RBAC
在Kubernetes中,授權有ABAC(基於屬性的訪問控制)、RBAC(基於角色的訪問控制)、Webhook、Node、AlwaysDeny(一直拒絕)和AlwaysAllow(一直允許)這6種模式。從1.6版本起,Kubernetes 預設啟用RBAC訪問控制策略。從1.8開始,RBAC已作為穩定的功能。通過設定–authorization-mode=RBAC,啟用RABC。在RABC API中,通過如下的步驟進行授權:

定義角色:在定義角色時會指定此角色對於資源的訪問控制的規則。
繫結角色:將主體與角色進行繫結,對使用者進行訪問授權。

在RBAC API中,角色包含代表權限集合的規則。在這裡,許可權只有被授予,而沒有被拒絕的設定。在Kubernetes中有兩類角色,即普通角色和叢集角色。可以通過Role定義在一個名稱空間中的角色,或者可以使用ClusterRole定義叢集範圍的角色。一個角色只能被用來授予訪問單一命令空間中的資源。

角色(Role)與叢集角色(ClusterRole)
對某個kubernetes的物件(Objects)上施加的一種行為(get post delete 等),我們稱為Permissions,把Permissions授於Role,就是一個角色了。能夠扮演為主體的就是我們之前講到的serviceAccount。角色可以由名稱空間(namespace)內的Role物件定義,而整個Kubernetes叢集範圍內有效的角色則通過ClusterRole物件實現。一個Role物件只能用於授予對某一單一名稱空間中資源的訪問許可權。

ClusterRole物件可以授予與Role物件相同的許可權,但由於它們屬於叢集範圍物件, 也可以使用它們授予對以下幾種資源的訪問許可權:
叢集範圍資源(例如節點,即node)
非資源型別endpoint(例如”/healthz”)
跨所有名稱空間的名稱空間範圍資源(例如pod,需要執行命令kubectl get pods --all-namespaces來查詢叢集中所有的pod)

建立Role
跟之前的資源一樣,Role也有apiVersion, kind和metadata;與之前不同的是它有rules
kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata:  name: test-role  namespace: defaultrules:  - apiGroups:    - ""    resources:      - pod    verbs:      - get      - list      - watch


建立ClusterRole
建立ClusterRole跟Role差不多。
kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata:  name: defaultrules:  - apiGroups:    - ""    resources:	      - pod    verbs:      - get      - list      - watch
角色繫結(RoleBinding)和叢集角色繫結(ClusterRoleBinding)
 RoleBinding將定義的Role授權給一個或者一組使用者,RoleBinding就包含了一組相關主體(即subject, 包括使用者——User、使用者組——Group、或者服務賬戶——Service Account)以及對被授權的Role的引用。在名稱空間中可以通過RoleBinding物件授予許可權,而叢集範圍的許可權授予則通過ClusterRoleBinding物件完成。

角色繫結(RoleBinding)
apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:  name: test-rolebinding  namespace: default
# 角色需要繫結的許可權roleRef:  apiGroup: rbac.authorization.k8s.io  kind: Role  name: test-role
# 需要繫結的角色subjects:- apiGroup: rbac.authorization.k8s.io  kind: User  name: alvin  namespace: default

叢集角色繫結(ClusterRoleBinding)
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:  name: test-rolebinding  namespace: defaultroleRef:  apiGroup: rbac.authorization.k8s.io  kind: ClusterRole  name: test-rolesubjects:- apiGroup: rbac.authorization.k8s.io  kind: User  name: alvin  namespace: default


案例
安裝kubernetes Web UI (dashboard)

安裝
在GitHub上[https://github.com/kubernetes/dashboard]上獲取安裝YAML檔案連線
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc5/aio/deploy/recommended.yaml


建立對所有名稱空間都有許可權的訪問令牌
# 建立serviceaccount
kubectl create serviceaccount dashboard-serviceaccount-admin -n kube-system

# 建立clusterrolebinding
kubectl create clusterrolebinding dashboard-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-serviceaccount-admin

# 獲取令牌
kubectl get secret -n kube-system |grep dashboard-serviceaccount-admin-token
# 檢視dashboard-serviceaccount中的口令
kubectl describe secret dashboard-serviceaccount-admin-token-5vc54 -n kube-system



建立只有default名稱空間許可權的令牌
# 建立serviceaccount
kubectl create serviceaccount def-ns-dashboard-sa -n default

# 建立rolebinding
 kubectl create rolebinding def-ns-dashboard-rb --clusterrole=cluster-admin --serviceaccount=default:def-ns-dashboard-sa

# 在secret中查詢def-ns-dashboard-sa
kubectl get secret

# 檢視def-ns-dashboard-sa中的口令
kubectl describe secret def-ns-dashboard-sa-token-b8plm

高階排程
kubernetes排程之汙點(taint)和容忍(toleration)
Taint(汙點)和 Toleration(容忍)可以作用於 node 和 pod 上,其目的是優化 pod 在叢集間的排程,這跟節點親和性類似,只不過它們作用的方式相反,具有 taint 的 node 和 pod 是互斥關係,而具有節點親和性關係的 node 和 pod 是相吸的。另外還有可以給 node 節點設定 label,通過給 pod 設定 nodeSelector 將 pod 排程到具有匹配標籤的節點上。

Taint 和 toleration 相互配合,可以用來避免 pod 被分配到不合適的節點上。每個節點上都可以應用一個或多個 taint ,這表示對於那些不能容忍這些 taint 的 pod,是不會被該節點接受的。如果將 toleration 應用於 pod 上,則表示這些 pod 可以(但不要求)被排程到具有相應 taint 的節點上。

設定汙點
汙點的三種模式(effect)
NoSchedule: 一定不能被排程
PreferNoSchedule: 儘量不要排程
NoExecute: 不僅不會排程, 還會驅逐Node上已有的Pod

汙點運算子
Equal:表示key是否等於value,預設
Exists:表示key是否存在,此時無需定義value

kubectl taint node k8s-node node-role.kubernetes.io=node:NoSchedule


刪除汙點
kubectl taint node k8s-node node-role.kubernetes.io:NoSchedule-

使用汙點排程
測試汙點
kind: DeploymentapiVersion: apps/v1metadata:  name: deployment-taints-test  namespace: default  labels:    app: deployment-testspec:  replicas: 2  selector:    matchLabels:      app: deployment-test-pod  template:    metadata:      name: deployment-pod-test      labels:        app: deployment-test-pod    spec:      tolerations:        - key: node-role.kubernetes.io/master          # 汙點的KEY          operator: "Equal"          # 汙點運算子          value: ""          # 匹配汙點的值          effect: "NoSchedule"          # 匹配的汙染效果
# tolerationSeconds # 容忍汙染的時間段。沒有設定,這意味著永遠容忍汙染(不要驅逐),零值和負值將被系統視為0(立即收回)      containers:        - name: deployment-pod-nginx          image: nginx          ports:            - containerPort: 80              name: http
在未使用容忍汙點的情況下,所有的Pod只能排程到Node節點上。

當使用容忍汙點的情況下,可以排程到Master節點上。

Kubernetes排程之親和性與反親和性
Pod是kubernetes中的核心概念,kubernetes對於Pod的管理也就是對Pod生命週期的管理以及對Pod進行排程管理。Kubernetes早期版本使用系統預設排程器來對Pod進行統一排程管理,在1.2版本中增加了多個排程器特性,多個排程器可以並行排程不同的Pod,並且可以允許使用者自己定義新的排程器並以外掛的方式供kubernetes使用。在1.6版本中對POD排程進行了增強,這裡稱之為“高階排程”,涉及到多個排程器配置變化、節點親和性/反親和性特性、Pod親和性/反親和性特性、汙點和容忍特性、報告節點問題特性。通常情況下,Pod分配到哪些Node是由scheduler自動實現。但有時,我們需要指定一些排程的限制,例如某些應用應該跑在具有SSD儲存的節點上,有些應用應該跑在同一個節點上等等。

在1.6版本中,Pod Spec結構體中新增了四個屬性,分別是Affinity、SchedulerName、AutomountServiceAccountToken、Tolerations。其中Affinity屬性對應結構體Affinity,負責節點親和性/反親和性特性和Pod親和性/反親和性特性;Tolerations屬性對應結構體Toleration,負責汙點和容忍特性;SchedulerName屬性就是這篇文章要介紹的多個排程器配置變化。其中結構體Affinity和結構體Toleration在1.5版本中已經存在了,但是並不是通過PodSpec結構體中Affinity和Tolerations兩個屬性進行關聯的。

HELM
Helm是Kubernetes的一個包管理工具,用來簡化基於Kubernetes平臺執行的應用的部署和管理,極大的方便了叢集運維人員及應用開發人員工作。
安裝
# 下載,在GitHub上下載[https://github.com/helm/helm/releases]

# 下載
wget https://get.helm.sh/helm-v3.1.0-linux-amd64.tar.gz

# 解壓
tar -zxvf helm-v3.0.0-linux-amd64.tar.gz

# 安裝
helm在解壓後的目錄中找到二進位制檔案,然後將其移至所需的目標位置(mv linux-amd64/helm /usr/local/bin/helm)

# 檢驗
helm

使用
三大概念
Chart:一個 Helm 包,其中包含了執行一個應用所需要的映象、依賴和資源定義等,還可能包含 Kubernetes 叢集中的服務定義,類似 Homebrew 中的 formula,APT 的 dpkg 或者 Yum 的 rpm 檔案。
Release: 在 Kubernetes 叢集上執行的 Chart 的一個例項。在同一個叢集上,一個 Chart 可以安裝很多次。每次安裝都會建立一個新的 release。例如一個 MySQL Chart,如果想在伺服器上執行兩個資料庫,就可以把這個 Chart 安裝兩次。每次安裝都會生成自己的 Release,會有自己的 Release 名稱。
Repository:用於釋出和儲存 Chart 的倉庫

搜尋圖表
helm search hub redis

配置國內Chart倉庫
微軟倉庫(http://mirror.azure.cn/kubernetes/charts/)這個倉庫強烈推薦,基本上官網有的chart這裡都有。
阿里雲倉庫(https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts )
官方倉庫(https://hub.kubeapps.com/charts/incubator)官方chart倉庫,國內有點不好使
# 增加微軟倉庫
helm repo add azure http://mirror.azure.cn/kubernetes/charts
# 增加阿里雲倉庫
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
# 更新倉庫
helm repo update
# 檢視倉庫
helm repo list

使用HELM安裝Redis
在安裝之前,需要建立一個10G以上的PV,供叢集使用。
helm install redis azure/redis-ha -n redis


第五章:基於k8s的專案
基於Kubernetes之上的Glusterfs
GlusterFS (Gluster File System) 是一個開源的分散式檔案系統,主要由 Z RESEARCH 公司負責開發。GlusterFS 是 Scale-Out 儲存解決方案 Gluster 的核心,具有強大的橫向擴充套件能力,通過擴充套件能夠支援數PB儲存容量和處理數千客戶端。GlusterFS 藉助 TCP/IP 或 InfiniBand RDMA 網路將物理分佈的儲存資源聚集在一起,使用單一全域性名稱空間來管理資料。GlusterFS 基於可堆疊的使用者空間設計,可為各種不同的資料負載提供優異的效能。

簡單安裝GlusterFS

# 所以節點執行
# 安裝
yum -y install centos-release-gluster glusterfs-server
# 設定開機自啟動
systemctl enable glusterd.service
# 啟動
systemctl start glusterd.servic
# 建立目錄
mkdir /root/volume/glusterfs/data{1..5}

# Master節點執行初始化Glusterfs
# 配置授信池
[root@k8s-master glusterfs]# gluster peer probe k8s-node
peer probe: success. 
# 資源池列表
[root@k8s-master glusterfs]# gluster pool list
UUID                                    Hostname        State
a0c7cc01-cfea-4d2b-a58d-a1ca437bfd04    k8s-node        Connected 
4d71abf2-3147-4cbb-a5b7-c535bf219cd7    k8s-node2       Connected 
63b6acf7-b2a3-4a38-a571-c3e48172888d    localhost       Connected 
# 建立分散式複製卷
gluster volume create alvin replica 3 \
k8s-master:/root/volume/glusterfs/data1 k8s-master:/root/volume/glusterfs/data2 k8s-master:/root/volume/glusterfs/data3 k8s-master:/root/volume/glusterfs/data4 k8s-master:/root/volume/glusterfs/data5 \
k8s-node:/root/volume/glusterfs/data1   k8s-node:/root/volume/glusterfs/data2   k8s-node:/root/volume/glusterfs/data3   k8s-node:/root/volume/glusterfs/data4   k8s-node:/root/volume/glusterfs/data5 \
k8s-node2:/root/volume/glusterfs/data1  k8s-node2:/root/volume/glusterfs/data2  k8s-node2:/root/volume/glusterfs/data3  k8s-node2:/root/volume/glusterfs/data4  k8s-node2:/root/volume/glusterfs/data5 \
force # 使用force避免報錯

# 啟動卷
[root@k8s-master glusterfs]# gluster volume start alvin
volume start: alvin: success
# 檢視卷詳情
gluster volume info alvin
# 停止卷
gluster volume stop alvin
# 刪除卷
gluster volume delete alvin

# 掛載
mount -t glusterfs k8s-master:/alvin /mnt/
# 檢視掛載結果
[root@k8s-master ~]# df -h | grep alvin
k8s-master:/alvin   20G  5.1G   14G  28% /mnt	
# 縮容
gluster volume remove-brick alvin k8s-node2:/root/volume/glusterfs/data1  k8s-node2:/root/volume/glusterfs/data2  k8s-node2:/root/volume/glusterfs/data3  k8s-node2:/root/volume/glusterfs/data4  k8s-node2:/root/volume/glusterfs/data5 start
# 擴容
gluster volume add-brick alvin k8s-node2:/root/volume/glusterfs/data1  k8s-node2:/root/volume/glusterfs/data2  k8s-node2:/root/volume/glusterfs/data3  k8s-node2:/root/volume/glusterfs/data4  k8s-node2:/root/volume/glusterfs/data5 force

基於GlusterFs的Kubernetes
Endpoints:api-server建立service物件,與service繫結的pod地址。kube-proxy監控service後端endpoint的動態變化,並且維護service和endpoint的對映關係。

建立Endpoint
kind: Endpoints
apiVersion: v1
metadata:
  name: glusterfs-svc
  namespace: default
subsets:
  - addresses:
      - ip: 172.26.220.13
      - ip: 172.26.203.186
      - ip: 172.26.203.185
    ports:
      - port: 49152
        protocol: TCP

建立Service
kind: Service
apiVersion: v1
metadata:
  namespace: default
  name: glusterfs-svc
spec:
  ports:
    - port: 49152
      targetPort: 49152
      protocol: TCP
  sessionAffinity: None
  type: ClusterIP

注:Service與EndPoints是通過名稱關聯的。
建立PV
kind: PersistentVolumeapiVersion: v1metadata:  name: gluster-pv  labels:    app: glusterspec:  accessModes:    - "ReadWriteMany"    - "ReadWriteOnce"  capacity:    storage: 20Gi  glusterfs:    endpoints: glusterfs-svc    path: alvin    readOnly: false

建立Heketi服務
Heketi提供了一個RESTful管理介面,可以用來管理GlusterFS卷的生命週期。 通過Heketi,就可以像使用OpenStack Manila,Kubernetes和OpenShift一樣申請可以動態配置GlusterFS卷。Heketi會動態在叢集內選擇bricks構建所需的volumes,這樣以確保資料的副本會分散到叢集不同的故障域內。同時Heketi還支援任意數量的ClusterFS叢集,以保證接入的雲伺服器不侷限於單個GlusterFS叢集。 便於管理員對GlusterFS進行以下操作。

安裝
yum install heketi-client -y
修改配置檔案
{
  "_port_comment": "Heketi Server Port Number",
  "port": "8080",

  "_use_auth": "Enable JWT authorization. Please enable for deployment",
  "use_auth": true,

  "_jwt": "Private keys for access",
  "jwt": {
    "_admin": "Admin has access to all APIs",
    "admin": {
      "key": "admin"
    },
    "_user": "User only has access to /volumes endpoint",
    "user": {
      "key": "admin"
    }
  },

  "_glusterfs_comment": "GlusterFS Configuration",
  "glusterfs": {
    "_executor_comment": [
      "Execute plugin. Possible choices: mock, ssh",
      "mock: This setting is used for testing and development.",
      "      It will not send commands to any node.",
      "ssh:  This setting will notify Heketi to ssh to the nodes.",
      "      It will need the values in sshexec to be configured.",
      "kubernetes: Communicate with GlusterFS containers over",
      "            Kubernetes exec api."
    ],
    "executor": "ssh",

    "_sshexec_comment": "SSH username and private key file information",
    "sshexec": {
      "keyfile": "/root/.ssh/id_rsa",
      "user": "root",
      "port": "22",
      "sudo": true, # 當非root使用者時需要
      "fstab": "/etc/fstab"
    },

    "_kubeexec_comment": "Kubernetes configuration",
    "kubeexec": {
      "host" :"https://kubernetes.host:8443",
      "cert" : "/path/to/crt.file",
      "insecure": false,
      "user": "kubernetes username",
      "password": "password for kubernetes user",
      "namespace": "OpenShift project or Kubernetes namespace",
      "fstab": "Optional: Specify fstab file on node.  Default is /etc/fstab"
    },

    "_db_comment": "Database file name",
    "db": "/var/lib/heketi/heketi.db",

    "_loglevel_comment": [
      "Set log level. Choices are:",
      "  none, critical, error, warning, info, debug",
      "Default is warning"
    ],
    "loglevel" : "warning"
  }
}
修改Service檔案(紅色標記)
vim /usr/lib/systemd/system/heketi.service

[Unit]
Description=Heketi Server

[Service]
Type=simple
WorkingDirectory=/var/lib/heketi
User=root
ExecStart=/usr/bin/heketi --config=/etc/heketi/heketi.json
Restart=on-failure
StandardOutput=syslog
StandardError=syslog

[Install]
WantedBy=multi-user.target
啟動並測試
# 啟動
systemctl enable heketi  && systemctl start heketi && systemctl status heketi

# 測試(方式一)
curl 127.0.0.1:18080/hello

# 測試(方式二)
 heketi-cli --user admin --secret admin --server http://k8s-master:18080 --json  cluster create 


建立GlusterFS叢集
heketi-cli --user admin --secret admin --server http://k8s-master:18080 --json  cluster create

新增節點
# 建立初始化節點及磁碟資訊
Cat /etc/heketi/init.json
{
    "clusters":[
        {
            "nodes":[
                {
                    "node":{
                        "hostnames":{
                            "manage":[
                                "k8s-master"
                            ],
                            "storage":[
                                "k8s-master"
                            ]
                        },
                        "zone":1
                    },
                    "devices":[
                        "/dev/vdb"
                    ]
                },
                {
                    "node":{
                        "hostnames":{
                            "manage":[
                                "k8s-node"
                            ],
                            "storage":[
                                "k8s-node"
                            ]
                        },
                        "zone":2
                    },
                    "devices":[
                        "/dev/vdb"
                    ]
                },
                {
                    "node":{
                        "hostnames":{
                            "manage":[
                                "k8s-node2"
                            ],
                            "storage":[
                                "k8s-node2"
                            ]
                        },
                        "zone":3
                    },
                    "devices":[
                        "/dev/vdb"
                    ]
                }
            ]
        }
    ]
}

heketi-cli --user admin --secret admin --server http://k8s-node:18080  topology load --json=/etc/heketi/init.json

# 檢視掛載資訊
heketi-cli --user admin --secret admin --server http://k8s-node:18080  topology info

此時,咱們的Kubernetes就可以基於GlusterFS儲存做應用了。
基於Kubernetes之上的Ceph
Ceph是一個統一的分散式儲存系統,設計初衷是提供較好的效能、可靠性和可擴充套件性。

Ceph專案最早起源於Sage就讀博士期間的工作(最早的成果於2004年發表),並隨後貢獻給開源社群。在經過了數年的發展之後,目前已得到眾多雲計算廠商的支援並被廣泛應用。RedHat、OpenStack及Kubernetes都可與Ceph整合以支援虛擬機器映象的後端儲存。它有高效能、高可用性、高擴充套件性等優秀的特性。

安裝叢集到CentOS上
準備三臺CentOS 7伺服器
IP
內網IP
主機名
備註

10.0.0.50
172.16.1.50
k8s-master
Master

10.0.0.51
172.16.1.51
k8s-node1
Node

10.0.0.52
172.16.1.52
k8s-node2
Node

注:節點解析
3個節點,配置3個OSD,1個mon
每個節點配置2個ceph daemon(OSD和mon)
每個OSD節點1個日誌盤和1個數據盤

設定yum源
vim /etc/yum.repo.d/ceph.repo
[Ceph]
name=Ceph packages for $basearch
baseurl=https://mirrors.aliyun.com/ceph/rpm-mimic/el7/$basearch
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc
priority=1

[Ceph-noarch]
name=Ceph noarch packages
baseurl=https://mirrors.aliyun.com/ceph/rpm-mimic/el7/noarch
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc
priority=1

[ceph-source]
name=Ceph source packages
baseurl=https://mirrors.aliyun.com/ceph/rpm-mimic/el7/SRPMS
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc
priority=1

$ yum clean all && yum makecache fast 
安裝部署工具
# 在Master節點上安裝ceph部署工具
yum install ceph-deploy  python-setuptools -y
# 利用工具初始化叢集
ceph-deploy new k8s-master 
安裝ceph
# 在所有節點上安裝ceph
yum install ceph ceph-radosgw -y
安裝客戶端
yum install ceph-common -y
建立mon監控元件
# 增加網路監控
vim /etc/ceph/ceph.conf
# 增加
public network = 10.0.0.0/24

# 將修改後的配置檔案推送到叢集的其他節點
ceph-deploy admin k8s-master k8s-node1 k8s-node2

# 初始化監控節點
ceph-deploy mon create-initial


# 檢視安裝結果
ceph -s

# 增加硬碟,用於OSD
Fdisk -l

# 使用zap清除磁碟資訊,準備建立OSD
ceph-deploy disk zap k8s-master /dev/sdb 
ceph-deploy disk zap k8s-node1  /dev/sdb 
ceph-deploy disk zap k8s-node2  /dev/sdb 

# 建立OSD
ceph-deploy osd create --data /dev/sdb k8s-master
ceph-deploy osd create --data /dev/sdb k8s-node1
ceph-deploy osd create --data /dev/sdb k8s-node2

# 建立管理元件mgr
ceph-deploy mgr create k8s-master


# 建立物件儲存閘道器
Ceph-deploy rgw create k8s-master


# 同步配置檔案
ceph-deploy admin k8s-master k8s-node1 k8s-node2
# 建立ceph連線閘道器的使用者
radosgw-admin user create --uid="ceph_user_id" --display-name="ceph_user" | egrep 'access_key|secret_key'


# 建立滿足條件的ceph儲存池
#建立pool 
#若少於5個OSD, 設定pg_num為128。 
#5~10個OSD,設定pg_num為512。 
#10~50個OSD,設定pg_num為4096。 
#超過50個OSD,可以參考pgcalc計算。

ceph osd pool create kubernetes-test-pool 128

rbd create -p kubernetes-test-pool -s 5G ceph-image
rbd info ceph-image -p kubernetes-test-pool

# 建立ceph使用者給K8S使用
ceph auth get-or-create client.kube mon 'allow r' osd 'allow class-read object_prefix rbd_children,allow rwx pool=kubernetes-test-pool'



使用ceph做kubernetes底層儲存
建立secret儲存
# 檢視KEY
ceph auth get-key client.admin | base64

# 建立Secret
vim ceph-admin-secret.yaml

kind: SecretapiVersion: v1metadata:  namespace: default  name: ceph-admin-secretdata:  key: QVFDS25VNWVYRmdUSEJBQStxc2RrVEtFUmE3MTRqd0dOc0JKOHc9PQ==type: kubernetes.io/rbd

# 同上
ceph auth get-key client.kube| base64

vim ceph-kube-secret.yaml

kind: SecretapiVersion: v1metadata:  namespace: default  name: ceph-kube-secretdata:  key: QVFDS25VNWVYRmdUSEJBQStxc2RrVEtFUmE3MTRqd0dOc0JKOHc34R==type: kubernetes.io/rbd
建立PV
kind: PersistentVolumeapiVersion: v1metadata:  name: ceph-pvspec:  capacity:    storage: 2Gi  accessModes:    - "ReadWriteMany"    - "ReadWriteOnce"  rbd:    image: ceph-image    monitors:      - k8s-master:6789    pool: kubernetes-test-pool    user: admin    fsType: ext4    readOnly: false    secretRef:      name: ceph-admin-secret  persistentVolumeReclaimPolicy: Retain

建立PVC
kind: PersistentVolumeClaimapiVersion: v1metadata:  name: ceph-pvc  namespace: defaultspec:  accessModes:    - "ReadWriteMany"  resources:    requests:      storage: 5Gi

建立POD
kind: PodapiVersion: v1metadata:  name: ceph-test-pod  namespace: defaultspec:  containers:    - name: ceph-test      image: nginx      volumeMounts:        - mountPath: /usr/share/nginx/html          name: ceph-test-volumes          readOnly: false  volumes:    - name: ceph-test-volumes      persistentVolumeClaim:        claimName: ceph-pvc

設定StorageClass

基於Kubernetes之上的Istio
什麼是Istio

使用雲平臺可以為組織提供豐富的好處。然而,不可否認的是,採用雲可能會給 DevOps 團隊帶來壓力。開發人員必須使用微服務已滿足應用的可移植性,同時運營商管理了極其龐大的混合和多雲部署。Istio 允許您連線、保護、控制和觀測服務。

在較高的層次上,Istio 有助於降低這些部署的複雜性,並減輕開發團隊的壓力。它是一個完全開源的服務網格,可以透明地分層到現有的分散式應用程式上。它也是一個平臺,包括允許它整合到任何日誌記錄平臺、遙測或策略系統的 API。Istio 的多樣化功能集使您能夠成功高效地執行分散式微服務架構,並提供保護、連線和監控微服務的統一方法。

什麼是服務網格
在從單體應用程式向分散式微服務架構的轉型過程中,開發人員和運維人員面臨諸多挑戰,使用 Istio 可以解決這些問題。

服務網格(Service Mesh)這個術語通常用於描述構成這些應用程式的微服務網路以及應用之間的互動。隨著規模和複雜性的增長,服務網格越來越難以理解和管理。它的需求包括服務發現、負載均衡、故障恢復、指標收集和監控以及通常更加複雜的運維需求,例如 A/B 測試、金絲雀釋出、限流、訪問控制和端到端認證等。

Istio 提供了一個完整的解決方案,通過為整個服務網格提供行為洞察和操作控制來滿足微服務應用程式的多樣化需求。

為什麼要使用 Istio?
Istio 提供一種簡單的方式來為已部署的服務建立網路,該網路具有負載均衡、服務間認證、監控等功能,而不需要對服務的程式碼做任何改動。想要讓服務支援 Istio,只需要在您的環境中部署一個特殊的 sidecar 代理,使用 Istio 控制平面功能配置和管理代理,攔截微服務之間的所有網路通訊:

HTTP、gRPC、WebSocket 和 TCP 流量的自動負載均衡。
通過豐富的路由規則、重試、故障轉移和故障注入,可以對流量行為進行細粒度控制。
可插入的策略層和配置 API,支援訪問控制、速率限制和配額。
對出入叢集入口和出口中所有流量的自動度量指標、日誌記錄和跟蹤。
通過強大的基於身份的驗證和授權,在叢集中實現安全的服務間通訊。
Istio 旨在實現可擴充套件性,滿足各種部署需求。


核心功能

Istio 在服務網路中統一提供了許多關鍵功能。

流量管理

通過簡單的規則配置和流量路由,您可以控制服務之間的流量和 API 呼叫。Istio 簡化了斷路器、超時和重試等服務級別屬性的配置,並且可以輕鬆設定 A/B測試、金絲雀部署和基於百分比的流量分割的分階段部署等重要任務。
通過更好地瞭解您的流量和開箱即用的故障恢復功能,您可以在問題出現之前先發現問題,使呼叫更可靠,並且使您的網路更加強大——無論您面臨什麼條件。

安全

Istio 的安全功能使開發人員可以專注於應用程式級別的安全性。Istio 提供底層安全通訊通道,並大規模管理服務通訊的認證、授權和加密。使用Istio,服務通訊在預設情況下是安全的,它允許您跨多種協議和執行時一致地實施策略——所有這些都很少或根本不需要應用程式更改。
雖然 Istio 與平臺無關,但將其與 Kubernetes(或基礎架構)網路策略結合使用,其優勢會更大,包括在網路和應用層保護 pod 間或服務間通訊的能力。

可觀察性

Istio 強大的跟蹤、監控和日誌記錄可讓您深入瞭解服務網格部署。通過 Istio 的監控功能,可以真正瞭解服務效能如何影響上游和下游的功能,而其自定義儀表板可以提供對所有服務效能的可視性,並讓您瞭解該效能如何影響您的其他程序。
Istio 的 Mixer 元件負責策略控制和遙測收集。它提供後端抽象和中介,將 Istio 的其餘部分與各個基礎架構後端的實現細節隔離開來,併為運維提供對網格和基礎架構後端之間所有互動的細粒度控制。
所有這些功能可以讓您可以更有效地設定、監控和實施服務上的 SLO。當然,最重要的是,您可以快速有效地檢測和修復問題。
平臺支援
Istio 是獨立於平臺的,旨在執行在各種環境中,包括跨雲、內部部署、Kubernetes、Mesos 等。您可以在 Kubernetes 上部署 Istio 或具有 Consul 的 Nomad 上部署。Istio 目前支援:
在 Kubernetes 上部署的服務
使用 Consul 註冊的服務
在虛擬機器上部署的服務

整合和定製
策略執行元件可以擴充套件和定製,以便與現有的 ACL、日誌、監控、配額、審計等方案整合。

安裝
# 下載Istio安裝包
curl -L https://istio.io/downloadIstio | sh -

# 安裝Istio
cp istio-1.4.5/bin/istioctl /usr/local/bin/

# 安裝Istio所需的POD
istioctl manifest apply --set profile=demo

# 檢視安裝結果
istioctl version

# 安裝bookinfo
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

# 檢視是否可以正常工作
kubectl exec -it $(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}') -c ratings -- curl productpage:9080
/productpage | grep -o "<title>.*</title>"

# 建立閘道器
kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

# 測試
http://150.109.97.81:31571/productpage

Pilot與服務發現
Pilot是為我們提供配置智慧路由(如A/B測試、金絲雀釋出等)、彈性(超時、重發、熔斷等)等功能的管理系統,它提供了一系列rules api,允許運維人員指定一系列高階的流量管理規則。Pilot負責將我們的配置轉換並寫入到每個sidecar(Enovy)。

Gateway
Mixer
顧名思義,Mixer混合了各種策略以及後端資料採集或遙測系統的介面卡,從而實現了前端Proxy與後端系統的隔離與匯合。Mixer是一個靈活的外掛模型(有沒有發現無論是k8s還是Istio,實現上都很青睞於外掛模型,這是一個很靈活的實現方式),它一端連著Envoy,同時我們可以將日誌、監控、遙測等各種系統“插入”到Mixer的另一端中,從而得到我們想要的資料或結果。

基於Kubernetes之上的Redis叢集
基於Kubernetes之上的MySQL叢集
基於Kubernetes之上的GitLab叢集
基於Kubernetes之上的Jenkins叢集
基於Kubernetes之上的Harbor叢集
基於Kubernetes之上的Prometheus+Grafana叢集
基於Kubernetes之上的ELK叢集
基於Kubernetes之上的大型微服務叢集