《kubernetes + .net core 》dev ops部分

目錄

1.kubernetes 預備知識

kubernetes是一個用go語言寫的容器編排框架,常與docker搭配使用。
kubernetes是谷歌內部的容器編排框架的開源實現。能夠用來方便的管理容器集羣。具備不少 優勢,要了解這些優勢,須要先來了解一下kubernetes中的集羣資源。這裏指的是kubernetes裏的原生的資源,kubernetes也支持自定義的資源。html

1.1 集羣資源

不區分名稱空間前端

  • cluster role
  • namespace
  • node
  • persistent volume
  • storage class

1.1.1 role

  • 普通角色 role
  • 集羣角色 culster role

羣集默認採用RBAC(role base access control)進行集羣資源的訪問控制,能夠將角色做用於用戶user服務service,限制它們訪問羣集資源的權限範圍。node

其中角色又區分爲 集羣角色 cluster role普通的角色 role ,他們的區別是能夠做用的範圍不一致。mysql

普通角色必有名稱空間限制,只能做用於與它同名稱空間的用戶或服務。linux

集羣角色沒有名稱空間限制,能夠用於全部名稱空間的用戶或服務。下面的目錄中會詳細介紹。先在這裏提一下nginx

1.1.2 namespace

不指定時默認做用default名稱空間,服務在跨名稱空間訪問其餘服務時 域名須要加上名稱空間後輟才能訪問git

1.1.3 node

  • 主節點 master
  • 工做節點 none
  • 邊緣節點 none

是一個包含操做系統的機器,操做系統能夠是Linux也能夠是windwos,能夠是實體機也能夠是虛擬機,其中的區別下面的其餘目錄會詳細說明程序員

1.1.4 persistent volume

持久卷 ,支持的類型不少,包括谷歌 亞馬遜 阿里云云服務提供商的各類存儲.github

因爲咱們的項目通常是用於局域網內的,因此這裏我着重介紹nfs(network file system)web

1.1.5 storage class

存儲類,用於根據pvc 自動建立/自動掛載/自動回收 對應的nfs目錄前

1.2 工做量資源 (消耗cpu ram)

  • pod
  • job
  • cron job
  • replica set
  • deployment
  • daemon set
  • statefull set

1.2.1 pod

工做量的最小單位是pod 其餘的類型的工做量都是控制Pod的。

pod至關於docker 中的docker composite,能夠由單個或多個容器組成,每一個pod有本身的docker網絡,pod裏的container處於同個局域網中。
其餘的控制器都有一個pod template,用於建立Pod

1.2.2 job

工做,一但應用到集羣將會建立一個pod作一些工做,具體的工做內容由Pod的實現決定,工做完成後Pod自動終結。

1.2.3 cron job

定時工做任務,一但應用到集羣,集羣將會定時建立pod 作一些工做,工做完成後pod自動終結

1.2.4 replica set

複製集或稱爲副本集,一但應用到集羣,會建立相n個相同的 pod,而且會維護這個pod的數量,若是有pod異常終結,replica set會建立一個新的Pod 以維護用戶指定的數量

1.2.5 deplyoment

deplyoment經常使用來建立無狀態的應用集羣。

部署,deplyoment依賴於replicaset ,它支持滾動更新,滾動更新的原理是,在原有的一個replica set的基礎上建立一個新版本的replica set ,

舊版本的replicaset 逐個減小 ,新版本的replicaset逐個新增, 能夠設置一個參數指定滾動更新時要保持的最小可用pod數量。

1.2.6 daemon set

守護進程集 ,顧名思義,他的做用就是維護某個操做系統(node)的某個進程(pod)始終工做。當一個dameon set被應用到k8s集羣,全部它指定的節點上都會建立某個pod
好比日誌採集器 一個節點上有一個,用daemon set就十分應景。

1.2.7 stateful set

stateful set經常使用於建立有狀態的服務集羣,它具備如下特色

  • 穩定的惟一網絡標識符
  • 穩定,持久的存儲
  • 有序,順暢的部署和擴展
  • 有序的自動滾動更新

舉個例子,你有一個容器須要存數據,好比mysql容器,這時你用deplyoment就不合適,由於多個mysql實例各自應該有本身的存儲,DNS名稱。
這個時候就應該使用statefull set。它原理是建立無頭服務(沒有集羣ip的服務)和有序號的Pod,並把這個無頭服務的域名+有序號的主機名(pod名稱),得到惟一的DNS名稱
好比設置stateful set的名稱爲web,redplica=2,則會有序的建立兩個Pod:web-0 web-1,當web-0就緒後纔會建立web-1,若是是擴容時也是這樣的,而收容的時候順序而是反過來的,會從序號大的Pod開始刪除多餘的Pod

若是把一個名稱爲nginx的無頭服務指向這個statufulset,則web-0的dns名稱應該爲 web-0.nginx

而且 stateful set會爲這兩個Pod建立各自的pvc,因爲pod的名稱是惟一的,因此故障重建Pod時,能夠把新的Pod關聯到原有的存儲捲上

1.3 存儲和配置資源 (消耗存儲)

  • config map
  • secret map
  • persistent volume claim

1.3.1 config map

ConfigMap 容許你將配置文件與鏡像文件分離,以使容器化的應用程序具備可移植性。

  • 如何建立
  • 如何使用
    建立:
# 從文件夾建立(文件夾裏的文本文件將會被建立成config map
  kubectl create configmap my-config --from-file=path/to/bar

  # 從文件建立
  kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt

  # 從字符串建立
  kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2

  # 從鍵值文本建立 
  kubectl create configmap my-config --from-file=path/to/bar

  # 從env文件建立
  kubectl create configmap my-config --from-env-file=path/to/bar.env

使用:

  • 做爲pod的環境變量
  • 做爲存儲卷掛載到Pod

1.3.2 secrets

Secret 是一種包含少許敏感信息例如密碼、令牌或密鑰的對象。 這樣的信息可能會被放在 Pod 規約中或者鏡像中。 用戶能夠建立 Secret,同時系統也建立了一些 Secret。

1.3.3 pvc

  • 由集羣管理員管理
  • 由storage class管理

若是由集羣管理員管理,由開發人員應向集羣管理員申請Pv ,集羣管理員要手動的建立Pv,pvc,把pvc給開發人員,開發人員手動掛載pvc到pod

若是由storage class管理,則集羣管理員只要建立一個provider, 以後provider會自動監視集羣中的Pvc 和pod ,自動建立pv和掛載

2.kubernetes node 組件

kubernetes集羣中,至少要有一個主節點,主節點應該有如下組件

  • kubelet
  • kuber-proxy
  • cni網絡插件
  • etcd
  • kube-apiserver
  • coreDNS
  • kube-controller-manager
  • kube-schedule

普通節點的組件

  • kubelet

  • kube-proxy

  • cni網絡插件

2.1 kubectl

kubernetes節點代理組件,每一個node上都須要有,他不是Kubernetes建立的容器,因此在集羣中查不到。
他的主要作的工做是

1.向kube api以hostname爲名註冊節點
2.監視pod運行參數,使Pod以參數預期的狀態運行,因此Pod有異常一般都能查詢Kubelet的日誌來排查錯誤

2.2 kube-proxy

kubernetes 的服務相關的組件,每一個Node上都須要有,除了無頭服務,其餘全部服務都由他處理流量

k8s集羣在初始化的時候,會指定服務網段和pod網段,其中服務網段的ip都是虛擬Ip

他主要作的工做是:

監視集羣的服務,若是服務知足某些條件,則經過ipvs 把這個服務的流量轉發到各個後端Pod (cluster ip)

2.3 cni網絡插件

cni: container network interface

k8s稱之爲窗口編排集羣,他的核心思想是把不一樣的容器網絡聯合起來,使全部的pod都在一個扁平的網絡裏,能夠互換訪問

爲達這個目的就須要容器網絡插件,下面介紹一下主流的cni插件,並大體說明一下優劣

  • flannel
  • calico
  • 其餘

2.3.1 flannel

Flannel
Flannel

flanel是橋接模式的表明插件,
他的工做原理是用daemonset在每一個節點上部署flannel插件,插件設置容器網絡並把容器網絡信息經過 kube api存儲到etcd中 。

這樣就確保不會重複註冊網段了,與不一樣node上的Pod經過 kube-proxy打包 發給其餘Node的kuber proxy,kuber proxy再拆包,發給pod
以達到跨node的扁平網絡訪問. 這種方式也稱vxlan 或overlay
優勢:

  • 網絡協議簡單,容易分析。
  • 社區規模比較大,成功案例比較多,資料比較全面,入門比較簡單

缺點:

  • 因爲有打包 拆包, 因此通信效率比較低下
  • 不支持網絡策略

2.3.2 calico

calico 是網關模式的表明插件。 它主要由如下幾部分構成
它基於邊界網關協議 BGP(border gateway protocol)
他的工做原理是用daemonset在每一個節點上部署calico node, 來構成扁平化容器網絡
calico node由如下幾個組件

  • felix
  • confid
  • BIRD(BGP Internet route daemon)

felix 負責編寫路由和訪問控制列表

confid 用於把 felix生成的數據記錄到etcd,用於持久化規則

