最近发现了个55R全局代理的软件感觉挺好用的
加个混淆免下流什么的...妥妥的
下载链接已放到Download页面
最近发现了个55R全局代理的软件感觉挺好用的
加个混淆免下流什么的...妥妥的
下载链接已放到Download页面
Windows Server 2003 Enterprise Edition SP2 32bit
进入 Rescue 模式
SSH 登陆
sudo su
wget -O- https://down.vps.re/Windows/DD/2003/KVM/windows2003.32bit.raw | dd of=/dev/sda
DD完毕后,进入正常模式(Boot in normal mode)
远程桌面
用户名:administrator
密码:changeme
系统盘 5GB (觉得小,可自行动态调整)
注意:KVM VPS在linux系统内进行DD操作,请先执行fdisk -l,看下硬盘是否是virtio模式,显示 /dev/sda1,则 dd of=/dev/sda,显示是 /dev/vda1 则 dd of=/dev/vda
Windows Server 2008 R2 Enterprise Edition 64bit
进入 Rescue 模式
SSH 登陆
sudo su
wget -O- https://down.vps.re/Windows/DD/2008/KVM/windows2008.R2.raw | dd of=/dev/sda
进度条显示100%完成后,点击 Boot in normal mode(进入正常模式)
DD完成后系统会自动重新识别安装硬件驱动,完成硬件驱动安装后会自动重启一次,大约5-10分钟后可以远程桌面
远程桌面
用户名:administrator
密码:Changeme!!!
系统盘 10GB
分区助手服务器版,安装后自行调整系统分区大小
这个模板包含 Intel全系列 Broadcom 57xx系列 网卡驱动 HP RAID卡 驱动 KVM virtio磁盘 virtio网卡 驱动 (适合所有基于KVM虚拟化VPS)
注意:KVM VPS在linux系统内进行DD操作,请先执行fdisk -l,看下硬盘是否是virtio模式,显示 /dev/sda1,则 dd of=/dev/sda,显示是 /dev/vda1 则 dd of=/dev/vda
深埋于心,剥离吾身。
不为生所知...
这是一个测试
这段时间工作中用到了Docker以及Kubernetes(简称K8S),现在整理下我学习Docker以及K8S过程中看的一些比较好的资料,方便自己回顾,也希望能给容器小白一些帮助。给自己定一个小目标,二月底之前完成。
这是本系列的第七篇文章, 先不介绍K8S了, 我打算先来介绍Docker Swarm(K8S以后再说吧)。
本篇文章基本翻译和总结自Docker官方文档, 做了精简和改动。
docker engine中已经内嵌了集群管理和编排的特性,加入到集群中的docker engine就是以swarm模式运行的。 我们可以以初始化一个swarm或者加入一个swarm的方式来使docker engine进入swarm模式。
一个Swarm就是一个docker engine的集群,我们可以在上面部署services。 我们也可以同时在同一台Docker实例上部署service和单独的docker容器。
这段时间工作中用到了Docker以及Kubernetes(简称K8S),现在整理下我学习Docker以及K8S过程中看的一些比较好的资料,方便自己回顾,也希望能给容器小白一些帮助。给自己定一个小目标,二月底之前完成。
这是本系列的第六篇文章, 将介绍Docker的原理之CGroup。(整理自: http://coolshell.cn/articles/17049.html)
前面,我们介绍了Linux Namespace,但是Namespace解决的问题主要是环境隔离的问题,这只是虚拟化中最最基础的一步,我们还需要解决对计算机资源使用上的隔离。也就是说,虽然你通过Namespace把我Jail到一个特定的环境中去了,但是我在其中的进程使用用CPU、内存、磁盘等这些计算资源其实还是可以随心所欲的。所以,我们希望对进程进行资源利用上的限制或控制。这就是linux CGroup出来了的原因。
Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。
Linux CGroupCgroup 可让您为系统中所运行任务(进程)的用户定义组群分配资源 — 比如 CPU 时间、系统内存、网络带宽或者这些资源的组合。您可以监控您配置的 cgroup,拒绝 cgroup 访问某些资源,甚至在运行的系统中动态配置您的 cgroup。
主要提供了如下功能:
使用 cgroup,系统管理员可更具体地控制对系统资源的分配、优先顺序、拒绝、管理和监控。可更好地根据任务和用户分配硬件资源,提高总体效率。
在实践中,系统管理员一般会利用CGroup做下面这些事(有点像为某个虚拟机分配资源似的):
那么CGroup是怎么干的呢?我们先来点感性认识吧。
首先,Linux把CGroup这个事实现成了一个file system,你可以mount。在我的Ubuntu 14.04下,你输入以下命令你就可以看到cgroup已为你mount好了。
1
2
3
4
5
6
7
8
9
10
11
12
|
hchen@ubuntu:~$ mount-t cgroup
cgroup on /sys/fs/cgroup/cpuset typecgroup (rw,relatime,cpuset)
cgroup on /sys/fs/cgroup/cpu typecgroup (rw,relatime,cpu)
cgroup on /sys/fs/cgroup/cpuacct typecgroup (rw,relatime,cpuacct)
cgroup on /sys/fs/cgroup/memory typecgroup (rw,relatime,memory)
cgroup on /sys/fs/cgroup/devices typecgroup (rw,relatime,devices)
cgroup on /sys/fs/cgroup/freezer typecgroup (rw,relatime,freezer)
cgroup on /sys/fs/cgroup/blkio typecgroup (rw,relatime,blkio)
cgroup on /sys/fs/cgroup/net_prio typecgroup (rw,net_prio)
cgroup on /sys/fs/cgroup/net_cls typecgroup (rw,net_cls)
cgroup on /sys/fs/cgroup/perf_event typecgroup (rw,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb typecgroup (rw,relatime,hugetlb)
|
或者使用lssubsys命令:
1
2
3
4
5
6
7
8
9
10
11
12
|
$ lssubsys -m
cpuset /sys/fs/cgroup/cpuset
cpu /sys/fs/cgroup/cpu
cpuacct /sys/fs/cgroup/cpuacct
memory /sys/fs/cgroup/memory
devices /sys/fs/cgroup/devices
freezer /sys/fs/cgroup/freezer
blkio /sys/fs/cgroup/blkio
net_cls /sys/fs/cgroup/net_cls
net_prio /sys/fs/cgroup/net_prio
perf_event /sys/fs/cgroup/perf_event
hugetlb /sys/fs/cgroup/hugetlb
|
我们可以看到,在/sys/fs下有一个cgroup的目录,这个目录下还有很多子目录,比如: cpu,cpuset,memory,blkio……这些,这些都是cgroup的子系统。分别用于干不同的事的。
如果你没有看到上述的目录,你可以自己mount,下面给了一个示例:
1
2
3
4
5
6
7
8
|
mkdircgroup
mount-t tmpfs cgroup_root ./cgroup
mkdircgroup/cpuset
mount-t cgroup -ocpuset cpuset ./cgroup/cpuset/
mkdircgroup/cpu
mount-t cgroup -ocpu cpu ./cgroup/cpu/
mkdircgroup/memory
mount-t cgroup -omemory memory ./cgroup/memory/
|
一旦mount成功,你就会看到这些目录下就有好文件了,比如,如下所示的cpu和cpuset的子系统:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
hchen@ubuntu:~$ ls/sys/fs/cgroup/cpu/sys/fs/cgroup/cpuset/
/sys/fs/cgroup/cpu:
cgroup.clone_children cgroup.sane_behavior cpu.shares release_agent
cgroup.event_control cpu.cfs_period_us cpu.stat tasks
cgroup.procs cpu.cfs_quota_us notify_on_release user
/sys/fs/cgroup/cpuset/:
cgroup.clone_children cpuset.mem_hardwall cpuset.sched_load_balance
cgroup.event_control cpuset.memory_migrate cpuset.sched_relax_domain_level
cgroup.procs cpuset.memory_pressure notify_on_release
cgroup.sane_behavior cpuset.memory_pressure_enabled release_agent
cpuset.cpu_exclusive cpuset.memory_spread_page tasks
cpuset.cpus cpuset.memory_spread_slab user
cpuset.mem_exclusive cpuset.mems
|
你可以到/sys/fs/cgroup的各个子目录下去make个dir,你会发现,一旦你创建了一个子目录,这个子目录里又有很多文件了。
1
2
3
4
5
|
hchen@ubuntu:/sys/fs/cgroup/cpu$ sudomkdirhaoel
[sudo] password forhchen:
hchen@ubuntu:/sys/fs/cgroup/cpu$ ls./haoel
cgroup.clone_children cgroup.procs cpu.cfs_quota_us cpu.stat tasks
cgroup.event_control cpu.cfs_period_us cpu.shares notify_on_release
|
好了,我们来看几个示例。
假设,我们有一个非常吃CPU的程序,叫deadloop,其源码如下:
1
2
3
4
5
6
|
intmain(void)
{
inti = 0;
for(;;) i++;
return0;
}
|
用sudo执行起来后,毫无疑问,CPU被干到了100%(下面是top命令的输出)
1
2
|
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3529 root 20 0 4196 736 656 R 99.6 0.1 0:23.13 deadloop
|
然后,我们这前不是在/sys/fs/cgroup/cpu下创建了一个haoel的group。我们先设置一下这个group的cpu利用的限制:
1
2
3
|
hchen@ubuntu:~# cat /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us
-1
root@ubuntu:~# echo 20000 > /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us
|
我们看到,这个进程的PID是3529,我们把这个进程加到这个cgroup中:
1
|
# echo 3529 >> /sys/fs/cgroup/cpu/haoel/tasks
|
然后,就会在top中看到CPU的利用立马下降成20%了。(前面我们设置的20000就是20%的意思)
1
2
|
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3529 root 20 0 4196 736 656 R 19.9 0.1 8:06.11 deadloop
|
下面的代码是一个线程的示例:
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
|
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
constintNUM_THREADS = 5;
void*thread_main(void*threadid)
{
/* 把自己加入cgroup中(syscall(SYS_gettid)为得到线程的系统tid) */
charcmd[128];
sprintf(cmd, "echo %ld >> /sys/fs/cgroup/cpu/haoel/tasks", syscall(SYS_gettid));
system(cmd);
sprintf(cmd, "echo %ld >> /sys/fs/cgroup/cpuset/haoel/tasks", syscall(SYS_gettid));
system(cmd);
longtid;
tid = (long)threadid;
printf("Hello World! It's me, thread #%ld, pid #%ld!\n", tid, syscall(SYS_gettid));
inta=0;
while(1) {
a++;
}
pthread_exit(NULL);
}
intmain (intargc, char*argv[])
{
intnum_threads;
if(argc > 1){
num_threads = atoi(argv[1]);
}
if(num_threads<=0 || num_threads>=100){
num_threads = NUM_THREADS;
}
/* 设置CPU利用率为50% */
mkdir("/sys/fs/cgroup/cpu/haoel", 755);
system("echo 50000 > /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us");
mkdir("/sys/fs/cgroup/cpuset/haoel", 755);
/* 限制CPU只能使用#2核和#3核 */
system("echo \"2,3\" > /sys/fs/cgroup/cpuset/haoel/cpuset.cpus");
pthread_t* threads = (pthread_t*) malloc(sizeof(pthread_t)*num_threads);
intrc;
longt;
for(t=0; t<num_threads; t++){
printf("In main: creating thread %ld\n", t);
rc = pthread_create(&threads[t], NULL, thread_main, (void*)t);
if(rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
/* Last thing that main() should do */
pthread_exit(NULL);
free(threads);
}
|
我们再来看一个限制内存的例子(下面的代码是个死循环,其它不断的分配内存,每次512个字节,每次休息一秒):
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
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
intmain(void)
{
intsize = 0;
intchunk_size = 512;
void*p = NULL;
while(1) {
if((p = malloc(p, chunk_size)) == NULL) {
printf("out of memory!!\n");
break;
}
memset(p, 1, chunk_size);
size += chunk_size;
printf("[%d] - memory is allocated [%8d] bytes \n", getpid(), size);
sleep(1);
}
return0;
}
|
然后,在我们另外一边:
1
2
3
4
5
6
|
# 创建memory cgroup
$ mkdir/sys/fs/cgroup/memory/haoel
$ echo64k > /sys/fs/cgroup/memory/haoel/memory.limit_in_bytes
# 把上面的进程的pid加入这个cgroup
$ echo[pid] > /sys/fs/cgroup/memory/haoel/tasks
|
你会看到,一会上面的进程就会因为内存问题被kill掉了。
我们先看一下我们的硬盘IO,我们的模拟命令如下:(从/dev/sda1上读入数据,输出到/dev/null上)
1
|
sudoddif=/dev/sda1of=/dev/null
|
我们通过iotop命令我们可以看到相关的IO速度是55MB/s(虚拟机内):
1
2
|
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
8128 be/4 root 55.74 M/s 0.00 B/s 0.00 % 85.65 % ddif=/de~=/dev/null...
|
然后,我们先创建一个blkio(块设备IO)的cgroup
1
|
mkdir/sys/fs/cgroup/blkio/haoel
|
并把读IO限制到1MB/s,并把前面那个dd命令的pid放进去(注:8:0 是设备号,你可以通过ls -l /dev/sda1获得):
1
2
|
root@ubuntu:~# echo '8:0 1048576' > /sys/fs/cgroup/blkio/haoel/blkio.throttle.read_bps_device
root@ubuntu:~# echo 8128 > /sys/fs/cgroup/blkio/haoel/tasks
|
再用iotop命令,你马上就能看到读速度被限制到了1MB/s左右。
1
2
|
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
8128 be/4 root 973.20 K/s 0.00 B/s 0.00 % 94.41 % ddif=/de~=/dev/null...
|
好了,有了以上的感性认识我们来,我们来看看control group有哪些子系统:
注意,你可能在Ubuntu 14.04下看不到net_cls和net_prio这两个cgroup,你需要手动mount一下:
1
2
3
4
5
6
7
|
$ sudomodprobe cls_cgroup
$ sudomkdir/sys/fs/cgroup/net_cls
$ sudomount-t cgroup -o net_cls none /sys/fs/cgroup/net_cls
$ sudomodprobe netprio_cgroup
$ sudomkdir/sys/fs/cgroup/net_prio
$ sudomount-t cgroup -o net_prio none /sys/fs/cgroup/net_prio
|
关于各个子系统的参数细节,以及更多的Linux CGroup的文档,你可以看看下面的文档:
CGroup有下述术语:
这段时间工作中用到了Docker以及Kubernetes(简称K8S),现在整理下我学习Docker以及K8S过程中看的一些比较好的资料,方便自己回顾,也希望能给容器小白一些帮助。给自己定一个小目标,二月底之前完成。
这是本系列的第五篇文章, 将介绍Docker的原理之Namespace。(整理自:http://coolshell.cn/articles/17010.html)
docker容器本质上是宿主机上的进程。Docker 通过namespace实现了资源隔离。
Linux Namespace是linux提供的一种内核级别环境隔离的方法。不知道你是否还记得很早以前的Unix有一个叫chroot的系统调用(通过修改根目录把用户jail到一个特定目录下),chroot提供了一种简单的隔离模式:chroot内部的文件系统无法访问外部的内容。Linux Namespace在此基础上,提供了对UTS、IPC、mount、PID、network、User等的隔离机制。
举个例子,我们都知道,Linux下的超级父亲进程的PID是1,所以,同chroot一样,如果我们可以把用户的进程空间jail到某个进程分支下,并像chroot那样让其下面的进程 看到的那个超级父进程的PID为1,于是就可以达到资源隔离的效果了(不同的PID namespace中的进程无法看到彼此)
šLinux Namespace 有如下种类,官方文档在这里《Namespace in Operation》
分类 | 系统调用参数 | 相关内核版本 |
---|---|---|
Mount namespaces | CLONE_NEWNS | Linux 2.4.19 |
šUTS namespaces | CLONE_NEWUTS | Linux 2.6.19 |
IPC namespaces | CLONE_NEWIPC | Linux 2.6.19 |
PID namespaces | CLONE_NEWPID | Linux 2.6.24 |
Network namespaces | CLONE_NEWNET | 始于Linux 2.6.24 完成于 Linux 2.6.29 |
User namespaces | CLONE_NEWUSER | 始于 Linux 2.6.23 完成于 Linux 3.8) |
主要是š三个系统调用
首先,我们来看一下一个最简单的clone()系统调用的示例,(后面,我们的程序都会基于这个程序做修改):
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
|
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024 * 1024)
staticcharcontainer_stack[STACK_SIZE];
char* constcontainer_args[] = {
"/bin/bash",
NULL
};
intcontainer_main(void* arg)
{
printf("Container - inside the container!\n");
/* 直接执行一个shell,以便我们观察这个进程空间里的资源是否被隔离了 */
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return1;
}
intmain()
{
printf("Parent - start a container!\n");
/* 调用clone函数,其中传出一个函数,还有一个栈空间的(为什么传尾指针,因为栈是反着的) */
intcontainer_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD, NULL);
/* 等待子进程结束 */
waitpid(container_pid, NULL, 0);
printf("Parent - container stopped!\n");
return0;
}
|
从上面的程序,我们可以看到,这和pthread基本上是一样的玩法。但是,对于上面的程序,父子进程的进程空间是没有什么差别的,父进程能访问到的子进程也能。
下面, 让我们来看几个例子看看,Linux的Namespace是什么样的。
下面的代码,我略去了上面那些头文件和数据结构的定义,只有最重要的部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
intcontainer_main(void* arg)
{
printf("Container - inside the container!\n");
sethostname("container",10); /* 设置hostname */
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return1;
}
intmain()
{
printf("Parent - start a container!\n");
intcontainer_pid = clone(container_main, container_stack+STACK_SIZE,
CLONE_NEWUTS | SIGCHLD, NULL); /*启用CLONE_NEWUTS Namespace隔离 */
waitpid(container_pid, NULL, 0);
printf("Parent - container stopped!\n");
return0;
}
|
运行上面的程序你会发现(需要root权限),子进程的hostname变成了 Container。
1
2
3
4
5
6
7
|
hchen@ubuntu:~$ sudo./uts
Parent - start a container!
Container - inside the container!
root@container:~# hostname
container
root@container:~# uname -n
container
|
IPC全称 Inter-Process Communication,是Unix/Linux下进程间通信的一种方式,IPC有共享内存、信号量、消息队列等方法。所以,为了隔离,我们也需要把IPC给隔离开来,这样,只有在同一个Namespace下的进程才能相互通信。如果你熟悉IPC的原理的话,你会知道,IPC需要有一个全局的ID,即然是全局的,那么就意味着我们的Namespace需要对这个ID隔离,不能让别的Namespace的进程看到。
要启动IPC隔离,我们只需要在调用clone时加上CLONE_NEWIPC参数就可以了。
1
2
|
intcontainer_pid = clone(container_main, container_stack+STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWIPC | SIGCHLD, NULL);
|
首先,我们先创建一个IPC的Queue(如下所示,全局的Queue ID是0)
1
2
3
4
5
6
7
|
hchen@ubuntu:~$ ipcmk -Q
Message queue id: 0
hchen@ubuntu:~$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0xd0d56eb2 0 hchen 644 0 0
|
如果我们运行没有CLONE_NEWIPC的程序,我们会看到,在子进程中还是能看到这个全启的IPC Queue。
1
2
3
4
5
6
7
8
9
|
hchen@ubuntu:~$ sudo./uts
Parent - start a container!
Container - inside the container!
root@container:~# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0xd0d56eb2 0 hchen 644 0 0
|
但是,如果我们运行加上了CLONE_NEWIPC的程序,我们就会下面的结果:
1
2
3
4
5
6
7
8
|
root@ubuntu:~$ sudo./ipc
Parent - start a container!
Container - inside the container!
root@container:~/linux_namespace# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
|
我们可以看到IPC已经被隔离了。
我们继续修改上面的程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
intcontainer_main(void* arg)
{
/* 查看子进程的PID,我们可以看到其输出子进程的 pid 为 1 */
printf("Container [%5d] - inside the container!\n", getpid());
sethostname("container",10);
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return1;
}
intmain()
{
printf("Parent [%5d] - start a container!\n", getpid());
/*启用PID namespace - CLONE_NEWPID*/
intcontainer_pid = clone(container_main, container_stack+STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);
printf("Parent - container stopped!\n");
return0;
}
|
运行结果如下(我们可以看到,子进程的pid是1了):
1
2
3
4
5
|
hchen@ubuntu:~$ sudo./pid
Parent [ 3474] - start a container!
Container [ 1] - inside the container!
root@container:~# echo $$
1
|
你可能会问,PID为1有个毛用啊?我们知道,在传统的UNIX系统中,PID为1的进程是init,地位非常特殊。他作为所有进程的父进程,有很多特权(比如:屏蔽信号等),另外,其还会为检查所有进程的状态,我们知道,如果某个子进程脱离了父进程(父进程没有wait它),那么init就会负责回收资源并结束这个子进程。所以,要做到进程空间的隔离,首先要创建出PID为1的进程,最好就像chroot那样,把子进程的PID在容器内变成1。
但是,我们会发现,在子进程的shell里输入ps,top等命令,我们还是可以看得到所有进程。说明并没有完全隔离。这是因为,像ps, top这些命令会去读/proc文件系统,所以,因为/proc文件系统在父进程和子进程都是一样的,所以这些命令显示的东西都是一样的。
所以,我们还需要对文件系统进行隔离。
下面的例程中,我们在启用了mount namespace并在子进程中重新mount了/proc文件系统。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
intcontainer_main(void* arg)
{
printf("Container [%5d] - inside the container!\n", getpid());
sethostname("container",10);
/* 重新mount proc文件系统到 /proc下 */
system("mount -t proc proc /proc");
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return1;
}
intmain()
{
printf("Parent [%5d] - start a container!\n", getpid());
/* 启用Mount Namespace - 增加CLONE_NEWNS参数 */
intcontainer_pid = clone(container_main, container_stack+STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);
printf("Parent - container stopped!\n");
return0;
}
|
运行结果如下:
1
2
3
4
5
6
7
|
hchen@ubuntu:~$ sudo./pid.mnt
Parent [ 3502] - start a container!
Container [ 1] - inside the container!
root@container:~# ps -elf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 1 0 0 80 0 - 6917 wait 19:55 pts/2 00:00:00/bin/bash
0 R root 14 1 0 80 0 - 5671 - 19:56 pts/2 00:00:00 ps-elf
|
上面,我们可以看到只有两个进程 ,而且pid=1的进程是我们的/bin/bash。我们还可以看到/proc目录下也干净了很多:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
root@container:~# ls /proc
1 dma key-users net sysvipc
16 driver kmsg pagetypeinfo timer_list
acpi execdomains kpagecount partitions timer_stats
asound fb kpageflags sched_debug tty
buddyinfo filesystems loadavg schedstat uptime
bus fs locks scsi version
cgroups interrupts mdstat self version_signature
cmdline iomem meminfo slabinfo vmallocinfo
consoles ioports misc softirqs vmstat
cpuinfo irq modules stat zoneinfo
crypto kallsyms mounts swaps
devices kcore mpt sys
diskstats keys mtrr sysrq-trigger
|
下图,我们也可以看到在子进程中的top命令只看得到两个进程了。
这里,多说一下。在通过CLONE_NEWNS创建mount namespace后,父进程会把自己的文件结构复制给子进程中。而子进程中新的namespace中的所有mount操作都只影响自身的文件系统,而不对外界产生任何影响。这样可以做到比较严格地隔离。
这段时间工作中用到了Docker以及Kubernetes(简称K8S),现在整理下我学习Docker以及K8S过程中看的一些比较好的资料,方便自己回顾,也希望能给容器小白一些帮助。给自己定一个小目标,二月底之前完成。
这是本系列的第四篇文章, 将介绍Docker的数据卷。
整理自: https://hujb2000.gitbooks.io/docker-flow-evolution/content/cn/advance/volume/preface.html
由于容器默认使用的AUFS文件系统的设计使得Docker可以提高镜像构建、存储和分发的效率,节省了时间和存储空间,然而也存在以下问题:
于是,Docker引入了数据卷(volume)机制(vfs文件系统,不支持写时复制)。
数据卷是存在于一个或多个容器中的特定的目录,这一目录绕过UFS,可以提供以下特性:
*注意:数据卷的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的数据卷。
Docker的数据的本质是容器中一个特殊的文件或目录(挂载点)。在容器的创建过程中,这个挂载点会被 挂载一个宿主机上的指定的目录 (一个以volumeID为名称的目录 或者指定的宿主机目录)。
例1: docker run -v /data busybox /bin/sh
//将宿主机上的volume_id目录绑定挂载到rootfs中指定的挂载点/data上 mount("/var/lib/docker/vfs/dir/volume_id","rootfs/data","none",MS_BIND,NULL);
例2: docker run -v /var/log:/data busybox /bin/bash
//将宿主机上的/var/log目录绑定挂载到rootfs中指定的挂载点/data上 mount("/var/log","rootfs/data","none",MS_BIND,NULL);
以上两种挂载方法的区别除了挂载的源目录不一样外,root/data目录下原来的文件在例1下是不存在的,但在例2下是仍旧存在的。
volume的来源只有3种:即从容器挂载、从宿主机挂载和其他容器共享。内部通过Mount对象来维护逻辑。
如果删除容器时带有-v标签或是这个容器运行时带有一个--rm标签,删除容器时会尝试删除这个容器所使用的volum。在将自己从volume的Container列表中删除后,判断volume的Container的列表是否为空,如果这个volume不被任务容器使用,则将这个volume删除 ,然后做以下两件事:
如果这个volume是从容器挂载的,所以只需要删除宿主机上对应的volume_id文件夹。
volume的相关配置文件
Docker的每个容器在docker/containers文件夹下有一个以容器ID命名的子文件夹,这个子文件夹中的config.json文件是这个容器的配置文件,可以从中看到这个容器所使用的volume ID以及它们的可写情况。 如果你要查看volume的具体信息,你可以在docker/volumes文件夹下找与volume id命名的子文件夹,这个子文件夹中的config.json文件包含了volume的具体信息。
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp Python app.py
Docker 挂载数据卷的默认权限是读写,用户也可以通过 :ro 指定为只读。
当然, 一次可以挂载多个数据卷。
查看数据卷的具体信息,docker inspect web 查看volume字段信息。
$ sudo docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
首先, 创建一个名为dbdata的数据卷容器
sudo docker run -d -v /dbdata --name dbdata training/postgres echo Data-only container for postgres
然后,在其他容器中使用 --volumes-from 来挂载 dbdata 容器中的数据卷。
$ sudo docker run -d --volumes-from dbdata --name db1 training/postgres
$ sudo docker run -d --volumes-from dbdata --name db2 training/postgres
可以使用超过一个的 --volumes-from 参数来指定从多个容器挂载不同的数据卷。 也可以从其他已经挂载了数据卷的容器来级联挂载数据卷。
$ sudo docker run -d --name db3 --volumes-from db1 training/postgres
注意:使用 --volumes-from 参数所挂载数据卷的容器自己并不需要保持在运行状态。如果删除了挂载的容器(包括 dbdata、db1 和 db2),数据卷并不会被自动删除。如果要删除一个数据卷,必须在删除最后一个还挂载着它的容器时使用 docker rm -v 命令来指定同时删除关联的容器。
首先使用 --volumes-from 标记来创建一个加载 dbdata 容器卷的容器,并从主机挂载当前目录到容器的 /backup 目录。命令如下:
$ sudo docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
容器启动后,使用了 tar 命令来将 dbdata 卷备份为容器中 /backup/backup.tar 文件,也就是主机当前目录下的名为 backup.tar 的文件。
如果要恢复数据到一个容器,首先创建一个带有空数据卷的容器 dbdata2。
$ sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash
然后创建另一个容器,挂载 dbdata2 容器卷中的数据卷,并使用 untar 解压备份文件到挂载的容器卷中。
$ sudo docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar
为了查看/验证恢复的数据,可以再启动一个容器挂载同样的容器卷来查看+
$ sudo docker run --volumes-from dbdata2 busybox /bin/ls /dbdata
这段时间工作中用到了Docker以及Kubernetes(简称K8S),现在整理下我学习Docker以及K8S过程中看的一些比较好的资料,方便自己回顾,也希望能给容器小白一些帮助。给自己定一个小目标,二月底之前完成。
这是本系列的第三篇文章, 将介绍如何访问Docker容器。
整理自: https://www.gitbook.com/book/yeasy/docker_practice/details
使用 docker ps 可以看到,本地主机的 49155 被映射到了容器的 5000 端口。此时访问本机的 49155 端口即可访问容器内 web 应用提供的界面。
$ sudo docker run -d -P training/webapp python app.py $ sudo docker ps -l CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse
-p(小写的)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort。
例如:
使用 hostPort:containerPort 格式本地的 5000 端口映射到容器的 5000 端口,可以执行
$ sudo docker run -d -p 5000:5000 training/webapp python app.py
此时默认会绑定本地所有接口上的所有地址。
容器的连接(linking)系统是除了端口映射外,另一种跟容器中应用交互的方式。
该系统会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。
使用 --link
参数可以让容器之间安全的进行交互。
下面先创建一个新的数据库容器。
$ sudo docker run -d --name db training/postgres
然后创建一个 web 容器,并将它连接到 db 容器
$ sudo docker run -d -P --name web --link db:db training/webapp python app.py
此时,db 容器和 web 容器建立互联关系。
--link 参数的格式为 --link name:alias,其中 name 是要链接的容器的名称,alias 是这个连接的别名。
使用 docker ps 来查看容器的连接
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 349169744e49 training/postgres:latest su postgres -c '/usr About a minute ago Up About a minute 5432/tcp db, web/db aed84ee21bde training/webapp:latest python app.py 16 hours ago Up 2 minutes 0.0.0.0:49154->5000/tcp web
可以看到自定义命名的容器,db 和 web,db 容器的 names 列有 db 也有 web/db。这表示 web 容器链接到 db 容器,web 容器将被允许访问 db 容器的信息。
Docker 在两个互联的容器之间创建了一个安全隧道,而且不用映射它们的端口到宿主主机上。在启动 db 容器的时候并没有使用 -p 和 -P 标记,从而避免了暴露数据库端口到外部网络上。