2026年7月

Docker部署

创建自签名证书

  1. 生成CA私钥和CA证书

    [root@ecs ~]# mkdir -p /data/rabbitmq/ssl && cd /data/rabbitmq/ssl
    [root@ecs ssl]# openssl req -x509 -newkey rsa:4096 -days 3650 -nodes \
    -keyout ca.key -out ca.crt \
    -subj "/C=CN/ST=Jiangsu/L=Nanjing/O=Maisi/OU=R&D/CN=RabbitMQ"
  2. 生成服务端私钥和证书请求

    [root@ecs ssl]# openssl req -newkey rsa:4096 -nodes \
    -keyout server.key -out server.csr \
    -subj "/C=CN/ST=Jiangsu/L=Nanjing/O=Maisi/OU=R&D/CN=RabbitMQ"
  3. 创建SAN(Subject Alternative Name,主题备用名称)扩展文件

    [root@ecs ssl]# cat > v3.ext <<EOF
    subjectAltName = @alt_names
    
    [alt_names]
    DNS.1 = rabbitmq.local
    DNS.2 = localhost
    IP.1 = 192.168.1.3
    IP.2 = 127.0.0.1
    EOF

    注:需要将192.168.1.3替换RabbitMQ真实的公网/内网地址。

  4. 使用CA签发服务端证书

    [root@ecs ssl]# openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
    -CAcreateserial -out server.crt -days 3650 -extfile v3.ext

运行rabbitmq容器

[root@ecs ~]# cat > /data/rabbitmq/rabbitmq.conf <<EOF
default_vhost = /

listeners.ssl.default = 5671
ssl_options.cacertfile = /etc/rabbitmq/ssl/ca.crt
ssl_options.certfile = /etc/rabbitmq/ssl/server.crt
ssl_options.keyfile = /etc/rabbitmq/ssl/server.key
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = false

management.tcp.port = 15672

management.ssl.port = 15671
management.ssl.cacertfile = /etc/rabbitmq/ssl/ca.crt
management.ssl.certfile = /etc/rabbitmq/ssl/server.crt
management.ssl.keyfile = /etc/rabbitmq/ssl/server.key
EOF
[root@ecs ~]# docker run --name=rabbitmq \
-p 5671-5672:5671-5672 \
-p 15671-15672:15671-15672 \
-e RABBITMQ_DEFAULT_USER=root \
-e RABBITMQ_DEFAULT_PASS=root \
--hostname rabbitmq \
-v /data/rabbitmq:/var/lib/rabbitmq \
-v /data/rabbitmq/ssl:/etc/rabbitmq/ssl/ \
-v /data/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro \
-d registry.cn-shanghai.aliyuncs.com/maisi/rabbitmq:4.2.2

--hostname是为了固定RabbitMQ的节点名称,从而保证数据的持久化。不指定--hostname,Docker创建容器时,会以容器ID作为主机名。RabbitMQ的数据(队列、交换机、用户、Vhost)保存在/var/lib/rabbitmq/mnesia/rabbit@[主机名]下,即使-v /data/rabbitmq:/var/lib/rabbitmq也会因为机器名不一致而无法直接使用之前的容器数据。

不指定RABBITMQ_DEFAULT_USER,rabbitmq默认会创建guest用户,并设置密码为guest。

常用端口说明:

端口协议说明
5672AMQP客户端连接端口
应用程序(java、node等)与RabbitMQ进行消息收发通信的端口
5671AMQP(TLS/SSL)客户端加密传输连接端口
15672HTTPWeb管理端口
15671HTTP(TLS/SSL)Web加密传输管理端口
1883MQTT客户端连接端口
15675MQTT客户端WebSocket传输连接端口
4369EPMD
Erlang Port Mapper Daemon
Erlang节点发现端口
25672Erlang Distribution节点间通信与CLI端口
15692Prometheus MetricsPrometheus监控端口

防火墙开放端口

[root@ecs ~]# firewall-cmd --zone=public --add-port=5671-5672/tcp --permanent
[root@ecs ~]# firewall-cmd --zone=public --add-port=15671-15672/tcp --permanent
[root@ecs ~]# firewall-cmd --zone=public --add-port={1883,15675}/tcp --permanent
[root@ecs ~]# firewall-cmd --reload

命令行操作

插件管理

  • 开启Web管理

    [root@ecs ~]# docker exec -it rabbitmq /bin/bash
    root@20104fa324e1:/# rabbitmq-plugins enable rabbitmq_management

    查看RabbitMQ监听的所有端口:

    root@20104fa324e1:/# rabbitmq-diagnostics listeners
      Asking node rabbit@rabbitmq to report its protocol listeners ...
      Interface: [::], port: 15692, protocol: http/prometheus, purpose: Prometheus exporter API over HTTP
      Interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
      Interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
      Interface: [::], port: 5671, protocol: amqp/ssl, purpose: AMQP 0-9-1 and AMQP 1.0 over TLS
      Interface: [::], port: 15671, protocol: https, purpose: HTTP API over TLS (HTTPS)

    Web管理地址:

  • 开启延时消息插件

    rabbitmq 4.2.2

    [root@ecs ~]# cd /usr/local/src
    [root@ecs src]# wget https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/v4.2.0/rabbitmq_delayed_message_exchange-4.2.0.ez
    [root@ecs src]# docker cp ./rabbitmq_delayed_message_exchange-4.2.0.ez rabbitmq:/plugins
                                             Successfully copied 44.5kB to rabbitmq:/opt/rabbitmq/plugins
    [root@ecs src]# docker exec -it rabbitmq /bin/bash
    root@20104fa324e1:/# rabbitmq-plugins enable rabbitmq_delayed_message_exchange
    Enabling plugins on node rabbit@20104fa324e1:
    rabbitmq_delayed_message_exchange
    The following plugins have been configured:
      rabbitmq_delayed_message_exchange
      rabbitmq_management
      rabbitmq_management_agent
      rabbitmq_prometheus
      rabbitmq_web_dispatch
    Applying plugin configuration to rabbit@20104fa324e1...
    The following plugins have been enabled:
      rabbitmq_delayed_message_exchange
    
    started 1 plugins.

    rabbitmq 4.0.4

    [root@ecs ~]# cd /usr/local/src
    [root@ecs src]# wget https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/v4.0.7/rabbitmq_delayed_message_exchange-v4.0.7.ez

    注:

    • rabbitmq_delayed_message_exchange和rabbitmq的主次版本号(x.y.z的x.y)必须一致,否则会无法兼容。例如:rabbitmq_delayed_message_exchange-4.2.0.ez可以用于rabbitmq:4.2.2,但不用用于rabbitmq:4.0.4
    • 开启延时消息插件后,必须重启rabbitmq容器才能生效。
  • 开启MQTT插件

    root@20104fa324e1:/# rabbitmq-plugins enable rabbitmq_web_mqtt

    开启rabbitmq_web_mqtt会自动开启rabbitmq_mqtt。mqtt连接参数示例值:

    wsmqtt
    Namemqttmqtt
    Hostws://192.168.1.54mqtt://192.168.1.54
    Port156751883
    Path/ws
    Usernamerootroot
    PassworddcIvMA3EHignTxAVdcIvMA3EHignTxAV

