Skip to content

ローカルのKukbernetesクラスターにIngressを作成し、localhostにcurlする

はじめに

ローカル環境に立ち上げた Kubernetes クラスターの Pod にホスト側(ブラウザなど)からアクセスしたいとき kubectl port-forward コマンドを実行しますが、リクエストを送信できる Pod が 1 つに決まってしまいます。
アプリケーションの動作確認なら良いのですが、リクエストが複数に分散できているか確認したい場合に不便です。
今回の記事では、ホスト側から Pod に curlできる環境を Kustomize、Helm 両方で作成します。

目標

  1. Nginx のイメージを使って Pod を作成し、ホスト側から curl でlocalhost:8080 にリクエストできるようにする。
  2. Pod のログを確認して、リクエストが振り分けられているか確認する。

構成図

blue-print

バージョン

バージョン
kindv0.19.0
kubectlgitVersion: v1.27.2
Kubernetesv1.27.1
Kustomizev5.1.0
Helmv3.8.0
stern1.25.0

クラスターを作成する

まずはクラスターの準備をします。

kind で作成する Node は Docker コンテナなので、extraPortMappings でポートを開ける必要があります。

kind-config.yaml
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
name: sandbox
nodes:
- 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

クラスターを作成します。

Terminal window
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

Terminal window
$ 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 をデプロイする

kind で Ingress リソースを使用できるようにするために、ingress-nginx をデプロイします。

Terminal window
$ 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

base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: default
resources:
- deployment.yaml
- service.yaml
- ingress.yaml
overlays/development/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: 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 イメージのタグが上書きされていることが確認できますね。

Terminal window
$ 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 も上書きできています。

Terminal window
$ kustomize build base | grep replicas:
$ kustomize build overlays/development | grep replicas:
replicas: 3

参考:replicas - kubectl.docs.kubernetes.io

base/(resource).yaml

必要なリソースを作成します。

base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-app
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- image: nginx:latest
name: nginx
ports:
- containerPort: 80
resources: {}
restartPolicy: Always
base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 8080
targetPort: 80
base/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
spec:
rules:
- http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: my-app
port:
number: 8080

kustomize でアプリケーションをデプロイする

Terminal window
$ kustomize build overlays/development | k apply -f -
service/my-app created
deployment.apps/my-app created
ingress.networking.k8s.io/my-app created

3つの Pod を作成しました。Pod 名のサフィックスは hdrq6tglfnxvc5h です。

Terminal window
$ k get pod
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 を使ってログを確認します。

Terminal window
$ 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}'

意図した通りにリクエストが振り分けられたので、目標を達成しました。 kustomize-curl-localhost

kustomization 片付け

作成したリソース、ingress-nginx を削除します。

Terminal window
$ 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

まずは土台となるチャートを作成しておきます。

Terminal window
$ helm create my-app

helm で Ingress-Nginx Controller をデプロイする

Terminal window
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
$ helm repo update

values.yaml を作成し、controller の nodeSelectortolerations の設定をします。

appVersionが 1.8.1 なので、chart がある url は ingress-nginx/charts/ingress-nginx - github.com です。

Terminal window
$ helm show chart ingress-nginx/ingress-nginx | grep appVersion
appVersion: 1.8.1

以下に提示した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 を作成する

Terminal window
$ helm show values ingress-nginx/ingress-nginx > ingress-nginx/values.yaml
ingress-nginx/values.yaml
controller:
...
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 の値が設定されています。

Terminal window
$ 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 を使ってデプロイします。

Terminal window
$ helm install ingress-nginx ingress-nginx/ingress-nginx -n ingress-nginx --create-namespace -f ingress-nginx/values.yaml

問題なくデプロイできました。

Terminal window
$ 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
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
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
templates/service.yaml
apiVersion: v1
kind: Service
metadata:
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
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: Ingress
metadata:
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 }}

Tips: helm のテンプレートは変数を定義できます。

{{- $fullName := include "my-app.fullname" . -}}

Variables - helm.sh

values.yaml

values.yaml
valus.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 でアプリケーションをデプロイする

Terminal window
$ helm install my-app my-app

3つの Pod を作成しました。Pod 名のサフィックスは fszgbmz84jswctv です。

Terminal window
$ k get pod
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 を使ってログを確認します。

Terminal window
$ 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-curl-localhost

helm 片付け

Terminal window
$ helm uninstall my-app
$ helm uninstall ingress-nginx -n ingress-nginx

クラスター削除

Terminal window
$ 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 を開くと以下の画像のようになります。 nginx.webp