00:00:18.520
My name is Laura Frank. I'm a senior engineer at CenturyLink Labs, which is a small R&D offshoot of CenturyLink. We primarily focus on research and development around Docker and technologies that are kind of tangentially related to Docker, such as Fleet, CoreOS, and Kubernetes.
00:00:25.480
We explore how these technologies serve the development community and work with them, hoping to improve them.
00:00:31.599
You can find me online. I also share a name with a reporter from a Colorado newspaper, but I'm not her.
00:00:37.079
As I mentioned, I work extensively with Docker, and today I'll talk about Docker and Ruby together.
00:00:44.160
Throughout this talk, I aim to answer three large and ambitious questions for you. The first is: What are Docker containers? The second is: How can you use Docker and Ruby together? Finally, how might you architect an application using Docker and Ruby to ensure it works effectively?
00:00:50.760
I assume that most of you have heard of Docker but might know very little about it, and that's perfectly fine.
00:00:56.160
I'll cover a lot of basic introductory content regarding Docker. If you're already a Docker user, hopefully, I'll shed some light on the technical underpinnings of Docker.
00:01:03.680
Has anyone here attended the Docker BoF last night? A few people? Great!
00:01:09.920
I didn't attend, as I was busy enjoying some fish, since I live in the Midwest.
00:01:15.960
So, what is Docker? We've probably heard about it because companies like Google, Spotify, Twitter, and countless others have been discussing how they're using Docker in production.
00:01:21.600
They talk about the fun and complex problems that Docker solves for them.
00:01:27.280
However, it's worth noting that the concept of containerization is not new. LXC, or Linux Containers, has been around since 2008.
00:01:34.240
But Docker gives this a nice, user-friendly front end, making it easier for everyone to use.
00:01:40.960
As the internet becomes more ubiquitous and more people use it, production workloads can fail at a much faster rate than they used to, especially before everyone had smartphones.
00:01:47.799
Docker fulfills a significant need in this context. In essence, Docker is a packaging tool.
00:01:55.560
It sits atop existing components of the Linux kernel, packaging applications into isolated execution environments.
00:02:01.759
Docker has a very limited scope; it does not schedule jobs for you. There are many things it does not do.
00:02:07.200
However, what it does is package your applications into containers.
00:02:13.040
A container is essentially a self-contained execution environment. I'll explain how this is set up shortly.
00:02:19.360
Think of it as something enclosed in a box that's isolated from everything else.
00:02:24.680
Containers share the kernel of the host system while maintaining isolation. This combination results in fast boot times and low overhead.
00:02:30.920
They operate very efficiently, reducing duplicate workloads, which often leads to significant performance benefits.
00:02:36.760
For instance, you will notice a substantial performance difference between a service running in a virtual machine versus one running in a container.
00:02:44.000
This is due to the major differences in operation between these two types of services.
00:02:50.959
Here's an example of a typical virtual machine setup, which many of you might have running right now.
00:02:56.400
In this example, we have two different services: App One and App Two, each with their instances.
00:03:01.519
In a virtual machine environment, each instance includes its libraries and a guest OS, all sitting on a hypervisor above the host OS and hardware.
00:03:09.000
However, in the Docker world, there's no need for a hypervisor. We can eliminate the guest OS in each virtual machine.
00:03:16.440
Instead, we introduce something called the Docker Engine.
00:03:21.879
This engine is the packaging tool on the host system that creates and deploys containers.
00:03:27.400
You'll notice that the libraries, instead of residing inside the container, are now beneath it.
00:03:33.239
This design allows each container to refer back to the same library, eliminating the need for duplicate copies.
00:03:39.040
A relatable analogy is the San Diego Zoo, where koalas are displayed in self-contained environments.
00:03:45.200
Each koala doesn't require its own caregiver; they can share the same food supply. It would be impractical to have a separate veterinarian for each koala.
00:03:51.360
Similarly, you don't want a separate library for each container.
00:03:56.680
Now, how does this work? Containers may seem abstract and possibly difficult to grasp, and it can be.
00:04:02.959
We've spent months working on a tool that sits on top of Docker, and we sometimes want to throw our laptops out the window.
00:04:09.120
However, there are four key components that enable Docker to function effectively and deliver those great performance benefits.
00:04:15.959
The first of these is libcontainer.
00:04:22.560
This is the container format.
00:04:29.000
Several months ago, Docker launched libcontainer, its own equivalent of LXC.
00:04:35.240
Previously, Docker relied on LXC, limiting it to Linux. With libcontainer, there is potential to run Docker anywhere.
00:04:41.800
You might see Docker thinking about running on Windows or natively on a Mac thanks to libcontainer.
00:04:48.680
For today’s examples, we'll focus on libcontainer since it simplifies the process.
00:04:54.080
The next crucial aspect is namespaces, which isolate the container from everything else.
00:05:00.800
Namespaces are about processes, not resources.
00:05:07.840
They include networking and process IDs, keeping processes in their own namespace to prevent conflicts with other containers.
00:05:14.680
On the other side, we have cgroups, or control groups, which enable containers to be good multi-tenant citizens, accessing shared resources when necessary.
00:05:21.560
Lastly, we have the union file system, which significantly increases Docker's speed.
00:05:27.400
The layered file system operates on a copy-on-write principle.
00:05:33.600
When building a Docker image, everything is done sequentially, stacking layers as you go.
00:05:39.040
Think of this process similar to your Git log; if you need to revert or rebuild an image, you can reference a previous state rather than starting from scratch.
00:05:45.000
These technical components make Docker a lightweight runtime and packaging tool that utilizes Linux kernel components.
00:05:50.640
However, Docker is more than just that; it represents a development workflow and an ecosystem.
00:05:55.680
This is why Docker is attractive to developers employed in small teams and at organizations that manage production loads, like Google.
00:06:00.960
Here's what Docker looks like at a high level.
00:06:06.400
On one side, we have the engine that handles the technical components, the code execution, and so forth.
00:06:12.800
On the other side is the Docker Hub, where people collaborate and share elements and resources related to Docker.
00:06:18.320
The cornerstone of this collaborative aspect is the Docker Registry.
00:06:24.120
You can find it at registry.hub.docker.com. This is where Docker images are stored.
00:06:30.080
If you create a Docker image that you'd like to share, you can push it to this public repository.
00:06:36.960
There's also the option to use a private registry for proprietary images.
00:06:43.120
About a third of the way down the page, you will see official repositories.
00:06:48.920
These repositories contain images from companies or communities that are using Docker. They often create an official image to help others avoid unnecessary repeated work.
00:06:55.120
For example, if you want to use MongoDB in your project, you don't have to build your own image; you can use the one created by MongoDB.
00:07:01.680
These official images usually come with extensive documentation to help bootstrap your own projects.
00:07:06.640
To utilize these resources, you must first install Docker.
00:07:13.360
One of my favorite observations about Docker is that it often faces criticism for being overly abstract.
00:07:20.000
For some, the many layers of this abstraction can be overwhelming, creating a gap between the programmer and the code running in a container.
00:07:26.000
I won't lie; overcoming this hurdle when starting with Docker can be challenging.
00:07:32.480
Fortunately, there are many tools available to help bridge this substantial gap.
00:07:39.120
For those on Linux, Docker was built primarily with Linux containers in mind. You can install Docker using official packages and get set up.
00:07:46.720
If you're using other operating systems, you will have to run a VM. I know this may sound contrary to what I've said about VMs being bloated and undesirable.
00:07:54.560
However, running a single VM that can support thousands of containers has minimal impact on performance.
00:08:00.800
Docker also provides a great tool called Boot2Docker, which you'll find in the installation instructions.
00:08:07.520
Boot2Docker is a lightweight tool that will set up a VM in the background, run Docker on it, and sync all your folders.
00:08:13.120
This allows you to interact with Docker from your Mac or Windows machine as if you were on a normal terminal.
00:08:19.360
In fact, I will be using Boot2Docker during this presentation, allowing it to appear as if I'm directly interacting with Docker.
00:08:26.000
When you eventually have Docker installed, whether through Boot2Docker or your own Vagrant file, you'll interact with Docker primarily via the CLI.
00:08:32.880
There is also a REST API for interfacing with Docker, both of which have beautifully written documentation available at docs.docker.com.
00:08:39.120
Now, let’s move to the exciting part: using Ruby with Docker.
00:08:44.640
Specifically, let’s discuss how to use the Docker API, CLI, and an image to construct a running Ruby container for your Ruby applications.
00:08:51.840
Before diving into building containers, we need to familiarize ourselves with what an image is.
00:08:57.839
Think of a Docker image as a class, while a container represents an instance of that class.
00:09:04.480
You cannot start a container without first having an image; otherwise, the container would lack any content.
00:09:10.760
Each Docker image is governed by a Dockerfile, which serves as a list of instructions.
00:09:17.080
We'll examine a Dockerfile shortly, and we will build an image together.
00:09:23.240
Images are built using the command ```docker build -t <name> .``` here, 't' signifies 'tag,' followed by the name you want to give it.
00:09:30.160
A common naming convention is your GitHub username followed by your image name, but you can name it as you wish.
00:09:36.640
The dot at the end signifies the current working directory where the Dockerfile is located.
00:09:44.600
Referring back to the Docker Registry I mentioned, whether you're using a public or private registry, you can pull an image using the format ```docker pull <username>/<imagename>```.
00:09:50.960
This also allows for optional version tagging using a colon.
00:09:57.680
For instance, when working with the Ruby image, here's a sample Dockerfile for a very simple application.
00:10:04.520
This sample is based on Ruby 2.1.2.
00:10:11.440
It executes specific tasks and ultimately culminates in a command to start the container.
00:10:19.160
Let's attempt to build this image now.
00:10:26.720
I'm currently in the directory with all necessary files to run this application, including the Dockerfile we previously reviewed.
00:10:33.760
I'll execute the command ```docker build -t hello-world .```.
00:10:40.000
You can see the layering file structure in action with various memory references being displayed as the process runs.
00:10:48.320
This may take a couple of moments, so while we wait for that...
00:10:54.560
Let’s hope that works!
00:10:59.720
Live coding or live demos can be a bit nerve-wracking for everyone, including the audience, so thank you for your patience.
00:11:05.840
Again, the Dockerfile is very simple. The `FROM` command is mandatory; every image must inherit from a base image.
00:11:13.360
Similar to the class-object analogy I mentioned before, inheritance can be utilized for Docker.
00:11:20.200
Any components the CenturyLink base image has will be included in my image because I reference it.
00:11:26.000
Notice the version identifier '2.1.2' in this instance, which corresponds to Ruby 2.1.2.
00:11:33.440
At CenturyLink, we invest significant effort in optimizing our Ruby images. You can find them on Docker Hub.
00:11:40.640
Next is the 'MAINTAINER' directive, which indicates me. If something breaks, you'll know whom to contact.
00:11:48.080
Following that is the 'EXPOSE' command, which announces that your service is running on a specific port.
00:11:54.080
This informs other containers on the same host about the exposed port.
00:12:00.080
Next up, we have the 'RUN' and 'ADD' commands. A 'RUN' command executes whatever command is provided.
00:12:07.760
In this case, we're creating a new directory named 'app'.
00:12:14.000
The 'ADD' command behaves similar to a copy command; it will copy everything in the current directory into the newly created 'app' directory.
00:12:21.120
After that, we set the 'app' directory as our working directory.
00:12:27.040
Going forward, all commands specified in the Dockerfile will execute in this working directory.
00:12:33.600
So, when we run 'bundle install', it will specifically execute for the Gemfile within that new directory.
00:12:41.200
Finally, there's the 'CMD' directive which specifies the actual operation to execute when the container runs.
00:12:47.440
In this case, we'll run a very simple Ruby script: 'hello_world.rb'.
00:12:53.120
Additionally, setting environment variables is essential.
00:12:59.760
Environment variables use the syntax of uppercase key and lowercase values. They are accessible during subsequent run commands.
00:13:06.480
We can also specify a mount point using the 'VOLUME' command. This allows for sharing code in containers by creating a mounted volume.
00:13:12.640
Now, let’s check on our image to see if it has downloaded successfully.
00:13:18.560
This 'FROM' command proves quite powerful. It's essential in determining the base image for every service.
00:13:25.040
In an ideal world, all services would use compatible Ruby versions.
00:13:30.720
However, you only truly need one base image, which you can reference from other Dockerfiles.
00:13:36.560
The advantage is that if Ruby gets updated, you can simply modify the base image that all others inherit from.
00:13:42.560
This shared infrastructure minimizes maintenance and update efforts across your Docker projects.
00:13:48.960
Okay, great! The build was successful, which is excellent news.
00:13:56.480
Let me clear the screen here so it's easier for those in the back to see.
00:14:02.040
You can view all the images on your host by using the command 'docker images'. I noticed a failed build earlier, which is why there's an odd entry.
00:14:10.000
Observe that my CenturyLink Ruby base image is regarded as a distinct image.
00:14:16.000
The hello-world image will reference everything that our base image provides.
00:14:22.240
If we delete the hello-world image now, I won't do this right now, but if we rebuild it, it eliminates redundant operations.
00:14:29.680
Docker will skip the steps needed to build the already created base image, resulting in efficiency.
00:14:37.040
In constructing a complex application with multiple services sharing a single base image, you save significant time and resources.
00:14:44.360
Now, let’s run the Docker image; it sounds like a good idea.
00:14:52.320
The command 'docker ps' allows you to view all running processes on your host.
00:14:58.680
Currently, there are none running. So let's execute a command to run our new container.
00:15:05.480
We'll include a port binding so that you can view this in your browser. This binding will map port 4567.
00:15:14.360
The final argument to pass is the name of the image, which is hello-world.
00:15:22.360
As you can see, we now have a containerized application running before your eyes!
00:15:30.360
It took mere milliseconds to start; imagine how long it would take in a traditional virtual machine.
00:15:37.760
Consider how often you restart a service while developing; with Docker, you can reduce that time from minutes to milliseconds.
00:15:44.600
Now, this container is running. I can access it through my browser, and I'll shut it down.
00:15:52.000
Let's check on the container status. It's exited, which is normal.
00:15:59.520
Docker provides exit codes for its containers. I can start it again by referencing its identifier or friendly name.
00:16:06.240
As expected, it generates a quirky name, usually a combination of an adjective and a scientist's name.
00:16:12.960
Now, that wraps up the Ruby application demonstration.
00:16:20.640
In this instance, we used the base image created by CenturyLink.
00:16:27.760
Rubу also has many official images available on Docker Hub, ranging from versions 1.9.3 and newer.
00:16:34.960
You can grab these easily with a simple 'docker pull' command. The official images come with excellent documentation.
00:16:41.840
Just as I mentioned earlier, the official Ruby images can be quite large.
00:16:49.280
My team is working diligently to optimize them, and we aspire for our images to become the official Ruby images.
00:16:56.640
You are welcome to choose either option based on your requirements.
00:17:02.960
In some cases, you might need specific Ruby gems for your project.
00:17:09.360
Swipely has done extensive work with Docker and provides several gems, one of which we use is the Docker API gem.
00:17:16.160
This gem enables interaction with the Docker API from within your application.
00:17:23.120
You can find a plethora of other gems on RubyGems.org related to Docker, with varying activity and user bases.
00:17:29.760
Debbugging in a container can indeed be a challenge. Docker faces criticism for its abstractions.
00:17:36.800
In our team, we often use Pry for debugging. We try to run things locally before deploying them in a container.
00:17:44.960
I recommend that if you haven't used Pry, start doing so as it provides a robust debugging session.
00:17:52.480
If you use Pry, you can directly use a command to enter a debug session while the application is running in the container.
00:17:59.440
You can use Docker run commands to drop yourself into a debugging session efficiently.
00:18:05.920
With that said, knowing how to address issues that arise can significantly improve your development workflow.
00:18:13.200
Now, let’s turn to architecture—a topic that often generates lively discussion.
00:18:18.880
Docker architecture is based on service-oriented architecture, which is beneficial.
00:18:25.200
Think about how quickly we got that service running—it took mere milliseconds!
00:18:31.920
When each service resides within its own container, if one fails, it can be spun up rapidly.
00:18:39.120
Scaling individual containers is also much easier than dealing with monolithic ones.
00:18:46.080
Take a look at this illustration: one service, one container, depicting a simple setup with a web framework and a database.
00:18:53.360
You might wonder how container A recognizes the existence of container B since they are isolated.
00:19:00.480
The answer lies in Docker's linking capabilities or through port mapping and environment configurations.
00:19:08.240
This sort of configuration is nothing but simple settings handled in two places: the Dockerfile and during the container's runtime.
00:19:15.440
Let's revisit that dummy application's Dockerfile and analyze best practices regarding configuration.
00:19:22.000
Essentially, it's acceptable to have everything coded in this format, but if I push it to Docker Hub, I don’t want to hardcode bindings.
00:19:29.120
It's crucial that I avoid embedding environment variables in the Dockerfile.
00:19:36.960
If someone unknowingly commits sensitive information, such as passwords, it could be exposed publicly.
00:19:43.440
Instead, set up environment variables during runtime using the Docker run command.
00:19:50.160
In this example, we again use 'docker run' followed by '-p' to declare the port mapping.
00:19:58.440
You can pass environment variables using '-e' and specify key-value pairs without hardcoding them into your Dockerfile.
00:20:05.440
The linking functionality mentioned earlier can also apply here—where one container can view and communicate with another.
00:20:11.600
As you can see, each container is executed through one command, while multiple containers can be instantiated from the same image.
00:20:19.360
You want to balance configurations between the Dockerfile and Docker run command to protect sensitive data.
00:20:27.200
Remember that you have to set configurations every time you run a container; as a lazy engineer, I look for shortcuts.
00:20:34.760
Thankfully, there is a process known as application templating that can simplify this.
00:20:42.080
My team utilizes such templates to streamline the configuration process of our Docker containers.
00:20:49.840
With this approach, after configuring once, you can define an application template containing your requirements.
00:20:56.080
Instead of executing each container separately, you just run the template that encapsulates your configurations.
00:21:03.440
A noteworthy option is Fig. Docker acquired Fig, and it is now part of the official Docker ecosystem.
00:21:10.760
Using Fig, you can organize your application specifications into a YAML file and initiate the configuration effortlessly.
00:21:17.760
If you're seeking a more visual approach, the tool my team created is called Panamax.
00:21:25.000
It’s a Docker workflow tool that may run multiple Ruby services in containers depending on how it's set up.
00:21:31.680
Panamax incorporates various Docker-related technologies, promoting service orchestration and discovery.
00:21:38.000
You may explore Panamax at panamax.io; it's maintained by the Docker community.
00:21:45.360
If you wish to download it, seek panamax.io/get-panamax. It’s an open source project built entirely in Ruby.
00:21:52.000
This is what an application template looks like on Panamax; it consists of a name, description, and support documentation.
00:22:00.640
The template lists images, like Rails, alongside their descriptions.
00:22:06.760
The configuration elements are persistent, so you need to configure them only once.
00:22:12.480
Let's navigate to the Panamax homepage. It’s directly connected to Docker registry.
00:22:19.840
Here, users can find various pre-existing templates, verified for functionality.
00:22:27.760
You can run templates with a simple click, avoiding the need for elaborate command line entries.
00:22:35.040
I’ll run a local instance of a Rails application to demonstrate.
00:22:41.680
This process involves downloading images, which will inevitably take some time.
00:22:48.560
The service should be successfully created soon.
00:22:55.040
Once it is up and running, the equivalent 'docker run' commands are displayed.
00:23:02.960
Using Panamax simplifies the user experience, while still maintaining transparency in configurations.
00:23:09.440
The application templating that Panamax offers is a transformative feature.
00:23:16.600
If you deal with multiple technologies or services in your projects, Panamax is particularly helpful.
00:23:23.680
It's a cakewalk to deploy everything at once instead of configuring each service independently.
00:23:30.080
All in all, we have discussed what Docker is and how to run Ruby applications using it.
00:23:37.040
Today, we explored how to architect applications and utilize templating to save time.
00:23:45.280
As you embark on your container journey, I recommend Boot2Docker; it's incredibly helpful.
00:23:53.200
With Boot2Docker, it won't feel like you're working on a separate VM, enabling a seamless development experience.
00:24:00.960
Extracting commonalities into a base image will yield significant efficiency gains.
00:24:07.600
Be mindful not to include passwords within your Dockerfiles; this keeps your sensitive information secure.
00:24:14.640
Lastly, employ templating as a rapid approach to kick-start your projects.
00:24:23.120
For additional resources and guidance, I highly recommend checking out the Docker Hub to explore existing images.
00:24:30.560
Utilizing available official images can save you considerable time.
00:24:37.920
Docker’s documentation is excellent, so utilize that, along with resources like Boot2Docker.
00:24:45.440
If you're interested in templating, I encourage you to explore tools such as Panamax or Fig.
00:24:53.760
My team regularly produces informative content, including longer tutorials, so check out our blog on CenturyLink's website.
00:25:02.240
Thank you for being an attentive audience today! If anyone has questions, I'd be glad to answer or help you.