歡迎光臨
每天分享高質量文章

使用Helm最佳化Kubernetes下的研發體驗:基礎設施即程式碼

容器即行程,Kubernetes則解決瞭如何部署和執行應用的問題。對於任何一個部署在Kubernetes的應用而言,通常都可以由幾個固定的部分組成:Ingress、Service、Deployment等。直接使用Kubernetes原生的YAML定義服務,雖然能一定程度上簡化應用的部署,但是對於大部分研發人員來說編寫和使用YAML依然是一件相對痛苦的事情。Helm應運而生,Helm作為Kubernetes下的包管理工具,對原生服務定義過程進行了增強,透過模板化,引數化的形式大大簡化使用者部署Kubernetes應用的複雜度。
本文中,筆者將以一個Spring Boot程式為例,介紹如何在軟體研髮端到端過程中是使用Helm。本文中所使用的示例程式碼可以透過GitHub[1]下載。
建立應用程式

專案採用Maven作為專案的編譯和構建工具,專案目錄結構如下:
├── README.md
├── chart
│   ├── Chart.yaml # Chart基本資訊
│   ├── charts # 依賴
│   ├── templates # Kubernetes模板
│   │   ├── NOTES.txt
│   │   ├── _helpers.tpl
│   │   ├── deployment.yaml
│   │   ├── ingress.yaml
│   │   └── service.yaml
│   └── values.yaml # 變數
├── Dockerfile # Dockerfile定義
├── entrypoint.sh # 容器的entrypoint.sh檔案
├── mvnw
├── mvnw.cmd
├── pom.xml 
├── src # 應用原始碼
│   └── main
│       └── java
│           └── hello
│               ├── Application.java
│               └── HelloController.java

該專案SCM中透過基礎實施即程式碼的方式,我們定義了應用的3大要素:應用原始碼,應用是如何構建的(Dockerfile)以及應用是如何部署的(Chart)。
構建容器映象

容器相關內容:
├── Dockerfile # Dockerfile定義
├── entrypoint.sh # 容器的entrypoint.sh檔案

為了簡化容器映象構建過程,在Dockerfile中我們採用了Multi-Stage Builds的方式構建映象,Dockerfile的具體內容如下:
# Build
FROM maven:3.5.0-jdk-8-alpine AS builder

ADD ./pom.xml pom.xml
ADD ./src src/
RUN mvn clean package

# Package
FROM java:8

COPY --from=builder target/gs-spring-boot-0.1.0.jar gs-spring-boot.jar
RUN bash -c 'touch /gs-spring-boot.jar'

ADD entrypoint.sh entrypoint.sh
RUN chmod +x entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]

在第一個階段中,我們將pom.xml以及原始碼載入到一個Maven基礎映象中,並命名為builder,透過mvn clean package命令實現Java原始碼的編譯打包,產生的jar包會儲存到容器的targets目錄下。
在第二個階段中,我們在java:8基礎映象的基礎上直接從builder容器中複製jar檔案,到當前容器中。為了能夠在容器中執行該jar檔案,這裡我們定義了一個entrypoint.sh作為容器的啟動命令,其內容如下:
#!/usr/bin/env bash
ACTIVE_PROFILE=${PROFILE:=default}
java -Xmx1024m -Djava.security.egd=file:/dev/./urandom -jar gs-spring-boot.jar --spring.profiles.active=${ACTIVE_PROFILE} $@

這裡需要註意的是在命令的最後我們添加了一個$@,該語法可以獲取命令命令列中的所有引數,這樣在後期執行容器時,可以在命令列中使用引數,改寫應用的預設配置,例如–spring.profiles.active=prod。
執行以下命令,編譯並打包應用:
$ docker build -t yunlzheng/spring-app . # 修改為自己的映象倉庫
Sending build context to Docker daemon  16.38MB
Step 1/10 : FROM maven:3.5.0-jdk-8-alpine AS builder
 ---> 67d11473f554
......
Successfully built e332622092ce
Successfully tagged yunlzheng/spring-app:latest

上傳映象到映象倉庫中(需要實現註冊容器映象服務)。
docker push yunlzheng/spring-app # 修改為自己的映象倉庫

