func(daemon *Daemon)ContainerStart(name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string)error { ... // check if hostConfig is in line with the current system settings. // It may happen cgroups are umounted or the like. if _, err = daemon.verifyContainerSettings(container.OS, container.HostConfig, nil, false); err != nil { return errdefs.InvalidParameter(err) } // Adapt for old containers in case we have updates in this function and // old containers never have chance to call the new function in create stage. if hostConfig != nil { if err := daemon.adaptContainerSettings(container.HostConfig, false); err != nil { return errdefs.InvalidParameter(err) } } return daemon.containerStart(container, checkpoint, checkpointDir, true) }
// containerStart prepares the container to run by setting up everything the // container needs, such as storage and networking, as well as links // between containers. The container is left waiting for a signal to // begin running. func(daemon *Daemon)containerStart(container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool)(err error) {
這個函式非常的重要,可以看一下該函式的註解 // containerStart prepares the container to run by setting up everything the // container needs, such as storage and networking, as well as links // between containers. The container is left waiting for a signal to // begin running.
再這個函式內會創建好相關的容器,並且會將該容器用到的相關資源(儲存/網路)等都準備好 ,由於我們要觀察的是 DNS 相關的資訊,所以我們要繼續往 initializeNetworking 的方向往下追。
// always connect default network first since only default // network mode support link and we need do some setting // on sandbox initialize for link, but the sandbox only be initialized // on first network connecting. defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() if nConf, ok := container.NetworkSettings.Networks[defaultNetName]; ok { cleanOperationalData(nConf) if err := daemon.connectToNetwork(container, defaultNetName, nConf.EndpointSettings, updateSettings); err != nil { return err }
func(sb *sandbox)setupDNS()error { var newRC *resolvconf.File ....
originResolvConfPath := sb.config.originResolvConfPath if originResolvConfPath == "" { // if not specified fallback to default /etc/resolv.conf originResolvConfPath = resolvconf.DefaultResolvConf } currRC, err := resolvconf.GetSpecific(originResolvConfPath) if err != nil { if !os.IsNotExist(err) { return err } // it's ok to continue if /etc/resolv.conf doesn't exist, default resolvers (Google's Public DNS) // will be used currRC = &resolvconf.File{} logrus.Infof("/etc/resolv.conf does not exist") }
iflen(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 { var ( err error dnsList = resolvconf.GetNameservers(currRC.Content, types.IP) dnsSearchList = resolvconf.GetSearchDomains(currRC.Content) dnsOptionsList = resolvconf.GetOptions(currRC.Content) ) iflen(sb.config.dnsList) > 0 { dnsList = sb.config.dnsList } iflen(sb.config.dnsSearchList) > 0 { dnsSearchList = sb.config.dnsSearchList } iflen(sb.config.dnsOptionsList) > 0 { dnsOptionsList = sb.config.dnsOptionsList } newRC, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList) if err != nil { return err } // After building the resolv.conf from the user config save the // external resolvers in the sandbox. Note that --dns 127.0.0.x // config refers to the loopback in the container namespace sb.setExternalResolvers(newRC.Content, types.IPv4, false) } else { // If the host resolv.conf file has 127.0.0.x container should // use the host resolver for queries. This is supported by the // docker embedded DNS server. Hence save the external resolvers // before filtering it out. sb.setExternalResolvers(currRC.Content, types.IPv4, true)
// Replace any localhost/127.* (at this point we have no info about ipv6, pass it as true) if newRC, err = resolvconf.FilterResolvDNS(currRC.Content, true); err != nil { return err } // No contention on container resolv.conf file at sandbox creation if err := ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, filePerm); err != nil { return types.InternalErrorf("failed to write unhaltered resolv.conf file content when setting up dns for sandbox %s: %v", sb.ID(), err) } }
// Write hash if err := ioutil.WriteFile(sb.config.resolvConfHashFile, []byte(newRC.Hash), filePerm); err != nil { return types.InternalErrorf("failed to write resolv.conf hash file when setting up dns for sandbox %s: %v", sb.ID(), err) }
returnnil }
這個函式會針對一些跟 DNS 相關的參數來進行處理,包含了
dnsServer
dnsSearch
dnsOptions
resolveConf
這邊的運作邏輯如下
先根據參數resolveConf來讀取當前 DNS 的全部設定
如果使用者有自行設定 DNS 的參數,就會全面使用這邊的設定,完全忽略(1)載入的設定 2.1 這邊最後會呼叫 resolvconf.Build 將參數的設定直接覆寫到容器內的 /etc/resolv.conf
// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs: // 1. It looks for localhost (127.*|::1) entries in the provided // resolv.conf, removing local nameserver entries, and, if the resulting // cleaned config has no defined nameservers left, adds default DNS entries // 2. Given the caller provides the enable/disable state of IPv6, the filter // code will remove all IPv6 nameservers if it is not enabled for containers // func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) { cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{}) // if IPv6 is not enabled, also clean out any IPv6 address nameserver if !ipv6Enabled { cleanedResolvConf = nsIPv6Regexp.ReplaceAll(cleanedResolvConf, []byte{}) } // if the resulting resolvConf has no more nameservers defined, add appropriate // default DNS servers for IPv4 and (optionally) IPv6 if len(GetNameservers(cleanedResolvConf, types.IP)) == 0 { logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns) dns := defaultIPv4Dns if ipv6Enabled { logrus.Infof("IPv6 enabled; Adding default IPv6 external servers: %v", defaultIPv6Dns) dns = append(dns, defaultIPv6Dns...) } cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...) } hash, err := ioutils.HashData(bytes.NewReader(cleanedResolvConf)) if err != nil { return nil, err } return &File{Content: cleanedResolvConf, Hash: hash}, nil }
首先先呼叫 ReplaceAll 把所有 localhost 127.0.0.0/8 相關的 IP 都清空。
清空之後,若發現這時候沒有 DNS 的話,直接透過 dns := defaultIPv4Dns 補上預設的 DNS (8.8.8.8/8.8.4.4)