it177模板网(www.it177.com)精品网站源码,织梦建站模版,游戏源代码分享平台

模板户源码

当前位置:首页 -> 下载中心 -> 软件工具 正文

Spring Cloud Kubernetes容器化实践

时间:2019-03-16 08:25:19 [整站源码]作者:zhaopulei


涂小刚 Docker


随着公司业务量和产品线的增加,项目越来越多,普通运维系统架构对整个软件研发生命周期的管理越来越难,效率低下,难以统一管理。近年来Docker统一了容器标准,对于软件开发流程产生了深远的影响,Docker可以一次打包,处处运行。过去几年Kubernetes平台发展日新月益,Kubernetes统一了容器排编王者的地位,我个人认为kubernetes可以说是对普通运维架构一次突破性的革命。

利用Kubrenets集群平台可以很方便的对容器服务进行集中管理,可以非常高效的对容器服务进行编排、调度、扩容、升级、回滚、监控、集中收集日志等,基本上把传统运维架构需要考虑的问题全部解决了,而DevOps容器化也是整个软件开发流程的必经之路,因此我们对现有老旧的运维平台进行替换,统一利用Kubernetes对所有业务进行管理。

原有运维系统缺点

  • 原有业务布署在虚拟机ECS、KVM上,脚本分散、日志分散、难于集中收集管理,监控不统一,CPU、内存、磁盘资源使用率低,运维效率极低,无法集中管理。

  • 新业务布署需要开通新的虚拟机,需要单独定制监控,各种Crontab,配置脚本,效率低下,CI/CD Jenkins配置繁琐。


Kubernetes容器化优势

  • 利用Kubernetes容器平台namespaces对不同环境进行区分,建立不同dev、test、stage、prod环境,实现隔离。

  • 通过容器化集中布署所有业务,实现一键布署所需环境业务。

  • 统一集中监控报警所有容器服务异常状态。

  • 统一集中收集所有服务日志至ELK集群,利用Kibana面板进行分类,方便开发查日志。

  • 基于Kubernetes命令行二次开发,相关开发、测试人员直接操作容器。

  • 基于RBAC对不同的环境授于不同的开发、测试访问Kubernetes权限,防止越权。

  • 通过Jenkins统一CI/CD编译发布过程。

  • 项目容器化后,整体服务器CPU、内存、磁盘、资源利用减少50%,运维效率提高60%,原来需要N个运维做的事,现在一个人即可搞定。


Kubernetes本身是一套分布式系统,要用好会遇到很多问题,不是说三天两头就能搞定,需要具备网络、Linux系统、存储,等各方面专业知识,在使用过程中我们也踩了不少坑,我们是基于二进制包的方式安装Kubernetes集群,我们Kubernetes集群版本为1.10,经过一段时间的实践,Kubernetes对于我们整个开发、测试、发布、运维流程帮助非常大,值得大力推广。

网络方案选择

  1. Flannel是 CoreOS 团队针对 Kubernetes 设计的一个覆盖网络(Overlay Network)工具,所有节点通过flanneld节点服务同步路由,使用简单、方便、稳定,是Kubernetes入门首选。

  2. Calico是基于BGP协议的路由方案,支持ACL,部署复杂,出现问题难排查。

  3. Weave是基于UDP承载容器之间的数据包,并且可以完全自定义整个集群的网络拓扑,国内使用较少。

  4. Open vSwitch是一个生产质量的多层虚拟交换机,它旨在通过编程扩展实现大规模网络自动化,同时仍支持标准管理接口和协议,OpenShift-kubernetes平台和混合云使用比较多。


我们对各个网络组件进行过调研对比,网络方案选择的是flanneld-hostgw+ipvs,在Kubernetes 1.9之前是不支持IPVS的,kube-proxy负责所有SVC规则的同步,使用的iptables,一个service会产生N条iptables记录。如果SVC增加到上万条,iptables-svc同步会很慢,得几分钟,使用IPVS之后,所有节点的SVC由IPVS LVS来负载,更快、更稳定,而且简单方便,使用门槛低。host-gw会在所有节同步路由表,每个容器都分配了一个IP地址,可用于与同一主机上的其他容器进行通信。对于通过网络进行通信,容器与主机的IP地址绑定。flanneld host-gw性能接近Calico,相对来说Falnneld配置布署比Calico简单很多。顺便提下flanneld-vxlan这种方式,需要通过UDP封包解包,效率较低,适用于一些私有云对网络封包有限制,禁止路由表添加等有限制的平台。

Flanneld通过为每个容器提供可用于容器到容器通信的IP来解决问题。它使用数据包封装来创建跨越整个群集的虚拟覆盖网络。更具体地说,Flanneld为每个主机提供一个IP子网(默认为/ 24),Docker守护程序可以从中为每个主机分配IP。

Flannel使用etcd来存储虚拟IP和主机地址之间的映射。一个Flanneld守护进程在每台主机上运行,并负责维护etcd信息和路由数据包。

在此提一下,在使用flannled使用过程中遇到过严重bug,即租约失效,flanneld会shutdown节点网络组件,节点网络直接崩掉,解决办法是设置永久租期:https://coreos.com/flannel/docs/latest/reservations.html#reservations。

传统业务迁移至Kubernetes遇到的问题和痛点,DevOps遇到的问题


使用Kubernetes会建立两套网络,服务之间调用通过service域名,默认网络、域名和现有物理网络是隔离的,开发,测试,运维无法像以前一样使用虚拟机,Postman IP+端口调试服务, 网络都不通,这些都是问题。

  • Pod网络和物理网络不通,Windows办公电脑、Linux虚拟机上现有的业务和Kubernetes是隔离的。

  • SVC网络和物理网络不通,Windows办公电脑、Linux虚拟机上现有的业务和Kubernetes是隔离的。

  • SVC域名和物理网络不通,Windows办公电脑、Linux虚拟机上现有的业务和Kubernetes是隔离的。

  • 原有Nginx配置太多的location路由规则,有的有几百层,不好迁移到ingress-nginx,ingress只支持简单的规则。

  • SVC-NodePort访问,在所有Node上开启端口监听,占用Node节点端口资源,需要记住端口号。

  • ingress-nginx http 80端口, 必需通过域名引入,http 80端口必需通过域名引入,原来简单nginx的location可以通过ingress引入。

  • ingress-nginx tcp udp端口访问需要配置一个lb,很麻烦,要先规划好lb节点同样也需要访问lb端口。

  • 原有业务不能停,继续运行,同时要能兼容Kubernetes环境,和Kubernetes集群内服务互相通讯调用,网络需要通。


