Docker 入门实战:在 Ubuntu 上跑起你的第一个容器
为什么折腾服务器的人都用 Docker:官方源安装、镜像/容器/卷/端口四个核心概念、跑一个带数据的容器,最后用 compose 编排收尾。

Ubuntu 系列完结后,下一个绕不开的话题就是 Docker。你在任何一个「自托管 XX 服务」的教程里都会看到它的身影。这篇不讲虚的,目标是让你在半小时内理解 Docker 的四个核心概念,并在自己的服务器上跑起真正有数据的容器。
为什么用 Docker,而不是 apt 直接装
apt 装软件本来挺顺,但自托管服务一多就会撞上这些墙:
- 依赖打架:服务 A 要 Python 3.10,服务 B 要 3.12,同一台机器上互相折磨。
- 卸载不干净:配置文件、数据目录散落各处,想删干净得满盘找。
- 环境不可复制:这台机器调好的服务,换台机器全部重来。
Docker 的思路是把每个服务和它的全部依赖打成一个集装箱:互不干扰、一条命令起停、删掉容器就删得干干净净、换个机器一条命令原地复活。这就是为什么自托管教程清一色用 Docker。
第一步:安装 Docker
Ubuntu 仓库里有 Docker 但版本旧,推荐用官方源(不要用 snap 版,坑多):
# 装依赖
sudo apt update
sudo apt install -y ca-certificates curl
# 添加 Docker 官方 GPG key
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# 添加软件源
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
两件收尾的事:
# 验证安装
sudo docker run hello-world
# 把当前用户加进 docker 组,以后敲命令不用 sudo(重新登录生效)
sudo usermod -aG docker $USER
注意官方源在国内可能很慢,可以先给 apt 走代理,或者搜「Docker 镜像加速器」配 registry-mirrors。
第二步:四个核心概念,一个例子讲透
Docker 只需要理解四个词:
| 概念 | 类比 | 说明 |
|---|---|---|
| 镜像(Image) | 安装光盘 | 只读模板,比如 nginx:latest |
| 容器(Container) | 用光盘装出来的系统 | 镜像跑起来的实例,可以起很多个 |
| 卷(Volume) | 移动硬盘 | 持久化数据,容器删了数据还在 |
| 端口映射 | 前台转接 | 把容器内部端口暴露到宿主机 |
直接上例子,跑一个 Nginx 容器:
docker run -d \
--name web \
-p 8080:80 \
-v web-data:/usr/share/nginx/html \
--restart unless-stopped \
nginx:latest
逐行拆解,这一行命令用到了全部四个概念:
-d:后台运行(detached)--name web:容器起名 web,不然 Docker 会随机生成怪名字-p 8080:80:端口映射,宿主机的 8080 → 容器内的 80。浏览器访问http://服务器IP:8080就能看到页面-v web-data:...:卷,容器里的网站目录挂载到名为 web-data 的卷上,容器删了重建,网页文件都还在--restart unless-stopped:服务器重启后容器自动拉起,自托管必备
验证:
docker ps # 看运行中的容器
curl http://localhost:8080
往卷里写个首页,刷新浏览器立即生效:
echo '<h1>Hello Docker</h1>' | sudo tee /var/lib/docker/volumes/web-data/_data/index.html
第三步:日常管理命令
docker ps # 运行中的容器
docker ps -a # 包括已停止的
docker logs -f web # 实时看日志(相当于 tail -f)
docker exec -it web bash # 进容器里操作(相当于 SSH 进集装箱)
docker stop web # 停止
docker start web # 启动
docker rm web # 删除(需先停止)
docker images # 看本地镜像
docker pull nginx:latest # 拉取/更新镜像
更新容器的标准流程是:拉新镜像 → 删旧容器 → 用同样的 docker run 参数重建。数据在卷里,所以重建无损。这就是为什么自托管服务一定要挂卷。
第四步:compose——把命令写成配置文件
容器一多,docker run 后面跟一长串参数很难维护。Docker Compose 把这些参数写进一个 YAML 文件,一键起停整组服务:
mkdir -p ~/docker/web && cd ~/docker/web
nano compose.yaml
services:
web:
image: nginx:latest
container_name: web
ports:
- "8080:80"
volumes:
- web-data:/usr/share/nginx/html
restart: unless-stopped
volumes:
web-data:
操作就三条:
docker compose up -d # 启动(配置文件有变动也会自动重建)
docker compose ps # 看状态
docker compose down # 停止并删除容器(卷里的数据保留)
以后你在网上看到的自托管教程,绝大多数都是给一个 compose.yaml 文件——学会这个格式,全世界的自托管服务你都能装。
常见坑
| 现象 | 原因与解决 |
|---|---|
permission denied 连不上 docker |
没加 docker 用户组,或加了之后没重新登录 |
| 拉镜像超时 | 网络问题,配代理或镜像加速器 |
端口冲突 port is already allocated |
宿主机端口被占,ss -tlnp 查一下,换个端口映射 |
| 容器重启后数据没了 | 没挂卷;只删容器不删卷数据就在 |
docker compose 提示命令不存在 |
老版本是 docker-compose(带横杠),新版是插件形式 docker compose |
小结
Docker 入门就是这么多:装引擎 → 理解镜像/容器/卷/端口 → 会跑会管 → 用 compose 写配置。从这篇开始,我的博客后续文章里凡是部署新服务,都会默认用 Docker 或 compose 的方式给出配置。
下一篇换个方向:给之前部署的 Nginx 站点挂上 HTTPS,把浏览器那个「不安全」警告消掉。
$ giscus --load# 使用 GitHub 账号登录即可发表评论