【kubernetes/k8s源码分析】calico node confd源码分析

confd是一款基于etcd的状态信息,结合本地模板自动生成并更新配置的工具,适用于复杂系统的集中化配置管理,实现配置变更的自动下发和及时生效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

github: https://github.com/kelseyhightower/confd

fork: https://github.com/projectcalico/confd

WHAT confd

     confd:根据etcd上状态信息,与本地模板,生成并更新BIRD配置

     通过lsdir获取当前目录下的所有子目录。第一层子目录为域名,根据域名即可生成acl规则、规则使用、后端名称等数据。
再重新通过瓶装域名目录,对域名目录执行lsdir,读取目录下的每个主机名,创建后端的server条目(一个域名下的负载均衡后段服务器),同时获取挂在这个目录下的属性键值对

 

WHY confd

       当系统变的复杂,配置项越来越多,一方面配置管理变得繁琐,另一方面配置修改后需要重新上线同样十分痛苦。这时候,需要有一套集中化配置管理系统,一方面提供统一的配置管理,另一方面提供配置变更的自动下发,及时生效。

      统一配置管理系统,常见的:zookeeper、etcd、consul、git等

 

1. InitConfig 函数

    首先初始化 confd 配置使用默认设置,从 confd 配置文件覆盖参数,从环境变量覆盖配置参数,最后从命令行参数覆盖配置参数

