Welcome back to “From Linux to Lambda,” a series of blog posts where I take a look at current day cloud services and technologies and where they came from. As a DevOps engineer who started coding after AWS was launched and Cloud already dominated the industry, it’s been interesting connecting the dots for myself on where services came from and why things are the way they are today.
Last time, I looked at the origins of virtualizations, exploring different server offerings across Clouds, how virtualization got started and the original virtualization tools run on-premises. Virtualization is a method of abstracting the concept of a server from the physical device, giving users the experience of running their own isolated servers on shared hardware. It is one solution to creating an isolated compute environment problem abstracted from its hardware, but today we’ll look at a lightweight alternative in the form of containers.
Virtual machines require hypervisors, which are special software that simulates the requirements of the OS they’re running. Containers take a different approach, sharing the host’s operating system kernel and creating an isolated environment within it. This limits what types of OS can be run on a machine, but saves resources that would be used to mimic OS-specific firmware. Due to their lightweight nature, container services have taken off in the recent years. Today, we’ll be looking at the history of them, popular container implementations like Docker, and the wide variety of container offerings that AWS, Azure and GCP provide.
Containers: Our Second Compute Abstraction
Let’s look back at our bare metal server, the basics of compute. If you wanted to split its resources into virtual machines, you’d install a hypervisor either as firmware or software and add servers as necessary of any OS type the hypervisor could support. For containers, we take another path, sticking with the operating system you have and setting up isolated compute environments within the same kernel (the main process of an operating system). Containers are a form of OS-level virtualization, where the OS allows for multiple separate user-space instances (most popularly referred to as containers) which can run smoothly as long as the operating system provides resources to support this, such as namespaces and cgroups in Linux. While they can be visualized as “containers”, a contained environment inside of a higher level environment, they all run side by side as processes in the same kernel (though “outer” processes would have visibility into processes “within” them).
Containers Before Cloud
Like Virtual Machines, containers had a long history before Cloud. Today, the word “container” seems almost synonymous with the most popular implementation at the time of this writing (and my whole time in this industry so far), Docker. Nevertheless, as with most technologies, they’re history goes back quite a bit further. In 1979, the Chroot command was added to Unix Version 7, which allowed for changing the root directory that appeared for a process, preventing a user from accessing anything outside of it. This would be a core component that would lead to the containers we know today.
This would lead to a container implementation called jails in BSD operating systems, by combining the chroot feature of isolating filesystem with other isolation techniques for processes, network resources and usernames, coming out for FreeBSD 4.0 in the year 2000. Oracle would release their Solaris Zones for Solaris operating systems in 2004. Over the next few years, development would be done to add namespaces to Linux for process isolation and cgroups for resource limitations, with cgroups released in 2007 and an early container implementation called Linux Containers or LXC in 2008, which would be used by early versions of Docker as well. Namespace improvements continued to be added to Linux and Docker would be released in 2013 which is currently the most popular implementation at the time of this writing. It’s packaging and container sharing capabilities vastly increased people’s desire to use containers. A powerful container orchestration tool called Kubernetes would release the following year along with lots of additional work from the cloud providers and tech community in developing improvements.
It’s always fascinating to see the old roots of a lot of our modern systems, however what’s notable about the dates here is that even Linux Containers weren’t released until 2008 while AWS came out with its initial offerings in 2006. Unlike virtual machines, containers didn’t really take off until their already was a cloud. Just a year after Docker’s release, AWS had third party integrations with Docker and announced their popular Elastic Container Service (ECS) offering.
A Quick Guide to Docker
Before getting onto the Cloud, I’d like to show a quick overview of Docker to show an example usage of a container service. In fact, while there’s a history of various container-like implementations and new container runtimes such as Podman, Docker is the most popular by far, and presently the only Container service built in and supported by Cloud Service providers. AWS’s Elastic Container Registry was announced as a “secure, fully-managed Docker container registry” before switching to support containers based on the Open Container Initiative (OCI) format and Kubernetes initially used Docker as its container runtime before switching to allow multiple runtimes via the Container Runtime Interface. While the changes will allow for more innovation in the container space, Docker’s integration with the container services of today make it the best fitting example.
If you wanted to run a container using Docker, you would install its runtime and command line tool. Depending on what you need in the container, you could either build your own Docker Image, or pull an existing one from a container registry such as Docker hub. For instance, there are official Docker base images for different Linux versions, official Python images that run anything passed to them as Python commands for the image version and a variety of official images for various tools and services like Hashicorp’s terraform images. You could use the “docker pull” command to pull the image you want from a remote repository, then run it with the “docker run” command, either passing an additional command to it or using “-it” mode to get an interactive shell.
If you wanted a custom image, you could define your own image specification using a Dockerfile. You’d add a list of RUN commands to it which would run various shell commands, an ENTRYPOINT specifying what to run when passing an additional command to the container, environment variables, files to copy the directory to run commands in, and more. A powerful tool of image building is layering, where you include a FROM option in the Dockerfile which tells it to start with the image specified and build on top of it. Often times, you could use this to specify an image for the OS you want, such as “FROM ubuntu:20.04” and then add commands to install desired tools as services for your app. Building and starting a container with services already installed can save a lot of startup time over having to install them after launch.
Once you have a Dockerfile written, you can build it with “docker build” and run it with “docker run.” If you have a remote repository, you may name it with “docker tag” and push it there with “docker push” for Cloud services or other users to pull. Other common commands are “docker ps” which shows running processes and “docker image ls” which shows all installed docker images.
Container Orchestration
With the ability to create contained apps and microservices came the need to orchestrate them and many (if not all) of the container-focused Cloud services are attempts to orchestrate them. Docker offers official orchestration features in the form of Docker Compose (released 2014) and Docker Swarm (2015). These are both official parts of docker with docker compose allowing users to define config files of multiple containers and network configurations and deploy a system of multiple containers that can communicate with each other as needed. Docker’s swarm mode allows users to deploy Docker containers across multiple physical servers or VMs, having a manager node talk to multiple worker nodes each running the Docker daemon. You can deploy a Docker stack to a Swarm using a Compose file as well.
By far, the most recognizable name in Container orchestration is Kubernetes, an open-source platform that which automates deployment, scaling and management of containerized applications written by Google and based off their internal server management tool called Borg. In Kubernetes, you write manifest files and apply them, detailing the application pods, as well as Kubernetes based roles for permissions, cluster load balancers and more. Kubernetes is a beast that could warrant its own articles or even books and tools are constantly being developed to simplify it, from services to generate cluster manifests to new UI’s to better understand the cluster. Speaking to the dominance of Kubernetes, AWS, GCP and Azure all have their own Kubernetes offerings.
Containers in the Cloud
Without further ado, let’s get into container services offered by each of the big 3 US Cloud Providers; AWS, Azure and GCP. Here, I’ll discuss all off their container-based services and the similarities and differences between them. I’ll also touch briefly on their “serverless functions” and kubernetes offerings as both are related, though I plan on saving in depth discussions on each for their own separate articles.
AWS
Amazon Web Services has a variety of Container Services. The most basic two are ECS – Elastic Container Service and ECR – Elastic Container Registry.
ECR acts as storage for Docker and OCI format container images allowing users to pull and push image via docker commands or OCI standards. Users can share images publicly or control access via their identity management service IAM.
ECS is AWS’s main container offering, allowing users to run containers using different hosting methods. ECS is a cluster control plane with the ability to schedule containers on a variety of data planes. Initially, this required you to set up a cluster of EC2 servers with an ECS agent installed to allow them to join the cluster. The primary component you work with in ECS is a task which consists of one or more container definitions. When you run a task, ECS will schedule the needed containers onto the resources in the data plane. Normally, when you run a task, the job finishes and the containers go away, useful for small jobs, but not if you want to run a website or longer task. For those, ECS has a service resource which can schedule a task definition to run on the data plane and let the service launch and manage tasks for you, helpful for scheduling or long running container jobs.
In 2017, ECS Fargate was announced, providing a serverless data plane where users no longer had to think about the underlying servers at all. Reducing the problem of having to scale both EC2 instance clusters and tasks, and vastly simplifying the process of running containers. This is probably the biggest update to ECS since launch, though many other changes have come to the service, from lots of integrations with other AWS services, improved service discovery, ability to run in a variety of locations such as wavelength zones, outposts and even on premises with the launch of ECS Anywhere and the help of Amazon’s SSM and ECS agents. At this point, there are a whole suite of AWS tools surrounding ECS from IAM integration, load balancing and monitoring to pipelines and deployments, there’s a ton of flexibility with what can be done with their containers and new announcements each year.
App Runner is a serverless offering by AWS starting in 2021 to abstract the process of running webservers or APIs through container offerings even further. It acts as a successor to Elastic Beanstalk (released by AWS in 2011) which creates infrastructure resources for a user after they upload their application code. App Runner takes application source code or a container image, along with environment variables and service port and scales it based on thresholds set by the user. It manages load balancing and can be set to autodeploy updates as the code in its source repository changes, and is touted as a serverless offering since you only manage the application code while AWS handles the rest.
EKS is Amazon’s Elastic Kubernetes Service where Amazon handles management of the Kubernetes control plane and let’s you run your worker nodes in EC2 and Fargate. Over Kubernetes itself, EKS provides integration with AWS’s many services, tying Amazon’s load balancer services to internal cluster load balancing, mapping IAM roles to roles within the cluster and VPC to allow for networking within the cluster. There are a lot of features built in for monitoring and managing EKS by taking advantages of AWS’s services.
Azure
ACR is Azure Container Registry, similar to ECR, let’s you store and interact with Docker and OCI standard containers, as well Helm Charts (unlike AWS). For the most part, the services are quite similar, with interactions with other services in their corresponding clouds.
ACI is Azure Container Instances is a fairly simple Containers-as-a-service offering, which lets you specify containers to run from default images or those from ACR or another image repository and run them without worrying about the underlying hardware. Each container instance gets its own IP and FQDM. It offers scaling, access to Azure’s networks and identity services, access to Azure’s persistent storage options and limited orchestration in the form of Container Groups which allow for managing and deploying a group of related containers which make up a larger application providing access to networking and storage resources as part of the group. Basically, this acts as a managed and scalable version of Docker Compose with native integration to other Azure Cloud Services.
AKS is Azure Kubernetes Service. Similar to EKS, Azure handles the control plane for you, and you pay for the worker nodes which are Azure VMs that containers will be scheduled on. It’s similar to the other Kubernetes services in that it offers autoscaling based on its configuration and integrates with Azures other IAM, networking and storage services.
ACA is Azure Container Apps which is a serverless offering built on top of Kubernetes helping users to build microservice architectures and event driven applications while removing the need to interact with Kubernetes itself. It provides more orchestration capabilities than ACI.
Google Cloud Platform
Cloud Run is Google’s serverless container service which let’s you deploy containers or have a container build automatically based on source code. Cloud Run services such as websites or APIs can be triggered in response to http requests or events and can be publicly or privately accessible. Cloud Run jobs can be used to run one time jobs such as database migrations. All services get an API endpoint and Cloud Run spins up a container when it’s hit and Cloud Run handles load balancing and integrates with other GCP services such as database or secret access and more.
Artifact Registry – At the time of writing this, Google’s Container Registry is deprecated and being replaced with Artifact Registry, which combines the Docker Image and OCI image/artifact support with package management support for packages such as NPM and Maven as well as support for Helm charts. It provides vulnerability scanning and CI/CD support and integration with other GCP services.
Google Kubernetes Service is the Kubernetes service of the cloud of the company that built Kubernetes and is mostly similar to other clouds offerings, managing the control plane and leaves the user to manage nodes in standard mode. It integrates well with GCP services, like the other Clouds and has an Autopilot mode which lets GKE manage the whole system and scale and configure nodes according to what’s needed for the workloads. This is similar to EKS’s Fargate though it abstracts away even more of the K8S resources compared to Fargate just hiding the underlying infrastructure.
Conclusion
I hope this provides a good overview of containerization and the movement to Cloud Native services, where it came from and its use in popular clouds today. There’s a lot more that could be dug into in many directions. Kubernetes is a beast of its own and there’s a whole ecosystem of tools popping up to work with it. At some point, I may write “Nick’s Guide to Kubernetes” as I’ve been working with it for years yet always find myself needing to remind myself of things as I dig into it. For our next step in the journey from Linux to Lambda, we’ll take a look at the “Lambda” part and dig into serverless, a concept touched on in some of the container offerings and how it can be taken even further to abstract away from anything even resembling an operating system to the user.




Leave a Reply