ASP.NET Core 藉助 K8S 玩轉容器編排

Production-Grade Container Orchestration - Automated container deployment, scaling, and management.
生產級別的容器編排系統——自動化的容器部署、擴展和管理。html

1. 引言

因爲最近在學習微服務,因此就基於以前docker的基礎上把玩一下k8s(Kubernetes),以瞭解基本概念和核心功能。node

2. What's k8s?

k8s涉及到不少基本概念,能夠看十分鐘帶你理解Kubernetes核心概念快速瞭解。
下面這張圖包含了k8s了核心組成模塊:
K8S clustergit

這裏就簡單羅列如下:github

  1. k8s Master:k8s主節點,主要包括:
  • API Server:提供能夠用來和集羣交互的REST端點。
  • Replication Controller:用來建立和複製Pod。
  1. Node:k8s節點,能夠是虛擬機或物理機。其又包含如下組件:
  • Kubelet:是主節點代理。
  • Kube-proxy:Service使用其將連接路由到Pod,如上文所述。
  • Docker或Rocket:Kubernetes使用的容器技術來建立容器。
  1. Pod:用來託管應用程序實例,包含:
  • Container:運行的容器
  • Volume:共享存儲 (卷)
  • IP Address:IP 地址
  1. Labels:標籤,用於給pod打標籤
  2. Service:服務,由一組相同Label的Pod組成,其用來控制訪問Pods的策略

3. 環境準備

梳理完基本概念,咱們來動手玩一玩吧。有三種玩法:一種就是跟隨k8s官方的在線實驗室進行實操;第二種就是基於Docker For Windows 中集成的k8s進行玩耍;第三種就是安裝MiniKube搗鼓。這裏選擇第二種進行講解。web

PS:不少初學者在環境準備階段遭遇挫折的後就直接放棄了,筆者爲了搭建這個k8s環境也耗費了很多時日,其中包含一次重裝系統,汗!但願下面的步驟助你k8s之行有個好的開端。docker

3.1. 在Docker for Windows中啓用Kubernetes

首先確保你已安裝Docker for Windows。
由於那道牆,在Docker For Windows Client中啓用Kubernetes,並無想象的那麼順利。最後參照這篇文章成功啓用:爲中國用戶在 Docker for Mac/Windows 中開啓 Kubernetes
若是安裝了最新版本的docker for windows 客戶端(v2.0.0.3),可參考如下步驟:shell

  1. 爲 Docker daemon 配置 Docker Hub 的中國官方鏡像加速 https://registry.docker-cn.com
  2. git clone https://github.com/AliyunContainerService/k8s-for-docker-desktop.git
  3. cd k8s-for-docker-desktop
  4. git checkout v2.0.0.2 (這一步很重要!!!)
  5. Powell shell執行./load_images.ps1
  6. Enable Kubernetes
  7. 執行kubectl cluster-info,輸出如下,表示正常啓動。
Kubernetes master is running at https://localhost:6445
KubeDNS is running at https://localhost:6445/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

環境搭建成功,你就成功了一半,請再接再礪動手完成如下實驗!windows

4. 運行第一個Pod

4.1. 建立初始鏡像

1:首先咱們執行dotnet new mvc -n K8s.NET.Demo 建立一個ASP.NET Core Mvc應用K8s.NET.Demo
修改HomeController以下所示:api

