如何使用 PostgREST 和 Apache APISIX 构建高效、安全的 RESTful API 解决方案

作者:admin · 2025-12-06 · 阅读时间:14分钟
方案初试​ 该解决方案主要使用的是 PostgreSQL 数据库,PostgreSQL 是一个开源 SQL 数 […]

方案初试​

该解决方案主要使用的是 PostgreSQL 数据库,PostgreSQL 是一个开源 SQL 数据库。同时我们没有编写 REST API,而是使用了 PostgREST 组件。

PostgREST 是一个独立的 Web 服务器,它可以将 PostgreSQL 数据库直接转换为 RESTful API。如果你想了解 PostgREST 的使用方法,可以参考入门指南文档,内容非常全面且开箱即用。

接下来,我们将它应用到一个简单的示例中。

具体步骤​

以下过程你可以在 GitHub 上找到完整源代码。 下方展示了一个通过 CRUD API 公开的 product 表。

表结构

由于我没有找到任何现成的 Docker 镜像,所以我单独创建了一份新的 Dockerfile。其中主要涉及依赖项的安装和参数化数据生成。

Dockerfile

FROM debian:bookworm-slim
ARG POSTGREST_VERSION=v10.1.1
ARG POSTGREST_FILE=postgrest-$POSTGREST_VERSION-linux-static-x64.tar.xz
RUN mkdir postgrest
WORKDIR postgrest
ADD https://github.com/PostgREST/postgrest/releases/download/$POSTGREST_VERSION/$POSTGREST_FILE \
    .
RUN apt-get update && \
    apt-get install -y libpq-dev xz-utils && \
    tar xvf $POSTGREST_FILE && \
    rm $POSTGREST_FILE

之后,Docker 镜像在 /postgrest 文件夹中会包含一个名为 postgrest 的可执行文件。这里可以通过 Docker Compose 来部署:

docker-compose.yml

version: "3"
services:
  postgrest:
    build: ./postgrest
    volumes:
      - ./postgrest/product.conf:/etc/product.conf:ro
    ports:
      - "3000:3000"
    entrypoint: ["/postgrest/postgrest"]
    command: ["/etc/product.conf"]
    depends_on:
      - postgres
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: "root"
    volumes:
      - ./postgres:/docker-entrypoint-initdb.d:ro

接下来可以执行以下命令,查询前文提到的 product 表:

curl localhost:3000/product

得到如下结果反馈:

[{"id":1,"name":"Stickers pack","description":"A pack of rad stickers to display on your laptop or wherever you feel like. Show your love for Apache APISIX","price":0.49,"hero":false},
 {"id":2,"name":"Lapel pin","description":"With this \"Powered by Apache APISIX\" lapel pin, support your favorite API Gateway and let everybody know about it.","price":1.49,"hero":false},
 {"id":3,"name":"Tee-Shirt","description":"The classic geek product! At a conference, at home, at work, this tee-shirt will be your best friend.","price":9.99,"hero":true}]

方案优化​

尽管上文提到的这套解决方案有效,但仍存在很大的改进空间。比如数据库用户不能更改数据、实际操作中每个人都可以访问相关数据等。这对于与产品相关的数据来说,可能不是一个大问题,但如果是医疗数据呢?

PostgREST 的官网使用文档中提到了这一点,并明确提出:建议用户使用反向代理。

提到反向代理,就不得不将目光转向到 API 网关行列。与 NGINX 不同,这里我选取了开源领域非常活跃的 API 网关产品 — Apache APISIX。APISIX 是一个动态、实时、高性能的 API 网关,提供了负载均衡、动态上游、灰度发布、精细化路由、限流限速、服务降级、服务熔断、身份认证、可观测性等数百项功能。

首先,我们可以在 Docker Compose 文件中补充 APISIX 相关信息,包括 APISIX 及其依赖的存储 etcd,而 etcd 主要用于存储 APISIX 的路由、插件等配置信息。

docker-compose.yml

version: "3"
services:
  apisix:
    image: apache/apisix:2.15.0-alpine
    volumes:
      - ./apisix/config.yml:/usr/local/apisix/conf/config.yaml:ro
    ports:
      - "9080:9080"
    restart: always
    depends_on:
      - etcd
      - postgrest
  etcd:
    image: bitnami/etcd:3.5.2
    environment:
      ETCD_ENABLE_V2: "true"
      ALLOW_NONE_AUTHENTICATION: "yes"
      ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2397"
      ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2397"

然后将 APISIX 配置为 postgrest 的代理进行调用。

curl http://apisix:9080/apisix/admin/upstreams/1 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
  "type": "roundrobin",
  "nodes": {
    "postgrest:3000": 1
  }
}'
curl http://apisix:9080/apisix/admin/routes/1 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
  "uri": "/*",
  "upstream_id": 1
}

现在再来查询端点,会得到与上文一致的返回结果。

curl localhost:9080/product

改进空间​

虽然到目前为止,我们还没有添加任何实际项目,但准备工作已经全部就绪了。接下来就让我们为这个 API 添加一些其他功能,让其更安全有效,易于追踪。

DDoS 保护​

API 作为一个连接属性的组件,必然要保证其过程中的传输安全。因此,在这里我们对 API 增加一些防护,让其免受 DDoS 攻击。APISIX 提供了非常多的官方插件,涉及鉴权、流量处理、监控等等。为了防止 DDoS,我们可以使用 APISIX 的 limit-count 插件。

