Skip to content

Instantly share code, notes, and snippets.

@vmagamedov
Created December 19, 2019 11:31
Show Gist options
  • Save vmagamedov/a5092fee2c26fcdc7edb746f76c81776 to your computer and use it in GitHub Desktop.
Save vmagamedov/a5092fee2c26fcdc7edb746f76c81776 to your computer and use it in GitHub Desktop.

Serverless и Boilerplate

Классика

Стек классического приложения:

  • Функции
  • Роутинг урлов
  • Окружение приложения
  • Раннер приложения (gunicorn, например)
  • Конфигурация, секреты
  • Контейнер с зависимостями
  • Описание деплоя
  • Внутрикластерный роутинг

Проблема: приложение слишком много знает про нюансы нашей инфраструктуры и всю эту информацию надо повторять в каждом сервисе и поддерживать её в актуальном состоянии.

Задача: писать только бизнес-логику, всё остальное переложить на инфраструктуру и тулинг.

Лямбда

https://d2908q01vomqb2.cloudfront.net/1b6453892473a467d07372d45eb05abc2031647a/2018/06/13/api-backends.png

Стек Лямбды:

  • Функция
  • Окружение функции
  • Конфигурация, секреты
  • Зависимости
  • Рантайм для конкретного языка программирования
  • API гейтвей, триггеры

Типичный пример Лямбды:

def request_handler(event, context):
    return 'ok'

Что у лямбда-рантайма под капотом:

while True:
    event_request = lambda_runtime_client.wait_next_invocation()
    ...
    result = request_handler(json_input, context)
    ...
    lambda_runtime_client.post_invocation_result(invoke_id, result)

Плюсы:

  • система асинхронных эвентов
  • множество видов триггеров

Минусы:

  • холодный запуск

Knative

Функции и serverless-style приложения в контейнерах с автоматическим масштабированием от 0 инстансов.

Состоит из компонентов:

  • Serving
  • Eventing

В составе был ещё и компонент Build, но его заменил проект tekton.dev.

Tekton заменяет CI систему, после чего вам по сути нужен только репозиторий кода и Kubernetes.

Serving значительно упрощает описание деплоя:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: example
spec:
  template:
    spec:
      containers:
      - image: gitlab.evo.dev/example/example

Eventing значительно сложнее и выглядит так:

https://knative.dev/v0.8-docs/eventing/images/control-plane.png

Что может работать в Knative:

app.run(port=os.environ['PORT'])

-- т.е. по сути любой HTTP сервис, реализованный на любом языке программирования, никаких специальных SDK не требуется.

  • Serving == Google Cloud Run
  • Eventing ~= Google Cloud Pub/Sub
  • Tekton ~= Google Cloud Build

Плюсы:

  • request-driven, а не event-driven
  • более универсальный чем просто Лямбда (serverless-style микросервисы)
  • использует CloudEvents стандарт
  • можно использовать gRPC
  • работает в любом клауде и на своём железе
  • рантайм от TriggerMesh умеет запускать Лямбда функции в Knative

Минусы:

  • по простоте и степени интеграции с другими сервисами пока далёк от решений клауд-провайдеров

Что остаётся?

Чего не решает ни Лямбда ни Knative

  • Окружение сервиса/функции
  • Конфигурация, секреты
  • Зависимости
  • Описание деплоя

А это много всё того же бойлерплейта.

Пример конфигурации сервиса:

db:
  value: postgres://user:pass@db:5432/users
auth:
  host: auth.default.svc.cluster.local
  port: 50051
bind:
  host: 0.0.0.0
  port: 80

Что можно сказать по этой конфигурации?

  • нужно проинициализировать соединение к базе данных PostgreSQL
  • нужно проинициализировать соединение к сервису аутентификации
  • нужно поднять HTTP сервер и слушать на порту 80

Далее:

  • нужно создать k8s ресурс Deployment с командой запуска сервиса и прочими деталями
  • нужно создать k8s ресурс Service с ClusterIP и портом 80
  • нужно создать k8s ресурсы Gateway и VirtualService с портом 80 и стандартным хостом

Вывод: из одной только конфигурации приложения можно много чего узнать о сервисе, и если эту конфигурацию описать должным образом, мы сможем избавиться от большей части бойлерплейта.

message Configuration {
    bool debug = 1;
    harness.postgres.DSN db = 2 [(harness.wire).input = "python/asyncpg:Connection"];
    harness.grpc.Channel auth = 3 [(harness.wire).input = "python/grpclib:Channel"];
    harness.grpc.Endpoint bind = 4 [(harness.wire).output = "python/grpclib:Server"];
}

Подробнее тут: https://github.com/vmagamedov/harness

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment