SkaffoldとHelmを使い環境の設定を切り替えてk8sリソースをデプロイする
はじめに
以前のブログでSkaffoldのローカルでの開発機能を試しました。もちろんSkaffoldはローカルでの開発をサポートするツールにとどまらず、テストやビルド、デプロイなどもサポートしています。
デプロイをおこない場合は環境ごとの変数をうまく切り替える必要があると思いますが、SkaffoldはHelmのサポートを行っているのでそいつを使えばうまくできそうだったので試してみようかと思います。
SkaffoldはProfilesという機能を持っておりコンテキストごとのデプロイ、テスト、ビルドを切り替えることができますが、今回はその機能は使わずにTemplated Fieldsの機能を使ってやってみたいと思います。
やってみる
Java/Springのアプリケーション作ってやってみようと思います。
ローカルのクラスターはMinikube(Docker Driver)を使って作成します。
環境
今回の実行環境は以下の通りです。
$ uname -srvmpio Linux 5.4.0-80-generic #90-Ubuntu SMP Fri Jul 9 22:49:44 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a LSB Version: core-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch Distributor ID: Ubuntu Description: Ubuntu 20.04.2 LTS Release: 20.04 Codename: focal $ minikube version minikube version: v1.22.0 commit: a03fbcf166e6f74ef224d4a63be4277d017bb62e $ kubectl version -o yaml clientVersion: buildDate: "2021-03-18T01:10:43Z" compiler: gc gitCommit: 6b1d87acf3c8253c123756b9e61dac642678305f gitTreeState: clean gitVersion: v1.20.5 goVersion: go1.15.8 major: "1" minor: "20" platform: linux/amd64 serverVersion: buildDate: "2021-06-16T12:53:14Z" compiler: gc gitCommit: 092fbfbf53427de67cac1e9fa54aaa09a28371d7 gitTreeState: clean gitVersion: v1.21.2 goVersion: go1.16.5 major: "1" minor: "21" platform: linux/amd64 $ skaffold version v1.29.0
Springアプリケーションの作成
今回はアプリケーションが環境ごとの変数の文字列を読み取ってリクエスト側に返すようなアプリケーションを作成します。
Spring Initializrで以下の設定でアプリを作成します。
ダウンロードしたZipを適当なIDEなどで開いて、SkaffoldDeployApplication
を編集し以下の用にコントローラーを作成しプロパティファイルから読み込んだ値を返す用に指定しておきます。
@SpringBootApplication @RestController @PropertySource("classpath:application.properties") public class SkaffoldDeployApplication { @Value("${skaffold.env}") private String env; @GetMapping("/envval") public String env() { return env; } public static void main(String[] args) { SpringApplication.run(SkaffoldDeployApplication.class, args); } }
読み込むプロパティをapplication.proerties
に記述します。
skaffold.env=test
デフォルトではdev
の文字列が変えるようになりますが、Spring Bootではこの値を環境変数SKAFFOLD_ENV
で上書きすることができます。(変数の上書き順序に関してはこちらを確認してください’)
アプリケーションを起動して、cURLを叩いてみます。
$ mvn spring-boot:run $ curl localhost:8080/envval test
デフォルトの文字列であるdevが返ってきますね。
SkaffoldとHelmを初期化する
プロジェクトルートで、以下のコマンドでSkaffoldの初期化を行います。
$ skaffold init -k helm apiVersion: skaffold/v2beta20 kind: Config metadata: name: skaffold-deploy deploy: kubectl: manifests: - helm ? Do you want to write this configuration to skaffold.yaml? Yes Configuration skaffold.yaml was written You can now run [skaffold build] to build the artifacts or [skaffold run] to build and deploy or [skaffold dev] to enter development mode, with auto-redeploy
この際にSkkafoldはマニフェストの位置を指定してやる必要があるため-k
オプションで指定します。
このディレクトリは存在する必要なないので、とりあえず作りたい場合は適当に埋めておきます。
次にHelmの初期化を行います。
プロジェクトのルートで、以下のコマンドを実行し初期化します。
$ helm create helm Creating helm
すると以下のようなファイルが作成されます。
$ tree helm helm ├── Chart.yaml ├── charts ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ └── tests │ └── test-connection.yaml └── values.yaml
不要なファイルもいくつかありますが、今はは一旦そのままにして先に進みます。
Helmのマニフェストを書き換える
helm create
でできあがったテンプレートを書き換えたり不要なファイルを削除したりします。
今回はServiceとDeploymentテンプレートのみで構成する簡単な環境を構築しようと思います。
ingress.yaml
、serviceaccount.yaml
、test/test-connection.yaml
、_helpers.tpl
、NOTES.txt
を削除します。
次に、deployment.yaml
のテンプレートを以下のように書き換えます。
apiVersion: apps/v1 kind: Deployment metadata: name: spring-app labels: app: spring-app spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: app: spring-app template: metadata: labels: app: spring-app spec: containers: - name: spring-app image: {{ .Values.image }} ports: - name: http containerPort: {{ .Values.app.port }} protocol: TCP env: - name: SKAFFOLD_ENV value: {{ .Values.app.env }} - name: SERVER_PORT value: {{ .Values.app.port }}
変数としてSKAFFOLD_ENV
とPod数、イメージを変えられるように設定しています。
同じようにservice.yaml
も書き換えます。
apiVersion: v1 kind: Service metadata: name: spring-app labels: app: spring-app spec: type: ClusterIP ports: - port: {{ .Values.service.port }} targetPort: {{ .Values.app.port }} protocol: TCP name: http selector: app: spring-app
ここではポートだけがDevelopmentの方の設定と同じ用になるように設定しています。
次にテンプレートに対するで、デベロップメントと用とプロダクション用の2種類のvalues.yamlを用意します。
まずは、デベロップメントようのvalues-dev.yaml
です
replicaCount: 2 image: spring-app app: port: 8081 env: dev service: type: NodePort port: 8081
Podのレプリカ数を2に設定し、環境をdev
で設定しています。
imageはSkaffoldでビルドするイメージを使うようにしておきます。
また、このブログではあまり重要ではありませんが、デベロップメント環境へのデプロイということでServiceのtypeもNodePortにしています。
次に、プロダクション用のvalues-prod.yaml
を用意します。
replicaCount: 4 image: spring-app app: port: 8081 env: prod service: type: ClusterIP port: 8081
先程のデベロップメントとの違いでいえば、レプリカ数を4に変え、環境をprod
で指定しています。
また、こちらも重要ではありませんが、プロダクション環境へのデプロイということでServiceのtypeはClusterIPにしています。
Skaffold側でHelmを使うように設定する
Helmの方の設定が終わったのでSkaffoldから利用する設定を記述します。
skaffold.yaml
を以下のように書き換えます。
apiVersion: skaffold/v2beta20 kind: Config metadata: name: skaffold-deploy build: artifacts: - image: spring-app buildpacks: builder: gcr.io/buildpacks/builder:v1 deploy: helm: releases: - name: spring-app namespace: default artifactOverrides: image: spring-app chartPath: helm valuesFiles: - "{{ .VALUES_FILE }}" portForward: - resourceType: service resourceName: spring-app port: 8081
buildディレクティブではBuildpacksを使ってアプリのBuildを行っています。
ここでのイメージ名を先程のvalues-*.yamlで書いた値と合わせておきます。
今回はローカルでビルドしたイメージを使うためこのような構成にしていますが、本来的にはDockerりぽじとりを使うことになると思います。
次に、deployディレクティブですがHelmの設定を行っています。注目すべきはchartPath
とvaluesFiles
部分でそれぞれHelm ChartとValuesファイルの置き場所を指します。
valuesFiles
には"{{ .VALUES_FILE }}"
という記述をしており、これはSkaffoldのTemplated Fieldsという機能を利用しています。
これで環境変数VALUES_FILE
で指定されるvalue-*.yamlが実際のデプロイ時に使われるようになります。
最後のportForwardはk8sのPort Fowordの設定です。書いてあるとおりですが、名前がspring-app
であるServiceに対して8081:8081
でPort Forwordを行います。
これでプロジェクトの作成と諸々の設定は完了です。
アプリをデプロイする
それでは、アプリをデプロイしていきます。
Skaffoldにはdeploy
コマンドや、buildとdeployを合わせたrun
コマンドなどがありますが、今回は検証のためにPort Forwordを行いたいためdev
コマンドを使います。
まずは、デベロップメントを想定したデプロイです。
以下のコマンドを実行します。
$ VALUES_FILE=./helm/values-dev.yaml skaffold dev
初回起動に時間がかかりますが、起動すれば、アプリケーションのログが流れ始めます。
これでvalues-dev.yaml
がテンプレートに反映されたリソースがデプロイされているはずです。
cURLでリクエストを送ったり、Podの数を確認したりしてみましょう。
$ curl localhost:9000/envval dev $ kubectl get deployment/spring-app NAME READY UP-TO-DATE AVAILABLE AGE spring-app 2/2 2 2 45m
Podが2個起動され、cURLでdev
の値が返ってきているので、想定通りですね。
次にプロダクションを想定したデプロイです。
$ VALUES_FILE=./helm/values-prod.yaml skaffold dev
アプリケーションログが流れ始めたら。 先ほどと同じように確認してみます。
$ curl localhost:8081/envval prod $ kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE spring-app 4/4 4 4 114s
本番用の設定でデプロイされていますね。