运维开发实践 - Docker - 容器实现原理

1.Docker容器是什么

按照,容器是运行在宿主机上的一个进程,但与宿主机上的其他进程相隔离;

2.容器实现原理

这种隔离机制使用了内核中的namespace和cgroups功能;

2.1.Linux namespace

Linux通过将系统的资源放置在不同的namespace下,实现资源的隔离;

类型 解释 Network 隔离网络资源 Mount 隔离文件系统的挂载点 UTS 隔离主机名和域名信息 IPC 隔离进程间通信 PID 隔离进程ID User 隔离用户和用户组ID
2.1.1.创建Namespace进程
clone系统调用:创建子进程
# flags: 控制新创建的进程隔离的资源
int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);
flag 隔离资源 描述 CLONE_NEWCGROUP 子进程的cgroup资源和当前进程隔离 隔离Cgroup根目录下不同层级目录的权限 CLONE_NEWIPC 子进程的ipc资源和当前进程隔离 隔离当前在不同进程间传递和交换信息的范围 CLONE_NEWNET …network… 隔离子进程的网络栈,路由表,防火墙规则等 CLONE_NEWNS …mount… 隔离文件系统的挂载点 CLONE_NEWPID …pid… 隔离进程的ID空间 CLONE_NEWUSER …user… 隔离用户uid,gid在宿主机中的权限 CLONE_NEWUTS …UTS… 隔离子进程的主机名,hostname和NIS域名

(NIS域名:Network information service,用共享网络信息的集中存储)

# 查看当前进程树
pstree -p
# 查看当前所有进程
# 在linux中一切皆文件,如下图我们可以看出进程本身其实也只是一个文件
ls /proc
# 查看pid=1的进程的namespace
ls -al /proc/1/ns
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/capability.h>
#include <stdio.h>
#include <sched.h>
#include <linux/sched.h>
#include <signal.h>
#include <unistd.h>

#define STACK_SIZE (1024 * 1024)

static char container_stack[STACK_SIZE];
char* const container_args[] = {
          
   
        "/bin/bash",
        NULL
};

int pipefd[2];

void set_map(char* file, int inside_id, int outside_id, int len) {
          
   
        FILE* mapfd = fopen(file, "w");
        if (NULL == mapfd) {
          
   
                perror("open file error");
                return;
        }
        fprintf(mapfd, "%d %d %d", inside_id, outside_id, len);
        fclose(mapfd);
}

void set_uid_map(pid_t pid, int inside_id, int outside_id, int len) {
          
   
        char file[256];
        sprintf(file, "/proc/%d/uid_map", pid);
        set_map(file, inside_id, outside_id, len);
}

void set_gid_map(pid_t pid, int inside_id, int outside_id, int len) {
          
   
        char file[256];
        sprintf(file, "/proc/%d/gid_map", pid);
        set_map(file, inside_id, outside_id, len);
}

int container_main()
{
          
   
        char ch;
        close(pipefd[1]);
        read(pipefd[0], &ch, 1);
        sethostname("container",10);

        /* Mount Namespace */
        mount("proc", "/proc", "proc", 0, NULL);
        mount("none", "/tmp", "tmpfs", 0, "");

        execv(container_args[0], container_args);
        return 1;
}

int main()
{
          
   
        const int gid=getgid(), uid=getuid();

        pipe(pipefd);

        int container_pid = clone(container_main, container_stack+STACK_SIZE,
            CLONE_NEWCGROUP|CLONE_NEWIPC|CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUSER | SIGCHLD, NULL);
        set_uid_map(container_pid, 0, uid, 1);
        set_gid_map(container_pid, 0, gid, 1);
        close(pipefd[1]);
        waitpid(container_pid, NULL, 0);
        return 0;
}

2.2.Linux cgroups

经验分享 程序员 微信小程序 职场和发展