// InitConfig initializes the confd configuration by first setting defaults,
// then overriding settings from the confd config file, then overriding
// settings from environment variables, and finally overriding
// settings from flags set on the command line.
// It returns an error if any.
func InitConfig(ignoreFlags bool) (*Config, error) {
	if configFile == "" {
		if _, err := os.Stat(defaultConfigFile); !os.IsNotExist(err) {
			configFile = defaultConfigFile
		}
	}

    1.1 首先使用默认设置配置参数

// Set defaults.
config := Config{
	ConfDir:  "/etc/confd",
	Interval: 600,
	Prefix:   "",
	Typha: TyphaConfig{
		// Non-zero defaults copied from <felix>/config/config_params.go.
		K8sNamespace: "kube-system",
		ReadTimeout:  30 * time.Second,
		WriteTimeout: 10 * time.Second,
	},
}

    1.2 如果指定配置文件则从配置文件覆盖参数

// Update config from the TOML configuration file.
if configFile == "" {
	log.Info("Skipping confd config file.")
} else {
	log.Info("Loading " + configFile)
	configBytes, err := ioutil.ReadFile(configFile)
	if err != nil {
		return nil, err
	}
	_, err = toml.Decode(string(configBytes), &config)
	if err != nil {
		return nil, err
	}
}

    1.3 迭代从命令行参数读取配置参数进行覆盖

// processFlags iterates through each flag set on the command line and
// overrides corresponding configuration settings.
func processFlags(config *Config) {
	log.Info("Processing command line flags")
	v := ConfigVisitor{config: config}
	flag.Visit(v.setConfigFromFlag)
}

type ConfigVisitor struct {
	config *Config
}

func (c *ConfigVisitor) setConfigFromFlag(f *flag.Flag) {
	switch f.Name {
	case "confdir":
		c.config.ConfDir = confdir
	case "interval":
		c.config.Interval = interval
	case "noop":
		c.config.Noop = noop
	case "prefix":
		c.config.Prefix = prefix
	case "sync-only":
		c.config.SyncOnly = syncOnly
	case "calicoconfig":
		c.config.CalicoConfig = calicoconfig
	case "onetime":
		c.config.Onetime = onetime
	case "keep-stage-file":
		c.config.Onetime = keepStageFile
	}
}

    1.4 从环境变量获取参数进行配置覆盖

     环境变量前缀 CONFD_ FELIX_ CALICO_,中间 TYPHA

func readTyphaConfig(typhaConfig *TyphaConfig) {
	// When Typha is in use, there will already be variables prefixed with FELIX_, so it's
	// convenient if confd honours those too.  However there may use cases for confd to
	// have independent settings, so honour CONFD_ also.  Longer-term it would be nice to
	// coalesce around CALICO_, so support that as well.
	supportedPrefixes := []string{"CONFD_", "FELIX_", "CALICO_"}
	kind := reflect.TypeOf(*typhaConfig)
	for ii := 0; ii < kind.NumField(); ii++ {
		field := kind.Field(ii)
		nameUpper := strings.ToUpper(field.Name)
		for _, prefix := range supportedPrefixes {
			varName := prefix + "TYPHA" + nameUpper
			if value := os.Getenv(varName); value != "" && value != "none" {
				log.Infof("Found %v=%v", varName, value)
				if field.Type.Name() == "Duration" {
					seconds, err := strconv.ParseFloat(value, 64)
					if err != nil {
						log.Error("Invalid float")
					}
					duration := time.Duration(seconds * float64(time.Second))
					reflect.ValueOf(typhaConfig).Elem().FieldByName(field.Name).Set(reflect.ValueOf(duration))
				} else {
					reflect.ValueOf(typhaConfig).Elem().FieldByName(field.Name).Set(reflect.ValueOf(value))
				}
				break
			}
		}
	}
}

 

2. Run 函数

func Run(config *config.Config) {
	log.Info("Starting calico-confd")
	storeClient, err := calico.NewCalicoClient(config)
	if err != nil {
		log.Fatal(err.Error())
	}

    2.1 NewCalicoClient

      路径 kelseyhightower/confd/pkg/backends/calico/client.go

     2.1.1 loadClientConfig 

      如果指定配置文件则从配置文件读取配置参数,未指定则从环境变量获取配置参数

// LoadClientConfig loads the ClientConfig from the specified file (if specified)
// or from environment variables (if the file is not specified).
func LoadClientConfig(filename string) (*CalicoAPIConfig, error) {

	// Override / merge with values loaded from the specified file.
	if filename != "" {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			return nil, err
		}

		c, err := LoadClientConfigFromBytes(b)
		if err != nil {
			return nil, fmt.Errorf("syntax error in %s: %v", filename, err)
		}
		return c, nil
	}
	return LoadClientConfigFromEnvironment()
}

    2.1.2 使用 etcdv3 或者 kubernetes 作为 backend

// Query the current BGP configuration to determine if the node to node mesh is enabled or
// not.  If it is we need to monitor all node configuration.  If it is not enabled then we
// only need to monitor our own node.  If this setting changes, we terminate confd (so that
// when restarted it will start watching the correct resources).
cc, err := clientv3.New(*config)
if err != nil {
	log.Errorf("Failed to create main Calico client: %v", err)
	return nil, err
}

    2.1.3 查询名为 default 资源为 bgpconfigurations

cfg, err := cc.BGPConfigurations().Get(
	context.Background(),
	"default",
	options.GetOptions{},
)
if _, ok := err.(lerr.ErrorResourceDoesNotExist); err != nil && !ok {
	// Failed to get the BGP configuration (and not because it doesn't exist).
	// Exit.
	log.Errorf("Failed to query current BGP settings: %v", err)
	return nil, err
}

    2.1.4 实例化模板配置 Config

templateConfig := template.Config{
	ConfDir:       config.ConfDir,
	ConfigDir:     filepath.Join(config.ConfDir, "conf.d"),
	KeepStageFile: config.KeepStageFile,
	Noop:          config.Noop,
	Prefix:        config.Prefix,
	SyncOnly:      config.SyncOnly,
	TemplateDir:   filepath.Join(config.ConfDir, "templates"),
	StoreClient:   storeClient,
}

 

3. WatchProcessor 实例化

func WatchProcessor(config Config, stopChan, doneChan chan bool, errChan chan error) Processor {
	return &watchProcessor{config, stopChan, doneChan, errChan, sync.WaitGroup{}}
}

 

4. sync 函数

    监控同步配置,如果更新过则重新 reload 命令

// sync compares the staged and dest config files and attempts to sync them
// if they differ. sync will run a config check command if set before
// overwriting the target config file. Finally, sync will run a reload command
// if set to have the application or service pick up the changes.
// It returns an error if any.
func (t *TemplateResource) sync() error {
	staged := t.StageFile.Name()
	if t.keepStageFile {
		log.Info("Keeping staged file: " + staged)
	} else {
		defer func() {
			if e := os.Remove(staged); e != nil {
				if !os.IsNotExist(e) {
					// Just log the error but don't carry it up the calling stack.
					log.WithError(e).WithField("filename", staged).Error("error removing file")
				} else {
					log.Debugf("Ignore not exists err. %s", e.Error())
				}
			}
		}()
	}

 

<think>我们正在处理一个关于Calico网络插件中BIRD配置文件的语法错误问题。用户提供的错误信息是:"bird: syntax error in config file /etc/calico/confd/config/.bird.cfg3801259349 line 9 column 11"。 根据引用[1],我们知道BIRD配置文件位于`/etc/calico/confd/config/bird.cfg`,但错误信息中的文件路径包含一个临时文件名(可能是由confd生成的临时文件)。因此,我们需要检查原始配置文件`bird.cfg`,因为confd可能会基于模板生成最终的配置文件。 步骤: 1. 定位到配置文件:虽然错误信息中的文件名是临时文件,但我们可以通过检查原始配置文件`/etc/calico/confd/config/bird.cfg`来查找问题。 2. 检查第9行第11列:错误指向第9行第11列,这意味着该位置附近可能存在语法错误。 由于我们无法直接查看用户节点的文件,我们需要指导用户如何检查和修复。 可能的原因: - 配置文件中的语法错误,比如缺少分号、括号不匹配、关键字错误等。 - 变量替换问题(如果使用了confd模板,可能模板渲染时出错)。 根据引用[2],我们知道calico-confd组件负责生成BIRD的配置文件,它使用Calico客户端从etcd或Kubernetes API获取数据,然后渲染模板。因此,问题可能出现在模板渲染过程中。 修复步骤: 1. 登录到出现问题的节点。 2. 查看原始配置文件模板(通常位于`/etc/calico/confd/templates/`目录下)以及配置文件(位于`/etc/calico/confd/config/`)。 3. 检查第9行第11列附近的语法。 但是,用户已经给出了具体的错误位置,我们可以直接查看该文件。 操作步骤: 1. 使用`kubectl exec`进入Pod(如引用[1]中所示): ```bash kubectl exec -ti calico-node-f7dc4 -n kube-system -- bash ``` 2. 然后查看错误中提到的文件(注意:错误中的文件是临时文件,但我们可以查看原始配置文件): ```bash cat /etc/calico/confd/config/bird.cfg ``` 或者直接定位到错误行: ```bash # 查看第9行附近 cat /etc/calico/confd/config/bird.cfg | awk 'NR>=5&&NR<=13' ``` 由于错误指向第9行第11列,我们重点检查该位置。 常见BIRD配置语法: - BIRD配置文件通常包含协议定义(如BGP)、路由过滤等。 - 每一条语句以分号结束。 - 使用花括号表示块。 假设第9行内容为(示例): ``` filter calico_ipip { ``` 如果第9行第11列是第一个花括号的位置,那么可能是前面缺少了关键字或括号不匹配。 但是,根据错误信息,我们需要具体分析。 另一种可能性:在引用变量时,如果变量未正确渲染,可能会留下未闭合的引号或特殊字符。 如果用户无法直接修复,可以尝试重新生成配置文件: 1. 重启calico-node容器(在Pod内)或重启整个Pod。 2. 检查calico-confd的日志,看是否有模板渲染错误: ```bash kubectl logs -n kube-system calico-node-f7dc4 -c calico-confd ``` 如果问题仍然存在,可能需要检查模板文件。模板文件通常位于`/etc/calico/confd/templates/`目录下。例如,bird.cfg的模板可能是`bird.cfg.template`。 根据引用[3],如果问题持续,可以尝试重启kubelet,但这通常不是解决配置语法错误的方法。 建议的解决方案: 1. 检查bird.cfg第9行附近的语法。 2. 如果发现明显错误(如缺少分号、括号等),手动修复。 3. 如果配置文件是由confd动态生成的,则检查模板和confd的配置。 4. 重启calico-confd容器以重新生成配置。 注意:Calico的配置文件通常是自动生成的,手动修改可能不是长久之计。因此,需要检查模板或数据源(etcd/Kubernetes)中是否有错误的数据导致生成错误配置。 如果用户没有提供具体的配置文件内容,我们可以给出一个常见的错误示例: - 例如,在BIRD配置中,协议定义必须以分号结束每个语句。 例如,以下是一个错误配置: ``` protocol kernel { learn; scan time 10; export all # 这里缺少分号 } ``` 在`export all`后面缺少分号,这会导致语法错误。 在用户的情况下,错误在第9行第11列,我们需要具体查看该行。 如果用户能够提供第9行的内容,我们可以更精确地判断。 但根据问题描述,我们无法直接看到内容,因此只能提供一般性指导。 总结步骤: 1. 使用`kubectl exec`进入calico-node Pod。 2. 查看`/etc/calico/confd/config/bird.cfg`的第9行及附近内容。 3. 检查第9行第11列(即第9行的第11个字符)附近的语法。 4. 修复后,重启BIRD进程或整个calico-node容器。 如果问题在临时文件上,说明是confd生成时出错,那么需要检查模板和confd的日志。 由于用户提到的是临时文件,我们可以忽略文件名,关注错误位置。 因此,我们给出以下具体指令: ### 解决步骤: 1. 进入calico-node Pod: ```bash kubectl exec -ti <calico-node-pod-name> -n kube-system -- bash ``` 注意:将`<calico-node-pod-name>`替换为实际的Pod名称(如引用[1]中的`calico-node-f7dc4`)。 2. 查看配置文件(临时文件路径在错误信息中给出,但我们可以查看原始配置文件): ```bash # 查看bird.cfg的第9行 cat -n /etc/calico/confd/config/bird.cfg | grep -A 5 -B 5 '^ 9' ``` 或者直接使用`vi`或`nano`编辑文件(如果容器内有编辑器): ```bash vi /etc/calico/confd/config/bird.cfg ``` 跳转到第9行(命令模式输入`:9`)。 3. 检查第9行第11列(即第9行的第11个字符)位置。注意:行号从1开始,列号从1开始。 4. 常见的错误可能包括: - 缺少分号(特别是在上一行的末尾) - 括号不匹配 - 引号不匹配 - 使用了未定义的关键字 5. 如果发现错误,修改配置文件并保存。 6. 重启BIRD进程(如果知道如何重启)或者重启整个Pod。 7. 如果配置文件是由confd动态生成的,修改可能被覆盖。因此,需要找到模板文件(在`/etc/calico/confd/templates/`目录下)进行修复,然后重启calico-confd容器或整个Pod。 8. 如果没有发现明显错误,可以尝试查看confd的日志: ```bash # 在Pod内,calico-confd容器的日志已经不在,但可以查看当前confd的运行日志(如果还在运行) # 或者退出Pod,在宿主机上查看该Pod的日志(特别是calico-confd容器) kubectl logs -n kube-system <calico-node-pod-name> -c calico-confd ``` 9. 如果问题依然存在,请提供配置文件第9行附近的内容,以便进一步分析。 注意:如果用户无法进入Pod,也可以将配置文件复制出来检查: ```bash kubectl cp -n kube-system <calico-node-pod-name>:/etc/calico/confd/config/bird.cfg ./bird.cfg ``` 然后使用本地编辑器检查。 ### 相关问题 1. 如何检查Calico网络插件的BIRD进程状态? 2. Calicoconfd组件是如何生成配置文件的? 3. 在Kubernetes中,如何调试Calico节点的网络问题? 4. 如何更新Calico的模板文件以避免配置生成错误?</think>### 解决BIRD配置文件语法错误 根据错误信息 `bird: syntax error in config file /etc/calico/confd/config/.bird.cfg3801259349 line 9 column 11`,以下是解决步骤: #### 1. **定位错误位置** - 错误发生在临时配置文件(由confd生成)的第9行第11列 - 使用以下命令查看文件内容: ```bash kubectl exec -ti <calico-node-pod> -n kube-system -- cat /etc/calico/confd/config/.bird.cfg3801259349 ``` 替换 `<calico-node-pod>` 为实际Pod名称(如 `calico-node-abcde`) #### 2. **常见语法错误原因** BIRD配置文件常见语法问题: 1. **缺少分号**:BIRD要求每行配置以分号结束 ```bird protocol kernel { # 正确 protocol kernel # 错误(缺少分号) ``` 2. **括号不匹配**:`{ }` 必须成对出现 3. **变量未定义**:使用未声明的变量如 `$VAR` 4. **特殊字符未转义**:如 `"` 或 `\` 需转义为 `\"` 和 `\\` #### 3. **诊断具体错误** 检查第9行第11列附近(示例): ```bird filter calico_pools { # 第8行 if ( net ~ 10.0.0.0/16 ) then { # 第9行(错误位置) accept; # 第10行 } # 第11行 ``` 可能问题: - 第9行 `if` 条件缺少括号 - `~` 运算符使用错误 - 花括号 `{` 位置不正确 #### 4. **修复步骤 ```bash # 1. 进入Calico Node Pod kubectl exec -ti <calico-node-pod> -n kube-system -- sh # 2. 检查模板文件(错误可能源于模板) cat /etc/calico/confd/templates/bird.cfg.template # 3. 检查confd配置 cat /etc/calico/confd/conf.d/bird.toml # 4. 手动修复后重启confd kill -HUP $(pidof calico-confd) ``` #### 5. **验证修复** ```bash # 检查BIRD状态 birdc -s /var/run/calico/bird.ctl show protocols # 查看日志确认 kubectl logs -n kube-system <calico-node-pod> -c calico-node | grep BIRD ``` > **注意**:临时文件由confd动态生成,需修复模板文件 `/etc/calico/confd/templates/bird.cfg.template` 才能永久解决[^1][^2]。 ### 相关问题 1. Calicoconfd组件如何动态生成BIRD配置文件? 2. 如何调试Calico节点的BGP协议连接问题? 3. Calico网络插件出现 `BIRD is not ready` 错误有哪些常见原因? 4. Kubernetes中如何安全地修改Calico的默认网络策略? [^1]: calico/node is not ready: BIRD is not ready。进入到node节点,查看网桥信息 `kubectl exec -ti calico-node-f7dc4 -n kube-system -- bash cat /etc/calico/confd/config/bird.cfg` [^2]: Run 函数 `func Run(config *config.Config) { log.Info("Starting calico-confd") storeClient, err := calico.NewCalicoClient(config)` [^3]: 停止kubelet `systemctl stop kubelet`,节点加入集群 `kubeadm join 192.168.106.252:6443 --token...`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值