Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ venv/
ENV/
env.bak/
venv.bak/
variables_env.tf

# mkdocs documentation
/site
Expand Down Expand Up @@ -143,3 +144,11 @@ GitHub.sublime-settings

.idea/*
*.sqlite3

# Terraform
.terraform/
.terraform.lock.hcl
terraform.tfstate
terraform.tfstate.*
terraform.tfstate.backup
.terraform.tfstate.lock.info
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ migrate:
dockerRunserver:
poetry run python manage.py waitForDB
poetry run python manage.py migrate
poetry run python manage.py runserver 0:8000
poetry run gunicorn -b 0.0.0.0:8000 answerking.wsgi:application

prepare: lint test
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,32 @@ DATABASE_ENGINE="django.db.backends.mysql"
### Swagger:
To view the AnswerKing Python API documentation in Swagger as per OpenAPI specification, visit the following URL while
your local server is running: http://127.0.0.1:8000/api/schema/swagger-ui/

***
### Terraform:
The infrastructure created by running terraform in the `terraform/ecs_fargate` folder is illustrated below:

![Alt text](terraform/ecs_fargate/ecs_fargate.svg?raw=true "VPC Subnet Module Diagram")

To deploy the application to a server accessible by a public IP address:
- create a `variables_env.tf` file in the `ecs_fargate` folder containing the following variables:
```
variable "django_secret_key" {
type = string
description = "Django secret key."
default = "<DJANGO_SECRET_KEY>"
}

variable "aws_account_id" {
type = string
default = "<AWS_ACCOUNT_ID>"
}
```
- run the following in the command line while in the `ecs_fargate` directory:
- `terraform init`
- `terraform apply`
- push to a release branch to build and push Docker image to the created ECR.
- from the AWS console search for Elastic Container Service, select `ak-python-ecs-cluster`.
- go to the Tasks tab and then select the running container. Here you can open or copy the IP address.
- when finished run `terraform destroy` to tear down the infrastructure.
- Note: If there is an image in the ECR, `terraform destroy` will not delete it. This must be done manually in the AWS Management Console.
2 changes: 1 addition & 1 deletion answerking/settings/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Django base settings for answerking project.
Django base settings for answerking project..
"""
import os
from pathlib import Path
Expand Down
864 changes: 442 additions & 422 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ freezegun = "^1.2.2"
pytz = "^2022.7"
tzdata = "^2022.7"
drf-spectacular = "^0.25.1"
gunicorn = "^20.1.0"

[tool.poetry.group.dev.dependencies]
black = "^22.10.0"
Expand Down
59 changes: 59 additions & 0 deletions terraform/ecs_fargate/backend.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
terraform {
backend "s3" {
bucket = "answerking-python-terraform"
key = "answerking-python-terraform.tfstate"
region = "eu-west-2"
dynamodb_table = "ak-python-terraform-state"
}
}

/*

# The commented out code was used to create the s3 bucket and dynamo table.
# It has been removed from the state so will not be impacted by terraform destroy.

resource "aws_s3_bucket" "terraform_backend_bucket" {
bucket = "answerking-python-terraform"
tags = {
Name = "${var.project_name}-terraform-bucket"
Owner = var.owner
}
}

resource "aws_s3_bucket_acl" "terraform_backend_bucket_acl" {
bucket = aws_s3_bucket.terraform_backend_bucket.id
acl = "private"

}

resource "aws_s3_bucket_public_access_block" "terraform_backend_bucket_public_access_block" {
bucket = aws_s3_bucket.terraform_backend_bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

resource "aws_s3_bucket_versioning" "terraform_backend_bucket_versioning" {
bucket = aws_s3_bucket.terraform_backend_bucket.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_dynamodb_table" "terraform_backend_state" {
name = "${var.project_name}-terraform-state"
read_capacity = 20
write_capacity = 20
hash_key = "LockID"

attribute {
name = "LockID"
type = "S"
}

tags = {
Name = "${var.project_name}-dynamo-table"
Owner = var.owner
}
}
*/
79 changes: 79 additions & 0 deletions terraform/ecs_fargate/ecs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
resource "aws_ecs_cluster" "ecs_cluster" {
name = "${var.project_name}-ecs-cluster"

tags = {
Name = "${var.project_name}-ecs"
Owner = var.owner
}
}

