本文介绍了 Kubernetes 中运用装备文件办理的最佳实践,并介绍了一些避免开发人员着手的装备文件处理技巧。

本文是作者多个 Kubernetes 改造项目经历的总结。期望经过本文能够让开发了解运维中装备文件办理需求考虑的问题,以及 Kubernetes 的完成方法,也能让运维了解 Java 运用的装备文件处理方法。

装备文件的方法论

12 Factor指的是布置到 PAAS 的运用应该具有的 12 个要素,最早由 PAAS 的先驱 Heroku 推出,现在现已被奉为云原生运用的经典。假如运用符合这 12 factor 的要求,能够起到非常好的作用。其间有几条是装备文件相关的。

装备

将装备保存到“环境”中,而不是代码、属性文件、构建或运用服务器

笔者的确遇到了以上几种状况。用代码保存装备明显不合适,不能每次装备变化时都去修改代码吧?的确有人将装备寄存到 Jenkins 上,这样做的隐含意思便是假如要更改装备,需求从头构建运用,也不合适;许多 Spring 运用将不同环境的装备保存在不同的装备文件中,权且不说添加环境或许也需求从头编译,让开发知道生产的装备也不是一个好的实践;将装备寄存在运用服务器的确是曾经常见的做法,但我的主动化运维经历告诉我,这样并不直观,也不利于主动化。

12 factor 中的环境特指“环境变量”,这在 PAAS 年代是最简略的方法。而在 Kubernetes 中,推荐运用ConfigMap来办理装备,此刻”环境“便是指的 Kubernetes ,更具体的便是 ConfigMap 。然后 Kubernetes 能够将 ConfigMap 的内容注入到运用的容器中。假如注入的内容比较简略,能够以环境变量的方法注入;假如注入的参数较多,能够将 ConfigMap 的内容变成文件,在运用运转时由 Kubernetes 注入到容器中文件体系中,运用能够按照读一般文件的方法读取,下面是一个 ConfigMap 的比如,咱们首要界说一个 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfigmap
data:
  application.yml: |-
    spring:
      datasource:
        name: test  #数据库名
        url: jdbc:mysql://localhost:3306/test #url
        username: root  #用户名
        password: 123456  #密码
        driver-class-name: com.mysql.jdbc.Driver  #数据库链接驱动

能够看到咱们界说了一个名为myconfigmap的 ConfigMap,其间有一个 key 为 application.yml 的元素,其内容便是一个 Spring Boot 的装备文件。然后,咱们能够在 Pod 中引用:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: openjdk:jre-alpine
    volumeMounts:
    - name: app-env-config
      mountPath: /apps/application.yml
      subPath: application.yml
  volumes:
  - name: app-env-config
    configMap:
      name: myconfigmap

能够看到上述装备将 ConfigMap 中 application.yml 的内容寄存到了容器的/apps/application.yml,运用能够按照一般文件方法读取 。

区别构建、发布和运转三个阶段

这是 12 factor 中另一项非常重要的要素,意义是基准代码转化为一份布置需求以下三个阶段:

  • 构建( Build )阶段是指将代码仓库转化为可履行包的进程。构建时会运用指定版别的代码,获取和打包 依靠项,编译成二进制文件和资源文件。
  • 发布( Release )阶段会将构建的结果和当前布置所需装备相结合,并能够立刻在运转环境中投入运用。
  • 运转( Run )阶段(或者说“运转时”)是指针对选定的发布版别,在履行环境中发动一系列运用程序 进程。

许多安排在搞CI/CD时会将这三个过程揉在一起,美其名曰”更加主动化”,其实这个要素重视的不是说这三个过程能不能串在一起,而是说能不能拆分分别履行。假如不能拆分,或许会有几个问题:

  • 在不同的环境发布运用时还需求从头构建,耽误时刻。
  • 不同环境构建的运用或许不同,或许会有未经测验的问题。
  • 因为没有对二进制文件与装备进行“发布(确认唯一版别号)”,所以回退或切换版别存在困难。
  • 缺少发布阶段,导致咱们无法预先对改变进行更清晰的可视化,需求在运转前进行装备修改,一方面添加了版别更新时刻,另一方面也会添加犯错的或许性。

三个过程的拆分处理了上述问题,假如运用 Kubernetes 三个过程能够选用以下方法进行拆分:

  • 构建( Build )阶段– 构建镜像。
  • 发布( Release )阶段– 修订Helm Chart(包括镜像与装备)并打版别。
  • 运转( Run )阶段– 针对 Helm Chart 按照打的版别履行 helm upgrade 。

