mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-01 04:05:54 +00:00
* filer: identify remote-mounted entries safe to drop under disk pressure ListEvictableRemoteEntries walks every mounted directory directly on the filer store (no lazy remote listing) and returns entries that hold local chunks fully synchronized with remote, ordered oldest-cached first. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: evict remote-cached chunks oldest-first and vacuum the garbage uncacheRemoteEntry applies the same transition remote.uncache does - cleared chunks plus a reset LastLocalSyncTsNs under the entry path lock - and evictRemoteCachedEntries serializes passes over all mounts until a byte target is met. Aged victims are preferred; a second pass accepts any synchronized cached entry when aged ones cannot cover the request, since a failed read is worse than a dropped hot object. Cleared chunks only become disk space after compaction, so reclaimRemoteCacheSpace pairs each pass with a rate-limited VacuumVolume call that also picks up orphaned partial fills. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: trigger remote cache eviction under storage pressure A periodic check (30s) reads disk usage from master topology and evicts remote-mounted cached chunks once any disk crosses -filer.remoteCacheEvictThreshold (default 0.9; 0 disables), with a vacuum pass to reclaim the tombstoned needles. The cold-read cache path also kicks the same reclaim when a fill fails on exhausted volumes - the request still falls back to streaming from the remote, but the cache stops being permanently wedged full. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: flush deletion queue before remote cache vacuum Vacuum ran immediately after eviction while evicted file IDs still sat in the asynchronous deletion queue, so compaction saw no garbage and the cache stayed wedged. Flush the queue synchronously first and shorten the vacuum cooldown so sustained pressure does not wait five minutes between reclaim passes. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: cover remote cache eviction under capacity pressure Unit tests pin the eligibility filter and oldest-first ordering; the integration test runs a constrained two-node setup that saturates the cache, verifies the oldest synced entry is evicted and vacuumed, and that a later read re-caches it. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: coalesce remote cache reclaim passes A failed cache fill used to queue behind any in-flight eviction, stacking full mount traversals during a write-failure storm. Skip the pass when one is already running; the caller falls back to streaming from remote regardless. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: stop the remote cache janitor on shutdown The eviction ticker kept running after Shutdown closed the metadata store and could traverse a closed store. Give the janitor a context cancelled from Shutdown and propagate it into its master RPCs and traversals. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: vacuum only tombstoned volumes and retry deferred passes VacuumVolume with no volume id swept every collection, compacting volumes unrelated to the cache fill that failed. Now the reclaim path collects the vids of file ids actually flushed from the deletion queue and compacts only those. Vids that land inside the vacuum cooldown stay in a pending set the janitor retries on each tick, so chunks evicted just after a sweep are not stranded until the next pressure event. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: count only pressured disks when evicting remote cache The janitor measured the largest excess on one disk but let bytes on healthy disks satisfy the reclaim target. Split the topology disk view per physical disk and count only chunk bytes whose volumes sit on an over-threshold disk; entries contributing nothing there are skipped. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: compare remote cache sync time at nanosecond precision Second-precision mtime comparisons let a local write in the same second as the last sync still qualify as evictable, discarding unsynced changes. Compare LastLocalSyncTsNs against full-precision mtime (mtime_ns round-trips through the entry codec), and apply the same fix to remote.uncache's inline check. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: invalidate remote sync stamp on local content change A local overwrite that keeps the remote entry's LastLocalSyncTsNs looks evictable even though the remote copy no longer matches, and some write paths stamp mtime at second precision so a timestamp comparison cannot catch it. UpdateEntry now clears the stamp when chunks change without a fresh stamp, leaving replicated updates authoritative. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: bound remote cache master rpcs and vacuum all evicted garbage VolumeList and VacuumVolume now run under a 30s context so a stalled master cannot wedge the eviction janitor. The targeted vacuum drops the garbage threshold so volumes with under 10% deleted bytes still compact. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: tolerate straggler fills in remote cache eviction test Detached fills from the concurrent wave keep racing the final checks: live chunks legitimately fill both volumes, and a re-cached object can be evicted again before its commit is observed. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: start remote cache eviction loop after filer init The janitor's first tick dereferences fs.filer; starting the goroutine before NewFiler assigns it could panic when startup exceeds an interval. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: keep remote cache vacuum intent across retries Evicted entries now record their chunk volumes for vacuum directly, so the intent survives whoever consumes the shared deletion queue first. A pending volume keeps several vacuum attempts so tombstones that land late are still compacted, and the janitor retries pending volumes under the reclaim mutex instead of flushing unrelated deletes every tick. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: bound each remote cache vacuum request independently A shared 30s deadline across pending volumes let one slow compaction cancel the rest. Each VacuumVolume now gets its own context, and pending volumes keep more attempts since the master reports request acceptance rather than compaction. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: tighten remote cache reclamation bound Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: treat chunk timestamp changes as content changes chunksEqual now also compares ModifiedTsNs so an update that rewrites a chunk record still invalidates the remote sync stamp. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: run remote cache queue flush under the reclaim context BatchDelete for flushed file ids now uses the caller's context instead of context.Background(), so a reclaim pass bounded by shutdown or timeout stops its deletes too. Other callers keep their existing behavior. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: scope remote cache vacuum to evicted volumes The flush no longer feeds the shared deletion queue's ids into the pending set — only evicted chunks' volumes are tracked, so ordinary deletions no longer pick up repeated vacuum attempts. The flush also runs under a shutdown-immune bounded context and is skipped when no volume is pending. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: retry remote cache vacuums even after unmount Pending volumes were only retried while a remote mount existed; removing the last mount skipped every later pass and left evicted bytes allocated. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer: reclaim partial cache fills that run out of capacity A fill that fails midway queues its written chunks for deletion, but when no entries remain evictable the reclaim pass found no pending volumes and skipped the flush and vacuum entirely, leaving the partial garbage to the slow periodic vacuum while the disk stayed full. Mark the failed fill's chunk volumes pending so the pass tombstones and compacts them even when nothing was evicted. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
477 lines
30 KiB
Go
477 lines
30 KiB
Go
package command
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb"
|
|
stats_collect "github.com/seaweedfs/seaweedfs/weed/stats"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
"github.com/seaweedfs/seaweedfs/weed/util/grace"
|
|
)
|
|
|
|
type ServerOptions struct {
|
|
cpuprofile *string
|
|
memprofile *string
|
|
debug *bool
|
|
debugPort *int
|
|
v VolumeServerOptions
|
|
}
|
|
|
|
var (
|
|
serverOptions ServerOptions
|
|
masterOptions MasterOptions
|
|
filerOptions FilerOptions
|
|
s3Options S3Options
|
|
sftpOptions SftpOptions
|
|
iamOptions IamOptions
|
|
webdavOptions WebDavOption
|
|
mqBrokerOptions MessageQueueBrokerOptions
|
|
mqAgentServerOptions MessageQueueAgentOptions
|
|
)
|
|
|
|
func init() {
|
|
cmdServer.Run = runServer // break init cycle
|
|
}
|
|
|
|
var cmdServer = &Command{
|
|
UsageLine: "server -dir=/tmp -volume.max=5 -ip=server_name",
|
|
Short: "start a master server, a volume server, and optionally a filer and a S3 gateway",
|
|
Long: `start both a volume server to provide storage spaces
|
|
and a master server to provide volume=>location mapping service and sequence number of file ids
|
|
|
|
This is provided as a convenient way to start both volume server and master server.
|
|
The servers acts exactly the same as starting them separately.
|
|
So other volume servers can connect to this master server also.
|
|
|
|
Optionally, a filer server can be started.
|
|
Also optionally, a S3 gateway can be started.
|
|
|
|
`,
|
|
}
|
|
|
|
var (
|
|
serverIp = cmdServer.Flag.String("ip", util.DetectedHostAddress(), "ip or server name, also used as identifier")
|
|
serverBindIp = cmdServer.Flag.String("ip.bind", "", "ip address to bind to. If empty, default to same as -ip option.")
|
|
serverTimeout = cmdServer.Flag.Int("idleTimeout", 30, "connection idle seconds")
|
|
serverDataCenter = cmdServer.Flag.String("dataCenter", "", "current volume server's data center name")
|
|
serverRack = cmdServer.Flag.String("rack", "", "current volume server's rack name")
|
|
serverWhiteListOption = cmdServer.Flag.String("whiteList", "", "comma separated Ip addresses having write permission. No limit if empty.")
|
|
serverDisableHttp = cmdServer.Flag.Bool("disableHttp", false, "disable http requests, only gRPC operations are allowed.")
|
|
serverIamConfig = cmdServer.Flag.String("iam.config", "", "path to the advanced IAM config file for S3. An alias for -s3.iam.config, but with lower priority.")
|
|
volumeDataFolders = cmdServer.Flag.String("dir", os.TempDir(), "directories to store data files. dir[,dir]...")
|
|
volumeMaxDataVolumeCounts = cmdServer.Flag.String("volume.max", "8", "maximum numbers of volumes, count[,count]... If set to zero, the limit will be auto configured as free disk space divided by volume size.")
|
|
volumeMinFreeSpacePercent = cmdServer.Flag.String("volume.minFreeSpacePercent", "1", "minimum free disk space (default to 1%). Low disk space will mark all volumes as ReadOnly (deprecated, use minFreeSpace instead).")
|
|
volumeMinFreeSpace = cmdServer.Flag.String("volume.minFreeSpace", "", "min free disk space (value<=100 as percentage like 1, other as human readable bytes, like 10GiB). Low disk space will mark all volumes as ReadOnly.")
|
|
serverMetricsHttpPort = cmdServer.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")
|
|
serverMetricsHttpIp = cmdServer.Flag.String("metricsIp", "", "metrics listen ip. If empty, default to same as -ip.bind option.")
|
|
|
|
// pulseSeconds = cmdServer.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
|
|
isStartingMasterServer = cmdServer.Flag.Bool("master", true, "whether to start master server")
|
|
isStartingVolumeServer = cmdServer.Flag.Bool("volume", true, "whether to start volume server")
|
|
isStartingFiler = cmdServer.Flag.Bool("filer", false, "whether to start filer")
|
|
isStartingS3 = cmdServer.Flag.Bool("s3", false, "whether to start S3 gateway")
|
|
isStartingSftp = cmdServer.Flag.Bool("sftp", false, "whether to start Sftp server")
|
|
isStartingIam = cmdServer.Flag.Bool("iam", false, "whether to start IAM service")
|
|
isStartingWebDav = cmdServer.Flag.Bool("webdav", false, "whether to start WebDAV gateway")
|
|
isStartingMqBroker = cmdServer.Flag.Bool("mq.broker", false, "whether to start message queue broker")
|
|
isStartingMqAgent = cmdServer.Flag.Bool("mq.agent", false, "whether to start message queue agent")
|
|
|
|
False = false
|
|
)
|
|
|
|
func init() {
|
|
serverOptions.cpuprofile = cmdServer.Flag.String("cpuprofile", "", "cpu profile output file")
|
|
serverOptions.memprofile = cmdServer.Flag.String("memprofile", "", "memory profile output file")
|
|
serverOptions.debug = cmdServer.Flag.Bool("debug", false, "serves runtime profiling data, e.g., http://localhost:6060/debug/pprof/goroutine?debug=2")
|
|
serverOptions.debugPort = cmdServer.Flag.Int("debug.port", 6060, "http port for debugging")
|
|
|
|
masterOptions.port = cmdServer.Flag.Int("master.port", 9333, "master server http listen port")
|
|
masterOptions.portGrpc = cmdServer.Flag.Int("master.port.grpc", 0, "master server grpc listen port")
|
|
masterOptions.metaFolder = cmdServer.Flag.String("master.dir", "", "data directory to store meta data, default to same as -dir specified")
|
|
masterOptions.peers = cmdServer.Flag.String("master.peers", "", "all master nodes in comma separated ip:masterPort list")
|
|
masterOptions.volumeSizeLimitMB = cmdServer.Flag.Uint("master.volumeSizeLimitMB", util.DefaultVolumeSizeLimitMB, "Master stops directing writes to oversized volumes.")
|
|
masterOptions.volumePreallocate = cmdServer.Flag.Bool("master.volumePreallocate", false, "Preallocate disk space for volumes.")
|
|
masterOptions.maxParallelVacuumPerServer = cmdServer.Flag.Int("master.maxParallelVacuumPerServer", 1, "maximum number of volumes to vacuum in parallel on one volume server")
|
|
masterOptions.vacuumIntervalSeconds = cmdServer.Flag.Int("master.vacuumIntervalSeconds", 840, "seconds between automatic vacuum sweeps")
|
|
masterOptions.vacuumDeleteEmptyAfterSeconds = cmdServer.Flag.Int("master.vacuumDeleteEmptyAfterSeconds", 0, "automatic sweep deletes volume copies that stay empty this many seconds; 0 disables")
|
|
masterOptions.defaultReplication = cmdServer.Flag.String("master.defaultReplication", "", "Default replication type if not specified.")
|
|
masterOptions.garbageThreshold = cmdServer.Flag.Float64("master.garbageThreshold", 0.3, "threshold to vacuum and reclaim spaces")
|
|
masterOptions.metricsAddress = cmdServer.Flag.String("master.metrics.address", "", "Prometheus gateway address")
|
|
masterOptions.metricsIntervalSec = cmdServer.Flag.Int("master.metrics.intervalSeconds", 15, "Prometheus push interval in seconds")
|
|
masterOptions.raftResumeState = cmdServer.Flag.Bool("master.resumeState", true, "resume previous state on start master server")
|
|
masterOptions.raftHashicorp = cmdServer.Flag.Bool("master.raftHashicorp", false, "use hashicorp raft")
|
|
masterOptions.raftBootstrap = cmdServer.Flag.Bool("master.raftBootstrap", false, "Whether to bootstrap the Raft cluster")
|
|
masterOptions.heartbeatInterval = cmdServer.Flag.Duration("master.heartbeatInterval", 300*time.Millisecond, "heartbeat interval of master servers, and will be randomly multiplied by [1, 1.25)")
|
|
masterOptions.electionTimeout = cmdServer.Flag.Duration("master.electionTimeout", 10*time.Second, "election timeout of master servers")
|
|
masterOptions.telemetryUrl = cmdServer.Flag.String("master.telemetry.url", "https://telemetry.seaweedfs.com/api/collect", "telemetry server URL to send usage statistics")
|
|
masterOptions.telemetryEnabled = cmdServer.Flag.Bool("master.telemetry", true, "report anonymous cluster statistics to master.telemetry.url, use -master.telemetry=false to opt out")
|
|
|
|
filerOptions.filerGroup = cmdServer.Flag.String("filer.filerGroup", "", "share metadata with other filers in the same filerGroup")
|
|
filerOptions.collection = cmdServer.Flag.String("filer.collection", "", "all data will be stored in this collection")
|
|
filerOptions.port = cmdServer.Flag.Int("filer.port", 8888, "filer server http listen port")
|
|
filerOptions.portGrpc = cmdServer.Flag.Int("filer.port.grpc", 0, "filer server grpc listen port")
|
|
filerOptions.publicPort = cmdServer.Flag.Int("filer.port.public", 0, "filer server public http listen port")
|
|
filerOptions.disableHttp = cmdServer.Flag.Bool("filer.disableHttp", false, "disable filer HTTP requests, only gRPC operations are allowed")
|
|
filerOptions.allowedOrigins = cmdServer.Flag.String("filer.allowedOrigins", "*", "comma separated list of allowed origins")
|
|
filerOptions.defaultReplicaPlacement = cmdServer.Flag.String("filer.defaultReplicaPlacement", "", "default replication type. If not specified, use master setting.")
|
|
filerOptions.disableDirListing = cmdServer.Flag.Bool("filer.disableDirListing", false, "turn off directory listing")
|
|
filerOptions.maxMB = cmdServer.Flag.Int("filer.maxMB", 4, "split files larger than the limit")
|
|
filerOptions.dirListingLimit = cmdServer.Flag.Int("filer.dirListLimit", 1000, "limit sub dir listing size")
|
|
filerOptions.cipher = cmdServer.Flag.Bool("filer.encryptVolumeData", false, "encrypt data on volume servers")
|
|
filerOptions.saveToFilerLimit = cmdServer.Flag.Int("filer.saveToFilerLimit", 0, "Small files smaller than this limit can be cached in filer store.")
|
|
filerOptions.concurrentUploadLimitMB = cmdServer.Flag.Int("filer.concurrentUploadLimitMB", 0, "limit total concurrent upload size, 0 means unlimited")
|
|
filerOptions.concurrentFileUploadLimit = cmdServer.Flag.Int("filer.concurrentFileUploadLimit", 0, "limit number of concurrent file uploads, 0 means unlimited")
|
|
filerOptions.localSocket = cmdServer.Flag.String("filer.localSocket", "", "default to /tmp/seaweedfs-filer-<port>.sock")
|
|
filerOptions.showUIDirectoryDelete = cmdServer.Flag.Bool("filer.ui.deleteDir", true, "enable filer UI show delete directory button")
|
|
filerOptions.downloadMaxMBps = cmdServer.Flag.Int("filer.downloadMaxMBps", 0, "download max speed for each download request, in MB per second")
|
|
filerOptions.diskType = cmdServer.Flag.String("filer.disk", "", "[hdd|ssd|<tag>] hard drive or solid state drive or any tag")
|
|
filerOptions.exposeDirectoryData = cmdServer.Flag.Bool("filer.exposeDirectoryData", true, "expose directory data via filer. If false, filer UI will be inaccessible.")
|
|
filerOptions.tusBasePath = cmdServer.Flag.String("filer.tusBasePath", "/.tus", "TUS resumable upload endpoint base path (e.g., /.tus)")
|
|
filerOptions.tusMaxSizeMB = cmdServer.Flag.Int("filer.tusMaxSizeMB", 5*1024, "maximum TUS upload size in MB")
|
|
filerOptions.tusSessionExpiry = cmdServer.Flag.Duration("filer.tusSessionExpiry", 24*time.Hour, "incomplete TUS upload sessions are cleaned up after this duration, e.g. \"48h\", \"7h30m\"")
|
|
filerOptions.allowUntrustedRemoteEndpoints = cmdServer.Flag.Bool("filer.allowUntrustedRemoteEndpoints", false, allowUntrustedRemoteEndpointsUsage)
|
|
filerOptions.remoteCacheEvictThreshold = cmdServer.Flag.Float64("filer.remoteCacheEvictThreshold", 0.9, "evict remote-cached objects (oldest first) when any volume disk exceeds this usage fraction; 0 disables")
|
|
|
|
serverOptions.v.port = cmdServer.Flag.Int("volume.port", 8080, "volume server http listen port")
|
|
serverOptions.v.portGrpc = cmdServer.Flag.Int("volume.port.grpc", 0, "volume server grpc listen port")
|
|
serverOptions.v.publicPort = cmdServer.Flag.Int("volume.port.public", 0, "volume server public port")
|
|
serverOptions.v.id = cmdServer.Flag.String("volume.id", "", "volume server id. If empty, default to ip:port")
|
|
serverOptions.v.indexType = cmdServer.Flag.String("volume.index", "memory", "Choose [memory|leveldb|leveldbMedium|leveldbLarge] mode for memory~performance balance.")
|
|
serverOptions.v.diskType = cmdServer.Flag.String("volume.disk", "", "[hdd|ssd|<tag>] hard drive or solid state drive or any tag")
|
|
serverOptions.v.tags = cmdServer.Flag.String("volume.tags", "", "comma-separated tag groups per data dir; each group uses ':' (e.g. fast:ssd,archive)")
|
|
serverOptions.v.fixJpgOrientation = cmdServer.Flag.Bool("volume.images.fix.orientation", false, "Adjust jpg orientation when uploading.")
|
|
serverOptions.v.readMode = cmdServer.Flag.String("volume.readMode", "proxy", "[local|proxy|redirect] how to deal with non-local volume: 'not found|read in remote node|redirect volume location'.")
|
|
serverOptions.v.compactionMBPerSecond = cmdServer.Flag.Int("volume.compactionMBps", 0, "limit compaction speed in mega bytes per second")
|
|
serverOptions.v.maintenanceMBPerSecond = cmdServer.Flag.Int("volume.maintenanceMBps", 0, "limit maintenance (replication / balance) IO rate in MB/s. Unset is 0, no limitation.")
|
|
serverOptions.v.fileSizeLimitMB = cmdServer.Flag.Int("volume.fileSizeLimitMB", 256, "limit file size to avoid out of memory")
|
|
serverOptions.v.ldbTimeout = cmdServer.Flag.Int64("volume.index.leveldbTimeout", 0, "alive time for leveldb (default to 0). If leveldb of volume is not accessed in ldbTimeout hours, it will be off loaded to reduce opened files and memory consumption.")
|
|
serverOptions.v.concurrentUploadLimitMB = cmdServer.Flag.Int("volume.concurrentUploadLimitMB", 0, "limit total concurrent upload size, 0 means unlimited")
|
|
serverOptions.v.concurrentDownloadLimitMB = cmdServer.Flag.Int("volume.concurrentDownloadLimitMB", 0, "limit total concurrent download size, 0 means unlimited")
|
|
serverOptions.v.publicUrl = cmdServer.Flag.String("volume.publicUrl", "", "publicly accessible address")
|
|
serverOptions.v.preStopSeconds = cmdServer.Flag.Int("volume.preStopSeconds", 10, "number of seconds between stop send heartbeats and stop volume server")
|
|
serverOptions.v.pprof = cmdServer.Flag.Bool("volume.pprof", false, "enable pprof http handlers. precludes -memprofile and -cpuprofile")
|
|
serverOptions.v.idxFolder = cmdServer.Flag.String("volume.dir.idx", "", "directory to store .idx files")
|
|
serverOptions.v.inflightUploadDataTimeout = cmdServer.Flag.Duration("volume.inflightUploadDataTimeout", 60*time.Second, "inflight upload data wait timeout of volume servers")
|
|
serverOptions.v.inflightDownloadDataTimeout = cmdServer.Flag.Duration("volume.inflightDownloadDataTimeout", 60*time.Second, "inflight download data wait timeout of volume servers")
|
|
|
|
serverOptions.v.hasSlowRead = cmdServer.Flag.Bool("volume.hasSlowRead", true, "<experimental> if true, this prevents slow reads from blocking other requests, but large file read P99 latency will increase.")
|
|
serverOptions.v.readBufferSizeMB = cmdServer.Flag.Int("volume.readBufferSizeMB", 4, "<experimental> larger values can optimize query performance but will increase some memory usage,Use with hasSlowRead normally")
|
|
serverOptions.v.allowUntrustedRemoteEndpoints = cmdServer.Flag.Bool("volume.allowUntrustedRemoteEndpoints", false, "if true, FetchAndWriteNeedle accepts arbitrary remote S3 endpoints including loopback / link-local hosts. Default rejects internal / metadata endpoints.")
|
|
serverOptions.v.setDiskIOProbeDefaults()
|
|
|
|
s3Options.port = cmdServer.Flag.Int("s3.port", 8333, "s3 server http listen port")
|
|
s3Options.portHttps = cmdServer.Flag.Int("s3.port.https", 0, "s3 server https listen port")
|
|
s3Options.portGrpc = cmdServer.Flag.Int("s3.port.grpc", 0, "s3 server grpc listen port")
|
|
s3Options.portIceberg = cmdServer.Flag.Int("s3.port.iceberg", 8181, "Iceberg REST Catalog server listen port (0 to disable)")
|
|
s3Options.portLance = cmdServer.Flag.Int("s3.port.lance", 9101, "Lance Namespace server listen port (0 to disable)")
|
|
s3Options.icebergCredentialRole = cmdServer.Flag.String("s3.iceberg.credentialRole", "", "IAM role ARN the Iceberg catalog assumes to vend table-scoped credentials (empty disables vending)")
|
|
s3Options.icebergCredentialDuration = cmdServer.Flag.Int("s3.iceberg.credentialDurationSeconds", 3600, "lifetime of credentials vended by the Iceberg catalog")
|
|
s3Options.domainName = cmdServer.Flag.String("s3.domainName", "", "suffix of the host name in comma separated list, {bucket}.{domainName}")
|
|
s3Options.allowedOrigins = cmdServer.Flag.String("s3.allowedOrigins", "*", "comma separated list of allowed origins")
|
|
s3Options.tlsPrivateKey = cmdServer.Flag.String("s3.key.file", "", "path to the TLS private key file")
|
|
s3Options.tlsCertificate = cmdServer.Flag.String("s3.cert.file", "", "path to the TLS certificate file")
|
|
s3Options.tlsCACertificate = cmdServer.Flag.String("s3.cacert.file", "", "path to the TLS CA certificate file")
|
|
s3Options.tlsVerifyClientCert = cmdServer.Flag.Bool("s3.tlsVerifyClientCert", false, "whether to verify the client's certificate")
|
|
s3Options.config = cmdServer.Flag.String("s3.config", "", "path to the config file")
|
|
s3Options.iamConfig = cmdServer.Flag.String("s3.iam.config", "", "path to the advanced IAM config file for S3. Overrides -iam.config if both are provided.")
|
|
s3Options.auditLogConfig = cmdServer.Flag.String("s3.auditLogConfig", "", "path to the audit log config file")
|
|
cmdServer.Flag.Bool("s3.allowEmptyFolder", true, "deprecated, ignored. Empty folder cleanup is now automatic.")
|
|
s3Options.allowDeleteBucketNotEmpty = cmdServer.Flag.Bool("s3.allowDeleteBucketNotEmpty", true, "allow recursive deleting all entries along with bucket")
|
|
s3Options.autoCreateBucket = cmdServer.Flag.Bool("s3.autoCreateBucket", true, "create the bucket on upload if it does not exist, for admin identities only")
|
|
s3Options.localSocket = cmdServer.Flag.String("s3.localSocket", "", "default to /tmp/seaweedfs-s3-<port>.sock")
|
|
s3Options.bindIp = cmdServer.Flag.String("s3.ip.bind", "", "ip address to bind to. If empty, default to same as -ip.bind option.")
|
|
s3Options.idleTimeout = cmdServer.Flag.Int("s3.idleTimeout", 120, "connection idle seconds")
|
|
s3Options.concurrentUploadLimitMB = cmdServer.Flag.Int("s3.concurrentUploadLimitMB", 0, "limit total concurrent upload size for S3, 0 means unlimited")
|
|
s3Options.concurrentFileUploadLimit = cmdServer.Flag.Int("s3.concurrentFileUploadLimit", 0, "limit number of concurrent file uploads for S3, 0 means unlimited")
|
|
s3Options.enableIam = cmdServer.Flag.Bool("s3.iam", true, "enable embedded IAM API on the same S3 port")
|
|
s3Options.iamReadOnly = cmdServer.Flag.Bool("s3.iam.readOnly", true, "disable IAM write operations on this server")
|
|
s3Options.cipher = cmdServer.Flag.Bool("s3.encryptVolumeData", false, "encrypt data on volume servers for S3 uploads")
|
|
s3Options.externalUrl = cmdServer.Flag.String("s3.externalUrl", "", "the external URL clients use to connect (e.g. https://api.example.com:9000). Advertised to Iceberg and Lance clients, and tried first when verifying S3 signatures behind a reverse proxy. Falls back to S3_EXTERNAL_URL env var.")
|
|
s3Options.defaultFileMode = cmdServer.Flag.String("s3.defaultFileMode", "", "default file mode for S3 uploaded objects, e.g. 0660, 0644, 0666")
|
|
s3Options.cacheSizeMB = cmdServer.Flag.Int64("s3.cacheCapacityMB", 0, "in-memory chunk cache capacity in MB for S3 GETs shared across requests (0 disables)")
|
|
s3Options.readerCacheSizeMB = cmdServer.Flag.Int64("s3.readerCacheSizeMB", 0, "memory budget in MiB for downloaded and in-flight reader buffers across all S3 GETs (0 means unlimited)")
|
|
s3Options.allowUntrustedRemoteEndpoints = cmdServer.Flag.Bool("s3.allowUntrustedRemoteEndpoints", false, allowUntrustedRemoteEndpointsUsage)
|
|
|
|
sftpOptions.port = cmdServer.Flag.Int("sftp.port", 2022, "SFTP server listen port")
|
|
sftpOptions.sshPrivateKey = cmdServer.Flag.String("sftp.sshPrivateKey", "", "path to the SSH private key file for host authentication")
|
|
sftpOptions.hostKeysFolder = cmdServer.Flag.String("sftp.hostKeysFolder", "", "path to folder containing SSH private key files for host authentication")
|
|
sftpOptions.authMethods = cmdServer.Flag.String("sftp.authMethods", "password,publickey", "comma-separated list of allowed auth methods: password, publickey, certificate")
|
|
sftpOptions.maxAuthTries = cmdServer.Flag.Int("sftp.maxAuthTries", 6, "maximum number of authentication attempts per connection")
|
|
sftpOptions.bannerMessage = cmdServer.Flag.String("sftp.bannerMessage", "SeaweedFS SFTP Server - Unauthorized access is prohibited", "message displayed before authentication")
|
|
sftpOptions.loginGraceTime = cmdServer.Flag.Duration("sftp.loginGraceTime", 2*time.Minute, "timeout for authentication")
|
|
sftpOptions.clientAliveInterval = cmdServer.Flag.Duration("sftp.clientAliveInterval", 5*time.Second, "interval for sending keep-alive messages")
|
|
sftpOptions.clientAliveCountMax = cmdServer.Flag.Int("sftp.clientAliveCountMax", 3, "maximum number of missed keep-alive messages before disconnecting")
|
|
sftpOptions.userStoreFile = cmdServer.Flag.String("sftp.userStoreFile", "", "path to JSON file containing user credentials and permissions")
|
|
sftpOptions.trustedUserCAKeysFile = cmdServer.Flag.String("sftp.trustedUserCAKeysFile", "", "path to a file with trusted user CA public keys (OpenSSH authorized_keys format); required when 'certificate' is in -sftp.authMethods")
|
|
sftpOptions.localSocket = cmdServer.Flag.String("sftp.localSocket", "", "default to /tmp/seaweedfs-sftp-<port>.sock")
|
|
iamOptions.port = cmdServer.Flag.Int("iam.port", 8111, "iam server http listen port")
|
|
|
|
webdavOptions.port = cmdServer.Flag.Int("webdav.port", 7333, "webdav server http listen port")
|
|
webdavOptions.collection = cmdServer.Flag.String("webdav.collection", "", "collection to create the files")
|
|
webdavOptions.replication = cmdServer.Flag.String("webdav.replication", "", "replication to create the files")
|
|
webdavOptions.disk = cmdServer.Flag.String("webdav.disk", "", "[hdd|ssd|<tag>] hard drive or solid state drive or any tag")
|
|
webdavOptions.tlsPrivateKey = cmdServer.Flag.String("webdav.key.file", "", "path to the TLS private key file")
|
|
webdavOptions.tlsCertificate = cmdServer.Flag.String("webdav.cert.file", "", "path to the TLS certificate file")
|
|
webdavOptions.cacheDir = cmdServer.Flag.String("webdav.cacheDir", os.TempDir(), "local cache directory for file chunks")
|
|
webdavOptions.cacheSizeMB = cmdServer.Flag.Int64("webdav.cacheCapacityMB", 0, "local cache capacity in MB")
|
|
webdavOptions.maxMB = cmdServer.Flag.Int("webdav.maxMB", 4, "split files larger than the limit")
|
|
webdavOptions.filerRootPath = cmdServer.Flag.String("webdav.filer.path", "/", "use this remote path from filer server")
|
|
|
|
mqBrokerOptions.port = cmdServer.Flag.Int("mq.broker.port", 17777, "message queue broker gRPC listen port")
|
|
mqBrokerOptions.logFlushInterval = cmdServer.Flag.Int("mq.broker.logFlushInterval", 5, "log buffer flush interval in seconds")
|
|
|
|
mqAgentServerOptions.brokersString = cmdServer.Flag.String("mq.agent.brokers", "localhost:17777", "comma-separated message queue brokers")
|
|
mqAgentServerOptions.port = cmdServer.Flag.Int("mq.agent.port", 16777, "message queue agent gRPC listen port")
|
|
|
|
}
|
|
|
|
func runServer(cmd *Command, args []string) bool {
|
|
|
|
if *serverOptions.debug {
|
|
go http.ListenAndServe(fmt.Sprintf(":%d", *serverOptions.debugPort), nil)
|
|
}
|
|
|
|
util.LoadSecurityConfiguration()
|
|
util.LoadConfiguration("master", false)
|
|
util.LoadConfiguration("volume", false)
|
|
serverOptions.v.applyDiskIOProbeConfig()
|
|
|
|
*serverOptions.cpuprofile = util.ResolvePath(*serverOptions.cpuprofile)
|
|
*serverOptions.memprofile = util.ResolvePath(*serverOptions.memprofile)
|
|
*serverIamConfig = util.ResolvePath(*serverIamConfig)
|
|
*masterOptions.metaFolder = util.ResolvePath(*masterOptions.metaFolder)
|
|
s3Options.resolvePaths()
|
|
webdavOptions.resolvePaths()
|
|
sftpOptions.resolvePaths()
|
|
grace.SetupProfiling(*serverOptions.cpuprofile, *serverOptions.memprofile)
|
|
|
|
if *isStartingS3 {
|
|
*isStartingFiler = true
|
|
}
|
|
if *isStartingSftp {
|
|
*isStartingFiler = true
|
|
}
|
|
if *isStartingIam {
|
|
*isStartingFiler = true
|
|
}
|
|
if *isStartingWebDav {
|
|
*isStartingFiler = true
|
|
}
|
|
if *isStartingMqBroker {
|
|
*isStartingFiler = true
|
|
}
|
|
if *isStartingMqAgent {
|
|
*isStartingMqBroker = true
|
|
*isStartingFiler = true
|
|
}
|
|
|
|
var actualPeersForComponents string
|
|
if *isStartingMasterServer {
|
|
// If we are starting a master, validate and complete the peer list
|
|
_, peerList := checkPeers(*serverIp, *masterOptions.port, *masterOptions.portGrpc, *masterOptions.peers)
|
|
actualPeersForComponents = strings.Join(pb.ToAddressStrings(peerList), ",")
|
|
} else if *masterOptions.peers != "" {
|
|
if isSingleMasterMode(*masterOptions.peers) {
|
|
glog.Fatalf("'-master.peers=none' is only valid when starting a master server, but master is not starting.")
|
|
}
|
|
// If not starting a master, just use the provided peers
|
|
actualPeersForComponents = *masterOptions.peers
|
|
}
|
|
|
|
if *serverBindIp == "" {
|
|
serverBindIp = serverIp
|
|
}
|
|
// Bind outbound connections to the same address up front so every
|
|
// component started below inherits it, before any of them dials.
|
|
util.SetOutboundLocalIP(*serverBindIp)
|
|
|
|
if *serverMetricsHttpIp == "" {
|
|
*serverMetricsHttpIp = *serverBindIp
|
|
}
|
|
|
|
// ip address
|
|
masterOptions.ip = serverIp
|
|
masterOptions.ipBind = serverBindIp
|
|
// Use actualPeersForComponents for volume/filer, not masterOptions.peers which might be "none"
|
|
filerOptions.masters = pb.ServerAddresses(actualPeersForComponents).ToServiceDiscovery()
|
|
filerOptions.ip = serverIp
|
|
filerOptions.bindIp = serverBindIp
|
|
s3Options.ip = serverIp
|
|
if *s3Options.bindIp == "" {
|
|
s3Options.bindIp = serverBindIp
|
|
}
|
|
if sftpOptions.bindIp == nil || *sftpOptions.bindIp == "" {
|
|
sftpOptions.bindIp = serverBindIp
|
|
}
|
|
iamOptions.ip = serverBindIp
|
|
iamOptions.masters = &actualPeersForComponents
|
|
webdavOptions.ipBind = serverBindIp
|
|
serverOptions.v.ip = serverIp
|
|
serverOptions.v.bindIp = serverBindIp
|
|
serverOptions.v.masters = pb.ServerAddresses(actualPeersForComponents).ToAddresses()
|
|
serverOptions.v.idleConnectionTimeout = serverTimeout
|
|
serverOptions.v.dataCenter = serverDataCenter
|
|
serverOptions.v.rack = serverRack
|
|
mqBrokerOptions.ip = serverIp
|
|
mqBrokerOptions.masters = filerOptions.masters.GetInstancesAsMap()
|
|
mqBrokerOptions.filerGroup = filerOptions.filerGroup
|
|
mqAgentServerOptions.ip = serverIp
|
|
mqAgentServerOptions.brokers = pb.ServerAddresses(*mqAgentServerOptions.brokersString).ToAddresses()
|
|
|
|
// serverOptions.v.pulseSeconds = pulseSeconds
|
|
// masterOptions.pulseSeconds = pulseSeconds
|
|
|
|
masterOptions.whiteList = serverWhiteListOption
|
|
// The master's /submit buffers the upload before handing it to the volume
|
|
// server started here, so it must not reject what that volume server accepts.
|
|
masterOptions.fileSizeLimitMB = serverOptions.v.fileSizeLimitMB
|
|
|
|
filerOptions.dataCenter = serverDataCenter
|
|
filerOptions.rack = serverRack
|
|
mqBrokerOptions.dataCenter = serverDataCenter
|
|
mqBrokerOptions.rack = serverRack
|
|
s3Options.dataCenter = serverDataCenter
|
|
sftpOptions.dataCenter = serverDataCenter
|
|
*filerOptions.disableHttp = *filerOptions.disableHttp || *serverDisableHttp
|
|
masterOptions.disableHttp = serverDisableHttp
|
|
|
|
filerAddress := string(pb.NewServerAddress(*serverIp, *filerOptions.port, *filerOptions.portGrpc))
|
|
s3Options.filer = &filerAddress
|
|
sftpOptions.filer = &filerAddress
|
|
iamOptions.filer = &filerAddress
|
|
webdavOptions.filer = &filerAddress
|
|
mqBrokerOptions.filerGroup = filerOptions.filerGroup
|
|
|
|
go stats_collect.StartMetricsServer(*serverMetricsHttpIp, *serverMetricsHttpPort)
|
|
|
|
*volumeDataFolders = util.ResolveCommaSeparatedPaths(*volumeDataFolders)
|
|
folders := strings.Split(*volumeDataFolders, ",")
|
|
|
|
if *masterOptions.volumeSizeLimitMB > util.MaxVolumeSizeLimitMB {
|
|
glog.Fatalf("master.volumeSizeLimitMB should not exceed %d", util.MaxVolumeSizeLimitMB)
|
|
}
|
|
|
|
if *masterOptions.metaFolder == "" {
|
|
*masterOptions.metaFolder = folders[0]
|
|
}
|
|
if err := util.TestFolderWritable(*masterOptions.metaFolder); err != nil {
|
|
glog.Fatalf("Check Meta Folder (-mdir=\"%s\") Writable: %s", *masterOptions.metaFolder, err)
|
|
}
|
|
filerOptions.defaultLevelDbDirectory = masterOptions.metaFolder
|
|
|
|
// Register Unix socket paths for gRPC services running in this process
|
|
// so local inter-service communication uses Unix sockets instead of TCP.
|
|
// Resolve gRPC ports early (same calculation each service does internally).
|
|
for _, svc := range []struct {
|
|
starting *bool
|
|
portGrpc *int
|
|
port *int
|
|
name string
|
|
}{
|
|
{isStartingMasterServer, masterOptions.portGrpc, masterOptions.port, "master"},
|
|
{isStartingVolumeServer, serverOptions.v.portGrpc, serverOptions.v.port, "volume"},
|
|
{isStartingFiler, filerOptions.portGrpc, filerOptions.port, "filer"},
|
|
{isStartingS3, s3Options.portGrpc, s3Options.port, "s3"},
|
|
} {
|
|
if *svc.starting {
|
|
if *svc.portGrpc == 0 {
|
|
*svc.portGrpc = 10000 + *svc.port
|
|
}
|
|
pb.RegisterLocalGrpcSocket(*serverIp, *svc.portGrpc, fmt.Sprintf("/tmp/seaweedfs-%s-grpc-%d.sock", svc.name, *svc.portGrpc))
|
|
}
|
|
}
|
|
|
|
serverWhiteList := util.StringSplit(*serverWhiteListOption, ",")
|
|
|
|
if *isStartingFiler {
|
|
if *filerOptions.diskType == "" && *serverOptions.v.diskType != "" {
|
|
filerOptions.diskType = serverOptions.v.diskType
|
|
}
|
|
go func() {
|
|
time.Sleep(1 * time.Second)
|
|
filerOptions.startFiler()
|
|
}()
|
|
}
|
|
|
|
if *isStartingS3 {
|
|
// Handle IAM config: -s3.iam.config takes precedence over -iam.config
|
|
if *s3Options.iamConfig == "" {
|
|
*s3Options.iamConfig = *serverIamConfig
|
|
} else if *serverIamConfig != "" && *s3Options.iamConfig != *serverIamConfig {
|
|
glog.V(0).Infof("both -s3.iam.config(%s) and -iam.config(%s) provided; using -s3.iam.config", *s3Options.iamConfig, *serverIamConfig)
|
|
}
|
|
// Share the S3 static identity config file with the filer
|
|
filerOptions.s3ConfigFile = s3Options.config
|
|
go func() {
|
|
time.Sleep(2 * time.Second)
|
|
s3Options.localFilerSocket = filerOptions.localSocket
|
|
s3Options.startS3Server()
|
|
}()
|
|
}
|
|
|
|
if *isStartingSftp {
|
|
go func() {
|
|
time.Sleep(2 * time.Second)
|
|
sftpOptions.localSocket = filerOptions.localSocket
|
|
sftpOptions.startSftpServer()
|
|
}()
|
|
}
|
|
|
|
if *isStartingIam {
|
|
go func() {
|
|
time.Sleep(2 * time.Second)
|
|
iamOptions.startIamServer()
|
|
}()
|
|
}
|
|
|
|
if *isStartingWebDav {
|
|
go func() {
|
|
time.Sleep(2 * time.Second)
|
|
webdavOptions.startWebDav()
|
|
|
|
}()
|
|
}
|
|
|
|
if *isStartingMqBroker {
|
|
go func() {
|
|
time.Sleep(2 * time.Second)
|
|
mqBrokerOptions.startQueueServer()
|
|
}()
|
|
}
|
|
|
|
if *isStartingMqAgent {
|
|
go func() {
|
|
time.Sleep(2 * time.Second)
|
|
mqAgentServerOptions.startQueueAgent()
|
|
}()
|
|
}
|
|
|
|
// start volume server
|
|
if *isStartingVolumeServer {
|
|
minFreeSpaces := util.MustParseMinFreeSpace(*volumeMinFreeSpace, *volumeMinFreeSpacePercent)
|
|
go serverOptions.v.startVolumeServer(*volumeDataFolders, *volumeMaxDataVolumeCounts, *serverWhiteListOption, minFreeSpaces)
|
|
}
|
|
|
|
if *isStartingMasterServer {
|
|
go startMaster(masterOptions, serverWhiteList)
|
|
}
|
|
|
|
select {}
|
|
}
|
|
|
|
func newHttpServer(h http.Handler, tlsConfig *tls.Config) *http.Server {
|
|
s := &http.Server{
|
|
Handler: h,
|
|
}
|
|
if tlsConfig != nil {
|
|
s.TLSConfig = tlsConfig.Clone()
|
|
}
|
|
return s
|
|
}
|