IT

KIND 소개와 일부 예제

p1p2 2024. 9. 8. 03:22

Kubernetes IN Docker (KIND)

배경

쿠버네티스 실습을 하려고 하는데, 윈도우에서는 현재 hyperv를 사용하고 있고, 맥의 사양이 좋지 못해 환경 구성이 쉽지 않았으나 윈도우에서 실습환경을 구성하는 것보다는 맥에서 환경을 구성하는 것으로 결정하고 저 멀리 바라보던 kind를 드디어 써 봐야 할 때가 왔다고 느낌

Cloudneta 스터디 KANS에서 “mac : k8s 실습 환경 구성 with kind”을 통해 제대로 된 환경 구축을 해보려고 함

Kind?

https://kind.sigs.k8s.io/

k8s 클러스터를 Docker 컨테이너 내에서 실행할 수 있도록 하는 도구.

로컬 개발 환경에서 가볍고 빠르게 Kubernetes 클러스터를 배포하고 테스트 가능

kind achitecture

kind는 쿠버네티스 노드를 도커 컨테이너를 사용해서 간단하게 클러스터를 구축 할 수 있게 해줌.

설정이 간단하고, 클러스터를 바로 생성 및 삭제 가능. 실환경과 비슷한 구성을 만드는데 최적.

Kind 설치

아키텍처 확인

~ ❯ sysctl -a |grep -i brand
machdep.cpu.brand_string: Apple M1
~ ❯ arch
arm64

자신과 맞는 환경의 CPU architecture를 선택 해야함.

https://hub.docker.com/_/ubuntu

도커 데스크탑 설치

https://docs.docker.com/desktop/install/mac-install/

kind 툴 설치

kind를 설치할때 gh가 없으면 설치가 안될 수 있음.

kind, kubectl, helm 설치

# Install Kind
**brew install kind gh**
kind --version

# Install kubectl
**brew install kubernetes-cli**
kubectl version --client=true

# Install Helm
**brew install helm**
helm version

kind cluster 생성

필요 경로를 생성하고 KUBECONFIG 환경변수를 설정하고 cluster 생성

mkdir -p ~/study/kind/
export KUBECONFIG=~/study/kind/config
**kind create cluster**

클러스터 배포 확인

~/study/kind ❯ kind get clusters

kind
~/study/kind ❯ kind get nodes

kind-control-plane
~/study/kind ❯ kubectl cluster-info

Kubernetes control plane is running at https://127.0.0.1:50201
CoreDNS is running at https://127.0.0.1:50201/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

~/study/kind ❯ kubectl get node -o wide

NAME                 STATUS   ROLES           AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION        CONTAINER-RUNTIME
kind-control-plane   Ready    control-plane   63s   v1.31.0   172.18.0.2    <none>        Debian GNU/Linux 12 (bookworm)   5.15.49-linuxkit-pr   containerd://1.7.18

~/study/kind ❯ kubectl get pod -A

NAMESPACE            NAME                                         READY   STATUS    RESTARTS   AGE
kube-system          coredns-6f6b679f8f-ng62x                     1/1     Running   0          70s
kube-system          coredns-6f6b679f8f-zk9rg                     1/1     Running   0          70s
kube-system          etcd-kind-control-plane                      1/1     Running   0          78s
kube-system          kindnet-s6f5w                                1/1     Running   0          70s
kube-system          kube-apiserver-kind-control-plane            1/1     Running   0          77s
kube-system          kube-controller-manager-kind-control-plane   1/1     Running   0          78s
kube-system          kube-proxy-vs8sg                             1/1     Running   0          70s
kube-system          kube-scheduler-kind-control-plane            1/1     Running   0          77s
local-path-storage   local-path-provisioner-57c5987fd4-j8s9d      1/1     Running   0          70s
~/study/kind ❯ kubectl get componentstatuses

Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE   ERROR
controller-manager   Healthy   ok        
scheduler            Healthy   ok        
etcd-0               Healthy   ok    

nginx pod 생성 테스트

kind클러스터 위에 실제 nginx pod 생성.

~/study/kind ❯ kubectl run nginx --image=nginx:alpine
pod/nginx created

~/study/kind ❯ kubectl get pod -owide

NAME    READY   STATUS    RESTARTS   AGE   IP           NODE                 NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          18s   10.244.0.5   kind-control-plane   <none>           <none>
~/study/kind ❯ kubectl describe node | grep Taints

Taints:             <none>

kind는 기본적으로 kind create cluster 명령어를 사용하면 싱글 노드 클러스터가 생성되며 control plane + worker node 역할을 동시에 수행함.

싱글 노드 클러스터에는 taints 설정이 없으므로 파드가 직접 배포 될 수 있음

https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/

KIND로 특정 버전 클러스터 배포

# 클러스터 배포 : 노드 이미지 버전과 sha256 같이 맞지 않을 경우 워커 노드가 제대로 join 되지 않는다
cat <<EOT> **kind-v131.yaml**
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  image: **kindest/node:v1.31.0@sha256:53df588e04085fd41ae12de0c3fe4c72f7013bba32a20e7325357a1ac94ba865**
- role: worker
  image: **kindest/node:v1.31.0@sha256:53df588e04085fd41ae12de0c3fe4c72f7013bba32a20e7325357a1ac94ba865**
EOT

kind create cluster --config **kind-v131.yaml**

# 노드 버전 확인 : 워커노드가 join 시 시간이 조금 소용된다. 기다리면 잘 join 완료 된다
**kubectl get nodes**

kind 레포의 releases 페이지에서 아래 형태의 image를 사용한다 sha256도 포함

v1.31.0: kindest/node:v1.31.0@sha256:53df588e04085fd41ae12de0c3fe4c72f7013bba32a20e7325357a1ac94ba865
v1.30.4: kindest/node:v1.30.4@sha256:976ea815844d5fa93be213437e3ff5754cd599b040946b5cca43ca45c2047114
v1.30.3: kindest/node:v1.30.3@sha256:bf91e1ef2f7d92bb7734b2b896b3dddea98f0496b34d96e37dd5d7df331b7e56
v1.29.8: kindest/node:v1.29.8@sha256:d46b7aa29567e93b27f7531d258c372e829d7224b25e3fc6ffdefed12476d3aa
v1.29.7: kindest/node:v1.29.7@sha256:f70ab5d833fca132a100c1f95490be25d76188b053f49a3c0047ff8812360baf
v1.28.13: kindest/node:v1.28.13@sha256:45d319897776e11167e4698f6b14938eb4d52eb381d9e3d7a9086c16c69a8110
v1.28.12: kindest/node:v1.28.12@sha256:fa0e48b1e83bb8688a5724aa7eebffbd6337abd7909ad089a2700bf08c30c6ea
v1.27.16: kindest/node:v1.27.17@sha256:3fd82731af34efe19cd54ea5c25e882985bafa2c9baefe14f8deab1737d9fabe
v1.26.15: kindest/node:v1.26.15@sha256:1cc15d7b1edd2126ef051e359bf864f37bbcf1568e61be4d2ed1df7a3e87b354
v1.25.16: kindest/node:v1.25.16@sha256:6110314339b3b44d10da7d27881849a87e092124afab5956f2e10ecdb463b025

https://github.com/kubernetes-sigs/kind/releases

KIND로 멀티 노드 클러스터 생성

2 노드 클러스터를 생성하는 예시

control plane 1, worker node 1 구성으로 배포

아래 설정파일에는 control plane과 worker node가 하나씩 정의되어 있음. worker node에는 extraPortMapping이 설정되어 있어 클러스터 내부의 특정 포트를 호스트 포트에 매핑.

cat <<EOT> **kind-2node.yaml**
# two node (one workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
**- role: control-plane
- role: worker**
  extraPortMappings:
  - **containerPort: 31000
    hostPort: 31000**
    listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0"
    protocol: tcp # Optional, defaults to tcp
  - **containerPort: 31001
    hostPort: 31001**
EOT

CLUSTERNAME=myk8s
kind create cluster --config **kind-2node.yaml** --name $CLUSTERNAME

클러스터 배포 확인

정상 배포 된 부분 확인

~/study/kind ❯ kind get clusters
myk8s
~/study/kind ❯ kind get nodes --name $CLUSTERNAME

myk8s-control-plane
myk8s-worker

~/study/kind ❯ kubectl get nodes -o wide

NAME                  STATUS   ROLES           AGE     VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION        CONTAINER-RUNTIME
myk8s-control-plane   Ready    control-plane   2m59s   v1.31.0   172.18.0.3    <none>        Debian GNU/Linux 12 (bookworm)   5.15.49-linuxkit-pr   containerd://1.7.18
myk8s-worker          Ready    <none>          2m48s   v1.31.0   172.18.0.2    <none>        Debian GNU/Linux 12 (bookworm)   5.15.49-linuxkit-pr   containerd://1.7.18

nginx deployment & service 배포

디플로이먼트에는 replicas:2로 설정되어 2개의 pod로 배포하고 80포트를 열어줌.

서비스에는 외부 접근 및 내부 포트는 80으로 설정하고 클러스터 외부에서 접근 가능하도록 nodeport는 31001 로 설정

# 디플로이먼트와 서비스 배포
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: **Deployment**
metadata:
  name: **deploy-websrv**
spec:
  replicas: 2
  selector:
    matchLabels:
      app: deploy-websrv
  template:
    metadata:
      labels:
        app: deploy-websrv
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: deploy-websrv
        image: **nginx:alpine**
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: **Service**
metadata:
  name: **deploy-websrv**
spec:
  ports:
    - name: svc-webport
      port: 80
      targetPort: 80
      nodePort: **31001**
  selector:
    app: deploy-websrv
  type: NodePort
EOF

정상 동작 확인

삭제

**kubectl delete deploy,svc deploy-websr**

docker image 를 kind로 주입

이미지 생성

custom nginx 이미지를 생성해서 kind로 이미지를 주입.

dockerfile로 build 후

# index.html 파일 작성
echo "Hello myk8s" > index.html

# Dockerfile 파일 작성
**cat << EOF > Dockerfile**
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
**EOF**

docker build -t m**mweb:1.0** .
docker images

이미지 주입

현재 kind 내에 이미지가 존재 하지 않는 사실 확인 후 위에서 생성한 이미지를 kind load 명령어로 이미지를 주입하고 배포


**~/study/kind ❯ docker exec -it $CLUSTERNAME-control-plane crictl images                               ryusstory@Rucas-MacBookPro

IMAGE                                           TAG                  IMAGE ID            SIZE
docker.io/kindest/kindnetd                      v20240813-c6f155d6   6a23fa8fd2b78       33.3MB
docker.io/kindest/local-path-helper             v20230510-486859a6   d022557af8b63       2.92MB
docker.io/kindest/local-path-provisioner        v20240813-c6f155d6   282f619d10d4d       17.4MB
registry.k8s.io/coredns/coredns                 v1.11.1              2437cf7621777       16.5MB
registry.k8s.io/etcd                            3.5.15-0             27e3830e14027       66.5MB
registry.k8s.io/kube-apiserver-arm64            v1.31.0              add78c37da6e2       92.6MB
registry.k8s.io/kube-apiserver                  v1.31.0              add78c37da6e2       92.6MB
registry.k8s.io/kube-controller-manager-arm64   v1.31.0              c50d473e11f63       86.9MB
registry.k8s.io/kube-controller-manager         v1.31.0              c50d473e11f63       86.9MB
registry.k8s.io/kube-proxy-arm64                v1.31.0              c573e1357a14e       95.9MB
registry.k8s.io/kube-proxy                      v1.31.0              c573e1357a14e       95.9MB
registry.k8s.io/kube-scheduler-arm64            v1.31.0              8377f1e14db4c       67MB
registry.k8s.io/kube-scheduler                  v1.31.0              8377f1e14db4c       67MB
registry.k8s.io/pause                           3.10                 afb61768ce381       268kB
~ ❯ docker exec -it $CLUSTERNAME-worker crictl images                                                 ryusstory@Rucas-MacBookPro

IMAGE                                           TAG                  IMAGE ID            SIZE
docker.io/kindest/kindnetd                      v20240813-c6f155d6   6a23fa8fd2b78       33.3MB
docker.io/kindest/local-path-helper             v20230510-486859a6   d022557af8b63       2.92MB
docker.io/kindest/local-path-provisioner        v20240813-c6f155d6   282f619d10d4d       17.4MB
docker.io/library/nginx                         alpine               70594c812316a       19.6MB
registry.k8s.io/coredns/coredns                 v1.11.1              2437cf7621777       16.5MB
registry.k8s.io/etcd                            3.5.15-0             27e3830e14027       66.5MB
registry.k8s.io/kube-apiserver-arm64            v1.31.0              add78c37da6e2       92.6MB
registry.k8s.io/kube-apiserver                  v1.31.0              add78c37da6e2       92.6MB
registry.k8s.io/kube-controller-manager-arm64   v1.31.0              c50d473e11f63       86.9MB
registry.k8s.io/kube-controller-manager         v1.31.0              c50d473e11f63       86.9MB
registry.k8s.io/kube-proxy-arm64                v1.31.0              c573e1357a14e       95.9MB
registry.k8s.io/kube-proxy                      v1.31.0              c573e1357a14e       95.9MB
registry.k8s.io/kube-scheduler-arm64            v1.31.0              8377f1e14db4c       67MB
registry.k8s.io/kube-scheduler                  v1.31.0              8377f1e14db4c       67MB
registry.k8s.io/pause                           3.10                 afb61768ce381       268kB

~/study/kind ❯ docker images
REPOSITORY                    TAG       IMAGE ID       CREATED              SIZE
mmweb                         1.0       5ad1a0471d00   About a minute ago   47MB
kindest/node                  <none>    9d05f134f12f   3 weeks ago          1.04GB
arm64v8/ubuntu                latest    1a799365aa63   5 weeks ago          101MB
amd64/ubuntu                  latest    edbfe74c41f8   5 weeks ago          78.1MB
gcr.io/k8s-minikube/kicbase   v0.0.42   62753ecb37c4   10 months ago        1.11GB
gcr.io/k8s-minikube/kicbase   v0.0.37   55c37b5c9b24   19 months ago        1.06GB

~/study/kind ❯ kind load docker-image mmweb:1.0 --name $CLUSTERNAME
Image: "mmweb:1.0" with ID "sha256:5ad1a0471d0079a9394e53787574cf2a1f98be70735fbfdf6dfe0d5cf48616f7" not yet present on node "myk8s-control-plane", loading...
Image: "mmweb:1.0" with ID "sha256:5ad1a0471d0079a9394e53787574cf2a1f98be70735fbfdf6dfe0d5cf48616f7" not yet present on node "myk8s-worker", loading...**

custom 이미지 pod 배포

mmweb:1.0 이미지로 배포 후 replias:2로 스케일링

~/study/kind ❯ kubectl create deployment deploy-myweb --image=mmweb:1.0
deployment.apps/deploy-myweb created
~/study/kind ❯ kubectl get deploy,pod

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/deploy-myweb   1/1     1            1           10s

NAME                                READY   STATUS    RESTARTS   AGE
pod/deploy-myweb-79cd55b85d-d26vr   1/1     Running   0          10s
~/study/kind ❯ kubectl scale deployment deploy-myweb --replicas 2

deployment.apps/deploy-myweb scaled
~/study/kind ❯ kubectl get deploy,pod

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/deploy-myweb   2/2     2            2           19s

NAME                                READY   STATUS    RESTARTS   AGE
pod/deploy-myweb-79cd55b85d-5tkbp   1/1     Running   0          5s
pod/deploy-myweb-79cd55b85d-d26vr   1/1     Running   0          19s
~/study/kind ❯     

서비스 배포

클러스터 내부에서 실행중인 custom nginx 애플리케이션에 외부에서 접근할 수 있도록 서비스 설정

nodeport 31001로 해서 설정


# 서비스 배포
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: **Service**
metadata:
  name: deploy-myweb
spec:
  type: **NodePort**
  ports:
  - name: svc-mywebport
    nodePort: **31001**
    port: 80
  selector:
    **app: deploy-myweb**
EOF

확인

배포한 서비스와 엔드포인트가 제대로 설정되었는지 확인 및 실제 호출

~/study/kind ❯ kubectl get svc,ep deploy-myweb

NAME                   TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
service/deploy-myweb   NodePort   10.96.178.5   <none>        80:31001/TCP   15s

NAME                     ENDPOINTS                     AGE
endpoints/deploy-myweb   10.244.1.4:80,10.244.1.5:80   15s
~/study/kind ❯ curl localhost:31001
Hello myk8s

클러스터 삭제

**kind delete cluster --name $CLUSTERNAME**

ingress

https://kind.sigs.k8s.io/docs/user/ingress/

클러스터 배포

Kind 클러스터를 생성하면서 노드 라벨포트 매핑을 설정하는 방법.

kubeletExtraArgs에서 node-labels: "ingress-ready=true"를 설정하면, 해당 노드에 "ingress-ready"라는 라벨이 붙음. Ingress 컨트롤러가 배포될 노드를 지정 가능

# 클러스터 배포 : 노드 라벨, 포트 맵핑
**cat <<EOT> kind-ingress.yaml**
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: **control-plane**
  **kubeadmConfigPatches**:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        **node-labels**: "**ingress-ready=true**"
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
  - containerPort: 30000
    hostPort: 30000
**EOT**

kind create cluster --config **kind-ingress.yaml** --name **myk8s**

라벨 확인

ingress-ready 라벨 확인

~/study/kind ❯ kubectl get nodes --show-labels

NAME                  STATUS   ROLES           AGE     VERSION   LABELS
myk8s-control-plane   Ready    control-plane   3h35m   v1.31.0   beta.kubernetes.io/arch=arm64,beta.kubernetes.io/os=linux,
                                                                 ingress-ready=true,kubernetes.io/arch=arm64,kubernetes.io/hostname=myk8s-control-plane,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=

ingress-nginx 배포

클러스터에서 ingress nginx 컨트롤러 배포.

클러스터 외부에서 80, 443 포트를 통해 Ingress 컨트롤러에 접근할 수 있도록 포트 포워딩이 설정됨
Ingress 컨트롤러가 Taints가 설정된 노드에서도 실행될 수 있도록 Toleration 설정
ingress-ready=true 라벨이 지정된 노드에 배포.

**kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml**

배포 확인

~/study/kind ❯ kubectl get deploy,svc,ep ingress-nginx-controller -n ingress-nginx 

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   1/1     1            1           13m

NAME                               TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
service/ingress-nginx-controller   NodePort   10.96.24.172   <none>        80:31883/TCP,443:32555/TCP   13m

NAME                                 ENDPOINTS                      AGE
endpoints/ingress-nginx-controller   10.244.0.7:443,10.244.0.7:80   13m

ingress 트래픽 라우팅 설정

마이크로 서비스 아키텍처에서 경로별 pod를 다르게 할 때 배포하기가 쉬워짐.

~/study/kind ❯ kubectl apply -f https://kind.sigs.k8s.io/examples/ingress/usage.yaml

pod/foo-app created
service/foo-service created
pod/bar-app created
service/bar-service created
Warning: path /foo(/|$)(.*) cannot be used with pathType Prefix
Warning: path /bar(/|$)(.*) cannot be used with pathType Prefix
ingress.networking.k8s.io/example-ingress created

 

