36,59 €
Explore the core functionality of containerizing your applications and making them production-ready
Key Features
Book Description
Containers enable you to package an application with all the components it needs, such as libraries and other dependencies, and ship it as one package. Docker containers have revolutionized the software supply chain in both small and large enterprises.
Starting with an introduction to Docker fundamentals and setting up an environment to work with it, you'll delve into concepts such as Docker containers, Docker images, and Docker Compose. As you progress, the book will help you explore deployment, orchestration, networking, and security. Finally, you'll get to grips with Docker functionalities on public clouds such as Amazon Web Services (AWS), Azure, and Google Cloud Platform (GCP), and learn about Docker Enterprise Edition features. Additionally, you'll also discover the benefits of increased security with the use of containers.
By the end of this Docker book, you'll be able to build, ship, and run a containerized, highly distributed application on Docker Swarm or Kubernetes, running on-premises or in the cloud.
What you will learn
Who this book is for
This book is for Linux professionals, system administrators, operations engineers, DevOps engineers, and developers or stakeholders who are interested in getting started with Docker from scratch. No prior experience with Docker containers is required. Users with a Linux system would be able to take full advantage of this book.
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 650
Veröffentlichungsjahr: 2020
Copyright © 2020 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.
Commissioning Editor: Vijin BorichaAcquisition Editor: Shrilekha InaniContent Development Editor: Ronn KurienSenior Editor: Richard Brookes-BlandTechnical Editor: Sarvesh JaywantCopy Editor: Safis EditingProject Coordinator: Neil DmelloProofreader: Safis EditingIndexer: Tejal Daruwale SoniProduction Designer: Deepika Naik
First published: April 2018 Second edition: March 2020
Production reference: 1130320
Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK.
ISBN 978-1-83882-747-2
www.packt.com
Packt.com
Subscribe to our online digital library for full access to over 7,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website.
Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals
Improve your learning with Skill Plans built especially for you
Get a free eBook or video every month
Fully searchable for easy access to vital information
Copy and paste, print, and bookmark content
Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.packt.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.
At www.packt.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks.
Gabriel N. Schenker has more than 25 years of experience as an independent consultant, architect, leader, trainer, mentor, and developer. Currently, Gabriel works as Lead Solution Architect at Techgroup Switzerland. Prior to that, Gabriel worked as Lead Curriculum Developer at Docker and at Confluent. Gabriel has a Ph.D. in Physics, and he is a Docker Captain, a Certified Docker Associate, a Certified Kafka Developer and Operator, and an ASP Insider. When not working, Gabriel enjoys time with his wonderful wife Veronicah and his children.
Francisco Javier Ramírez Urea is a technology enthusiast and professional, Docker Captain, casual developer, open source advocate, a certified trainer and solutions architect at HoplaSoftware, and a technical book writer and reviewer.
He is also a Kubernetes Certified Administrator, a Docker Certified Associate, a Docker Certified Instructor, and a Docker MTA program Consultant, as well as a Docker/Kubernetes and NGINX expert and a DevOps/CI-CD solutions integrator.
He currently works as a solutions architect focused on containers and microservices technologies. He is passionate to teach his students everything he know. Continuous learning is the main motivation of his career.
If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.
Title Page
Copyright and Credits
Learn Docker – Fundamentals of Docker 19.x Second Edition
About Packt
Why subscribe?
Contributors
About the author
About the reviewer
Packt is searching for authors like you
Preface
Who this book is for
What this book covers
To get the most out of this book
Download the example code files
Download the color images
Conventions used
Get in touch
Reviews
Section 1: Motivation and Getting Started
What Are Containers and Why Should I Use Them?
What are containers?
Why are containers important?
What's the benefit for me or for my company?
The Moby project
Docker products
Docker CE
Docker EE
Container architecture
Summary
Questions
Further reading
Setting Up a Working Environment
Technical requirements
The Linux command shell
PowerShell for Windows
Using a package manager
Installing Homebrew on macOS
Installing Chocolatey on Windows
Installing Git
Choosing a code editor
Installing VS Code on macOS
Installing VS Code on Windows
Installing VS Code on Linux
Installing VS Code extensions
Installing Docker for Desktop
Installing Docker for Desktop on macOS
Installing Docker for Desktop on Windows
Installing Docker CE on Linux
Installing Docker Toolbox
Installing Docker Toolbox on macOS
Installing Docker Toolbox on Windows
Setting up Docker Toolbox
Installing Minikube
Installing Minikube on macOS and Windows
Testing Minikube and kubectl
Summary
Questions
Further reading
Section 2: Containerization, from Beginner to Black Belt
Mastering Containers
Technical requirements
Running the first container
Starting, stopping, and removing containers
Running a random trivia question container
Listing containers
Stopping and starting containers
Removing containers
Inspecting containers
Exec into a running container
Attaching to a running container
Retrieving container logs
Logging drivers
Using a container-specific logging driver
Advanced topic – changing the default logging driver
Anatomy of containers
Architecture
Namespaces
Control groups (cgroups)
Union filesystem (Unionfs)
Container plumbing
runC
Containerd
Summary
Questions
Further reading
Creating and Managing Container Images
What are images?
The layered filesystem
The writable container layer
Copy-on-write
Graph drivers
Creating images
Interactive image creation
Using Dockerfiles
The FROM keyword
The RUN keyword
The COPY and ADD keywords
The WORKDIR keyword
The CMD and ENTRYPOINT keywords
A complex Dockerfile
Building an image
Multi-step builds
Dockerfile best practices
Saving and loading images
Lift and shift: Containerizing a legacy app
Analysis of external dependencies
Source code and build instructions
Configuration
Secrets
Authoring the Dockerfile
The base image
Assembling the sources
Building the application
Defining the start command
Why bother?
Sharing or shipping images
Tagging an image
Image namespaces
Official images
Pushing images to a registry
Summary
Questions
Further reading
Data Volumes and Configuration
Technical requirements
Creating and mounting data volumes
Modifying the container layer
Creating volumes
Mounting a volume
Removing volumes
Accessing volumes created with Docker for Desktop
Sharing data between containers
Using host volumes
Defining volumes in images
Configuring containers
Defining environment variables for containers
Using configuration files
Defining environment variables in container images
Environment variables at build time
Summary
Questions
Further reading
Debugging Code Running in Containers
Technical requirements
Evolving and testing code running in a container
Mounting evolving code into the running container
Auto restarting code upon changes
Auto-restarting for Node.js
Auto-restarting for Python
Auto-restarting for .NET
Line-by-line code debugging inside a container
Debugging a Node.js application
Debugging a .NET application
Instrumenting your code to produce meaningful logging information
Instrumenting a Python application
Instrumenting a .NET C# application
Using Jaeger to monitor and troubleshoot
Summary
Questions
Further reading
Using Docker to Supercharge Automation
Technical requirements
Executing simple admin tasks in a container
Using test containers
Integration tests for a Node.js application
The Testcontainers project
Using Docker to power a CI/CD pipeline
Summary
Questions
Further reading
Advanced Docker Usage Scenarios
Technical requirements
All of the tips and tricks of a Docker pro
Keeping your Docker environment clean
Running Docker in Docker
Formatting the output of common Docker commands
Filtering the output of common Docker commands
Optimizing your build process
Limiting resources consumed by a container
Read-only filesystem
Avoid running a containerized app as root 
Running your Terminal in a remote container and accessing it via HTTPS
Running your development environment inside a container
Running your code editor in a remote container and accessing it via HTTPS
Summary
Questions
Further reading
Section 3: Orchestration Fundamentals and Docker Swarm
Distributed Application Architecture
Understanding the distributed application architecture
Defining the terminology
Patterns and best practices
Loosely coupled components
Stateful versus stateless
Service discovery
Routing
Load balancing
Defensive programming
Retries
Logging
Error handling
Redundancy
Health checks
Circuit breaker pattern
Running in production
Logging
Tracing
Monitoring
Application updates
Rolling updates
Blue-green deployments
Canary releases
Irreversible data changes
Rollback
Summary
Questions
Further reading
Single-Host Networking
Technical requirements
Dissecting the container network model
Network firewalling
Working with the bridge network
The host and null network
The host network
The null network
Running in an existing network namespace
Managing container ports
HTTP-level routing using a reverse proxy
Containerizing the monolith
Extracting the first microservice
Using Traefik to reroute traffic
Summary
Questions
Further reading
Docker Compose
Technical requirements
Demystifying declarative versus imperative
Running a multi-service app
Building images with Docker Compose
Running an application with Docker Compose
Scaling a service
Building and pushing an application
Using Docker Compose overrides
Summary
Questions
Further reading
Orchestrators
What are orchestrators and why do we need them?
The tasks of an orchestrator
Reconciling the desired state
Replicated and global services
Service discovery
Routing
Load balancing
Scaling
Self-healing
Zero downtime deployments
Affinity and location awareness
Security
Secure communication and cryptographic node identity
Secure networks and network policies
Role-based access control (RBAC)
Secrets
Content trust
Reverse uptime
Introspection
Overview of popular orchestrators
Kubernetes
Docker Swarm
Apache Mesos and Marathon
Amazon ECS
Microsoft ACS 
Summary
Questions
Further reading
Introduction to Docker Swarm
The Docker Swarm architecture
Swarm nodes
Swarm managers
Swarm workers
Stacks, services, and tasks
Services
Task
Stack
Multi-host networking
Creating a Docker Swarm
Creating a local single node swarm
Creating a local Swarm in VirtualBox or Hyper-V
Using Play with Docker to generate a Swarm
Creating a Docker Swarm in the cloud
Deploying a first application
Creating a service
Inspecting the service and its tasks
Logs of a service
Reconciling the desired state
Deleting a service or a stack
Deploying a multi-service stack
The swarm routing mesh
Summary
Questions
Further reading
Zero-Downtime Deployments and Secrets
Technical requirements
Zero-downtime deployment
Popular deployment strategies
Rolling updates
Health checks
Rollback
Blue–green deployments
Canary releases
Storing configuration data in the swarm
Protecting sensitive data with Docker secrets
Creating secrets
Using a secret
Simulating secrets in a development environment
Secrets and legacy applications
Updating secrets
Summary
Questions
Further reading
Section 4: Docker, Kubernetes, and the Cloud
Introduction to Kubernetes
Technical requirements
Kubernetes architecture
Kubernetes master nodes
Cluster nodes
Introduction to Minikube
Kubernetes support in Docker for Desktop
Introduction to pods
Comparing Docker container and Kubernetes pod networking
Sharing the network namespace
Pod life cycle
Pod specifications
Pods and volumes
Kubernetes ReplicaSet
ReplicaSet specification
Self-healing
Kubernetes deployment
Kubernetes service
Context-based routing
Comparing SwarmKit with Kubernetes
Summary
Questions
Further reading
Deploying, Updating, and Securing an Application with Kubernetes
Technical requirements
Deploying a first application
Deploying the web component
Deploying the database
Streamlining the deployment
Defining liveness and readiness
Kubernetes liveness probe
Kubernetes readiness probe
Kubernetes startup probe
Zero downtime deployments
Rolling updates
Blue-green deployment
Kubernetes secrets
Manually defining secrets
Creating secrets with kubectl
Using secrets in a pod
Secret values in environment variables
Summary
Questions
Further reading
Monitoring and Troubleshooting an App Running in Production
Technical requirements
Monitoring an individual service
Instrumenting a Node.js-based service
Instrumenting a .NET Core-based service
Using Prometheus to monitor a distributed application
Architecture
Deploying Prometheus to Kubernetes
Deploying our application services to Kubernetes
Deploying Grafana to Kubernetes
Troubleshooting a service running in production
The netshoot container
Summary
Questions
Further reading
Running a Containerized App in the Cloud
Technical requirements
Deploying and using Docker EE on AWS
Provisioning the infrastructure
Installing Docker
Installing Docker UCP
Using remote admin for the UCP cluster
Deploying to Docker Swarm
Deploying to Kubernetes
Exploring Microsoft's Azure Kubernetes Service (AKS)
Preparing the Azure CLI
Creating a container registry on Azure
Pushing our images to ACR
Creating a Kubernetes cluster
Deploying our application to the Kubernetes cluster
Understanding GKE
Summary
Questions
Further reading
Assessments
Chapter 1
Chapter 2
Chapter 3
Chapter 4
Chapter 5
Chapter 6
Chapter 7 
Chapter 8
Chapter 9
Chapter 10
Chapter 11
Chapter 12
Chapter 13
Chapter 14
Chapter 15
Chapter 16
Chapter 17
Chapter 18
Other Books You May Enjoy
Leave a review - let other readers know what you think
Developers are faced with ever-increasing pressure to build, modify, test, and deploy highly distributed applications in a high cadence. Operations engineers are looking for a uniform deployment strategy that encompasses most or all of their ever-growing portfolio of applications, and stakeholders want to keep their total cost of ownership low. Docker containers combined with a container orchestrator such as Kubernetes help them all to achieve these goals.
Docker containers accelerate and simplify the building, shipping, and running of highly distributed applications. Containers turbo-charge CI/CD pipelines, and containerized applications allow a company to standardize on one common deployment platform, such as Kubernetes. Containerized applications are more secure and can be run on any platform that's able to run containers, on premises or in the cloud.
This book is targeted at system administrators, operations engineers, DevOps engineers, and developers or stakeholders who are interested in getting started with Docker from scratch.
Chapter 1, What Are Containers and Why Should I Use Them?, introduces the concept of containers and why they are so extremely useful in the software industry.
Chapter 2, Setting Up a Working Environment, discusses in detail how to set up an ideal environment for developers, DevOps, and operators that can be used when working with Docker containers.
Chapter 3, Mastering Containers, explains how to start, stop, and remove containers. We will also see how to inspect containers to retrieve additional metadata from them. Furthermore, we'll see how to run additional processes, how to attach to the main process in an already running container, and how to retrieve logging information from a container that is produced by the processes running inside it. Finally, the chapter introduces the inner workings of a container, including such things as Linux namespaces and groups.
Chapter 4, Creating and Managing Container Images, presents the different ways to create the container images that serve as the templates for containers. It introduces the inner structure of an image and how it is built. This chapter also explains how to lift and shift an existing legacy application so that it can run in containers.
Chapter 5, Data Volumes and Configuration, introduces data volumes, which can be used by stateful components running in containers. The chapter also shows how we can define individual environment variables for the application running inside the container, as well as how to use files containing whole sets of configuration settings.
Chapter 6, Debugging Code Running in Containers, discusses techniques commonly used to allow a developer to evolve, modify, debug, and test their code while running in a container. With these techniques at hand, the developer will enjoy a frictionless development process for applications running in a container, similar to what they experience when developing applications that run natively.
Chapter 7, Using Docker to Supercharge Automation, shows how we can use tools to perform administrative tasks without having to install those tools on the host computer. We will also see how to use containers that host and run test scripts or code used to test and validate application services running in containers. Finally, this chapter guides us through the task of building a simple Docker-based CI/CD pipeline.
Chapter 8, Advanced Docker Usage Scenarios, presents advanced tips, tricks, and concepts that are useful when containerizing complex distributed applications, or when using Docker to automate sophisticated tasks.
Chapter 9, Distributed Application Architecture, introduces the concept of a distributed application architecture and discusses the various patterns and best practices that are required to run a distributed application successfully. Finally, it discusses the additional requirements that need to be fulfilled to run such an application in production.
Chapter 10, Single-Host Networking, presents the Docker container networking model and its single-host implementation in the form of the bridge network. This chapter introduces the concept of software-defined networks and explains how they are used to secure containerized applications. It also discusses how container ports can be opened to the public and thus make containerized components accessible from the outside world. Finally, it introduces Traefik, a reverse proxy, to enable sophisticated HTTP application-level routing between containers.
Chapter 11, Docker Compose, addresses the concept of an application consisting of multiple services, each running in a container, and how Docker Compose allows us to easily build, run, and scale such an application using a declarative approach.
Chapter 12, Orchestrators, presents the concept of orchestrators. It explains why orchestrators are needed and how they work conceptually. The chapter will also provide an overview of the most popular orchestrators and name a few of their pros and cons.
Chapter 13, Introduction to Docker Swarm, introduces Docker's native orchestrator, SwarmKit. We will see all the concepts and objects SwarmKit uses to deploy and run a distributed, resilient, robust, and highly available application in a cluster on premises or in the cloud. The chapter also introduces how SwarmKit ensures secure applications using software-defined networks to isolate containers and secrets to protect sensitive information. Additionally, this chapter shows how to install a highly available Docker swarm in the cloud. It introduces the routing mesh, which provides Layer 4 routing and load balancing. Finally, it shows how to deploy an application consisting of multiple services onto the swarm.
Chapter 14, Zero-Downtime Deployments and Secrets, explains how to deploy services or applications onto a Docker swarm with zero downtime and automatic rollback capabilities. It also introduces secrets as a means to protect sensitive information.
Chapter 15, Introduction to Kubernetes, introduces the current most popular container orchestrator. It introduces the core Kubernetes objects that are used to define and run a distributed, resilient, robust, and highly available application in a cluster. Finally, it introduces MiniKube as a way to locally deploy a Kubernetes application, and also the integration of Kubernetes with Docker for Mac and Docker for Windows.
Chapter 16, Deploying, Updating, and Securing an Application with Kubernetes, explains how to deploy, update, and scale applications into a Kubernetes cluster. It also explains how to instrument your application services with liveness and readiness probes to support Kubernetes in its health and availability checking. Furthermore, the chapter explains how zero-downtime deployments are achieved to enable disruption-free updates and rollbacks of mission-critical applications. Finally, the chapter introduces Kubernetes secrets as a means to configure services and protect sensitive data.
Chapter 17, Monitoring and Troubleshooting an App Running in Production, teaches different techniques to monitor an individual service or a whole distributed application running on a Kubernetes cluster. It also shows how to troubleshoot an application service that is running in production without altering the cluster or the cluster nodes on which the service is running.
Chapter 18, Running a Containerized App in the Cloud, provides an overview of some of the most popular ways of running containerized applications in the cloud. We include self-hosting and hosted solutions and discuss their pros and cons. Fully managed offerings of vendors such as Microsoft Azure and Google Cloud Engine are briefly discussed.
A solid understanding of distributed application architecture and an interest in accelerating and simplifying the building, shipping, and running of highly distributed applications are expected. No prior experience with Docker containers is required.
Access to a computer with Windows 10 Professional or macOS installed is highly recommended. The computer should have at least 16 GB of memory.
Software/Hardware covered in the book
OS Requirements
Docker for Desktop, Docker Toolbox, Visual Studio Code, Powershell or Bash Terminal.
Windows 10 Pro/macOS/ Linux minimum of 8GB RAM
If you are using the digital version of this book, we advise you to type the code yourself or access the code via the GitHub repository (link available in the next section). Doing so will help you avoid any potential errors related to copy/pasting of code.
You can download the example code files for this book from your account at www.packt.com. If you purchased this book elsewhere, you can visit www.packtpub.com/support and register to have the files emailed directly to you.
You can download the code files by following these steps:
Log in or register at
www.packt.com
.
Select the
Support
tab.
Click on
Code Downloads
.
Enter the name of the book in the
Search
box and follow the onscreen instructions.
Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:
WinRAR/7-Zip for Windows
Zipeg/iZip/UnRarX for Mac
7-Zip/PeaZip for Linux
The code bundle for the book is also hosted on GitHub at https://github.com/PacktPublishing/Learn-Docker---Fundamentals-of-Docker-19.x-Second-Edition. In case there's an update to the code, it will be updated on the existing GitHub repository.
We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!
We also provide a PDF file that has color images of the screenshots/diagrams used in this book. You can download it here: http://www.packtpub.com/sites/default/files/downloads/9781838827472_ColorImages.pdf.
There are a number of text conventions used throughout this book.
CodeInText: Indicates code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles. Here is an example: "The container runtime on a Docker host consists of containerd and runc."
A block of code is set as follows:
{ "name": "api", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC"}
When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:
ARG BASE_IMAGE_VERSION=12.7-stretch
FROM node:
${BASE_IMAGE_VERSION}
WORKDIR /appCOPY packages.json .RUN npm installCOPY . .CMD npm start
Any command-line input or output is written as follows:
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Bold: Indicates a new term, an important word, or words that you see onscreen. For example, words in menus or dialog boxes appear in the text like this. Here is an example: "Select System info from the Administration panel."
Feedback from our readers is always welcome.
General feedback: If you have questions about any aspect of this book, mention the book title in the subject of your message and email us at [email protected].
Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/support/errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.
Piracy: If you come across any illegal copies of our works in any form on the Internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.
If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.
Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!
For more information about Packt, please visit packt.com.
The objective of Part One is to introduce you to the concept of containers and explain why they are so extremely useful in the software industry. You will also prepare your working environment for the use of Docker.
This section comprises the following chapters:
Chapter 1
,
What Are Containers and Why Should I Use Them?
Chapter 2
,
Setting Up a Working Environment
This first chapter will introduce you to the world of containers and their orchestration. This book starts from the very beginning, in that it assumes that you have no prior knowledge of containers, and will give you a very practical introduction to the topic.
In this chapter, we will focus on the software supply chain and the friction within it. Then, we'll present containers, which are used to reduce this friction and add enterprise-grade security on top of it. We'll also look into how containers and the ecosystem around them are assembled. We'll specifically point out the distinction between the upstream Open Source Software (OSS) components, united under the code name Moby, that form the building blocks of the downstream products of Docker and other vendors.
The chapter covers the following topics:
What are containers?
Why are containers important?
What's the benefit for me or for my company?
The Moby project
Docker products
Container architecture
After completing this module, you will be able to do the following:
Explain what containers are, using an analogy such as physical containers, in a few simple sentences to an interested layman
Justify why containers are so important using an analogy such as physical containers versus traditional shipping or apartment homes versus single-family homes, and so on, to an interested lay person
Name at least four upstream open source components that are used by Docker products, such as Docker for Desktop
Identify at least three Docker products
A software container is a pretty abstract thing, so it might help if we start with an analogy that should be pretty familiar to most of you. The analogy is a shipping container in the transportation industry. Throughout history, people have been transporting goods from one location to another by various means. Before the invention of the wheel, goods would most probably have been transported in bags, baskets, or chests on the shoulders of the humans themselves, or they might have used animals such as donkeys, camels, or elephants to transport them.
With the invention of the wheel, transportation became a bit more efficient as humans built roads that they could move their carts along. Many more goods could be transported at a time. When the first steam-driven machines, and later gasoline-driven engines, were introduced, transportation became even more powerful. We now transport huge amounts of goods on trains, ships, and trucks. At the same time, the types of goods became more and more diverse, and sometimes complex to handle.
In all these thousands of years, one thing didn't change, and that was the necessity to unload goods at a target location and maybe load them onto another means of transportation. Take, for example, a farmer bringing a cart full of apples to a central train station where the apples are then loaded onto a train, together with all the apples from many other farmers. Or think of a winemaker bringing his barrels of wine with a truck to the port where they are unloaded, and then transferred to a ship that will transport them overseas.
This unloading from one means of transportation and loading onto another means of transportation was a really complex and tedious process. Every type of product was packaged in its own way and thus had to be handled in its own particular way. Also, loose goods faced the risk of being stolen by unethical workers or damaged in the process of being handled.
Then, there came containers and they totally revolutionized the transportation industry. A container is just a metallic box with standardized dimensions. The length, width, and height of each container is the same. This is a very important point. Without the World agreeing on a standard size, the whole container thing would not have been as successful as it is now.
Now, with standardized containers, companies who want to have their goods transported from A to B package those goods into these containers. Then, they call a shipper, which comes with a standardized means for transportation. This can be a truck that can load a container or a train whose wagons can each transport one or several containers. Finally, we have ships that are specialized in transporting huge numbers of containers. Shippers never need to unpack and repackage goods. For a shipper, a container is just a black box, and they are not interested in what is in it, nor should they care in most cases. It is just a big iron box with standard dimensions. Packaging goods into containers is now fully delegated to the parties who want to have their goods shipped, and they should know how to handle and package those goods.
Since all containers have the same agreed-upon shape and dimensions, shippers can use standardized tools to handle containers; that is, cranes that unload containers, say from a train or a truck, and load them onto a ship and vice versa. One type of crane is enough to handle all the containers that come along over time. Also, the means of transportation can be standardized, such as container ships, trucks, and trains.
Because of all this standardization, all the processes in and around shipping goods could also be standardized and thus made much more efficient than they were before the age of containers.
Now, you should have a good understanding of why shipping containers are so important and why they revolutionized the whole transportation industry. I chose this analogy purposefully, since the software containers that we are going to introduce here fulfill the exact same role in the so-called software supply chain that shipping containers do in the supply chain of physical goods.
In the old days, developers would develop a new application. Once that application was completed in their eyes, they would hand that application over to the operations engineers, who were then supposed to install it on the production servers and get it running. If the operations engineers were lucky, they even got a somewhat accurate document with installation instructions from the developers. So far, so good, and life was easy.
But things get a bit out of hand when, in an enterprise, there are many teams of developers that create quite different types of application, yet all of them need to be installed on the same production servers and kept running there. Usually, each application has some external dependencies, such as which framework it was built on, what libraries it uses, and so on. Sometimes, two applications use the same framework but in different versions that might or might not be compatible with each other. Our operations engineer's life became much harder over time. They had to be really creative with how they could load their ship, (their servers,) with different applications without breaking something.
Installing a new version of a certain application was now a complex project on its own, and often needed months of planning and testing. In other words, there was a lot of friction in the software supply chain. But these days, companies rely more and more on software, and the release cycles need to become shorter and shorter. We cannot afford to just release twice a year or so anymore. Applications need to be updated in a matter of weeks or days, or sometimes even multiple times per day. Companies that do not comply risk going out of business, due to the lack of agility. So, what's the solution?
One of the first approaches was to use virtual machines (VMs). Instead of running multiple applications, all on the same server, companies would package and run a single application on each VM. With this, all the compatibility problems were gone and life seemed to be good again. Unfortunately, that happiness didn't last long. VMs are pretty heavy beasts on their own since they all contain a full-blown operating system such as Linux or Windows Server, and all that for just a single application. This is just as if you were in the transportation industry and were using a whole ship just to transport a single truckload of bananas. What a waste! That could never be profitable.
The ultimate solution to this problem was to provide something that is much more lightweight than VMs, but is also able to perfectly encapsulate the goods it needs to transport. Here, the goods are the actual application that has been written by our developers, plus – and this is important – all the external dependencies of the application, such as its framework, libraries, configurations, and more. This holy grail of a software packaging mechanism was the Docker container.
Developers use Docker containers to package their applications, frameworks, and libraries into them, and then they ship those containers to the testers or operations engineers. To testers and operations engineers, a container is just a black box. It is a standardized black box, though. All containers, no matter what application runs inside them, can be treated equally. The engineers know that, if any container runs on their servers, then any other containers should run too. And this is actually true, apart from some edge cases, which always exist.
Thus, Docker containers are a means to package applications and their dependencies in a standardized way. Docker then coined the phrase Build, ship, and run anywhere.
These days, the time between new releases of an application become shorter and shorter, yet the software itself doesn't become any simpler. On the contrary, software projects increase in complexity. Thus, we need a way to tame the beast and simplify the software supply chain.
Also, every day, we hear that cyber-attacks are on the rise. Many well-known companies are and have been affected by security breaches. Highly sensitive customer data gets stolen during such events, such as social security numbers, credit card information, and more. But not only customer data is compromised – sensitive company secrets are stolen too.
Containers can help in many ways. First of all, Gartner found that applications running in a container are more secure than their counterparts not running in a container. Containers use Linux security primitives such as Linux kernel namespaces to sandbox different applications running on the same computers and control groups (cgroups) in order to avoid the noisy-neighbor problem, where one bad application is using all the available resources of a server and starving all other applications.
Due to the fact that container images are immutable, it is easy to have them scanned for common vulnerabilities and exposures (CVEs), and in doing so, increase the overall security of our applications.
Another way to make our software supply chain more secure is to have our containers use a content trust. A content trust basically ensures that the author of a container image is who they pretend to be and that the consumer of the container image has a guarantee that the image has not been tampered with in transit. The latter is known as a man-in-the-middle (MITM) attack.
Everything I have just said is, of course, technically also possible without using containers, but since containers introduce a globally accepted standard, they make it so much easier to implement these best practices and enforce them.
OK, but security is not the only reason why containers are important. There are other reasons too.
One is the fact that containers make it easy to simulate a production-like environment, even on a developer's laptop. If we can containerize any application, then we can also containerize, say, a database such as Oracle or MS SQL Server. Now, everyone who has ever had to install an Oracle database on a computer knows that this is not the easiest thing to do, and it takes up a lot of precious space on your computer. You wouldn't want to do that to your development laptop just to test whether the application you developed really works end-to-end. With containers at hand, we can run a full-blown relational database in a container as easily as saying 1, 2, 3. And when we're done with testing, we can just stop and delete the container and the database will be gone, without leaving a trace on our computer.
Since containers are very lean compared to VMs, it is not uncommon to have many containers running at the same time on a developer's laptop without overwhelming the laptop.
A third reason why containers are important is that operators can finally concentrate on what they are really good at: provisioning the infrastructure and running and monitoring applications in production. When the applications they have to run on a production system are all containerized, then operators can start to standardize their infrastructure. Every server becomes just another Docker host. No special libraries or frameworks need to be installed on those servers, just an OS and a container runtime such as Docker.
Also, operators do not have to have intimate knowledge of the internals of applications anymore, since those applications run self-contained in containers that ought to look like black boxes to them, similar to how shipping containers look to the personnel in the transportation industry.
Somebody once said that, today, every company of a certain size has to acknowledge that they need to be a software company. In this sense, a modern bank is a software company that happens to specialize in the business of finance. Software runs all businesses, period. As every company becomes a software company, there is a need to establish a software supply chain. For the company to remain competitive, their software supply chain has to be secure and efficient. Efficiency can be achieved through thorough automation and standardization. But in all three areas – security, automation, and standardization – containers have been shown to shine. Large and well-known enterprises have reported that, when containerizing existing legacy applications (many call them traditional applications) and establishing a fully automated software supply chain based on containers, they can reduce the cost for the maintenance of those mission-critical applications by a factor of 50% to 60% and they can reduce the time between new releases of these traditional applications by up to 90%.
That being said, the adoption of container technologies saves these companies a lot of money, and at the same time it speeds up the development process and reduces the time to market.
Originally, when Docker (the company) introduced Docker containers, everything was open source. Docker didn't have any commercial products at this time. The Docker engine that the company developed was a monolithic piece of software. It contained many logical parts, such as the container runtime, a network library, a RESTful (REST) API, a command-line interface, and much more.
Other vendors or projects such as Red Hat or Kubernetes were using the Docker engine in their own products, but most of the time, they were only using part of its functionality. For example, Kubernetes did not use the Docker network library for the Docker engine but provided its own way of networking. Red Hat, in turn, did not update the Docker engine frequently and preferred to apply unofficial patches to older versions of the Docker engine, yet they still called it the Docker engine.
Out of all these reasons, and many more, the idea emerged that Docker had to do something to clearly separate the Docker open source part from the Docker commercial part. Furthermore, the company wanted to prevent competitors from using and abusing the name Docker for their own gains. This was the main reason why the Moby project was born. It serves as an umbrella for most of the open source components Docker developed and continues to develop. These open source projects do not carry the name Docker in them anymore.
The Moby project provides components that are used for image management, secret management, configuration management, and networking and provisioning, to name just a few. Also, part of the Moby project is special Moby tools that are, for example, used to assemble components into runnable artifacts.
Some components that technically belong to the Moby project have been donated by Docker to the Cloud-Native Computing Foundation (CNCF) and thus do not appear in the list of components anymore. The most prominent ones are notary, containerd, and runc, where the first is used for content trust and the latter two form the container runtime.
Docker currently separates its product lines into two segments. There is the CommunityEdition (CE), which is closed-source yet completely free, and then there is the Enterprise Edition (EE), which is also closed-source and needs to be licensed on a yearly basis. These enterprise products are backed by 24/7 support and are supported by bug fixes.
Part of the Docker Community Edition are products such as the Docker Toolbox and Docker for Desktop with its editions for Mac and Windows. All these products are mainly targeted at developers.
Docker for Desktop is an easy-to-install desktop application that can be used to build, debug, and test Dockerized applications or services on a macOS or Windows machine. Docker for macOS and Docker for Windows are complete development environments that are deeply integrated with their respective hypervisor framework, network, and filesystem. These tools are the fastest and most reliable way to run Docker on a Mac or Windows.
Under the CE umbrella, there are also two products that are more geared toward operations engineers. These products are Docker for Azure and Docker for AWS.
For example, with Docker for Azure, which is a native Azure application, you can set up Docker in a few clicks, optimized for and integrated with underlying Azure Infrastructure as a Service (IaaS) services. It helps operations engineers accelerate time to productivity when building and running Docker applications in Azure.
Docker for AWS works very similarly but for Amazon's cloud.
The Docker Enterprise Edition consists of the Universal Control Plane (UCP) and the Docker Trusted Registry (DTR), both of which run on top of Docker Swarm. Both are swarm applications. Docker EE builds on top of the upstream components of the Moby project and adds enterprise-grade features such as role-based access control (RBAC), multi-tenancy, mixed clusters of Docker swarm and Kubernetes, web-based UI, and content trust, as well as image scanning on top.
Now, let's discuss how a system that can run Docker containers is designed at a high level. The following diagram illustrates what a computer that Docker has been installed on looks like. Note that a computer that has Docker installed on it is often called a Docker host because it can run or host Docker containers:
In the preceding diagram, we can see three essential parts:
On the bottom, we have the
Linux operating system
In the middle, in dark gray, we have the container runtime
On the top, we have the
Docker engine
Containers are only possible due to the fact that the Linux OS provides some primitives, such as namespaces, control groups, layer capabilities, and more, all of which are leveraged in a very specific way by the container runtime and the Docker engine. Linux kernel namespaces, such as process ID (pid) namespaces or network (net) namespaces, allow Docker to encapsulate or sandbox processes that run inside the container. Control Groups make sure that containers cannot suffer from the noisy-neighbor syndrome, where a single application running in a container can consume most or all of the available resources of the whole Docker host. Control Groups allow Docker to limit the resources, such as CPU time or the amount of RAM, that each container is allocated.
The container runtime on a Docker host consists of containerd and runc. runc is the low-level functionality of the container runtime, while containerd, which is based on runc, provides higher-level functionality. Both are open source and have been donated by Docker to the CNCF.
The container runtime is responsible for the whole life cycle of a container. It pulls a container image (which is the template for a container) from a registry if necessary, creates a container from that image, initializes and runs the container, and eventually stops and removes the container from the system when asked.
The Docker engine provides additional functionality on top of the container runtime, such as network libraries or support for plugins. It also provides a REST interface over which all container operations can be automated. The Docker command-line interface that we will use frequently in this book is one of the consumers of this REST interface.
In this chapter, we looked at how containers can massively reduce friction in the software supply chain and, on top of that, make the supply chain much more secure.
In the next chapter, we will learn how to prepare our personal or working environment such as that we can work efficiently and effectively with Docker. So, stay tuned.
Please answer the following questions to assess your learning progress:
Which statements are correct (multiple answers are possible)?
A. A container is kind of a lightweight VM B. A container only runs on a Linux host C. A container can only run one process D. The main process in a container always has PID 1 E. A container is one or more processes encapsulated by Linux namespaces and restricted by cgroups
In your own words, maybe by using analogies, explain what a container is.
Why are containers considered to be a game-changer in IT? Name three or four reasons.
What does it mean when we claim:
If a container runs on a given platform, then it runs anywhere...?
Name two to three reasons why this is true.
Docker containers are only really useful for modern greenfield applications based on microservices. Please justify your answer.
A. True B. False
How much does a typical enterprise save when containerizing its legacy applications?
A. 20% B. 33% C. 50% D. 75%
Which two core concepts of Linux are containers based on?
The following is a list of links that lead to more detailed information regarding the topics we discussed in this chapter:
Docker overview:
https://docs.docker.com/engine/docker-overview/
The Moby project:
https://mobyproject.org/
Docker products:
https://www.docker.com/get-started
Cloud-Native Computing Foundation:
https://www.cncf.io/
containerd
– an
industry-standard container runtime:
https://containerd.io/
In the last chapter, we learned what Docker containers are and why they're important. We learned what kinds of problems containers solve in a modern software supply chain.
In this chapter, we are going to prepare our personal or working environment to work efficiently and effectively with Docker. We will discuss in detail how to set up an ideal environment for developers, DevOps, and operators that can be used when working with Docker containers.
This chapter covers the following topics:
The Linux command shell
PowerShell for Windows
Installing and using a package manager
Installing Git and cloning the code repository
Choosing and installing a code editor
Installing Docker for Desktop on macOS or Windows
Installing Docker Toolbox
Installing Minikube
For this chapter, you will need a laptop or a workstation with either macOS or Windows, preferably Windows 10 Professional, installed. You should also have free internet access to download applications and permission to install those applications on your laptop.
It is also possible to follow along with this book if you have a Linux distribution as your operating system, such as Ubuntu 18.04 or newer. I will try to indicate where commands and samples differ significantly from the ones on macOS or Windows.
Docker containers were first developed on Linux for Linux. It is hence natural that the primary command-line tool used to work with Docker, also called a shell, is a Unix shell; remember, Linux derives from Unix. Most developers use the Bash shell. On some lightweight Linux distributions, such as Alpine, Bash is not installed and consequently one has to use the simpler Bourne shell, just called sh. Whenever we are working in a Linux environment, such as inside a container or on a Linux VM, we will use either /bin/bash or /bin/sh, depending on their availability.
Although Apple's macOS X is not a Linux OS, Linux and macOS X are both flavors of Unix and hence support the same set of tools. Among those tools are the shells. So, when working on macOS, you will probably be using the Bash shell.
In this book, we expect from you a familiarity with the most basic scripting commands in Bash and PowerShell, if you are working on Windows. If you are an absolute beginner, then we strongly recommend that you familiarize yourself with the following cheat sheets:
Linux Command Line Cheat Sheet
by Dave Child at
http://bit.ly/2mTQr8l
PowerShell Basic Cheat Sheet
at
http://bit.ly/2EPHxze
On a Windows computer, laptop, or server, we have multiple command-line tools available. The most familiar is the command shell. It has been available on any Windows computer for decades. It is a very simple shell. For more advanced scripting, Microsoft has developed PowerShell. PowerShell is very powerful and very popular among engineers working on Windows. On Windows 10, finally, we have the so-called Windows Subsystem for Linux, which allows us to use any Linux tool, such as the Bash or Bourne shells. Apart from this, there are also other tools that install a Bash shell on Windows, for example, the Git Bash shell. In this book, all commands will use Bash syntax. Most of the commands also run in PowerShell.
Our recommendation for you is hence to either use PowerShell or any other Bash tool to work with Docker on Windows.
The easiest way to install software on a macOS or Windows laptop is to use a good package manager. On macOS, most people use Homebrew, and on Windows, Chocolatey is a good choice. If you're using a Debian-based Linux distribution such as Ubuntu, then the package manager of choice for most is apt, which is installed by default.
Homebrew is the most popular package manager on macOS, and it is easy to use and very versatile. Installing Homebrew on macOS is simple; just follow the instructions at https://brew.sh/:
In a nutshell, open a new Terminal window and execute the
following
command to install Homebrew:
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Once the installation is finished, test whether Homebrew is working by entering
brew --version
in the Terminal. You should see something like this:
$ brew --version
Homebrew 2.1.4Homebrew/homebrew-core (git revision 77d1b; last commit 2019-06-07)
Now, we are ready to use Homebrew to install tools and utilities. If we, for example, want to install the Vi text editor, we can do so like this:
$ brew install vim
This will then download and install the editor for you.
Chocolatey is a popular package manager for Windows, built on PowerShell. To install the Chocolatey package manager, please follow the instructions at https://chocolatey.org/ or open a new PowerShell window in admin mode and execute the following command:
PS> Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Once Chocolatey is installed, test it with the
choco --version
command. You should see output similar to the following:
PS> choco --version
0.10.15
To install an application such as the Vi editor, use the following command:
PS> choco install -y vim
The -y parameter makes sure that the installation happens without asking for reconfirmation.
We are using Git to clone the sample code accompanying this book from its GitHub repository. If you already have Git installed on your computer, you can skip this section:
To install Git on your macOS, use the following command in a Terminal window:
$ choco install git
To install Git on Windows, open a PowerShell window and use Chocolatey to install it:
PS>
choco install git -y
Finally, on your Debian or Ubuntu machine, open a Bash console and execute the following command:
$ sudo apt update && sudo apt install -y git
Once Git is installed, verify that it is working. On all platforms, use the following:
$ git --version
This should output something along the lines of the following:
git version 2.16.3
Now that Git is working, we can clone the source code accompanying this book from GitHub. Execute the following command:
$ cd ~
$ git clone https://github.com/PacktPublishing/Learn-Docker---Fundamentals-of-Docker-19.x-Second-Edition fod-solution
This will clone the content of the master branch into your local folder, ~/fod-solution. This folder will now contain all of the sample solutions for the labs we are going to do together in this book. Refer to these sample solutions if you get stuck.
Now that we have installed the basics, let's continue with the code editor.
Using a good code editor is essential to working productively with Docker. Of course, which editor is the best is highly controversial and depends on your personal preference. A lot of people use Vim, or others such as Emacs, Atom, Sublime, or Visual Studio Code (VS Code), to just name a few. VS Code is a completely free and lightweight editor, yet it is very powerful and is available for macOS, Windows, and Linux. According to Stack Overflow, it is currently by far the most popular code editor. If you are not yet sold on another editor, I highly recommend that you give VS Code a try.
But if you already have a favorite code editor, then please continue using it. As long as you can edit text files, you're good to go. If your editor supports syntax highlighting for Dockerfiles and JSON and YAML files, then even better. The only exception will be Chapter 6, Debugging Code Running in a Container. The examples presented in that chapter will be heavily tailored toward VS Code.
Follow these steps for installation:
Open a new Terminal window and execute the following command:
$ brew cask install visual-studio-code
Once VS Code has been installed successfully, navigate to your home directory (
~
) and create a folder,
fundamentals-of-docker
; then navigate into this new folder:
$ mkdir ~/fundamentals-of-docker && cd ~/fundamentals-of-docker
Now open VS Code from within this folder:
$ code .
Don't forget the period (.) in the preceding command. VS will start and open the current folder (~/fundamentals-of-docker) as the working folder.
Follow these steps for installation:
Open a new PowerShell window in admin mode and execute the following command:
PS>
choco install vscode -y
Close your PowerShell window and open a new one, to make sure VS Code is in your path.
Now navigate to your home directory and create a folder,
fundamentals-of-docker
; then navigate into this new folder:
PS>
mkdir ~\fundamentals-of-docker; cd ~\fundamentals-of-docker
Finally open Visual Studio Code from within this folder:
PS>
code .
Don't forget the period (.) in the preceding command. VS will start and open the current folder (~\fundamentals-of-docker) as the working folder.
Follow these steps for installation:
On your Debian or Ubuntu-based Linux machine, open a Bash Terminal and execute the following statement to install VS Code:
$ sudo snap install --classic code
If you're using a Linux distribution that's not based on Debian or Ubuntu, then please follow the following link for more details:
https://code.visualstudio.com/docs/setup/linux
Once VS Code has been installed successfully, navigate to your home directory (
~
) and create a folder
,
fundamentals-of-docker
; then navigate into this new folder:
$ mkdir ~/fundamentals-of-docker && cd ~/fundamentals-of-docker
Now open Visual Studio Code from within this folder:
$ code .
Don't forget the period (.) in the preceding command. VS will start and open the current folder (~/fundamentals-of-docker) as the working folder.
Extensions are what make VS Code such a versatile editor. On all three platforms, macOS, Windows, and Linux, you can install VS Code extensions the same way:
