Kontiki, Python용 async 마이크로서비스 프레임워크

발행: (2026년 3월 30일 PM 07:01 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

Kontiki는 aio‑pikaasyncio를 통해 AMQP (RabbitMQ) 위에 구축된 파이썬 마이크로‑서비스 프레임워크입니다.
Nameko에 익숙한 팀이라면 전체적인 철학이 친숙하게 느껴질 것입니다:

  • RabbitMQ 위에 구축된 메시지‑드리븐 서비스
  • RPC와 이벤트를 일급 원시값으로 제공

Kontiki는 같은 “패밀리”의 프레임워크에 속하면서도 다음에 중점을 둡니다:

  • asyncio‑네이티브 구현
  • 위임‑지향 서비스 구조
  • 구성‑기반 실행기

이 프로젝트는 오픈‑소스(Apache‑2.0)이며 코드는 GitHub에서 확인할 수 있습니다:
github.com/kontiki-org/kontiki

서비스 구조

Kontiki는 서비스 클래스를 진입점 레이어로 유지하고 도메인 로직을 델리게이트(ServiceDelegate)로 이동하며 setup/start/stop 라이프사이클 훅을 사용하도록 권장합니다.

# delegate.py
from kontiki.delegate import ServiceDelegate
from kontiki.messaging import Messenger, on_event, rpc

class MyDelegate(ServiceDelegate):
    async def setup(self):
        # initialize from self.container.config
        pass

    async def start(self):
        # optional: start background tasks / open connections
        pass

    async def stop(self):
        # optional: stop background tasks / close connections
        pass

    def do_something(self, x: int) -> int:
        return x * 2
# service.py
class MyService:
    name = "compute-api"          # optional; defaults to class name
    delegate = MyDelegate()
    messenger = Messenger()       # publish events / call other services

    @rpc
    async def compute(self, x: int):
        return self.delegate.do_something(x)

    @on_event("thing_happened")
    async def on_thing(self, payload):
        await self.messenger.publish("thing_processed", {"payload": payload})

서비스 실행

서비스를 cli.run(...)을 통해 CLI 엔트리 포인트로 노출하고 스크립트로 등록합니다.

pyproject.toml에 엔트리 포인트를 추가합니다 (Poetry 예시):

구성 파일 하나(또는 여러 개)와 함께 서비스를 실행합니다.

이벤트 (Fire‑and‑Forget)

소비자는 @on_event("event_type") 로 구독하고, 퍼블리셔는 Messenger.publish(...) 로 이벤트를 발행합니다.

# Server‑side (service)
class UserEventsService:
    @on_event("user.created")
    async def on_user_created(self, payload: dict):
        ...
# Client‑side (stand‑alone script)
async def publish_event_from_script():
    async with Messenger(standalone=True) as messenger:
        await messenger.publish("user.created", {"id": "u_123"})
# Client‑side (from another service)
class BillingService:
    messenger = Messenger()

    @rpc
    async def create_invoice(self, user_id: str):
        await self.messenger.publish("user.created", {"id": user_id})
        return "ok"

RPC (요청/응답)

Kontiki RPC는 AMQP를 통한 동기식 요청/응답 방식입니다.

  • Server side@rpc 로 핸들러를 노출합니다 (rpc_error(code, message) 를 반환할 수 있음).
  • Client sideRpcProxy 를 사용해 원격 메서드를 호출합니다.
# Server‑side (service)
class RpcService:
    @rpc
    async def rpc_example(self, feature: str):
        if feature == "bad_input":
            return rpc_error("USER_INPUT_ERROR", "Invalid feature value")
        return "ok"
# Client‑side (stand‑alone script)
async def call_rpc_from_script():
    async with Messenger(standalone=True) as messenger:
        rpc_service = RpcProxy(messenger, service_name="RpcService")
        return await rpc_service.rpc_example("bad_input")
# Client‑side (from another service)
class ApiGatewayService:
    messenger = Messenger()

    @rpc
    async def compute(self, feature: str):
        rpc_service = RpcProxy(self.messenger, service_name="RpcService")
        return await rpc_service.rpc_example(feature)

HTTP 엔드포인트

Kontiki는 @http(...)를 사용하여 HTTP 라우트를 직접 노출할 수 있습니다.
선택적으로 Pydantic 모델로 요청 본문을 검증하고 OpenAPI/Swagger UI를 생성할 수 있습니다.

class SimpleHttpService:
    http_error_handlers = {HttpExampleError: (400, "Example error occurred")}

    @http("/health", "GET", version="v1", response_model=HelloResponse)
    async def health(self, request):
        return HelloResponse(message="ok").model_dump()

주기적 작업

@task(interval=..., immediate=...)를 사용하여 서비스 루프 내에서 주기적인 비동기 작업을 실행합니다.

class TaskService:
    @task(interval=10, immediate=True)
    async def task_example(self):
        ...

구성

서비스는 여러 YAML 파일을 사용하여 반복 가능한 --config 옵션으로 시작할 수 있습니다 (예: 공유 base.yaml와 환경별 env.yaml).

# base.yaml
kontiki:
  amqp:
    url: amqp://guest:guest@localhost/

app:
  cache:
    ttl_seconds: 60
# env.yaml
app:
  cache:
    enabled: true
my_service --config base.yaml --config env.yaml

구성 값은 일반적으로 setup()에서 점(.) 기반 경로를 사용하여 로드됩니다:

from kontiki.configuration import get_parameter
from kontiki.delegate import ServiceDelegate

class MyDelegate(ServiceDelegate):
    async def setup(self):
        self.cache_ttl_seconds = get_parameter(
            self.container.config,
            "app.cache.ttl_seconds",
            60,
        )

Health & Degraded‑Mode Checks

When the Kontiki registry service is running, services can register and expose operational state:

  • Heartbeats at a configurable interval
  • Degraded mode checks (custom logic)
  • Optional event/exception tracking for visibility
from pathlib import Path
from kontiki.decorators import degraded_on

class MyService:
    @degraded_on
    def is_degraded(self) -> bool:
        required_dir = Path("/var/lib/my-service/uploads")
        return not required_dir.exists()

For an operator‑friendly view, the sibling repository kontiki-tui provides a terminal UI to inspect and operate the Kontiki ecosystem.

Source:

TL;DR

Kontiki는 RabbitMQ 위에서 asyncio‑native, message‑driven 마이크로‑서비스에 적합한 선택이며, 이벤트RPC를 위한 일류 프리미티브를 제공하면서 저수준 AMQP 연결, 라이프사이클 관리 및 설정 처리를 추상화합니다.

모든 서비스에 대한 합리적인 파이프라인

이 문서는 간략한 개요를 제공합니다. Kontiki는 핵심 기능을 중심으로 다음과 같은 고급 옵션도 제공합니다:

  • 세션
  • 브로드캐스트 전송
  • 메시지 헤더
  • 오류/재시도 동작
  • 그 외 다수…

레포지토리에는 실행 가능한 예제가 풍부하게 포함되어 있습니다. RabbitMQ를 로컬에서 실행하고 Makefile 타깃을 통해 대부분의 예제를 실행할 수 있습니다. 예:

make run-amqp
make run-

(원하는 run-* 명령으로 “ 를 교체하세요.)

0 조회
Back to Blog

관련 글

더 보기 »