構建Chart

透過容器映象我們為服務定義了一個隔離的執行時環境,而為了能夠讓我們的應用程式能夠執行到Kubernetes叢集當中,我們還需要定義Helm相關的內容,來標準化容器的編排和部署資訊:
├── chart
│   ├── Chart.yaml # Chart基本資訊
│   ├── charts # 依賴
│   ├── templates # Kubernetes模板
│   │   ├── NOTES.txt
│   │   ├── _helpers.tpl
│   │   ├── deployment.yaml
│   │   ├── ingress.yaml
│   │   └── service.yaml
│   └── values.yaml # 變數

在以上結構中我們定義了該應用是如何在Kubernetes叢集中執行的。在初始化應用時,使用者可以透過使用Helm命令生成以上內容:
$ helm create chart
Creating chart

Chart我們可以理解為一組Kubernetes manifest檔案的模板,Chart.yaml中包含了該Chart的基本資訊,如名稱,版本等:
apiVersion: v1
appVersion: "1.0"
description: A Spring Boot Application
name: chart
version: 0.1.0

在values.yaml中,我們定義了當前模板中所有的變數,如下所示:
replicaCount: 1

image:
  repository: yunlzheng/spring-app #修改為自己的映象
  tag: latest
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 8080 #容器對映的埠

ingress:
  enabled: true # 開啟叢集ingress
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  path: /
  hosts:
    - spring-example.local
  tls: []

