Docker就是把你应用和它要的环境打包成一个箱子,到哪都能直接跑。以前最烦的就是“在我机器上好好的啊”,环境不一致简直是噩梦。
第一步:装Docker,我一开始就搞错了
别去看官方文档了,直接上命令:
“bash
Ubuntu/Debian
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
CentOS/RHEL
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum
一套环境打通所有流程"
style="max-width:100%;height:auto;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,.08);"
loading="lazy" width="800" height="500">
install docker-ce docker-ce-cli containerd.io
启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
验证安装
docker --version
docker run hello-world
`
装完先把自己加到docker用户组里,不然每次都得敲sudo,烦得很。`bash`
sudo usermod -aG docker $USER
newgrp docker
核心概念:镜像、容器、仓库
这三个词,我一开始也绕晕了。后来自己琢磨了个比喻:
- 镜像:就像炒菜的菜谱,是死的,只读的。
- 容器:照着菜谱炒出来的一盘菜,是活的,能跑能停。
- 仓库:就是个存菜谱的网站(Docker Hub),想吃什么去那找。
实战:把个Flask应用塞进Docker
我做了一个巨简单的Web应用,一步步把它打包成镜像。
项目结构就三文件:
``
my-flask-app/
├── app.py # Flask应用
├── requirements.txt # Python依赖
└── Dockerfile # 构建镜像的配方
app.py内容:
`python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello from Docker!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
`
requirements.txt:
``
flask==2.3.0
最关键的Dockerfile来了。为啥要这么写?因为每个指令都会多一层,层数越少镜像越小,构建越快。
`dockerfile
用轻量级基础镜像(为啥不用ubuntu?太沉了,1.2G变180M的秘密就在这)
FROM python:3.9-slim
设置工作目录,省的复制的时候路径乱套
WORKDIR /app
先复制依赖文件。这是个小技巧,为了利用缓存。只有这个文件变了,才重新下载依赖
COPY requirements.txt .
装依赖
RUN pip install --no-cache-dir -r requirements.txt
最后复制代码,因为代码经常改,放前面缓存就白费了
COPY app.py .
声明一下端口,实际映射还得靠运行时
EXPOSE 5000
启动命令
CMD ["python", "app.py"]
`
构建并运行:
`bash`
docker build -t my-flask-app:v1.0 .
docker run -d -p 5000:5000 --name my-app my-flask-app:v1.0
去浏览器打开http://localhost:5000,看到那句"Hello from Docker!",就成了。
第二个坑:镜像太大怎么办?
我一开始用python:3.9(带一堆系统工具),镜像1.2G,吓死人。后来换成python:3.9-slim,直接降到180M。如果你还不满意,可以用python:3.9-alpine,能压到50M以下。不过alpine版本有时候兼容性会有点小问题,我一般不推荐。
还有个绝招叫多阶段构建。比如编译型语言(Go、Java),先用个大镜像编译,再换个小的只放编译结果。
`dockerfile
第一阶段:编译
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
第二阶段:运行
FROM alpine:3.18
WORKDIR /app
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
`
最后的镜像只有十几兆。一开始觉得这设计反人类,后来发现,是真高效。
(核心示意:一张多阶段构建的流程图,左侧大镜像编译,右侧小镜像运行)
第三个坑:容器之间怎么说话?
实际项目不可能只跑一个容器。比如我有个Web应用要连MySQL,怎么搞?
方法一:用–link(这玩意儿已经废弃了,别用)
方法二:用Docker网络
我推荐方法二,建个自定义网络:
`bash
docker network create my-network
启动MySQL
docker run -d --name mysql-db \
--network my-network \
-e MYSQL_ROOT_PASSWORD=root123 \
-e MYSQL_DATABASE=myapp \
mysql:8.0
启动应用(连到同一个网络)
docker run -d -p 5000:5000 \
--name my-app \
--network my-network \
-e DB_HOST=mysql-db \
my-flask-app:v1.0
`
在app.py里连接数据库时,主机名直接填mysql-db(容器名)就行,Docker会自动帮你解析。
部署实战:用docker-compose一键启动
写个docker-compose.yml文件,世界安静了:
`yaml
version: '3.8'
services:
web:
build: .
ports:
- "5000:5000"
depends_on:
- db
environment:
- DB_HOST=db
- DB_USER=root
- DB_PASSWORD=root123
db:
image: mysql:8.0
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: myapp
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:
`
启动命令就一行:
`bash`
docker-compose up -d
从开发到部署,一套配置搞定所有环境。
第四个坑:数据说没就没
容器一删,数据就丢了,这设计真坑爹。其实有方案:卷(Volume)和绑定挂载。
`bash
用卷(推荐,Docker帮你管)
docker run -d -v mysql-data:/var/lib/mysql mysql:8.0
绑定挂载(适合开发时调试,能热加载代码)
docker run -d -v /host/path:/container/path my-app
`
卷的好处是跨容器共享、方便备份、性能好。绑定挂载适合开发时直接改代码看效果。
优化技巧:启动从45秒缩到8秒
镜像大了启动就慢,试试这几个:
文件,把node_modules、.git这些没用的排除掉,别分两行写,省得pip缓存一堆垃圾`dockerignore`
.git
node_modules
__pycache__
*.pyc
.env
监控和调试
常用命令,顺手记一下:
`bash
看日志
docker logs -f my-app
进容器里看看
docker exec -it my-app /bin/bash
看资源占用情况
docker stats
清理无用的镜像和容器,释放磁盘空间
docker system prune -a
`
(总结前示意:一张Docker命令速查表,包含构建、运行、网络、卷等关键命令)
最后说三个能立刻用的点
。对了,生产环境千万别图省事用root用户跑容器,记得加个USER appuser`限制权限。还有,镜像里别写死密码,用环境变量或者Docker Secrets。
行了,就这些,开干吧!