Linux 和 Docker 学习笔记

这篇是我学 Linux 和 Docker 时的学习笔记,时间大约在 2021 年前后。

起因是项目做到一定程度后发现自己不会部署,只写了代码不知道怎么让它跑起来。开始是在云服务器上捣鼓,后来把一台闲置笔记本装了 CentOS7 做自己的”测试服务器”,顺便把 Linux 和 Docker 的基础一起补了。

文章内容比较杂,更像是操作记录,不是系统化的教程。留在这里主要方便自己翻阅。


Linux 常用命令速查

以下是日常最常用的那些,完整的 man page 还是要靠查文档。

文件操作

ls -la              # 列出含隐藏文件的详细列表
pwd # 显示当前目录
cd ~ # 回到家目录
mkdir -p a/b/c # 创建多级目录
cp -r src/ dst/ # 递归拷贝目录
rm -rf dir/ # 强制递归删除(危险,确认后再执行)
mv old new # 移动或重命名
find . -name "*.log" -size +100M # 按名称+大小查找文件

文件查看

cat -n file.txt             # 显示行号查看
tail -f /var/log/syslog # 实时追踪日志(常用)
grep -r "keyword" ./ # 递归搜索关键词
less file.txt # 分页查看大文件

tail -f 是看应用日志最常用的命令,配合 grep 做过滤更好用:tail -f app.log | grep ERROR

进程与系统

ps -ef | grep java          # 查找 Java 进程
kill -9 <pid> # 强制杀进程
top # 实时监控(按 M 按内存排序,按 P 按 CPU)
df -h # 查看磁盘使用
du -sh /home/docker # 查看指定目录大小
netstat -anp | grep 8080 # 查看端口占用

服务管理

systemctl start nginx       # 启动服务
systemctl stop nginx # 停止服务
systemctl restart nginx # 重启
systemctl enable nginx # 开机自启
systemctl status nginx # 查看状态

文件权限

chmod 755 script.sh         # rwxr-xr-x(所有者可写,其他人只读可执行)
chmod +x script.sh # 添加执行权限
chown user:group file # 修改所有者和组

压缩解压

tar -zcvf archive.tar.gz dir/       # 压缩目录
tar -zxvf archive.tar.gz # 解压到当前目录
tar -zxvf archive.tar.gz -C /home/ # 解压到指定目录

Docker 安装

CentOS7 安装 Docker

# 安装工具
yum install -y yum-utils

# 添加阿里云仓库(官方源在国内慢)
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

yum makecache fast
yum install docker-ce docker-ce-cli containerd.io

# 启动并设置开机自启
systemctl start docker
systemctl enable docker

配置镜像加速(必做,不然拉镜像很慢)

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"http://hub-mirror.c.163.com"
],
"bip": "192.168.200.1/24"
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

Docker 常用命令

# 镜像管理
docker images # 查看本地镜像列表
docker pull nginx:latest # 拉取镜像
docker rmi nginx:latest # 删除镜像
docker build -t myapp:1.0 . # 构建镜像(需要 Dockerfile)

# 容器操作
docker run -d -p 8080:8080 --name myapp myapp:1.0 # 后台启动容器
docker run --rm -it ubuntu bash # 交互式运行(退出后自动删除)
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器(含已停止)
docker stop myapp # 停止容器
docker start myapp # 启动已停止的容器
docker restart myapp # 重启容器
docker rm myapp # 删除容器(先 stop)
docker rm -f myapp # 强制删除运行中的容器

# 调试
docker logs -f myapp # 实时查看日志
docker logs --tail 100 myapp # 查看最后 100 行
docker exec -it myapp bash # 进入容器 shell
docker inspect myapp # 查看容器详细配置

# 清理
docker system prune -a # 清理所有未使用的镜像/容器/网络(腾空间用)

Dockerfile 基础

# 基础镜像
FROM openjdk:11-jre-slim

# 设置工作目录
WORKDIR /app

# 复制 jar 包
COPY target/myapp.jar app.jar

# 暴露端口
EXPOSE 8080

# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]

常用指令说明:

指令说明
FROM指定基础镜像,每个 Dockerfile 必须有
WORKDIR设置工作目录(后续 RUN/COPY 的相对路径基准)
COPY拷贝文件,不会解压
ADD类似 COPY,但能自动解压 .tar.gz 文件
RUN构建时执行命令(每条 RUN 产生一个镜像层,能合并尽量合并)
ENV设置环境变量
EXPOSE声明容器监听端口(仅声明,实际映射还需要 -p)
ENTRYPOINT容器启动时执行的命令
CMD容器启动默认命令(可被 docker run 末尾参数覆盖)

RUN 命令多了会导致镜像层数多、体积大,建议用 && 合并:

RUN yum update -y && yum install -y curl wget && yum clean all

Docker Compose 基础

适合在单机上启动多个关联容器(开发环境/测试环境常用)。

version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: mysql
ports:
- "3306:3306"
volumes:
- /mydata/mysql/data:/var/lib/mysql
- /mydata/mysql/conf:/etc/mysql/conf.d
environment:
- MYSQL_ROOT_PASSWORD=your_password
restart: always

redis:
image: redis:6.2
container_name: redis
ports:
- "6379:6379"
volumes:
- /mydata/redis/data:/data
command: redis-server --appendonly yes
restart: always

app:
image: myapp:1.0
container_name: myapp
ports:
- "8080:8080"
depends_on:
- mysql
- redis
restart: always
docker-compose up -d      # 后台启动所有服务
docker-compose down # 停止并删除容器
docker-compose ps # 查看服务状态
docker-compose logs -f # 查看所有服务日志

depends_on 只控制启动顺序,不保证依赖服务已经”就绪”,如果 app 启动比 MySQL 快,仍然可能连接失败。生产环境建议在应用层做重连逻辑。


踩坑记录

1. 宿主机无法访问容器里的服务(网卡地址冲突)

现象curl http://127.0.0.1:8080 返回 Connection reset by peer

原因:Docker 默认网桥 docker0 的 IP 与宿主机其他网卡地址冲突

修复步骤

sudo service docker stop
sudo ip link set dev docker0 down
sudo brctl delbr docker0 # brctl 需要安装: yum install -y bridge-utils
sudo iptables -t nat -F POSTROUTING
sudo brctl addbr docker0
sudo ip addr add 192.168.200.1/24 dev docker0
sudo ip link set dev docker0 up

# 修改 daemon.json 固定 docker0 的网段
vi /etc/docker/daemon.json
# 添加 "bip": "192.168.200.1/24"

systemctl restart docker

2. Docker 映射端口后防火墙关了仍然能从外网访问

原因:Docker 在 iptables 里直接写了规则(绕过了 firewalld),即使 firewalld 关了对应端口,Docker 映射的端口仍然对外开放。

# 查看 docker 在 iptables 里的规则
iptables -nL DOCKER

# 如果要关闭某个映射端口的公网访问,需要手动删除 iptables 规则
iptables -D DOCKER 2

更好的做法:只暴露需要对外的端口,敏感服务(如 MySQL、Redis)不映射到公网 IP 或绑定 127.0.0.1:

docker run -p 127.0.0.1:3306:3306 --name mysql ...

3. 多次构建镜像后磁盘爆满(vfs Storage Driver 问题)

现象:CI/CD 发版时构建失败,提示磁盘不足。查看发现 /home/docker/vfs 目录占用将近 200G。

原因:Docker 的 Storage Driver 设置为了 vfs,此模式不使用 Copy-on-Write,每个镜像层都是完整副本,构建次数多了磁盘自然爆掉。

docker info | grep "Storage Driver"
# Storage Driver: vfs ← 问题所在

解决

# 临时清理
docker system prune -a

# 修改为 devicemapper(支持 CoW,节省空间)
vi /etc/docker/daemon.json
# 添加 "storage-driver": "devicemapper"

systemctl restart docker

为什么同一批机器会有不同的 Storage Driver?后来发现是 Docker 版本差异导致的默认值不同(20.x 和 23.x 默认策略不同)。环境初始化脚本里固定 storage-driver 可以避免这类问题。


小结

折腾了大半年 Linux 和 Docker 之后,最大的感受是:Docker 解决的不是”会不会部署”的问题,解决的是”部署一致性”的问题

在没有 Docker 之前,测试环境和生产环境经常因为环境差异出 bug,有了 Docker 之后这类问题少了很多。虽然容器本身也会有坑,但那是更高层次的问题了。