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) 능력을 정교하게 제어하도록 구축되었습니다."
>
전체 글
-
임시저장
12:26:49
- 쿠버네티스 기본 개념 Pod, Replicaset, Deployment 들여다보기 2025.03.01
- anaconda 설치 2025.02.21
임시저장
쿠버네티스 기본 개념 Pod, Replicaset, Deployment 들여다보기
쿠버네티스 기본 개념에 대해 알아보려고 한다.
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'Kubernetes' 카테고리의 다른 글
| GKE 구글 클라우드 쉘을 이용하여 애플리케이션을 쿠버네티스 클러스터에 배포하기 (0) | 2025.02.16 |
|---|---|
| GKE(구글 쿠버네티스 엔진) 클러스터 생성 (1) | 2025.02.16 |
anaconda 설치
anaconda는 데이터 사이언스, 머신 러닝과 AI에 활용할 수 있는 여러 패키지를 찾고 설치할 수 있게 도와주는 파이썬 배포판 플랫폼으로 오픈소스이다. 설치를 하면 command line 도구와 GUI로 된 Anaconda Navigator를 이용하여 환경과 패키지를 관리할 수 있다.
1. https://www.anaconda.com/download 접속하여 설치 파일을 다운로드 한다. 이때 가입 폼이 있지만 Skip registration을 클릭하여 굳이 가입하지 않고도 다운로드가 가능하다.

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

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

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을 새로 실행하지 않았기 때문이다.

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'Setting&Registration > Macbook M1 Settings' 카테고리의 다른 글
| java 설치 (1) | 2025.02.15 |
|---|---|
| nvm을 이용한 node 설치 방법 및 자주 쓰는 nvm 명령어 옵션 (0) | 2025.02.09 |
| homebrew path 설정 (0) | 2025.02.09 |