Jina.ai logo
Jina and Terraform-image

Jina and Terraform

Susana Guzmán
Susana Guzmán

Jina ❤️ Terraform

Hi everyone! Recently I've been working with Terraform and tinkering with it to see how we can integrate it with Jina, so I thought to share with you all that I got.

First thing was to see what Terraform is and why everyone is so hyped about it. According to their website:

So that's a lot of fancy words to say it's a way for you to define your infrastructure via code. No need to go to AWS console and define every part of the infrastructure there. This comes in very handy since it's easier to debug when you have everything in one script so you can see all the details in one single place.

Cool! Nice, sold, what now?

Let's tweak our South Park example and instead of running our Docker image locally and then checking the results with Curl, let's set everything on an AWS instance and use that URL. And of course, because otherwise this post wouldn't make sense and I could be napping instead, let's set up the infrastructure with Terraform. 

What will you need?

  1. Terraform
  2. An AWS account
  3. The South Park Docker image

The first thing is to install Terraform, and the installation will depend on your OS. I'm using macOS and I installed it via Homebrew:

brew install terraform

Once you have it installed, you need to configure your AWS, so on your console the first command you'll run is:

aws configure

This will ask for the credentials of your AWS account. Once you set this, you won't need to specify keys through code, which is good because that's a bad practice and you will end up with your account in quarantine…this might have or not have happened to me. 

Ok! we have all the setup ready and we can start to write our Terraform file. 

What is happening? What is going to happen?

  1. Create an image repository in AWS and push our South Park docker image
  2. Create an ECS Cluster & Task
  3. Create an ECS service & load balancer

Create an image repository in AWS and push our South Park docker image

We already have a Docker image with the South Park example so we don't need to create a new one.

So the first thing is to create our Terraform script. It can be called whatever you want but with a .tf extension

provider "aws" {
  version = "~> 2.0"
  region  = "us-east-2"

#Create repo
resource "aws_ecr_repository" "southpark" {
  name = "sp-repo" # Naming my repository
  tags = {
    Name = "southpark_repo"

The first part is to create a provider, with Terraform you can use AWS, Azure, Google Cloud Platform or many others, you can even write your own if you feel like life is too boring and you are an expert in Go. Whatever floats your boat. We'll use AWS for this example. The second part is to create our repository and we just need to set the name for it. The tags are always optional.

This is the simplest Terraform file you can have, if you run this, Terraform will create an AWS repository for you, so let's do that. 

You can run 

terraform plan

And this will print a plan with all the resources that will be created, modified, or deleted. In this case, we are only creating a resource so it will show that. This command is always nice to do for a sanity check, so you see what will happen before it happens, and if we agree with this we run the script with

terraform apply

After we run this, if we go to our AWS console, we'll see that an sp-repo has been created

Great, we have our repository ready. But at the moment it's empty, we need to push our Southpark docker image into it. To do that click on the repository name, and then in the upper right corner you'll see a button of "View Push commands"

We will use that information to push our Docker image

Follow the steps that are shown there except step 2, we don't need that since we already have our Docker image. After you've done that, you should have the Southpark image in your repository, yay. 

Create a Cluster & Task

The next step is to create a Cluster.

resource "aws_ecs_cluster" "southpark_cluster" {
  name = "southpark_cluster" # Naming the cluster

Ok, for the task we need a bit more details

#Create task
resource "aws_ecs_task_definition" "southpark_task" {
  family                   = "southpark_task" 
  container_definitions    = <<DEFINITION
      "name": "southpark_task",
      "image": "${aws_ecr_repository.southpark.repository_url}",
      "essential": true,
      "portMappings": [
          "containerPort": 45678,
          "hostPort": 45678
      "memory": 2014,
      "cpu": 1024
  requires_compatibilities = ["FARGATE"] # Stating that we are using ECS Fargate
  network_mode             = "awsvpc"    # Using awsvpc as our network mode as this is required for Fargate
  memory                   = 2048         # Specifying the memory our container requires
  cpu                      = 1024        # Specifying the CPU our container requires
  execution_role_arn       = "${aws_iam_role.ecsExecutionRole.arn}"

For this, we will set the image URL of our Docker image and set the ports to 45678, the same that the Southpark example is using. 

If you run terraform apply you should see a new cluster with a task attached to it

Create a service & load balancer

Ok, now we want to create a service that will use the task we just created as a blueprint. Here we will set the name of the service, a reference to our cluster, and to the task we just made. We will have only one container so we set desired_count to 1

#create service
resource "aws_ecs_service" "southpark_service" {
  name            = "southpark_service"                             # Naming our first service
  cluster         = "${aws_ecs_cluster.southpark_cluster.id}"             # Referencing our created Cluster
  task_definition = "${aws_ecs_task_definition.southpark_task.arn}" # Referencing the task our service will spin up
  launch_type     = "FARGATE"
  desired_count   = 1

  load_balancer {
    target_group_arn = "${aws_lb_target_group.target_group.arn}" # Referencing our target group
    container_name   = "${aws_ecs_task_definition.southpark_task.family}"
    container_port   = 45678 # Specifying the container port

  network_configuration {
    subnets          = data.aws_subnet_ids.default.ids
    assign_public_ip = true                                                # Providing our containers with public IPs
    security_groups  = ["${aws_security_group.service_security_group.id}"] # Setting the security group
  depends_on = [aws_lb_listener.lsr, aws_iam_role_policy_attachment.ecsTaskExecutionRole_policy]

We need a load balancer as the access point to our container. The load balancer needs a security group, so we need to create that too

# Creating a security group for the load balancer:
# This is the one that will receive traffic from internet
resource "aws_security_group" "load_balancer_security_group" {
  description = "control access to the ALB"
  ingress {
    from_port   = 45678
    to_port     = 45678
    protocol    = "tcp"
    cidr_blocks = [""] # Allowing traffic in from all sources

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

#ECS will receive traffic from the ALB
resource "aws_security_group" "service_security_group" {
  description = "Allow acces only from the ALB"
  ingress {
    from_port = 0
    to_port   = 0
    protocol  = "-1"
    # Only allowing traffic in from the load balancer security group
    security_groups = ["${aws_security_group.load_balancer_security_group.id}"]

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

The idea is that all traffic will be received by our load_balancer_security_group (ALB), and the service_security_group will receive only from the ALB.

Just for debugging, we have also an output to show us the URL that will use to access our Southpark example

output "alb_url" {
  value = "http://${aws_alb.application_load_balancer.dns_name}"

If you run terraform apply you will have now a cluster with 1 task and 1 service running on it.

Also in your terminal, you'll see the output with the URL, we can use that one to check our results with CURL as in the Southpark example.

That's it! But just before you go and nap, do a terraform destroy if you won't need it anymore so you release all the resources and don't get charged extra in AWS if you're using the free version.

© 2021 Jina AI GmbH. All rights reserved.Terms of Service|Privacy Policy