传统虚拟机上布署服务我们只需要一个地址+端口直接访问调试各种服务,Kubernetes是否能做到不用改变用户使用习惯,无感知使用呢?答案是打通DevOps全链路,像虚拟机一样访部Kubernetes集群服务 , 我们打通Kubernetes网络和物理网络直通,物理网络的DNS域名调用Kubernetes DNS域名服务直接互访,所有服务互通。公司原有业务和现有Kubernetes集群无障碍互访。

配置一台Kubernetes Node节点机做路由转发,配置不需要太高,布署成路由器模式,所有外部访问Kubernetes集群流量都经该节点,本机IP:192.168.2.71。

  1. vim /etc/sysctl.conf

  2. net.ipv4.ip_forward = 1



设置全网路由通告,交换机或者Linux、Windows主机加上静态路由,打通网络。

  1. route add -net 172.20.0.0 netmask 255.255.0.0 gw 192.168.2.71

  2. route add -net 172.21.0.0 netmask 255.255.0.0 gw 192.168.2.71



增加DNS服务器代理,外部服务需要访问Kubernetes service域名,首先需要解析域名,Kubernetes服务只对集群内部开放,此时需要外部能调用KubeDNS 53号端口,所有办公电脑,业务都来请求KubeDNS肯定撑不住,事实上确实是撑不住,我们做过测试,此时需要配置不同的域名进行分流策略,公网域名走公网DNS,内部.svc.cluster.local走KubeDNS。

1、建立DNS代理服务器,ingress建立一个nginx-ingress服务反代KubeDNS,ingress-nginx绑定到DNS节点运行,在节点上监听DNS 53端口。

  1. [root@master1 kube-dns-proxy-1.10]# cat tcp-services-configmap.yaml

  2. kind: ConfigMap

  3. apiVersion: v1

  4. metadata:

  5.  name: tcp-services

  6.  namespace: ingress-nginx

  7. data:

  8.  53: "kube-system/kube-dns:53"

  9. [root@master1 kube-dns-proxy-1.10]# cat udp-services-configmap.yaml

  10. kind: ConfigMap

  11. apiVersion: v1

  12. metadata:

  13.  name: udp-services

  14.  namespace: ingress-nginx

  15. data:

  16.  53: "kube-system/kube-dns:53"

  17. [root@master1 kube-dns-proxy-1.10]# cat ingress-nginx-deploy.yaml

  18. apiVersion: extensions/v1beta1

  19. kind: Deployment

  20. metadata:

  21.  name: nginx-ingress-controller-dns

  22.  namespace: ingress-nginx

  23. spec:

  24.  replicas: 1

  25.  selector:

  26.    matchLabels:

  27.      app: ingress-nginx-dns

  28.  template:

  29.    metadata:

  30.      labels:

  31.        app: ingress-nginx-dns

  32.      annotations:

  33.        prometheus.io/port: 10254

  34.        prometheus.io/scrape: true

  35.    spec:

  36.      hostNetwork: true

  37.      serviceAccountName: nginx-ingress-serviceaccount

  38.      containers:

  39.        - name: nginx-ingress-controller-dns

  40.          image: registry-k8s.novalocal/public/nginx-ingress-controller:0.12.0

  41.          args:

  42.            - /nginx-ingress-controller

  43.            - --default-backend-service=$(POD_NAMESPACE)/default-http-backend

  44.           # - --configmap=$(POD_NAMESPACE)/nginx-configuration

  45.            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services

  46.            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services

  47.            - --annotations-prefix=nginx.ingress.kubernetes.io

  48.          env:

  49.            - name: POD_NAME

  50.              valueFrom:

  51.                fieldRef:

  52.                  fieldPath: metadata.name

  53.            - name: POD_NAMESPACE

  54.              valueFrom:

  55.                fieldRef:

  56.                  fieldPath: metadata.namespace

  57.          ports:

  58.          - name: http

  59.            containerPort: 80

  60.          #- name: https

  61.          #  containerPort: 443

  62.          livenessProbe:

  63.            failureThreshold: 3

  64.            httpGet:

  65.              path: /healthz

  66.              port: 10254

  67.              scheme: HTTP

  68.            initialDelaySeconds: 10

  69.            periodSeconds: 10

  70.            successThreshold: 1

  71.            timeoutSeconds: 1

  72.          readinessProbe:

  73.            failureThreshold: 3

  74.            httpGet:

  75.              path: /healthz

  76.              port: 10254

  77.              scheme: HTTP

  78.            periodSeconds: 10

  79.            successThreshold: 1

  80.            timeoutSeconds: 1

  81.      nodeSelector:

  82.        node: dns



2、最简单快捷的方式是安装Dnsmasq,当然你也可以用Bind,PowerDNS,CoreDNS等改造,上游DNS配置为上一步骤增加nginx-ingress dns的地址,所有办公,业务电脑全部设置DNS为此机,dnsmasq.conf配置分流策略。

  1. no-resolv

  2. server=/local/192.168.1.97

  3. server=114.114.114.114



完成以上步骤后Kubernetes pod网络、service网络、 service域名和办公网络,现有ECS、虚拟机完美融合,无缝访问,容器网络问题完美搞定。

Windows访问Kubernetes service畅通无组,开发测试,完美无缝对接。
 
 

ingress-nginx服务入口接入


