DevOps 工具

DevOps 的目的是:“减少从系统更改到投入生产之间的时间,同时确保高质量”。

想要充分落地 DevOps,当然离不开软件工具和平台的支持。

当你考虑构建和运行系统时,实际上会有很多事情要做。以下是要考虑的:

  1. 获取计算机/服务器硬件
  2. 配置计算机/服务器硬件(操作系统,网络等)
  3. 监视计算机/服务器硬件
  4. 获取网络设备(负载均衡器,防火墙,路由器等)
  5. 配置网络设备
  6. 监控网络设备
  7. 构造软件
  8. 编译软件
  9. 测试软件
  10. 打包软件
  11. 部署/发布软件
  12. 监控软件

在 DevOps之前,我们曾经有四个不同的团队来完成这项工作:

  • 开发人员:他们将执行#7,#8,有时甚至是#10
  • QA团队:他们将执行#9,有时甚至执行#11
  • 系统管理员:他们将执行#1,#2,#3,#12
  • 网络管理员:他们将执行#4,#5,#6

随着 DevOps 的出现,关键是能够打破不同团队间障碍,使每个人成为一个团队的一部分,使所有系统的配置,部署和管理方式保持一致。

DevOps:构建基于 kubernetes 的 DevOps 流程 - 图1
DevOps 生态圈有丰富的软件对其进行支持,DevOps 工程师可以选择合适的工具构建 DevOps 流程。

  1. 软件存储库:用于管理软件版本的工具– Git 是当今使用最广泛的工具。
  2. 构建工具:一些软件需要先进行编译,然后才能打包或使用,传统的构建工具包括 Make,Ant,Maven 和 MSBuild。
  3. 持续集成工具:每次你将代码提交存储库时,它都会构建,部署和测试软件。这通常可以提高代码质量和减少投产时间。这个市场上最受欢迎的工具是 Jenkins,Travis,TeamCity 和 Bamboo。
  4. 代码分析器/查看工具:这些工具可查找代码格式和质量以及测试覆盖范围中的错误。SonarQube 是该领域的流行工具。
  5. 配置管理:配置管理工具和数据库,通常会存储有关硬件和软件项目信息,并提供脚本或模板来自动化常见的任务。这个领域常见的工具有:Chef,Puppet,和 Salt。
  6. 部署工具:这些工具有助于软件的部署。许多 CI 工具也是 CD 工具,可帮助部署软件。传统上,在 Ruby中,Capistrano 工具已被广泛使用。在 Java 中,Maven 被许多人使用。所有编排工具也都支持这种部署。
  7. 编排工具:这些工具可配置,协调和管理计算机系统和软件。它们通常将自动化”和“工作流”作为其服务的一部分。Kubernetes 是一种非常流行的编排工具,专注于容器。Terraform 也是一种非常流行的编排工具,它的应用范围更加广泛,包括云编排。此外,每个云提供商都拥有自己的一套工具(CloudFormation,GCP Deployment Manager 和 ARM)。
  8. 监视工具:这些工具允许监视硬件和软件。通过监视程序和日志文件等,以确保系统的运行状况。Nagios 和 Prometheus 是一种流行的监视工具。
  9. 测试工具:测试工具用于管理测试以及测试自动化,包括性能和负载测试。

许多工具跨类别,并提供两个或多个类别的功能。

如你所见,Ansible,Terraform 和云工具(AWS,GCP 和 Azure)等正尝试将部署,配置管理和服务编排结合在一起。较旧的工具集:如 Puppet,Chef 和 SaltStack 专注于配置管理和自动化,但已扩展到编排和部署。还有一些工具,例如 GitLab 和 Azure DevOps,它们试图涵盖几乎所有类别的 DevOps。

基于 kubernetes 的 CI/CD

使用 Jenkins + Gitlab + Harbor + Helm + Kubernetes 来实现一个完整的 CI/CD 流水线作业,并且所有的组件都运行在 Kubernetes 中。