用户管理

  • 添加用户

    root@20104fa324e1:/# rabbitmqctl add_user maisi mM1lO8nN2sO4eK8q
  • 修改用户密码

    root@20104fa324e1:/# rabbitmqctl change_password maisi mM1lO8nN2sO4eK8q
  • 设置用户角色

    root@20104fa324e1:/# rabbitmqctl set_user_tags maisi administrator

    设置administrator后即可登录Web Management UI。

  • 删除指定用户

    root@20104fa324e1:/# rabbitmqctl delete_user maisi
  • 查看所有用户

    root@20104fa324e1:/# rabbitmqctl list_users

vhost管理

  • 添加vhost

    root@20104fa324e1:/# rabbitmqctl add_vhost maisi

    注:maisi/maisi是2个不同的vhost,vhost名称允许有/路径符号。

  • 设置用户权限

    用户maisi在虚拟主机maisi下拥有Configure、Write和Read任意资源的权限。

    root@20104fa324e1:/# rabbitmqctl set_permissions -p maisi maisi ".*" ".*" ".*"
    • -p:指定vhost。
    • ".*" ".*" ".*".*是正则表达式,表示任意字符。分别对应Configure(配置任意名称的Exchange和Queue)、Write(给任意名称的Exchange或Queue发送消息)和Read(从任意名称的Queue读取消息)。
  • 查看所有vhost

    root@20104fa324e1:/# rabbitmqctl list_vhosts
  • 删除指定vhost

    root@20104fa324e1:/# rabbitmqctl delete_vhost maisi

队列管理

  • 查看队列列表

    root@20104fa324e1:/# rabbitmqctl list_queues -p maisi
  • 删除指定队列

    root@20104fa324e1:/# rabbitmqctl delete_queue default-delay -p maisi

客户端调用

Node.JS

const amqp = require('amqplib');
const fs = require('fs');
const path = require('path');

(async () => {
    const username = 'maisi';
    const password = 'mM1lO8nN2sO4eK8q';
    const host = '192.168.1.3';
    const port = 5672;
    const vhost = 'maisi';

    try {
        /*
        const connection = await amqp.connect(`amqp://${username}:${password}@${host}:${port}/${vhost}?heartbeat=60&frameMax=8192`);
        */

        const connection = await amqp.connect({
            protocol: 'amqps',
            hostname: host,
            port: 5671,
            username,
            password,
            vhost,
            heartbeat: 60,
            frameMax: 8192
        }, {
            //报错:Error: self-signed certificate in certificate chain
            //方法一:忽略自签名证书校验
            rejectUnauthorized: false,
            //方法二:添加CA
            rejectUnauthorized: true,
            ca: [fs.readFileSync(path.join(__dirname, 'ca.crt'))]
        });
        console.log({ connection });
    } catch (e) {
        console.log(e);
    }
})();

注:RabbitMQ从4.1.0开始,RabbitMQ官方为了提高性能和安全性,将AMQP协议中frame_max(最大帧大小)的最小允许值从4096字节提升到了8192字节。如果amqplib版本较旧,连接RabbitMQ时最大帧大小默认使用的是4096,不符合新版RabbitMQ的最低要求,RabbitMQ就会拒绝连接,并抛出异常:failed to negotiate connection parameters: negotiated frame_max = 4096 is lower than the minimum allowed value (8192)

解决方法(任选其一):

  • 升级amqplib到最新版(当前:2.0.1);
  • 修改连接参数,添加frameMax=8192

    const url = `amqp://${username}:${password}@${hostname}:${port}/${vhost}?heartbeat=60&frameMax=8192`;

Python

import pika

host = '192.168.1.3'
port = 5672
username = 'maisi'
password = 'mM1lO8nN2sO4eK8q'
vhost = 'maisi'

try:
    credentials = pika.PlainCredentials(username, password)
    # connection = pika.BlockingConnection(pika.ConnectionParameters(host, port, vhost, credentials))
    connection = pika.BlockingConnection(pika.ConnectionParameters(
        host=host,
        port=port,
        virtual_host=vhost,
        credentials=credentials
    ))
    channel = connection.channel()
    print(channel)
except Exception as e:
    print(e)
finally:
    if 'connection' in locals() and connection.is_open:
        connection.close()

安装Node.js

下载地址:https://nodejs.org/download/release/

[root@ecs ~]# yum install -y wget
[root@ecs ~]# cd /usr/local/src
[root@ecs src]# wget https://nodejs.org/download/release/v22.23.1/node-v22.23.1-linux-x64.tar.xz
[root@ecs src]# tar -Jxvf node-v22.23.1-linux-x64.tar.xz
[root@ecs src]# mv node-v22.23.1-linux-x64 /usr/local/node
[root@ecs src]# echo "export PATH=\$PATH:/usr/local/node/bin/" >> /etc/profile
[root@ecs src]# source /etc/profile
[root@ecs src]# node --version
v22.9.0
[root@ecs src]# npm --version
10.8.3

Node.js从v18开始,CentOS 7.9默认glibc的版本低于Node.js动态链接库依赖的glibc版本,因此必须升级glibc版本

Node.js v18+要求glibc的版本不能低于2.28,升级教程参考:CentOS 7.9升级glibc到2.28-2.33

动态链接库系统默认支持最高版本符号node v18+依赖glibc最低版本符号
/lib64/libm.so.6GLIBC_2.15GLIBC_2.27
/lib64/libstdc++.so.6GLIBCXX_3.4.19
CXXABI_1.3.7
GLIBCXX_3.4.21
CXXABI_1.3.9
/lib64/libc.so.6GLIBC_2.17GLIBC_2.28

查看node依赖的动态链接库