服务发布后最终对接的是用户,用户访问Kubernetes服务需要通过nginx或其它http服务器接入,对于服务接入我们同时使用两种不同的方案,取决于nginx location的复杂度,location规则简单的我们使用第一种方案,由于各种问题,location复杂我们使用第二种方案。

  1. client-------ingress-nginx-----upstream----podip,对于ingress-nginx官方使用的原始方案,先配置ingress规则路由,ingress对接不同的service-dns域名,ingress自动发现后端podip,通过upstream负载不同的后端podip,不同的域名路由到不同的Kubernetes后端podip,用户客户端访问流量会负载到不同的Pod上。

  2. client------nginx-------upstream------svc-----podip改造现有nginx兼容Kubernetes,对接Kubernetes service服务。对于nginx location规则过多,不能很好的兼容nginx-ingress导致使用Kubernetes非常困难,难以普及,在不变更现有nginx配置的情况下如何对接Kubernetes这是一个问题,经过前面网络打通的步骤我们所有网络的问题都已解决。现在只需改动很小部分即可兼容,由于Kubernetes podip是漂移的,IP总是会变的,nginx只能是对接SVC域名才能持久,但是nginx解析域名有个bug,只解析一次,如果在此期间删除了yaml,nginx会找不到后端svcip,所以这里要设置代理变量set $backend,设置resolver的DNS为代理DNS地址,设置解析域名时间和变量解决该问题。


  1. location /tomcat/ {

  2.            resolver 192.168.1.97 valid=3600s;

  3.            set $backend "tomcat.dac-prod.svc.cluster.local";

  4.            error_log  logs/dac_error.log  error;

  5.            access_log  logs/dac_access.log  main;

  6.            proxy_set_header X-real-ip $remote_addr;

  7.            proxy_read_timeout 300;

  8.            proxy_connect_timeout 300;

  9.            proxy_redirect     off;

  10.            client_max_body_size 100M;

  11.            proxy_pass http://${backend}:9090;

  12.    }


大家可能担心Eureka和Kubernetes service有冲突,Spring Cloud本身自带服务发现Eureka,组件之间的调用通过Eureka注册调用,其实你直接布署就行了,Eureka和Service没任何冲突,和普通Java应用一样用。

监控方案


目前使用的Kubernetes官方的Heapster,Monitoring-InfluxDB-Grafana +自定议脚本+自定义Grafana面板可以灵活报警。

监控面板按业务环境dev/test/stage/prod/对CPU/内存/网络等分类进行展示。

节点资源监控: 


Pod CPU、内存、网络等监控: 


监控脚本,可以很灵活跟据设定参数进行钉钉报警,报告有问题的Pod、Node,自动处理有问题的服务。

  1. #!/bin/bash

  2. #最大内存排除的node节点

  3. exclude_node="node7|node1|node2|node3|master1"

  4. exclude_pod="redis|kafka|mongo|zookeeper|Evicted|Completed"

  5. #node使用的最大报警内存%比

  6. node_mem_max="100"

  7. #node最大使用cpu百分比

  8. node_cpu_max="80"

  9. #pod使用的最大报警内存MB

  10. pod_mem_max="4096"

  11. pod_top="5"

  12. pod_top_cpu="10"

  13. #pod的启动错误时间,单位为秒s

  14. pod_error_m_time="120"

  15. pyding="$HOME/k8s-dev/dingd-zabbix.python"

  16. #pod的内存以及cpu的使用状态

  17. pod_mem=$(/usr/local/bin/kubectl top pod --all-namespaces  |sort -n -k4 )

  18. #node的内存使用状态

  19. node_status=$(/usr/local/bin/kubectl top node|egrep -v "${exclude_node}" |egrep -v "MEMORY%")

  20. #pod的运行状态

  21. pod_status=$(/usr/local/bin/kubectl get pod --all-namespaces -o wide|grep -v NAMESPACE)

  22. #设定有问题的pod存取文件路径

  23. alert_error_pod="/tmp/alert-error-pod.txt"

  24. #设定最大内存占用节点上pod的文件列表路径

  25. alert_list="/tmp/alert-mem-list.txt"

  26. #监控cpu百分比文件输出路径

  27. alert_node_cpu_list="/tmp/alert_node_cpu_list.txt"

  28. #取node内存的百分比数字值

  29. #node_pre_mem=$(echo "${node_mem}"|awk {print $5}|sed -e "s/%//g")

  30. #监控node的内存百分比,列出占用内存最高的应用并重启top5应用

  31. node_mem_mon () {

  32.        echo "${node_status}" |awk {print $1,$5}|sed -e "s/%//g" |while read node_name node_mem_status;do

  33.        #echo $node_name $node_mem_status

  34.                if [ "${node_mem_status}" -gt "${node_mem_max}" ];then

  35.                         >${alert_list}

  36.                        #找到该节点上的所有的pod名

  37.                        find_pod=$(echo "${pod_status}"|egrep ${node_name}|awk {print $2})

  38.                        #找到所有节点倒排序使用最大的内存的pod列表

  39.                        for i in $(echo "${find_pod}");do

  40.                                echo "${pod_mem}"|grep $i  >>${alert_list}

  41.                        done

  42.                        date_time=`date +%F-%T`

  43.                        echo -e " ${node_name}最大内存超过 %${node_mem_max} 以下pod应用将被重启 ------------------ "

  44.                        cat ${alert_list}|sort -n -k 4|tail -${pod_top}

  45.                        python ${pyding}  "`echo -e " ${date_time} ${node_name}当前内存为${node_mem_status}%,最大内存超过 %${node_mem_max} 以下pod应用将被重启 ------------------ " ;cat ${alert_list}|sort -n -k 4|egrep -v "$exclude_pod"|tail -${pod_top}` "

  46.                        cat ${alert_list}|sort -n -k 4|egrep -v "$exclude_pod"|tail -${pod_top}|egrep -v "应用将被重启" | awk {print  "/usr/local/bin/kubectl delete pod  "$2" -n "$1" " | "/bin/bash"}

  47.                fi

  48.        done

  49. }



钉钉报警图: 


Kubernetes集群yaml容器编排管理


Kubernetes通过yaml对容器进行管理,yaml配置编排文件是管理整个容器生命周期重要的一部份,管理好yaml非常重要。我开发了一套类似于Helm的模板的脚本框架,用于所有环境的yaml初始化工作 ,自己写脚本的好处就是可以灵活控制,比如哪个组件要挂载存储,共享卷,要配置私有hosts等,我可以一次性定制好,初始化时只需要init-yaml直接批量搞定,不需要每个yml单独去修改,之后就是kubectl create 直接用。

