Ubuntu Docker Integration
Introduction
Docker has revolutionized how developers build, share, and run applications by using containerization technology. In this guide, we'll explore how to integrate Docker with Ubuntu, one of the most popular Linux distributions for development environments.
Containers allow you to package an application with all its dependencies and run it in isolation from other processes. Unlike virtual machines, containers share the host system's kernel, making them lightweight and fast to start.
Why Use Docker on Ubuntu?
Ubuntu provides excellent compatibility with Docker due to its:
- Regular kernel updates supporting the latest container features
- Large community support for both technologies
- Official Docker packages maintained for Ubuntu releases
- Strong integration with cloud platforms where both are widely used
Prerequisites
Before we begin, you should have:
- Ubuntu 20.04 LTS or newer
- A user account with sudo privileges
- Terminal access
- Basic familiarity with command-line operations
Installing Docker on Ubuntu
Let's install Docker on your Ubuntu system.
Removing Old Versions
First, remove any older installations:
sudo apt-get remove docker docker-engine docker.io containerd runc
Setting Up the Repository
Install packages to allow apt to use a repository over HTTPS:
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
Add Docker's official GPG key:
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
Set up the repository:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Installing Docker Engine
Update the apt package index and install Docker:
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Verifying Installation
Verify Docker installation by running a test container:
sudo docker run hello-world
Expected output:
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
...
Post-Installation Steps
Running Docker Without Sudo
To run Docker commands without sudo
, add your user to the Docker group:
sudo groupadd docker
sudo usermod -aG docker $USER
Apply the new group membership:
newgrp docker
Test that you can run Docker without sudo:
docker run hello-world
Configuring Docker to Start on Boot
Enable Docker to start on system boot:
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
Docker Basic Concepts
Images and Containers
In Docker:
- Image: A read-only template containing instructions for creating a container
- Container: A runnable instance of an image
Architecture
Working with Docker Images
Pulling Images
Pull an Ubuntu image from Docker Hub:
docker pull ubuntu
Output:
Using default tag: latest
latest: Pulling from library/ubuntu
677076032cca: Pull complete
Digest: sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
Listing Images
List locally available images:
docker images
Output:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 825d55fb6340 2 weeks ago 72.8MB
hello-world latest bf756fb1ae65 13 months ago 13.3kB
Running Containers
Starting a Container
Run an Ubuntu container with an interactive terminal:
docker run -it ubuntu bash
This command:
-i
keeps STDIN open-t
allocates a pseudo-TTYubuntu
is the image namebash
is the command to run inside the container
You'll enter the Ubuntu container's shell, where you can execute commands.
Container Lifecycle
Start, stop, and remove containers:
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# Stop a container
docker stop container_id
# Start a stopped container
docker start container_id
# Remove a container
docker rm container_id
Building Custom Docker Images
Creating a Dockerfile
A Dockerfile defines how to build a custom image. Create a file named Dockerfile
:
mkdir my-ubuntu-app
cd my-ubuntu-app
nano Dockerfile
Add the following content:
# Base image
FROM ubuntu:latest
# Update packages and install curl
RUN apt-get update && apt-get install -y curl
# Set working directory
WORKDIR /app
# Copy local files to container
COPY . .
# Command to run when container starts
CMD ["bash"]
Building the Image
Build your custom image:
docker build -t my-ubuntu-app .
Output:
Sending build context to Docker daemon 2.048kB
Step 1/5 : FROM ubuntu:latest
---> 825d55fb6340
Step 2/5 : RUN apt-get update && apt-get install -y curl
---> Running in a89b2266d41b
Get:1 http://archive.ubuntu.com/ubuntu jammy InRelease [270 kB]
...
Removing intermediate container a89b2266d41b
---> 3c39b57eb9ab
Step 3/5 : WORKDIR /app
---> Running in 319e2b8e81b1
Removing intermediate container 319e2b8e81b1
---> 9b60d64992a9
Step 4/5 : COPY . .
---> 6e03e968f9db
Step 5/5 : CMD ["bash"]
---> Running in 1179b0a2a9fc
Removing intermediate container 1179b0a2a9fc
---> d83e9801b07e
Successfully built d83e9801b07e
Successfully tagged my-ubuntu-app:latest
Running Your Custom Image
Run a container using your custom image:
docker run -it my-ubuntu-app
Practical Project: Creating a Simple Web Server
Let's create a simple Node.js web server in a Docker container.
Project Structure
Create a new project:
mkdir node-docker-app
cd node-docker-app
Create a simple server file app.js
:
nano app.js
Add this content:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello from Docker on Ubuntu!');
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running at http://localhost:${port}/`);
});
Create a package.json
file:
nano package.json
Add this content:
{
"name": "node-docker-app",
"version": "1.0.0",
"description": "A simple Node.js app in Docker",
"main": "app.js",
"scripts": {
"start": "node app.js"
}
}
Creating the Dockerfile
Create a Dockerfile:
nano Dockerfile
Add this content:
FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Building and Running the Application
Build the Docker image:
docker build -t node-docker-app .
Run the container, mapping port 3000 from the container to port 8080 on the host:
docker run -p 8080:3000 -d --name my-node-app node-docker-app
Test the application:
curl http://localhost:8080
Output:
Hello from Docker on Ubuntu!
Docker Compose for Multi-Container Applications
For applications with multiple services, Docker Compose simplifies management.
Installing Docker Compose
Docker Compose is included with Docker Desktop for Windows and Mac, but on Ubuntu, we need to install it separately:
sudo apt-get install docker-compose-plugin
Verify installation:
docker compose version
Creating a Multi-Container Application
Let's create a web application with Node.js and MongoDB:
mkdir node-mongo-docker
cd node-mongo-docker
Create docker-compose.yml
:
nano docker-compose.yml
Add this content:
version: '3'
services:
web:
build: .
ports:
- "8080:3000"
environment:
- MONGODB_URI=mongodb://mongo:27017/nodeapp
depends_on:
- mongo
restart: always
mongo:
image: mongo
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
volumes:
mongodb_data:
Create app.js
:
nano app.js
Add this content:
const express = require('express');
const mongoose = require('mongoose');
const app = express();
const port = 3000;
// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true })
.then(() => console.log('MongoDB connected'))
.catch(err => console.log(err));
// Create a schema and model
const visitorSchema = new mongoose.Schema({
date: { type: Date, default: Date.now }
});
const Visitor = mongoose.model('Visitor', visitorSchema);
// Routes
app.get('/', async (req, res) => {
// Create a new visitor record
const visitor = new Visitor();
await visitor.save();
// Count all visitors
const count = await Visitor.countDocuments();
res.send(`Hello from Docker on Ubuntu! You are visitor number ${count}`);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
Create package.json
:
nano package.json
Add this content:
{
"name": "node-mongo-docker",
"version": "1.0.0",
"description": "Node.js with MongoDB in Docker",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.17.1",
"mongoose": "^5.13.7"
}
}
Create Dockerfile
:
nano Dockerfile
Add this content:
FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Running the Multi-Container Application
Start the application:
docker compose up -d
Test the application:
curl http://localhost:8080
Output:
Hello from Docker on Ubuntu! You are visitor number 1
Refresh a few times, and the visitor count will increase.
Managing the Application
Stop the application:
docker compose down
To stop and remove volumes:
docker compose down -v
Docker Volumes for Persistent Data
Docker volumes provide a way to persist data generated by containers.
Creating and Managing Volumes
Create a volume:
docker volume create my_data
List volumes:
docker volume ls
Use a volume with a container:
docker run -it --name ubuntu-with-volume -v my_data:/data ubuntu bash
Inside the container, you can now create files in /data
that will persist even after the container is removed:
echo "Hello, persistent data!" > /data/test.txt
exit
Remove the container:
docker rm ubuntu-with-volume
Create a new container using the same volume:
docker run -it --name new-ubuntu-container -v my_data:/data ubuntu bash
Verify the data persists:
cat /data/test.txt
Output:
Hello, persistent data!
Docker Networking
Docker provides several network types for container communication.
Network Types
- bridge: Default network for containers on a single host
- host: Uses the host's networking directly
- none: No networking
- overlay: For multi-host communication in Swarm mode
- macvlan: Assigns a MAC address to a container
Working with Networks
Create a custom network:
docker network create my-network
List networks:
docker network ls
Run containers in a custom network:
docker run -d --name container1 --network my-network ubuntu sleep infinity
docker run -d --name container2 --network my-network ubuntu sleep infinity
These containers can now communicate using their container names as hostnames.
Best Practices for Docker on Ubuntu
- Use official images when possible
- Keep images small by using multi-stage builds and minimal base images
- Use volumes for persistent data
- Don't run containers as root when possible
- Use Docker Compose for multi-container applications
- Regularly update your Docker installation
- Use .dockerignore files to exclude unnecessary files
- Set resource limits on containers in production
Troubleshooting Common Issues
Permission Denied
If you see permission denied
errors:
sudo chown $USER:$USER ~/.docker -R
sudo chmod g+rwx /var/run/docker.sock
Cannot Connect to Docker Daemon
If Docker daemon isn't running:
sudo systemctl start docker
Disk Space Issues
Clean up unused Docker resources:
docker system prune -a
Summary
In this guide, we've learned how to:
- Install and configure Docker on Ubuntu
- Work with Docker images and containers
- Build custom Docker images
- Use Docker Compose for multi-container applications
- Manage persistent data with volumes
- Configure Docker networking
- Follow best practices
Docker and Ubuntu provide a powerful combination for modern application development. With containers, you can ensure consistent environments across development, testing, and production, making your software more reliable and easier to deploy.
Additional Resources
- Official Docker Documentation
- Docker Hub for finding official and community images
- Docker Compose Documentation
Practice Exercises
- Create a Docker image for a simple Python Flask application
- Set up a WordPress site with MySQL using Docker Compose
- Create a CI/CD pipeline using Docker for an application of your choice
- Compare container performance between different Ubuntu versions
- Set up Docker Swarm for container orchestration across multiple Ubuntu hosts
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)