背景介紹
Kubernetes(簡稱k8s)已成為目前業界容器編排的事實標准,其搭配Docker可建立非常高效便捷的高可擴展、高可用應用服務架構。
Kubernetes的名字來自希臘語,意思是“舵手” 或 “領航員”。K8s是將8個字母“ubernete”替換為“8”的縮寫。
Kubernetes的由來
Kubernetes最初源於谷歌內部的Borg,提供了面向應用的容器集群部署和管理系統。Kubernetes 的目標旨在消除編排物理/虛擬計算,網絡和存儲基礎設施的負擔,並使應用程序運營商和開發人員完全將重點放在以容器為中心的原語上進行自助運營。Kubernetes 也提供穩定、兼容的基礎(平台),用於構建定制化的workflows 和更高級的自動化任務。
Kubernetes具備完善的集群管理能力,包括多層次的安全防護和准入機制、多租戶應用支撐能力、透明的服務注冊和服務發現機制、內建負載均衡器、故障發現和自我修復能力、服務滾動升級和在線擴容、可擴展的資源自動調度機制、多粒度的資源配額管理能力。
Kubernetes還提供完善的管理工具,涵蓋開發、部署測試、運維監控等各個環節。
Borg簡介
Borg是谷歌內部的大規模集群管理系統,負責對谷歌內部很多核心服務的調度和管理。Borg的目的是讓用戶能夠不必操心資源管理的問題,讓他們專注於自己的核心業務,並且做到跨多個數據中心的資源利用率最大化。
Borg主要由BorgMaster、Borglet、borgcfg和Scheduler組成,如下圖所示

- BorgMaster是整個集群的大腦,負責維護整個集群的狀態,並將數據持久化到Paxos存儲中;
- Scheduer負責任務的調度,根據應用的特點將其調度到具體的機器上去;
- Borglet負責真正運行任務(在容器中);
- borgcfg是Borg的命令行工具,用於跟Borg系統交互,一般通過一個配置文件來提交任務。
Kubernetes架構
Kubernetes借鑒了Borg的設計理念,比如Pod、Service、Labels和單Pod單IP等。Kubernetes的整體架構跟Borg非常像,如下圖所示

Kubernetes主要由以下幾個核心組件組成:
etcd保存了整個集群的狀態;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提供GUIFederation提供跨可用區的集群Fluentd-elasticsearch提供集群日志采集、存儲與查詢


Kubernetes設計理念和功能其實就是一個類似Linux的分層架構,如下圖所示

- 核心層:Kubernetes最核心的功能,對外提供API構建高層的應用,對內提供插件式應用執行環境
- 應用層:部署(無狀態應用、有狀態應用、批處理任務、集群應用等)和路由(服務發現、DNS解析等)
- 管理層:系統度量(如基礎設施、容器和網絡的度量),自動化(如自動擴展、動態Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等)
- 接口層:kubectl命令行工具、客戶端SDK以及集群聯邦
- 生態系統:在接口層之上的龐大容器集群管理調度的生態系統,可以划分為兩個范疇
Kubernetes外部:日志、監控、配置管理、CI、CD、Workflow、FaaS、OTS應用、ChatOps等
Kubernetes內部:CRI、CNI、CVI、鏡像倉庫、Cloud Provider、集群自身的配置和管理等
Kubernetes 常用技術概念
業務類型
目前K8s中的業務主要可以分為
| 業務類型 | 技術概念 |
|---|---|
| 長期伺服型(long-running) | Deployment |
| 批處理型(batch) | Job |
| 節點后台支撐型(node-daemon) | DaemonSet |
| 有狀態應用型(stateful application) | PetSet |
Pod(微服務)
Pod是在K8s集群中運行部署應用或服務的最小單元,它是可以支持多容器的。Pod的設計理念是支持多個容器在一個Pod中共享網絡地址和文件系統,可以通過進程間通信和文件共享這種簡單高效的方式組合完成服務。
比如你運行一個操作系統發行版的軟件倉庫,一個Nginx容器用來發布軟件,另一個容器專門用來從源倉庫做同步,這兩個容器的鏡像不太可能是一個團隊開發的,但是他們一塊兒工作才能提供一個微服務;這種情況下,不同的團隊各自開發構建自己的容器鏡像,在部署的時候組合成一個微服務對外提供服務。
Deployment(部署)
以K8s的發展方向,未來對所有長期伺服型的的業務的管理,都會通過Deployment來管理。
部署表示用戶對K8s集群的一次更新操作。部署可以是創建一個新的服務,更新一個新的服務,也可以是滾動升級一個服務。
Service(服務)
服務發現完成的工作,是針對客戶端訪問的服務,找到對應的的后端服務實例。在K8s集群中,客戶端需要訪問的服務就是Service對象。每個Service會對應一個集群內部有效的虛擬IP,集群內部通過虛擬IP訪問一個服務。
在K8s集群中微服務的負載均衡是由Kube-proxy實現的。Kube-proxy是K8s集群內部的負載均衡器。它是一個分布式代理服務器,在K8s的每個節點上都有一個;這一設計體現了它的伸縮性優勢,需要訪問服務的節點越多,提供負載均衡能力的Kube-proxy就越多,高可用節點也隨之增多。
Job(任務)
Job是K8s用來控制批處理型任務的API對象。批處理業務與長期伺服業務的主要區別是批處理業務的運行有頭有尾,而長期伺服業務在用戶不停止的情況下永遠運行。Job管理的Pod根據用戶的設置把任務成功完成就自動退出了。
DaemonSet(后台支撐服務集)
后台支撐型服務的核心關注點在K8s集群中的節點(物理機或虛擬機),要保證每個節點上都有一個此類Pod運行。
典型的后台支撐型服務包括,存儲,日志和監控等在每個節點上支持K8s集群運行的服務。
PetSet(有狀態服務集)
在雲原生應用的體系里,有下面兩組近義詞;第一組是無狀態(stateless)、牲畜(cattle)、無名(nameless)、可丟棄(disposable);第二組是有狀態(stateful)、寵物(pet)、有名(having name)、不可丟棄(non-disposable)。
PetSet是用來控制有狀態服務,PetSet中的每個Pod的名字都是事先確定的,不能更改。適合於PetSet的業務包括數據庫服務MySQL和PostgreSQL,集群化管理服務Zookeeper、etcd等有狀態服務。
Volume(存儲卷)
K8s集群中的存儲卷跟Docker的存儲卷有些類似,只不過Docker的存儲卷作用范圍為一個容器,而K8s的存儲卷的生命周期和作用范圍是一個Pod。每個Pod中聲明的存儲卷由Pod中的所有容器共享。
K8s還支持使用Persistent Volume Claim即PVC這種邏輯存儲,使用這種存儲,使得存儲的使用者可以忽略后台的實際存儲技術(例如AWS,Google或GlusterFS和Ceph),而將有關存儲實際技術的配置交給存儲管理員通過Persistent Volume來配置。
Node(節點)
K8s集群中的計算能力由Node提供,是所有Pod運行所在的工作主機,可以是物理機也可以是虛擬機。不論是物理機還是虛擬機,工作主機的統一特征是上面要運行kubelet管理節點上運行的容器。
假設的前提
- 已經開啟Windows10系統的WSL2,並且把版本2設置為默認。
- 已經安裝並成功運行Docker Desktop For Windows
- 已經設置好Docker Desktop的鏡像加速,比如阿里雲、中科大
- 已經安裝並啟用至少一個Windows10 Linux發行版,比如Ubuntu 20.04
- 已經設置好Linux發行版為Docker運行的默認容器。



接下來,以Docker Desktop版本v2.3.0.4為例,其中自帶的Kubernetes版本為v1.16.5
准備Kubernetes所需的鏡像組合包
由於K8S需要一些鏡像,如果一個個去下載,還是有點麻煩,我們可以借助一個阿里雲現有的項目AliyunContainerService/k8s-for-docker-desktop,一次把所有Image都下載好。
打開PowerShell,執行命令行
git clone git@github.com:AliyunContainerService/k8s-for-docker-desktop.git

git clone完畢之后,切換到它的目錄。
cd .\k8s-for-docker-desktop\

這里需要注意的是,由於不同的Docker Desktop帶的Kubernetes是不一樣的,然后我們需要的鏡像版本應該和自帶的Kubernetes保持一致,舉例,如果你的Docker Desktop關於頁面寫着Kubernetes的版本是v1.16.5的話,我們需要把這個項目切換到對應的分支。

git checkout v1.16.5
官網給的清單如下:
- 如Kubernetes版本為
v1.18.3, 請使用下面命令切換v1.18.3分支git checkout v1.18.3 - 如Kubernetes版本為
v1.16.5, 請使用下面命令切換v1.16.5分支git checkout v1.16.5 - 如Kubernetes版本為
v1.15.5, 請使用下面命令切換v1.15.5分支git checkout v1.15.5 - 如Kubernetes版本為
v1.15.4, 請使用下面命令切換v1.15.4分支git checkout v1.15.4 - 如Kubernetes版本為
v1.14.8, 請使用下面命令切換v1.14.8分支git checkout v1.14.8 - 如Kubernetes版本為
v1.14.7, 請使用下面命令切換v1.14.7分支git checkout v1.14.7 - 如Kubernetes版本為
v1.14.6, 請使用下面命令切換v1.14.6分支git checkout v1.14.6 - 如Kubernetes版本為
v1.14.3, 請使用下面命令切換v1.14.3分支git checkout v1.14.3 - 如Kubernetes版本為
v1.14.1, 請使用下面命令切換v1.14.1分支git checkout v1.14.1 - 如Kubernetes版本為
v1.13.0, 請使用下面命令切換v1.13.0分支git checkout v1.13.0 - 如Kubernetes版本為
v1.10.11, 請使用下面命令切換v1.10.11分支git checkout v1.10.11

