k8s kubernetes 核心筆記 鏡像倉庫 項目k8s改造(含最新k8s v1.16.2版本)


k8s kubernetes 核心筆記 鏡像倉庫 項目k8s改造

2019/10/24 Chenxin

一 基本資料

一 參考:
https://kubernetes.io/ 官網
https://kubernetes.io/docs/home/ 官網文檔
<<kerbernetes 權威指南>> <<每天5分鍾玩轉k8s>> 圖書
aws 和 阿里雲 的k8s 集群,請參考對應雲知識筆記內容.

二 說明:
docker內容請參考<<Docker 基礎>>,<Dockerfile 以及 Docker compose>> 筆記
k8s yaml 配置文件請參考對應單獨筆記內容.
k8s 文件說明請參考對應獨立筆記內容.

二 k8s常用指令

kubectl get all    # 查看所有
kubectl get all --all-namespaces
kubectl get pods|deployments.|replicasets.|service -o wide
kubectl get nodes --show-labels    # 查看node標簽

kubectl describe pods|deployments.|replicasets.|service xxx_name
kubectl apply -f xxx.yml

kubectl delete pods|deployments.|replicasets.|service xxx_name
kubectl delete -f xxx.yml

kubectl edit daemonsets. kube-proxy --namespace=kube-system    # 編輯內存里配置文件.應該是及時修改后生效,但測試並未生效
kubectl edit service nginx-svc

kubectl logs pod_name    # 查看pod內進程輸出 (查看最后200行 kubectl logs --tail=200 pod_name 或最后1小時 --since=1h  )
kubectl api-versions    # 查看k8s當前支持的api版本
kubectl run busybox --rm -it --image=busybox /bin/sh    # 臨時啟用一個工具箱
kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword    # 臨時啟動一個mysql客戶端
kubectl apply -f httpd.v1.yml --record    # 登記到revision,用於回滾
kubectl rollout history deployment httpd    # 查看歷史記錄,為回滾做准備
kubectl rollout undo deployment httpd --to-revision=1   # 回滾到版本1
kubectl exec mypod-id touch /mydata/hello    # 到Pod的container里執行指令
kubectl exec mypod-id df | grep data    # 到Pod里執行指令
kubectl create configmap game-config --from-file=docs/gameconfig/    # 目錄內含有多個配置文件(每個配置文件里可以有很多內容)
journalctl -l -u kubelet    # 查看kubelet日志

kubectl get pods -o yaml     # 配置yaml格式詳情
kubectl get endpoints    # 查看終端服務節點

故障排查主要使用
kubectl logs pod_name     # 查看控制台輸出(pod運行的報錯信息)
kubectl describe pods|deployments.|replicasets.|service xxx_name

kubectl top node node1 --v=8  #開啟debug模式輸出.使用top指令查看資源情況,需部署heapster(1.8之前版本默認提供.之后修改為service-metric).參考https://github.com/helm/charts/tree/master/stable/heapster

自動命令補全額外說明
具體請參考本文檔"master命令自動補全"部分.如果失效,需要手動再次 source <(kubectl completion bash )

三 架構+安裝服務(master+node)

架構說明
1個master,2個node(192.168為二次實驗地址)
k8s-master 10.0.0.105 192.168.143.130
k8s-node1 10.0.0.106 192.168.143.131
k8s-node2 10.0.0.107 192.168.143.132

版本說明
首次測試為k8s的1.13版本,第二次測試為1.16.1版本

操作系統版本說明
k8s只支持centos7.x以上版本.
這里采用系統版本為:
AWS Marketplace的CentOS官方鏡像而來(更新時間: 19/1/31) 默認為7.6版本(2018/10).
修改/etc/hostname

更新系統
yum update -y # 更新到最新系統版本

更新iptables(略過)
RHEL7.4無需更新iptables,因為iptables -V -> iptables v1.4.21

配置網絡橋接與路由轉發
/etc/sysctl.conf 文件增加

net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

安裝/啟動docker

a.配置yum倉庫

b.正式安裝
yum install -y docker-ce
docker --version -> Docker version 18.09.2 (首次版本,第二次為Docker version 19.03.3, build a872fc2f86)

c.啟動docker
systemctl start docker.service
systemctl enable docker.service

報錯解決
Error: Package: 3:docker-ce-18.09.2-3.el7.x86_64 (docker-ce-stable) Requires: container-selinux >= 2.9
到centos官網尋找最新的container-selinux,然后安裝,如下

yum install -y http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.74-1.el7.noarch.rpm  

安裝k8s的kubeadm, kubelet 和 kubectl
參考: https://kubernetes.io/zh/docs/setup/independent/install-kubeadm/

  • 國內-阿里雲
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
  • 國外-配置google的源地址(也可以采用國內阿里雲,中科大鏡像)
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kube*
EOF

將 SELinux 設置為 permissive 模式(鏡像默認已經將其禁用)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

master+node安裝
yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes # 會自動安裝conntrack-tools cri-tools ebtables socat等依賴包

添加開機自啟
systemctl enable docker.service # master+node都需要.
systemctl enable kubelet.service # master+node都需要.mster:先啟動kubelet,通過它自啟apiserver,controller-manager,scheduler.node:先kubelet后,自動通過kubelet啟kube-proxy

到此可以重啟一下系統.

四 創建集群

master初始化集群

1.綁定host

[root@master ~]# cat /etc/hostname 
master
[root@master ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.143.130 master
[root@master ~]# hostname
master

2.確保master上的docker已啟動
ps axf|grep dockerd

3.(選做)可以將系統鏡像里默認開啟的rpc暫時關掉,並開機不啟動
systemctl stop rpcbind.service rpcbind.socket
systemctl disable rpcbind.service rpcbind.socket

4.master上進行init

[root@ip-10-0-0-105 ~]# 
普通執行 kubeadm init --apiserver-advertise-address 10.0.0.105 --pod-network-cidr=10.244.0.0/16
或更改網絡段  kubeadm init --apiserver-advertise-address 192.168.134.130 --pod-network-cidr=10.10.0.0/16
或指定k8s版本 kubeadm init --apiserver-advertise-address 192.168.134.130 --pod-network-cidr=10.10.0.0/16 --kubernetes-version=v1.16.1
或指定拉取國內鏡像 kubeadm init --apiserver-advertise-address 192.168.134.130 --pod-network-cidr=10.10.0.0/16 --kubernetes-version=v1.16.1 --image-repository=registry.aliyuncs.com/google_containers

失敗后的重新初始化(先復位 kubeadm reset) 
嘗試1.kubeadm init --apiserver-advertise-address 192.168.134.130 --pod-network-cidr=10.10.0.0/16 --image-repository=registry.aliyuncs.com/google_containers 
嘗試2.kubeadm init --apiserver-advertise-address 0.0.0.0 --pod-network-cidr=10.10.0.0/16 --image-repository=registry.aliyuncs.com/google_containers 

提示信息如下(正常執行狀態)
[preflight] Running pre-flight checks    # kubeadm執行初始化前的檢查
[certs] Using certificateDir folder "/etc/kubernetes/pki"    # 生成token和證書
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"    # 生成kubeconfig文件,kubelet需要這個文件與master通信
[apiclient] All control plane components are healthy after 21.506225 seconds    # 安裝master組件,會從Google的Registry下載組件的Docker鏡像.
[addons] Applied essential addon: CoreDNS    # 安裝附加組件 kube-proxy和 kube-dns
Your Kubernetes master has initialized successfully!    # Kubernetes Master初始化成功

To start using your cluster, you need to run the following as a regular user:    # 提示如何配置kubectl
  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config
  
You should now deploy a pod network to the cluster.    # 提示如何安裝Pod網絡
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/
  
You can now join any number of machines by running the following on each node as root:    # 提示注冊其他的node節點到這個cluster
  kubeadm join 10.0.0.105:6443 --token xj0xjp.d6r8fzuecc1ve0bu --discovery-token-ca-cert-hash sha256:cd505e2d2494e429cac81e50942c7c3d4eda50908fddccdb667aefccf0543517
或
  kubeadm join 192.168.143.130:6443 --token cgy56z.02yki9avqnukpxtg \
    --discovery-token-ca-cert-hash sha256:152fd4be14843846e48c6752b1bd003bf7fb5e56e22085b31002f6babd58a66f 

init報錯收集(解決辦法)

[ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1
[ERROR Swap]: running with swap on is not supported. Please disable swap

a.需要關閉swap,"swapoff -a".
b.echo "1" >/proc/sys/net/bridge/bridge-nf-call-iptables (橋接模式/etc/sysctl.conf里net.bridge.bridge-nf-call-iptables = 1 ;如果系統無bridge-nf-call-iptables文件,則"modprobe br_netfilter"向內核中加載該模塊).
c.國內訪問,會自動docker pull k8s.gcr.io/xxx 需要翻牆(也可以通過從國內pull后打tag的方式變相處理,具體請百度搜索).

5.執行init失敗后采取的恢復方法
a.init執行失敗,重新執行init的時候,需要添加 --ignore-preflight-errors=all ,這樣會生成多個cluster(后期再刪除)
b.執行 kubeadm reset 命令將主機恢復原狀(注意,會將下載完的多個docker image也一並刪除),然后重新執行 kubeadm init 命令再次進行init安裝.

master配置kubectl
根據init初始化中的提示(root執行會有一些問題)這里創建一個centos用戶來執行.
useradd centos
visudo里添加 centos ALL=(ALL) NOPASSWD: ALL # 涉及拷貝root權限的文件

拷貝配置文件
su - centos;
cd ~; mkdir .kube; sudo cp -i /etc/kubernetes/admin.conf .kube/config; sudo chown $(id -u)😒(id -g) .kube/config
如果希望kubectl命令可以在root下執行,如kubectl get nodes,則切換到root后,執行上面這行指令.否則k8s會提示拒絕連接.

master啟用kubectl自動補全
a.bash客戶端工具
在centos用戶下,
cd ~;echo "source <(kubectl completion bash)" >> .bashrc
或如 . <(kubectl completion bash) 當然,也可以寫入到/etc/profile里.退出當前終端,重啟進去,以便加載source.

b.其他交互式 Kubernetes 客戶端工具
參考 https://www.hi-linux.com/posts/44953.html
1.bash 略
2.ZSH-> echo "source <(kubectl completion zsh)" >> ~/.zshrc
3.Kube-prompt
4.Kube-shell
5.Kubectl Aliases

c.故障排查
在使用一段時間的kubectl命令自動補全后(安裝k8s的metrics-server后),發現有些失效了
需要手動重新執行: source <(kubectl completion bash)
原因,未知,可能是被覆蓋過.

master安裝pod網絡
在centos用戶下,
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

master配置k8s自啟動
systemctl list-unit-files |grep kubelet.service

node加入master
1.確保每個node上的docker已啟動 -> ps axf|grep dockerd
2.master上查看token -> [root@ip-10-0-0-105 log]# kubeadm token list
3.修改hostname和hosts文件(類似master)
4.同樣需要關閉swap,打開網絡橋接(略,同master)
5.到各個node上,執行加入master指令

[root@ip-10-0-0-106 ~]# kubeadm join 10.0.0.105:6443 --token nc8...8zihj --discovery-token-ca-cert-hash  sha256:9d...a5
或 kubeadm join 192.168.143.130:6443 --token cgy56z.02yki9avqnukpxtg --discovery-token-ca-cert-hash sha256:152fd4be14843846e48c6752b1bd003bf7fb5e56e22085b31002f6babd58a66f
輸出信息
...Run 'kubectl get nodes' on the master to see this node join the cluster.

6.開啟開機自啟動 -> systemctl enable kubelet.service
7.當master長時間運行后,加入新的node方法
重新獲取token以及ca對應的hash值方法.默認token的有效期為24小時,當過期之后,該token就不可用了。解決方法,
a.重新生成新的token: kubeadm token create
b.查看token: kubeadm token list
c.獲取ca證書sha256編碼hash值:
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
d.節點重新加入集群:
kubeadm join 10.0.0.105:6443 --token cbdz...sj7 --discovery-token-ca-cert-hash sha256:ee...452d --ignore-preflight-errors=All

刪除+清理節點
因加入node的hostname忘記修改了,故刪掉重做.

1.master上操作
執行命令
kubectl drain localhost.localdomain --delete-local-data --force --ignore-daemonsets
kubectl delete node localhost.localdomain
檢查命令
kubectl get nodes

2.node上操作
在master上面刪除node並不會清理localhost.localdomain上面運行的容器,需要在刪除節點上面手動運行清理命令
執行命令(重置所有kubeadm安裝狀態)
kubeadm reset
檢查命令
按y重置狀態,重置后docker ps檢查容器消失

檢查node,pod狀態(master)
(會由於網絡pull原因,需要等待一段時間才會正常顯示如下)

[centos@master ~]$ kubectl get nodes 
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   78m   v1.16.1
node1    Ready    <none>   33m   v1.16.1
node2    Ready    <none>   32m   v1.16.1

[centos@master ~]$ kubectl get pods --all-namespaces -o wide
NAMESPACE     NAME                          READY STATUS  RESTARTS IP              NODE   
kube-system   coredns-58cc8c89f4-4djwg      1/1   Running 0        10.10.0.2       master 
kube-system   coredns-58cc8c89f4-p9kmk      1/1   Running 0        10.10.0.3       master 
kube-system   etcd-master                   1/1   Running 0        192.168.143.130 master 
kube-system   kube-apiserver-master         1/1   Running 0        192.168.143.130 master 
kube-system   kube-controller-manager-master1/1   Running 1        192.168.143.130 master 
kube-system   kube-flannel-ds-amd64-8zqxr   1/1   Running 2        192.168.143.131 node1  
kube-system   kube-flannel-ds-amd64-mkrx6   1/1   Running 0        192.168.143.130 master 
kube-system   kube-flannel-ds-amd64-rvxr5   1/1   Running 0        192.168.143.132 node2  
kube-system   kube-proxy-cnqzl              1/1   Running 0        192.168.143.132 node2  
kube-system   kube-proxy-npwpj              1/1   Running 0        192.168.143.130 master 
kube-system   kube-proxy-prblt              1/1   Running 2        192.168.143.131 node1  
kube-system   kube-scheduler-master         1/1   Running 1        192.168.143.130 master 

到此,可以隨意重啟master 或 node 機器,而k8s集群都可以正常啟動.

其他安裝部署方式說明
1.RKE (單master)
使用Kubernetes的輕量級工具——Rancher Kubernetes Engine(RKE)進行Kubernetes的安裝部署。RKE是一個用Golang編寫的Kubernetes安裝程序,相對於其它的工具來說,更加簡單易用和上手。
具體請參考Rancher官網說明,或其他網上資源.
2.Sealos (多master)
多master,多node的安裝方式.為k8s高可用方案之一.具體請參考本文檔"高可用"部分.

五 k8s服務+應用部署流程

Master
Master上運行的Pod(或開放的Port)包括:
coredns 負責集群內的解析
etcd 服務發現機制(鍵值對存放).負責保存k8s Cluster的配置信息和各種資源狀態信息.數據發生變化時,etcd會快速通知k8s的相關組件.
kube-apiserver 是供CLI或UI等外部組件訪問Cluster的API.
kube-controller-manager 例如replication controller負責管理Deployment.
kube-scheduler 決定將Pod放到哪個Node上運行.
kube-proxy 通過proxy代理,將請求轉發給對應的后端node的Pod
kube-flannel(Pod network) 可以提供Pod間相互通信的基礎.比如flannel.

Node
Node上運行的Pod或Deamon/Port包括:
kubelet Node的agent,當Scheduler確定在哪個Node上運行Pod后,將Pod配置信息(image,volume)發給該節點kubelet.kubelet據此創建容器,並向Master報告狀態.
kube-proxy service代表了后端的多個Pod,外界通過service訪問Pod.service接收到的請求通過kube-proxy轉發給Pod.若有多個副本,kube-proxy會實現負載均衡.
kube-flannel(Pod network) 可以提供Pod間相互通信的基礎.比如flannel.

部署pod流程說明
master上執行:

kubectl run httpd-app --image=httpd --replicas=2
kubectl get deployments
kubectl get pods -o wide    # 會在master上顯示各個pod的具體信息(包括對應node主機名和pod的IP)
NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE            NOMINATED NODE   READINESS GATES
httpd-app-f9ccf4675-9zlkz   1/1     Running   0          31m   10.244.3.2   ip-10-0-0-107   <none>           <none>
httpd-app-f9ccf4675-tkvt5   1/1     Running   0          31m   10.244.1.2   ip-10-0-0-106   <none>           <none>

kubectl發送部署請求到api-server.
api-server通知controller-manager創建一個Deployment資源.
scheduler執行調度任務,將兩個副本Pod分發到node1和node2.
node1和node2上的kubelet在各自的節點上創建並運行Pod.

補充說明
master上不會pull httpd的image,而由各個node去pull.然后在各自node上運行container.
應用的配置和當前狀態信息保存在master的etcd中,執行kubectl get pods時,api-server會從etcd中讀取.
flannel會為每個Pod都分配IP.因為沒有創建service,所以目前kube-proxy還沒有參與進來.

運行應用 controller-manager

k8s通過各種Controller來管理Pod的生命周期.為滿足不同的業務場景,k8s具有以下幾種controller:
Deployment,
ReplicaSet,
DaemonSet,
StatefuleSet,
Job.

進行run (Deployment)
kubectl run nginx-deployment --image=nginx:1.7.9 --replicas=2 # 等同於 kubectl run --generator=deployment/apps.v1 nginx-deployment --image=nginx:1.7.9 --replicas=2
提示:
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use
kubectl run --generator=run-pod/v1 or kubectl create instead.

k8s新版本可以用以下方式,然后再scale
kubectl create deployment --image=nginx:1.7.9 nginx-deployment
kubectl scale deployment nginx-deployment --replicas=2
注意,create一般是用於yml的配置文件.

換個頻道,按照提示運行 kubectl run --generator=run-pod/v1 nginx-deployment --image=nginx:1.7.9 --replicas=2
這里replicas沒有生效,只創建了1個pod.為什么?
沒有再生產Deployment,而是直接創建了pod,為什么呢?
如何scale這種pod?
可以使用kubectl delete pods podname來刪除創建的pod.

查看Deployment
[centos@ip-10-0-0-105 ~]$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 2/2 2 2 9m23s

查看Deployment詳細信息
[centos@ip-10-0-0-105 ~]$ kubectl describe deployments

查看ReplicaSet
[centos@ip-10-0-0-105 ~]$ kubectl get replicasets

查看由哪個Deployment而來以及此pod創建過程
[centos@ip-10-0-0-105 ~]$ kubectl describe pods nginx-deployment-578fb949d8-2rgwj

以上流程的總結
用戶通過kubectl 創建 Deployment.
Deployment 創建 ReplicaSet.
ReplicaSet 創建 Pod.
也可以看得出來,對象的命名方式是"子對象名字=父對象名字+隨機字符串".這樣,可以從Pod名字上追溯到ReplicaSet,再追溯到Deployment.

k8s master 與 node 上各配置文件解析說明

請參考<<k8s 軟件配置文件解析>>筆記

k8s API用法查詢 + YAML文件 + kubectl apply使用

具體參見<<k8s yml yaml 配置文件語法解析>>筆記
查看api版本
kubectl api-versions 查看當前k8s支持哪些api版本
kubectl explain Deployment.spec 查看用法/幫助手冊(支持多個子項目,用"."表示)

示例

[centos@master deployment]$ cat nginx.yml 
apiVersion: apps/v1 # apiVersion是當前配置格式的版本
kind: Deployment    # kind 是要創建的資源類型, 這里是Deployment.
metadata:   # metadata 是該資源的元數據, name 是必須的元數據項.
  name: nginx-deployment
spec:   # spec 部分是該Deployment的規格說明.
  selector: #新加入部分
    matchLabels:
      app: nginx-deployment
  replicas: 2   # replicas 指明副本數量, 默認是1.
  template: # template 定義Pod 的模板, 這是配置文件的重要部分.
    metadata:   # metadata 定義Pod的元數據, 至少要定義一個label. label 的key和value 可以任意指定.
      labels:
        app: nginx-deployment
    spec:   # spec 描述 Pod的規格,此部分定義Pod中的每一個容器的屬性, name 和 image 是必須的.
      containers:
      - name: nginx
        image: nginx:1.7.9
        #ports:
        #        #- containerPort: 80

創建
kubectl apply -f nginx.yml

查看
以下指令輸出相同
kubectl get deployments # kubectl get deployments. 或kubectl get deployments.apps 或kubectl get deployments.extensions
kubectl get replicasets # kubectl get replica. replicasets.apps replicasets.extensions
kubectl describe deployments # kubectl describe deployments. deployments.apps deployments.extensions

刪除
kubectl delete deployments. nginx-deployment
kubectl delete -f nginx.yml

伸縮
增加: 當前已經有2個pod處於running狀態.想增加到5個.則修改 nginx.yml,將 replicas: 5 ,然后再次 kubectl apply -f nginx.yml
減少: 同上.

宕機
將node2關機,k8s會自動在node1上開啟新的pod,以達到配置文件里的replicas數量.這個過程大約需要5分鍾以上.
宕機的node會由 Ready 變成 NotReady (需要1分鍾)
宕機的pod會由 running 變成 Terminating (需要5分鍾)

恢復宕機
啟動node2機器后,並不會將當前正常的pod再重新分一部分到node2上去.只有新的pod會發過去.
node2啟動后,可能需要手動 systemctl start kubelet.service(在沒有enable的情況下)

重啟node1機器
因node1 reboot時間比較短(aws雲主機),k8s在短時間內,並不會在node2上創建pod.而是node1重啟后,其上的pod直接重啟.kubectl get pods -o wide: node2上的3個pod的RESTARTS會由0->1.

用label控制Pod的位置

給對應node打個label: kubectl label nodes ip-10-0-0-106.ap-southeast-1.compute.internal disktype=ssd
查看label: kubectl get nodes --show-labels
修改nginx.yml配置文件,在containers同級,添加
nodeSelector:
disktype: ssd
應用配置: kubectl apply -f nginx.yml
確認全部pod在正確的node上產生: kubectl get pods -o wide
刪除label: kubectl label nodes ip-10-0-0-106.ap-southeast-1.compute.internal disktype-
這時pod不會改變: kubectl get pods -o wide
即使重新apply也不會改變pod(因為沒有合適的node可用,會提示"unchange"): kubectl apply -f nginx.yml; kubectl get pods -o wide
恢復nginx.yml配置文件: 注釋掉修改部分
應用配置: kubectl apply -f nginx.yml; kubectl get pods -o wide # 已恢復至修改label前的狀態.

嘗試修改node1,2的主機名試試k8s是否異常
會異常,查看kubelet日志: journalctl -l -u kubelet

DaemonSet類型的controller說明

kubectl get pods --namespace=kube-system -o wide # -> 包括 kube-flannel-ds 和 kube-proxy

kube-flannel-ds
在部署pod網絡的時候, 使用 kubectl apply -f https://....yml , 即使用的這個yml文件:
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
從中可以看到 DaemonSet 的 kube-flannel 配置.

kube-proxy
由於無法拿到kube-proxy的yml配置文件,只能這樣查看,
kubectl edit daemonsets. kube-proxy --namespace=kube-system

其實, k8s集群中的每個當前運行的資源,都可以通過 kubectl edit 查看配置和運行狀態.比如,
kubectl edit deployments. nginx-deployment

Job類型的controller說明

Job即為工作類容器類型.
做完工作,就會自動退出.

cat myjob.yml
apiVersion: batch/v1    # 指明當前Job的apiVersion
kind:   Job    # 指明當前資源類型為 Job
metadata:
    name:   myjob
spec:
    template:
        metadata:
            name:   myjob
        spec:
            containers:
            - name: hello
              image: busybox
              command: ["echo", "hello k8s job!"]
            restartPolicy: Never    # restartPolicy 指定重啟容器.對應Job,只能設置為 Never或者 OnFailure.對於其他controller,如Deployment,可以設置為Always.

kubectl apply -f myjob.yml
kubectl get job
kubectl get pods
kubectl get pods -o wide # 或 kubectl get pods --show-all
kubectl logs myjob-q8h86 # 查看job的輸出"hello k8s job!"

若執行有問題,比如command: ["echoxxxx", "hello k8s job!"],k8s會一直嘗試創建並執行pod(產生多個pod),排查,在Events里會有對應描述. -> kubectl describe pods myjob-4hrk5
如果將restartPolicy配置成OnFailure,k8s則只會嘗試重啟pod,不會創建新的.

Job的並行性: 通過 parallelism: 2 # 並行
Job配置總數量: 略
定時Job( CronJob ): 略

[centos@ip-10-0-0-105 ~]$ kubectl api-versions
admissionregistration.k8s.io/v1beta1
...
storage.k8s.io/v1beta1
v1

六 通過 Service (clusterIP) /Node:Port 訪問 Pod 提供的服務

Service只是邏輯上的一個IP地址(綁定后端的多個Pod),並非物理實體(所以是無法ping通的).這點區別於Pod(可以ping通所有Pod的IP).

通過IP方式訪問服務
kubernetes Service 從邏輯上代表了一組Pod,具體哪些pod則由label挑選.有了service,外部才有訪問到Pod的途徑(nodeIP+port對應serviceIP+port對應PodIP+port).

每個node有自己的物理IP.
每個service也有自己的IP(也就是ClusterIP),這個IP是不變的.客戶端只需要訪問Service的IP,K8S則負責建立和維護Service和Pod的映射關系.
每個pod都有自己的IP.Pod隨時可以消亡,不能認為Pod是穩定的,而應該注重Service的穩定性.

K8S通過iptables來實現將 Pod+port映射到 -->ClusterIP+port (Service上) --> nodesIP+port (Node上).

目前kubernetes提供了兩種負載分發策略:RoundRobin和SessionAffinity
RoundRobin:輪詢模式,即輪詢將請求轉發到后端的各個Pod上
SessionAffinity:基於客戶端IP地址進行會話保持的模式,第一次客戶端訪問后端某個Pod,之后的請求都轉發到這個Pod上.默認是RoundRobin模式.
實現基於客戶端 IP 的會話親和性,可以將 service.spec.sessionAffinity 的值設置為 "ClientIP" (默認值為 "None")。

制作Deployment的yml

[centos@master httpd]$ cat  http.yml 
apiVersion: apps/v1 # apiVersion是當前配置格式的版本
kind: Deployment    # kind 是要創建的資源類型, 這里是Deployment.
metadata:   # metadata 是該資源的元數據, name 是必須的元數據項.
  name: httpd-deployment
spec:   # spec 部分是該Deployment的規格說明.
  selector: # 新加入部分,通過Label選擇對應的pod
    matchLabels:
      app: httpd-pod
  replicas: 2   # replicas 指明副本數量, 默認是1.
  template: # template 定義Pod 的模板, 這是配置文件的重要部分.
    metadata:   # metadata 定義Pod的元數據, 至少要定義一個label. label 的key和value 可以任意指定.
      labels:
        #run: httpd-pod
        app: httpd-pod
    spec:   # spec 描述 Pod的規格,此部分定義Pod中的每一個容器的屬性, name 和 image 是必須的.
      containers:
      - name: httpd
        image: httpd
        ports:
        - containerPort: 80
      #nodeSelector:
      #        #disktype: ssd

部署Deployment: kubectl apply -f http.yml

制作Service的yml

[centos@master httpd]$ cat http-svc.yml 
apiVersion: v1 # apiVersion是當前配置格式的版本
kind: Service    # kind 是要創建的資源類型
metadata:   # metadata 是該資源的元數據, name 是必須的元數據項.
  name: httpd-svc   # Service 的名字
spec:   # spec 部分是該Deployment的規格說明.
  type: NodePort # NodePort方式.還可以是ClusterIP,或者LoadBalancer
  selector:
    app: httpd-pod  # selector 指明挑選那些 label 為 app:httpd 的 Pod 作為 Service的后端
  ports:
  - protocol: TCP # 將service的8080端口映射到Pod的80端口,使用TCP協議
    nodePort: 30000 # 是各個節點上監聽的端口(默認隨機30000-32767)
    port: 8080  # 是ClusterIP上監聽的端口(Service的)
    targetPort: 80  # 是Pod監聽的端口

部署Service: kubectl apply -f httpd-svc.yml

查看Service與Pod的對應關系

[centos@ip-10-0-0-105 ~]$ kubectl describe service httpd-svc
Name:                     httpd-svc
Namespace:                default
...
Type:                     NodePort
IP:                       10.103.10.254
Port:                     <unset>  8080/TCP    # Service
TargetPort:               80/TCP    # Pod
NodePort:                 <unset>  30000/TCP    # Node
Endpoints:                10.244.1.17:80,10.244.2.29:80,10.244.2.30:80    # Pod IP+Pod port
...

在master和nodes上分別確認開放的kube-proxy端口(默認隨機30000-32767)
sudo netstat -npl

訪問service對應的port (目前只能本地可以訪問,無法掛載外部EIP)
kubectl get service httpd-svc # 獲取clusterIP -> 10.107.183.131
wget 10.103.10.254:8080 或 wget httpd-svc:8080 (這個需要在相同的namespace里才行)

訪問node對應的port (目前唯一可以跨越互聯網訪問的方式)
wget 10.0.0.105:30000
wget 10.0.0.106:30000
wget 10.0.0.107:30000

訪問pod對應的port (目前只能本地可以訪問,無法掛載外部EIP)
kubectl get pods -o wide
wget 10.244.1.17
wget 10.244.2.29
wget 10.244.2.30

其中,在master和各個node物理機上,只能看到開放的30000端口,而8080和80都是看不到的(通過iptables內部映射的).

訪問service的請求(訪問clusterIP),會通過iptables將流量轉發到后端的Pod.而且使用的是類似輪詢的負載均衡策略.
Cluster的每個節點都配置了相同的iptables規則,這樣就確保了整個cluster都能夠通過service的clusterIP訪問service.

固定service的IP地址

[centos@master nginx]$ cat nginx-svc.yml 
apiVersion: v1 # apiVersion是當前配置格式的版本
kind: Service    # kind 是要創建的資源類型
metadata:   # metadata 是該資源的元數據, name 是必須的元數據項.
  name: nginx-svc   # Service 的名字
spec:   # spec 部分是該Deployment的規格說明.
  type: NodePort # NodePort方式.還可以是ClusterIP,或者LoadBalancer
  selector:
    #run: httpd  # selector 指明挑選那些 label 為 run:httpd 的 Pod 作為 Service的后端
    app: nginx-deployment2  # selector 指明挑選那些 label 為 run:httpd 的 Pod 作為 Service的后端
  clusterIP: 10.108.72.147
  ports:
  - protocol: TCP # 將service的8080端口映射到Pod的80端口,使用TCP協議
    nodePort: 30000 # 是各個節點上監聽的端口(默認隨機30000-32767)
    port: 8080  # 是ClusterIP上監聽的端口(Service的)
    targetPort: 80  # 是Pod監聽的端口

在 selector 同級 clusterIP: 10.108.72.147 ,指定service的IP地址.
也可以指定為其他網段,只要和Pod不沖突就可以,如 clusterIP: 10.101.72.10 .

通過DNS方式訪問 Service
這里需要跟對應的Service在同一個namespace里才行.在物理機上(master和nodes)是無法解析的.
以下是在相同namespace里運行的busybox訪問service和pod服務
[centos@ip-10-0-0-105 ~]$ kubectl run busybox --rm -it --image=busybox /bin/sh

在busybox里,可以通過的方式
wget 10.0.0.105:30000 # 訪問master或nodes物理IP地址
wget 10.103.10.254:8080 # 訪問ClusterIP(虛擬IP),也就是service的IP
wget httpd-svc:8080 # 訪問service的NAME.也可以httpd-svc.default:8080
wget 10.244.2.30 # 訪問Pod的IP

在busybox里,無法通過的方式
wget httpd-8c6c4bd9b-5rjz8 # 訪問Pod的NAME

其他略.

外網訪問Service
1.ClusterIP
只有cluster內部可以訪問service.是默認類型.
2.NodePort
配置文件里需要 type: NodePort,通過cluster節點的靜態端口對外提供服務.cluster外部可以通過 NodeIP:NodePort方式訪問service.
3.LoadBalance
service里有cloud provider特有的load balancer對外提供服務.比如AWS等.

報錯/問題處理
報錯1.
tailf /var/log/messages
Oct 18 14:44:15 master kubelet: E1018 14:44:15.594052 736 summary_sys_containers.go:47] Failed to get system container stats for "/system.slice/docker.service": failed to get cgroup stats for "/system.slice/docker.service": failed to get container info for "/system.slice/docker.service": unknown container "/system.slice/docker.service"
解決方法:
1.修改kubelet配置文件 /lib/systemd/system/kubelet.service.d/10-kubeadm.conf ,添加一行
Environment="KUBELET_CGROUP_ARGS=–cgroup-driver=systemd –runtime-cgroups=/systemd/system.slice –kubelet-cgroups=/systemd/system.slice"
2.重啟kubelet: systemctl restart kubelet

報錯2
在pod和service都已經啟動后,wget/telnet訪問不了以下地址
物理機 192.168.143.130/131/132:30000
Service 10.108.72.146:8080
Pod 10.10.2.12/10.10.3.13:80

原因,iptables -L (無轉發策略).

iptables -L
正確的:
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
KUBE-FORWARD  all  --  anywhere             anywhere             /* kubernetes forwarding rules */
KUBE-SERVICES  all  --  anywhere             anywhere             ctstate NEW /* kubernetes service portals */
DOCKER-ISOLATION-STAGE-1  all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  10.244.0.0/16        anywhere            
ACCEPT     all  --  anywhere             10.244.0.0/16       

錯誤的:    Chain FORWARD (policy DROP) ...

解決方式2種:
1.配置iptables轉發: (無需重啟服務,只是臨時生效,重啟后會被還原)
iptables -P FORWARD ACCEPT
2.修改/etc/sysctl.conf(需要重啟機器,否則手動逐條修改iptables的FORWARD鏈里的多條規則)
net.ipv4.ip_forward = 1

七 關於資源(限制資源申請,查看資源占用,改變資源標簽)

控制pod申請的資源大小
采自阿里雲ACK文檔 "容器服務Kubernetes版 > 最佳實踐 > 集群 > Kubernetes集群選型及高可靠推薦配置 > 高可靠推薦配置 ->聲明每個Pod的resource"
下面的例子中,聲明Nginx這個Pod需要1核CPU,256M的內存,運行中實際使用不能超過2核CPU和512M內存。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    resources: # 資源聲明
      requests:
        memory: "256Mi"
        cpu: "1000m"
      limits:
        memory: "512Mi"
        cpu: "2000m"
若node是單核,則Pod會一直處於Pending狀態,原因 kubectl describe pods nginx-deployment-775c7756c6-mbvsz,報錯為
1 node(s) had taints that the pod didn't tolerate, 2 Insufficient cpu.(node節點的CPU數量不足).
正常的話,kubectl apply -f nginx.yml 會先新建新的pod,然后刪除舊的pod.

labels改變
labels改變,需要先kubectl delete -f xxx.yml.而不能直接kubectl apply -f xxx.yml.

kubectl top 查看node/pod等的cpu,內存占用情況
kubernetes 從1.8版本開始不再集成cadvisor,也廢棄了heapster,使用metrics server來提供metrics.
官網: https://github.com/kubernetes-incubator/metrics-server/ 下載zip文件.解壓至任意目錄.

unzip metrics-server-master.zip
kubectl apply -f metrics-server-master/deploy/1.8+/ #需要等待大約2分鍾

需要修改 deploy/1.8+/metrics-server-deployment.yaml 文件,添加以下參數(否則kubectl top node node2 會報Error from server (NotFound): nodemetrics.metrics.k8s.io "node2" not found)
...
      containers:
      - name: metrics-server
        image: k8s.gcr.io/metrics-server-amd64:v0.3.6
        args:   #這里加入args參數
        - --kubelet-insecure-tls
        - --kubelet-preferred-address-types=InternalIP

注意,如果metrics-server的pod會報訪問10.96.0.1 443超時(通過kubectl logs pod-name指令,有報錯dial tcp 10.96.0.1:443: i/o timeout),原因是node節點的iptables里沒有對應的SNAT(MASQUERADE),具體參考"關於網絡"部分.

八 關於網絡

K8S采用的是扁平化的網絡模型,每個Pod都有自己的IP,並且可以直接通信.可以被其他Pod或node節點ping通.而Service是邏輯上的IP,不能被ping通.
CNI規范使得K8S可以靈活選擇多種Plugin實現集群網絡.
Network Policy賦予了K8S強大的網絡訪問控制機制(可以不用這個,直接用雲平台的訪問控制就可以了).

1.pod訪問外部地址空間
1.處於default命名空間的Pod busybox 10.10.3.32 且落在了node1上(192.168.143.131).busybox訪問其他IP測試.
a.可以訪問:
10.10.3.21 80
10.10.2.32 80
10.101.72.10 8080 nginx-svc
10.96.0.10 53
10.10.0.10 53
10.10.0.11 53
192.168.143.131 30000 因為busybox在node1上.
b.不能訪問:
192.168.143.130 30000
192.168.143.132 30000
10.96.0.1 443
192.168.143.130 6443
8.8.8.8

2.節點機器 192.168.143.130/131/132
a.可以訪問:全部
b.不能訪問:無

原因是node1節點的iptables默認沒有開啟Pod對外訪問的MASQUERADE.
在192.168.143.131(node1)上執行

iptables -t nat -I POSTROUTING -s 10.10.0.0/16 -j MASQUERADE

這樣busybox就可以訪問之前訪問不了的那些地址空間了(包括外網地址,如8.8.8.8).
查看當前iptables的nat表:

iptables -nL -t nat (查看nat表的所有鏈)
iptables -nL POSTROUTING -t nat (指定POSTROUTING鏈)

2.關於節點iptables的NAT
a.SNAT
SNAT是source NAT源地址目標轉換(如ADSL家用路由器多台電腦上網,服務器接收到的IP地址不是電腦的內網IP,而是路由器的出口IP).

b.DNAT
DNAT是destination NAT目標網絡地址轉換(內網提供的服務器,對外提供服務.如員工通過外部訪問公司內服務器的VPN/WEB等.路由器將目標地址轉換為公司內網服務器IP).

c.MASQUERADE
MASQUERADE,地址偽裝,是SNAT的一個特例.

SNAT是指在數據包從網卡發送出去的時候,把數據包中的源地址部分替換為指定的IP,這樣,接收方就認為數據包的來源是被替換的那個IP的主機。
MASQUERADE是用發送數據的網卡上的IP來替換源IP,因此,對於那些IP不固定的場合,比如撥號網絡或者通過dhcp分配IP的情況下,就得用MASQUERADE。
DNAT,就是指數據包從網卡發送出去的時候,修改數據包中的目的IP,表現為如果你想訪問A,可是因為網關做了DNAT,把所有訪問A的數據包的目的IP全部修改為B,那么,你實際上訪問的是B。

d.在哪個鏈上執行
因為,路由是按照目的地址來選擇的,因此,DNAT是在PREROUTING鏈上來進行的,而SNAT是在數據包發送出去的時候才進行,因此是在POSTROUTING鏈上進行的

e.地址/網卡接口說明
但使用SNAT的時候,出口ip的地址范圍可以是一個,也可以是多個,例如:

如下命令表示把所有10.8.0.0網段的數據包SNAT成192.168.5.3的ip然后發出去
iptables -t nat -A POSTROUTING -s 10.8.0.0/255.255.255.0 -o eth0 -j SNAT –to-source 192.168.5.3

如下命令表示把所有10.8.0.0網段的數據包SNAT成192.168.5.3/192.168.5.4/192.168.5.5等幾個ip然后發出去
iptables -t nat -A POSTROUTING -s 10.8.0.0/255.255.255.0 -o eth0 -j SNAT –to-source 192.168.5.3-192.168.5.5

這就是SNAT的使用方法,即可以NAT成一個地址,也可以NAT成多個地址.

但是,對於SNAT,不管是幾個地址,必須明確的指定要SNAT的ip(MASQUERADE則無需指定).
假如當前系統用的是ADSL動態撥號方式,那么每次撥號,出口ip192.168.5.3都會改變,而且改變的幅度很大,不一定是192.168.5.3到192.168.5.5范圍內的地址.這個時候如果按照現在的方式來配置iptables就會出現問題了.因為每次撥號后,服務器地址都會變化,而iptables規則內的ip是不會隨着自動變化的.每次地址變化后都必須手工修改一次iptables,把規則里邊的固定ip改成新的ip.這樣是非常不好用的.

MASQUERADE就是針對這種場景而設計的,他的作用是,從服務器的網卡上,自動獲取當前ip地址來做NAT
比如下邊的命令:

iptables -t nat -A POSTROUTING -s 10.8.0.0/255.255.255.0 -o eth0 -j MASQUERADE 或
iptables -t nat -I POSTROUTING -s 10.10.0.0/16 -j MASQUERADE

如此配置的話,不用指定SNAT的目標ip了.不管現在eth0的出口獲得了怎樣的動態ip,MASQUERADE會自動讀取eth0現在的ip地址然后做SNAT出去.
這樣就實現了很好的動態SNAT地址轉換.

f.示例說明
我們可能需要將訪問主機的7979端口映射到8080端口。iptables重定向:
iptables -t nat -A PREROUTING -p tcp --dport 7979 -j REDIRECT --to-ports 8080

將外網訪問192.168.75.5的80端口轉發到192.168.75.3:8000端口。
iptables -t nat -A PREROUTING -d 192.168.75.5 -p tcp --dport 80 -j DNAT --to-destination 192.168.75.3:8000

將192.168.75.3 8000端口將數據返回給客戶端時,將源ip改為192.168.75.5
iptables -t nat -A POSTROUTING -d 192.168.75.3 -p tcp --dport 8000 -j SNAT 192.168.75.5

3.CNI
CNI是Container Network Interface的是一個標准的,通用的接口。現在容器平台:docker,kubernetes,mesos,容器網絡解決方案:flannel,calico,weave。只要提供一個標准的接口,就能為同樣滿足該協議的所有容器平台提供網絡功能,而CNI正是這樣的一個標准接口協議。

CNI用於連接容器管理系統和網絡插件。提供一個容器所在的network namespace,將network interface插入該network namespace中(比如veth的一端),並且在宿主機做一些必要的配置(例如將veth的另一端加入bridge中),最后對namespace中的interface進行IP和路由的配置。

CNI的工作是從容器管理系統處獲取運行時信息,包括network namespace的路徑,容器ID以及network interface name,再從容器網絡的配置文件中加載網絡配置信息,再將這些信息傳遞給對應的插件,由插件進行具體的網絡配置工作,並將配置的結果再返回到容器管理系統中。

目錄與文件說明
CNI插件是可執行文件,會被kubelet調用。
啟動kubelet --network-plugin=cni,--cni-conf-dir 指定networkconfig配置,默認路徑是:/etc/cni/net.d,並且,--cni-bin-dir 指定plugin可執行文件路徑,默認路徑是:/opt/cni/bin;

常見問題
CNI 網絡插件配置錯誤,導致多主機網絡不通,比如:IP 網段與現有網絡沖突/插件使用了底層網絡不支持的協議/忘記開啟 IP 轉發等.
sysctl net.ipv4.ip_forward
sysctl net.bridge.bridge-nf-call-iptables

九 更新 與 回滾(1.16版本未測試)

更新回滾采用的是漸進式的方式逐步替換掉舊版本Pod(默認是一個一個替換,可以通過K8S參數maxSurge和maxUnavaiable來精細控制Pod的替換數量).
如果更新不如預期期望,可以回滾.

[centos@ip-10-0-0-105 ~]$ cat httpd.v1.yml
apiVersion: apps/v1beta1 # apiVersion是當前配置格式的版本
kind: Deployment    # kind 是要創建的資源類型, 這里是Deployment.
metadata:   # metadata 是該資源的元數據, name 是必須的元數據項.
  name: httpd
spec:   # spec 部分是該Deployment的規格說明.
  revisionHistoryLimit: 10  # 默認配置下,K8S只會保留最近的幾個revision.
  replicas: 3   # replicas 指明副本數量, 默認是1.
  template: # template 定義Pod 的模板, 這是配置文件的重要部分.
    metadata:   # metadata 定義Pod的元數據, 至少要定義一個label. label 的key和value 可以任意指定.
      labels:
        run: httpd
    spec:   # spec 描述 Pod的規格,此部分定義Pod中的每一個容器的屬性, name 和 image 是必須的.
      containers:
      - name: httpd
        image: httpd:2.4.16
        ports:
        - containerPort: 80

httpd.v2.yml    # image: httpd:2.4.17
httpd.v3.yml    # image: httpd:2.4.18

更新
kubectl apply -f httpd.v1.yml --record
kubectl get deployments. httpd -o wide # 查看

kubectl apply -f httpd.v2.yml --record # 更新到v2
kubectl apply -f httpd.v3.yml --record # 更新到v3

回滾
kubectl rollout history deployment httpd # 查看歷史記錄
kubectl rollout undo deployment httpd --to-revision=1 # 回滾到歷史記錄里的哪個版本

kubectl get deployments. httpd -o wide
kubectl rollout history deployment httpd # 查看歷史記錄(發生變化)

十 健康檢查 Health Check

自愈的默認實現方式是自動重啟發生故障的容器.
除此之外,用戶還可以利用Liveness 和 Readiness 探測機制設置更精細的健康檢查,進而實現:
零停機部署.
避免部署無效的鏡像.
更加安全的滾動升級.

默認的健康檢查
每個容器啟動時都會執行一個進程,該進程由Dockerfile的CMD或ENTRYPOINT指定.如果進程退出時返回碼非零,則認為容器發生故障,K8S就會根據restartPolicy重啟容器.

示例,創建Pod,如下

[centos@ip-10-0-0-105 ~]$ cat healthcheck.yml
apiVersion: v1 # apiVersion是當前配置格式的版本
kind: Pod    # kind 是要創建的資源類型
metadata:   # metadata 是該資源的元數據, name 是必須的元數據項.
  name: healthcheck   # 名字
  labels:
    test: healthcheck
spec:   # spec 規格說明.
  restartPolicy: OnFailure
  containers:
  - name: healthcheck
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 10;exit 1
kubectl apply -f healthcheck.yml    # 部署
kubectl get pods    # 會看到不斷的變化
kubectl delete -f healthcheck.yml    # 刪除

缺點: 只能簡單判斷.但比如web服務器返回500這類,就不行了.就需要 Liveness 探測.

Liveness探測
功能:
健康的時候就不重啟(活着的時候).Liveness探測告訴K8S什么時候通過重啟容器來實現自愈.

[centos@ip-10-0-0-105 ~]$ cat liveness.yml
apiVersion: v1 # apiVersion是當前配置格式的版本
kind: Pod    # kind 是要創建的資源類型
metadata:   # metadata 是該資源的元數據, name 是必須的元數據項.
  name: liveness   # 名字
  labels:
    test: liveness
spec:   # spec 規格說明.
  restartPolicy: OnFailure
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:    # 配置文件
      exec:
        command:
        - cat
        - /tmp/healthy  # 如果該文件存在,則認為容器正常.返回值非0,就認為異常.然后重啟
      initialDelaySeconds: 10   # 指定容器啟動多少秒后開始執行Liveness探測.具體看啟動服務需要花費的時間.
      periodSeconds: 5  # 指定每隔幾秒探測1次.K8S如果連續執行3次探測是吧,則會殺掉並重啟容器.

部署    kubectl apply -f liveness.yml
確認    kubectl get pods
查看日志    kubectl describe pod liveness
刪除    kubectl delete -f liveness.yml

Readiness探測
功能:
健康的時候(准備就緒了),就放到service里.Readiness探測告訴K8S什么時候可以將容器加入到Service負載均衡池中對外提供服務.
Readiness語法與Liveness完全一樣.對應將yml里的所有liveness全部替換為readiness.

查看日志 kubectl describe pod readiness

對Liveness 和 Readiness 探測比較
1.Liveness和Readiness是兩種Health Check機制,如果不特意配置,K8S將對兩種探測采取相同的默認行為,即通過判斷容器啟動進程的返回值是否為0來判斷探測是否成功.
2.兩種探測的配置方法完全一樣.不同之處是: Liveness探測會重啟容器,Readiness探測則是將容器設置為不可用,不接收Service轉發的請求.
3.Liveness 和 Readiness 探測是獨立執行的,二者之間沒有依賴,所以可以單獨使用,也可以同時使用.

Health Check 在Scale Up中的應用
通過Readiness判斷容器是否就緒(一般容器初始化會花時間,不能啟動立刻提供服務),避免將請求發送給未就緒的backend.
配置,略(健康檢查會涉及到用戶代碼部分)
...
readinessProbe:
httpGet:
scheme: HTTP
path: /healthy # 用戶自己實現
port: 8080
inittialDeploySeconds: 10
periodSeconds: 5
...

Health Check 在滾動更新中的應用
另一個場景就是Rolling Update.具體略.
參數說明

...# 假設10個副本
spec:
  strategy:
    rolingUpdate:
      maxSurge: 25%    # 控制滾動更新過程中,副本總數最大值不超過多少.公式: roundUp(10+10*35%)=13(向上取整)
      maxUnavailable: 25%    # 控制滾動更新過程中,不可用副本比例,向下取整.
...

滾動失敗,可以通過 kubectl rollout undo 回滾到上一個版本.

十一 數據管理

概述
K8S如何管理存儲資源
emptyDir和hostPath類型的Volume使用最方便,但可持久性不強.
K8S支持多種外部存儲系統的Volume.
PV和PVC分離了管理員和普通用戶的職責,更適合生產環境.
StorageClass實現更高效的動態供給.

Volume
容器和Pod是短暫的.
Volume的生命周期獨立於容器(注意,不是Pod).
當Volume被mount到Pod,Pod中的所有容器都可以訪問這個Volume.
K8s Volume支持多種backend類型,包括
emptyDir
hostPath
GCE Persistent Disk
AWS Elastic Block Store
NFS
Ceph
...
對於容器來說,所有類型的Volume都只是一個目錄.

emptyDir
是Host上的一個空目錄.對於容器來說是持久的,對於Pod則不是.也就是emptyDir Volume的生命周期與 Pod一致.
yml文件

[centos@ip-10-0-0-105 ~]$ cat emptyDir.yml
apiVersion: v1
kind: Pod
metadata:
  name: producer-consumer
spec:
  containers:
  - image: busybox
    name: producer
    volumeMounts:
    - mountPath: /producer_dir  # producer容器將 shared-volume 到 /producer_dir目錄
      name: shared-volume
    args:
    - /bin/sh   # producer容器通過echo將數據寫入到文件hello里
    - -c
    - echo "hello world" > /producer_dir/hello ; sleep 30000
  - image: busybox
    name: consumer
    volumeMounts:
    - mountPath: /consumer_dir  # consumer容器將shared-volume 到 /consumer_dir目錄
      name: shared-volume
    args:
    - /bin/sh   # consumer容器通過cat從文件hello讀數據
    - -c
    - cat /consumer_dir/hello ; sleep 30000
  volumes:
  - name: shared-volume # 定義了一個emptyDir類型的 Volume shared-volume
    emptyDir: {}

部署
kubectl apply -f emptyDir.yml

查看
kubectl logs producer-consumer consumer

到對應的host上查看volume綁定的目錄是否一致
docker inspect producer_id
docker inspect consumer_id

emptyDir是Host上創建的臨時目錄,其優點是能夠方便的為Pod中的容器提供共享存儲,不需要額外配置.
它不具備持久性,如果Pod不存在了,emptyDir也就沒有了.根據這個特性,emptyDir特別適合Pod中的容器需要臨時共享存儲空間的場景,比如上面的生產者消費者用例.

hostPath
hostPath Volume 的作用是將Docker Host文件系統中的已經存在的目錄 mount給Pod的容器.一般都不要這么干,會增加Pod與節點的耦合,限制了Pod的使用.
不過那些需要訪問K8s或docker內部數據(配置文件和二進制庫)的應用則需要使用hostPath.

外部 Storage Provider
使用雲盤方式.
也可以使用主流的分布式存儲,比如Ceph, GlusterFS等.

可持久化 PersistentVolume 與 PersistentVolumeClaim
PersistentVolume(PV)是外部存儲系統中的一塊存儲空間,由系統管理員創建和維護.
PersistentVolumeClaim(PVC)是對PV的申請.由普通用戶創建和維護.用戶創建PVC后,由K8S查找並提供滿足條件的PV.
有了PV和PVC,開發人員和運維人員才能降低耦合,提高效率和安全.

NFS PV
NFS搭建請參考<<NFS 文件服務器搭建>>
在NFS上創建目錄
mkdir -p /nfsdata/pv1/
在NFS上配置目錄
[root@ip-10-0-0-104 nfsdata]# cat /etc/exports
/nfsdata/ 10.0.0.0/24(rw,sync,no_root_squash,no_all_squash)
重啟NFS服務 systemctl restart nfs
確認 showmount -e localhost

創建PV

[centos@ip-10-0-0-105 nfs]$ cat nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 1Gi    # capacity指定PV的容量為1GB
  accessModes:
    - ReadWriteOnce # ReadWriteOnce,ReadOnlyMany,ReadWriteMany 3種.Once表示單個節點
  persistentVolumeReclaimPolicy: Recycle    # PV的回收策略.Retain表示管理員手動回收.Recycle表示清除PV中的數據,同rm -rf /thevolume/*.Delete表示刪除Storage Provider上對應的存儲資源,例如AWS EBS.
  storageClassName: nfs # 指定PV的class為nfs.相當於為PV設置了一個分類,PVC可以指定class申請相應的class的PV.
  nfs:
    path: /nfsdata/pv1  # 指定PV在NFS服務器上對應的目錄
    server: 10.0.0.104

部署並查看
kubectl apply -f nfs-pv1.yml
kubectl get pv

創建PVC

[centos@ip-10-0-0-105 nfs]$ cat nfs-pvc1.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi    # PV和PVC僅僅是通過這個大小來進行匹配.
  storageClassName: nfs

PVC只需要指定PV的容量,訪問模式,class即可.

部署並查看
kubectl apply -f nfs-pvc1.yml
kubectl get pvc
kubectl get pv
確認mypvc1已經Bound到mypv1上.

創建Pod

[centos@ip-10-0-0-105 nfs]$ cat pod1.yml
apiVersion: v1
kind: Pod
metadata:
  name: mypod1
spec:
  containers:
    - name: mypod1
      image: busybox
      args:
      - /bin/sh
      - -c
      - sleep 30000
      volumeMounts:
      - mountPath: "/mydata"
        name: mydata
  volumes:
    - name: mydata
      persistentVolumeClaim:
        claimName: mypvc1

與使用普通Volume的格式類似,在volumes中通過persistentVolumeClaim指定使用mypvc1申請Volume.

部署並查看
kubectl apply -f pod1.yml
kubectl get pods -o wide

測試
kubectl exec mypod1 touch /mydata/hello
到nfs服務器上,對應目錄下,確認該文件是否已經生成.

遇到的問題:
在apply的時候,pod創建一直處於ContainerCreating狀態.使用describe確認故障緣由:
kubectl describe pod PodName
提示mounting 10.0.0.104:/nfsdata/pv1 failed,那么可以確定是因為NFS服務器上沒有/nfsdata/pv1/目錄導致.手動到NFS上去創建即可.

回收PV
刪除pod,刪除pvc,刪除pv.
pv中的刪除策略目前是Recycle,表示刪除pv的時候,數據也會被刪除.修改為persistentVolumeReclaimPolicy: Retain,刪除pv后,不會刪除數據.數據可以手動到nfs服務器上刪除.

PV動態供給(暫未測試)
靜態供給: 創建PV,創建PVC,創建Pod
動態供給: 如果沒有滿足PVC條件的PV,則動態創建PV.這樣就不需要提前創建PV了.動態供給通過StorageClass實現.

[centos@ip-10-0-0-105 nfs]$ cat cloud-pv1.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain

[centos@ip-10-0-0-105 nfs]$ cat cloud-pvc1.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: standard

mysql示例
模擬一個node宕機,新的pod依然通過volume實現數據可持久化.

編寫PV

[centos@ip-10-0-0-105 mysql-nfs]$ cat mysql-pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
spec:
  capacity:
    storage: 1Gi    # capacity指定PV的容量為1GB
  accessModes:
    - ReadWriteOnce # ReadWriteOnce,ReadOnlyMany,ReadWriteMany 3種.Once表示單個節點
  persistentVolumeReclaimPolicy: Retain    # PV的回收策略
  storageClassName: nfs # 指定PV的class為nfs.相當於為PV設置了一個分類,PVC可以指定class申請相應的class的PV.
  nfs:
    path: /nfsdata/mysql-pv # 指定PV在NFS服務器上對應的目錄
    server: 10.0.0.104

編寫PVC

[centos@ip-10-0-0-105 mysql-nfs]$ cat mysql-pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs

部署PV,PVC

kubectl apply -f mysql-pv.yml
kubectl apply -f mysql-pvc.yml
kubectl get pv,pvc

編寫Service和Deployment

[centos@ip-10-0-0-105 mysql-nfs]$ cat mysql.yml
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql

---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pvc

部署

kubectl apply -f  mysql.yml

啟動一個臨時mysql client pod 測試

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword
mysql> use mysql
mysql> create table my_id( id int(4) );
mysql> insert my_id values( 111 );
mysql> select * from my_id;

到pod所在node上,shutdown該node.等待5-10分鍾后,新的pod會在另一個node上產生.
查看mysql數據是否持久化的:

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword
mysql> use mysql
mysql> select * from my_id;

十二 Secret 與 Configmap

介紹如何向Pod傳遞配置信息.如果信息需要加密,可以使用Secret;如果是一般的配置信息,可以使用ConfigMap.
Secret和ConfigMap支持4種定義方法.Pod在使用他們時,可以選擇Volume方式或者是環境變量方式,不過只有Volume方式支持動態更新(動態更新大約延遲幾十秒).
生產環境若對安全要求沒那么高,建議用ConfigMap.

創建Secret
1.通過原始的方式 --from-literal
kubectl create secret generic mysecret --from-literal=useranme=admin --from-literal=password=123456
kubectl get secret

2.通過文件方式 --from-file
echo -n admin > ./username
echo -n 123456 > ./password
kubectl create secret generic mysecret2 --from-file=./username --from-file=./password
kubectl get secrets

3.通過變量方式 --from-env-file
[centos@ip-10-0-0-105 Secret-ConfigMap]$ cat env.txt # 文件env.txt每行Key=Value對應一個信息條目
useranme=admin
password=123456
應用一下 kubectl create secret generic mysecret3 --from-env-file=env.txt
kubectl get secrets

4.通過YAML配置文件

echo -n admin| base64
echo -n 123456|base64
[centos@ip-10-0-0-105 Secret-ConfigMap]$ cat mysecret.yml
apiVersion: v1
kind: Secret
metadata:
  name: mysecret4
data:
  username: YWRtaW4=
  password: MTIzNDU2
應用一下    kubectl apply -f mysecret.yml
kubectl get secrets

查看Secret

kubectl get secrets    # 能顯示內部data數據條目個數
kubectl describe secrets mysecret[1-4]    # 顯示Data    ====    password:  6 bytes    useranme:  5 bytes 看不到具體數據
kubectl edit secrets mysecret[1-4]    # 顯示base64加密后的 data:      password: MTIzNDU2      username: YWRtaW4=
要查看明文,可以--decode一下,如下
echo -n MTIzNDU2|base64 --decode    和    echo -n YWRtaW4=|base64 --decode

在Pod中使用Secret
volume方式
以Volume方式使用的Secret支持動態更新,Secret更新后,容器中的數據也會更新.

[centos@ip-10-0-0-105 Secret-ConfigMap]$ cat mypod.yml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: busybox
    args:
      - /bin/sh
      - -c
      - sleep 10; touch /tmp/healthy; sleep 30000
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"    # 將foo mount到容器的/etc/foo目錄,可以指定讀寫權限為readOnly
      readOnly: true
  volumes:
  - name: foo    # 定義volume foo,來源secret mysecret
    secret:
      secretName: mysecret

驗證: kubectl exec -it mypod sh # 可以查看/etc/foo下的文件內容.文件username和password內容都是明文.

也可以修改路徑

mypod.yml
...
  volumes:
  - name: foo
    secret:
      secretName: mysecret2    # 注意,這里不能用"1"那種方式了,可以2-4.推薦4的方式,以便Secret更新后,一分鍾左右,容器中的數據也隨之更新
      items:    # 修改路徑,文件會放置到/etc/foo/my-group/my-username
      - key: username
        path: my-group/my-username    
      - key: password
        path: my-group/my-password

環境變量方式
通過Volume使用Secret,容器必須從文件讀取數據,稍顯麻煩,K8S還支持通過環境變量使用Secret.缺點是無法支持Secret動態更新.
具體請參考相關資料

ConfigMap(主要使用2,4這兩種方式)
區別於Secret,用於非敏感信息.比如配置文件.
使用方式與Secret類似.

創建方式4種

第一種
kubectl create configmap myconfigmap --from-literal=config1=xxx --from-literal=config2=yyy
第二種    從文件或含有多個文件的目錄
echo -n xxx > ./config1
echo -n yyy > ./config2
kubectl create configmap myconfigmap2 --from-file=./config1 --from-file=./config2    # 文件
kubectl create configmap game-config --from-file=docs/gameconfig/    # 目錄內含有多個配置文件(每個配置文件里可以有很多內容)
第三種
[centos@ip-10-0-0-105 Secret-ConfigMap]$ cat env2.txt
config1=xxx
config2=yyy
kubectl create configmap myconfigmap3 --from-env-file=env2.txt
第四種 yml文件
[centos@ip-10-0-0-105 Secret-ConfigMap]$ cat myconfigmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfigmap4
data:
  config1: xxx    # 這里可以嵌套實現一個文件里多對鍵值.還可以有多個文件.
  config2: yyy
kubectl apply -f myconfigmap.yml

查看
kubectl get configmaps

編寫pod文件

[centos@ip-10-0-0-105 Secret-ConfigMap]$ cat mypod2.yml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: busybox
    args:
      - /bin/sh
      - -c
      - sleep 10; touch /tmp/healthy; sleep 30000
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:    # 采用volume方式,環境變量方式:略
  - name: foo
    configMap:
      name: myconfigmap4

kubectl apply -f mypod2.yml

查看config內容
kubectl exec -it mypod sh # 進入container后,執行 cat /etc/foo/config1,顯示xxx.

多數情況下,配置文件都是以文件形式提供.所以在創建ConfigMap時,通常采用 --from-file或 YAML方式.讀取ConfigMap通常采用Volume方式.
一般都是以2,4兩種方式,具體使用方式和語法內容請參考P109-110.

思考: ConfigMap遇到很長的配置參數的時候,如何處理呢?Pod里需要創建很多個文件嗎?
解決方案

apiVersion: v1 
data: 
  game-special-key: |     # game-special-key變成文件名
    enemies=aliens     # 這個是game-special-key文件的第一行
    lives=3     # 第二行
...

十三 Helm - K8S的包管理器

類似Centos的yum.

Helm架構
helm包括chart和release.
helm包含2個組件,Helm客戶端和Tiller服務器.

Helm客戶端安裝
安裝
curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get |bash
查看
which helm
helm version # 因服務器端還沒安裝,這里會報無法連接
添加命令補全
helm completion bash > .helmrc
echo "source .helmrc" >> .bashrc

Tiller服務器端安裝
helm init
查看
kubectl get --namespace=kube-system service tiller-deploy
kubectl get --namespace=kube-system deployments. tiller-deploy
kubectl get --namespace=kube-system pods tiller-deploy-6d6cc8dcb5-wvvr4
helm version # 能夠看到服務器版本信息

Helm使用
搜索 helm search

執行命名添加權限
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

安裝chart的mysql應用
helm install stable/mysql
會自動部署 Service,Deployment,Secret 和 PersistentVolumeClaim,並給與很多提示信息,比如mysql密碼獲取,連接端口等.

查看release各個對象
kubectl get service doltish-beetle-mysql
kubectl get deployments. doltish-beetle-mysql
kubectl get pods doltish-beetle-mysql-75fbddbd9d-f64j4
kubectl get pvc doltish-beetle-mysql
helm list # 顯示已經部署的release

刪除
helm delete doltish-beetle
kubectl get pods
kubectl get service
kubectl get deployments.
kubectl get pvc

chart詳解

十四 K8S Dashboard

介紹K8S Dashboard的安裝和使用方法.
Dashboard能完成日常管理的大部分工作,可以作為命令行工具kubectl的有益補充.同時,可以比較直觀的看到各類資源情況.
Dashboard提供了kubectl的絕大部分功能.

部署
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml

查看

kubectl --namespace=kube-system get deployments. kubernetes-dashboard
kubectl --namespace=kube-system get service kubernetes-dashboard
kubectl --namespace=kube-system get pods -o wide

kubectl --namespace=kube-system get pods kubernetes-dashboard-57df4db6b-r6qn6 -o wide
NAME  READY STATUS  RESTARTS AGE   IP   NODE  NOMINATED NODE   READINESS GATES
kubernetes-dashboard-xxx 1/1 Running 0 40m 10.244.1.36 ip-10-0-0-106.xxx <none> <none>

修改service端口
修改成NodeIP:NodePort模式便於訪問
kubectl --namespace=kube-system edit service kubernetes-dashboard
修改 type: ClusterIP 為 type: NodePort
確認修改成功
kubectl --namespace=kube-system get service kubernetes-dashboard

登陸Dashboard(需要token)
Dashboard支持Kubeconfig和Token兩種認證方式,為簡化配置,我們通過配置文件 dashboard-admin.yaml 為 Dashboard 默認用戶賦予 admin 權限。
創建登陸token

mkdir dashboard; cd dashboard/
[centos@ip-10-0-0-105 dashboard]$ cat dashboard-admin.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kube-system

應用並獲取token
kubectl apply -f dashboard-admin.yaml
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')
把token填入web登陸里就可以了.

訪問Dashboard
可以通過任何node節點IP訪問到Dashboard(Dashboard的pod在node1上).如下
master https://3.1.57.171:32133/
node1 https://52.221.27.128:32133
node2 https://52.221.118.136:32133

宕機測試
將master1關機,再次訪問node1,node2上面的服務,提示
node1上報:
Internal Server Error (500)
Get https://10.96.0.1:443/api/v1/namespaces/default/replicationcontrollers: dial tcp 10.96.0.1:443: i/o timeout
node2上報:
Internal Server Error (500)
Get https://10.96.0.1:443/api/v1/namespaces: dial tcp 10.96.0.1:443: connect: no route to host
將master開機(kubelet自啟動),服務恢復正常.

將node1關機,,再次訪問master,node2上面的服務
服務恢復大約5分鍾(切換到node2上)
啟動node1,大約需要1分鍾,即可以通過node1IP:node1Port訪問服務了(實際Pod仍在node2上)

Dashboard可以干什么
查看集群,節點等常規信息
可以在線修改yaml配置文件
查看describe信息
查看logs輸出信息(kubectl logs)
查看pod日志
監控集群以及一些相關項(宕機會體現在Dashboard上)

十五 K8S 集群監控

Weave Scope 可以展示集群和應用的完整視圖.其出色的交互性讓用戶能夠輕松對容器化應用進行實時監控和問題診斷.
Heapster是K8S原生的集群監控方案.預定義的Dashboard能夠從Cluster和Pods兩個層次監控K8S.
Prometheus Operator可能是目前功能最全面的開源方案.除了能監控Node和Pod,還支持集群的各種管理組件,如API Server,Scheduler,Controller Manager等.

Weave Scope
部署
[centos@ip-10-0-0-105 ~]$ kubectl apply -f "https://cloud.weave.works/k8s/scope.yaml?k8s-version=$(kubectl version | base64 | tr -d '\n')"

修改端口
kubectl --namespace=weave edit service weave-scope-app
將ClusterIP更換為NodePort.

瀏覽器訪問
可以通過hosts里的命令行,直接獲取root權限.這個怎么考慮的呢?控制台登陸沒有密碼認證的啊....

刪除
[centos@ip-10-0-0-105 ~]$ kubectl delete -f "https://cloud.weave.works/k8s/scope.yaml?k8s-version=$(kubectl version | base64 | tr -d '\n')"

Heapster
Heapster是K8S原生的集群監控方案.

部署
git clone https://github.com/kubernetes/heapster.git
kubectl apply -f heapster/deploy/kube-config/influxdb/
kubectl apply -f heapster/deploy/kube-config/rbac/heapster-rbac.yaml

查看
kubectl get --namespace=kube-system deployments. |grep -e heapster -e monitor
kubectl get --namespace=kube-system service|grep -e heapster -e monitor
kubectl get --namespace=kube-system pods -o wide|grep -e heapster -e monitor

修改端口
kubectl --namespace=kube-system edit service monitoring-grafana # 為type: NodePort
確認修改成功
kubectl get --namespace=kube-system service|grep -e heapster -e monitor

貌似看不到數據呢,需要登陸么?

Prometheus Operator
監控到的會比Weave Scope 和Heapster更多.
Prometheus Operator是CoreOS開發的.是目前功能最全面的開源方案.
Prometheus提供了數據搜集,存儲,處理,可視化和告警一套完整的解決方案.
具體請參考在線文檔或相關資料.

十六 K8S集群日志管理

K8S開發了一個Elasticsearch附加組件來實現集群的日志管理.
是Elasticsearch,Fluentd和Kibana的組合.
Elasticsearch是一個搜索引擎,負責存儲日志並提供查詢接口.
Fluentd負責從K8S搜集日志並發送給Elasticsearch.
Kibana提供Web GUI.
具體: 略.

十七 k8s kerbernetes 高可用方案部署

2019/03/18 Chenxin
https://kubernetes.io/zh/docs/admin/high-availability/
https://kubernetes.io/docs/setup/independent/high-availability/ 英文最新版
https://blog.51cto.com/bigboss/2174899 網友給出的1.11版本的方案,過於復雜
當前來看,k8s高可用自建方案還不夠成熟.方式較多,也都比較復雜,官方目前還是內測階段.建議待成熟后再跟進.當前可以研究雲廠商的現成產品,如aws的EKS等.

2019/10/16 chenxin
https://juejin.im/post/5da44a765188251b643eb5e9 使用 Sealos 在 3 分鍾內快速部署一個生產級別的 Kubernetes 高可用集群.
目前好像使用的還很少,但貌似很方便

十八 鏡像倉庫實踐

2019/03/28 Chenxin
參考:
https://kubernetes.io/zh/docs/concepts/containers/images/ 官方資料 - 鏡像
http://www.cnblogs.com/justmine/p/8666907.html 詳解docker實戰之搭建私有鏡像倉庫 - kurbernetes
http://www.cnblogs.com/justmine/p/8678758.html k8s實戰之從私有倉庫拉取鏡像 - kubernetes
建議采用阿里雲或AWS 私有倉庫.這里為自行搭建的私有倉庫測試.

十九 思考問題

K8S如何能踢掉一台故障的node?
kubectl delete nodes ip-10-0-0-107

node重新加入集群?
參考本筆記中"節點加入集群"部分的"重新加入集群"內容.

在所有服務正常提供情況下,突然主機node2宕機(aws控制台手動terminal該node),如何處理?
等待幾分鍾后,等k8s將所有原node2上的pod轉移到其他node.
node2當前狀態為"NotReady".手動在master上刪除node2即可.

手動重啟master或者node有什么影響?
因為只有單台master,重啟會影響服務.不過起來后,服務會自動恢復.
重啟node幾乎沒有影響,service有負載均衡能力,待故障node起來后,上面的pod會繼續提供服務(如果重啟需要時間很久,比如超過10分鍾,k8s會自動將pod轉移到正常的node上).

k8s如何根據當前負載情況自動橫向擴容或縮容?
Kubernetes HPA(Horizontal Pod Autoscaling)Pod水平自動伸縮.
以下暫未通過(安裝了heapster,但可能還需要metrics-server)

以下是HPA(V1版本)
Deployment和svc配置

[centos@ip-10-0-0-105 hpa]$ cat php-apache-deploy-and-svc.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  replicas: 1
  selector:
    matchLabels:
      app: php-apache
  template:
    metadata:
      name: php-apache
      labels:
        app: php-apache
    spec:
      containers:
      - name: php-apache
        image: gcr.io/google_containers/hpa-example
        resources:
          requests:
            cpu: 200m
        ports:
        - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: php-apache
spec:
  ports:
  - port: 80
  selector:
    app: php-apache

hpa配置

[centos@ip-10-0-0-105 hpa]$ cat hpa-php-apche.yml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1beta1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

准備用於壓測的busybox

[centos@ip-10-0-0-105 hpa]$ cat busybox-pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
spec:
  containers:
  - name: busybox
    image: busybox
    command: ["sleep", "3600"]

HPA(v2)控制器
HPA(v1)只能基於cpu實現自動彈性伸縮,HPA(v2)控制器基支持基於核心指標CPU和內存資源及基於任意自定義指標資源占用狀態實現規模的彈性伸縮,它從metrics-service中請求查看核心指標。
如:

]# cat hpav2-mynginx.yaml 
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
...

