方法一

方法一

有没有一种方法可以让应用程序/proc/net/stat从另一个名称空间访问?

显而易见的事情是将应用程序(即进程)放入该 netns 中,但是,我想到的该应用程序应该监视统计数据,/proc/net/stat并应通过网络将它们转发到数据库,但是数据库只是路由-能够从默认命名空间。

我希望会有类似的路径,/proc/namespace-root/foo/net/stat但没有。

另外,我希望我不必依赖其他名称空间中的随机 PID 并通过/proc/$somePID/ns/net.

答案1

给一个进程一个不同的网络命名空间:

$ ps aux | grep '[s]leep'
root      716080  0.4  0.0   2292   748 ?        Ss   19:09   0:00 sleep 24h

$ sudo ls -l /proc/$$/ns/net /proc/716080/ns/net
lrwxrwxrwx 1 user    group   0 Jul 14 19:11 /proc/715845/ns/net -> 'net:[4026531992]'
lrwxrwxrwx 1 root    root    0 Jul 14 19:09 /proc/716080/ns/net -> 'net:[4026532200]'

方法一

我不知道/proc/net/stats是什么——我没有——所以我/proc/net/stat/nf_conntrack用这个例子来代替。如果我在默认命名空间中查看该文件,我会看到:

$ cat /proc/net/stat/nf_conntrack
entries  searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error  expect_new expect_create expect_delete search_restart
00000001  00000000 00000000 00000000 00000000 000000b8 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000
00000001  00000000 00000000 00000000 00000002 00000087 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

您可以使用该nsenter命令在不同的命名空间中运行命令。在这里,您可以cat在目标进程的网络命名空间中运行:

$ sudo nsenter --net=/proc/716080/ns/net cat /proc/net/stat/nf_conntrack
entries  searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error  expect_new expect_create expect_delete search_restart
00000000  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000
00000000  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

仔细一看,数值是不一样的;使用的nsenter结果是目标进程的网络命名空间中该 proc 文件的内容。

方法2

OP 正在寻找一种在指标收集应用程序环境中工作的解决方案。我整理了以下示例应用程序,它将实现预期的结果:

#define _GNU_SOURCE
#include <fcntl.h>
#include <limits.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <pid>\n", argv[0]);
        return 1;
    }

    int pipe_fds[2] = {};

    if (pipe(pipe_fds) < 0) {
        perror("pipe");
        return 2;
    }

    const pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        return 3;
    }

    if (pid == 0) { // child
        char file_path[PATH_MAX];

        if (dup2(pipe_fds[1], STDOUT_FILENO) < 0) {
            perror("dup2");
            return 4;
        }
        close(pipe_fds[0]);
        close(pipe_fds[1]);

        snprintf(file_path, sizeof(file_path) - 1, "/proc/%s/ns/net", argv[1]);

        const int fd = open(file_path, O_RDONLY);
        if (fd < 0) {
            perror("open");
            return 5;
        }

        if (setns(fd, CLONE_NEWNET) < 0) {
            perror("setns");
            return 6;
        }


        FILE* const proc_file = fopen("/proc/net/stat/nf_conntrack", "r");
        if (proc_file == NULL) {
            fprintf(stderr, "fopen failed\n");
            return 7;
        }


        char* line = NULL;
        size_t line_len = 0;
        while (getline(&line, &line_len, proc_file) != -1) {
            printf("%s", line);
        }

        free(line);
        fclose(proc_file);

        return 0; // Child process is done
    }

    // parent
    if (dup2(pipe_fds[0], STDIN_FILENO) < 0) {
        perror("dup2");
        return 8;
    }
    close(pipe_fds[0]);
    close(pipe_fds[1]);

    char* line = NULL;
    size_t line_len = 0;

    while (getline(&line, &line_len, stdin) != -1) {
        printf("%s", line);
    }

    free(line);

    // Clean up our dead child
    wait(NULL);

    return 0;
}

应用程序在默认网络命名空间中启动。它需要一个命令行参数——目标网络命名空间中进程的 PID。

该程序创建一个管道,然后创建分叉。子进程将其标准输出连接到管道的写入端。父级将其标准输入连接到管道的读取端。

子进程使用打开目标进程的网络命名空间proc文件,然后使用系统setns()调用将其网络命名空间切换到目标进程的网络命名空间。然后,子进程打开/proc文件,逐行读取它,并将结果写入标准输出(此处为管道)。

父级从标准输入(管道)读取行并将这些行写入标准输出。

在这里,父进程可以扮演指标收集应用程序的角色。它保留在默认网络命名空间中,并且可以在默认网络命名空间的上下文中连接到某些远程网络主机,以便传达读取的值。

请注意,fork()基于 - 的方法并不是唯一的选择,它只是我使用的方法。

程序的运行:

$ sudo ./a.out 716080
entries  searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error  expect_new expect_create expect_delete search_restart
00000000  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000
00000000  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000
$

相关内容