1. 계층형 시스템 아키텍처 다이어그램 (System Architecture Diagram)
AI 에이전트 워크플로우 엔진의 모듈화, 확장성, 결합도 낮춤(Decoupling)을 강조한 5-Layer Architecture입니다.
+-----------------------------------------------------------------------------------+
|                            1. Client & Interface Layer                            |
|   [ REST / GraphQL API ]       [ Webhook Receiver ]       [ Event Stream (SSE) ]  |
+-----------------------------------------------------------------------------------+
                                         │
                                         ▼
+-----------------------------------------------------------------------------------+
|                        2. Workflow Orchestration Layer Engine                     |
|  +------------------------+  +------------------------+  +---------------------+  |
|  |  DAG / Graph Engine    |  |  State Manager         |  | Node Dispatcher     |  |
|  |  (Graph Traversal)     |  |  (State Persist/Sync)  |  | (Async Execution)   |  |
|  +------------------------+  +------------------------+  +---------------------+  |
|  +------------------------+  +------------------------+  +---------------------+  |
|  |  Interrupt Manager     |  |  Retry & Fallback      |  | Scheduler / Queue   |  |
|  |  (HITL Pause/Resume)   |  |  (Fault Tolerance)     |  | (Task Management)   |  |
|  +------------------------+  +------------------------+  +---------------------+  |
+-----------------------------------------------------------------------------------+
                                         │
                                         ▼
+-----------------------------------------------------------------------------------+
|                                3. Agent Core Layer                                |
|   [ Planner Node ]        [ Prompt Template Engine ]     [ Output Guardrail ]     |
|   (ReAct / Plan-Execute)  (Dynamic Prompt Compiler)     (Validation/Sanitize)    |
+-----------------------------------------------------------------------------------+
                      │                                      │
                      ▼                                      ▼
+-----------------------------------+  +--------------------------------------------+
|     4. External Integration Layer |  |         5. Persistence & Memory Layer      |
|  +-----------------------------+  |  |  +------------------+ +-----------------+  |
|  | LLM Provider Adapter        |  |  |  | Checkpointer DB  | | Vector DB       |  |
|  | (OpenAI / Anthropic / Local)|  |  |  | (State Engine DB)| | (RAG Memory)    |  |
|  +-----------------------------+  |  |  +------------------+ +-----------------+  |
|  +-----------------------------+  |  |  +------------------+ +-----------------+  |
|  | Tool Executor Sandbox       |  |  |  | Execution Log    | | Redis Cache     |  |
|  | (REST API / Code / DB)      |  |  |  | (Tracing/Audit)  | | (Lock/Session)  |  |
|  +-----------------------------+  |  |  +------------------+ +-----------------+  |
+-----------------------------------+  +--------------------------------------------+

💡 [핵심 설명]
* Orchestration Layer: 특정 에이전트 논리와 독립된 순수 그래프 실행 엔진입니다. 노드 간 의존성을 분석하고 상태 전이를 제어합니다.
* Interrupt Manager: Human-in-the-Loop 구현의 핵심 모듈로, 작업 상태를 멈추고 외부 승인 이벤트를 대기(Polling 또는 Webhook)하도록 지원합니다.
* LLM Provider Adapter: 파사드(Facade) 패턴을 적용하여 향후 LLM 모델이 변경되어도 엔진 코드가 영향받지 않도록 추상화합니다.
2. 워크플로우 상태 전이 다이어그램 (Workflow State Machine / DAG)
에이전트가 의도를 파악하고, 도구를 실행하며, 예외와 사용자 승인을 거쳐 완료되는 전체 상태 흐름(State Transition)입니다.
       [ START ]
           │
           ▼
  ( State: INIT )
  ┌─────────────────┐
  │  Intent Parser  │ ◄─────────────┐ (Re-planning / Retry)
  └────────┬────────┘               │
           │                        │
           ▼                        │
   / Dynamic Plan /                 │
  /  Generated?  /                  │
   ────────┬─────                   │
           │                        │
           ▼                        │
  ┌─────────────────┐               │
  │ Tool Selector   │               │
  └────────┬────────┘               │
           │                        │
           ▼                        │
   / Requires Human / ──── YES ───► ┌──────────────────────┐
  /   Approval?    /                │ State: WAITING_HUMAN │
   ────────┬─────                   └──────────┬───────────┘
           │ NO                                │ (User Approve)
           ▼                                   ▼
  ┌─────────────────┐               ┌──────────────────────┐
  │ Tool Execution  │ ── (Fail) ──► │  Fallback Router     │
  └────────┬────────┘               └──────────┬───────────┘
           │ (Success)                         │
           │                                   ├─ Retry Count < Max ──┘
           │                                   └─ Exceed ──► [ ERROR Node ] ──► [ END ]
           ▼
  ┌─────────────────┐
  │ Answer Synthesizer│
  └────────┬────────┘
           │
           ▼
   / Output Valid? / ──── NO ─────► [ Re-prompting Node ] ──┘
   ────────┬─────
           │ YES
           ▼
     ( State: DONE )
           │
           ▼
        [ END ]

