Ubuntu 20.04 Docker安装配置与容器化实践指南
1. 项目概述:为什么要在Ubuntu 20.04上拥抱Docker?
如果你是一名在Ubuntu 20.04上折腾的开发或运维,大概率遇到过“环境依赖地狱”:项目A需要Python 3.8,项目B却死死依赖Python 3.6;一个微服务需要特定版本的Redis,另一个则要求完全不同的配置。更别提那些因为系统库版本差异,在测试环境跑得好好的,一到生产就崩掉的尴尬场景。Docker的出现,正是为了解决这些痛点。它通过容器化技术,将应用及其所有依赖打包成一个标准化的单元,实现了“一次构建,处处运行”的梦想。
Ubuntu 20.04 LTS(Focal Fossa)作为一个长期支持版本,以其稳定性和广泛的社区支持,成为了众多服务器和开发机的首选系统。在这个系统上部署Docker,意味着你能在一个成熟、可靠的基础设施上,构建起灵活、高效的现代化应用交付流水线。无论是想快速搭建一个MySQL测试实例,还是部署一个复杂的微服务架构,Docker都能让你像搭积木一样轻松。今天,我就以一个过来人的身份,带你从零开始,在Ubuntu 20.04上把Docker装好、配好、用起来,并分享那些官方文档里不会写的实操细节和避坑指南。
2. 核心思路与准备工作:知其然,更知其所以然
在动手敲命令之前,我们先理清几个关键概念和准备工作,这能让你在后续遇到问题时,知道从哪里着手排查,而不是盲目地复制粘贴。
2.1 Docker的核心组件与Ubuntu适配性解析
很多人以为Docker就是一个命令工具,其实它是一套引擎。在Ubuntu上,我们主要与以下组件打交道:
-
Docker Engine(Docker守护进程)
:这是核心,一个常驻后台的进程(
dockerd),负责管理容器、镜像、网络和存储卷。我们通过客户端命令行工具与它交互。 -
Docker Client(命令行工具)
:我们常用的
docker命令就是客户端,它通过REST API与守护进程通信。 - Docker Registry(镜像仓库) :比如Docker Hub,是存储和分发Docker镜像的地方。默认从Docker Hub拉取,但在国内,直接访问速度堪忧,所以配置国内镜像加速是必选项。
为什么选择在Ubuntu 20.04上通过APT仓库安装,而不是下载二进制包?原因在于
依赖管理和自动更新
。APT(Advanced Package Tool)是Ubuntu的包管理器,它能自动处理Docker运行所需的所有库依赖(如
containerd.io
,
docker-ce-cli
),并且未来可以通过
sudo apt upgrade
一键更新Docker到新版本,省心又安全。相比之下,二进制包安装需要手动处理依赖和更新路径,更适合无法使用包管理器的特殊环境。
2.2 安装前的系统检查与必要配置
安装前的检查就像手术前的体检,能避免很多“手术中”的意外。请依次执行以下命令:
# 1. 检查系统版本,确认是Ubuntu 20.04
lsb_release -a
你应该看到类似
Description: Ubuntu 20.04.x LTS
的输出。
# 2. 更新现有软件包列表,确保获取最新的仓库信息
sudo apt update
# 3. 安装一些基础工具,后续会用到
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
这里解释一下这几个包的作用:
apt-transport-https
允许APT通过HTTPS协议访问仓库,更安全;
ca-certificates
是CA根证书,用于验证HTTPS连接;
curl
用于从网络下载;
software-properties-common
提供了管理软件源的工具;
gnupg
用于处理GPG密钥,验证软件包签名。
# 4. (关键步骤)检查内核版本与虚拟化支持
uname -r
Docker需要Linux内核版本在3.10以上,Ubuntu 20.04默认的内核(5.4)完全满足。更关键的是虚拟化支持,尤其是如果你后续想运行非Linux系统的容器(通过
qemu
),或者使用Docker Desktop的某些高级功能。不过,对于标准的Linux容器,以下检查更为重要:
# 检查cgroups和命名空间支持,这是容器的基石
grep -E 'cgroup|namespace' /proc/self/mountinfo
如果能看到
cgroup
、
cgroup2
、
namespace
相关的挂载信息,说明系统基础支持是完好的。很多人在虚拟机上安装失败,问题往往出在这里。
注意 :如果你是在虚拟机(如VMware、VirtualBox)或云服务器上操作,请务必在宿主机的BIOS/UEFI设置中,以及虚拟机软件配置里,开启CPU的虚拟化支持(Intel VT-x / AMD-V)。对于物理机,如果
kvm-ok命令提示不支持,也需要进入BIOS开启。这是很多“Docker启动失败”问题的根源。
3. 分步安装与配置实战
准备工作就绪,现在开始正式的安装流程。我会详细解释每一步的目的,让你不仅会操作,还明白为什么这么做。
3.1 添加Docker官方APT仓库
为什么不直接用Ubuntu自带的
docker.io
包?因为那个版本通常很旧,是Docker早期开源版本的一个分支(现在叫
moby
),缺少很多新特性和安全更新。我们要安装的是Docker官方维护的社区版(Docker CE)。
# 1. 添加Docker的官方GPG密钥
# 这一步是为了验证从Docker仓库下载的软件包是真实且未被篡改的。
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
这里用了管道符
|
,将
curl
下载的密钥直接传递给
gpg
命令处理。
-fsSL
参数让
curl
在失败时静默退出、跟随重定向、显示错误、并以列表形式输出,是安全的下载习惯。
--dearmor
是将ASCII格式的密钥转换为二进制格式。
# 2. 设置稳定版(stable)仓库
# 这里我们明确指定了Ubuntu 20.04 (focal)的amd64架构仓库。
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
-
arch=$(dpkg --print-architecture)会自动获取你的系统架构(如amd64, arm64)。 -
signed-by=指向我们刚刚导入的GPG密钥文件,确保来源可信。 -
$(lsb_release -cs)会输出你的Ubuntu代号,对于20.04就是focal。 -
tee命令将内容同时输出到屏幕和文件,> /dev/null将屏幕输出丢弃,保持终端整洁。
3.2 安装Docker Engine及相关组件
添加仓库后,就可以安装了。
# 1. 再次更新APT包索引,使新增的Docker仓库生效
sudo apt update
# 2. 安装Docker Engine、命令行工具以及Containerd
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
-
docker-ce: Docker社区版引擎。 -
docker-ce-cli: Docker命令行工具。 -
containerd.io: 行业标准的容器运行时,Docker Engine底层依赖它来管理容器生命周期。 -
docker-compose-plugin: 这是Docker Compose V2,作为一个CLI插件安装。旧式的docker-compose(Python编写)已不再推荐,这个插件版本性能更好,集成度更高。
安装过程会自动创建
docker
用户组,并将Docker守护进程设置为开机自启。
3.3 安装后的关键配置与验证
安装完成只是第一步,正确的配置才能让它好用又安全。
1. 管理用户权限(避免每次都sudo)
默认情况下,执行
docker
命令需要
root
权限(即加
sudo
)。为了方便,可以将当前用户加入
docker
用户组。
# 将当前用户加入docker组
sudo usermod -aG docker $USER
重要提醒
:执行此命令后,
必须完全注销当前会话并重新登录
,或者重启系统,用户组变更才会生效。仅仅新开一个终端标签页是不够的。这是新手最常忽略的一点,会导致执行
docker ps
时依然报权限错误。
2. 配置国内镜像加速器 从Docker Hub拉取镜像速度慢且不稳定。配置国内镜像加速是提升体验的关键。这里以阿里云镜像加速为例(需要注册阿里云账号获取专属地址)。
# 创建或修改Docker守护进程的配置文件
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://your_mirror_url.mirror.aliyuncs.com"],
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
-
registry-mirrors: 将your_mirror_url替换为你从阿里云容器镜像服务控制台获取的专属加速器地址。也可以添加多个,如中科大、网易的镜像源。 -
exec-opts: 设置cgroup驱动为systemd,这与Ubuntu 20.04默认使用systemd作为初始化系统保持一致,能获得更好的稳定性。 -
log-driver和log-opts: 配置日志驱动和单个日志文件最大尺寸,防止日志撑爆磁盘。 -
storage-driver: 存储驱动,overlay2是当前推荐且性能较好的驱动,适用于Ubuntu 20.04的ext4或xfs文件系统。
3. 重启Docker服务并验证 应用配置后,需要重启服务。
# 重新加载systemd配置,重启Docker服务
sudo systemctl daemon-reload
sudo systemctl restart docker
# 设置Docker服务开机自启
sudo systemctl enable docker
# 运行一个测试容器,验证安装和配置是否成功
docker run --rm hello-world
如果一切顺利,你将看到一段“Hello from Docker!”的欢迎信息,并说明你的安装看起来工作正常。这个命令会从配置的镜像加速器拉取
hello-world
镜像,创建并运行一个容器,输出信息后自动删除容器(
--rm
参数作用)。
4. Docker基础使用与核心概念实操
安装配置妥当,我们来真正“用”起来。理解下面几个核心操作,你就能应对80%的日常场景。
4.1 镜像(Image)操作:应用的模板
镜像是只读的模板,包含了运行应用所需的代码、运行时、库、环境变量和配置文件。可以把它理解成面向对象编程中的“类”。
# 搜索镜像,例如搜索Ubuntu镜像
docker search ubuntu
# 拉取镜像,从仓库下载到本地。不指定标签则默认为latest。
docker pull ubuntu:20.04
docker pull nginx:alpine # 拉取更轻量的nginx alpine版本
# 列出本地已下载的镜像
docker images
# 或者使用更强大的格式
docker image ls --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedAt}}\t{{.Size}}"
# 删除本地镜像
docker rmi <image_id或image_name:tag>
# 强制删除(即使有容器基于它)
docker rmi -f <image_id>
# 清理所有未被使用的镜像(悬空镜像)
docker image prune
4.2 容器(Container)操作:镜像的运行实例
容器是镜像的可运行实例。你可以创建、启动、停止、移动或删除容器。它是“类”的“对象”。
# 1. 运行一个新容器
# -d: 后台运行
# --name: 给容器起个名字,方便管理
# -p: 端口映射,将宿主机的8080端口映射到容器的80端口
docker run -d --name my-nginx -p 8080:80 nginx:alpine
# 2. 查看容器状态
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器(包括已停止的)
# 3. 与容器交互
# 查看容器日志
docker logs my-nginx
# 实时查看日志(类似 tail -f)
docker logs -f my-nginx
# 进入正在运行的容器内部执行命令(打开一个交互式终端)
docker exec -it my-nginx /bin/sh
# 退出容器终端,输入 exit 或按 Ctrl+D
# 4. 容器的生命周期管理
docker stop my-nginx # 停止容器
docker start my-nginx # 启动已停止的容器
docker restart my-nginx # 重启容器
# 5. 删除容器
docker rm my-nginx # 删除已停止的容器
docker rm -f my-nginx # 强制删除运行中的容器
4.3 数据持久化与网络:让容器更实用
默认情况下,容器内创建的文件只在容器生命周期内存在,容器删除,数据就没了。网络方面,容器有独立的网络命名空间。
数据卷(Volume) :是持久化容器数据的首选机制,完全由Docker管理,与宿主机生命周期解耦。
# 创建一个名为`my-data`的数据卷
docker volume create my-data
# 运行一个容器,并将数据卷挂载到容器内的`/app/data`目录
docker run -d --name my-app -v my-data:/app/data some-image
# 查看所有数据卷
docker volume ls
# 查看数据卷详情(如挂载点)
docker volume inspect my-data
# 删除未使用的数据卷
docker volume prune
绑定挂载(Bind Mount) :将宿主机上的一个特定目录或文件挂载到容器中。适合开发时同步代码。
# 将宿主机的`/home/user/project`目录挂载到容器的`/code`目录
docker run -d -v /home/user/project:/code some-dev-image
网络(Network)
:默认情况下,Docker会创建三种网络:
bridge
(默认)、
host
、
none
。你可以创建自定义网络,让容器间通过容器名通信。
# 创建一个自定义的桥接网络
docker network create my-network
# 运行两个容器,并加入同一个自定义网络
docker run -d --name web --network my-network nginx
docker run -it --name client --network my-network alpine ping web # 在client容器内可以直接ping通`web`这个主机名
5. 使用Docker Compose编排多容器应用
当你的应用需要多个容器协同工作时(比如一个Web应用需要Web服务器、数据库、缓存),手动管理每个容器非常繁琐。Docker Compose就是用来定义和运行多容器Docker应用的工具。我们安装的是V2插件版本,命令是
docker compose
(注意中间没有横线)。
5.1 编写docker-compose.yml文件
在一个项目目录下,创建一个名为
docker-compose.yml
的文件。下面是一个经典的WordPress + MySQL的编排示例:
version: '3.8' # 指定Compose文件格式版本
services:
# 数据库服务
db:
image: mysql:8.0 # 使用MySQL 8.0镜像
container_name: wordpress_db
restart: always # 总是重启,保证服务可用性
environment: # 设置环境变量,这里是数据库的root密码和创建的数据库名、用户
MYSQL_ROOT_PASSWORD: your_strong_root_password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: your_wordpress_db_password
volumes:
- db_data:/var/lib/mysql # 使用命名卷持久化数据库数据
networks:
- wp-network
# WordPress服务
wordpress:
image: wordpress:latest
container_name: wordpress_app
restart: always
depends_on: # 依赖关系,确保db服务先启动
- db
ports:
- "8080:80" # 将宿主机的8080端口映射到容器的80端口
environment:
WORDPRESS_DB_HOST: db:3306 # 这里直接用服务名`db`,Compose网络会自动解析
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: your_wordpress_db_password
WORDPRESS_DB_NAME: wordpress
volumes:
- wp_data:/var/www/html # 持久化WordPress网站文件
networks:
- wp-network
# 定义数据卷,供上面的服务使用
volumes:
db_data:
wp_data:
# 定义网络,让两个服务在同一个隔离网络中通信
networks:
wp-network:
driver: bridge
5.2 使用Compose命令管理应用
在包含
docker-compose.yml
文件的目录下,执行以下命令:
# 1. 启动所有服务(在后台运行)
docker compose up -d
# 2. 查看当前目录下Compose应用的状态
docker compose ps
# 3. 查看服务的日志(可以指定服务名,如`db`)
docker compose logs
docker compose logs -f wordpress # 实时跟踪wordpress服务的日志
# 4. 停止并移除所有容器、网络(默认不会移除数据卷)
docker compose down
# 5. 停止并移除所有容器、网络,同时移除数据卷(数据会丢失!)
docker compose down -v
# 6. 在运行中的服务上执行命令,例如进入db容器的bash
docker compose exec db bash
通过Compose,你用一个YAML文件和几条简单的命令,就管理起了一个完整的多服务应用栈,极大地简化了部署复杂度。
6. 常见问题排查与性能优化心得
即使按照步骤操作,你也可能会遇到一些问题。这里记录了几个我踩过的坑和解决方案。
6.1 Docker服务启动失败与权限问题
问题现象
:执行
sudo systemctl status docker
发现服务状态不是
active (running)
,或者执行
docker ps
时提示
Cannot connect to the Docker daemon
。
排查思路 :
-
检查服务状态与日志 :
sudo systemctl status docker sudo journalctl -xu docker --no-pager -n 50 # 查看Docker服务的详细日志日志是定位问题的第一手资料。常见错误包括:
-
iptables相关错误:可能是防火墙规则冲突。可以尝试暂时禁用ufw(sudo ufw disable) 或调整Docker的iptables配置。 -
cgroup挂载错误:检查/proc/self/mountinfo中是否有cgroup相关条目。确保系统已正确启用cgroup。 -
存储驱动错误
:如果日志提到
overlay2和xfs相关错误,可能是文件系统不支持d_type。用xfs_info /检查ftype是否为1。如果不是,可能需要重新格式化分区。
-
-
检查用户组权限 : 如果你已经将用户加入
docker组但仍然需要sudo,请确认你是否已经 重新登录 了会话。可以运行groups命令查看当前用户所属组是否包含docker。也可以直接测试:docker run --rm hello-world如果不加sudo能运行,说明权限正确。 -
检查端口冲突 : 如果Docker能启动,但运行容器时端口映射失败(如
Error starting userland proxy: listen tcp4 0.0.0.0:8080: bind: address already in use),说明宿主机8080端口已被占用。使用sudo netstat -tulpn | grep :8080查找占用进程并停止它,或者为容器映射另一个端口。
6.2 镜像拉取缓慢或失败
问题现象
:
docker pull
速度极慢,或出现
net/http: TLS handshake timeout
等超时错误。
解决方案 :
-
确认镜像加速器配置正确
:检查
/etc/docker/daemon.json文件中的registry-mirrors地址是否有效且格式正确(JSON格式要求严格,最后一个项后不能有逗号)。修改后务必执行sudo systemctl restart docker。 -
使用多个镜像源
:可以在
registry-mirrors数组中配置多个地址,Docker会依次尝试。"registry-mirrors": [ "https://hub-mirror.c.163.com", "https://mirror.baidubce.com", "https://docker.mirrors.ustc.edu.cn" ] -
直接使用国内镜像仓库
:对于一些常用镜像(如
ubuntu,nginx,mysql),可以直接从阿里云、腾讯云的镜像仓库拉取,例如docker pull registry.cn-hangzhou.aliyuncs.com/library/ubuntu:20.04。
6.3 容器磁盘空间占用过大
问题现象
:运行一段时间后,
/var/lib/docker
目录(Docker默认数据根目录)体积暴涨,导致系统磁盘空间不足。
清理策略 : Docker占用的空间主要包括:未使用的镜像、停止的容器、构建缓存、数据卷。定期清理是必要的。
# 1. 查看Docker磁盘使用概况
docker system df
# 2. 一键清理所有未使用的资源(镜像、容器、网络、构建缓存),但**不清理数据卷**。
docker system prune -a
# 3. 谨慎清理数据卷(会丢失数据!)
docker volume prune
# 4. 清理所有停止的容器
docker container prune
# 5. 清理所有悬空镜像(未被任何容器引用的镜像)
docker image prune
实操心得 :建议将
docker system prune加入定时任务(如cron每周执行一次),但务必注意生产环境,避免误删正在使用的中间镜像或缓存。对于数据卷,清理前一定要确认其数据是否已备份或可丢弃。
6.4 容器内应用时区不对
问题现象
:在容器内运行
date
命令,显示的是UTC时间,而不是东八区(北京时间)。
解决方案 :在运行容器时,将宿主机的时区文件挂载到容器内。
docker run -d --name some-app \
-v /etc/localtime:/etc/localtime:ro \
-v /etc/timezone:/etc/timezone:ro \
your-image
或者,在构建自己的Docker镜像时,在Dockerfile中设置时区环境变量:
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y tzdata
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7. 进阶使用:构建自定义镜像与Dockerfile编写
拉取现成镜像只是开始,更多时候我们需要定制自己的镜像。Dockerfile是一个文本文件,包含了一条条构建镜像所需的指令。
7.1 一个完整的Dockerfile示例
假设我们要为一个简单的Python Flask应用构建镜像。
# 第一阶段:构建阶段,使用较大的基础镜像包含构建工具
FROM python:3.9-slim as builder
WORKDIR /app
# 将依赖文件复制到工作目录
COPY requirements.txt .
# 使用清华PyPI镜像加速,安装依赖到/usr/local
RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
# 第二阶段:运行阶段,使用更小的基础镜像
FROM python:3.9-alpine
# 设置时区
RUN apk add --no-cache tzdata && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone && \
apk del tzdata
WORKDIR /app
# 从构建阶段仅复制安装好的Python包
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
# 复制应用代码
COPY . .
# 声明容器运行时监听的端口
EXPOSE 5000
# 设置环境变量,指定Flask应用入口
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
# 定义容器启动时执行的命令
CMD ["flask", "run"]
7.2 构建与运行自定义镜像
在包含
Dockerfile
和
app.py
、
requirements.txt
的目录下执行:
# 构建镜像,-t 参数给镜像打标签,格式为 name:tag
docker build -t my-flask-app:1.0 .
# 查看构建好的镜像
docker images | grep my-flask-app
# 运行容器
docker run -d -p 5000:5000 --name my-flask-container my-flask-app:1.0
7.3 Dockerfile编写最佳实践与心得
- 使用多阶段构建 :如上例所示,可以显著减小最终镜像体积。第一阶段安装编译依赖、构建代码;第二阶段只复制构建产物到一个小体积的基础镜像中。
- 合理利用构建缓存 :Docker会缓存每一层。把变化频率低的指令(如安装系统包、依赖)放在Dockerfile前面,把变化频率高的指令(如复制源代码)放在后面。
-
使用
.dockerignore文件 :类似于.gitignore,可以避免将本地不必要的文件(如__pycache__,.git,.env)复制到镜像构建上下文中,加速构建过程并避免泄露敏感信息。 - 一个容器只运行一个进程 :这是微服务的最佳实践。如果需要运行多个关联进程(如Web服务器和后台Worker),应考虑使用Supervisor等进程管理工具,或者更好的方式是拆分为多个容器,用Docker Compose编排。
-
以非root用户运行容器
:在Dockerfile中创建并使用一个非root用户,增强安全性。
RUN groupadd -r appuser && useradd -r -g appuser appuser USER appuser -
标签(Tag)管理
:为镜像使用有意义的标签,如版本号(
v1.2.3)、Git提交哈希(git-abc123),避免过度依赖latest标签,这有助于回滚和追踪问题。
在Ubuntu 20.04上搭建好Docker环境,并掌握了这些核心操作和问题排查技巧后,你就拥有了一个强大且一致的开发、测试和部署环境。无论是个人项目还是团队协作,容器化都能极大地提升效率和可靠性。记住,关键不在于记住所有命令,而在于理解其背后的原理(镜像、容器、数据卷、网络),这样无论遇到什么新工具或复杂场景,你都能从容应对。
更多推荐
所有评论(0)