Installation with 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 guides in the Getting Started section for complete and runnable examples.

    The following procedure describes the general workflow:

    1. Create Terraform configuration files for the necessary components:

      • : Use the HashiCorp Terraform modules to create the ECS task definition.
      • ECS service: Use the resource to create an ECS service that schedules service mesh tasks to run on ECS.
    2. to deploy the resources in AWS

    If you want to operate Consul in production environments, follow the instructions in the Secure Configuration documentation. The instructions describe how to enable ACLs and TLS and gossip encyption, which provide network security for production-grade deployments.

    Requirements

    • You should have some familiarity with using Terraform. Refer to the Terraform documentation 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 for details.
    • If you intend to use the gateway-task module to deploy mesh gateways, all Consul server and client agents in all datacenters must have TLS and gossip encryption enabled. Refer to the documentation for instructions.

    To run an application in ECS with Consul service mesh, you must create an ECS task definition. The task definition includes your application containers and additional sidecar containers, such as the Consul agent container and the Envoy sidecar proxy container.

    Create a Terraform configuration file and include the mesh-task module. The module automatically adds the necessary sidecar containers.

    If you intend to peer the service mesh to multiple Consul datacenters or partitions, you can also include the gateway-task module. The module enables connectivity between datacenters across service meshes.

    Create a Terraform configuration file and specify the mesh-task module in the source field. The mesh-task module automatically includes the necessary sidecar containers.

    In the following example, the Terraform configuration file called mesh-task.tf creates a task definition with an application container called example-client-app:

    mesh-task.tf

    1. module "my_task" {
    2. source = "hashicorp/consul-ecs/aws//modules/mesh-task"
    3. version = "<latest version>"
    4. family = "my_task"
    5. container_definitions = [
    6. {
    7. name = "example-client-app"
    8. image = "docker.io/org/my_task:v0.0.1"
    9. essential = true
    10. portMappings = [
    11. {
    12. containerPort = 9090
    13. hostPort = 9090
    14. protocol = "tcp"
    15. }
    16. ]
    17. cpu = 0
    18. mountPoints = []
    19. volumesFrom = []
    20. }
    21. ]
    22. port = 9090
    23. retry_join = ["<address of the Consul server>"]
    24. consul_datacenter = "<name of your Consul datacenter>"
    25. }

    The following fields are required. Refer to the for a complete reference.

    ECS services 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.

    1. module "my_task" {
    2. source = "hashicorp/consul-ecs/aws//modules/mesh-task"
    3. ...
    4. }
    5. resource "aws_ecs_service" "my_task" {
    6. name = "my_task_service"
    7. task_definition = module.my_task.task_definition_arn
    8. launch_type = "FARGATE"
    9. propagate_tags = "TASK_DEFINITION"
    10. ...
    11. }

    Installation - 图2

    mesh-task.tf

    1. module "my_task" {
    2. source = "hashicorp/consul-ecs/aws//modules/mesh-task"
    3. ...
    4. }
    5. resource "aws_ecs_service" "my_task" {
    6. name = "my_task_service"
    7. task_definition = module.my_task.task_definition_arn
    8. launch_type = "FARGATE"
    9. propagate_tags = "TASK_DEFINITION"
    10. ...
    11. }

    The example shows a partially configured ECS service to highlight significant fields. Refer to for a complete configuration reference.

    Input VariableTypeDescription
    namestringThe name of the ECS service. This name is required by AWS but is not used by Consul service mesh.
    task_definitionstringThe task definition used to start tasks. Set this option to the task definition ARN returned by the mesh-task module.
    launch_typestringThe launch type. Consul on ECS supports the FARGATE and EC2 launch types.
    propagate_tagsstringThis option 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 then starts your application in a task. The task automatically registers 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 block so that ECS can pull the Docker images.

    Add the gateway-task to your Terraform configuration if you want to deploy a mesh gateway. Mesh gateways enable service to service communication across the WAN, as well as federate service mesh traffic across Consul admin partitions and Consul datacenters over the WAN. Refer to the following documentation to learn more about mesh gateways:

    You must add and configure a gateway-task for each Consul datacenter in your network. You must also enable TLS and gossip encryption on all server and client agents in all data centers per the . Mesh gateways operate by sniffing and extracting the server name indication (SNI) header from the service mesh session and routing the connection to the appropriate destination based on the server name requested.

    • Consul client
    • Envoy gateway proxy
    • Mesh init

    You will need to provide inputs for the artifacts created by the gateway-task module. The following example defines a mesh gateway task called my-gateway in a file called mesh-gateway.tf:

    1. module "my_mesh_gateway" {
    2. source = "hashicorp/consul-ecs/aws//modules/gateway-task"
    3. version = "<latest version>"
    4. kind = "mesh-gateway"
    5. family = "my-gateway"
    6. ecs_cluster_arn = "<ECS cluster ARN>"
    7. retry_join = ["<address of the Consul server>"]
    8. tls = true
    9. consul_server_ca_cert_arn = "<Secrets Manager secret ARN>"
    10. gossip_key_secret_arn = "<Secrets Manager secret ARN>"
    11. }

    mesh-gateway.tf

    1. module "my_mesh_gateway" {
    2. source = "hashicorp/consul-ecs/aws//modules/gateway-task"
    3. version = "<latest version>"
    4. kind = "mesh-gateway"
    5. family = "my-gateway"
    6. ecs_cluster_arn = "<ECS cluster ARN>"
    7. retry_join = ["<address of the Consul server>"]
    8. tls = true
    9. consul_server_ca_cert_arn = "<Secrets Manager secret ARN>"
    10. gossip_key_secret_arn = "<Secrets Manager secret ARN>"
    11. }

    The following fields are required. Refer to the module reference documentation for a complete reference.

    Refer to the for additional example configurations.

    ECS service

    The ECS service is automatically created by the gateway-task module. The service can run one or more instances of the gateway.

    Mesh init

    The mesh-init container is a short-lived container that sets up the initial configurations for Consul and Envoy. The gateway-task module automatically configures the mesh-init container based on the inputs specified in the task definition and configuration.

    For additional information, refer to Task Startup for additional information.

    Gateway task configuration examples

    The following examples illustrate how to configure the gateway-task for different use cases.

    Ingress

    Mesh gateways need to be reachable over the WAN to route traffic between datacenters. Configure the following options in the gateway-task to enable ingress through the mesh gateway.

    Input variableTypeDescription
    lb_enabledBooleanSet to true to automatically deploy and configure a network load balancer for ingress to the mesh gateway.
    lb_vpc_idstringSpecifies the VPC to launch the load balancer in.
    lb_subnetslist of stringsSpecifies one or more public subnets to associate with the load balancer.
    1. module "my_mesh_gateway" {
    2. ...
    3. lb_enabled = true
    4. lb_vpc_id = "<VPC ID>"
    5. lb_subnets = ["<public subnet IDs>"]
    6. }

    Installation - 图4

    mesh-gateway.tf

    1. module "my_mesh_gateway" {
    2. ...
    3. lb_enabled = true
    4. lb_vpc_id = "<VPC ID>"
    5. lb_subnets = ["<public subnet IDs>"]
    6. }

    Alternatively, you can manually configure ingress to the mesh gateway and provide the wan_address and wan_port inputs to the gateway-task module. The wan_port field is optional. Port 8443 is used by default.

    1. module "my_mesh_gateway" {
    2. ...
    3. wan_address = "<public WAN address>"
    4. wan_port = <public WAN port>
    5. }

    mesh-gateway.tf

    Mesh gateways route L4 TCP connections and do not terminate mTLS sessions. If you manually configure for ingress to a mesh gateway, you must use an AWS Network Load Balancer or a .

    ACLs

    Configure the following options in the gateway-task when ACLs are enabled.

    1. module "my_mesh_gateway" {
    2. ...
    3. acls = true
    4. consul_http_addr = "<HTTP address of the Consul server>"
    5. consul_https_ca_cert_arn = "<Secrets Manager secret ARN>"
    6. }

    Installation - 图6

    mesh-gateway.tf

    1. module "my_mesh_gateway" {
    2. ...
    3. acls = true
    4. consul_http_addr = "<HTTP address of the Consul server>"
    5. consul_https_ca_cert_arn = "<Secrets Manager secret ARN>"
    6. }
    WAN federation

    Configure the following options in the gateway-task to enable WAN federation via mesh gateways.

    OptionTypeDescription
    consul_datacenterstringSpecifies the name of the local Consul datacenter.
    consul_primary_datacenterstringSpecifies the name of the primary Consul datacenter.
    enable_mesh_gateway_wan_federationBooleanSet to true to enable WAN federation.
    enable_acl_token_replicationBooleanSet to true to enable ACL token replication and allow the creation of local tokens secondary datacenters.

    The following example shows how to configure the gateway-task module.

    1. module "my_mesh_gateway" {
    2. ...
    3. consul_datacenter = "<name of the local Consul datacenter>"
    4. consul_primary_datacenter = "<name of the primary Consul datacenter>"
    5. enable_mesh_gateway_wan_federation = true
    6. enable_acl_token_replication = true
    7. }

    mesh-gateway.tf

    1. module "my_mesh_gateway" {
    2. ...
    3. consul_datacenter = "<name of the local Consul datacenter>"
    4. consul_primary_datacenter = "<name of the primary Consul datacenter>"
    5. enable_mesh_gateway_wan_federation = true
    6. enable_acl_token_replication = true
    7. }

    When federating Consul datacenters over the WAN with ACLs enabled, must be enabled on all server and client agents in all datacenters.

    Run 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 documentation for complete configuration details.

    1. terraform {
    2. required_providers {
    3. aws = {
    4. source = "hashicorp/aws"
    5. version = "<latest version>"
    6. }
    7. }
    8. provider "aws" {
    9. region = "<AWS region>"
    10. ...
    11. }

    provider.tf

    1. terraform {
    2. required_providers {
    3. aws = {
    4. source = "hashicorp/aws"
    5. version = "<latest version>"
    6. }
    7. }
    8. provider "aws" {
    9. region = "<AWS region>"
    10. ...
    11. }

    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:

    1. $ ls
    2. mesh-task.tf
    3. provider.tf
    4. ...
    1. $ ls
    2. mesh-task.tf
    3. provider.tf
    4. ...

    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 for more information and Terraform best practices.

    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):

    1. module "web" {
    2. family = "web"
    3. upstreams = [
    4. {
    5. destinationName = "backend"
    6. localBindPort = 8080
    7. }
    8. ]
    9. }

    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:

    1. module "web" {
    2. family = "web"
    3. upstreams = [
    4. {
    5. destinationName = "backend"
    6. localBindPort = 8080
    7. }
    8. ]
    9. container_definitions = [
    10. {
    11. name = "web"
    12. environment = [
    13. {
    14. name = "BACKEND_URL"
    15. value = "http://localhost:8080"
    16. }
    17. ]
    18. ...
    19. }
    20. ]
    21. ...
    22. }
    1. module "web" {
    2. family = "web"
    3. upstreams = [
    4. {
    5. destinationName = "backend"
    6. localBindPort = 8080
    7. }
    8. ]
    9. container_definitions = [
    10. {
    11. name = "web"
    12. environment = [
    13. {
    14. name = "BACKEND_URL"
    15. value = "http://localhost:8080"
    16. }
    17. ]
    18. ...
    19. }
    20. ]
    21. ...
    22. }

    Configure the bind address

    To ensure that your application only receives traffic through the service mesh, you must change the address that your application listens on to the loopback address. The loopback address is also called localhost, lo, and 127.0.0.1. Binding to the loopback address allows the sidecar proxy running in the same task to only make requests within the service mesh.

    If your application is listening on all interfaces, such as 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 or framework you’re using, binding the loopback address to a dynamic value, such as an environment variable, is a best practice:

    1. export BIND_ADDRESS="127.0.0.1:8080"
    1. export BIND_ADDRESS="127.0.0.1:8080"

    The following examples demonstrate how to bind the loopback address to an environment variable in golang and Django (Python):

    Go

    • Go
    • Bash
    1. s := &http.Server{
    2. Addr: os.Getenv("BIND_ADDRESS"),
    3. ...
    4. }
    5. log.Fatal(s.ListenAndServe())
    1. s := &http.Server{
    2. Addr: os.Getenv("BIND_ADDRESS"),
    3. ...
    4. }
    5. log.Fatal(s.ListenAndServe())
    1. python manage.py runserver "$BIND_ADDRESS"
    • Follow the Secure Configuration to get production-ready.
    • View the documentation to understand what’s going on under the hood.