接下來,就可以執行其中的ps腳本:load_images.ps1
.\load_images.ps1
其中PS腳本內容如下:
foreach($line in Get-Content .\images.properties) {
$data = $line.Split('=')
$key = $data[0];
$value = $data[1];
Write-Output "$key=$value"
docker pull ${value}
docker tag ${value} ${key}
docker rmi ${value}
}
而images.properties文件中,包括哪些鏡像呢?
k8s.gcr.io/pause:3.1=registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1
k8s.gcr.io/kube-controller-manager:v1.16.5=registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.16.5
k8s.gcr.io/kube-scheduler:v1.16.5=registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.16.5
k8s.gcr.io/kube-proxy:v1.16.5=registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.16.5
k8s.gcr.io/kube-apiserver:v1.16.5=registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.16.5
k8s.gcr.io/etcd:3.3.15-0=registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.3.15-0
k8s.gcr.io/coredns:1.6.2=registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:1.6.2
quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1=registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:0.26.1
所以我們能看出,阿里雲這個批處理呢,實際上就是配置走阿里雲的鏡像中心來加速相關Image的下載速度。
不小心,可能就會遇到一個權限坑

不要慌,這是要允許Powershell來執行未知腳本。
請在開始菜單圖標上右鍵,選Windows PowerShell管理員模式運行。

根據提示執行命令即可
Set-ExecutionPolicy RemoteSigned

然后記得重啟一次PowerShell使其生效。
好了,解除權限限制之后,便可以開始繼續上訴腳本執行了。

如果一切順利,不要多久你可以看到所有的鏡像都拉取到本地了。
我們可以簡單查驗下
docker images

本地HOST環境設置
為了避免遇到一些奇怪的問題,我們先需要給本地的Host文件添加一些項。
# Kubernetes Start
127.0.0.1 kubernetes.docker.internal
# Kubernetes End
# GitHub Start
52.74.223.119 github.com
192.30.253.119 gist.github.com
54.169.195.247 api.github.com
185.199.111.153 assets-cdn.github.com
151.101.76.133 raw.githubusercontent.com
151.101.108.133 user-images.githubusercontent.com
151.101.76.133 gist.githubusercontent.com
151.101.76.133 cloud.githubusercontent.com
151.101.76.133 camo.githubusercontent.com
151.101.76.133 avatars0.githubusercontent.com
151.101.76.133 avatars1.githubusercontent.com
151.101.76.133 avatars2.githubusercontent.com
151.101.76.133 avatars3.githubusercontent.com
151.101.76.133 avatars4.githubusercontent.com
151.101.76.133 avatars5.githubusercontent.com
151.101.76.133 avatars6.githubusercontent.com
151.101.76.133 avatars7.githubusercontent.com
151.101.76.133 avatars8.githubusercontent.com
# GitHub End

- 其中
kubernetes.docker.internal指向127.0.0.1可避免遇到kubernetes.docker.internal: no such host的問題。 - 其中
Github相關的Host呢,是避免遇到raw.githubusercontent.com 找不到host的問題。

另外需要注意,Docker Desktop For Windows 10的日志這個路徑,如果需要可以查看
C:\ProgramData\DockerDesktop\service.txt

另外,Docker Desktop For Windows 10生成的Kubernetes的配置文件路徑位置在
C:\Users\UserName\.kube
開啟並安裝Docker Desktop版Kubernetes
事實上,Docker Desktop自帶了一個單節點版本的Kubernetes,我們執行下命令就會發現了
kubectl version

但是,其實也看到,Kubernetes服務並沒有起來,好了,我們接下來啟用並安裝自帶的K8S吧。

在桌面右下角,右鍵打開你的Docker Desktop小圖標,進入Settings頁面的最后一項Kubernetes

請勾選其中的Enable Kubernetes項,然后點擊Apply & Restart即可開始安裝。
如果你想通過Docker命令行查看Kubernetes內部的容器資源,那么可以多勾選一個
Show System Containers(Advanced),默認不建議勾選,以免它產生的實例形成干擾信息。


