Linux系统下Docker安装与核心概念实战指南
1. 从“为什么需要Docker”开始聊起
如果你在Linux服务器上折腾过软件部署,大概率经历过这样的场景:为了部署一个应用,你需要先安装特定版本的Python,然后安装一堆依赖包,结果发现某个依赖包和系统里另一个服务的依赖版本冲突,导致整个环境崩掉。或者,你在一台Ubuntu 22.04上跑得好好的服务,迁移到CentOS 7上就各种报错,光是排查环境差异就耗掉大半天。这种“在我机器上能跑”的困境,是每个运维和开发者都头疼的问题。
Docker的出现,就是为了解决这个核心痛点。它不是一个轻量级的虚拟机,而是一个 容器化 技术。你可以把它理解为一个超级轻便、标准化的“软件集装箱”。这个集装箱里打包了你的应用代码、运行环境、系统工具、系统库和设置。无论这个集装箱被运到哪台支持Docker的Linux机器上(我们称之为“宿主机”),它都能以完全一致的方式运行起来。这彻底解决了环境不一致的问题,让开发、测试、生产环境的部署变得极其简单和可靠。
我最早接触Docker是在一个微服务项目里,当时十几个服务,每个服务的依赖环境都不同,手动部署简直是噩梦。自从用上Docker,所有服务的环境都被固化在镜像里,通过一个简单的 docker-compose up 命令就能拉起整个集群,部署时间从几小时缩短到几分钟。这种效率的提升是革命性的。所以,无论你是想学习一项热门技术,还是切实想提升自己的开发和部署效率,掌握Linux下的Docker都是非常值得投入时间的一步。
2. 安装前的准备:理清思路与避坑指南
在动手安装Docker之前,有几个关键点必须搞清楚,这能帮你避开后面90%的坑。很多人一上来就照着教程敲命令,结果遇到各种“virtualization support not detected”或者权限问题,其实根源都在准备阶段没做好。
首先,明确你的Linux发行版和版本。 Docker对主流发行版的支持都很好,但安装方式略有不同。最常见的是基于Debian的(如Ubuntu、Debian)和基于RPM的(如CentOS、RHEL、Fedora)。你需要用 cat /etc/os-release 命令确认一下。这篇文章的实操部分会以 Ubuntu 22.04 LTS 和 CentOS 7 这两个最典型的系统为例进行讲解,其他发行版可以举一反三。
其次,理解Docker的架构和核心概念。 Docker采用的是客户端-服务器(C/S)架构。我们平时在命令行里敲的 docker 命令,其实是 Docker客户端(Client) 。它通过REST API与一个常驻后台进程通信,这个后台进程就是 Docker守护进程(Daemon) ,它负责创建、运行和管理容器。我们安装Docker,主要就是安装这个守护进程、客户端以及相关的工具链。
最后,也是最重要的,检查系统环境。 这里有两个大坑:
- 内核版本 :Docker需要较新的Linux内核来支持其底层特性(如cgroups、namespaces)。对于生产环境,建议内核版本在3.10以上。用
uname -r查看。 - 存储驱动 :Docker镜像和容器数据需要存储驱动来管理。对于较新的发行版,默认的
overlay2驱动是首选,性能最好。但有些旧系统或云主机可能用了devicemapper,这可能会带来性能问题。安装后需要确认。
注意 :网上很多教程会提到要“开启虚拟化支持”,这通常是针对在Windows或macOS上安装 Docker Desktop 时的要求。在纯Linux环境下安装Docker Engine(也就是我们常说的Docker), 并不需要CPU的虚拟化技术(如Intel VT-x/AMD-V) 。Docker容器直接共享宿主机内核,利用的是Linux内核的cgroup和namespace等特性来实现隔离,这与虚拟机(VM)的硬件虚拟化是完全不同的技术路线。所以,如果你在Linux主机上遇到类似“virtualization support not detected”的错误提示,那很可能是你错误地尝试安装Docker Desktop for Linux,或者混淆了概念。在Linux上,我们安装的是原生的Docker Engine。
3. Ubuntu 22.04 系统下的Docker安装详解
Ubuntu是个人学习和开发环境中使用最广泛的Linux发行版,其软件包管理工具 apt 非常方便。下面我们走一遍最稳妥的安装流程,我会解释每一步的作用。
3.1 卸载旧版本与配置仓库
第一步永远是清理战场,避免旧版本残留导致冲突。即使你是新系统,执行一下也无妨。
sudo apt-get remove docker docker-engine docker.io containerd runc
这条命令移除了可能存在的旧版Docker包。 containerd 和 runc 是Docker依赖的底层容器运行时,我们后续会安装更新的版本。
接下来,我们需要让 apt 知道从哪里获取Docker官方软件包。Docker官方维护了一个APT仓库,比Ubuntu自带源中的版本更新、更稳定。
# 更新apt包索引并安装一些工具,这些工具允许apt通过HTTPS使用仓库
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
# 添加Docker官方的GPG密钥。GPG密钥用于验证软件包的签名,确保你下载的包是官方发布的,没有被篡改。
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 设置稳定版仓库。这里将仓库地址写入/etc/apt/sources.list.d/docker.list文件。
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
这里有个细节: $(lsb_release -cs) 会自动获取你的Ubuntu代号,比如 jammy (对应22.04)。这确保了仓库地址与你的系统版本匹配。
3.2 安装Docker Engine
仓库配置好后,安装就很简单了。
# 再次更新apt包索引,这次它会读取我们刚添加的Docker仓库信息。
sudo apt-get update
# 安装Docker Engine、命令行工具cli、containerd以及docker-compose-plugin。
# 注意包名是`docker-ce`(社区版),`docker-ee`是企业版。
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
执行这个命令后, apt 会自动处理所有依赖。安装完成后,Docker守护进程会自动启动。
3.3 验证安装与权限配置
安装是否成功,跑一个最经典的测试镜像就知道了。
sudo docker run hello-world
如果看到“Hello from Docker!”等欢迎信息,说明Docker已经成功安装并可以拉取和运行容器了。
但每次命令都要加 sudo 很麻烦,而且有安全风险。通常我们会将当前用户加入 docker 用户组,这样就能直接执行docker命令。
# 创建docker用户组(通常安装时已自动创建)
sudo groupadd docker
# 将当前用户加入docker组
sudo usermod -aG docker $USER
关键一步 :退出当前终端并重新登录,或者新开一个终端,用户组的更改才会生效。之后,你就可以不用 sudo 直接运行 docker ps 等命令了。
验证一下:
docker run --rm ubuntu:22.04 /bin/echo 'Hello, Docker!'
能成功输出信息,说明权限配置正确。
4. CentOS 7 系统下的Docker安装与差异点处理
CentOS 7(以及RHEL 7)在企业服务器中非常常见,但它的内核版本相对较老,且使用 yum 作为包管理器,安装过程与Ubuntu有显著区别。
4.1 清理环境与添加仓库
CentOS的Extras仓库提供了一个旧版本的 docker 包(实际上是 docker.io ),但版本非常老旧,我们一定要先卸载它,然后使用Docker官方仓库。
# 卸载旧版本
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
# 安装yum工具包,它提供了`yum-config-manager`工具,用于管理仓库。
sudo yum install -y yum-utils
# 添加Docker官方仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
4.2 安装Docker Engine及可能的内核升级
CentOS 7默认内核是3.10,虽然满足Docker的最低要求,但某些新特性可能不支持。如果条件允许,建议升级到更新的内核(如ELRepo仓库的长期支持版内核)。这里我们先按默认内核安装。
# 安装Docker Engine
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
如果安装过程中提示 container-selinux 版本冲突,这是因为Docker依赖的 container-selinux 版本高于CentOS默认仓库提供的。你需要从CentOS Extras仓库安装一个特定版本,或者先安装 centos-release-extras 包。一个常见的解决命令是:
sudo yum install -y https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-<version>.rpm
# 然后再安装docker-ce
具体版本号需要去Docker仓库查看。这是CentOS 7安装Docker时最常见的坑。
4.3 启动服务与配置防火墙
CentOS使用 systemd 管理服务,且默认开启了防火墙 firewalld ,需要额外配置。
# 启动Docker守护进程
sudo systemctl start docker
# 设置开机自启
sudo systemctl enable docker
# 验证安装
sudo docker run hello-world
防火墙配置 :Docker守护进程默认会创建一个 docker0 的网桥,容器通过它进行网络通信。为了让容器能与外界(或宿主机)正常通信,需要在防火墙中放行相关流量。
# 永久放行Docker服务使用的端口范围(默认是2375/2376,但通常我们不用)
# 更常见的做法是,信任docker0网桥的流量
sudo firewall-cmd --permanent --zone=trusted --add-interface=docker0
sudo firewall-cmd --reload
如果你在容器内运行了Web服务(如映射了80端口),还需要在防火墙中放行对应的宿主机端口:
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --reload
同样,记得将用户加入 docker 组以避免 sudo :
sudo groupadd docker
sudo usermod -aG docker $USER
# 重新登录后生效
5. Docker核心概念与基础命令实战
安装好了,我们来真正用起来。要熟练使用Docker,必须理解三个核心概念: 镜像(Image) 、 容器(Container) 、 仓库(Registry) 。它们的关系就像面向对象编程中的 类 和 对象 。
- 镜像(Image) :一个只读的模板,里面包含了运行应用所需的代码、库、环境变量和配置文件。它相当于一个“类”的定义。例如
ubuntu:22.04,nginx:latest,python:3.9-slim。 - 容器(Container) :镜像的一个运行实例。你可以创建、启动、停止、删除容器。容器与容器之间是相互隔离的。它相当于根据“类”创建出来的一个“对象”。
- 仓库(Registry) :集中存放镜像的地方。最大的公共仓库是 Docker Hub ,你可以从这里拉取镜像,也可以推送自己的镜像上去。私有的仓库可以自己搭建,比如用Harbor。
下面是一组你最需要掌握的基础命令,我附上了实际使用场景和解释。
5.1 镜像相关操作
# 从仓库拉取镜像。如果不指定标签,默认拉取 `latest`。
# 建议生产环境总是指定明确的版本标签,避免因`latest`更新导致意外。
docker pull nginx:1.23-alpine
# 列出本地已下载的镜像
docker images
# 或者使用更强大的新命令 `docker image ls`
docker image ls
# 搜索Docker Hub上的镜像(通常直接去网站搜更直观)
docker search mysql
# 删除本地镜像。如果镜像有对应的容器(即使已停止),需要先删除容器才能删除镜像。
# 使用 `-f` 可以强制删除,但慎用。
docker rmi nginx:1.23-alpine
# 删除所有未被容器使用的镜像(悬空镜像)
docker image prune
# 删除所有未被使用的镜像(包括未被任何容器引用的镜像)
docker image prune -a
5.2 容器生命周期管理
这是最核心的部分,容器就像一个轻量级的进程。
# 运行一个容器
# `-d`: 后台运行(detached mode)
# `--name mynginx`: 给容器起个名字,否则Docker会随机分配一个
# `-p 8080:80`: 端口映射,将宿主机的8080端口映射到容器的80端口
# `nginx:alpine`: 使用的镜像
docker run -d --name mynginx -p 8080:80 nginx:alpine
# 此时,在浏览器访问 http://你的服务器IP:8080,就能看到Nginx欢迎页。
# 列出正在运行的容器
docker ps
# 列出所有容器(包括已停止的)
docker ps -a
# 停止一个运行中的容器
docker stop mynginx
# 启动一个已停止的容器
docker start mynginx
# 重启容器
docker restart mynginx
# 进入一个正在运行的容器的命令行终端
# `-it` 是 `-i` (交互式) 和 `-t` (分配一个伪终端) 的组合,让你可以像SSH一样操作容器。
docker exec -it mynginx /bin/sh
# 对于基于Alpine的镜像,shell通常是`/bin/sh`;对于Ubuntu等,是`/bin/bash`。
# 查看容器的日志输出,排查问题非常有用
docker logs mynginx
# 实时查看日志(类似 `tail -f`)
docker logs -f mynginx
# 删除一个已停止的容器
docker rm mynginx
# 强制删除一个运行中的容器(不推荐,应先停止)
docker rm -f mynginx
# 删除所有已停止的容器(清理空间)
docker container prune
5.3 容器与宿主机的数据交互
容器默认是“无状态”的,其内部文件系统的改动在容器删除后会丢失。为了持久化数据(如数据库文件、配置文件、日志),我们需要使用 卷(Volume) 或 绑定挂载(Bind Mount) 。
卷(Volume) :由Docker管理,存储在宿主机文件系统的某个区域(通常是 /var/lib/docker/volumes/ ),与容器的生命周期解耦。这是持久化数据的推荐方式。
# 创建一个名为 `my-vol` 的卷
docker volume create my-vol
# 运行容器并使用这个卷,将容器内的 `/app/data` 目录挂载到卷上
docker run -d --name myapp -v my-vol:/app/data myapp-image
# 即使 `myapp` 容器被删除,`my-vol` 卷及其数据依然存在,可以被其他容器挂载。
绑定挂载(Bind Mount) :将宿主机上的一个特定目录或文件直接挂载到容器中。常用于开发环境,方便在宿主机上修改代码,容器内实时生效。
# 将宿主机的 `/home/user/project` 目录挂载到容器的 `/app` 目录
docker run -d --name dev-app -v /home/user/project:/app dev-app-image
# 现在你在宿主机上修改代码,容器内的 `/app` 下文件也会同步变化。
6. 使用Dockerfile定制专属镜像
从公共镜像仓库拉取镜像很方便,但更多时候我们需要构建自己的镜像,比如部署自己的Python应用、Java应用。这就需要编写 Dockerfile 。Dockerfile是一个文本文件,包含了一条条构建镜像所需的指令。
下面我们以一个简单的Python Flask应用为例,手把手构建一个镜像。
项目结构 :
myflaskapp/
├── app.py
├── requirements.txt
└── Dockerfile
app.py (一个最简单的Flask应用):
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, Docker!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt :
Flask==2.3.2
Dockerfile :
# 第一阶段:使用官方Python轻量级镜像作为构建环境
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-slim
# 设置工作目录
WORKDIR /app
# 从构建阶段复制已安装的Python包
COPY --from=builder /usr/local /usr/local
# 复制应用代码
COPY . .
# 声明容器运行时监听的端口
EXPOSE 5000
# 定义容器启动时执行的命令
CMD ["python", "app.py"]
逐行解释 :
-
FROM: 指定基础镜像。我们使用了两阶段构建,第一阶段builder用于安装依赖,第二阶段使用同一个基础镜像但只复制安装结果,可以显著减小最终镜像体积。 -
WORKDIR: 设置工作目录,相当于cd到这个目录。 -
COPY: 将宿主机文件复制到镜像内。 -
RUN: 在构建镜像时执行的命令,这里用来安装依赖。我们使用了国内镜像源加速下载。 -
EXPOSE: 声明容器打算使用的端口,这是一个文档性质的说明,实际端口映射需要在docker run时用-p指定。 -
CMD: 指定容器启动时默认执行的命令。一个Dockerfile中只能有一条CMD指令。
构建镜像 : 在 myflaskapp 目录下执行:
# `-t` 给镜像打标签,格式为 `名称:标签`,标签默认为 `latest`
# `.` 表示Dockerfile所在的当前目录为构建上下文
docker build -t my-flask-app:1.0 .
运行容器 :
# 将宿主机的5000端口映射到容器的5000端口
docker run -d --name myapp -p 5000:5000 my-flask-app:1.0
访问 http://localhost:5000 ,就能看到“Hello, Docker!”了。
7. 使用Docker Compose编排多容器应用
现实中的应用很少是单个容器,通常由多个服务组成,比如一个Web应用需要搭配数据库、缓存等。手动用 docker run 启动每一个容器并配置网络链接非常繁琐。 Docker Compose 就是用来定义和运行多容器Docker应用的工具。它使用一个YAML文件(默认 docker-compose.yml )来配置所有服务。
假设我们有一个WordPress博客,需要WordPress容器和MySQL数据库容器。
创建一个 docker-compose.yml 文件:
version: '3.8' # 指定Compose文件格式版本
services:
# 数据库服务
db:
image: mysql:8.0
# 容器重启策略:除非手动停止,否则总是重启
restart: always
environment:
# 设置MySQL的root密码和环境变量,这是必须的
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
# 命名卷,用于持久化数据库数据
volumes:
- db_data:/var/lib/mysql
# 自定义网络,让服务间可以通过服务名通信
networks:
- wp-network
# WordPress服务
wordpress:
depends_on:
- db # 声明依赖,确保db服务先启动
image: wordpress:latest
restart: always
ports:
- "8080:80" # 将宿主机的8080端口映射到WordPress容器的80端口
environment:
# 告诉WordPress如何连接数据库,这里`db`就是上面定义的服务名
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
# 绑定挂载,将当前目录下的`wp-content`目录挂载,方便主题/插件管理
- ./wp-content:/var/www/html/wp-content
networks:
- wp-network
# 定义卷,Docker Compose会自动创建和管理它
volumes:
db_data:
# 定义网络,Docker Compose会自动创建一个名为`项目名_wp-network`的网络
networks:
wp-network:
driver: bridge
运行与管理 : 在包含 docker-compose.yml 的目录下,执行以下命令:
# 启动所有服务(后台运行)
docker-compose up -d
# 查看服务状态
docker-compose ps
# 查看所有服务的日志
docker-compose logs
# 查看特定服务(如wordpress)的日志
docker-compose logs wordpress
# 停止所有服务
docker-compose stop
# 停止并删除所有容器、网络(但保留卷)
docker-compose down
# 停止并删除所有容器、网络、卷(数据也会被删除!)
docker-compose down -v
Docker Compose极大地简化了多服务应用的部署和管理,是开发测试环境的利器。对于更复杂的生产环境集群编排,则需要用到 Kubernetes (K8s) 或 Docker Swarm 。
8. 生产环境部署的进阶考量与优化
将Docker用于个人学习或开发很简单,但一旦要上生产环境,就需要考虑更多因素:安全性、性能、日志、监控、镜像仓库等。
1. 镜像优化与安全扫描
- 使用多阶段构建 :如上文Flask例子所示,可以大幅减小最终镜像体积,减少攻击面。
- 使用特定标签 :不要使用
latest标签,而应使用明确的版本号,如python:3.9.16-slim-bullseye。 - 使用非root用户运行容器 :在Dockerfile中创建并使用非root用户。
RUN groupadd -r appuser && useradd -r -g appuser appuser USER appuser - 定期扫描镜像漏洞 :可以使用
docker scan命令(集成Snyk)或第三方工具(如Trivy、Clair)扫描镜像中的已知安全漏洞。
2. 日志管理 容器默认将日志输出到 stdout 和 stderr ,Docker守护进程会捕获并存储。在生产环境中,需要将日志集中收集和管理。
- 使用日志驱动 :可以配置Docker守护进程将日志发送到
json-file(默认)、syslog、journald或fluentd等。 - 使用Docker Compose的
logging配置 :可以限制日志文件大小,防止磁盘被撑满。services: myapp: image: myapp logging: driver: "json-file" options: max-size: "10m" max-file: "3"
3. 资源限制 防止单个容器耗尽宿主机资源。
# 运行容器时限制CPU和内存
docker run -d --name myapp \
--cpus="1.5" \ # 限制使用1.5个CPU核心
--memory="512m" \ # 限制内存为512MB
--memory-swap="1g" \ # 限制内存+交换分区总量为1GB
myapp-image
4. 使用私有镜像仓库 生产环境不应直接从Docker Hub拉取镜像。应该搭建私有仓库(如Harbor、Nexus)或使用云服务商提供的仓库(如ACR、ECR、GCR)。
- 登录私有仓库 :
docker login myregistry.example.com - 给镜像打上私有仓库标签 :
docker tag myapp:1.0 myregistry.example.com/myapp:1.0 - 推送镜像 :
docker push myregistry.example.com/myapp:1.0 - 拉取镜像 :
docker pull myregistry.example.com/myapp:1.0
5. 服务发现与网络 对于多主机集群,Docker原生的 bridge 网络不够用。需要结合 Overlay网络 (Swarm模式)或直接使用Kubernetes的CNI(容器网络接口)插件来管理跨主机的容器网络。
从单机安装到多容器编排,再到生产环境的考量,Docker的学习路径是循序渐进的。我个人的经验是,先在本机或虚拟机里把单容器和Docker Compose玩熟练,理解镜像、容器、数据卷、网络这些核心概念。当你的应用变得复杂,需要高可用、自动伸缩时,再去探索Kubernetes的世界。Docker是这一切的基石,扎实的基础会让你后续的学习轻松很多。在实际操作中,最常遇到的坑往往是权限问题( Got permission denied )、端口冲突、镜像拉取慢(记得配置国内镜像加速器)以及磁盘空间不足(记得定期 prune )。多动手,多查日志,解决问题的过程本身就是最好的学习。
更多推荐

所有评论(0)