容器编排yaml文件按空间环境dev、test、stage、prod进行模板base分类,复制一套yaml模板即可生成其它各环境,容器编排按业务类型模块配置conf app-list。

  1. [root@master1 config]# ls

  2.  public-dev_app_list.conf      public-test-base.yml    

  3.  public-dev-base.yml           sms-test_app_list.conf  

  4.  public-pretest_app_list.conf  sms-test-base.yml        

  5.  public-pretest-base.yml       wbyh-dev_app_list.conf  

  6.  public-stage_app_list.conf    wbyh-dev-base.yml        

  7.  public-stage-base.yml         wbyh-stage_app_list.conf

  8.  public-test_app_list.conf     wbyh-stage-base.yml



通过Kubernetes核心排编脚本进行init-yml初始化对应环境,生成所有Pod的yaml排编文件,每套环境可以生成环境对应的MySQL、Redis、Kafka、MongoDB等,直接启动即可调用。

  1. [root@master1 k8s-dev]# ./k8s wbyh-stage init-yml

  2. /root/k8s-dev/config

  3. [root@master1 k8s-dev]# tree

  4. wbyh-stage/

  5. ├── app

  6.   ├── dac-api-center

  7.     └── dac-api-center.yml

  8.   ├── dac-app-web

  9.     └── dac-app-web.yml

  10.   ├── dac-config-server

  11.     └── dac-config-server.yml

  12.   ├── dac-eureka-server

  13.     └── dac-eureka-server.yml

  14.   ├── dac-task

  15.     └── dac-task.yml

  16.   ├── dac-task-apply

  17.     └── dac-task-apply.yml

  18.   ├── dac-task-h5

  19.     └── dac-task-h5.yml

  20.   ├── dac-web

  21.     └── dac-web.yml

  22.   ├── dac-message-center

  23.     └── dac-message-center.yml

  24.   ├── dac-quartz-jfdata

  25.     └── dac-quartz-jfdata.yml

  26.   ├── dac-quartz-mach

  27.     └── dac-quartz-mach.yml

  28.   ├── dac-quartz-dac

  29.     └── dac-quartz-dac.yml

  30.   ├── dac-resources-center

  31.     └── dac-resources-center.yml

  32.   ├── dac-resources-item

  33.     └── dac-resources-item.yml

  34.   ├── dac-usercenter-web

  35.     └── dac-usercenter-web.yml

  36.   └── tomcat

  37.       └── tomcat.yml

  38. └── stateful-sets

  39.    ├── kafka

  40.       ├── 10kafka-config-0420yml

  41.       ├── 10kafka-config.yml

  42.       ├── 20dns.yml

  43.       └── 50kafka.yml

  44.    ├── mongo

  45.       └── mongo-statefulset.yml

  46.    ├── redis

  47.       ├── primary.yml

  48.       └── redis-configmap.yml

  49.    └── zookeeper

  50.        ├── 10zookeeper-config.yml

  51.        ├── 30service.yml

  52.        └── 50pzoo.yml

  53. 22 directories, 26 files



通过Kubernetes脚本调用kubectl可以直接批量创建该空间下所有服务。

  1. [root@master1 k8s-dev]# ./k8s wbyh-stage create_all

  2. /root/k8s-dev/config

  3. configmap "dac-eureka-server-filebeat-config" created

  4. service "dac-eureka-server" created

  5. deployment.extensions "dac-eureka-server" created

  6. configmap "dac-config-server-filebeat-config" created

  7. service "dac-config-server" created

  8. deployment.extensions "dac-config-server" created

  9. configmap "tomcat-filebeat-config" created

  10. service "tomcat" created

  11. deployment.extensions "tomcat" created



所有代码存入GitLab做版本管理,即基础设施即代码。

  1.    add svn-jar-version ll item

  2. commit 29dc05530d839c826130eef81541ce96a155107b

  3. Author: idea77 <idea77@qq.com>

  4. Date:   Thu Sep 20 16:11:00 2018 +0800

  5.    mod ossfs to /Rollback/oss

  6. commit 880bcd9483a6ee1f5ca440fef017b30ba7cd14fe

  7. Author: idea77 <idea77@qq.com>

  8. Date:   Wed Sep 19 16:57:43 2018 +0800



存储方案


目前公司一部份应用挂载的卷为NFS,读写要求不高的可以配置NFS, 一部份要求比较高的用的Ceph,如MySQL、Kafka之类的就需要Ceph支撑,对于需要持久化的DB类型存储的管理用StorageClass存储类对接管理,很方便自动建立存储卷PV-PVC对接,共享卷类型可以直接挂载卷。

NFS配置需要在每个Node节点安装NFS-Utils,配置yml,注意CentOS 7低版本3.10内核的nfs-server有bug,导致服务器重启,升到4.0以上内核解决问题。

  1.      - name: tomcat-img

  2.        nfs:

  3.          path: /home/k8s-nfs-data/dac-test-tomcat-img

  4.          server: 192.168.8.30



Ceph Kubernetes Node节点安装ceph-commo,配置StorageClass。

  1. ceph-class.yaml

  2. apiVersion: storage.k8s.io/v1beta1

  3. kind: StorageClass

  4. metadata:

  5.   name: ceph-db

  6. provisioner: kubernetes.io/rbd

  7. parameters:

  8.  monitors: 192.168.1.31:6789

  9.  adminId: admin

  10.  adminSecretName: ceph-secret

  11.  adminSecretNamespace: kube-system

  12.  pool: rbd

  13.  userId: admin

  14.  userSecretName: ceph-secret



Jenkins CI/CD编译发布阶段


Jenkins CI/CD控制台完成整个jar包编译,Dockerfile编译、docker push、Kubernetes deployment镜像滚动升级功能。

Jenkins Manage and Assign Roles授权不同的开发、测试组不同的用户权限,隔离不同的项目编译发布权限。 


目前没有完全用上流水线服务,完全流水线需要构建不报错,一报错也就无法完成,不是很灵活,构建jar包和发布docker-image是分开的,需要跟据公司业务来。

编译阶段我们做了钉钉通知,每个项目拉了自己的群,编译jar包是否成功整个组都有通知,同样update发布是否成功都有提示,群内可见。 


