kubernetes是一个用go语言写的容器编排框架,常与docker搭配使用。
kubernetes是谷歌内部的容器编排框架的开源实现。能够用来方便的管理容器集群。具备不少 优势,要了解这些优势,须要先来了解一下kubernetes中的集群资源。这里指的是kubernetes里的原生的资源,kubernetes也支持自定义的资源。html
不区分名称空间前端
群集默认采用RBAC(role base access control)
进行集群资源的访问控制,能够将角色做用于用户user
或服务service
,限制它们访问群集资源的权限范围。node
其中角色又区分为 集群角色 cluster role
和普通的角色 role
,他们的区别是能够做用的范围不一致。mysql
普通角色必有名称空间限制,只能做用于与它同名称空间的用户或服务。linux
集群角色没有名称空间限制,能够用于全部名称空间的用户或服务。下面的目录中会详细介绍。先在这里提一下nginx
不指定时默认做用default名称空间,服务在跨名称空间访问其余服务时 域名须要加上名称空间后辍才能访问git
是一个包含操做系统的机器,操做系统能够是Linux也能够是windwos,能够是实体机也能够是虚拟机,其中的区别下面的其余目录会详细说明程序员
持久卷 ,支持的类型不少,包括谷歌 亚马逊 阿里云云服务提供商的各类存储.github
因为咱们的项目通常是用于局域网内的,因此这里我着重介绍nfs(network file system)
web
存储类,用于根据pvc 自动建立/自动挂载/自动回收 对应的nfs目录前
工做量的最小单位是pod 其余的类型的工做量都是控制Pod的。
pod至关于docker 中的docker composite,能够由单个或多个容器组成,每一个pod有本身的docker网络,pod里的container处于同个局域网中。
其余的控制器都有一个pod template,用于建立Pod
工做,一但应用到集群将会建立一个pod作一些工做,具体的工做内容由Pod的实现决定,工做完成后Pod自动终结。
定时工做任务,一但应用到集群,集群将会定时建立pod 作一些工做,工做完成后pod自动终结
复制集或称为副本集,一但应用到集群,会建立相n个相同的 pod,而且会维护这个pod的数量,若是有pod异常终结,replica set会建立一个新的Pod 以维护用户指定的数量
deplyoment经常使用来建立无状态的应用集群。
部署,deplyoment依赖于replicaset ,它支持滚动更新,滚动更新的原理是,在原有的一个replica set的基础上建立一个新版本的replica set ,
旧版本的replicaset 逐个减小 ,新版本的replicaset逐个新增, 能够设置一个参数指定滚动更新时要保持的最小可用pod数量。
守护进程集 ,顾名思义,他的做用就是维护某个操做系统(node)的某个进程(pod)始终工做。当一个dameon set被应用到k8s集群,全部它指定的节点上都会建立某个pod
好比日志采集器 一个节点上有一个,用daemon set就十分应景。
stateful set经常使用于建立有状态的服务集群,它具备如下特色
举个例子,你有一个容器须要存数据,好比mysql容器,这时你用deplyoment就不合适,由于多个mysql实例各自应该有本身的存储,DNS名称。
这个时候就应该使用statefull set。它原理是建立无头服务(没有集群ip的服务)和有序号的Pod,并把这个无头服务的域名+有序号的主机名(pod名称),得到惟一的DNS名称
好比设置stateful set的名称为web,redplica=2,则会有序的建立两个Pod:web-0 web-1,当web-0就绪后才会建立web-1,若是是扩容时也是这样的,而收容的时候顺序而是反过来的,会从序号大的Pod开始删除多余的Pod
若是把一个名称为nginx的无头服务指向这个statufulset,则web-0的dns名称应该为 web-0.nginx
而且 stateful set会为这两个Pod建立各自的pvc,因为pod的名称是惟一的,因此故障重建Pod时,能够把新的Pod关联到原有的存储卷上
ConfigMap 容许你将配置文件与镜像文件分离,以使容器化的应用程序具备可移植性。
# 从文件夹建立(文件夹里的文本文件将会被建立成config map kubectl create configmap my-config --from-file=path/to/bar # 从文件建立 kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt # 从字符串建立 kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2 # 从键值文本建立 kubectl create configmap my-config --from-file=path/to/bar # 从env文件建立 kubectl create configmap my-config --from-env-file=path/to/bar.env
使用:
Secret 是一种包含少许敏感信息例如密码、令牌或密钥的对象。 这样的信息可能会被放在 Pod 规约中或者镜像中。 用户能够建立 Secret,同时系统也建立了一些 Secret。
若是由集群管理员管理,由开发人员应向集群管理员申请Pv ,集群管理员要手动的建立Pv,pvc,把pvc给开发人员,开发人员手动挂载pvc到pod
若是由storage class管理,则集群管理员只要建立一个provider, 以后provider会自动监视集群中的Pvc 和pod ,自动建立pv和挂载
kubernetes集群中,至少要有一个主节点,主节点应该有如下组件
普通节点的组件
kubelet
kube-proxy
cni网络插件
kubernetes节点代理组件,每一个node上都须要有,他不是Kubernetes建立的容器,因此在集群中查不到。
他的主要作的工做是
1.向kube api以hostname为名注册节点
2.监视pod运行参数,使Pod以参数预期的状态运行,因此Pod有异常一般都能查询Kubelet的日志来排查错误
kubernetes 的服务相关的组件,每一个Node上都须要有,除了无头服务,其余全部服务都由他处理流量
k8s集群在初始化的时候,会指定服务网段和pod网段,其中服务网段的ip都是虚拟Ip
他主要作的工做是:
监视集群的服务,若是服务知足某些条件,则经过ipvs 把这个服务的流量转发到各个后端Pod (cluster ip)
cni: container network interface
k8s称之为窗口编排集群,他的核心思想是把不一样的容器网络联合起来,使全部的pod都在一个扁平的网络里,能够互换访问
为达这个目的就须要容器网络插件,下面介绍一下主流的cni插件,并大体说明一下优劣
flanel是桥接模式的表明插件,
他的工做原理是用daemonset在每一个节点上部署flannel插件,插件设置容器网络并把容器网络信息经过 kube api存储到etcd中 。
这样就确保不会重复注册网段了,与不一样node上的Pod经过 kube-proxy打包 发给其余Node的kuber proxy,kuber proxy再拆包,发给pod
以达到跨node的扁平网络访问. 这种方式也称vxlan 或overlay
优势:
缺点:
calico 是网关模式的表明插件。 它主要由如下几部分构成
它基于边界网关协议 BGP(border gateway protocol)
他的工做原理是用daemonset在每一个节点上部署calico node, 来构成扁平化容器网络
calico node由如下几个组件
felix 负责编写路由和访问控制列表
confid 用于把 felix生成的数据记录到etcd,用于持久化规则
BIRD 用于广播felix写到系统内核的路由规则和访问控制列表acl和calico的网络
当集群规模比较大的时候还能够可选的安装 BGP Rotue Reflector(BIRD) 和 Typha
前者用于快速广播协议,后者用于直接与ETCD通信,减少 kubeapi的压力
优势:
缺点:
etc distributed ,一款使用go语言编写的基于raft共识算法的分布式KV缓存框架 ,
不像redis重性能,而像zookeeper 同样重数据一致性
特色是有较高的写入性能
k8s 暴露给外部的web api,用于集群的交互 有各类语言的api client开源项目 ,程序员也能够在程序中引用,监视一些集群资源
用于集群中的service 和 pod的域名解析,
也能够配置对集群外的域名应该用哪一个DNS解析
用于 各类控制器(消耗cpu ram)的管理
用于 管理控制 Pod调度相关
raft是etcd的共识算法,kubernetes用etcd来存储集群的配置。config map /secret都是基于etcd。
理解raft共识算法能够知道
在高可用环境, keepalived用于虚拟ip的选举,一旦持有虚拟Ip的节点发生故障,其余的主节点会选择出新的主节点持有虚拟ip。而且能够配置smtp信息,当节点故障的时候发邮件通知相关的责任人
onfiguration File for keepalived global_defs { notification_email { kok.bing@qq.com } notification_email_from Alexandre.Cassen@firewall.loc smtp_server 127.0.0.1 smtp_connect_timeout 30 router_id LVS_1 } vrrp_instance VI_1 { state MASTER interface eth0 lvs_sync_daemon_inteface eth0 virtual_router_id 79 advert_int 1 priority 100 #权重 m1 100 m2 90 m3 80 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.44.200/24 dev eth0 } }
每一个主节点都部署了haproxy代理kube api端口, 因此当它持有虚拟ip的时候,会把全部对kube api的请求负载均衡到全部的主节点上。
global chroot /var/lib/haproxy daemon group haproxy user haproxy log 127.0.0.1:514 local0 warning pidfile /var/lib/haproxy.pid maxconn 20000 spread-checks 3 nbproc 8 defaults log global mode tcp retries 3 option redispatch listen https-apiserver bind *:8443 mode tcp balance roundrobin timeout server 900s timeout connect 15s server m1 192.168.44.201:6443 check port 6443 inter 5000 fall 5 server m2 192.168.44.202:6443 check port 6443 inter 5000 fall 5 server m3 192.168.44.203:6443 check port 6443 inter 5000 fall 5
kubernetes集群中的认证对象分为
除此以外,还有一些其余的非kubernetes集群管理的服务会须要访问集群资源的状况
可是这个暂时不实践,由于haoyun目前不会使用到这种状况
用户不是kuebrnetes 的资源,因此单独拎出来说。
master node在加入集群时,会提示咱们手动复制默认的管理员用户到 $HOME/.kube
文件夹
因此查看 $HOME/.kube/config文件能够知道集群 用户 认证上下文
[root@www .kube]# cat config apiVersion: v1 clusters: - cluster: certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZ...#略 server: https://www.haoyun.vip:8443 name: kubernetes contexts: - context: cluster: kubernetes user: kubernetes-admin name: kubernetes-admin@kubernetes current-context: kubernetes-admin@kubernetes kind: Config preferences: {} users: - name: kubernetes-admin user: client-certificate-data: LS0tLS1CRUdJTiBDR...#略 client-key-data: LS0tLS1CRUdJTiBS..#.略
建立1用户,并进入用户文件夹
[root@www .kube]# useradd hbb && cd /home/hbb
建立私钥
[root@www hbb]# openssl genrsa -out hbb_privateKey.key 2048 Generating RSA private key, 2048 bit long modulus ............................................................+++ .............................................................................................................................................................+++ e is 65537 (0x10001)
建立x.509证书签名请求 (CSR) ,CN会被识别为用户名 O会被识别为组
openssl req -new -key hbb_privateKey.key \ -out hbb.csr \ -subj "/CN=hbb/O=hbbGroup1/O=hbbGroup2" #O能够省略也能够写多个
为CSR签入kubernetes 的证书和证书公钥,生成证书
openssl x509 -req -in hbb.csr \ -CA /etc/kubernetes/pki/ca.crt \ -CAkey /etc/kubernetes/pki/ca.key \ -CAcreateserial \ -out hbb.crt -days 50000 #证书有效天数 50000天
建立证书目录 ,存入把公钥(hbb.crt)和私钥(hbb_private.key) 放进去
[root@www hbb]# mkdir .certs [root@www hbb]# cd .certs/ [root@www .certs]# mv ../hbb_privateKey.key ../hbb.crt . [root@www .certs]# ll total 8 -rw-r--r--. 1 root root 940 Sep 5 15:41 hbb.csr -rw-r--r--. 1 root root 1679 Sep 5 15:29 hbb_privateKey.key
建立集群用户
kubectl config set-credentials hbb \ --client-certificate=/home/hbb/.certs/hbb.crt \ --client-key=/home/hbb/.certs/jean_privatekey.key
建立用户上下文
kubectl config set-context hbb-context \ --cluster=kubernetes --user=hbb
这时原有的config文件里就多了Hbb这个用户了
users: - name: hbb user: client-certificate: /home/hbb/.certs/hbb.crt client-key: /home/hbb/.certs/hbb_privatekey.key
复制 原有的.kube/config文件到hbb用户文件夹 ,在副本上删除kubernetes-admin的上下文和用户信息。
[root@www .kube]# mkdir /home/hbb/.kube [root@www .kube]# cp config /home/hbb/.kube/ [root@www .kube]# cd /home/hbb/.kube [root@www .kube]# vim config [root@www .kube]# cat config apiVersion: v1 clusters: - cluster: certificate-authority-data: LS0tLS1CRUdJTiBDRVJU...#略 server: https://www.haoyun.vip:8443 name: kubernetes contexts: - context: cluster: kubernetes user: hbb name: hbb-context current-context: hbb-context kind: Config preferences: {} users: - name: hbb user: client-certificate: /home/hbb/.certs/hbb.crt client-key: /home/hbb/.certs/hbb_privateKey.key
以后把hbb用户文件夹受权给hbb用户
[root@www .kube]# chown -R hbb: /home/hbb/ #-R 递归文件夹 #hbb: 只设置了用户没有设置组
这样就建立一个用户了,此时退出root用户,使用hbb用户登上去,默认就是使用Hbb的user去访问集群
可是这时尚未给hbb受权,因此基本上什么操做都执行不了,由于没有权限
[hbb@www .certs]$ kubectl get pod -A Error from server (Forbidden): pods is forbidden: User "hbb" cannot list resource "pods" in API group "" at the cluster scope
相关的kubernetes 资源
roles和rolebindings 若是要创建关联,他们必须是同一个名称空间内。
clusterRoles和clusterRolebindings 没有名称空间的限制,它们的规则做用于集群范围
在 RBAC API 中,一个角色包含一组相关权限的规则。权限是纯粹累加的(不存在拒绝某操做的规则)。 角色能够用 Role
来定义到某个命名空间上, 或者用 ClusterRole
来定义到整个集群做用域。
一个 Role
只能够用来对某一命名空间中的资源赋予访问权限。 下面的 Role
示例定义到名称为 "default" 的命名空间,能够用来授予对该命名空间中的 Pods 的读取权限:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: default #若是是clusterRoles 则没删除这一行 name: pod-reader rules: - apiGroups: [""] # "" 指定 API 组 resources: ["pods","pods/log"] #子资源 verbs: ["get", "watch", "list"] - apiGroups: [""] resources: ["configmaps"] resourceNames: ["my-configmap"] #具体名称的资源 verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] #下面这个只有clusterRoles 才可使用 - nonResourceURLs: ["/healthz", "/healthz/*"] # '*' 在 nonResourceURL 中的意思是后缀全局匹配。 verbs: ["get", "post"]
clusterRoles比 roles 多出如下的能力
rolebindings也可使用ClusterRoles,会将里面的资源的做用域限定到rolebindings的名称空间范围内。
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods namespace: default subjects: - kind: User #用户 name: jane apiGroup: rbac.authorization.k8s.io - kind: Group #用户组 name: "frontend-admins" apiGroup: rbac.authorization.k8s.io - kind: ServiceAccount #服务账户 name: default namespace: kube-system - kind: Group # 全部myNamespace名称空间下的服务 name: system:serviceaccounts:myNamespace apiGroup: rbac.authorization.k8s.io - kind: Group #全部名称空间的全部服务 name: system:serviceaccounts apiGroup: rbac.authorization.k8s.io - kind: Group #全部认证过的用户 name: system:authenticated apiGroup: rbac.authorization.k8s.io - kind: Group #全部未谁的用户 name: system:authenticated apiGroup: rbac.authorization.k8s.io roleRef: kind: Role #能够是集群名角或普通的角色 name: pod-reader apiGroup: rbac.authorization.k8s.io
官方文档
服务是微服务抽象,经常使用于经过选择符访问一组Pod, 也能够访问其余对象,
上面这两种状况,服务的选择符能够省略。
kubernetes v1.0时使用用户空间代理 (userspace)
v1.1添加了iptable代理模式,
v1.2默认使用iptables代理模式
v1.11添加ipvs 模式
当Node上不支持ipvs会回退使用iptables模式
每一个结点上部署kube-proxy ,它会监视主结点的apiserver对service 和 endpoints的增删改
为每一个server随机开一个端口,并写入集群Ip写入iptables,把对集群服务的集群Ip的流量 转发到这个随机的端口上 ,
而后再转发到后端的Pod上, 通常是采用轮询的规则,根据服务上的sessionAnfinity来设置链接的亲和性
与userspace的区别是 不只把service写入Iptables,同时把endpoints也写入了iptables,
因此不用在内核空间和用户空间之间来回切换,性能提高
ipvs(ip virtrual server)和iptables都是基于netfilter ,但ipvs以哈希表作为基础数据结构,并工做在内核空间
相比iptables,因此他有更好的性能,也支持更多的负载均衡算法
若是须要粘性会话,能够在服务中设置
service.spec.sessionAffinity 为 clusterip ,默认是none
service.spec.sessionAffinityConfig.clientIP.timeoutSeconds 能够调整会话最长时间,默认是10800秒
服务能够经过环境变量和DNS的方式来发现服务,推荐的作法是经过DNS
一个名称为 "redis-master" 的 Service 暴露了 TCP 端口 6379, 同时给它分配了 Cluster IP 地址 10.0.0.11 ,
这个 Service 生成了以下环境变量:
REDIS_MASTER_SERVICE_HOST=10.0.0.11 REDIS_MASTER_SERVICE_PORT=6379 REDIS_MASTER_PORT=tcp://10.0.0.11:6379 REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379 REDIS_MASTER_PORT_6379_TCP_PROTO=tcp REDIS_MASTER_PORT_6379_TCP_PORT=6379 REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
若是须要在pod中使用这些环境变量,须要在启动pod以前先启动服务。
服务直接用服务名称为域名,
Pod会在服务以前加上ip为域名
例如在名称空间 hbb下有服务 hbb-api, 服务指向的后端pod Ip地址是10-244-6-27.,则会有dns记录
# 服务 hscadaexapi.hbb.svc.cluster.local
# pod 10-244-6-27.hscadaexapi.hbb.svc.cluster.local
dns应该尽量使用,最好不要使用环境变量的方式
虚拟的ip ,一般指向一组pod (真实ip)
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - name: http protocol: TCP port: 80 targetPort: 9376 - name: https protocol: TCP port: 443 targetPort: 9377
每一个主结点上的具体端口,一般把 node ip+端口 转发到 一组pod(真实ip)
apiVersion: v1 kind: Service metadata: name: my-service spec: type: NodePort selector: app: MyApp ports: # 默认状况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。 - port: 80 targetPort: 80 # 可选字段 # 默认状况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号(默认:30000-32767) nodePort: 30007
一般把 外部流量 转发到 一组pod(真实ip) ,外部ip通常是在双网卡的边缘节点上
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376 clusterIP: 10.0.171.239 loadBalancerIP: 78.11.24.19 type: LoadBalancer status: loadBalancer: ingress: - ip: 146.148.47.155
将外部的流量引入服务 ,这种外部Ip 不禁集群管理,由由集群管理员维护
kind: Service apiVersion: v1 metadata: name: my-service spec: selector: app: MyApp ports: - name: http protocol: TCP port: 80 targetPort: 9376 externalIPs: - 80.11.12.10
kube-proxy组件不对无头服务进行代理,无头服务 加上序号 指向 Pod,固定搭配,
因此即便 服务的pod挂了, 重启来的服务的 域名也不会换一个,用于有状态的服务。
后面讲到Pod控制器statefulset会再细讲
kube-proxy组件不会对外部服务进行代理则是映射到dns 用于描述一个集群外部的服务,有解耦合的做用,
因此它和无头服务同样没有选择器,他也不禁集群管理,而是由集群管理员维护
apiVersion: v1 kind: Service metadata: name: my-service namespace: prod spec: type: ExternalName externalName: my.database.example.com
因为iptables代理模块或亦 ipvs代理模式都是4层负载均衡,没法对7层协议进行负载均衡,因此对于外部的流量 ,常使用入口资源来进行负载均衡,把外部的流量均衡到服务上
ingress controller 是负载均衡器实例,一个集群中能够部署多个, 每一个又能够自为一个负载均衡集群
在建立ingress资源的时候,能够用 注解Annotations:来指定要使用哪一个ingress controller
kubernetes.io/ingress.class: nginx
这个nginx是controller容器启动时 用命令行的方式指定的
Args: /nginx-ingress-controller --default-backend-service=kube-system/my-nginx-ingress-default-backend --election-id=ingress-controller-leader --ingress-class=nginx
Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。
能够将 Ingress 配置为服务提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS,以及提供基于名称的虚拟主机等能力。 Ingress 控制器 一般负责经过负载均衡器来实现 Ingress,尽管它也能够配置边缘路由器或其余前端来帮助处理流量。
Ingress 不会公开任意端口或协议。 将 HTTP 和 HTTPS 之外的服务公开到 Internet 时,一般使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 类型的服务
`apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: test-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - http: paths: - path: /testpath pathType: Prefix backend: serviceName: test servicePort: 80
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: name-virtual-host-ingress spec: rules: - host: foo.bar.com http: paths: - backend: serviceName: service1 servicePort: 80 - host: bar.foo.com http: paths: - backend: serviceName: service2 servicePort: 80
ConfigMap 容许你将配置文件与镜像文件分离,以使容器化的应用程序具备可移植性。
# 从文件夹建立(文件夹里的文本文件将会被建立成config map kubectl create configmap my-config --from-file=path/to/bar # 从文件建立 kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt # 从字符串建立 kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2 # 从键值文本建立 kubectl create configmap my-config --from-file=path/to/bar # 从env文件建立 kubectl create configmap my-config --from-env-file=path/to/bar.env
建立1个config map 配置文件,在default名称空间里
kubectl create configmap hbb-config --from-literal=key1=aaa --from-literal=key2=bbb
建立一个pod ,使用busybox镜像,并把上面的cm 加载到环境变量,在pod 的container里面加上
containers: - name: busybox image: busybox:1.28 command: - sleep - "3600" imagePullPolicy: IfNotPresent env: - name: HBBKEY1 valueFrom: configMapKeyRef: name: hbb-config key: key1 - name: HBBKEY2 valueFrom: configMapKeyRef: name: hbb-config key: key2
简易写法,加载全部hbb-config里的key value
containers: - name: busybox image: busybox:1.28 command: - sleep - "3600" imagePullPolicy: IfNotPresent envFrom: - configMapRef: name: hbb-config
经测试,若是修改了config map ,Pod的环境变量是不会自动更新的,除非删除pod从新建立
containers: - name: busybox image: busybox:1.28 command: - sleep - "3600" imagePullPolicy: IfNotPresent volumeMounts: - name: hbb-cm-volume mountPath: /etc/config volumes: - name: hbb-cm-volume configMap: name: hbb-config
经验证,修改config map后,去查看挂载上去的卷,文件中的值也随之发生了改变,因此这种方式是比较好的方式。
Secret 是一种包含少许敏感信息例如密码、令牌或密钥的对象。 这样的信息可能会被放在 Pod 规约中或者镜像中。 用户能够建立 Secret,同时系统也建立了一些 Secret。
#生成文件 echo -n 'admin' > ./username.txt echo -n '1f2d1e2e67df' > ./password.txt #从文件生成 kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
默认的键名就是文件名,若是要另外指定能够--from-file=[key=]source,密码也是如此
#从文件生成 kubectl create secret generic db-user-pass --from-file=hbb-key=./username.txt --from-file=hbb-pas=./password.txt
说明:特殊字符(例如 $
、*
、*
、=
和 !
)可能会被 sell转义,因此要用''括起来
kubectl create secret generic dev-db-secret \ --from-literal=username=devuser \ --from-literal=password='S!B\*d$zDsb='
加密用户名admin和密码password
[root@www ~]# echo -n 'admin' | base64 ; echo -n 'password' |base64 YWRtaW4= cGFzc3dvcmQ=
建立一个mysecret.yaml
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm
用kubectl 建立
kubectl apply -f ./mysecret.yaml
建立1个 mysecret.yaml
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque stringData: config.yaml: |- apiUrl: "https://my.api.com/api/v1" username: hbb password: hbb-password
执行
kubectl apply -f ./mysecret.yaml
将会建立 一个mysecret 资源,里面有一个config.yaml的key,它的value是一个加密的字符串。
注: mysecret.yaml第7行的|- 的意思是:将下面三行字符串组合起来,替换右边的缩进(空格和换行)成一个换行符。
这种方式的好处是,能够和helm一块儿使用,helm使用go 的模版,能够配置明文的字符
[root@www ~]# kubectl describe secret/dev-db-secret Name: dev-db-secret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password: 12 bytes username: 7 bytes
使用get 和describe 查看secret都不能直接看到data里的数据。
若是要查看secret加密前的值应该用 get -o yaml 查看到base64字符串后再解码
[root@www ~]# kubectl get secret/dev-db-secret -o yaml apiVersion: v1 data: password: UyFCXCpkJHpEc2I9 username: ZGV2dXNlcg== kind: Secret ...
[root@www ~]# echo UyFCXCpkJHpEc2I9|base64 --decode S!B\*d$zDsb= [root@www ~]# echo ZGV2dXNlcg==|base64 --decode devuser
和config map一毛同样,略
集群管理员建立storage class
storage class监视集群中的 pvc和pod
PersistentVolume(PV)是已经由管理员提供或者动态使用供应的集群中的一块存储的存储类。它是集群中的资源,就像节点是集群资源同样。PV是相似于Volumes的卷插件,可是其生命周期与使用PV的任何单个Pod无关。
若是用户删除了Pod正在使用的PVC,则不会当即删除该PVC。PVC的清除被推迟,直到任何Pod再也不主动使用PVC。另外,若是管理员删除绑定到PVC的PV,则不会当即删除该PV。PV的去除被推迟,直到PV再也不与PVC结合。
当用户完成其卷处理后,他们能够从容许回收资源的API中删除PVC对象。PersistentVolume的回收策略告诉集群在释放其声明以后如何处理该卷。当前,能够保留,回收或删除卷。
这种策略在pvc删除后,会保留pv,并释放pv,但这个pv不能被其余pvc重用。若是要回收须要集群管理员手动的去回收,回收步骤以下
默认的策略是删除,删除Pvc ,若是存储卷类型支持的话,将会同时删除pv及其中的文件数据
若是pv的介质支持的话,此回收策略将会使用
rm -rf /volume/*
清理pv的数据,而后使这个pv能够被其余pvc使用,因为这样常常会致使意外终结的Pod,pv里的数据来不及排查就被回收,因此这种方式已被 弃用。应该使用动态配置+手动回收来避免这种状况发生。
因为咱们集群环境是私有的局域网,因此一般只会使用nfs来做为存储的介质,其余的类型还有不少,这里不会介绍更多,须要有须要了解能够到k8s中文社区自行查阅资料,下面是一个由nfs provisioner建立的pv,能够从存储源看出对应的nfs服务的相关信息
Source: Type: NFS (an NFS mount that lasts the lifetime of a pod) Server: www.haoyun.nfs1 Path: /k8s/default-hbb-pvc-pvc-02d94a26-d5df-4e70-9e3f-a12630f2bd41 ReadOnly: false
pv一旦设置了节点亲和力,则与Pv结合的Pod都会部署到命中的节点上
PersistentVolumeClaim(PVC)是由用户进行存储的请求。它相似于pod。pod消耗节点资源,PVC消耗PV资源。Pod能够请求特定级别的资源(CPU和内存)。声明能够请求特定的大小和访问模式
静态供应状况下,pvc绑定Pv
动态供应状况下,pvc绑定 storage class
虽然PersistentVolumeClaims容许用户使用抽象存储资源,但对于不一样的pod,用户一般须要具备不一样存储介质,例如机械硬盘和固态硬盘,机房1和机房2,集群管理员须要可以提供各类PersistentVolume,这些PersistentVolume不只在大小和访问模式上有更多差别,并且还不让用户了解如何实现这些卷的细节。
这个是推荐使用的方式,因此这里重点实践
略,
验证
1. 安装nfs-client # yum install -y nfs-utils 2. 建立挂载目录 # mkdir /var/nfs 3. 查看NFS Server目录 # showmount -e www.haoyun.nfs1 4. 挂载NFS Server目录 # mount -t nfs www.haoyun.nfs1:/k8s ~/nfs-test 5. 测试完卸载 # umount -t nfs ~/nfs-test/
略
用工具安装部署完nfs provisioner时已经生成,略
建立文件 pvc.yaml
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: hbb-pvc annotations: volume.beta.kubernetes.io/storage-class: "nfs-client" spec: accessModes: - ReadWriteMany resources: requests: storage: 5Mi
添加pvc到集群中的nfs名称空间,storage class是集群资源,任何名称空间均可以使用
kubectl apply -f pvc.yaml -n nfs
建立podWithPvc.yaml
kind: Pod apiVersion: v1 metadata: name: podwithpvc spec: containers: - name: podwithpvc image: busybox:1.28 command: - "/bin/sh" args: - "-c" - "touch /mnt/SUCCESS && exit 0 || exit 1" #建立一个SUCCESS文件后退出 volumeMounts: - name: nfs-pvc mountPath: "/mnt" restartPolicy: "Never" volumes: - name: nfs-pvc persistentVolumeClaim: claimName: hbb-pvc #与PVC名称保持一致
添加pod到nfs名称空间
kubectl apply -f podWithPvc.yaml
这个Pod会挂载storage class生成的pv到/mnt目录,并建立一个名为seccess的文件
到nfs服务器上查看/k8s目录 能够看到多了一个名为storageClass名称+pod名称
的目录
[root@www k8s]# ll total 0 drwxrwxrwx 2 root root 21 Aug 29 09:20 nfs-hbb-pvc-pvc-f1a0750e-2214-43ac-a148-cff3db88d4f4
删除pod 再删除pvc 再去查看
[root@www k8s]# ll total 0 drwxrwxrwx 2 root root 21 Aug 29 09:20 archived-nfs-hbb-pvc-pvc-f1a0750e-2214-43ac-a148-cff3db88d4f4
目录的名称变为archived + storageClass名称 + pod名称
,这是由于storage class 声明时设置成删除且归档了
[root@www ~]# kubectl describe sc -n nfs nfs-client Name: nfs-client IsDefaultClass: No Annotations: meta.helm.sh/release-name=hbb-nfs,meta.helm.sh/release-namespace=nfs Provisioner: cluster.local/hbb-nfs-nfs-client-provisioner Parameters: archiveOnDelete=true AllowVolumeExpansion: True MountOptions: <none> ReclaimPolicy: Delete VolumeBindingMode: Immediate Events: <none>
这种方式比较直接删除更能保障数据安全,可是须要集群管理员定时删除没必要要的归档,(例如超过1个月的归档没有开发人员认领,就删除掉)
补充两种卷,
当 Pod 指定到某个节点上时,首先建立的是一个 emptyDir
卷,而且只要 Pod 在该节点上运行,卷就一直存在。 就像它的名称表示的那样,卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir
卷的路径可能相同也可能不一样,可是这些容器均可以读写 emptyDir
卷中相同的文件。 当 Pod 由于某些缘由被从节点上删除时,emptyDir
卷中的数据也会永久删除。
说明: 容器崩溃并不会致使 Pod 被从节点上移除,所以容器崩溃时
emptyDir
卷中的数据是安全的。
emptyDir
的一些用途:
默认状况下, emptyDir
卷存储在支持该节点所使用的介质上;这里的介质能够是磁盘或 SSD 或网络存储,这取决于您的环境。 可是,您能够将 emptyDir.medium
字段设置为 "Memory"
,以告诉 Kubernetes 为您安装 tmpfs(基于 RAM 的文件系统)。 虽然 tmpfs 速度很是快,可是要注意它与磁盘不同。 tmpfs 在节点重启时会被清除,而且您所写入的全部文件都会计入容器的内存消耗,受容器内存限制约束。
示例
apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: k8s.gcr.io/test-webserver name: test-container volumeMounts: - mountPath: /cache name: cache-volume volumes: - name: cache-volume emptyDir: {}
不经常使用的方式, Pod挂载node上的文件目录到pod中,略
[root@www ~]# kubectl get no www.haoyun.edge1 --show-labels NAME STATUS ROLES AGE VERSION LABELS www.haoyun.edge1 Ready <none> 6d21h v1.18.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,isEdgeNode=,kubernetes.io/arch=amd64,kubernetes.io/hostname=www.haoyun.edge1,kubernetes.io/os=linux
挑选一个内置的hostname标签来验证
kubernetes.io/hostname=www.haoyun.edge1
建立一个busybox-nodeselector.yaml,在里面加上一个节点选择器,
使用了内置的label kubernetes.io/hostname
,限定Pod只能部署到edge1上
[root@www ~]# vim busybox-nodeselector.yaml apiVersion: v1 kind: Pod metadata: name: busybox-nodeselector-test namespace: default spec: nodeSelector: kubernetes.io/hostname: www.haoyun.edge1 containers: - name: busybox image: busybox:1.28 command: - sleep - "3600" imagePullPolicy: IfNotPresent restartPolicy: Always
应用它到集群
kubectl apply -f busybox-nodeselector.yaml
查看运行状态,确实跑到edge1上了
[root@www ~]# kubectl get pod -A -o wide|grep nodeselec default busybox-nodeselector-test 1/1 Running 0 22s 10.244.6.61 www.haoyun.edge1 <none> <none>
查看生成的pod详情
spec: containers: - command: - sleep - "3600" image: busybox:1.28 imagePullPolicy: IfNotPresent name: busybox resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: default-token-74k2x readOnly: true dnsPolicy: ClusterFirst enableServiceLinks: true nodeName: www.haoyun.edge1 nodeSelector: kubernetes.io/hostname: www.haoyun.edge1
验证经过
污点是打在node上的标记,容忍是打在Pod上的标记
查看主节点www.haoyu.m1的污点
kubectl describe no www.haoyun.m1
查看spec.taints
spec: podCIDR: 10.244.0.0/24 podCIDRs: - 10.244.0.0/24 taints: - effect: NoSchedule key: node-role.kubernetes.io/master
已打一个名为node-role.kubernetes.io/master 的Noschedule的污点
先用get pod -o wide查看到一个部署在www.haoyun.m1结点上的Pod
而后用describe 或edit查看到它的spec.tolerations
tolerations: - key: CriticalAddonsOnly operator: Exists - effect: NoSchedule key: node-role.kubernetes.io/master - effect: NoExecute key: node.kubernetes.io/not-ready operator: Exists tolerationSeconds: 300 - effect: NoExecute key: node.kubernetes.io/unreachable operator: Exists tolerationSeconds: 300
其中的
- effect: NoSchedule key: node-role.kubernetes.io/master
表示能够容忍节点上存在这个污点
略
亲和力/反亲和力 又分为
node亲和力用于限定Pod部署到Node上的命中规则,
Pod亲和力则用于限定 Pod与pod 布置到同一个Node上
他们都有多种匹配规则和 “软” “硬”两次匹配策略
交叉相实际上是四种不一样的设置,但因为他们大同小异,故在此只以节点的亲和力来作说明。
节点亲和力 /反亲和力使用步骤以下
例如给名为www.haoyun.edge1的结点打上 isedgenode的标签
kubectl label no www.haoyun.edge1 isEdgeNode=true
查看节点已打的标签
[root@www ~]# kubectl get no www.haoyun.edge1 --show-labels NAME STATUS ROLES AGE VERSION LABELS www.haoyun.edge1 Ready <none> 6d20h v1.18.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,isEdgeNode=,kubernetes.io/arch=amd64,kubernetes.io/hostname=www.haoyun.edge1,kubernetes.io/os=linux
查看 nginx-ingress-controller的deployment
kubectl edit deployment -n kube-system
找到pod的template
template: metadata: creationTimestamp: null labels: app: nginx-ingress app.kubernetes.io/component: controller component: controller release: my-nginx-ingress spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: isEdgeNode operator: Exists
能够看到spec.affinity.nodeAffinity
里有一条规则,必需
节点选择器命中isedgenode这个标签存在, 也就是说打标签的时候无论是设置什么值,只要有isedgenode就能够部署上去。
requiredDuringSchedulingIgnoredDuringExecution
必需preferredDuringSchedulingIgnoredDuringExecution
尽量相比起节点选择器,他有更灵活的匹配替换,并且能够有“软”,“硬”两种策略
写法相对也复杂得多。
因为已经部署过了,因此直接查看,能够看到nginx-ingress-controller的Pod是在www.haoyun.edge1的结点上的。
[root@www ~]# kubectl get pod -A -o wide|grep ingress kube-system my-nginx-ingress-controller-77d976f664-dj5vg 1/1 Running 3 6d20h 10.244.6.56 www.haoyun.edge1 <none> <none>
pod不会自动消息,除非自愿干扰或 非自愿干扰
PDB(pod disruption budget)
非自愿干扰
自愿干扰
pdb用于确保pod驱逐的过程当中服务的可用Pod数量在安全的范围内。
官方文档对不一样的部署方案的例子
无状态前端:
单实例有状态应用程序:
多实例有状态应用程序,例如Consul,ZooKeeper或etcd:
可从新启动的批处理做业:
关注:在自愿中断的状况下,工做须要完成。
pdb经过设置 maxunAvailable (最大不可用pod个数)或 minAvailable(最小可用pod个数) 来保证Pod驱逐时服务的可用性,例:
例:
apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: zk-pdb spec: maxUnavailable: 1 selector: matchLabels: app: zookeeper
apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: zk-pdb spec: minAvailable: 2 selector: matchLabels: app: zookeeper
这里只着重介绍资源不足致使的Pod被驱逐,由于其余状况都与配置无关
eviction api是一组kubelet的api,用于指定当Node的资源不足时,(通常是指硬盘空间不足或内存不足的状况 ,)要如何驱逐Pod以保证node的资源始终在合理的范围内,能够在kubelet 的config map里配置,或者命令行启动参数的方式设置eviction api, 可参阅文档
一个例子:
10Gi
kubelet
等)。为了促成这个场景,kubelet
将像下面这样启动:
--eviction-hard=memory.available<500Mi --system-reserved=memory=1.5Gi
这个配置的暗示是理解系统保留应该包含被驱逐阈值覆盖的内存数量。
要达到这个容量,要么某些 pod 使用了超过它们请求的资源,要么系统使用的内存超过 1.5Gi - 500Mi = 1Gi
。
这个配置将保证在 pod 使用量都不超过它们配置的请求值时,若是可能当即引发内存压力并触发驱逐时,调度器不会将 pod 放到这个节点上。
本节只会介绍排空,由于2 通常用于云供应商平台,3太简单略。
有时候某个Node须要中止运行维护,好比加内存之类的操做,这时若是只是删除Node上的pod, 则很大几率新的pod会从新被调度到这个node上,这种状况下集群管理员应该排空这个node,在维护结束后再结束以后再使用uncordon,使Pod能被调试到这个维护结束的node上
例如对 www.haoyun.edge1结点作排空操做
kubectl drain www.haoyun.edge1
维护结束以后恢复
kubectl uncordon www.haoyun.edge1
略,须要配合promethues 之类的数据采集才可使用,可能以后专门讲promethues再讨论,由于这个功能对浩云来讲不是很重要
写在docker file里
使用helm 写pod 参数里传递
使用config map
略
参见 6.配置和存储
略
kubelet 使用存活探测器来知道何时要重启容器。 例如,存活探测器能够捕捉到死锁(应用程序在运行,可是没法继续执行后面的步骤)。 这样的状况下重启容器有助于让应用程序在有问题的状况下更可用。
kubelet 使用就绪探测器能够知道容器何时准备好了并能够开始接受请求流量, 当一个 Pod 内的全部容器都准备好了,才能把这个 Pod 看做就绪了。 这种信号的一个用途就是控制哪一个 Pod 做为 Service 的后端。 在 Pod 尚未准备好的时候,会从 Service 的负载均衡器中被剔除的。
kubelet 使用启动探测器能够知道应用程序容器何时启动了。 若是配置了这类探测器,就能够控制容器在启动成功后再进行存活性和就绪检查, 确保这些存活、就绪探测器不会影响应用程序的启动。 这能够用于对慢启动容器进行存活性检测,避免它们在启动运行以前就被杀掉。
探针探测类型
探针可配置项
启动探针 startupProbe
就绪探针 readinessProbe
存活探针 livenessProbe
因为各类探针的写法是同样的,只是名称不一样,做用也不一样
因而这里以存活探针为例,实践如下几种探针的探测方法
命令返回成功存活,失败kubelet根据restartPolicy处置Pod
Pod 的
spec
中包含一个restartPolicy
字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。
apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-exec spec: containers: - name: liveness image: k8s.gcr.io/busybox args: - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600 livenessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 5 #首次延迟5秒检测 periodSeconds: 5 #每5秒检测一次
http responed 大于等于200小于400成功 ,不然大于等于400失败,kubelet根据restartPolicy处置 pod
Pod 的
spec
中包含一个restartPolicy
字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。
1.13版本以前,若是设置了 http_proxy环境变量,则探针会使用代理,1.13版本后探针不使用代理。
apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-http spec: containers: - name: liveness image: k8s.gcr.io/liveness args: - /server livenessProbe: httpGet: path: /healthz port: 8080 httpHeaders: - name: Custom-Header value: Awesome initialDelaySeconds: 3 periodSeconds: 3
tcp探针的示例使用了就绪探针和存探针
并非就绪探针探测就绪了才会使用存活探针去检测存活,这两个探针是并行的
若是须要设置在容器启动成功后再探测存活,应该使用启动探针 (startupProbe
apiVersion: v1 kind: Pod metadata: name: goproxy labels: app: goproxy spec: containers: - name: goproxy image: k8s.gcr.io/goproxy:0.1 ports: - containerPort: 8080 readinessProbe: tcpSocket: port: 8080 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: 8080 initialDelaySeconds: 15 periodSeconds: 20
Probe 有不少配置字段,可使用这些字段精确的控制存活和就绪检测的行为:
initialDelaySeconds
:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。periodSeconds
:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。timeoutSeconds
:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。successThreshold
:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活探测的这个值必须是 1。最小值是 1。failureThreshold
:当探测失败时,Kubernetes 的重试次数。 存活探测状况下的放弃就意味着从新启动容器。 就绪探测状况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。HTTP Probes 能够在 httpGet
上配置额外的字段:
host
:链接使用的主机名,默认是 Pod 的 IP。也能够在 HTTP 头中设置 “Host” 来代替。scheme
:用于设置链接主机的方式(HTTP 仍是 HTTPS)。默认是 HTTP。path
:访问 HTTP 服务的路径。httpHeaders
:请求中自定义的 HTTP 头。HTTP 头字段容许重复。port
:访问容器的端口号或者端口名。若是数字必须在 1 ~ 65535 之间。对于 HTTP 探测,kubelet 发送一个 HTTP 请求到指定的路径和端口来执行检测。 除非 httpGet
中的 host
字段设置了,不然 kubelet 默认是给 Pod 的 IP 地址发送探测。 若是 scheme
字段设置为了 HTTPS
,kubelet 会跳过证书验证发送 HTTPS 请求。 大多数状况下,不须要设置host
字段。 这里有个须要设置 host
字段的场景,假设容器监听 127.0.0.1,而且 Pod 的 hostNetwork
字段设置为了 true
。那么 httpGet
中的 host
字段应该设置为 127.0.0.1。 可能更常见的状况是若是 Pod 依赖虚拟主机,你不该该设置 host
字段,而是应该在 httpHeaders
中设置 Host
。
对于一次 TCP 探测,kubelet 在节点上(不是在 Pod 里面)创建探测链接, 这意味着你不能在 host
参数上配置服务名称,由于 kubelet 不能解析服务名称。
为了第十章,必须把helm的一直基本姿式介绍一下,因此有第9章
helm是kubernetes的包管理器,用于查找,分享,使用kubernetes生态的应用。
通常helm都是安装在主节点上,开发人员用有权限范围的用户登陆上去操做helm便可,
能够和git结合使用,或安装helm局域网服务器Tiller
也能够在开发人员机器上经过config+kubectl 直接链接 上集群 ,可是这样开发人员须要在本身电脑上安装kubectl。
不管是windows仍是linux,都是直接去下载helm的二进制文件。
linux复制helm二进制到bin目录:
mv helm /usr/bin
windows设置环境变量:
个人电脑属性->高级->设置环境变量->path+=
xxx_chart/ Chart.yaml # chart的信息文件 LICENSE # 可选: 许可证 如:GPL LGPL README.md # 可选: values.yaml # 默认的配置值文件 values.schema.json # 可选: 对value.yaml提供输入格式校验的josn文件 charts/ # 依赖的其余图表的存放文件夹 crds/ # 自定义资源 templates/ # 使用go模版的yaml文件,会从value.yaml或其余文件读值生成资源yaml templates/NOTES.txt # 可选: 安装成功时,显示在终端上的文字
helm语法是yaml混合 go模版的语法
听起来很复杂,其实学起来不复杂
仅须要抓着helm chart安装生成的文件抽丝剥茧,就能快速掌握,由于自己并不复杂
上下文
.
根
.value
value.yaml
.release
安装时用户输入
.chart
chart.yaml
模版
其余语法 去官网文档查
现有两个.net core web app
- api
- blazor
前端将经过ingress暴露给集群外部,后端则只在集群内部。以此为例实践第10章
略
这个在前端和后端web app中都须要作,由于都要部署到集群。
在visual studio 安装10.1中的扩展后
选择kubernetes/helm
肯定后生成chart文件夹和docker file,还有一个azds.yaml (部署到微软云上才用到),
public class Program { public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults( webBuilder => webBuilder .UseStartup<Startup>().UseUrls("http://*:5001")); }
value.yaml中修改服务的targetport
service: type: ClusterIP port: 80 targetPort: 5001
修改deployment.yaml里的image 和containerPort
apiVersion: apps/v1 kind: Deployment metadata: name: {{ template "hscadaexapi.fullname" . }} labels: # 略 spec: revisionHistoryLimit: 0 replicas: {{ .Values.replicaCount }} selector: matchLabels: app: {{ template "hscadaexapi.name" . }} release: {{ .Release.Name }} template: # 略 spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: {{ .Values.service.targetPort}} protocol: TCP
.net core由于安全性设计,默认不支持非同源策略的域名对.net web app进行访问
下表给出了与 URL http://store.company.com/dir/page.html
的源进行对比的示例:
URL | 结果 | 缘由 |
---|---|---|
http://store.company.com/dir2/other.html |
同源 | 只有路径不一样 |
http://store.company.com/dir/inner/another.html |
同源 | 只有路径不一样 |
https://store.company.com/secure.html |
失败 | 协议不一样 |
http://store.company.com:81/dir/etc.html |
失败 | 端口不一样 ( http:// 默认端口是80) |
http://news.company.com/dir/other.html |
失败 | 主机不一样 |
定义1个策略名称
public readonly string myAllowSpecificOrigins = "myAllowSpecificOrigins";
配置策略
// startup.cs public void ConfigureServices(IServiceCollection services) { //略 services.AddCors(o => { o.AddPolicy(myAllowSpecificOrigins, build => { build .AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); }); //略 }
添加到中单件管道
// startup.cs public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //略 app.UseCors(myAllowSpecificOrigins); //略 }
略
同10.2.2.1.1,略
launchSetting.json
{ "profiles": { "HScadaEx.Blazor": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "userApi": "http://localhost:5001" }, "applicationUrl": "http://localhost:5011" } } }
value.yaml
# 略 userApi: http://hscadaexapi.hbb.svc # http://服务名称.名称空间.svc # 略
deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: {{ template "hscadaexblazor.fullname" . }} # 略 spec: #略 template: # 略 spec: containers: - name: {{ .Chart.Name }} # 略 env: # 略 - name: userApi value: {{ .Values.userApi | quote }}
程序中使用环境变量
// startup.cs public void ConfigureServices(IServiceCollection services) { var userApi = Environment.GetEnvironmentVariable("userApi"); services.AddHttpClient("usersApi", x => { x.BaseAddress = new Uri($"{userApi}/api/Users/"); x.DefaultRequestHeaders.Add("User-Agent", "BlazorSever"); x.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); }).SetHandlerLifetime(TimeSpan.FromSeconds(30)); Console.WriteLine($"环境变量userApi = {userApi}"); services.AddTransient<IBLL.User.IUserService, Service.UsersServer>(); }
我但愿访问www.haoyun.blazor的时候,能够从集群外部访问前端应用
访问链路:用户-> www.haoyun.blazor->dns->集群边缘节点外部ip->ingress controller(集群)->blazor server--ipvs-->pod(集群)
values.yaml
ingress: enabled: true annotations: # kubernetes.io/tls-acme: "true" path: / hosts: - www.haoyun.blazor tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local
/template/ingerss.yaml 保持不变便可
{{- if .Values.ingress.enabled -}} {{- $fullName := include "hscadaexblazor.fullname" . -}} {{- $servicePort := .Values.service.port -}} {{- $ingressPath := .Values.ingress.path -}} apiVersion: extensions/v1beta1 kind: Ingress metadata: name: {{ $fullName }} labels: app: {{ template "hscadaexblazor.name" . }} chart: {{ template "hscadaexblazor.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} {{- with .Values.ingress.annotations }} annotations: {{ toYaml . | indent 4 }} {{- end }} spec: {{- if .Values.ingress.tls }} tls: {{- range .Values.ingress.tls }} - hosts: {{- range .hosts }} - {{ . }} {{- end }} secretName: {{ .secretName }} {{- end }} {{- end }} rules: {{- range .Values.ingress.hosts }} - host: {{ . }} http: paths: - path: {{ $ingressPath }} backend: serviceName: {{ $fullName }} servicePort: http {{- end }} {{- end }}
默认的docker file解读
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base #以..3.1环境为基础建立一个名为base的镜像 WORKDIR /app #设置工做目录 保存为匿名镜像 EXPOSE 80 #导出端口 80 保存为匿名镜像 FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build #以..sdk3.1为基础建立一个名为build的镜像 WORKDIR /src #设置工做目录 保存为匿名镜像 COPY ["src/test/test.csproj", "src/test/"] #复制csobj文件 保存为匿名镜像 RUN dotnet restore "src/test/test.csproj" #还原nuget包 保存为匿名镜像 COPY . . #递归复制解决方案到 src目录 保存为匿名镜像 WORKDIR "/src/src/test" #设置工做目录为镜像中的test项目目录 保存为匿名镜像 RUN dotnet build "test.csproj" -c Release -o /app/build #在镜像中编译 保存为匿名镜像 FROM build AS publish #以build 为基础 建立一个名为publish的镜像 RUN dotnet publish "test.csproj" -c Release -o /app/publish #发布到/app/publish目录 保存为匿名镜像 FROM base AS final #base为基础建立一个名为final的镜像 WORKDIR /app #设置工做目录,保存为匿名镜像 COPY --from=publish /app/publish . #复制publish镜像的/app/publish文件夹到final的工做目录 保存为匿名镜像 ENTRYPOINT ["dotnet", "test.dll"] # 设置入口 保存为匿名镜像
修改release->debug ,添加调试工具后
以api 的dockerfile为例, blazor略
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base WORKDIR /app EXPOSE 5001 #导出所需端口 RUN apt-get update && apt-get install -y --no-install-recommends unzip && apt-get install -y procps && rm -rf /var/lib/apt/lists/* && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg # 调试工具安装 FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build WORKDIR /src COPY ["src/HScadaEx.API/HScadaEx.API.csproj", "src/HScadaEx.API/"] COPY ["src/HScadaEx.IBLL/HScadaEx.IBLL.csproj", "src/HScadaEx.IBLL/"] COPY ["src/HScada.Model/HScada.Model.csproj", "src/HScada.Model/"] COPY ["src/HscadaEx.BLL/HscadaEx.BLL.csproj", "src/HscadaEx.BLL/"] COPY ["src/HScadaEx.Core/HScadaEx.Core.csproj", "src/HScadaEx.Core/"] RUN dotnet restore "src/HScadaEx.API/HScadaEx.API.csproj" COPY . . WORKDIR "/src/src/HScadaEx.API" RUN dotnet build "HScadaEx.API.csproj" -c Debug -o /app/build #改成debug,不须要调试就用release FROM build AS publish RUN dotnet publish "HScadaEx.API.csproj" -c Debug -o /app/publish#改成debug,不须要调试就用release FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "HScadaEx.API.dll"]
工做节点
上主节点
上因为我的计算机资源有限,因此我这里只使用docker save
和docker load
命令 将docker镜像复制到仅有的一个工做节点上。正确的作法是在集群外部搭建一个docker私有仓库,按需拉取。
已知私有仓库部署方式
对于 2和3 有认证受权的镜像
正确的姿式是配置好证书后,导入为secret资源,在拉取镜像时指定secret资源便可。
一样 chart也有私有服务器能够搭建,名为Tiller
,正确的姿式也是应该搭服务器,让集群调度的时候按需获取,
更多信息查看官网文档 或 helm -h
查看
helm ls -n 名称空间
添加repository
helm add repo 名称 url
安装
helm install -n 名称空间 release名称 chart #其中的chart 能够是本地文件,也能够是tiller上的chart路径
更新
helm upgrade -n 名称空间 release名称 chart
查看变动历史记录
helm history -n 名称空间 release名称
回滚
helm rollback
删除
helm delete -n 名称空间 release名称
下载kubectl二进制,添加到系统变量path
复制集群管理员生成的 config文件到 c:\用户\.kube 文件夹
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base WORKDIR /app EXPOSE 5001 EXPOSE 5002 RUN apt-get update && apt-get install -y --no-install-recommends unzip && apt-get install -y procps && rm -rf /var/lib/apt/lists/* && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build WORKDIR /src COPY ["src/HScadaEx.API/HScadaEx.API.csproj", "src/HScadaEx.API/"] COPY ["src/HScadaEx.IBLL/HScadaEx.IBLL.csproj", "src/HScadaEx.IBLL/"] COPY ["src/HScada.Model/HScada.Model.csproj", "src/HScada.Model/"] COPY ["src/HscadaEx.BLL/HscadaEx.BLL.csproj", "src/HscadaEx.BLL/"] COPY ["src/HScadaEx.Core/HScadaEx.Core.csproj", "src/HScadaEx.Core/"] RUN dotnet restore "src/HScadaEx.API/HScadaEx.API.csproj" COPY . . WORKDIR "/src/src/HScadaEx.API" RUN dotnet build "HScadaEx.API.csproj" -c Debug -o /app/build FROM build AS publish RUN dotnet publish "HScadaEx.API.csproj" -c Debug -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "HScadaEx.API.dll"]
这里用cloud code插件生成调试配置
launchSetting.json
{ "version": "0.2.0", "configurations": [ { "name": "Attach to Kubernetes Pod (.NET Core)", "type": "cloudcode.kubernetes", "request": "attach", "language": "NETCore", "podSelector": { "app": "hscadaexblazor" //pod的app label }, "localRoot": "${workspaceFolder}", //本地工做区 "remoteRoot": "/app" //远程容器中的工做目录 } ] }
.net core目前只能用于附加调试, go/node.js/py 能够热重载调试,从谷歌插件的文档上看到的。
这种方式的缺点很明显,若是要调试过程修改了代码,因为不能热重载,只能在本地改好代码->重生成docker image->推送到docker repository ->更新image到集群 ->再附加调试
因此比较推荐另外一种方式的调试,虽然也不支持热重载,可是用dotnet watch run,在代码重生成时实时响应到容器中,并重启容器中的程序
okteto是一个开源的项目,用于简化各类技术栈 在kubernetes中的开发工程。
传统的kubernetes 服务开发过程就是不断的重复这个过程
while(调试ing) { 附加调试->修改代码->生成docker镜像->推送镜像->拉镜像->更新到集群 }
okteto的方式是把开发环境打包成一个镜像,实时同步容器与本地的文件变化,转发本地流量到容器 、转发容器流量到本地 等链接调试用的是ssh ,配合dotnet watch run 能够仅是生成代码就把生成的结果应用到容器中,虽然不是真正的热重载,容器中的程序会重启,但快速了不少,并且这是各类技术栈都能用的,不只限于.net 的万金油,因而流程简化为
do { dotnet watch run }while(调试ing) { 附加调试->修改代码->生成 }
name: hscadaexapi #deployment service 的名称 namespace: hbb #名称空间 image: mcr.microsoft.com/dotnet/core/sdk #开发环境变量 environment: - ASPNETCORE_ENVIRONMENT=Development #环境变量 command: - bash #启动命令 workdir: /okteto #工做目录 remote: 22000 # ssh调试端口 把本地22000->容器22 sync: #同步的文件夹 本地:容器 - .:/okteto forward: #端口转发 本地:容器 - 5001:5001 persistentVolume: {}
okteto up
这里集群里会多出一个deployment 和service 资源, 跑起一个pod,这个pod是空的,仅仅是一个开发环境,
而后okteto会并 当前文件夹的文件同步到远程容器中的$workdir
,并执行 $command
接下去就是在容器中启动.net core程序
dotnet watch run
查看工做台输出 已经启动了程序的话, 在本地访问 localhost:forward
应该访问到容器上了
大多数ide都支持ssh远程调试,这里我以visual studioi为例
alt+shift+p 附加调试,选择 ssh ,配置参数
以后就会让你选择要附加的进程了