权限系统探索-快速部署OpenFGA

504 阅读5分钟

配置Openfga

部署Openfga之前,我们需要先去配置数据库,不然的话,会默认使用内存作为数据库。

OpenFGA支持多种数据库,如PostgreSQL 14, MySQL 8 和 SQLite。

官方提供了三种配置方式:

  1. 配置文件
  2. 环境变量
  3. 命令行

本文只介绍配置文件,其他配置可参考openfga.dev/docs/gettin…

配置文件的方式: 在本地启动的话,新建一个config.yaml放到项目文件夹的工作区中 内容如下:

datastore:
  engine: postgres
  uri: postgres://user:password@localhost:5432/mydatabase

authn:
  method: preshared
  preshared:
    keys: ["key1", "key2"]

上面是指定数据库引擎以及数据库配置,下面是用于客户端的权限配置,权限配置可以参考(openfga.dev/docs/gettin…

部署

打包部署OpenFGA

部署到Linux的话,需要将项目打包为二进制文件,然后上传到Linux下

可以使用打包命令


    go build // 在openfga目录下,默认打出的可执行文件名称是openfga(windows 是 openfga.exe)
    
    go build main.go //这个命令,打出的是名称是 main(windows 是 main.exe)
    
    go build -o myapp // 这个命令指定打出的可执行文件名称是 myapp
    
    go build -o ./bin/myapp //指定文件的生成路径

将生成的二进制文件通过SFTP的工具,移动到linux的文件目录下,这里我放到了/opt/openfga目录下。

配置文件

将配置文件放到指定目录下。

我们需要将我们之前写好的配置文件config.yaml放到openfga可以找到的地方。

openfga默认通过三种方式并且按照顺序查找配置文件:

  • /etc/openfga
  • $HOME/.openfga
  • 当前工作的文件夹目录。

放好文件后,我们启动openFGA.

启动Openfga

我们移动到openFGA的二进制文件目录下,使用命令

./openfga run

即可成功运行。

启动脚本

对于启动的命令,我们可以封装为一个脚本start_openfga.sh,下次直接运行脚本即可,以下是DeepSeek生成的脚本,用于启动OpenFGA并且追加日志

#!/bin/bash

# 定义日志文件路径
LOG_DIR="/opt/openfga/logs"
LOG_FILE="$LOG_DIR/openfga.log"

# 检查日志文件所在目录是否存在,如果不存在则创建
if [ ! -d "$LOG_DIR" ]; then
  mkdir -p "$LOG_DIR"
fi

# 检查日志文件是否存在,如果不存在则创建空文件
if [ ! -f "$LOG_FILE" ]; then
  touch "$LOG_FILE"
fi

# 启动 OpenFGA 服务并将输出重定向到日志文件
nohup ./openfga run --profiler-enabled --profiler-addr :3002 --playground-enabled --playground-port 3001 &> "$LOG_FILE" &

# 输出提示信息
echo "OpenFGA 服务已在后台启动,日志输出到 $LOG_FILE"

~                                    

对于脚本的启动也可以使用命令重命名来快速启动,并且打印日志

alias startOpenfga='/opt/openfga/start_openfga.sh && tail -f /opt/openfga/logs/openfga.log'

部署时可能会遇到的问题

1. openfga启动不了

如果提示 bash: ./main: cannot execute binary file: Exec format error

一般这种情况是可执行文件的架构与 Linux 服务器的架构不兼容。检查一下打的包是否正确:

在服务器上执行:

uname -m

常见输出:

  • x86_64:64 位 Intel/AMD 系统。
  • aarch64:64 位 ARM 架构(如 AWS Graviton、树莓派 4)。

然后检查可执行文件的架构

在本地编译的 main 文件所在目录执行:

file main

输出示例:

  • 不兼容的情况

    main: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows  # 这是 Windows 可执行文件
    main: Mach-O 64-bit executable x86_64                                               # 这是 macOS 可执行文件
    
  • 兼容的情况

    main: ELF 64-bit LSB executable, x86-64, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=...
    

解决方法 指定编译环境

# 明确指定目标平台(如 Linux x86_64) 
GOOS=linux GOARCH=amd64 go build -o openfga

2. 与数据库数据不一致(数据为空)

这种情况一般是config.yaml未能放到正确的位置,检查一下config.yaml是否符合上述所描述的三种位置(我一般建议你直接放到/etc/openfga/目录下)

3. OpenFGA的PlayGround用不了

访问 http://ip/3000/playground, 却发现弹出以下提示:

QQ_1739243286791.png

打开控制台发现:

QQ_1739243297321.png

到这里你可能怀疑是不是哪一步出错了,不,你没错!

到目前这一步说明Openfga成功部署了,问题在于playground上面,playground只允许localhost进行访问。

官网中的描述:

QQ_1739247782190.png

通过在OpenFGA的issue上面的查找,发现

QQ_1739247840177.png

OpenFGA的开发人员认为,将playground暴露给其他ip可能会有安全的风险(因为其他人可以直接改动目前正在使用的model的Tuple数据)

也就是说我们将Openfga部署到服务器上后,无法在除了服务器本身的地方使用playground。

但同时官方也提供了替代方案:

QQ_1739247960097.png

详情可以查看一下这个issue:github.com/openfga/ope…

但是官方其实将palyground开源加入了他们的计划表(优先级不高)

QQ_1739259978156.png

详细讨论可以参照 github.com/orgs/openfg…

OpenFGA的项目经理认为这个功能非核心,且由于使用了他们的内部库,导致如果要开源就要大量的重写代码。

但是也有人提出了其中一种访问服务器playground的解决方法,如下:

QQ_1739260156555.png

OpenFGA的局限性

水平部署

目前OpenFGA没有提供水平多机部署的示例,不过应该也很好做,找一个负载均衡的中间件使用即可

分布式缓存

目前OpenFGA还不支持分布式缓存,这导致多机部署的话,缓存会出现不一致性(缓存失效的通知问题,或者分布式缓存)。

官方目前已经将分布式缓存加入了roadmap中,但是不知道什么时候能够上线,所以后续我们开发时,就需要自己修改OpenFGA源码去实现分布式缓存。

QQ_1739248361588.png

补充

stop脚本

我之前是直接使用打好的包进行启动的,所以可以使用一个shell脚本来执行stop操作。

shell脚本如下


#!/bin/bash

# 脚本功能:精准停止所有通过 "main" 进程运行的 OpenFGA 服务
# 适用场景:二进制部署的 OpenFGA,进程名称为 main

# -------------------------- 核心逻辑 --------------------------
# 1. 精准匹配进程(避免误杀其他名为 main 的进程)
#    假设 OpenFGA 启动命令包含特征参数(例如 "--config")
OPENFGA_PIDS=$(pgrep -f "main.*(run|start|--config)")

if [ -z "$OPENFGA_PIDS" ]; then
    echo "No running OpenFGA (main) processes found."
    exit 0
fi

# 2. 优雅终止进程
echo "Stopping OpenFGA processes (PIDs: $OPENFGA_PIDS)..."
kill -15 $OPENFGA_PIDS  # 发送 SIGTERM 信号

# 3. 等待进程退出(最长等待 10 秒)
timeout=10
end_time=$(( $(date +%s) + timeout ))

for PID in $OPENFGA_PIDS; do
    # 等待进程自然退出
    while [ $(date +%s) -lt $end_time ]; do
        if ! ps -p $PID > /dev/null; then
            echo "Process $PID exited gracefully."
            break
        fi
        sleep 1
    done

    # 超时后强制终止
    if ps -p $PID > /dev/null; then
        echo "Force killing PID $PID (did not exit in $timeout seconds)..."
        kill -9 $PID
    fi
done

echo "All OpenFGA (main) processes stopped."

重启脚本

直接将关闭和启动合二为一,先关闭,再启动

#!/bin/bash
# OpenFGA 重启脚本(支持自定义二进制名称)

# 配置区(用户可修改以下变量)
BINARY_NAME="openfga"        # 在此处修改二进制文件名
LOG_DIR="/opt/openfga/logs"  # 日志目录
LOG_FILE="$LOG_DIR/${BINARY_NAME}.log"  # 自动生成日志路径

# 获取精确进程ID
get_pid() {
  pgrep -f "^\./${BINARY_NAME} run"  # 精确匹配启动命令
}

# 终止旧进程
stop_service() {
  local pid=$(get_pid)
  [ -n "$pid" ] && {
    echo "终止运行中的 $BINARY_NAME (PID: $pid)"
    kill -15 $pid 2>/dev/null && sleep 2
    # 双重确认强制终止
    if pgrep -f "^\./${BINARY_NAME} run" >/dev/null; then
      kill -9 $pid 2>/dev/null
      sleep 1
    fi
  }
}

# 启动新进程
start_service() {
  # 确保日志目录存在
  mkdir -p "$LOG_DIR"
  [ ! -f "$LOG_FILE" ] && touch "$LOG_FILE"
  
  nohup "./${BINARY_NAME}" run --profiler-enabled --profiler-addr :3002 \
    --playground-enabled --playground-port 3001 &>> "$LOG_FILE" &
}

# 主流程
stop_service
start_service

# 结果检查
if get_pid >/dev/null; then
  echo "重启成功!PID: $(get_pid), 日志: $LOG_FILE"
else
  echo "重启失败,请检查日志: $LOG_FILE"
  exit 1
fi