01-Docker 基础:安装、镜像容器与容器操作
📝 笔记摘要:Docker 基础,安装、镜像容器与容器操作
Docker 说白了就是「把一整套运行环境打包成一个文件」。装 MySQL 不用再纠结版本兼容,装 Java 不用配环境变量,镜像往服务器上一扔就起来了。
但新手栽的坑大多不在「跑不起来」,而在「跑起来了但不是你要的样子」:容器起来了访问不了、命令报权限错误、镜像拉下来了但跑的版本不对。这篇按「装 → 理解 → 用」的顺序捋一遍,每一步都写清楚它到底解决什么问题。
Docker 系列 共 4 篇,这是第 1 篇
01-Docker 基础 安装、概念与容器操作 ← 本篇
02 Docker 镜像与数据卷 分层结构、挂载与 Dockerfile
03 Docker 网络与 Compose 容器互访与多容器编排
04 Docker 常见问题排查 守护进程、权限与镜像拉取
1. 装 Docker
Linux 上装 Docker 现在只有一条路:官方那个 get.docker.com 脚本。它自己判断发行版、自己配源、自己装依赖,比手抄 apt / yum 命令靠谱得多——手抄的那套命令十个里有三个是过期的。
# 全系统通用,Debian / Ubuntu / CentOS / RHEL / Rocky 都能跑
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
脚本装完不会自动配开机自启
它会装好 systemd 服务,但enable这一步要自己补
sudo systemctl enable docker
少这一步的现象很典型:今天还好好的,重启一次docker ps就报连不上守护进程
装完验三个东西,缺一不可
docker --version客户端版本
docker info守护进程连不连得上
docker run hello-world真拉一个镜像跑起来
前两个过了不代表第三个也能过,第三个才是端到端的验证
版本别追新
Docker Engine 29 是当前主线,25 还在维护期
生产环境的镜像更别用latest——latest是会动的指针,不是版本号
要用就钉死成nginx:1.27.5这种带版本号的 tag
CentOS 7 得多做一步
CentOS 7 已经在 2024 年 6 月 30 日停止维护,官方 yum 源整个下线了。mirrorlist.centos.org 不再提供任何内容,yum 会直接报 Could not resolve host 或者 Cannot find a valid baseurl。
这不是网络故障,是源没了。得先把 yum 指向归档仓库,再谈装 Docker:
# 官方源换成归档仓库,国内用阿里云的 centos-vault
sudo sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*.repo
sudo sed -i 's|#baseurl=http://mirror.centos.org|baseurl=https://mirrors.aliyun.com/centos-vault|g' /etc/yum.repos.d/CentOS-*.repo
sudo yum clean all
sudo yum makecache
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
CentOS 7 上的 Docker 版本会被卡住
新的 Docker CE 要求 CentOS 8 以上,7 上能装的最高是 20.10 系列
这不是 yum 的问题,是包依赖真的不支持
要新版本就换 Rocky Linux 或 AlmaLinux,它们跟 CentOS 7 是二进制兼容的,迁移成本很低
CentOS 7 上的坑不止 Docker
它自己的防火墙行为也跟现在的规则不一样,Docker 还能绕过 firewalld
见 CentOS 7 防火墙常用操作 那篇
2. 配镜像加速
Docker Hub 的服务器在境外,国内直连的状态是「看运气」:小镜像可能就过来了,几 G 大的 CUDA、PyTorch 这类基本是拉到一半超时。
解法是在 /etc/docker/daemon.json 里配 registry-mirrors,让 Docker 先问国内的中转站:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
"registry-mirrors": [
"https://docker.1panel.live",
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
改完验证一下有没有真的加载上:
docker info | grep -A 5 "Registry Mirrors"
加速源是缓存,不是代理
镜像没被缓存过时,加速站会转发到 Docker Hub 去拉,第一次照样慢
拉过一次之后才有本地缓存,第二次才是真的快
所以「配了加速还是慢」很多时候是正常现象,多拉两次就快了
多配几条,故障切换用
这些站点随时可能挂,Docker 会自动试下一条
只配一条等于单点故障,那条挂了就是全线拉不动
阿里云那个得登录才有
控制台 → 容器镜像服务 → 镜像工具 → 镜像加速器
给你一个专属地址,带独立带宽,不跟大众源挤
国内机器上这是最稳的一个,比公共源优先级高
自己搭一个更可控
把 Docker Hub 反代到自己域名下,缓存也是自己的
公共源全挂了也不影响你,见 docker 搭建 docker 仓库代理
有一批地址已经死了,别再抄
huecker.io、dockerhub.timeweb.cloud、noohub.ru这几个俄罗斯源
2026 年 10 月实测全部连不上,配进去只会让 Docker 多等几秒再超时,不会更快
加速源的生命周期很短,抄来的地址三个月后再验一次
3. 镜像、容器、仓库
这三个词最容易混。分清了,后面所有概念都通顺。
镜像 image 一套只读的模板
里面是完整的文件系统:二进制、依赖库、配置文件
本身不能跑,它只是个模板
容器 container 镜像跑起来的一个实例
在上面加一层可写层,进程真正跑在里面
镜像可以起无数个容器,彼此隔离
仓库 repository 存镜像的地方
Docker Hub 是公共的,自己的服务器也能建一个
镜像只有拉下来才能用,本地没有就是拉
镜像为什么能做得这么小、为什么几个镜像能共享数据,见 Docker 镜像与数据卷 那篇的分层结构部分。
4. 数据都放在哪
容器是「用完即弃」的东西,宿主机上真正占地方的是 /var/lib/docker。
镜像层、容器可写层、卷,全在这一个目录下
/var/lib/docker/overlay2镜像层和容器层
/var/lib/docker/volumes具名卷的真实数据
/var/lib/docker/containers每个容器的日志 json 文件
容器日志不轮转会把磁盘吃满
stdout / stderr 会被 Docker 存成 json 文件,它不会自动清理
跑上几个月的服务,日志涨几十 G 是常事
磁盘告警先来这里看,见 Docker 常见问题排查
数据目录能不能整个搬走
能,Docker 支持data-root改到别的盘
但要停服务再搬,不能在线改
根分区小的话,镜像多的话这个很值得提前规划
5. 常用参数
docker ps # 只看运行中的容器
docker ps -a # 连没启动的、已退出的都列出来
docker ps -q # 只打印容器 ID,脚本里好用
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
docker images # 本地镜像列表
docker images -a # 连中间层和悬空镜像都列出来
docker stats # 实时看 CPU / 内存 / 网络
docker system df # 看 Docker 占了多少磁盘,分几类
docker system prune -a # 清理没用的镜像和容器,慎用
-a和-q是两个正交的维度
-a决定「列多少」,-q决定「输出成什么格式」
-a是 all,-q是 quiet(只出 ID),两个能叠
排查问题先看docker ps -a
一个容器「起不来」,去docker ps -a里能看到它躺在那儿,状态是 Exited
光看docker ps是看不到的,这个差异经常让人以为容器根本没创建
docker system prune -a会删掉没在用的镜像
包括那些「暂时没容器在跑但你还想留」的镜像
执行前先docker system df看一眼到底能省多少
6. 容器为什么自己停了
docker run -d 之后容器立刻变成 Exited,这是新手最常撞的一个现象。
容器跟着主进程走
Dockerfile 里CMD/ENTRYPOINT指定的那条命令,就是容器的主进程
主进程一退出,容器立刻停止,这是设计,不是 bug
三种典型原因
命令跑完就结束了 比如CMD echo "hello",打印完就退出
容器里没有常驻服务 比如拿centos当底子,进去就是 bash,退出 bash 容器就死
服务自己崩了 端口冲突、配置文件缺失、连不上数据库
排查顺序
docker ps -a确认容器状态和退出码
docker logs 容器名看最后几行报错
docker inspect 容器名看State.ExitCode和State.Error
退出码 137 是被 kill(多半是 OOM),139 是段错误,其他非 0 值看日志
要它别停就加--restart
--restart=always一直重启,Docker 守护进程起来也会拉
--restart=unless-stopped跟 always 差不多,但手动 stop 之后不自动拉起
生产环境一律要加,服务器重启或者 Docker 守护进程重启后服务能自己回来
真的需要一个长驻容器
把命令改成不会退出的,比如tail -f /dev/null,然后用docker exec进去干活
7. 怎么进容器
两条命令都能进去,区别在于进的是「谁的进程」。
docker exec -it 容器名 /bin/bash # 新开一个进程
docker attach 容器名 # 接管主进程的输入输出
exec是日常该用的那个
在容器里另起一个进程,不影响主进程
进去以后exit退出,容器照常跑
可以同时开好几个 exec,进出互不干扰
attach会「抢」终端
它接的是主进程的 stdin / stdout,你敲的键会直接喂给主进程
主进程没在读输入的话,看起来就是卡住不动
卡住时按Ctrl+P再按Ctrl+Q脱离,不要直接 Ctrl+C
Ctrl+C 是发给容器里的进程的,不是断开会话
attach 还有个隐患
你 attach 着的这段时间,docker logs就看不到输出了,两边抢同一个 stdout
排查问题用docker logs,日常操作用docker exec
镜像里没有 bash 怎么办
精简镜像(alpine 那种)只有/bin/sh,exec -it 容器名 /bin/bash会报 not found
换成/bin/sh就行
8. 部署 nginx
docker run -d --name nginx -p 3344:80 nginx
跑起来之后访问 http://服务器IP:3344,能出默认页面就成了。端口映射的格式是 宿主机端口:容器端口,左边那个可以随便改,右边那个是镜像里写死的,改不了。
容器内的 80 端口和你映射的 3344 是两回事
镜像里 nginx 监听 80,Docker 负责把宿主机 3344 的流量转进容器的 80
你也可以不映射,那就只能在容器内部访问,外面进不来
端口被占了换个宿主机端口
报bind: address already in use就是这个原因,左边改个数字就行
查谁占着:ss -tlnp | grep 3344
重启后 IP 会变,端口映射不会
容器 IP 是临时的,-p映射的端口是宿主机的,容器重建也不影响
所以配置文件里写http://宿主机IP:3344是稳的,写容器 IP 不稳
要改配置就挂出来
-v /data/nginx/conf.d:/etc/nginx/conf.d
改宿主机的文件,容器里的 nginx 立刻读到,重载一下就生效
9. 部署 tomcat
docker run -d --name tomcat -p 8080:8080 tomcat:9
起完之后浏览器访问 http://服务器IP:8080,默认应该看到一个 Tomcat 页面。
页面空白的常见原因
官方 Tomcat 镜像里的webapps目录是空的,它把示例应用挪到了webapps.dist
这不是镜像坏了,是官方镜像故意这么做——默认不跑示例应用
想看默认页面就把它复制回去:
docker exec tomcat cp -r webapps.dist webapps
自己的项目要放在webapps里
把 war 包挂进/usr/local/tomcat/webapps,容器启动会自动解压部署
看日志确认部署结果
docker logs tomcat能看 Tomcat 启动和部署的完整过程
war 包部署失败十有八九是webapps挂载位置不对
10. 找文件位置
容器里没有 which、locate 这类工具的时候,用 whereis。
whereis docker # 列出程序、源码、手册的路径
whereis nginx
whereis java
whereis查的是「安装位置」,不是「你 cd 到的位置」
它搜的是/usr/bin、/usr/sbin这些固定目录
它不查当前目录,也不查 PATH 里的任意位置
想找当前目录树里的文件,用find
whereis和which的区别
which查 PATH 里第一个匹配,whereis查固定安装目录
脚本里判断某个命令存不存在用which更准
11. 图形化管理
命令敲烦了可以用面板。Portainer 是最常见的一个,开源免费。
docker volume create portainer_data
docker run -d \
-p 9443:9443 \
--name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:lts
浏览器打开 https://服务器IP:9443,自签证书浏览器会报警告,点「继续前往」就行。
端口是 9443 不是 9000
早期的 Portainer 走9000明文 HTTP,新版改成了9443自签 HTTPS
照着老文章敲-p 9000:9000会连不上
镜像名是portainer/portainer-ce
不是portainer/portainer,那个名字已经停更了
数据一定要挂出来
-v portainer_data:/data存的是账号、设置、镜像加速配置
不挂的后果是重建容器后全部丢失,得重新设一遍
挂docker.sock等于交出宿主机
面板通过它直接控制 Docker 守护进程,能建特权容器、能删卷
面板被攻破和宿主机被攻破是同一件事
不要直接把面板端口扔公网,要么 VPN 兜住,要么只监听内网
国产面板可以对比着看
docker 搭建 Dpanel 容器管理面板 那篇讲授权边界讲得比较细
DPanel 是自定义非商业授权,商用要买,个人自用随便用
12. 权限:把用户加进 docker 组
普通用户敲 docker ps 报权限错误,解法就一条命令:
sudo gpasswd -a $USER docker
newgrp docker
newgrp这步不能省
改的是组数据库,当前这个已经登录的 shell 不会自动知道
忘了这步的现象是「命令还是报同样的错」,很多人以为上面那条没生效
重新登录一次也一样
docker 组基本等于免 sudo
能进这个组,就能通过挂 socket 起特权容器把宿主机根目录挂进去
只把确实需要用 Docker 的账号加进去
别图省事chmod 666 /var/run/docker.sock,那是把整个守护进程对全机开放
报的是 permission denied 但 daemon 明明在跑
这两种报错长得像,根因完全不同:
connect: permission denied 是 socket 权限问题,加组
Cannot connect to the Docker daemon 是守护进程没起来或者 socket 路径不对
详细区分见 Docker 常见问题排查 那篇
💡 补充几个容易忽略的:
用官方脚本装最省事,但别在生产机上随手跑
脚本会把仓库配置、软件源、启动服务一次性全改掉
想看清每一步做了什么就手动装,或者在测试机上先验一遍
国内机器先想清楚要不要配加速
配了就得知道配的是哪几个地址,加速源会挂,要能自己换
生产环境镜像一律钉版本
nginx:1.27.5而不是nginx:latest
latest 变了不会通知你,重启一次拉到的可能就是另一个版本
容器的日志在宿主机上是 json 文件
不是/var/log下面,是/var/lib/docker/containers里
这个目录不做轮转,磁盘满了第一个被撑爆的就是它
Docker Desktop 和服务器上的 Docker 不是一回事
Mac 和 Windows 上的 Desktop 有图形界面、能一键起虚拟机
但生产环境用的永远是服务器上的原生 Docker,行为有差异,别拿 Desktop 的经验套
镜像瘦身是有收益的
生产镜像用-slim或-alpine变体,拉取快、攻击面小
代价是某些系统工具没有,需要的时候再手动装
🔥 一句话总结:装 Docker 用官方
get.docker.com脚本,装完记得systemctl enable docker;国内机器在daemon.json里配多条registry-mirrors,注意加速源只是缓存、第一次照样慢;镜像只读、容器可写、仓库存镜像,这三个词分清后面都通顺;容器跟着主进程走,主进程一退容器就停,排查先看docker ps -a再看docker logs;进容器日常用docker exec -it而不是attach;普通用户用不了 Docker 就加进docker组并记得newgrp docker,但要清楚这个组基本等于免 sudo。