templates目錄下,則是Kubernetes使用者熟悉的如deployment.yaml,service.yaml。當然你也可以根據自己的需求新增更多的模板檔案。
以deployment.yaml為例,檔案內容如下所示:
# deployment.yaml
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: {{ template "chart.fullname" . }}
  labels:
    app: {{ template "chart.name" . }}
    chart: {{ template "chart.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ template "chart.name" . }}
      release: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: {{ template "chart.name" . }}
        release: {{ .Release.Name }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP

在檔案中使用了values.yaml中定義的相關變數,如Values.replicaCount,Values.image.repository,Values.image.tag等,使用這些變數的好處是,在部署Chart的時候,我們可以在命令列中動態修改這些變數的值,例如,修改映象部署的版本等, service.yaml中的內容也是類似的,這裡就不做描述。
完成以上內容後,我們就可以將當前應用打包成一個Chart檔案,首先我們需要驗證一下Chart檔案的內容:
$ cd chart
$ helm lint
==> Linting .
[INFO] Chart.yaml: icon is recommended

1 chart(s) linted, no failures

在確認Chart格式沒有問題之後,開發人員就可以直接透過Helm部署實體到Kubernetes叢集:
$ cd chart
$ helm install .
# 省略其它輸出
==> v1/Service
NAME                        TYPE       CLUSTER-IP    EXTERNAL-IP  PORT(S)   AGE
womping-sparrow-spring-app  ClusterIP  172.19.11.41  <none>       8080/TCP  1s

NOTES:
1. Get the application URL by running these commands:
  http://spring-example.local/

開發人員訪問,並驗證應用是否按照預期執行:
釋出Chart

在確認應用能夠正常執行之後,我們就可以對Chart進行打包和釋出了。對於運維和測試人員,而言,他們只需要直接使用特定版本的應用Chart,並對其進行測試或者是部署:
$ cd ..
$ helm package chart
Successfully packaged chart and saved it to: /Users/yunlong/workspace/project-samples/containerization-spring-with-helm/chart-0.1.0.tgz

在預設情況下,helm package命令會使用Charts.yaml中檔案定義的版本。 而如果在持續整合工具中,如果我們希望每次都能動態生成一個新版本的Chart,那在打包時,可以透過–version,動態修改,從而確保每次持續整合過程都能產生一個新的版本,並且能夠對該版本進行獨立驗證。
$ helm package chart --version 0.0.2
Successfully packaged chart and saved it to: /workspace/tmp/spring-sample/chart-0.0.2.tgz

萬事具備,當然現在還沒有任何人能夠使用你構建的Chart,為了能夠讓其他人(測試、運維、or anyone)能夠使用Chart,我們需要將Chart釋出到一個公共的倉庫(Repository)當中。
Helm官方提供了一個名叫Chartmusem的開源專案,支援對接AWS S3,Google Storage,Alibaba OSS等儲存服務,使用者可透過其API上傳Chart,並且自動生成倉庫索引檔案,有精力的同學可以自行研究。
這裡我們直接使用阿裡雲效提供的Helm倉庫服務,使用者只需要註冊賬號,並開通私有倉庫服務,即可免費建立自己私有的,無容量限制的Helm倉庫。
由於透過阿裡雲效建立的Helm倉庫是私有的,因此在新增倉庫時需要透過引數–username=kHKvnX和–password=WsCH7zuHH2指定使用者名稱和密碼:
helm repo add play-helm https://repomanage.rdc.aliyun.com/helm_repositories/26125-play-helm --username=kHKvnX --password=WsCH7zuHH2

為了更好的Chart釋出體驗,Helm官方為Chartmusem提供了一個Helm Push的外掛,雲效Helm倉庫服務對該外掛進行了完整相容,因此使用者可以直接使用該外掛完成Chart的釋出:
安裝Helm Push外掛:
$ helm plugin install https://github.com/chartmuseum/helm-push
Downloading and installing helm-push v0.7.1 ...
https://github.com/chartmuseum/helm-push/releases/download/v0.7.1/helm-push_0.7.1_darwin_amd64.tar.gz
Installed plugin: push

由於已經將Helm倉庫新增到了本地,我們可以直接使用以下命令將Chart釋出到倉庫中:
$ helm push chart-0.1.0.tgz play-helm 
Pushing chart-0.1.0.tgz to play-helm...
Done.

釋出完成後重新更新本地倉庫索引:
$ helm update
...Successfully got an update from the "play-helm" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈ Happy Helming!⎈ 

搜尋play-helm倉庫並部署的Chart:
$ helm search play-helm
NAME            CHART VERSION   APP VERSION     DESCRIPTION              
play-helm/chart 0.1.0           1.0             A Spring Boot Application

$ helm install play-helm/chart

其它的小技巧

在釋出映象的時候指定版本:
$ helm push chart-0.1.0.tgz play-helm --version=0.2.0
Pushing chart-0.2.0.tgz to play-helm...
Done.

直接釋出Chart目錄:
$ helm push chart play-helm --version=0.3.0
Pushing chart-0.1.0.tgz to play-helm...
Done.

在不新增Helm倉庫的情況下直接釋出Chart:
$ helm push chart https://repomanage.rdc.aliyun.com/helm_repositories/26125-play-helm --username=kHKvnX --password=WsCH7zuHH2
Pushing chart-0.1.0.tgz to https://repomanage.rdc.aliyun.com/helm_repositories/26125-play-helm...
Done.

小結

到目前為止,我們展示瞭如何在軟體研發的端到端過程中使用Helm,透過基礎設施即程式碼的樣式,開發人員可以直接在原始碼中透過Chart定義管理應用的部署架構,在完成開發工作後開發人員只需要將Chart釋出到Helm倉庫中,接下來無論是測試,還是運維都可以直接使用Chart快速在Kubernetes叢集中對應用進行測試與釋出。
下一篇文章中,我們將會介紹如何使用Jenkins構建一條基於容器和Helm的持續交付流水線,同時介紹研發團隊中的不同角色如何圍繞Helm,圍繞持續交付流水線實現一個高效,協作的研發流程。
相關連結:
  1. https://github.com/yunlzheng/project-samples/tree/master/containerization-spring-with-helm

Kubernetes應用實戰培訓

Kubernetes應用實戰培訓將於2018年10月19日在上海開課,3天時間帶你係統學習Kubernetes本次培訓包括:容器特性、映象、網路;Docker特性、架構、元件、概念、Runtime;Docker安全;Docker實踐;Kubernetes架構、核心元件、基本功能;Kubernetes設計理念、架構設計、基本功能、常用物件、設計原則;Kubernetes的實踐、執行時、網路、外掛已經落地經驗;微服務架構、DevOps等,點選下方圖片檢視詳情。

贊(0)

分享創造快樂