Using Containers in Production
The course is part of this learning path
This course is for anyone with a basic understanding of what containers are, and even why you'd want to use them, but who doesn't understand the nitty-gritty of how they work yet. You should have a basic technical literacy, as well as an understanding of cloud app architecture. In this course, you'll learn about the major concepts around containers. You'll learn about different container systems, learn all about orchestration, get a better understanding of how and why to build and run 12-factor apps on containers, container security issues, and you'll even get a quick look at how to get a microservices app up-and-running on your computer in about two minutes with Docker compose.
- Understand the options for running containers
- Understand the common security concerns
- Know why orchestration is important
- Understand what types of applications should be containerized
- Understand how logging and monitoring works with containers
- Project Manager
- Business Manager
A good prerequisite for this course is to take the Introduction to Containers course.
This Course Includes
- 47 minutes of high-definition video
- Console demonstrations
What You'll Learn
- Course Intro: What to expect from this course
- Microservices: Designing Microservices and 12-factor apps.
- Running a Microserve App on Compose: Demo of using Compose.
- Container Orchestration: What is orchestration and how does it work?
- Container Security: Best practices to make containers more secure.
- Container Logging and Monitoring: An overview of tools commonly used for monitoring.
- Container Systems and OSs: Different Linux Containers: LXC, Docker, rkt, OCI, and an overview of Windows Containers.
- Wrap-Up: Course summary
Containers and microservices tend to come hand in hand. While having a large app wrapped in a container can be useful for some testing, to get the benefits out of using containers in production you'll need to architect your app with containers in mind. In this lesson, we'll go over a popular method for doing that called 12-Factor Apps and give you an example of a microservice app in the wild.
Here's a brief review from our Intro to Containers course on what a microservice app architecture would look like. If you're still unclear, you might want to go back to that course before taking this one. 12-Factor Apps are a standard way of architecting an app as microservices. The 12 factors are like a checklist for each microservice that makes it easier to ensure that your app is healthy and all of your services will work together.
The 12-Factor App manifesto was originally developed by Heroku, an app-hosting platform. But a lot of other infrastructure startups, including Docker, the main container software, explicitly work towards supporting 12-Factor Apps now. We'll go through each of the 12-Factor Apps, quickly stating what each one means in practice. This won't be a tutorial for building them, but you can find plenty more resources for that online.
Factor one: The codebase.
There should be one codebase, tracked in revision control, with many deploys. This means that you should be using version control software like Git, or ideally GitHub or another code-hosting platform. You should track all the changes that everyone makes and they should be easy to roll back. Any modern software development system relies on version control at this point. So this doesn't apply just to containerized or 12-Factor Apps.
In addition, you should be frequently testing the code you check-in to the version control system, preferably with automated tests.
Factor two: Dependencies. Explicitly declare and isolate dependencies. This means that if your code depends on something to run properly, whether in a library, a specific database, or another app, that should be explicit in the code and easy to setup.
Containers make this easy because with systems like Docker Compose you can define all the dependencies and get them up and running in other containers with a single command.
Factor three: Config. Store the config in the environment. The idea is that anyhting that may vary between environment should be stored in that environment, probably as config files.
This would be stuff like logins, IPs of machines your app will talk to, that kind of thing. Docker will let you store some of this in, for instance, the Docker file, a config file, or a compose file. But people argue whether or not that means it's stored in the code. For our purposes, that's good enough.
Factor four: Backing services. Treat backing services as attached resources. Here any other resources that your code needs to talk to, whether they be a database, another microservice, an email service, or whatever, should be accessible via URL and maybe a username and password. It shouldn't matter whether the service is hosted on the same physical machine, the same cloud service, or even a completely different service. Your app should treat them all the same.
Factor five: Build, release, run. Strictly separate build and run stages. The big idea here is that starting and stopping an app should be simple and require little human intervention. The way it's suggested to do that is to separate build, release, and run stages. To build the app is to trim the code into executable pieces, whether those be binary, script, containers, or whatever. And this should probably be handled by the dev team. To release the code is to put the executable pieces on your production servers. And this is handled by DevOps or SysAdmins. Once an app is released, the run stage is as simple as running a script to turn on or shut down the app and requires no extra building or releasing.
Factor six: Processes. Execute the app as one or more stateless processes. Statelessness is a very important part of container apps. Any given container should be stateless. When a user is interacting with it, that user's state should be stored somewhere else, so that if that container goes down, they can continue right where they left off on a different container. Each step the user goes through should export all the necessary data for any other container to know right where that user was.
Factor seven: Port binding. Export services via port binding. This is the flip side of factor four because your service will be treated like an attached resource by other services. This means you should expose your service as a URL, which may be public or private, and usually means creating an API for other services to interact with yours, just like you interact with them.
Factor eight: Concurrency. Scale out via the process model. This is a fancy way of saying that you should scale by adding more servers and services, not by adding more resources to a given server. Or with containers, you scale by spinning up more containers of a certain type. A lot of this is handled by your orchestration software, which we'll explain in the next lesson.
Factor nine: Disposability. Maximize robustness with a fast startup and graceful shutdown. This is very important when designing for containers, which can be spun up or spun down at any time. Your services should be designed so that shutdowns and crashes don't affect the entire system, and starting up a service should be as fast as possible.
Factor 10: Development production parity. Keep development, staging, and production as similar as possible. This is actually a place where containers make it easier to follow the factor, rather than the other way around. One of the main benefits of containers is that they are the same across different machines. So if you're running code in containers on both dev and production, you shouldn't see many errors when moving code between the two.
Factor 11: Logs. Treat logs as event streams. This is frankly the least-relevant factor. Yes, it's important to capture logs and errors for long-term recording and there are tools that let you do that with containers. You'll more or less get this for free if you use any modern container-monitoring software, which we'll cover a few lessons from now.
Finally, factor 12: Admin processes. Run admin and management tasks as one-off processes. This is an important element of your development and management processes. If you want to, for instance, add data to the database, you should do that from the console on your app in production, not by running code directly on the database. This means designing your system so that developers can gain admin access to all of your services and can run custom jobs on them. This will ensure that you don't break the system by accidentally reintroducing poorly-formatted data at any point.
So those are the 12 factors and what they mean in plain language. Each of your services should be designed to match each of these factors as best as possible. And then they'll work together well. Now, let's suppose you have a 12-Factor microservices app. Can you just dump all of your services in containers by writing Docker files and be ready to go? Well, not quite.
First, you need to make sure that not everything is containerized. In particular, if you have any necessary stateful elements of your app, those probably shouldn't be put in a container. The best practice would be to eventually refactor them to be stateless, but until you do, keep them on a machine somewhere so they don't go up and down too much. In addition, data should never be stored in a container. Database software can be containerized, but the database itself needs to be stored on a mounted volume.
Finally, while containers are very secure, there are some applications which require a level of security that even containers cannot guarantee. As an example, when working with medical data.
About the Author
Adrian M Ryan is an educator and product manager. He was an early employee at General Assembly, has co-founded an education startup and a consultancy, and he loves teaching. He grew up in rural Alaska, and while he now lives in New York City he makes sure to find time to get out in the woods hiking whenever possible.