Kubernetes

EKS-NodeLess-03-Karpenter-01-intro

NodeLess 컨셉에서 제일 중요한 역할을 맡고 있는 Karpenter 다.

Karpenter 의 기본적인 아키텍처 부터 리뷰해볼까 한다. 그렇다면 그전에 Cluster Autoscaler 부터 설명해야 한다.

Cluster Autoscaler 는 보통 CA라 부른다. 간략하게 플로우를 설명하겠다.

  1. Kubernetes에 새로운 Pod 가 프로비저닝 되었을때 Pod는 노드그룹에 스케줄링 된다.
  2. 노드그룹에 자원이 부족하면 CA가 트리거 된다.
  3. CA는 AWS 의 ASG에 새로운 로드를 요청한다.
  4. ASG는 새로운노드를 생성하고 노드그룹에 추가한다.
  5. 새로 스케줄링된 노드에 Pod가 생성된다.

생략된 단계가 있지만 실제로 이 단계를 모두 거쳐야 인스턴스가 EKS에 연결되고 노드그룹에 인스턴스가 노출된다. 그렇다면 단순히 ASG에서 노드를 제거해본 경험이 있는가? 있다면 알것이다. 이건 가끔 커피한잔하고 와도 제거안된 인스턴스가 있는 경우도 있다.

ASG가 나쁜건 아니다. 반응성이 좀 떨어질 뿐이다.
하지만, Kubernetes 에 어울리는 솔루션은 아니라 생각했다.

그렇다면 Kerpenter 의 간략한 플로우는 어떨까?

  1. default-scheduler 는 Karpenter API에 새 파드를 요청한다.
  2. Karpenter 컨트롤러는 Pod를 프로비저닝 할수 있는 노드를 찾는다.
  3. 노드가 없으면 Karpenter는 AWS SDK를 이용하여 AWS에 새 노드를 요청한다.
  4. AWS는 새노드를 생성하고 새노드가 프로비저닝 되면 클러스터에 Join 하게 된다
  5. 노드가 추가되면 Karpenter는 Node를 감지하여 Kubernetes scheduler에 Node 준비를 알린다.
  6. Kubernetes scheduler 는 Pod를 프로비저닝 한다

조금더 상세한 내용을 추가해서 설명했는데, 실제로는 노드그룹이나 CA ASG같은게 빠지고 노드에 대한 부분은 카펜터가 모두 AWS SDK로 컨트롤 한다. 여러 단계들을 제거함으로 빨라진것이다.

EKS-NodeLess-02-Fargate

# k get pod -A -o wide
NAMESPACE     NAME                         READY   STATUS    RESTARTS   AGE    IP               NODE                                                        NOMINATED NODE   READINESS GATES
default       nginx-pod                    1/1     Running   0          11m    192.168.12.217   ip-192-168-12-150.ap-northeast-2.compute.internal           <none>           <none>
karpenter     karpenter-5bffc6f5d8-6f779   1/1     Running   0          125m   192.168.12.99    fargate-ip-192-168-12-99.ap-northeast-2.compute.internal    <none>           <none>
karpenter     karpenter-5bffc6f5d8-84mjn   1/1     Running   0          130m   192.168.11.201   fargate-ip-192-168-11-201.ap-northeast-2.compute.internal   <none>           <none>
kube-system   aws-node-h5z8d               1/1     Running   0          11m    192.168.12.150   ip-192-168-12-150.ap-northeast-2.compute.internal           <none>           <none>
kube-system   coredns-fd69467b9-4nk6x      1/1     Running   0          127m   192.168.12.52    fargate-ip-192-168-12-52.ap-northeast-2.compute.internal    <none>           <none>
kube-system   coredns-fd69467b9-cqqpq      1/1     Running   0          125m   192.168.11.122   fargate-ip-192-168-11-122.ap-northeast-2.compute.internal   <none>           <none>
kube-system   kube-proxy-z8qlj             1/1     Running   0          11m    192.168.12.150   ip-192-168-12-150.ap-northeast-2.compute.internal           <none>           <none>

먼저 예제를 보여준다.
NodeLess EKS 컨셉의 기반이다. nginx-pod / aws-node-h5z8d / kube-proxy-z8qlj 는 카펜터가 만든 노드위에 올라가 있다.

NodeLess 의 컨셉은 두가지를 기반으로 한다.