外部如何訪問到K8S集群里的service?
可以通過nodeIP+nodePort
當前service的yml配置支持type的LoadBalancer,一些公有雲會自動分配一個公網IP給它.

二十 項目k8s部署

2019/03/28 Chenxin
三種模式選擇
單master集群(自建集群)
阿里雲k8s集群
亞馬遜k8s集群

容器化改造需要處理問題
創建mysql,consul,redis服務 完成
創建私有docker資源倉庫 完成
掛載外部存儲到每個Pod上.將配置文件,腳本文件全部放此.日志也放在這里. 完成
控制台以及日志輸出(寫到Dockerfile或yml里).仍然重定向輸出到文件中,且保持java前台運行(bash ls -al 2&>1 | tee ./ha.log),進程掛了后,文件不刪除.暫時不用,因為故障自動轉移功能. 完成
k8s的kubectl能否在普通賬號下執行指令(可以)按照官方入門文檔里的為EKS配置kubectl操作即可.其中會提示aws configure的執行(配置密鑰). 完成
將PV改成S3測試,不方便修改文件.暫時不用這種方式 完成
分布式的fight,match如何處理?不使用容器(否則每個地區建集群,太費機器). 完成
停止nfs機器,會發生什么? worker機器上df -h 無法執行(卡在獲取不到磁盤信息).登陸容器也不行.但容器狀態還是run.等nfs機器手動開機后,一切恢復正常. 完成
創建主機,磁盤,內存監控報警.使用雲平台自帶功能實現. 略 完成
排查java進程異常或故障的時候,直接到雲盤對應文件夾里去找報錯信息(包括控制台,日志信息) 完成
使用xbzj賬號管理docker.(將xbzj加入docker用戶組) 完成
更換小的鏡像測試(官網).openjdk:13-alpine跟java:latest還是有區別.比如沒有/bin/bash文件,一些JVM參數不支持,載入/etc/profile后,會出現找不到java命令問題. 完成
測試java其他image,如 java:8-jre-alpine (jdk是開發包,含運行環境;jre只有運行環境,自動載入環境變量).FROM java:openjdk-8u111-jre基礎鏡像可以. 完成

