ローカルのKukbernetesクラスターにIngressを作成し、localhostにcurlする
ローカル環境に立ち上げた 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 | 
クラスターを作成する
Section titled “クラスターを作成する”まずはクラスターの準備をします。
kind で作成する Node は Docker コンテナなので、extraPortMappings でポートを開ける必要があります。
apiVersion: kind.x-k8s.io/v1alpha4kind: Clustername: sandboxnodes:- role: control-plane  image: kindest/node:v1.27.1  kubeadmConfigPatches:  - |    kind: InitConfiguration    nodeRegistration:      kubeletExtraArgs:        node-labels: "ingress-ready=true"  extraPortMappings:    - containerPort: 80      hostPort: 8080- role: worker  image: kindest/node:v1.27.1- role: worker  image: kindest/node:v1.27.1クラスターを作成します。
kind create cluster --config kind-confing.yamlKustomization
Section titled “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
$ tree.├── base│   ├── deployment.yaml│   ├── ingress.yaml│   ├── kustomization.yaml│   └── service.yaml└── overlays    └── development        └── kustomization.yaml参考:2) Create variants using overlays - kubectl.docs.kubernetes.io
単一ファイルの Ingress-Nginx Controller をデプロイする
Section titled “単一ファイルの 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-nginxNAME                                        READY   STATUS      RESTARTS   AGEingress-nginx-admission-create-jk67f        0/1     Completed   0          18singress-nginx-admission-patch-czs55         0/1     Completed   1          18singress-nginx-controller-6b7f45576b-gdrms   0/1     Running     0          18skustomization.yaml
Section titled “kustomization.yaml”apiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomizationnamespace: defaultresources:  - deployment.yaml  - service.yaml  - ingress.yamlapiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomizationnamespace: default
images:- name: nginx  newTag: stable-alpine3.17-slim
replicas:- name: my-app  count: 3
resources:  - ../../base参考:The Kustomization File - kubectl.docs.kubernetes.io
nginx イメージのタグが上書きされていることが確認できますね。
$ kustomize build base | grep image:      - image: nginx:latest
$ 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: 3参考:replicas - kubectl.docs.kubernetes.io
base/(resource).yaml
Section titled “base/(resource).yaml”必要なリソースを作成します。
apiVersion: apps/v1kind: Deploymentmetadata:  labels:    app: my-app  name: my-appspec:  selector:    matchLabels:      app: my-app  template:    metadata:      labels:        app: my-app    spec:      containers:      - image: nginx:latest        name: nginx        ports:        - containerPort: 80        resources: {}      restartPolicy: AlwaysapiVersion: v1kind: Servicemetadata:  name: my-appspec:  selector:    app: my-app  ports:  - protocol: TCP    port: 8080    targetPort: 80apiVersion: networking.k8s.io/v1kind: Ingressmetadata:  name: my-appspec:  rules:  - http:      paths:      - pathType: Prefix        path: /        backend:          service:            name: my-app            port:              number: 8080kustomize でアプリケーションをデプロイする
Section titled “kustomize でアプリケーションをデプロイする”$ kustomize build overlays/development | k apply -f -service/my-app createddeployment.apps/my-app createdingress.networking.k8s.io/my-app created3つの Pod を作成しました。Pod 名のサフィックスは hdrq6、tglfn、xvc5h です。
$ k get podNAME                    READY   STATUS    RESTARTS   AGEmy-app-74569ccd-hdrq6   1/1     Running   0          4m46smy-app-74569ccd-tglfn   1/1     Running   0          4m46smy-app-74569ccd-xvc5h   1/1     Running   0          4m46slocalhost: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 片付け
Section titled “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 create my-apphelm で Ingress-Nginx Controller をデプロイする
Section titled “helm で Ingress-Nginx Controller をデプロイする”$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx$ helm repo updatevalues.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 appVersionappVersion: 1.8.1以下に提示したURLの箇所を修正すれば良さそうです。
ingress-nginx の values.yaml を作成する
Section titled “ingress-nginx の values.yaml を作成する”$ helm show values ingress-nginx/ingress-nginx > ingress-nginx/values.yamlcontroller:
  ...
  hostPort:    enabled: false    enabled: true
  ...
  tolerations: []  tolerations:  - effect: NoSchedule      key: node-role.kubernetes.io/master      operator: Equal  - effect: NoSchedule    key: node-role.kubernetes.io/control-plane    operator: Equal
...
  nodeSelector:    kubernetes.io/os: linux    ingress-ready: "true"values.yaml の値が設定されています。
$ helm template ingress-nginx ingress-nginx/ingress-nginx -f ingress-nginx/values.yaml | grep -C 1 ingress-ready      nodeSelector:        ingress-ready: "true"        kubernetes.io/os: linux
$ helm template ingress-nginx ingress-nginx/ingress-nginx -f ingress-nginx/values.yaml | grep -A 6 tolerations      tolerations:        - effect: NoSchedule          key: node-role.kubernetes.io/master          operator: Equal        - effect: NoSchedule          key: node-role.kubernetes.io/control-plane          operator: Equal
$ helm template ingress-nginx ingress-nginx/ingress-nginx -f ingress-nginx/values.yaml | yq 'select(.kind=="Deployment") | .spec.template.spec.containers[].ports'- name: http  containerPort: 80  protocol: TCP  hostPort: 80- name: https  containerPort: 443  protocol: TCP  hostPort: 443- name: webhook  containerPort: 8443  protocol: TCP先ほど作成した 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 wideNAME                                        READY   STATUS    RESTARTS   AGE   IP            NODE                    NOMINATED NODE   READINESS GATESingress-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
Section titled “templates/(resource).yaml”templates/deployment.yaml
apiVersion: apps/v1kind: Deploymentmetadata:  name: {{ include "my-app.fullname" . }}  labels:    {{- include "my-app.labels" . | nindent 4 }}spec:  replicas: {{ .Values.replicaCount }}  selector:    matchLabels:      {{- include "my-app.selectorLabels" . | nindent 6 }}  template:    metadata:      {{- with .Values.podAnnotations }}      annotations:        {{- toYaml . | nindent 8 }}      {{- end }}      labels:        {{- include "my-app.selectorLabels" . | nindent 8 }}    spec:      serviceAccountName: {{ include "my-app.serviceAccountName" . }}      containers:        - name: {{ .Chart.Name }}          securityContext:            {{- toYaml .Values.securityContext | nindent 12 }}          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"          imagePullPolicy: {{ .Values.image.pullPolicy }}          ports:            - name: http              containerPort: 80              protocol: TCP          livenessProbe:            httpGet:              path: /              port: http          readinessProbe:            httpGet:              path: /              port: http          resources:            {{- toYaml .Values.resources | nindent 12 }}templates/service.yaml
apiVersion: v1kind: Servicemetadata:  name: {{ include "my-app.fullname" . }}  labels:    {{- include "my-app.labels" . | nindent 4 }}spec:  type: {{ .Values.service.type }}  ports:    - port: {{ .Values.service.port }}      targetPort: http      protocol: TCP      name: http  selector:    {{- 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}}  {{- end }}{{- end }}{{- 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{{- else -}}apiVersion: extensions/v1beta1{{- end }}kind: Ingressmetadata:  name: {{ $fullName }}  labels:    {{- include "my-app.labels" . | nindent 4 }}  {{- with .Values.ingress.annotations }}  annotations:    {{- toYaml . | nindent 4 }}  {{- end }}spec:  {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}  ingressClassName: {{ .Values.ingress.className }}  {{- end }}  {{- if .Values.ingress.tls }}  tls:    {{- range .Values.ingress.tls }}    - hosts:        {{- range .hosts }}        - {{ . | quote }}        {{- end }}      secretName: {{ .secretName }}    {{- end }}  {{- end }}  rules:    {{- range .Values.ingress.hosts }}    - host: {{ .host | quote }}      http:        paths:          {{- range .paths }}          - path: {{ .path }}            {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}            pathType: {{ .pathType }}            {{- end }}            backend:              {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}              service:                name: {{ $fullName }}                port:                  number: {{ $svcPort }}              {{- else }}              serviceName: {{ $fullName }}              servicePort: {{ $svcPort }}              {{- end }}          {{- end }}    {{- end }}{{- end }}values.yaml
Section titled “values.yaml”values.yaml
replicaCount: 3
image:  repository: nginx  pullPolicy: IfNotPresent  # Overrides the image tag whose default is the chart appVersion.  tag: ""
nameOverride: ""fullnameOverride: ""
serviceAccount:  create: false  annotations: {}  name: ""
podAnnotations: {}
securityContext: {}
service:  type: ClusterIP  port: 8080
ingress:  enabled: true  className: ""  annotations: {}  hosts:    - paths:      - path: /        pathType: Prefix
resources: {}helm でアプリケーションをデプロイする
Section titled “helm でアプリケーションをデプロイする”$ helm install my-app my-app3つの Pod を作成しました。Pod 名のサフィックスは fszgb、mz84j、swctv です。
$ k get podNAME                      READY   STATUS    RESTARTS   AGEmy-app-7c5586dd55-fszgb   1/1     Running   0          5smy-app-7c5586dd55-mz84j   1/1     Running   0          5smy-app-7c5586dd55-swctv   1/1     Running   0          5slocalhost: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 片付け
Section titled “helm 片付け”$ helm uninstall my-app$ helm uninstall ingress-nginx -n ingress-nginxクラスター削除
Section titled “クラスター削除”$ kind delete cluster --name sandboxkind + Ingress-Nginx Controller で Ingress が使える環境を Kustomization、helm で用意してみました。これで Horizontal Pod Autoscaler や Ingress の勉強にも役に立ちそうです。
今回使用したコードは blog-code/2023/07/kind-ingress においています。
Kustomize、helm それぞれで curl を実行しましたが、そのタイミングでブラウザから localhost:8080 を開くと以下の画像のようになります。
