0%

了解 DevOps

DevOps 是指对企业文化、业务自动化和平台设计等方面进行全方位变革,从而实现迅捷、优质的服务交付,提升企业响应能力和价值。只有通过快速迭代的 IT 服务交付,这一切才能实现。DevOps可以将传统应用和最新的云原生应用与基础架构彼此相连。

DevOps简介

DevOps 就是开发(Development)、测试(QA)、运维(Operations)这三个领域的合并。
DevOps

从字面上来看,”DevOps”一词是由英文 Development(开发)和 Operations (运维)组合而成,但它所代表的理念和实践要比这广阔的多。DevOps 涵盖了安全、协作方式、数据分析等许多方面。

DevOps 强调通过一系列手段来实现既快又稳的工作流程,使每个想法(比如一个新的软件功能,一个功能增强请求或者一个 bug 修复)在从开发到生产环境部署的整个流程中,都能不断地为用户带来价值。这种方式需要开发团队和运维团队密切交流、高效协作并且彼此体谅。此外,DevOps 还要能够方便扩展,灵活部署。有了 DevOps,需求最迫切的工作就能通过自助服务和自动化得到解决;通常在标准开发环境编写代码的开发人员也可与 IT 运维人员紧密合作,加速软件的构建、测试和发布,同时保障开发成果的稳定可靠。

容器与 DevOps 有什么关系?

DevOps 可以加快一个想法从提出到部署的整个过程。DevOps 的核心在于,在应用的整个生命周期中,都要确保日常运维任务自动化和环境的标准化。容器可以提供标准化的环境,你需要一个平台来管理它们,同时提供内置的自动化功能并支持各种基础架构。

DevOps 与 CI/CD

选择支持流程的工具对于 DevOps 的成功至关重要。运维团队要跟上快速开发周期,就需要利用高度灵活的平台,并像开发团队对待代码一样,对待平台的基础架构。手动部署不仅速度慢,而且可能出错。因此,您也可通过自动化来简化平台调配和部署。

持续集成和持续部署管道(CI/CD)是实施 DevOps 的一大重要成果。CI/CD 可帮助您频繁地向客户交付应用并检验软件质量,而且只需极少的人工干预。

具体而言,CI/CD 在整个应用生命周期内(从集成和测试阶段,到交付和部署)都引入了持续自动化和持续监控,让您能够快速识别和改正问题与缺陷。这些彼此关联的实践通常被统称为“CI/CD 管道”,需要开发和运维团队以敏捷方式协同支持。

CICD简介

  • 持续集成(Continuous Integration ,CI)
  • 持续交付(Continuous Delivery)
  • 持续部署(Continuous Deploy)

参考链接

云原生CICD实现

CICD
流程:

  1. 用户本地完成开发;
  2. 代码提交到GitLab上;
  3. Gitlab收到代码提交请求后通过webhook触发Jenkins;
  4. Jenkins被触发后,首先从代码仓库拉取源码,进行构建、静态分析和单元测试,然后创建镜像推送到镜像仓库Harbor,最后调用Kubernetes API更新应用;
  5. Kubernetes从Harbor拉取最新镜像,更新应用。

在spring boot中,集成Mybatis可以使用完全注解的方式,完全不用新增任何配置文件。多条件判断,可以使用<script><set>搭配实现。

@Update

@Update("<script>update t_user " +
        "<set> " +
        "<if test='userName != null'> user_name = #{userName},</if>" +
        "<if test='userPwd != null'> user_pwd = #{userPwd},</if>" +
        "<if test='userRemark != null'> user_remark = #{userRemark},</if>" +
        "<if test='userPhone != null'> user_phone = #{userPhone},</if>" +
        "<if test='userEmail != null'> user_email = #{userEmail},</if>" +
        "</set> " +
        "where user_id = #{userId}</script>")

@Select

@Select("select id,name,description,enabled,deleted,date_created as dateCreated,last_modified as lastModified from admin_role (#{roleParam})")
//不使用 @Param 时,那么,此时 collection 需要定义为 list,否则会报错
@Select({
        "<script>",
        "SELECT * ",
        "FROM users WHERE id IN",
        "<foreach item='id' index='index' collection='ids' open='(' separator=',' close=')'>",
        "#{id}",
        "</foreach>",
        "</script>"
})
List<UserEntity> getUserById(@Param("ids") List<String> ids);

@Insert

@Insert("insert into t_alert_log (alert_name,severity,message,start_at,end_at) " +
            "values(#{alertName},#{severity},#{message},#{startAt},#{endAt})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")

@Update

@Update("update t_alert_log set status=true,remark=#{remark} where token=#{token}")

@ResultType

@ResultType(String.class)

注解方式传入表名和字段名

// 一般通过#{xxx}方式传入内容,但是表名和字段名不能通过这个方式.
// 动态传入表名和字段名用到${xxx}
@Delete("delete from ${tableName} where DATE(${columnName}) <= DATE(DATE_SUB(NOW(),INTERVAL #{interval} day));")
void delete(String tableName, String columnName, int interval);

Helm用途

