Pama

PostHog 自开发部署

· 约 12 分钟读完

目录

写在前面

自部署 PostHog 实际上非常有难度且繁琐,本质上是 PostHog 对于自部署的支持十分有限,官方也并不希望用户自行部署(不便于卖他的 Cloud 服务 😂)。

下面的流程主要是如何在本机(macOS 或 Unix 类机器)上启动一个可以二次开发的部署。

如果你希望将自部署的 PostHog 用于生产(或预生产)环境,推荐使用官方提供的 Ubuntu 一键部署脚本,可以迅速启动一个完整的单机 PostHog,具备自部署的完整功能。

如果希望得到更多 PostHog 中文社区的帮助,可以联系 me@jiake.li。

❗️ 本指南仅适用于 PostHog 本身的开发。如果你想部署 PostHog 用于产品分析需求,请参考官方的自托管文档。

PostHog 内部结构是什么样的?

在开始设置之前,先了解 PostHog 的组成部分。该应用由 4 个同时运行的组件组成:

  • Celery worker(处理后台任务)
  • Django 服务器
  • Node.js 插件服务器(处理事件接收和应用/插件)
  • 使用 Node.js 构建的 React 前端

这些组件依赖于以下外部服务:

  • ClickHouse:存储大数据(事件、用户、分析查询)
  • Kafka:事件接收队列
  • MinIO:存储文件(会话录制、文件导出)
  • PostgreSQL:存储普通数据(用户、项目、保存的洞察)
  • Redis:缓存和服务间通信
  • Zookeeper:协调 Kafka 和 ClickHouse 集群

启动 PostHog 开发实例时,推荐以下配置:

  • 外部服务通过 docker compose 在 Docker 中运行
  • PostHog 本身在主机(你的系统)上运行

下面的指南就使用这种配置。

技术上也可以完全在 Docker 中运行 PostHog,但同步更改会变得更慢,而且为了开发,你仍然需要在主机上安装 PostHog 的依赖(如格式化或类型检查工具)。另一种方式是完全在主机上运行所有内容,但从头搭建 Kafka 或 ClickHouse 太复杂,实践中不可行。

本指南假设你运行的是 macOS 或当前的 Ubuntu Linux LTS(24.04)。其他 Linux 发行版请按需调整步骤(例如用 dnf 或 pacman 代替 apt)。

Windows 本身不支持,但可以运行 Linux 虚拟机,推荐最新的 Ubuntu LTS Desktop(不推荐 Ubuntu Server,因为调试前端需要能访问 localhost 的浏览器)。

选项一:使用 Codespaces 开发

这是更快的入门方式,适合快速试一试。想完整搭建请看选项二。

  1. 创建你的 codespace。

    创建 codespace

  2. 把机器类型改为 8 核(最小配置可能跑不动 PostHog)。

    选择机器类型

  3. 用列表中的 "Open in" 选项之一打开 codespace。

  4. 在 codespace 中打开终端,运行 docker compose -f docker-compose.dev.yml up。

  5. 在另一个终端中运行 pnpm i(后续命令都在这个终端里执行)。

  6. 运行 pip install -r requirements.txt -r requirements-dev.txt。

  7. 运行 ./bin/migrate,然后运行 ./bin/start。

  8. 在浏览器中打开 http://localhost:8010/。

  9. 想要一些测试数据,运行 DEBUG=1 ./manage.py generate_demo_data。

选项二:本地开发

先决条件

macOS

  1. 安装 Xcode Command Line Tools:xcode-select --install。
  2. 按照官方说明安装 Homebrew。安装后务必按终端提示把 Homebrew 加入 $PATH,否则命令行找不到用 brew 安装的包。
  3. 用 brew install orbstack 安装 OrbStack,它是性能更好的 Docker Desktop 替代品。在 OrbStack 设置里把内存上限设为至少 4 GB(能给 8 GB 更好),CPU 上限设为至少 4 核(即 400%)。
  4. 继续下面的「克隆仓库」。

Ubuntu

  1. 按照官方说明安装 Docker。

  2. 安装 build-essential:

    sudo apt install -y build-essential
  3. 继续下面的「克隆仓库」。

克隆仓库

克隆 PostHog 仓库。后续命令都假设你在 posthog/ 目录中。

git clone https://github.com/PostHog/posthog && cd posthog/

即时设置(Flox)

