You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ze...@apache.org on 2020/03/24 07:36:26 UTC

[servicecomb-service-center] branch master updated: Improve syncer configuration file

This is an automated email from the ASF dual-hosted git repository.

zenlin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new e851bb5  Improve syncer configuration file
e851bb5 is described below

commit e851bb5de3ddc31d44a5136eb989193ae42b1562
Author: chinx <c5...@126.com>
AuthorDate: Tue Feb 4 15:08:51 2020 +0800

    Improve syncer configuration file
---
 etc/conf/syncer.yaml                       |  56 ++++++++
 go.mod                                     |   4 +-
 syncer/cmd/daemon.go                       |  44 +++---
 syncer/config/config.go                    | 155 +++++---------------
 syncer/config/config_test.go               | 218 ++++++++++++++++++++++++++---
 syncer/config/const.go                     | 111 +++++++++++++++
 syncer/config/merge.go                     | 131 +++++++++++++++++
 syncer/config/tlsconfig.go                 | 191 -------------------------
 syncer/config/verify.go                    | 203 +++++++++++++++++++++++++++
 syncer/pkg/utils/addr.go                   |  17 ++-
 syncer/server/convert.go                   | 111 +++++++++++++++
 syncer/server/handler.go                   |  10 +-
 syncer/server/server.go                    |  57 +++++---
 syncer/servicecenter/servicecenter_test.go |   4 +-
 14 files changed, 939 insertions(+), 373 deletions(-)

