docker 部署 nodejs express demo

hxy    2021-04-02 09:12

需求描述:基于docker容器,搭建并部署前后端分离的 vue+express 登录 demo.

开发环境:
  • 系统:Windows 10 64位教育版
  • nodejs: v14.15.5
  • 后端:express 4.17.1
  • 前端:vue 2.5.2


1. 准备环境。
常规操作安装express 项目生成器,并安装依赖库,启动项目: 

npm install express-generator -g
express -e demo
cd demo
npm install
npm start


####安装热更新,方便部署
$ npm install -g supervisor
启动项目
$ supervisor bin/www

后端项目搭建完成。接下来新建路由:
在 demo/routes目录下新建 api.js
粘贴如下内容:
var express = require("express");
var router = express.Router();

/* POST login page. */
router.post("/login", function (req, res, next) {
  //获取请求传递过来的数据
  var currentData = req.body.params;
  console.log("currentdata:", currentData);
  //模拟数据库查询判断,设置返回数据str
  if (currentData.username == "1" && currentData.password == "1") {
    var str = {
      result: {
        username: currentData.username,
      },
      tip: "登录成功!",
      code: 1,
    };
  } else {
    var str = { result: {}, tip: "用户名或密码错误!", code: 0 };
  }
  res.writeHead(200, {
    "Content-Type": "text/plain",
    charset: "utf-8",
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "PUT,POST,GET,DELETE,OPTIONS",
  }); //可以解决跨域的请求
  str = JSON.stringify(str);
  //打印要返回的数据str
  console.log("resData:  " + str);
  res.end(str);
});

module.exports = router;
用于处理前端登录请求。
之后,在demo/app.js 中添加路由规则:
var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");

var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var apiRouter = require("./routes/api");

var app = express();

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

// 自定义跨域中间件
var allowCors = function (req, res, next) {
  res.header("Access-Control-Allow-Origin", req.headers.origin);
  res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS");
  res.header("Access-Control-Allow-Headers", "Content-Type");
  res.header("Access-Control-Allow-Credentials", "true");
  next();
};
app.use(allowCors); //使用跨域中间件

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));

app.use("/", indexRouter);
app.use("/users", usersRouter);
app.use("/api", apiRouter); // 登录处理

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;
保存即可。

2. vue 前端项目搭建
先安装脚手架:

npm install -g vue-cli
vue init webpack test

之后会提示:

? Project name test
? Project description A Vue.js project
? Author neuhxy
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? Yes
? Should we run `npm install` for you after the project has been created? (recommended) npm

   vue-cli · Generated "test".


修改前端页面的 App.vue,修改后内容如下:
<template>
  <div id="app">
    <!-- <img src="./assets/logo.png" /> -->
    <h3>化检验数据录入小工具</h3>
    <input type="text" name="username" v-model="username" /> <br /><br />
    <input type="password" name="password" v-model="password" /><br /><br />
    <button @click="login">登录</button>
    <br />
    <p>{{ msg }}</p>

    <!-- <router-view /> -->
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "App",
  data() {
    return {
      username: "",
      password: "",
      msg: "",
    };
  },
  methods: {
    login: function () {
      axios
        .post("http://localhost:3000/api/login", {
          params: {
            username: this.username,
            password: this.password,
          },
        })
        .then((res) => {
          //data属性名称是固定的,用于获取后台响应的数据
          console.log(res.data);
          this.msg = res.data.tip;
        });
    },
  },

  watch: {
    username: function (username) {
      this.username = username;
    },
    password: function (password) {
      this.password = password;
    },
  },
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
需要安装 axios 用于后台数据的交互:
yarn add axios

大概需要几分钟,安装之后,打开 lcoalhost:8080, 输入用户名密码,如提示跨域错误,前端test/config/index.js 的proxyTable添加后台服务器地址及端口,完整index.js代码如下:
"use strict";
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require("path");

