《kubernetes + .net core 》dev ops部分

目录

1.kubernetes 预备知识

kubernetes是一个用go语言写的容器编排框架,常与docker搭配使用。
kubernetes是谷歌内部的容器编排框架的开源实现。能够用来方便的管理容器集群。具备不少 优势,要了解这些优势,须要先来了解一下kubernetes中的集群资源。这里指的是kubernetes里的原生的资源,kubernetes也支持自定义的资源。html

1.1 集群资源

不区分名称空间前端

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

1.1.1 role

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

群集默认采用RBAC(role base access control)进行集群资源的访问控制,能够将角色做用于用户user服务service,限制它们访问群集资源的权限范围。node

其中角色又区分为 集群角色 cluster role普通的角色 role ,他们的区别是能够做用的范围不一致。mysql

普通角色必有名称空间限制,只能做用于与它同名称空间的用户或服务。linux

集群角色没有名称空间限制,能够用于全部名称空间的用户或服务。下面的目录中会详细介绍。先在这里提一下nginx

1.1.2 namespace

不指定时默认做用default名称空间,服务在跨名称空间访问其余服务时 域名须要加上名称空间后辍才能访问git

1.1.3 node

  • 主节点 master
  • 工做节点 none
  • 边缘节点 none

是一个包含操做系统的机器,操做系统能够是Linux也能够是windwos,能够是实体机也能够是虚拟机,其中的区别下面的其余目录会详细说明程序员

1.1.4 persistent volume

持久卷 ,支持的类型不少,包括谷歌 亚马逊 阿里云云服务提供商的各类存储.github

因为咱们的项目通常是用于局域网内的,因此这里我着重介绍nfs(network file system)web

1.1.5 storage class

存储类,用于根据pvc 自动建立/自动挂载/自动回收 对应的nfs目录前

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

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

1.2.1 pod

工做量的最小单位是pod 其余的类型的工做量都是控制Pod的。

pod至关于docker 中的docker composite,能够由单个或多个容器组成,每一个pod有本身的docker网络,pod里的container处于同个局域网中。
其余的控制器都有一个pod template,用于建立Pod

1.2.2 job

工做,一但应用到集群将会建立一个pod作一些工做,具体的工做内容由Pod的实现决定,工做完成后Pod自动终结。

1.2.3 cron job

定时工做任务,一但应用到集群,集群将会定时建立pod 作一些工做,工做完成后pod自动终结

1.2.4 replica set

复制集或称为副本集,一但应用到集群,会建立相n个相同的 pod,而且会维护这个pod的数量,若是有pod异常终结,replica set会建立一个新的Pod 以维护用户指定的数量

1.2.5 deplyoment

deplyoment经常使用来建立无状态的应用集群。

部署,deplyoment依赖于replicaset ,它支持滚动更新,滚动更新的原理是,在原有的一个replica set的基础上建立一个新版本的replica set ,

旧版本的replicaset 逐个减小 ,新版本的replicaset逐个新增, 能够设置一个参数指定滚动更新时要保持的最小可用pod数量。

1.2.6 daemon set

守护进程集 ,顾名思义,他的做用就是维护某个操做系统(node)的某个进程(pod)始终工做。当一个dameon set被应用到k8s集群,全部它指定的节点上都会建立某个pod
好比日志采集器 一个节点上有一个,用daemon set就十分应景。

1.2.7 stateful set

stateful set经常使用于建立有状态的服务集群,它具备如下特色

  • 稳定的惟一网络标识符
  • 稳定,持久的存储
  • 有序,顺畅的部署和扩展
  • 有序的自动滚动更新

举个例子,你有一个容器须要存数据,好比mysql容器,这时你用deplyoment就不合适,由于多个mysql实例各自应该有本身的存储,DNS名称。
这个时候就应该使用statefull set。它原理是建立无头服务(没有集群ip的服务)和有序号的Pod,并把这个无头服务的域名+有序号的主机名(pod名称),得到惟一的DNS名称
好比设置stateful set的名称为web,redplica=2,则会有序的建立两个Pod:web-0 web-1,当web-0就绪后才会建立web-1,若是是扩容时也是这样的,而收容的时候顺序而是反过来的,会从序号大的Pod开始删除多余的Pod

若是把一个名称为nginx的无头服务指向这个statufulset,则web-0的dns名称应该为 web-0.nginx

而且 stateful set会为这两个Pod建立各自的pvc,因为pod的名称是惟一的,因此故障重建Pod时,能够把新的Pod关联到原有的存储卷上

1.3 存储和配置资源 (消耗存储)

  • config map
  • secret map
  • persistent volume claim

1.3.1 config map

ConfigMap 容许你将配置文件与镜像文件分离,以使容器化的应用程序具备可移植性。

  • 如何建立
  • 如何使用
    建立:
# 从文件夹建立(文件夹里的文本文件将会被建立成config map
  kubectl create configmap my-config --from-file=path/to/bar

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

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

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

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

使用:

  • 做为pod的环境变量
  • 做为存储卷挂载到Pod

1.3.2 secrets

Secret 是一种包含少许敏感信息例如密码、令牌或密钥的对象。 这样的信息可能会被放在 Pod 规约中或者镜像中。 用户能够建立 Secret,同时系统也建立了一些 Secret。

1.3.3 pvc

  • 由集群管理员管理
  • 由storage class管理

若是由集群管理员管理,由开发人员应向集群管理员申请Pv ,集群管理员要手动的建立Pv,pvc,把pvc给开发人员,开发人员手动挂载pvc到pod

若是由storage class管理,则集群管理员只要建立一个provider, 以后provider会自动监视集群中的Pvc 和pod ,自动建立pv和挂载

2.kubernetes node 组件

kubernetes集群中,至少要有一个主节点,主节点应该有如下组件

  • kubelet
  • kuber-proxy
  • cni网络插件
  • etcd
  • kube-apiserver
  • coreDNS
  • kube-controller-manager
  • kube-schedule

普通节点的组件

  • kubelet

  • kube-proxy

  • cni网络插件

2.1 kubectl

kubernetes节点代理组件,每一个node上都须要有,他不是Kubernetes建立的容器,因此在集群中查不到。
他的主要作的工做是

1.向kube api以hostname为名注册节点
2.监视pod运行参数,使Pod以参数预期的状态运行,因此Pod有异常一般都能查询Kubelet的日志来排查错误

2.2 kube-proxy

kubernetes 的服务相关的组件,每一个Node上都须要有,除了无头服务,其余全部服务都由他处理流量

k8s集群在初始化的时候,会指定服务网段和pod网段,其中服务网段的ip都是虚拟Ip

