2023-07-07: Deploying Serverless Containers via AWS

Introduction

Cloud computing has been increasing in popularity for some time now and continuously evolving. One recent trend in cloud computing is the use of serverless technologies. As a matter of fact, serverless made the Forbe's top 5 cloud computing trends in 2022. When many think of serverless technologies, they think of functions like AWS Lambda or Google Cloud Functions. However, serverless containers like Google Cloud Run and Azure Container Apps have become available over the past few years and are growing in usage. They allow developers to containerize applications with the desired flexibility and scalability while avoiding a lot of the hassles of infrastructure provisioning, patching, and complicated configurations that might be encountered with technologies like Kubernetes. In this blog, I will describe how to deploy a simple Python application using two serverless container options provided by Amazon Web Services (AWS): AWS Elastic Beanstalk (EBS) and AWS Elastic Container Service (ECS) using the Fargate option.

Before Getting Started

For those who may want to try these examples, a free AWS account can be created by going to the AWS Management Console. There are many free tier services available through AWS and other public cloud service providers. If you do decide to experiment, just remember to pay close attention to any fine print and take advantage of cost calculators that are made available by the cloud service providers. Also, be sure to delete resources created after they are no longer needed so that you do not inadvertently incur fees for cloud services.

Once logged into your AWS account, the Services menu is found in the top left corner of the page as shown in Figure 1. Specific services can be selected from this menu. However, services can also always be found by typing the name or acronym for the service in the Search field, which is also found in the top left corner of the page. 


Fig 1. AWS Management Console Home Page

I will be creating all of my services in the us-east-1 AWS region, as indicated by N. Virginia in the top right corner of the page, also seen in Figure 1. The AWS regions represent geographically separated physical cloud infrastructure locations. Consumers of cloud services usually want to select a region closest to its user base to reduce latency. Service costs can vary by region, which is something to also consider. However, for the examples described in this blog post, it should not matter much.

For the purposes of this blog, I have created a simple Python Flask web application to support walking through these steps. Its dependencies are already captured in the included requirements file. The project also contains a simple Dockerfile, which will be needed for creating a service using ECS. Feel free to also use my project or create your own.

A. AWS Elastic Beanstalk

AWS Elastic Beanstalk is an AWS managed service for deploying web server applications and/or worker applications. Deploying an application to AWS Elastic Beanstalk is surely the simplest of the two options I will describe in this blog but you can be the official judge on that. 

A.1. Gathering the Project Files

Before starting, make sure a requirements.txt file is included within the project folder of the Python application that will be used. If it is missing, one can be easily created using the pip freeze > requirements.txt command. From there, go to your local file explorer, select the contents within the project folder, then add them to a zip file. A Dockerfile does not need to be included in the zip file since a Docker container will not be created for the Elastic Beanstalk deployment. However, if it is present, it should not cause any errors. Once created, remember where the zip file is saved so that it can be retrieved a little later.

A.2.  Create an IAM Role for Elastic Beanstalk

A service role that Elastic Beanstalk assumes when calling other services should be created in advance. To do this, go to the Identity and Access Management (IAM) service by typing IAM in the Search field at the top of the page and click IAM once the name appears in the search results. Click the Roles link from the left menu then click the Create Role button on the right side of the page once it is visible. In Select trusted entity section, leave the AWS Service radio button selected. In the Use Case section, select the EC2 radio button then click the Next button. Type AWSElasticBeanstalkWebTier in the Filter policies search field then hit the Enter key on the keyboard to filter the list of policies. Check the checkbox adjacent to the AWSElasticBeanstalkWebTier AWS managed policy once it appears, as depicted in Figure 2. Click the Next button.  

Fig 2. Adding Permissions to an IAM Role

On the next page, enter a name for the role in the Role name text field. I entered asw-ebs-ec2-role as the name but just remember whatever it is called for later. Click the Create role button to finish creating the role.

A.3. Create an Elastic Beanstalk Environment

Go to the Elastic Beanstalk service by typing its name in the Search text field at the top of the page then clicking the name once it appears in the search results. Click the Create Application button, which should open the configuration wizard. In the first step of the wizard, select the Web server environment radio button then specify a name for the application in the Application name text field. I used my-blog-app as the name but feel free to use a different name. The environment name field should automatically populate based on the application name entered. A domain name for the web application can be entered but if not, Elastic Beanstalk will autogenerate one later. In the Platform section, select Python from the Platform selection menu. The other parameters can remain unchanged for now to include the Sample application selection under the Application code section and Single instance (free tier eligible) in the Preset section. Click the Next button.


Fig 3. Elastic Beanstalk Application Configuration Wizard

Select the Use an existing service role radio button. In the Existing Service roles and EC2 instance profile selection menu, select the role that was previously created via the IAM service. The role I created was named asw-ebs-ec2-role. Click the Next button. Select the default virtual VPC from the VPC selection menu in the Virtual Private Cloud (VPC) section. In the Instance settings section, check the Activated checkbox in the Public IP address sectionSelect at least one availability zone in the Instance subnets section by checking the appropriate checkboxes. I selected us-east-1a and us-east-1b but the options available will depend on the region you are working in. Click the Next button at the bottom of the page. Scroll down the page to the EC2 Security groups section then ensure the default checkbox is checked. Notice that autoscaling parameter options are found here as well but I will not be describing steps for enabling autoscaling. Click the Skip to review button at the bottom of the page. Finally, click the Submit button and wait as Elastic Beanstalk begins launching the environment.


