http://man7.org/linux/man-pages/man7/user_namespaces.7.html
有c代码有助于理解
DevOps
场景:
当你把一个已经占用了1GB内存的进程转移到一个新的cgroup后,新的cgroup看到的该进程使用的内存大小会是多少呢?
memory.max_usage_in_bytes 和 memory.usage_in_bytes 都不会将这1GB计算在内的,只有该进程继续申请内存才会计算新申请的内存。
同样,如果把一个占用内存很多的进程从cgroup中移出来,memory.max_usage_in_bytes 和 memory.usage_in_bytes 应该也不会被更新
需要安装软件包: bash-completion ; 该软件包包含文件 /etc/profile.d/bash_completion.sh ;该文件会被自动执行,而且该文件会去加载/etc/bash_completion.d 下面的脚本
docker-ce-17.07.0 版本开始支持overlay2的磁盘配额,该版本 8月29号release了,现在可以使用edge版本:
overlay2的配额限制是有条件的:
1 |
mount -o pquota,uqnoenforce /dev/rbd2 /xfs |
xfs的pquota是个啥东西: https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Storage_Administration_Guide/xfsquota.html
noenforce
; this will allow usage reporting without enforcing any limits. Valid quota mount options are:uquota
/uqnoenforce
– User quotasgquota
/gqnoenforce
– Group quotaspquota
/pqnoenforce
– Project quota注意:
pquota和gquota 不能同时出现
如果是kickstart 安装的系统,用如下命令:
1 |
logvol / --fstype xfs --mkfsoptions='-n ftype=1' |
参考: http://foxhound.blog.51cto.com/1167932/1841487
docker-ce-17.07.0 关于xfs上的overlay2存储驱动的磁盘限额,使用效果如下:
注意:
阿里云提供的centos7镜像的文件系统类型似乎都是ext4的
并非所有的D状态的进程都是kill不掉的:
docker 的磁盘限额是可以通过xfs_quota 查看到的(kernel内核版本要求4.6以上才能查看到,小于4.6的内核版本上不是不支持限额,只是通过xfs_quota 看不到而已)
mlock可以锁住一部分内存,锁住干啥?
这里的锁住的真实含义是不让交换到swap上去。
ulimit可以限制允许mlock的内存大小
cat /proc/$pid/status |grep VmLck
1 |
bozo:100000:65536 |
从上面分析来看,userns-remap使用起来是比较麻烦的、且功能太有限; 我们期望不需要太多配置的情况下,不同容器中所有进程、子进程的用户都能自动映射到外部单独的uid,可能实现这个确实比较麻烦(参考下面原理部分)
看个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
#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 <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(void* arg) { printf("Container [%5d] - inside the container!\n", getpid()); printf("Container: eUID = %ld; eGID = %ld, UID=%ld, GID=%ld\n", (long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid()); /* 等待父进程通知后再往下执行(进程间的同步) */ char ch; close(pipefd[1]); read(pipefd[0], &ch, 1); printf("Container [%5d] - setup hostname!\n", getpid()); //set hostname sethostname("container",10); //remount "/proc" to make sure the "top" and "ps" show container's information mount("proc", "/proc", "proc", 0, NULL); execv(container_args[0], container_args); printf("Something's wrong!\n"); return 1; } int main() { const int gid=getgid(), uid=getuid(); printf("Parent: eUID = %ld; eGID = %ld, UID=%ld, GID=%ld\n", (long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid()); pipe(pipefd); printf("Parent [%5d] - start a container!\n", getpid()); int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUSER | SIGCHLD, NULL); printf("Parent [%5d] - Container [%5d]!\n", getpid(), container_pid); //To map the uid/gid, // we need edit the /proc/PID/uid_map (or /proc/PID/gid_map) in parent //The file format is // ID-inside-ns ID-outside-ns length //if no mapping, // the uid will be taken from /proc/sys/kernel/overflowuid // the gid will be taken from /proc/sys/kernel/overflowgid (container_pid, 0, uid, 1); set_gid_map(container_pid, 0, gid, 1); printf("Parent [%5d] - user/group mapping done!\n", getpid()); /* 通知子进程 */ close(pipefd[1]); waitpid(container_pid, NULL, 0); printf("Parent - container stopped!\n"); return 0; } |
在没有使用–userns-map的情况下,很多容器都使用相同的镜像(所以,相同用户名的uid基本都一样),每个容器都可以通过容器中的sshd进行登录,sshd登录后都会有相应的limits限制,以单个用户最大进程数量为例,一般来讲,root都是没有限制的,其他用户都是有限制的,对于mysql用户,通常会产生很多线程,从容器的角度来看,该用户名下的进程数量确实不多,甚至整个容器都没有几个进程,这时候,启动mysql时却是失败的;原因在于:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
core file size (blocks, -c) unlimited data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 15726 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 65536 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) unlimited virtual memory (kbytes, -v) unlimited file locks (-x) unlimited |
问题:
参考资料:
有些限制是系统级别的,有些限制是进程级别的:
参考:
memory.oom_control 文件中有个under_oom , 一般情况下值为0 ;当disable oom时,如果内存不够用了,进程会被pause,这时候under_oom 值为 1 ; under_oom 就是做这个用的,没别的意思
比较新一点版本的free添加了available列,可以直接看出还有多少内存可用
比较老一点版本的free为:
3.3.9 版本的free就没有available列,另外,和内核版本也有关系,3.14 以后的内核是支持的; 2.6.27+ 的内核上是模拟出来的
available
Estimation of how much memory is available for starting new applications, without swapping. Unlike
the data provided by the cache or free fields, this field takes into account page cache and also
that not all reclaimable memory slabs will be reclaimed due to items being in use (MemAvailable in
/proc/meminfo, available on kernels 3.14, emulated on kernels 2.6.27+, otherwise the same as free)
不知不觉中,我的博客的mysql用的内存越来越多了,我的1核1G的主机有点儿内存不太够用了,一个偶尔的操作,mysqld被oom了,重启就报错,大概意思是:
1 |
InnoDB: mmap(137363456 bytes) failed; errno 12 |
因为我的机器只有1G内存,而且没有swap,mmap想map大于1g的文件,结果失败了;
比较大气的解决办法是: 加内存
比较小气的解决办法是: 加swap