FPs

Salt Minion ID 變爲FQDN 記錄的問題

最近在一臺外網機器上起了salt minion ,但是同事發現/etc/salt/minion_id 不對,之前自動生成的minion_id 都是機器的/etc/hostname,這回變成了一個奇怪的域名:cncXXXX.XXX.ln.cn,並且這個域名和使用 hostname --all-fqdns 返回的結果相同。先查下minion_id 是怎麼生成的。

Github:saltstack/salt/doc/topics/tutorials/walkthrough.rst:

When the minion is started, it will generate an id value, unless it has been generated on a previous run and cached (in /etc/salt/minion_id by default). This is the name by which the minion will attempt to authenticate to the master. The following steps are attempted, in order to try to find a value that is not localhost:

  1. The Python function socket.getfqdn() is run
  2. /etc/hostname is checked (non-Windows only)
  3. /etc/hosts (%WINDIR%\system32\drivers\etc\hosts on Windows hosts) is checked for hostnames that map to anything within 127.0.0.0/8.

If none of the above are able to produce an id which is not localhost, then a sorted list of IP addresses on the minion (excluding any within 127.0.0.0/8) is inspected. The first publicly-routable IP address is used, if there is one. Otherwise, the first privately-routable IP address is used.

If all else fails, then localhost is used as a fallback.

應該是第一點,通過socket.getfqdn 拿到的結果,也驗證了和上文提到的hostname --all-fqdns 拿到的結果一樣。

FQDN是什麼?

FQDN全稱是"Fully qualified domain name",中文叫完整網域名稱,更細緻的介紹看鳥哥的解釋完整主機名稱: Fully Qualified Domain Name (FQDN)

socket.getfqdn()

看下這個函數的實現, socket.py

def getfqdn(name=''):
    """Get fully qualified domain name from name.

    An empty argument is interpreted as meaning the local host.

    First the hostname returned by gethostbyaddr() is checked, then
    possibly existing aliases. In case no FQDN is available, hostname
    from gethostname() is returned.
    """
    name = name.strip()
    if not name or name == '0.0.0.0':
        name = gethostname()
    try:
        hostname, aliases, ipaddrs = gethostbyaddr(name)
    except error:
        pass
    else:
        aliases.insert(0, hostname)
        for name in aliases:
        if '.' in name:
            break
        else:
            name = hostname
    return name

返回結果應該是gethostbyaddr() 這個函數返回的,這個函數的介紹如下:

gethostbyaddr() -- map an IP number or hostname to DNS info.

拿到的結果應該就是一個反查IP的時候拿到的PTR記錄。證實一下:

salt-minion-id-dig-x

看來這個IP 之前有人用過,然後添加了一條PTR 記錄,也沒辦法刪了,再繼續看看這個gethostbyaddr 的實現和工作原理,看看能怎麼解決。

關於這個函數更詳細的介紹,來自python.org/2/library/socket

socket.gethostbyaddr(ip_address)

Return a triple (hostname, aliaslist, ipaddrlist) where hostname is the primary host name responding to the given ip_address, aliaslist is a (possibly empty) list of alternative host names for the same address, and ipaddrlist is a list of IPv4/v6 addresses for the same interface on the same host (most likely containing only a single address). To findthe fully qualified domain name, use the function getfqdn(). gethostbyaddr() supports both IPv4 and IPv6.

這個函數的實現:cpython/socketmodule.c, 應該是調用了系統的gethostbyaddr_r,不過也有一個自己實現的cpython/getaddrinfo.c),Linux 系統提供的gethostbyaddr 的介紹,實現上應該是類似的, GETHOSTBYNAME(3), 這個函數依賴了下列三個配置文件:

/etc/host.conf
  resolver configuration file

/etc/hosts
  host database file

/etc/nsswitch.conf
  name service switch configuration

其中nsswitch.conf 定義了一些C 函數庫進行操作搜索的動作,先後順序。

Name Service Switch

The Name Service Switch (NSS) is a facility in Unix-like operating systems that provides a variety of sources for common configuration databases and name resolution mechanisms. These sources include local operating system files (such as /etc/passwd, /etc/group, and /etc/hosts), the Domain Name System (DNS), the Network Information Service (NIS), and LDAP.

nsswith.conf 文件中指定了hosts 查詢的先後順序:

hosts Host names and numbers, used by gethostbyname(3) and related functions.

目前的配置如下:

#       dns                     Use DNS (Domain Name Service)
#       files                   Use the local files

#hosts:     db files nisplus nis dns
hosts:      files dns myhostname

參考手冊,按照配置的定義,關於hosts,會先查files,而對於hosts,對應的files 是/etc/hosts,然後是dns 查詢,最後是看/etc/hostname。 做下實驗,把配置改爲:

hosts:      files 

socket.getfqdn() 返回結果爲空。

配置改成:

hosts:  myhostname files dns

socket.getfqdn() 返回結果爲/etc/hostname 的內容。

到這裏可以找到解決方法了,把文章開頭出問題的這臺機器的nsswitch.conf 如上配置,myhostname 放到前面即可解決問題。

hostname --all-fqdns

在返回來看看,一開始hostname --all-fqdns 的結果和socket.getfqdn() 一樣,並再上文更改nsswithc.conf 的配置過程中,插件結果表現也是一致的。 找Hostname 的源代碼看下,
hostname-getnameinfo

getnameinfo - address-to-name translation in protocol-independent manner

getnameinfo() 的功能和 gethostbyaddr() 類似,具體見:GETNAMEINFO(3) ,也可以通過nsswitch.conf 配置hosts 的查詢順序。

--END

This article is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
If you reprint it, please indicate the source: http://fangpeishi.com/salt_minion_id_getfqdn.html