他主要作的工做是:

监视集群的服务,若是服务知足某些条件,则经过ipvs 把这个服务的流量转发到各个后端Pod (cluster ip)

2.3 cni网络插件

cni: container network interface

k8s称之为窗口编排集群,他的核心思想是把不一样的容器网络联合起来,使全部的pod都在一个扁平的网络里,能够互换访问

为达这个目的就须要容器网络插件,下面介绍一下主流的cni插件,并大体说明一下优劣

  • flannel
  • calico
  • 其余

2.3.1 flannel

Flannel
Flannel

flanel是桥接模式的表明插件,
他的工做原理是用daemonset在每一个节点上部署flannel插件,插件设置容器网络并把容器网络信息经过 kube api存储到etcd中 。

这样就确保不会重复注册网段了,与不一样node上的Pod经过 kube-proxy打包 发给其余Node的kuber proxy,kuber proxy再拆包,发给pod
以达到跨node的扁平网络访问. 这种方式也称vxlan 或overlay
优势:

  • 网络协议简单,容易分析。
  • 社区规模比较大,成功案例比较多,资料比较全面,入门比较简单

缺点:

  • 因为有打包 拆包, 因此通信效率比较低下
  • 不支持网络策略

2.3.2 calico

calico 是网关模式的表明插件。 它主要由如下几部分构成
它基于边界网关协议 BGP(border gateway protocol)
他的工做原理是用daemonset在每一个节点上部署calico node, 来构成扁平化容器网络
calico node由如下几个组件

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

felix 负责编写路由和访问控制列表

confid 用于把 felix生成的数据记录到etcd,用于持久化规则

BIRD 用于广播felix写到系统内核的路由规则和访问控制列表acl和calico的网络

当集群规模比较大的时候还能够可选的安装 BGP Rotue Reflector(BIRD) 和 Typha

前者用于快速广播协议,后者用于直接与ETCD通信,减少 kubeapi的压力

优势:

  • pod跨node的网络流量 直接进系统内核 走路由表,效率极高
  • 支持网络策略

缺点:

  • 跨node的数据包通过DNAT和SNAT后,分析网络封包会比较复杂
  • 部署也比较复杂

Calico
Calico

2.4 etcd

etc distributed ,一款使用go语言编写的基于raft共识算法的分布式KV缓存框架 ,
不像redis重性能,而像zookeeper 同样重数据一致性
特色是有较高的写入性能
Etcd Disk

Etcd Network

Etcd Cpu

Etcd Memory

Etcd Throughput

Etcd Latency Distribution

2.5 kube-apiserver

k8s 暴露给外部的web api,用于集群的交互 有各类语言的api client开源项目 ,程序员也能够在程序中引用,监视一些集群资源

2.6 coreDNS

用于集群中的service 和 pod的域名解析,

也能够配置对集群外的域名应该用哪一个DNS解析

2.7 kube-controller-manager

用于 各类控制器(消耗cpu ram)的管理

2.8 kube-schedule

用于 管理控制 Pod调度相关

3.集群的高可用

  • 分布式共识算法 Raft
  • keepalived
  • haproxy

File

3.1 etcd的raft算法

raft

raft是etcd的共识算法,kubernetes用etcd来存储集群的配置。config map /secret都是基于etcd。

理解raft共识算法能够知道

  • 为何高可用集群主节点是3个 5个 7个 而不是 2个 4个 6个
  • kubernetes的主节点发生单点故障的时候, 存储的行为会有什么改变

3.2 keepalived

在高可用环境, keepalived用于虚拟ip的选举,一旦持有虚拟Ip的节点发生故障,其余的主节点会选择出新的主节点持有虚拟ip。而且能够配置smtp信息,当节点故障的时候发邮件通知相关的责任人

onfiguration File for keepalived

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

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

3.3 haproxy

每一个主节点都部署了haproxy代理kube api端口, 因此当它持有虚拟ip的时候,会把全部对kube api的请求负载均衡到全部的主节点上。

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

defaults
        log     global
        mode    tcp
        retries 3
        option redispatch

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

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

4 认证/受权

4.1 authentication

kubernetes集群中的认证对象分为

  1. 用户
  2. 服务

除此以外,还有一些其余的非kubernetes集群管理的服务会须要访问集群资源的状况

可是这个暂时不实践,由于haoyun目前不会使用到这种状况

4.1.1 用户

用户不是kuebrnetes 的资源,因此单独拎出来说。

4.1.1.1 查看用户

master node在加入集群时,会提示咱们手动复制默认的管理员用户到 $HOME/.kube 文件夹

因此查看 $HOME/.kube/config文件能够知道集群 用户 认证上下文

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

4.1.1.2 新增用户

建立1用户,并进入用户文件夹

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

建立私钥

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

建立x.509证书签名请求 (CSR) ,CN会被识别为用户名 O会被识别为组

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

为CSR签入kubernetes 的证书和证书公钥,生成证书

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

建立证书目录 ,存入把公钥(hbb.crt)和私钥(hbb_private.key) 放进去

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

建立集群用户

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

建立用户上下文

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

这时原有的config文件里就多了Hbb这个用户了

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

复制 原有的.kube/config文件到hbb用户文件夹 ,在副本上删除kubernetes-admin的上下文和用户信息。

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

以后把hbb用户文件夹受权给hbb用户

[root@www .kube]# chown -R hbb:  /home/hbb/
#-R 递归文件夹 
#hbb: 只设置了用户没有设置组

这样就建立一个用户了,此时退出root用户,使用hbb用户登上去,默认就是使用Hbb的user去访问集群

可是这时尚未给hbb受权,因此基本上什么操做都执行不了,由于没有权限

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

4.2 authorization (RBAC)

官方文档

相关的kubernetes 资源

  • namespace
  • roles/clusterRoles
  • rolebindings/clusterRolebindings

4.2.1 namespace

roles和rolebindings 若是要创建关联,他们必须是同一个名称空间内。

clusterRoles和clusterRolebindings 没有名称空间的限制,它们的规则做用于集群范围

4.2.2 roles/clusterRoles

在 RBAC API 中,一个角色包含一组相关权限的规则。权限是纯粹累加的(不存在拒绝某操做的规则)。 角色能够用 Role 来定义到某个命名空间上, 或者用 ClusterRole 来定义到整个集群做用域。

一个 Role 只能够用来对某一命名空间中的资源赋予访问权限。 下面的 Role 示例定义到名称为 "default" 的命名空间,能够用来授予对该命名空间中的 Pods 的读取权限:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default #若是是clusterRoles 则没删除这一行
  name: pod-reader
rules:
- apiGroups: [""] # "" 指定 API 组
  resources: ["pods","pods/log"] #子资源
  verbs: ["get", "watch", "list"]
  
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["my-configmap"] #具体名称的资源
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  
#下面这个只有clusterRoles 才可使用
- nonResourceURLs: ["/healthz", "/healthz/*"] # '*' 在 nonResourceURL 中的意思是后缀全局匹配。
  verbs: ["get", "post"]

clusterRoles比 roles 多出如下的能力

  • 集群范围资源 (好比 nodes)
  • 非资源端点(好比 "/healthz")
  • 跨命名空间访问的有名字空间做用域的资源(如 get pods --all-namespaces)

4.2.3 rolebindings/clusterRolebindings

rolebindings也可使用ClusterRoles,会将里面的资源的做用域限定到rolebindings的名称空间范围内。

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
  
subjects:
- kind: User #用户
  name: jane
  apiGroup: rbac.authorization.k8s.io
- kind: Group #用户组
  name: "frontend-admins"
  apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount #服务账户
  name: default
  namespace: kube-system
- kind: Group # 全部myNamespace名称空间下的服务
  name: system:serviceaccounts:myNamespace
  apiGroup: rbac.authorization.k8s.io
- kind: Group #全部名称空间的全部服务
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io
- kind: Group #全部认证过的用户
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
- kind: Group #全部未谁的用户
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
  
  
roleRef:
  kind: Role #能够是集群名角或普通的角色
  name: pod-reader 
  apiGroup: rbac.authorization.k8s.io

5.服务

官方文档
服务是微服务抽象,经常使用于经过选择符访问一组Pod, 也能够访问其余对象,

  • 集群外部的服务
  • 其余名称空间的服务

上面这两种状况,服务的选择符能够省略。

5.1 代理模式

kubernetes v1.0时使用用户空间代理 (userspace)

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

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

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

5.1.1 userSpace模式

Userspace

每一个结点上部署kube-proxy ,它会监视主结点的apiserver对service 和 endpoints的增删改

为每一个server随机开一个端口,并写入集群Ip写入iptables,把对集群服务的集群Ip的流量 转发到这个随机的端口上 ,

而后再转发到后端的Pod上, 通常是采用轮询的规则,根据服务上的sessionAnfinity来设置链接的亲和性

5.1.2 iptables模式

Iptables

与userspace的区别是 不只把service写入Iptables,同时把endpoints也写入了iptables,
因此不用在内核空间和用户空间之间来回切换,性能提高

5.1.3 ipvs

Ipvs

ipvs(ip virtrual server)和iptables都是基于netfilter ,但ipvs以哈希表作为基础数据结构,并工做在内核空间
相比iptables,因此他有更好的性能,也支持更多的负载均衡算法

  • rr: round-robin 轮询
  • lc: least connection (smallest number of open connections) 最少链接
  • dh: destination hashing 目标哈希
  • sh: source hashing 源哈希
  • sed: shortest expected delay 最低延迟
  • nq: never queue 不排队

若是须要粘性会话,能够在服务中设置
service.spec.sessionAffinity 为 clusterip ,默认是none
service.spec.sessionAffinityConfig.clientIP.timeoutSeconds 能够调整会话最长时间,默认是10800秒

5.2 服务发现

服务能够经过环境变量和DNS的方式来发现服务,推荐的作法是经过DNS

5.2.1 经过环境变量

一个名称为 "redis-master" 的 Service 暴露了 TCP 端口 6379, 同时给它分配了 Cluster IP 地址 10.0.0.11 ,
这个 Service 生成了以下环境变量:

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

若是须要在pod中使用这些环境变量,须要在启动pod以前先启动服务。

5.2.2 经过DNS

服务直接用服务名称为域名,
Pod会在服务以前加上ip为域名
例如在名称空间 hbb下有服务 hbb-api, 服务指向的后端pod Ip地址是10-244-6-27.,则会有dns记录

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

dns应该尽量使用,最好不要使用环境变量的方式

5.3 服务类型

  • clusterip 集群IP
  • nodeport 结点IP
  • loadbalance 外部负载均衡器
  • external ip 外部IP
  • none 无头服务(有状态服务)
  • externalname 外部服务

5.3.1 clusterip 集群IP

虚拟的ip ,一般指向一组pod (真实ip)

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

5.3.2 nodeport 结点IP

每一个主结点上的具体端口,一般把 node ip+端口 转发到 一组pod(真实ip)

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app: MyApp
  ports:
      # 默认状况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
    - port: 80
      targetPort: 80
      # 可选字段
      # 默认状况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号(默认:30000-32767)
      nodePort: 30007

5.3.3 loadbalance 外部负载均衡器

一般把 外部流量 转发到 一组pod(真实ip) ,外部ip通常是在双网卡的边缘节点上

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

5.3.4 external IP

将外部的流量引入服务 ,这种外部Ip 不禁集群管理,由由集群管理员维护

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

5.3.5 none 无头服务(有状态服务)

kube-proxy组件不对无头服务进行代理,无头服务 加上序号 指向 Pod,固定搭配,

因此即便 服务的pod挂了, 重启来的服务的 域名也不会换一个,用于有状态的服务。

后面讲到Pod控制器statefulset会再细讲

5.3.6 externalname 外部服务

kube-proxy组件不会对外部服务进行代理则是映射到dns 用于描述一个集群外部的服务,有解耦合的做用,

因此它和无头服务同样没有选择器,他也不禁集群管理,而是由集群管理员维护

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

5.4 集群入口 ingress

因为iptables代理模块或亦 ipvs代理模式都是4层负载均衡,没法对7层协议进行负载均衡,因此对于外部的流量 ,常使用入口资源来进行负载均衡,把外部的流量均衡到服务上

  • ingress contorller
  • ingress 资源

5.4.1 ingress controller

ingress controller 是负载均衡器实例,一个集群中能够部署多个, 每一个又能够自为一个负载均衡集群

在建立ingress资源的时候,能够用 注解Annotations:来指定要使用哪一个ingress controller

kubernetes.io/ingress.class: nginx

这个nginx是controller容器启动时 用命令行的方式指定的

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

5.4.2 ingress 资源

Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。

能够将 Ingress 配置为服务提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS,以及提供基于名称的虚拟主机等能力。 Ingress 控制器 一般负责经过负载均衡器来实现 Ingress,尽管它也能够配置边缘路由器或其余前端来帮助处理流量。

Ingress 不会公开任意端口或协议。 将 HTTP 和 HTTPS 之外的服务公开到 Internet 时,一般使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 类型的服务

5.4.2.1 捕获重写Path 转发

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

