はじめに
ローカル環境に立ち上げた Kubernetes クラスターの Pod にホスト側(ブラウザなど)からアクセスしたいとき kubectl port-forward
コマンドを実行しますが、リクエストを送信できる Pod が 1 つに決まってしまいます。
アプリケーションの動作確認なら良いのですが、リクエストが複数に分散できているか確認したい場合に不便です。
今回の記事では、ホスト側から Pod に curlできる環境を Kustomize、Helm 両方で作成します。
目標
Nginx のイメージを使って Pod を作成し、ホスト側から curl でlocalhost:8080
にリクエストできるようにする。
Pod のログを確認して、リクエストが振り分けられているか確認する。
構成図
バージョン
バージョン kind v0.19.0 kubectl gitVersion: v1.27.2 Kubernetes v1.27.1 Kustomize v5.1.0 Helm v3.8.0 stern 1.25.0
クラスターを作成する
まずはクラスターの準備をします。
kind で作成する Node は Docker コンテナなので、extraPortMappings
でポートを開ける必要があります。
apiVersion : kind.x-k8s.io/v1alpha4
image : kindest/node:v1.27.1
node-labels: "ingress-ready=true"
image : kindest/node:v1.27.1
image : kindest/node:v1.27.1
クラスターを作成します。
kind create cluster --config kind-confing.yaml
Kustomization
今回のように Nginx を立てるだけなら、1 つのファイルに Deployment や Service をまとめても良いと思います。
Group related objects into a single file whenever it makes sense. One file is often easier to manage than several. See the guestbook-all-in-one.yaml file as an example of this syntax.
関連するオブジェクトを1つのファイルにまとめる。複数のファイルよりも1つのファイルの方が管理しやすいことがよくあります。この構文の例として、guestbook-all-in-one.yaml ファイルをご覧ください。
参考:Configuration Best Practices - kubernetes.io
しかし、業務ではファイルをリソースごとに分ける場合が多いので、リソースごとにファイルを分けます。
Resources should be organized by kind, where the resource is in a file that is the lower-case hyphenized form of the Resource kind.
リソースは種類ごとに整理されるべきで、リソースは、リソースの種類を小文字のハイフンで繋げた形のファイルに格納します。
参考:Resource file naming - Kustomize Best Practices.md
参考:2) Create variants using overlays - kubectl.docs.kubernetes.io
単一ファイルの Ingress-Nginx Controller をデプロイする
kind で Ingress リソースを使用できるようにするために、ingress-nginx をデプロイします。
$ k apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
$ k get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-jk67f 0/1 Completed 0 18s
ingress-nginx-admission-patch-czs55 0/1 Completed 1 18s
ingress-nginx-controller-6b7f45576b-gdrms 0/1 Running 0 18s
kustomization.yaml
apiVersion : kustomize.config.k8s.io/v1beta1
apiVersion : kustomize.config.k8s.io/v1beta1
newTag : stable-alpine3.17-slim
参考:The Kustomization File - kubectl.docs.kubernetes.io
nginx イメージのタグが上書きされていることが確認できますね。
$ kustomize build base | grep image:
$ kustomize build overlays/development | grep image:
- image: nginx:stable-alpine3.17-slim
参考:images - kubectl.docs.kubernetes.io
Deployment の replicas も上書きできています。
$ kustomize build base | grep replicas:
$ kustomize build overlays/development | grep replicas:
参考:replicas - kubectl.docs.kubernetes.io
base/(resource).yaml
必要なリソースを作成します。
apiVersion : networking.k8s.io/v1
kustomize でアプリケーションをデプロイする
$ kustomize build overlays/development | k apply -f -
deployment.apps/my-app created
ingress.networking.k8s.io/my-app created
3つの Pod を作成しました。Pod 名のサフィックスは hdrq6
、tglfn
、xvc5h
です。
NAME READY STATUS RESTARTS AGE
my-app-74569ccd-hdrq6 1/1 Running 0 4m46s
my-app-74569ccd-tglfn 1/1 Running 0 4m46s
my-app-74569ccd-xvc5h 1/1 Running 0 4m46s
localhost:8080 に curl をし、stern を使ってログを確認します。
$ for x in {1..10} ; do curl localhost:8080 -o /dev/null -w '%{http_code}\n' -s ; sleep 1 ; done
$ stern my-app | awk '{print $1}'
意図した通りにリクエストが振り分けられたので、目標を達成しました。
kustomization 片付け
作成したリソース、ingress-nginx を削除します。
$ kustomize build overlays/development | k delete -f -
$ k delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
Helm
まずは土台となるチャートを作成しておきます。
helm で Ingress-Nginx Controller をデプロイする
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
Caution
このまま helm install
しても Ingress は使えません。
理由:
Deployment の .spec.template.spec.containers.ports.hostPort
に port が設定されていない
tolerations
が設定されていないため、control-plane に ingress-ngnix-controller がスケジュールされない
Deployment (name: ingress-nginx-controller) に ingress-ready: "true"
の nodeSelector が設定されていない
詳細:
helm install ingress-nginx/ingress-nginx -n ingress-nginx
をそのまま実行すると、ingress-nginx-controller の Pod が worker ノードにスケジュールされてしまいます。
クラスターを作成する 、では以下のように、control-plane にラベルをつけていました。
apiVersion : kind.x-k8s.io/v1alpha4
image : kindest/node:v1.27.1
node-labels: "ingress-ready=true"
そして、Kustomization のときはhttps://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
のマニフェストを使って ingress-nginx-controller をデプロイしました。
このマニフェストは Deployment (name: ingress-nginx-controller) に nodeSelector
があり ingress-ready: "true"
を設定していました。さらに tolerations
も設定されていため、Deployment に紐づく Pod が control-plane に配置され、Ingress が使用できました。
$ curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml | yq 'select(.kind=="Deployment") | .spec.template.spec.containers[].ports'
$ curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml | grep -A 6 tolerations
key: node-role.kubernetes.io/master
key: node-role.kubernetes.io/control-plane
$ curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml | grep -C 1 ingress-ready
helm template
で確認すると、設定がありません。
$ helm template ingress-nginx ingress-nginx/ingress-nginx | grep ingress-ready
$ helm template ingress-nginx ingress-nginx/ingress-nginx | grep -A 6 tolerations
$ helm template ingress-nginx ingress-nginx/ingress-nginx | yq 'select(.kind=="Deployment") | .spec.template.spec.containers[].ports'
values.yaml
を作成し、controller の nodeSelector
や tolerations
の設定をします。
appVersionが 1.8.1
なので、chart がある url は ingress-nginx/charts/ingress-nginx - github.com です。
$ helm show chart ingress-nginx/ingress-nginx | grep appVersion
以下に提示したURLの箇所を修正すれば良さそうです。
https://github.com/kubernetes/ingress-nginx/blob/652a80042222f881ce56b3b689498e2b6fe4de72/charts/ingress-nginx/values.yaml#L80-L82
https://github.com/kubernetes/ingress-nginx/blob/652a80042222f881ce56b3b689498e2b6fe4de72/charts/ingress-nginx/values.yaml#L194-L198
https://github.com/kubernetes/ingress-nginx/blob/652a80042222f881ce56b3b689498e2b6fe4de72/charts/ingress-nginx/values.yaml#L262-L263
ingress-nginx の values.yaml を作成する
$ helm show values ingress-nginx/ingress-nginx > ingress-nginx/values.yaml
key : node-role.kubernetes.io/master
key : node-role.kubernetes.io/control-plane
values.yaml
の値が設定されています。
$ helm template ingress-nginx ingress-nginx/ingress-nginx -f ingress-nginx/values.yaml | grep -C 1 ingress-ready
$ helm template ingress-nginx ingress-nginx/ingress-nginx -f ingress-nginx/values.yaml | grep -A 6 tolerations
key: node-role.kubernetes.io/master
key: node-role.kubernetes.io/control-plane
$ helm template ingress-nginx ingress-nginx/ingress-nginx -f ingress-nginx/values.yaml | yq 'select(.kind=="Deployment") | .spec.template.spec.containers[].ports'
先ほど作成した values.yaml
を使ってデプロイします。
$ helm install ingress-nginx ingress-nginx/ingress-nginx -n ingress-nginx --create-namespace -f ingress-nginx/values.yaml
問題なくデプロイできました。
$ k get pod -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-controller-6cf94958b4-n47q9 1/1 Running 0 37s 10.244.0.16 sandbox-control-plane <none> <none>
参考: https://github.com/kubernetes-sigs/kind/issues/1693#issuecomment-1060872664
templates/(resource).yaml
templates/deployment.yaml
name : {{ include "my-app.fullname" . }}
{{- include "my-app.labels" . | nindent 4 }}
replicas : {{ .Values.replicaCount }}
{{- include "my-app.selectorLabels" . | nindent 6 }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- include "my-app.selectorLabels" . | nindent 8 }}
serviceAccountName : {{ include "my-app.serviceAccountName" . }}
- name : {{ .Chart.Name }}
{{- toYaml .Values.securityContext | nindent 12 }}
image : "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy : {{ .Values.image.pullPolicy }}
{{- toYaml .Values.resources | nindent 12 }}
templates/service.yaml
name : {{ include "my-app.fullname" . }}
{{- include "my-app.labels" . | nindent 4 }}
type : {{ .Values.service.type }}
- port : {{ .Values.service.port }}
{{- include "my-app.selectorLabels" . | nindent 4 }}
templates/ingress.yaml
{{- if .Values.ingress.enabled - }}
{{- $fullName := include "my-app.fullname" . - }}
{{- $svcPort := .Values.service.port - }}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion - }}
apiVersion : networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion - }}
apiVersion : networking.k8s.io/v1beta1
apiVersion : extensions/v1beta1
{{- include "my-app.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
{{- toYaml . | nindent 4 }}
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName : {{ .Values.ingress.className }}
{{- if .Values.ingress.tls }}
{{- range .Values.ingress.tls }}
secretName : {{ .secretName }}
{{- range .Values.ingress.hosts }}
- host : {{ .host | quote }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType : {{ .pathType }}
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
serviceName : {{ $fullName }}
servicePort : {{ $svcPort }}
values.yaml
values.yaml
# Overrides the image tag whose default is the chart appVersion.
helm でアプリケーションをデプロイする
$ helm install my-app my-app
3つの Pod を作成しました。Pod 名のサフィックスは fszgb
、mz84j
、swctv
です。
NAME READY STATUS RESTARTS AGE
my-app-7c5586dd55-fszgb 1/1 Running 0 5s
my-app-7c5586dd55-mz84j 1/1 Running 0 5s
my-app-7c5586dd55-swctv 1/1 Running 0 5s
localhost:8080 に curl をし、stern を使ってログを確認します。
$ for x in {1..10} ; do curl localhost:8080 -o /dev/null -w '%{http_code}\n' -s ; sleep 1 ; done
$ stern my-app -e kube-probe | awk '{print $1}'
意図した通りにリクエストが振り分けられたので、目標を達成しました。
helm 片付け
$ helm uninstall ingress-nginx -n ingress-nginx
クラスター削除
$ kind delete cluster --name sandbox
まとめ
kind + Ingress-Nginx Controller で Ingress が使える環境を Kustomization、helm で用意してみました。これで Horizontal Pod Autoscaler や Ingress の勉強にも役に立ちそうです。
今回使用したコードは blog-code/2023/07/kind-ingress においています。
Kustomize、helm それぞれで curl を実行しましたが、そのタイミングでブラウザから localhost:8080
を開くと以下の画像のようになります。