你可以用 Flox 一步设置好开发环境。Flox 是一个开发环境管理器,负责管理开发 PostHog 所需的整个依赖关系图,这些依赖都声明在仓库的 .flox/manifest.toml 中。

  1. 安装 Flox(以及预提交检查要用的 ruff 和 rustup,它们在 Flox 环境之外):

    brew install flox ruff rustup && rustup-init && rustup default stable
  2. 在仓库根目录激活环境(首次激活时会询问是否用 direnv 自动激活):

    flox activate

这样就得到了一个功能齐全的环境,可执行文件和链接库都在 .flox/ 下,终端里会打印迁移和启动应用的说明。仓库内容的介绍可以看官方的项目结构文档。要提交更改,基于 master 新建分支开始开发即可。

手动设置

如果不想用 Flox,也可以手动设置环境。

1. 启动外部服务

这一步启动 PostHog 所需的全部外部服务。

首先把 127.0.0.1 kafka clickhouse 加到 /etc/hosts,否则 ClickHouse 和 Kafka 无法互相通信:

echo '127.0.0.1 kafka clickhouse' | sudo tee -a /etc/hosts

如果你用的是较新版本(4.1 及以上)的 Podman 而不是 Docker,主机的 /etc/hosts 默认会作为容器的基础 hosts 文件,这可能导致 ClickHouse 容器里的主机名解析失败。可以在 containers.conf 中设置 base_hosts_file="none" 解决。

然后启动 Docker Compose:

docker compose -f docker-compose.dev.yml up

几个常见问题:

  • 看到 Error while fetching server API version: 500 Server Error for http+docker://localhost/version,多半是 Docker Engine 没有运行。

  • 任何地方出现 Exit Code 137,说明容器内存不足,需要在 OrbStack 设置里分配更多内存。

  • 在 Linux 上可能需要 sudo,参见 Docker 文档中以非 root 用户管理 Docker 的部分,或者考虑用支持无根容器的 Podman。

  • 看到 Ports are not available: exposing port TCP 0.0.0.0:5432 ... address already in use,说明本机已经有 Postgres 在运行。可以用 lsof -i :5432 看是哪个进程,Linux 上可以这样停掉它:

    sudo service postgresql stop

接着用 docker ps 和 docker logs(或 OrbStack 面板)确认这些服务都在运行,日志大致如下:

# docker ps
CONTAINER ID   IMAGE                                      COMMAND                  CREATED          STATUS                    PORTS                                                                                            NAMES
5a38d4e55447   temporalio/ui:2.10.3                       "./start-ui-server.sh"   51 seconds ago   Up 44 seconds             0.0.0.0:8081->8080/tcp                                                                           posthog-temporal-ui-1
89b969801426   temporalio/admin-tools:1.20.0              "tail -f /dev/null"      51 seconds ago   Up 44 seconds                                                                                                              posthog-temporal-admin-tools-1
81fd1b6d7b1b   clickhouse/clickhouse-server:23.6.1.1524   "/entrypoint.sh"         51 seconds ago   Up 50 seconds             0.0.0.0:8123->8123/tcp, 0.0.0.0:9000->9000/tcp, 0.0.0.0:9009->9009/tcp, 0.0.0.0:9440->9440/tcp   posthog-clickhouse-1
f876f8bff35f   bitnami/kafka:2.8.1-debian-10-r99          "/opt/bitnami/script…"   51 seconds ago   Up 50 seconds             0.0.0.0:9092->9092/tcp                                                                           posthog-kafka-1
d22559261575   temporalio/auto-setup:1.20.0               "/etc/temporal/entry…"   51 seconds ago   Up 45 seconds             6933-6935/tcp, 6939/tcp, 7234-7235/tcp, 7239/tcp, 0.0.0.0:7233->7233/tcp                         posthog-temporal-1
5313fc278a70   postgres:12-alpine                         "docker-entrypoint.s…"   51 seconds ago   Up 50 seconds (healthy)   0.0.0.0:5432->5432/tcp                                                                           posthog-db-1
c04358d8309f   zookeeper:3.7.0                            "/docker-entrypoint.…"   51 seconds ago   Up 50 seconds             2181/tcp, 2888/tcp, 3888/tcp, 8080/tcp                                                           posthog-zookeeper-1
09add699866e   maildev/maildev:2.0.5                      "bin/maildev"            51 seconds ago   Up 50 seconds (healthy)   0.0.0.0:1025->1025/tcp, 0.0.0.0:1080->1080/tcp                                                   posthog-maildev-1
61a44c094753   elasticsearch:7.16.2                       "/bin/tini -- /usr/l…"   51 seconds ago   Up 50 seconds             9200/tcp, 9300/tcp                                                                               posthog-elasticsearch-1
a478cadf6911   minio/minio:RELEASE.2022-06-25T15-50-16Z   "sh -c 'mkdir -p /da…"   51 seconds ago   Up 50 seconds             9000/tcp, 0.0.0.0:19000-19001->19000-19001/tcp                                                   posthog-object_storage-1
91f838afe40e   redis:6.2.7-alpine                         "docker-entrypoint.s…"   51 seconds ago   Up 50 seconds             0.0.0.0:6379->6379/tcp                                                                           posthog-redis-1
 