做为Kubernetes的一个包管理工具,Helm具有如下功能:

  • 创建新的chart
  • chart打包成tgz格式
  • 上传chart到chart仓库或从仓库中下载chart
  • 在Kubernetes集群中安装或卸载chart
  • 管理用Helm安装的chart的发布周期
    Helm有三个重要概念:
  • chart:包含了创建Kubernetes的一个应用实例的必要信息
  • config:包含了应用发布配置信息
  • release:是一个chart及其配置的一个运行实例

Helm常用命令

操作类型 命令
添加仓库 helm repo add loki https://grafana.github.io/loki/charts
更新仓库 helm repo update
查看helm仓库列表 helm repo list
查看本地已安装的包 helm list (ls)
查看全部release(包括删除的…) helm list -a
查看helm版本 helm version
删除release helm delete loki
设置安装release名称 –name test
设置安装的namespace –namespace test
设置自定义属性 –set “loki.serviceName=loki”
从文件读取自定义属性集合 -f values.yaml
查找本地release的版本列表 helm search testapi -l
指定charts版本 –version 8.2.4
查看安装历史 helm history prometheus-operator
版本回滚 helm rollback prometheus-operator 1
打包chart helm package mychart
获取charts helm fetch stable/mysql –version 0.2.8 –untar
检查chart是否存在问题 helm lint mysql
创建一个本地仓库 helm serve –address 0.0.0.0:8879 –repo-path ./charts
创建一个chart helm create mychart
查看release状态 helm status mysql
更新release helm upgrade mysql -f mysql/values.yaml –set resources.requests.memory=1024Mi mysql
查看指定release的历史版本部署时部分配置信息 helm get –revision 1 mysql
对chart的模板和配置进行测试 helm install –dry-run –debug ./
查看release默认配置 helm inspect values stable/prometheus-operator

本文使用的istio版本:1.4.2

上章内容只是简单运行了Bookinfo示例(Istio使用【Bookinfo示例】),访问页面,Reviews虽然有三个版本,但是刷新浏览器,三个版本是随机返回。本章内容仍然使用官方样例配置,定义一些自定义路由规则。在这之前最好了解下Istio使用【CRDs】

创建所有服务的DestinationRule

kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml

定义V1V2版本8:2比重,V3不返回

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 80
    - destination:
        host: reviews
        subset: v2
      weight: 20

定义如果Header中包含jason用户返回V2版本,否则返回V3版本

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: reviews
        subset: v2
  - route:
    - destination:
        host: reviews
        subset: v3

其它可以参考samples/bookinfo/networking/目录下配置。

Bookinfo示例简介

Bookinfoistio官网示例,应用程序分为四个单独的微服务:

  • productpage。该productpage微服务调用detailsreviews微服务来填充页面。
  • details。该details微服务包含图书信息。
  • reviews。该reviews微服务包含了书评。它们调用ratings微服务。
  • ratings。该ratings微服务包含预定伴随书评排名信息。

reviews微服务有3个版本:

  • 版本v1不会调用该ratings服务。
  • 版本v2调用该ratings服务,并将每个等级显示为1到5个黑星
  • 版本v3调用该ratings服务,并将每个等级显示为1到5个红色星号

Bookinfo架构图

BookinfoIstio中架构

如果想要在Istio中运行BookinfoBookinfo本身不需要任何改动,只需要为Bookinfo的微服务注入IstioSidecar。最终架构图如下:

Bookinfo架构图

所有的微服务都与Envoy边车打包在一起,该Envoy边车拦截对服务的出/入请求,并与Istio控制面交互,提供路由、采集、实施各种策略等。

启动Bookinfo服务,参考官网

进入istio目录

[root@k8s-master istio-1.4.2]# pwd
/root/istio/istio-1.4.2

设置namespace自动注入sidecar

kubectl label namespace default istio-injection=enabled

部署bookinfo服务

kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

你也可以手动为这个yaml注入sidecar再部署,参考Istio使用【sidecar注入】

kubectl apply -f <(istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml)

确定启动完成

[root@k8s-master istio-1.4.2]# kubectl get pod
NAME                                                       READY   STATUS    RESTARTS   AGE
details-v1-74f858558f-7gx6r                                2/2     Running   0          31h
productpage-v1-8554d58bff-fwcj4                            2/2     Running   0          31h
ratings-v1-7855f5bcb9-r7z5l                                2/2     Running   0          31h
reviews-v1-59fd8b965b-jppqr                                2/2     Running   0          31h
reviews-v2-d6cfdb7d6-rx648                                 2/2     Running   0          31h
reviews-v3-75699b5cfb-qpdjm                                2/2     Running   0          31h
[root@k8s-master istio-1.4.2]# kubectl get svc
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
details       ClusterIP   10.102.10.128    <none>        9080/TCP    31h
productpage   ClusterIP   10.110.251.239   <none>        9080/TCP    31h
ratings       ClusterIP   10.99.146.247    <none>        9080/TCP    31h
reviews       ClusterIP   10.102.77.22     <none>        9080/TCP    31h

确定程序运行正常

kubectl exec -it $(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}') -c ratings -- curl productpage:9080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>

定义应用的入口网关

kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

确认网关已创建