쿠버네티스 컴포넌트 kube-system namespace Pod들은 Fargate에 올린다.
여기에 에드온이나 관리가 필요한 Pod도 포함된다. karpenter controller라던가..AWS ELB Controller 라던가 그런 에드온들이 그런 역할을 한다.

Node가 필요한 Pod는 NodeGroup을 사용하지 않고 Karpenter를 사용한다.

그럼 NodeGroup이 없는 클러스터부터 만드는 방법이다.

https://eksctl.io/usage/fargate-support/

eksctl create cluster --fargate

간단하다 옵션으로 --fargate를 주면된다.

Fargate profile 같은경우에는 사실 콘솔에서 손으로 만들면 편하다. subnet이나 iam role 넣어주는게....그렇지 않다면 먼저 aws cli 부터 학습해야 한다. eksctl이 자동으로 해주는 부분도 있지만 필수요소는 알아야 하기 때문이다.

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/eks/create-fargate-profile.html

aws eks create-fargate-profile --fargate-profile-name kube-system --cluster-name myeks --pod-execution-role-arn arn:aws:iam::123456789:role/AmazonEKSFargatePodExecutionRole --subnets "subnet-1" "subnet-2" "subnet-3"

https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/pod-execution-role.html 역할생성은 이링크를 참고한다.

이런식으로 만들고 파게이트는 네임스페이스로 지정하면 네임스페이스에 만들어지는 톨레이션이나 다른 노드 어피니티등을 가지지 않은 Pod를 Fargate로 프로비저닝 한다. 이때 일반적인 쿠버네티스와 다른 부분은 Fargate 스케줄러(fargate-scheduler)가 별도로 동작하여 Fargate를 프로비저닝한다. 일반적인 경우엔 (default-scheduler)가 Pod를 프로비저닝 한다.

이 차이를 알아두면 어떤 노드를 물고있는지 확인하기 편하다.

EKS-NodeLess-01-CoreDNS

EKS의 관리영역중 Addon 이나 필수 컴포넌트중에 Node에서 동작해야하는 것들이 있다. 이 경우에 NodeGroup을 운영해야한다. NodeGroup에 여러 파드들이 스케줄링되고 관리형 Pod들은 다른 서비스에 운영되는 NodeGroup과 섞여서 스케줄리되어야 하는데, 이것의 가장큰 문제는 Node의 사망이 기능의 장애로 이어진다는 점이다. 따라서 Node를 전용 Node로 사용하면 좋은데 아주작은 노드를 스케줄링한다고 해도 관리되어야 하는 대상이 됨은 틀림없고, 노드를 정해서 사용해야 하는 문제점들이 생기게된다.

이러한 문제를 해결하기에 EKS에서는 Fargate가 있다. 1Node - 1Pod 라는게 아주 중요한 포인트다.

CoreDNS는 클러스터에 최저 2개의 Pod가 스케줄링되어야 한다.

https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/fargate-profile.html

eksctl를 사용하여 Fargate 프로파일을 생성하려면 다음 eksctl 명령으로 Fargate 프로파일을 생성하고 모든 example value를 고유한 값으로 바꿉니다. 네임스페이스를 지정해야 합니다. 그러나 --labels 옵션은 필요하지 않습니다.

eksctl create fargateprofile \
    --cluster my-cluster \
    --name kube-system \
    --namespace kube-system

다음과 같이 생성해 주면된다. 그럼 kube-system namespace로 스케줄링되는 Pod는 Fargate로 생성되게 된다.

그다음은 CoreDNS를 패치하고 재시작하면된다.

https://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/patterns/deploy-coredns-on-amazon-eks-with-fargate-automatically-using-terraform-and-python.html

kubectl patch deployment coredns -n kube-system --type=json -p='[{"op": "remove", "path": "/spec/template/metadata/annotations", "value": "eks.amazonaws.com/compute-type"}]'
kubectl rollout restart -n kube-system deployment coredns

이렇게 진행하면 CoreDNS를 Fargate로 실행하게 된다.

 k get pod -o wide
NAME                      READY   STATUS    RESTARTS   AGE     IP              NODE                                                       NOMINATED NODE   READINESS GATES
coredns-fd69467b9-bsh88   1/1     Running   0          5h18m   192.168.13.23   fargate-ip-192-168-13-23.ap-northeast-2.compute.internal   <none>           <none>
coredns-fd69467b9-gn24k   1/1     Running   0          5h18m   192.168.12.34   fargate-ip-192-168-12-34.ap-northeast-2.compute.internal   <none>           <none>

