引言
- 项目概况:本篇文章着重介绍我使用Docker在服务器上运行起我的练习网站项目——一个简易CMS。需要部署的项目有三个:一个Node.js编写的后端,一个前端管理项目,一个前端新闻展示项目。
- Docker 优势:
- 环境一致性:
- Docker 提供了一个一致的运行环境,无论是在开发、测试还是生产环境中,应用程序都运行在相同的容器中。这消除了“在我机器上可以正常运行”的问题。
- 依赖管理:
- Docker 容器包含了应用所需的所有依赖项,因此不需要担心在不同服务器上安装和配置不同版本的依赖。
- 易于扩展和缩放:
- 使用 Docker,可以轻松地在多个容器实例之间扩展应用程序。这对于处理高流量和负载平衡非常有用。
- 快速部署:
- Docker 容器启动速度快,可以在几秒钟内启动新实例,这对于部署更新和快速恢复服务非常有优势。
- 资源隔离:
- Docker 容器提供了资源隔离,使得每个应用程序都在自己的容器中运行,不会干扰其他应用程序。这也增加了安全性。
- 版本控制和回滚:
- Docker 镜像可以进行版本控制,允许轻松回滚到先前的版本。这对于处理更新问题和快速恢复旧版本非常有用。
- 跨平台兼容性:
- Docker 可以在任何支持 Docker 的平台上运行,包括本地开发环境、测试服务器和云环境,提供了跨平台的兼容性。
- 简化的 CI/CD 工作流:
- Docker 容器可以与持续集成/持续部署(CI/CD)工具无缝集成,简化了构建、测试和部署的自动化流程。
- 开发和生产环境的隔离:
- Docker 允许在同一台机器上运行多个不同的环境,帮助开发者在本地测试不同的应用配置,而不会影响生产环境。
准备工作
- 云服务器:我选择了京东云-轻服务器。选择了2核、2G内存配置,这是最便宜的服务器,毕竟学习使用,能用就行。
- 服务器系统:选择CentOS 7.9
Docker安装
添加 Docker 仓库国内源:
// 这样下载docker时能快很多
sudo yum-config-manager --add-repo https://download.docker.com+https://mirrors.tuna.tsinghua.edu.cn/docker-ce
安装 Docker 引擎:
sudo yum install docker-ce docker-ce-cli containerd.io
- Docker CE (Community Edition)
- 简介:Docker 的社区版,提供完整的容器化功能。
- 功能:包含 Docker 引擎,用于创建和管理容器。
- 用途:适合开发和测试环境。
- Docker CE CLI
- 简介:Docker 的命令行工具。
- 功能:用于执行 Docker 命令(如启动、停止容器)。
- 用途:通过命令行管理 Docker 容器。
- containerd.io
- 简介:容器运行时,管理容器生命周期。
- 功能:负责容器的创建、执行和管理。
- 用途:Docker 和 Kubernetes 的核心组件。
设置Docker镜像源:
Docker基本认识
- 构建镜像:
- 使用
Dockerfile
定义应用程序的环境、依赖和配置。通过docker build
命令,可以从Dockerfile
构建出一个 Docker 镜像。
- 使用
- 管理镜像:
- 构建好的镜像可以通过
docker push
命令上传到 Docker 仓库(例如 Docker Hub),方便共享和分发。 - 也可以使用
docker pull
命令从仓库中拉取镜像,获取其他开发者或团队发布的应用环境。 - 镜像可以通过
docker save
命令保存为.tar
文件,便于备份或传输。 - 使用
docker load
命令可以从.tar
文件加载镜像,恢复镜像到 Docker 环境中。
- 构建好的镜像可以通过
- 运行容器:
- 使用
docker run
命令可以将镜像实例化为一个运行中的容器。容器是镜像的可执行实例,包含应用程序及其运行时环境。
- 使用
- 管理容器:
- 如果对运行中的容器进行了修改,可以使用
docker commit
命令将这些更改保存为一个新的镜像,便于后续使用或分发。
- 如果对运行中的容器进行了修改,可以使用
Docker常用命令
// 启动docker
sudo systemctl start docker
# 镜像相关命令
// 列出本地镜像
docker images
docker pull <image_name>
docker rmi <image_name_or_id>
# 容器相关命令
// 列出正在运行的容器
docker ps
// 列出所有容器(包括已停止的)
docker ps -a
// 启动容器
docker start <container_id_or_name>
// 停止容器
docker stop <container_id_or_name>
// 删除容器
docker rm <container_id_or_name>
// 运行一个新容器
docker run -d -p 80:80 <image_name>
# 容器管理
// 进入正在运行的容器
docker exec -it <container_id_or_name> /bin/bash
// 查看容器日志:
docker logs <container_id_or_name>
#网络和卷
// 使用 docker-compose 来管理多个服务
docker compose -f /var/www/cms/cms-server-node/docker-compose.yml up -d
docker compose -f /var/www/cms/cms-server-node/docker-compose.yml down -d
// 重新构建服务
docker compose -f /var/www/cms/cms-server-node/docker-compose.yml up -d --build
方案设计
如下,启动5个容器,Nginx容器负责接收用户请求,将/api开头的请求代理到Nodejs服务容器上,将/admin开头的请求代理到CMS后台管理项目容器上,将/开头的请求代理到CMS新闻展示项目容器上。
项目结构
三个独立的项目放在/cms文件夹下。每个项目下放一个Dockerfile文件。在后端nodejs服务项目下放总体构建的docker-compose.yml文件和用于反向代理的nginx.conf
/cms
│
├── /cms-front-vue
│ ├── 前端后台管理项目源码
│ ├── Dockerfile
│
├── /cms-web-vue
│ ├── 前台新闻网站项目源码
│ ├── Dockerfile
│
├── /cms-server-node
├── 后端nodejs服务项目源码
├── Dockerfile
├── docker-compose.yml
├── nginx.conf
部署实施
- 上传项目:将本地/cms文件夹上传到服务器/var/www目录下。
rsync -av -e "ssh -i ~/.ssh/jdc_ssh_key.pem" --exclude='node_modules' /本地路径/cms root@116.198.34.50:/var/www
scp和rsync命令都可以上传文件到服务器,但是rsync命令更强大,比如增加 --exclude='node_modules'参数可以不上传node_modules文件夹,rsync每次上传只上传更改的文件,上传效率更高。
- 编写 Dockerfile
- 前端后台管理项目/前端新闻项目
两个vue.js的Dockerfile趋同,只展示一个。
# 使用 Node.js 18 作为构建阶段的基础镜像
FROM node:18 AS build
# 设置工作目录
WORKDIR /app
# 复制 package.json 和 package-lock.json(如果有)
COPY package*.json ./
# 设置 npm 镜像源
RUN npm config set registry https://registry.npmmirror.com
# 安装项目依赖
RUN npm install
# 复制项目文件到容器中
COPY . .
# 构建项目
RUN npm run build-only
# 使用 Nginx 作为生产阶段的基础镜像
FROM nginx:alpine
# 复制构建的文件到 Nginx 默认的静态文件目录
COPY --from=build /app/dist /usr/share/nginx/html
# 暴露端口
EXPOSE 80
# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]
- Nodejs服务端项目
# 使用 Node.js 18 作为基础镜像
FROM node:18
# 设置工作目录
WORKDIR /app
# 复制 package.json 和 package-lock.json(如果存在)
COPY package*.json ./
# 设置 npm 镜像源
RUN npm config set registry https://registry.npmmirror.com
# 安装应用程序依赖
RUN npm install
# 如果你在生产环境中使用,可以使用以下命令代替
# RUN npm ci --only=production
# 复制应用程序的源代码到工作目录
COPY . .
# 暴露应用程序运行的端口(假设应用程序在 3000 端口上运行)
EXPOSE 8080
# 定义容器启动时要运行的命令
CMD ["npm", "start"]
- 配置 Docker Compose
- 编写
docker-compose.yml
文件,定义后端、两个前端、反向代理和数据库服务。
- 编写
services:
frontend-manage:
build:
context: ./../cms-front-vue
dockerfile: Dockerfile
depends_on:
- backend
environment:
- NODE_ENV=production
frontend-web:
build:
context: ./../cms-web-vue
dockerfile: Dockerfile
depends_on:
- backend
environment:
- NODE_ENV=production
backend:
build:
context: ./
dockerfile: Dockerfile
depends_on:
- db
db:
image: mysql:8.0
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
MYSQL_DATABASE: cms
MYSQL_USER: admin
MYSQL_PASSWORD: 12345678
volumes:
- mysql-data:/var/lib/mysql
- ./db/backup.sql:/docker-entrypoint-initdb.d/backup.sql
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- frontend-manage
- frontend-web
- backend
volumes:
mysql-data:
- 配置 Nginx
在docker-compose.yml配置中,三个容器可以通过服务名来进行通信,所以在Nginx配置中,我们指定这三个服务名,Nginx容器就可以代理到它们。
worker_processes 1; #全局块
events {
worker_connections 1024;
}
http {
upstream backend {
server backend:8080;
}
upstream frontend_manage {
server frontend-manage:80;
}
upstream frontend_web {
server frontend-web:80;
}
server {
listen 80;
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 去掉 /api 前缀
rewrite ^/api/(.*)$ /$1 break;
}
location /admin {
proxy_pass http://frontend_manage/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://frontend_web;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
- 启动服务
在服务器上执行以下命令,我们就将后端、两个前端、反向代理和数据库服务都启动起来啦。
// 使用 docker-compose 来管理多个服务
docker compose -f /var/www/cms/cms-server-node/docker-compose.yml up -d
// 每次更改代码后,重新构建服务
docker compose -f /var/www/cms/cms-server-node/docker-compose.yml up -d --build
总结
回顾部署过程。我们学习到了Docker的基本用法,已经如何用docker在服务器上运行我们的全栈项目。还有一点不足是上传项目使用的比较原始的上传文件方式,现在都流行CI/CD,也就是代码推送到git服务器自动集成和部署,下一步打算研究这部分内容。
附录
源码地址:
CMS前端前台: github.com/double-chen…
CMS前端后台: github.com/double-chen…
CMS后端服务: github.com/double-chen…
项目展示:文章的重点不在于这个练习项目,随便展示两个页面。
后台文章列表页
新闻网站首页