以下因為 game.properties 配置文件里,很多需要自己的固定一個IP,故沒有測試以下項.
多個gateway如何處理呢?gateway訪問gamecenter: 不同service之間通過service的IP+Port去調用.
相同Pod多個實例如何處理控制台輸出和日志文件.拷貝模板配置文件,gateway+時間+PodIP創建臨時文件夾.啟動進程前,讀取此文件夾配置.container獲取Pod IP的方法 https://blog.csdn.net/kozazyh/article/details/79463688 . 然后定期手動或自動到雲盤上清理老舊文件夾(模板文件夾例外).
如何確保用戶每次連上來的都是同一個gateway的Pod? 使用service的SessionAffinity負載均衡策略
容器故障后,重啟后,如何獲知?之前通過進程判斷,自動拉起進程方式處理,並發送通知報警.可以查看pod的age時間.
鏡像文件太大,上傳到預熱服比較慢,發布到生產服也慢,如何優化.

搭建gamecenter服務
說明
kubectl機器IP: 10.0.0.100
NFS機器IP: 10.0.0.101
EKS的worker node IP: 192.168.0.0/16
kubectl在系統的xbzj賬號下執行各類管理指令.

搭建mysql,consul,redis服務
將gamecenter相關的class文件,啟動腳本,配置文件,全部放置到/opt/java_demo/formal/[conf-dir Dockerfile manage-script package]
為節省資源,直接將服務搭建在 10.0.0.100 上.

