标签 Docker 下的文章

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('事件名', {})这样的调用方式是无效的。

安装Postgres

Docker部署

  1. 拉取镜像

    [root@ecs ~]# docker pull registry.cn-shanghai.aliyuncs.com/maisi/postgres:16.13-alpine
    [root@ecs ~]# docker tag registry.cn-shanghai.aliyuncs.com/maisi/postgres:16.13-alpine postgres:16.13-alpine
    [root@ecs ~]# docker rmi registry.cn-shanghai.aliyuncs.com/maisi/postgres:16.13-alpine
  2. 创建容器

    [root@ecs ~]# docker run --name postgres \
    --privileged=true \
    -e POSTGRES_PASSWORD=wfb88Os2xBk0sUQH \
    -p 5432:5432 \
    -v /data/postgresql:/var/lib/postgresql/data \
    -d postgres:16.13-alpine

PSQL操作

  • 登录Postgres

    [root@ecs ~]# docker exec -it postgres /bin/bash
    dc4d208b728b:/# psql -U postgres;
    psql (16.13)
    Type "help" for help.
    
    postgres=# 

    常见指令说明:

    命令说明
    \l,\l+列出所有数据库
    \du列出所有用户
    \c [db-name]切换数据库
    \d列出数据库内所有内容,包含table、sequence等
    \dt列出数据库内所有表
    \d [table-name]查看表结构
    \q退出登录
  • 创建用户

    postgres=# create user maisi with password 'Ri71kHISuS9mDVBF';
    CREATE ROLE

    或者:

    postgres=# create role maisi password 'Ri71kHISuS9mDVBF' login;
    CREATE USER is now an alias for CREATE ROLE. The only difference is that when the command is spelled CREATE USER, LOGIN is assumed by default, whereas NOLOGIN is assumed when the command is spelled CREATE ROLE.

    create user默认有login权限,而create role默认没有login权限,需要单独指定。

  • 删除用户

    postgres=# drop user maisi;
  • 创建数据库

    postgres=# create database maisi_db with owner maisi;
    CREATE DATABASE
  • 授权数据库

    postgres=# grant all on database maisi_db to maisi;
    GRANT

    这是数据库级别的授权,权限包括:CONNECT(允许连接数据库)、CREATE(允许在数据库创建schema)和TEMPORARY(允许在数据库创建临时表),不会授予任何表的权限,即只能连上数据库,看不到、也操作不了数据库里面的任何表。

  • 授权表

    postgres=# \c maisi_db;
    You are now connected to database "maisi_db" as user "postgres".
    maisi_db=# grant select,insert,update,delete on all tables in schema public to maisi;
    GRANT

    这是表级别的授权,作用对象是public schema下的已存在的所有表。权限包括:SELECT、INSERT、UPDATE和DELETE,不包含TRUNCATE、REFERENCES、TRIGGER等权限。

    注:只对当前已存在的表生效,新建的表不会自动获得授权。

    自动授权新建表

    默认情况下,maisi用户新创建的表,maisi自动拥有所有权限,但如果是其他用户创建的表,maisi用户就没有操作权限了。可以通过alter default privileges将其他用户新创建的表自动授权给maisi用户。

    postgres=# \c maisi_db;
    maisi_db=# alter default privileges in schema public grant select,insert,update,delete on tables to maisi;
    ALTER DEFAULT PRIVILEGES
    maisi_db=# \ddp
             Default access privileges
     Owner | Schema | Type | Access privileges 
    -------+--------+------+-------------------
    (0 rows)

    maisi(被授权用户)=a(INSERT)r(SELECT)w(UPDATE)d(DELETE)/postgres(授权用户):postgres用户在maisi_db数据库下创建的表,自动将arwd权限授权给maisi用户。

  • 撤回表授权

    postgres=# revoke select,insert,update,delete on all tables in schema public from maisi;
    REVOKE
  • 撤回数据库授权

    postgres=# revoke all on database maisi_db from maisi;
    REVOKE
    postgres=# revoke all on database maisi_db from public;
    REVOKE

    注:如果只是revoke from maisi,依旧可以使用psql -U maisi -d maisi_db;登录数据库,因为数据库默认授权给public角色CONNECT权限,必须revoke from public后才可以彻底拒绝maisi用户登录maisi_db数据库。

  • 登录数据库

    maisi_db=# \q
    dc4d208b728b:/# psql -U maisi -d maisi_db;
    psql (16.13)
    Type "help" for help.
    
    maisi_db=> 

    注:不指定数据库的情况下,默认会登录到和用户名同名的数据库。如果数据库名称和用户名不一致,必须指定数据库名称,否则会报错。

  • 创建表

    maisi_db=> create table "user"(
      id serial primary key,
      username varchar(255) unique not null,
      password varchar(50) not null,
      created_at timestamp with time zone default current_timestamp,
      updated_at timestamp with time zone default current_timestamp
    );
    CREATE TABLE
  • 插入记录

    maisi_db=> insert into "user"(username,password) values('root','password');
    INSERT 0 1
  • 导入sql

    dc4d208b728b:/# cat > /tmp/user.sql << 'EOF'
    drop table if exists "user";
    
    create table "user" (
      id serial primary key,
      username varchar(255) unique not null,
      password varchar(60) not null,
      created_at timestamp with time zone default current_timestamp,
      updated_at timestamp with time zone default current_timestamp
    );
    
    create index user_id_idx on "user"(id);
    
    insert into "user"(username, password) values ('admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa');
    EOF
    dc4d208b728b:/# psql -h 127.0.0.1 -p 5432 -U maisi -d maisi_db -W -f /tmp/user.sql

    注:shell中``表示执行命令,并将命令输出替换当前位置,如`user`是执行user命令,但linux没有user命令,因此就会报错。如果需要保留`user`,只需将EOF改成'EOF'即可。

    在postgres中,user是保留关键字,不能直接用来作为表名,如必须使用user作为表名,必须使用""强制转义。

  • 查询记录

    maisi_db=> select * from "user";
  • 删除表

    maisi_db=> drop table "user";
    DROP TABLE
  • 修改数据库所有者

    maisi_db=> \q
    dc4d208b728b:/# psql -U postgres;
    psql (16.13)
    Type "help" for help.
    
    postgres=# alter database maisi_db owner to postgres;
    ALTER DATABASE
    postgres=# select datname,rolname from pg_database d join pg_roles r on d.datdba=r.oid order by d.datname;
      datname  | rolname  
    -----------+----------
     postgres  | postgres
     template0 | postgres
     template1 | postgres
     maisi_db  | postgres
    (4 rows)

    注:修改数据库的所有者后,使用psql -U maisi -d maisi_db;仍可以登录,而且依旧可以用maisi用户对表进行SELECT、INSERT、UPDATE和DELETE操作,这是因为数据库默认会将CONNECT权限授予给PUBLIC角色(所有用户),而且之前grant select,insert,update,delete on all tables in schema public to maisi将表操作权限授予给maisi用户,因此即使更改数据库的所有者,也不影响表授权。

  • 删除数据库

    postgres=# drop database maisi_db;
    DROP DATABASE

    注:如果删除的数据库是当前登录的数据库,则必须先试用\q退出登录,然后登录到postgres后再删除数据库。

命令行操作

  • 创建数据库

    https://www.postgresql.org/docs/current/app-createdb.html

    语法:createdb [connection-option...] [option...] [dbname [description]]

    dc4d208b728b:/# createdb -h 127.0.0.1 -p 5432 -U postgres -e maisi_db "maisi"
    SELECT pg_catalog.set_config('search_path', '', false);
    CREATE DATABASE maisi_db;
    COMMENT ON DATABASE maisi_db IS 'maisi';
    参数值描述
    -h host指定服务器主机名
    -p port指定服务器监听端口
    -U username连接数据库用户名
    -w忽略输入密码(前提是没有设置用户密码)
    -W连接时强制要求输入密码
    非必须,如果用户设置了密码,createdb会自动提示输入密码。但是createdb会先尝试一次连接才发现服务器需要验证密码,造成额外的连接尝试
    -D tablespace指定数据库默认表空间
    -e,--echo显示创建过程中的交互信息
    -E encoding指定数据库编码
    -l locale指定数据库语言
  • 删除数据库

    https://www.postgresql.org/docs/current/app-dropdb.html

    语法:dropdb [connection-option...] [option...] [dbname]

    dc4d208b728b:/# dropdb -h 127.0.0.1 -p 5432 -U postgres maisi_db