yaml 파일 설명

pod 2개를 배포 (foo, bar)하고 /foo 경로는 foo-service로 보내고, /bar는 bar-service로 보냄

foo-app은 8080포트 사용

nginx.ingress.kubernetes.io/rewrite-target 어노테이션을 통해 특정 경로로 들어온 경우 특정 서비스로 보냄. 관련 문서 : https://kubernetes.github.io/ingress-nginx/examples/rewrite/

이를 통해 http://localhost/foo http://localhost/bar 형태로 분리가 가능함

특정 도메인 아래 여러 경로에 대한 트래픽을 효율적으로 관리가 가능

kind: Pod
apiVersion: v1
metadata:
  name: foo-app
  labels:
    app: foo
spec:
  containers:
  - command:
    - /agnhost
    - netexec
    - --http-port
    - "8080"
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    name: foo-app
---
kind: Service
apiVersion: v1
metadata:
  name: foo-service
spec:
  selector:
    app: foo
  ports:
  # Default port used by the image
  - port: 8080
---
kind: Pod
apiVersion: v1
metadata:
  name: bar-app
  labels:
    app: bar
spec:
  containers:
  - command:
    - /agnhost
    - netexec
    - --http-port
    - "8080"
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    name: bar-app
---
kind: Service
apiVersion: v1
metadata:
  name: bar-service
spec:
  selector:
    app: bar
  ports:
  # Default port used by the image
  - port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - http:
      paths:
      - pathType: Prefix
        path: /foo(/|$)(.*)
        backend:
          service:
            name: foo-service
            port:
              number: 8080
      - pathType: Prefix
        path: /bar(/|$)(.*)
        backend:
          service:
            name: bar-service
            port:
              number: 8080
---

배포 확인

~/study/kind ❯ kubectl get ingress,pod -owide

NAME                                        CLASS    HOSTS   ADDRESS   PORTS   AGE
ingress.networking.k8s.io/example-ingress   <none>   *                 80      5s

NAME          READY   STATUS              RESTARTS   AGE   IP       NODE                  NOMINATED NODE   READINESS GATES
pod/bar-app   0/1     ContainerCreating   0          5s    <none>   myk8s-control-plane   <none>           <none>
pod/foo-app   0/1     ContainerCreating   0          5s    <none>   myk8s-control-plane   <none>           <none>
~/study/kind ❯ kubectl get ingress,pod -owide

NAME                                        CLASS    HOSTS   ADDRESS     PORTS   AGE
ingress.networking.k8s.io/example-ingress   <none>   *       localhost   80      7m1s

NAME          READY   STATUS    RESTARTS   AGE    IP           NODE                  NOMINATED NODE   READINESS GATES
pod/bar-app   1/1     Running   0          7m1s   10.244.0.9   myk8s-control-plane   <none>           <none>
pod/foo-app   1/1     Running   0          7m1s   10.244.0.8   myk8s-control-plane   <none>           <none>

호출

정상 동작 확인.

~/study/kind ❯ curl localhost/foo/hostname

foo-app%                                                                                                                         
~/study/kind ❯ curl localhost/bar/hostname

bar-app%                                                                                                                         

~/study/kind ❯ kubectl exec -it foo-app -- curl localhost:8080/foo/hostname

NOW: 2024-09-07 17:42:50.949731213 +0000 UTC m=+3515.197047484%                                                                  
~/study/kind ❯ kubectl exec -it foo-app -- curl localhost:8080/hostname

foo-app%                                                                                                                         
~/study/kind ❯ kubectl exec -it bar-app -- curl localhost:8080/bar/hostname

NOW: 2024-09-07 17:43:03.215489719 +0000 UTC m=+3526.635941239%                                                                  
~/study/kind ❯ kubectl exec -it bar-app -- curl localhost:8080/hostname

bar-app%                                                                                                                         
~/study/kind ❯  

#