目前我们Kubernetes容器启动分为两种架构:

  1. 容器发布后启动基础JDK镜像,Wget去http服务器下载对应目录编译好的jar包,然后启动,即无镜像模式,适合频繁发布类型的业务,push jar to oss有一部份业务是跑虚拟机,需要jar包,oss可以做共享。

  2. 容器发布按照标准的方式打image update-imae模式,适合出错及时回滚的业务,即编译dockerfile-push-docke-image-update-deployment。


build-$namespace通过空间变量名拟写对应脚本,基本是做一个通用模板base,复制生成对应项目的build.sh供Jenkins传参调用,每套环境有自己的基础镜像base,基础镜像就是打入JDK等一些私有的配置,编译的时候在基础镜像上加上jar包。

  1. if [[ $MY_POD_NAMESPACE =~ -dev ]];then

  2.    #定义启动基础镜相

  3.    base_image="registry-k8s.novalocal/public/yh-centos7-jdk-1.8"

  4.    #定义APP镜像仓库地址

  5.    image_path="registry-k8s.novalocal/xl_public/$MY_POD_NAMESPACE/${APP}"

  6. elif [[ $MY_POD_NAMESPACE =~ "-test" ]];then

  7.    #定义启动基础镜相

  8.    base_image="registry-k8s.novalocal/public/yh-centos7-jdk-1.8"

  9.    #定义APP镜像仓库地址

  10.    image_path="registry-k8s.novalocal/xl_public/$MY_POD_NAMESPACE/${APP}:${date_time}"

  11. elif [[ $MY_POD_NAMESPACE =~ -stage ]];then

  12.    #定义启动基础镜相

  13.    base_image="registry-k8s.novalocal/xl_public/wbyh-base/centos7-jdk-1.8"

  14.    #定义idc镜相仓库路径

  15.    image_path="registry.cn-hangzhou-idc.com/xl_dac/wbyh-stage-${APP}:${date_time}"

  16.    vpc_image_path="registry-vpc.cn-hangzhou-idc.com/wbyh-stage-${APP}:${date_time}"

  17. fi

  18.       #初始化dockerfile

  19.        init_dockerfile () {

  20.                #生成Dockerfile

  21.                cd /Rollback/build-docker/

  22.                echo "" >$MY_POD_NAMESPACE/${APP}/Dockerfile

  23.                #生成基础镜像地址

  24.                echo -e "${base_image}"  >>${MY_POD_NAMESPACE}/${APP}/Dockerfile

  25.                #生成docker作者

  26.                echo -e "MAINTAINER idea77@qq.com" >>${MY_POD_NAMESPACE}/${APP}/Dockerfile

  27.                echo -e "USER root"  >>${MY_POD_NAMESPACE}/${APP}/Dockerfile

  28.                #获取启动脚本

  29.                cp -f  start-sh/${MY_POD_NAMESPACE}-sh/${APP}.sh  $MY_POD_NAMESPACE/${APP}/

  30.                echo -e "ADD ./${APP}.sh /home/deploy/" >>${MY_POD_NAMESPACE}/${APP}/Dockerfile

  31.                #添加 jar包到/home/deploy/

  32.                echo -e "${add_jar}" >>${MY_POD_NAMESPACE}/${APP}/Dockerfile

  33.                #暴露端口

  34.                echo -e "EXPOSE 9090"  >>${MY_POD_NAMESPACE}/${APP}/Dockerfile

  35.                #添加docker入口启动文件

  36.                cp -f  start-sh/templates/docker-entrypoint.sh $MY_POD_NAMESPACE/${APP}/

  37.                echo -e "ADD ./docker-entrypoint.sh  /docker-entrypoint.sh" >>$MY_POD_NAMESPACE/$APP/Dockerfile

  38.                echo -e "RUN  chown -R deploy:deploy /home/deploy &&  chown -R deploy:deploy /docker-entrypoint.sh && ls -t --full /home/deploy " >>$MY_POD_NAMESPACE/$APP/Dockerfile

  39.                echo -e "USER deploy"  >>$MY_POD_NAMESPACE/$APP/Dockerfile

  40.                echo -e ENTRYPOINT ["/docker-entrypoint.sh"] >>$MY_POD_NAMESPACE/$APP/Dockerfile

  41.                if [[ ${MY_POD_NAMESPACE} =~ -prod ]];then

  42.                        docker images |grep xl_prod|grep ${APP}|awk {print $1":"$2}|xargs docker rmi -f

  43.                else

  44.                        docker images |grep min-test|grep ${APP}|awk {print $1":"$2}|xargs docker rmi -f

  45.                fi

  46.                name="${MY_POD_NAMESPACE},build ${image_path}-${svn_version}"

  47.                cd /Rollback/build-docker/$MY_POD_NAMESPACE/$APP/

  48.                docker build  --no-cache -t ${image_path}-${svn_version} .

  49.                check

  50.                if [[ $MY_POD_NAMESPACE =~ -stage  ]];then

  51.                        #vpc专有镜相地址修改到yml文件

  52.                        sed -i "s@registry-vpc.cn-hangzhou-idc.com/xl_public(.*)@${vpc_image_path}-${svn_version}@g"    /home/deploy/k8s-dev/${MY_POD_NAMESPACE}/app/$APP/$APP.yml

  53.                elif [[ $MY_POD_NAMESPACE =~ -test  ]];then

  54.                        sed -i "s@registry-k8s.novalocal/xl_public/(.*)@${image_path}-${svn_version}@g"    /home/deploy/k8s-dev/${MY_POD_NAMESPACE}/app/$APP/$APP.yml

  55.                fi

  56.                name="push ${APP}"

  57.                docker push  ${image_path}-${svn_version}

  58.                check

  59. }



Jenkins触发: 


build-----push------updae-deployment-----image,整个过程是流水线形式,一次性连续完成,完成后通过机器人通知到各业务组,中间有任何问题,机器人会告诉我们在哪个阶段出错,很方便排查问题,镜像的版本号根据Git或SVN的版本号来获取,然后加上当前时间戳,在jar包编译阶段版本号会写入特定文件,Jenkins会跟据当前编译的版本生成对应的Docker镜像版本。 


Kubernetes日志方案


普通虚拟机日志分散,难管理,需要登陆虚拟机一个个查看,利用Kubernetes Pod多容器策略可以很方便帮我们收集管理日志,日志方案有几种。

  1. 应用打到docker stdout前台输出,Docker输出到/var/lib/containers,通过Filebeat、Fluentd、DaemonSet组件收集,这种对于小量日志还可以,大量日志性能很差,写入很慢。

  2. Pod挂载host-path把日志打到宿主机,宿主机启动Filebeat、Fluentd、DaemonSet收集,无法判断来自哪个容器,哪个Pod和namespace空间。

  3. Pod的yml中定义两个container,同时启动一个附加的Filebeat,两个container挂载一个共享卷来收集日志。


我们用第三种方案,通过一个附加容器Filebeat来收集所有日志,filebeat–kakfa–logstash–es,自定义编译Filebeat容器镜像,为Filebeat打上podip空间service名等标签,方便识别来自哪个容器,哪个namespace,配置config-map以及yaml。

  1. filebeat----kafkacluster-----logstash----es

  2. apiVersion: v1

  3. kind: ConfigMap

  4. metadata:

  5.  namespace: dac-prod

  6.  name: dac-config-server-filebeat-config

  7. data:

  8.  filebeat.yml: |

  9.    filebeat.prospectors:

  10.    - input_type: log

  11.      fields:

  12.        namespace: dac-prod

  13.        service-name: dac-config-server

  14.        #pod-ip:

  15.      paths:

  16.        - "/mnt/*.log"

  17.      multiline:

  18.        pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}

  19.        negate: true

  20.        match: after

  21.    #output.elasticsearch:

  22.    output.kafka:

  23.      hosts: ["10.31.222.108:9092", "10.31.222.109:9092", "10.31.222.110:9092"]

  24.      topic: applog

  25.      required_acks: 1

  26.      compression: gzip

  27.    # Available log levels are: critical, error, warning, info, debug

  28.    logging.level: info

  29. ---

  30. apiVersion: v1

  31. kind: Service

  32. metadata:

  33.  name: dac-config-server

  34.  namespace: dac-prod

  35. spec:

  36.  ports:

  37.  - port: 9090

  38.    name: http

  39.  selector:

  40.    app: dac-config-server

  41. ---

  42. apiVersion: apps/v1

  43. kind: Deployment

  44. metadata:

  45.  name: dac-config-server

  46.  namespace: dac-prod

  47.  labels:

  48.    app: dac-config-server

  49. spec:

  50.  replicas: 1

  51.  strategy:

  52.    rollingUpdate:

  53.      maxSurge: 1

  54.      maxUnavailable: 1

  55.    type: RollingUpdate

  56.  selector:

  57.    matchLabels:

  58.      app: dac-config-server

  59.  template:

  60.    metadata:

  61.      labels:

  62.        app: dac-config-server

  63.    spec:

  64.      affinity:

  65.        podAntiAffinity:

  66.          requiredDuringSchedulingIgnoredDuringExecution:

  67.          - labelSelector:

  68.              matchExpressions:

  69.              - key: app

  70.                operator: In

  71.                values:

  72.                - dac-config-server

  73.            topologyKey: "kubernetes.io/hostname"

  74.      imagePullSecrets:

  75.        - name: myregistrykey

  76.      containers:

  77.      - image: registry-vpc.cn-hangzhou-idc.com/dac-prod-dac-config-server:v1

  78.       name: dac-config-server

  79.        imagePullPolicy: Always

  80.        resources:

  81.          limits:

  82.            cpu: 4000m

  83.            memory: 4096Mi

  84.          requests:

  85.            cpu: 150m

  86.            memory: 1024Mi

  87.        env:

  88.        - name: APP

  89.          value: dac-config-server

  90.      #public

  91.        - name: JAVA_OPTS

  92.          value: "-Xms4g -Xmx4g"

  93.        - name: CONTAINER_CORE_LIMIT

  94.          value: "4"

  95.        - name: POD_IP

  96.          valueFrom:

  97.            fieldRef:

  98.              fieldPath: status.podIP

  99.        - name: MY_POD_NAMESPACE

  100.          valueFrom:

  101.            fieldRef:

  102.              fieldPath: metadata.namespace

  103.        readinessProbe:

  104.          tcpSocket:

  105.            port: 9090

  106.          initialDelaySeconds: 60

  107.          timeoutSeconds: 3

  108.        livenessProbe:

  109.          tcpSocket:

  110.            port: 9090

  111.          initialDelaySeconds: 60

  112.          timeoutSeconds: 3

  113.        ports:

  114.        - name: http

  115.          containerPort: 9090

  116.        volumeMounts:

  117.        #- name: opt-data

  118.          #mountPath: /home/deploy

  119.        - name: logs

  120.          mountPath: /home/deploy/logs

  121.        - name: host-time

  122.          mountPath: /etc/localtime

  123.          readOnly: true

  124.      - image: registry-vpc.cn-hangzhou-idc.com/dac_prod/filebeat:6.0.0

  125.        name: filebeat

  126.        imagePullPolicy: Always

  127.        env:

  128.        - name: POD_IP

  129.          valueFrom:

  130.            fieldRef:

  131.              fieldPath: status.podIP

  132.        volumeMounts:

  133.        - name: logs

  134.          mountPath: /mnt

  135.        - name: filebeat-conf

  136.          mountPath: /etc/filebeat

  137.        - name: host-time

  138.          mountPath: /etc/localtime

  139.          readOnly: true

  140.      nodeSelector:

  141.        node: public

  142.      volumes:

  143.      - name: logs

  144.        emptyDir: {}

  145.      - name: filebeat-conf

  146.        configMap:

  147.          name: dac-config-server-filebeat-config

  148.      #- name: opt-data

  149.        #nfs:

  150.          #path: /home/k8s-nfs-data/public-dev-base

  151.          #server: 10.10.1.30

  152.      - name: host-time

  153.        hostPath:

  154.          path: /etc/localtime



Filebeat收集日志打上关键字标签,namespace,svc,podip等。 


Kibana集中日志展示,建立Dashboard分类,用户可以按namespce分类不同环境,过滤选择查看不同模块的应用日志。 


RBAC+二次开发Kubernetes脚本