public class HomeController : Controller {
    public IActionResult Index () {
        var hostname = Dns.GetHostName ();
        ViewBag.HostName = hostname;
        ViewBag.HostIp = Dns.GetHostAddresses (hostname).FirstOrDefault (ip => ip.AddressFamily == AddressFamily.InterNetwork);
        return View ();
    }
    public IActionResult Privacy () {
        return View ();
    }
    public IActionResult CheckHealth () {
        if (new Random ().Next (100) > 50) {
            return Ok ("OK");
        } else {
            return BadRequest ();
        }
    }
    [ResponseCache (Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error () {
        return View (new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
    }
}

修改Index.cshtml以下:數組

@{
    ViewData["Title"] = "Home Page";
}
<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <h1>Host Name:@ViewBag.HostName</h1>
    <h1>Host IP:@ViewBag.HostIp</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

2:而後添加Dockerfile:

FROM microsoft/dotnet:sdk AS build-env
WORKDIR /app

# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore

# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out

# Build runtime image
FROM microsoft/dotnet:aspnetcore-runtime
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "K8s.NET.Demo.dll"]

3:而後執行docker build -t k8s.net.demo .構造鏡像,構形成功後執行docker images便可查看到名爲k8s.net.demo的鏡像。

4.2. 建立 pod 描述文件

添加k8s-web-pod.yaml文件以下:

apiVersion: v1
kind: Pod # 定義Kubernetes資源的類型爲Pod
metadata:
  name: k8s-net-pod # 定義資源的名稱
  labels: # 爲Pod貼上標籤,後面會介紹其用處
    app: k8s-net-pod
spec: # 定義資源的狀態,對於Pod來講,最重要屬性就是containers
  containers: # containers一個數組類型,若是你但願部署多個容器,能夠添加多項
    - name: web # 定義本Pod中該容器的名稱
      image: k8s.net.demo # 定義Pod啓動的容器鏡像地址
      imagePullPolicy: IfNotPresent # k8s默認值爲Always,老是從遠端拉取鏡像,經過設置IfNotPresent或Never來使用本地鏡像
      ports:
        - containerPort: 80 # 定義容器監聽的端口(與Dockerfile中的EXPOSE相似,只是爲了提供文檔信息)
      livenessProbe: # 存活探針定義
        httpGet:
          path: /Home/CheckHealth # 存活探針請求路徑
          port: 80 #存活探針請求端口

4.3. 使用kubectl create 建立 pod

執行如下命令完成pod的建立:

$ kubectl create -f k8s-web-pod.yaml
pod "k8s-web-pod.yaml" created
$ kubectl get pod
NAME           READY     STATUS    RESTARTS   AGE
k8s-net-pod   1/1       Running   0         1m

4.4. 訪問 pod 中運行的容器

要想與 pod 進行通訊,能夠經過kubectl port-forward配置端口轉發,來完成。

$ kubectl port-forward k8s-net-pod 8090:80
Forwarding from 127.0.0.1:8090 -> 80
Forwarding from [::1]:8090 -> 80

瀏覽器訪問http://localhost:8090/,效果以下圖所示:

至此咱們成功跑起了第一個pod。

這時你可能會問,這和我直接用docker run -d -p 8091:80 k8s.net.demo 運行一個容器有什麼區別呢?並無看到k8s強大在哪裏啊?!
別急,你如今再執行一次kubectl get pod,我來告訴你答案。

$ kubectl get po
NAME           READY     STATUS    RESTARTS   AGE
k8s-net-pod   1/1       Running   17         1h

看到RESTARTS列沒有,它是用來講明pod重啓了多少次。使用docker運行容器,若是容器掛掉,docker是不會負責給你重啓容器的。
而在k8s中,只須要配置存活探針,k8s就會自動探測容器的運行狀態,進行自動重啓。而存活探針僅須要在yaml文件中指定livenessProbe節點便可。(PS:/home/checkhealth 使用隨機數來模擬容器應用運行狀態,當隨機數小於50,就返回BadRequest。)

而這,只是k8s的冰山一角。

5. 運行第一個 Service

Pod運行於集羣內部,雖然使用kubect port-forward能夠映射端口在本機訪問,但對於外部依舊沒法訪問,若是須要暴露供外部直接訪問,則須要建立 service。

5.1. 使用 kubectl expose 建立 service

咱們能夠經過kubectl expose pod直接將當前運行的pod實例暴露出去。

$ kubectl expose pod k8s-net-pod --name k8s-net-service --type=NodePort
service "k8s-net-service" exposed
$ kubectl get service
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
k8s-net-service    NodePort    10.98.62.192    <none>        80:30942/TCP   7m

如上,它有一個CLUSTER-IP爲10.98.62.192,所以咱們能夠在集羣內使用10.98.62.192:80來訪問該服務,若是是在集羣外部,可使用NodeIP:30942(節點所在服務器IP)來訪問。

5.2. 使用 servive 描述文件建立

另一種方式就是建立描述文件來建立了,添加k8s-net-service.yaml文件:

apiVersion: v1
kind: Service # 定義Kubernetes資源的類型爲Service
metadata:
  name: k8s-net-service # 定義資源的名稱
spec:
  selector: # 指定對應的Pod
    app: k8s-net-pod # 指定Pod的標籤爲k8s-net-pod
  ports:
  - protocol: TCP # 協議類型
    port: 80 # 指定Service訪問的端口
    targetPort: 80 # 指定Service轉發請求的端口
    nodePort: 30000
  type: NodePort # 指定Service的類型,在這裏使用NodePort來對外訪問

執行kubectl create -f k8s-net-service.yaml來建立service。

$ kubectl create -f k8s-net-service.yaml
service "k8s-net-service" created
$ kubectl get service
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
k8s-net-service   NodePort    10.98.62.192    <none>        80:30942/TCP   23m
k8s-net-service   NodePort    10.97.110.150   <none>        80:30000/TCP   34s
kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP        1d

6. 試試 k8s 的自由伸縮

是時候來體驗下k8s強大的自動伸縮功能了。k8s中經過建立ReplicaSetDeployment來管理 pod,進而完成自動化擴展和管理。
PS: 也可使用ReplicaController,但推薦使用ReplicaSet,由於其標籤匹配功能更強大。

6.1. 運行第一個 ReplicaSet

首先定義 ReplicaSet 描述文件k8s-net-replicaset.yaml

apiVersion: apps/v1beta2 # rs 的版本號爲apps/v1beta2
kind: ReplicaSet # 定義Kubernetes資源的類型爲ReplicaSet
metadata:
  name: k8s-net-replicaset # 定義資源的名稱
spec:
  replicas: 3 # 指定pod實例的個數
  selector: # pod選擇器
    matchLabels: # 指定匹配的標籤
      app: k8s-net-pod # 指定Pod的標籤爲k8s-net-pod
  template: # 建立新的pod模板配置
    metadata:
      labels:
        app: k8s-net-pod # 指定使用哪一個pod
    spec:
      containers:
      - name: k8s-net-replicaset
        image: k8s.net.demo # 指定使用的鏡像 
        imagePullPolicy: IfNotPresent # k8s默認值爲Always,老是從遠端拉取鏡像,經過設置IfNotPresent或Never來使用本地鏡像

執行如下命令建立 ReplicaSet,並觀察自動建立的pod實例。

$ kubectl create -f k8s-net-replicaset.yaml
replicaset.apps "k8s-net-replicaset" created
$ kubectl get rs
NAME                 DESIRED   CURRENT   READY     AGE
k8s-net-replicaset   3         3         3         8s
$ kubectl get pod
NAME                       READY     STATUS    RESTARTS   AGE
k8s-net-pod                1/1       Running   61         12h
k8s-net-replicaset-bxw9c   1/1       Running   0          35s
k8s-net-replicaset-k6kf7   1/1       Running   0          35s
$ kubectl delete po k8s-net-replicaset-bxw9c
pod "k8s-net-replicaset-bxw9c" deleted
$ kubectl get po
NAME                       READY     STATUS        RESTARTS   AGE
k8s-net-pod                1/1       Running       61         12h
k8s-net-replicaset-bxw9c   0/1       Terminating   0          2m
k8s-net-replicaset-k6kf7   1/1       Running       0          2m
k8s-net-replicaset-xvb9l   1/1       Running       0          6s

從上面看到,k8s-net-replicasetk8s-net-pod爲模板建立了額外兩個pod副本,當咱們嘗試刪除其中一個副本後,再次查看pod列表,replicaset會自動幫咱們從新建立一個pod。
那咱們嘗試把剛建立的k8s-net-replicaset暴露爲Service,看看實際運行是什麼效果吧。依次執行如下命令:

$ kubectl expose replicaset k8s-net-replicaset --type=LoadBalancer --port=8091 --target-port=80 --name k8s-net-rs
-service
service "k8s-net-rs-service" exposed
$ kubectl get service
NAME                 TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
k8s-net-rs-service   LoadBalancer   10.99.134.237   localhost     8091:32641/TCP   8s
k8s-net-service      NodePort       10.104.21.80    <none>        80:30000/TCP     12h
kubernetes           ClusterIP      10.96.0.1       <none>        443/TCP          12h

而後瀏覽器訪問http://localhost:8091/,嘗試屢次刷新瀏覽器,顯示效果以下,咱們發現ReplicaSet已幫咱們作好了負載均衡。
負載均衡效果

假如如今網站訪問量劇增,3個實例任然沒法有效支撐,能夠不中止應用的狀況下作到水平伸縮嗎?Of course, Yes!
僅需執行kubectl scale命令進行擴展便可。

$ kubectl get pod
NAME                       READY     STATUS    RESTARTS   AGE
k8s-net-replicaset-g4n6g   1/1       Running   0          13m
k8s-net-replicaset-lkrf7   1/1       Running   0          13m
k8s-net-replicaset-tf992   1/1       Running   0          13m
$ kubectl scale replicaset k8s-net-replicaset --replicas=6
replicaset.extensions "k8s-net-replicaset" scaled
$ kubectl get pod
NAME                       READY     STATUS              RESTARTS   AGE
k8s-net-replicaset-cz2bs   0/1       ContainerCreating   0          3s
k8s-net-replicaset-g4n6g   1/1       Running             0          13m
k8s-net-replicaset-lkrf7   1/1       Running             0          13m
k8s-net-replicaset-pjl9m   0/1       ContainerCreating   0          3s
k8s-net-replicaset-qpn2l   0/1       ContainerCreating   0          3s
k8s-net-replicaset-tf992   1/1       Running             0          13m

從以上的輸出能夠看,咱們一句命令就擴展pod實例到6個,是否是很簡單?!

你可能又問了,我如今訪問高峯過了,我怎麼快速縮放應用呢?啊,和上面同樣的,你把--replicas參數改小點就是了,就像這樣kubectl scale replicaset k8s-net-replicaset --replicas=3

7. 最後

本文從使用docker建立image,到使用k8s建立第一個pod,到暴露第一個Service,再到使用ReplicaSet 進行容器伸縮,基本串通了k8s的核心基礎概念,從而對k8s有了基礎的認知,但願對你的K8S之路有所幫助。

因爲篇幅有限,筆者也是初玩,k8s的不少功能並未一一羅列,那就留着下次分享吧。
若是要問我,k8s有什麼好書推薦,首推《Kubernetes In Action》,國內已經有中文版了,翻譯的不錯!
本文示例代碼已上傳至GitHub: K8S.NET.Demo

參考資料
雨夜朦朧 - Kubernetes初探[1]:部署你的第一個ASP.NET Core應用到k8s集羣

相關文章
相關標籤/搜索