kubernetes是一個用go語言寫的容器編排框架,常與docker搭配使用。
kubernetes是谷歌內部的容器編排框架的開源實現。能夠用來方便的管理容器集羣。具備不少 優勢,要了解這些優勢,須要先來了解一下kubernetes中的集羣資源。這裏指的是kubernetes裏的原生的資源,kubernetes也支持自定義的資源。html
不區分名稱空間前端
羣集默認採用RBAC(role base access control)
進行集羣資源的訪問控制,能夠將角色做用於用戶user
或服務service
,限制它們訪問羣集資源的權限範圍。node
其中角色又區分爲 集羣角色 cluster role
和普通的角色 role
,他們的區別是能夠做用的範圍不一致。mysql
普通角色必有名稱空間限制,只能做用於與它同名稱空間的用戶或服務。linux
集羣角色沒有名稱空間限制,能夠用於全部名稱空間的用戶或服務。下面的目錄中會詳細介紹。先在這裏提一下nginx
不指定時默認做用default名稱空間,服務在跨名稱空間訪問其餘服務時 域名須要加上名稱空間後輟才能訪問git
是一個包含操做系統的機器,操做系統能夠是Linux也能夠是windwos,能夠是實體機也能夠是虛擬機,其中的區別下面的其餘目錄會詳細說明程序員
持久卷 ,支持的類型不少,包括谷歌 亞馬遜 阿里云云服務提供商的各類存儲.github
因爲咱們的項目通常是用於局域網內的,因此這裏我着重介紹nfs(network file system)
web
存儲類,用於根據pvc 自動建立/自動掛載/自動回收 對應的nfs目錄前
工做量的最小單位是pod 其餘的類型的工做量都是控制Pod的。
pod至關於docker 中的docker composite,能夠由單個或多個容器組成,每一個pod有本身的docker網絡,pod裏的container處於同個局域網中。
其餘的控制器都有一個pod template,用於建立Pod
工做,一但應用到集羣將會建立一個pod作一些工做,具體的工做內容由Pod的實現決定,工做完成後Pod自動終結。
定時工做任務,一但應用到集羣,集羣將會定時建立pod 作一些工做,工做完成後pod自動終結
複製集或稱爲副本集,一但應用到集羣,會建立相n個相同的 pod,而且會維護這個pod的數量,若是有pod異常終結,replica set會建立一個新的Pod 以維護用戶指定的數量
deplyoment經常使用來建立無狀態的應用集羣。
部署,deplyoment依賴於replicaset ,它支持滾動更新,滾動更新的原理是,在原有的一個replica set的基礎上建立一個新版本的replica set ,
舊版本的replicaset 逐個減小 ,新版本的replicaset逐個新增, 能夠設置一個參數指定滾動更新時要保持的最小可用pod數量。
守護進程集 ,顧名思義,他的做用就是維護某個操做系統(node)的某個進程(pod)始終工做。當一個dameon set被應用到k8s集羣,全部它指定的節點上都會建立某個pod
好比日誌採集器 一個節點上有一個,用daemon 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關聯到原有的存儲捲上
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
使用:
Secret 是一種包含少許敏感信息例如密碼、令牌或密鑰的對象。 這樣的信息可能會被放在 Pod 規約中或者鏡像中。 用戶能夠建立 Secret,同時系統也建立了一些 Secret。
若是由集羣管理員管理,由開發人員應向集羣管理員申請Pv ,集羣管理員要手動的建立Pv,pvc,把pvc給開發人員,開發人員手動掛載pvc到pod
若是由storage class管理,則集羣管理員只要建立一個provider, 以後provider會自動監視集羣中的Pvc 和pod ,自動建立pv和掛載
kubernetes集羣中,至少要有一個主節點,主節點應該有如下組件
普通節點的組件
kubelet
kube-proxy
cni網絡插件
kubernetes節點代理組件,每一個node上都須要有,他不是Kubernetes建立的容器,因此在集羣中查不到。
他的主要作的工做是
1.向kube api以hostname爲名註冊節點
2.監視pod運行參數,使Pod以參數預期的狀態運行,因此Pod有異常一般都能查詢Kubelet的日誌來排查錯誤
kubernetes 的服務相關的組件,每一個Node上都須要有,除了無頭服務,其餘全部服務都由他處理流量
k8s集羣在初始化的時候,會指定服務網段和pod網段,其中服務網段的ip都是虛擬Ip
他主要作的工做是:
監視集羣的服務,若是服務知足某些條件,則經過ipvs 把這個服務的流量轉發到各個後端Pod (cluster ip)
cni: container network interface
k8s稱之爲窗口編排集羣,他的核心思想是把不一樣的容器網絡聯合起來,使全部的pod都在一個扁平的網絡裏,能夠互換訪問
爲達這個目的就須要容器網絡插件,下面介紹一下主流的cni插件,並大體說明一下優劣
flanel是橋接模式的表明插件,
他的工做原理是用daemonset在每一個節點上部署flannel插件,插件設置容器網絡並把容器網絡信息經過 kube api存儲到etcd中 。
這樣就確保不會重複註冊網段了,與不一樣node上的Pod經過 kube-proxy打包 發給其餘Node的kuber proxy,kuber proxy再拆包,發給pod
以達到跨node的扁平網絡訪問. 這種方式也稱vxlan 或overlay
優勢:
缺點:
calico 是網關模式的表明插件。 它主要由如下幾部分構成
它基於邊界網關協議 BGP(border gateway protocol)
他的工做原理是用daemonset在每一個節點上部署calico node, 來構成扁平化容器網絡
calico node由如下幾個組件
felix 負責編寫路由和訪問控制列表
confid 用於把 felix生成的數據記錄到etcd,用於持久化規則
BIRD 用於廣播felix寫到系統內核的路由規則和訪問控制列表acl和calico的網絡
當集羣規模比較大的時候還能夠可選的安裝 BGP Rotue Reflector(BIRD) 和 Typha
前者用於快速廣播協議,後者用於直接與ETCD通信,減少 kubeapi的壓力
優勢:
缺點:
etc distributed ,一款使用go語言編寫的基於raft共識算法的分佈式KV緩存框架 ,
不像redis重性能,而像zookeeper 同樣重數據一致性
特色是有較高的寫入性能
k8s 暴露給外部的web api,用於集羣的交互 有各類語言的api client開源項目 ,程序員也能夠在程序中引用,監視一些集羣資源
用於集羣中的service 和 pod的域名解析,
也能夠配置對集羣外的域名應該用哪一個DNS解析
用於 各類控制器(消耗cpu ram)的管理
用於 管理控制 Pod調度相關
raft是etcd的共識算法,kubernetes用etcd來存儲集羣的配置。config map /secret都是基於etcd。
理解raft共識算法能夠知道
在高可用環境, 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 } }
每一個主節點都部署了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
kubernetes集羣中的認證對象分爲
除此以外,還有一些其餘的非kubernetes集羣管理的服務會須要訪問集羣資源的狀況
可是這個暫時不實踐,由於haoyun目前不會使用到這種狀況
用戶不是kuebrnetes 的資源,因此單獨拎出來說。
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..#.略
建立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
相關的kubernetes 資源
roles和rolebindings 若是要創建關聯,他們必須是同一個名稱空間內。
clusterRoles和clusterRolebindings 沒有名稱空間的限制,它們的規則做用於集羣範圍
在 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 多出如下的能力
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
官方文檔
服務是微服務抽象,經常使用於經過選擇符訪問一組Pod, 也能夠訪問其餘對象,
上面這兩種狀況,服務的選擇符能夠省略。
kubernetes v1.0時使用用戶空間代理 (userspace)
v1.1添加了iptable代理模式,
v1.2默認使用iptables代理模式
v1.11添加ipvs 模式
當Node上不支持ipvs會回退使用iptables模式
每一個結點上部署kube-proxy ,它會監視主結點的apiserver對service 和 endpoints的增刪改
爲每一個server隨機開一個端口,並寫入集羣Ip寫入iptables,把對集羣服務的集羣Ip的流量 轉發到這個隨機的端口上 ,
而後再轉發到後端的Pod上, 通常是採用輪詢的規則,根據服務上的sessionAnfinity來設置鏈接的親和性
與userspace的區別是 不只把service寫入Iptables,同時把endpoints也寫入了iptables,
因此不用在內核空間和用戶空間之間來回切換,性能提高
ipvs(ip virtrual server)和iptables都是基於netfilter ,但ipvs以哈希表作爲基礎數據結構,並工做在內核空間
相比iptables,因此他有更好的性能,也支持更多的負載均衡算法
若是須要粘性會話,能夠在服務中設置
service.spec.sessionAffinity 爲 clusterip ,默認是none
service.spec.sessionAffinityConfig.clientIP.timeoutSeconds 能夠調整會話最長時間,默認是10800秒
服務能夠經過環境變量和DNS的方式來發現服務,推薦的作法是經過DNS
一個名稱爲 "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以前先啓動服務。
服務直接用服務名稱爲域名,
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應該儘量使用,最好不要使用環境變量的方式
虛擬的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
每一個主結點上的具體端口,一般把 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
一般把 外部流量 轉發到 一組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
將外部的流量引入服務 ,這種外部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
kube-proxy組件不對無頭服務進行代理,無頭服務 加上序號 指向 Pod,固定搭配,
因此即便 服務的pod掛了, 重啓來的服務的 域名也不會換一個,用於有狀態的服務。
後面講到Pod控制器statefulset會再細講
kube-proxy組件不會對外部服務進行代理則是映射到dns 用於描述一個集羣外部的服務,有解耦合的做用,
因此它和無頭服務同樣沒有選擇器,他也不禁集羣管理,而是由集羣管理員維護
apiVersion: v1 kind: Service metadata: name: my-service namespace: prod spec: type: ExternalName externalName: my.database.example.com
因爲iptables代理模塊或亦 ipvs代理模式都是4層負載均衡,沒法對7層協議進行負載均衡,因此對於外部的流量 ,常使用入口資源來進行負載均衡,把外部的流量均衡到服務上
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
Ingress 公開了從集羣外部到集羣內服務的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 資源上定義的規則控制。
能夠將 Ingress 配置爲服務提供外部可訪問的 URL、負載均衡流量、終止 SSL/TLS,以及提供基於名稱的虛擬主機等能力。 Ingress 控制器 一般負責經過負載均衡器來實現 Ingress,儘管它也能夠配置邊緣路由器或其餘前端來幫助處理流量。
Ingress 不會公開任意端口或協議。 將 HTTP 和 HTTPS 之外的服務公開到 Internet 時,一般使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 類型的服務
`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
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
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
建立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從新建立
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後,去查看掛載上去的卷,文件中的值也隨之發生了改變,因此這種方式是比較好的方式。
Secret 是一種包含少許敏感信息例如密碼、令牌或密鑰的對象。 這樣的信息可能會被放在 Pod 規約中或者鏡像中。 用戶能夠建立 Secret,同時系統也建立了一些 Secret。
#生成文件 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
說明:特殊字符(例如 $
、*
、*
、=
和 !
)可能會被 sell轉義,因此要用''括起來
kubectl create secret generic dev-db-secret \ --from-literal=username=devuser \ --from-literal=password='S!B\*d$zDsb='
加密用戶名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
建立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 的模版,能夠配置明文的字符
[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
和config map一毛同樣,略
集羣管理員建立storage class
storage class監視集羣中的 pvc和pod
PersistentVolume(PV)是已經由管理員提供或者動態使用供應的集羣中的一塊存儲的存儲類。它是集羣中的資源,就像節點是集羣資源同樣。PV是相似於Volumes的卷插件,可是其生命週期與使用PV的任何單個Pod無關。
若是用戶刪除了Pod正在使用的PVC,則不會當即刪除該PVC。PVC的清除被推遲,直到任何Pod再也不主動使用PVC。另外,若是管理員刪除綁定到PVC的PV,則不會當即刪除該PV。PV的去除被推遲,直到PV再也不與PVC結合。
當用戶完成其卷處理後,他們能夠從容許回收資源的API中刪除PVC對象。PersistentVolume的回收策略告訴集羣在釋放其聲明以後如何處理該卷。當前,能夠保留,回收或刪除卷。
這種策略在pvc刪除後,會保留pv,並釋放pv,但這個pv不能被其餘pvc重用。若是要回收須要集羣管理員手動的去回收,回收步驟以下
默認的策略是刪除,刪除Pvc ,若是存儲卷類型支持的話,將會同時刪除pv及其中的文件數據
若是pv的介質支持的話,此回收策略將會使用
rm -rf /volume/*
清理pv的數據,而後使這個pv能夠被其餘pvc使用,因爲這樣常常會致使意外終結的Pod,pv裏的數據來不及排查就被回收,因此這種方式已被 棄用。應該使用動態配置+手動回收來避免這種狀況發生。
因爲咱們集羣環境是私有的局域網,因此一般只會使用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
pv一旦設置了節點親和力,則與Pv結合的Pod都會部署到命中的節點上
PersistentVolumeClaim(PVC)是由用戶進行存儲的請求。它相似於pod。pod消耗節點資源,PVC消耗PV資源。Pod能夠請求特定級別的資源(CPU和內存)。聲明能夠請求特定的大小和訪問模式
靜態供應狀況下,pvc綁定Pv
動態供應狀況下,pvc綁定 storage class
雖然PersistentVolumeClaims容許用戶使用抽象存儲資源,但對於不一樣的pod,用戶一般須要具備不一樣存儲介質,例如機械硬盤和固態硬盤,機房1和機房2,集羣管理員須要可以提供各類PersistentVolume,這些PersistentVolume不只在大小和訪問模式上有更多差別,並且還不讓用戶瞭解如何實現這些卷的細節。
這個是推薦使用的方式,因此這裏重點實踐
略,
驗證
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/
略
用工具安裝部署完nfs provisioner時已經生成,略
建立文件 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
建立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個月的歸檔沒有開發人員認領,就刪除掉)
補充兩種卷,
當 Pod 指定到某個節點上時,首先建立的是一個 emptyDir
卷,而且只要 Pod 在該節點上運行,卷就一直存在。 就像它的名稱表示的那樣,卷最初是空的。 儘管 Pod 中的容器掛載 emptyDir
卷的路徑可能相同也可能不一樣,可是這些容器均可以讀寫 emptyDir
卷中相同的文件。 當 Pod 由於某些緣由被從節點上刪除時,emptyDir
卷中的數據也會永久刪除。
說明: 容器崩潰並不會致使 Pod 被從節點上移除,所以容器崩潰時
emptyDir
卷中的數據是安全的。
emptyDir
的一些用途:
默認狀況下, 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: {}
不經常使用的方式, Pod掛載node上的文件目錄到pod中,略
[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
建立一個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
查看運行狀態,確實跑到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
驗證經過
污點是打在node上的標記,容忍是打在Pod上的標記
查看主節點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的污點
先用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
表示能夠容忍節點上存在這個污點
略
親和力/反親和力 又分爲
node親和力用於限定Pod部署到Node上的命中規則,
Pod親和力則用於限定 Pod與pod 佈置到同一個Node上
他們都有多種匹配規則和 「軟」 「硬」兩次匹配策略
交叉相實際上是四種不一樣的設置,但因爲他們大同小異,故在此只以節點的親和力來作說明。
節點親和力 /反親和力使用步驟以下
例如給名爲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
查看 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
儘量相比起節點選擇器,他有更靈活的匹配替換,並且能夠有「軟」,「硬」兩種策略
寫法相對也複雜得多。
因爲已經部署過了,因此直接查看,能夠看到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>
pod不會自動消息,除非自願干擾或 非自願干擾
PDB(pod disruption budget)
非自願干擾
自願干擾
pdb用於確保pod驅逐的過程當中服務的可用Pod數量在安全的範圍內。
官方文檔對不一樣的部署方案的例子
無狀態前端:
單實例有狀態應用程序:
多實例有狀態應用程序,例如Consul,ZooKeeper或etcd:
可從新啓動的批處理做業:
關注:在自願中斷的狀況下,工做須要完成。
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
這裏只着重介紹資源不足致使的Pod被驅逐,由於其餘狀況都與配置無關
eviction api是一組kubelet的api,用於指定當Node的資源不足時,(通常是指硬盤空間不足或內存不足的狀況 ,)要如何驅逐Pod以保證node的資源始終在合理的範圍內,能夠在kubelet 的config map裏配置,或者命令行啓動參數的方式設置eviction api, 可參閱文檔
一個例子:
10Gi
kubelet
等)。爲了促成這個場景,kubelet
將像下面這樣啓動:
--eviction-hard=memory.available<500Mi --system-reserved=memory=1.5Gi
這個配置的暗示是理解系統保留應該包含被驅逐閾值覆蓋的內存數量。
要達到這個容量,要麼某些 pod 使用了超過它們請求的資源,要麼系統使用的內存超過 1.5Gi - 500Mi = 1Gi
。
這個配置將保證在 pod 使用量都不超過它們配置的請求值時,若是可能當即引發內存壓力並觸發驅逐時,調度器不會將 pod 放到這個節點上。
本節只會介紹排空,由於2 通常用於雲供應商平臺,3太簡單略。
有時候某個Node須要中止運行維護,好比加內存之類的操做,這時若是隻是刪除Node上的pod, 則很大機率新的pod會從新被調度到這個node上,這種狀況下集羣管理員應該排空這個node,在維護結束後再結束以後再使用uncordon,使Pod能被調試到這個維護結束的node上
例如對 www.haoyun.edge1結點作排空操做
kubectl drain www.haoyun.edge1
維護結束以後恢復
kubectl uncordon www.haoyun.edge1
略,須要配合promethues 之類的數據採集纔可使用,可能以後專門講promethues再討論,由於這個功能對浩雲來講不是很重要
寫在docker file裏
使用helm 寫pod 參數裏傳遞
使用config map
略
參見 6.配置和存儲
略
kubelet 使用存活探測器來知道何時要重啓容器。 例如,存活探測器能夠捕捉到死鎖(應用程序在運行,可是沒法繼續執行後面的步驟)。 這樣的狀況下重啓容器有助於讓應用程序在有問題的狀況下更可用。
kubelet 使用就緒探測器能夠知道容器何時準備好了並能夠開始接受請求流量, 當一個 Pod 內的全部容器都準備好了,才能把這個 Pod 看做就緒了。 這種信號的一個用途就是控制哪一個 Pod 做爲 Service 的後端。 在 Pod 尚未準備好的時候,會從 Service 的負載均衡器中被剔除的。
kubelet 使用啓動探測器能夠知道應用程序容器何時啓動了。 若是配置了這類探測器,就能夠控制容器在啓動成功後再進行存活性和就緒檢查, 確保這些存活、就緒探測器不會影響應用程序的啓動。 這能夠用於對慢啓動容器進行存活性檢測,避免它們在啓動運行以前就被殺掉。
探針探測類型
探針可配置項
啓動探針 startupProbe
就緒探針 readinessProbe
存活探針 livenessProbe
因爲各類探針的寫法是同樣的,只是名稱不一樣,做用也不一樣
因而這裏以存活探針爲例,實踐如下幾種探針的探測方法
命令返回成功存活,失敗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秒檢測一次
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
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
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 不能解析服務名稱。
爲了第十章,必須把helm的一直基本姿式介紹一下,因此有第9章
helm是kubernetes的包管理器,用於查找,分享,使用kubernetes生態的應用。
通常helm都是安裝在主節點上,開發人員用有權限範圍的用戶登陸上去操做helm便可,
能夠和git結合使用,或安裝helm局域網服務器Tiller
也能夠在開發人員機器上經過config+kubectl 直接鏈接 上集羣 ,可是這樣開發人員須要在本身電腦上安裝kubectl。
不管是windows仍是linux,都是直接去下載helm的二進制文件。
linux複製helm二進制到bin目錄:
mv helm /usr/bin
windows設置環境變量:
個人電腦屬性->高級->設置環境變量->path+=
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 # 可選: 安裝成功時,顯示在終端上的文字
helm語法是yaml混合 go模版的語法
聽起來很複雜,其實學起來不復雜
僅須要抓着helm chart安裝生成的文件抽絲剝繭,就能快速掌握,由於自己並不複雜
上下文
.
根
.value
value.yaml
.release
安裝時用戶輸入
.chart
chart.yaml
模版
其餘語法 去官網文檔查
現有兩個.net core web app
- api
- blazor
前端將經過ingress暴露給集羣外部,後端則只在集羣內部。以此爲例實踐第10章
略
這個在前端和後端web app中都須要作,由於都要部署到集羣。
在visual studio 安裝10.1中的擴展後
選擇kubernetes/helm
肯定後生成chart文件夾和docker file,還有一個azds.yaml (部署到微軟雲上纔用到),
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
.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.1,略
launchSetting.json
{ "profiles": { "HScadaEx.Blazor": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "userApi": "http://localhost:5001" }, "applicationUrl": "http://localhost:5011" } } }
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>(); }
我但願訪問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 }}
默認的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"]
工做節點
上主節點
上因爲我的計算機資源有限,因此我這裏只使用docker save
和docker load
命令 將docker鏡像複製到僅有的一個工做節點上。正確的作法是在集羣外部搭建一個docker私有倉庫,按需拉取。
已知私有倉庫部署方式
對於 2和3 有認證受權的鏡像
正確的姿式是配置好證書後,導入爲secret資源,在拉取鏡像時指定secret資源便可。
一樣 chart也有私有服務器能夠搭建,名爲Tiller
,正確的姿式也是應該搭服務器,讓集羣調度的時候按需獲取,
更多信息查看官網文檔 或 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名稱
下載kubectl二進制,添加到系統變量path
複製集羣管理員生成的 config文件到 c:\用戶\.kube 文件夾
#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"]
這裏用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,在代碼重生成時實時響應到容器中,並重啓容器中的程序
okteto是一個開源的項目,用於簡化各類技術棧 在kubernetes中的開發工程。
傳統的kubernetes 服務開發過程就是不斷的重複這個過程
while(調試ing) { 附加調試->修改代碼->生成docker鏡像->推送鏡像->拉鏡像->更新到集羣 }
okteto的方式是把開發環境打包成一個鏡像,實時同步容器與本地的文件變化,轉發本地流量到容器 、轉發容器流量到本地 等鏈接調試用的是ssh ,配合dotnet watch run 能夠僅是生成代碼就把生成的結果應用到容器中,雖然不是真正的熱重載,容器中的程序會重啓,但快速了不少,並且這是各類技術棧都能用的,不只限於.net 的萬金油,因而流程簡化爲
do { dotnet watch run }while(調試ing) { 附加調試->修改代碼->生成 }
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: {}
okteto up
這裏集羣裏會多出一個deployment 和service 資源, 跑起一個pod,這個pod是空的,僅僅是一個開發環境,
而後okteto會並 當前文件夾的文件同步到遠程容器中的$workdir
,並執行 $command
接下去就是在容器中啓動.net core程序
dotnet watch run
查看工做臺輸出 已經啓動了程序的話, 在本地訪問 localhost:forward
應該訪問到容器上了
大多數ide都支持ssh遠程調試,這裏我以visual studioi爲例
alt+shift+p 附加調試,選擇 ssh ,配置參數
以後就會讓你選擇要附加的進程了