Merge pull request #11825 from hashicorp/pglass/ecs-merge-arch-into-overview

docs: ECS 0.3.0 Updates
This commit is contained in:
Paul Glass 2022-01-27 11:57:41 -06:00 committed by GitHub
commit 5bd733c9a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1481 additions and 219 deletions

View File

@ -19,15 +19,16 @@ The following diagram shows the main components of the Consul architecture when
1. **Sidecar proxy:** The sidecar proxy container runs [Envoy](https://envoyproxy.io/). All requests
to and from the application container(s) run through the sidecar proxy. This communication
is called _data plane_ communication.
1. **Mesh Init:** Each task runs a short-lived container, called `mesh-init`, which sets up initial configuration
for Consul and Envoy.
1. **Health Syncing:** Optionally, an additional `health-sync` container can be included in a task to sync health statuses
from ECS into Consul.
1. **ACL Controller:** Automatically provisions Consul ACL tokens for Consul clients and service mesh services
in an ECS Cluster.
For more information about how Consul works in general, see Consul's [Architecture Overview](/docs/architecture).
In addition to the long-running Consul client and sidecar proxy containers, the `mesh-init` container runs
at startup and sets up initial configuration for Consul and Envoy.
### Task Startup
## Task Startup
This diagram shows the timeline of a task starting up and all its containers:
@ -44,7 +45,7 @@ This diagram shows the timeline of a task starting up and all its containers:
- If applicable, the `health-sync` container syncs health checks from ECS to Consul (see [ECS Health Check Syncing](#ecs-health-check-syncing)).
- **T2:** The `sidecar-proxy` container is marked as healthy by ECS. It uses a health check that detects if its public listener port is open. At this time, your application containers are started since all Consul machinery is ready to service requests. The only running containers are `consul-client`, `sidecar-proxy`, and your application container(s).
### Task Shutdown
## Task Shutdown
This diagram shows an example timeline of a task shutting down:
@ -64,7 +65,7 @@ This diagram shows an example timeline of a task shutting down:
- Updates about this task have reached the rest of the Consul cluster, so downstream proxies have been updated to stopped sending traffic to this task.
- **T4**: At this point task shutdown should be complete. Otherwise, ECS will send a KILL signal to any containers still running. The KILL signal cannot be ignored and will forcefully stop containers. This will interrupt in-progress operations and possibly cause errors.
### Automatic ACL Token Provisioning
## Automatic ACL Token Provisioning
Consul ACL tokens secure communication between agents and services.
The following containers in a task require an ACL token:
@ -83,13 +84,13 @@ token does not yet exist.
The ACL controller stores all ACL tokens in AWS Secrets Manager, and tasks are configured to pull these
tokens from AWS Secrets Manager when they start.
### ECS Health Check Syncing
## ECS Health Check Syncing
If the following conditions apply, ECS health checks automatically sync with Consul health checks for all application containers:
* marked as `essential`
* have ECS `healthChecks`
* are not configured with native Consul health checks
* marked as `essential`
* have ECS `healthChecks`
* are not configured with native Consul health checks
The `mesh-init` container creates a TTL health check for
every container that fits these criteria and the `health-sync` container ensures

View File

@ -0,0 +1,147 @@
---
layout: docs
page_title: AWS ECS
description: >-
Configuration Reference for Consul on AWS ECS (Elastic Container Service).
Do not modify by hand! This is automatically generated documentation.
---
# Configuration Reference
This pages details the configuration options for the JSON config format used
by the `consul-ecs` binary. This configuration is passed to the `consul-ecs`
binary as a string using the `CONSUL_ECS_CONFIG_JSON` environment variable.
This configuration format follows a [JSON schema](https://github.com/hashicorp/consul-ecs/blob/main/config/schema.json)
that can be used for validation.
## Terraform Mesh Task Module Configuration
The `mesh-task` Terraform module provides input variables for commonly used fields.
The following table shows which Terraform input variables correspond to each field
of the Consul ECS configuration. Refer to the
[Terraform registry documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs)
for a complete reference of supported input variables for the `mesh-task` module.
| Terraform Input Variable | Consul ECS Config Field |
| ------------------------ | ------------------------------------- |
| `upstreams` | [`proxy.upstreams`](#proxy-upstreams) |
| `checks` | [`service.checks`](#service-checks) |
| `consul_service_name` | [`service.name`](#service) |
| `consul_service_tags` | [`service.tags`](#service) |
| `consul_service_meta` | [`service.meta`](#service) |
| `consul_namespace` | [`service.namespace`](#service) |
| `consul_partition` | [`service.partition`](#service) |
Each of these Terraform input variables follow the Consul ECS config schema.
The remaining fields of the Consul ECS configuration not listed in this table can be passed
using the `consul_ecs_config` input variable.
# Top-level fields
These are the top-level fields for the Consul ECS configuration format.
| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `bootstrapDir` | `string` | required | The directory at which to mount the shared volume where Envoy bootstrap configuration is written by `consul-ecs mesh-init`. |
| `healthSyncContainers` | `array` | optional | The names of containers that will have health check status synced from ECS into Consul. Cannot be specified with `service.checks`. |
| [`proxy`](#proxy) | `object` | optional | Configuration for the sidecar proxy registration with Consul. |
| [`service`](#service) | `object` | required | Configuration for Consul service registration. |
# `service`
Configuration for Consul service registration.
| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| [`checks`](#service-checks) | `array` | optional | The list of Consul checks for the service. Cannot be specified with `healthSyncContainers`. |
| `enableTagOverride` | `boolean` | optional | Determines if the anti-entropy feature for the service is enabled |
| `meta` | `object` | optional | Key-value pairs of metadata to include for the Consul service. |
| `name` | `string` | optional | The name the service will be registered as in Consul. Defaults to the Task family name if empty or null. |
| `namespace` | `string` | optional | The Consul namespace where the service will be registered [Consul Enterprise]. |
| `partition` | `string` | optional | The Consul admin partition where the service will be registered [Consul Enterprise]. |
| `port` | `integer` | required | Port the application listens on, if any. |
| `tags` | `array` | optional | List of string values that can be used to add service-level labels. |
| [`weights`](#service-weights) | `object` | optional | Configures the weight of the service in terms of its DNS service (SRV) response. |
# `service.checks`
Defines the Consul checks for the service. Each check may contain these fields.
| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `aliasNode` | `string` | optional | Specifies the ID of the node for an alias check. |
| `aliasService` | `string` | optional | Specifies the ID of a service for an alias check. |
| `args` | `array` | optional | Command arguments to run to update the status of the check. |
| `body` | `string` | optional | Specifies a body that should be sent with `HTTP` checks. |
| `checkId` | `string` | optional | The unique ID for this check on the node. Defaults to the check `name`. |
| `failuresBeforeCritical` | `integer` | optional | Specifies the number of consecutive unsuccessful results required before check status transitions to critical. |
| `grpc` | `string` | optional | Specifies a `gRPC` check. Must be an endpoint that supports the [standard gRPC health checking protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md). The endpoint will be probed every `interval`. |
| `grpcUseTls` | `boolean` | optional | Specifies whether to use TLS for this gRPC health check. |
| `h2ping` | `string` | optional | Specifies this is an h2ping check. Must be an address, which will be pinged every `interval`. |
| `h2pingUseTls` | `boolean` | optional | Specifies whether TLS is used for an h2ping check. |
| `header` | `object` | optional | Specifies a set of headers that should be set for HTTP checks. Each header can have multiple values. |
| `http` | `string` | optional | Specifies this is an HTTP check. Must be a URL against which request is performed every `interval`. |
| `interval` | `string` | optional | Specifies the frequency at which to run this check. Required for HTTP and TCP checks. |
| `method` | `string` | optional | Specifies the HTTP method to be used for an HTTP check. When no value is specified, `GET` is used. |
| `name` | `string` | optional | The name of the check. |
| `notes` | `string` | optional | Specifies arbitrary information for humans. This is not used by Consul internally. |
| `status` | `string` | optional | Specifies the initial status the health check. Must be one of `passing`, `warning`, `critical`, `maintenance`, or`null`. |
| `successBeforePassing` | `integer` | optional | Specifies the number of consecutive successful results required before check status transitions to passing. |
| `tcp` | `string` | optional | Specifies this is a TCP check. Must be an IP/hostname plus port to which a TCP connection is made every `interval`. |
| `timeout` | `string` | optional | Specifies a timeout for outgoing connections in the case of a Script, HTTP, TCP, or gRPC check. Must be a duration string, such as `10s` or `5m`. |
| `tlsServerName` | `string` | optional | Specifies an optional string used to set the SNI host when connecting via TLS. |
| `tlsSkipVerify` | `boolean` | optional | Specifies if the certificate for an HTTPS check should not be verified. |
| `ttl` | `string` | optional | Specifies this is a TTL check. Must be a duration string, such as `10s` or `5m`. |
# `service.weights`
Configures the weight of the service in terms of its DNS service (SRV) response.
| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `passing` | `integer` | required | Weight for the service when its health checks are passing. |
| `warning` | `integer` | required | Weight for the service when it has health checks in `warning` status. |
# `proxy`
Configuration for the sidecar proxy registration with Consul.
| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `config` | `object` | optional | Object value that specifies an opaque JSON configuration. The JSON is stored and returned along with the service instance when called from the API. |
| [`meshGateway`](#proxy-meshgateway) | `object` | optional | Specifies the mesh gateway configuration for the proxy. |
| [`upstreams`](#proxy-upstreams) | `array` | optional | The list of the upstream services that the proxy should create listeners for. |
# `proxy.upstreams`
The list of the upstream services that the proxy should create listeners for. Each upstream may contain these fields.
| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `config` | `object` | optional | Specifies opaque configuration options that will be provided to the proxy instance for the upstream. |
| `datacenter` | `string` | optional | Specifies the datacenter to issue the discovery query to. |
| `destinationName` | `string` | required | Specifies the name of the upstream service or prepared query to route the service mesh to. |
| `destinationNamespace` | `string` | optional | Specifies the namespace containing the upstream service [Consul Enterprise]. |
| `destinationPartition` | `string` | optional | Specifies the name of the admin partition containing the upstream service [Consul Enterprise]. |
| `destinationType` | `string` | optional | Specifies the type of discovery query the proxy should use for finding service mesh instances. Must be one of `service`, `prepared_query`, or`null`. |
| `localBindAddress` | `string` | optional | Specifies the address to bind a local listener to. |
| `localBindPort` | `integer` | required | Specifies the port to bind a local listener to. The application will make outbound connections to the upstream from the local port. |
| [`meshGateway`](#proxy-upstreams-meshgateway) | `object` | optional | Specifies the mesh gateway configuration for the proxy for this upstream. |
## `proxy.upstreams.meshGateway`
Specifies the mesh gateway configuration for the proxy for this upstream.
| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `mode` | `string` | required | Specifies how the upstream with a remote destination datacenter gets resolved. Must be one of `none`, `local`, or`remote`. |
# `proxy.meshGateway`
Specifies the mesh gateway configuration for the proxy.
| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `mode` | `string` | required | Specifies how upstreams with a remote destination datacenter get resolved. Must be one of `none`, `local`, or`remote`. |

View File

@ -27,10 +27,10 @@ module "my_task" {
## Licensing
~> **Warning:** Consul Enterprise is currently only fully supported when [ACLs are enabled](/docs/ecs/get-started/production-installation#deploy-acl-controller).
~> **Warning:** Consul Enterprise is currently only fully supported when [ACLs are enabled](/docs/ecs/production-installation#deploy-acl-controller).
Consul Enterprise [requires a license](/docs/enterprise/license/overview). If running
Consul on ECS with [ACLs enabled](/docs/ecs/get-started/production-installation#deploy-acl-controller), the license
Consul on ECS with [ACLs enabled](/docs/ecs/production-installation#deploy-acl-controller), the license
will be automatically pulled down from Consul servers.
Currently there is no capability for specifying the license when ACLs are disabled so if you wish to

View File

@ -1,186 +0,0 @@
---
layout: docs
page_title: Installation - AWS ECS
description: >-
Install Consul Service Mesh on AWS ECS (Elastic Container Service).
---
# Installation
Installing Consul on ECS is a multi-part process:
1. [**Task Module:**](#task-module) Define the [`mesh-task` Terraform module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task)
to create a task definition with the necessary sidecar containers for your application to join the service mesh.
1. [**Routing:**](#routing) With your tasks as part of the mesh, you must specify their upstream
services and change the URLs the tasks are using so that they're making requests through the service mesh.
1. [**Bind Address:**](#bind-address) Now that all communication is flowing through the service mesh,
you should change the address your application is listening on to `127.0.0.1`
so that it only receives requests through the sidecar proxy.
-> **NOTE:** This page assumes you're familiar with ECS. See [What is Amazon Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) for more details.
## Task Module
In order to add the necessary sidecar containers for your task to join the mesh,
you must use the [`mesh-task` module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task):
```hcl
module "my_task" {
source = "hashicorp/consul-ecs/aws//modules/mesh-task"
version = "<latest version>"
family = "my_task"
container_definitions = [
{
name = "example-client-app"
image = "docker.io/org/my_task:v0.0.1"
essential = true
portMappings = [
{
containerPort = 9090
hostPort = 9090
protocol = "tcp"
}
]
cpu = 0
mountPoints = []
volumesFrom = []
}
]
port = "9090"
retry_join = ["<address of the Consul server>"]
}
```
All possible inputs are documented on the [module reference documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs),
however there are some important inputs worth highlighting:
- `family` is used as the [task definition family](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#family)
but it's also used as the name of the service that gets registered in Consul.
- `container_definitions` accepts an array of [container definitions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definitions).
This is where you include application containers.
- `port` is the port that your application listens on. This should be set to a
string, not an integer, i.e. `port = "9090"`, not `port = 9090`.
- `retry_join` is passed to the [`-retry-join`](/docs/agent/options#_retry_join) option for the Consul agent. This tells
the agent the location of your Consul servers so that it can join the Consul cluster.
-> **NOTE:** If your tasks run in a public subnet, they must have `assign_public_ip = true`
in their [`network_configuration`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service#network_configuration) block so that ECS can pull the Docker images.
## ECS Service
To define an ECS Service, reference the mesh-task module's `task_definition_arn` output value
in your `aws_ecs_service` resource:
```hcl
resource "aws_ecs_service" "my_task" {
...
task_definition = module.my_task.task_definition_arn
}
```
After running `terraform apply`, you should see your tasks registered in
the Consul UI.
## Routing
Now that your tasks are registered in the mesh, you're able to use the service
mesh to route between them.
In order to make calls through the service mesh, you must configure the sidecar
proxy to listen on a different port for each upstream service your application
needs to call. You then must modify your application to make requests to the sidecar
proxy on that port.
For example, if your application `web` makes calls to another application called `backend`, then you would first configure the `mesh-task` module's upstream(s):
`backend`.
```hcl
module "web" {
family = "web"
upstreams = [
{
destination_name = "backend"
local_bind_port = 8080
}
]
}
```
- Set the `destination_name` to the name of the upstream service (in this case `backend`)
- Set `local_bind_port` to an unused port. This is the port that the sidecar proxy
will listen on. Any requests to this port will be forwarded over to the `destination_name`.
This does not have to be the port that `backend` is listening on because the service mesh
will handle routing the request to the right port.
If you have multiple upstream services they each need to be listed here.
Next, configure your application to make requests to `localhost:8080` when
it wants to call the `backend` service.
For example, if your service allows configuring the URL for `backend` via the
`BACKEND_URL` environment variable, you would set:
```hcl
module "web" {
family = "web"
upstreams = [
{
destination_name = "backend"
local_bind_port = 8080
}
]
container_definitions = [
{
name = "web"
environment = [
{
name = "BACKEND_URL"
value = "http://localhost:8080"
}
]
...
}
]
...
}
```
## Bind Address
To ensure that your application only receives traffic through the service mesh,
you must change the address that your application is listening on to only the loopback address
(also known as `localhost`, `lo`, and `127.0.0.1`)
so that only the sidecar proxy running in the same task can make requests to it.
If your application is listening on all interfaces, e.g. `0.0.0.0`, then other
applications can call it directly, bypassing its sidecar proxy.
Changing the listening address is specific to the language and framework you're
using in your application. Regardless of which language/framework you're using,
it's a good practice to make the address configurable via environment variable.
For example in Go, you would use:
```go
s := &http.Server{
Addr: "127.0.0.1:8080",
...
}
log.Fatal(s.ListenAndServe())
```
In Django you'd use:
```bash
python manage.py runserver "127.0.0.1:8080"
```
## Next Steps
- Configure a secure [Production Installation](/docs/ecs/get-started/production-installation).
- Now that your applications are running in the service mesh, read about
other [Service Mesh features](/docs/connect).
- View the [Architecture](/docs/ecs/architecture) documentation to understand
what's going on under the hood.

View File

@ -8,9 +8,9 @@ description: >-
# AWS ECS
Consul can be deployed on [AWS ECS](https://aws.amazon.com/ecs/) (Elastic Container Service) using our official Terraform modules.
![Consul on ECS Architecture](/img/consul-ecs-arch.png)
Consul service mesh applications can be deployed on [AWS Elastic Container Service](https://aws.amazon.com/ecs/) (ECS)
using either our official [Terraform modules](/docs/ecs/terraform/install) or without Terraform by [manually configuring
the task definition](/docs/ecs/manual/install).
## Service Mesh
@ -18,6 +18,17 @@ Using Consul on AWS ECS enables you to add your ECS tasks to the service mesh an
take advantage of features such as zero-trust-security, intentions, observability,
traffic policy, and more.
## Architecture
![Consul on ECS Architecture](/img/consul-ecs-arch.png)
Consul on ECS follows an [architecture](/docs/internals/architecture) similar to other platforms, but each ECS task is a
Consul node. An ECS task runs the user application container(s), as well as a Consul client container for control plane
communication and an [Envoy](https://envoyproxy.io/) sidecar proxy container to faciliate data plane communication for
[Consul Connect](/docs/connect).
For a detailed architecture overview, see the [Architecture](/docs/ecs/architecture) page.
## Getting Started
There are several ways to get started with Consul with ECS.
@ -27,4 +38,6 @@ There are several ways to get started with Consul with ECS.
* The [Consul with Dev Server on Fargate](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/examples/dev-server-fargate) example installation deploys a sample application in ECS using the Fargate launch type.
* The [Consul with Dev Server on EC2](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/examples/dev-server-ec2) example installation deploys a sample application in ECS using the EC2 launch type.
See the [Requirements](/docs/ecs/get-started/requirements) and the full [Install Guide](/docs/ecs/get-started/install) when you're ready to install Consul on an existing ECS cluster and add existing tasks to the service mesh.
Refer to the [Requirements](/docs/ecs/requirements) and use one of the following sets of instructions when you're ready to install Consul on an existing ECS cluster and add tasks to the service mesh:
* [Install with Terraform](/docs/ecs/terraform/install)
* [Install Manually](/docs/ecs/manual/install)

View File

@ -0,0 +1,213 @@
---
layout: docs
page_title: ACL Controller - AWS ECS
description: >-
Manual Deployment of the ACL Controller for Consul Service Mesh on AWS ECS (Elastic Container Service).
---
# Install the ACL Controller
This topic describes how to manually deploy the ACL controller to [automatically provision ACL tokens](/docs/ecs/architecture#automatic-acl-token-provisioning) for Consul on ECS.
If you are using Terraform, refer to the [Terraform Secure Configuration](/docs/ecs/terraform/secure-configuration) page to deploy the ACL controller.
## Prerequisites
* Your application tasks must include certain tags to be compatible with the ACL controller.
Refer to the [Task Tags](/docs/ecs/manual/install#task-tags) section of the installation page.
* You should be familiar with configuring Consul's secure features, including how to create ACL tokens and policies. Refer to the following [Learn Guides](https://learn.hashicorp.com/collections/consul/security) for an introduction and the [ACL system](/docs/security/acl) documentation for more information.
## Set Up Secrets
The ACL controller supports managing secrets in AWS Secrets Manager.
Before deploying the ACL controller for the first time, you must [create the following secrets](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html) from Consul in AWS Secrets Manager.
| Secret | Initial Value | Sample Secret Name |
| --------------------- | -------------- | ------------------------------ |
| Consul server CA cert | Set | `my-consul-ca-cert` |
| Bootstrap ACL Token | Set | `my-consul-bootstrap-token` |
| Consul Client ACL Token | Empty | `<PREFIX>-consul-client-token` |
The secret for the client token must be intially empty. The ACL controller creates the client token in Consul
and stores the token in Secrets Manager. In the secret name, `<PREFIX>` should be replaced with the
[secret name prefix](/docs/ecs/manual/acl-controller#secret-name-prefix) of your choice.
### Secret Name Prefix
The ACL controller requires that the secrets it reads and writes are named with a unique prefix. The name prefix is used
in the [Task Role Policy](/docs/ecs/manual/acl-controller#task-role-policy) to limit the ACL controller's access within
AWS Secrets Manager to only those secrets strictly needed by the ACL controller.
The name prefix should be unique among secrets in your AWS account. We recommend using a short (8 character) random
string for the prefix.
-> **NOTE:** If you are using the ACL controller with multiple ECS clusters, each cluster requires
its own instance of the ACL controller, and each instance of the ACL controller should have a unique
name prefix.
## Task Definition
You must create a task definition to deploy the ACL controller in your ECS cluster.
The ACL controller must run in the same ECS cluster hosting your service mesh application
tasks.
The following example shows how the task definition should be configured for the ACL controller.
```json
{
"family": "my-consul-acl-controller",
"networkMode": "awsvpc",
"containerDefinitions": [
{
"name": "acl-controller",
"image": "public.ecr.aws/hashicorp/consul-ecs:<CONSUL_ECS_VERSION>",
"essential": true,
"command": [
"acl-controller",
"-consul-client-secret-arn", "arn:aws:secretsmanager:us-west-2:000000000000:secret:<PREFIX>-consul-client-token",
"-secret-name-prefix", "<PREFIX>",
],
"secrets": [
{
"name": "CONSUL_HTTP_TOKEN",
"valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-bootstrap-token"
},
{
"name": "CONSUL_CACERT_PEM",
"valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-ca-cert"
}
],
"environment": [
{
"name": "CONSUL_HTTP_ADDR",
"value": "<Consul server HTTP API address>"
}
]
}
]
}
```
You must include the following top-level fields.
| Field name | Type | Description |
| ----------- | ------- | ---------------------------------------------------------------------------- |
| `family` | string | The task family name of your choice. |
| `networkMode` | string | Must be `awsvpc`, which is the only network mode supported by Consul on ECS. |
In the `containerDefinitions` list, include one container with the following fields.
| Field name | Type | Description |
| ----------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The container name, which should be `acl-controller` |
| `image` | string | The `consul-ecs` image. Use our public AWS registry, `public.ecr.aws/hashicorp/consul-ecs`, to avoid rate limits. |
| `command` | list | Must be set as shown. The startup command for the ACL controller. |
| `essential` | boolean | Must be `true` to ensure the health of your application container affects the health status of the task. |
| `secrets` | list | Must have `CONSUL_HTTP_TOKEN` set to the ACL bootstrap token and `CONSUL_CACERT_PEM` set to the Consul server CA certificate. |
| `environment` | string | Must set the `CONSUL_HTTP_ADDR` environment variable to the address of the HTTP API of your Consul servers. |
The following CLI options are required in the `command` field of the container definition.
| Flag | Type | Description |
| --------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `-consul-client-secret-arn` | string | The secret where the ACL controller will store the Consul client token. |
| `-secret-name-prefix` | string | The [secret name prefix](/docs/ecs/manual/acl-controller#secret-name-prefix) that you chose for this ACL controller. |
## ECS Service
Once the task definition is created, define an ECS service in order to start an ACL controller task.
The following example contains the recommended settings for the ACL controller. Refer to
the [ECS service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service_definition_parameters.html) documentation
to complete the remaining details for your use case.
```json
{
"cluster": "<Your ECS cluster ARN>"
"desiredCount": 1,
"launchType": "FARGATE",
"serviceName": "my-acl-controller",
"taskDefinition": "<task definition ARN>",
...
}
```
| Field name | Type | Description |
| ---------------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
| `cluster` | string | Set to your ECS cluster name or ARN. This must be the same ECS cluster where your service mesh applications run. |
| `desiredCount` | integer | Must be `1`. Only one instance of the ACL controller should run per ECS cluster. |
| `launchType` | string | Consul on ECS supports both the `FARGATE` and `EC2` launch types. |
| `serviceName` | string | The service name of your choice. |
| `taskDefinition` | string | Must be set to the ACL controller [task definition](/docs/ecs/manual/acl-controller#task-definition). |
## AWS IAM Roles
The ECS task and execution roles must be configured to allow the ACL controller access
to the ECS API and Secrets Manager API.
### Task Role Policy
The following example shows the policy needed for the ECS task role for the ACL controller.
This grants the ACL controller permission to list tasks, describe tasks, and read and update
secrets.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:ListTasks",
"ecs:DescribeTasks"
],
"Resource": ["*"]
},
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:UpdateSecret"
],
"Resource": [
"arn:aws:secretsmanager:us-west-2:000000000000:secret:<PREFIX>-*"
]
}
]
}
```
The following are the required permissions. You will need to substitute `<PREFIX>` with your chosen [name prefix](/docs/ecs/manual/acl-controller#secret-name-prefix).
| Action | Resource | Description |
| ------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `ecs:ListTasks` | `*` | Allow the ACL controller to watch for new tasks. |
| `ecs:DescribeTasks` | `*` | Allow the ACL controller to retrieve details for new tasks. |
| `secretsmanager:GetSecretValue` | `arn:aws:secretsmanager:us-west-2:000000000000:secret:<PREFIX>-*` | Allow the ACL controller to read secrets with a name prefix. |
| `secretsmanager:UpdateSecret` | `arn:aws:secretsmanager:us-west-2:000000000000:secret:<PREFIX>-*` | Allow the ACL controller to store Consul ACL tokens in secrets with a name prefix. |
### Execution Role Policy
The following IAM policy document allows ECS to retrieve secrets needed
to start the ACL controller task from AWS Secrets Manager, including the ACL
bootstrap token.
The following example shows the policy needed for the execution role.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-bootstrap-token",
"arn:aws:secretsmanager:us-west-2:000000000000:secret:<PREFIX>-consul-client-token"
]
}
]
}
```

View File

@ -0,0 +1,552 @@
---
layout: docs
page_title: Manual Installation - AWS ECS
description: >-
Manually Install Consul Service Mesh on AWS ECS (Elastic Container Service).
---
# Manual Installation
The following instructions describe how to manually create the ECS task definition using the [`consul-ecs` Docker image](https://gallery.ecr.aws/hashicorp/consul-ecs) without Terraform. Refer to the [Consul ECS Terraform module](/docs/ecs/terraform/install) documentation for an alternative method for installing Consul on ECS.
This topic does not include instructions for creating all AWS resources necessary to install Consul, such as a VPC or the ECS cluster. Refer to the linked guides in the [Getting Started](/docs/ecs#getting-started) section for complete, runnable examples.
## Prerequisites
You should have some familiarity with AWS ECS. See [What is Amazon Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) for details.
## Task Definition
You must create a task definition, which includes the following containers:
* Your application container
* An Envoy sidecar-proxy container
* A Consul client container
* A `consul-ecs-mesh-init` container for service mesh setup
* Optionally, a `consul-ecs-health-sync` container to sync ECS health checks into Consul
## Top-level fields
Your task definition must include the following top-level fields.
The `volumes` list contains two [bind mounts](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/bind-mounts.html),
named `consul_data` and `consul_binary`. Bind mounts are directories on the host which can be mounted into one or more containers
in order to share files among containers. For Consul on ECS, certain binaries and configuration are shared among containers
during task startup.
```json
{
"family": "my-example-client-app",
"networkMode": "awsvpc",
"volumes": [
{
"name": "consul_data",
},
{
"name": "consul_binary",
}
],
"containerDefinitions": [...]
"tags": [
{
"key": "consul.hashicorp.com/mesh",
"value": "true"
},
{
"key": "consul.hashicorp.com/service-name",
"value": "example-client-app"
}
]
}
```
| Field name | Type | Description |
| ---------------------- | ------ | ------------------------------------------------------------------------------------------------------------------ |
| `family` | string | The task family name. This is used as the Consul service name by default. |
| `networkMode` | string | Must be `awsvpc`, which is the only network mode supported by Consul on ECS. |
| `volumes` | list | Must be defined as shown above. Volumes are used to share configuration between containers for intial task setup. |
| `containerDefinitions` | list | The list of containers to run in this task (see [Application container](#application-container)). |
### Task Tags
The `tags` list must include the following if you are using the ACL controller in a [secure configuration](/docs/manual/secure-configuration).
Without these tags, the ACL controller will be unable to provision a service token for the task.
| Tag Key | Tag Value | Description |
| ----------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `consul.hashicorp.com/mesh` | `true` (string) | The ACL controller ignores tasks without this tag set to `true`. |
| `consul.hashicorp.com/service-name` | Consul service name | Specifies the Consul service associated with this task. Required if the service name is different than the task `family`. |
## Application container
First, include your application container in the `containerDefinitions` list
in the task definition.
Ensure that the `containerName` and `condition` fields in the `dependsOn` list
are specified as described in the following example. These are container dependencies,
which must be used to enforce a specific [startup order](/docs/ecs/architecture#task-startup).
By using the following settings, your application container will start after `consul-ecs-mesh-init`
has completed task setup and after `sidecar-proxy` is ready to proxy traffic between
this task and the service mesh.
```json
{
"containerDefinitions": [
{
"name": "example-client-app",
"image": "docker.io/org/my_task:v0.0.1",
"essential": true,
"dependsOn": [
{
"containerName": "consul-ecs-mesh-init",
"condition": "SUCCESS"
},
{
"containerName": "sidecar-proxy",
"condition": "HEALTHY"
}
],
...
}
]
}
```
| Field name | Type | Description |
| ----------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The name of your application container. |
| `image` | string | The container image used to run your application. |
| `essential` | boolean | Must be `true` to ensure the health of your application container affects the health status of the task. |
| `dependsOn` | list | Must be set as shown above. Container dependencies ensure your application container starts after service mesh setup is complete. |
See the [ECS Task Definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html) documentation for a complete reference.
## `sidecar-proxy` container
The `sidecar-proxy` container runs [Envoy proxy](/docs/connect/proxies/envoy) for Consul Connect. In most cases, the container should contain the following parameters and values.
The `mountPoints` list must be set as shown in the following example. This will mount the shared `consul_data` volume into the
`sidecar-proxy` container at the path `/consul`. This volume is where the `consul-ecs-mesh-init` container copies the `envoy-bootstrap.json`
file and the `consul-ecs` binary, which are required to start Envoy. The `dependsOn` list must also be defined as follows to ensure the
`sidecar-proxy` container starts after `consul-ecs-mesh-init` has successfully written these files to the shared volume.
<CodeBlockConfig highlight="8-40">
```json
{
"containerDefinitions": [
{
"name": "example-client-app",
"image": "docker.io/org/my_task:v0.0.1",
...
},
{
"name": "sidecar-proxy",
"image": "envoyproxy/envoy-alpine:<VERSION>",
"essential": false,
"dependsOn": [
{
"containerName": "consul-ecs-mesh-init",
"condition": "SUCCESS"
}
],
"healthCheck": {
"retries": 3,
"command": ["nc", "-z", "127.0.0.1", "20000"],
"timeout": 5,
"interval": 30
},
"mountPoints": [
{
"readOnly": true,
"containerPath": "/consul",
"sourceVolume": "consul_data"
}
],
"ulimits": [
{
"name": "nofile",
"softLimit": 1048576,
"hardLimit": 1048576
}
],
"command": ["envoy", "--config-path", "/consul/envoy-bootstrap.json"],
"entryPoint": ["/consul/consul-ecs", "envoy-entrypoint"],
}
]
}
```
</CodeBlockConfig>
The following table describes the necessary configuration settings.
| Field name | Type | Description |
| ------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The container name, which must be `sidecar-proxy`. |
| `image` | string | The Envoy image. This must be a [supported version of Envoy](/docs/connect/proxies/envoy#supported-versions). |
| `dependsOn` | list | Must be set as shown above to ensure Envoy starts after the `consul-ecs-mesh-init` container has written the `envoy-bootstrap.json` config file for Envoy. |
| `healthCheck` | list | Must be set as shown above to monitor the health of Envoy's primary listener port, which ties into container dependencies and startup ordering. |
| `mountPoints` | list | Must be set as shown above to access the files shared in the `/consul` directory, like the Envoy bootstrap configuration file and the `consul-ecs` binary. |
| `ulimits` | list | The `nofile` ulimit must be raised to a sufficiently high value so that Envoy does not fail to open sockets. |
| `entrypoint` | list | Must be set to the custom Envoy entrypoint, `consul-ecs envoy-entrypoint`, to facilitate graceful shutdown. |
| `command` | list | The startup command. This passes the bootstrap configuration to Envoy. |
-> **NOTE**: Envoy and Consul must be compatible versions. See the [supported versions of Envoy](/docs/connect/proxies/envoy#supported-versions) in the Consul documentation.
## `consul-client` container
Each task must include a Consul client container in order for the task to join your Consul cluster.
<CodeBlockConfig highlight="13-31">
```json
{
"containerDefinitions": [
{
"name": "example-client-app",
"image": "docker.io/org/my_task:v0.0.1",
...
},
{
"name": "sidecar-proxy",
"image": "envoyproxy/envoy-alpine:<ENVOY_VERSION>",
...
}
{
"name": "consul-client"
"image": "public.ecr.aws/hashicorp/consul:<CONSUL_VERSION>",
"mountPoints": [
{
"readOnly": false,
"containerPath": "/consul",
"sourceVolume": "consul_data"
},
{
"containerPath": "/bin/consul-inject",
"sourceVolume": "consul_binary"
}
],
"entryPoint": ["/bin/sh", "-ec"],
"command": [
"cp /bin/consul /bin/consul-inject/consul\n\nECS_IPV4=$(curl -s $ECS_CONTAINER_METADATA_URI_V4 | jq -r '.Networks[0].IPv4Addresses[0]')\n\n\ncat << EOF > /consul/agent-defaults.hcl\naddresses = {\n dns = \"127.0.0.1\"\n grpc = \"127.0.0.1\"\n http = \"127.0.0.1\"\n}\nadvertise_addr = \"$ECS_IPV4\"\nadvertise_reconnect_timeout = \"15m\"\nclient_addr = \"0.0.0.0\"\ndatacenter = \"dc1\"\nenable_central_service_config = true\nleave_on_terminate = true\nports {\n grpc = 8502\n}\nretry_join = [\n \"<Consul server location>",\n]\ntelemetry {\n disable_compat_1.9 = true\n}\n\nEOF\n\ncat << EOF > /consul/agent-extra.hcl\naddresses = {\n dns = \"0.0.0.0\"\n}\nlog_level = \"debug\"\n\nEOF\n\nexec consul agent \\\n -data-dir /consul/data \\\n -config-file /consul/agent-defaults.hcl \\\n -config-file /consul/agent-extra.hcl\n"
]
}
]
}
```
</CodeBlockConfig>
| Field name | Type | Description |
| ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The container name, which should always be `consul-client`. |
| `image` | string | The Consul image. Use our public AWS registry, `public.ecr.aws/hashicorp/consul`, to avoid rate limits. |
| `mountPoints` | list | Must be set as shown above. Volumes are mounted to share information with other containers for task setup. |
| `entrypoint` | list | Must be set to a plain shell so that the startup `command` works properly. |
| `command` | list | Specifies the contents of the [startup script](#consul-client-startup-script). Copy the script and format it into a JSON string. |
### Consul client startup script
The following script is used to start the Consul client for Consul on ECS.
```shell
# Copy the consul binary to a shared volume for `consul-ecs-mesh-init` to use to generate Envoy configuration.
cp /bin/consul /bin/consul-inject/consul
# At runtime, determine the IP address assigned to this ECS Task.
ECS_IPV4=$(curl -s $ECS_CONTAINER_METADATA_URI_V4 | jq -r '.Networks[0].IPv4Addresses[0]')
# Write the Consul agent configuration file.
cat << EOF > /consul/agent-defaults.hcl
addresses = {
dns = "127.0.0.1"
grpc = "127.0.0.1"
http = "127.0.0.1"
}
advertise_addr = "$ECS_IPV4"
advertise_reconnect_timeout = "15m"
client_addr = "0.0.0.0"
datacenter = "dc1"
enable_central_service_config = true
leave_on_terminate = true
ports {
grpc = 8502
}
retry_join = ["<consul server location>"]
telemetry {
disable_compat_1.9 = true
}
EOF
# Start the consul agent.
exec consul agent \
-data-dir /consul/data \
-config-file /consul/agent-defaults.hcl
```
The following table describes the values that you should use to configure the `command` script:
| Field name | Type | Description |
| -------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `addresses.*` | strings | Set the DNS, GRPC, and HTTP addresses to `127.0.0.1` to ensure these are not accessible outside of the task. |
| `advertise_addr` | string | Must be set to the task IP address so that other Consul agents know how to reach this agent. |
| `client_addr` | string | Must be set to an interface reachable by other Consul agents. |
| `datacenter` | string | Must be set to the Consul datacenter this task will join. |
| `leave_on_terminate` | boolean | Must be set to `true` so that the Consul agent leaves the cluster gracefully before exiting. |
| `retry_join` | string | Must be set to your Consul server location(s) so this agent can join the Consul cluster. |
-> **NOTE**: Use `exec` to start the Consul agent so that the Consul agent runs as PID 1. This ensures
the Consul agent directly receives signals from ECS, which is important for graceful shutdown of the Consul agent.
Refer to the [Consul Agent documentation](/docs/agent/options#configuration_files) for a complete reference of Consul agent
configuration options.
## `consul-ecs-mesh-init` container
The `consul-ecs-mesh-init` container runs at task startup to setup this instance for Consul service mesh.
It registers the service and proxy for this task with Consul and writes Envoy bootstrap
configuration to a shared volume.
<CodeBlockConfig highlight="18-41">
```json
{
"containerDefinitions": [
{
"name": "example-client-app",
"image": "docker.io/org/my_task:v0.0.1",
...
},
{
"name": "sidecar-proxy",
"image": "envoyproxy/envoy-alpine:<ENVOY_VERSION>",
...
},
{
"name": "consul-client"
"image": "public.ecr.aws/hashicorp/consul:<CONSUL_VERSION>",
...
},
{
"name": "consul-ecs-mesh-init",
"image": "public.ecr.aws/hashicorp/consul-ecs:<CONSUL_ECS_VERSION>",
"command": ["mesh-init"],
"essential": false,
"environment": [
{
"name": "CONSUL_ECS_CONFIG_JSON",
"value": "{\"bootstrapDir\":\"/consul\",\"healthSyncContainers\":[],\"proxy\":{\"upstreams\":[{\"destinationName\":\"example-server-app\",\"localBindPort\":1234}]},\"service\":{\"checks\":[],\"meta\":{},\"name\":\"example-client-app\",\"port\":9090,\"tags\":[]}}"
}
],
"mountPoints": [
{
"readOnly": false,
"containerPath": "/consul",
"sourceVolume": "consul_data"
},
{
"readOnly": true,
"containerPath": "/bin/consul-inject",
"sourceVolume": "consul_binary"
}
]
}
]
}
```
</CodeBlockConfig>
| Field name | Type | Description |
| ----------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The container name should be `consul-ecs-mesh-init`. |
| `image` | string | The `consul-ecs` image. Use our public AWS registry, `public.ecr.aws/hashicorp/consul-ecs`, to avoid rate limits. |
| `mountPoints` | list | Must be set as show above, so the `consul` and `consul-ecs` binaries can be shared among containers for task setup. |
| `command` | list | Set to `["mesh-init"]` so that the container runs the `consul-ecs mesh-init` command. |
| `environment` | list | This must include the [`CONSUL_ECS_CONFIG_JSON`](/docs/ecs/manual-installation#consul_ecs_config_json) variable. See below for details. |
### `CONSUL_ECS_CONFIG_JSON`
Configuration is passed to the `consul-ecs` binary in JSON format using the `CONSUL_ECS_CONFIG_JSON` environment variable.
The following is an example of the configuration that might be used for a service named `example-client-app` with one upstream
service name `example-server-app`. The `proxy` and `service` blocks include information used by `consul-ecs-mesh-init` to perform
[service registration](/docs/discovery/services) with Consul during task startup. The same configuration format is used for
the `consul-ecs-health-sync` container.
```json
{
"bootstrapDir": "/consul",
"healthSyncContainers": [],
"proxy": {
"upstreams": [
{
"destinationName": "example-server-app",
"localBindPort": 1234
}
]
},
"service": {
"checks": [],
"meta": {},
"name": "example-client-app",
"port": 9090,
"tags": []
}
}
```
| Field name | Type | Description |
| ---------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `bootstrapDir` | string | This is the path of a shared volume that is mounted to other containers, where `consul-ecs-mesh-init` will write out Envoy configuration. |
| `healthSyncContainers` | list | Used for [health status syncing](/docs/ecs/manual-installation#consul-ecs-health-sync-container) from ECS to Consul. See below for details. |
| `proxy.upstreams` | list | The upstream services that your application calls over the service mesh, if any. The `destinationName` and `localBindPort` fields are required. |
| `service.name` | string | The name used to register this service into the Consul service catalog. |
| `service.port` | integer | The port your application listens on. Set to `0` if your application does not listen on any port. |
| `service.checks` | list | Consul [checks](/docs/discovery/checks) to include so that Consul can run health checks against your application. |
See the [Configuration Reference](/docs/ecs/configuration-reference) for a complete reference of fields.
## `consul-ecs-health-sync` container
Optionally, Consul ECS can sync health checks for this task into Consul checks.
This allows you to configure a health check for your application in one place and
see a consistent health status in both ECS and Consul.
For example, the following defines an ECS health check command that runs `curl localhost:9090/health`:
<CodeBlockConfig highlight="6-11">
```json
{
"containerDefinitions": [
{
"name": "example-client-app",
"image": "docker.io/org/my_task:v0.0.1",
"healthCheck": {
"retries": 3,
"command": ["CMD-SHELL", "curl localhost:9090/health"],
"timeout": 5,
"interval": 30
},
...
},
...
]
}
```
</CodeBlockConfig>
First, define which containers need their health status synced into Consul. To do this,
add the container name(s) to the `healthSyncContainers` list of the `CONSUL_ECS_CONFIG_JSON` variable,
as shown in the following example. This configuration must be passed to both the `consul-ecs-mesh-init`
and `consul-ecs-health-sync` containers.
<CodeBlockConfig highlight="3-3">
```json
{
"bootstrapDir": "/consul",
"healthSyncContainers": ["example-client-app"],
...
}
```
</CodeBlockConfig>
Next, set the `CONSUL_ECS_CONFIG_JSON` variable for the `consul-ecs-mesh-init` container.
The following example shows how the `CONSUL_ECS_CONFIG_JSON` variable should be formatted.
The JSON configuration is compacted down to a single line and escaped.
<CodeBlockConfig highlight="7-10">
```json
{
"containerDefinitions": [
{
"name": "consul-ecs-mesh-init",
"image": "public.ecr.aws/hashicorp/consul-ecs:<VERSION>",
"environment": [
{
"name": "CONSUL_ECS_CONFIG_JSON",
"value": "{\"bootstrapDir\":\"/consul\",\"healthSyncContainers\":[\"example-client-app\"],\"proxy\":{\"upstreams\":[{\"destinationName\":\"example-server-app\",\"localBindPort\":1234}]},\"service\":{\"checks\":[],\"meta\":{},\"name\":\"example-client-app\",\"port\":9090,\"tags\":[]}}"
}
],
...
},
...
]
}
```
</CodeBlockConfig>
Finally, include the `consul-ecs-health-sync` container in the `containerDefinitions` list.
Pass the same value for `CONSUL_ECS_CONFIG_JSON` for both the `consul-ecs-health-sync`
and `consul-ecs-mesh-init` containers.
<CodeBlockConfig highlight="23-40">
```json
{
"containerDefinitions": [
{
"name": "example-client-app",
"image": "docker.io/org/my_task:v0.0.1",
...
},
{
"name": "sidecar-proxy",
"image": "envoyproxy/envoy-alpine:<ENVOY_VERSION>",
...
},
{
"name": "consul-client"
"image": "public.ecr.aws/hashicorp/consul:<CONSUL_VERSION>",
...
},
{
"name": "consul-ecs-mesh-init",
"image": "public.ecr.aws/hashicorp/consul-ecs:<CONSUL_ECS_VERSION>",
...
},
{
"name": "consul-ecs-health-sync",
"image": "public.ecr.aws/hashicorp/consul-ecs:<CONSUL_ECS_VERSION>",
"command": ["health-sync"],
"essential": false,
"dependsOn": [
{
"containerName": "consul-ecs-mesh-init",
"condition": "SUCCESS"
}
],
"environment": [
{
"name": "CONSUL_ECS_CONFIG_JSON",
"value": "{\"bootstrapDir\":\"/consul\",\"healthSyncContainers\":[\"example-client-app\"],\"proxy\":{\"upstreams\":[{\"destinationName\":\"example-server-app\",\"localBindPort\":1234}]},\"service\":{\"checks\":[],\"meta\":{},\"name\":\"example-client-app\",\"port\":9090,\"tags\":[]}}"
}
]
}
]
}
```
</CodeBlockConfig>
| Field name | Type | Description |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------- |
| `name` | string | The container name, which must be `consul-ecs-health-sync`. |
| `image` | string | The `consul-ecs` image. Use our public AWS registry, `public.ecr.aws/hashicorp/consul-ecs`, to avoid rate limits. |
| `command` | list | Must be set to `["health-sync"]` to run the `consul-ecs health-sync` command. |
| `dependsOn` | list | Must be set as shown above to ensure the `health-sync` container starts after service registration has completed. |
| `environment` | list | Must include the `CONSUL_ECS_CONFIG_JSON` variable to pass configuration to the `consul-ecs health-sync` command. |
# Next Steps
* Create the task definition using the [AWS Console](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html) or the [AWS CLI](https://docs.aws.amazon.com/cli/latest/reference/ecs/register-task-definition.html), or another method of your choice.
* Create an [ECS Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html) to start tasks using the task definition.
* Follow the [Secure Configration](/docs/ecs/manual/secure-configuration) to get production-ready.

View File

@ -0,0 +1,215 @@
---
layout: docs
page_title: Secure Configuration - AWS ECS
description: >-
Manual Secure Confguration of the Consul Service Mesh on AWS ECS (Elastic Container Service).
---
# Secure Configuration
For a production-ready installation of Consul on ECS, you will need to make sure that the cluster is secured.
A secure Consul cluster should include the following:
1. [TLS Encryption](/docs/security/encryption#rpc-encryption-with-tls) for RPC communication between Consul clients and servers.
1. [Gossip Encryption](/docs/security/encryption#gossip-encryption) for encrypting gossip traffic.
1. [Access Control (ACLs)](/docs/security/acl) for authentication and authorization for Consul clients and services on the mesh.
-> **NOTE:** In this topic, we assume that you have already configured your Consul server with the security-related features.
## Prerequisites
* You should already have followed the [installation instructions](/docs/ecs/manual/install) to understand how to define
the necessary components of the task definition for Consul on ECS.
* You should be familiar with [specifying sensitive data](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html) on ECS.
* You should be familiar with configuring Consul's secure features, including how to create ACL tokens and policies. Refer to the following [Learn Guides](https://learn.hashicorp.com/collections/consul/security) for an introduction and the [ACL system](/docs/security/acl) documentation for more information.
## ACL Tokens
You must create two types of ACL tokens for Consul on ECS:
* **Client tokens:** used by the `consul-client` containers to join the Consul cluster
* **Service tokens:** used by sidecar containers for service registration and health syncing
The following sections describe the ACL polices which must be associated with these token types.
-> **NOTE:** This section describes how operators would create ACL tokens by hand. To ease operator
burden, the ACL Controller can automatically create ACL tokens for Consul on ECS. Refer to the
[ACL Controller](/docs/manual/acl-controller) page for installation details.
### Create Consul client token
You must create a token for the Consul client. This is a shared token used by the `consul-client`
containers to join the Consul cluster.
The following is the ACL policy needed for the Consul client token:
```hcl
node_prefix "" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
```
This policy allows `node:write` for any node name, which is necessary because the Consul node
names on ECS are not known until runtime.
### Create service tokens
Service tokens should be associated with a [service identity](https://www.consul.io/docs/security/acl/acl-system#acl-service-identities).
The service identity includes `service:write` permissions for the service and sidecar proxy.
The following example shows how to use the Consul CLI to create a service token for a service named `example-client-app`:
```shell
consul acl token create -service-identity=example-client-app ...
```
-> **NOTE**: You will need to create one service token for each registered Consul service in ECS,
including when new services are added to the service mesh.
## Secret storage
You should securely store the following secrets in order to make them available to ECS tasks.
1. Consul Server CA certificate
2. Consul gossip encryption key
3. Consul client ACL token
4. Consul service ACL tokens (one per service)
These secrets can be securely stored and passed to ECS tasks using either of the following AWS secret services:
* [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data-parameters.html)
* [AWS Secrets Manager](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data-secrets.html)
Once the secrets are stored they can be referenced using their ARN. The following shows
example secret ARNs when using AWS Secrets Manager:
| Secret | Sample Secret ARN |
| ---------------------- | ---------------------------------------------------------------------------------- |
| Consul Server CA Cert | `arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-ca-cert` |
| Gossip encryption key | `arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-gossip-key` |
| Client token | `arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-client-token` |
| Service token | `arn:aws:secretsmanager:us-west-2:000000000000:secret:my-example-client-app-token` |
## Configure `consul-client`
The following secrets must be passed to the `consul-client` container:
* Consul server CA certificate
* Gossip encryption key
* Consul client ACL token
The following example shows how to include these secrets in the task definition. The `secrets`
list specifies environment variable `name`s that will be set to the secret values for this container.
ECS automatically fetches the secret values specified in the `valueFrom` fields during task provisioning.
```json
{
"containerDefinitions": [
{
"name": "consul-client"
"image": "public.ecr.aws/hashicorp/consul:<CONSUL_VERSION>",
"secrets": [
{
"name": "CONSUL_CACERT",
"valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-ca-cert"
},
{
"name": "CONSUL_GOSSIP_ENCRYPTION_KEY",
"valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-gossip-key"
},
{
"name": "AGENT_TOKEN",
"valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-client-token"
}
]
},
...
]
}
```
Next, update Consul configuration options to pass the secrets to the Consul client.
The following is an example of the *additional* content to include in the `consul-client` startup script. Refer to the [install
page](/docs/ecs/manual/install#consul-client-container) for the remainder of the startup script and how to pass this
script to the container.
<CodeBlockConfig highlight="3-4,10-29">
```shell
...
# Write the CA Cert to a file
echo "$CONSUL_CACERT" > /tmp/consul-ca-cert.pem
# Write the Consul agent configuration file.
cat << EOF > /consul/agent-defaults.hcl
...
# Configure gossip encryption key
encrypt = "$CONSUL_GOSSIP_ENCRYPTION_KEY"
# Configure TLS settings
auto_encrypt = {
tls = true
ip_san = ["$ECS_IPV4"]
}
ca_file = "/tmp/consul-ca-cert.pem"
verify_outgoing = true
# Configure ACLs
acl {
enabled = true
default_policy = "deny"
down_policy = "async-cache"
tokens {
agent = "$AGENT_TOKEN"
}
}
EOF
```
</CodeBlockConfig>
The following table describes the additional fields that must be included in the Consul client configuration file.
| Field name | Type | Description |
| --------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------ |
| [`encrypt`](/docs/agent/options#_encrypt) | string | Specifies the gossip encryption key |
| [`ca_file`](/docs/agent/options#ca_file) | string | Specifies the Consul server CA cert for TLS verification. |
| [`acl.enabled`](/docs/agent/options#acl_enabled) | boolen | Enable ACLs for this agent. |
| [`acl.tokens.agent`](/docs/agent/options#acl_tokens_agent) | string | Specifies the Consul client token which authorizes this agent with Consul servers. |
## Configure `consul-ecs-mesh-init` and `consul-ecs-health-sync`
Both `consul-ecs-mesh-init` and `consul-ecs-health-sync` containers need to be configured with
the service ACL token. This allows these containers to make HTTP API requests to the local
Consul client for service registration and health syncing.
The following shows how to set the `CONSUL_HTTP_TOKEN` variable to the service token for the `example-client-app` service,
if the token is stored in AWS Secrets Manager.
<CodeBlockConfig highlight="5-8">
```json
{
"containerDefinitions": [
{
"secrets": [
{
"name": "CONSUL_HTTP_TOKEN",
"valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-example-client-app-token"
}
]
},
...
],
...
}
```
</CodeBlockConfig>

View File

@ -9,8 +9,18 @@ description: >-
The following requirements must be met in order to install Consul on ECS:
1. **Terraform:** The tasks that you want to add to the service mesh must first be modeled in Terraform.
1. **Launch Type:** Fargate and EC2 launch types are supported.
1. **Subnets:** ECS Tasks can run in private or public subnets. Tasks must have [network access](https://aws.amazon.com/premiumsupport/knowledge-center/ecs-pull-container-api-error-ecr/) to Amazon ECR or other public container registries to pull images.
1. **Consul Servers:** You can use your own Consul servers running on virtual machines or use [HashiCorp Cloud Platform Consul](https://www.hashicorp.com/cloud-platform) to host the servers for you. For development purposes or testing, you may use the `dev-server` [Terraform module](https://github.com/hashicorp/terraform-aws-consul-ecs/tree/main) that runs the Consul server as an ECS task. The `dev-server` does not support persistent storage.
1. **ACL Controller:** If you are running a secure Consul installation with ACLs enabled, configure the ACL controller.
1. **Sidecar containers:** Consul on ECS requires two sidecar containers to run in each ECS task: a
Consul agent container and a sidecar proxy container. These additional sidecar containers must
be included in the ECS task definition. The [Consul ECS Terraform module](/docs/ecs/terraform/install)
will include these sidecar containers for you. If you do not use Terraform, you can construct
the task definition yourself by following [our documentation](/docs/ecs/manual/install).
1. **Routing:** With your application running in tasks as part of the mesh, you must specify the
upstream services that your application calls. You will also need to change the URLs your
application uses to ensure the application is making requests through the service mesh.
1. **Bind Address:** Once all communication is flowing through the service mesh, you should change
the address your application is listening on to `127.0.0.1` so that it only receives requests
through the sidecar proxy.

View File

@ -0,0 +1,276 @@
---
layout: docs
page_title: Installing Consul on AWS ECS using Terraform
description: >-
Install Consul Service Mesh on AWS ECS with Terraform (Elastic Container Service).
---
# Installation with Terraform
This topic describes how to use the [`mesh-task`](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task) Terraform module to launch your application in AWS ECS as part of Consul service mesh. If you do not use Terraform, see the [Manual Installation](/docs/ecs/manual-installation) page to install Consul on ECS without Terraform.
This topic does not include instructions for creating all AWS resources necessary to install Consul, such as a VPC or the ECS cluster. Refer to the linked guides in the [Getting Started](/docs/ecs#getting-started) section for complete, runnable examples.
## Overview
This topic describes the following procedure:
1. Create Terraform configuration files for the necessary components:
* [ECS task definition](#using-the-mesh-task-module): Use the `mesh-task` module to create an ECS task definition for Consul on ECS
* [ECS service](#ecs-service): Use the `aws_ecs_service` resource to create an ECS service that schedules service mesh tasks to run on ECS
2. [Run Terraform](#running-terraform) to deploy the resources in AWS
## Prerequisites
* You should have some familiarity with using Terraform. Refer to the [Terraform documentation](https://www.terraform.io/docs) to learn about infrastructure as code and how to get started with Terraform.
* You should also be familiar with AWS ECS before following these instructions. See [What is Amazon Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) for details.
## Using the Mesh Task Module
To run an application in ECS with Consul service mesh, you must create an ECS task definition, which includes your application container(s)
and additional sidecar containers, such as the Consul agent container and the Envoy sidecar proxy container.
The [`mesh-task` module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task) will automatically include the necessary sidecar containers.
The following example shows a Terraform configuration file that creates a task definition with an application container called `example-client-app` in a file called `mesh-task.tf`:
<CodeBlockConfig filename="mesh-task.tf">
```hcl
module "my_task" {
source = "hashicorp/consul-ecs/aws//modules/mesh-task"
version = "<latest version>"
family = "my_task"
container_definitions = [
{
name = "example-client-app"
image = "docker.io/org/my_task:v0.0.1"
essential = true
portMappings = [
{
containerPort = 9090
hostPort = 9090
protocol = "tcp"
}
]
cpu = 0
mountPoints = []
volumesFrom = []
}
]
port = 9090
retry_join = ["<address of the Consul server>"]
}
```
</CodeBlockConfig>
The following fields are required. Refer to the [module reference documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs) for a complete reference.
| Input Variable | Type | Description |
| ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `source` | string | Must be set to the source location of the `mesh-task` module, `hashicorp/consul-ecs/aws//modules/mesh-task`. |
| `version` | string | Must be set to the version of the `mesh-task` module. |
| `family` | string | The [ECS task definition family](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#family). The family is also used as the Consul service name by default. |
| `container_definitions` | list | This is the list of [container definitions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definitions) for the task definition. This is where you include your application containers. |
| `essential` | boolean | Must be `true` to ensure the health of your application container affects the health status of the task. |
| `port` | integer | The port that your application listens on, if any. If your application does not listen on a port, set `outbound_only = true`. |
| `retry_join` | list | The is the [`retry_join`](/docs/agent/options#_retry_join) option for the Consul agent, which specifies the locations of your Consul servers. |
### Running Terraform
You will need to run Terraform to create the task definition.
Save the Terraform configuration for the task definition to a file, such as `mesh-task.tf`.
You should place this file in a directory alongside other Terraform configuration files for your project.
The `mesh-task` module requires the AWS Terraform provider. The following example shows how to include
and configure the AWS provider in a file called `provider.tf`. Refer to the [AWS Terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)
documentation for complete configuration details.
<CodeBlockConfig filename="provider.tf">
```hcl
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "<latest version>"
}
}
}
provider "aws" {
region = "<AWS region>"
...
}
```
</CodeBlockConfig>
Additional AWS resources for your project can be included in additional Terraform configuration files
in the same directory. The following example shows a basic project directory:
```shell-session
$ ls
mesh-task.tf
provider.tf
...
```
Terraform should be run in your project directory as follows.
* Run `terraform init` first to download dependencies, such as Terraform providers
* Run `terraform apply` to have Terraform create AWS resources, such as the task definition from the `mesh-task` module.
Terraform automatically reads all files in the current directory that have a `.tf` file extension.
Refer to the [Terraform documentation](https://www.terraform.io/docs) for more information and Terraform best practices.
## ECS Service
[ECS services](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html) are one of the most common
ways to start tasks using a task definition.
To define an ECS service, reference the `mesh-task` module's `task_definition_arn` output value
in your `aws_ecs_service` resource. The following example shows how to include the service in the `mesh-task.tf` file.
<CodeBlockConfig filename="mesh-task.tf" highlight="6-12">
```hcl
module "my_task" {
source = "hashicorp/consul-ecs/aws//modules/mesh-task"
...
}
resource "aws_ecs_service" "my_task" {
name = "my_task_service"
task_definition = module.my_task.task_definition_arn
launch_type = "FARGATE"
propagate_tags = "TASK_DEFINITION"
...
}
```
</CodeBlockConfig>
This is a partial configuration to highlight some important fields.
See the [`aws_ecs_service`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) documentation for a complete reference.
| Input Variable | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The name of the ECS service. This is required by AWS but is not used by Consul service mesh. |
| `task_definition` | string | The task definition used to start tasks. Set this to the task definition ARN returned by the `mesh-task` module. |
| `launch_type` | string | The launch type. Consul on ECS supports the `FARGATE` and `EC2` launch types. |
| `propagate_tags` | string | This must be set to `TASK_DEFINITION` so that tags added by `mesh-task` to the task definition are copied to tasks. |
After including the ECS service in your Terraform configuration, run `terraform apply`
from your project directory to create the ECS service resource. The ECS service will
soon start your application in a task. The task will automatically register itself
into the Consul service catalog during startup.
-> **NOTE:** If your tasks run in a public subnet, they must have `assign_public_ip = true`
in their [`network_configuration`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service#network_configuration) block so that ECS can pull the Docker images.
## Routing
Now that your tasks are registered in the mesh, you're able to use the service
mesh to route between them.
In order to make calls through the service mesh, you must configure the sidecar
proxy to listen on a different port for each upstream service your application
needs to call. You then must modify your application to make requests to the sidecar
proxy on that port.
For example, if your application `web` makes calls to another application called `backend`, then you would first configure the `mesh-task` module's upstream(s):
```hcl
module "web" {
family = "web"
upstreams = [
{
destinationName = "backend"
localBindPort = 8080
}
]
}
```
| Input Variable | Type | Description |
| ----------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `destinationName` | string | The name of the upstream service, as it is registered in the Consul service catalog. |
| `localBindPort` | integer | Requests to this port will be forwarded by the proxy to the upstream service. This must be an unused port, but does not need to match the upstream service port. |
If you have multiple upstream services they each need to be listed here.
Next, configure your application to make requests to `localhost:8080` when
it wants to call the `backend` service.
For example, if your service allows configuring the URL for `backend` via the
`BACKEND_URL` environment variable, you would set:
```hcl
module "web" {
family = "web"
upstreams = [
{
destinationName = "backend"
localBindPort = 8080
}
]
container_definitions = [
{
name = "web"
environment = [
{
name = "BACKEND_URL"
value = "http://localhost:8080"
}
]
...
}
]
...
}
```
## Bind Address
To ensure that your application only receives traffic through the service mesh,
you must change the address that your application is listening on to only the loopback address
(also known as `localhost`, `lo`, and `127.0.0.1`)
so that only the sidecar proxy running in the same task can make requests to it.
If your application is listening on all interfaces, e.g. `0.0.0.0`, then other
applications can call it directly, bypassing its sidecar proxy.
Changing the listening address is specific to the language and framework you're
using in your application. Regardless of which language/framework you're using,
it's a good practice to make the address configurable via environment variable.
For example in Go, you would use:
```go
s := &http.Server{
Addr: "127.0.0.1:8080",
...
}
log.Fatal(s.ListenAndServe())
```
In Django you'd use:
```bash
python manage.py runserver "127.0.0.1:8080"
```
## Next Steps
- Follow the [Secure Configuration](/docs/ecs/secure-configuration) to get production-ready.
- Now that your applications are running in the service mesh, read about
other [Service Mesh features](/docs/connect).
- View the [Architecture](/docs/ecs/architecture) documentation to understand
what's going on under the hood.

View File

@ -90,7 +90,7 @@ module "my_task" {
}
]
port = "9090"
port = 9090
retry_join = ["<address of the Consul server>"]
}
```
@ -111,5 +111,5 @@ resource.
Now that your task(s) are migrated to the `mesh-task` module,
- Start at the [ECS Service section](/docs/ecs/get-started/install#ecs-service) of the Installation Guide to continue installing Consul on ECS.
- Start at the [ECS Service section](/docs/ecs/terraform/install#ecs-service) of the Installation Guide to continue installing Consul on ECS.
- Refer to the [`mesh-task` reference documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs) for all available inputs to your mesh tasks.

View File

@ -1,11 +1,11 @@
---
layout: docs
page_title: Production Installation - AWS ECS
page_title: Secure Configuration - AWS ECS
description: >-
Production Installation of the Consul Service Mesh on AWS ECS (Elastic Container Service).
Secure Configuration of the Consul Service Mesh on AWS ECS (Elastic Container Service) with Terraform.
---
# Production Installation
# Secure Configuration
For a production-ready installation of Consul on ECS, you will need to make sure that the cluster is secured.
A secure Consul cluster should include the following:
@ -68,7 +68,7 @@ deploying this controller.
## Deploy Services
Once the ACL controller is up and running, you will be able to deploy services on the mesh using the [`mesh-task` module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task).
Start with the basic configuration for the [Task Module](/docs/ecs/get-started/install#task-module) and specify additional settings to make the configuration production-ready.
Start with the basic configuration for the [Task Module](/docs/ecs/terraform/install#task-module) and specify additional settings to make the configuration production-ready.
First, you will need to create an AWS Secrets Manager secret for the gossip encryption key that the Consul clients
should use.
@ -104,5 +104,5 @@ module "my_task" {
}
```
Now you can deploy your services! Follow the rest of the steps in the [Installation instructions](/docs/ecs/get-started/install#task-module)
Now you can deploy your services! Follow the rest of the steps in the [Installation instructions](/docs/ecs/terraform/install#task-module)
to deploy and connect your services.

View File

@ -608,23 +608,40 @@
"path": "ecs"
},
{
"title": "Get Started",
"title": "Requirements",
"path": "ecs/requirements"
},
{
"title": "Install with Terraform",
"routes": [
{
"title": "Requirements",
"path": "ecs/get-started/requirements"
},
{
"title": "Installation",
"path": "ecs/get-started/install"
"path": "ecs/terraform/install"
},
{
"title": "Production Installation",
"path": "ecs/get-started/production-installation"
"title": "Secure Configuration",
"path": "ecs/terraform/secure-configuration"
},
{
"title": "Migrate Existing Tasks",
"path": "ecs/get-started/migrate-existing-tasks"
"path": "ecs/terraform/migrate-existing-tasks"
}
]
},
{
"title": "Install Manually",
"routes": [
{
"title": "Installation",
"path": "ecs/manual/install"
},
{
"title": "Secure Configuration",
"path": "ecs/manual/secure-configuration"
},
{
"title": "ACL Controller",
"path": "ecs/manual/acl-controller"
}
]
},
@ -635,6 +652,10 @@
{
"title": "Consul Enterprise",
"path": "ecs/enterprise"
},
{
"title": "Configuration Reference",
"path": "ecs/configuration-reference"
}
]
},