简化kubectl 命令,提供给研发团队使用。实际上这里功能和Jenkins以及Kibana上是重复的,但是必需考虑到所有团队成员的使用感受,有人喜欢命令行,有人喜欢界面,简单好用就够。我打个比方,比如看日志,有人可能喜欢用命令行tail -f看日志,用grep过滤等,有人喜欢用Kibana看,那怎么办?于是就有了两种方案,喜欢用图形界面用Jenkins或Kibana,想用命令可以用命令操作,满足你一切需求。统一集中通过指定的机器提供给开发、测试、运维、使用,方便调试、排障。通过统一的入口可以直接对容器进行服务创建、扩容、重启、登陆、查看日志、查看Java启动参数等,方便整个团队沟通。

在这里我们通过Kubernetes RBAC授权身份认证,生成不同的证书configkey,授于不同项目组不同的管理权限,不同的项目组只有自己项目的权限。权限做了细分,不同研发、测试团队互不干扰。 


  1. [deploy@185 app]# k8s dac-test  get_all

  2. NAME                          READY     STATUS    RESTARTS   AGE       IP             NODE

  3. accountant-3536198527-dtrc9   2/2       Running   0          21h       172.20.1.5     node3.k8s.novalocal

  4. analyzer-1843296997-vz9nc     2/2       Running   0          21h       172.20.87.15   node5.k8s.novalocal

  5. api-1260757537-gxrp2          2/2       Running   0          21h       172.20.71.6    k8s-monitor.novalocal

  6. calculator-1151720239-pr69x   2/2       Running   0          21h       172.20.1.12    node3.k8s.novalocal

  7. consul-0                      1/1       Running   0          21h       172.20.87.3    node5.k8s.novalocal

  8. dispatcher-2608806384-kp433   2/2       Running   0          21h       172.20.4.6     lb1.k8s.novalocal

  9. geo-1318383076-c7th2          2/2       Running   0          5m        172.20.94.6    node6.k8s.novalocal

  10. greeter-79754259-s3bs2        2/2       Running   0          21h       172.20.19.5    jenkins-master.k8s.novalocal

  11. kafka-0                       1/1       Running   0          21h       172.20.1.4     node3.k8s.novalocal

  12. mqtt-0                        1/1       Running   0          21h       172.20.94.15   node6.k8s.novalocal

  13. mysql-0                       2/2       Running   0          21h       172.20.47.7    elk-k8sdata.novalocal

  14. pusher-2834145138-lfs21       2/2       Running   0          21h       172.20.19.6    jenkins-master.k8s.novalocal

  15. recovery-261893050-70s3w      2/2       Running   0          21h       172.20.32.13   node4.k8s.novalocal

  16. redis-0                       1/1       Running   0          21h       172.20.4.5     lb1.k8s.novalocal

  17. robot-1929938921-6lz6f        2/2       Running   0          21h       172.20.47.8    elk-k8sdata.novalocal

  18. scheduler-3437011440-rsnj6    2/2       Running   0          21h       172.20.5.10    db.k8s.novalocal

  19. valuation-2088176974-5kwbr    2/2       Running   0          21h       172.20.94.20   node6.k8s.novalocal

  20. zookeeper-0                   1/1       Running   0          21h       172.20.4.4     lb1.k8s.novalocal

  1. 注意,如何操作用户自己有权限的空间,必需填写default-namespace.conf

  2. 注意,当gitlab master分支有合并的时候,目前我们ci自动会构建编译最新的jar版本,推送至nexus仓库,k8s容器里的jar包可以指定更新

  3. k8s  init-yml        #初始化生成用户自己本人的yml文件

  4. k8s  get_all         #查看用户自己本人空间下的所有运行的容器

  5. k8s  create_all      #创建用户自己本人所有服务

  6. k8s  delall_app      #删除本人空间下所有app服务,除基础服务mysql、 consul、 kafka、 redis、 zookeeper、mqtt 以外的所有服务

  7. k8s  apply  api      #修改了用户自己本人yml配置文件,应用配置生效

  8. k8s  create api      #用户自己本人空间下创建一个api服务

  9. k8s  delete api      #用户自己本人空间下删除一个api服务

  10. k8s  scale  api 2    #用户自己本人空间下把api服务扩容成2个pod

  11. k8s  login  api      #用户本人空间下登录api所在的docker容器

  12. k8s  logs   api      #用户自己本人空间用tail -f 命令的方式查看容器内/home/deploy/api/logs/api.log 的日志

  13. k8s  error-logs api  #用户自己本人空间用tail -f 命令的方式查看容器内/home/deploy/api/logs/api.error.log 的日志

  14. k8s  clean api       #如果编译出错,在用户自己本人空间用gradlew clean清理命令的方式清理编译

  15. k8s  push_jar        #更新本人空间下所有容器的jar包版本,重启所有容器,默认拉取backend / push-envelope -git最终版本,该版本为合并编译成功后的最新版本号

  16. k8s  push_jar  20170927-1731   #选择指定的jar版本号20170927-1731 进行更新 ,重启所有容器

  17. k8s  reinit-mysql    #重新更新所有容器jar版本后api无法启动,清空用户空间下的数据库,重新创建导入数据

  18. 批量操作

  19. k8s  scale  api-geo 2 #在dev用户下把api和geo 扩容

  20. k8s  delete api-geo   #在dev用户下删除api 和geo服务

  21. k8s  create api-geo   #在dev用户下创建api和geo服务

  22. 所有人员通用命令,要操作某个用户的资源,必需先生成所需要的yml文件

  23. 但是必需指定第二个参数名dev test stage等。

  24. k8s stage init-yml       #初始化生成stage用户的yml文件 注意要操作stage用户的容器要先成配置文件

  25. k8s test init-yml        #初始化生成test空间的yml文件

  26. k8s dev init-yml         #初始化生成dev空间的yml文件

  27. k8s dev  get_all         #查看dev用户空间下的所有运行的容器

  28. k8s dev  create_all      #创建dev空间下所有服务

  29. k8s dev  delall_app       #删除dev空间下的app服务,除基础服务mysql、 consul、 kafka、 redis、 zookeeper、mqtt 以外的所有服务

  30. k8s dev  apply  api      #修改了yml配置文件,应用配置生效

  31. k8s dev  create api      #dev空间下创建一个api服务

  32. k8s dev  delete api      #dev空间下删除一个api服务

  33. k8s dev  scale  api 2    #dev空间下把api服务扩容成2个pod

  34. k8s dev  login  api      #dev空间下登录api所在的docker容器

  35. k8s dev  logs   api      #dev空间用tail -f 命令的方式查看容器内/home/deploy/api/logs/api.log 的日志

  36. k8s dev  error-logs api  #dev空间用tail -f 命令的方式查看容器内/home/deploy/api/logs/api.error.log 的日志

  37. k8s dev  push_jar        #更新dev空间下所有容器的jar包版本,重启所有容器,默认拉取backend /-git最终版本,该版本为合并编译成功后的最新版本号

  38. k8s dev  push_jar  20170927-1731   #选择指定的jar版本号20170927-1731 进行更新 ,重启所有容器

  39. k8s dev  clean api       #如果编译出错,dev用户空间用gradlew clean清理命令的方式清理编译

  40. k8s dev  reinit-mysql    #重新更新所有容器jar版本后api无法启动,清空dev空间下的数据库,重新创建导入数据

  41. 批量操作

  42. k8s dev  scale api-geo 2  #在dev空间把api和geo 扩容

  43. k8s dev  delete api-geo  #在dev空间删除api 和geo服务

  44. k8s dev  create api-geo  #在dev空间下创建api和geo服务

  45. 管理员专用命令,注意管理员第二个参数一定要填

  46. k8s dev  create_rsync    #创建dev空间的rsync配置

  47. k8s dev  create_passwd   #创建dev空间的解压密码下发密钥

  48. k8s dev  create   rbac   #创建dev空间的集群授权认证

  49. k8s dev  delete   rbac   #删除dev空间的集群授权认证

  50. k8s dev  delete_all      #删除dev空间下所有服务


