上线一个nodejs项目的流程

上线一个nodejs项目的流程

  • 在本地测试好nodejs项目
  • 代码上传服务器
  • 申请域名证书
  • pm2 启动

本地项目实例代码

express为例, 以下index代码包含一个静态服务器的几项功能

  • 静态文件服务,public目录下的文件可以被访问
  • 模板,支持swig模板
  • 支持http/https访问
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    const express = require('express');
    const path = require('path');
    const swig = require('swig');

    var app = express();

    // 利用express.static中间件来托管静态资源。
    app.use(express.static(path.join(__dirname, 'public')));

    // ----------------- 模板引擎 -------------------------
    //使用swig渲染html文件
    app.engine('html', swig.renderFile);

    //设置默认页面扩展名
    app.set('view engine', 'html');

    //设置模板编译无缓存
    app.set('view cache', false);

    //设置项目的页面文件,也就是html文件的位置
    app.set('views', path.join(__dirname, 'views'));

    swig.setDefaults({
    //关闭swig模板缓存
    cache: 'memory',

    //从文件载入模板,请写绝对路径,不要使用相对路径
    loader: swig.loaders.fs(__dirname + '/views')
    });

    app.get('/', (req, res) => {
    res.render('index');
    })
    app.get('/p2', (req, res) => {
    res.render('p2');
    })

    const port = 8888;

    app.listen(port, () => {
    console.log(`start: http://localhost:${port}/`)
    })

上传代码

代码上传路径: /usr/local/src/website/app

pm2 启动项目

pm2 基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 转到项目根目录下
cd /usr/local/src/website/app
# 启动项目几种方式
# 直接启动
pm2 start index.js
# 监听启动
pm2 start index.js --watch
# 多线程启动
# -i 线程数,max为最大
pm2 start index.js --watch -i max

# 查看项目状态
pm2 list
┌─────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├─────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ index │ default │ 1.0.0 │ fork │ 16884 │ 46m │ 22 │ online │ 0% │ 42.9mb │ root │ disabled │
└─────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

# 停止项目
pm2 stop index.js

# 重启项目
pm2 restart index.js


# 删除项目
pm2 delete 0
1
2
3
# 全局安装
npm i pm2 -g

acme 申请Let’s Encrypt免费证书

example.com 域名为例,申请通配符证书。在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# 安装acme客户端
# email 换成自己的邮箱
curl https://get.acme.sh | sh -s email=my@example.com

# 生成证书
# -d 参数为 二级域名/三级域名
# --webroot 为站点静态服务根目录
# acme客户端在申请证书的过程中会动态创建用于验证的文件,并在申请证书结束后删除
acme.sh --issue -d example.com -d www.example.com --webroot /usr/local/src/website/app/puclic