BIRD 用於廣播felix寫到系統內核的路由規則和訪問控制列表acl和calico的網絡

當集羣規模比較大的時候還能夠可選的安裝 BGP Rotue Reflector(BIRD) 和 Typha

前者用於快速廣播協議,後者用於直接與ETCD通信,減少 kubeapi的壓力

優勢:

  • pod跨node的網絡流量 直接進系統內核 走路由表,效率極高
  • 支持網絡策略

缺點:

  • 跨node的數據包通過DNAT和SNAT後,分析網絡封包會比較複雜
  • 部署也比較複雜

Calico
Calico

2.4 etcd

etc distributed ,一款使用go語言編寫的基於raft共識算法的分佈式KV緩存框架 ,
不像redis重性能,而像zookeeper 同樣重數據一致性
特色是有較高的寫入性能
Etcd Disk

Etcd Network

Etcd Cpu

Etcd Memory

Etcd Throughput

Etcd Latency Distribution

2.5 kube-apiserver

k8s 暴露給外部的web api,用於集羣的交互 有各類語言的api client開源項目 ,程序員也能夠在程序中引用,監視一些集羣資源

2.6 coreDNS

用於集羣中的service 和 pod的域名解析,

也能夠配置對集羣外的域名應該用哪一個DNS解析

2.7 kube-controller-manager

用於 各類控制器(消耗cpu ram)的管理

2.8 kube-schedule

用於 管理控制 Pod調度相關

3.集羣的高可用

  • 分佈式共識算法 Raft
  • keepalived
  • haproxy

File

3.1 etcd的raft算法

raft

raft是etcd的共識算法,kubernetes用etcd來存儲集羣的配置。config map /secret都是基於etcd。

理解raft共識算法能夠知道

  • 爲何高可用集羣主節點是3個 5個 7個 而不是 2個 4個 6個
  • kubernetes的主節點發生單點故障的時候, 存儲的行爲會有什麼改變

3.2 keepalived

在高可用環境, keepalived用於虛擬ip的選舉,一旦持有虛擬Ip的節點發生故障,其餘的主節點會選擇出新的主節點持有虛擬ip。而且能夠配置smtp信息,當節點故障的時候發郵件通知相關的責任人

onfiguration File for keepalived

global_defs {
   notification_email {
        kok.bing@qq.com
   }
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id LVS_1
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    lvs_sync_daemon_inteface eth0
    virtual_router_id 79
    advert_int 1
    priority 100                    #權重 m1 100  m2 90  m3 80
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
      192.168.44.200/24 dev eth0 
    }
}

3.3 haproxy

每一個主節點都部署了haproxy代理kube api端口, 因此當它持有虛擬ip的時候,會把全部對kube api的請求負載均衡到全部的主節點上。

global
        chroot  /var/lib/haproxy
        daemon
        group haproxy
        user haproxy
        log 127.0.0.1:514 local0 warning
        pidfile /var/lib/haproxy.pid
        maxconn 20000
        spread-checks 3
        nbproc 8

defaults
        log     global
        mode    tcp
        retries 3
        option redispatch

listen https-apiserver
        bind *:8443
        mode tcp
        balance roundrobin
        timeout server 900s
        timeout connect 15s

        server m1  192.168.44.201:6443 check port 6443 inter 5000 fall 5
        server m2  192.168.44.202:6443 check port 6443 inter 5000 fall 5
        server m3  192.168.44.203:6443 check port 6443 inter 5000 fall 5

4 認證/受權

4.1 authentication

kubernetes集羣中的認證對象分爲

  1. 用戶
  2. 服務

除此以外,還有一些其餘的非kubernetes集羣管理的服務會須要訪問集羣資源的狀況

可是這個暫時不實踐,由於haoyun目前不會使用到這種狀況

4.1.1 用戶

用戶不是kuebrnetes 的資源,因此單獨拎出來說。

4.1.1.1 查看用戶

master node在加入集羣時,會提示咱們手動複製默認的管理員用戶到 $HOME/.kube 文件夾

因此查看 $HOME/.kube/config文件能夠知道集羣 用戶 認證上下文

[root@www .kube]# cat config
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZ...#略 
    server: https://www.haoyun.vip:8443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDR...#略
    client-key-data: LS0tLS1CRUdJTiBS..#.略

4.1.1.2 新增用戶

建立1用戶,並進入用戶文件夾

[root@www .kube]# useradd hbb && cd /home/hbb

建立私鑰

[root@www hbb]# openssl genrsa -out hbb_privateKey.key 2048
Generating RSA private key, 2048 bit long modulus
............................................................+++
.............................................................................................................................................................+++
e is 65537 (0x10001)

建立x.509證書籤名請求 (CSR) ,CN會被識別爲用戶名 O會被識別爲組

openssl req -new -key hbb_privateKey.key \
-out hbb.csr \
-subj "/CN=hbb/O=hbbGroup1/O=hbbGroup2"
#O能夠省略也能夠寫多個

爲CSR簽入kubernetes 的證書和證書公鑰,生成證書

openssl x509 -req -in hbb.csr \
-CA /etc/kubernetes/pki/ca.crt \
-CAkey /etc/kubernetes/pki/ca.key \
-CAcreateserial \
-out hbb.crt -days 50000 
#證書有效天數 50000天

建立證書目錄 ,存入把公鑰(hbb.crt)和私鑰(hbb_private.key) 放進去

[root@www hbb]# mkdir .certs
[root@www hbb]# cd .certs/
[root@www .certs]# mv ../hbb_privateKey.key ../hbb.crt .
[root@www .certs]# ll
total 8
-rw-r--r--. 1 root root  940 Sep  5 15:41 hbb.csr
-rw-r--r--. 1 root root 1679 Sep  5 15:29 hbb_privateKey.key

建立集羣用戶

kubectl config set-credentials hbb \
--client-certificate=/home/hbb/.certs/hbb.crt \
--client-key=/home/hbb/.certs/jean_privatekey.key

建立用戶上下文

kubectl config set-context hbb-context \
--cluster=kubernetes --user=hbb

這時原有的config文件裏就多了Hbb這個用戶了

users:
- name: hbb
  user:
    client-certificate: /home/hbb/.certs/hbb.crt
    client-key: /home/hbb/.certs/hbb_privatekey.key

複製 原有的.kube/config文件到hbb用戶文件夾 ,在副本上刪除kubernetes-admin的上下文和用戶信息。

[root@www .kube]# mkdir /home/hbb/.kube
[root@www .kube]# cp config /home/hbb/.kube/
[root@www .kube]# cd /home/hbb/.kube
[root@www .kube]# vim config
[root@www .kube]# cat config
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJU...#略
    server: https://www.haoyun.vip:8443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: hbb
  name: hbb-context
current-context: hbb-context
kind: Config
preferences: {}
users:
- name: hbb
  user:
    client-certificate: /home/hbb/.certs/hbb.crt
    client-key: /home/hbb/.certs/hbb_privateKey.key

以後把hbb用戶文件夾受權給hbb用戶

[root@www .kube]# chown -R hbb:  /home/hbb/
#-R 遞歸文件夾 
#hbb: 只設置了用戶沒有設置組

這樣就建立一個用戶了,此時退出root用戶,使用hbb用戶登上去,默認就是使用Hbb的user去訪問集羣

可是這時尚未給hbb受權,因此基本上什麼操做都執行不了,由於沒有權限

[hbb@www .certs]$ kubectl get pod -A
Error from server (Forbidden): pods is forbidden: User "hbb" cannot list resource "pods" in API group "" at the cluster scope

4.2 authorization (RBAC)

官方文檔

相關的kubernetes 資源

  • namespace
  • roles/clusterRoles
  • rolebindings/clusterRolebindings

4.2.1 namespace

roles和rolebindings 若是要創建關聯,他們必須是同一個名稱空間內。

clusterRoles和clusterRolebindings 沒有名稱空間的限制,它們的規則做用於集羣範圍

4.2.2 roles/clusterRoles

在 RBAC API 中,一個角色包含一組相關權限的規則。權限是純粹累加的(不存在拒絕某操做的規則)。 角色能夠用 Role 來定義到某個命名空間上, 或者用 ClusterRole 來定義到整個集羣做用域。

一個 Role 只能夠用來對某一命名空間中的資源賦予訪問權限。 下面的 Role 示例定義到名稱爲 "default" 的命名空間,能夠用來授予對該命名空間中的 Pods 的讀取權限:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default #若是是clusterRoles 則沒刪除這一行
  name: pod-reader
rules:
- apiGroups: [""] # "" 指定 API 組
  resources: ["pods","pods/log"] #子資源
  verbs: ["get", "watch", "list"]
  
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["my-configmap"] #具體名稱的資源
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  
#下面這個只有clusterRoles 纔可使用
- nonResourceURLs: ["/healthz", "/healthz/*"] # '*' 在 nonResourceURL 中的意思是後綴全局匹配。
  verbs: ["get", "post"]

clusterRoles比 roles 多出如下的能力

  • 集羣範圍資源 (好比 nodes)
  • 非資源端點(好比 "/healthz")
  • 跨命名空間訪問的有名字空間做用域的資源(如 get pods --all-namespaces)

4.2.3 rolebindings/clusterRolebindings