假如咱们选用 ArgoCD 以 GitOps 的方法,那么会变成:

  • 发布( Release )阶段– 修订 ArgoCD 的 Application 界说(包括镜像以及装备)。
  • 运转( Run )阶段– 在 ArgoCD 界面上履行运转。

Helm Chart 和 ArgoCD 的篇幅太长,今后有机会再独自拿出来分享。但无论是哪个东西,一般也是利用 ConfigMap 来完成的装备文件办理。

开发环境与线上环境等价

这是一个比较广泛的原则,即期望咱们的开发环境与生产环境要尽或许的共同,然后避免环境差异形成的问题。在装备文件层面,假如不同环境的装备参数的条目附近,可是值不同很大,能够考虑将装备文件的这些差异做成 Helm Chart 的变量。

独自的装备办理东西的优缺点

就像开发范畴出现了许多不同编程语言的微服务结构一样,程序员们也发明了许多装备办理东西。例如 Nacos, ZooKeeper, Consul, Apollo 等。这些软件的确处理了大型安排中开发人员的装备办理问题,可是同微服务结构一样,当这些软件与 Kubernetes 合作运用时,或许需求做一些调整。

相对于 Kubernetes 的 ConfigMap 极其衍生东西的计划,这类装备办理东西的有一些缺少:

  • 本地开发:运用这种装备办理东西时,即便是开发一个简略的运用,也需求提早布置好装备办理服务。假如是 ConfigMap 的计划,程序员本地开发时还能够持续运用文件,而在 Kubernetes 环境中,程序能够读到咱们用 ConfigMap 装备的文件。
  • 运用发布:经过前面所说的区别“构建、发布和运转三个阶段”,咱们能够完成软件版别和装备文件的绑定,然后能够完成更高效的版别切换。假如运用外部的装备办理东西,或许需求规划某个手法完成软件版别更新与装备更新的联动。
  • 装备改变生效:假如装备办理东西的装备发生改变,假如运用设置成主动改写装备,能够完成不断服务的更新。运用装备文件也能到达相似的作用。运用也能够监听装备文件,假如 ConfigMap 的装备改变,会触发这个 ConfigMap 对应文件的改变,然后引发不断机的服务更新。并且,更好的一点是,假如运用做不到主动更新,咱们能够经过一些手法,在 ConfigMap 发生改变时主动触发服务的重启,然后使装备主动生效。

因而,假如运用假如还在运用装备文件,这不是坏事,经过 ConfigMap 咱们能够完成相似的能力,并且有或许更好用。

装备文件处理事例

又到了开发和运维部分调停时刻。前面的探针功用强烈依靠开发的支撑,假如开发提供的探针接口不对,会让勘探作用大打折扣。不过装备文件相对好一点,即便缺少开发的合作,运维也能经过一些手法完成许多方针。

在我带过的传统架构转 Kubernetes 的项目中,大多数开发部分的运用仍是比较规范的,往往微服务或运用都运用规范的装备文件。并且开发团队的领导也能从全体上剖析问题,测验从结构上做一些一致的调整,所以在 Kubernetes 层面,咱们只需求做一些惯例的装备即可。

不过,也的确有一些运用团队,或许是缺少一致的规划,装备文件出现了百家争鸣的局势,一个运用有几个、乃至几十个装备文件的状况。然后又或许是因为开发缺少强有力的技能领导者或动力,所以不能很好的合作装备文件的改造,所以咱们便只在 kubernetes 层面进行调整,力求完成前面说的“装备文件的方法论”的需求。以下便是几个事例。

Spring Boot 规范装备

Spring Boot 本身就包括了对装备文件的支撑,包括了如何将装备文件外化,假如运用很灵巧的只需求一个装备文件,咱们能够运用环境变量SPRING_CONFIG_LOCATION来指定装备文件,也能够添加命令行发动参数--spring.config.location=<configfile_path>,下面是一个实践的比如:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: openjdk:jre-alpine
    env:
      - name: SPRING_CONFIG_LOCATION
        value: file:///apps/application.yml
    volumeMounts:
    - name: app-env-config
      mountPath: /apps/application.yml
      subPath: application.yml
      readOnly: true
  volumes:
  - name: app-env-config
    configMap:
      name: myconfigmap

其间application.yml的内容,现现已过myconfigmap这个 ConfigMap 布置到了 Kubernetes。