[Wed Oct 12 02:25:58 EDT 2022] Using CA: https://acme.zerossl.com/v2/DV90
[Wed Oct 12 02:25:58 EDT 2022] Multi domain='DNS:jpfinance.vip,DNS:www.jpfinance.vip'
[Wed Oct 12 02:25:58 EDT 2022] Getting domain auth token for each domain
[Wed Oct 12 02:26:06 EDT 2022] Getting webroot for domain='jpfinance.vip'
[Wed Oct 12 02:26:06 EDT 2022] Getting webroot for domain='www.jpfinance.vip'
[Wed Oct 12 02:26:06 EDT 2022] Verifying: jpfinance.vip
[Wed Oct 12 02:26:10 EDT 2022] Processing, The CA is processing your order, please just wait. (1/30)
[Wed Oct 12 02:26:17 EDT 2022] Success
[Wed Oct 12 02:26:17 EDT 2022] Verifying: www.jpfinance.vip
[Wed Oct 12 02:26:21 EDT 2022] Processing, The CA is processing your order, please just wait. (1/30)
[Wed Oct 12 02:26:25 EDT 2022] Processing, The CA is processing your order, please just wait. (2/30)
[Wed Oct 12 02:26:30 EDT 2022] Processing, The CA is processing your order, please just wait. (3/30)
[Wed Oct 12 02:26:35 EDT 2022] Success
[Wed Oct 12 02:26:35 EDT 2022] Verify finished, start to sign.
[Wed Oct 12 02:26:35 EDT 2022] Lets finalize the order.
[Wed Oct 12 02:26:35 EDT 2022] Le_OrderFinalize='https://acme.zerossl.com/v2/DV90/order/_33Hij4Qf6fFsvhQLjjQ_g/finalize'
[Wed Oct 12 02:26:37 EDT 2022] Order status is processing, lets sleep and retry.
[Wed Oct 12 02:26:37 EDT 2022] Retry after: 15
[Wed Oct 12 02:26:53 EDT 2022] Polling order status: https://acme.zerossl.com/v2/DV90/order/_33Hij4Qf6fFsvhQLjjQ_g
[Wed Oct 12 02:26:55 EDT 2022] Downloading cert.
[Wed Oct 12 02:26:55 EDT 2022] Le_LinkCert='https://acme.zerossl.com/v2/DV90/cert/dUv6k0tSCC4ONCslRI5QEw'
[Wed Oct 12 02:26:56 EDT 2022] Cert success.
-----BEGIN CERTIFICATE-----
MIIGfDCCBGSgAwIBAgIRAIP+hbhLSgKwKWRR+p0F5gEwDQYJKoZIhvcNAQEMBQAw
SzELMAkGA1UEBhMCQVQxEDAOBgNVBAoTB1plcm9TU0wxKjAoBgNVBAMTIVplcm9T
U0wgUlNBIERvbWFpbiBTZWN1cmUgU2l0ZSBDQTAeFw0yMjEwMTIwMDAwMDBaFw0y
MzAxMTAyMzU5NTlaMBgxFjAUBgNVBAMTDWpwZmluYW5jZS52aXAwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDd714oy696nHRhz0qQnnBbuxcBIVHVawjm
jRLxpmwEoFKl2CaVpXDmAfSwMMagLslK9KpZtkZPu/aGcOdoGRHVTIIl2CPZBoNe
e+tZl1wK6P/ibXtlxR7cYASZd52Hj5C883mspj8baoC/wIlV69o2f1HPhYwQSFIE
QZqikBtYNkJn+E/GLgfe+3FPs7zAKTPauL0EwnCg0zLsHC+ok0ToElSyqDUrIXau
X7qdouKWor5B2Z8+nTH3rRVgAUOmrIelgarnnm9vf6OCzG85Z1nHXR6+aQf90uHC
M6J/OXyGsZUnRRfP4bhNlO9nlEuZkk28FXm+d04MMyB4kMjffQtHAgMBAAGjggKM
MIICiDAfBgNVHSMEGDAWgBTI2XhootkZaNU9ct5fCj7ctYaGpjAdBgNVHQ4EFgQU
fjGu2rUIfeQH+5Ok5TYA18UfMA0wDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQC
MAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEkGA1UdIARCMEAwNAYL
KwYBBAGyMQECAk4wJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9D
UFMwCAYGZ4EMAQIBMIGIBggrBgEFBQcBAQR8MHowSwYIKwYBBQUHMAKGP2h0dHA6
Ly96ZXJvc3NsLmNydC5zZWN0aWdvLmNvbS9aZXJvU1NMUlNBRG9tYWluU2VjdXJl
U2l0ZUNBLmNydDArBggrBgEFBQcwAYYfaHR0cDovL3plcm9zc2wub2NzcC5zZWN0
aWdvLmNvbTCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AK33vvp8/xDIi509nB4+
GGq0Zyldz7EMJMqFhjTr3IKKAAABg8rfrccAAAQDAEcwRQIhAPdvrEdlpr1wCe5H
kp/9CciYTo9FJjHIh//mNB8JJz14AiBZV6BDSUbq3B9JK/Y+bIThpK5z5StPq9oF
5rz2oZdRYwB2AHoyjFTYty22IOo44FIe6YQWcDIThU070ivBOlejUutSAAABg8rf
ragAAAQDAEcwRQIhAL5DUGT7knMNyzDHYzKbO4e1Y99dGFJ0HCRnaUpzyGZ1AiB9
pwC+ayvCuGO8CSQDesNse40Wx1deUhlU+t+X/1KekzArBgNVHREEJDAigg1qcGZp
bmFuY2UudmlwghF3d3cuanBmaW5hbmNlLnZpcDANBgkqhkiG9w0BAQwFAAOCAgEA
BK3epIjHwOGNL72Umj1tfVxB8L18KPVD7sT+pOewF+B9JP7Tr3Q8uZ1SxjVsUrS4
Wuz3GrEehNLXVft1xR+qW3P8SPhRFi/tb38Vsny4DhGBFp0SMq+G89+fRwkBtPuK
B0zr0zW/pe+8a6K1ajOecodpzqC8YFHKVAqkoJPo3RlinfLO84ry4gXaT2ek9KQr
8+uswxgbbfnRObLjTN3rP2VBRUJB+dT3r1Z3F4Ilvg+xZ4Oclj3JJHlmss3JV/Oo
XMxmpSlk5AjUYhsk6UYOcPjDQorDSM7JnL0M2rE1EKCyFnJR1hx8OJeCPLkEg1cx
8TnL4qIKSG6SgUeFZAg8beuhVqJGca0n0Z9bM0e2yIQYAeBsCmuYTeTuIh59cP41
5B657vfIMM11IDgMe5udUnAt9pzCgE3UkYHQwPzmApifj0vB3dLuRNUztGB6pWiu
DQ7auOd55bOtFxVUAkYkxF/pAAF2qruOeulmwcM1zopFztmBOSPtop2WAbBAZCn2
DV4aKXG/pdCuvrnpFeYUcTXRObfr2QJuObey6k/XbIRLBy6grvYOe3UCtq+P6tSA
k3PQmhbkh19RrYh9WRHVM283d0gbJpzEyTmk2W03MpbVMhbIa8Xdzt1oQ0XFu1kL
UzzBHRqg93mixLLhAbP3JMN5J0xoY24c9Aq3zNgdg1w=
-----END CERTIFICATE-----
[Wed Oct 12 02:26:56 EDT 2022] Your cert is in: /root/.acme.sh/jpfinance.vip/jpfinance.vip.cer
[Wed Oct 12 02:26:56 EDT 2022] Your cert key is in: /root/.acme.sh/jpfinance.vip/jpfinance.vip.key
[Wed Oct 12 02:26:56 EDT 2022] The intermediate CA cert is in: /root/.acme.sh/jpfinance.vip/ca.cer
[Wed Oct 12 02:26:56 EDT 2022] And the full chain certs is there: /root/.acme.sh/jpfinance.vip/fullchain.cer

