上一篇介绍了kubends相关代码,但他并不是直接提供服务的,而是dnsmasq-nanny。nanny这个单词是保姆的意思,就是一个dns服务的保姆,挺形象的,那么我们看看这个保姆是如何服务的。
首先看服务启动:
func main() {
parseFlags()
glog.V(0).Infof("opts: %v", opts)
sync := config.NewFileSync(opts.configDir, opts.syncInterval)
dnsmasq.RunNanny(sync, opts.RunNannyOpts)
}
有两个点需要主要第一是dnsmasq启动参数:
opts = struct {
dnsmasq.RunNannyOpts
configDir string
syncInterval time.Duration
}{
RunNannyOpts: dnsmasq.RunNannyOpts{
DnsmasqExec: "/usr/sbin/dnsmasq",
RestartOnChange: false,
},
configDir: "/etc/k8s/dns/dnsmasq-nanny",
syncInterval: 10 * time.Second,
}
制定启动路径和参数目录,当然这些参数也是可以通过外面传入进行修改。
第二个问题是sync文件监听器,当configdir下的配置文件修改后会自动重新加载服务。具体看下面实现:
func (syncSource *kubeFileSyncSource) Periodic() <-chan syncResult {
// TODO: drive via inotify?
go func() {
ticker := syncSource.clock.Tick(syncSource.period)
for {
if result, err := syncSource.load(); err != nil {
glog.Errorf("Error loading config from %s: %v", syncSource.dir, err)
} else {
syncSource.channel <- result
}
<-ticker
}
}()
return syncSource.channel
}
将变化的结果(配置文件名和文件内容)放到syncSource.channel中。说清楚了这两个问题后,接着回到第一个函数启动服务,
func RunNanny(sync config.Sync, opts RunNannyOpts) {
defer glog.Flush()
currentConfig, err := sync.Once()
if err != nil {
glog.Errorf("Error getting initial config, using default: %v", err)
currentConfig = config.NewDefaultConfig()
}
nanny := &Nanny{Exec: opts.DnsmasqExec}
nanny.Configure(opts.DnsmasqArgs, currentConfig)
if err := nanny.Start(); err != nil {
glog.Fatalf("Could not start dnsmasq with initial configuration: %v", err)
}
configChan := sync.Periodic()
for {
select {
case status := <-nanny.ExitChannel:
glog.Flush()
glog.Fatalf("dnsmasq exited: %v", status)
break
case currentConfig = <-configChan:
if opts.RestartOnChange {
glog.V(0).Infof("Restarting dnsmasq with new configuration")
nanny.Kill()
nanny = &Nanny{Exec: opts.DnsmasqExec}
nanny.Configure(opts.DnsmasqArgs, currentConfig)
nanny.Start()
} else {
glog.V(2).Infof("Not restarting dnsmasq (--restartDnsmasq=false)")
}
break
}
}
}
先通过nanny.Start()启动服务,下面是for的死循环中select,当配置变化后重启服务。
下面看看nanny.Start()怎么启动服务的
func (n *Nanny) Start() error {
glog.V(0).Infof("Starting dnsmasq %v", n.args)
n.cmd = exec.Command(n.Exec, n.args...)
stderrReader, err := n.cmd.StderrPipe()
if err != nil {
return err
}
stdoutReader, err := n.cmd.StdoutPipe()
if err != nil {
return err
}
if err := n.cmd.Start(); err != nil {
return err
}
logToGlog := func(stream string, reader io.Reader) {
bufReader := bufio.NewReader(reader)
for {
bytes, err := bufReader.ReadBytes('\n')
if len(bytes) > 0 {
glog.V(1).Infof("%v", string(bytes))
}
if err == io.EOF {
glog.V(1).Infof("%v", string(bytes))
glog.Warningf("Got EOF from %v", stream)
return
} else if err != nil {
glog.V(1).Infof("%v", string(bytes))
glog.Errorf("Error reading from %v: %v", stream, err)
return
}
}
}
go logToGlog("stderr", stderrReader)
go logToGlog("stdout", stdoutReader)
n.ExitChannel = make(chan error)
go func() {
n.ExitChannel <- n.cmd.Wait()
}()
return nil
}
就是通过exec.Command(n.Exec, n.args…)启动dnsmasq服务。停止服务就简单了Process.Kill()停止进程,并把cmd(/usr/sbin/dnsmasq)置空,和上面对应:
func (n *Nanny) Kill() error {
glog.V(0).Infof("Killing dnsmasq")
if n.cmd == nil {
return fmt.Errorf("Process is not running")
}
if err := n.cmd.Process.Kill(); err != nil {
glog.Errorf("Error killing dnsmasq: %v", err)
return err
}
n.cmd = nil
return nil
}
dnsmasq-nanny服务相对比较简单,主要还是依赖dnsmasq。还剩最后一个细节没有交代清楚就是kubedns和dnsmasq-nanny什么关系,看看dnsmasq-nanny的启动参数就明白了:
- --server=/__PILLAR__DNS__DOMAIN__/127.0.0.1#10053
- --server=/in-addr.arpa/127.0.0.1#10053
- --server=/ip6.arpa/127.0.0.1#10053
其实kubedns是dnsmasq的upstream,是它的上游服务器,当然如果你的局域网内还有自己DNS域名服务器,也可以都加入进来。所以他是一个很好中继器。关于dnsmasq本身我还想讲解一下,它有两个重要的功能:dns服务和dncp服务。
关于dhcp服务
# dhcp动态分配的地址范围
dhcp-range=192.168.1.50,192.168.1.150,48h
# dhcp服务的静态绑定
dhcp-host=00:0C:29:5E:F2:6F,192.168.1.201,os02
关于dns服务
# 设置静态IP解析相当于hosts配置
address=/doubleclick.net/127.0.0.1
可以设置upstream
server=10.36.8.40
还可以设置某个域名的upstream
server=/cluster.local/172.30.0.1