2016-06-03 09:09:14 +08:00
package command
2012-08-07 16:29:22 +08:00
import (
2022-06-21 10:04:49 +08:00
"fmt"
2022-10-07 00:30:30 +08:00
hashicorpRaft "github.com/hashicorp/raft"
2019-06-05 16:30:24 +08:00
"net/http"
"os"
2022-06-21 10:04:49 +08:00
"path"
2019-06-05 16:30:24 +08:00
"strings"
2020-10-07 16:25:39 +08:00
"time"
2020-01-30 01:09:55 +08:00
2022-07-31 01:26:56 +08:00
"golang.org/x/exp/slices"
2022-03-15 07:22:52 +08:00
"github.com/gorilla/mux"
2022-07-28 03:12:40 +08:00
"github.com/seaweedfs/raft/protobuf"
2022-03-15 07:22:52 +08:00
"github.com/spf13/viper"
"google.golang.org/grpc/reflection"
2022-07-31 01:26:56 +08:00
stats_collect "github.com/seaweedfs/seaweedfs/weed/stats"
2022-07-29 15:17:28 +08:00
"github.com/seaweedfs/seaweedfs/weed/util/grace"
2020-07-17 13:50:14 +08:00
2022-07-29 15:17:28 +08:00
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/pb"
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
"github.com/seaweedfs/seaweedfs/weed/security"
weed_server "github.com/seaweedfs/seaweedfs/weed/server"
"github.com/seaweedfs/seaweedfs/weed/storage/backend"
"github.com/seaweedfs/seaweedfs/weed/util"
2012-08-07 16:29:22 +08:00
)
2019-06-23 18:08:27 +08:00
var (
m MasterOptions
)
type MasterOptions struct {
2020-07-14 22:34:16 +08:00
port * int
2021-09-12 17:19:10 +08:00
portGrpc * int
2020-07-14 22:34:16 +08:00
ip * string
ipBind * string
metaFolder * string
peers * string
volumeSizeLimitMB * uint
volumePreallocate * bool
2020-06-05 01:52:01 +08:00
// pulseSeconds *int
2019-06-23 18:08:27 +08:00
defaultReplication * string
garbageThreshold * float64
whiteList * string
disableHttp * bool
metricsAddress * string
metricsIntervalSec * int
2020-10-03 17:03:41 +08:00
raftResumeState * bool
2022-01-20 00:43:22 +08:00
metricsHttpPort * int
2022-02-12 18:19:49 +08:00
heartbeatInterval * time . Duration
electionTimeout * time . Duration
2022-04-04 16:50:56 +08:00
raftHashicorp * bool
raftBootstrap * bool
2019-06-23 18:08:27 +08:00
}
2012-08-07 16:29:22 +08:00
func init ( ) {
2012-09-04 10:18:02 +08:00
cmdMaster . Run = runMaster // break init cycle
2019-06-23 18:08:27 +08:00
m . port = cmdMaster . Flag . Int ( "port" , 9333 , "http listen port" )
2021-09-21 05:05:59 +08:00
m . portGrpc = cmdMaster . Flag . Int ( "port.grpc" , 0 , "grpc listen port" )
2021-03-24 08:27:57 +08:00
m . ip = cmdMaster . Flag . String ( "ip" , util . DetectedHostAddress ( ) , "master <ip>|<server> address, also used as identifier" )
2022-03-12 06:02:39 +08:00
m . ipBind = cmdMaster . Flag . String ( "ip.bind" , "" , "ip address to bind to. If empty, default to same as -ip option." )
2019-06-23 18:08:27 +08:00
m . metaFolder = cmdMaster . Flag . String ( "mdir" , os . TempDir ( ) , "data directory to store meta data" )
2020-04-14 03:58:45 +08:00
m . peers = cmdMaster . Flag . String ( "peers" , "" , "all master nodes in comma separated ip:port list, example: 127.0.0.1:9093,127.0.0.1:9094,127.0.0.1:9095" )
2019-06-23 18:08:27 +08:00
m . volumeSizeLimitMB = cmdMaster . Flag . Uint ( "volumeSizeLimitMB" , 30 * 1000 , "Master stops directing writes to oversized volumes." )
m . volumePreallocate = cmdMaster . Flag . Bool ( "volumePreallocate" , false , "Preallocate disk space for volumes." )
2020-06-05 01:52:01 +08:00
// m.pulseSeconds = cmdMaster.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
2021-08-14 17:54:13 +08:00
m . defaultReplication = cmdMaster . Flag . String ( "defaultReplication" , "" , "Default replication type if not specified." )
2019-06-23 18:08:27 +08:00
m . garbageThreshold = cmdMaster . Flag . Float64 ( "garbageThreshold" , 0.3 , "threshold to vacuum and reclaim spaces" )
m . whiteList = cmdMaster . Flag . String ( "whiteList" , "" , "comma separated Ip addresses having write permission. No limit if empty." )
m . disableHttp = cmdMaster . Flag . Bool ( "disableHttp" , false , "disable http requests, only gRPC operations are allowed." )
2020-09-19 15:03:00 +08:00
m . metricsAddress = cmdMaster . Flag . String ( "metrics.address" , "" , "Prometheus gateway address <host>:<port>" )
2019-06-23 18:08:27 +08:00
m . metricsIntervalSec = cmdMaster . Flag . Int ( "metrics.intervalSeconds" , 15 , "Prometheus push interval in seconds" )
2022-01-20 00:43:22 +08:00
m . metricsHttpPort = cmdMaster . Flag . Int ( "metricsPort" , 0 , "Prometheus metrics listen port" )
2020-10-04 01:16:47 +08:00
m . raftResumeState = cmdMaster . Flag . Bool ( "resumeState" , false , "resume previous state on start master server" )
2022-02-12 18:19:49 +08:00
m . heartbeatInterval = cmdMaster . Flag . Duration ( "heartbeatInterval" , 300 * time . Millisecond , "heartbeat interval of master servers, and will be randomly multiplied by [1, 1.25)" )
m . electionTimeout = cmdMaster . Flag . Duration ( "electionTimeout" , 10 * time . Second , "election timeout of master servers" )
2022-04-04 16:50:56 +08:00
m . raftHashicorp = cmdMaster . Flag . Bool ( "raftHashicorp" , false , "use hashicorp raft" )
m . raftBootstrap = cmdMaster . Flag . Bool ( "raftBootstrap" , false , "Whether to bootstrap the Raft cluster" )
2012-08-07 16:29:22 +08:00
}
var cmdMaster = & Command {
2012-09-04 10:18:02 +08:00
UsageLine : "master -port=9333" ,
Short : "start a master server" ,
2019-02-10 13:07:12 +08:00
Long : ` start a master server to provide volume = > location mapping service and sequence number of file ids
2020-12-08 08:46:48 +08:00
The configuration file "security.toml" is read from "." , "$HOME/.seaweedfs/" , "/usr/local/etc/seaweedfs/" , or "/etc/seaweedfs/" , in that order .
2019-02-10 13:07:12 +08:00
The example security . toml configuration file can be generated by "weed scaffold -config=security"
2012-08-07 16:29:22 +08:00
` ,
}
var (
2019-06-23 18:08:27 +08:00
masterCpuProfile = cmdMaster . Flag . String ( "cpuprofile" , "" , "cpu profile output file" )
masterMemProfile = cmdMaster . Flag . String ( "memprofile" , "" , "memory profile output file" )
2012-08-07 16:29:22 +08:00
)
func runMaster ( cmd * Command , args [ ] string ) bool {
2019-02-19 04:11:52 +08:00
2019-06-05 16:30:24 +08:00
util . LoadConfiguration ( "security" , false )
util . LoadConfiguration ( "master" , false )
2019-02-19 04:11:52 +08:00
2020-04-28 14:10:23 +08:00
grace . SetupProfiling ( * masterCpuProfile , * masterMemProfile )
2017-06-22 16:33:58 +08:00
2020-10-24 09:18:46 +08:00
parent , _ := util . FullPath ( * m . metaFolder ) . DirAndName ( )
2020-10-25 11:12:04 +08:00
if util . FileExists ( string ( parent ) ) && ! util . FileExists ( * m . metaFolder ) {
2020-10-24 09:18:46 +08:00
os . MkdirAll ( * m . metaFolder , 0755 )
}
2020-07-17 13:50:14 +08:00
if err := util . TestFolderWritable ( util . ResolvePath ( * m . metaFolder ) ) ; err != nil {
2019-06-23 18:08:27 +08:00
glog . Fatalf ( "Check Meta Folder (-mdir) Writable %s : %s" , * m . metaFolder , err )
2013-12-10 05:27:09 +08:00
}
2019-07-28 16:55:05 +08:00
2022-08-07 16:34:32 +08:00
masterWhiteList := util . StringSplit ( * m . whiteList , "," )
2019-06-23 18:08:27 +08:00
if * m . volumeSizeLimitMB > util . VolumeSizeLimitGB * 1000 {
2017-07-17 12:40:47 +08:00
glog . Fatalf ( "volumeSizeLimitMB should be smaller than 30000" )
}
2013-08-14 00:31:19 +08:00
2022-01-20 00:43:22 +08:00
go stats_collect . StartMetricsServer ( * m . metricsHttpPort )
2019-07-28 16:55:05 +08:00
startMaster ( m , masterWhiteList )
2014-02-15 09:10:49 +08:00
2019-07-28 16:55:05 +08:00
return true
}
2014-04-26 13:09:42 +08:00
2019-07-28 16:55:05 +08:00
func startMaster ( masterOption MasterOptions , masterWhiteList [ ] string ) {
2019-07-28 18:58:13 +08:00
2020-01-30 01:09:55 +08:00
backend . LoadConfiguration ( util . GetViper ( ) )
2019-11-29 10:33:18 +08:00
2021-09-21 05:05:59 +08:00
if * masterOption . portGrpc == 0 {
* masterOption . portGrpc = 10000 + * masterOption . port
}
2022-03-12 06:02:39 +08:00
if * masterOption . ipBind == "" {
2022-03-18 05:25:25 +08:00
* masterOption . ipBind = * masterOption . ip
2022-03-12 06:02:39 +08:00
}
2021-09-21 05:05:59 +08:00
2021-09-13 13:47:52 +08:00
myMasterAddress , peers := checkPeers ( * masterOption . ip , * masterOption . port , * masterOption . portGrpc , * masterOption . peers )
2019-07-28 18:58:13 +08:00
2022-03-27 04:33:17 +08:00
masterPeers := make ( map [ string ] pb . ServerAddress )
for _ , peer := range peers {
2022-04-02 08:34:42 +08:00
masterPeers [ string ( peer ) ] = peer
2022-03-27 04:33:17 +08:00
}
2019-07-28 16:55:05 +08:00
r := mux . NewRouter ( )
2022-03-27 04:33:17 +08:00
ms := weed_server . NewMasterServer ( r , masterOption . toMasterOption ( masterWhiteList ) , masterPeers )
2021-09-08 10:29:42 +08:00
listeningAddress := util . JoinHostPort ( * masterOption . ipBind , * masterOption . port )
2020-06-02 15:10:35 +08:00
glog . V ( 0 ) . Infof ( "Start Seaweed Master %s at %s" , util . Version ( ) , listeningAddress )
2022-09-15 02:59:55 +08:00
masterListener , masterLocalListener , e := util . NewIpAndLocalListeners ( * masterOption . ipBind , * masterOption . port , 0 )
2014-03-21 02:07:15 +08:00
if e != nil {
2015-01-14 09:04:41 +08:00
glog . Fatalf ( "Master startup error: %v" , e )
2012-09-29 01:21:06 +08:00
}
2022-03-15 07:22:52 +08:00
2019-07-28 16:55:05 +08:00
// start raftServer
2022-06-21 10:04:49 +08:00
metaDir := path . Join ( * masterOption . metaFolder , fmt . Sprintf ( "m%d" , * masterOption . port ) )
2022-02-12 18:19:49 +08:00
raftServerOption := & weed_server . RaftServerOption {
GrpcDialOption : security . LoadClientTLS ( util . GetViper ( ) , "grpc.master" ) ,
2022-03-27 04:13:19 +08:00
Peers : masterPeers ,
2022-02-12 18:19:49 +08:00
ServerAddr : myMasterAddress ,
2022-06-21 10:04:49 +08:00
DataDir : util . ResolvePath ( metaDir ) ,
2022-02-12 18:19:49 +08:00
Topo : ms . Topo ,
RaftResumeState : * masterOption . raftResumeState ,
HeartbeatInterval : * masterOption . heartbeatInterval ,
ElectionTimeout : * masterOption . electionTimeout ,
2022-04-04 16:50:56 +08:00
RaftBootstrap : * m . raftBootstrap ,
2022-02-12 18:19:49 +08:00
}
2022-04-04 16:50:56 +08:00
var raftServer * weed_server . RaftServer
var err error
if * m . raftHashicorp {
2022-04-04 20:51:51 +08:00
if raftServer , err = weed_server . NewHashicorpRaftServer ( raftServerOption ) ; err != nil {
glog . Fatalf ( "NewHashicorpRaftServer: %s" , err )
}
2022-04-04 16:50:56 +08:00
} else {
raftServer , err = weed_server . NewRaftServer ( raftServerOption )
2022-04-04 20:51:51 +08:00
if raftServer == nil {
2022-07-29 15:17:28 +08:00
glog . Fatalf ( "please verify %s is writable, see https://github.com/seaweedfs/seaweedfs/issues/717: %s" , * masterOption . metaFolder , err )
2022-04-04 20:51:51 +08:00
}
2019-07-28 16:55:05 +08:00
}
ms . SetRaftServer ( raftServer )
r . HandleFunc ( "/cluster/status" , raftServer . StatusHandler ) . Methods ( "GET" )
2022-07-31 01:26:56 +08:00
r . HandleFunc ( "/cluster/healthz" , raftServer . HealthzHandler ) . Methods ( "GET" , "HEAD" )
2022-04-04 16:50:56 +08:00
if * m . raftHashicorp {
2022-04-04 22:16:06 +08:00
r . HandleFunc ( "/raft/stats" , raftServer . StatsRaftHandler ) . Methods ( "GET" )
2022-04-04 16:50:56 +08:00
}
2019-07-28 16:55:05 +08:00
// starting grpc server
2021-09-12 17:19:10 +08:00
grpcPort := * masterOption . portGrpc
2022-03-16 13:28:18 +08:00
grpcL , grpcLocalL , err := util . NewIpAndLocalListeners ( * masterOption . ipBind , grpcPort , 0 )
2019-07-28 16:55:05 +08:00
if err != nil {
glog . Fatalf ( "master failed to listen on grpc port %d: %v" , grpcPort , err )
}
2020-03-04 16:39:47 +08:00
grpcS := pb . NewGrpcServer ( security . LoadServerTLS ( util . GetViper ( ) , "grpc.master" ) )
2019-07-28 16:55:05 +08:00
master_pb . RegisterSeaweedServer ( grpcS , ms )
2022-04-04 16:50:56 +08:00
if * m . raftHashicorp {
raftServer . TransportManager . Register ( grpcS )
} else {
protobuf . RegisterRaftServer ( grpcS , raftServer )
}
2019-07-28 16:55:05 +08:00
reflection . Register ( grpcS )
2020-06-02 15:10:35 +08:00
glog . V ( 0 ) . Infof ( "Start Seaweed Master %s grpc server at %s:%d" , util . Version ( ) , * masterOption . ipBind , grpcPort )
2022-03-16 13:28:18 +08:00
if grpcLocalL != nil {
go grpcS . Serve ( grpcLocalL )
}
2019-07-28 16:55:05 +08:00
go grpcS . Serve ( grpcL )
2017-01-10 17:01:12 +08:00
2022-04-04 20:51:51 +08:00
timeSleep := 1500 * time . Millisecond
if ! * m . raftHashicorp {
go func ( ) {
time . Sleep ( timeSleep )
2022-08-25 00:49:05 +08:00
ms . Topo . RaftServerAccessLock . RLock ( )
isEmptyMaster := ms . Topo . RaftServer . Leader ( ) == "" && ms . Topo . RaftServer . IsLogEmpty ( )
if isEmptyMaster && isTheFirstOne ( myMasterAddress , peers ) && ms . MasterClient . FindLeaderFromOtherPeers ( myMasterAddress ) == "" {
raftServer . DoJoinCommand ( )
2020-10-07 16:25:39 +08:00
}
2022-08-25 00:49:05 +08:00
ms . Topo . RaftServerAccessLock . RUnlock ( )
2022-04-04 20:51:51 +08:00
} ( )
}
2020-10-07 16:25:39 +08:00
2021-11-06 08:52:15 +08:00
go ms . MasterClient . KeepConnectedToMaster ( )
2019-07-28 18:58:13 +08:00
2019-01-19 06:14:47 +08:00
// start http server
2022-03-15 07:22:52 +08:00
var (
clientCertFile ,
certFile ,
keyFile string
)
useTLS := false
useMTLS := false
if viper . GetString ( "https.master.key" ) != "" {
useTLS = true
certFile = viper . GetString ( "https.master.cert" )
keyFile = viper . GetString ( "https.master.key" )
}
if viper . GetString ( "https.master.ca" ) != "" {
useMTLS = true
clientCertFile = viper . GetString ( "https.master.ca" )
}
2017-01-10 17:01:12 +08:00
httpS := & http . Server { Handler : r }
2022-09-15 02:59:55 +08:00
if masterLocalListener != nil {
go httpS . Serve ( masterLocalListener )
2022-03-16 13:28:18 +08:00
}
2022-03-15 07:22:52 +08:00
if useMTLS {
httpS . TLSConfig = security . LoadClientTLSHTTP ( clientCertFile )
}
if useTLS {
go httpS . ServeTLS ( masterListener , certFile , keyFile )
} else {
go httpS . Serve ( masterListener )
}
2017-01-10 17:01:12 +08:00
2022-10-07 00:30:30 +08:00
grace . OnInterrupt ( ms . Shutdown )
grace . OnInterrupt ( grpcS . GracefulStop )
grace . OnReload ( func ( ) {
if ms . Topo . HashicorpRaft != nil && ms . Topo . HashicorpRaft . State ( ) == hashicorpRaft . Leader {
ms . Topo . HashicorpRaft . LeadershipTransfer ( )
}
} )
2019-07-28 16:55:05 +08:00
select { }
2012-08-07 16:29:22 +08:00
}
2018-08-13 05:25:31 +08:00
2021-09-13 13:47:52 +08:00
func checkPeers ( masterIp string , masterPort int , masterGrpcPort int , peers string ) ( masterAddress pb . ServerAddress , cleanedPeers [ ] pb . ServerAddress ) {
2020-04-14 03:58:45 +08:00
glog . V ( 0 ) . Infof ( "current: %s:%d peers:%s" , masterIp , masterPort , peers )
2021-09-13 13:47:52 +08:00
masterAddress = pb . NewServerAddress ( masterIp , masterPort , masterGrpcPort )
cleanedPeers = pb . ServerAddresses ( peers ) . ToAddresses ( )
2018-08-13 05:25:31 +08:00
hasSelf := false
for _ , peer := range cleanedPeers {
2021-09-13 13:47:52 +08:00
if peer . ToHttpAddress ( ) == masterAddress . ToHttpAddress ( ) {
2018-08-13 05:25:31 +08:00
hasSelf = true
break
}
}
if ! hasSelf {
2019-07-28 18:58:13 +08:00
cleanedPeers = append ( cleanedPeers , masterAddress )
2018-08-13 05:25:31 +08:00
}
2019-07-28 18:58:13 +08:00
if len ( cleanedPeers ) % 2 == 0 {
2021-09-13 13:47:52 +08:00
glog . Fatalf ( "Only odd number of masters are supported: %+v" , cleanedPeers )
2018-08-13 05:25:31 +08:00
}
return
}
2019-06-23 18:08:27 +08:00
2021-09-13 13:47:52 +08:00
func isTheFirstOne ( self pb . ServerAddress , peers [ ] pb . ServerAddress ) bool {
2022-04-18 10:35:43 +08:00
slices . SortFunc ( peers , func ( a , b pb . ServerAddress ) bool {
return strings . Compare ( string ( a ) , string ( b ) ) < 0
2021-09-13 13:47:52 +08:00
} )
2020-10-07 16:25:39 +08:00
if len ( peers ) <= 0 {
return true
}
return self == peers [ 0 ]
}
2019-06-23 18:08:27 +08:00
func ( m * MasterOptions ) toMasterOption ( whiteList [ ] string ) * weed_server . MasterOption {
2021-09-13 13:47:52 +08:00
masterAddress := pb . NewServerAddress ( * m . ip , * m . port , * m . portGrpc )
2019-06-23 18:08:27 +08:00
return & weed_server . MasterOption {
2021-09-13 13:47:52 +08:00
Master : masterAddress ,
2020-07-14 22:34:16 +08:00
MetaFolder : * m . metaFolder ,
2021-08-13 08:54:34 +08:00
VolumeSizeLimitMB : uint32 ( * m . volumeSizeLimitMB ) ,
2020-07-14 22:34:16 +08:00
VolumePreallocate : * m . volumePreallocate ,
2020-06-05 01:52:01 +08:00
// PulseSeconds: *m.pulseSeconds,
2019-06-23 18:08:27 +08:00
DefaultReplicaPlacement : * m . defaultReplication ,
GarbageThreshold : * m . garbageThreshold ,
WhiteList : whiteList ,
DisableHttp : * m . disableHttp ,
MetricsAddress : * m . metricsAddress ,
MetricsIntervalSec : * m . metricsIntervalSec ,
}
2019-06-24 06:30:16 +08:00
}