[root@ecs ~]# cd /usr/local/node/bin
[root@ecs bin]# ldd node
./node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by ./node)
./node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by ./node)
./node: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by ./node)
./node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by ./node)
./node: /lib64/libc.so.6: version `GLIBC_2.27' not found (required by ./node)
./node: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by ./node)
./node: /lib64/libc.so.6: version `GLIBC_2.25' not found (required by ./node)
        linux-vdso.so.1 =>  (0x00007ffd8d1f4000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f46a0408000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f46a0100000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f469fdfe000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f469fbe8000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f469f9cc000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f469f5fe000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f46a060c000)

查看版本符号信息

[root@ecs ~]# strings /lib64/libm.so.6 | grep GLIBC
GLIBC_2.2.5
GLIBC_2.4
GLIBC_2.15
GLIBC_PRIVATE
GLIBC_2.15
__strtold_nan@@GLIBC_PRIVATE
__errno_location@@GLIBC_2.2.5
errno@@GLIBC_PRIVATE
qsort@@GLIBC_2.2.5
__strtod_nan@@GLIBC_PRIVATE
fputs@@GLIBC_2.2.5
memset@@GLIBC_2.2.5
feupdateenv@@GLIBC_2.2.5
GLIBC_2.4
_rtld_global_ro@@GLIBC_PRIVATE
__strtof_nan@@GLIBC_PRIVATE
memmove@@GLIBC_2.2.5
fwrite@@GLIBC_2.2.5
__cxa_finalize@@GLIBC_2.2.5
stderr@@GLIBC_2.2.5

查看依赖库版本

[root@ecs ~]# ll /lib64/libc.so.6
lrwxrwxrwx. 1 root root 12 Jun 27 17:06 /lib64/libc.so.6 -> libc-2.17.so

libc-2.17.so表示libc的版本是2.17。

安装yarn

yarn由Facebook和Google等公司联合推出的js包管理工具。yarn可以同步安装所有package,且遇到已经安装过的包会从本地缓存获取。为了防止拉取模块时出现版本兼容问题,yarn.lock会记录安装的模块版本号,这样就可以确保每次拉取项目依赖时模块的版本都是一致的。

[root@ecs ~]# npm -g install yarn --registry=https://registry.npmmirror.com

配置阿里云镜像源

[root@ecs ~]# yarn config set registry https://registry.npmmirror.com
[root@ecs ~]# yarn config get registry

打开power shell,执行以下策略,即可在vscode终端执行yarn。

PS C:\Users\Administrator> set-executionpolicy remotesigned

缓存操作

指令说明
yarn cache dir查看缓存目录
Linux:/usr/local/share/.cache/yarn/v6
Windows:C:\Users\Administrator\AppData\Local\Yarn\Cache\v6
yarn cache list查看包缓存列表
yarn cache clean清空缓存,生产服务器无需yarn缓存,在安装好项目依赖后,可以清空缓存以释放更多的磁盘空间。

升级gcc

CentOS 7.9默认安装的gcc版本为4.8.5,编译glibc 2.28(Node.js从18开始,要求glibc最低为2.28)要求:

  • gcc不能低于4.9,推荐gcc版本:8.5
  • make版本不能低于4.0,也不能高于4.3。make(>=4.4)的jobserver与glibc(<=2.31)不兼容,推荐make版本:4.3

升级gcc有两种方法:

安装devtoolset-8

[root@ecs ~]# yum install centos-release-scl -y
[root@ecs ~]# sed -i 's%mirrorlist=http://mirrorlist.centos.org?arch=$basearch&release=7&repo=sclo-sclo%baseurl=http://mirrors.aliyun.com/centos/7/sclo/$basearch/sclo/%g' /etc/yum.repos.d/CentOS-SCLo-scl.repo
[root@ecs ~]# sed -i 's%mirrorlist=http://mirrorlist.centos.org?arch=$basearch&release=7&repo=sclo-rh%baseurl=http://mirrors.aliyun.com/centos/7/sclo/$basearch/rh/%g' /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo
[root@ecs ~]# yum repolist && yum clean all && yum makecache
[root@ecs ~]# yum install devtoolset-8 -y
[root@ecs ~]# echo "source /opt/rh/devtoolset-8/enable" >> /etc/profile
[root@ecs ~]# source /etc/profile
[root@ecs ~]# gcc -v 2>&1 | tail -n 1
gcc version 8.3.1 20190311 (Red Hat 8.3.1-3) (GCC)
[root@ecs ~]# make -v | head -1
GNU Make 4.2.1

devtoolset-8安装简单且速度快,但不会升级libstdc++,可以下载一个编译好的高版本libstdc++覆盖CentOS 7.9的默认版本。

[root@ecs ~]# mv -f /usr/local/src/libstdc++.so.6.0.25 /usr/lib64/libstdc++.so.6.0.25
[root@ecs ~]# ln -sfv /usr/lib64/libstdc++.so.6.0.25 /usr/lib64/libstdc++.so.6

警告:此方法只适用于升级libstdc++,切勿使用编译好的libm-2.17.so或libc-2.28.so来升级libm和libc,一旦操作,系统必崩溃!

编译安装gcc

gcc下载地址:https://ftp.gnu.org/pub/gnu/gcc/

阿里云镜像:https://mirrors.aliyun.com/gnu/gcc/

gcc源码安装会升级libstdc++,但是安装时间相当长(可能1小时以上)。

gcc 8.5.0

[root@ecs ~]# yum install -y bzip2 m4 gcc gcc-c++

