7pc8.com还是在原来服务器上
作者: wakmemory&lody
SSTap
最近发现了个55R全局代理的软件感觉挺好用的
加个混淆免下流什么的…妥妥的

下载链接已放到Download页面
KVM安装Windows2003
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
KVM安装Windows2008
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
最弱
深埋于心,剥离吾身。
不为生所知…
MS Office 发布测试
这是一个测试

【转载】Docker与Kubernetes系列(七): Docker Swarm
这段时间工作中用到了Docker以及Kubernetes(简称K8S),现在整理下我学习Docker以及K8S过程中看的一些比较好的资料,方便自己回顾,也希望能给容器小白一些帮助。给自己定一个小目标,二月底之前完成。
这是本系列的第七篇文章, 先不介绍K8S了, 我打算先来介绍Docker Swarm(K8S以后再说吧)。
本篇文章基本翻译和总结自Docker官方文档, 做了精简和改动。
一、Swarm的一些核心概念
docker engine中已经内嵌了集群管理和编排的特性,加入到集群中的docker engine就是以swarm模式运行的。 我们可以以初始化一个swarm或者加入一个swarm的方式来使docker engine进入swarm模式。
一个Swarm就是一个docker engine的集群,我们可以在上面部署services。 我们也可以同时在同一台Docker实例上部署service和单独的docker容器。
Node
Service和Task
二、在Swarm上部署一个service
- 初始化一个以swarm模式运行的docker engine集群
- 向这个集群中加入两个worker node
- 部署一个service
初始化Swarm
加入Worker nodes
–token XXX \
10.66.137.222:2377
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
dsu1tginb4deet36qqzo4bbr3 localhost.localdomain Ready Active Reachable
t2213ncsiy7oxfg73umm7fnyh localhost.localdomain Ready Active
wbbl4h5qldptdaq6ogawvonhi * localhost.localdomain Ready Active Leader
部署service
【转载】Docker与Kubernetes系列(六): Docker的原理2_Linux CGroup
这段时间工作中用到了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。
主要提供了如下功能:
- Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。
- Prioritization: 优先级控制,比如:CPU利用和磁盘IO吞吐。
- Accounting: 一些审计或一些统计,主要目的是为了计费。
- Control: 挂起进程,恢复执行进程。
使用 cgroup,系统管理员可更具体地控制对系统资源的分配、优先顺序、拒绝、管理和监控。可更好地根据任务和用户分配硬件资源,提高总体效率。
在实践中,系统管理员一般会利用CGroup做下面这些事(有点像为某个虚拟机分配资源似的):
- 隔离一个进程集合(比如:nginx的所有进程),并限制他们所消费的资源,比如绑定CPU的核。
- 为这组进程 分配其足够使用的内存
- 为这组进程分配相应的网络带宽和磁盘存储限制
- 限制访问某些设备(通过设置设备的白名单)
那么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 限制
假设,我们有一个非常吃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掉了。
磁盘I/O限制
我们先看一下我们的硬盘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…
|
CGroup的子系统
好了,有了以上的感性认识我们来,我们来看看control group有哪些子系统:
- blkio — 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等)。
- cpu — 这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问。
- cpuacct — 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。
- cpuset — 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。
- devices — 这个子系统可允许或者拒绝 cgroup 中的任务访问设备。
- freezer — 这个子系统挂起或者恢复 cgroup 中的任务。
- memory — 这个子系统设定 cgroup 中任务使用的内存限制,并自动生成内存资源使用报告。
- net_cls — 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。
- net_prio — 这个子系统用来设计网络流量的优先级
- hugetlb — 这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。
注意,你可能在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的术语
CGroup有下述术语:
- 任务(Tasks):就是系统的一个进程。
- 控制组(Control Group):一组按照某种标准划分的进程,比如官方文档中的Professor和Student,或是WWW和System之类的,其表示了某进程组。Cgroups中的资源控制都是以控制组为单位实现。一个进程可以加入到某个控制组。而资源的限制是定义在这个组上,就像上面示例中我用的haoel一样。简单点说,cgroup的呈现就是一个目录带一系列的可配置文件。
- 层级(Hierarchy):控制组可以组织成hierarchical的形式,既一颗控制组的树(目录结构)。控制组树上的子节点继承父结点的属性。简单点说,hierarchy就是在一个或多个子系统上的cgroups目录树。
- 子系统(Subsystem):一个子系统就是一个资源控制器,比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。Cgroup的子系统可以有很多,也在不断增加中。
【转载】Docker与Kubernetes系列(五): Docker的原理1_Linux Namespace
这段时间工作中用到了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) |
- UTS: hostname
- IPC: 进程间通信
- PID: “chroot”进程树
- NS: 挂载点,首次登陆Linux
- NET: 网络访问,包括接口
- USER: 将本地的虚拟user-id映射到真实的user-id
主要是š三个系统调用
- šclone() – 实现线程的系统调用,用来创建一个新的进程,并可以通过设计上述参数达到隔离。
- šunshare() – 使某进程脱离某个namespace
- šsetns() – 把某进程加入到某个namespace
二、几种Namespace
clone()系统调用
首先,我们来看一下一个最简单的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是什么样的。
UTS 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 Namespace
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已经被隔离了。
PID Namespace
我们继续修改上面的程序:
|
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 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系列(四): Docker的数据卷
这段时间工作中用到了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
volume的来源只有3种:即从容器挂载、从宿主机挂载和其他容器共享。内部通过Mount对象来维护逻辑。
- 删除Volume
如果删除容器时带有-v标签或是这个容器运行时带有一个–rm标签,删除容器时会尝试删除这个容器所使用的volum。在将自己从volume的Container列表中删除后,判断volume的Container的列表是否为空,如果这个volume不被任务容器使用,则将这个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