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
相关产品推荐
相关产品推荐