💡 [핵심 설명]
* Explicit State Control: 노드 간 상태는 전역 Context 객체(WorkflowState)로 전달되며, 각 단계는 성공/실패 여부에 따라 정해진 조건부 엣지(Conditional Edge)를 통해 상태가 이동합니다.
* State Interruption: WAITING_HUMAN 상태에 진입하면 스레드 작업이 종료되는 것이 아니라, 현재 실행 지점(Checkpoint ID)을 DB에 저장하고 프로세스 자원을 반환(Yield)합니다.
3. 실행 시퀀스 다이어그램 (Runtime Sequence Diagram)
유저의 요청 처리부터 비동기 도구 실행, 스냅샷 저장, 승인 대기 및 재개 프로세스를 시간 순서대로 명시한 시퀀스입니다.
[User]       [API Gate]    [Workflow Engine]  [Checkpointer]   [LLM Adapter]   [External Tool]
  │              │                 │                 │               │                │
  │ 1. Task 제출  │                 │                 │               │                │
  ├─────────────►│ 2. Job 생성     │                 │               │                │
  │              ├────────────────►│                 │               │                │
  │              │                 │ 3. Init State   │               │                │
  │              │                 ├────────────────►│               │                │
  │              │                 │                 │               │                │
  │              │                 │ 4. Prompt 전달  │               │                │
  │              │                 ├────────────────────────────────►│                │
  │              │                 │ 5. Tool Call 응답               │                │
  │              │                 │◄────────────────────────────────┤                │
  │              │                 │                 │               │                │
  │              │                 │ 6. [위험 감지] HITL Interrupt 발생                │
  │              │                 │ 7. Save State (WAITING_HUMAN)   │                │
  │              │                 ├────────────────►│               │                │
  │ 8. 승인 요청  │                 │                 │               │                │
  │◄─────────────┴─────────────────┤                 │               │                │
  │                                │                 │               │                │
  │ 9. 사용자 승인 (Resume Token)  │                 │               │                │
  ├───────────────────────────────►│                 │               │                │
  │                                │ 10. Load State  │               │                │
  │                                ├────────────────►│               │                │
  │                                │                 │               │                │
  │                                │ 11. Tool 실행 요청              │                │
  │                                ├─────────────────────────────────────────────────►│
  │                                │ 12. Tool 결과 반환              │                │
  │                                │◄─────────────────────────────────────────────────┤
  │                                │                 │               │                │
  │                                │ 13. 최종 응답 생성              │                │
  │                                ├────────────────────────────────►│                │
  │ 14. 결과 전달  │               │                 │               │                │
  │◄─────────────┴─────────────────┤ 15. Save State (COMPLETED)      │                │
  │              │                 ├────────────────►│               │                │

💡 [핵심 설명]
* State Restoration: 사용자 승인이 완료되었을 때 이전 상태를 복원(Resume)하기 위해 Resume Token 또는 Thread ID + Checkpoint ID를 매개변수로 전달받아 엔진이 10번 단계에서 상태를 복구합니다.
* Non-blocking Execution: 외부 승인 대기 단계(6~9번)에서 서버가 대기 상태로 유지되지 않고, 상태 저장 후 메모리에서 내려가므로 수만 명의 사용자 동시 처리가 가능한 비동기형 솔루션 구조를 형성합니다.
4. 상태 스키마 (State Schema) 명세

{
  "thread_id": "thr_20261014_001",
  "checkpoint_id": "chk_step_03",
  "status": "WAITING_HUMAN",
  "context": {
    "user_input": "100만원 계좌 이체해줘",
    "plan": ["check_balance", "transfer_money"],
    "current_step": 1,
    "tool_calls": [
      {
        "tool_name": "transfer_money",
        "args": {"amount": 1000000, "target": "123-456"},
        "is_risk": true
      }
    ],
    "tool_results": {
      "check_balance": {"balance": 5000000}
    }
  },
  "retry_count": 0,
  "max_retries": 3,
  "error_log": null
}

📝 요약
* 문제 제기: AI 에이전트는 일반적인 비순환 그래프(DAG)와 달리 반복성(Loop), 비결정성(LLM 판단), 인적 개입(HITL)이라는 특성을 가짐.
* 해결책 (설계 포인트):
   * 컴포넌트 분리: 실행 엔진(Orchestrator)과 판단 로직(Agent Core)을 분리.
   * Checkpointer DB 기반 상태 지속성: 장애 복구 및 Pause/Resume을 지원.
   * 안전 구역(Guardrail) 및 승인 레이어 구축: 실수 및 환각 방지.
* 결론: 본 솔루션 아키텍처는 에이전트의 자율성을 보장함과 동시에 정적 그래프 엔진의 안정성 및 추적 가능성(Observability)을 확보할 수 있도록 설계됨.