# 安装证书
# -d 为 域名
# --key-file key文件安装路径
# --fullchain-file 证书文件安装路径.windows 用crt证书,linux用pem cer证书
# --reloadcmd 证书更新后,重启服务命令。可选参数,也可为pm2 添加--watch参数
acme.sh --install-cert -d jpfinance.vip \
--key-file /usr/local/src/website/app/cert/server.key \
--fullchain-file /usr/local/src/website/app/cert/fullchain.cer
--reloadcmd "pm2 restart all"

参考

启用https

修改js程序,添加以下代码。重启pm2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// ............
var https = require('https');
var http = require('http');
var fs = require('fs');

//同步读取密钥和签名证书
var options = {
key:fs.readFileSync('./cert/server.key'),
cert:fs.readFileSync('./cert/server.cer')
}

// ............
var app = express();



// ............
var httpsServer = https.createServer(options,app);
var httpServer = http.createServer(app);

const httpPort = 80;
const httpsPort = 443


//https监听端口
httpsServer.listen(httpsPort,function(){ console.log(`start: https://localhost:${httpsPort}/`) });
//http监听端口
httpServer.listen(httpPort,function(){ console.log(`start: http://localhost:${httpPort}/`) });

问题

站点无法访问的原因

域名解析未生效

可以利用 在线ping工具查询站点解析是否正确,连通情况。如有解析不正确,需要登录域名管理后台修改dns解析记录。修改后的DNS解析记录不会立即生效,最迟48小时后才能在全球所有DNS服务器同步,所以期间某些地区无法访问站点是正常的。一般修改记录后1小时就可以再次使用在线ping工具验证。