在 APISIX 中使用插件时,你可以在创建特定路由时在每个路由上设置某个插件。如果你想让某个插件在每个路由上都生效,则可以使用全局规则。如下方所示,我们希望在默认情况下可以保护每个路由,所以使用全局规则设定 limit-count 插件。

curl http://apisix:9080/apisix/admin/global_rules/1 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
  "plugins": {
    "limit-count": {
      "count": 1,
      "time_window": 5,
      "rejected_code": 429
    }
  }
}'

现在,如果我们执行太多的请求,APISIX 将会保护上游。

curl localhost:9080/product

<html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>openresty</center>
</body>
</html>

增加鉴权​

PostgREST 还在根端提供了一个 OpenAPI endpoint。因此,我们现在有两条路由: / (控制 Open API 规范)和 /product (控制产品)。

假设我们现在需要制定一套限制访问的操作,即不允许未经授权的人访问数据。普通用户可以访问产品端信息,而管理员用户可以访问 Open API 规范和产品端信息。

APISIX 提供了几种身份验证方法,这些身份认证方式都可以通过插件进行实现。这里我们选取 APISIX 中最常用也是最简单的认证插件 key-auth,它依赖于 Consumer(消费者)抽象。 key-auth 插件的使用中需要一个特定的 header,这样插件就可以根据值数据进行反向查找,并找到其对应的 Consumer。

以下代码展示了如何新建一个 Consumer:

curl http://apisix:9080/apisix/admin/consumers -H 'X-API-KEY: 123xyz' -X PUT -d '
{
  "username": "admin",
  "plugins": {
    "key-auth": {
      "key": "admin"
    }
  }
}'

同样的,我们需要对Consumer user 和Key user进行相关操作。现在可以创建一个专用路由来配置它们,以便只有来自admin的请求才能通过:

curl http://apisix:9080/apisix/admin/routes -H 'X-API-KEY: 123xyz' -X POST -d '
{
  "uri": "/",
  "upstream_id": 1,
  "plugins": {
    "key-auth": {},
    "consumer-restriction": {
      "whitelist": [ "admin" ]
    }
  }
}'

然后使用以下命令测试一下:

curl localhost:9080

发现并没有起作用。这是因为我们没有通过 API 密钥的 header 进行身份验证。

{"message":"Missing API key found in request"}

添加 header 后再次进行测试:

curl -H "apikey: user" localhost:9080

{"message":"The consumer_name is forbidden."}

发现仍然没有效果。这是因为这里 API key 为 user ,我们前边仅为 admin 也就是管理员设置了相关权限。所以如果更换为 admin ,就会如期返回 Open API 规范的相关信息。

配置监控​

在软件系统中,总有一个被人们低估重要性的功能 —— 可观测性。在生产环境中部署了任何组件,都需要监控其运行状况。

如今,很多服务都提供了可观测性的功能,比如 Prometheus。得益于 Prometheus 的开源属性,它被广泛应用于实践中。因此,这里我们也选用 Prometheus 进行相关数据的监控。

为了通过图表等形式显示数据,我们也同时需要依赖于 Grafana。接下来,将这些组件添加到 Docker Compose 文件中。

docker-compose.yml

version: "3"
services:
  prometheus:
    image: prom/prometheus:v2.40.1
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    depends_on:
      - apisix
  grafana:
    image: grafana/grafana:8.5.15
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/dashboards:/var/lib/grafana/dashboards
      - ./grafana/config/grafana.ini:/etc/grafana/grafana.ini
    ports:
      - "3001:3000"
    depends_on:
      - prometheus

以上操作需注意:APISIX 的默认监控方案中自带 Grafana,因此只需从 APISIX 中获取相关配置 即可。同时将默认端口从 3000 更改为 3001 是为了避免与 PostgREST 服务发生冲突。

一旦监控基础设施到位,我们只需要指示 APISIX 以 Prometheus 期望的格式提供数据即可。可以通过配置插件和新的全局规则来实现这一目标:

config.yaml

plugin_attr:
  prometheus:
    export_addr:
      ip: "0.0.0.0"
      port: 9091

curl http://apisix:9080/apisix/admin/global_rules/2 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
  "plugins": {
    "prometheus": {}
  }
}'

此时发送几个查询请求,并打开 Grafana 仪表板,可看到类似数据。如果运行较多请求,则会出现更丰富的数据仪表。

仪表盘

监控数据

总结​

创建一个成熟的 RESTful API 是一项巨大的投资。你可以通过 PostgREST 将数据库暴露在 CRUD API 中来快速测试一个简单的 API。但是,这样的体系结构不适用于实际生产。要想使其更具实践性,就需要在 PostgREST 前设置一个 facade、一个反向代理,或者更好的 API 网关。

Apache APISIX 作为云原生 API 网关,提供了广泛的特性,从流量处理到认证授权和可观测性等。有了 APISIX,你就可以用较低的成本快速验证你的 API 需求。锦上添花的是,当你验证需求完成之后,还可以保留现有的 facade,并用自定义开发的 API 来替换 PostgREST。

文章来源:How to build an efficient and secure RESTful API solution using PostgREST and Apache APISIX