Kubernetes集群规划和问题总结


1、集群资源规划request +limit+maxpods+eviction参数,需要计算好再配置,配置有问题可能导致资源利用不均衡,一部节点资源利用过高,一部节点资源利用过低。

2、Kubernetes Node节点一定要留有足够的磁盘空间,跟据Pod个数和image大小决定磁盘空间数。

3、JDK无法获取正确的CPU数,默认获取的是宿主机CPU,会致创建的线程数过多,系统崩溃,可以通过:https://github.com/obmarg/libsysconfcpus.git 解决。

  1. if [ "x$CONTAINER_CORE_LIMIT" != "x" ]; then

  2.  LIBSYSCONFCPUS="$CONTAINER_CORE_LIMIT"

  3.  if [ ${LIBSYSCONFCPUS} -lt 2 ]; then

  4.     LIBSYSCONFCPUS=2

  5.  fi

  6.  export LIBSYSCONFCPUS

  7. fi

  8. export LD_PRELOAD="/usr/local/lib/libsysconfcpus.so:$LD_PRELOAD"



4、nfs-server一定要用async,充份利用缓存加快写入速度,注意内核版本bug。

5、应用产生的日志必需要设置轮转数和大小,防止过大日志撑暴宿主机磁盘。

6、发布版本越多,随着下载镜像版本越来越多,磁盘会撑爆,合理配置kubelet image gc参数,配置gc回收优化磁盘空间。

7、Docker CE以前的版本经常会出现Docker失控,使用过程中整个节点容器无法删除,无法创建,只能重启,对业务影响很大,建议全部更新到18-CE版本,和Kubernetes容性更好。

8、节点的亲和性和反亲和Affinity一定要提前规划好,为了达到高可用目的,多副本必需配置。

9、应用异常检测,跟据实际情况配置探针ReadinessProbe、LivenessProbe防止应用假死,Kubernetes提前剔除有问题的Pod容器。

Q&A


Q:使用NFS有存在性能瓶颈或单点故障的问题吗,如何解决,对于持久化要求高的Redis应该采用哪种存储?
A:具体看你的规模数量,测试、开发环境,单节点NFS毫无压力,数据是先写到缓存内存,速度很快,我文章中的说的内核注意bug,没必要做高可用,公有云有NAS服务,不必担心,自建机房可以用drbd Keepalived vip。

Q:为什么网络没有使用Traefik,Spring Cloud的相关组件是怎么部署的,是用yaml文件还是使用Helm方式?
A:考虑到Traefik性能没有nginx好,所以用nginx,ymal是自己写的模板生成的,没有用Helm。我们正在调研,Eureka可以单独定制多个yml互相注册。与外部服务通过打通网络直通,通过SVC对接。

Q:请问下所有环境都在一个集群,压测怎么办?
A:压测只是对应用产生压力,你可以把需要压测的应用调度到不同的节点NodeSelecto隔离运行。

Q:对于局域网微信回调是如何做,没有公网IP?
A:打通网络之后,设置WIFI指向DNS为Kubernetes DNS,Service直接互通。

Q:Eureka注册时服务IP用的什么?
A:Kubernetes集群内会用的podip去注册。

Q:有状态应用的场景,使用容器部署与传统部署有啥区别,容器部署是否存在一些坑?
A:有状态容器创建后,尽量少动,少迁移,遇到过卡住,容器无法迁移或删除,重要的MySQL之类的建议放外部运行。


Kubernetes应用实战培训


Kubernetes应用实战培训将于2018年11月9日在北京开课,3天时间带你系统学习Kubernetes本次培训包括:容器特性、镜像、网络;Docker特性、架构、组件、概念、Runtime;Docker安全;Docker实践;Kubernetes架构、核心组件、基本功能;Kubernetes设计理念、架构设计、基本功能、常用对象、设计原则;Kubernetes的实践、运行时、网络、插件已经落地经验;微服务架构、DevOps等,点击下方图片查看详情。

    阅读原文

    发送中

    阅读原文

    本文标签:AutoTags插件服务端需要您提供购买者的账号和密码才能继续访问  折翼天使  莎莎源码  吾爱源码  其他源码 

    转载请注明来源:PHP手机端发卡多种支付商业版源码

    本文永久链接地址:https://www.suibianlu.com/11942.html

    郑重声明:
    本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,不存在任何商业目的与商业用途。
    若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。 我们不承担任何技术及版权问题,且不对任何资源负法律责任。
    如无法链接失效或侵犯版权,请给我们来信:admin@suibianlu.com

    栏目导航
    最新文章
    热门文章
    Top