1. 계층형 아키텍처 레이어별 상세 명세
시스템 아키텍처 다이어그램의 5개 레이어는 "엔진의 스케일아웃 가능성, 결합도 분리(Decoupling), 상태 복구력(Resilience)"을 확보하기 위해 구체적으로 다음과 같이 동작합니다.
① Client & Interface Layer (입출력 접점)
엔진 외부와의 통신 프로토콜을 다루는 레이어로, 다양한 클라이언트 환경(웹, 앱, 외부 시스템)을 수용합니다.
* REST / GraphQL API: 워크플로우의 생성, 상태 조회, 단발성 실행 요청을 처리합니다. GraphQL은 복잡한 워크플로우의 실행 상태 데이터를 선택적으로 조회할 때 유리합니다.
* Webhook Receiver: 외부 시스템에서 발생하는 이벤트(예: 제3자 서비스 콜백, 이메일 승인 링크 클릭 등)를 수신하여 일시 중단된 워크플로우를 재개(Resume)시키는 창구입니다.
* Event Stream (SSE / WebSocket): LLM의 실시간 토큰 생성 과정(Streaming) 및 워크플로우의 노드별 진척 상황(Node-by-Node Status)을 사용자 클라이언트에 실시간으로 밀어주는(Push) 역할을 합니다.
② Workflow Orchestration Layer Engine (핵심 엔진)
에이전트 판단 로직과 분리되어, 워크플로우의 실행 제어 및 상태 변화만 전담하는 순수 물리 엔진입니다.
* DAG / Graph Engine (Graph Traversal): 정의된 노드(Node) 간의 의존성 및 조건부 엣지(Conditional Edge)를 해석하여 다음 실행할 노드를 결정하는 핵심 알고리즘 모듈입니다.
* State Manager: 노드가 실행될 때마다 전달되는 전역 Context 데이터의 일관성을 유지하고, 메모리 상의 상태 변화를 아래의 Checkpointer DB와 수시로 동기화합니다.
* Node Dispatcher: 결정된 노드를 실제로 실행하기 위해 작업(Task)을 비동기 Worker 쓰레드/프로세스에 할당하는 배치기입니다.
* Interrupt Manager: Human-in-the-Loop(HITL) 이벤트 발생 시 현재 노드의 실행을 안전하게 정지(Pause)시키고, 승인 토큰이 들어올 때까지 대기 상태(Waiting Status)로 전환하는 핵심 모듈입니다.
* Retry & Fallback: 도구 호출 실패, API 타임아웃, LLM 응답 오류 시 지수 백오프(Exponential Backoff) 기반 재시도를 수행하거나 미리 정의된 대체 노드(Fallback Node)로 라우팅합니다.
* Scheduler / Queue: 분산 환경에서 다량의 워크플로우 요청이 들어왔을 때 작업 우선순위를 관리하고 Worker 들에 부하를 분산(RabbitMQ, Redis Streams 기반)합니다.
③ Agent Core Layer (추론 및 판단 단)
순수 오케스트레이션 엔진 위에서 "LLM의 지능"을 이용해 어떤 행동을 할지 결정하는 논리 레이어입니다.
* Planner Node: 복잡한 목표(Goal)를 받았을 때 하위 작업(Sub-tasks)으로 분해하는 역할을 합니다. ReAct(Reasoning + Acting) 패턴이나 Plan-and-Solve 알고리즘이 적용됩니다.
* Prompt Template Engine: 전역 State 데이터, 대화 이력, RAG 검색 결과 등을 조합하여 현재 노드 목적에 맞는 최적의 프롬프트를 동적으로 구성(Compile)합니다.
* Output Guardrail: LLM이 반환한 결과물이 정해진 JSON 스키마를 준수하는지, 환각(Hallucination)이나 유해성/개인정보(PII) 유출이 없는지 검증하고, 검증 실패 시 재요청(Re-prompting)을 유도합니다.
④ External Integration Layer (외부 연동)
LLM 및 외부 시스템과의 직접적인 통신을 전담하며, 엔진 내부 로직을 외부 환경 변화로부터 보호합니다.
* LLM Provider Adapter: OpenAI, Anthropic, HuggingFace local LLM 등 다양한 파운데이션 모델의 API 인터페이스를 하나로 추상화한 파사드(Facade) 모듈입니다. (토큰 단가 계산, API Rate Limit 제어 포함)
* Tool Executor Sandbox: 에이전트가 실행하는 Python 코드, DB 쿼리, REST API 호출 등의 도구(Tool)를 보안상 격리된 환경(Docker Container, WebAssembly 등)에서 실행하여 메인 서버 시스템을 보호합니다.
⑤ Persistence & Memory Layer (영속성 및 저장소)
모든 비동기 작업과 인텔리전스 데이터가 저장되는 하부 구조입니다.
* Checkpointer DB: 매 노드 전환 시점마다 전체 State 데이터의 스냅샷을 저장합니다. 시스템 장애 시 마지막 성공 지점부터 복구(Crash Recovery)하거나 time-traveling(이전 상태로 되돌리기)을 가능하게 합니다.
* Vector DB: 에이전트의 장기 기억(Long-term Memory) 시스템입니다. 과거의 유사 작업 이력이나 RAG용 지식 문서를 엔베딩하여 유사도 검색을 제공합니다.
* Execution Log: OpenTelemetry 규격을 준수하여 모든 노드의 입출력, 실행 시간, 토큰 사용량, Trace ID를 기록하여 가시성(Observability)을 확보합니다.
* Redis Cache: 동시성 제어를 위한 Distributed Lock(분산 락)을 관리하고, 노드 간 자주 쓰이는 임시 상태 데이터를 고속 메모리에 캐싱합니다.


2. DAG(Directed Acyclic Graph) 개념과 AI 에이전트 프레임워크에서의 의미
"전통적인 DAG(Airflow 등)"와 "AI 에이전트에서의 Graph(LangGraph 등)"의 차이를 언급하는 것이 핵심 포인트입니다.
[ 전통적 DAG (Data Pipeline) ]           [ AI Agent Graph (Dynamic / Cyclic) ]

     (A) -> (B) -> (C)                      (A) ---> (B) ---> (C)
             │      │                                 │        │ (LLM 판단: 불충분)
             ▼      ▼                                 └────────┘ (Loop 발생)
            (D)    (E)                                   ▲
                                                         │ (Human Reject)
(방향성 있음, 순환 없음/Loop 불가)                     (D) ────────┘

① 전통적인 DAG의 특징과 한계
* Acyclic (비순환): 화살표가 한 방향으로만 흐르며, 자기 자신이나 이전 노드로 돌아오는 순환(Loop)이 절대 불가능합니다.
* Deterministic (정적/결정론적): 파이프라인이 시작되기 전에 실행할 노드의 순서와 구조가 100% 고정되어 있습니다. (예: Apache Airflow, Kubeflow)
* 한계: AI 에이전트는 "LLM의 판단 결과에 따라 이전 작업으로 돌아가 재시도(Re-plan/Retry)"하거나 "유저가 만족할 때까지 대화를 반복(Loop)"해야 하는데, 전통적 DAG 모듈은 이 순환 구조를 처리하지 못하고 에러를 발생시킵니다.
② AI 에이전트 프레임워크에서의 해결책: Cyclic State Graph
에이전트 엔진은 DAG의 단방향 제약을 극복하기 위해 "상태 기반 순환 그래프(Cyclic State Graph)" 구조를 도입합니다.
* Loop(순환)의 공식 허용:
   * Evaluator Node에서 결과가 NG이면 다시 Generator Node로 되돌아가는 Feedback Loop를 허용합니다.
* 무한 루프 방지 (Cycle Limit Guardrail):
   * 그래프 내부 순환이 무한히 발생하지 않도록, State 내부에 recursion_limit (예: 최대 20회 반복) 매개변수를 두고 이를 초과하면 강제로 Error handling Node로 분기시킵니다.
* 조건부 엣지(Conditional Edge)를 통한 동적 분기:
   * 실행 시점에 LLM의 Output(JSON/Enum)을 매핑하여 다음 이동할 노드 ID를 정적으로 결정하지 않고 동적으로 계산합니다.

> 💡 요약 서술
> *"본 설계 아키텍처의 오케스트레이터는 전통적인 단방향 DAG의 한계를 넘어, 조건부 엣지(Conditional Edge)와 무한 루프 가드레일을 결합한 상태 기반 순환 그래프(Cyclic State Graph)*를 지원함으로써 에이전트의 피드백 루프 및 자율적 재계획(Re-planning) 능력을 정교하게 제어하도록 구축되었습니다."
>


쿠버네티스 기본 개념에 대해 알아보려고 한다.

 

Pod

쿠버네티스 노드는 여러 Pod로 구성될 수 있고, 각 Pod는 여러 Container를 가질 수 있다.

@cloudshell:~ (proud-curve)$ kubectl explain pods
KIND:       Pod
VERSION:    v1

DESCRIPTION:
    Pod is a collection of containers that can run on a host. This resource is
    created by clients and scheduled onto hosts.
    
FIELDS:
  apiVersion    <string>
    APIVersion defines the versioned schema of this representation of an object.
    Servers should convert recognized schemas to the latest internal value, and
    may reject unrecognized values. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

  kind  <string>
    Kind is a string value representing the REST resource this object
    represents. Servers may infer this from the endpoint the client submits
    requests to. Cannot be updated. In CamelCase. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

  metadata      <ObjectMeta>
    Standard object's metadata. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  spec  <PodSpec>
    Specification of the desired behavior of the pod. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

  status        <PodStatus>
    Most recently observed status of the pod. This data may not be up to date.
    Populated by the system. Read-only. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

 

한 Pod에 있는 컨테이너들은 리소스를 공유하고 같은 Pod에 있는 컨테이너들은 내부 IP를 통해 통신한다.

 

1. get pods를 통해 pod 리스트를 확인할 수 있다.

@cloudshell:~ (proud-curve)$ kubectl get pods
NAME                                  READY   STATUS    RESTARTS   AGE
hello-world-rest-api-8c7449fd-f7lh2   1/1     Running   0          16h
@cloudshell:~ (proud-curve)$ kubectl get pods -o wide
NAME                                  READY   STATUS    RESTARTS   AGE   IP          NODE                                                 NOMINATED NODE   READINESS GATES
hello-world-rest-api-8c7449fd-f7lh2   1/1     Running   0          16h   10.44.1.5   gke-in28minutes-cluster-default-pool-24f9f7fb-xq70   <none>           <none>

 

 

2. 특정 pod에 대해 상세 조회를 하려면 1번 단계에서 확인한 pod name을 이용해서 kubectl describe pod를 하면 된다.

@cloudshell:~ (proud-curve)$ kubectl describe pod hello-world-rest-api-8c7449fd-f7lh2
Name:             hello-world-rest-api-8c7449fd-f7lh2
Namespace:        default
Priority:         0
Service Account:  default
Node:             gke-in28minutes-cluster-default-pool-24f9f7fb-xq70/10.128.0.7
Start Time:       Sun, 16 Feb 2025 03:44:56 +0000
Labels:           app=hello-world-rest-api
                  pod-template-hash=8c7449fd
Annotations:      cloud.google.com/cluster_autoscaler_unhelpable_since: 2025-02-15T13:37:43+0000
                  cloud.google.com/cluster_autoscaler_unhelpable_until: Inf
