本文由图雀社区成员 mRc 写做而成,欢迎加入 图雀社区,一块儿创做精彩的免费技术教程,予力编程行业发展。若是您以为咱们写得还不错,记得 点赞 + 关注 + 评论 三连,鼓励咱们写出更好的教程💪html
本篇文章是系列文章终篇,咱们将实现应用的部署,这篇教程将首先 Docker 来容器化你的应用,接着教你配置 MongoDB 的身份验证机制,给你的数据库添加一份安全守护,最后咱们会带你使用阿里的云的容器镜像服务将整个全栈应用部署到云端,使得你互联网上的用户能够访问你的网站,但愿这篇教程能解决长期困扰你的部署上云的问题!前端
欢迎阅读《从零到部署:用 Vue 和 Express 实现迷你全栈电商应用》系列:vue
首先,若是你是一路跟着前面七篇教程一路敲过来的,那么将整个 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
这种经典的架构有如下优点:面试
首先,让咱们来容器化以前用 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 是一个强大的多容器管理工具,经过一个 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
,对应咱们的三个容器(db
,api
和 nginx
):
db
服务指定镜像为 mongo
,而后设置 restart: always
,确保因某种缘由中止后自动重启api
服务指定镜像经过 server
目录构建,端口映射规则为 3000:3000
nginx
服务指定镜像经过 client
目录构建,端口映射规则为 8080:80
注意在指定每一个 service 时,若是使用
image
字段指定镜像,那么就会直接从镜像仓库拉取该镜像,这里咱们的db
服务就是如此。若是使用build
字段指定镜像,则会根据指定的目录下的Dockerfile
文件构建镜像,例如这里咱们指定server
和client
目录分别构建api
和nginx
。提示
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 能够自行去搜索引擎找答案哦。
在这一小节中,咱们学习了:
在以前的部署配置中,有一个重大的安全隐患:咱们的 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
:链接密码,经过环境变量注入而后在 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.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 公司已经作了一个叫 Docker Hub 的镜像仓库,提供了丰富的官方维护镜像,以及自定义镜像的存储和分发。咱们在平时用的镜像(例如 mongo
、nginx
、node
等)都是 Docker Hub 上的官方镜像(或者是其余代理加速器)。
镜像的命名规则以下:
<registry_name>/<username>/<image_name>
其中:
registry_name
表明镜像仓库的名称,若是省略的话就是 Docker Hubusername
表明镜像仓库的用户名,若是和 registry_name
一块儿省略的话就是 Docker 官方镜像image_name
就是镜像名称Docker Hub 虽然说是官方出品,但实际上存在如下问题:
而咱们接下来要体验的阿里的云镜像仓库服务则能很好地解决以上问题。
由于某些缘由,掘全不容许出现某云厂商的名称,因此如下都用阿里的云代称。
首先让咱们访问镜像仓库服务的官方网站,而后在产品列表中找到“镜像仓库服务”,点击开通。开通后进入控制台,建立镜像命名空间,以下图所示:
名称随意填写,这里咱们填的是 vue-online-shop
。建立后以下图所示:
建立好命名空间后,就能够为咱们应用的每一个镜像(除了 MongoDB 数据库镜像)建立相应的镜像仓库。点击“建立镜像仓库”按钮,以下图所示:
第一步,填写镜像仓库相关信息:
第二步,选择代码源,这里咱们选择“本地仓库”:
建立好两个镜像仓库(api
和 nginx
)后,能够看到镜像列表以下:
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
中的 api
和 nginx
服务改为使用云端镜像(下面是个人镜像仓库地址,记得改为你本身的喔):
// ... 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
在这一步中,咱们:
在整整八篇教程后,咱们迷你全栈电商系列也进入尾声了。自 2019 年 12月 21日发布以来,历时 86天,期间广受读者喜好,也有人要求图雀社区快点更,这样能够看完整个系列好去面试,还有的人直接加了图雀社区的学习交流群,在里面讨论技术... 不管怎样,咱们衷心的但愿你,学习这系列教程是轻松愉悦的,能收获实际的知识。
终于,咱们的迷你全栈电商系列实战就到此为止啦🎉🎉,感谢一路看下来始终不离不弃、热爱学习的你😘!让咱们在接下来更精彩的文章中再见👋~
想要学习更多精彩的实战技术教程?来 图雀社区逛逛吧。本文所涉及的源代码都放在了 Github 上,若是您以为咱们写得还不错,但愿您能给❤️这篇文章点赞+Github仓库加星❤️哦