Spring Cloud 的 bootstrap 装备

Spring Cloud 会根据 bootstrap.yml 的内容加载装备,咱们能够经过--spring.cloud.bootstarp.location=<configfile_path>指定:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: openjdk:jre-alpine
    command: ["/usr/bin/java","-Xms512M","-Xmx512M","-jar","app.jar","--spring.cloud.bootstrap.location=/apps/bootstrap.yml"]
    volumeMounts:
    - name: app-env-config
      mountPath: /apps/application.yml
      subPath: application.yml
      readOnly: true
    - name: app-env-config
      mountPath: /apps/bootstrap.yml
      subPath: bootstrap.yml
      readOnly: true
  volumes:
  - name: app-env-config
    configMap:
      name: myconfigmap

经过 Tomcat ClassPath 读取装备

咱们遇到的一个布置在 Tomcat 中的运用,它需求从 ClassPath 中读取一些装备,所以咱们测验经过 ConfigMap 中包括一份修改后的 Tomcat 的装备文件,使之能在指定的路径加载咱们的运用装备文件,这个运用装备文件也是经过 ConfigMap 注入的。首要,咱们需求预备一个修改过的catalina.properties装备文件,其间修改的行是:


shared.loader=/usr/local/tomcat/addcp/

然后,咱们运用 ConfigMap 资源包括catalina.properties文件:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ template "log4j2.fullname" . }}-env-config
data:
  log4j2.xml: |-
{{ tpl (.Files.Get .Values.log4j2.configFile) . | indent 4 }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-catalina-properties
data:
  catalina.properties: |-
{{ tpl (.Files.Get .Values.tomcat.catalinaFile) . | indent 4 }}

需求注意的是,上面的 ConfigMap 是用 Helm Chart 模板的语法。 经过.Files.Get方法,咱们能够加载文件体系里的catalina.properties文件,使之成为 ConfigMap 的一部分,然后经过 Helm 发布到 Kubernetes 里。

然后,咱们在 Pod 模板的界说里,加载这些文件:

          volumeMounts:
            - name: env-config
              mountPath: /apps/application.yml
              subPath: application.yml
            - name: log4j2-config
              mountPath: /apps/log4j2.xml
              subPath: log4j2.xml
            - name: storage-pvc
              mountPath: "/attachment"
            - name: catalina-properties
              mountPath: /usr/local/tomcat/conf/catalina.properties
              subPath: catalina.properties
            - name: setting-config
              mountPath: /usr/local/tomcat/addcp/config.setting
              subPath: config.setting

能够看到咱们界说的catalina.properties挂载到了 Tomcat 目录 。别的界说了一个名为 setting-config 的 ConfigMap,将 config.setting 挂载到了/usr/local/tomcat/addcp/目录。这样,Tomcat 发动时就会将/usr/local/tomcat/addcp/视为 ClassPath,然后运用能够读到这个装备文件。

从可履行 Jar 包的 ClassPath 读取装备

可履行 Jar 包不能指定 ClassPath,所以咱们想到的一个方法便是将装备文件动态的保存到 Jar 包里。首要,咱们需求预备一个repackage.sh脚本:

#!/bin/bash
mkdir -p ./BOOT-INF/classes/properties/
cp lib/config.setting  ./BOOT-INF/classes/properties/
jar uf app.jar BOOT-INF/classes/properties/config.setting

然后咱们经过 ConfigMap 将repackage.shconfig.setting挂载到容器中对应的目录,然后咱们需求调整容器的发动命令:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
  labels:
    apps: jar
spec:
  containers:
  - name: mypod
    image: openjdk:jre-alpine
    command: ["/bin/sh","-c"]
      args: ["sh lib/repackage.sh; /usr/bin/java -Xms512M -Xmx512M -jar app.jar --spring.cloud.bootstrap.location=/apps/lib/bootstrap.properties"]
    volumeMounts:
      - name: setting-config
        mountPath: /apps/lib/config.setting
        subPath: config.setting
      - name: repackage-shell
        mountPath: /apps/lib/repackage.sh
        subPath: repackage.sh
  volumes:
    - name: setting-config
      configMap:
        name: my-pod-setting-config
    - name: repackage-shell
      configMap:
        name: my-pod-repackage-shell

能够看到容易发动时会首要设法将装备文件注入到可履行 Jar 包中,然后再履行 Jar 包,完成了咱们运转时指定装备文件的方针。

本文在云云众生yylives.cc/)首发,欢迎大家拜访。