創建Dockerfile文件.並將鏡像上傳到ECR里,供k8s調用
[xbzj@ip-10-0-0-100 gamecenter-df]$ cat Dockerfile

FROM java:openjdk-8u111-jre    # 這個鏡像正常
#FROM java:latest    # 這個鏡像正常
#FROM openjdk:12.0.1-jdk    # 啟動應用的時候報錯
#FROM java:8-jre-alpine    # 啟動應用的時候報錯
#FROM openjdk:13-alpine    # 啟動應用的時候報錯

#將當前目錄復制到
COPY . /home/xbzj

#建立工作目錄
WORKDIR /home/xbzj/manage

#運行命令
#RUN ./Manage.sh

#自啟動命令
#CMD ["java","Main"]
ENTRYPOINT ["./Manage.sh"]
[xbzj@ip-10-0-0-100 gamecenter-df]$ cat .dockerignore    # 不打入鏡像的文件
Dockerfile
./bak
.dockerignore

如下(在ERS里,創建1個xbzj庫.登陸ERS/TAG/PUSH等,在aws控制台里有對應的提示指令,直接復制執行就可以了)(此步驟也在docker筆記中有對應說明)
docker build -t xbzj . # 這里的"."就是context(鏡像構建上下文) -t指明創建的鏡像名稱
aws ecr get-login --no-include-email --region ap-southeast-1 # 登陸ECR,執行此命令的輸出內容即完成登陸.
docker tag xbzj:latest 615624949551.dkr.ecr.ap-southeast-1.amazonaws.com/xbzj:latest
docker push 615624949551.dkr.ecr.ap-southeast-1.amazonaws.com/xbzj:latest # 上傳到ECR倉庫
注意,日常上傳鏡像只需要build,tag,push這3步就可以了.

