配合 Takuto 使用外部数据库

默认情况下,Takuto 把数据存放在一个本地 SQLite 文件里(数据目录下的 takuto.db)。 若是多用户部署或生产使用,你可以改为把 Takuto 指向一个外部的 PostgreSQLMariaDBMySQL 数据库。

用 takuto-cli 接入已有的数据库 container

如果你用 Takuto CLI,且数据库跑在本机的一个 container 里,你不用手动翻译任何东西。 在 takuto setup 时选择 “A database container running on this machine (auto-wire it)”,并按 你从宿主机使用的方式给出 URL —— 也就是你会粘进 psql 或 GUI 客户端的那个 localhost 串:

[database]
connection = "postgres://takuto:<DB_PASSWORD>@localhost:5433/takuto"
local_container = true   # 由向导写入 —— 告诉 CLI 在启动时自动接线

之后每次 takuto start,CLI 会 (1) 找到发布该端口的运行中 container 及其内部端口, (2) 用稳定别名 takuto_db 把它接到 Takuto 的网络上,(3) 把一个面向 container 的 TAKUTO_DATABASE_CONNECTION…@takuto_db:5432/…)写进 takuto.env,供引擎在启动时使用。 你照旧用 localhost 思考;网络命名空间的翻译交给 CLI。

唯一的要求:takuto start 之前先启动你的数据库 container。如果还没有任何东西发布该端口, CLI 会给出警告并保持连接串不变 —— 把数据库起起来再重跑 takuto start

远程宿主机本地的数据库则不同:在向导里选 remote / host-native 选项,并给出一个本身 就可达的 URL。

内置的 Compose overlay

如果你是用 Docker Compose 从 Takuto Core 跑引擎,受支持的路径就是内置的数据库 overlay。它会把数据库添加到正确的网络上,替你注入连接串,并在 Takuto 启动前 等待数据库变为健康状态 —— 从而规避掉下面那两个网络与启动竞态的问题:

# PostgreSQL
docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d
# or: make start BACKEND=postgres

# MariaDB / MySQL
docker compose -f docker-compose.yml -f docker-compose.mariadb.yml up -d
# or: make start BACKEND=mariadb

在你的环境里用 POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB(或对应的 MARIADB_*) 覆盖默认值。这个 overlay 会设置 TAKUTO_DATABASE_CONNECTION(它会覆盖 [database].connection), 所以你不必手动编辑任何连接串,并且首次启动时会跑一次性的 SQLite → 外部数据库导入。

手动来做(直接运行引擎)

如果你不用 CLI、自己运行 Takuto Core,就没有自动接线:你要设置一个 Takuto container 能直接连上的连接串。本页其余部分讲的就是这种情形 —— 你单独运行、或已经在另一个网络上的数据库。

最要紧的一条规则

config.toml 里的连接串是由 Takuto container 本身使用的。所以那个串里的主机 必须从该 container 内部可达:

  • ✅ 如果你的数据库作为另一个 container 运行,请使用它的 container/服务名及其 内部端口,并确保两个 container 共享同一个网络。
  • 不要localhost127.0.0.1 —— 在 Takuto container 内部,这指回的是 Takuto 自己,而非你的数据库。
  • 不要宿主发布的端口(例如 -p 5433:5432 这样的映射)。那个端口只存在于你 的宿主机上,而不在 container 网络上。

经验法则: localhost:<published-port> 是用来从你的宿主机连接的(psql、某个 GUI 客户端)。<container-name>:<internal-port> 才是给 Takuto 用的。

复用你已经有的数据库 container

已经在某个 container 里跑着 PostgreSQL(或 MariaDB/MySQL)—— 也许来自另一个 Compose 项目?不用重建。把它接到 takuto container 实际使用的网络上 —— 即 dind sidecar 的网络 —— 给它一个稳定的别名,再用这个别名构造连接串:

# 1. 找到 dind sidecar(也即 takuto)所在的网络:
docker inspect takuto-dind | grep -A8 '"Networks"'

# 2. 用一个稳定的别名把你已有的 DB container 接到该网络:
docker network connect --alias takuto_db <第1步得到的网> <你的-db-container>

# 3. 用别名 + 数据库的内部端口构造连接串,例如:
#    postgresql://<user>:<password>@takuto_db:5432/<db>

连接串里的 host 是这个别名takuto_db)—— 绝不是 localhost —— 端口是 container 的 内部端口(5432 / 3306),而不是用 -p 发布的端口。把它填进 [database].connection (第 2 步)并重启。下面的步骤则介绍如何从零创建一个数据库。

第 1 步 —— 在 Takuto 的网络上跑一个数据库 container

Takuto 的各 container 共享一个 Docker/Podman 网络(通过 Compose 启动时,就是该项目的 默认网络,例如 <project>_default)。把你的数据库放到同一个网络上。最简单的做法是把 数据库作为一个服务加到同一个 Compose 文件里,让它自动加入;或者,把它独立运行并挂接到该 网络上。

每种引擎都需要在首次启动时创建一个数据库和一个应用用户:

PostgreSQL

docker run -d --name takuto_db --network <takuto-network> \
  -e POSTGRES_USER=takuto \
  -e POSTGRES_PASSWORD='<DB_PASSWORD>' \
  -e POSTGRES_DB=takuto \
  -v takuto_db-data:/var/lib/postgresql/data \
  -p 5433:5432 \
  postgres:16-alpine

