为什么你的项目总在“环境”上翻车?
相信不少开发者都有过这样的经历:本地跑得飞起的代码,一上服务器就报错。数据库版本不一致、依赖冲突、系统库缺失……这些“环境玄学”往往比写代码本身更让人头疼。而 Docker 的出现,彻底解决了这个问题。它把整个运行环境打包成一个“集装箱”,无论你在哪里运行,结果都是一致的。
今天,站长就带你从零开始,手把手完成一次完整的 docker 部署 实战。读完这篇文章,你不仅能掌握部署的完整流程,还能避开那些新手最容易踩的坑。
第一步:准备工作——选服务器与装 Docker
在开始之前,你需要一台云服务器。这里笔者建议选择主流的云服务商(如阿里云、腾讯云、华为云等),系统推荐 Ubuntu 20.04 或 CentOS 7.x。购买时注意选择靠谱的配置,2核4G 起步对于大多数中小型项目已经足够。
连接上服务器后,我们开始安装 Docker。执行以下命令(以 Ubuntu 为例):
# 更新软件源
sudo apt update
# 安装依赖包
sudo apt install apt-transport-https ca-certificates curl software-properties-common
# 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 设置稳定版仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
# 启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
# 验证安装
sudo docker --version
看到版本号输出,说明 Docker 安装成功。记得将当前用户加入 docker 组,避免每次使用 sudo:sudo usermod -aG docker $USER,然后重新登录。
第二步:编写 Dockerfile——把你的应用“装进箱子”
假设我们有一个基于 Node.js 的 Express 应用。在项目根目录下创建 Dockerfile 文件,内容如下:
# 基于官方 Node.js 镜像
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 先复制 package.json,利用 Docker 缓存层
COPY package.json ./
# 安装依赖
RUN npm install --production
# 复制项目代码
COPY . .
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "server.js"]
关键点解析:
- 选择 Alpine 版本:镜像体积小,部署更快。
- 分层构建:先复制 package.json 再安装依赖,如果源码改了但依赖没变,Docker 会直接使用缓存层,大幅缩短构建时间。
- EXPOSE 只是声明:真正暴露端口需要在运行容器时用
-p参数指定。
如果你用的是其他语言(如 Python、Java、Go),原理完全一样,只是基础镜像和命令不同。比如 Python 项目:
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
第三步:构建镜像与运行容器
在项目根目录下执行:
# 构建镜像,-t 指定标签名
docker build -t my-app:v1 .
# 查看本地镜像
docker images
构建成功后,运行容器:
# 后台运行,将容器的 3000 端口映射到宿主机的 8080 端口
docker run -d --name my-app-container -p 8080:3000 my-app:v1
# 查看运行中的容器
docker ps
# 查看日志
docker logs my-app-container
此时,访问 http://你的服务器IP:8080 就能看到你的应用了。
常见问题:
- 端口被占用:检查
sudo lsof -i:8080,换一个端口即可。 - 容器启动后立即退出:用
docker logs 容器ID查看错误日志,通常是代码或配置问题。
第四步:使用 Docker Compose 管理多服务
如果你的项目需要数据库、缓存等配套服务(比如 Node.js + MySQL + Redis),手动管理多个容器会很麻烦。这时 Docker Compose 就派上用场了。
在项目目录下创建 docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "8080:3000"
depends_on:
- db
- redis
environment:
- DB_HOST=db
- REDIS_HOST=redis
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: myapp
volumes:
- db_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
db_data:
然后一键启动所有服务:
docker-compose up -d
停止和删除:docker-compose down。注意,volumes 配置了数据持久化,即使容器删除,数据库文件也不会丢失。
第五步:生产环境优化与安全加固
本地跑通了,不代表生产环境就稳了。以下是站长总结的几个关键优化点:
1. 镜像瘦身
使用多阶段构建,只保留最终产物。例如 Go 项目:
# 第一阶段:编译
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o main .
# 第二阶段:运行
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/main .
CMD ["./main"]
这样最终镜像只有几十 MB,而不是几百 MB。
2. 使用非 root 用户
默认容器以 root 运行,存在安全隐患。在 Dockerfile 中添加:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
3. 日志管理
限制日志文件大小,防止撑爆磁盘:
docker run -d --log-opt max-size=10m --log-opt max-file=3 my-app:v1
4. 健康检查
在 Dockerfile 或 docker-compose.yml 中添加健康检查:
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
5. 资源限制
防止单个容器占用全部资源:
docker run --memory="512m" --cpus="1.0" my-app:v1
避坑指南:新手最容易翻车的 3 个点
| 坑 | 表现 | 解决方法 |
|---|---|---|
| 时区问题 | 日志时间比实际晚 8 小时 | 在 Dockerfile 中添加 ENV TZ=Asia/Shanghai |
| 文件权限 | 容器内无法写入挂载目录 | 确保宿主机目录权限为 777,或使用 user: 1000:1000 指定 UID |
| 网络连接 | 容器无法访问宿主机服务 | 使用 host.docker.internal 代替 localhost |
部署后的下一步
当你的应用稳定运行后,可以考虑配置 域名 和 HTTPS。购买一个正规域名,使用 Nginx 反向代理到 Docker 容器,再用 Let’s Encrypt 免费申请 SSL 证书。这样你的服务就真正“上线”了,用户可以通过域名安全访问。
如果你需要长期运营,建议选择靠谱的云服务商,购买带数据盘的服务器,并将 Docker 的数据目录迁移到数据盘上,避免系统盘被写满导致服务异常。
Docker 部署的本质是“一次构建,到处运行”。掌握这套流程后,无论你是部署个人博客、API 服务还是微服务架构,都能游刃有余。希望这篇文章能帮你少走弯路,顺利把项目跑在云端。