You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker容器内httpd端口检查Python脚本权限报错求助

Docker容器内Apache健康检查脚本权限问题解决

我正在为自身IT环境定制Docker镜像,希望借助Docker实现容器健康检查。由于系统配置使用不同端口,编写了一个动态获取httpd进程PID对应TCP端口并检查的Python脚本,脚本在容器外运行正常,但在Docker内执行时出现权限错误。

原脚本代码

#! /usr/sbin/python
import psutil
import socket

def get_httpd_pids():
    pids = []
    for proc in psutil.process_iter(['name', 'cmdline']):
        if proc.name() == 'httpd':
            pids.append(proc.pid)
    return pids

def get_open_tcp_ports(pid):
    process = psutil.Process(pid)
    connections = process.net_connections()
    tcp_connections = [conn for conn in connections if conn.type == socket.SocketKind.SOCK_STREAM]
    open_ports = [(conn.laddr.ip, conn.laddr.port) for conn in tcp_connections]
    return open_ports

def check_port_open(ip, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(2)
    try:
        s.connect((ip, port))
        print(f"Port {port} on {ip} is open")
        return True
    except socket.error:
        print(f"Port {port} on {ip} is closed")
        return False
    finally:
        s.close()

def main():
    ports = []
    for pid in get_httpd_pids():
        ports += get_open_tcp_ports(pid)
    ports = [(ip.replace('::', 'localhost'), port) for ip, port in ports]
    for ip, port in ports:
        if not check_port_open(ip, port):
            print("At least one port is closed. Exiting.")
            exit(1)
    exit(0)

if __name__ == "__main__":
    main()

错误信息

Traceback (most recent call last):
  File "/usr/lib/python3.12/site-packages/psutil/_pslinux.py", line 1717, in wrapper
    return fun(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/psutil/_pslinux.py", line 2348, in net_connections
    ret = _net_connections.retrieve(kind, self.pid)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/psutil/_pslinux.py", line 1034, in retrieve
    inodes = self.get_proc_inodes(pid)
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/psutil/_pslinux.py", line 857, in get_proc_inodes
    inode = readlink("%s/%s/fd/%s" % (self._procfs_path, pid, fd))
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/psutil/_pslinux.py", line 215, in readlink
    path = os.readlink(path)
           ^^^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/proc/16/fd/0'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/etc/httpd/scripts/ApacheHC.py", line 44, in <module>
    main()
  File "/etc/httpd/scripts/ApacheHC.py", line 35, in main
    ports += get_open_tcp_ports(pid)
             ^^^^^^^^^^^^^^^^^^^^^^^
  File "/etc/httpd/scripts/ApacheHC.py", line 14, in get_open_tcp_ports
    connections = process.net_connections()
                  ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/psutil/__init__.py", line 1223, in net_connections
    return self._proc.net_connections(kind)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/psutil/_pslinux.py", line 1719, in wrapper
    raise AccessDenied(self.pid, self._name)
psutil.AccessDenied: (pid=16)

解决方案

1. 权限问题根源

Docker容器内,非root用户无法访问其他进程的/proc/{pid}/fd目录——Linux默认该目录权限归进程所属用户,其他用户无访问权限。而psutil的net_connections方法需要读取这些fd链接来获取网络连接信息,因此触发PermissionError。

2. 方案一:修改脚本逻辑(推荐,安全)

放弃通过psutil读取进程端口的方式,改为直接解析Apache配置或通过官方命令获取监听端口,避免权限问题:

#! /usr/sbin/python
import socket
import subprocess

def get_httpd_listen_ports():
    # 用apachectl命令获取监听端口
    result = subprocess.run(['apachectl', '-S'], capture_output=True, text=True)
    ports = set()
    for line in result.stdout.split('\n'):
        if 'listen' in line.lower():
            # 提取端口,支持"*:80"或"[::]:443"格式
            parts = line.split(':')
            if len(parts) >= 2:
                port_str = parts[-1].strip()
                if port_str.isdigit():
                    ports.add(int(port_str))
    return list(ports)

def check_port_open(ip, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(2)
    try:
        s.connect((ip, port))
        print(f"Port {port} on {ip} is open")
        return True
    except socket.error:
        print(f"Port {port} on {ip} is closed")
        return False
    finally:
        s.close()

def main():
    ports = get_httpd_listen_ports()
    for port in ports:
        if not check_port_open('localhost', port):
            print("At least one port is closed. Exiting.")
            exit(1)
    exit(0)

if __name__ == "__main__":
    main()

3. 方案二:以root用户运行容器(简单但不安全)

在Dockerfile中添加USER root,或运行容器时指定参数:

docker run --user root your-image-name

此方法会降低容器安全性,不建议生产环境使用。

4. 方案三:添加系统权限(折中)

运行容器时添加SYS_PTRACE权限,允许进程读取其他进程的部分/proc信息:

docker run --cap-add SYS_PTRACE your-image-name

该权限存在一定安全风险,需评估业务场景后使用。

内容的提问来源于stack exchange,提问作者FrankStein

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 05:08:09