MariaDB

docker run -d --name takuto_db --network <takuto-network> \
  -e MARIADB_ROOT_PASSWORD='<ROOT_PASSWORD>' \
  -e MARIADB_DATABASE=takuto \
  -e MARIADB_USER=takuto \
  -e MARIADB_PASSWORD='<DB_PASSWORD>' \
  -v takuto_db-data:/var/lib/mysql \
  -p 3306:3306 \
  mariadb:11

MySQL

docker run -d --name takuto_db --network <takuto-network> \
  -e MYSQL_ROOT_PASSWORD='<ROOT_PASSWORD>' \
  -e MYSQL_DATABASE=takuto \
  -e MYSQL_USER=takuto \
  -e MYSQL_PASSWORD='<DB_PASSWORD>' \
  -v takuto_db-data:/var/lib/mysql \
  -p 3306:3306 \
  mysql:8

持久化: 上面那个 -v takuto_db-data:/… 命名卷,正是让你的数据在 container 重建后 依然留存的关键。没有它,删除 container 就会删掉数据。

究竟哪个网络才是 “Takuto 的网络”? 这正是大家容易栽跟头的地方。当 Docker-in-Docker sidecar 启用时,takuto container 没有属于自己的网络 —— 它共享 dind container 的 网络命名空间(network_mode: service:dind)。所以真正要紧的网络,是 dind container 所 挂接的那个(通常是 Compose 项目的默认网络,例如 takuto-core_default),而不是一个以 Takuto 命名的网络。用这条命令找到它:

docker inspect <dind-container> | grep -A8 '"Networks"'

把你的数据库挂接到那个网络上,并按它在该网络上的名称/别名来引用它。

数据库已经跑在另一个网络上了? 你不必重建它 —— 把它桥接到 Takuto 的网络上,并赋予它你 连接串所期望的那个别名:

docker network connect --alias takuto_db <takuto-network> <your-db-container>

--alias takuto_db 会让该数据库在那个网络上解析为 takuto_db,于是像 …@takuto_db:5432/… 这样的连接串无需改动便能继续工作。

第 2 步 —— 在 config.toml 里设置连接串

编辑 .takuto/config.toml[database] 小节:

[database]
connection = "<CONNECTION_STRING>"
fail_fast = true          # 数据库不可达时中止启动(推荐)
import_from_sqlite = true # 首次连接时迁移已有的本地 SQLite 数据

为你的引擎使用对应的 scheme 和内部端口,并以数据库的 container 名作为主机:

引擎连接串
PostgreSQLpostgresql://takuto:<DB_PASSWORD>@takuto_db:5432/takuto
MariaDBmysql://takuto:<DB_PASSWORD>@takuto_db:3306/takuto
MySQLmysql://takuto:<DB_PASSWORD>@takuto_db:3306/takuto

MariaDB 和 MySQL 都使用 mysql:// scheme。注意这里是内部端口(5432 / 3306)—— 而不是 -p 里发布出来的那个。

第 3 步 —— 重启 Takuto

Takuto 在启动时读取 config.toml,所以重启它以应用变更:

takuto restart

第 4 步 —— 验证

查看 Takuto 的日志。一次成功的连接会记录类似这样的内容:

Multi-user database initialized (external backend)  backend="postgres"  url="postgresql://takuto:****@takuto_db:5432/takuto"

fail_fast = true 时,配置错误或不可达的数据库会让启动中止并报错,而不是悄悄回退到 SQLite —— 所以一次干净的启动就意味着外部数据库已被启用。当 Takuto 的 egress 层识别出 该主机时,它也会记录 Allowing database sidecar: takuto_db:5432。你也可以连上数据库,确认 Takuto 的那些表(userssessionscredentialsrepositories 等等)已被创建。

从你的宿主机连接(可选)

要用本地客户端检视数据库,请使用发布出来的端口(-p),并经由 localhost 连接:

postgresql://takuto:<DB_PASSWORD>@localhost:5433/takuto
mysql://takuto:<DB_PASSWORD>@localhost:3306/takuto

这是唯一一处 localhost 是正确的地方 —— 因为你是从宿主机连接的,而不是从 Takuto container 内部。

故障排查

症状可能原因
启动时 pool timed out / connection refused连接串用了 localhost 或发布出来的端口,而非 <container-name>:<internal-port>;又或者数据库 container 不在 Takuto 的网络上。
启动时 Database backend unreachable两种常见原因:(1) 当 Takuto 跑那个 5 秒的 SELECT 1 探测时,数据库还没准备好接受连接 —— 一种启动竞态;或 (2) 数据库不在 Takuto 的网络上。修复办法:使用内置的 overlay(它会等待数据库的健康检查)、确保数据库在 Takuto 启动前已经起来且健康,或把数据库挂接到 dind container 的网络上(见第 1 步)。
启动立即中止fail_fast = true 且数据库不可达或凭据有误 —— 修正连接串/凭据。(在 fail_fast = false 时,它会改为记录一条警告并回退到 SQLite。)
重建数据库 container 后数据消失没有挂接命名卷;加上一个(第 1 步)。
认证 / 访问错误应用用户/数据库没有被创建,或连接串里的密码与 container 的环境变量不一致。

完整的键集(连接池大小、超时、SQLite 导入)见 [database] 参考