module.exports = {
  dev: {
    // Paths
    assetsSubDirectory: "static",
    assetsPublicPath: "/",
    proxyTable: {
      "/api": {
        target: "http://localhost:3000",
        changeOrigin: true,
        pathRewrite: {
          "^/api": ""
        }
      }
    },

    // Various Dev Server settings
    host: "localhost", // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: false,
    errorOverlay: true,
    notifyOnErrors: true,
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-

    /**
     * Source Maps
     */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: "cheap-module-eval-source-map",

    // If you have problems debugging vue-files in devtools,
    // set this to false - it *may* help
    // https://vue-loader.vuejs.org/en/options.html#cachebusting
    cacheBusting: true,

    cssSourceMap: true
  },

  build: {
    // Template for index.html
    index: path.resolve(__dirname, "../dist/index.html"),

    // Paths
    assetsRoot: path.resolve(__dirname, "../dist"),
    assetsSubDirectory: "static",
    assetsPublicPath: "/",

    /**
     * Source Maps
     */

    productionSourceMap: true,
    // https://webpack.js.org/configuration/devtool/#production
    devtool: "#source-map",

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ["js", "css"],

    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
  }
};
接下来请修改后台 demo/app.js ,添加 express 允许跨域代码(如直接粘贴上面的代码,请忽略此操作):
// 自定义跨域中间件
var allowCors = function (req, res, next) {
  res.header("Access-Control-Allow-Origin", req.headers.origin);
  res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS");
  res.header("Access-Control-Allow-Headers", "Content-Type");
  res.header("Access-Control-Allow-Credentials", "true");
  next();
};
app.use(allowCors); //使用跨域中间件

输入用户名、密码都是1,再次点击登录,OK了。


3. docker 打包
先打包后台:
在(express后台) demo 根目录创建Dockerfile,写入如下内容:
# 常规配置:940Mb左右
# FROM node    
# COPY . /app    
# WORKDIR /app    
# RUN npm install    
# EXPOSE 3000    
# CMD npm start

# 最小压缩配置:62Mb
FROM alpine AS builder
WORKDIR /home/www/node
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk add --no-cache --update nodejs nodejs-npm
COPY package.json package-lock.json ./
RUN npm install --production

FROM alpine
WORKDIR /home/www/express
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk add --no-cache --update nodejs nodejs-npm
COPY --from=builder /home/www/node/node_modules ./node_modules
COPY . .
EXPOSE 3000
ENTRYPOINT ["npm", "run"]
CMD ["start"]
然后执行:docker build -t nodeserver .
运行:docker run -p 3000:3000 -d --name nodeserver nodeserver
打开浏览器:localhost:3000可以看到启动成功。

然后配置前端:
# 常规配置,配置完大约132Mb
# FROM nginx
# COPY dist/ /usr/share/nginx/html/
# COPY nginx/default.conf /etc/nginx/conf.d/default.conf

# 最小压缩配置
# 拉取最小体积的node环境容器, 并安装cnpm (加快容器构建速度,npm比较慢)
FROM node:lts-alpine as build-stage
# 进入容器的app目录, 然后拷贝当前目录(根目录)的所有文件到容器的当前目录中(/app)
WORKDIR /
COPY . .

# 删除vue环境配置文件(主要想通过容器的环境变量来达到不同的环境切换,以及为了部署到阿里云的容器服务或华为云的容器服务时, 通过配置项来配置相关的环境变量, 如果不需要可自行删除以下这句代码)
# RUN rm -f .env.*
# ENV VUE_APP_TEST_VALUE="My test Value"

#在容器内build
RUN npm install && npm run build
# production stage
# 最后通过nginx部署build出来的文件(/dist)
FROM nginx:stable-alpine as production-stage
COPY dist/ /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
执行:docker build -t vtest .
启动命令:docker run -p 80:80 -d --name vueApp vtest
这样,用两个docker包就可以搞定了。





 
Last Modified: 2021-04-14 13:49
Views: 1.4K

[[total]] comments

Post your comment
  1. [[item.time]]
    [[item.user.username]] [[item.floor]]Floor
  2. Click to load more...
  3. Post your comment