Documentation
¶
Overview ¶
Package ingress 是入口与证书适配器(T2.15/T2.16;架构 §2.6 入口真源): 平台自管 Traefik(global service 每节点,v0.1 单机 1 实例)、路由与证书 经 HTTP provider 由控制面集中下发、控制面内嵌 ACME(lego)集中签发。
纪律(Spike B 实测,spike/b/README.md §6——硬约束):
- Traefik 只拒绝「显式空 map」与解析失败,**裸 {} 会清空全部路由**; 因此控制面合成配置必须保证 http.routers/services 键存在且非空, 先校验后写(Validate),坏配置不换入内存视图、Traefik 端永远取 不到残缺配置;
- 配置服务不可达时 Traefik 保留上一份成功配置(原生兜底,实测);
- 路由发布严格晚于健康门(release-semantics §2.5 不变量)——本包的 PublishRoutes 由发布引擎在首健康→切流之后调用。
核心不出现第三方概念的一般化纪律在本包的反向落实:Traefik/lego 类型 只存在于本包内部,出口是 Route/DynamicConfig 等本包类型;发布引擎经 engine.RoutePublisher 端口(本包 Manager 隐式实现)消费,不感知 Traefik 存在。
Index ¶
- Constants
- Variables
- func GenerateAccountKey() ([]byte, error)
- func GenerateToken() (string, error)
- func MarshalJSONBytes(cfg *DynamicConfig) ([]byte, error)
- func NewID() string
- func RouterName(app, service string) string
- func Validate(cfg *DynamicConfig) error
- type ACMEConfig
- type CertificatePair
- type CertificateRef
- type Config
- type DomainCheck
- type DynamicConfig
- type DynamicService
- type FileEntry
- type ForwardingTimeouts
- type HTTPDynamic
- type LoadBalancer
- type Manager
- func (m *Manager) Config() Config
- func (m *Manager) EnsureTraefik(ctx context.Context) error
- func (m *Manager) Handler(ctx context.Context) (http.Handler, error)
- func (m *Manager) PublishRoutes(ctx context.Context, in PublishInput) error
- func (m *Manager) Run(ctx context.Context) error
- func (m *Manager) SetConfigPort(port int)
- func (m *Manager) Status(ctx context.Context) (Status, error)
- func (m *Manager) WithClock(f func() time.Time) *Manager
- func (m *Manager) WithdrawAppRoutes(ctx context.Context, appID string) error
- type PublishInput
- type Route
- type Router
- type RouterTLS
- type Server
- type ServersTransport
- type ServiceRoutes
- type Status
- type TLSCertificate
- type TLSDynamic
Constants ¶
const ( // DefaultTraefikImage 是 Traefik 钉版镜像(Spike B 实测版本 v3.5; // 升级走镜像钉版变更 + 回归,不追 latest)。 DefaultTraefikImage = "traefik:v3.5" // TraefikCertMountPath 是证书目录在 Traefik 容器内的只读挂载点 //(tls.certificates 的 certFile/keyFile 以该路径书写)。 TraefikCertMountPath = "/fleetly-certs" // DefaultServersTransportIdleTimeout 是 serversTransport 空闲连接 // 回收时长。依据 V4 实测(architecture §2.5 连接治理行):keep-alive // 连接池会复用已退出的任务,治理主键是应用侧优雅退出, // serversTransport 降为辅助——只治理空闲池(90s 默认 → 15s 保守值), // 对 in-flight 请求无效。 DefaultServersTransportIdleTimeout = 15 * time.Second // DefaultServersTransportDialTimeout 是后端拨号超时(保守值)。 DefaultServersTransportDialTimeout = 5 * time.Second // DefaultRenewBefore 是证书续期窗口(到期前 30 天,T2.16 交付物)。 DefaultRenewBefore = 30 * 24 * time.Hour // DefaultRenewScanInterval 是续期扫描周期。 DefaultRenewScanInterval = 12 * time.Hour // DefaultACMECADirURL 是 LE production 目录端点(测试用 Pebble: // 配置 ingress.acme.ca_dir_url 指向本地 pebble /dir)。 DefaultACMECADirURL = "https://acme-v02.api.letsencrypt.org/directory" )
平台钉版与保守缺省(architecture §2.6 入口行 + §2.5 连接治理行)。
const IngressServiceName = "fleetly-ingress"
IngressServiceName 是平台入口服务名(契约:fleetly-ingress)。
Variables ¶
var ErrNotSwarmReady = errors.New("docker engine is not an active swarm manager")
ErrNotSwarmReady 是 swarm 未就绪的哨兵(服务壳据此降级重试)。
Functions ¶
func GenerateAccountKey ¶
GenerateAccountKey 生成 ACME 账号私钥(ECDSA P256;PEM PKCS8)。
func GenerateToken ¶
GenerateToken 生成随机 token(配置端点鉴权;32 字节 hex)。
func MarshalJSONBytes ¶
func MarshalJSONBytes(cfg *DynamicConfig) ([]byte, error)
MarshalJSONBytes 序列化载荷(缩进关闭——Traefik 轮询高频路径的最小形)。
func RouterName ¶
RouterName 返回 app 服务对应的 router/service 键(fleetly-<app>-<service> ——与 Swarm 服务名同形,键空间唯一且可读)。
func Validate ¶
func Validate(cfg *DynamicConfig) error
Validate 执行 Spike B 纪律校验(先校验后写):
- http.routers 键必须存在且非空(裸 {} 与显式空 map 双拒——空合成 由兜底路由承载,恒过本条);
- http.services 键必须存在(nil map 序列化为 null,同样是缺键形态; 值可以为空 map——只有内建引用的兜底形态合法,见 isInternalService);
- 每个 router 引用的 service 必须存在(悬空引用拒绝——坏 router 会被 Traefik 连同整份配置应用;Traefik 内建服务 @internal 限定名豁免);
- 每个 service 必须有非空 server URL。
Types ¶
type ACMEConfig ¶
type ACMEConfig struct {
// CADirURL 是 ACME 目录端点(ca_dir_url;默认 LE production,测试用
// Pebble URL)。
CADirURL string
// Email 是 ACME 账号邮箱(email;空 = 无 contact 注册)。
Email string
// CAPoolFile 是 CA 根证书池 PEM(ca_pool_file;Pebble/私有 CA 场景
// 信任自定义根)。空 = 系统信任池。
CAPoolFile string
// AccountKeyFile 是 ACME 账号私钥文件(account_key_file;空 =
// <CertDir>/acme-account.key)。
AccountKeyFile string
// Enabled 报告是否启用集中签发(enabled;false = 只发布路由不下发
// TLS 证书——无域名/离线环境的显式关闭位)。nil 缺省 = true。
Enabled *bool
}
ACMEConfig 是集中 ACME 配置(config 键 ingress.acme.*)。
type CertificatePair ¶
type CertificatePair struct {
App string
Domains []string
// CertPEM 是证书链 PEM(leaf + 中间;lego Bundle=true 形态)。
CertPEM []byte
// KeyPEM 是私钥 PEM(ECDSA P256)。
KeyPEM []byte
// NotAfter 是叶证书到期时间(UTC)。
NotAfter time.Time
// SHA256 是 CertPEM 内容摘要(台账登记;变更即重发布 TLS 段)。
SHA256 string
}
CertificatePair 是一张落盘证书(app 级多 SAN 单证书,architecture §2.4 域名行:列表内域名同服务同路由、证书按 app 域名集合出一张)。
type CertificateRef ¶
CertificateRef 是一张已落库证书的引用(文件名 = app 名;sha256 与到期 时间随台账登记)。
type Config ¶
type Config struct {
// TraefikImage 是入口镜像(钉版;traefik_image)。
TraefikImage string
// HTTPPort / HTTPSPort 是宿主发布端口(host 模式 80/443;http_port/
// https_port)。
HTTPPort int
HTTPSPort int
// ConfigAddr 是控制面配置端点监听地址(config_addr)。默认 0.0.0.0
// ——Traefik 任务(容器 netns)须经宿主 IP 访问;鉴权 token 强制,
// 非回环绑定的暴露面由 token 承担(取舍见 provider.go 注释)。
ConfigAddr string
// ConfigAdvertiseIP 是下发给 Traefik 的控制面可达 IP
//(config_advertise_ip;空 = 自动探测:Swarm advertise addr 优先,
// 出口本地地址兜底)。Docker Desktop 形态 advertise addr 是 VM 内部
// IP,须显式配置为宿主可达地址。
ConfigAdvertiseIP string
// TokenFile 是配置端点 bearer token 的持久化文件(token_file;首启
// 生成,与 Traefik 静态配置 --providers.http.headers 同步传递)。
TokenFile string
// CertDir 是证书存储根目录(cert_dir;控制面侧明文 PEM,文件权限
// 0600)。state-model §2.1:证书材料属控制面、独立备份目录。Traefik
// 经平台自管命名卷(CertVolume)消费证书——控制面签发后经 seed 容器
// 复制进卷(swarm 不接受 Windows 宿主路径 bind 挂载,实机验证结论;
// Linux 单机 bind 亦被卷方案统一替代——一条代码路径)。
CertDir string
// CertVolume 是 Traefik 证书只读挂载的命名卷(cert_volume)。
CertVolume string
// CertSeedImage 是证书 seed 容器镜像(cert_seed_image;stopped 容器 +
// docker API 拷贝,卷的持久化让 Traefik 重启不丢证书)。
CertSeedImage string
// ACME 是集中签发器配置。
ACME ACMEConfig
// RenewBefore 是续期窗口(到期前;renew_before_days)。
RenewBefore time.Duration
// RenewScanInterval 是续期扫描周期(renew_scan_seconds)。
RenewScanInterval time.Duration
// PollInterval 是 Traefik 拉取配置的轮询周期(静态配置参数;供收敛
// 等待逻辑对齐节奏)。
PollInterval time.Duration
}
Config 是入口适配器配置(config 键 ingress.*;缺省值经 Normalize 回落, 单一事实源在本包)。
type DomainCheck ¶
type DomainCheck struct {
Domain string `json:"domain"`
IPs []string `json:"ips"`
// Resolved 报告本机是否解析得到至少一个地址。
Resolved bool `json:"resolved"`
// HTTP80 是配置 HTTP 端口的探测结果("" = 不可达;否则记录响应状态行)。
HTTP80 string `json:"http_80,omitempty"`
// HTTPS443 是配置 HTTPS 端口的 TLS 握手结果("" = 不可达)。
HTTPS443 string `json:"https_443,omitempty"`
// CertSubject / CertNotAfter 是 443 端口实收证书的诚实记录
//(不经信任判定——私有 CA/临期证书照实呈现)。
CertSubject string `json:"cert_subject,omitempty"`
CertDNSNames []string `json:"cert_dns_names,omitempty"`
CertNotAfter string `json:"cert_not_after,omitempty"`
Err string `json:"error,omitempty"`
}
DomainCheck 是单域名的验证报告。HTTP80/HTTPS443 字段名沿用既有 http_80/https_443 契约键(proto 契约面),E4 后承载的是「配置端口」的 探测结果(非默认端口部署下即该端口,不再恒为 80/443)。
func VerifyDomains ¶
func VerifyDomains(ctx context.Context, domains []string, httpPort, httpsPort int) []DomainCheck
VerifyDomains 对域名集执行本机视角验证(并发 1 串行——探测量 ≤ 每服务 5 × 每应用 10 的契约上界,串行足够)。探测端口 httpPort/httpsPort 与 入口发布端口同源(调用方传 Manager 配置;E4,S19)。
type DynamicConfig ¶
type DynamicConfig struct {
HTTP *HTTPDynamic `json:"http,omitempty"`
TLS *TLSDynamic `json:"tls,omitempty"`
}
DynamicConfig 是 Traefik 动态配置载荷(只承载平台产出的子集;字段名 对齐 Traefik v3 动态配置 JSON 契约)。
func EmptyConfig ¶
func EmptyConfig() *DynamicConfig
EmptyConfig 是显式空 map 载荷(视图未发布时的应答:Traefik 拒绝该载荷 并保留其侧既有配置——「控制面没有好配置时不动入口」语义,Spike B b1 实测「键在值空」的 decode 拒绝路径;裸 {} 的清空形态永不出本包)。
func Synthesize ¶
func Synthesize(routes []Route) *DynamicConfig
Synthesize 从全量路由集合成动态配置(确定性:路由按 app/service 字典 序;域名已由 compose 层归一化排序)。routers/services 键恒存在; serversTransport 平台默认恒注入(V4 连接治理参数)。
type DynamicService ¶
type DynamicService struct {
LoadBalancer *LoadBalancer `json:"loadBalancer"`
}
DynamicService 是一个负载均衡服务(VIP 形态:单 server 指向 fleetly-<app>-<service> 的 overlay VIP + expose 端口)。
type ForwardingTimeouts ¶
type ForwardingTimeouts struct {
DialTimeout string `json:"dialTimeout,omitempty"`
IdleConnTimeout string `json:"idleConnTimeout,omitempty"`
}
ForwardingTimeouts 是转发超时(字符串时长形态——Spike B 脚本同款, Traefik v3 HTTP provider 实测接受)。
type HTTPDynamic ¶
type HTTPDynamic struct {
Routers map[string]*Router `json:"routers"`
Services map[string]*DynamicService `json:"services"`
ServersTransports map[string]*ServersTransport `json:"serversTransports,omitempty"`
}
HTTPDynamic 是 http 段(routers/services 键恒在——空 map 也显式写出, 让「缺键」形态在载荷层面不可能出现)。
type LoadBalancer ¶
type LoadBalancer struct {
Servers []Server `json:"servers"`
ServersTransport string `json:"serversTransport,omitempty"`
}
LoadBalancer 是 LB 配置(serversTransport 恒引用平台默认连接治理参数, V4 依据见 config.go)。
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager 是入口与证书管理器。
func NewManager ¶
NewManager 构造入口管理器(cfg 缺省回落;docker client 按 DOCKER_HOST/本机套接字构造)。返回 cleanup 释放 docker 连接。
func NewManagerWithDocker ¶
func NewManagerWithDocker(cfg Config, store *state.Store, dc dockerClient, log *slog.Logger) *Manager
NewManagerWithDocker 以注入的 dockerClient 构造(单测)。
func (*Manager) EnsureTraefik ¶
EnsureTraefik 幂等收敛入口服务(不存在创建;存在比对差异更新)。 swarm 未就绪返回 ErrNotSwarmReady(服务壳降级重试)。
func (*Manager) PublishRoutes ¶
func (m *Manager) PublishRoutes(ctx context.Context, in PublishInput) error
PublishRoutes 实现 engine.RoutePublisher:域名台账对账 → Traefik 收敛 与网络接入 → 全量配置收敛(先校验后换视图)→ 证书保障(缺/域名集变化/ 临期即签发;签发失败不阻断已生效的 HTTP 路由,TLS 段随下次成功签发 收敛)。
语义注释(T2.15 设计取舍):发布失败的处置权在调用方(引擎)——部署 不回滚、route.publish_failed 单独告警 + 审计;本方法自身是幂等收敛 (重调即重试)。
func (*Manager) Run ¶
Run 是周期任务:Traefik 收敛 + 全量重发布 + 证书续期扫描(sweep)。 由 fleetlyd ingress 服务壳调用(ctx 取消返回)。收敛失败只降级日志 (下轮重试),不影响控制面其余服务。
func (*Manager) SetConfigPort ¶
SetConfigPort 由服务壳在监听确定后回填(挑战应答 URL 依赖)。
func (*Manager) WithdrawAppRoutes ¶
WithdrawAppRoutes 撤销 app 的全部路由(app 删除管线的显式撤销入口, H9):删该 app 域名台账行 → 全量重发布(台账空出的视图自然落 noop 兜底——Traefik 侧真实撤销旧路由)。幂等(无行删除为空操作、重发布 幂等);台账写入方唯一纪律(本包)决定域名清理必须经本通道。失败 语义与 PublishRoutes 同构:处置权在调用方(DeleteApp 侧失败告警)。
type PublishInput ¶
type PublishInput struct {
AppID string
AppName string
// Services 是入口服务集(有 fleetly.domains label 的服务)。
Services []ServiceRoutes
}
PublishInput 是一次路由发布的输入(engine.RoutePublisher 契约的载荷; domains/port 来自归一化 compose——首健康后由引擎提取)。
type Route ¶
type Route struct {
App string
Service string
// Port 是后端目标端口(compose expose 首端口)。
Port string
// Domains 是该服务的域名集(归一化 compose spec 产出)。
Domains []string
// Cert 标记该 app 证书是否已就绪(就绪则 443 路由 + tls.certificates
// 下发;未就绪只发 80 路由)。
Cert *CertificateRef
}
Route 是一条应用路由(app 的一个入口服务;domains 已归一化)。
type Router ¶
type Router struct {
Rule string `json:"rule"`
EntryPoints []string `json:"entryPoints"`
Service string `json:"service"`
// Priority 显式优先级(ACME 挑战路由用——压过 host 路由的默认
// 规则长度优先级)。
Priority int `json:"priority,omitempty"`
TLS *RouterTLS `json:"tls,omitempty"`
}
Router 是一条路由规则。
type ServersTransport ¶
type ServersTransport struct {
MaxIdleConnsPerHost int `json:"maxIdleConnsPerHost,omitempty"`
ForwardingTimeouts *ForwardingTimeouts `json:"forwardingTimeouts,omitempty"`
}
ServersTransport 是连接治理参数集(V4 辅助治理;缺省 fleetly-default 由平台写死,不暴露配置面)。
type ServiceRoutes ¶
type ServiceRoutes struct {
Service string
// Port 是后端端口(compose expose 首端口)。
Port string
Domains []string
}
ServiceRoutes 是单个入口服务的路由声明。
type TLSCertificate ¶
TLSCertificate 是一张证书的文件引用(容器内路径)。
type TLSDynamic ¶
type TLSDynamic struct {
Certificates []TLSCertificate `json:"certificates,omitempty"`
}
TLSDynamic 是 tls 段:证书集中下发(tls.certificates 文件引用形态, 文件经证书目录只读挂载到达 Traefik)。