☰
Linux查看IP地址全攻略:从ip addr到公网IP排查
2026/10/15 4:27:53 网站建设 项目流程

1. 为什么"查看主机IP"这件小事,能难住不少人

我最早接触Linux的时候,第一件事就是想知道这台机器到底长什么IP。按理说这应该是最基础的入门操作,但我发现"查看IP"这件事在Linux下远比想象中容易踩坑——你在一台Ubuntu上敲惯了的命令,换到CentOS可能直接报错,换到最小化安装的服务器上,甚至发现命令根本不存在。

先说一个我自己的真实经历。有次远程登录一台刚交付的机器,终端黑黑的,连个桌面都没有,我下意识敲了ifconfig,结果返回command not found。当时我就意识到,这不是机器有问题,而是这台系统没装net-tools工具包。后来换用ip addr,一次搞定。从那以后我慢慢总结出一套完整的查看IP的思路,不是死记硬背某一条命令,而是搞清楚每条命令的适用场景、依赖环境和输出含义。

这篇文章我按照自己平时排查的习惯,把查看IP这件事拆成了几个层次:第一层是系统自带的核心命令,比如ip和hostname,基本任何发行版都能用;第二层是传统工具包net-tools里的ifconfig,老系统和管理脚本里很常见;第三层是拿到IP之后怎么判断它是内网地址还是公网地址,以及怎么确认当前生效的到底是哪块网卡。最后我会用一个完整的排查案例,把从登录服务器到最终确定IP的整个思考过程走一遍。

适合看这篇文章的人,我觉得有三类:一是刚入坑Linux的运维新手,需要一套完整、不出错的查IP方案;二是经常在最小化安装、容器环境、多云服务器之间切换的老手,想确认自己有没有漏掉什么细节;三是在写自动化脚本时需要稳定获取IP的开发者,脚本里取错IP可比手敲慢得多,错一次就够你头疼的。

先给结论:在任何一台现代Linux上,ip addr是覆盖面最广、最不会出错的命令。但光会这一条还不够,因为有些场景下你需要的是"对外访问的公网IP",有些场景需要的是"内网某个网卡上的私有IP",还有些场景IP是动态分配的,这次查到下次就变了。下面我按实际使用频率和场景,把每条路都拆开讲。

2. ip命令家族:现代Linux的默认答案

2.1 ip addr是首选,但不是唯一

ip命令是iproute2软件包提供的,这个包在99%的Linux发行版里默认安装。无论是Ubuntu、Debian、CentOS、Fedora、openSUSE,还是云厂商的定制镜像,只要是近十年发布的版本,基本都自带ip。

输入ip addr,你会看到类似这样的输出:

$ ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:16:3e:0a:1b:2c brd ff:ff:ff:ff:ff:ff inet 172.31.20.10/20 brd 172.31.31.255 scope global dynamic eth0 valid_lft 31536000sec preferred_lft 31536000sec inet6 fe80::216:3eff:fe0a:1b2c/64 scope link valid_lft forever preferred_lft forever

第一行里的lo是回环接口,IP永远是127.0.0.1,这个可以直接跳过。往下找状态是UP的非loopback接口,比如这里的eth0,它下面那个inet行就是你需要的IPv4地址:172.31.20.10。/20代表子网掩码长度,换算成十进制掩码是255.255.240.0,这个子网内的可用主机范围是172.31.16.1到172.31.31.254。brd是广播地址,scope global表示这是一个全局可路由的地址,不是只在本机有效的地址。

如果你想只看每个网卡简要信息,可以用ip -br addr,输出会比较紧凑:

$ ip -br addr lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 172.31.20.10/20 fe80::216:3eff:fe0a:1b2c/64

这里UP直接显示在第二列,一眼就能看出哪块网卡是活的。对于排查"为什么上不了网"这种问题,-br参数尤其好用,因为你可以立刻看到是不是有某块网卡没拿到地址。

2.2 为什么ifconfig反而不是首选

刚才说的ifconfig命令,它来自net-tools这个老工具包。在CentOS 7之前、Ubuntu 16.04之前,它确实是主流,很多老教程、老脚本都用它。但后来几个主流发行版逐渐用iproute2取代了net-tools,甚至在CentOS 8和Ubuntu 20.04的最小化安装里,net-tools默认根本不在。

这不是说net-tools过时了不好用了,而是它对现代网络环境的支持实在跟不上。比如ifconfig默认不显示你网卡上所有的IPv6地址,处理多网卡、多路由策略、VLAN、Policy Routing这些场景时能力很弱。iproute2当初设计就是要把net-tools里的ifconfig、route、arp、netstat这些工具统一管起来,你可以用ip route、ip neigh、ip link分别替代它们。既然iproute2既强大又默认可用,那你新登录的机器上,完全没必要为了看个IP再走ifconfig这条路。