[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -jxvf gmp-6.3.0.tar.bz2
[root@ecs src]# cd /usr/local/src/gmp-6.3.0
[root@ecs gmp-6.3.0]# ./configure
[root@ecs gmp-6.3.0]# make && make install

[root@ecs gmp-6.3.0]# cd /usr/local/src
[root@ecs src]# tar -jxvf mpfr-4.2.1.tar.bz2
[root@ecs src]# cd /usr/local/src/mpfr-4.2.1
[root@ecs mpfr-4.2.1]# ./configure
[root@ecs mpfr-4.2.1]# make && make install
[root@ecs mpfr-4.2.1]# cp /usr/local/lib/libmpfr.so.6 /usr/lib64

[root@ecs mpfr-4.2.1]# cd /usr/local/src
[root@ecs src]# tar -zxvf mpc-1.3.1.tar.gz
[root@ecs src]# cd /usr/local/src/mpc-1.3.1
[root@ecs mpc-1.3.1]# ./configure
[root@ecs mpc-1.3.1]# make && make install

[root@ecs mpc-1.3.1]# cd /usr/local/src
[root@ecs src]# tar -zxvf gcc-8.5.0.tar.gz
[root@ecs src]# cd /usr/local/src/gcc-8.5.0
[root@ecs gcc-8.5.0]# mkdir build && cd build
[root@ecs build]# ../configure --enable-languages=c,c++ --disable-multilib
[root@ecs build]# make -j$(nproc) && make install
[root@ecs build]# cp /usr/local/lib64/libstdc++.so.6.0.25 /usr/lib64
[root@ecs build]# ln -sfv /usr/lib64/libstdc++.so.6.0.25 /usr/lib64/libstdc++.so.6
[root@ecs build]# gcc --version | head -1
gcc (GCC) 8.5.0

gcc 9.5.0

[root@ecs ~]# yum install -y bzip2 m4 gcc gcc-c++

[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -jxvf gmp-6.3.0.tar.bz2
[root@ecs src]# cd /usr/local/src/gmp-6.3.0
[root@ecs gmp-6.3.0]# ./configure
[root@ecs gmp-6.3.0]# make && make install

[root@ecs gmp-6.3.0]# cd /usr/local/src
[root@ecs src]# tar -jxvf mpfr-4.2.2.tar.bz2
[root@ecs src]# cd /usr/local/src/mpfr-4.2.2
[root@ecs mpfr-4.2.2]# ./configure
[root@ecs mpfr-4.2.2]# make && make install
[root@ecs mpfr-4.2.2]# cp /usr/local/lib/libmpfr.so.6 /usr/lib64

[root@ecs mpfr-4.2.2]# cd /usr/local/src
[root@ecs src]# tar -xvf mpc-1.4.1.tar.xz
[root@ecs src]# cd /usr/local/src/mpc-1.4.1
[root@ecs mpc-1.4.1]# ./configure
[root@ecs mpc-1.4.1]# make && make install

[root@ecs mpc-1.4.1]# cd /usr/local/src
[root@ecs src]# tar -zxvf gcc-9.5.0.tar.gz
[root@ecs src]# cd /usr/local/src/gcc-9.5.0
[root@ecs gcc-9.5.0]# mkdir build && cd build
[root@ecs build]# ../configure --enable-languages=c,c++ --disable-multilib
[root@ecs build]# make -j$(nproc) && make install
[root@ecs build]# cp /usr/local/lib64/libstdc++.so.6.0.28 /usr/lib64
[root@ecs build]# ln -sfv /usr/lib64/libstdc++.so.6.0.28 /usr/lib64/libstdc++.so.6
[root@ecs build]# gcc --version | head -1
gcc (GCC) 9.5.0
gcc版本号libstdc++版本号
8.5.025
9.5.028
10.5.028
11.1.029
12.1.030
13.1.031
14.1.0 ~ 14.4.033
15.1.034
16.1.035

gcc 15.1.0要求gcc环境必须支持C++14,因此必须升级gcc到5.0或更高版本(推荐14.1.0),gcc 4.8开始部分支持C++14(-std=c++1y),4.9支持C++14的核心特性,5.0开始完全对齐C++14标准。

gcc 15.1.0默认开启了-Werror,即警告视为报错。比如原本只是警告的discarded-qualifiers(丢弃了const限定符),开启了-Werror之后被认为是报错,从而中断编译。必须在configure时加上--disable-werror来忽略这些报错。

升级make

make下载地址:https://ftp.gnu.org/pub/gnu/make/

安装devtoolset-8会升级make到4.2.1,无需单独升级make。

[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -xzvf make-4.3.tar.gz && cd make-4.3
[root@ecs make-4.3]# ./configure  --prefix=/usr/local/make
[root@ecs make-4.3]# make && make install
[root@ecs make-4.3]# mv /usr/bin/make /usr/bin/make.3.82
[root@ecs make-4.3]# ln -sv /usr/local/make/bin/make /usr/bin/make
[root@ecs make-4.3]# make -v | head -1
GNU Make 4.3

升级glibc

glibc下载地址:https://ftp.gnu.org/pub/gnu/glibc

升级glibc会生成高版本的libm和libc。

glibc 2.28

[root@ecs ~]# yum install -y gcc bison
[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -zxvf glibc-2.28.tar.gz && cd glibc-2.28
[root@ecs glibc-2.28]# mkdir build && cd build
[root@ecs build]# ../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
[root@ecs build]# make -j$(nproc) && make install
[root@ecs build]# ll /lib64/libc.so.6
lrwxrwxrwx. 1 root root 12 Jul  9 20:33 /lib64/libc.so.6 -> libc-2.28.so
[root@ecs build]# strings /lib64/libc.so.6 | grep ^GLIBC | sort -t'.' -k 2,2 -n -r | head -1
GLIBC_2.28

注:glibc 2.29开始要求python>=3.4,使用yum install python3 -y安装python3;

常见报错

  • make install时报错:/usr/bin/ld: cannot find -lnss_test2

    报错原因是scripts/test-installation.pl调用了gcc -lnss_test2,而make过程中并没有生成libnss_test2.so,从而导致了报错。

    devtoolset升级gcc后安装glibc,必定出现这个报错,而编译安装gcc后安装glibc则不一定会出现这个报错。究其原因是因为devtoolset会自动安装perl,而编译安装gcc则不会,在没有安装perl的情况下是无法执行该脚本的,因此也就不会报错了。

    解决方法:test-installation.pl中gcc不要调用-lnss_test2,在scripts/test-installation.pl脚本128行前面插入\t&& $name ne "nss_test2"

  • 报错:/lib/../lib64/libnss_nis.so: undefined reference to `_nsl_default_nss@GLIBC_PRIVATE'

    libnss_nis.so依赖_nsl_default_nss@GLIBC_PRIVATElibnss_nis.so自身并没有定义_nsl_default_nss@GLIBC_PRIVATE,而是调用libnsl.so的。glibc2.28默认移除了nsl的支持,因此生成的libnsl.so并不包含_nsl_default_nss@GLIBC_PRIVATE。configure时加上--enable-obsolete-nsl后,生成的libnsl.so即可包含_nsl_default_nss@GLIBC_PRIVATE

    查看定义:

    [root@ecs ~]# readelf -Ws /lib64/libnss_nis.so | grep _nsl_default_nss
        16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _nsl_default_nss@GLIBC_PRIVATE (6)
       155: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _nsl_default_nss@@GLIBC_PRIVATE
    [root@ecs ~]# nm -D /lib64/libnss_nis.so | grep _nsl_default_nss
                     U _nsl_default_nss
    [root@ecs ~]# readelf -Ws /lib64/libnsl.so | grep _nsl_default_nss
       210: 000000000000ef80    81 FUNC    GLOBAL DEFAULT   12 _nsl_default_nss@@GLIBC_PRIVATE
       371: 000000000000ef80    81 FUNC    GLOBAL DEFAULT   12 _nsl_default_nss

    UND表示未定义函数,只是调用。

    解决方法(二选一):

    • 让glibc支持nsl,configure时添加--enable-obsolete-nsl
    • test-installation.pl中gcc不要调用-lnss_nis,在scripts/test-installation.pl脚本128行前面插入\t&& $name ne "nss_nis"

    如果出现以上报错,则使用以下方法安装glibc:

    [root@ecs ~]# yum install -y gcc bison
    [root@ecs ~]# cd /usr/local/src
    [root@ecs src]# tar -zxvf glibc-2.28.tar.gz && cd glibc-2.28
    [root@ecs glibc-2.28]# mkdir build && cd build
    [root@ecs build]# ../configure --prefix=/usr --disable-profile --enable-add-ons \
    --with-headers=/usr/include --with-binutils=/usr/bin \
    --enable-obsolete-nsl
    [root@ecs build]# sed -i '128i\\t&& $name ne "nss_test2"' ../scripts/test-installation.pl
    [root@ecs build]# sed -i '128i\\t&& $name ne "nss_nis"' ../scripts/test-installation.pl
    [root@ecs build]# make -j$(nproc) && make install
    [root@ecs build]# strings /lib64/libc.so.6 | grep ^GLIBC | sort -t'.' -k 2,2 -n -r | head -1
    GLIBC_2.28

    监控安装过程

    [root@ecs ~]# yum install inotify-tools -y
    [root@ecs ~]# inotifywait -m -r -e create --format '%T %w%f %e' --timefmt '%Y-%m-%d %H:%M:%S' /usr/local/src/glibc-2.28 | grep "nss_test"
    • -m:monitor,持续监控;
    • -r:recursive,递归监控;
    • -e create:event,只监控创建事件;
    • --format '%T %w%f %e'

      • %T:timestamp,时间戳;
      • %w:watched path,被监控目录路径;
      • %f:filename,发生变化文件文件名;
      • %e:event,事件名

注意事项

glibc版本注意事项
2.28-2.31make>=4.0且make<=4.3
2.32-2.33make>=4.0
2.34-2.36必须先使用sln统一libc.so.6和ld-linux-x86-64.so.2的版本后再make install
2.37-2.41binutils>=2.36且configure时加上:CC="gcc -B/usr/local/bin"
2.42gcc>=12.1.0
2.43openat2要求Linux内核>=5.6,而CentOS 7.9的默认内核是3.10.0,必须先升级系统内核

同步libc和ld

从glibc 2.34开始,libc.so.6在运行时需要调用ld-linux-x86-64.so.2的一个内部私有符号__tunable_get_val。在编译安装glibc 2.34的过程中,libc.so.6升级到2.34后,ld-linux-x86-64.so.2还是旧版的,而旧版不存在私有符号__tunable_get_val,libc.so.6找不到这个私有符号就会报错:gcc: relocation error: /lib64/libc.so.6: symbol __tunable_get_val, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2 with link time reference

几乎所有Linux命令(包括 ls, cp, ln, sudo, ssh 等)都依赖这两个库,因此一旦出现这个报错,基本就要重装系统了。

为了避免新版libc.so.6调用旧版ld-linux-x86-64.so.2的情况,使用sln(不需要ld加载动态链接库)先强制将libc.so.6和ld-linux-x86-64.so.2都链接到glibc 2.34新编译的版本(在build目录内),再执行make install。

glibc 2.34

[root@ecs ~]# yum install -y gcc bison python3
[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -zxvf glibc-2.34.tar.gz && cd glibc-2.34
[root@ecs glibc-2.34]# mkdir build && cd build
[root@ecs build]# ../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
[root@ecs build]# make -j$(nproc)
[root@ecs build]# sln /usr/local/src/glibc-2.34/build/libc.so.6 /lib64/libc.so.6
[root@ecs build]# sln /usr/local/src/glibc-2.34/build/dlfcn/libdl.so.2 /lib64/libdl.so.2
[root@ecs build]# sln /usr/local/src/glibc-2.34/build/nptl/libpthread.so.0 /lib64/libpthread.so.0
[root@ecs build]# sln /usr/local/src/glibc-2.34/build/elf/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2
[root@ecs build]# make install
[root@ecs build]# strings /lib64/libc.so.6 | grep ^GLIBC | sort -t'.' -k 2,2 -n -r | head -1
GLIBC_2.34

sln不依赖libc.so.6,因此设置LD_PRELOAD=/lib64/libc-2.17.so 没有任何效果。

[root@ecs ~]# file /sbin/sln
/sbin/sln: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, not stripped
[root@ecs ~]# ldd /sbin/sln
        not a dynamic executable

升级binutils

glibc从2.37开始使用了AVX-512的vpcmpneqb指令,binutils(包含as、ld等工具)从2.36开始才支持这个指令。使用低版本的binutils编译glibc 2.37会报错:Error: no such instruction: `vpcmpneqb (64 * 4)(%rdi),%zmm17,%k1'

CentOS 7.9默认的binutils是2.27,必须升级binutils(推荐2.43)才能支持vpcmpneqb。升级方法也适用于最新版2.46.1。

[root@ecs ~]# yum install -y texinfo bison
[root@ecs ~]# cd /usr/local/src
[root@ecs src]# wget https://ftp.gnu.org/gnu/binutils/binutils-2.43.tar.xz
[root@ecs src]# tar -xvf binutils-2.43.tar.xz
[root@ecs src]# cd binutils-2.43/
[root@ecs binutils-2.40]# ./configure
[root@ecs binutils-2.40]# make && make install

测试升级是否成功:

[root@ecs ~]# echo "vpcmpneqb (64 * 4)(%rdi),%zmm17,%k1" | as -o /dev/null - 2>&1

查看as版本:

[root@ecs ~]# as --version | head -1
GNU assembler (GNU Binutils) 2.40

查看gcc调用的as版本

[root@ecs ~]# $(gcc -print-prog-name=as) --version | head -1
GNU assembler (GNU Binutils) 2.40

查看PATH优先级

[root@ecs ~]# echo $PATH | tr ':' '\n' | grep -E "binutils|local"
/usr/local/sbin
/usr/local/bin

strace跟踪实际调用

[root@ecs ~]# yum install -y strace
[root@ecs ~]# strace -f -e execve make -j1 2>&1 | grep "execve.*as" | head -5

glibc 2.37

虽然升级了binutils,但是升级后的as安装路径是/usr/local/bin/as,而glibc 2.37在编译的时候会调用/usr/bin/as,此as版本还是旧版,依旧会报错:Error: no such instruction: `vpcmpneqb (64 * 4)(%rdi),%zmm17,%k1'。因此必须在configure时指定binutils升级后的安装目录:configure CC="gcc -B/usr/local/bin"

[root@ecs ~]# yum install -y gcc bison python3
[root@ecs ~]# cd /usr/local/src
[root@ecs src]# tar -zxvf glibc-2.37.tar.gz && cd glibc-2.37
[root@ecs glibc-2.37]# mkdir build && cd build
[root@ecs build]# ../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin CC="gcc -B/usr/local/bin"
[root@ecs build]# make -j$(nproc)
[root@ecs build]# sln /usr/local/src/glibc-2.37/build/libc.so.6 /lib64/libc.so.6
[root@ecs build]# sln /usr/local/src/glibc-2.37/build/dlfcn/libdl.so.2 /lib64/libdl.so.2
[root@ecs build]# sln /usr/local/src/glibc-2.37/build/nptl/libpthread.so.0 /lib64/libpthread.so.0
[root@ecs build]# sln /usr/local/src/glibc-2.37/build/elf/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2
[root@ecs build]# make install

升级Linux内核

  1. 查看当前内核

    [root@ecs ~]# uname -r
    3.10.0-1160.53.1.el7.x86_64
  2. 安装kernel-ml

    下载地址:https://dl.lamp.sh/kernel/el7/

    [root@ecs ~]# rpm -ivh kernel-ml-6.9.10-1.el7.x86_64.rpm
    Preparing...                          ################################# [100%]
    Updating / installing...
    1:kernel-ml-6.9.10-1.el7           ################################# [100%]
    [root@ecs ~]# rpm -ivh kernel-ml-devel-6.9.10-1.el7.x86_64.rpm
    Preparing...                          ################################# [100%]
    Updating / installing...
    1:kernel-ml-devel-6.9.10-1.el7     ################################# [100%]
    [root@ecs ~]# rpm -e --nodeps kernel-headers
    [root@ecs ~]# rpm -ivh kernel-ml-headers-6.9.10-1.el7.x86_64.rpm
    Preparing...                          ################################# [100%]
    Updating / installing...
    1:kernel-ml-headers-6.9.10-1.el7   ################################# [100%]

    注:必须先卸载kernel-headers,否则无法安装kernel-ml-headers。卸载kernel-headers时要加上--nodeps以防止级联卸载glibc-devel等。

  3. 查看内核列表

    [root@ecs ~]# cat /boot/grub2/grub.cfg |grep ^menuentry
    menuentry 'CentOS Linux (6.9.10-1.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-6.9.10-1.el7.x86_64-advanced-14b582ff-4423-4795-9cf7-6ab774787c65' {
    menuentry 'CentOS Linux (3.10.0-1160.119.1.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-1160.119.1.el7.x86_64-advanced-14b582ff-4423-4795-9cf7-6ab774787c65' {
    menuentry 'CentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-1160.el7.x86_64-advanced-14b582ff-4423-4795-9cf7-6ab774787c65' {
    menuentry 'CentOS Linux (0-rescue-10ba6baecb054ccbad49bfb149680c75) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-0-rescue-10ba6baecb054ccbad49bfb149680c75-advanced-14b582ff-4423-4795-9cf7-6ab774787c65' {
  4. 设置启动内核

    [root@ecs ~]# grub2-set-default 0

    或者:

    [root@ecs ~]# grub2-set-default 'CentOS Linux (6.9.10-1.el7.x86_64) 7 (Core)'
  5. 生成grub2配置文件

    [root@ecs ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
    Generating grub configuration file ...
    Found linux image: /boot/vmlinuz-6.9.10-1.el7.x86_64
    Found initrd image: /boot/initramfs-6.9.10-1.el7.x86_64.img
    Found linux image: /boot/vmlinuz-3.10.0-1160.119.1.el7.x86_64
    Found initrd image: /boot/initramfs-3.10.0-1160.119.1.el7.x86_64.img
    Found linux image: /boot/vmlinuz-3.10.0-1160.el7.x86_64
    Found initrd image: /boot/initramfs-3.10.0-1160.el7.x86_64.img
    Found linux image: /boot/vmlinuz-0-rescue-10ba6baecb054ccbad49bfb149680c75
    Found initrd image: /boot/initramfs-0-rescue-10ba6baecb054ccbad49bfb149680c75.img
    done
  6. 重启

    [root@ecs ~]# reboot
  7. 查看默认启动内核

    [root@ecs ~]# grubby --default-kernel
    /boot/vmlinuz-6.9.10-1.el7.x86_64

Umami是一款开源、注重隐私的网络分析工具,可作为谷歌分析的替代方案。它能提供有关网站流量、用户行为和性能的重要见解,同时始终将数据隐私放在首位。不收集或存储个人数据,无需使用Cookie,并且符合GDPR(General Data Protection Regulation,通用数据保护条例)和PECR(Privacy and Electronic Communications Regulations,隐私与电子通信条例)标准。

Github:https://github.com/umami-software/umami

官网网址:https://umami.is/

Docker部署umami

手动部署

  1. 运行postgres

    [root@ecs ~]# docker run --name umami-db \
    --privileged=true \
    -e POSTGRES_DB=umami_db \
    -e POSTGRES_USER=umami \
    -e POSTGRES_PASSWORD=Ri71kHISuS9mDVBF \
    -p 5432:5432 \
    -v /data/umami/data:/var/lib/postgresql/data \
    -d postgres:16.13-alpine
  2. 编译docker镜像(非必须,可以直接使用官方docker镜像)

    [root@ecs ~]# cd /usr/local/src
    [root@ecs src]# wget https://tvv.tw/https://github.com/umami-software/umami/archive/refs/tags/v3.2.0.tar.gz
    [root@ecs src]# tar -zxvf v3.2.0.tar.gz
    [root@ecs src]# cd umami-3.2.0
    [root@ecs umami-3.2.0]# sed -i '/ARG NODE_IMAGE_VERSION/s/^/# /; /ARG NODE_IMAGE_VERSION/a\ARG NODE_IMAGE_VERSION="22-bookworm-slim"' Dockerfile
    [root@ecs umami-3.2.0]# sed -i '/apk add/d' Dockerfile
    [root@ecs umami-3.2.0]# sed -i '/npm install -g pnpm/s#$# \&\& pnpm config set registry https://registry.npmmirror.com#' Dockerfile
    [root@ecs umami-3.2.0]# sed -i 's#https://#https://tvv.tw/https://#g' scripts/build-geo.js
    [root@ecs umami-3.2.0]# sed -i -e '/ARG BASE_PATH/a\ARG SKIP_BUILD_GEO' -e '/ENV DATABASE_URL/a\ENV SKIP_BUILD_GEO=\${SKIP_BUILD_GEO:-0}' Dockerfile
    [root@ecs umami-3.2.0]# docker build \
    --progress=plain \
    --no-cache \
    --build-arg SKIP_BUILD_GEO=1 \
    --build-arg NODE_IMAGE_VERSION=22-bookworm-slim \
    -t umami .

    说明:

    • /ARG NODE_IMAGE_VERSION/:查找ARG NODE_IMAGE_VERSION所在行;
    • s/^/# /:在行开头添加#
    • ;:执行下一个命令;
    • /ARG NODE_IMAGE_VERSION/a:在ARG NODE_IMAGE_VERSION下一行追加内容。
  3. 运行umami

    [root@ecs ~]# wget https://tvv.tw/https://raw.githubusercontent.com/GitSquared/node-geolite2-redist/master/redist/GeoLite2-City.tar.gz -P /usr/local/src/
    [root@ecs ~]# mkdir -p /data/umami/{geo,data}
    [root@ecs ~]# tar -zxvf /usr/local/src/GeoLite2-City.tar.gz -C /usr/local/src/
    [root@ecs ~]# mv /usr/local/src/GeoLite2-City_20260703/GeoLite2-City.mmdb /data/umami/geo/
    [root@ecs ~]# docker run --name umami \
    --restart=always \
    -p 3000:3000 \
    -e DATABASE_URL=postgresql://umami:Ri71kHISuS9mDVBF@db:5432/umami_db \
    -e DATABASE_TYPE=postgresql \
    -e APP_SECRET=$(openssl rand -hex 8) \
    --link umami-db:db \
    -v /data/umami/geo/GeoLite2-City.mmdb:/app/geo/GeoLite2-City.mmdb:ro \
    -d docker.umami.is/umami-software/umami:postgresql-latest

    访问地址:http://47.101.38.23:3000/,初始用户名:admin,密码:umami

  4. nginx反向代理设置

    {
        location /
        {
            proxy_pass http://127.0.0.1:3000;
            proxy_http_version 1.1;
    
            # WebSocket支持
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
    
            proxy_set_header Host $host;
            proxy_set_header X-real-ip $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
    
            # 将客户端原始请求协议(HTTP/HTTPS)传递给Next.js
            proxy_set_header X-Forwarded-Proto $scheme;
    
            # 将客户端真实请求域名传递给Next.js(解决Origin不匹配)
            proxy_set_header X-Forwarded-Host $host; 
            proxy_set_header X-Forwarded-Port $server_port;
        }
    }

自动部署

  1. 编写docker-compose.yaml

    postgres版

    version: '3'
    services:
      umami:
        image: ghcr.nju.edu.cn/umami-software/umami:postgresql-latest
        ports:
        - "3000:3000"
        environment:
          DATABASE_URL: postgresql://umami:Ri71kHISuS9mDVBF@db:5432/umami_db
          DATABASE_TYPE: postgresql
          APP_SECRET: fVBjK4ZA5LUDZgFB
        volumes:
        - /data/umami/geo/GeoLite2-City.mmdb:/app/geo/GeoLite2-City.mmdb:ro
        depends_on:
        - db
        restart: always
      db:
        image: postgres:16.13-alpine
        environment:
          POSTGRES_DB: umami_db
          POSTGRES_USER: umami
          POSTGRES_PASSWORD: Ri71kHISuS9mDVBF
        volumes:
        - /data/umami/data:/var/lib/postgresql/data
        restart: always

    mysql版

    version: '3'
    services:
      umami:
        image: docker.umami.is/umami-software/umami:mysql-latest
        ports:
        - "3000:3000"
        environment:
          DATABASE_URL: mysql://umami:Ri71kHISuS9mDVBF@db:3306/umami_db
          DATABASE_TYPE: mysql
          APP_SECRET: fVBjK4ZA5LUDZgFB
        volumes:
        - /data/umami/geo/GeoLite2-City.mmdb:/app/geo/GeoLite2-City.mmdb:ro
        depends_on:
        - db
        restart: always
     
      db:
        image: mysql:8.0
        ports:
        - "3306:3306"
        environment:
          MYSQL_ROOT_PASSWORD: Ri71kHISuS9mDVBF
          MYSQL_DATABASE: umami_db
          MYSQL_USER: umami
          MYSQL_PASSWORD: Ri71kHISuS9mDVBF
        volumes:
        - /data/umami/data:/var/lib/mysql
        - /data/umami/log:/var/log/mysql
        restart: always

    注:umami 2.x使用mysql数据库,mysql仅支持8.0及以上版本。目前(截止到2026年7月8日)官网的umami:mysql-latest的版本为2.20.2,而官网的umami:postgresql-latest最新版本为3.2.0,最新版本性能更佳,推荐使用postgres的umami。

    如果需要使用1.x版本,可以使用:ghcr.io/mikecao/umami:mysql-latest(1.33.2),Github:https://github.com/mikecao/umami/pkgs/container/umami/versions

    version: '3'
    services:
      umami:
        image: ghcr.nju.edu.cn/mikecao/umami:mysql-latest
        ports:
        - "3000:3000"
        environment:
          DATABASE_URL: mysql://umami:Ri71kHISuS9mDVBF@db:3306/umami_db
          DATABASE_TYPE: mysql
          HASH_SALT: fVBjK4ZA5LUDZgFB
        volumes:
        - /data/umami/geo/GeoLite2-City.mmdb:/app/public/geo/GeoLite2-Country.mmdb:ro
        depends_on:
        - db
        restart: always
     
      db:
        image: mysql:5.7
        ports:
        - "3306:3306"
        environment:
          MYSQL_ROOT_PASSWORD: Ri71kHISuS9mDVBF
          MYSQL_DATABASE: umami_db
          MYSQL_USER: umami
          MYSQL_PASSWORD: Ri71kHISuS9mDVBF
        volumes:
        - /data/umami/data:/var/lib/mysql
        - /data/umami/log:/var/log/mysql
        restart: always
  2. 运行

    [root@ecs umami]# docker compose up -d

Node.js部署umami

  1. 源码编译

    [root@ecs ~]# npm -g install pnpm --registry=https://registry.npmmirror.com
    [root@ecs ~]# pnpm config set registry https://registry.npmmirror.com
    [root@ecs ~]# cd /usr/local/src
    [root@ecs src]# wget https://tvv.tw/https://github.com/umami-software/umami/archive/refs/tags/v3.2.0.tar.gz
    [root@ecs src]# tar -zxvf v3.2.0.tar.gz
    [root@ecs src]# mv umami-3.2.0 /data/umami-3.2.0
    [root@ecs src]# cd /data/umami-3.2.0
    [root@ecs umami-3.2.0]# pnpm install
    [root@ecs umami-3.2.0]# cat > .env << EOF
    DATABASE_URL=postgresql://umami:Ri71kHISuS9mDVBF@192.168.1.3:5432/umami_db
    EOF
    [root@ecs umami-3.2.0]# export SKIP_BUILD_GEO=1
    [root@ecs umami-3.2.0]# pnpm build
    [root@ecs umami-3.2.0]# pnpm start

    注:必须先pnpm build编译生成生产环境代码(生成到.next目录)才能pnpm start

  2. 使用pm2管理进程

    [root@ecs src]# npm -g install pm2 --registry=https://registry.npmmirror.com
    [root@ecs src]# pm2 start pnpm --name umami -- start
    [root@ecs src]# pm2 startup
    [root@ecs src]# pm2 save

    pm2其它启动方式:

    • pm2 start "pnpm start" --name umami
    • pm2 start npx --name "umami" -- next start -p 3000 -H 0.0.0.0
    • pm2 start npm --name "umami" -- start

    说明:

    • pm2 start pnpm --name umami -- start最后的--是参数分隔符,表示属于pm2的参数到此为止,--后面所有的内容都传递给启动脚本或程序(pnpm);
    • pm2 startup设置开机自启动pm2,可以使用pm2 unstartup systemd取消开机自启动;
    • 设置了开机自启动后,必须使用pm2 save生成进程快照,当服务器重启后,pm2可以根据进程快照重新拉起所有业务程序。
  3. 修改项目端口

    umami默认监听3000端口,可以使用以下方法将umami运行在其它端口:

    • 启动命令中指定端口号

      [root@ecs src]# npx next start -p 3000

      pm2启动进程使用:

      [root@ecs src]# pm2 start npx --name umami -- next start -p 3000 -H 0.0.0.0
    • start-env脚本调用环境变量

      [root@ecs src]# sed -i "2s/next-start/next-start.js/" /data/umami-3.2.0/scripts/start-env.js
      [root@ecs src]# HOSTNAME=0.0.0.0 && pnpm start-env

      pm2启动进程使用:

      [root@ecs src]# pm2 start "HOSTNAME=0.0.0.0 && pnpm start-env" --name umami

      分析/data/umami-3.2.0/scripts/start-env.js源码:

      import 'dotenv/config';
      import cli from 'next/dist/cli/next-start';
      
      cli.nextStart({
          port: process.env.PORT || 3000,
          hostname: process.env.HOSTNAME || '0.0.0.0',
      });

      注:

      • 必须给next/dist/cli/next-start添加.js后缀,否则会找不到引入文件;
      • PORT可以直接在.env中指定,但是HOSTNAME必须在命令行中指定。CentOS 7.9默认在/etc/profile设置了HOSTNAME的值为当前机器名,dotenv默认保护机制不允许.env中设置的环境变量覆盖process.env中已存在的值,因此在.env中设置HOSTNAME将无效,而如果HOSTNAME使用机器名,将无法进行外网访问。

使用

事件跟踪

  • css调用

    要启用事件跟踪,只需给跟踪的元素添加一个特殊的css类。

    示例:

    umami 2.x/3.x:

    <button id="signup" class="button" data-umami-event="signup" data-umami-event-foo="bar">Sign up</button>

    umami 1.x:

    <button id="signup" class="button umami--click--signup">Sign up</button>

    当用户点击按钮时,Umami将记录一个名为signup的事件,事件类型为click

    注:umami 1.x的事件类型仅支持click,即类的有效值必须是umami--click--<事件名称>

  • javascript调用

    示例:

    umami 2.x/3.x:

    const button = document.getElementById('signup');
    button.onclick = () => umami.track('signup', {foo: 'bar'});

    umami 1.x:

    const button = document.getElementById('signup');
    button.onclick = () => umami('signup');

    当用户点击按钮时,Umami将记录一个名为signup的事件,事件类型为custom

    注:umami 1.x不支持事件数据,即umami('事件名', {})这样的调用方式是无效的。

  1. 备份设置

    打开Beyond Compare 3,在主界面工具栏依次点击:工具->导出设置,将当前设置备份一份;

  2. 删除授权

    Windows 7及以上操作系统,打开目录:
    C:\Users\Administrator\AppData\Roaming\Scooter Software\Beyond Compare 3\

    Windows XP打开目录:
    C:\Documents and Settings\Administrator\Application Data\Scooter Software\Beyond Compare 3\

    删除目录内的所有文件;

    macOS删除文件:
    /Users/$(whoami)/Library/Application Support/Beyond Compare/registry.dat

    注:macOS仅需删除registry.dat即可,无需重新授权。

  3. 重新授权

    重新打开Beyond Compare 3,自动进入初始化向导,在导入设置页面导入第1步备份的设置文件。

    进入主界面后,在工具栏依次点击:帮助->输入密钥,复制以下密钥粘贴到输入框:

    --- BEGIN LICENSE KEY ---
    mv1nPlXAywBDCdhxFc9QOVv6TBcQHLAXBQUAKTh3ie4fqSEOnWrPsnVkF
    yt0wAkJHweoExRJWWVwwCniKNROSdJzJXE6YVapYW7f+tRRXRFI4yn4Nj
    jZ0RiiqGRCTVzwComUcXB-eiFWRBY6JpSsCNkmIxL5KsRCo442djHhTZE
    --- END LICENSE KEY -----

    勾选“注册到所有用户”后点击“确认”退出即可。