如何将 Angular Universal 应用程序部署到 Node.js 生产服务器?

问题描述:

我有一个带有 Universal 的 Angular 8 应用程序,我想将其部署到共享的 Web 主机生产服务器.我提前咨询了网络主机,他们告诉我可以在他们的共享网络托管上托管一个有角度的通用网络应用程序.但是,无论我做什么,我都无法使网站正常工作.当我访问该网站时,我一直看到消息:无法访问此网站"

I have an Angular 8 application with Universal that I want to deploy to a shared web host production server. I checked with the web host in advance and they told me that hosting an angular universal web app is possible on their shared web hosting. However, whatever I do, I can't get the website to work. When I go to the website I keep seeing the message: "This site can't be reached"

到目前为止我做过的事情:

Things I have done so far:

  1. 使用 npm run build:ssr 构建项目,它创建了一个带有浏览器和服务器文件夹以及 server.js 文件的 dist 文件夹
  2. 将 dist 文件夹移动到服务器的 public_html 文件夹内.然后通过 SSH 访问服务器并执行以下操作:
  3. 安装 Node.js 和 npm
  4. npm 安装
  5. npm install pm2 -g
  6. pm2 启动 dist/server.js

pm2 启动没有问题.

pm2 starts without problems.

这些是一些项目文件.如果有任何遗漏,请提问,我会将它们添加到问题中.

These are some of the project files. If any are missing please ask and I'll add them to the question.

带有脚本的 package.json 的一部分:

Part of the package.json with the scripts:

  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "compile:server": "webpack --config webpack.server.config.js --progress --colors",
    "serve:ssr": "node dist/server",
    "build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
    "build:client-and-server-bundles": "ng build --prod && ng run ProjectName:server:production --bundleDependencies all"
  },

来自构建的 server.js(只有快速部分,因为它有 25000 多行):

server.js from the build (only the express part, since it's 25000+ lines):

const app = express__WEBPACK_IMPORTED_MODULE_1__();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(process.cwd(), 'dist/browser');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap } = __webpack_require__(144);
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [
        provideModuleMap(LAZY_MODULE_MAP)
    ]
}));
app.set('view engine', 'html');
app.set('views', DIST_FOLDER);
// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express__WEBPACK_IMPORTED_MODULE_1__["static"](DIST_FOLDER, {
    maxAge: '1y'
}));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
    res.render('index', { req });
});
// Start up the Node server
app.listen(PORT, () => {
    console.log(`Node Express server listening on http://localhost:${PORT}`);
});

根据 SO 或其他地方的几个答案,您只需"将 dist 文件夹复制粘贴到服务器,运行 pm2 并且您的网站应该可以正常工作.不过,我觉得要让它发挥作用还有很多不足.

According to several answers on SO or elsewhere you "simply" copy paste the dist folder to the server, run pm2 and your website supposedly works. I feel like there's a lot missing to get it working though.

有人知道如何将 Angular Universal 网站正确部署到生产服务器吗?

Does someone know how to properly deploy an Angular Universal website to a production server?

我最终在 DirectAdmin 控制台中使用了一个名为NodeJS Selector"的工具,因为 PM2 不断给我带来问题.它是安装应用程序、执行 npm install 并启动应用程序的工具.所以基本上你会使用 SSH 做什么,但是在一个花哨的用户界面中.设置如下所示:

I ended up using a tool in my DirectAdmin console called 'NodeJS Selector', because PM2 kept causing problems for me. It's a tool to install your application, do npm install and start the app. So basically what you'd do using SSH, but in a fancy UI. The setup looks like this:

我的文件夹结构如下:

domains
    - appname.com
        - public_html
            - browser (=> this set as the document root in Apache, because index is located here)
                - index.html
                - .htaccess
                - other files...
            - server
            - server.js
            - package.json

我让我的网络主机做两件事,因为我不是服务器上的 root 用户.他们将 documentroot 设置为 Apache 的 httpd.conf 文件中的浏览器文件夹.在同一个文件中,他们还向端口 4000 添加了代理设置,因为这是我的应用程序运行的地方.所以在 httpd.conf 文件中会是这样的:

I had my web host do 2 things, because I'm not a root user on the server. They set the documentroot to the browser folder in Apache's httpd.conf file. In the same file they also added proxy settings to port 4000 because that's where my app is running on. So in the httpd.conf file will be something like this:

 DocumentRoot "/domains/appname.com/public_html/browser"

 <Proxy *>
  Order allow,deny
  Allow from all
 </Proxy>
 ProxyPreserveHost On
 ProxyRequests Off
 ProxyPass / https://localhost:4000/
 ProxyPassReverse / https://localhost:4000/

接下来在 NodeJS 选择器中将根设置为启动文件所在的位置(在我的例子中是 server.js).package.json 也需要位于 de public_html 文件夹中,以便您可以在 Selector 中执行 npm install.

Next in the NodeJS selector you set the root to where the startup file is located (in my case server.js). The package.json needs to be in de public_html folder as well so you can do npm install in the Selector.

要启动应用程序,请单击运行 JS 脚本",然后选择 serve:ssr 选项,它会运行节点服务器命令.

To start the app you click 'Run JS script' and choose the serve:ssr option and it runs the node server command.

Angular 文档描述了如何在服务器上重写一些规则.Apache 安装在我的 Web 服务器上,因此我添加了一个 .htaccess 文件,其中包含文档中的重写规则.然而,这个应用程序由 Node 提供服务,Node 可以提供实际路径.因此,在我的情况下,我不必添加具有重写规则的 .htaccess 文件.相反,当我添加 .htaccess 文件时,它导致我的应用程序的通用端无法完全呈现.有关详细信息,请参阅此问题.

The Angular docs describe how you have to rewrite some rules on the server. Apache is installed on my web server so I added an .htaccess file with the rewrite rules from the docs. However this app is served by Node and Node can serve the actual paths. So in my case I didn't have to add the .htaccess file with rewrite rules. On the contrary, when I added the .htaccess file it caused the Universal side of my app not to fully render. See this question for more info.