Typically, helm is used for creating deployment charts for kubernetes platform but I like the helm templating features to generate YAML specifications which can be used to deploy to any platform.

AWS Elastic Container Service(ECS) is another platform where you can run container workloads in AWS Cloud. While I won’t go deep into AWS ECS platform or setting up cluster in this article, we will quickly look at how we can deploy a service to an existing ECS cluster with Helm Templates and AWS CLI.

To deploy a sample application to an AWS ECS cluster, you’ll need at least the following specifications. Additional specifications may be required depending on your cluster design and workloads.

  • Task Definition
  • Service

Task Defintion

Let’s start with Task Definition. A task definition is a blueprint for your application. It is a text file in JSON/YAML format that describes the parameters and one or more containers that form your application.

Below is the helm template for task definition spec.

templates\taskdefinition.yaml

{{ $app := $.Chart.Name -}}
{{ $version := $.Chart.AppVersion -}}
{{ $releaseVersion := $.Chart.Version -}}
containerDefinitions:
{{- range .Values.deployment.containers }}
- cpu: 0
  environment:
  - name: "APPLICATION_NAME"
    value: {{ $app }}
  - name: "APPLICATION_VERSION"
    value: {{ $releaseVersion }}
  {{- range.envs }}
  - name: {{ .name | quote }}
    value: {{ .value | quote }}
  {{- end }}
  essential: true
  image: {{ .Values.deployment.image | quote }}
  volumesFrom: []
  mountPoints: []
  logConfiguration:
    logDriver: awslogs
    options:
      awslogs-group: {{ $.Values.cluster }}
      awslogs-region: {{ $.Values.region }}
      awslogs-stream-prefix: {{ $app }}/{{ $releaseVersion }}
  name: {{ $app }}
  portMappings:
  {{- range.ports }}
  - appProtocol: http
    containerPort: {{ .containerPort }}
    hostPort: {{ .containerPort }}
    name: {{ $app }}-{{ .containerPort }}-tcp
    protocol: tcp
  {{- end }}
{{- end }}
cpu: {{ .Values.deployment.cpu | quote }}
family: {{ $app}}-{{ .Values.environment }}
memory: {{ .Values.deployment.memory | quote }}
taskRoleArn: {{ .Values.deployment.taskRoleArn }}
networkMode: awsvpc

Service

For Service. A service definition defines how to run your Amazon ECS service.

Below is the helm template for service spec.

templates\service.yaml

{{ $app := $.Chart.Name -}}
{{ $version := $.Chart.AppVersion -}}
{{ $releaseVersion := $.Chart.Version -}}
cluster: {{ .Values.cluster }}
deploymentConfiguration:
  maximumPercent: 200
  minimumHealthyPercent: 0
desiredCount: {{ .Values.deployment.desiredCount }}
enableECSManagedTags: true
enableExecuteCommand: true
{{- if .Values.service.targetGroups }}
loadBalancers:
{{- range .Values.service.targetGroups }}
- containerName: {{ $app }}
  containerPort: {{ .targetPort }}
  targetGroupArn: {{ .targetGroupArn }}
{{- end }}
{{- end }}
networkConfiguration:
  awsvpcConfiguration:
    assignPublicIp: DISABLED
    securityGroups:
    - {{ .Values.service.securityGroupId | quote }}
    subnets:
    {{- range .Values.service.subnets }}
    - {{ .subnetId | quote }}
    {{- end }}
propagateTags: SERVICE
taskDefinition: "{{ $app }}-{{ .Values.environment }}:{{ .Values.revision }}"

Helm Values

Now with following example values file spec, you can generate re-usable YAMLs for different types of workloads.

values.yaml

cluster: default
region: us-east-1
service:
#  targetGroups:
#  - targetGroupArn: 'arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/sample-app-int-dev/xxxxx'
#    targetPort: 8080
#  - targetGroupArn: 'arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/sample-app-ext-dev/xxxxx'
#    targetPort: 8080
  securityGroupId: sg-01234567890
  subnets:
  - subnetId: subnet-01234567890
deployment:
  image: "nginx:latest"
  desiredCount: 0
  cpu: 1024
  memory: 256
  taskRoleArn: arn:aws:iam::1234567890:role/ecsTaskExecutionRole
  containers:
  - name: "sample-app"
    ports:
    - containerPort: 8080
    envs:
    - name: NGINX_PORT
      value: "8080"

CLI Commands

Here are the commands for sample nginx application.

# Let's create folder to save the yaml files generated through helm.
mkdir -p helm-charts-out
# Set target example app name, target cluster and environment
target_env=dev
target_cluster=default
service_name=sample-demo-ecs-service
# Below helm generates task definition yaml needed for AWS CLI to create the task definition
helm template aws-ecs-helm-templates \
    -s templates/taskdefinition.yaml \
    -f ./aws-ecs-helm-templates/values.yaml \
    --set environment=dev \
    > ./helm-charts-out/taskdefinition_out.yaml
# Let's execute AWS CLI ECS to create task definition and capture the revision which we will use later for service.
revision=$(aws ecs register-task-definition --cli-input-yaml file://helm-charts-out/taskdefinition_out.yaml --query 'taskDefinition.revision')

# Generate service specicification.
helm template aws-ecs-helm-templates \
    -s templates/service.yaml \
    -f ./aws-ecs-helm-templates/values.yaml \
    --set revision=$revision \
    --set environment=$target_env \
    > ./helm-charts-out/service_out.yaml
# Let's check if service already exists or not.
serviceName=$(aws ecs describe-services --cluster $target_cluster --services "$service_name-$target_env" --query 'services[0].serviceName')

# if service is null then let's create it or else update with revision from earlier command.
if [[ $serviceName == "null" ]]
then
  echo -e "creating the service!!\n"
  aws ecs create-service \
      --cluster $target_cluster \
      --service-name "$service_name-$target_env" \
      --cli-input-yaml file://helm-charts-out/service_out.yaml
else
  echo -e "updating the existing service!!\n"
  aws ecs update-service \
      --cluster $target_cluster \
      --service "$service_name-$target_env" \
      --force-new-deployment \
      --cli-input-yaml file://helm-charts-out/service_out.yaml
fi

That’s it! You’ve now mastered the basics. Next, you can leverage templates and values files to streamline app deployment using the same helm charts across your organization.

The templates shown above are available in this repo.

If you have any feedback/issues, you can submit an issue in site repo.