Status:           Running
IP:               10.44.1.5
IPs:
  IP:           10.44.1.5
Controlled By:  ReplicaSet/hello-world-rest-api-8c7449fd
Containers:
  hello-world-rest-api:
    Container ID:   containerd://a5f84557e8c2e25fe5ba0e04bcabac8f9b77cbfbb491b98846169c71687f5c1e
    Image:          in28min/hello-world-rest-api:0.0.1.RELEASE
    Image ID:       docker.io/in28min/hello-world-rest-api@sha256:00469c343814aabe56ad1034427f546d43bafaaa11208a1eb0720993743f72be
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sun, 16 Feb 2025 03:45:15 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-d2tdw (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True 
  Initialized                 True 
  Ready                       True 
  ContainersReady             True 
  PodScheduled                True 
Volumes:
  kube-api-access-d2tdw:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:                      <none>

 

여기에서 Labels에 있는 이름이 중요하다. 나중에 리플리카셋이나 서비스 등으로 Pod을 묶을 때 쓰일 수 있다. 그리고 Annotation은 Pod에 대한 메타 정보이다.

 

 

 

Replicaset

리플리카셋은 Pod를 모니터링 하고 있다가 필요한 Pod 수보다 줄어들게 되면 다시 Pod를 생성한다. 특정 수 이상의 Pod이 실행되도록 만들어준다.

@cloudshell:~ (proud-curve)$ kubectl explain replicaset
GROUP:      apps
KIND:       ReplicaSet
VERSION:    v1

DESCRIPTION:
    ReplicaSet ensures that a specified number of pod replicas are running at
    any given time.
    
FIELDS:
  apiVersion    <string>
    APIVersion defines the versioned schema of this representation of an object.
    Servers should convert recognized schemas to the latest internal value, and
    may reject unrecognized values. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

  kind  <string>
    Kind is a string value representing the REST resource this object
    represents. Servers may infer this from the endpoint the client submits
    requests to. Cannot be updated. In CamelCase. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

  metadata      <ObjectMeta>
    If the Labels of a ReplicaSet are empty, they are defaulted to be the same
    as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More
    info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  spec  <ReplicaSetSpec>
    Spec defines the specification of the desired behavior of the ReplicaSet.
    More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

  status        <ReplicaSetStatus>
    Status is the most recently observed status of the ReplicaSet. This data may
    be out of date by some window of time. Populated by the system. Read-only.
    More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

 

 

1. 리플리카셋 리스트를 여러 명령어로 조회할 수 있다.

@cloudshell:~ (proud-curve)$ kubectl get replicasets
NAME                            DESIRED   CURRENT   READY   AGE
hello-world-rest-api-8c7449fd   1         1         1       17h
@cloudshell:~ (proud-curve)$ kubectl get replicaset
NAME                            DESIRED   CURRENT   READY   AGE
hello-world-rest-api-8c7449fd   1         1         1       17h
@cloudshell:~ (proud-curve)$ kubectl get rs
NAME                            DESIRED   CURRENT   READY   AGE
hello-world-rest-api-8c7449fd   1         1         1       17h

 

 

2. Pod을 삭제하면 다른 이름으로 자동으로 Pod이 생성되는 것을 확인할 수 있는데, 이 역할을 하는 것이 Replicaset이라고 할 수 있다.

@cloudshell:~ (proud-curve)$ kubectl get pods
NAME                                  READY   STATUS    RESTARTS   AGE
hello-world-rest-api-8c7449fd-f7lh2   1/1     Running   0          17h
@cloudshell:~ (proud-curve)$ kubectl delete pod hello-world-rest-api-8c7449fd-f7lh2
pod "hello-world-rest-api-8c7449fd-f7lh2" deleted
@cloudshell:~ (proud-curve)$ kubectl get pods
NAME                                  READY   STATUS    RESTARTS   AGE
hello-world-rest-api-8c7449fd-bw2hn   1/1     Running   0          5s

 

 

3. pod을 3개로 늘려보자. 인스턴스가 3개로 늘어났다고 볼 수 있다.

@cloudshell:~ (proud-curve)$ kubectl scale deployment hello-world-rest-api --replicas=3
deployment.apps/hello-world-rest-api scaled
@cloudshell:~ (proud-curve)$ kubectl get pods
NAME                                  READY   STATUS              RESTARTS   AGE
hello-world-rest-api-8c7449fd-bw2hn   1/1     Running             0          5m47s
hello-world-rest-api-8c7449fd-fzg7d   0/1     ContainerCreating   0          4s
hello-world-rest-api-8c7449fd-mgg96   0/1     ContainerCreating   0          4s
@cloudshell:~ (proud-curve-451012-r7)$ kubectl get rs
NAME                            DESIRED   CURRENT   READY   AGE
hello-world-rest-api-8c7449fd   3         3         3       17h

 

 

4. Pod이 3개로 늘어나고 나서 hello-world 서비스를 브라우저로 접근하면 Pod가 돌아가면서 동작하여 서비스가 됨을 알 수 있다.

 

 

5. kubectl scale deployment를 통해 내부적으로 어떤 동작이 있었는지 보기 위해 event를 확인해보면 pod 2개가 추가로 생성된 것을 확인할 수 있다.

@cloudshell:~ (proud-curve)$ kubectl get events --sort-by=.metadata.creationTimestamp
LAST SEEN   TYPE     REASON              OBJECT                                     MESSAGE
17m         Normal   Scheduled           pod/hello-world-rest-api-8c7449fd-bw2hn    Successfully assigned default/hello-world-rest-api-8c7449fd-bw2hn to gke-in28minutes-cluster-default-pool-24f9f7fb-xq70
17m         Normal   Killing             pod/hello-world-rest-api-8c7449fd-f7lh2    Stopping container hello-world-rest-api
17m         Normal   SuccessfulCreate    replicaset/hello-world-rest-api-8c7449fd   Created pod: hello-world-rest-api-8c7449fd-bw2hn
17m         Normal   Pulled              pod/hello-world-rest-api-8c7449fd-bw2hn    Container image "in28min/hello-world-rest-api:0.0.1.RELEASE" already present on machine
17m         Normal   Created             pod/hello-world-rest-api-8c7449fd-bw2hn    Created container hello-world-rest-api
17m         Normal   Started             pod/hello-world-rest-api-8c7449fd-bw2hn    Started container hello-world-rest-api
12m         Normal   SuccessfulCreate    replicaset/hello-world-rest-api-8c7449fd   Created pod: hello-world-rest-api-8c7449fd-fzg7d
12m         Normal   ScalingReplicaSet   deployment/hello-world-rest-api            Scaled up replica set hello-world-rest-api-8c7449fd to 3 from 1
12m         Normal   Scheduled           pod/hello-world-rest-api-8c7449fd-fzg7d    Successfully assigned default/hello-world-rest-api-8c7449fd-fzg7d to gke-in28minutes-cluster-default-pool-24f9f7fb-vzdc
12m         Normal   Scheduled           pod/hello-world-rest-api-8c7449fd-mgg96    Successfully assigned default/hello-world-rest-api-8c7449fd-mgg96 to gke-in28minutes-cluster-default-pool-24f9f7fb-6q96
12m         Normal   SuccessfulCreate    replicaset/hello-world-rest-api-8c7449fd   Created pod: hello-world-rest-api-8c7449fd-mgg96
12m         Normal   Pulling             pod/hello-world-rest-api-8c7449fd-fzg7d    Pulling image "in28min/hello-world-rest-api:0.0.1.RELEASE"
12m         Normal   Pulling             pod/hello-world-rest-api-8c7449fd-mgg96    Pulling image "in28min/hello-world-rest-api:0.0.1.RELEASE"
12m         Normal   Created             pod/hello-world-rest-api-8c7449fd-fzg7d    Created container hello-world-rest-api
12m         Normal   Started             pod/hello-world-rest-api-8c7449fd-mgg96    Started container hello-world-rest-api
12m         Normal   Created             pod/hello-world-rest-api-8c7449fd-mgg96    Created container hello-world-rest-api
12m         Normal   Pulled              pod/hello-world-rest-api-8c7449fd-mgg96    Successfully pulled image "in28min/hello-world-rest-api:0.0.1.RELEASE" in 3.116s (3.116s including waiting). Image size: 88504325 bytes.
12m         Normal   Started             pod/hello-world-rest-api-8c7449fd-fzg7d    Started container hello-world-rest-api
12m         Normal   Pulled              pod/hello-world-rest-api-8c7449fd-fzg7d    Successfully pulled image "in28min/hello-world-rest-api:0.0.1.RELEASE" in 3.163s (3.163s including waiting). Image size: 88504325 bytes.

 

 

Deployment

다운타임 없이 새로운 버전을 배포할 수 있도록 Pod와 Replicaset의 배포를 관리한다.

@cloudshell:~ (proud-curve)$ kubectl explain deployment
GROUP:      apps
KIND:       Deployment
VERSION:    v1

DESCRIPTION:
    Deployment enables declarative updates for Pods and ReplicaSets.
    
FIELDS:
  apiVersion    <string>
    APIVersion defines the versioned schema of this representation of an object.
    Servers should convert recognized schemas to the latest internal value, and
    may reject unrecognized values. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

  kind  <string>
    Kind is a string value representing the REST resource this object
    represents. Servers may infer this from the endpoint the client submits
    requests to. Cannot be updated. In CamelCase. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

  metadata      <ObjectMeta>
    Standard object's metadata. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  spec  <DeploymentSpec>
    Specification of the desired behavior of the Deployment.

  status        <DeploymentStatus>
    Most recently observed status of the Deployment.

 

 

1. 애플리케이션 도커 이미지를 변경한다.

@cloudshell:~ (proud-curve)$ kubectl get rs -o wide
NAME                            DESIRED   CURRENT   READY   AGE   CONTAINERS             IMAGES                                       SELECTOR
hello-world-rest-api-8c7449fd   3         3         3       17h   hello-world-rest-api   in28min/hello-world-rest-api:0.0.1.RELEASE   app=hello-world-rest-api,pod-template-hash=8c7449fd
@cloudshell:~ (proud-curve)$ kubectl set image deployment hello-world-rest-api hello-world-rest-api=in28min/hello-world-rest-api:0.0.2.RELEASE
deployment.apps/hello-world-rest-api image updated

 

 

2. 이미지 변경을 하게 되면 애플리케이션 버전2로 deployment 된 것인데, 이 과정에서 replicaset은 새로운 이름으로 생성이 되고 pod도 새 버전의 애플리케이션을 위한 pod이 구성된다.

@cloudshell:~ (proud-curve)$ kubectl get rs -o wide
NAME                             DESIRED   CURRENT   READY   AGE   CONTAINERS             IMAGES                                       SELECTOR
hello-world-rest-api-89b8d4c97   3         3         3       25s   hello-world-rest-api   in28min/hello-world-rest-api:0.0.2.RELEASE   app=hello-world-rest-api,pod-template-hash=89b8d4c97
hello-world-rest-api-8c7449fd    0         0         0       18h   hello-world-rest-api   in28min/hello-world-rest-api:0.0.1.RELEASE   app=hello-world-rest-api,pod-template-hash=8c7449fd
@cloudshell:~ (proud-curve)$ kubectl get pods -o wide
NAME                                   READY   STATUS    RESTARTS   AGE   IP          NODE                                                 NOMINATED NODE   READINESS GATES
hello-world-rest-api-89b8d4c97-6l564   1/1     Running   0          44s   10.44.1.9   gke-in28minutes-cluster-default-pool-24f9f7fb-xq70   <none>           <none>
hello-world-rest-api-89b8d4c97-6w9w5   1/1     Running   0          41s   10.44.2.8   gke-in28minutes-cluster-default-pool-24f9f7fb-6q96   <none>           <none>
hello-world-rest-api-89b8d4c97-m88z8   1/1     Running   0          39s   10.44.0.8   gke-in28minutes-cluster-default-pool-24f9f7fb-vzdc   <none>           <none>

 

 

3. 내부적으로 일어난 동작을 보면 새로운 relicaset을 먼저 생성한 후 pod 1개를 먼저 생성하여 image를 내려받아 컨테이너를 실행하고 기존 replicaset의 개수를 2로 줄이면서 기존 container를 중지하고 pod을 삭제한다. 이 작업을 replicas 개수만큼 3번만큼 진행하여 기존 relicaset에서 실행되던 pod을 순차적으로 신규 relicaset으로 이관되게 된다. 롤링 업데이트 전략이 적용된 예시이다.

@cloudshell:~ (proud-curve)$ kubectl get events --sort-by=.metadata.creationTimestamp
LAST SEEN   TYPE     REASON              OBJECT                                      MESSAGE
...
6m36s       Normal   SuccessfulCreate    replicaset/hello-world-rest-api-89b8d4c97   Created pod: hello-world-rest-api-89b8d4c97-6l564
6m35s       Normal   Scheduled           pod/hello-world-rest-api-89b8d4c97-6l564    Successfully assigned default/hello-world-rest-api-89b8d4c97-6l564 to gke-in28minutes-cluster-default-pool-24f9f7fb-xq70
6m36s       Normal   ScalingReplicaSet   deployment/hello-world-rest-api             Scaled up replica set hello-world-rest-api-89b8d4c97 to 1
6m35s       Normal   Pulling             pod/hello-world-rest-api-89b8d4c97-6l564    Pulling image "in28min/hello-world-rest-api:0.0.2.RELEASE"
6m34s       Normal   Started             pod/hello-world-rest-api-89b8d4c97-6l564    Started container hello-world-rest-api
6m34s       Normal   Created             pod/hello-world-rest-api-89b8d4c97-6l564    Created container hello-world-rest-api
6m34s       Normal   Pulled              pod/hello-world-rest-api-89b8d4c97-6l564    Successfully pulled image "in28min/hello-world-rest-api:0.0.2.RELEASE" in 1.45s (1.45s including waiting). Image size: 88504316 bytes.
6m33s       Normal   Pulling             pod/hello-world-rest-api-89b8d4c97-6w9w5    Pulling image "in28min/hello-world-rest-api:0.0.2.RELEASE"
6m33s       Normal   ScalingReplicaSet   deployment/hello-world-rest-api             Scaled down replica set hello-world-rest-api-8c7449fd to 2 from 3
6m33s       Normal   Killing             pod/hello-world-rest-api-8c7449fd-bw2hn     Stopping container hello-world-rest-api
6m33s       Normal   SuccessfulDelete    replicaset/hello-world-rest-api-8c7449fd    Deleted pod: hello-world-rest-api-8c7449fd-bw2hn
6m33s       Normal   Scheduled           pod/hello-world-rest-api-89b8d4c97-6w9w5    Successfully assigned default/hello-world-rest-api-89b8d4c97-6w9w5 to gke-in28minutes-cluster-default-pool-24f9f7fb-6q96
6m33s       Normal   ScalingReplicaSet   deployment/hello-world-rest-api             Scaled up replica set hello-world-rest-api-89b8d4c97 to 2 from 1
6m33s       Normal   SuccessfulCreate    replicaset/hello-world-rest-api-89b8d4c97   Created pod: hello-world-rest-api-89b8d4c97-6w9w5
6m32s       Normal   Started             pod/hello-world-rest-api-89b8d4c97-6w9w5    Started container hello-world-rest-api
6m32s       Normal   Created             pod/hello-world-rest-api-89b8d4c97-6w9w5    Created container hello-world-rest-api
6m32s       Normal   Pulled              pod/hello-world-rest-api-89b8d4c97-6w9w5    Successfully pulled image "in28min/hello-world-rest-api:0.0.2.RELEASE" in 879ms (879ms including waiting). Image size: 88504316 bytes.
6m31s       Normal   Scheduled           pod/hello-world-rest-api-89b8d4c97-m88z8    Successfully assigned default/hello-world-rest-api-89b8d4c97-m88z8 to gke-in28minutes-cluster-default-pool-24f9f7fb-vzdc
6m31s       Normal   Pulling             pod/hello-world-rest-api-89b8d4c97-m88z8    Pulling image "in28min/hello-world-rest-api:0.0.2.RELEASE"
6m31s       Normal   SuccessfulDelete    replicaset/hello-world-rest-api-8c7449fd    Deleted pod: hello-world-rest-api-8c7449fd-mgg96
6m31s       Normal   Killing             pod/hello-world-rest-api-8c7449fd-mgg96     Stopping container hello-world-rest-api
6m31s       Normal   SuccessfulCreate    replicaset/hello-world-rest-api-89b8d4c97   Created pod: hello-world-rest-api-89b8d4c97-m88z8
6m31s       Normal   ScalingReplicaSet   deployment/hello-world-rest-api             Scaled down replica set hello-world-rest-api-8c7449fd to 1 from 2
6m31s       Normal   ScalingReplicaSet   deployment/hello-world-rest-api             Scaled up replica set hello-world-rest-api-89b8d4c97 to 3 from 2
6m30s       Normal   Started             pod/hello-world-rest-api-89b8d4c97-m88z8    Started container hello-world-rest-api
6m30s       Normal   Pulled              pod/hello-world-rest-api-89b8d4c97-m88z8    Successfully pulled image "in28min/hello-world-rest-api:0.0.2.RELEASE" in 856ms (856ms including waiting). Image size: 88504316 bytes.
6m30s       Normal   Created             pod/hello-world-rest-api-89b8d4c97-m88z8    Created container hello-world-rest-api
6m29s       Normal   Killing             pod/hello-world-rest-api-8c7449fd-fzg7d     Stopping container hello-world-rest-api
6m29s       Normal   SuccessfulDelete    replicaset/hello-world-rest-api-8c7449fd    Deleted pod: hello-world-rest-api-8c7449fd-fzg7d
6m29s       Normal   ScalingReplicaSet   deployment/hello-world-rest-api             Scaled down replica set hello-world-rest-api-8c7449fd to 0 from 1

anaconda는 데이터 사이언스, 머신 러닝과 AI에 활용할 수 있는 여러 패키지를 찾고 설치할 수 있게 도와주는 파이썬 배포판 플랫폼으로 오픈소스이다. 설치를 하면 command line 도구와 GUI로 된 Anaconda Navigator를 이용하여 환경과  패키지를 관리할 수 있다.
 
1. https://www.anaconda.com/download 접속하여 설치 파일을 다운로드 한다. 이때 가입 폼이 있지만 Skip registration을 클릭하여 굳이 가입하지 않고도 다운로드가 가능하다.

anaconda download

 
 
2. OS에 따라 설치 파일이 다르고, Mac의 경우 인텔 칩이냐 애플 실리콘 칩이냐 그리고 GUI가 있는 installer인지 command line installer인지 선택하여 설치 파일을 다운로드 할 수 있다. 설치 편의성을 위해 Graphical Installer를 다운로드 하였다.

anaconda installation on mac

 
 
3. Graphical Installer는 .pkg 파일이 다운로드 되니 더블 클릭하여 일반적인 맥용 설치 파일처럼 동의하고 패스워드 넣고 설치하면 된다. 설치를 하면 command line interface인 conda와 데스크탑 애플리케이션인 Anaconda Navigator를 이용하여 개발 환경과 패키지를 관리할 수 있다.

anaconda distribution Graphical installer

 
 
4. 설치 후에 터미널에서 conda 명령어를 사용할 수 있다. 그런데 설치가 다 되었음에도 명령어를 인식하지 못할 수도 있다.

lazy@MacBook-M1-Pro ~ % conda 
zsh: command not found: conda

 
이런 경우, 공식 가이드 문서 troubleshooting(https://docs.anaconda.com/reference/troubleshooting/#conda-command-not-found-on-macos-linux)에 보면 몇 가지 원인과 해결 방법을 안내해 둔 내용을 참고하여 조치를 해보면 된다. 그 중 가장 손쉽게 해볼 수 있으면서도 유력한 원인은 anaconda 설치 후 shell을 새로 실행하지 않았기 때문이다.

Conda: command not found on macOS

 
shell을 새로 열어 which conda를 해보거나 conda list와 같은 명령어를 터미널에서 실행했을 때 동작하는 것을 확인할 수 있다.

(base) lazy@MacBook-M1-Pro ~ % which conda
conda () {
	\local cmd="${1-__missing__}"
	case "$cmd" in
		(activate | deactivate) __conda_activate "$@" ;;
		(install | update | upgrade | remove | uninstall) __conda_exe "$@" || \return
			__conda_reactivate ;;
		(*) __conda_exe "$@" ;;
	esac
}
(base) lazy@MacBook-M1-Pro ~ % conda list
# packages in environment at /opt/anaconda3:
#
# Name                    Version                   Build  Channel
_anaconda_depends         2024.10         py312_openblas_0

 
 단, 기본 터미널 쉘에서 conda가 활성화되는 것을 방지하기 위해서는 아래 conda config를 설정해야 한다.

(base) lazy@MacBook-M1-Pro ~ % conda config --set auto_activate_base false

+ Recent posts