linuxer-admin

FinOps Certified Practitioner (FOCP) -Review

바쁜 와중에 요즘 자격증 추가가 좀 뜸한거 같아서 2022년 말에 도전했다가 떨어진 FOCP를 다시 도전하였다.

FOCP의 경우에는 Practitioner 자격이라 난이도가 높지는 않다.

하지만 자격증에서 요구하는 바가 일반적인 IT의 시각이 아니다.

FinOps는 재무와의 연결도 포함하고있어서 단순히 DevOps로서의 사일로 뿐만아니라 조직에 대한 사일로 까지 없애려는 노력을 해야하기 때문이다.

https://www.finops.org/introduction/what-is-finops/: FinOps Certified Practitioner (FOCP) -Review

시험의 모든내용은 FinOps Foundation 에 공개되어있다.

그렇기에 어느정도 FinOps문화를 이해할 준비가 되었다면 시험을 볼만하다고 생각한다.

문제가 되었던 장벽은 다음과 같다.

  1. 시험가격 $300
  2. 시험가격 $300
  3. 시험가격 $300
  4. 시험가격 $300
  5. 시험가격 $300

그렇다 시험이 비싸다 할인할때 보자. 1번떨어진 나는 대략 $400 정도 썼다.

시험한번에 3번의 기회를 준다. 나처럼 뒤로가기 눌러서 마지막 시험기회를 날리는 우를 범하지 않길 바란다.

이상으로 글을 마치겠다.

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>

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

AWS-SQS-터져랏

일단 SQS를 터질때 까지 밀어넣어 보기로 했다.

목표 메시지수는 100만건.

100만건의 메시지를 100초안에 SQS에 넣는게 목표다. TPS 10000 이라는 소리다.

목표를 이루기위해선 첫번째 SQS의 TPS는 3000이다. 초당 3000의 메시지를 넣을수 있다.

먼저 큐를 4개를 만들었다. 목표수치에 가려면 TPS가 10000은 나와야한다.

그렇다면 큐를 병렬로 줄세운다 4개의 큐를 만든다.

이제 넣어봤다.

import boto3
import json
import uuid
from concurrent.futures import ThreadPoolExecutor
import random
import string

sqs = boto3.client('sqs')
queue_urls = [
    'linuxer-sqs-1',
    'linuxer-sqs-2',
    'linuxer-sqs-3',
    'linuxer-sqs-4'
]

def random_string(length):
    return ''.join(random.choices(string.ascii_letters + string.digits, k=length))

def create_dummy_data():
    return {
        'id': str(uuid.uuid4()),
        'data': f"host-{random_string(5)}-count",
        'padding': random_string(10 * 1024 - 100)  # 10KB 크기의 더미 데이터를 생성
    }

def send_message_batch(queue_url, messages):
    entries = []

    for idx, message in enumerate(messages):
        entries.append({
            'Id': str(idx),
            'MessageBody': json.dumps(message)
        })

    response = sqs.send_message_batch(
        QueueUrl=queue_url,
        Entries=entries
    )
    return response

