s3: actually bind outbound connections when -ip.bind is set (#9849)

* s3: set outbound bind IP before the first filer dial

Standalone weed s3 dialed the filer for GetFilerConfiguration before
SetOutboundLocalIP ran, so that gRPC conn was created with the stock
dialer and no source address. gRPC caches conns by address and reuses
the original dialer on reconnect, so the s3->filer connection kept
leaving from the OS-chosen source for the life of the process even
after the bind IP was set a moment later.

* grpc: install the outbound-bind dialer unconditionally

The dialer was installed only when OutboundLocalAddr was already set at
GrpcDial time, baking the source-address decision into the cached conn,
so a conn dialed before the bind IP was configured never bound.

Install the context dialer always and decide per dial: bind through
OutboundDialContext once a source is set, otherwise fall back to the
stock net.Dialer so default deployments keep gRPC's dial timeout and
keepalive behavior. The bind now applies on the next reconnect
regardless of ordering, matching the HTTP transport's unconditional
DialContext.
This commit is contained in:
Chris Lu
2026-06-07 10:20:58 -07:00
committed by GitHub
parent 0e9fc6c5ba
commit 755af4adf4
2 changed files with 10 additions and 5 deletions
+3 -1
View File
@@ -261,6 +261,9 @@ func (s3opt *S3Options) resolvePaths() {
func (s3opt *S3Options) startS3Server() bool {
// Before the first filer dial below; gRPC caches conns and binds at dial time.
util.SetOutboundLocalIP(*s3opt.bindIp)
filerAddresses := pb.ServerAddresses(*s3opt.filer).ToAddresses()
filerBucketsPath := "/buckets"
@@ -324,7 +327,6 @@ func (s3opt *S3Options) startS3Server() bool {
if *s3opt.bindIp == "" {
*s3opt.bindIp = "0.0.0.0"
}
util.SetOutboundLocalIP(*s3opt.bindIp)
defaultFileMode, fileModeErr := s3opt.parseDefaultFileMode()
if fileModeErr != nil {
+7 -4
View File
@@ -212,11 +212,14 @@ func GrpcDial(ctx context.Context, address string, waitForReady bool, opts ...gr
var d net.Dialer
return d.DialContext(ctx, "unix", socketPath)
}))
} else if util.OutboundLocalAddr() != nil {
// Bind outbound gRPC connections to the configured -ip.bind source
// address. Only installed when a source address is set, so default
// deployments keep gRPC's stock dialer behavior.
} else {
// Always install so a conn cached before SetOutboundLocalIP binds once set;
// the stock net.Dialer until then preserves gRPC's default dial behavior.
options = append(options, grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
if util.OutboundLocalAddr() == nil {
var d net.Dialer
return d.DialContext(ctx, "tcp", addr)
}
return util.OutboundDialContext(ctx, "tcp", addr)
}))
}