啟動容器測試
為了確保鏡像是正確的,可以先隨便找台docker測試一下是否正常.
docker container run -it my-java:first /bin/bash
ctrl + p,q 放置到后台
查看當前控制台輸出 docker container attach container_id
登陸容器中的shell執行其他指令 docker container exec -it container_id /bin/bash ; # 可以確認需要的文件是否全部拷貝過來了,啟動的端口是否如預期等.

創建K8S集群
參考aws k8s 集群入門部分.(使用cloudformation創建VPC;創建EKS;使用cloudformation創建worker組;配置kubectl;將worker加入集群;)

創建對等連接,修改路由,安全組
因為kubectl主機(上面創建了redis,mysql,consul等服務)在VPC-01(10.0.0.0/16)上.而eks的worker都在eks-vpc里.所以默認兩個vpc里的主機是無法通訊的.
也就是worker上面啟動的pod(image里打的redis地址是10.0.0.100)無法訪問到redis服務.
需要創建vpc對等連接.
然后在兩邊的vpc上創建對應的路由表(讓兩個vpc能夠找到對方).
然后分別修改kubectl主機,以及worker主機對應的安全組,允許彼此內網IP訪問自己的所有服務.
如果需要kubectl主機訪問worker或者worker上的pod,則記得在worker的安全組里加入對應的安全組規則(允許內網地址訪問自己所有服務).默認生成worker的安全組的時候,並不會自動添加這一條安全策略,需要手動添加.