5.4.2.2 基于主机域名转发

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

6.配置和存储

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

6.1 config map

ConfigMap 容许你将配置文件与镜像文件分离,以使容器化的应用程序具备可移植性。

6.1.1 建立config map

# 从文件夹建立(文件夹里的文本文件将会被建立成config map
  kubectl create configmap my-config --from-file=path/to/bar

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

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

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

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

6.1.2 使用config map

  • 做为pod的环境变量
  • 做为存储卷挂载到Pod

6.1.2.1 做为pod的环境变量

建立1个config map 配置文件,在default名称空间里

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

建立一个pod ,使用busybox镜像,并把上面的cm 加载到环境变量,在pod 的container里面加上

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

简易写法,加载全部hbb-config里的key value

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

经测试,若是修改了config map ,Pod的环境变量是不会自动更新的,除非删除pod从新建立

6.1.2.2 做为存储卷挂载到pod

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

经验证,修改config map后,去查看挂载上去的卷,文件中的值也随之发生了改变,因此这种方式是比较好的方式。

6.2 secrets

Secret 是一种包含少许敏感信息例如密码、令牌或密钥的对象。 这样的信息可能会被放在 Pod 规约中或者镜像中。 用户能够建立 Secret,同时系统也建立了一些 Secret。

  • 建立secret
  • 验证 secret
  • 使用 secret

6.2.1 建立secrets

  • 经过文件生成
  • 经过字符串生成
  • 手动建立
  • 经过stringData 应用时加密明文secret
  • 查看验证

6.2.1.1 经过文件生成

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

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

默认的键名就是文件名,若是要另外指定能够--from-file=[key=]source,密码也是如此

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

6.2.1.2 经过字符串生成

说明:特殊字符(例如 $**=!)可能会被 sell转义,因此要用''括起来

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

6.2.1.3 手动建立 secret

加密用户名admin和密码password

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

建立一个mysecret.yaml

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

用kubectl 建立

kubectl apply -f ./mysecret.yaml

6.2.1.4 经过stringData 应用时加密明文

建立1个 mysecret.yaml

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

执行

kubectl apply -f ./mysecret.yaml

将会建立 一个mysecret 资源,里面有一个config.yaml的key,它的value是一个加密的字符串。

注: mysecret.yaml第7行的|- 的意思是:将下面三行字符串组合起来,替换右边的缩进(空格和换行)成一个换行符。

这种方式的好处是,能够和helm一块儿使用,helm使用go 的模版,能够配置明文的字符

6.2.2 查看验证secret

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

Type:  Opaque

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

使用get 和describe 查看secret都不能直接看到data里的数据。

若是要查看secret加密前的值应该用 get -o yaml 查看到base64字符串后再解码

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

6.2.3 使用secret

和config map一毛同样,略

6.3 nfs persistent volume

  • provisioning 供应方式
  • persistent volume 持久卷
  • persistent volume claim 持久卷声明
  • storage class 存储类

6.3.1 provisioning 持久卷供应方案

  • 静态
  • 动态

6.3.1.1 静态供应

  1. 集群管理员建立Pv
  2. 集群用户 建立pvc
  3. 集群用户 绑定pvc到Pod

6.3.1.2 动态供应

  1. 集群管理员建立storage class

  2. storage class监视集群中的 pvc和pod

    • 集群用户建立pvc时若是挂在这个storage class上,则会自动建立pv,pv继承storage class的回收策略
    • pod使用了Pvc, 则storage class会自动把pv挂载到pod的卷上
    • pvc 删除时 根据storage的回收策略回收 pv

6.3.2 pv persistent volume

PersistentVolume(PV)是已经由管理员提供或者动态使用供应的集群中的一块存储的存储类。它是集群中的资源,就像节点是集群资源同样。PV是相似于Volumes的卷插件,可是其生命周期与使用PV的任何单个Pod无关。

  • access mode 访问模式
  • reclaim policy 回收策略
  • source 存储源
  • 节点亲和力

6.3.2.1 access mode 访问模式

  • ReadWriteOnce-能够经过单个节点以读写方式安装该卷RWO
  • ReadOnlyMany-该卷能够被许多节点只读安装ROX
  • ReadWriteMany-该卷能够被许多节点读写安装RWX

6.3.2.2 reclaim policy 回收策略

若是用户删除了Pod正在使用的PVC,则不会当即删除该PVC。PVC的清除被推迟,直到任何Pod再也不主动使用PVC。另外,若是管理员删除绑定到PVC的PV,则不会当即删除该PV。PV的去除被推迟,直到PV再也不与PVC结合。

当用户完成其卷处理后,他们能够从容许回收资源的API中删除PVC对象。PersistentVolume的回收策略告诉集群在释放其声明以后如何处理该卷。当前,能够保留,回收或删除卷。

  • retain 保留
  • delete 删除
  • Recycle 回收(已弃用,使用动态供应代替)
6.3.2.2.1 retain 保留

这种策略在pvc删除后,会保留pv,并释放pv,但这个pv不能被其余pvc重用。若是要回收须要集群管理员手动的去回收,回收步骤以下

  1. 删除pv
  2. 手动清理pv中的文件数据
  3. 若是要重用存储介质,须要重声明一个pv
6.3.2.2.2 delete 删除

默认的策略是删除,删除Pvc ,若是存储卷类型支持的话,将会同时删除pv及其中的文件数据