我们利用 Kubernetes 来动态运行 Jenkins 的 Slave 节点,可以很好的来解决传统的 Jenkins Slave 浪费大量资源的缺点。

当前 CI/CD 的流程图:
image.png

    1. 开发人员提交代码到 Gitlab 代码仓库
    1. 通过 Gitlab 配置的 Jenkins Webhook 触发 Pipeline 自动构建
    1. Jenkins 触发构建构建任务,根据 Pipeline 脚本定义分步骤构建
    1. 先进行代码静态分析,单元测试
    1. 然后进行 Maven 构建(Java 项目)
    1. 根据构建结果构建 Docker 镜像
    1. 推送 Docker 镜像到 Harbor 仓库
    1. 触发更新服务阶段,使用 Helm 安装/更新 Release
    1. 查看服务是否更新成功。

基于 Prometheus 的监控

clipboard.png
整体流程比较简单,Prometheus 直接接收指标数据,在本地存储所有的获取的指标数据,并对这些数据进行一些规则整理,用来生成一些聚合数据或者报警信息,Grafana 用来可视化这些数据。

监控应用

应用只有提供一个 /metrics 接口暴露给 Prometheus,才能被采集到应用运行的指标数据。

  • 一些应用本身内置了一个 /metrics 接口,比如 Kubernetes 的各个组件、istio 服务网格都直接提供了数据指标接口。
  • 一些应用没有原生集成该接口,但是 Prometheus 官方或第三方可以提供一些 exporter 来获取到指标数据,比如 mysqld_exporternode_exporter,这些 exporter 就有点类似于传统监控服务中的 agent,作为服务一直存在,用来收集目标服务的指标数据然后直接暴露给 Prometheus。
  • 自己实现 /metrics ,像 Java 应用就可以添加 Prometheus jar 包来实现这个接口。

监控集群节点

我们这里通过 Prometheus 来采集节点的监控指标数据,可以通过 node_exporter 来获取,顾名思义,node_exporter 就是抓取用于采集服务器节点的各种运行指标,目前 node_exporter 支持几乎所有常见的监控点,比如 conntrack,cpu,diskstats,filesystem,loadavg,meminfo,netstat 等,详细的监控点列表可以参考其 Github 仓库

我们可以通过 DaemonSet 控制器来部署该服务,这样每一个节点都会自动运行一个这样的 Pod,如果我们从集群中删除或者添加节点后,也会进行自动扩展。

目前只是监控了 k8s 部分组件,和所有节点。应用程序需要纳入监控的话,需要应用本身可以提供 /metric 接口可以采集监控数据。

报警方式现在是通过我个人的 QQ 邮箱发邮件到我的公司邮箱,同一类报警信息间隔5小时发送邮件通知。

基于 EFK 的日志收集

Kubernetes 中比较流行的日志收集解决方案是 Elasticsearch、Fluentd 和 Kibana(EFK)技术栈,也是官方现在比较推荐的一种方案。

Elasticsearch 是一个实时的、分布式的可扩展的搜索引擎,允许进行全文、结构化搜索,它通常用于索引和搜索大量日志数据,也可用于搜索许多不同类型的文档。

Elasticsearch 通常与 Kibana 一起部署,Kibana 是 Elasticsearch 的一个功能强大的数据可视化 Dashboard,Kibana 允许你通过 web 界面来浏览 Elasticsearch 日志数据。

Fluentd是一个流行的开源数据收集器,我们将在 Kubernetes 集群节点上安装 Fluentd,通过获取容器日志文件、过滤和转换日志数据,然后将数据传递到 Elasticsearch 集群,在该集群中对其进行索引和存储。

DevOps 流程框架

image.png

业务系统迁移步骤