다음과 같이 스케줄링되면 정상적으로 배포 된것이다.

PKOS-5Week-Final!

마지막 주차이다.

5 주차는 보안 관련한 주제였다. 대표적으로 생각나는 쿠버네티스 보안 사건부터 이야기할까한다.

생각보다 많은 사람들이 쓰는 오픈소스중에 Rancher가 있다.

Rancher가 설치된 클러스터에서 이상한 증상이 발생했다. 배포된 POD가 재대로 성능이 나지않고 WorkerNode의 CPU사용율이 굉장히 높았다. 클러스터 외부에서 원인을 파악할 수 없어서 결국 WokerNode에 SSH를 접속해서 확인했었다.

WorkerNode에선 대량의 마이닝툴이 발견되었다. Pirvate 환경인데다가 외부에서 SSH도 불가능한 환경에서 발생한 침해사고라, 플랫폼의 문제로 대두되었다. 그러던 중 클러스터에서 동작중인 대시보드들을 확인하였다.

당시에는 다들 쿠버네티스에 익숙한 상황이 아니라서 확인이 좀오래 걸렸다.

실상은 사용자의 문제이지만, Rancher 에서 사용되는 privileged모드를 정확히 모르고 사용하여, Rancher 의 privileged 취약점을 통해 API로 손쉽게 대량의 마이닝툴을 인스톨 할수있는 이슈였다.

Rancher 2.4버전까진 privileged모드에 대한 안내가 없지만 2.5버전부턴 생겼다.

https://ranchermanager.docs.rancher.com/v2.5/pages-for-subheaders/rancher-on-a-single-node-with-docker#privileged-access-for-rancher-v25

가볍게 이전에 경험했던 이야기를 하면서 unsafe 에대한 이야기를 하려한다.

#kubelet --allowed-unsafe-sysctls 

에 대한 이야기다. 한창 K8S의 성능에 대한 고민이 많던 시기다.
이때에 쿠버네티스 네트워킹에 대해서 한창 많은 공부를 했던것 같다.

K8S의 성능이슈가 발생하였다. 흔히 말하는 쓰로틀링 이슈로 보틀넥이 되는 부분을 찾아야하는 상태였다. 증상은 이랬다. 일정이상의 트래픽이 발생하면 패킷이 드랍됬다.

이문제를 재현하는 것부터 시작했다. 최대한 많은 스트레스를 줘야했기에, 테스트 툴부터 시작했다. Locust를 선택했다. 가볍고 많은 트래픽을 만들수 있는 툴이다. Python 기반이라 테스트 스크립트 작성도 쉽다.

혹시나 같은 네트워크나 같은 클러스터등 다른 요인이 될만한 요소들을 최대한 제거해서 테스트했다. 이 테스트는 거의 2-3주 정도 진행했는데 테스트 결과가 너무 들쭉날쭉했다. 이유는 처음에 나는 CNI를 의심했다.

이유는 네트워크 이슈니까.

그런데 테스트를 진행할수록 CNI는 잘못이 없었다.
오히려 굉장히 최적화가 잘되어있다는 것을 알 수 있었다.

그 테스트를 진행하면서, 서버가 정상적으로 네트워크를 처리못하는 느낌이 들었다. 엔지니어 시절에는 커널파라 미터 튜닝도 은근 자주했기에 backlog / net.core.somaxconn 등을 의심했다.

net.core.netdev_max_backlog 옵션은 각 네트워크 별로 커널이 처리하도록 쌓아두는 Queue의 크기를 정해주는 파라미터다.

net.core.somaxconn 는 Listen backlog / Linsten 으로 바인딩된 서버 소켓에서 Accept를 기다리는 소캣 카운터의 하드리밋이다.

cat /var/lib/kubelet/kubelet.conf
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous: {}
  webhook:
    cacheTTL: 0s
  x509: {}
authorization:
  webhook:
    cacheAuthorizedTTL: 0s
    cacheUnauthorizedTTL: 0s
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
logging:
  flushFrequency: 0
  options:
    json:
      infoBufferSize: "0"
  verbosity: 0
memorySwap: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
runtimeRequestTimeout: 0s
shutdownGracePeriod: 0s
shutdownGracePeriodCriticalPods: 0s
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s

여기에 박아서 사용할수 있다.

allowed-unsafe-sysctls:
- net.core.netdev_max_backlog
- net.core.somaxconn

설정을 추가했다. 이제 프로세스를 새시작하고 테스트 했다.

32s                 Normal    NodeReady                 Node/i-0a7504d19e11fb642   Node i-0a7504d19e11fb642 status is now: NodeReady
31s                 Warning   SysctlForbidden           Pod/unsafe                 forbidden sysctl: "net.core.somaxconn" not allowlisted
14s (x6 over 30s)   Warning   FailedMount               Pod/unsafe                 MountVolume.SetUp failed for volume "kube-api-access-8w64p" : object "default"/"kube-root-ca.crt" not registered
4s                  Warning   SysctlForbidden           Pod/unsafe                 forbidden sysctl: "net.core.somaxconn" not allowlisted
4s                  Normal    Scheduled                 Pod/unsafe                 Successfully assigned default/unsafe to i-0a7504d19e11fb642

어라...이젠 이전에 했던 방법이 안먹는다. 그럼 뭐..더 고전적인 방법이다.

systemctl status kubelet.service 
● kubelet.service - Kubernetes Kubelet Server
     Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled)

보면 서비스 경로가 보인다. 이안에 kubelet을 시작하기 위한 경로들이 모여있다.