6.3.2.2.3 recycle 回收(弃用

若是pv的介质支持的话,此回收策略将会使用

rm -rf /volume/*

清理pv的数据,而后使这个pv能够被其余pvc使用,因为这样常常会致使意外终结的Pod,pv里的数据来不及排查就被回收,因此这种方式已被 弃用。应该使用动态配置+手动回收来避免这种状况发生。

6.3.2.3 存储源

因为咱们集群环境是私有的局域网,因此一般只会使用nfs来做为存储的介质,其余的类型还有不少,这里不会介绍更多,须要有须要了解能够到k8s中文社区自行查阅资料,下面是一个由nfs provisioner建立的pv,能够从存储源看出对应的nfs服务的相关信息

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

6.3.2.4 节点亲和力

pv一旦设置了节点亲和力,则与Pv结合的Pod都会部署到命中的节点上

6.3.3 persistent volume claim

PersistentVolumeClaim(PVC)是由用户进行存储的请求。它相似于pod。pod消耗节点资源,PVC消耗PV资源。Pod能够请求特定级别的资源(CPU和内存)。声明能够请求特定的大小和访问模式

静态供应状况下,pvc绑定Pv

动态供应状况下,pvc绑定 storage class

6.3.4 storage class

虽然PersistentVolumeClaims容许用户使用抽象存储资源,但对于不一样的pod,用户一般须要具备不一样存储介质,例如机械硬盘和固态硬盘,机房1和机房2,集群管理员须要可以提供各类PersistentVolume,这些PersistentVolume不只在大小和访问模式上有更多差别,并且还不让用户了解如何实现这些卷的细节。

这个是推荐使用的方式,因此这里重点实践

  1. nfs 服务器搭建(www.haoyun.nfs1),并验证可用性
  2. 基于nfs部署一个 nfs client provisioner(www.haoyun.nfs1/k8s)
  3. 建立1个storage class
  4. 建立1个绑定storage class的pvc
  5. 建立1个绑定pvc的pod
  6. 验证

6.3.4.1 nfs服务器搭建

略,

验证

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

2. 建立挂载目录
  # mkdir /var/nfs

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

4. 挂载NFS Server目录
  # mount -t nfs www.haoyun.nfs1:/k8s ~/nfs-test
  
5. 测试完卸载
   # umount -t nfs ~/nfs-test/

6.3.4.2 nfs client provisioner

6.3.4.3 建立1个storage class

用工具安装部署完nfs provisioner时已经生成,略

6.3.4.4 建立1个绑定storage class的pvc

建立文件 pvc.yaml

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

添加pvc到集群中的nfs名称空间,storage class是集群资源,任何名称空间均可以使用

kubectl apply -f pvc.yaml -n nfs

6.3.4.5 建立pod,验证nfs provisioner

建立podWithPvc.yaml

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

添加pod到nfs名称空间

kubectl apply -f podWithPvc.yaml

这个Pod会挂载storage class生成的pv到/mnt目录,并建立一个名为seccess的文件

到nfs服务器上查看/k8s目录 能够看到多了一个名为storageClass名称+pod名称的目录

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

删除pod 再删除pvc 再去查看

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

目录的名称变为archived + storageClass名称 + pod名称,这是由于storage class 声明时设置成删除且归档了

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

这种方式比较直接删除更能保障数据安全,可是须要集群管理员定时删除没必要要的归档,(例如超过1个月的归档没有开发人员认领,就删除掉)

6.4 empty volume 和 hostpath volume

补充两种卷,

  • empty
  • hostpath

6.4.1 empty volume

当 Pod 指定到某个节点上时,首先建立的是一个 emptyDir 卷,而且只要 Pod 在该节点上运行,卷就一直存在。 就像它的名称表示的那样,卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不一样,可是这些容器均可以读写 emptyDir 卷中相同的文件。 当 Pod 由于某些缘由被从节点上删除时,emptyDir 卷中的数据也会永久删除。

说明: 容器崩溃并不会致使 Pod 被从节点上移除,所以容器崩溃时 emptyDir 卷中的数据是安全的。

emptyDir 的一些用途:

  • 缓存空间,例如基于磁盘的归并排序。
  • 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
  • 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。

默认状况下, emptyDir 卷存储在支持该节点所使用的介质上;这里的介质能够是磁盘或 SSD 或网络存储,这取决于您的环境。 可是,您能够将 emptyDir.medium 字段设置为 "Memory",以告诉 Kubernetes 为您安装 tmpfs(基于 RAM 的文件系统)。 虽然 tmpfs 速度很是快,可是要注意它与磁盘不同。 tmpfs 在节点重启时会被清除,而且您所写入的全部文件都会计入容器的内存消耗,受容器内存限制约束。

示例

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

6.4.2 hostpath volume

不经常使用的方式, Pod挂载node上的文件目录到pod中,略

文档

7.调度

文档

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

7.1 nodeSelector(节点选择器)

  • 节点标签
  • Pod 加spec.nodeSelector
  • 部署验证

7.1.1 节点标签

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

挑选一个内置的hostname标签来验证

kubernetes.io/hostname=www.haoyun.edge1

7.1.2 pod加节点选择器

建立一个busybox-nodeselector.yaml,在里面加上一个节点选择器,

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

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

应用它到集群

kubectl apply -f busybox-nodeselector.yaml

7.1.3 部署验证

查看运行状态,确实跑到edge1上了

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

查看生成的pod详情

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

验证经过

7.2 taints/tolerationss (污点/容忍)

污点是打在node上的标记,容忍是打在Pod上的标记

  1. 给node加污点 taints
  2. 给pod打容忍标记 tolerations
  3. 部署pod验证

7.2.1 给node 打污点标记

查看主节点www.haoyu.m1的污点

kubectl describe no www.haoyun.m1

查看spec.taints

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

已打一个名为node-role.kubernetes.io/master 的Noschedule的污点

7.2.2 给pod打容忍标记

先用get pod -o wide查看到一个部署在www.haoyun.m1结点上的Pod

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

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

其中的

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

表示能够容忍节点上存在这个污点

7.2.3 部署验证

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

亲和力/反亲和力 又分为

  • 节点亲和力/反亲和力
  • pod亲和力/反亲和力

node亲和力用于限定Pod部署到Node上的命中规则,

Pod亲和力则用于限定 Pod与pod 布置到同一个Node上

他们都有多种匹配规则和 “软” “硬”两次匹配策略

交叉相实际上是四种不一样的设置,但因为他们大同小异,故在此只以节点的亲和力来作说明。

7.3.1 节点亲和力

节点亲和力 /反亲和力使用步骤以下

  1. 给结点标签
  2. 给pod加上节点亲和力 /反亲和力
  3. 部署Pod验证

7.3.1.1 给结点打标签

例如给名为www.haoyun.edge1的结点打上 isedgenode的标签

kubectl label no www.haoyun.edge1 isEdgeNode=true

查看节点已打的标签

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

7.3.1.2 给Pod加上节点亲和力

查看 nginx-ingress-controller的deployment

kubectl edit deployment -n kube-system

找到pod的template

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

能够看到spec.affinity.nodeAffinity里有一条规则,必需节点选择器命中isedgenode这个标签存在, 也就是说打标签的时候无论是设置什么值,只要有isedgenode就能够部署上去。

  • requiredDuringSchedulingIgnoredDuringExecution 必需
  • preferredDuringSchedulingIgnoredDuringExecution 尽量

相比起节点选择器,他有更灵活的匹配替换,并且能够有“软”,“硬”两种策略

写法相对也复杂得多。

7.3.1.3 部署Pod验证

因为已经部署过了,因此直接查看,能够看到nginx-ingress-controller的Pod是在www.haoyun.edge1的结点上的。

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

7.4 disruption 干扰调度

pod不会自动消息,除非自愿干扰或 非自愿干扰

  • PDB(pod disruption budget)

  • 非自愿干扰

  • 自愿干扰

7.4.1 pod disruption budget

pdb用于确保pod驱逐的过程当中服务的可用Pod数量在安全的范围内。

官方文档对不一样的部署方案的例子

  • 无状态前端:

    • 关注:服务容量减小不要超过10%。
      • 解决方案:例如,使用minAvailable 90%的PDB。
  • 单实例有状态应用程序:

    • 关注:请勿在不与我交谈的状况下终止此应用程序。
      • 可能的解决方案1:请勿使用PDB,而且能够承受偶尔的停机时间。
      • 可能的解决方案2:将PDB设置为maxUnavailable = 0。了解(在Kubernetes以外)集群操做员须要在终止以前咨询您。当集群操做员与您联系时,请准备停机,而后删除PDB以代表已准备好进行中断。以后从新建立。
  • 多实例有状态应用程序,例如Consul,ZooKeeper或etcd:

    • 关注:不要将实例数量减小到仲裁如下,不然写入将失败。
      • 可能的解决方案1:将maxUnavailable设置为1(适用于不一样的应用程序规模)。
      • 可能的解决方案2:将minAvailable设置为法定大小(例如,小数位数为5时为3)。(一次容许更多中断)。
  • 可从新启动的批处理做业:

    • 关注:在自愿中断的状况下,工做须要完成。

      • 可能的解决方案:不要建立PDB。做业控制器将建立一个替换容器。

pdb经过设置 maxunAvailable (最大不可用pod个数)或 minAvailable(最小可用pod个数) 来保证Pod驱逐时服务的可用性,例:

例:

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

7.4.2 非自愿干扰

  • 节点下层物理机的硬件故障
  • 集群管理员错误地删除虚拟机(实例)
  • 云提供商或虚拟机管理程序中的故障致使的虚拟机消失
  • 内核错误
  • 节点因为集群网络隔离从集群中消失
  • 因为节点资源不足致使 pod 被驱逐。

这里只着重介绍资源不足致使的Pod被驱逐,由于其余状况都与配置无关

7.4.2.1 eviction api

eviction api是一组kubelet的api,用于指定当Node的资源不足时,(通常是指硬盘空间不足或内存不足的状况 ,)要如何驱逐Pod以保证node的资源始终在合理的范围内,能够在kubelet 的config map里配置,或者命令行启动参数的方式设置eviction api, 可参阅文档

资源不足

设置Kubelet参数

一个例子:

  • 节点内存容量:10Gi
  • 操做员但愿为系统守护进程保留 10% 内存容量(内核、kubelet等)。
  • 操做员但愿在内存用量达到 95% 时驱逐 pod,以减小对系统的冲击并防止系统 OOM 的发生。

为了促成这个场景,kubelet将像下面这样启动:

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

这个配置的暗示是理解系统保留应该包含被驱逐阈值覆盖的内存数量。

要达到这个容量,要么某些 pod 使用了超过它们请求的资源,要么系统使用的内存超过 1.5Gi - 500Mi = 1Gi

这个配置将保证在 pod 使用量都不超过它们配置的请求值时,若是可能当即引发内存压力并触发驱逐时,调度器不会将 pod 放到这个节点上。

7.4.3 自愿干扰

  • 程序全部者主动干扰
  • 集群管理员主动干扰

7.4.3.1 程序全部者主动干扰

  • 删除 Deployment 或其余管理 Pod 的控制器
  • 更新了 Deployment 的 Pod 模板致使 Pod 重启
  • 直接删除 Pod(例如,由于误操做)

7.4.3.2 集群管理员主动干扰

  • 排空节点以进行维修或升级。
  • 从集群中排出节点以缩小集群规模(了解集群自动缩放 )。
  • 从节点上删除pod,以容许其余东西被调度到该节点。

本节只会介绍排空,由于2 通常用于云供应商平台,3太简单略。

7.4.3.2.1 drain /uncordon(排空 )

有时候某个Node须要中止运行维护,好比加内存之类的操做,这时若是只是删除Node上的pod, 则很大几率新的pod会从新被调度到这个node上,这种状况下集群管理员应该排空这个node,在维护结束后再结束以后再使用uncordon,使Pod能被调试到这个维护结束的node上

例如对 www.haoyun.edge1结点作排空操做

kubectl drain www.haoyun.edge1

维护结束以后恢复

kubectl uncordon www.haoyun.edge1

7.5 Horizontal pod Autoscaler

略,须要配合promethues 之类的数据采集才可使用,可能以后专门讲promethues再讨论,由于这个功能对浩云来讲不是很重要

8.pod配置

  • enviorment 环境变量
  • volume 卷
  • proms 探针

8.1 设置pod环境变量

  • 写在docker file里

  • 使用helm 写pod 参数里传递

  • 使用config map

8.2 设置pod的存储卷

参见 6.配置和存储

8.3 probes 探针

kubelet 使用存活探测器来知道何时要重启容器。 例如,存活探测器能够捕捉到死锁(应用程序在运行,可是没法继续执行后面的步骤)。 这样的状况下重启容器有助于让应用程序在有问题的状况下更可用。

kubelet 使用就绪探测器能够知道容器何时准备好了并能够开始接受请求流量, 当一个 Pod 内的全部容器都准备好了,才能把这个 Pod 看做就绪了。 这种信号的一个用途就是控制哪一个 Pod 做为 Service 的后端。 在 Pod 尚未准备好的时候,会从 Service 的负载均衡器中被剔除的。

kubelet 使用启动探测器能够知道应用程序容器何时启动了。 若是配置了这类探测器,就能够控制容器在启动成功后再进行存活性和就绪检查, 确保这些存活、就绪探测器不会影响应用程序的启动。 这能够用于对慢启动容器进行存活性检测,避免它们在启动运行以前就被杀掉。

  • 探针探测类型

  • 探针可配置项

  • 启动探针 startupProbe

  • 就绪探针 readinessProbe

  • 存活探针 livenessProbe

8.3.1 探针探测类型

因为各类探针的写法是同样的,只是名称不一样,做用也不一样

因而这里以存活探针为例,实践如下几种探针的探测方法

  • 命令行
  • http
  • tcp

8.3.1.1 命令行探针

命令返回成功存活,失败kubelet根据restartPolicy处置Pod

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

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

8.3.1.2 http探针

http responed 大于等于200小于400成功 ,不然大于等于400失败,kubelet根据restartPolicy处置 pod

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

1.13版本以前,若是设置了 http_proxy环境变量,则探针会使用代理,1.13版本后探针不使用代理。

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

8.3.1.3 tcp探针

tcp探针的示例使用了就绪探针和存探针

并非就绪探针探测就绪了才会使用存活探针去检测存活,这两个探针是并行的

若是须要设置在容器启动成功后再探测存活,应该使用启动探针 (startupProbe

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

8.3.2 探针可配置项

Probe 有不少配置字段,可使用这些字段精确的控制存活和就绪检测的行为:

  • initialDelaySeconds:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。
  • periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。
  • timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
  • successThreshold:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活探测的这个值必须是 1。最小值是 1。
  • failureThreshold:当探测失败时,Kubernetes 的重试次数。 存活探测状况下的放弃就意味着从新启动容器。 就绪探测状况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。

HTTP Probes 能够在 httpGet 上配置额外的字段:

  • host:链接使用的主机名,默认是 Pod 的 IP。也能够在 HTTP 头中设置 “Host” 来代替。
  • scheme :用于设置链接主机的方式(HTTP 仍是 HTTPS)。默认是 HTTP。
  • path:访问 HTTP 服务的路径。
  • httpHeaders:请求中自定义的 HTTP 头。HTTP 头字段容许重复。
  • port:访问容器的端口号或者端口名。若是数字必须在 1 ~ 65535 之间。

对于 HTTP 探测,kubelet 发送一个 HTTP 请求到指定的路径和端口来执行检测。 除非 httpGet 中的 host 字段设置了,不然 kubelet 默认是给 Pod 的 IP 地址发送探测。 若是 scheme 字段设置为了 HTTPS,kubelet 会跳过证书验证发送 HTTPS 请求。 大多数状况下,不须要设置host 字段。 这里有个须要设置 host 字段的场景,假设容器监听 127.0.0.1,而且 Pod 的 hostNetwork 字段设置为了 true。那么 httpGet 中的 host 字段应该设置为 127.0.0.1。 可能更常见的状况是若是 Pod 依赖虚拟主机,你不该该设置 host 字段,而是应该在 httpHeaders 中设置 Host

对于一次 TCP 探测,kubelet 在节点上(不是在 Pod 里面)创建探测链接, 这意味着你不能在 host 参数上配置服务名称,由于 kubelet 不能解析服务名称。

9. helm

为了第十章,必须把helm的一直基本姿式介绍一下,因此有第9章

  • helm是什么
  • 安装helm
  • chart 是文件结构介绍
  • helm 语法

9.1 helm是什么

helm是kubernetes的包管理器,用于查找,分享,使用kubernetes生态的应用。

9.2 安装helm

  • 前置条件
  • 安装
    • windows
    • linux

9.2.1 前置条件

  • 拥有一个集群
  • 拥有kubectl,并配置用于与kubeapi通信的config链接配置文件

通常helm都是安装在主节点上,开发人员用有权限范围的用户登陆上去操做helm便可,

能够和git结合使用,或安装helm局域网服务器Tiller

也能够在开发人员机器上经过config+kubectl 直接链接 上集群 ,可是这样开发人员须要在本身电脑上安装kubectl。

9.2.2 安装

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

linux复制helm二进制到bin目录:

mv helm /usr/bin

windows设置环境变量:

个人电脑属性->高级->设置环境变量->path+=

9.3 chart文件结构

xxx_chart/
  Chart.yaml          # chart的信息文件
  LICENSE             # 可选: 许可证 如:GPL LGPL
  README.md           # 可选: 
  values.yaml         # 默认的配置值文件
  values.schema.json  # 可选: 对value.yaml提供输入格式校验的josn文件
  charts/             # 依赖的其余图表的存放文件夹
  crds/               # 自定义资源
  templates/          # 使用go模版的yaml文件,会从value.yaml或其余文件读值生成资源yaml
  templates/NOTES.txt # 可选: 安装成功时,显示在终端上的文字

9.4 helm语法

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

听起来很复杂,其实学起来不复杂

仅须要抓着helm chart安装生成的文件抽丝剥茧,就能快速掌握,由于自己并不复杂

  • 上下文

    • .

    • .value value.yaml

    • .release 安装时用户输入

    • .chart chart.yaml

  • 模版

    • 定义
    • 使用
  • 其余语法 去官网文档查

10.把.net core web app部署到集群并附加调试

现有两个.net core web app

  1. api
  2. blazor

前端将经过ingress暴露给集群外部,后端则只在集群内部。以此为例实践第10章

  • 工具推荐
    • visual studio
      • visual studio tools for kubernetes
    • visual studio code
      • C#
      • YAML
      • kubernetes
      • cloud code for visual studio code
  • 将现有.net core web app部署到集群

10.1 工具推荐

10.2 将现有.net core web app部署到集群

10.2.1 为现有项目添加chart和docker file

这个在前端和后端web app中都须要作,由于都要部署到集群。

在visual studio 安装10.1中的扩展后

image-20200906164002575

选择kubernetes/helm

image-20200906164131015

肯定后生成chart文件夹和docker file,还有一个azds.yaml (部署到微软云上才用到),

image-20200906164442774

10.2.2 程序配置

10.2.2.1 后端

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

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

value.yaml中修改服务的targetport

service:
  type: ClusterIP
  port: 80
  targetPort: 5001

修改deployment.yaml里的image 和containerPort

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

.net core由于安全性设计,默认不支持非同源策略的域名对.net web app进行访问

下表给出了与 URL http://store.company.com/dir/page.html 的源进行对比的示例:

URL 结果 缘由
http://store.company.com/dir2/other.html 同源 只有路径不一样
http://store.company.com/dir/inner/another.html 同源 只有路径不一样
https://store.company.com/secure.html 失败 协议不一样
http://store.company.com:81/dir/etc.html 失败 端口不一样 ( http:// 默认端口是80)
http://news.company.com/dir/other.html 失败 主机不一样

定义1个策略名称

public readonly string myAllowSpecificOrigins = "myAllowSpecificOrigins";

配置策略

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

添加到中单件管道

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

10.2.2.2 前端

  • 端口
  • userApi url
    • 调试时根据launch.json传变环境变量
    • 部署时经过deployment的pod 模版传入环境变量
10.2.2.2.1 端口

同10.2.2.1.1,略

10.2.2.2.2 userApi环境变量
10.2.2.2.2.1 调试时

launchSetting.json

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

value.yaml

# 略
userApi: http://hscadaexapi.hbb.svc # http://服务名称.名称空间.svc
# 略

deployment.yaml

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

程序中使用环境变量

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

        }
10.2.2.2.3 ingress 集群入口资源

我但愿访问www.haoyun.blazor的时候,能够从集群外部访问前端应用

访问链路:用户-> www.haoyun.blazor->dns->集群边缘节点外部ip->ingress controller(集群)->blazor server--ipvs-->pod(集群)

values.yaml

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

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

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

10.2.3 理解/修改docker file以支持调试

默认的docker file解读

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base #以..3.1环境为基础建立一个名为base的镜像
WORKDIR /app #设置工做目录 保存为匿名镜像
EXPOSE 80 #导出端口 80  保存为匿名镜像

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build #以..sdk3.1为基础建立一个名为build的镜像
WORKDIR /src #设置工做目录 保存为匿名镜像
COPY ["src/test/test.csproj", "src/test/"] #复制csobj文件 保存为匿名镜像

RUN dotnet restore "src/test/test.csproj" #还原nuget包 保存为匿名镜像
COPY . . #递归复制解决方案到 src目录 保存为匿名镜像
WORKDIR "/src/src/test" #设置工做目录为镜像中的test项目目录 保存为匿名镜像
RUN dotnet build "test.csproj" -c Release -o /app/build #在镜像中编译 保存为匿名镜像

FROM build AS publish #以build 为基础 建立一个名为publish的镜像
RUN dotnet publish "test.csproj" -c Release -o /app/publish #发布到/app/publish目录 保存为匿名镜像

FROM base AS final #base为基础建立一个名为final的镜像
WORKDIR /app #设置工做目录,保存为匿名镜像
COPY --from=publish /app/publish . #复制publish镜像的/app/publish文件夹到final的工做目录 保存为匿名镜像
ENTRYPOINT ["dotnet", "test.dll"] # 设置入口 保存为匿名镜像

修改release->debug ,添加调试工具后

以api 的dockerfile为例, blazor略

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

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

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

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

10.2.4 部署

  1. 构建docker ,把docker镜像传输到工做节点
  2. 复制chart到 主节点
  3. 使用helm 部署到 hbb名称空间

10.2.4.1 构建docker镜像

因为我的计算机资源有限,因此我这里只使用docker savedocker load命令 将docker镜像复制到仅有的一个工做节点上。正确的作法是在集群外部搭建一个docker私有仓库,按需拉取。

已知私有仓库部署方式

  • docker-registry 简易的,没有认证受权功能
  • Harbor
  • nexus

对于 2和3 有认证受权的镜像

正确的姿式是配置好证书后,导入为secret资源,在拉取镜像时指定secret资源便可。

10.2.4.2 复制chart到主节点

一样 chart也有私有服务器能够搭建,名为Tiller,正确的姿式也是应该搭服务器,让集群调度的时候按需获取,

10.2.4.3 helm 经常使用指令

更多信息查看官网文档helm -h

查看

helm ls -n 名称空间

添加repository

helm add repo 名称 url

安装

helm install -n 名称空间 release名称 chart
#其中的chart 能够是本地文件,也能够是tiller上的chart路径

更新

helm upgrade -n 名称空间 release名称  chart

查看变动历史记录

helm history -n 名称空间 release名称

回滚

helm rollback

删除

helm delete -n 名称空间 release名称

10.2.3 调试

  • 经过kubernetes api 的客户端进行附加调试
  • 开发过程当中的快速调试okteto

10.2.3.1 经过kubernetes api客户端

10.2.3.1.1 安装kubectl 配置 集群链接信息
  • 下载kubectl二进制,添加到系统变量path

  • 复制集群管理员生成的 config文件到 c:\用户\.kube 文件夹

10.2.3.1.2 修改docker file
  • release->debug
  • 安装vsdbg
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 5001
EXPOSE 5002
RUN apt-get update   && apt-get install -y --no-install-recommends unzip   && apt-get install -y procps && rm -rf /var/lib/apt/lists/*  && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg

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

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

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

这里用cloud code插件生成调试配置

launchSetting.json

{

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

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

    ]
}

.net core目前只能用于附加调试, go/node.js/py 能够热重载调试,从谷歌插件的文档上看到的。

这种方式的缺点很明显,若是要调试过程修改了代码,因为不能热重载,只能在本地改好代码->重生成docker image->推送到docker repository ->更新image到集群 ->再附加调试

因此比较推荐另外一种方式的调试,虽然也不支持热重载,可是用dotnet watch run,在代码重生成时实时响应到容器中,并重启容器中的程序

10.2.3.2 经过okteto 在开发过程当中快速调试

okteto是一个开源的项目,用于简化各类技术栈 在kubernetes中的开发工程。

传统的kubernetes 服务开发过程就是不断的重复这个过程

while(调试ing)
{
    附加调试->修改代码->生成docker镜像->推送镜像->拉镜像->更新到集群
}

okteto的方式是把开发环境打包成一个镜像,实时同步容器与本地的文件变化,转发本地流量到容器 、转发容器流量到本地 等链接调试用的是ssh ,配合dotnet watch run 能够仅是生成代码就把生成的结果应用到容器中,虽然不是真正的热重载,容器中的程序会重启,但快速了不少,并且这是各类技术栈都能用的,不只限于.net 的万金油,因而流程简化为

do 
{
    dotnet watch run
}while(调试ing)
{
    附加调试->修改代码->生成
}
10.2.3.2.1 安装okteto
  • 下载二进制文件,并加到系统环境变量path
  • 复制集群管理员生成的config ,放到 c:\用户\.kube文件夹
10.2.3.2.2 在解决方案中添加okteto.yaml

更多参考官方文档

name: hscadaexapi #deployment service 的名称
namespace: hbb #名称空间
image: mcr.microsoft.com/dotnet/core/sdk #开发环境变量
environment:
- ASPNETCORE_ENVIRONMENT=Development #环境变量
command:
- bash #启动命令
workdir: /okteto #工做目录
remote: 22000 # ssh调试端口  把本地22000->容器22
sync:  #同步的文件夹   本地:容器
- .:/okteto
forward:  #端口转发    本地:容器
- 5001:5001
persistentVolume: {}
10.2.3.2.3 启动okteto ,
okteto up

这里集群里会多出一个deployment 和service 资源, 跑起一个pod,这个pod是空的,仅仅是一个开发环境,

而后okteto会并 当前文件夹的文件同步到远程容器中的$workdir ,并执行 $command

接下去就是在容器中启动.net core程序

dotnet watch run

查看工做台输出 已经启动了程序的话, 在本地访问 localhost:forward 应该访问到容器上了

10.2.3.2.4 经过ssh链接容器调试

大多数ide都支持ssh远程调试,这里我以visual studioi为例

alt+shift+p 附加调试,选择 ssh ,配置参数

image-20200912202749683

以后就会让你选择要附加的进程了

image-20200912202913252