clipboard.png

  • 首先部署业务系统,要知道业务系统是怎样运行的,它是由哪些组件构成的;

    • 例如,业务系统是由 Java 开发,并使用 PostgreSQL 数据库,那么系统要运行起来,就需要准备一个 Java 程序运行的环境(基础镜像:openjdk:8-jre-alpine),搞定服务运行的依赖包(Maven 构建),确定访问集群外数据库 PostgreSQL 的方式,确定是否对外提供服务及服务发现策略,是否高可用。
  • 获取代码,本地运行测试是否正常

    • 例如,java springboot 项目可以在本地 mvn package 打包成 jar 包,然后本地运行 java -jar xxx.jar 看看日志启动是否报错。 ```bash

      本地测试打包是否成功

      cd springboot-web-demo mvn package cd target/

看一下 jar 中有什么文件

jar -tf springboot-web-demo-1.0-SNAPSHOT.jar

本地命令运行,测试是否成功,可以看到springboot启动日志

java -jar springboot-web-demo-1.0-SNAPSHOT.jar

  1. - 编写 Dockerfile,构建镜像, 本地执行`docker run -it xxx:xxx` 运行容器,测试镜像是否可用
  2. ```bash
  3. cd springboot-web-demo
  4. vim Dockerfile
  5. # Dockerfile文件内容:
  6. FROM openjdk:8-jre-alpine
  7. COPY target/springboot-web-demo-1.0-SNAPSHOT.jar /spring-web-demo.jar
  8. ENTRYPOINT ["java","-jar","/springboot-web-demo.jar"]
  9. # 制作镜像
  10. docker build -t springboot-web:v1 .
  11. # 测试镜像是否可用,run 一下
  12. docker run -it springboot-web:v1
  • 进入 Jenkins 创建和配置 Pipeline Job:devops-demo-springboot-web
    • 配置 构建触发器
    • 配置 流水线 ,注意 脚本路径用来指定 Pipeline 执行文件 Jenkinsfile 的路径,这里放在项目的根目录了

image.png
image.png

  • 前往 Gitlab 中配置项目 devops-demo-java 的 Webhook,settings -> Webhooks,填写上面得到的 trigger

image.png

  • 编写 Jenkinsfile 来实现具体的流水线

    1. Clone代码 -> 单元测试 -> Maven编译打包 -> Docker镜像构建/推送 -> Helm部署服务 -> isRollBack
    • 第一个阶段:单元测试。我们可以在这个阶段是运行一些单元测试或者静态代码分析的脚本,我们这里直接忽略。
    • 第二个阶段:代码编译打包。我们可以看到我们是在一个 maven 的容器中来执行的,我们只需要在该容器中获取到代码,然后在代码目录下面执行打包命令即可,如下所示:

      1. stage('代码编译打包') {
      2. try {
      3. container('maven') {
      4. echo "2.代码编译打包阶段"
      5. sh """
      6. cd springboot-web-demo
      7. mvn clean package
      8. """
      9. }
      10. } catch (exc) {
      11. println "构建失败 - ${currentBuild.fullDisplayName}"
      12. throw(exc)
      13. }
      14. }
    • 第三个阶段:构建 Docker 镜像。要构建 Docker 镜像,就需要提供镜像的名称和 tag,要推送到 Harbor 仓库,就需要提供登录的用户名和密码,所以我们这里使用到了 withCredentials 方法,在里面可以提供一个credentialsId 为 dockerhub 的认证信息,如下:

      1. stage('构建 Docker 镜像') {
      2. withCredentials([[$class: 'UsernamePasswordMultiBinding',
      3. credentialsId: 'docker-auth',
      4. usernameVariable: 'DOCKER_USER',
      5. passwordVariable: 'DOCKER_PASSWORD']]) {
      6. container('docker') {
      7. echo "3. 构建 Docker 镜像阶段"
      8. sh """
      9. docker login ${registryUrl} -u ${DOCKER_USER} -p ${DOCKER_PASSWORD}
      10. docker build -t ${image} .
      11. docker push ${image}
      12. """
      13. }
      14. }
      15. }
    • 第四个阶段:部署应用。现在镜像我们都已经推送到了 Harbor 仓库中去了,接下来就可以部署应用到 Kubernetes 集群中了,当然可以直接通过 kubectl 工具去操作 YAML 文件来部署,我们这里的示例,编写了一个 Helm Chart 模板,所以我们也可以直接通过 Helm 来进行部署,所以当然就需要一个具有 helm 命令的容器,这里我们使用 helm 这个镜像,这个镜像就是简单的将 helm 二进制文件下载下来放到 PATH 路径下面去即可。

      1. stage('运行 Helm') {
      2. withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
      3. container('helm') {
      4. sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
      5. echo "4.开始 Helm 部署"
      6. def userInput = input(
      7. id: 'userInput',
      8. message: '选择一个部署环境',
      9. parameters: [
      10. [
      11. $class: 'ChoiceParameterDefinition',
      12. choices: "Dev\nQA\nProd",
      13. name: 'Env'
      14. ]
      15. ]
      16. )
      17. echo "部署应用到 ${userInput} 环境"
      18. // 选择不同环境下面的 values 文件
      19. if (userInput == "Dev") {
      20. // 选择一个 Dev values 文件
      21. } else if (userInput == "QA"){
      22. // 选择一个 QA values 文件
      23. } else {
      24. // 选择一个 Pro values 文件
      25. }
      26. helmDeploy(
      27. debug : false,
      28. name : "${appName}",
      29. chartDir : "./helm",
      30. namespace : "${nameSpace}",
      31. valuePath : "./helm/my-values.yaml",
      32. imageTag : "${imageTag}"
      33. )
      34. echo "[INFO] Helm 部署应用成功..."
      35. }
      36. }
      37. }

      其中 helmDeploy 方法可以在全局中进行定义封装: ```groovy def helmLint(String chartDir) { println “校验 chart 模板” sh “helm lint ${chartDir}” }

def helmDeploy(Map args) { if (args.debug) { println “Debug 应用” sh “helm upgrade —dry-run —debug —install ${args.name} ${args.chartDir} -f ${args.valuePath} —set image.tag=${args.imageTag} —namespace ${args.namespace}” } else { println “部署应用” sh “helm upgrade —install ${args.name} ${args.chartDir} -f ${args.valuePath} —set image.tag=${args.imageTag} —namespace ${args.namespace}” echo “应用 ${args.name} 部署成功. 可以使用 helm status ${args.name} 查看应用状态” } }

  1. 然后由于每次我们构建的镜像 tag 都会变化,所以我们可以通过 `--set` 来动态设置。
  2. 然后去构建应用的时候,在 Helm 部署阶段就会看到 Stage View 界面出现了暂停的情况,需要我们选择一个环境来进行部署:![](https://cdn.nlark.com/yuque/0/2020/png/1471554/1599546329042-be73e268-b396-42ca-a3ac-bd3f23b9d043.png#height=527&id=RlVaO&margin=%5Bobject%20Object%5D&originHeight=702&originWidth=1590&originalType=binary&ratio=1&size=0&status=done&style=shadow&width=1193)
  3. - **第五个阶段:**快速回滚。有时候我们部署的应用即使有很多测试,但是也难免会出现一些错误,这个时候如果我们是部署到线上的话,就需要要求能够立即进行回滚,这里我们同样可以使用 Helm 来非常方便的操作,添加如下一个回滚的阶段:
  4. ```groovy
  5. stage('快速回滚?') {
  6. withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
  7. container('helm') {
  8. sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
  9. def userInput = input(
  10. id: 'userInput',
  11. message: '是否需要快速回滚?',
  12. parameters: [
  13. [
  14. $class: 'ChoiceParameterDefinition',
  15. choices: "Y\nN",
  16. name: '回滚?'
  17. ]
  18. ]
  19. )
  20. if (userInput == "Y") {
  21. sh "helm rollback ${appName} --namespace kube-ops"
  22. }
  23. }
  24. }
  25. }

最后一条完整的流水线就完成了

转载 https://www.yuque.com/xiongsanxiansheng/xu3l80/lkra5g