這里需要等待一段時間,這期間,如果你勾選了Show System Containers(Advanced),希望檢查是否成功執行,可以通過以下命令來查看實例創建情況
docker ps
你會發現,已經在開始陸續創建了所需的Docker實例了。
如果不出意外的話,最終你會看到Kubernetes Running的勝果。
理論上,只要上訴步驟你都操作了,按理沒有坑了,如果你漏了,極有可能會在這里等待很久哦。
如果你沒有勾選Show System Containers(Advanced),你會看到一個干凈的Docker Desktop Dashboard面板,里面不會看到Kubernetes相關的容器,如果你勾選了,會新增一堆實例。

底部狀態的Kubernetes Running就代表我們已經安裝並啟用成功了。

接下來,驗證下集群的狀態,執行命令
kubectl cluster-info

查看的node節點
kubectl get nodes

如果以上命令都成功執行,如圖輸出,說明已經運轉正常了。
配置Kubernetes控制台(Dashboard)面板
如果你設置了前面的host文件呢,這時候其中raw.githubusercontent.com的配置就馬上要起到作用了,不然接下來這個命令就慘了。

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc5/aio/deploy/recommended.yaml

這一步會安裝並設置Kubernetes的Dashboard面板。
接下來,我們檢查下Kubernetes的Dashboard面板的Pod狀態。
kubectl get pod -n kubernetes-dashboard
剛開始你會發現,都是0/1,這說明還需要在等等,實際上后台正在部署kubernetes-dashboard所需的Pod。
多執行幾次上訴命令,接下來你會看到每一個Pod都開始變成1/1,這時候說明Dashboard面板實例已經就位了,等待我們訪問。

生成可登錄Kubernetes Dashboard的Token
先執行如下命令,生成Token
$TOKEN=((kubectl -n kube-system describe secret default | Select-String "token:") -split " +")[1]

然后把這個Token,寫入Kubernetes的上下文docker-desktop中,以便下一步登錄使用
kubectl config set-credentials docker-desktop --token="${TOKEN}"

我們把這個Token,在終端中打印出來,並且復制保存哈。。
echo $TOKEN

接着,我們開啟API Server訪問代理
kubectl proxy

然后,我們就可以使用Url來訪問可視化界面了。

打開后,我們選擇Token的登錄方式,輸入我們得到的Token信息。

點擊登錄,即可進入傳說中的Dashboard


恭喜你,開啟成功!
實戰Kubernetes高可用架構
- 創建一個
.Net Core 3.1的API實例項目DeployToK8s



- 准備演示應用的領域模型
/// <summary>
/// 用戶表
/// </summary>
public class User
{
/// <summary>
/// Id
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
/// <summary>
/// 用戶名
/// </summary>
public string Name { get; set; }
}
建立一個領域叫User,設置其Id作為主鍵,並且自增。
- 添加
MYSQL-EFCore相關的支持包到項目中
- Pomelo.EntityFrameworkCore.MySql
- Microsoft.EntityFrameworkCore.Proxies


並設置好MYSQL的連接字符串
server=localhost;Database=deployk8s;user id=root;password=yourrootpassword;CharSet=utf8mb4;port=3307;AllowZeroDatetime=True;ConvertZeroDatetime=True;Pooling=true;Max Pool Size=32767;Allow User Variables=True;AllowLoadLocalInfile=true;SslMode=none
為了讓EF在啟動時自動根據我們的Domain創建表,我們需要加上
# public void ConfigureServices(IServiceCollection services)
// 添加Mysql數據上下文
services.AddTransient<DataContext>();
# public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
// 如果不遷移數據,只是需要在首次運行時,完全按照在上下文類中的DbContext模型來創建數據庫,則可以使用
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetRequiredService<DataContext>();
context.Database.EnsureCreated();
}
// 如果已經創建了遷移,則可以在Startup.cs中執行它們,如下所示
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetRequiredService<DataContext>();
context.Database.Migrate();
}
往數據中插入一些模擬數據,創建好默認Controller,然后並設置項目啟動目錄指向它。

- 給項目添加Docker支持
在項目上右鍵,添加 -> Docker支持 -> Linux模式 -> 確認

它將在項目中新建一個DockerFile,這個是后續生成Docker鏡像的配置文件。
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["DeployToK8s.csproj", ""]
RUN dotnet restore "./DeployToK8s.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "DeployToK8s.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "DeployToK8s.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "DeployToK8s.dll"]
- 構建Docker 鏡像
直接在項目的這個DockerFile文件上右鍵,有一個選項是生成Docker鏡像,點擊即可。


它將開始執行構建,一共有18步驟,很快就能完成。