# docker logs posthog-db-1 -n 1
2021-12-06 13:47:08.325 UTC [1] LOG:  database system is ready to accept connections
 
# docker logs posthog-redis-1 -n 1
1:M 06 Dec 2021 13:47:08.435 * Ready to accept connections
 
# docker logs posthog-clickhouse-1 -n 1
Saved preprocessed configuration to '/var/lib/clickhouse/preprocessed_configs/users.xml'.
 
# ClickHouse 的日志写在 /var/log/clickhouse-server/ 下,而不是 stdout/stderr,出问题时可以 cat 这些文件:
# docker exec posthog-clickhouse-1 cat /var/log/clickhouse-server/clickhouse-server.log
# docker exec posthog-clickhouse-1 cat /var/log/clickhouse-server/clickhouse-server.err.log
 
# docker logs posthog-kafka-1
[2021-12-06 13:47:23,814] INFO [KafkaServer id=1001] started (kafka.server.KafkaServer)
 
# docker logs posthog-zookeeper-1
# ClickHouse 和 Kafka 都连着 Zookeeper,所以这里会有很多日志,这是正常的。

Kafka 目前是唯一的 x86 容器,在 ARM 上可能会随机崩溃,遇到时重启它即可。

2. 安装本地 Postgres 工具

即使 Postgres 跑在 Docker 里,本机也需要装 Postgres(11 及以上)的 CLI 工具和开发库,pip 安装 psycopg2 时要用到。

  • macOS:

    brew install postgresql

    这会同时安装服务器和工具,装完不要启动服务器。

  • Debian 系 Linux:

    sudo apt install -y postgresql-client postgresql-contrib libpq-dev

    这里只装客户端和驱动,不装服务器。如果已经装了服务器,需要停掉它,因为它和 Postgres 容器抢同一个端口:sudo systemctl disable postgresql.service。

3. 准备 Django 服务器

  1. 安装 SAML 需要的依赖(详情见 xmlsec 仓库):

    • macOS:brew install libxml2 libxmlsec1 pkg-config。如果 xmlsec 装不上,试试把 macOS 升级到最新版本。
    • Debian 系 Linux:sudo apt install -y libxml2 libxmlsec1-dev libffi-dev pkg-config
  2. 安装 Python 3.11。

    • macOS:brew install python@3.11

    • Debian 系 Linux:

      sudo add-apt-repository ppa:deadsnakes/ppa -y
      sudo apt update
      sudo apt install python3.11 python3.11-venv python3.11-dev -y

    在 venv 外部始终用 python3 而不是 python,后者在某些系统上可能指向 Python 2。装了多个 Python 3 版本时用 python3.11。也可以用 pyenv 管理多个版本。

  3. 安装 uv。它是一个很快的 Python 虚拟环境和依赖管理工具,在任何 pip 命令前加上 uv 就能提速。

  4. 创建并激活虚拟环境:

    uv venv env --python 3.11
     
    # bash/zsh 等
    source env/bin/activate
    # fish
    source env/bin/activate.fish
  5. 升级 pip:

    uv pip install -U pip
  6. 安装依赖。如果你用的是 Apple Silicon Mac,第一次安装时需要指定 OpenSSL 头文件(grpcio 和 psycopg2 要用):

    brew install openssl
    CFLAGS="-I /opt/homebrew/opt/openssl/include $(python3.11-config --includes)" LDFLAGS="-L /opt/homebrew/opt/openssl/lib" GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=1 GRPC_PYTHON_BUILD_SYSTEM_ZLIB=1 uv pip install -r requirements.txt

    之后(或在其他机器上)直接运行:

    uv pip install -r requirements.txt -r requirements-dev.txt