[root@k8s-master istio-1.4.2]# kubectl get gateway
NAME               AGE
bookinfo-gateway   30h

下面可以通过Isito的入口网关来访问了,在访问前,需要确定Isito网关IP和端口

获取Istio入口网关IP和端口:参考官网

kubectl get svc istio-ingressgateway -n istio-system
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                                                                                      AGE
istio-ingressgateway   LoadBalancer   10.110.94.234   <pending>     15020:32344/TCP,80:31380/TCP,443:31390/TCP,31400:31400/TCP,15029:31933/TCP,15030:30470/TCP,15031:31361/TCP,15032:31151/TCP,15443:31081/TCP   2d2h

如果EXTERNAL-IP设置了该值,则您的环境具有可用于入口网关的外部负载平衡器。如果EXTERNAL-IP值是<none>(或永久<pending>),则您的环境不为入口网关提供外部负载平衡器。在这种情况下,您可以使用服务的节点端口来访问网关。

确定端口

  • 这里80对应的端口是Http服务的端口,映射的主机端口31380

  • 这里443对应的端口是Https服务的端口,映射的主机端口31390

确定IP

可以通过下面命令找个hostIP

[root@k8s-master istio-1.4.2]# kubectl get po -l istio=ingressgateway -n istio-system -o yaml | grep hostIP: -C3
---
      state:
        running:
          startedAt: "2019-12-24T06:46:29Z"
    hostIP: 192.168.1.212
    phase: Running
    podIP: 10.244.3.136
    qosClass: Burstable
---

也可以通过下面的命令找到Ingress部署的节点。

[root@k8s-master istio-1.4.2]# kubectl get po -l istio=ingressgateway -n istio-system -o wide
NAME                                    READY   STATUS    RESTARTS   AGE    IP             NODE     NOMINATED NODE   READINESS GATES
istio-ingressgateway-6b7bfd7459-wljhh   1/1     Running   0          2d2h   10.244.3.136   k8s-02   <none>           <none>

访问Bookinfo应用

浏览器访问刚刚或者的IP+端口+/productpage,例如我的是http://192.168.1.212:31380/productpage

Reviewer-v1
Reviewer-v2
Reviewer-v3

不停的刷新页面,可以看到返回的Reviewer是不同的版本。

简单分析下

针对samples/bookinfo/networking/bookinfo-gateway.yaml,我们可以看看默认配置做了啥。

[root@k8s-master istio-1.4.2]# cat samples/bookinfo/networking/bookinfo-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        host: productpage
        port:
          number: 9080
  • 首先创建一个Gateway,这是Istio的一个自定义资源类型(CRD),它创建了这个bookinfo应用的网关bookinfo-gateway,使用了istio默认的controller——ingressgateway,如上文,istioingress网关定义了很多类型端口,这里bookinfo-gateway使用了80端口,域名使用的通配符 *

  • 定义VirtualService,这里需要绑定刚刚创建的bookinfo-gateway,定义了匹配的URI和后台服务。

本文使用的istio版本:1.4.2

开启链路监控

....
#
# addon jaeger tracing configuration
#
tracing:
  enabled: true
....

可以看出。istio链路监控集成使用的是JaegerJaeger是什么,参考开发分布式追踪OpenTracing与Jaeger相关文档整理

查看是否启动成功

[root@k8s-master istio-1.4.2]# kubectl get po -n istio-system --show-labels | grep jaeger
istio-tracing-795c9c64c4-224wm            1/1     Running     0          47h   app=jaeger,chart=tracing,heritage=Tiller,pod-template-hash=795c9c64c4,release=istio

访问jaeger UI

[root@k8s-master istio-1.4.2]# kubectl get svc -n istio-system | grep jaeger
jaeger-agent             ClusterIP      None             <none>        5775/UDP,6831/UDP,6832/UDP                                                                                                                   47h
jaeger-collector         ClusterIP      10.110.206.207   <none>        14267/TCP,14268/TCP,14250/TCP                                                                                                                47h
jaeger-query             NodePort       10.101.53.38     <none>        16686:31944/TCP 

如上所示,默认情况下,jaeger-query类型也是ClusterIP,为了便于访问,直接修改成NodePort类型:

kubectl edit svc jaeger-query  -n istio-system

然后访问IP:31944即可。

UI预览

Jaeger UI
Jaeger UI
Jaeger UI

采集频率控制

如果所有的请求都采集监控,不管出于性能考虑还是资源考虑,都是不实际的。

pilot组件中PILOT_TRACE_SAMPLING环境变量用于控制采集率,默认为10~100),测试的时候可以改大点便于分析:

 kubectl -n istio-system get deploy istio-pilot -oyaml | grep PILOT_TRACE_SAMPLING -A5
 
--
        - name: PILOT_TRACE_SAMPLING
          value: "1"
        - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND
          value: "true"
        - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND
          value: "false"
---

监控数据采集存储

参考控制台kiali配置,其中配置了Prometheusgrafana。参考Istio链路监控和监控可视化

  • prometheus作为基础数据采集和存储方式,可以通过PromQL查询指标。
  • grafana可定制化报表展示。

grafana