Azimutt Docker Compose 部署指南
用 Docker Compose 部署 Azimutt 2.0 三件套(应用 main 构建 + PostgreSQL 16 元数据库 + @azimutt/gateway 0.1.26)全流程:compose 全文、demo 库初始化、启动与登录/gateway schema/query 验证、等价 docker run、升级回滚与 403/400/404 等常见故障处理(2026-11-03 实测)。
Azimutt Docker Compose 部署指南
目标:从零用 Docker Compose 部署 Azimutt 2.0(应用 + PostgreSQL 16 元数据库 + Node.js gateway 三件套),跑通登录、项目 API 与 gateway schema/query 全链路,并给出等价 docker run、升级回滚与常见故障处理。
一、环境信息
- 宿主:Ubuntu 26.04.1 LTS x86_64,IP
192.168.1.200 - Docker:
29.9.2 (build a82e528);Docker Compose:v5.6.1 - 部署组件:Azimutt
main(release 构建号2.0.1787693602,OTP 25 / Elixir 1.14.3 / ERTS 13.1.4)、gateway@azimutt/gateway@0.1.26、元数据库postgres:16 - 对外端口:应用
192.168.1.200:8136(容器内 4000)、gateway192.168.1.200:8137(容器内 3000)、PostgreSQL192.168.1.200:5432 - 专用网络:
azimutt-main-net(bridge,固定子网10.89.61.0/24,db/server/gateway 分别固定10.89.61.10/20/30) - 部署目录:
/opt/azimutt-stack(compose.yaml、.env、init/ 与 data/ 都在这里)
二、镜像选择说明
| 用途 | 镜像 | 选择理由(2026-11-03 实测) |
|---|---|---|
| Azimutt 应用 | ghcr.io/azimuttapp/azimutt:main | 官方唯一滚动发布渠道。GitHub 无 Releases,ghcr 仓库只有 main 与一个特性分支 tag,没有版本化 tag 可选;main 即最新构建,真实版本以 release 构建号标识(/app/bin/azimutt version → azimutt 2.0.1787693602) |
| gateway | node:20-alpine + npm 全局安装 @azimutt/gateway@0.1.26 | gateway 无官方镜像,官方口径就是用 Node.js 运行 npm 包;启动命令里固定包版本 0.1.26 以保证可复现 |
| 元数据库 | postgres:16 | Azimutt 后端为 Elixir/Phoenix,元数据必须存 PostgreSQL;16 为当前稳定大版本 |
⚠️
main是滚动 tag,今天与明天的main内容可能不同。生产部署务必用docker inspect记录当前 digest(见「十、常用运维命令」),升级/回滚都以 digest 为准。
三、前置准备
$ docker --version
Docker version 29.9.2, build a82e528
$ docker compose version
Docker Compose version v5.6.1
端口规划:
| 端口 | 用途 | 暴露给谁 |
|---|---|---|
| 8136 | Azimutt Web(容器内 4000) | 浏览器 |
| 8137 | gateway(容器内 3000) | 浏览器(前端直连)与 CLI |
| 5432 | PostgreSQL(容器内 5432) | gateway 访问业务库/元数据库;测试环境临时对宿主开放 |
生成应用加密密钥(应至少 64 字节随机值):
$ openssl rand -hex 32
cd0ba5b8f7f85b8b0a140a25af22106b5aebf37cc39206a67559877eeb649afb
四、部署步骤
4.1 目录与 .env
/opt/azimutt-stack/
├── compose.yaml
├── .env
├── init/
│ ├── 01-demo-db.sh # 建 demo 库
│ └── demo/02-demo-schema.sql # 示例业务 schema(6 表 31 列 7 外键)
└── data/
├── db/ # PostgreSQL 数据目录(持久化)
└── uploads/ # Azimutt local 文件存储(持久化)
.env 只需一个变量(compose 用 ${SECRET_KEY_BASE} 插值):
$ cat .env
SECRET_KEY_BASE=08383932639413583b2343a17290e77c8dca6026d3abc6a032fbb09e0be930c3183a1b7142bbfdf5
4.2 compose.yaml(全文)
name: azimutt-main-stack
services:
azimutt-db:
image: postgres:16
container_name: azimutt-main-db
hostname: azimutt-main-db
restart: unless-stopped
environment:
POSTGRES_DB: azimutt
POSTGRES_USER: postgres
POSTGRES_PASSWORD: q1w2e3r4
ports:
- "192.168.1.200:5432:5432"
volumes:
- ./data/db:/var/lib/postgresql/data
- ./init:/docker-entrypoint-initdb.d:ro
networks:
azimutt-net:
ipv4_address: 10.89.61.10
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d azimutt"]
interval: 5s
timeout: 5s
retries: 12
azimutt:
image: ghcr.io/azimuttapp/azimutt:main
container_name: azimutt-main-server
hostname: azimutt-main-server
restart: unless-stopped
depends_on:
azimutt-db:
condition: service_healthy
ports:
- "192.168.1.200:8136:4000"
environment:
PHX_SERVER: "true"
PHX_PROTOCOL: ipv4
PHX_HOST: localhost
PORT: "4000"
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
DATABASE_URL: postgresql://postgres:q1w2e3r4@azimutt-main-db:5432/azimutt
DATABASE_POOL_SIZE: "10"
GATEWAY_URL: http://192.168.1.200:8137
FILE_STORAGE_ADAPTER: local
AUTH_PASSWORD: "true"
SKIP_ONBOARDING_FUNNEL: "true"
SKIP_EMAIL_CONFIRMATION: "true"
PUBLIC_SITE: "false"
volumes:
- ./data/uploads:/app/bin/uploads
networks:
azimutt-net:
ipv4_address: 10.89.61.20
gateway:
image: node:20-alpine
container_name: azimutt-main-gateway
hostname: azimutt-main-gateway
restart: unless-stopped
command: sh -c "npm install -g @azimutt/gateway@0.1.26 --no-audit --no-fund && node /usr/local/lib/node_modules/@azimutt/gateway/out/start.js"
ports:
- "192.168.1.200:8137:3000"
environment:
NODE_ENV: production
LOG_LEVEL: info
API_HOST: 0.0.0.0
API_PORT: "3000"
CORS_ALLOW_ORIGIN: http://192.168.1.200:8136
networks:
azimutt-net:
ipv4_address: 10.89.61.30
networks:
azimutt-net:
name: azimutt-main-net
driver: bridge
ipam:
config:
- subnet: 10.89.61.0/24
三处拓扑要点:
DATABASE_URL指向元数据库azimutt-main-db:5432/azimutt(应用自身账号/项目/注释存这里);GATEWAY_URL必须是浏览器可达的地址(这里是宿主192.168.1.200:8137),它会被注入前端页面window.gateway_url,浏览器随后直连 gateway——写内部域名则浏览器解析不到;- gateway 的
CORS_ALLOW_ORIGIN要与浏览器访问应用的地址完全一致(http://192.168.1.200:8136),否则跨域被拒。
4.3 demo 业务库初始化脚本(可选)
init/ 挂载进 postgres 的 /docker-entrypoint-initdb.d,仅首次初始化(数据目录为空)时执行,用来造一个供 Azimutt 演示的 demo 库。
init/01-demo-db.sh:
#!/bin/bash
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE DATABASE demo;
GRANT ALL PRIVILEGES ON DATABASE demo TO $POSTGRES_USER;
EOSQL
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname demo -f /docker-entrypoint-initdb.d/demo/02-demo-schema.sql
init/demo/02-demo-schema.sql(6 表:categories/users/products/orders/order_items/reviews,7 个外键):
CREATE TABLE categories (
id bigserial PRIMARY KEY,
name varchar(100) NOT NULL UNIQUE,
slug varchar(120) NOT NULL UNIQUE,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE users (
id bigserial PRIMARY KEY,
email varchar(255) NOT NULL UNIQUE,
full_name varchar(200) NOT NULL,
category_id bigint REFERENCES categories (id),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE products (
id bigserial PRIMARY KEY,
sku varchar(40) NOT NULL UNIQUE,
title varchar(200) NOT NULL,
price numeric(10, 2) NOT NULL CHECK (price >= 0),
category_id bigint NOT NULL REFERENCES categories (id),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE orders (
id bigserial PRIMARY KEY,
user_id bigint NOT NULL REFERENCES users (id),
status varchar(20) NOT NULL DEFAULT 'pending',
total numeric(12, 2) NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE order_items (
id bigserial PRIMARY KEY,
order_id bigint NOT NULL REFERENCES orders (id),
product_id bigint NOT NULL REFERENCES products (id),
quantity integer NOT NULL CHECK (quantity > 0),
unit_price numeric(10, 2) NOT NULL
);
CREATE TABLE reviews (
id bigserial PRIMARY KEY,
product_id bigint NOT NULL REFERENCES products (id),
user_id bigint NOT NULL REFERENCES users (id),
rating smallint NOT NULL CHECK (rating BETWEEN 1 AND 5),
body text,
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO categories (name, slug) VALUES
('Books', 'books'), ('Electronics', 'electronics'), ('Home', 'home');
INSERT INTO users (email, full_name, category_id) VALUES
('alice@example.com', 'Alice', 1), ('bob@example.com', 'Bob', 2);
INSERT INTO products (sku, title, price, category_id) VALUES
('B-001', 'The Elixir Book', 39.90, 1),
('E-001', 'USB-C Hub', 25.00, 2);
INSERT INTO orders (user_id, status, total) VALUES (1, 'paid', 64.90);
INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES
(1, 1, 1, 39.90), (1, 2, 1, 25.00);
INSERT INTO reviews (product_id, user_id, rating, body) VALUES
(1, 2, 5, 'Great book');
4.4 启动
$ cd /opt/azimutt-stack
$ docker compose up -d
Network azimutt-main-net Creating
Network azimutt-main-net Creating
Network azimutt-main-net Created
Network azimutt-main-net Created
Container azimutt-main-gateway Creating
Container azimutt-main-db Creating
Container azimutt-main-db Created
Container azimutt-main-server Creating
Container azimutt-main-gateway Created
Container azimutt-main-server Created
Container azimutt-main-db Starting
Container azimutt-main-gateway Starting
Container azimutt-main-db Started
Container azimutt-main-db Waiting
Container azimutt-main-gateway Started
Container azimutt-main-db Healthy
Container azimutt-main-server Starting
Container azimutt-main-server Started
(Network 行重复出现是 Compose v5.6.1 的实际输出,未删减。)depends_on: condition: service_healthy 保证 server 在 db 健康后才启动。应用启动日志(docker logs azimutt-main-server):
Setup Local file storage
Setup Local email provider
Setup Password auth
02:32:49.317 [info] Migrations already up
Setup Local file storage
Setup Local email provider
Setup Password auth
02:32:50.078 [info] Running AzimuttWeb.Endpoint with cowboy 2.12.0 at 0.0.0.0:4000 (http)
02:32:50.080 [info] Access AzimuttWeb.Endpoint at https://localhost
Setup 行是 runtime.exs 按环境变量装配文件存储/邮件/认证的运行痕迹(镜像入口先跑迁移再启动,故出现两次)。gateway 首次启动需 npm 安装约 1 分钟,就绪日志:
{"level":30,"time":1793672876496,"pid":1,"hostname":"azimutt-main-gateway","msg":"Server listening at http://0.0.0.0:3000"}
4.5 部署验证
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
azimutt-main-db postgres:16 "docker-entrypoint.s…" azimutt-db 51 seconds ago Up 50 seconds (healthy) 192.168.1.200:5432->5432/tcp
azimutt-main-gateway node:20-alpine "docker-entrypoint.s…" gateway 51 seconds ago Up 50 seconds 192.168.1.200:8137->3000/tcp
azimutt-main-server ghcr.io/azimuttapp/azimutt:main "sh -c '/app/bin/mig…" azimutt 51 seconds ago Up 45 seconds 192.168.1.200:8136->4000/tcp
应用入口状态码(PUBLIC_SITE=false 使首页重定向登录页):
$ curl -s -o /dev/null -w 'root=%{http_code} redirect=%{redirect_url}\n' http://192.168.1.200:8136/
root=302 redirect=http://192.168.1.200:8136/login
$ curl -s -o /dev/null -w 'register=%{http_code}\n' http://192.168.1.200:8136/register
register=200
$ curl -s -o /dev/null -w 'health=%{http_code}\n' http://192.168.1.200:8136/health
health=200
登录(先取 CSRF token 与会话 cookie,再 POST;会话 cookie 带 Secure; SameSite=None,http 下 curl 的 cookie jar 不落盘,需手动拼 Cookie 头):
$ curl -s -D get.hdr -o login.html http://192.168.1.200:8136/login
$ TOK=$(grep -oE 'name="_csrf_token"[^>]*value="[^"]+"' login.html | sed -E 's/.*value="([^"]+)".*/\1/' | head -1)
$ CK=$(grep -i 'set-cookie: _azimutt_key' get.hdr | sed -E 's/.*_azimutt_key=([^;]+).*/_azimutt_key=\1/')
$ curl -s -H "Cookie: $CK" -D login2.hdr -o /dev/null -X POST http://192.168.1.200:8136/login \
--data-urlencode _utf8=✓ --data-urlencode "_csrf_token=$TOK" \
--data-urlencode 'user[email]=admin@azimutt.local' \
--data-urlencode 'user[password]=q1w2e3r4q1w2'
$ grep -iE '^(HTTP|location)' login2.hdr
HTTP/1.1 302 Found
location: /home
登录后用新会话 cookie 调项目 API:
$ SCK=$(grep -i 'set-cookie: _azimutt_key' login2.hdr | tail -1 | sed -E 's/.*_azimutt_key=([^;]+).*/_azimutt_key=\1/')
$ curl -s -H "Cookie: $SCK" http://192.168.1.200:8136/api/v1/organizations
[{"description":null,"id":"32170407-49ef-4e66-ab0f-44214d50eaf3","name":"admin","slug":"admin"}]
$ curl -s -H "Cookie: $SCK" http://192.168.1.200:8136/api/v1/organizations/32170407-49ef-4e66-ab0f-44214d50eaf3/projects
[{"name":"demo-shop","id":"4d40deb4-3df6-48d9-be54-76636daf6c65","slug":"demo-shop","organization_id":"32170407-49ef-4e66-ab0f-44214d50eaf3","storage_kind":"local","nb_sources":1,"nb_tables":6,"nb_columns":31,"nb_relations":7,"nb_types":0,"nb_comments":0,"nb_layouts":0,"nb_notes":0,"nb_memos":0,"created_at":"2026-11-03T01:55:54Z","updated_at":"2026-11-03T01:55:54Z","imported_at":"2026-11-03T01:55:54Z","visibility":"private","default_source":null,"archived":false}]
gateway 三验(健康、schema、query——注意参数名是 url):
$ curl -s http://192.168.1.200:8137/health
{"status":200,"version":"x.y.z"}
$ curl -s -X POST http://192.168.1.200:8137/gateway/schema -H "Content-Type: application/json" \
-d '{"url":"postgresql://postgres:q1w2e3r4@192.168.1.200:5432/demo"}' | head -c 200
{"tables":[{"schema":"public","table":"categories","columns":[{"name":"id","type":"bigint","default":"nextval('categories_id_seq'::regclass)"},{"name":"name","type":"character varying(100)"},{"name":"
$ curl -s -X POST http://192.168.1.200:8137/gateway/schema -H "Content-Type: application/json" \
-d '{"url":"postgresql://postgres:q1w2e3r4@192.168.1.200:5432/demo"}' | python3 -c 'import json,sys; d=json.load(sys.stdin); print("tables:",len(d["tables"]),"relations:",len(d["relations"]),"types:",len(d["types"]))'
tables: 6 relations: 7 types: 0
$ curl -s -X POST http://192.168.1.200:8137/gateway/query -H "Content-Type: application/json" \
-d '{"url":"postgresql://postgres:q1w2e3r4@192.168.1.200:5432/demo","query":"select id, sku, title, price from products order by id limit 5"}'
{"query":"select id, sku, title, price from products order by id limit 5","columns":[{"name":"id","ref":{"table":"public.products","column":"id"}},{"name":"sku","ref":{"table":"public.products","column":"sku"}},{"name":"title","ref":{"table":"public.products","column":"title"}},{"name":"price","ref":{"table":"public.products","column":"price"}}],"rows":[{"id":1,"sku":"B-001","title":"The Elixir Book","price":"39.90"},{"id":2,"sku":"E-001","title":"USB-C Hub","price":"25.00"}]}
五、数据持久化说明
| 挂载 | 宿主路径 | 作用 |
|---|---|---|
./data/db:/var/lib/postgresql/data | /opt/azimutt-stack/data/db | 元数据库(账号/组织/项目)+ demo 业务库 |
./data/uploads:/app/bin/uploads | /opt/azimutt-stack/data/uploads | FILE_STORAGE_ADAPTER=local 时 remote 项目的 schema JSON 落盘目录 |
./init:/docker-entrypoint-initdb.d:ro | /opt/azimutt-stack/init | 仅首次初始化执行 |
$ ls -l data/
total 8
drwx------ 19 dnsmasq root 4096 Nov 3 10:26 db
drwxr-xr-x 2 root root 4096 Nov 3 06:56 uploads
$ docker exec azimutt-main-db psql -U postgres -d demo -t -c "select count(*) from information_schema.columns where table_schema='public'"
31
$ docker exec azimutt-main-db psql -U postgres -d azimutt -t -c "select count(*) from users"
1
注意:storage_kind=local 的项目 schema 草稿存在浏览器端,服务端 uploads 目录为空属正常;只有 remote 项目才落文件。备份与恢复不在本指南范围,以元数据库整库备份为主即可。
六、环境变量表
应用(azimutt 服务):
| 变量 | 本例取值 | 作用 |
|---|---|---|
PHX_SERVER | true | 以服务器模式启动 Phoenix(必需,否则不监听) |
PHX_PROTOCOL | ipv4 | 监听 IPv4(默认 ipv6,bridge 网络下必须改) |
PHX_HOST | localhost | 构建绝对 URL 的 host(生产填域名) |
PORT | 4000 | 应用监听端口 |
SECRET_KEY_BASE | .env 注入 | 会话/cookie 加密密钥,≥64 字节随机值 |
DATABASE_URL | postgresql://postgres:q1w2e3r4@azimutt-main-db:5432/azimutt | 元数据库连接串 |
DATABASE_POOL_SIZE | 10 | 连接池大小(默认即 10) |
GATEWAY_URL | http://192.168.1.200:8137 | 注入前端 window.gateway_url,须浏览器可达 |
FILE_STORAGE_ADAPTER | local | 文件存储驱动(local/s3) |
AUTH_PASSWORD | true | 启用邮箱+密码认证 |
SKIP_ONBOARDING_FUNNEL | true | 跳过新用户引导流程 |
SKIP_EMAIL_CONFIRMATION | true | 跳过邮箱验证(无邮件服务时必开) |
PUBLIC_SITE | false | 关闭官网页,/ 直接 302 到 /login |
gateway(gateway 服务,取自官方 gateway.env):
| 变量 | 本例取值 | 作用 |
|---|---|---|
NODE_ENV | production | 运行环境 |
LOG_LEVEL | info | pino 日志级别 |
API_HOST | 0.0.0.0 | 监听地址 |
API_PORT | 3000 | 监听端口 |
CORS_ALLOW_ORIGIN | http://192.168.1.200:8136 | 允许的应用来源,必须与浏览器访问地址一致 |
七、配置文件挂载说明
Azimutt 无独立配置文件可挂:全部配置经环境变量注入,config/runtime.exs 在每次启动(以及 eval/rpc 等命令执行)时读取。实测佐证:执行任何 release 命令都会先打印装配日志——
Setup Local file storage
Setup Local email provider
Setup Password auth
因此改配置的唯一动作是:改 .env/compose environment → docker compose up -d 重建生效。唯一需要挂载的「配置」是 init/ 脚本(首次初始化 demo 库)与 data/(持久化)。
八、等价 docker run
不用 compose 也可逐个拉起(已实测;azimutt-main-net 网络与 azimutt-main-db 数据库沿用 compose 建好的)。应用实例(8138 为验证端口,实测通过后即删):
$ docker run -d --name azimutt-run --network azimutt-main-net \
-e PHX_SERVER=true -e PHX_PROTOCOL=ipv4 -e PHX_HOST=localhost -e PORT=4000 \
-e SECRET_KEY_BASE=08383932639413583b2343a17290e77c8dca6026d3abc6a032fbb09e0be930c3183a1b7142bbfdf5 \
-e DATABASE_URL=postgresql://postgres:q1w2e3r4@azimutt-main-db:5432/azimutt \
-e DATABASE_POOL_SIZE=10 -e GATEWAY_URL=http://192.168.1.200:8137 \
-e FILE_STORAGE_ADAPTER=local -e AUTH_PASSWORD=true \
-e SKIP_ONBOARDING_FUNNEL=true -e SKIP_EMAIL_CONFIRMATION=true -e PUBLIC_SITE=false \
-v /opt/azimutt-stack/data/uploads:/app/bin/uploads \
-p 192.168.1.200:8138:4000 ghcr.io/azimuttapp/azimutt:main
94e999a1e39b6d9fc3163689609f5300d6405d5eef94cba68c328dab078c59be
$ curl -s -o /dev/null -w 'run-health=%{http_code}\n' http://192.168.1.200:8138/health
run-health=200
$ docker exec azimutt-run /app/bin/azimutt pid
65
$ docker exec azimutt-run /app/bin/azimutt version
azimutt 2.0.1787693602
$ docker exec azimutt-run /app/bin/azimutt stop
$ curl -s -m 5 -o /dev/null -w 'after-stop=%{http_code}\n' http://192.168.1.200:8138/health
after-stop=000
$ docker rm -f azimutt-run
stop 无输出即成功(Erlang release 标准行为),随后端口即失联。gateway:
$ docker run -d --name azimutt-gw-run \
-e NODE_ENV=production -e LOG_LEVEL=info -e API_HOST=0.0.0.0 -e API_PORT=3000 \
-e CORS_ALLOW_ORIGIN=http://192.168.1.200:8136 \
-p 192.168.1.200:8139:3000 node:20-alpine \
sh -c "npm install -g @azimutt/gateway@0.1.26 --no-audit --no-fund && node /usr/local/lib/node_modules/@azimutt/gateway/out/start.js"
e1e2be576b7e4455b579553edd4b1470058560f1b667032d431bb21541cb3893
$ curl -s http://192.168.1.200:8139/health
{"status":200,"version":"x.y.z"}
$ docker logs azimutt-gw-run 2>&1 | grep 'Server listening'
{"level":30,"time":1793672245067,"pid":1,"hostname":"e1e2be576b7e","msg":"Server listening at http://0.0.0.0:3000"}
$ docker rm -f azimutt-gw-run
九、常用运维命令
日常巡检以 docker compose ps(见 4.5)与 docker compose logs azimutt 为主。核对当前运行的 release 构建号:
$ docker exec azimutt-main-server /app/bin/azimutt version
azimutt 2.0.1787693602
升级(main 滚动 tag):
$ docker compose pull
Image node:20-alpine Pulling
Image postgres:16 Pulling
Image ghcr.io/azimuttapp/azimutt:main Pulling
Image node:20-alpine Pulled
Image postgres:16 Pulled
Image ghcr.io/azimuttapp/azimutt:main Pulled
$ docker compose up -d
升级后务必 docker exec azimutt-main-server /app/bin/azimutt version 核对构建号是否变化。
回滚:官方无版本化 tag,回滚只能按 digest 钉住旧构建。每次部署后记录当前 digest:
$ docker inspect --format "{{json .RepoDigests}}" ghcr.io/azimuttapp/azimutt:main
["ghcr.io/azimuttapp/azimutt@sha256:ecb0ca8b51ed397cf5e8500de748c3d1490cdf1f9773777be8f769f4b9f1ae99"]
需要回滚时把 compose 里的 image: 改成该 @sha256:... 摘要再 docker compose up -d。
十、常见问题
10.1 登录返回 403「Oops, not this time. Try harder later」
POST /login 未携带有效会话 cookie 或 CSRF token 时,Phoenix 防伪造拦截渲染 403(实测空 token 提交:HTTP/1.1 403 Forbidden)。会话 cookie _azimutt_key 带 Secure; HttpOnly; SameSite=None,curl 在 http 下不会自动收发——cookie jar 直接不落盘(实测 jar.txt 只有注释头)。解决:从响应头手动提取 cookie 值并用 -H "Cookie: _azimutt_key=..." 提交(见 4.5 登录流程)。
10.2 浏览器 http 访问能打开页面但登录后仍像未登录
同为 Secure; SameSite=None cookie 惹的祸:现代浏览器拒收 http 连接下发的 Secure cookie。生产应上 TLS 反向代理并设 PHX_PROTOCOL=https、PHX_HOST=<域名>(日志 Access AzimuttWeb.Endpoint at https://localhost 即 host 拼绝对 URL 的体现)。
10.3 密码设 8 位登录报「Email or Password invalid.」
Azimutt 强制密码 12~72 位。实测 8 位口令 q1w2e3r4 登录返回 200 且页面含:
<p class="alert alert-danger">Email or Password invalid.</p>
示例账号因此用 admin@azimutt.local / q1w2e3r4q1w2(12 位)。
10.4 gateway 返回 400 Bad Request
请求体参数名必须是 url,写成 connection 即 400(实测 badparam=400,响应体 {"error":"Bad Request"})。
10.5 gateway 返回 404
0.1.26 的路由是 /gateway/schema、/gateway/query、/gateway/column-stats、/gateway/table-stats、/gateway/parse-url、/health、/ping、/status;旧文档的 /net/inspect 已不存在(实测 oldroute=404,日志 Route GET:/net/inspect not found)。
10.6 gateway 刚启动 curl 不通
gateway 启动命令先 npm install -g(约 1 分钟),就绪前请求挂起返回 000。以日志出现 Server listening at http://0.0.0.0:3000 为准。
10.7 前端连不上 gateway(CORS 或地址不可达)
GATEWAY_URL 注入的 window.gateway_url 由浏览器直连($SCK 为 4.5 中提取的会话 cookie):
$ curl -s -H "Cookie: $SCK" http://192.168.1.200:8136/projects/4d40deb4-3df6-48d9-be54-76636daf6c65 | grep -o "window.gateway_url[^;]*"
window.gateway_url = 'http://192.168.1.200:8137'
填内部域名(如 http://azimutt-main-gateway:3000)浏览器无法解析;同时 gateway 的 CORS_ALLOW_ORIGIN 必须等于应用访问 origin。
十一、参考链接
- 《Azimutt 介绍与选型指南》:定位、能力与版本生态
- 官方安装文档:https://github.com/azimuttapp/azimutt/blob/main/INSTALL.md (含环境变量全表)
- 官方制品仓库:https://github.com/azimuttapp/azimutt/pkgs/container/azimutt
- gateway npm 包:https://www.npmjs.com/package/@azimutt/gateway
- Phoenix 部署概念:https://hexdocs.pm/phoenix/deployment.html