Docker项目部署详解教程
引言
Docker作为现代软件开发和部署的重要工具,极大地改变了应用交付的方式。本教程将详细讲解如何使用Docker部署项目,从基础概念到实际操作,帮助开发者掌握容器化部署技术。
1. Docker基础概念与价值
1.1 Docker与传统虚拟机的对比
Docker容器技术相比传统虚拟机有显著优势:
特性 | 容器 | 虚拟机 |
---|---|---|
启动时间 | 秒级 | 分钟级 |
存储空间 | 通常为MB级别 | 通常为GB级别 |
性能表现 | 接近原生性能 | 性能有所损耗 |
资源利用率 | 单机可支持上千个容器 | 一般支持几十个虚拟机 |
操作系统 | 共享宿主机内核 | 需要完整的Guest OS |
1.2 Docker核心概念
Docker生态系统中有三个基本概念,理解它们是掌握Docker的关键:
-
镜像(Image):只读的模板,包含运行应用所需的程序、依赖、配置等。镜像构建完成后是不可变的,仅用于启动容器。
-
容器(Container):镜像的运行实例,可以被启动、停止、删除。每个容器彼此独立且安全隔离,可以理解为一个轻量级的操作系统,专门用来运行特定应用。
-
仓库(Repository):存储和分发Docker镜像的服务,如Docker Hub(类似于GitHub),用户可以在上面分享和获取镜像。
Docker的工作流程是:通过Dockerfile定义镜像的构建过程→构建镜像或从仓库拉取镜像→使用镜像创建并运行容器。
1.3 为什么要使用Docker
Docker为现代开发与部署带来了多方面的优势:
- 环境一致性:彻底解决"在我的电脑上能运行"的问题,确保开发、测试和生产环境完全一致
- 版本隔离:在同一台服务器上运行不同版本的应用(如不同版本的Node.js、Python等)
- 服务迁移:容器化后的应用可以轻松地在不同服务器间迁移,无需担心环境差异
- 资源效率:比传统虚拟机更轻量,资源占用更小
- 标准化交付:提供统一的应用交付标准,简化部署流程,减少人为错误
- 微服务友好:特别适合微服务架构,每个服务独立容器化
虽然前端开发可能日常工作中较少直接接触容器化技术,但了解Docker已成为全栈开发者的基本素养,能显著提升项目的可维护性和部署效率。
2. Docker环境搭建
2.1 Docker安装
Docker分为稳定版(stable)、测试版(test)和每日构建版(nightly)三个更新频道。根据不同操作系统,安装方法略有不同:
Linux安装
# Ubuntu系统
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
# CentOS系统
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
Windows/Mac安装
直接下载并安装Docker Desktop,该软件提供了图形界面,更易于管理容器和镜像。
2.2 验证安装
安装完成后,通过以下命令验证安装是否成功:
docker --version
docker info
如果安装正确,可以尝试运行一个简单的Nginx容器测试:
docker run -d -p 80:80 --name webserver nginx
成功后,访问http://localhost
应该能看到Nginx的欢迎页面。测试完成后可以停止并删除容器:
docker stop webserver
docker rm webserver
2.3 配置镜像加速
在中国大陆使用Docker时,从官方仓库拉取镜像可能较慢,可以配置国内镜像源加速:
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com",
"https://docker.1ms.run",
"https://docker.1panel.dev"
]
在Docker Desktop中,可以通过Settings/Preferences → Docker Engine,添加上述配置并应用重启。
3. 前端项目容器化
3.1 准备工作
要将Vue前端项目容器化,需要创建以下三个关键文件:
- Dockerfile - 定义如何构建Docker镜像
- .dockerignore - 指定构建时要排除的文件
- nginx配置文件 - 配置前端应用的web服务器
Dockerfile
# 多阶段构建:第一阶段 - 构建应用
FROM node:16.14.2 AS builder
# 设置工作目录
WORKDIR /app
# 先复制package.json文件
COPY package*.json ./
# 安装依赖
RUN yarn install
# 复制所有源代码
COPY . .
# 构建应用
RUN npm run build
# 多阶段构建:第二阶段 - 配置Nginx服务
FROM nginx:stable-alpine
# 从构建阶段复制构建结果到Nginx目录
COPY --from=builder /app/dist /usr/share/nginx/html
# 复制自定义Nginx配置
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
# 暴露80端口
EXPOSE 80
# 启动Nginx服务
CMD ["nginx", "-g", "daemon off;"]
这里使用了Docker的多阶段构建功能,可以有效减小最终镜像的大小:
- 第一阶段使用Node.js环境构建应用
- 第二阶段仅包含Nginx和构建结果,舍弃了Node.js环境和源代码
.dockerignore文件
.DS_Store
node_modules
/dist
# 本地环境文件
.env.local
.env.*.local
# 日志文件
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# 编辑器目录和文件
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
dist.zip
.git
.dockerignore
文件类似于.gitignore
,用于指定构建Docker镜像时要排除的文件和目录,这可以提高构建速度并减小镜像体积。
Nginx配置文件
server {
listen 80;
server_name localhost;
# 静态资源配置
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html; # 支持SPA应用路由
}
# 错误页面配置
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
这个配置文件设置了Nginx如何提供前端应用。特别是try_files $uri $uri/ /index.html
这一行配置支持了单页应用(SPA)的路由功能。
3.2 构建前端项目镜像
创建好上述文件后,在项目根目录执行以下命令构建Docker镜像:
docker build -t frontend-app:v1 .
其中:
-t frontend-app:v1
给镜像命名为frontend-app
,标签为v1
.
表示使用当前目录作为构建上下文
构建成功后,可以通过以下命令查看已创建的镜像:
docker images
3.3 运行前端容器
使用以下命令从刚才构建的镜像创建并运行容器:
docker run -d -p 3000:80 --name frontend-container frontend-app:v1
参数说明:
-d
表示在后台运行容器-p 3000:80
将主机的3000端口映射到容器的80端口--name frontend-container
给容器命名为frontend-container
现在可以通过访问http://localhost:3000
查看部署的前端应用。
4. 后端服务容器化
4.1 创建简易Node.js后端服务
首先创建一个简单的Node.js后端服务作为示例:
const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
const app = new Koa();
const router = new Router();
app.use(bodyParser());
// 通用响应处理函数
async function handleRequest(ctx) {
try {
ctx.body = {
"code": 200,
"status": 1,
"message": "ok",
"data": {
"userRole": 1,
"userId": "000000000000000001",
"companyId": "1000000000000000001",
"userName": "test",
}
}
} catch(error) {
ctx.status = 500;
ctx.body = {
error: 'Internal Server Error'
};
}
}
// 登录接口
router.post('/Account/SignIn', async (ctx) => {
await handleRequest(ctx);
});
// 使用路由中间件
app.use(router.routes());
app.use(router.allowedMethods());
// 启动服务器
const port = process.env.PORT || 1000;
app.listen(port, () => {
console.log(`服务器运行在端口 ${port}`);
});
创建package.json文件:
{
"name": "backend-service",
"version": "1.0.0",
"type": "commonjs",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js"
},
"dependencies": {
"koa": "^2.15.2",
"koa-bodyparser": "^4.4.1",
"koa-router": "^12.0.1"
},
"devDependencies": {
"nodemon": "^3.1.9"
}
}
4.2 后端服务Dockerfile
为后端服务创建Dockerfile:
# 使用Node.js官方镜像作为基础镜像
FROM node:16.14.2-alpine
# 设置工作目录
WORKDIR /app
# 复制package.json和package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm ci --production
# 复制项目文件
COPY . .
# 设置环境变量
ENV PORT=1000
ENV NODE_ENV=production
# 暴露端口
EXPOSE 1000
# 启动服务
CMD ["node", "app.js"]
使用alpine版本的Node.js镜像可以大幅减小镜像体积。npm ci --production
命令只安装生产环境依赖,进一步减小镜像大小。
4.3 构建并运行后端服务镜像
构建后端服务镜像:
docker build -t backend-service:v1 -f Dockerfile.backend .
运行后端服务容器:
docker run -d --name backend-container -p 9000:1000 backend-service:v1
可以使用Postman或curl测试后端服务是否正常工作:
curl -X POST http://localhost:9000/Account/SignIn
5. 配置Nginx反向代理
在生产环境中,通常需要使用Nginx反向代理将前端应用的API请求转发到后端服务。这一步配置至关重要,可以解决跨域问题并提供统一的访问入口。
5.1 查找Docker容器IP
首先需要获取后端服务容器的IP地址:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' backend-container
或者通过Docker Desktop图形界面查看容器详情。
5.2 修改Nginx配置实现反向代理
更新前端容器中的Nginx配置文件:
server {
listen 80;
server_name localhost;
# 静态资源配置
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# API代理配置 - 通用API路径
location /api/ {
rewrite /api/(.*) /$1 break; # 路径重写
proxy_pass http://172.17.0.3:1000; # 后端服务容器的IP和端口
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 特定接口路径
location /Account/ {
proxy_pass http://172.17.0.3:1000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 错误页面配置
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
5.3 应用新的Nginx配置
有两种方法可以应用新的Nginx配置:
方法1:进入容器修改配置
# 进入前端容器
docker exec -it frontend-container bash
# 编辑配置文件
vi /etc/nginx/conf.d/default.conf
# 测试Nginx配置是否正确
nginx -t
# 重新加载Nginx配置
nginx -s reload
如果容器内没有vi编辑器,可以先安装:
# Debian/Ubuntu系统
apt-get update && apt-get install -y vim
# Alpine系统
apk add --no-cache vim
方法2:重建前端容器
如果不想直接修改容器内的文件,可以更新本地的Nginx配置文件,然后重新构建前端镜像:
- 在项目中修改
nginx/default.conf
- 重新构建前端镜像:
docker build -t frontend-app:v2 .
- 停止并删除旧容器:
docker stop frontend-container && docker rm frontend-container
- 运行新容器:
docker run -d -p 3000:80 --name frontend-container frontend-app:v2
5.4 改进方案:使用Docker网络
上述方法中直接使用容器IP存在问题,因为容器重启后IP可能会变化。更好的做法是使用Docker网络:
# 创建Docker网络
docker network create app-network
# 运行后端容器,连接到网络
docker run -d --name backend-container --network app-network backend-service:v1
# 运行前端容器,连接到网络
docker run -d -p 3000:80 --name frontend-container --network app-network frontend-app:v1
然后在Nginx配置中,可以直接使用容器名称作为主机名:
location /api/ {
rewrite /api/(.*) /$1 break;
proxy_pass http://backend-container:1000;
# ...其他配置...
}
6. 使用Docker Compose简化部署
为了简化多容器应用的部署和管理,可以使用Docker Compose。在项目根目录创建docker-compose.yml
文件:
version: '3'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "80:80"
depends_on:
- backend
networks:
- app-network
backend:
build:
context: ./backend
dockerfile: Dockerfile
environment:
- NODE_ENV=production
networks:
- app-network
networks:
app-network:
driver: bridge
使用Docker Compose可以一键启动整个应用:
docker-compose up -d
停止并移除所有容器:
docker-compose down
7. 常用Docker命令速查表
命令 | 说明 |
---|---|
docker build -t <镜像名>:<标签> . |
构建镜像 |
docker run -d -p <主机端口>:<容器端口> --name <容器名> <镜像名> |
创建并启动容器 |
docker ps |
查看运行中的容器 |
docker ps -a |
查看所有容器(包括已停止) |
docker images |
查看所有镜像 |
docker stop <容器ID/名称> |
停止容器 |
docker start <容器ID/名称> |
启动已停止的容器 |
docker rm <容器ID/名称> |
删除容器 |
docker rmi <镜像ID/名称> |
删除镜像 |
docker exec -it <容器ID/名称> bash |
进入容器内部 |
docker logs <容器ID/名称> |
查看容器日志 |
docker network create <网络名称> |
创建Docker网络 |
docker network ls |
列出所有网络 |
docker-compose up -d |
启动所有服务(后台运行) |
docker-compose down |
停止并删除所有服务 |
8. 最佳实践与优化建议
8.1 镜像优化
- 使用多阶段构建:分离构建环境和运行环境,减小最终镜像体积
- 使用Alpine基础镜像:Alpine Linux基础镜像通常只有几MB大小
- 合理组织Dockerfile指令:充分利用Docker的缓存机制
- 减少镜像层数:合并RUN指令,减少层数
- 使用.dockerignore排除不必要文件
8.2 安全与访问控制
- 不要在容器中运行应用为root用户
- 使用环境变量管理敏感信息
- 定期更新基础镜像以修补安全漏洞
- 限制容器资源使用
8.3 持久化数据
对于需要持久化的数据(如数据库、上传文件等),应使用Docker卷(volumes):
# 创建卷
docker volume create data-volume
# 使用卷运行容器
docker run -v data-volume:/app/data -d my-image
或在docker-compose.yml中:
services:
app:
# ...其他配置...
volumes:
- data-volume:/app/data
volumes:
data-volume:
9. CI/CD整合与自动化部署
将Docker与CI/CD工具(如GitHub Actions、GitLab CI、Jenkins)结合,可以实现代码提交后自动构建、测试和部署。
简单的GitHub Actions工作流示例:
name: Docker Build and Deploy
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
push: true
tags: username/app:latest
总结
本教程详细介绍了如何使用Docker部署前后端分离的Web应用,涵盖了Docker的基本概念、镜像构建、容器运行、网络配置等核心内容。通过容器化技术,可以显著提高应用的可移植性、可维护性和部署效率。
虽然对于纯前端开发来说,Docker可能不是日常必备技能,但随着DevOps理念的普及和全栈开发的趋势,了解并掌握Docker已成为现代开发者的基本素养。希望本教程能帮助您理解Docker的核心概念并在实际项目中应用容器化技术。
通过实践和不断优化,Docker不仅能简化您的部署流程,还能为应用提供更加稳定和一致的运行环境。