創建NFS服務器
參考對應NFS筆記內容.
附錄,Manage.sh文件放置在 /data/gamecenter/

[root@ip-10-0-0-101 gamecenter]# cat Manage.sh
#!/bin/bash
#Function declaration:
#This script is used to start and stop the program
#Date:20180717 Update:
if [ -f /etc/init.d/functions ];then
. /etc/init.d/functions;
fi
source /etc/profile # 這里不能用於openjdk
export CODE_BASE="../package"
export GAME_ID='game002'
export SERVER_ID='aws-pub'
export REGION='default'
java $(exec ./BuildOpts.sh  --config_dir ./ --memory 500M)

創建gamecenter對應的PV,PVC

[root@ip-10-0-0-100 gamecenter]# cat gamecenter-pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: gamecenter-pv
spec:
  capacity:
    storage: 1Gi    # capacity指定PV的容量為1GB
  accessModes:
    - ReadWriteOnce # ReadWriteOnce,ReadOnlyMany,ReadWriteMany 3種.Once表示單個節點
  persistentVolumeReclaimPolicy: Retain    # PV的回收策略.Retain表示管理員手動回收.Recycle表示清除PV中的數據,同rm -rf /thevolume/*.Delete表示刪除Storage Provider上對應的存儲資源,例如AWS EBS.
  storageClassName: nfs # 指定PV的class為nfs.相當於為PV設置了一個分類,PVC可以指定class申請相應的class的PV.
  nfs:
    path: /data/gamecenter  # 指定PV在NFS服務器上對應的目錄.這里直接將 BuildOpts.sh  Manage.sh 腳本以及配置文件game.properties  放到此文件夾.生成的一些日志也放在此.
    server: 10.0.0.101