4. 准备数据库

后端准备好了,Postgres 和 ClickHouse 也在运行,但数据库还是空的,需要运行迁移来建表:

cargo install sqlx-cli # 如果还没有安装
DEBUG=1 ./bin/migrate

5. 启动 PostHog

同时启动 PostHog 的所有组件(后端、worker、插件服务器和前端):

./bin/start

这个命令用 mprocs 在一个终端窗口里运行所有开发进程。

打开 http://localhost:8010 查看应用。

第一次运行时可能会报 "layout.html is not defined",等前端编译完再刷新即可。

想要测试数据,运行 DEBUG=1 ./manage.py generate_demo_data,加 --help 可以查看可用参数。


如果你只是想启动一个 PostHog 实例,下面的内容可以不用看了 😂

测试

要合并 PostHog 的 PR,所有测试都必须通过,最好还要补充新的测试,所以能方便地跑测试很重要。

前端

运行前端单元测试:

pnpm test:unit

只跑某个路径下的文件:

pnpm jest --testPathPattern=frontend/src/lib/components/IntervalFilter/intervalFilterLogic.test.ts

更新所有视觉回归测试快照前,先确保 Storybook 在运行(另开一个终端执行 pnpm storybook),可能还需要 pnpm exec playwright install。然后运行:

pnpm test:visual

只更新某个路径下的 stories 快照:

pnpm test:visual:update frontend/src/lib/Example.stories.tsx

后端

运行后端测试:

pytest

只跑某个文件:

pytest posthog/test/test_example.py

只跑匹配函数名的用例:

pytest posthog/test/test_example.py -k test_something

想看调试日志(例如 ClickHouse 查询),加上 --log-cli-level=DEBUG。

额外:使用特性标志

本地开发时设置 DEBUG=1(会启用 SELF_CAPTURE),本地实例的分析数据都来自实例本身,更准确地说是当前选中的项目。你的操作会立即反映在当前项目里,这对测试功能很有用。例如,开发实例启用了哪些特性标志,取决于你当前打开的项目。

所以,开发依赖特性标志 foo-bar 的功能时,在本地实例里新建这个 key 的特性标志并发布即可。

想一次性拥有 PostHog 里现有的所有特性标志,运行 DEBUG=1 python3 manage.py sync_feature_flags,它们会被添加到实例中的每个项目,默认全量开启。以 _EXPERIMENT 结尾的标志会自动设置成带 control 和 test 两个变体的多变量标志。

后端侧的标志只在本地评估,需要设置 POSTHOG_PERSONAL_API_KEY 环境变量,可以在用户设置里生成。

额外:使用 VS Code 调试

PostHog 仓库自带 VS Code 调试配置。在 VS Code 的「运行和调试」里选择想调试的服务运行即可,之后就可以打断点、单步执行。前端和后端测试也有对应的调试配置。

可以用主配置 "PostHog" 调试所有服务。如果你用 ./bin/start 跑了大部分服务、只想调试后端,记得先在启动脚本里临时注释掉该服务。

额外:在 PyCharm 中调试后端

借助 PyCharm 内置的 Django 支持,后端调试很容易配置,尤其适合跟踪一个请求从客户端一路到服务器的处理过程。

设置 PyCharm

  1. 打开仓库文件夹。
  2. 设置 Python 解释器(Settings > Project: posthog > Python Interpreter > Add Interpreter):选择 "Existing",路径设为 path_to_repo/posthog/env/bin/python。
  3. 设置 Django 支持(Settings > Languages & Frameworks > Django):
    • Django project root:path_to_repo
    • Settings:posthog/settings/__init__.py

启动调试环境

  1. 不用手动运行 docker compose,直接打开 docker-compose.dev.yml,点击 services 旁边的双播放图标。
  2. 在运行配置中选择:
    • "PostHog",点击调试
    • "Celery",点击调试(可选)
    • "Frontend",点击运行
    • "Plugin server",点击运行

额外:访问 Postgres

开发时可能需要直接连数据库查询或修改数据。用 pgAdmin 之类的工具,连接信息如下:host localhost,port 5432,database posthog,username posthog,password posthog。

额外:访问 Django Admin

如果打不开 http://localhost:8000/admin/,可能是你的本地用户不是 staff。连上数据库,找到你在 posthog_user 表里的记录,把 is_staff 设为 true 即可。