Fig 4. Elastic Beanstalk Environment Information Screen

Once completed, a green message banner should appear at the top of the screen to indicate that the environment has been successfully launched as seen in the top of Figure 4. Under Domain in the Environment overview section, click the URL to view the AWS sample web application that was deployed.

A.4. Deploy Your Python Application

Now that the environment is set up, click the orange Upload and deploy button on the right side of the page. Once the Upload and deploy dialog window appears, click the Choose file button then find and select the zip file that was previously created and saved on your local file system. Click the Deploy button to close the dialog and start the application deployment. Once the deployment has completed, click the URL under Domain again to see that your own application is now deployed.  


Fig 5. Elastic Beanstalk Application Upload and Deploy Dialog Screen

A.5. Clean Up When Done

Remember to delete the resources created in this example to prevent any unnecessary charges. This includes the Elastic Beanstalk application and environment. An S3 bucket created by Elastic Beanstalk that is used to store application data may also need to be deleted. Search for the S3 service and find the bucket with elastic beanstalk in the name.  The bucket must be emptied and the policy attached to the bucket (that prevents deletion) will need to be removed before deleting it.

B. AWS Elastic Container Services using Fargate

Deploying an application to the AWS Elastic Container Service (ECS) is a little more involved but may be warranted depending on the application needs. Creating a Docker container is required as a part of this. Therefore, Docker should already installed in the local environment before walking through this example. There will be some variations in how Docker is setup, which is dependent upon the operating environment and some user preferences, which I will not further elaborate on in this blog post. The AWS command line interface (CLI) should also be installed locally.

B.1. Create a User for Container Management

A new user will be created to support later interactions with a cloud based container image repository. Go to the Identity and Access Management (IAM) service by typing IAM in the Search field at the top of the page then clicking the name once it appears in the search results. Before creating the user, a new policy will be created that will permit the creation of access keys for a user. Click the Policies link from the left menu then click the Create policy button once it appears on the right side of the page. Click the JSON button then in the Policy editor, copy the policy as shown in Figure 6 into the editor.