[root@ip-10-0-0-100 gamecenter]# cat gamecenter-pvc.yml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: gamecenter-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs

執行對應的PV,PVC:略.

制作主yml文件(含PV,PVC的說明).

[root@ip-10-0-0-100 gamecenter]# cat gamecenter.yml
apiVersion: apps/v1beta1 # apiVersion是當前配置格式的版本
kind: Deployment    # kind 是要創建的資源類型, 這里是Deployment.
metadata:   # metadata 是該資源的元數據, name 是必須的元數據項.
  name: gamecenter-deployment
spec:   # spec 部分是該Deployment的規格說明.
  replicas: 1   # replicas 指明副本數量, 默認是1.
  template: # template 定義Pod 的模板, 這是配置文件的重要部分.
    metadata:   # metadata 定義Pod的元數據, 至少要定義一個label. label 的key和value 可以任意指定.
      labels:
        app: gamecenter_server
    spec:   # spec 描述 Pod的規格,此部分定義Pod中的每一個容器的屬性, name 和 image 是必須的.
      containers:
      - name: gamecenter
        image: 615624949551.dkr.ecr.ap-southeast-1.amazonaws.com/xbzj:latest
        #ports:
        #- containerPort: 80
        volumeMounts:
        - mountPath: "/home/xbzj/manage"
          name: gamecenterdata
      volumes:
        - name: gamecenterdata
          persistentVolumeClaim:
            claimName: gamecenter-pvc

---
apiVersion: v1 # apiVersion是當前配置格式的版本
kind: Service    # kind 是要創建的資源類型
metadata:   # metadata 是該資源的元數據, name 是必須的元數據項.
  name: gamecenter-svc   # Service 的名字
spec:   # spec 部分是該Deployment的規格說明.
  type: LoadBalancer # NodePort方式.還可以是ClusterIP,或者LoadBalancer
  loadBalancerSourceRanges: # 只允許內網訪問(EKS會自動在負載均衡器里配置相應的安全組)
  - "10.0.0.0/16"
  - "192.168.0.0/16"
  selector:
    app: gamecenter_server # selector 指明挑選那些 label 為 run:httpd 的 Pod 作為 Service的后端
  ports:
  - protocol: TCP # 將service的8080端口映射到Pod的80端口,使用TCP協議
    name: name8200
    #nodePort: 30000 # 是各個節點上監聽的端口(默認隨機30000-32767)
    port: 8200  # 是ClusterIP上監聽的端口(Service的)
    targetPort: 8200  # 是Pod監聽的端口
  - protocol: TCP # 將service的8080端口映射到Pod的80端口,使用TCP協議
    name: name8201
    #nodePort: 30000 # 是各個節點上監聽的端口(默認隨機30000-32767)
    port: 8201  # 是ClusterIP上監聽的端口(Service的)
    targetPort: 8201  # 是Pod監聽的端口

備注: 只允許內網IP訪問的yml配置參考 https://kubernetes.io/docs/concepts/services-networking/service/#network-load-balancer-support-on-aws-alpha

執行yml文件
kubectl apply -f gamecenter.yml
會依次創建pod,Deployment,service,以及對應的ELB,和ELB綁定的安全組.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM