从零到部署:用 Vue 和 Express 实现迷你全栈电商应用(终篇)

从零到部署:用 Vue 和 Express 实现迷你全栈电商应用(终篇)

本文由图雀社区成员 mRc 写做而成,欢迎加入 图雀社区,一块儿创做精彩的免费技术教程,予力编程行业发展。

若是您以为咱们写得还不错,记得 点赞 + 关注 + 评论 三连,鼓励咱们写出更好的教程💪html

本篇文章是系列文章终篇,咱们将实现应用的部署,这篇教程将首先 Docker 来容器化你的应用,接着教你配置 MongoDB 的身份验证机制,给你的数据库添加一份安全守护,最后咱们会带你使用阿里的云的容器镜像服务将整个全栈应用部署到云端,使得你互联网上的用户能够访问你的网站,但愿这篇教程能解决长期困扰你的部署上云的问题!前端

欢迎阅读《从零到部署:用 Vue 和 Express 实现迷你全栈电商应用》系列:vue

应用容器化和 Docker Compose 配置

首先,若是你是一路跟着前面七篇教程一路敲过来的,那么将整个 Vue 前端项目放到新建立的 client 目录中,把整个 Express 后端项目放到新建立的 server 目录。若是你打算直接从这一篇开始学习部署,能够经过直接下载咱们提供的代码:node

git clone -b deploy-start https://github.com/tuture-dev/vue-online-shop-frontend.git
本文所涉及的源代码都放在了 Github 上,若是您以为咱们写得还不错,但愿您能给❤️ 这篇文章点赞+Github仓库加星❤️哦

在正式开始整个全栈应用的容器化以前,让咱们经过一张图来梳理一下思路:nginx

能够看到,咱们将使用三个容器:git

  • nginx 容器包括了 Nginx 服务器(存放了 Vue 框架实现的前端静态页面)
  • api 容器则包括了咱们用 Express 框架实现的 API 服务器
  • db 容器则是 MongoDB 数据库

咱们将整个应用经过 Nginx 实现反向代理。也就是说,用户想要访问咱们的应用,必须得先通过 Nginx。而且,全部获取前端资源的请求(例如 HTML、CSS、JS 等静态文件资源),Nginx 能够直接返回;全部获取 API 端点的请求(例如 /api/v1/products ),则将请求转交给给 API 服务器,而后再将 API 服务器返回的 JSON 数据返回给用户。github

这种经典的架构有如下优点:面试

  • 经过 Nginx 能够实现访问控制,过滤掉不合法的请求
  • 解决了先后端跨域的问题,由于前端页面和后端 API 都经过同一个端点访问
  • 整个应用架构对用户透明,能够轻松进行配置扩容,而且 Nginx 搭配了负载均衡

前端应用的容器化

首先,让咱们来容器化以前用 Vue 完成的前端应用。进入 client 目录,而后把 Vue 前端项目构建成静态页面:mongodb

npm run build
# 或者 yarn build

而后添加 client/config/nginx.conf 配置文件,代码以下:docker

server {
    listen 80;
    root /www;
    index index.html;
    sendfile on;
    sendfile_max_chunk 1M;
    tcp_nopush on;
    gzip_static on;

    location /api/v1 {
      proxy_pass http://api:3000;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

其中须要关注的就是两条 location 规则:

  • 若是访问 /api/v1 ,那么一概把请求传递给 api 容器
  • 若是访问 / ,则直接返回前端静态页面(index.html)

而后在前端访问后端的代码中,咱们须要作一点改变。打开 client/src/store/actions.js 文件,修改 API_BASE 常量以下:

// ...
import { Message } from 'element-ui';

const API_BASE = '/api/v1';

export const productActions = {
  // ...
};

export const manufacturerActions = {
  // ...
}

这样修改后,前端实际访问的 API 就取决于当前该页面的 URL,而不是硬编码的 localhost:3000

在作好准备工做后,咱们就要正式开始容器化了。

提示

若是你对 Docker 的核心概念不太熟悉,推荐学习一波咱们图雀社区的《一杯茶的时间,上手 Docker》,帮助你快速掌握镜像容器这两个重要概念,并手把手带你容器化第一个应用。

client 目录下建立 Dockerfile 文件,代码以下:

FROM nginx:1.13

# 删除 Nginx 的默认配置
RUN rm /etc/nginx/conf.d/default.conf

# 添加自定义 Nginx 配置
COPY config/nginx.conf /etc/nginx/conf.d/

# 将前端静态文件拷贝到容器的 /www 目录下
COPY dist /www

建立 client/.dockerignore 文件,确保在构建镜像时忽略掉 node_modules

node_modules

后端应用的容器化

容器化前端应用以后,接下来就开始准备后端应用的容器化。首先咱们要把硬编码的 MongoDB 链接字符串改为经过环境变量注入。修改 server/app.js 文件,代码以下:

// ...
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// Datbase connection here
mongoose.connect(process.env.MONGO_URI || `mongodb://localhost:27017/test`);

// ...

接着建立 server/Dockerfile ,代码以下:

FROM node:10

# 指定工做目录为 /usr/src/app,接下来的命令所有在这个目录下操做
WORKDIR /usr/src/app

# 将 package.json 拷贝到工做目录
COPY package.json .

# 安装 npm 依赖
RUN npm config set registry https://registry.npm.taobao.org && npm install

# 拷贝源代码
COPY . .

# 设置环境变量
ENV NODE_ENV=production
ENV MONGO_URI=mongodb://db:27017/test
ENV HOST=0.0.0.0
ENV PORT=3000

# 开放 3000 端口
EXPOSE 3000

# 设置镜像运行命令
CMD [ "node", "./bin/www" ]

和前端同样,建立 server/.dockerignore 文件,确保 node_modules 不会被打包到镜像中去:

node_modules

Docker Compose 配置

Docker Compose 是一个强大的多容器管理工具,经过一个 YAML 文件配置完成后,只须要一个命令就能够启动所有容器(服务)。在项目根目录建立 docker-compose.yml ,代码以下:

version: '3'

services: 
  db:
    image: mongo
    restart: always
  api:
    build: server
    restart: always
  nginx:
    build: client
    restart: always
    ports:
      - 8080:80

能够看到,咱们建立了三个 service ,对应咱们的三个容器(dbapinginx ):

  • db 服务指定镜像为 mongo ,而后设置 restart: always ,确保因某种缘由中止后自动重启
  • api 服务指定镜像经过 server 目录构建,端口映射规则为 3000:3000
  • nginx 服务指定镜像经过 client 目录构建,端口映射规则为 8080:80
注意

在指定每一个 service 时,若是使用 image 字段指定镜像,那么就会直接从镜像仓库拉取该镜像,这里咱们的 db 服务就是如此。若是使用 build 字段指定镜像,则会根据指定的目录下的 Dockerfile 文件构建镜像,例如这里咱们指定 serverclient 目录分别构建 apinginx

提示

Docker Compose 默认为全部服务建立了一个 Docker 网络,使得容器之间能够经过服务发现的机制进行相互通讯(而不是经过固定 IP),这也就是为何在 Nginx 配置中能够直接指定 http://api:3000 ,以及将 MongoDB 链接字符串设置为 mongodb://db:27017/test 。若是想要透彻理解 Docker 网络,可浏览以前发布的《梦境亦相通:用 Network 实现容器互联》

一切就绪,咱们在电商根目录下经过一个命令实现整个应用的构建 + 运行:

docker-compose up --build

初次构建可能须要至关久的时间(拉取基础镜像),这时候不妨给本身点一杯咖啡☕️,静静等待结果的到来~ 若是你在控制台看到了以下输出,就表明镜像已经构建完毕了:

接着每一个镜像会输出各自的日志信息。咱们经过

docker ps

命令进一步确认三个容器的状态:

OK,咱们能够经过 localhost:8080 访问咱们的站点了!

而且,咱们还经过内网(例如同一 WiFi 下的其余设备)访问咱们的站点,经过查询本机的内网 IP(例如 192.168.1.1),而后在手机的浏览器里面输入这个 IP 地址,就能够经过 192.168.1.1:8080 访问。查询本机内网 IP 能够自行去搜索引擎找答案哦。

小结

在这一小节中,咱们学习了:

  • 经过 Nginx 容器提供前端静态页面,并将后端请求转发给 API 容器
  • 容器化后端应用,创建与数据库的链接
  • 经过 Docker Compose 一键构建和启动应用

配置 MongoDB 的身份验证

在以前的部署配置中,有一个重大的安全隐患:咱们的 MongoDB 数据库没有配置任何的身份验证措施,这意味着全部可以访问数据库的请求均可以对数据库做出任何修改!接下来,咱们就来搞定 MongoDB 的身份验证,为咱们的数据安全保驾护航。

修改 MongoDB 链接设置

首先,咱们修改 server/app.js 中的 MongoDB 链接设置,代码以下:

// ...

// Datbase connection here
mongoose.connect(process.env.MONGO_URI || `mongodb://localhost:27017/test`, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  user: process.env.MONGO_USER,
  pass: process.env.MONGO_PASSWORD,
});

// ...

四个选项的含义分别以下:

  • useNewUrlParser :使用新的 MongoDB 驱动 URL 解析器
  • useUnifiedTopology :使用新的链接管理引擎,可以大大提升链接的稳定性,支持重连
  • user :链接用户名,经过环境变量注入
  • pass :链接密码,经过环境变量注入

Dockerfile 中注入环境变量

而后在 server/Dockerfile 中加入这些环境变量:

// ...

# 设置环境变量
ENV NODE_ENV=production
ENV MONGO_URI=mongodb://db:27017/admin
ENV MONGO_USER=mongoadmin
ENV MONGO_PASSWORD=secret
ENV HOST=0.0.0.0
ENV PORT=3000

// ...

注意到咱们调整了 MONGO_URI ,把数据库从 test 设置为默认生成的 admin ,这是为了使用 admin 做为鉴权数据库(Authentication Database)。

Docker Compose 中配置初始密码

最后在 docker-compose.yml 里面为 db 服务添加初始密码环境变量:

// ...
  db:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: mongoadmin
      MONGO_INITDB_ROOT_PASSWORD: secret
  api:
    build: server
    restart: always
  // ...

OK,一切就绪,首先把以前建立的容器群完全删除:

docker-compose down --volumes

down 命令的含义就和以前的 up 恰好相反,把 up 建立的全部镜像、容器、网络、数据卷所有删除,而且咱们经过 --volumes 参数删除 MongoDB 容器建立的数据卷。

注意

若是不把以前 MongoDB 容器的数据卷删干净,接下来建立带有身份验证的 MongoDB 容器就会复用以前的数据卷,直接跳过初始化用户的过程(笔者在这个地方踩了接近两个小时的坑)。若是你担忧数据卷还没删干净,能够运行 docker volume prune

而后从新构建并开启容器群:

docker-compose up --build

这时候再检查咱们的应用(访问 localhost:8080 ),应该看到一切正常。不过一颗悬着的心终于放下了——此次咱们的数据库再也不处于“裸奔”状态了!

小结

这一节中,咱们完整地实践了一波如何为 MongoDB 容器配备身份验证。不过平心而论,咱们采用的方法仍是至关原始的,把机密信息明文写在代码文件中。在大型的容器编排系统中(例如 Kubernetes 和 Docker Swarm),都集成了完善的、企业级的机密信息管理方案。因为这一系列教程的入门性质,咱们就点到为止啦。

此外,咱们也没有讲 MongoDB 数据库备份和恢复的细节,若是想要了解和学习,能够阅读咱们以前的《拒绝删库跑路!上手 Docker 容器数据管理》

使用镜像仓库服务

到了这一步,实际上咱们已经能够轻松地进行应用部署了。经过 SSH(或其余方式)链接到远程主机后,而后运行如下命令:

# 把仓库抓下来
git clone https://github.com/tuture-dev/vue-online-shop-frontend.git
cd vue-online-shop-frontend

# 构建前端代码
cd client && npm install && npm run build && cd ..

# 经过 Docker Compose 启动全部容器,并进入守护态运行
docker-compose up -d --build

这个时候,经过远程主机的 IP(或域名)加上端口号(这里是 8080 ,固然你能够在 docker-compose.yml 中自行修改 nginx 服务的端口配置)。例如咱们远程主机的 IP 是 1.2.3.4 ,那么就能够经过 1.2.3.4:8080 访问咱们的网站啦!

实际上,咱们还能够经过一种更高效的方式进行镜像的分发与部署——云端的镜像仓库服务

Docker Hub 和镜像命名规则

实际上,Docker 公司已经作了一个叫 Docker Hub 的镜像仓库,提供了丰富的官方维护镜像,以及自定义镜像的存储和分发。咱们在平时用的镜像(例如 mongonginxnode 等)都是 Docker Hub 上的官方镜像(或者是其余代理加速器)。

镜像的命名规则以下:

<registry_name>/<username>/<image_name>

其中:

  • registry_name 表明镜像仓库的名称,若是省略的话就是 Docker Hub
  • username 表明镜像仓库的用户名,若是和 registry_name 一块儿省略的话就是 Docker 官方镜像
  • image_name 就是镜像名称

Docker Hub 虽然说是官方出品,但实际上存在如下问题:

  1. 免费用户支持 1 个私有镜像
  2. 上传和拉取的速度在国内不稳定
  3. 没有镜像安全扫描功能

而咱们接下来要体验的阿里的云镜像仓库服务则能很好地解决以上问题。

体验阿里的云镜像仓库服务

由于某些缘由,掘全不容许出现某云厂商的名称,因此如下都用阿里的云代称。

首先让咱们访问镜像仓库服务的官方网站,而后在产品列表中找到“镜像仓库服务”,点击开通。开通后进入控制台,建立镜像命名空间,以下图所示:

名称随意填写,这里咱们填的是 vue-online-shop。建立后以下图所示:

建立好命名空间后,就能够为咱们应用的每一个镜像(除了 MongoDB 数据库镜像)建立相应的镜像仓库。点击“建立镜像仓库”按钮,以下图所示:

第一步,填写镜像仓库相关信息:

第二步,选择代码源,这里咱们选择“本地仓库”:

建立好两个镜像仓库(apinginx)后,能够看到镜像列表以下:

OK,而后点击单个仓库的“管理”按钮,按照指示进行镜像的上传。在这里咱们贴一下示例代码(实际操做时按本身控制台的指示说明为准):

# 登陆阿里的云镜像仓库,aliyunUser 改为本身的帐户名
docker login --username=aliyunUser registry.cn-shanghai.aliyuncs.com

# 构建和推送 api 镜像
docker build -t registry.cn-shanghai.aliyuncs.com/vue-online-shop/api server
docker push registry.cn-shanghai.aliyuncs.com/vue-online-shop/api

# 构建和推送 nginx 镜像
docker build -t registry.cn-shanghai.aliyuncs.com/vue-online-shop/nginx client
docker push registry.cn-shanghai.aliyuncs.com/vue-online-shop/nginx

提示

实际部署时,建议给每一个镜像加上一个标签,推荐是当前 Git 提交的 Hash,例如:

docker build -t registry.cn-shanghai.aliyuncs.com/vue-online-shop/api:9ca500a server

在镜像推送完成后,咱们把 docker-compose.yml 中的 apinginx 服务改为使用云端镜像(下面是个人镜像仓库地址,记得改为你本身的喔):

// ...
      MONGO_INITDB_ROOT_USERNAME: mongoadmin
      MONGO_INITDB_ROOT_PASSWORD: secret
  api:
    image: registry.cn-shanghai.aliyuncs.com/vue-online-shop/api
    restart: always
  nginx:
    image: registry.cn-shanghai.aliyuncs.com/vue-online-shop/nginx
    restart: always
    ports:
      - 8080:80

搞定以后,咱们只需把这个 docker-compose.yml 文件放到远程主机上,而后在所在的目录开启 Docker Compose 容器群便可:

# 拉取全部镜像的最新版本
docker-compose pull

# 启动全部容器
docker-compose up -d

小结

在这一步中,咱们:

  • 首先了解了如何经过 Git 抓取代码的方式在远程主机上进行部署
  • 而后了解了 Docker Hub 及镜像命名的规则,并分析了一波 Docker Hub 的缺陷
  • 接着一步步带你体验和使用阿里的云镜像仓库服务,轻松实现镜像的分发与部署

在整整八篇教程后,咱们迷你全栈电商系列也进入尾声了。自 2019 年 12月 21日发布以来,历时 86天,期间广受读者喜好,也有人要求图雀社区快点更,这样能够看完整个系列好去面试,还有的人直接加了图雀社区的学习交流群,在里面讨论技术... 不管怎样,咱们衷心的但愿你,学习这系列教程是轻松愉悦的,能收获实际的知识。

终于,咱们的迷你全栈电商系列实战就到此为止啦🎉🎉,感谢一路看下来始终不离不弃、热爱学习的你😘!让咱们在接下来更精彩的文章中再见👋~

想要学习更多精彩的实战技术教程?来 图雀社区逛逛吧。

本文所涉及的源代码都放在了 Github 上,若是您以为咱们写得还不错,但愿您能给❤️这篇文章点赞+Github仓库加星❤️哦

相关文章
相关标签/搜索