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.