服务端口未开放

端口不同有2种情况。服务器提供商一般会有安全组设置,安全组中包含端口开发和关闭的规则。服务器本身也会运行防火墙软件,默认只会开启ssh端口。要保证安全组和防火墙软件同时开发目标端口,站点才能访问。
参考

  • linux端口状态查询
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    netstat -tunlp
    Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
    tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 778/sshd
    tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 874/master
    tcp6 0 0 :::80 :::* LISTEN 17653/node /usr/loc
    tcp6 0 0 :::22 :::* LISTEN 778/sshd
    tcp6 0 0 ::1:25 :::* LISTEN 874/master
    tcp6 0 0 :::443 :::* LISTEN 17653/node /usr/loc
    udp 0 0 127.0.0.1:323 0.0.0.0:* 450/chronyd
    udp6 0 0 ::1:323 :::* 450/chronyd

    # ss是新的netstat
    ss -tunlp
    Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
    udp UNCONN 0 0 127.0.0.1:323 *:* users:(("chronyd",pid=450,fd=5))
    udp UNCONN 0 0 [::1]:323 [::]:* users:(("chronyd",pid=450,fd=6))
    tcp LISTEN 0 128 *:22 *:* users:(("sshd",pid=778,fd=3))
    tcp LISTEN 0 100 127.0.0.1:25 *:* users:(("master",pid=874,fd=13))
    tcp LISTEN 0 128 [::]:80 [::]:* users:(("node /usr/local",pid=17653,fd=21))
    tcp LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=778,fd=4))
    tcp LISTEN 0 100 [::1]:25 [::]:* users:(("master",pid=874,fd=14))
    tcp LISTEN 0 128 [::]:443 [::]:* users:(("node /usr/local",pid=17653,fd=20))

    # lsof -iTCP -sTCP:LISTEN仅显示TCP状态为LISTEN的socket文件
    lsof -nP -iTCP -sTCP:LISTEN
    COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
    sshd 445 root 3u IPv4 16434 0t0 TCP *:22 (LISTEN)
    sshd 445 root 4u IPv6 16445 0t0 TCP *:22 (LISTEN)
    apache2 515 root 4u IPv6 16590 0t0 TCP *:80 (LISTEN)
    mysqld 534 mysql 30u IPv6 17636 0t0 TCP *:3306 (LISTEN)
    mysqld 534 mysql 33u IPv6 19973 0t0 TCP *:33060 (LISTEN)
    apache2 764 www-data 4u IPv6 16590 0t0 TCP *:80 (LISTEN)
    apache2 765 www-data 4u IPv6 16590 0t0 TCP *:80 (LISTEN)
    master 929 root 13u IPv4 19637 0t0 TCP *:25 (LISTEN)
  • 开放linux服务器端口
    1
    2
    3
    4
    5
    # firewall
    firewall-cmd --zone=public --add-port=1935/tcp --permanent

    # iptables
    /sbin/iptables -I INPUT -p tcp --dport 8080 -j ACCEPT
    参考: linux开放指定端口命令

上线一个nodejs项目的流程
https://jacksiongt.github.io/2021/04/06/部署Nodejs服务/
作者
Jacksion
发布于
2021年4月6日
许可协议