Skip to content

Commit a211b97

Browse files
committed
django on fargate, training on ec2
1 parent 48b28ca commit a211b97

File tree

9 files changed

+321
-130
lines changed

9 files changed

+321
-130
lines changed

.github/workflows/push-django-ecs.yml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# This workflow will build and push a new container image to Amazon ECR,
2+
# and then will deploy a new task definition to Amazon ECS, when there is a push to the "main" branch.
3+
#
4+
# To use this workflow, you will need to complete the following set-up steps:
5+
#
6+
# 1. Create an ECR repository to store your images.
7+
# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`.
8+
# Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name.
9+
# Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region.
10+
#
11+
# 2. Create an ECS task definition, an ECS cluster, and an ECS service.
12+
# For example, follow the Getting Started guide on the ECS console:
13+
# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun
14+
# Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service.
15+
# Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster.
16+
#
17+
# 3. Store your ECS task definition as a JSON file in your repository.
18+
# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`.
19+
# Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file.
20+
# Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container
21+
# in the `containerDefinitions` section of the task definition.
22+
#
23+
# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
24+
# See the documentation for each action used below for the recommended IAM policies for this IAM user,
25+
# and best practices on handling the access key credentials.
26+
27+
name: ECS Django Container Deployment
28+
29+
# Only trigger when user clicks "run workflow"
30+
on:
31+
workflow_dispatch:
32+
pull_request:
33+
34+
env:
35+
AWS_REGION: "us-east-1" # set this to your preferred AWS region, e.g. us-west-1
36+
ECR_REPOSITORY: "django" # set this to your Amazon ECR repository name
37+
ECS_SERVICE: "django" # set this to your Amazon ECS service name
38+
ECS_CLUSTER: "backend" # set this to your Amazon ECS cluster name
39+
CONTAINER_NAME: "django" # set this to the name of the container in the containerDefinitions section of your task definition
40+
41+
permissions:
42+
contents: read
43+
actions: write
44+
45+
jobs:
46+
deploy:
47+
name: Deploy
48+
runs-on: ubuntu-latest
49+
environment: production
50+
steps:
51+
- name: Get current branch
52+
run: echo running on branch ${GITHUB_REF##*/}
53+
54+
- name: Checkout
55+
uses: actions/checkout@v3
56+
57+
- name: Configure AWS credentials
58+
uses: aws-actions/configure-aws-credentials@v1
59+
with:
60+
aws-access-key-id: ${{ secrets.AWS_DEPLOY_ACCESS_KEY_ID }}
61+
aws-secret-access-key: ${{ secrets.AWS_DEPLOY_SECRET_ACCESS_KEY }}
62+
aws-region: ${{ env.AWS_REGION }}
63+
64+
- name: Login to Amazon ECR
65+
id: login-ecr
66+
uses: aws-actions/amazon-ecr-login@v1
67+
68+
- name: Build, tag, and push image to Amazon ECR
69+
id: build-image
70+
env:
71+
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
72+
IMAGE_TAG: ${{ github.sha }}
73+
run: |
74+
# Build a docker container and
75+
# push it to ECR so that it can
76+
# be deployed to ECS.
77+
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG training -f training/Dockerfile.prod
78+
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
79+
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
80+
81+
- name: Download task definition
82+
run: |
83+
aws ecs describe-task-definition --task-definition django --query taskDefinition > temp-task-definition.json
84+
- name: Fill in the new image ID in the Amazon ECS task definition
85+
id: task-def
86+
uses: aws-actions/amazon-ecs-render-task-definition@v1
87+
with:
88+
task-definition: temp-task-definition.json
89+
container-name: ${{ env.CONTAINER_NAME }}
90+
image: ${{ steps.build-image.outputs.image }}
91+
92+
- name: Deploy Amazon ECS task definition
93+
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
94+
with:
95+
task-definition: ${{ steps.task-def.outputs.task-definition }}
96+
service: ${{ env.ECS_SERVICE }}
97+
cluster: ${{ env.ECS_CLUSTER }}
98+
wait-for-service-stability: true

dlp-terraform/ecs/alb.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ resource "aws_lb_target_group" "app" {
3434
vpc_id = aws_vpc.main.id
3535
protocol = "HTTP"
3636
port = 8000
37-
target_type = "instance"
37+
target_type = "ip"
3838

3939
health_check {
4040
enabled = true

dlp-terraform/ecs/ecr.tf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ resource "aws_ecr_repository" "training" {
88
}
99
}
1010

11+
resource "aws_ecr_repository" "django" {
12+
name = "django"
13+
image_tag_mutability = "MUTABLE"
14+
force_delete = true
15+
16+
image_scanning_configuration {
17+
scan_on_push = true
18+
}
19+
}
20+
1121
output "training_repo_url" {
1222
value = aws_ecr_repository.training.repository_url
1323
}
24+
25+
output "django_repo_url" {
26+
value = aws_ecr_repository.django.repository_url
27+
}

dlp-terraform/ecs/ecs.tf

Lines changed: 7 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -31,105 +31,6 @@ resource "aws_iam_instance_profile" "ecs_node" {
3131
role = aws_iam_role.ecs_node_role.name
3232
}
3333

34-
# --- ECS Node Security Group ---
35-
resource "aws_security_group" "ecs_node_sg" {
36-
name_prefix = "backend-ecs-node-sg-"
37-
vpc_id = aws_vpc.main.id
38-
39-
ingress {
40-
from_port = 0
41-
to_port = 0
42-
protocol = "-1"
43-
# cidr_blocks = [aws_vpc.main.cidr_block]
44-
security_groups = [ aws_security_group.http.id ]
45-
}
46-
47-
egress {
48-
from_port = 0
49-
to_port = 0
50-
protocol = "-1"
51-
cidr_blocks = ["0.0.0.0/0"]
52-
}
53-
}
54-
55-
# --- ECS Launch Template ---
56-
resource "aws_launch_template" "ecs_lt_training" {
57-
name_prefix = "training-ecs-template-"
58-
image_id = "ami-01ff5874b57a57613"
59-
instance_type = "g4dn.xlarge"
60-
61-
vpc_security_group_ids = [aws_security_group.ecs_node_sg.id]
62-
iam_instance_profile {
63-
arn = aws_iam_instance_profile.ecs_node.arn
64-
}
65-
monitoring {
66-
enabled = true
67-
}
68-
69-
user_data = base64encode(<<-EOF
70-
#!/bin/bash
71-
echo ECS_CLUSTER=${aws_ecs_cluster.main.name} >> /etc/ecs/ecs.config;
72-
EOF
73-
)
74-
}
75-
76-
# --- ECS ASG ---
77-
resource "aws_autoscaling_group" "training" {
78-
name_prefix = "training-ecs-asg-"
79-
vpc_zone_identifier = aws_subnet.public[*].id
80-
min_size = 0
81-
max_size = 2
82-
desired_capacity = 1
83-
health_check_grace_period = 0
84-
health_check_type = "EC2"
85-
protect_from_scale_in = false
86-
87-
launch_template {
88-
id = aws_launch_template.ecs_lt_training.id
89-
version = "$Latest"
90-
}
91-
92-
tag {
93-
key = "Name"
94-
value = "backend-ecs-cluster"
95-
propagate_at_launch = true
96-
}
97-
98-
tag {
99-
key = "AmazonECSManaged"
100-
value = ""
101-
propagate_at_launch = true
102-
}
103-
}
104-
105-
# --- ECS Capacity Provider ---
106-
resource "aws_ecs_capacity_provider" "training" {
107-
name = "training-ecs-ec2"
108-
109-
auto_scaling_group_provider {
110-
auto_scaling_group_arn = aws_autoscaling_group.training.arn
111-
managed_termination_protection = "DISABLED"
112-
113-
managed_scaling {
114-
maximum_scaling_step_size = 2
115-
minimum_scaling_step_size = 1
116-
status = "ENABLED"
117-
target_capacity = 100
118-
}
119-
}
120-
}
121-
122-
resource "aws_ecs_cluster_capacity_providers" "main" {
123-
cluster_name = aws_ecs_cluster.main.name
124-
capacity_providers = [aws_ecs_capacity_provider.training.name]
125-
126-
default_capacity_provider_strategy {
127-
capacity_provider = aws_ecs_capacity_provider.training.name
128-
base = 1
129-
weight = 100
130-
}
131-
}
132-
13334
# --- ECS Task Role ---
13435
data "aws_iam_policy_document" "ecs_task_doc" {
13536
statement {
@@ -169,7 +70,12 @@ resource "aws_iam_role_policy_attachment" "ecs_exec_role_policy" {
16970
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
17071
}
17172

172-
resource "aws_cloudwatch_log_group" "ecs" {
173-
name = "/ecs/backend"
73+
resource "aws_cloudwatch_log_group" "training" {
74+
name = "/ecs/training"
17475
retention_in_days = 14
17576
}
77+
78+
resource "aws_cloudwatch_log_group" "django" {
79+
name = "/ecs/django"
80+
retention_in_days = 14
81+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
resource "aws_ecs_task_definition" "django" {
2+
family = "django"
3+
task_role_arn = aws_iam_role.ecs_task_role.arn
4+
execution_role_arn = aws_iam_role.ecs_exec_role.arn
5+
network_mode = "awsvpc"
6+
requires_compatibilities = ["FARGATE"]
7+
cpu = 1024
8+
memory = 2048
9+
10+
container_definitions = jsonencode([
11+
{
12+
"name": "django",
13+
"image" : "${aws_ecr_repository.django.repository_url}:latest",
14+
"cpu": 1024,
15+
"memory": 2048,
16+
"essential": true,
17+
"portMappings": [
18+
{
19+
"name" : "gunicorn-port",
20+
"containerPort" : 8000,
21+
"hostPort" : 8000,
22+
"protocol" : "tcp",
23+
}
24+
],
25+
"logConfiguration" : {
26+
"logDriver" : "awslogs",
27+
"options" : {
28+
"awslogs-create-group" : "true",
29+
"awslogs-region" : "us-east-1",
30+
"awslogs-group" : aws_cloudwatch_log_group.django.name,
31+
"awslogs-stream-prefix" : "ecs"
32+
}
33+
},
34+
"environment": [
35+
{
36+
"name": "ALLOWED_HOST",
37+
"value": "${aws_lb.main.dns_name}"
38+
}
39+
]
40+
}
41+
])
42+
}
43+
44+
# --- ECS Django Security Group ---
45+
resource "aws_security_group" "ecs_django_sg" {
46+
name_prefix = "backend-ecs-django-sg-"
47+
vpc_id = aws_vpc.main.id
48+
}
49+
50+
resource "aws_vpc_security_group_ingress_rule" "ecs_django_sg_ingress" {
51+
security_group_id = aws_security_group.ecs_django_sg.id
52+
53+
ip_protocol = "-1"
54+
referenced_security_group_id = aws_security_group.http.id
55+
}
56+
57+
resource "aws_vpc_security_group_egress_rule" "ecs_django_sg_egress" {
58+
security_group_id = aws_security_group.ecs_django_sg.id
59+
60+
ip_protocol = "-1"
61+
cidr_ipv4 = "0.0.0.0/0"
62+
}
63+
64+
resource "aws_ecs_service" "django" {
65+
name = "django"
66+
cluster = aws_ecs_cluster.main.id
67+
task_definition = aws_ecs_task_definition.django.arn
68+
desired_count = 2
69+
launch_type = "FARGATE"
70+
71+
network_configuration {
72+
security_groups = [ aws_security_group.ecs_django_sg.id]
73+
subnets = aws_subnet.public[*].id
74+
assign_public_ip = true
75+
}
76+
77+
lifecycle {
78+
ignore_changes = [desired_count]
79+
}
80+
81+
load_balancer {
82+
target_group_arn = aws_lb_target_group.app.arn
83+
container_name = "django"
84+
container_port = 8000
85+
}
86+
87+
depends_on = [aws_lb_target_group.app]
88+
}

0 commit comments

Comments
 (0)