{
   "Version": "2012-10-17",
   "Statement": [
        {
            "Sid": "CreateOwnAccessKeys",
            "Effect": "Allow",
            "Action": [
                "iam:CreateAccessKey",
                "iam:GetUser",
                "iam:ListAccessKeys"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"     
        }
    ]
 }

Fig 6. Custom Policy for User Created Access Keys

Click the Next button. Enter a name for the policy in the Policy name text field then click the Create policy button at the bottom of the page. The policy should be created now.

Click the Users link from the left menu then click the Add users button once it appears on the right side of the page. Type a name in the User name field then click the Next button. Select the Attach policies directly radio button. In the Filter policies search field within the Permissions policies section, enter the name of the custom policy that was just created. When the policy appear in the results below the search field, check the checkbox adjacent to the policy name. Enter AmazonEC2ContainerRegistryFullAccess in the Filter policies search field then check its checkbox once it appears as well. Click the Next button.  Both policies should be seen in the Permissions summary section of the review screen as show in Figure 7.



Figure 7.  Create User - Review and Create page

Click the Create user button. Once the screen returns to the User list, click the name of the user that was just created. Click the Security credentials tab found below the Summary section. Scroll down to the Access keys section then click the Create access key button. For the Use case, select the Other radio button then click the Next button. The access key and secret access key should be shown on the page and can be copied and pasted from the page to another file for later usage. Alternately, download the .csv file containing them both to the local file system. Either way, they will be needed for the next step.

B.2. Set AWS Credentials in the Local Environment 

The user that was just created will be used to configure the local environment for access to AWS. On the local machine, open a terminal window. If you use an integrated development environment (IDE) like Visual Studio (VS) code, you can choose to open a terminal window there. In the terminal window, navigate to the directory of the Python project if not already there. Type the aws configure command as depicted in Figure 8 then hit the Enter key on the keyboard.  


Figure 8. Configuring AWS in a Terminal Window 

Copy and paste the access key and secret access key that were just created when prompted.  Enter name of the region you are working in when prompted (I used us-east-1). Just hit the Enter key on the keyboard when prompted for the Default output formatLeave this terminal window open.

B.3. Create a User for Task Execution

Return to the IAM service. Click the Roles link in the left menu then click the Create Role button on right side of the page once it appears. In Select trusted entity section, leave the AWS Service radio button selected. In the Use Case section, select the EC2 radio button and click the Next button. Type AmazonECSTaskExecutionRolePolicy in the Filter policies search field then hit the Enter key on the keyboard to filter the list of permissions.  Check the checkbox next to AmazonECSTaskExecutionRolePolicy once it appears then click the Next button. Enter a name for the role in the Role name text field then click the Create role button to finish creating the role.

B.4. Create an Elastic Container Registry Repository

An Amazon Elastic Container Registry (ECR) repository needs to be created to which a Docker container image will be uploaded later. In the Search field of the AWS Management Console, enter ECS or Elastic Container Registry and click the name once it appears in the search results. Click the Get Started button under Create a repository. Leave the Private radio button selected and enter a repository name. I named my repository my-blog-app as shown in Figure 9. Click the Create repository button.  


Fig 9. Elastic Container Registry Private Repository Listing

The newly created repository should be listed under Private repositories. Click the repository name to view the repository details screen. There will be no images listed but keep the window open for the next step.

B.5. Create and Deploy the Docker Image

A Docker container image will be created from the Python application then pushed to the repository that was just created.  That image is uploaded so that it can be used to launch new containers by a service. AWS CLI and Docker should already be properly installed in the local environment as previously mentioned. A properly configured Dockerfile should also be present in the Python application's project folder.

In the repository details screen that should still be open in the browser from the last step, click the View push commands button. Four ordered push commands for the repository should appear in a pop-up window. These commands make it easy to push the docker image to the repository. Copy the first command from the browser and paste it in the terminal window that remained open from step B.2. Hit the Enter key on the keyboard. This first command authenticates the local machine with the private repository. Copy the remaining push commands from the browser and paste them in the terminal window in order, ensuring that each command completes execution before going on to the next. The remaining commands will build the docker image, tag the build, then push the image to the elastic container registry repository.
 

Fig 10. Elastic Container Registry Push Commands

Once all commands have been successfully executed, click the Close button in the pop up window in the browser.  The terminal window can also be closed at this point. An image with latest in the Image tag column should now be found in the tabular list of images for the repository. Click the Copy URI link in the Image URI column of the table.

B.6. Create an Elastic Container Service (ECS)

Now that the image has been uploaded to ECR, a service can be created. First, create a cluster for the service, which allows services to be grouped. However, the creation of only one service is described in this blog. Type ECS or Elastic Container Service in the Search field at the top of the page then click the name of the service once it appear in the search results. From the left menu, click the Clusters link then click the Create cluster button once it appears on the right side of the page. Provide a name for the cluster in the Cluster name text field. Since the other default settings should be appropriate for this example, click the Create button at the bottom of the page.

Next, a task definition is created to set parameters associated with the containers that will be launched by the service. Click the Task definitions link in the left menu then click the Create new task definition button once it appears. Enter a name for the task in the Task definition family name text field. In the Container - 1 section, enter a name for the container task and paste the container image URI that was copied from ECR into the Image URI text field. In the Container port field, enter the port number that was exposed in the container. If using the code that I provided, use 5000 as seen in the Dockerfile. Also, enter a name for the port mapping in the Port name text field. Click the Next button.

In the Environment section, default values for most options should be appropriate for this example. Select the role that was previously created in step B.3 from the Task role and Task execution role drop-down menus. The default values in the optional Storage and Monitoring and logging sections do not need to be modified in this case. Click the Next button then click the Create button to complete the creation of the task definition.


Fig. 11 - Elastic Container Service - Launching Tasks for a Service

Now, go back to the cluster by selecting Clusters again from the left menu then clicking the cluster name once it is visible. Scroll down the page, ensure the Services tab is selected, then click the Create button. Under Environment, click the Launch type radio buttonScroll down to the Deployment configuration section. Ensure that the Service radio button is selected then select the newly created task definition from the Family selection menu. Enter a name for the service in the Service name text field. In the Desired tasks text field, enter the number 2. This means the target number of containers to be running at any time by the service is two. While there are also options here for networking, load balancing, and auto-scaling that could be modified, those will be ignored for now. Click the Create button. The server deployment will begin. Click the Tasks tab. There should be two tasks listed that are being launched or have launched for the service as specified by the Desired tasks field, similar to Figure 11. The launch has successfully completed once the Last status column for the two tasks displays Running. The status of the Service can also be viewed by returning to the Service tab.

B.7. Clean Up When Done

Once the resources are no longer needed, be sure to delete the cluster, service, and task definition in ECS. Delete the images and repositories from ECR. Logs collected by CloudWatch can be deleted as well as unneeded users and roles found in the IAM service.

Continuing Cloud Development

Cloud service providers are constantly making changes to their services and interfaces so there is no guarantee that some adjustments to the guidance provided here will not be needed in the future to get your serverless containers up and running. You should also consider some of the services and service options that I did not discuss in this blog such as application load balancers, autoscaling, and cloud storage solutions like Amazon S3 and DynamoDBGoogle Cloud, Microsoft Azure, and other public cloud service providers also offer free tier services that could support your exploration of their serverless options as well.

Cloud technologies are continuing to change rapidly so you should also keep your head in the cloud to learn about new capabilities and services that will help improve the performance of your cloud environment and/or get capabilities to your users faster. Low code or no code services as well as machine learning capabilities are definitely ones to keep an eye on.

--Bathsheba (@sheissheba)






Comments