1.Docker容器是什么
按照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;
}