{"id":19268646,"url":"https://github.com/moolen/bent","last_synced_at":"2026-05-16T15:34:29.630Z","repository":{"id":144213885,"uuid":"174714489","full_name":"moolen/bent","owner":"moolen","description":"AWS Fargate Service Mesh using Envoy","archived":false,"fork":false,"pushed_at":"2019-07-14T21:37:09.000Z","size":297,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-05T12:41:57.807Z","etag":null,"topics":["aws","envoy","fargate","mesh","service","xds"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moolen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-09T15:56:14.000Z","updated_at":"2022-05-06T18:35:13.000Z","dependencies_parsed_at":null,"dependency_job_id":"16fa06a3-414e-40bb-9f87-30e5c657db21","html_url":"https://github.com/moolen/bent","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moolen%2Fbent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moolen%2Fbent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moolen%2Fbent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moolen%2Fbent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moolen","download_url":"https://codeload.github.com/moolen/bent/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240371742,"owners_count":19790888,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","envoy","fargate","mesh","service","xds"],"created_at":"2024-11-09T20:16:57.367Z","updated_at":"2026-05-16T15:34:24.591Z","avatar_url":"https://github.com/moolen.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":" Bent [![Go Report Card](https://goreportcard.com/badge/github.com/moolen/bent)](https://goreportcard.com/report/github.com/moolen/bent) [![Build Status](https://travis-ci.com/moolen/bent.svg?branch=master)](https://travis-ci.com/moolen/bent)\n ====\n\nRun a Service Mesh on top of AWS Fargate containers.\n\n## What's the point?\n\nThis implementation is a PoC on how to deploy a envoy-based service mesh on top of `AWS FARGATE`. I know, there's App Mesh, but it's not yet released in all regions (can i haz eu-central plx?) and it's bleeding edge. I wanted to experiment and see how to build a service mesh on my own (see limitations below).\n\nFor my own sanity aswell as my wallet, this thingy also runs locally leveraging [docker-compose](https://github.com/moolen/bent/blob/master/example/compose/docker-compose.yml). See `Playground` below.\n\n## Installation\n\nThere's a [terraform example](./deploy) to bootstrap the AWS infrastructure.\nYou may also want to take a look at the [docker-compose](https://github.com/moolen/bent/blob/master/example/compose/docker-compose.yml) example.\n\n### Building from source\n\n```bash\n$ make test\n$ make build\n```\n\n### Playground\n\n![DAG](./example/compose/dag.png)\n\n```bash\n$ docker-compose up -f ./example/compose/docker-compose.yml -d\n```\nOpen the jaeger dashboard at [http://localhost:16686](http://localhost:16686) in your browser. There's a traffic-generator built-in.\nJust change the annotations in the `config.yaml`. They are reloaded at runtime.\n\nThe front proxy is HTTP Basic auth protected\n\n```bash\n$ export http_proxy=http://localhost:4100 # points to \"ingress\"\n$ curl beta.svc # 401\n$ curl jimmy:1234@beta.svc\n```\n\nA lot of features are built into this implementation:\n\n- configurable health checking\n- configurable circuit breaking\n- authz HTTP Basic Auth front-proxy\n- endpoint weights\n- json access logs\n- native open tracing integration (jaeger)\n- prometheus endpoint (`0.0.0.0:15090/stats/prometheus`)\n- configurable fault injection\n\n## Concepts\n\nBent makes assumptions about the way services are running and how they interact with each other. These concepts were initially tailored towards services running on `AWS Fargate`.\n\n### Service Mesh\nAll ECS FARGATE **tasks** are meshed. All ingress and egress traffic flows through the sidecar.\n\nExample:\n\napp1 talks to app2. The traffic flows first to the local sidecar, then to the remote sidecar and finally to `app2`.\n\n```\n+---------+               +---------+\n|         |               |         |\n|  envoy  +---------------\u003e  envoy  |\n|         |               |         |\n+----^----+               +----+----+\n     |                         |\n+----+----+               +----v----+\n|         |               |         |\n|  app1   |               |  app2   |\n|         |               |         |\n+---------+               +---------+\n```\n\n* the envoy-sidecar listens on a well-known port (4000) for egress traffic\n* the envoy-sidecar listens on a well-known port (4100) for inress traffic\n\n### Egress Traffic\n\nThe egress traffic originating from an application container **MUST** be proxied through the envoy sidecard using the `HTTP_PROXY` semantics. Many runtimes (e.g. go, nodejs, bash/curl) make use of environment variables. The Egress traffic will then go to\n\n```bash\n$ export http_proxy=\"http://localhost:4000\"\n$ curl foo.svc\n```\n\nEnvoy has `ALL THE ROUTES` of all other services on the egress listener (:4000). These routes point to the sidecar.\n\n### Ingress Traffic\nFor every service in a task there is a route in the ingress listener chain (:4100). Requests are being forwarded based on the `Host` header.\n\n\n### Limitations / NYI\n* apps have to use either `HTTP_PROXY` or specify the `Host` when talking to the egress envoy listener. It is not possible to do iptables wizardry and redirect the traffic to envoy\n* High Availability: the cluster/endpoint state is not being persistet yet\n* High Availability: no clustering mechanisms implemented yet\n\n## Discovery Mechanisms\n\nBent supports two discovery mechanisms: `file` and `aws fargate`.\n\n### AWS Fargate\n\nThe controlplane depends on the [well-known AWS environment variables](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html).\n\nBent's abstraction of endpoints and services is a different one than the one AWS Fargate makes.\n\n* it doesn't care about \"ECS services\"\n* all (ecs) tasks are equal\n* a task may expose multiple endpoints\n* tasks use dockerLabels to configure the sidecar\n\nExample schema of a ecs task-definition:\n\n```yaml\n- name: sidecar\n  image: moolen/bent-envoy:latest\n  environment:\n  - name: ENVOY_XDS_HOST\n    value: my.controlplane\n\n- name: echo\n  image: jmalloc/echo-server\n  environment:\n  - name: PORT\n    value: '3000'\n  dockerLabels:\n    # traffic to \"echo.alpha\" is being forwarded to container \"echo\" / port 3000\n    envoy.service.echo.alpha: echo:3000\n    envoy.service.echo.alpha.annotations.enable-retry: ''\n    envoy.service.echo.alpha.annotations.num-retries: '3'\n    envoy.service.echo.alpha.annotations.healthcheck.path: \"/gimme-healthz\"\n```\n\n\n### File-based\n\nSpecify a configuration in the following format and launch Bent with `-provider file` and `-config path/to/config.yaml`. Bent will continuously read the file and change the envoy configuration.\n\n```yaml\n# services are a global collection of endpoints\n# the \"local\" service which is being sidecar-ed is defined below at \"nodes\"\nservices:\n- name: \"beta.svc\"\n  annotations:\n    fault.inject: \"\"\n    fault.delay.duration: \"150\"\n    fault.delay.percent: \"10\"\n    fault.abort.code: \"501\"\n    fault.abort.percent: \"5\"\n  endpoints:\n  - address: 10.123.0.22\n    port: 4100\n- name: \"gamma.svc\"\n  endpoints:\n  - address: 10.123.0.24\n    port: 4100\n\n# these are the \"local\" services which are being sidecar-ed\nnodes:\n\n  alpha:\n    # alpha node does not expose endpoints\n\n  beta:\n    # incoming requests that match beta.svc\n    # are being forwarded to 10.123.0.23:3000\n    # a node may specify multiple services\n    - name: \"beta.svc\"\n      endpoints:\n      - address: 10.123.0.23\n        port: 3000\n\n  gamma:\n    - name: \"gamma.svc\"\n      endpoints:\n      - address: 10.123.0.25\n        port: 3000\n\n```\n\n## Annotations\n\nOn Fargate, use dockerLabels to specify annotations. The annotations must follow the schema:\n\n* `envoy.service.\u003cservice-name\u003e.annotations.\u003cannotation\u003e`\n\nIf you want to specify the health-check path for your `echo.alpha` service, use this label: `envoy.service.echo.alpha.annotations.healthcheck.path: \"/gimme-healthz\"`\n\n\nHere's a list of all Annotations:\n\n```go\n\t// ------\n\t// cluster level annotations\n\t// ------\n\n\t// AnnotationHealthCheckPath specifies the HTTP Path for health-checks\n\tAnnotationHealthCheckPath = \"healthcheck.path\"\n\t// AnnotationHealthInterval specifies the health check interval in milliseconds\n\tAnnotationHealthInterval = \"healthcheck.interval\"\n\t// AnnotationHealthCacheDuration specifies the health check cache duration in milliseconds\n\tAnnotationHealthCacheDuration = \"healthcheck.cache\"\n\t// AnnotationHealthTimeout specifies the timeout of a health-check in milliseconds\n\tAnnotationHealthTimeout = \"healthcheck.timeout\"\n\t// AnnotationHealthPort specifies the tcp port for the health-check\n\tAnnotationHealthPort = \"healthcheck.port\"\n\t// AnnotationHealthExpectedStatus specifies the accepted status codes\n\tAnnotationHealthExpectedStatus = \"healthcheck.expected-status\"\n\n\t// AnnotaionCBMaxConn sets the maximum number of connections that Envoy will make to the upstream\n\tAnnotaionCBMaxConn = \"circuit-breaker.max-connections\"\n\t// AnnotaionCBMaxPending sets the maximum number of pending requests that Envoy will\n\t// allow to the upstream cluster\n\tAnnotaionCBMaxPending = \"circuit-breaker.max-pending\"\n\t// AnnotaionCBMaxRequests sets the maximum number of parallel requests\n\tAnnotaionCBMaxRequests = \"circuit-breaker.max-requests\"\n\t// AnnotaionCBMaxRetries sets maximum number of parallel retries that Envoy\n\t// will allow to the upstream cluster\n\tAnnotaionCBMaxRetries = \"circuit-breaker.max-retries\"\n\n\t// ------\n\t// endpoint level annotations\n\t// ------\n\n\t// AnnotaionEndpointWeight specifies the loadbalancer weight of the endpoint\n\tAnnotaionEndpointWeight = \"endpoint.weight\"\n\n\t// ------\n\t// listener level annotations\n\t// ------\n\n\t// AnnotaionFaultInject enables fault injection\n\tAnnotaionFaultInject = \"fault.inject\"\n\t// AnnotaionFaultDelayPercent int value, specifies the delay injection percentage\n\tAnnotaionFaultDelayPercent = \"fault.delay.percent\"\n\t// AnnotaionFaultDelayDuration in milliseconds\n\tAnnotaionFaultDelayDuration = \"fault.delay.duration\"\n\t// AnnotaionFaultAbortPercent int value, specifies the abort injection percentage\n\tAnnotaionFaultAbortPercent = \"fault.abort.percent\"\n\t// AnnotaionFaultAbortCode specify the response status code\n\tAnnotaionFaultAbortCode = \"fault.abort.code\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoolen%2Fbent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoolen%2Fbent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoolen%2Fbent/lists"}