[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/kubernetes/kubernetes
After=containerd.service

[Service]
EnvironmentFile=/etc/sysconfig/kubelet
ExecStart=/usr/local/bin/kubelet "$DAEMON_ARGS"
Restart=always
RestartSec=2s
StartLimitInterval=0
KillMode=process
User=root
CPUAccounting=true
MemoryAccounting=true

[Install]
WantedBy=multi-user.target

우린 /etc/sysconfig/kubelet 에 삽입하면 된다.

cat /etc/sysconfig/kubelet 
DAEMON_ARGS="--anonymous-auth=false --authentication-token-webhook=true --authorization-mode=Webhook --cgroup-driver=systemd --cgroup-root=/ --client-ca-file=/srv/kubernetes/ca.crt --cloud-provider=external --cluster-dns=169.254.20.10 --cluster-domain=cluster.local --enable-debugging-handlers=true --eviction-hard=memory.available<100Mi,nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<10%,imagefs.inodesFree<5% --feature-gates=CSIMigrationAWS=true,InTreePluginAWSUnregister=true --hostname-override=i-0a7504d19e11fb642 --kubeconfig=/var/lib/kubelet/kubeconfig --max-pods=100 --pod-infra-container-image=registry.k8s.io/pause:3.6@sha256:3d380ca8864549e74af4b29c10f9cb0956236dfb01c40ca076fb6c37253234db --pod-manifest-path=/etc/kubernetes/manifests --protect-kernel-defaults=true --register-schedulable=true --resolv-conf=/run/systemd/resolve/resolv.conf --v=2 --volume-plugin-dir=/usr/libexec/kubernetes/kubelet-plugins/volume/exec/ --cloud-config=/etc/kubernetes/in-tree-cloud.config --node-ip=172.30.52.244 --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock --tls-cert-file=/srv/kubernetes/kubelet-server.crt --tls-private-key-file=/srv/kubernetes/kubelet-server.key --config=/var/lib/kubelet/kubelet.conf"
HOME="/root"

여기 DAEMON_ARGS 뒤에 --allowed-unsafe-sysctls 'kernel.msg*,net.core.somaxconn' 를 추가해준다.

재시작 까지하면 프로세스에 추가된 파라미터가 보인다.

ps afxuwww | grep unsafe
root       27576  0.0  0.0   8168   656 pts/0    S+   13:07   0:00                          \_ grep --color=auto unsafe
root       27340  2.3  2.4 1789712 94144 ?       Ssl  13:06   0:00 /usr/local/bin/kubelet --anonymous-auth=false --authentication-token-webhook=true --authorization-mode=Webhook --cgroup-driver=systemd --cgroup-root=/ --client-ca-file=/srv/kubernetes/ca.crt --cloud-provider=external --cluster-dns=169.254.20.10 --cluster-domain=cluster.local --enable-debugging-handlers=true --eviction-hard=memory.available<100Mi,nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<10%,imagefs.inodesFree<5% --feature-gates=CSIMigrationAWS=true,InTreePluginAWSUnregister=true --hostname-override=i-0a7504d19e11fb642 --kubeconfig=/var/lib/kubelet/kubeconfig --max-pods=100 --pod-infra-container-image=registry.k8s.io/pause:3.6@sha256:3d380ca8864549e74af4b29c10f9cb0956236dfb01c40ca076fb6c37253234db --pod-manifest-path=/etc/kubernetes/manifests --protect-kernel-defaults=true --register-schedulable=true --resolv-conf=/run/systemd/resolve/resolv.conf --v=2 --volume-plugin-dir=/usr/libexec/kubernetes/kubelet-plugins/volume/exec/ --cloud-config=/etc/kubernetes/in-tree-cloud.config --node-ip=172.30.52.244 --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock --tls-cert-file=/srv/kubernetes/kubelet-server.crt --tls-private-key-file=/srv/kubernetes/kubelet-server.key --config=/var/lib/kubelet/kubelet.conf --allowed-unsafe-sysctls kernel.msg*,net.core.somaxconn
# k events 
5s                      Normal    NodeAllocatableEnforced   Node/i-0a7504d19e11fb642   Updated Node Allocatable limit across pods
4s                       Normal    Scheduled                 Pod/unsafe                 Successfully assigned default/unsafe to i-0a7504d19e11fb642
3s                       Normal    Pulling                   Pod/unsafe                 Pulling image "centos:7"
k get pod
NAME     READY   STATUS    RESTARTS   AGE
unsafe   1/1     Running   0          20s

정상적으로 스케줄링된것이 보인다.

이로서 unsafe 파라미터를 영구적으로 적용할수 있게되었다.

이렇게 파라미터를 적용하고 나서, net.core.somaxconn 을 테스트하였다. 정상적으로 적용된것이 보인다. 사실 단순히 somaxconn만 적용한다고 뭔가 달라지지 않는다. syn_backlog 값도 같이 늘려야 한다. 그리고 적용된 값은 Listen() 시스템 콜을 할때 적용 되기 때문에 이 파라미터 들은 컨테이너가 시작될때 적용된다고 보면 된다.

이런 과정을 거쳐서 테스트를 진행했으나, 실은 트래픽을 20% 더 받았을 뿐 증상이 완화 되지 않았다.

결국 해결은 kubenetes 안에 있지 않았고 Linux 안에 있었다. irqbalance의 smp_affinity 가 정상적으로 인터럽트 해주지 않아서 cpu0만 열심히 일하고 있었던 것이다.

이런과정에서 privileged / unsafe 에 대해서 알게되었다.

5주차 과정을 진행하면서 다시금 그때의 기억과 내용을 자세히 알게되면서 새로운 부분도 알되게었고, 새로이 정리도 하게되었다.

PKOS는 쿠버네티스의 전체적인 패턴과 컴포넌트들을 학습할수 있는 기회였다.

PKOS-4Week

이번주 스터디는 K8S에서의 Pometheus Grafana 다.

이글은 chatgpt를 이용해서 삽질하는 리눅서를 담고있다.

프로메테우스는 아이콘이 불꽃 모양인데 정말 모니터링계에 불을 가져다준 혁신과도 같은 존재다. 이전에는 Nagios / Zabbix가 나눠먹고 있었다.

나는 이번에 뭘 모니터링 해볼까 고민하다가 HPA를 모니터링 해볼까한다.

먼저 내가 요즘 일하는 방식을 보여줄까한다.

바로 Chatgpt다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-deployment
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

https://github.com/rakyll/hey

go 기반이라 bash로 수정해달라고 했다.

#!/bin/bash

SERVICE_IP=$(kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
CONCURRENCY=100
REQUESTS=50000

# Run load test
for i in $(seq 1 $CONCURRENCY); do
  (for j in $(seq 1 $(($REQUESTS / $CONCURRENCY))); do
    curl -s -o /dev/null http://${SERVICE_IP}/
  done) &
done

# Wait for all background processes to complete
wait

위의 Manifast 를 적용한 결과는 다음과 같다.

 k get all 
NAME                                    READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-7c6895c677-cblrh   1/1     Running   0          15s
pod/nginx-deployment-7c6895c677-s6b5c   1/1     Running   0          15s
pod/nginx-deployment-7c6895c677-smkxv   1/1     Running   0          15s

NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/nginx-service   LoadBalancer   100.66.65.216   <pending>     80:30979/TCP   15s

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   3/3     3            3           15s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-deployment-7c6895c677   3         3         3       15s

NAME                                            REFERENCE                     TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/nginx-hpa   Deployment/nginx-deployment   <unknown>/50%   3         10        3          19s

정말 잘동작한다.

이제 ingress 답변을 받았다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
spec:
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-service
                port:
                  number: 80
k get ingress
NAME         CLASS    HOSTS   ADDRESS                                                                     PORTS   AGE
my-ingress   <none>   *       k8s-nginx-myingres-106681c1f1-1873214288.ap-northeast-2.elb.amazonaws.com   80      72s

ingress - svc - deployment - hpa 구조 같은건 이제 3분이면 나온다.

gpt가 알려준 스크립트중

kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

부분을

kubectl get ingress my-ingress -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'

다음과 같이 수정하였다.

curl k8s-nginx-myingres-106681c1f1-1873214288.ap-northeast-2.elb.amazonaws.com 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

잘 뜬다.

이제 공격 간다

굉장한 스크립트 였다... 한방에 100개의 스크립트가 떴다...후....

근데 nginx 는 너무 가벼운 친구라...ㅠㅠ hpa 가 잘작동하지 않았다. 리미트를 준다.

        resources:
          limits:
            cpu: 15m
            memory: 10Mi
          requests:
            cpu: 15m
            memory: 10Mi
            
k get hpa
NAME        REFERENCE                     TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
nginx-hpa   Deployment/nginx-deployment   33%/50%   3         10        3          25m

좋다 적절하다.

이제 셋업은 다되었다. hpa 가 동작하는 대시보드와 hap 가 일정이상 동작해서 max 에 가 까워지면 알럿을 날릴거다.

Chatgpt에게 물었고, 비슷한 쿼리를 작성했다.

kube_horizontalpodautoscaler_status_desired_replicas{horizontalpodautoscaler="nginx-hpa", namespace="nginx"}

결과는 잘 작동한다. 그럼이걸 알럿을 보낼거다.

프로메테우스에서 graph로 grafana 에서 가져온 쿼리를 넣는다

1- avg(rate(kube_horizontalpodautoscaler_status_desired_replicas{horizontalpodautoscaler="nginx-hpa", namespace="nginx"}[1m]))

 k edit cm prometheus-kube-prometheus-stack-prometheus-rulefiles-0
configmap/prometheus-kube-prometheus-stack-prometheus-rulefiles-0 edited

edit 로 수정했다.

추가를 하다가 잘 안되서 helm에 있는 config를 뜯어봤다. 어떤구성인지,

     containers:
      - env:
        - name: BITNAMI_DEBUG
          value: "false"
        - name: NGINX_HTTP_PORT_NUMBER
          value: "8080"
        image: docker.io/bitnami/nginx:1.23.3-debian-11-r17
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 6
          initialDelaySeconds: 30
          periodSeconds: 10
          successThreshold: 1
          tcpSocket:
            port: http
          timeoutSeconds: 5
        name: nginx
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 5
          successThreshold: 1
          tcpSocket:
            port: http
          timeoutSeconds: 3
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      - command:
        - /usr/bin/exporter
        - -nginx.scrape-uri
        - http://127.0.0.1:8080/status
        image: docker.io/bitnami/nginx-exporter:0.11.0-debian-11-r44
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /metrics
            port: metrics
            scheme: HTTP
          initialDelaySeconds: 15
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
        name: metrics
        ports:
        - containerPort: 9113
          name: metrics
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /metrics
            port: metrics
            scheme: HTTP
          initialDelaySeconds: 5
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      serviceAccount: default
      serviceAccountName: default
      shareProcessNamespace: false
      terminationGracePeriodSeconds: 30

exporter 가 사이드카로 붙어서 모니터링을 하고있었다.

https://github.com/nginxinc/nginx-prometheus-exporter

온종일 삽질의 연속이다가 오늘또 k8s에서의 프로메테우스와 그라파나 패턴을 까보면서 helm을 써야할까 하는 고민이들었다. kustomize를 고민했는데 좀더 뭘깍을지 생각해봐야겠다.