当然,如果你管理的都是老系统,或者公司内部的其他同事习惯在脚本里用ifconfig解析输出,那你还是得懂它。下面我简单说一下net-tools的安装和输出差异,方便你在老机器上操作。

# Debian/Ubuntu sudo apt install net-tools # RHEL/CentOS sudo yum install net-tools

安装后运行ifconfig,输出长这样:

$ ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.31.20.10 netmask 255.255.240.0 broadcast 172.31.31.255 inet6 fe80::216:3eff:fe0a:1b2c prefixlen 64 scopeid 0x20<link> ether 00:16:3e:0a:1b:2c txqueuelen 1000 (Ethernet) RX packets 12345 bytes 12845678 (12.2 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 6789 bytes 987654 (987.6 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

注意看flags=4163<UP,BROADCAST,RUNNING,MULTICAST>这里,UP和RUNNING同时存在,才说明这块网卡是真正在运行且接入链路的。如果只有UP没有RUNNING,多半是网卡配置了但物理链路有问题,比如网线没插好、对端交换机端口down了。

2.3 用hostname快速拿"这台机器叫什么IP"

有时候你不是要面对复杂的网卡列表,只是想快速知道这台机器的名字对应哪个IP。hostname命令可以干这事,而且它比ip addr更简洁:

$ hostname -i 172.31.20.10

-i输出的是当前主机名解析出来的IP地址,通常取自/etc/hosts或DNS。这个命令的问题在于,它返回的地址可能是回环地址,也可能是第一个解析结果,不一定是你对外通信用的那个。比如/etc/hosts里写的是127.0.1.1 myhost,那hostname -i返回的就是127.0.1.1,而不是网卡上的真实地址。

如果你的主机名在DNS里有多个A记录,hostname -I(大写I)会列出所有地址,注意它和-i的差别:

$ hostname -I 172.31.20.10 10.0.0.5

-I不会返回回环地址,而是直接枚举所有非loopback接口的IPv4和IPv6地址,用空格分隔。这在写脚本时很实用,缺点是你得自己从多个地址里挑出真正要用的那个。

这里插一句我经常遇到的问题:为什么有的机器用hostname -I能列出一堆地址,比如172.31.20.10和10.0.0.5同时在列。原因通常是这台机器上有多个网卡——比如一个走管理网,一个走业务网,或者Docker、虚拟机网桥也都会被算进去。这时候拿到IP后,你得根据网段的规划来判断哪个是你要连的那个IP,不能随便拿一个用。

3. 内网IP和公网IP:别拿错"身份"

3.1 为什么我查到的IP和别人说的不一样

这是我在工单里见过最多的困惑。用户反馈:"我用ip addr查到了192.168.1.100,但别人访问我用的却是203.0.113.50,到底哪个是我这台机器的IP?"

答案其实取决于你站在哪个网络边界看。ip addr只能告诉你网卡上的配置,那是这台机器在自己所处局域网内的地址,我们叫内网IP或私有IP。RFC 1918规定了几段私有地址范围:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16。这些地址在公网上不可路由,只能在内网使用。

如果这台机器在云上,比如你在某个云厂商那里买了一台云服务器,那云平台通常会给它分配一个私有IP和一个公网IP。公网IP一般不是直接配在Linux网卡上的,而是通过底层网络地址转换(NAT)映射到私网IP上。所以你登录机器敲ip addr,怎么看都只能看到私网IP,因为公网IP是云平台在虚拟交换机那层做的地址转换,不体现在操作系统网卡上。

举个更好理解的例子:你家路由器对外有一个公网IP,但你家电脑、手机、电视的内网IP全是192.168.x.x。你在电脑上查IP,查到的是192.168.1.5,这个没问题。但如果别人从互联网访问你家,他访问的是路由器那个公网IP。Linux服务器的NAT场景本质和这个一模一样。

3.2 通过外部服务获取公网IP

当排查"服务器在公网到底显示什么IP"时,最直接的办法就是向一台外部服务器发起请求,让它告诉你它看到的源IP。常见的做法是用curl访问一些专门提供IP查询的服务:

# 方法一:查询HTTP头里的客户端IP curl ifconfig.me # 方法二:另一个常用服务 curl ip.sb # 方法三:也可以用DNS解析服务 dig +short myip.opendns.com @resolver1.opendns.com

这些服务背后的原理很简单:你的请求到达对方服务器时,对方的Web服务或DNS服务从TCP连接上看到你的源IP,然后原样返回给你。我平时最常用的是curl ifconfig.me,因为它输出极其干净,只返回一个IP。写脚本判断"我当前的出口IP是否变了"时非常方便。

这里有个很关键的点:如果服务器是经过多层NAT出网的,那么curl ifconfig.me拿到的是最外层NAT网关的IP,而不是你本机网卡上的IP。比如你公司内网所有机器都通过一个出口访问互联网,那所有机器用这个命令查询,返回的IP都是同一个出口IP。网络上有人把这种出口IP叫"公网IP",严格来说它确实是公网可路由的IP,但它不是你真正的"主机独有公网IP"。

既然提到了DNS方式,我再展开说说。dig +short myip.opendns.com @resolver1.opendns.com走的是DNS协议而不是HTTP,它的输出同样只有一行IP。用DNS服务的优势在于,有些防火墙环境会限制HTTP出网但放行DNS出网,这种情况下这个命令就能绕开限制。不过这只适用于有dig命令的环境,没有的话可以用nslookup:

nslookup myip.opendns.com resolver1.opendns.com

3.3 怎么判断一个IP是公网还是私网

拿到IP后,我一般会先看一眼网段。私有地址段就只有三个:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16。另外还有几个特殊用途地址段,比如169.254.0.0/16是链路本地地址,通常是DHCP获取失败时的自动配置地址;127.0.0.0/8是回环地址。

如果不想自己判断,可以在Linux上用Python一行搞定:

python3 -c "import ipaddress; print(ipaddress.ip_address('172.31.20.10').is_private)"

输出True说明是私网IP,False说明是公网可路由IP。这个模块还能判断is_global(全局可路由)、is_loopback等属性,写脚本时比手工匹配前缀靠谱得多。我遇到过不少人以为自己拿到的是公网IP,实际是私网IP或者保留地址,用这个脚本检查能省不少事。

4. 动态IP、多网卡与容器环境:这些场景最容易看走眼

4.1 网卡很多,哪块才是"业务IP"

在笔记本上你可能只有一块无线网卡加一块有线网卡,但在服务器上,网卡数量可以轻松上两位数。我曾经在测试机上见过eth0到eth7共八块网卡,加上一个docker0、一个br0,ip addr输出占了整整一屏。

这种时候,我的处理顺序是:

  1. 先过滤出状态为UP的接口:ip -br addr | grep UP。
  2. 排除lo、docker0、veth*、br-*这些非物理接口。
  3. 看剩下的物理网卡里哪块的IP属于你的业务网段或管理网段。
  4. 如果还是不确定,用ip route get反查下一跳,或者直接查路由表看默认路由走哪张网卡。

第4步是个隐藏技巧,我特别想展开说说。当你访问一个外部地址时,系统会选择默认路由对应的网卡出去。你可以用下面这条命令查看"如果要访问某个特定IP,系统会从哪块网卡出、源IP是什么":

$ ip route get 8.8.8.8 8.8.8.8 via 172.31.16.1 dev eth0 src 172.31.20.10 uid 1000 cache

看到src 172.31.20.10没?这行就是系统在访问外网时选择的源IP。这个结果比你在ip addr里边翻边猜靠谱得多,因为它直接告诉你"系统就是拿这个地址去和外界说话"。同样的道理,你可以把目标IP换成任意内网地址,比如数据库服务器的IP:

$ ip route get 10.10.0.50 10.10.0.50 via 10.0.0.1 dev eth1 src 10.0.0.5

这样你就能知道,访问某一段业务网络时走的是哪个网卡、源IP是什么。我在排查多网卡服务器路由不对、流量走了错误网卡的问题时,这条命令几乎是必用的。

4.2 DHCP动态分配时,如何在几分钟内定位IP变化

云服务器默认分配的IP大多是静态的,但办公环境、测试环境、实验室里很常见DHCP动态分配。如果你机器租约到期后重新获取了一个新IP,那你原本通过旧IP访问的连接全断了。这种情况下有个经验:先拔掉网线或者down掉网卡再up,但更稳妥的是老老实实看网卡当前租约。

DHCP获取到的IP信息里有个重要字段是租约时间。如果你用ip addr看到valid_lft 31536000sec,那说明DHCP服务器给了一年的租约,短期内不会变。如果看到valid_lft 600sec这种,说明只有十分钟租期,随时可能换地址。

查看DHCP详细租约,不同系统路径不一样:

# Debian/Ubuntu cat /var/lib/dhcp/dhclient.leases # CentOS/RHEL 7及更早 cat /var/lib/dhclient/dhclient.leases # CentOS/RHEL 8及之后,用NetworkManager cat /var/lib/NetworkManager/dhclient-*.conf

还有一种更省事的思路:与其每次手动确认IP,不如直接把DHCP客户端的hostname在服务器端固定。比如在路由器或DHCP服务器上,把这台机器的MAC地址和固定IP做绑定,这样无论DHCP怎么分配,它拿到的都是同一个地址。这个操作视具体设备而定,但思路是通用的,尤其适合自己搭的测试环境。

4.3 容器和虚拟机的IP查询差异

如果你在Docker容器里查看IP,情况又不一样了。容器里执行ip addr,看到的通常是172.17.x.x这样的Docker网桥分配地址。这没问题,容器访问外部时是经过宿主机的NAT出去的。但如果你的业务程序在容器里需要知道宿主机的IP,就不能靠容器内ip addr了。

常见做法是在容器启动时通过环境变量注入宿主机IP,比如:

docker run -e HOST_IP=$(hostname -I | awk '{print $1}') my_image

或者利用Docker内置的DNS名称host.docker.internal,不过这个特性在Linux下还需要额外配置,并不像在Mac/Windows上开箱即用。所以我自己的习惯是在容器里尽量避免依赖宿主机IP,要么在启动脚本里通过环境变量注入,要么直接让业务从网关地址反推——毕竟容器网桥的网关通常就是宿主机在容器网络里的地址。

虚拟机场景类似。如果你在一台KVM/QEMU虚拟机的Guest系统里查IP,拿到的是它自己那块虚拟网卡上的IP,而不是宿主机物理机IP。有时候你从宿主机的virsh list看到虚拟机在运行,但不知道它的IP,可以用virsh domifaddr查询域接口地址:

$ virsh domifaddr <vm_name> Name MAC address Protocol Address ---------------------------------------------------------------------------- vnet0 52:54:00:aa:bb:cc ipv4 192.168.122.100/24

不过这个命令需要虚拟机的网卡里有安装QEMU guest agent,不然拿不到地址。没有agent的情况下,只能去宿主机上用arp表查:

arp -n | grep 52:54:00:aa:bb:cc

5. 一次完整的"查IP"排查链路复盘

5.1 问题背景:登录新机器却不知道自己在哪

某天下午,我在管理一批新交付的测试服务器。这批机器用PXE方式批量安装了系统,配置了DHCP自动获取IP。供应商给了我一串主机名,但没有告诉我每台机器的IP。网络环境里又没有DNS对主机名做解析,于是我需要通过某种方式找到每台机器的实际IP。

我登录物理控制台(或者说是带外管理口),在本地终端里敲命令。这其实是一个很典型的场景:你人已经坐在机器面前了,但网络那头还没人知道这台机器的IP是多少。

5.2 逐步排查:从网卡状态、路由表到配置文件

第一步,我先看整体的网卡和地址状态:

$ ip -br addr lo UNKNOWN 127.0.0.1/8 ::1/128 enp3s0 DOWN enp4s0 UP 192.168.122.15/24

这里的输出已经很清楚了:enp3s0是DOWN状态,说明这块网卡没有启用或者没有接网线;enp4s0是UP状态,已经有IP192.168.122.15/24。如果这块网卡的IP就是DHCP分配的,那基本可以直接拿它走人。但我还是习惯多一步确认:看一眼当前默认路由走哪张网卡。

$ ip route show default via 192.168.122.1 dev enp4s0 proto dhcp src 192.168.122.15 metric 100

这条路由说明系统默认出网走的是enp4s0,源IP就是192.168.122.15。到这里,我对这台机器的IP基本有把握了。

第二步,我打开网卡配置文件确认一下它是静态配置还是DHCP动态分配。因为如果是DHCP,下次重启可能换IP,提前知道可以避免以后登录不上。

$ cat /etc/network/interfaces source /etc/network/interfaces.d/* auto enp4s0 iface enp4s0 inet dhcp

如果是Debian/Ubuntu系,配置在/etc/network/interfaces;如果是CentOS/RHEL系,配置一般是这样:

$ cat /etc/sysconfig/network-scripts/ifcfg-enp4s0 BOOTPROTO=dhcp DEVICE=enp4s0 ONBOOT=yes

从配置看,这台机器走的是DHCP,那我就要在交付文档里标注清楚:当前IP是192.168.122.15,但后续最好在DHCP服务器上做MAC绑定,或者改成静态配置,不然每次重启都可能变。

5.3 确认连通性:能ping通才是真的IP

查到了IP,我一般会再做一个连通性测试:从另一台机器ping这个IP,确认网络路径是通的。如果只是看着IP存在但网络不通,那排查还得继续。

我从另一台管理机上执行:

$ ping -c 3 192.168.122.15 PING 192.168.122.15 (192.168.122.15) 56(84) bytes of data. 64 bytes from 192.168.122.15: icmp_seq=1 ttl=64 time=0.432 ms 64 bytes from 192.168.122.15: icmp_seq=2 ttl=64 time=0.432 ms 64 bytes from 192.168.122.15: icmp_seq=3 ttl=64 time=0.432 ms

能通,说明这台机器的网络配置没问题,IP和路由都正确。到这里,问题算解决了。

这个排查链路看起来简单,但如果你把顺序换一下,就会很吃亏。我自己早期踩过的坑是:一上来就ifconfig,发现没这个命令就慌了,然后去装包;装完再一看,发现只看到回环地址;然后再去翻配置,发现网卡没起来。这一套下来半小时没了。所以我现在固定套路就是ip -br addr先看状态,ip route show看默认路由,最后配置文件确认动态静态,最多三分钟搞定。

6. Linux各发行版查IP的命令差异对照

我在实际工作中接触过的服务器,系统版本从CentOS 6到Ubuntu 22.04都有。为了方便你对照,我把常用发行版的环境差异和推荐命令整理成一张表格:

系统环境是否自带ip命令是否自带ifconfig推荐做法
Ubuntu 20.04及以上是否(需安装net-tools)ip addr
Debian 10及以上是否(默认无net-tools)ip -br addr
CentOS 7是是(默认装net-tools)ip addr,兼容ifconfig
CentOS 8及以上是否(最小化安装无)ip addr
openSUSE是否ip addr
Alpine Linux是否ip addr(busybox自带简化版)
最小化安装的任意发行版通常有通常没有优先ip addr

这张表的结论很明确:不管哪个环境,ip addr几乎都是通吃的。所以我建议你在任何新环境里,先跑ip addr,而不是ifconfig。这不是说ifconfig不能用了,只是它的可用性越来越差,你依赖它只会徒增兼容性问题。

另外补充一个为了在容器里快速拿IP,我常用的取巧写法。它把ip addr的输出通过awk和grep过滤,直接打印出所有非回环接口的IPv4地址:

ip -o -4 addr show scope global | awk '{print $2, $4}'

输出长这样:

eth0 172.31.20.10/20 eth1 10.0.0.5/24

-o让输出变成一行一条,-4只显示IPv4,scope global把回环和链路本地地址过滤掉,awk取接口名和CIDR地址。配合sed 's#/.*##'还能把子网掩码部分去掉,只留纯IP:

ip -o -4 addr show scope global | awk '{print $4}' | cut -d/ -f1

这个写法在自动化脚本里很实用,我自己的部署脚本里就是这么干的。它可以避免用hostname -I时碰到多IP顺序不稳定的问题,也能避免手动从一长串输出里复制错。

7. 我踩过的坑和最后想强调的两件事

查IP这事儿,虽然看着简单,但我在写自动化脚本和排查故障时,踩过的坑还真不少。最典型的一个是:脚本里用hostname -I取IP,结果/etc/hosts配置比较特殊,返回了多个地址,脚本拿着第一个地址就去注册服务了,结果注册了一个内网地址,外网完全访问不了。后来我改成用ip route get的方式确认源IP,才杜绝了这类问题。

第二个坑是判断公网IP。以前我一直以为云服务器网卡上配的IP就是公网IP,直到有次要配置白名单,我把网卡IP填进去,怎么都不生效,查了半天才发现那个IP是内网地址,真正的公网IP根本不在系统网卡上,而是在云平台的NAT规则里。自那以后,凡是涉及公网访问的排查,我都习惯先用curl ifconfig.me确认出口IP,再回来倒查映射关系。

第三个坑是关于命令的实时性。ip addr显示的是操作系统当前内核里的地址状态,它是实时准确的,但hostname -I有时候会受DNS缓存和主机名解析方式影响,返回一些不准确的地址。如果你在写脚本,建议以ip addr为准,必要时配合ip route get确认出口源IP。

最后我想说的是,查看Linux主机的IP地址,本质上是一个"确定这台机器在网络坐标系中的位置"的过程。你先要弄清楚自己在哪个网络边界(内网还是公网),再判断哪块网卡承载了你想要的那个通信路径,最后确认地址是静态还是动态,这样你拿到的IP才是可用的、可靠的。不要嫌这些步骤繁琐,真正的生产环境里,IP拿错一步,后面的网络策略、防火墙规则、服务注册全都会跟着错。希望这篇文章能帮你少走一些我当年走过的弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询