diff --git a/etc/conf/syncer.yaml b/etc/conf/syncer.yaml
new file mode 100644
index 0000000..45b36c1
--- /dev/null
+++ b/etc/conf/syncer.yaml
@@ -0,0 +1,56 @@
+# run mode, supports (single, cluster)
+mode: signle
+# node name, must be unique on the network
+node: syncer-node
+# Cluster name, clustering by this name
+cluster: syncer-cluster
+dataDir: ./syncer-data/
+listener:
+  # Address used to network with other Syncers in LAN
+  bindAddr: 0.0.0.0:30190
+  # Address used to network with other Syncers in WAN
+  advertiseAddr: ""
+  # Address used to synchronize data with other Syncers
+  rpcAddr: 0.0.0.0:30191
+  # Address used to communicate with other cluster peers
+  peerAddr: 127.0.0.1:30192
+  tlsMount:
+    enabled: false
+    name: syncer
+join:
+  enabled: false
+  # Address to join the network by specifying at least one existing member
+  address: 127.0.0.1:30190
+  # Limit the maximum of RetryJoin, default is 0, means no limit
+  retryMax: 3
+  retryInterval: 30s
+task:
+  kind: ticker
+  params:
+    # Time interval between timing tasks, default is 30s
+    - key: interval
+      value: 30s
+registry:
+  plugin: servicecenter
+  address: http://127.0.0.1:30100
+  tlsMount:
+    enabled: false
+    name: servicecenter
+tlsConfigs:
+  - name: syncer
+    verifyPeer: true
+    minVersion: TLSv1.2
+    caFile: ./certs/trust.cer
+    certFile: ./certs/server.cer
+    keyFile: ./certs/server_key.pem
+    passphrase: ""
+    ciphers:
+      - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+      - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+      - TLS_RSA_WITH_AES_256_GCM_SHA384
+      - TLS_RSA_WITH_AES_128_GCM_SHA256
+  - name: servicecenter
+    verifyPeer: false
+    caFile: ./certs/trust.cer
+    certFile: ./certs/server.cer
+    keyFile: ./certs/server_key.pem
\ No newline at end of file
diff --git a/go.mod b/go.mod
index ebc0929..a40bbc3 100644
--- a/go.mod
+++ b/go.mod
@@ -73,8 +73,9 @@ require (
 	github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
 	github.com/opentracing/opentracing-go v1.0.2
 	github.com/openzipkin/zipkin-go-opentracing v0.3.3-0.20180123190626-6bb822a7f15f
+	github.com/pborman/uuid v1.2.0 // indirect
 	github.com/pierrec/lz4 v2.0.5+incompatible // indirect
-	github.com/pkg/errors v0.8.1 // indirect
+	github.com/pkg/errors v0.8.1
 	github.com/prometheus/client_golang v0.8.1-0.20170628125436-ab4214782d02
 	github.com/prometheus/client_model v0.0.0-20170216185247-6f3806018612
 	github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect
@@ -86,6 +87,7 @@ require (
 	github.com/soheilhy/cmux v0.1.4 // indirect
 	github.com/spf13/cobra v0.0.0-20170624150100-4d647c8944eb
 	github.com/spf13/pflag v1.0.0
+	github.com/stretchr/testify v1.3.0
 	github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
 	github.com/ugorji/go v1.1.1 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
diff --git a/syncer/cmd/daemon.go b/syncer/cmd/daemon.go
index 8596a41..942330c 100644
--- a/syncer/cmd/daemon.go
+++ b/syncer/cmd/daemon.go
@@ -26,7 +26,7 @@ import (
 )
 
 var (
-	conf       = config.DefaultConfig()
+	conf       = &config.Config{}
 	configFile = ""
 )
 
@@ -42,28 +42,28 @@ func init() {
 	syncerCmd.Flags().StringVar(&conf.Mode, "mode", conf.Mode,
 		"run mode")
 
-	syncerCmd.Flags().StringVar(&conf.NodeName, "node", conf.NodeName,
+	syncerCmd.Flags().StringVar(&conf.Node, "node", conf.Node,
 		"node name")
 
-	syncerCmd.Flags().StringVar(&conf.BindAddr, "bind-addr", conf.BindAddr,
-		"address to bind listeners to")
+	syncerCmd.Flags().StringVar(&conf.Cluster, "cluster", conf.Cluster,
+		"cluster name")
 
-	syncerCmd.Flags().StringVar(&conf.RPCAddr, "rpc-addr", conf.RPCAddr,
-		"port to bind RPC listener to")
+	syncerCmd.Flags().StringVar(&conf.Listener.BindAddr, "bind-addr", conf.Listener.BindAddr,
+		"address used to network with other Syncers")
 
-	syncerCmd.Flags().StringVar(&conf.JoinAddr, "join-addr", conf.JoinAddr,
-		"address to join the cluster by specifying at least one existing member")
+	syncerCmd.Flags().StringVar(&conf.Listener.RPCAddr, "rpc-addr", conf.Listener.RPCAddr,
+		"port used to synchronize data with other Syncers")
 
-	syncerCmd.Flags().StringVar(&conf.SC.Addr, "sc-addr", conf.SC.Addr,
-		"address to monitor the service-center")
+	syncerCmd.Flags().StringVar(&conf.Listener.PeerAddr, "peer-addr", conf.Listener.PeerAddr,
+		"port used to communicate with other cluster members")
 
-	syncerCmd.Flags().StringVar(&conf.ClusterName, "cluster-name", conf.ClusterName,
-		"name to group members into cluster")
+	syncerCmd.Flags().StringVar(&conf.Join.Address, "join", "",
+		"address to join the network by specifying at least one existing member")
 
-	syncerCmd.Flags().IntVar(&conf.ClusterPort, "cluster-port", conf.ClusterPort,
-		"port to communicate between cluster members")
+	syncerCmd.Flags().StringVar(&conf.Registry.Address, "registry", conf.Registry.Address,
+		"address to monitor the registry")
 
-	syncerCmd.Flags().StringVar(&conf.SC.Plugin, "sc-plugin", conf.SC.Plugin,
+	syncerCmd.Flags().StringVar(&conf.Registry.Plugin, "plugin", conf.Registry.Plugin,
 		"plugin name of servicecenter")
 
 	syncerCmd.Flags().StringVar(&configFile, "config", "",
@@ -72,16 +72,26 @@ func init() {
 
 // runSyncer Runs the Syncer service.
 func runSyncer(cmd *cobra.Command, args []string) {
+	if conf.Join.Address != "" {
+		conf.Join.Enabled = true
+	}
+
+	defaultConfig := config.DefaultConfig()
+
 	if configFile != "" {
 		fromFile, err := config.LoadConfig(configFile)
 		if err != nil {
 			log.Errorf(err, "load config file failed")
 			return
 		}
-		conf.Merge(fromFile)
+		if fromFile != nil {
+			*defaultConfig = config.Merge(*defaultConfig, *fromFile)
+		}
 	}
 
-	if err := conf.Verify(); err != nil {
+	*conf = config.Merge(*defaultConfig, *conf)
+	err := config.Verify(conf)
+	if err != nil {
 		log.Errorf(err, "verify syncer config failed")
 		return
 	}
diff --git a/syncer/config/config.go b/syncer/config/config.go
index 35a27ea..c334320 100644
--- a/syncer/config/config.go
+++ b/syncer/config/config.go
@@ -17,82 +17,47 @@
 package config
 
 import (
-	"crypto/md5"
-	"fmt"
 	"io/ioutil"
 	"os"
-	"strings"
+	"path/filepath"
+	"strconv"
 
 	"github.com/apache/servicecomb-service-center/pkg/log"
-	"github.com/apache/servicecomb-service-center/syncer/etcd"
 	"github.com/apache/servicecomb-service-center/syncer/pkg/utils"
-	"github.com/apache/servicecomb-service-center/syncer/plugins"
-	_ "github.com/apache/servicecomb-service-center/syncer/plugins/eureka"
-	"github.com/apache/servicecomb-service-center/syncer/plugins/servicecenter"
-	"github.com/apache/servicecomb-service-center/syncer/serf"
+	"github.com/pkg/errors"
 	"gopkg.in/yaml.v2"
+	"k8s.io/apimachinery/pkg/util/uuid"
 )
 
-var (
-	DefaultDCPort         = 30100
-	DefaultClusterPort    = 30192
-	DefaultTickerInterval = 30
-	DefaultConfigPath     = "./conf/config.yaml"
-
-	syncerName        = ""
-	servicecenterName = "servicecenter"
-)
-
-// Config is the configuration that can be set for Syncer. Some of these
-// configurations are exposed as command-line flags.
-type Config struct {
-	// Wraps the serf config
-	*serf.Config
-
-	// Wraps the etcd config
-	Etcd    *etcd.Config
-	LogFile string `yaml:"log_file"`
-
-	// JoinAddr The management address of one gossip pool member.
-	JoinAddr          string         `yaml:"join_addr"`
-	TickerInterval    int            `yaml:"ticker_interval"`
-	Profile           string         `yaml:"profile"`
-	EnableCompression bool           `yaml:"enable_compression"`
-	AutoSync          bool           `yaml:"auto_sync"`
-	TLSConfig         *TLSConfig     `yaml:"tls_config"`
-	SC                *ServiceCenter `yaml:"servicecenter"`
-}
-
-// ServiceCenter configuration
-type ServiceCenter struct {
-	// Addr servicecenter address, which is the service registry address.
-	// Cluster mode is supported, and multiple addresses are separated by an English ",".
-	Addr      string     `yaml:"addr"`
-	Plugin    string     `yaml:"plugin"`
-	TLSConfig *TLSConfig `yaml:"tls_config"`
-	Endpoints []string   `yaml:"-"`
-}
-
 // DefaultConfig returns the default config
 func DefaultConfig() *Config {
-	serfConf := serf.DefaultConfig()
-	etcdConf := etcd.DefaultConfig()
 	hostname, err := os.Hostname()
 	if err != nil {
 		log.Errorf(err, "Error determining hostname: %s", err)
-		return nil
+		hostname = string(uuid.NewUUID())
 	}
-	serfConf.NodeName = hostname
-	etcdConf.Name = hostname
+
 	return &Config{
-		TickerInterval: DefaultTickerInterval,
-		Config:         serfConf,
-		Etcd:           etcdConf,
-		TLSConfig:      DefaultTLSConfig(),
-		SC: &ServiceCenter{
-			Addr:      fmt.Sprintf("127.0.0.1:%d", DefaultDCPort),
-			Plugin:    servicecenter.PluginName,
-			TLSConfig: NewTLSConfig(servicecenterName),
+		Mode:    ModeSingle,
+		Node:    hostname,
+		DataDir: defaultDataDir + hostname,
+		Listener: Listener{
+			BindAddr: "0.0.0.0:" + strconv.Itoa(defaultBindPort),
+			RPCAddr:  "0.0.0.0:" + strconv.Itoa(defaultRPCPort),
+			PeerAddr: "127.0.0.1:" + strconv.Itoa(defaultPeerPort),
+		},
+		Task: Task{
+			Kind: "ticker",
+			Params: []Label{
+				{
+					Key:   defaultTaskKey,
+					Value: defaultTaskValue,
+				},
+			},
+		},
+		Registry: Registry{
+			Address: "http://127.0.0.1:30100",
+			Plugin:  defaultDCPluginName,
 		},
 	}
 }
@@ -100,10 +65,10 @@ func DefaultConfig() *Config {
 // LoadConfig loads configuration from file
 func LoadConfig(filepath string) (*Config, error) {
 	if filepath == "" {
-		filepath = DefaultConfigPath
+		return nil, nil
 	}
 	if !(utils.IsFileExist(filepath)) {
-		err := fmt.Errorf("file is not exist")
+		err := errors.New("file is not exist")
 		log.Errorf(err, "Load config from %s failed", filepath)
 		return nil, err
 	}
@@ -123,63 +88,15 @@ func LoadConfig(filepath string) (*Config, error) {
 	return conf, nil
 }
 
-// Merge other configuration into the current configuration
-func (c *Config) Merge(other *Config) {
-	if c.TLSConfig != nil && other.TLSConfig != nil {
-		c.TLSConfig.Merge(syncerName, other.TLSConfig)
-	}
-
-	if c.SC != nil && c.SC.TLSConfig != nil && other.SC != nil && other.SC.TLSConfig != nil {
-		c.SC.TLSConfig.Merge(servicecenterName, other.SC.TLSConfig)
-	}
-}
-
-// Verify Provide config verification
-func (c *Config) Verify() error {
-	ip, port, err := utils.SplitHostPort(c.BindAddr, serf.DefaultBindPort)
-	if err != nil {
-		return err
-	}
-	if ip == "127.0.0.1" {
-		c.BindAddr = fmt.Sprintf("0.0.0.0:%d", port)
-	}
-
-	ip, port, err = utils.SplitHostPort(c.RPCAddr, serf.DefaultRPCPort)
-	if err != nil {
-		return err
-	}
-	c.RPCPort = port
-	if ip == "127.0.0.1" {
-		c.RPCAddr = fmt.Sprintf("0.0.0.0:%d", c.RPCPort)
-	}
-
-	if c.JoinAddr != "" {
-		c.RetryJoin = strings.Split(c.JoinAddr, ",")
-	}
-
-	if c.ClusterName == "" {
-		c.ClusterName = fmt.Sprintf("%x", md5.Sum([]byte(c.SC.Addr)))
-	}
-
-	c.TLSEnabled = c.TLSConfig.Enabled
-
-	c.SC.Endpoints = strings.Split(c.SC.Addr, ",")
-
-	c.Etcd.SetName(c.NodeName)
-	return nil
+func (c *Config) GetTLSConfig(name string) *TLSConfig {
+	return findInTLSConfigs(c.TLSConfigs, name)
 }
 
-func (sc *ServiceCenter) SCConfigOps() []plugins.SCConfigOption {
-	opts := []plugins.SCConfigOption{plugins.WithEndpoints(strings.Split(sc.Addr, ","))}
-	if sc.TLSConfig.Enabled {
-		opts = append(opts,
-			plugins.WithTLSEnabled(sc.TLSConfig.Enabled),
-			plugins.WithTLSVerifyPeer(sc.TLSConfig.VerifyPeer),
-			plugins.WithTLSPassphrase(sc.TLSConfig.Passphrase),
-			plugins.WithTLSCAFile(sc.TLSConfig.CAFile),
-			plugins.WithTLSCertFile(sc.TLSConfig.CertFile),
-			plugins.WithTLSKeyFile(sc.TLSConfig.KeyFile),
-		)
+func pathFromSSLEnvOrDefault(server, path string) string {
+	env := os.Getenv(defaultEnvSSLRoot)
+	if len(env) == 0 {
+		wd, _ := os.Getwd()
+		return filepath.Join(wd, defaultCertsDir, server, path)
 	}
-	return opts
+	return os.ExpandEnv(filepath.Join("$"+defaultEnvSSLRoot, server, path))
 }
diff --git a/syncer/config/config_test.go b/syncer/config/config_test.go
index 4afb608..317460d 100644
--- a/syncer/config/config_test.go
+++ b/syncer/config/config_test.go
@@ -16,33 +16,215 @@
  */
 package config
 
-import "testing"
+import (
+	"github.com/apache/servicecomb-service-center/syncer/pkg/utils"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
 
-func TestDefaultVerification(t *testing.T) {
+	"github.com/stretchr/testify/assert"
+)
+
+func TestDefaultConfig(t *testing.T) {
 	conf := DefaultConfig()
-	conf.Verify()
+	assert.NotNil(t, conf)
 }
 
-func TestBindAddrVerification(t *testing.T) {
-	conf := DefaultConfig()
-	conf.BindAddr = "abcde"
-	conf.Verify()
+func TestLoadConfig(t *testing.T) {
+	configFile := ""
+	conf, err := LoadConfig(configFile)
+	assert.Nil(t, conf)
+
+	configFile = "./test.yaml"
+	conf, err = LoadConfig(configFile)
+	assert.NotNil(t, err)
+
+	defer os.Remove(configFile)
+	err = createFile(configFile, notYAMLData())
+	assert.Nil(t, err)
+	conf, err = LoadConfig(configFile)
+	assert.NotNil(t, err)
+
+	err = createFile(configFile, correctConfiguration())
+	assert.Nil(t, err)
+	conf, err = LoadConfig(configFile)
+	assert.Nil(t, err)
 }
 
-func TestRPCAddrAddrVerification(t *testing.T) {
-	conf := DefaultConfig()
-	conf.RPCAddr = "abcde"
-	conf.Verify()
+func TestGetTLSConfig(t *testing.T) {
+	configFile := "./test.yaml"
+	defer os.Remove(configFile)
+	err := createFile(configFile, correctConfiguration())
+	assert.Nil(t, err)
+	conf, err := LoadConfig(configFile)
+	assert.Nil(t, err)
+
+	tlsConf := conf.GetTLSConfig(conf.Listener.TLSMount.Name)
+	assert.NotNil(t, tlsConf)
 }
 
-func TestLocalBindAddrVerification(t *testing.T) {
-	conf := DefaultConfig()
-	conf.BindAddr = "127.0.0.1"
-	conf.Verify()
+func TestMerge(t *testing.T) {
+	configFile := "./test.yaml"
+	defer os.Remove(configFile)
+	err := createFile(configFile, correctConfiguration())
+	assert.Nil(t, err)
+	conf, err := LoadConfig(configFile)
+	assert.Nil(t, err)
+
+	nConf := Merge(*conf, *conf, *DefaultConfig())
+	assert.NotNil(t, nConf)
 }
 
-func TestLocalRPCAddrVerification(t *testing.T) {
+func TestVerify(t *testing.T) {
 	conf := DefaultConfig()
-	conf.RPCAddr = "127.0.0.1"
-	conf.Verify()
+	conf.DataDir = ""
+	err := Verify(conf)
+	assert.Nil(t, err)
+
+	configFile := "./test.yaml"
+	defer os.Remove(configFile)
+	err = createFile(configFile, correctConfiguration())
+	assert.Nil(t, err)
+	conf, err = LoadConfig(configFile)
+	assert.Nil(t, err)
+	err = Verify(conf)
+	assert.Nil(t, err)
+
+	bindAddr := conf.Listener.BindAddr
+	conf.Listener.BindAddr = ""
+	err = Verify(conf)
+	conf.Listener.BindAddr = bindAddr
+	assert.NotNil(t, err)
+
+	rpcAddr := conf.Listener.RPCAddr
+	conf.Listener.RPCAddr = ""
+	err = Verify(conf)
+	conf.Listener.RPCAddr = rpcAddr
+	assert.NotNil(t, err)
+
+	peerAddr := conf.Listener.PeerAddr
+	conf.Listener.PeerAddr = ""
+	err = Verify(conf)
+	conf.Listener.PeerAddr = peerAddr
+	assert.NotNil(t, err)
+
+	conf.Listener.TLSMount.Enabled = true
+	err = Verify(conf)
+	assert.Nil(t, err)
+
+	conf.Listener.TLSMount.Name += "_test"
+	err = Verify(conf)
+	conf.Listener.TLSMount.Enabled = false
+	assert.NotNil(t, err)
+
+	conf.Registry.TLSMount.Enabled = true
+	err = Verify(conf)
+	assert.NotNil(t, err)
+
+	conf.Registry.TLSMount.Name += "_test"
+	err = Verify(conf)
+	conf.Registry.TLSMount.Enabled = false
+	assert.NotNil(t, err)
+
+	registry := conf.Registry.Address
+	conf.Registry.Address = "127.0.0.1:xxx"
+	err = Verify(conf)
+	conf.Registry.Address = registry
+	assert.NotNil(t, err)
+
+	conf.Join.Enabled = true
+	joinAddr := conf.Join.Address
+	conf.Join.Address = "http://127.0.0.1:9999"
+	err = Verify(conf)
+	conf.Join.Address = joinAddr
+	assert.NotNil(t, err)
+
+	conf.Join.RetryMax = -1
+	conf.Join.RetryInterval = "3mams"
+	err = Verify(conf)
+	conf.Join.Enabled = false
+	assert.Nil(t, err)
+
+	params := conf.Task.Params
+	conf.Task.Kind = ""
+	conf.Task.Params = []Label{{Key: "test", Value:"test"}, {Key:defaultTaskKey, Value: "3mams"}}
+	err = Verify(conf)
+	conf.Task.Params = params
+	assert.NotNil(t, err)
+}
+
+func createFile(path string, data []byte) error {
+	fileDir := filepath.Dir(path)
+	if !utils.IsDirExist(fileDir) {
+		err := os.MkdirAll(fileDir, 0640)
+		if err != nil {
+			return err
+		}
+	}
+	return ioutil.WriteFile(path, data, 0640)
 }
+
+func notYAMLData() []byte {
+	return []byte("xxxxxxxxxxxx")
+}
+
+func correctConfiguration() []byte {
+	return []byte(`# run mode, supports (single, cluster)
+mode: signle
+# node name, must be unique on the network
+node: syncer-node
+# Cluster name, clustering by this name
+cluster: syncer-cluster
+dataDir: ./syncer-data/
+listener:
+  # Address used to network with other Syncers in LAN
+  bindAddr: 0.0.0.0:30190
+  # Address used to network with other Syncers in WAN
+  advertiseAddr: ""
+  # Address used to synchronize data with other Syncers
+  rpcAddr: 0.0.0.0:30191
+  # Address used to communicate with other cluster peers
+  peerAddr: 127.0.0.1:30192
+  tlsMount:
+    enabled: false
+    name: syncer
+join:
+  enabled: false
+  # Address to join the network by specifying at least one existing member
+  address: 127.0.0.1:30190
+  # Limit the maximum of RetryJoin, default is 0, means no limit
+  retryMax: 3
+  retryInterval: 30s
+task:
+  kind: ticker
+  params:
+    # Time interval between timing tasks, default is 30s
+    - key: interval
+      value: 30s
+registry:
+  plugin: servicecenter
+  address: http://127.0.0.1:30100
+  tlsMount:
+    enabled: false
+    name: servicecenter
+tlsConfigs:
+  - name: syncer
+    verifyPeer: true
+    minVersion: TLSv1.2
+    caFile: ./certs/trust.cer
+    certFile: ./certs/server.cer
+    keyFile: ./certs/server_key.pem
+    passphrase: ""
+    ciphers:
+      - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+      - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+      - TLS_RSA_WITH_AES_256_GCM_SHA384
+      - TLS_RSA_WITH_AES_128_GCM_SHA256
+  - name: servicecenter
+    verifyPeer: false
+    caFile: ./certs/trust.cer
+    certFile: ./certs/server.cer
+    keyFile: ./certs/server_key.pem
+`)
+}
\ No newline at end of file
diff --git a/syncer/config/const.go b/syncer/config/const.go
new file mode 100644
index 0000000..5465ea9
--- /dev/null
+++ b/syncer/config/const.go
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package config
+
+const (
+	defaultBindPort          = 30190
+	defaultRPCPort           = 30191
+	defaultPeerPort          = 30192
+	defaultTaskKind          = "ticker"
+	defaultTaskKey           = "interval"
+	defaultTaskValue         = "30s"
+	defaultDataDir           = "./syncer-data/"
+	defaultDCPluginName      = "servicecenter"
+	defaultRetryJoinMax      = 3
+	defaultRetryJoinInterval = "30s"
+
+	defaultEnvSSLRoot = "SSL_ROOT"
+	defaultCertsDir   = "certs"
+	defaultCAName     = "trust.cer"
+	defaultCertName   = "server.cer"
+	defaultKeyName    = "server_key.pem"
+
+	// ModeSingle run as a single server
+	ModeSingle = "single"
+	// ModeCluster run as a cluster peer
+	ModeCluster = "cluster"
+)
+
+// Config is the configuration that can be set for Syncer. Some of these
+// configurations are exposed as command-line flags.
+type Config struct {
+	Mode       string       `yaml:"mode"`
+	Node       string       `yaml:"node"`
+	Cluster    string       `yaml:"cluster"`
+	DataDir    string       `yaml:"dataDir"`
+	Listener   Listener     `yaml:"listener"`
+	Join       Join         `yaml:"join"`
+	Task       Task         `yaml:"task"`
+	Registry   Registry     `yaml:"registry"`
+	TLSConfigs []*TLSConfig `yaml:"tlsConfigs"`
+}
+
+// Listener Configuration for Syncer listener
+type Listener struct {
+	BindAddr      string `yaml:"bindAddr"`
+	AdvertiseAddr string `yaml:"advertiseAddr"`
+	RPCAddr       string `yaml:"rpcAddr"`
+	PeerAddr      string `yaml:"peerAddr"`
+	TLSMount      Mount  `yaml:"tlsMount"`
+}
+
+// Join Configuration for Syncer join the network
+type Join struct {
+	Enabled       bool   `yaml:"enabled"`
+	Address       string `yaml:"address"`
+	RetryMax      int    `yaml:"retryMax"`
+	RetryInterval string `yaml:"retryInterval"`
+}
+
+// Task
+type Task struct {
+	Kind   string  `yaml:"kind"`
+	Params []Label `yaml:"params"`
+}
+
+// Label pair of key and value
+type Label struct {
+	Key   string
+	Value string
+}
+
+// Registry configuration
+type Registry struct {
+	// Address is the service registry address.
+	Address  string `json:"address"`
+	Plugin   string `yaml:"plugin"`
+	TLSMount Mount  `yaml:"tlsMount"`
+}
+
+// Mount Specifying config and purpose
+type Mount struct {
+	Enabled bool   `yaml:"enabled"`
+	Name    string `yaml:"name"`
+}
+
+// TLSConfig tls configuration
+type TLSConfig struct {
+	Name       string   `yaml:"name"`
+	VerifyPeer bool     `yaml:"verifyPeer"`
+	MinVersion string   `yaml:"minVersion"`
+	Passphrase string   `yaml:"passphrase"`
+	CAFile     string   `yaml:"caFile"`
+	CertFile   string   `yaml:"certFile"`
+	KeyFile    string   `yaml:"keyFile"`
+	Ciphers    []string `yaml:"ciphers"`
+}
diff --git a/syncer/config/merge.go b/syncer/config/merge.go
new file mode 100644
index 0000000..2b3f2b2
--- /dev/null
+++ b/syncer/config/merge.go
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package config
+
+import (
+	"time"
+)
+
+// Merge multiple configurations into one
+func Merge(configs ...Config) (conf Config) {
+	for _, config := range configs {
+		conf = merge(conf, config)
+	}
+	return
+}
+
+func merge(src, dst Config) Config {
+	src.Mode = mergeString(src.Mode, dst.Mode)
+	src.Node = mergeString(src.Node, dst.Node)
+	src.Cluster = mergeString(src.Cluster, dst.Cluster)
+	src.DataDir = mergeString(src.DataDir, dst.DataDir)
+
+	src.Listener.BindAddr = mergeString(src.Listener.BindAddr, dst.Listener.BindAddr)
+	src.Listener.RPCAddr = mergeString(src.Listener.RPCAddr, dst.Listener.RPCAddr)
+	src.Listener.PeerAddr = mergeString(src.Listener.PeerAddr, dst.Listener.PeerAddr)
+	src.Listener.TLSMount.Enabled = mergeBool(src.Listener.TLSMount.Enabled, dst.Listener.TLSMount.Enabled)
+	src.Listener.TLSMount.Name = mergeString(src.Listener.TLSMount.Name, dst.Listener.TLSMount.Name)
+
+	src.Join.Enabled = mergeBool(src.Join.Enabled, dst.Join.Enabled)
+	src.Join.Address = mergeString(src.Join.Address, dst.Join.Address)
+	src.Join.RetryMax = mergeInt(src.Join.RetryMax, dst.Join.RetryMax)
+	src.Join.RetryInterval = mergeTimeString(src.Join.RetryInterval, dst.Join.RetryInterval)
+
+	src.Task.Kind = mergeString(src.Task.Kind, dst.Task.Kind)
+	src.Task.Params = mergeLabels(src.Task.Params, dst.Task.Params)
+
+	src.Registry.Address = mergeString(src.Registry.Address, dst.Registry.Address)
+	src.Registry.Plugin = mergeString(src.Registry.Plugin, dst.Registry.Plugin)
+	src.Registry.TLSMount.Enabled = mergeBool(src.Registry.TLSMount.Enabled, dst.Registry.TLSMount.Enabled)
+	src.Registry.TLSMount.Name = mergeString(src.Registry.TLSMount.Name, dst.Registry.TLSMount.Name)
+
+	src.TLSConfigs = mergeTLSConfigs(src.TLSConfigs, dst.TLSConfigs)
+	return src
+}
+
+func mergeString(src, dst string) string {
+	if dst != "" {
+		return dst
+	}
+	return src
+}
+
+func mergeInt(src, dst int) int {
+	if dst != 0 {
+		return dst
+	}
+	return src
+}
+
+func mergeBool(src, dst bool) bool {
+	return dst
+}
+
+func mergeTimeString(src, dst string) string {
+	_, err := time.ParseDuration(dst)
+	if err != nil {
+		return src
+	}
+	return dst
+}
+
+func mergeLabels(src, dst []Label) []Label {
+	if len(src) == 0 {
+		return dst[:]
+	}
+
+	merges := src[:]
+	for _, dv := range dst {
+		if findInLabels(src, dv.Key) == nil {
+			merges = append(merges, dv)
+		}
+	}
+	return merges
+}
+
+func findInLabels(labels []Label, key string) *Label {
+	for _, item := range labels {
+		if item.Key == key {
+			return &item
+		}
+	}
+	return nil
+}
+
+func mergeTLSConfigs(src, dst []*TLSConfig) []*TLSConfig {
+	if len(src) == 0 {
+		return dst[:]
+	}
+
+	merges := src[:]
+	for _, dv := range dst {
+		if findInTLSConfigs(src, dv.Name) == nil {
+			merges = append(merges, dv)
+		}
+	}
+	return merges
+}
+
+func findInTLSConfigs(list []*TLSConfig, name string) *TLSConfig {
+	for _, item := range list {
+		if item.Name == name {
+			return item
+		}
+	}
+	return nil
+}
diff --git a/syncer/config/tlsconfig.go b/syncer/config/tlsconfig.go
deleted file mode 100644
index 9f0ce4d..0000000
--- a/syncer/config/tlsconfig.go
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package config
-
-import (
-	"crypto/tls"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-	"sync"
-
-	"github.com/apache/servicecomb-service-center/pkg/log"
-	"github.com/apache/servicecomb-service-center/pkg/tlsutil"
-	"github.com/apache/servicecomb-service-center/syncer/pkg/utils"
-)
-
-const (
-	dirName    = "certs"
-	caCert     = "trust.cer"
-	serverCert = "server.cer"
-	serverKey  = "server_key.pem"
-)
-
-// TLSConfig tls configuration
-type TLSConfig struct {
-	Enabled         bool        `yaml:"enabled"`
-	VerifyPeer      bool        `yaml:"verify_peer"`
-	MinVersion      string      `yaml:"min_version"`
-	Passphrase      string      `yaml:"passphrase"`
-	CAFile          string      `yaml:"ca_file"`
-	CertFile        string      `yaml:"cert_file"`
-	KeyFile         string      `yaml:"key_file"`
-	Ciphers         []string    `yaml:"ciphers"`
-	clientTlsConfig *tls.Config `yaml:"-"`
-	serverTlsConfig *tls.Config `yaml:"-"`
-	mux             sync.Mutex  `yaml:"-"`
-}
-
-// DefaultTLSConfig returns default tls configuration
-func DefaultTLSConfig() *TLSConfig {
-	return &TLSConfig{
-		Enabled: false,
-	}
-}
-
-// NewTLSConfig returns tls configuration by server
-func NewTLSConfig(server string) *TLSConfig {
-	return &TLSConfig{
-		Enabled:    true,
-		VerifyPeer: true,
-		CAFile:     pathFromSSLEnvOrDefault(server, caCert),
-		CertFile:   pathFromSSLEnvOrDefault(server, serverCert),
-		KeyFile:    pathFromSSLEnvOrDefault(server, serverKey),
-	}
-}
-
-// Merge other tls configuration into the current configuration
-func (t *TLSConfig) Merge(server string, other *TLSConfig) {
-	if !other.Enabled {
-		return
-	}
-	t.Enabled = other.Enabled
-	t.VerifyPeer = other.VerifyPeer
-	t.MinVersion = other.MinVersion
-
-	if other.CAFile == "" && t.CAFile == "" {
-		other.CAFile = pathFromSSLEnvOrDefault(server, caCert)
-	}
-	t.CAFile = other.CAFile
-
-	if other.CertFile == "" && t.CertFile == "" {
-		other.CertFile = pathFromSSLEnvOrDefault(server, serverCert)
-	}
-	t.CertFile = other.CertFile
-
-	if other.KeyFile == "" && t.KeyFile == "" {
-		other.KeyFile = pathFromSSLEnvOrDefault(server, serverKey)
-	}
-	t.KeyFile = other.KeyFile
-}
-
-// Verify the tls configuration
-func (t *TLSConfig) Verify() (err error) {
-	if !t.Enabled {
-		return
-	}
-
-	if t.CAFile == "" || !utils.IsFileExist(t.CAFile) {
-		err = fmt.Errorf("tls ca file '%s' is not found", t.CAFile)
-	}
-
-	if err == nil && t.CertFile == "" || !utils.IsFileExist(t.CertFile) {
-		err = fmt.Errorf("tls cert file '%s' is not found", t.CertFile)
-	}
-
-	if err == nil && t.KeyFile == "" || !utils.IsFileExist(t.KeyFile) {
-		err = fmt.Errorf("tls key file '%s' is not found", t.KeyFile)
-	}
-
-	if err == nil {
-		for _, cipher := range t.Ciphers {
-			if _, ok := tlsutil.TLS_CIPHER_SUITE_MAP[cipher]; !ok {
-				err = fmt.Errorf("cipher %s not exist", cipher)
-				break
-			}
-		}
-	}
-
-	if err != nil {
-		log.Error("verify tls configuration failed", err)
-	}
-	return
-}
-
-// ClientTlsConfig get the tls.config of client
-func (t *TLSConfig) ClientTlsConfig() (*tls.Config, error) {
-	if !t.Enabled {
-		return nil, nil
-	}
-
-	t.mux.Lock()
-	defer t.mux.Unlock()
-	if t.clientTlsConfig != nil {
-		return t.clientTlsConfig, nil
-	}
-
-	opts := append(tlsutil.DefaultClientTLSOptions(), t.toOptions()...)
-	conf, err := tlsutil.GetClientTLSConfig(opts...)
-	if err != nil {
-		log.Error("get client tls config failed", err)
-	}
-	t.clientTlsConfig = conf
-	return conf, err
-}
-
-// ServerTlsConfig get the tls.config of server
-func (t *TLSConfig) ServerTlsConfig() (*tls.Config, error) {
-	if !t.Enabled {
-		return nil, nil
-	}
-
-	if t.serverTlsConfig != nil {
-		return t.serverTlsConfig, nil
-	}
-
-	opts := append(tlsutil.DefaultServerTLSOptions(), t.toOptions()...)
-	conf, err := tlsutil.GetServerTLSConfig(opts...)
-	if err != nil {
-		log.Error("get server tls config failed", err)
-	}
-	t.serverTlsConfig = conf
-	return conf, err
-}
-
-func (t *TLSConfig) toOptions() []tlsutil.SSLConfigOption {
-	return []tlsutil.SSLConfigOption{
-		tlsutil.WithVerifyPeer(t.VerifyPeer),
-		tlsutil.WithVersion(tlsutil.ParseSSLProtocol(t.MinVersion), tls.VersionTLS12),
-		tlsutil.WithCipherSuits(
-			tlsutil.ParseDefaultSSLCipherSuites(strings.Join(t.Ciphers, ","))),
-		tlsutil.WithKeyPass(t.Passphrase),
-		tlsutil.WithCA(t.CAFile),
-		tlsutil.WithCert(t.CertFile),
-		tlsutil.WithKey(t.KeyFile),
-	}
-}
-
-func pathFromSSLEnvOrDefault(server, path string) string {
-	env := os.Getenv("SSL_ROOT")
-	if len(env) == 0 {
-		wd, _ := os.Getwd()
-		return filepath.Join(wd, dirName, server, path)
-	}
-	return os.ExpandEnv(filepath.Join("$SSL_ROOT", server, path))
-}
diff --git a/syncer/config/verify.go b/syncer/config/verify.go
new file mode 100644
index 0000000..13e37d6
--- /dev/null
+++ b/syncer/config/verify.go
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package config
+
+import (
+	"crypto/md5"
+	"fmt"
+	"net"
+	"net/url"
+	"path/filepath"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/apache/servicecomb-service-center/pkg/log"
+	"github.com/apache/servicecomb-service-center/pkg/tlsutil"
+	"github.com/apache/servicecomb-service-center/syncer/pkg/utils"
+	"github.com/pkg/errors"
+)
+
+// Verify Provide config verification
+func Verify(c *Config) (err error) {
+	if err = verifyListener(&c.Listener); err != nil {
+		return
+	}
+
+	if err = verifyJoin(&c.Join); err != nil {
+		return
+	}
+
+	if err = verifyTask(&c.Task); err != nil {
+		return
+	}
+
+	if err = verifyRegistry(&c.Registry); err != nil {
+		return
+	}
+
+	if c.Listener.TLSMount.Enabled {
+		listenerTls := c.GetTLSConfig(c.Listener.TLSMount.Name)
+		if listenerTls == nil {
+			err = errors.Errorf("listener tls config notfound, name = %s", c.Listener.TLSMount.Name)
+			return
+		}
+		if err = verifyTLSConfig(listenerTls); err == nil {
+			return
+		}
+	}
+
+	if c.Registry.TLSMount.Enabled {
+		registryTls := c.GetTLSConfig(c.Listener.TLSMount.Name)
+		if registryTls == nil {
+			err = errors.Errorf("registry tls config notfound, name = %s", c.Registry.TLSMount.Name)
+			return
+		}
+		if err = verifyTLSConfig(registryTls); err == nil {
+			return
+		}
+	}
+
+	if c.Cluster == "" {
+		endpoints := strings.Split(c.Registry.Address, ",")
+		sort.Strings(endpoints)
+		str := strings.Join(endpoints, ",")
+		c.Cluster = fmt.Sprintf("%x", md5.Sum([]byte(str)))
+	}
+
+	if c.DataDir == "" {
+		c.DataDir = defaultDataDir + c.Node
+	}
+	c.DataDir = filepath.Dir(c.DataDir)
+	return nil
+}
+
+func verifyListener(listener *Listener) (err error) {
+	bindHost, bindPort, bErr := utils.SplitHostPort(listener.BindAddr, defaultBindPort)
+	if bErr != nil {
+		err = errors.Wrapf(bErr, "verify bind address failed, url is %s", listener.BindAddr)
+		return
+	}
+	listener.BindAddr = bindHost + ":" + strconv.Itoa(bindPort)
+
+	rpcHost, rpcPort, rErr := utils.SplitHostPort(listener.RPCAddr, defaultRPCPort)
+	if rErr != nil {
+		err = errors.Wrapf(rErr, "verify rpc address failed, url is %s", listener.RPCAddr)
+		return
+	}
+	listener.RPCAddr = rpcHost + ":" + strconv.Itoa(rpcPort)
+
+	peerHost, peerPort, pErr := utils.SplitHostPort(listener.PeerAddr, defaultPeerPort)
+	if pErr != nil {
+		err = errors.Wrapf(pErr, "verify peer address failed, url is %s", listener.PeerAddr)
+		return
+	}
+	listener.PeerAddr = peerHost + ":" + strconv.Itoa(peerPort)
+	return
+}
+
+func verifyJoin(join *Join) (err error) {
+	if !join.Enabled {
+		return
+	}
+	endpoints := strings.Split(join.Address, ",")
+	for _, addr := range endpoints {
+		_, _, err1 := net.SplitHostPort(addr)
+		if err1 != nil {
+			err = errors.Wrapf(err1, "Verify joinAddr failed, urls has %s", addr)
+			return
+		}
+	}
+
+	if join.RetryMax < 0 {
+		join.RetryMax = defaultRetryJoinMax
+	}
+
+	if _, err1 := time.ParseDuration(join.RetryInterval); err1 != nil {
+		log.Warnf("join retry interval '%s' is wrong", join.RetryInterval)
+		join.RetryInterval = defaultRetryJoinInterval
+	}
+	return
+}
+
+func verifyTask(task *Task) (err error) {
+	if task.Kind == "" {
+		task.Kind = defaultTaskKind
+	}
+
+	if task.Kind == defaultTaskKind {
+		for _, label := range task.Params {
+			if label.Key != defaultTaskKey {
+				continue
+			}
+			_, err1 := time.ParseDuration(label.Value)
+			if err1 != nil {
+				err = errors.Wrapf(err1, "Verify task params failed, key = %s, value = %s", label.Key, label.Value)
+				return
+			}
+		}
+	}
+	return
+}
+
+func verifyRegistry(r *Registry) (err error) {
+	endpoints := strings.Split(r.Address, ",")
+	for _, addr := range endpoints {
+		_, err = url.Parse(addr)
+		if err != nil {
+			log.Errorf(err, "Verify registry endpoints failed, urls has %s", addr)
+			return err
+		}
+	}
+	return nil
+}
+
+func verifyTLSConfig(conf *TLSConfig) (err error) {
+	if conf.CAFile == "" || !utils.IsFileExist(conf.CAFile) {
+		conf.CAFile = pathFromSSLEnvOrDefault(conf.Name, defaultCAName)
+		if !utils.IsFileExist(conf.CAFile) {
+			err = errors.Errorf("tls ca file '%s' is not found", conf.CAFile)
+			return
+		}
+	}
+
+	if conf.CertFile == "" || !utils.IsFileExist(conf.CertFile) {
+		conf.CertFile = pathFromSSLEnvOrDefault(conf.Name, defaultCertName)
+		if !utils.IsFileExist(conf.CertFile) {
+			err = errors.Errorf("tls cert file '%s' is not found", conf.CertFile)
+			return
+		}
+	}
+
+	if conf.KeyFile == "" || !utils.IsFileExist(conf.KeyFile) {
+		conf.KeyFile = pathFromSSLEnvOrDefault(conf.Name, defaultKeyName)
+		if !utils.IsFileExist(conf.KeyFile) {
+			err = errors.Errorf("tls key file '%s' is not found", conf.KeyFile)
+			return
+		}
+	}
+
+	for _, cipher := range conf.Ciphers {
+		if _, ok := tlsutil.TLS_CIPHER_SUITE_MAP[cipher]; !ok {
+			err = errors.Errorf("cipher %s not exist", cipher)
+			return
+		}
+	}
+	return
+}
diff --git a/syncer/pkg/utils/addr.go b/syncer/pkg/utils/addr.go
index f84c4fe..0fbde0b 100644
--- a/syncer/pkg/utils/addr.go
+++ b/syncer/pkg/utils/addr.go
@@ -17,21 +17,32 @@
 package utils
 
 import (
-	"fmt"
 	"net"
+	"strconv"
+	"strings"
 )
 
 // SplitHostPort returns the parts of the address and port. If the port does not exist, use defaultPort.
 func SplitHostPort(address string, defaultPort int) (string, int, error) {
 	_, _, err := net.SplitHostPort(address)
 	if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
-		address = fmt.Sprintf("%s:%d", address, defaultPort)
-		_, _, err = net.SplitHostPort(address)
+		index := strings.LastIndexByte(address, ':')
+		return SplitAddress(address[:index+1] + strconv.Itoa(defaultPort))
 	}
+	return ResolveAddr(address)
+}
+
+// SplitAddress returns the parts of the address and port.
+func SplitAddress(address string) (string, int, error) {
+	_, _, err := net.SplitHostPort(address)
 	if err != nil {
 		return "", 0, err
 	}
+	return ResolveAddr(address)
+}
 
+// ResolveAddr Resolve the address with tcp.
+func ResolveAddr(address string) (string, int, error) {
 	addr, err := net.ResolveTCPAddr("tcp", address)
 	if err != nil {
 		return "", 0, err
diff --git a/syncer/server/convert.go b/syncer/server/convert.go
new file mode 100644
index 0000000..1f56762
--- /dev/null
+++ b/syncer/server/convert.go
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package server
+
+import (
+	"crypto/tls"
+	"net/url"
+	"strings"
+	"time"
+
+	"github.com/apache/servicecomb-service-center/pkg/tlsutil"
+	"github.com/apache/servicecomb-service-center/syncer/config"
+	"github.com/apache/servicecomb-service-center/syncer/etcd"
+	"github.com/apache/servicecomb-service-center/syncer/pkg/utils"
+	"github.com/apache/servicecomb-service-center/syncer/plugins"
+	"github.com/apache/servicecomb-service-center/syncer/serf"
+)
+
+func convertSerfConfig(c *config.Config) *serf.Config {
+	conf := serf.DefaultConfig()
+	conf.NodeName = c.Node
+	conf.ClusterName = c.Cluster
+	conf.Mode = c.Mode
+	conf.TLSEnabled = c.Listener.TLSMount.Enabled
+	conf.BindAddr = c.Listener.BindAddr
+	_, conf.ClusterPort, _ = utils.ResolveAddr(c.Listener.PeerAddr)
+	_, conf.RPCPort, _ = utils.ResolveAddr(c.Listener.RPCAddr)
+	if c.Join.Enabled {
+		conf.RetryJoin = strings.Split(c.Join.Address, ",")
+		conf.RetryInterval, _ = time.ParseDuration(c.Join.RetryInterval)
+		conf.RetryMaxAttempts = c.Join.RetryMax
+	}
+	return conf
+}
+
+func convertEtcdConfig(c *config.Config) *etcd.Config {
+	conf := etcd.DefaultConfig()
+	conf.Name = c.Node
+	conf.Dir = c.DataDir
+	proto := "http://"
+
+	if c.Listener.TLSMount.Enabled {
+		proto = "https://"
+	}
+
+	peer, _ := url.Parse(proto + c.Listener.PeerAddr)
+	conf.APUrls = []url.URL{*peer}
+	conf.LPUrls = []url.URL{*peer}
+	return conf
+}
+
+func convertTickerInterval(c *config.Config) int {
+	strNum := ""
+	for _, label := range c.Task.Params {
+		if label.Key == "interval" {
+			strNum = label.Value
+			break
+		}
+	}
+	interval, _ := time.ParseDuration(strNum)
+	return int(interval.Seconds())
+}
+
+func convertSCConfigOption(c *config.Config) []plugins.SCConfigOption {
+	endpoints := make([]string, 0, 10)
+	for _, endpoint := range strings.Split(c.Registry.Address, ",") {
+		endpoints = append(endpoints, endpoint)
+	}
+	opts := []plugins.SCConfigOption{plugins.WithEndpoints(endpoints)}
+
+	if c.Registry.TLSMount.Enabled {
+		tlsConf := c.GetTLSConfig(c.Registry.TLSMount.Name)
+		opts = append(
+			opts, plugins.WithTLSEnabled(c.Registry.TLSMount.Enabled),
+			plugins.WithTLSVerifyPeer(tlsConf.VerifyPeer),
+			plugins.WithTLSPassphrase(tlsConf.Passphrase),
+			plugins.WithTLSCAFile(tlsConf.CAFile),
+			plugins.WithTLSCertFile(tlsConf.CertFile),
+			plugins.WithTLSKeyFile(tlsConf.KeyFile),
+		)
+	}
+	return opts
+}
+
+func tlsConfigToOptions(t *config.TLSConfig) []tlsutil.SSLConfigOption {
+	return []tlsutil.SSLConfigOption{
+		tlsutil.WithVerifyPeer(t.VerifyPeer),
+		tlsutil.WithVersion(tlsutil.ParseSSLProtocol(t.MinVersion), tls.VersionTLS12),
+		tlsutil.WithCipherSuits(
+			tlsutil.ParseDefaultSSLCipherSuites(strings.Join(t.Ciphers, ","))),
+		tlsutil.WithKeyPass(t.Passphrase),
+		tlsutil.WithCA(t.CAFile),
+		tlsutil.WithCert(t.CertFile),
+		tlsutil.WithKey(t.KeyFile),
+	}
+}
diff --git a/syncer/server/handler.go b/syncer/server/handler.go
index d18a20b..e7c349e 100644
--- a/syncer/server/handler.go
+++ b/syncer/server/handler.go
@@ -23,6 +23,7 @@ import (
 	"strconv"
 
 	"github.com/apache/servicecomb-service-center/pkg/log"
+	"github.com/apache/servicecomb-service-center/pkg/tlsutil"
 	"github.com/apache/servicecomb-service-center/pkg/util"
 	"github.com/apache/servicecomb-service-center/syncer/grpc"
 	pb "github.com/apache/servicecomb-service-center/syncer/proto"
@@ -45,7 +46,7 @@ func (s *Server) tickHandler(ctx context.Context) {
 	s.servicecenter.FlushData()
 
 	// sends a UserEvent on Serf, the event will be broadcast between members
-	err := s.agent.UserEvent(EventDiscovered, util.StringToBytesWithNoCopy(s.conf.ClusterName), true)
+	err := s.agent.UserEvent(EventDiscovered, util.StringToBytesWithNoCopy(s.conf.Cluster), true)
 	if err != nil {
 		log.Errorf(err, "Syncer send user event failed")
 	}
@@ -63,7 +64,6 @@ func (s *Server) HandleEvent(event serf.Event) {
 	if !s.etcd.IsLeader() {
 		return
 	}
-
 	switch event.EventType() {
 	case serf.EventUser:
 		s.userEvent(event.(serf.UserEvent))
@@ -80,7 +80,7 @@ func (s *Server) userEvent(event serf.UserEvent) {
 	clusterName := util.BytesToStringWithNoCopy(event.Payload)
 
 	// Excludes notifications from self, as the gossip protocol inevitably has redundant notifications
-	if s.conf.ClusterName == clusterName {
+	if s.conf.Cluster == clusterName {
 		return
 	}
 
@@ -102,7 +102,9 @@ func (s *Server) userEvent(event serf.UserEvent) {
 	}
 	var tlsConfig *tls.Config
 	if enabled {
-		tlsConfig, err = s.conf.TLSConfig.ClientTlsConfig()
+		conf := s.conf.GetTLSConfig(s.conf.Listener.TLSMount.Name)
+		sslOps := append(tlsutil.DefaultClientTLSOptions(), tlsConfigToOptions(conf)...)
+		tlsConfig, err = tlsutil.GetClientTLSConfig(sslOps...)
 		if err != nil {
 			log.Error("get grpc client tls config failed", err)
 			return
diff --git a/syncer/server/server.go b/syncer/server/server.go
index 8df56f6..e5eafdf 100644
--- a/syncer/server/server.go
+++ b/syncer/server/server.go
@@ -18,6 +18,7 @@ package server
 
 import (
 	"context"
+	"crypto/tls"
 	"errors"
 	"net/url"
 	"strconv"
@@ -25,14 +26,20 @@ import (
 
 	"github.com/apache/servicecomb-service-center/pkg/gopool"
 	"github.com/apache/servicecomb-service-center/pkg/log"
+	"github.com/apache/servicecomb-service-center/pkg/tlsutil"
 	"github.com/apache/servicecomb-service-center/syncer/config"
 	"github.com/apache/servicecomb-service-center/syncer/etcd"
 	"github.com/apache/servicecomb-service-center/syncer/grpc"
 	"github.com/apache/servicecomb-service-center/syncer/pkg/syssig"
 	"github.com/apache/servicecomb-service-center/syncer/pkg/ticker"
+	"github.com/apache/servicecomb-service-center/syncer/pkg/utils"
 	"github.com/apache/servicecomb-service-center/syncer/plugins"
 	"github.com/apache/servicecomb-service-center/syncer/serf"
 	"github.com/apache/servicecomb-service-center/syncer/servicecenter"
+
+	// import plugins
+	_ "github.com/apache/servicecomb-service-center/syncer/plugins/eureka"
+	_ "github.com/apache/servicecomb-service-center/syncer/plugins/servicecenter"
 )
 
 var stopChanErr = errors.New("stopped syncer by stopCh")
@@ -60,7 +67,8 @@ type Server struct {
 	// Wrap the servicecenter
 	servicecenter servicecenter.Servicecenter
 
-	etcd *etcd.Agent
+	etcd     *etcd.Agent
+	etcdConf *etcd.Config
 
 	// Wraps the serf agent
 	agent *serf.Agent
@@ -118,8 +126,10 @@ func (s *Server) Run(ctx context.Context) {
 	s.servicecenter.SetStorageEngine(s.etcd.Storage())
 
 	s.agent.RegisterEventHandler(s)
-
 	gopool.Go(s.tick.Start)
+
+	log.Info("start service done")
+
 	<-s.stopCh
 
 	s.Stop()
@@ -173,52 +183,63 @@ func (s *Server) initialization() (err error) {
 		return
 	}
 
-	s.agent, err = serf.Create(s.conf.Config)
+	s.agent, err = serf.Create(convertSerfConfig(s.conf))
 	if err != nil {
 		log.Errorf(err, "Create serf failed, %s", err)
 		return
 	}
 
-	s.etcd = etcd.NewAgent(s.conf.Etcd)
+	s.etcdConf = convertEtcdConfig(s.conf)
+	s.etcd = etcd.NewAgent(s.etcdConf)
 
-	s.tick = ticker.NewTaskTicker(s.conf.TickerInterval, s.tickHandler)
+	s.tick = ticker.NewTaskTicker(convertTickerInterval(s.conf), s.tickHandler)
 
-	s.servicecenter, err = servicecenter.NewServicecenter(s.conf.SC.SCConfigOps()...)
+	s.servicecenter, err = servicecenter.NewServicecenter(convertSCConfigOption(s.conf)...)
 	if err != nil {
 		log.Error("create servicecenter failed", err)
 		return
 	}
 
-	tlsConfig, err := s.conf.TLSConfig.ServerTlsConfig()
-	if err != nil {
-		log.Error("get grpc server tls config failed", err)
-		return
+	var tlsConfig *tls.Config
+	if s.conf.Listener.TLSMount.Enabled {
+		conf := s.conf.GetTLSConfig(s.conf.Listener.TLSMount.Name)
+		sslOps := append(tlsutil.DefaultServerTLSOptions(), tlsConfigToOptions(conf)...)
+		tlsConfig, err = tlsutil.GetServerTLSConfig(sslOps...)
+		if err != nil {
+			log.Error("get grpc server tls config failed", err)
+			return
+		}
 	}
-	s.grpc = grpc.NewServer(s.conf.RPCAddr, s, tlsConfig)
+
+	s.grpc = grpc.NewServer(s.conf.Listener.RPCAddr, s, tlsConfig)
 	return nil
 }
 
 // initPlugin Initialize the plugin and load the external plugin according to the configuration
 func (s *Server) initPlugin() {
-	plugins.SetPluginConfig(plugins.PluginServicecenter.String(), s.conf.SC.Plugin)
+	plugins.SetPluginConfig(plugins.PluginServicecenter.String(), s.conf.Registry.Plugin)
 	plugins.LoadPlugins()
 }
 
 // configureCluster Configuring the cluster by serf group member information
 func (s *Server) configureCluster() error {
-	proto := "http" // todoļ¼šIntroduce tls config to manage protocol
+	proto := "http"
+	if s.conf.Listener.TLSMount.Enabled {
+		proto = "https"
+	}
 	initialCluster := ""
 
 	// get local member of serf
 	self := s.agent.LocalMember()
-	peerUrl, err := url.Parse(proto + "://" + self.Addr.String() + ":" + strconv.Itoa(s.conf.ClusterPort))
+	_, peerPort, _ := utils.SplitAddress(s.conf.Listener.PeerAddr)
+	peerUrl, err := url.Parse(proto + "://" + self.Addr.String() + ":" + strconv.Itoa(peerPort))
 	if err != nil {
 		log.Error("parse url from serf local member failed", err)
 		return err
 	}
 
 	// group members from serf as initial cluster members
-	for _, member := range s.agent.GroupMembers(s.conf.ClusterName) {
+	for _, member := range s.agent.GroupMembers(s.conf.Cluster) {
 		initialCluster += member.Name + "=" + proto + "://" + member.Addr.String() + ":" + member.Tags[serf.TagKeyClusterPort] + ","
 	}
 
@@ -228,8 +249,8 @@ func (s *Server) configureCluster() error {
 		log.Error("etcd peer not found", err)
 		return err
 	}
-	s.conf.Etcd.APUrls = []url.URL{*peerUrl}
-	s.conf.Etcd.LPUrls = []url.URL{*peerUrl}
-	s.conf.Etcd.InitialCluster = initialCluster[:len(initialCluster)-1]
+	s.etcdConf.APUrls = []url.URL{*peerUrl}
+	s.etcdConf.LPUrls = []url.URL{*peerUrl}
+	s.etcdConf.InitialCluster = initialCluster[:len(initialCluster)-1]
 	return nil
 }
diff --git a/syncer/servicecenter/servicecenter_test.go b/syncer/servicecenter/servicecenter_test.go
index 5b8c4a5..641b93c 100644
--- a/syncer/servicecenter/servicecenter_test.go
+++ b/syncer/servicecenter/servicecenter_test.go
@@ -52,7 +52,7 @@ func TestNewServicecenter(t *testing.T) {
 
 func TestOnEvent(t *testing.T) {
 	conf := config.DefaultConfig()
-	conf.SC.Plugin = mockplugin.PluginName
+	conf.Registry.Plugin = mockplugin.PluginName
 	initPlugin(conf)
 	dc, err := servicecenter.NewServicecenter(
 		plugins.WithEndpoints([]string{"127.0.0.1:30100"}))
@@ -120,5 +120,5 @@ func TestOnEvent(t *testing.T) {
 }
 
 func initPlugin(conf *config.Config) {
-	plugins.SetPluginConfig(plugins.PluginServicecenter.String(), conf.SC.Plugin)
+	plugins.SetPluginConfig(plugins.PluginServicecenter.String(), conf.Registry.Plugin)
 }