resource "aws_ecs_service" "service" {
depends_on = [
module.rds_serverless_cluster_setup.rds_cluster_instance_endpoint,
aws_iam_role.ecs_task_execution_role
]
name = "${var.project_name}-ecs-service"
cluster = aws_ecs_cluster.ecs_cluster.id
task_definition = aws_ecs_task_definition.task_definition.arn
desired_count = 1
launch_type = var.service_launch_type
scheduling_strategy = var.scheduling_strategy

network_configuration {
security_groups = [aws_security_group.ecs_sg.id]
subnets = [module.vpc_subnet_setup.public_subnet_ids[0]]
assign_public_ip = true
}

tags = {
Name = "${var.project_name}-service"
Owner = var.owner
}
}

resource "aws_ecs_task_definition" "task_definition" {
family = "${var.project_name}-task"
network_mode = var.network_mode
requires_compatibilities = [var.service_launch_type]
cpu = var.ecs_task_cpu
memory = var.ecs_task_memory
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
container_definitions = jsonencode([
{
name = "${var.project_name}-container"
image = "${var.image_url}"
cpu = 256
essential = true
networkMode = var.network_mode
portMappings = [
{
containerPort = "${var.container_port}"
hostPort = "${var.host_port}"
}]
logConfiguration = {
logDriver = "awslogs",
options = {
"awslogs-group": "${var.project_name}-log-group",
"awslogs-region": var.aws_region,
"awslogs-stream-prefix": "${var.project_name}-log-stream"
}
}
environment = [
{"name": "DATABASE_NAME", "value": var.database_name},
{"name": "DATABASE_HOST", "value": module.rds_serverless_cluster_setup.rds_cluster_instance_endpoint},
{"name": "DATABASE_PORT", "value": var.database_port},
{"name": "DATABASE_USER", "value": module.rds_serverless_cluster_setup.rds_cluster_master_username},
{"name": "DATABASE_PASS", "value": module.rds_serverless_cluster_setup.rds_cluster_master_password},
{"name": "SECRET_KEY", "value": var.django_secret_key},
{"name": "DJANGO_SETTINGS_MODULE", "value": var.django_settings_module},
{"name": "DATABASE_ENGINE", "value": var.django_database_engine}

]
}
])

tags = {
Name = "${var.project_name}-task-definition"
Owner = var.owner
}
}
4 changes: 4 additions & 0 deletions terraform/ecs_fargate/ecs_fargate.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions terraform/ecs_fargate/iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
resource "aws_iam_role" "ecs_task_execution_role" {
name = "ecsTaskExecutionRole-python"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_iam_role_policy_attachment" "ecs-task-execution-role-policy-attachment" {
role = aws_iam_role.ecs_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
9 changes: 9 additions & 0 deletions terraform/ecs_fargate/logs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
resource "aws_cloudwatch_log_group" "log-group" {
name = "${var.project_name}-log-group"
retention_in_days = 1
}

resource "aws_cloudwatch_log_stream" "log-stream" {
name = "${var.project_name}-log-stream"
log_group_name = aws_cloudwatch_log_group.log-group.name
}
24 changes: 24 additions & 0 deletions terraform/ecs_fargate/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
provider "aws" {
region = "eu-west-2"
skip_credentials_validation = true
}

module "vpc_subnet_setup" {
source = "git::https://github.com/AnswerConsulting/AnswerKing-Infrastructure.git//Terraform_modules/vpc_subnets?ref=v1.0.0"
project_name = var.project_name
owner = var.owner
num_public_subnets = 1
num_private_subnets = 2
}

module "rds_serverless_cluster_setup" {
source = "git::https://github.com/AnswerConsulting/AnswerKing-Infrastructure.git//Terraform_modules/rds_serverless_cluster?ref=v1.0.0"
project_name = var.project_name
owner = var.owner
database_name = var.database_name
database_engine = var.database_engine
database_engine_version = var.database_engine_version
database_subnet_ids = module.vpc_subnet_setup.private_subnet_ids
database_availability_zone = module.vpc_subnet_setup.az_zones[0]
database_security_groups = [aws_security_group.rds_sg.id]
}
60 changes: 60 additions & 0 deletions terraform/ecs_fargate/sg.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
resource "aws_security_group" "ecs_sg" {
vpc_id = module.vpc_subnet_setup.vpc_id

ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

ingress {
from_port = 8000
to_port = 8000
protocol = "tcp"
cidr_blocks = [ "0.0.0.0/0" ]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
Name = "${var.project_name}-ecs-sg"
Owner = var.owner
}
}

resource "aws_security_group" "rds_sg" {
vpc_id = module.vpc_subnet_setup.vpc_id

ingress {
protocol = "tcp"
from_port = var.database_port
to_port = var.database_port
cidr_blocks = ["0.0.0.0/0"]
security_groups = [aws_security_group.ecs_sg.id]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
Name = "${var.project_name}-rds-sg"
Owner = var.owner
}
}
Loading