微服务架构师封神之路06-一个简单例子,入门helm
- 定义一个简单的需求
- helm是干什么的?
- helm chart 的文件目录结构
- helloworld的chart实现
- Chart.yaml
- templates文件夹
- deployment.yaml
- service.yaml
- values.yaml
- 等会儿马上要用到的几个helm命令
- helm lint
- helm uninstall
- helm package
- helm install
- 修改pom.xml
- 如何通过helm命令修改chart的部署参数
定义一个简单的需求
- 用spring-boot实现一个微服务,helloworld
- 如果要了解项目的细节,可以参考我前面的两篇文章(不过要快速解决问题,直接阅读本文应该也没有问题):
- 微服务架构师封神之路01-利用minikube部署一个最简单的应用。介绍了如何创建docker image,并直接使用kubernetes命令来部署用用和创建service。
- 微服务架构师封神之路05-使用yaml文件装载kubernetes部署信息,maven实现自动部署。介绍了如何将应用的deployment和service的所有部署信息写入yaml文件,应用kubectl apply + yaml文件的方式来实现部署。
- 微服务helloworld需要一个前端的负载均衡,kubernetes service
- 用helm实现maven项目自动部署
helm是干什么的?
我们已经有了kubernetes,为什么还要费劲的使用helm?这个问题留在后面的例子里逐步来说明。
helm是建立在kubernetes层次之上,有了helm,你可以利用helm的命令来实现部署,删除等等的操作,并且可以方便的管理部署信息。
helm有三个重要的概念:
- chart. chart是一个helm package,里面包括了应用在kubernetes上部署所需要的所有信息。
- repository. 存放chart的地方,收集和共享chart信息,有点类似docker registry。
- release. A Release is an instance of a chart running in a Kubernetes cluster.
这三个概念的关系可以简单的描述为,
Helm installs charts into Kubernetes, creating a new release for each installation. And to find new charts, you can search Helm chart repositories.
更详细的信息可以参阅 helm doc
helm chart 的文件目录结构
这其中templates文件夹,Chart.yaml和values.yaml两个文件是必须的。
- Chart.yaml,用来保存chart的信息
- values.yaml,保存一些kubernetes部署所需信息的默认值,这些默认值用来替换templates目录下yaml文件中的变量。
- templates, 关于kubernetes部署的yaml文件都保存在这个目录下面。在yaml文件中定义了一些变量,形如{{ .Chart.Name }}和{{ .Values.deployment.replicas }}。{{ .Chart.Name }}是Chart.yaml文件中的name变量;{{ .Values.deployment.replicas }}就是Values.yaml中的deployment.replicas变量。
helloworld的chart实现
我们要实现微服务及其service的部署,只要将包含这两个对象kubernetes部署信息的yaml文件放入templates目录下。
先看整体的看一下目录结构,
下面我们再一个一个文件的细看。
Chart.yaml
必需文件,用来保存chart信息。
apiVersion: v1
name: @project.artifactId@
version: @project.version@
appVersion: @project.version@
description: @project.description@
keywords:
- @project.artifactId@
以下3项是必须的
apiVersion: The chart API version (required)
name: The name of the chart (required)
version: A SemVer 2 version (required)
我们在文件中使用了一些变量替换符,待会pom会使用maven-resources-plugin来拷贝这些文件到target的指定位置。这些变量在maven build的过程中会被环境变量所替换。
另外需要注意的是,一般我们定义maven替换的变量,使用类似${project.artifactId}的变量名,但是在spring-boot中,必须要使用@project.artifactId@,否则无效。
templates文件夹
我们需要定义kubernetes deployment和service,可以选择把他们定义在一个yaml文件中或分开定义两个文件。我把它们分开定义成两个文件,deployment是deployment,service是service,清晰方便一点。
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
labels:
app: {{ .Chart.Name }}
spec:
selector:
matchLabels:
app: {{ .Chart.Name }}
replicas: {{ .Values.deployment.replicas }}
template:
metadata:
labels:
app: {{ .Chart.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: {{ .Values.docker.registry }}/@project.artifactId@:@project.version@
ports:
- containerPort: {{ .Values.deployment.targetPort }}
env:
- name: JAVA_OPTS
value: -server -Xms32m -Xmx32m
可以看到里面定义了两类变量,
- {{ .Chart.xxx }} 和 {{ .Values.yyy }},前面已经说了,.Chart的值要去Chart.yaml中拿;.Values的值要去values.yaml中拿。这里要着重强调一下,为什么要定义values.yaml哪?这也是为什么需要helm的一个原因。values.yaml中定义的是它们的默认值,如果我们在使用helm部署chart的时候为这些变量指定其它的值,那么部署过程就会使用指定的这些值,而不是使用默认值。
- @xxx.yyy@,这些变量替换符号是为maven-resources-plugin准备的,它们会在maven build的过程中被替换掉,替换后的文件都放在target里的指定目录下。
service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
labels:
app: {{ .Chart.Name }}
spec:
selector:
app: {{ .Chart.Name }}
type: NodePort
ports:
- protocol: TCP
port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
nodePort: {{ .Values.service.nodePort }}
同样也有定义变量,它们的值存放在Chart.yaml或values.yaml中。
values.yaml
现在我们再来看看values.yaml。刚刚在deployment.yaml和service.yaml中定义的那些变量的值到底是多少?你自己对照这两个文件找一找。
docker:
registry: b5wang
deployment:
replicas: 1
targetPort: 9090
service:
port: 9090
targetPort: 9090
nodePort: 30090
为什么我们会把这些值定义成变量?
因为我想在不修改任何文件的条件下,通过改变helm部署命令的参数,来动态的修改部署信息。
等会儿马上要用到的几个helm命令
helm lint
用来测试chart的文件结构,格式等。
helm lint <PATH> [flags]
flags是形如–xxx的一些参数,可以用helm lint --help来获取详细信息。
在maven build中的test phase我们可以使用这个命令来对chart文件进行检查,及时报错终止build。
helm uninstall
如果helm已经部署过同名的chart要先删除,否着会报错。
helm uninstall <RELEASE_NAME> --namespace <K8S_NAMESPACE>
helm package
打包chart
helm package <PATH> -d <TARGET_DIR>
把指定的chart目录打包成versioned chart archive file,并把结果放到指定的目录。
helm install
将chart部署到kubernetes。
helm install <NAME> <CHART_PATH> [flags]
修改pom.xml
为了实现从打包chart到部署的完整流程,我们需要在pom中定义以下的操作,
- (maven-resources-plugin)拷贝制作docker image必要的文件到${project.build.directory}/docker-build
- (maven-resources-plugin)拷贝打包chart archive必要的文件到${project.build.directory}/helm-chart/${project.artifactId}
- (exec-maven-plugin)测试chart文件,如果有错误及时终止maven build - helm lint
- (exec-maven-plugin)生成部署所需的docker image - docker build
- (exec-maven-plugin)如果存在的话,删除已经部署的同名chart - helm uninstall
- (exec-maven-plugin)打包chart - helm package
- (exec-maven-plugin)部署chart - helm install
下面是完整的pom.xml,
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
</parent>
<groupId>com.b5wang.cloudlab</groupId>
<artifactId>helloworld</artifactId>
<version>1.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>helloworld</name>
<description>A sample project for micro-services with spring-boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<docker.registry>b5wang</docker.registry>
<!-- dependencies' version -->
<spring-boot-dependencies.version>2.3.0.RELEASE</spring-boot-dependencies.version>
<log4j-1.2-api.version>2.13.3</log4j-1.2-api.version>
</properties>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- =========================================================================
== Copy resources into target folder ==
========================================================================= -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions>
<!-- resources to build docker image -->
<execution>
<id>generate-docker-build</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/docker-build</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/docker</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
<!-- resources to build helm chart -->
<execution>
<id>generate-exploded-chart</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/helm-chart/${project.artifactId}</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/helm/chart</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- =========================================================================
== Execute commands ==
========================================================================= -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<!-- test helm chart files format -->
<execution>
<id>exec-helm-lint</id>
<phase>test</phase>
<configuration>
<executable>helm</executable>
<commandlineArgs>lint ${project.build.directory}/helm-chart/${project.artifactId}</commandlineArgs>
</configuration>
<goals>
<goal>exec</goal>
</goals>
</execution>
<!-- Build docker image -->
<execution>
<id>exec-docker-build</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>docker</executable>
<commandlineArgs>build --no-cache --tag ${docker.registry}/${project.artifactId}:${project.version} -f ${project.build.directory}/docker-build/Dockerfile ${project.build.directory}</commandlineArgs>
</configuration>
</execution>
<!-- delete released helm chart, ignore if no release -->
<execution>
<id>exec-helm-delete</id>
<phase>package</phase>
<configuration>
<executable>helm</executable>
<commandlineArgs>uninstall ${project.artifactId} --namespace default</commandlineArgs>
<successCodes>
<successCode>0</successCode>
<successCode>1</successCode>
</successCodes>
</configuration>
<goals>
<goal>exec</goal>
</goals>
</execution>
<!-- package helm chart -->
<execution>
<id>exec-helm-package</id>
<phase>package</phase>
<configuration>
<executable>helm</executable>
<commandlineArgs>package ${project.build.directory}/helm-chart/${project.artifactId} -d ${project.build.directory}</commandlineArgs>
</configuration>
<goals>
<goal>exec</goal>
</goals>
</execution>
<!-- release helm chart -->
<execution>
<id>exec-helm-install</id>
<phase>pre-integration-test</phase>
<configuration>
<executable>helm</executable>
<commandlineArgs>install ${project.artifactId} ${project.build.directory}/${project.artifactId}-${project.version}.tgz --wait --debug</commandlineArgs>
</configuration>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- fix error java.lang.ClassNotFoundException: org.apache.maven.doxia.siterenderer.DocumentContent -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.8.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>${log4j-1.2-api.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
</project>
现在你可以运行mvn clean install来测试一下。
如何通过helm命令修改chart的部署参数
我们在helloworld的部署配置文件(templates文件夹下的deployment.yaml和service.yaml文件)中定义了一些变量,它们的默认值是放在values.yaml中的。
比如,{{ .Values.deployment.replicas }}和{{ .Values.service.nodePort }}这两个变量,在values.yaml中它们的默认值是
deployment:
replicas: 1
service:
nodePort: 30090
我想在运行helm部署的时候修改它们的值。在target目录下找到已经打好的chart包,helloworld-1.2-SNAPSHOT.tgz。
运行如下命令,
helm install target\helloworld-1.1-SNAPSHOT.tgz --name helloworld --wait --debug --set deployment.replicas=2 --set service.nodePort=30099
这次再去minikube-dashboard看看deployment的详细信息,replicas和nodePort是不是已经变了?
这就是helm的方便之处。chart中定义了kubernetes部署应用所需要的所有信息,但它的配置不是死的,可以在应用部署的时候再决定采用那些具体的参数值。并不需要修改任何部署文件。如果你的应用很复杂,这种部署方式就显得很灵活了。
今天我们通过一个很简单的例子让大家了解了helm的基本功能。但还没有涉及到helm repository,就是指定helm chart的名字,helm在部署的时候如果本地没有找到chart会自动的去repository上面寻找所需要的chart。以后我通过自己的学习再慢慢为大家介绍。谢谢~
参考