def generate_and_send_dummy_data(num_messages=100000, batch_size=10, num_threads=10):
    with ThreadPoolExecutor(max_workers=num_threads) as executor:
        for _ in range(num_messages // (batch_size * num_threads * len(queue_urls))):
            batch_futures = []
            for queue_url in queue_urls:
                messages = [create_dummy_data() for _ in range(batch_size)]
                future = executor.submit(send_message_batch, queue_url, messages)
                batch_futures.append(future)

            for future in batch_futures:
                future.result()

if __name__ == '__main__':
    generate_and_send_dummy_data()

대충 이코드는 TPS 100 정도이다.

5분 정도 걸려서 10만 건의 메시지를 모두 PUT했다. 분당 20000 TPS 333정도다.

병렬처리했다.

import boto3
import json
import uuid
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Process
import random
import string

sqs = boto3.client('sqs')
queue_urls = [
    'linuxer-sqs-1',
    'linuxer-sqs-2',
    'linuxer-sqs-3',
    'linuxer-sqs-4'
]

def random_string(length):
    return ''.join(random.choices(string.ascii_letters + string.digits, k=length))

def create_dummy_data():
    return {
        'id': str(uuid.uuid4()),
        'data': f"host-{random_string(5)}-count",
        'padding': random_string(10 * 1024 - 100)  # 10KB 크기의 더미 데이터를 생성
    }

def send_message_batch(queue_url, messages):
    entries = []

    for idx, message in enumerate(messages):
        entries.append({
            'Id': str(idx),
            'MessageBody': json.dumps(message)
        })

    response = sqs.send_message_batch(
        QueueUrl=queue_url,
        Entries=entries
    )
    return response

def generate_and_send_dummy_data(queue_url, num_messages=1000000, batch_size=10, num_threads=10):
    with ThreadPoolExecutor(max_workers=num_threads) as executor:
        for _ in range(num_messages // (batch_size * num_threads)):
            batch_futures = []
            for _ in range(num_threads):
                messages = [create_dummy_data() for _ in range(batch_size)]
                future = executor.submit(send_message_batch, queue_url, messages)
                batch_futures.append(future)

            for future in batch_futures:
                future.result()

def start_processes(num_processes):
    processes = []
    for queue_url in queue_urls:
        for _ in range(num_processes):
            process = Process(target=generate_and_send_dummy_data, args=(queue_url,))
            processes.append(process)
            process.start()

    for process in processes:
        process.join()

if __name__ == '__main__':
    num_processes = 4
    start_processes(num_processes)
501 17512 16975   0  9:27PM ttys001    0:00.21 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python main.py
  501 17513 17512   0  9:27PM ttys001    0:00.05 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.resource_tracker import main;main(6)
  501 17514 17512   0  9:27PM ttys001    0:14.41 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=9) --multiprocessing-fork
  501 17515 17512   0  9:27PM ttys001    0:14.50 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=11) --multiprocessing-fork
  501 17516 17512   0  9:27PM ttys001    0:14.36 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=14) --multiprocessing-fork
  501 17517 17512   0  9:27PM ttys001    0:14.60 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=16) --multiprocessing-fork
  501 17518 17512   0  9:27PM ttys001    0:14.55 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=18) --multiprocessing-fork
  501 17519 17512   0  9:27PM ttys001    0:14.21 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=20) --multiprocessing-fork
  501 17520 17512   0  9:27PM ttys001    0:14.16 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=22) --multiprocessing-fork
  501 17521 17512   0  9:27PM ttys001    0:14.11 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=24) --multiprocessing-fork
  501 17522 17512   0  9:27PM ttys001    0:14.46 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=27) --multiprocessing-fork
  501 17523 17512   0  9:27PM ttys001    0:14.55 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=29) --multiprocessing-fork
  501 17524 17512   0  9:27PM ttys001    0:14.24 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=31) --multiprocessing-fork
  501 17525 17512   0  9:27PM ttys001    0:14.19 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=33) --multiprocessing-fork
  501 17526 17512   0  9:27PM ttys001    0:14.28 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=35) --multiprocessing-fork
  501 17527 17512   0  9:27PM ttys001    0:14.18 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=37) --multiprocessing-fork
  501 17528 17512   0  9:27PM ttys001    0:14.49 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=39) --multiprocessing-fork
  501 17529 17512   0  9:27PM ttys001    0:14.46 /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=7, pipe_handle=41) --multiprocessing-fork

이제 멀티프로세스로 꼽는다!!

코드보니 400만개를 넣도록 되어있어서 100만개 넣을시점에 끊었다.

10분정도에 100만개의 메시지.. TPS 1600 정도다 아직 더 올릴수 있는 가망성이 보이지만 이건이제 컴퓨팅의 문제다.
이제 병렬처리만으로도 가능함을 알았으니..producer 의 병렬성을 더 올린다. num_processes = 12 4배다! 일단 터트려 보자.

ps -ef | grep "multiprocessing.spawn" | wc -l
      49

49개의 프로세스가 미친듯이 공격을 한다. M1 진짜 좋다.

아...내 노트북으로 낼수있는 TPS는 1600이 한계다. 이제 컨슈밍을 할거다. SQS에 있는 데이터를 꺼내쓰는 속도를 확인할거다. 큐에는 100만개의 데이터가 쌓여있고 이걸 모두 소모하는 속도를 확인하려한다.

import boto3
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from multiprocessing import Process

sqs = boto3.client('sqs')
queue_urls = [
    'linuxer-sqs-1',
    'linuxer-sqs-2',
    'linuxer-sqs-3',
    'linuxer-sqs-4'
]

def receive_and_delete_message(queue_url, wait_time=20):
    while True:
        response = sqs.receive_message(
            QueueUrl=queue_url,
            AttributeNames=['All'],
            MaxNumberOfMessages=1,
            WaitTimeSeconds=wait_time
        )

        if 'Messages' in response:
            message = response['Messages'][0]
            receipt_handle = message['ReceiptHandle']
            sqs.delete_message(
                QueueUrl=queue_url,
                ReceiptHandle=receipt_handle
            )
        else:
            break

def process_messages(queue_url, num_threads=10):
    with ThreadPoolExecutor(max_workers=num_threads) as executor:
        futures = [executor.submit(receive_and_delete_message, queue_url) for _ in range(num_threads)]
        for future in as_completed(futures):
            future.result()

def start_processes(num_processes):
    processes = []
    for queue_url in queue_urls:
        for _ in range(num_processes):
            process = Process(target=process_messages, args=(queue_url,))
            processes.append(process)
            process.start()

    for process in processes:
        process.join()

if __name__ == '__main__':
    num_processes = 100
    start_time = time.time()
    start_processes(num_processes)
    end_time = time.time()

    print(f"Elapsed time: {end_time - start_time:.2f} seconds")

멀티프로세스 100개를 돌렸더니 M1이 뻣었다. 그렇지만 1분에 10만개 정도는 가볍게 뽑아가는걸 확인할수있었다.

이 결과만으로 SQS는 튼튼한 큐라는걸 알수 있었다. 그럼 이렇게 하드코어하게 넣었으니 에러레이트를 확인해 봐야했다.

지연되거나 데드레터큐에 쌓인메시지는 없었다. 모두 정상적으로 소진된것이다.

여기서 결론은 mac book M1 air 로서 낼수 있는 퍼포먼스는 TPS1600이다. 분당 10만건의 메시지를 처리할수 있는 능력이라는것.. 이다음은 컴퓨팅 자원을 마음껏 넣어서 큐를 터트려 보겠다.

밤새 머리속에서 어떻게 하면 좋을까 고민하다가 EKS에서 Job을 이용해 병렬 처리속도를 늘려보기로 했다.

도커로 말고~

CMD로 job을 실행할때 파일을 지정해서 실행하도록 했다.

# Dockerfile
FROM python:3.8-slim

WORKDIR /app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY send.py receive.py ./

CMD ["python", "receive.py"]
apiVersion: batch/v1
kind: Job
metadata:
  name: sqs-test
spec:
  parallelism: 20
  template:
    spec:
      containers:
      - name: sqs-test
        image: sqs_test:latest
        command: ["python3", "send.py"] // ["python", "receive.py"] 로 변경할수 있다.
        resources:
          limits:
            cpu: "2"
            memory: "2Gi"
          requests:
            cpu: "1500m"
            memory: "1Gi"
      restartPolicy: Never
  backoffLimit: 4

컨테이너 말고 parallelism 으로 20의 pod를 예약했다.

Send 는 분당 26만개 TPS 대략 4300

receive 는 32만개 대략 TPS 5000 정도이다.

5분에 100만개를 처리할수 있는 능력이라 보이고 이 플로우의 장점은 job에서 모든 데이터를 다꺼내쓰면 Completed로 컨테이너가 완료되므로 시간에 따른 큐에 대한 스케줄링이 가능하다는 뜻이다.

job을 스케줄링할때 큐에 쌓인 지연시간+큐에쌓인 갯수를 모니터링하고 job을 이용해 큐를 비우는 방식의 아키텍처를 설계할수 있다는 이야기다.

 k get pod
NAME                  READY   STATUS      RESTARTS   AGE
sqs-test-send-2s6h4   0/1     Completed   0          4m23s
sqs-test-send-5276z   0/1     Completed   0          4m23s
sqs-test-send-72ndr   0/1     Completed   0          4m22s
sqs-test-send-c24kn   0/1     Completed   0          4m22s
sqs-test-send-ccz5r   0/1     Completed   0          4m23s
sqs-test-send-fjfnk   0/1     Completed   0          4m23s
sqs-test-send-h2jhv   0/1     Completed   0          4m22s
sqs-test-send-k7b8q   0/1     Completed   0          4m22s
sqs-test-send-ljbv5   0/1     Completed   0          4m23s
sqs-test-send-mjvh9   0/1     Completed   0          4m23s
sqs-test-send-n8wh4   0/1     Completed   0          4m23s
sqs-test-send-ngskk   0/1     Completed   0          4m22s
sqs-test-send-qj9ks   0/1     Completed   0          4m22s
sqs-test-send-r87hf   0/1     Completed   0          4m22s
sqs-test-send-rr58h   0/1     Completed   0          4m23s
sqs-test-send-sf2bd   0/1     Completed   0          4m23s
sqs-test-send-svn8d   0/1     Completed   0          4m22s
sqs-test-send-tqfg4   0/1     Completed   0          4m23s
sqs-test-send-tv68j   0/1     Completed   0          4m22s
sqs-test-send-w99hx   0/1     Completed   0          4m23s

결과가 놀라운데 4분23초 만에 pod의 스케줄링+job(120만건의 메시징컨슘)이 모두 완료된건이다.

이아키텍처에는 카펜터가 사용되었는데, 카펜터의 노드는 0에서 시작하여 새로 노드를 프로비저닝해서 깔끔하게 모두 완료된것이다. 스케줄링이 놀랍다.

재미있는 테스트였다.

이다음은 KaFka를 테스트 하겠다.