rolebindings也可使用ClusterRoles,會將裏面的資源的做用域限定到rolebindings的名稱空間範圍內。

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
  
subjects:
- kind: User #用戶
  name: jane
  apiGroup: rbac.authorization.k8s.io
- kind: Group #用戶組
  name: "frontend-admins"
  apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount #服務賬戶
  name: default
  namespace: kube-system
- kind: Group # 全部myNamespace名稱空間下的服務
  name: system:serviceaccounts:myNamespace
  apiGroup: rbac.authorization.k8s.io
- kind: Group #全部名稱空間的全部服務
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io
- kind: Group #全部認證過的用戶
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
- kind: Group #全部未誰的用戶
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
  
  
roleRef:
  kind: Role #能夠是集羣名角或普通的角色
  name: pod-reader 
  apiGroup: rbac.authorization.k8s.io

5.服務

官方文檔
服務是微服務抽象,經常使用於經過選擇符訪問一組Pod, 也能夠訪問其餘對象,

  • 集羣外部的服務
  • 其餘名稱空間的服務

上面這兩種狀況,服務的選擇符能夠省略。

5.1 代理模式

kubernetes v1.0時使用用戶空間代理 (userspace)

v1.1添加了iptable代理模式,

v1.2默認使用iptables代理模式

v1.11添加ipvs 模式
當Node上不支持ipvs會回退使用iptables模式

5.1.1 userSpace模式

Userspace

每一個結點上部署kube-proxy ,它會監視主結點的apiserver對service 和 endpoints的增刪改

爲每一個server隨機開一個端口,並寫入集羣Ip寫入iptables,把對集羣服務的集羣Ip的流量 轉發到這個隨機的端口上 ,

而後再轉發到後端的Pod上, 通常是採用輪詢的規則,根據服務上的sessionAnfinity來設置鏈接的親和性

5.1.2 iptables模式

Iptables

與userspace的區別是 不只把service寫入Iptables,同時把endpoints也寫入了iptables,
因此不用在內核空間和用戶空間之間來回切換,性能提高

5.1.3 ipvs

Ipvs

ipvs(ip virtrual server)和iptables都是基於netfilter ,但ipvs以哈希表作爲基礎數據結構,並工做在內核空間
相比iptables,因此他有更好的性能,也支持更多的負載均衡算法

  • rr: round-robin 輪詢
  • lc: least connection (smallest number of open connections) 最少鏈接
  • dh: destination hashing 目標哈希
  • sh: source hashing 源哈希
  • sed: shortest expected delay 最低延遲
  • nq: never queue 不排隊

若是須要粘性會話,能夠在服務中設置
service.spec.sessionAffinity 爲 clusterip ,默認是none
service.spec.sessionAffinityConfig.clientIP.timeoutSeconds 能夠調整會話最長時間,默認是10800秒

5.2 服務發現

服務能夠經過環境變量和DNS的方式來發現服務,推薦的作法是經過DNS

5.2.1 經過環境變量

一個名稱爲 "redis-master" 的 Service 暴露了 TCP 端口 6379, 同時給它分配了 Cluster IP 地址 10.0.0.11 ,
這個 Service 生成了以下環境變量:

REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

若是須要在pod中使用這些環境變量,須要在啓動pod以前先啓動服務。

5.2.2 經過DNS

服務直接用服務名稱爲域名,
Pod會在服務以前加上ip爲域名
例如在名稱空間 hbb下有服務 hbb-api, 服務指向的後端pod Ip地址是10-244-6-27.,則會有dns記錄

# 服務
hscadaexapi.hbb.svc.cluster.local
# pod 
10-244-6-27.hscadaexapi.hbb.svc.cluster.local

dns應該儘量使用,最好不要使用環境變量的方式

5.3 服務類型

  • clusterip 集羣IP
  • nodeport 結點IP
  • loadbalance 外部負載均衡器
  • external ip 外部IP
  • none 無頭服務(有狀態服務)
  • externalname 外部服務

5.3.1 clusterip 集羣IP

虛擬的ip ,一般指向一組pod (真實ip)

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

5.3.2 nodeport 結點IP

每一個主結點上的具體端口,一般把 node ip+端口 轉發到 一組pod(真實ip)

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app: MyApp
  ports:
      # 默認狀況下,爲了方便起見,`targetPort` 被設置爲與 `port` 字段相同的值。
    - port: 80
      targetPort: 80
      # 可選字段
      # 默認狀況下,爲了方便起見,Kubernetes 控制平面會從某個範圍內分配一個端口號(默認:30000-32767)
      nodePort: 30007

5.3.3 loadbalance 外部負載均衡器

一般把 外部流量 轉發到 一組pod(真實ip) ,外部ip通常是在雙網卡的邊緣節點上

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  loadBalancerIP: 78.11.24.19
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
      - ip: 146.148.47.155

5.3.4 external IP

將外部的流量引入服務 ,這種外部Ip 不禁集羣管理,由由集羣管理員維護

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 9376
  externalIPs:
  - 80.11.12.10

5.3.5 none 無頭服務(有狀態服務)

kube-proxy組件不對無頭服務進行代理,無頭服務 加上序號 指向 Pod,固定搭配,

因此即便 服務的pod掛了, 重啓來的服務的 域名也不會換一個,用於有狀態的服務。

後面講到Pod控制器statefulset會再細講

5.3.6 externalname 外部服務

kube-proxy組件不會對外部服務進行代理則是映射到dns 用於描述一個集羣外部的服務,有解耦合的做用,

因此它和無頭服務同樣沒有選擇器,他也不禁集羣管理,而是由集羣管理員維護

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

5.4 集羣入口 ingress

因爲iptables代理模塊或亦 ipvs代理模式都是4層負載均衡,沒法對7層協議進行負載均衡,因此對於外部的流量 ,常使用入口資源來進行負載均衡,把外部的流量均衡到服務上

  • ingress contorller
  • ingress 資源

5.4.1 ingress controller

ingress controller 是負載均衡器實例,一個集羣中能夠部署多個, 每一個又能夠自爲一個負載均衡集羣

在建立ingress資源的時候,能夠用 註解Annotations:來指定要使用哪一個ingress controller

kubernetes.io/ingress.class: nginx

這個nginx是controller容器啓動時 用命令行的方式指定的

Args:
      /nginx-ingress-controller
      --default-backend-service=kube-system/my-nginx-ingress-default-backend
      --election-id=ingress-controller-leader
      --ingress-class=nginx

5.4.2 ingress 資源

Ingress 公開了從集羣外部到集羣內服務的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 資源上定義的規則控制。

能夠將 Ingress 配置爲服務提供外部可訪問的 URL、負載均衡流量、終止 SSL/TLS,以及提供基於名稱的虛擬主機等能力。 Ingress 控制器 一般負責經過負載均衡器來實現 Ingress,儘管它也能夠配置邊緣路由器或其餘前端來幫助處理流量。

Ingress 不會公開任意端口或協議。 將 HTTP 和 HTTPS 之外的服務公開到 Internet 時,一般使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 類型的服務

5.4.2.1 捕獲重寫Path 轉發

`apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          serviceName: test
          servicePort: 80

5.4.2.2 基於主機域名轉發

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
  - host: bar.foo.com
    http:
      paths:
      - backend:
          serviceName: service2
          servicePort: 80

6.配置和存儲

  • config map
  • secrets
  • nfs persistent volume
  • empty/hostpath

6.1 config map

ConfigMap 容許你將配置文件與鏡像文件分離,以使容器化的應用程序具備可移植性。

6.1.1 建立config map

# 從文件夾建立(文件夾裏的文本文件將會被建立成config map
  kubectl create configmap my-config --from-file=path/to/bar

  # 從文件建立
  kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt

  # 從字符串建立
  kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2

  # 從鍵值文本建立 
  kubectl create configmap my-config --from-file=path/to/bar

  # 從env文件建立
  kubectl create configmap my-config --from-env-file=path/to/bar.env

6.1.2 使用config map

  • 做爲pod的環境變量
  • 做爲存儲卷掛載到Pod

6.1.2.1 做爲pod的環境變量

建立1個config map 配置文件,在default名稱空間裏

kubectl create configmap hbb-config --from-literal=key1=aaa --from-literal=key2=bbb

建立一個pod ,使用busybox鏡像,並把上面的cm 加載到環境變量,在pod 的container裏面加上

containers:
  - name: busybox
    image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    env:
      - name: HBBKEY1
        valueFrom:
          configMapKeyRef:
            name: hbb-config
            key: key1
      - name: HBBKEY2
        valueFrom:
          configMapKeyRef:
            name: hbb-config
            key: key2

簡易寫法,加載全部hbb-config裏的key value

containers:
  - name: busybox
    image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
      envFrom:
      - configMapRef:
          name: hbb-config

經測試,若是修改了config map ,Pod的環境變量是不會自動更新的,除非刪除pod從新建立

6.1.2.2 做爲存儲卷掛載到pod

containers:
  - name: busybox
    image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
      volumeMounts:
      - name: hbb-cm-volume
        mountPath: /etc/config
  volumes:
    - name: hbb-cm-volume
      configMap:
        name: hbb-config

經驗證,修改config map後,去查看掛載上去的卷,文件中的值也隨之發生了改變,因此這種方式是比較好的方式。

6.2 secrets

Secret 是一種包含少許敏感信息例如密碼、令牌或密鑰的對象。 這樣的信息可能會被放在 Pod 規約中或者鏡像中。 用戶能夠建立 Secret,同時系統也建立了一些 Secret。

  • 建立secret
  • 驗證 secret
  • 使用 secret

6.2.1 建立secrets

  • 經過文件生成
  • 經過字符串生成
  • 手動建立
  • 經過stringData 應用時加密明文secret
  • 查看驗證

6.2.1.1 經過文件生成

#生成文件
echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt

#從文件生成
kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt

默認的鍵名就是文件名,若是要另外指定能夠--from-file=[key=]source,密碼也是如此

#從文件生成
kubectl create secret generic db-user-pass --from-file=hbb-key=./username.txt --from-file=hbb-pas=./password.txt

6.2.1.2 經過字符串生成

說明:特殊字符(例如 $**=!)可能會被 sell轉義,因此要用''括起來

kubectl create secret generic dev-db-secret \
  --from-literal=username=devuser \
  --from-literal=password='S!B\*d$zDsb='

6.2.1.3 手動建立 secret

加密用戶名admin和密碼password

[root@www ~]# echo -n 'admin' | base64 ; echo -n 'password' |base64
YWRtaW4=
cGFzc3dvcmQ=

建立一個mysecret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

用kubectl 建立

kubectl apply -f ./mysecret.yaml

6.2.1.4 經過stringData 應用時加密明文

建立1個 mysecret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  config.yaml: |-
    apiUrl: "https://my.api.com/api/v1"
    username: hbb
    password: hbb-password

執行

kubectl apply -f ./mysecret.yaml

將會建立 一個mysecret 資源,裏面有一個config.yaml的key,它的value是一個加密的字符串。

注: mysecret.yaml第7行的|- 的意思是:將下面三行字符串組合起來,替換右邊的縮進(空格和換行)成一個換行符。

這種方式的好處是,能夠和helm一塊兒使用,helm使用go 的模版,能夠配置明文的字符

6.2.2 查看驗證secret

[root@www ~]# kubectl describe secret/dev-db-secret
Name:         dev-db-secret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password:  12 bytes
username:  7 bytes

使用get 和describe 查看secret都不能直接看到data裏的數據。

若是要查看secret加密前的值應該用 get -o yaml 查看到base64字符串後再解碼

[root@www ~]# kubectl get secret/dev-db-secret -o yaml
apiVersion: v1
data:
  password: UyFCXCpkJHpEc2I9
  username: ZGV2dXNlcg==
kind: Secret
...
[root@www ~]# echo UyFCXCpkJHpEc2I9|base64 --decode
S!B\*d$zDsb=
[root@www ~]# echo ZGV2dXNlcg==|base64 --decode
devuser

6.2.3 使用secret

和config map一毛同樣,略

6.3 nfs persistent volume

  • provisioning 供應方式
  • persistent volume 持久卷
  • persistent volume claim 持久卷聲明
  • storage class 存儲類

6.3.1 provisioning 持久卷供應方案

  • 靜態
  • 動態

6.3.1.1 靜態供應

  1. 集羣管理員建立Pv
  2. 集羣用戶 建立pvc
  3. 集羣用戶 綁定pvc到Pod

6.3.1.2 動態供應

  1. 集羣管理員建立storage class

  2. storage class監視集羣中的 pvc和pod

    • 集羣用戶建立pvc時若是掛在這個storage class上,則會自動建立pv,pv繼承storage class的回收策略
    • pod使用了Pvc, 則storage class會自動把pv掛載到pod的捲上
    • pvc 刪除時 根據storage的回收策略回收 pv

6.3.2 pv persistent volume

PersistentVolume(PV)是已經由管理員提供或者動態使用供應的集羣中的一塊存儲的存儲類。它是集羣中的資源,就像節點是集羣資源同樣。PV是相似於Volumes的卷插件,可是其生命週期與使用PV的任何單個Pod無關。

  • access mode 訪問模式
  • reclaim policy 回收策略
  • source 存儲源
  • 節點親和力

6.3.2.1 access mode 訪問模式

  • ReadWriteOnce-能夠經過單個節點以讀寫方式安裝該卷RWO
  • ReadOnlyMany-該卷能夠被許多節點只讀安裝ROX
  • ReadWriteMany-該卷能夠被許多節點讀寫安裝RWX

6.3.2.2 reclaim policy 回收策略

若是用戶刪除了Pod正在使用的PVC,則不會當即刪除該PVC。PVC的清除被推遲,直到任何Pod再也不主動使用PVC。另外,若是管理員刪除綁定到PVC的PV,則不會當即刪除該PV。PV的去除被推遲,直到PV再也不與PVC結合。

當用戶完成其卷處理後,他們能夠從容許回收資源的API中刪除PVC對象。PersistentVolume的回收策略告訴集羣在釋放其聲明以後如何處理該卷。當前,能夠保留,回收或刪除卷。

  • retain 保留
  • delete 刪除
  • Recycle 回收(已棄用,使用動態供應代替)
6.3.2.2.1 retain 保留

這種策略在pvc刪除後,會保留pv,並釋放pv,但這個pv不能被其餘pvc重用。若是要回收須要集羣管理員手動的去回收,回收步驟以下

  1. 刪除pv
  2. 手動清理pv中的文件數據
  3. 若是要重用存儲介質,須要重聲明一個pv
6.3.2.2.2 delete 刪除

默認的策略是刪除,刪除Pvc ,若是存儲卷類型支持的話,將會同時刪除pv及其中的文件數據

6.3.2.2.3 recycle 回收(棄用

若是pv的介質支持的話,此回收策略將會使用

rm -rf /volume/*

清理pv的數據,而後使這個pv能夠被其餘pvc使用,因爲這樣常常會致使意外終結的Pod,pv裏的數據來不及排查就被回收,因此這種方式已被 棄用。應該使用動態配置+手動回收來避免這種狀況發生。

6.3.2.3 存儲源

因爲咱們集羣環境是私有的局域網,因此一般只會使用nfs來做爲存儲的介質,其餘的類型還有不少,這裏不會介紹更多,須要有須要瞭解能夠到k8s中文社區自行查閱資料,下面是一個由nfs provisioner建立的pv,能夠從存儲源看出對應的nfs服務的相關信息

Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    www.haoyun.nfs1
    Path:      /k8s/default-hbb-pvc-pvc-02d94a26-d5df-4e70-9e3f-a12630f2bd41
    ReadOnly:  false

6.3.2.4 節點親和力

pv一旦設置了節點親和力,則與Pv結合的Pod都會部署到命中的節點上

6.3.3 persistent volume claim

PersistentVolumeClaim(PVC)是由用戶進行存儲的請求。它相似於pod。pod消耗節點資源,PVC消耗PV資源。Pod能夠請求特定級別的資源(CPU和內存)。聲明能夠請求特定的大小和訪問模式

靜態供應狀況下,pvc綁定Pv

動態供應狀況下,pvc綁定 storage class

6.3.4 storage class

雖然PersistentVolumeClaims容許用戶使用抽象存儲資源,但對於不一樣的pod,用戶一般須要具備不一樣存儲介質,例如機械硬盤和固態硬盤,機房1和機房2,集羣管理員須要可以提供各類PersistentVolume,這些PersistentVolume不只在大小和訪問模式上有更多差別,並且還不讓用戶瞭解如何實現這些卷的細節。

這個是推薦使用的方式,因此這裏重點實踐

  1. nfs 服務器搭建(www.haoyun.nfs1),並驗證可用性
  2. 基於nfs部署一個 nfs client provisioner(www.haoyun.nfs1/k8s)
  3. 建立1個storage class
  4. 建立1個綁定storage class的pvc
  5. 建立1個綁定pvc的pod
  6. 驗證

6.3.4.1 nfs服務器搭建

略,

驗證

1. 安裝nfs-client
  # yum install -y nfs-utils

2. 建立掛載目錄
  # mkdir /var/nfs

3. 查看NFS Server目錄
  # showmount -e www.haoyun.nfs1

4. 掛載NFS Server目錄
  # mount -t nfs www.haoyun.nfs1:/k8s ~/nfs-test
  
5. 測試完卸載
   # umount -t nfs ~/nfs-test/

6.3.4.2 nfs client provisioner

6.3.4.3 建立1個storage class

用工具安裝部署完nfs provisioner時已經生成,略

6.3.4.4 建立1個綁定storage class的pvc

建立文件 pvc.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: hbb-pvc
  annotations:
    volume.beta.kubernetes.io/storage-class: "nfs-client"
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Mi

添加pvc到集羣中的nfs名稱空間,storage class是集羣資源,任何名稱空間均可以使用

kubectl apply -f pvc.yaml -n nfs

6.3.4.5 建立pod,驗證nfs provisioner

建立podWithPvc.yaml

kind: Pod
apiVersion: v1
metadata:
  name: podwithpvc
spec:
  containers:
  - name: podwithpvc
    image: busybox:1.28
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"   #建立一個SUCCESS文件後退出
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: hbb-pvc  #與PVC名稱保持一致

添加pod到nfs名稱空間

kubectl apply -f podWithPvc.yaml

這個Pod會掛載storage class生成的pv到/mnt目錄,並建立一個名爲seccess的文件

到nfs服務器上查看/k8s目錄 能夠看到多了一個名爲storageClass名稱+pod名稱的目錄

[root@www k8s]# ll
total 0
drwxrwxrwx 2 root root 21 Aug 29 09:20 nfs-hbb-pvc-pvc-f1a0750e-2214-43ac-a148-cff3db88d4f4

刪除pod 再刪除pvc 再去查看

[root@www k8s]# ll
total 0
drwxrwxrwx 2 root root 21 Aug 29 09:20 archived-nfs-hbb-pvc-pvc-f1a0750e-2214-43ac-a148-cff3db88d4f4

目錄的名稱變爲archived + storageClass名稱 + pod名稱,這是由於storage class 聲明時設置成刪除且歸檔了

[root@www ~]# kubectl describe sc -n nfs nfs-client
Name:                  nfs-client
IsDefaultClass:        No
Annotations:           meta.helm.sh/release-name=hbb-nfs,meta.helm.sh/release-namespace=nfs
Provisioner:           cluster.local/hbb-nfs-nfs-client-provisioner
Parameters:            archiveOnDelete=true
AllowVolumeExpansion:  True
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>

這種方式比較直接刪除更能保障數據安全,可是須要集羣管理員定時刪除沒必要要的歸檔,(例如超過1個月的歸檔沒有開發人員認領,就刪除掉)

6.4 empty volume 和 hostpath volume

補充兩種卷,

  • empty
  • hostpath

6.4.1 empty volume

當 Pod 指定到某個節點上時,首先建立的是一個 emptyDir 卷,而且只要 Pod 在該節點上運行,卷就一直存在。 就像它的名稱表示的那樣,卷最初是空的。 儘管 Pod 中的容器掛載 emptyDir 卷的路徑可能相同也可能不一樣,可是這些容器均可以讀寫 emptyDir 卷中相同的文件。 當 Pod 由於某些緣由被從節點上刪除時,emptyDir 卷中的數據也會永久刪除。

說明: 容器崩潰並不會致使 Pod 被從節點上移除,所以容器崩潰時 emptyDir 卷中的數據是安全的。

emptyDir 的一些用途:

  • 緩存空間,例如基於磁盤的歸併排序。
  • 爲耗時較長的計算任務提供檢查點,以便任務能方便地從崩潰前狀態恢復執行。
  • 在 Web 服務器容器服務數據時,保存內容管理器容器獲取的文件。

默認狀況下, emptyDir 卷存儲在支持該節點所使用的介質上;這里的介質能夠是磁盤或 SSD 或網絡存儲,這取決於您的環境。 可是,您能夠將 emptyDir.medium 字段設置爲 "Memory",以告訴 Kubernetes 爲您安裝 tmpfs(基於 RAM 的文件系統)。 雖然 tmpfs 速度很是快,可是要注意它與磁盤不同。 tmpfs 在節點重啓時會被清除,而且您所寫入的全部文件都會計入容器的內存消耗,受容器內存限制約束。

示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

6.4.2 hostpath volume

不經常使用的方式, Pod掛載node上的文件目錄到pod中,略

文檔

7.調度

文檔

  • nodeSelector
  • taints/tolerattions
  • affinity/antiAffinity
  • distuption
  • HPA(Horizontal pod Autoscaler)

7.1 nodeSelector(節點選擇器)

  • 節點標籤
  • Pod 加spec.nodeSelector
  • 部署驗證

7.1.1 節點標籤

[root@www ~]# kubectl get no www.haoyun.edge1 --show-labels
NAME               STATUS   ROLES    AGE     VERSION   LABELS
www.haoyun.edge1   Ready    <none>   6d21h   v1.18.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,isEdgeNode=,kubernetes.io/arch=amd64,kubernetes.io/hostname=www.haoyun.edge1,kubernetes.io/os=linux

挑選一個內置的hostname標籤來驗證

kubernetes.io/hostname=www.haoyun.edge1

7.1.2 pod加節點選擇器

建立一個busybox-nodeselector.yaml,在裏面加上一個節點選擇器,

使用了內置的label kubernetes.io/hostname ,限定Pod只能部署到edge1上

[root@www ~]# vim busybox-nodeselector.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox-nodeselector-test
  namespace: default
spec:
  nodeSelector:
    kubernetes.io/hostname: www.haoyun.edge1
  containers:
  - name: busybox
    image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always

應用它到集羣

kubectl apply -f busybox-nodeselector.yaml

7.1.3 部署驗證

查看運行狀態,確實跑到edge1上了

[root@www ~]# kubectl get pod -A -o wide|grep nodeselec
default       busybox-nodeselector-test                                 1/1     Running       0          22s     10.244.6.61      www.haoyun.edge1   <none>           <none>

查看生成的pod詳情

spec:
  containers:
  - command:
    - sleep
    - "3600"
    image: busybox:1.28
    imagePullPolicy: IfNotPresent
    name: busybox
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-74k2x
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: www.haoyun.edge1
  nodeSelector:
    kubernetes.io/hostname: www.haoyun.edge1

驗證經過

7.2 taints/tolerationss (污點/容忍)

污點是打在node上的標記,容忍是打在Pod上的標記

  1. 給node加污點 taints
  2. 給pod打容忍標記 tolerations
  3. 部署pod驗證

7.2.1 給node 打污點標記

查看主節點www.haoyu.m1的污點

kubectl describe no www.haoyun.m1

查看spec.taints

spec:
  podCIDR: 10.244.0.0/24
  podCIDRs:
  - 10.244.0.0/24
  taints:
  - effect: NoSchedule
    key: node-role.kubernetes.io/master

已打一個名爲node-role.kubernetes.io/master 的Noschedule的污點

7.2.2 給pod打容忍標記

先用get pod -o wide查看到一個部署在www.haoyun.m1結點上的Pod

而後用describe 或edit查看到它的spec.tolerations

tolerations:
  - key: CriticalAddonsOnly
    operator: Exists
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300

其中的

- effect: NoSchedule
    key: node-role.kubernetes.io/master

表示能夠容忍節點上存在這個污點

7.2.3 部署驗證

7.3 affinity/antiAffinity(親和力/反親和力)

親和力/反親和力 又分爲

  • 節點親和力/反親和力
  • pod親和力/反親和力

node親和力用於限定Pod部署到Node上的命中規則,

Pod親和力則用於限定 Pod與pod 佈置到同一個Node上

他們都有多種匹配規則和 「軟」 「硬」兩次匹配策略

交叉相實際上是四種不一樣的設置,但因爲他們大同小異,故在此只以節點的親和力來作說明。

7.3.1 節點親和力

節點親和力 /反親和力使用步驟以下

  1. 給結點標籤
  2. 給pod加上節點親和力 /反親和力
  3. 部署Pod驗證

7.3.1.1 給結點打標籤

例如給名爲www.haoyun.edge1的結點打上 isedgenode的標籤

kubectl label no www.haoyun.edge1 isEdgeNode=true

查看節點已打的標籤

[root@www ~]# kubectl get no www.haoyun.edge1 --show-labels
NAME               STATUS   ROLES    AGE     VERSION   LABELS
www.haoyun.edge1   Ready    <none>   6d20h   v1.18.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,isEdgeNode=,kubernetes.io/arch=amd64,kubernetes.io/hostname=www.haoyun.edge1,kubernetes.io/os=linux

7.3.1.2 給Pod加上節點親和力

查看 nginx-ingress-controller的deployment

kubectl edit deployment -n kube-system

找到pod的template

template:
      metadata:
        creationTimestamp: null
        labels:
          app: nginx-ingress
          app.kubernetes.io/component: controller
          component: controller
          release: my-nginx-ingress
      spec:
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: isEdgeNode
                  operator: Exists

能夠看到spec.affinity.nodeAffinity裏有一條規則,必需節點選擇器命中isedgenode這個標籤存在, 也就是說打標籤的時候無論是設置什麼值,只要有isedgenode就能夠部署上去。

  • requiredDuringSchedulingIgnoredDuringExecution 必需
  • preferredDuringSchedulingIgnoredDuringExecution 儘量

相比起節點選擇器,他有更靈活的匹配替換,並且能夠有「軟」,「硬」兩種策略

寫法相對也複雜得多。

7.3.1.3 部署Pod驗證

因爲已經部署過了,因此直接查看,能夠看到nginx-ingress-controller的Pod是在www.haoyun.edge1的結點上的。

[root@www ~]# kubectl get pod -A -o wide|grep ingress
kube-system   my-nginx-ingress-controller-77d976f664-dj5vg              1/1     Running       3          6d20h   10.244.6.56      www.haoyun.edge1   <none>           <none>

7.4 disruption 干擾調度

pod不會自動消息,除非自願干擾或 非自願干擾

  • PDB(pod disruption budget)

  • 非自願干擾

  • 自願干擾

7.4.1 pod disruption budget

pdb用於確保pod驅逐的過程當中服務的可用Pod數量在安全的範圍內。

官方文檔對不一樣的部署方案的例子

  • 無狀態前端:

    • 關注:服務容量減小不要超過10%。
      • 解決方案:例如,使用minAvailable 90%的PDB。
  • 單實例有狀態應用程序:

    • 關注:請勿在不與我交談的狀況下終止此應用程序。
      • 可能的解決方案1:請勿使用PDB,而且能夠承受偶爾的停機時間。
      • 可能的解決方案2:將PDB設置爲maxUnavailable = 0。瞭解(在Kubernetes以外)集羣操做員須要在終止以前諮詢您。當集羣操做員與您聯繫時,請準備停機,而後刪除PDB以代表已準備好進行中斷。以後從新建立。
  • 多實例有狀態應用程序,例如Consul,ZooKeeper或etcd:

    • 關注:不要將實例數量減小到仲裁如下,不然寫入將失敗。
      • 可能的解決方案1:將maxUnavailable設置爲1(適用於不一樣的應用程序規模)。
      • 可能的解決方案2:將minAvailable設置爲法定大小(例如,小數位數爲5時爲3)。(一次容許更多中斷)。
  • 可從新啓動的批處理做業:

    • 關注:在自願中斷的狀況下,工做須要完成。

      • 可能的解決方案:不要建立PDB。做業控制器將建立一個替換容器。

pdb經過設置 maxunAvailable (最大不可用pod個數)或 minAvailable(最小可用pod個數) 來保證Pod驅逐時服務的可用性,例:

例:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: zookeeper
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

7.4.2 非自願干擾

  • 節點下層物理機的硬件故障
  • 集羣管理員錯誤地刪除虛擬機(實例)
  • 雲提供商或虛擬機管理程序中的故障致使的虛擬機消失
  • 內核錯誤
  • 節點因爲集羣網絡隔離從集羣中消失
  • 因爲節點資源不足致使 pod 被驅逐。

這裏只着重介紹資源不足致使的Pod被驅逐,由於其餘狀況都與配置無關

7.4.2.1 eviction api

eviction api是一組kubelet的api,用於指定當Node的資源不足時,(通常是指硬盤空間不足或內存不足的狀況 ,)要如何驅逐Pod以保證node的資源始終在合理的範圍內,能夠在kubelet 的config map裏配置,或者命令行啓動參數的方式設置eviction api, 可參閱文檔

資源不足

設置Kubelet參數

一個例子:

  • 節點內存容量:10Gi
  • 操做員但願爲系統守護進程保留 10% 內存容量(內核、kubelet等)。
  • 操做員但願在內存用量達到 95% 時驅逐 pod,以減小對系統的衝擊並防止系統 OOM 的發生。

爲了促成這個場景,kubelet將像下面這樣啓動:

--eviction-hard=memory.available<500Mi
--system-reserved=memory=1.5Gi

這個配置的暗示是理解系統保留應該包含被驅逐閾值覆蓋的內存數量。

要達到這個容量,要麼某些 pod 使用了超過它們請求的資源,要麼系統使用的內存超過 1.5Gi - 500Mi = 1Gi

這個配置將保證在 pod 使用量都不超過它們配置的請求值時,若是可能當即引發內存壓力並觸發驅逐時,調度器不會將 pod 放到這個節點上。

7.4.3 自願干擾

  • 程序全部者主動干擾
  • 集羣管理員主動干擾

7.4.3.1 程序全部者主動干擾

  • 刪除 Deployment 或其餘管理 Pod 的控制器
  • 更新了 Deployment 的 Pod 模板致使 Pod 重啓
  • 直接刪除 Pod(例如,由於誤操做)

7.4.3.2 集羣管理員主動干擾

  • 排空節點以進行維修或升級。
  • 從集羣中排出節點以縮小集羣規模(瞭解集羣自動縮放 )。
  • 從節點上刪除pod,以容許其餘東西被調度到該節點。

本節只會介紹排空,由於2 通常用於雲供應商平臺,3太簡單略。

7.4.3.2.1 drain /uncordon(排空 )

有時候某個Node須要中止運行維護,好比加內存之類的操做,這時若是隻是刪除Node上的pod, 則很大機率新的pod會從新被調度到這個node上,這種狀況下集羣管理員應該排空這個node,在維護結束後再結束以後再使用uncordon,使Pod能被調試到這個維護結束的node上

例如對 www.haoyun.edge1結點作排空操做

kubectl drain www.haoyun.edge1

維護結束以後恢復

kubectl uncordon www.haoyun.edge1

7.5 Horizontal pod Autoscaler

略,須要配合promethues 之類的數據採集纔可使用,可能以後專門講promethues再討論,由於這個功能對浩雲來講不是很重要

8.pod配置

  • enviorment 環境變量
  • volume 卷
  • proms 探針

8.1 設置pod環境變量

  • 寫在docker file裏

  • 使用helm 寫pod 參數裏傳遞

  • 使用config map

8.2 設置pod的存儲卷

參見 6.配置和存儲

8.3 probes 探針

kubelet 使用存活探測器來知道何時要重啓容器。 例如,存活探測器能夠捕捉到死鎖(應用程序在運行,可是沒法繼續執行後面的步驟)。 這樣的狀況下重啓容器有助於讓應用程序在有問題的狀況下更可用。

kubelet 使用就緒探測器能夠知道容器何時準備好了並能夠開始接受請求流量, 當一個 Pod 內的全部容器都準備好了,才能把這個 Pod 看做就緒了。 這種信號的一個用途就是控制哪一個 Pod 做爲 Service 的後端。 在 Pod 尚未準備好的時候,會從 Service 的負載均衡器中被剔除的。

kubelet 使用啓動探測器能夠知道應用程序容器何時啓動了。 若是配置了這類探測器,就能夠控制容器在啓動成功後再進行存活性和就緒檢查, 確保這些存活、就緒探測器不會影響應用程序的啓動。 這能夠用於對慢啓動容器進行存活性檢測,避免它們在啓動運行以前就被殺掉。

  • 探針探測類型

  • 探針可配置項

  • 啓動探針 startupProbe

  • 就緒探針 readinessProbe

  • 存活探針 livenessProbe

8.3.1 探針探測類型

因爲各類探針的寫法是同樣的,只是名稱不一樣,做用也不一樣

因而這裏以存活探針爲例,實踐如下幾種探針的探測方法

  • 命令行
  • http
  • tcp

8.3.1.1 命令行探針

命令返回成功存活,失敗kubelet根據restartPolicy處置Pod

Pod 的 spec 中包含一個 restartPolicy 字段,其可能取值包括 Always、OnFailure 和 Never。默認值是 Always。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5 #首次延遲5秒檢測
      periodSeconds: 5 #每5秒檢測一次

8.3.1.2 http探針

http responed 大於等於200小於400成功 ,不然大於等於400失敗,kubelet根據restartPolicy處置 pod

Pod 的 spec 中包含一個 restartPolicy 字段,其可能取值包括 Always、OnFailure 和 Never。默認值是 Always。

1.13版本以前,若是設置了 http_proxy環境變量,則探針會使用代理,1.13版本後探針不使用代理。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

8.3.1.3 tcp探針

tcp探針的示例使用了就緒探針和存探針

並非就緒探針探測就緒了纔會使用存活探針去檢測存活,這兩個探針是並行的

若是須要設置在容器啓動成功後再探測存活,應該使用啓動探針 (startupProbe

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: k8s.gcr.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

8.3.2 探針可配置項

Probe 有不少配置字段,可使用這些字段精確的控制存活和就緒檢測的行爲:

  • initialDelaySeconds:容器啓動後要等待多少秒後存活和就緒探測器才被初始化,默認是 0 秒,最小值是 0。
  • periodSeconds:執行探測的時間間隔(單位是秒)。默認是 10 秒。最小值是 1。
  • timeoutSeconds:探測的超時後等待多少秒。默認值是 1 秒。最小值是 1。
  • successThreshold:探測器在失敗後,被視爲成功的最小連續成功數。默認值是 1。 存活探測的這個值必須是 1。最小值是 1。
  • failureThreshold:當探測失敗時,Kubernetes 的重試次數。 存活探測狀況下的放棄就意味着從新啓動容器。 就緒探測狀況下的放棄 Pod 會被打上未就緒的標籤。默認值是 3。最小值是 1。

HTTP Probes 能夠在 httpGet 上配置額外的字段:

  • host:鏈接使用的主機名,默認是 Pod 的 IP。也能夠在 HTTP 頭中設置 「Host」 來代替。
  • scheme :用於設置鏈接主機的方式(HTTP 仍是 HTTPS)。默認是 HTTP。
  • path:訪問 HTTP 服務的路徑。
  • httpHeaders:請求中自定義的 HTTP 頭。HTTP 頭字段容許重複。
  • port:訪問容器的端口號或者端口名。若是數字必須在 1 ~ 65535 之間。

對於 HTTP 探測,kubelet 發送一個 HTTP 請求到指定的路徑和端口來執行檢測。 除非 httpGet 中的 host 字段設置了,不然 kubelet 默認是給 Pod 的 IP 地址發送探測。 若是 scheme 字段設置爲了 HTTPS,kubelet 會跳過證書驗證發送 HTTPS 請求。 大多數狀況下,不須要設置host 字段。 這裏有個須要設置 host 字段的場景,假設容器監聽 127.0.0.1,而且 Pod 的 hostNetwork 字段設置爲了 true。那麼 httpGet 中的 host 字段應該設置爲 127.0.0.1。 可能更常見的狀況是若是 Pod 依賴虛擬主機,你不該該設置 host 字段,而是應該在 httpHeaders 中設置 Host

對於一次 TCP 探測,kubelet 在節點上(不是在 Pod 裏面)創建探測鏈接, 這意味着你不能在 host 參數上配置服務名稱,由於 kubelet 不能解析服務名稱。

9. helm

爲了第十章,必須把helm的一直基本姿式介紹一下,因此有第9章

  • helm是什麼
  • 安裝helm
  • chart 是文件結構介紹
  • helm 語法

9.1 helm是什麼

helm是kubernetes的包管理器,用於查找,分享,使用kubernetes生態的應用。

9.2 安裝helm

  • 前置條件
  • 安裝
    • windows
    • linux

9.2.1 前置條件

  • 擁有一個集羣
  • 擁有kubectl,並配置用於與kubeapi通信的config鏈接配置文件

通常helm都是安裝在主節點上,開發人員用有權限範圍的用戶登陸上去操做helm便可,

能夠和git結合使用,或安裝helm局域網服務器Tiller

也能夠在開發人員機器上經過config+kubectl 直接鏈接 上集羣 ,可是這樣開發人員須要在本身電腦上安裝kubectl。

9.2.2 安裝

不管是windows仍是linux,都是直接去下載helm的二進制文件

linux複製helm二進制到bin目錄:

mv helm /usr/bin

windows設置環境變量:

個人電腦屬性->高級->設置環境變量->path+=

9.3 chart文件結構

xxx_chart/
  Chart.yaml          # chart的信息文件
  LICENSE             # 可選: 許可證 如:GPL LGPL
  README.md           # 可選: 
  values.yaml         # 默認的配置值文件
  values.schema.json  # 可選: 對value.yaml提供輸入格式校驗的josn文件
  charts/             # 依賴的其餘圖表的存放文件夾
  crds/               # 自定義資源
  templates/          # 使用go模版的yaml文件,會從value.yaml或其餘文件讀值生成資源yaml
  templates/NOTES.txt # 可選: 安裝成功時,顯示在終端上的文字

9.4 helm語法

helm語法是yaml混合 go模版的語法

聽起來很複雜,其實學起來不復雜

僅須要抓着helm chart安裝生成的文件抽絲剝繭,就能快速掌握,由於自己並不複雜

  • 上下文

    • .

    • .value value.yaml

    • .release 安裝時用戶輸入

    • .chart chart.yaml

  • 模版

    • 定義
    • 使用
  • 其餘語法 去官網文檔查

10.把.net core web app部署到集羣並附加調試

現有兩個.net core web app

  1. api
  2. blazor

前端將經過ingress暴露給集羣外部,後端則只在集羣內部。以此爲例實踐第10章

  • 工具推薦
    • visual studio
      • visual studio tools for kubernetes
    • visual studio code
      • C#
      • YAML
      • kubernetes
      • cloud code for visual studio code
  • 將現有.net core web app部署到集羣

10.1 工具推薦

10.2 將現有.net core web app部署到集羣

10.2.1 爲現有項目添加chart和docker file

這個在前端和後端web app中都須要作,由於都要部署到集羣。

在visual studio 安裝10.1中的擴展後

image-20200906164002575

選擇kubernetes/helm

image-20200906164131015

肯定後生成chart文件夾和docker file,還有一個azds.yaml (部署到微軟雲上纔用到),

image-20200906164442774

10.2.2 程序配置

10.2.2.1 後端

  • 端口
  • 跨域策略
  • api實現
10.2.2.1.1 端口配置5001
public class Program
    {
        public static void Main(string[] args)
            => CreateHostBuilder(args).Build().Run();

        public static IHostBuilder CreateHostBuilder(string[] args)
            => Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(
                    webBuilder => webBuilder
                    .UseStartup<Startup>().UseUrls("http://*:5001"));
    }

value.yaml中修改服務的targetport

service:
  type: ClusterIP
  port: 80
  targetPort: 5001

修改deployment.yaml裏的image 和containerPort

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ template "hscadaexapi.fullname" . }}
  labels:
    # 略
spec:
  revisionHistoryLimit: 0
  replicas: {{ .Values.replicaCount }}
  selector: 
    matchLabels:
      app: {{ template "hscadaexapi.name" . }}
      release: {{ .Release.Name }}
  template:
    # 略
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.targetPort}}
              protocol: TCP
10.2.2.1.2 跨域策略

.net core由於安全性設計,默認不支持非同源策略的域名對.net web app進行訪問

下表給出了與 URL http://store.company.com/dir/page.html 的源進行對比的示例:

URL 結果 緣由
http://store.company.com/dir2/other.html 同源 只有路徑不一樣
http://store.company.com/dir/inner/another.html 同源 只有路徑不一樣
https://store.company.com/secure.html 失敗 協議不一樣
http://store.company.com:81/dir/etc.html 失敗 端口不一樣 ( http:// 默認端口是80)
http://news.company.com/dir/other.html 失敗 主機不一樣

定義1個策略名稱

public readonly string myAllowSpecificOrigins = "myAllowSpecificOrigins";

配置策略

// startup.cs 
        public void ConfigureServices(IServiceCollection services)
        {
            //略
            services.AddCors(o =>
            {
                o.AddPolicy(myAllowSpecificOrigins, build =>
                 {
                     build
                     .AllowAnyOrigin()
                     .AllowAnyHeader()
                     .AllowAnyMethod();
                 });
            });
            //略
        }

添加到中單件管道

// startup.cs
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //略
            app.UseCors(myAllowSpecificOrigins);
            //略
        }
10.2.2.1.3 api實現

10.2.2.2 前端

  • 端口
  • userApi url
    • 調試時根據launch.json傳變環境變量
    • 部署時經過deployment的pod 模版傳入環境變量
10.2.2.2.1 端口

同10.2.2.1.1,略

10.2.2.2.2 userApi環境變量
10.2.2.2.2.1 調試時

launchSetting.json

{
  "profiles": {
    "HScadaEx.Blazor": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "userApi": "http://localhost:5001"
      },
      "applicationUrl": "http://localhost:5011"
    }
  }
}
10.2.2.2.2.2 部署時

value.yaml

# 略
userApi: http://hscadaexapi.hbb.svc # http://服務名稱.名稱空間.svc
# 略

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ template "hscadaexblazor.fullname" . }}
  # 略
spec:
  #略
  template:
    # 略
    spec:
      containers:
        - name: {{ .Chart.Name }}
          # 略
          env:
            # 略
            - name: userApi
              value: {{ .Values.userApi | quote }}

程序中使用環境變量

// startup.cs
		public void ConfigureServices(IServiceCollection services)
        {
            var userApi = Environment.GetEnvironmentVariable("userApi");
            services.AddHttpClient("usersApi", x =>
             {
                 x.BaseAddress = new Uri($"{userApi}/api/Users/");
                 x.DefaultRequestHeaders.Add("User-Agent", "BlazorSever");
                 x.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
             }).SetHandlerLifetime(TimeSpan.FromSeconds(30));
            Console.WriteLine($"環境變量userApi = {userApi}");
            services.AddTransient<IBLL.User.IUserService, Service.UsersServer>();

        }
10.2.2.2.3 ingress 集羣入口資源

我但願訪問www.haoyun.blazor的時候,能夠從集羣外部訪問前端應用

訪問鏈路:用戶-> www.haoyun.blazor->dns->集羣邊緣節點外部ip->ingress controller(集羣)->blazor server--ipvs-->pod(集羣)

values.yaml

ingress:
  enabled: true
  annotations:
    # kubernetes.io/tls-acme: "true"
  path: /
  hosts:
    - www.haoyun.blazor
  tls: []
    # - secretName: chart-example-tls
    #   hosts:
    #     - chart-example.local

/template/ingerss.yaml 保持不變便可

{{- if .Values.ingress.enabled -}}
{{- $fullName := include "hscadaexblazor.fullname" . -}}
{{- $servicePort := .Values.service.port -}}
{{- $ingressPath := .Values.ingress.path -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: {{ $fullName }}
  labels:
    app: {{ template "hscadaexblazor.name" . }}
    chart: {{ template "hscadaexblazor.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
  annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
  tls:
  {{- range .Values.ingress.tls }}
    - hosts:
      {{- range .hosts }}
        - {{ . }}
      {{- end }}
      secretName: {{ .secretName }}
  {{- end }}
{{- end }}
  rules:
  {{- range .Values.ingress.hosts }}
    - host: {{ . }}
      http:
        paths:
          - path: {{ $ingressPath }}
            backend:
              serviceName: {{ $fullName }}
              servicePort: http
  {{- end }}
{{- end }}

10.2.3 理解/修改docker file以支持調試

默認的docker file解讀

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base #以..3.1環境爲基礎建立一個名爲base的鏡像
WORKDIR /app #設置工做目錄 保存爲匿名鏡像
EXPOSE 80 #導出端口 80  保存爲匿名鏡像

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build #以..sdk3.1爲基礎建立一個名爲build的鏡像
WORKDIR /src #設置工做目錄 保存爲匿名鏡像
COPY ["src/test/test.csproj", "src/test/"] #複製csobj文件 保存爲匿名鏡像

RUN dotnet restore "src/test/test.csproj" #還原nuget包 保存爲匿名鏡像
COPY . . #遞歸複製解決方案到 src目錄 保存爲匿名鏡像
WORKDIR "/src/src/test" #設置工做目錄爲鏡像中的test項目目錄 保存爲匿名鏡像
RUN dotnet build "test.csproj" -c Release -o /app/build #在鏡像中編譯 保存爲匿名鏡像

FROM build AS publish #以build 爲基礎 建立一個名爲publish的鏡像
RUN dotnet publish "test.csproj" -c Release -o /app/publish #發佈到/app/publish目錄 保存爲匿名鏡像

FROM base AS final #base爲基礎建立一個名爲final的鏡像
WORKDIR /app #設置工做目錄,保存爲匿名鏡像
COPY --from=publish /app/publish . #複製publish鏡像的/app/publish文件夾到final的工做目錄 保存爲匿名鏡像
ENTRYPOINT ["dotnet", "test.dll"] # 設置入口 保存爲匿名鏡像

修改release->debug ,添加調試工具後

以api 的dockerfile爲例, blazor略

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 5001 #導出所需端口
RUN apt-get update   && apt-get install -y --no-install-recommends unzip   && apt-get install -y procps && rm -rf /var/lib/apt/lists/*  && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg # 調試工具安裝

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["src/HScadaEx.API/HScadaEx.API.csproj", "src/HScadaEx.API/"]
COPY ["src/HScadaEx.IBLL/HScadaEx.IBLL.csproj", "src/HScadaEx.IBLL/"]
COPY ["src/HScada.Model/HScada.Model.csproj", "src/HScada.Model/"]
COPY ["src/HscadaEx.BLL/HscadaEx.BLL.csproj", "src/HscadaEx.BLL/"]
COPY ["src/HScadaEx.Core/HScadaEx.Core.csproj", "src/HScadaEx.Core/"]
RUN dotnet restore "src/HScadaEx.API/HScadaEx.API.csproj"
COPY . .
WORKDIR "/src/src/HScadaEx.API"
RUN dotnet build "HScadaEx.API.csproj" -c Debug -o /app/build #改成debug,不須要調試就用release

FROM build AS publish
RUN dotnet publish "HScadaEx.API.csproj" -c Debug -o /app/publish#改成debug,不須要調試就用release

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "HScadaEx.API.dll"]

10.2.4 部署

  1. 構建docker ,把docker鏡像傳輸到工做節點
  2. 複製chart到 主節點
  3. 使用helm 部署到 hbb名稱空間

10.2.4.1 構建docker鏡像

因爲我的計算機資源有限,因此我這裏只使用docker savedocker load命令 將docker鏡像複製到僅有的一個工做節點上。正確的作法是在集羣外部搭建一個docker私有倉庫,按需拉取。

已知私有倉庫部署方式

  • docker-registry 簡易的,沒有認證受權功能
  • Harbor
  • nexus

對於 2和3 有認證受權的鏡像

正確的姿式是配置好證書後,導入爲secret資源,在拉取鏡像時指定secret資源便可。

10.2.4.2 複製chart到主節點

一樣 chart也有私有服務器能夠搭建,名爲Tiller,正確的姿式也是應該搭服務器,讓集羣調度的時候按需獲取,

10.2.4.3 helm 經常使用指令

更多信息查看官網文檔helm -h

查看

helm ls -n 名稱空間

添加repository

helm add repo 名稱 url

安裝

helm install -n 名稱空間 release名稱 chart
#其中的chart 能夠是本地文件,也能夠是tiller上的chart路徑

更新

helm upgrade -n 名稱空間 release名稱  chart

查看變動歷史記錄

helm history -n 名稱空間 release名稱

回滾

helm rollback

刪除

helm delete -n 名稱空間 release名稱

10.2.3 調試

  • 經過kubernetes api 的客戶端進行附加調試
  • 開發過程當中的快速調試okteto

10.2.3.1 經過kubernetes api客戶端

10.2.3.1.1 安裝kubectl 配置 集羣鏈接信息
  • 下載kubectl二進制,添加到系統變量path

  • 複製集羣管理員生成的 config文件到 c:\用戶\.kube 文件夾

10.2.3.1.2 修改docker file
  • release->debug
  • 安裝vsdbg
#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 5001
EXPOSE 5002
RUN apt-get update   && apt-get install -y --no-install-recommends unzip   && apt-get install -y procps && rm -rf /var/lib/apt/lists/*  && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["src/HScadaEx.API/HScadaEx.API.csproj", "src/HScadaEx.API/"]
COPY ["src/HScadaEx.IBLL/HScadaEx.IBLL.csproj", "src/HScadaEx.IBLL/"]
COPY ["src/HScada.Model/HScada.Model.csproj", "src/HScada.Model/"]
COPY ["src/HscadaEx.BLL/HscadaEx.BLL.csproj", "src/HscadaEx.BLL/"]
COPY ["src/HScadaEx.Core/HScadaEx.Core.csproj", "src/HScadaEx.Core/"]
RUN dotnet restore "src/HScadaEx.API/HScadaEx.API.csproj"
COPY . .
WORKDIR "/src/src/HScadaEx.API"
RUN dotnet build "HScadaEx.API.csproj" -c Debug -o /app/build

FROM build AS publish
RUN dotnet publish "HScadaEx.API.csproj" -c Debug -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "HScadaEx.API.dll"]
10.2.3.1.3 LaunchSetting.json

這裏用cloud code插件生成調試配置

launchSetting.json

{

    "version": "0.2.0",
    "configurations": [       

        {
            "name": "Attach to Kubernetes Pod (.NET Core)",
            "type": "cloudcode.kubernetes",
            "request": "attach",
            "language": "NETCore",
            "podSelector": {
                "app": "hscadaexblazor" //pod的app label
            },
            "localRoot": "${workspaceFolder}", //本地工做區
            "remoteRoot": "/app" //遠程容器中的工做目錄
        }

    ]
}

.net core目前只能用於附加調試, go/node.js/py 能夠熱重載調試,從谷歌插件的文檔上看到的。

這種方式的缺點很明顯,若是要調試過程修改了代碼,因爲不能熱重載,只能在本地改好代碼->重生成docker image->推送到docker repository ->更新image到集羣 ->再附加調試

因此比較推薦另外一種方式的調試,雖然也不支持熱重載,可是用dotnet watch run,在代碼重生成時實時響應到容器中,並重啓容器中的程序

10.2.3.2 經過okteto 在開發過程當中快速調試

okteto是一個開源的項目,用於簡化各類技術棧 在kubernetes中的開發工程。

傳統的kubernetes 服務開發過程就是不斷的重複這個過程

while(調試ing)
{
    附加調試->修改代碼->生成docker鏡像->推送鏡像->拉鏡像->更新到集羣
}

okteto的方式是把開發環境打包成一個鏡像,實時同步容器與本地的文件變化,轉發本地流量到容器 、轉發容器流量到本地 等鏈接調試用的是ssh ,配合dotnet watch run 能夠僅是生成代碼就把生成的結果應用到容器中,雖然不是真正的熱重載,容器中的程序會重啓,但快速了不少,並且這是各類技術棧都能用的,不只限於.net 的萬金油,因而流程簡化爲

do 
{
    dotnet watch run
}while(調試ing)
{
    附加調試->修改代碼->生成
}
10.2.3.2.1 安裝okteto
  • 下載二進制文件,並加到系統環境變量path
  • 複製集羣管理員生成的config ,放到 c:\用戶\.kube文件夾
10.2.3.2.2 在解決方案中添加okteto.yaml

更多參考官方文檔

name: hscadaexapi #deployment service 的名稱
namespace: hbb #名稱空間
image: mcr.microsoft.com/dotnet/core/sdk #開發環境變量
environment:
- ASPNETCORE_ENVIRONMENT=Development #環境變量
command:
- bash #啓動命令
workdir: /okteto #工做目錄
remote: 22000 # ssh調試端口  把本地22000->容器22
sync:  #同步的文件夾   本地:容器
- .:/okteto
forward:  #端口轉發    本地:容器
- 5001:5001
persistentVolume: {}
10.2.3.2.3 啓動okteto ,
okteto up

這裏集羣裏會多出一個deployment 和service 資源, 跑起一個pod,這個pod是空的,僅僅是一個開發環境,

而後okteto會並 當前文件夾的文件同步到遠程容器中的$workdir ,並執行 $command

接下去就是在容器中啓動.net core程序

dotnet watch run

查看工做臺輸出 已經啓動了程序的話, 在本地訪問 localhost:forward 應該訪問到容器上了

10.2.3.2.4 經過ssh鏈接容器調試

大多數ide都支持ssh遠程調試,這裏我以visual studioi爲例

alt+shift+p 附加調試,選擇 ssh ,配置參數

image-20200912202749683

以後就會讓你選擇要附加的進程了

image-20200912202913252

相關文章
相關標籤/搜索