二、Docker—Namespace隔离

  • A+
所属分类:docker

博主:运维Giao

博客:www.giaogg.cn

联系方式:QQ1035587908

交流群:1071386609

一、Namespace隔离


Namespace到底是啥

一台服务器硬件资源过剩,会希望硬件上多跑几个数据,在一个电脑安装多个服务,那么这个电脑变得不是很安全,服务越多越不安全。
原则:在Linux运行某个服务器中,安装的软件越少越好,每个容器运行一个软件。

容器云是一个弹性伸缩,在需要的功能上增加访问量,在不需要的功能上减少访问量。

单个服务容器如何形成?
在伸缩的时候,一个nginx变成4个nginx的时候,会出现问题,需要一个nginx放在一个空间,这个空间叫做Namespace,解决软件冲突问题。
相同的软件会有冲突的点,解决方法便是把他们彻底分开,放在不同的空间。

容器——box——文件夹
软件在放在box里,去运行,运行时会检测到把需要的东西放进去,软件开启的时候,会误以为box是一个操作系统。


容器代表一个品类,有很多种:
容器应用:docker、podman、coreOS、lxc......
images+docker=container(application)
容器包括容器本身和镜像,运行时创造一个空间,容器能做什么取决于镜像。

操作系统会默认支持podman(容器)
底层实现结构,命令行与docker相同

[root@localhost ~]# yum install podman -y
[root@localhost ~]# podman images
REPOSITORY   TAG   IMAGE ID   CREATED   SIZE

容器运行
[root@localhost ~]# podman run -it --name giao docker.io/library/centos /bin/bash
Trying to pull docker.io/library/centos...
Getting image source signatures
Copying blob 6910e5a164f7 done  
Copying config 831691599b done  
Writing manifest to image destination
Storing signatures
[root@776e4dc6aa8b /]# ls
bin  etc   lib    lost+found  mnt  proc  run   srv  tmp  var
dev  home  lib64  media       opt  root  sbin  sys  usr
[root@776e4dc6aa8b /]# 
[root@776e4dc6aa8b /]# exit
exit

删除镜像
[root@localhost ~]# podman rm -f giao
776e4dc6aa8b363913d7f6776679fdb2450c7b5cab9442386e87f99f20c4b934

为什么使用容器?

容器是软件具备了超强的可移植性,瞬间开启、瞬间关闭、省电。

二、Docker—Namespace隔离
kerbel:内核
system:操作系统,在system中安装程序(mail)
shell(bash、zsh、tsh):通过shell操作程序
exploer:windows的shell环境叫做exploder
user:复制下达命令给shell
lib:库文件,告知操作系统执行的命令
dll:动态链接库,软件和操作系统之间的协议
driver:驱动,操作系统和内核
hardware:硬件


首先user下达执行去操作shell——>system——>lib——>应用程序——>驱动程序——>内核,内核调用硬件,相同的应用程序的驱动只需要下载一次。

在正常的应用中,一些东西是可以共用的,shell是电脑的操作系统和user交互的地方。
指令下达给bash,bash把指令给操作系统,操作系统下达给应用程序需要的dll文件,(dynamic link library),交给内核,内核调取硬件,还需要驱动程序。


解耦状态下的虚拟化:
完美没有冲突的(vm、vnox、kvm)
半解耦状态下的虚拟化:
细节上有可能冲突(docker容器)
虚拟化是让一个服务多开。


耦合:是指两个或两个以上的体系或者两种运动形式间通过相互作用而彼此影响以至联合起来的现象。如:nginx与apache在同一服务器运行都占用80端口,起冲突时我们修改其中一个端口为8080

半耦合:同一个操作系统中两者共存各有各的特征,就是在同一个操作系统开启相同的程序。

解耦:在当前服务器安装一个虚拟机再安装服务(类似在自己电脑上安装的虚拟机)然后把所需要的应用程序再装一遍。

二、Docker—Namespace隔离
图中:
解耦:会将一些没有必要的东西再重新安装一遍
解耦状态虚拟化思路
在电脑上安装一个额操作系统(里面会有一层新的内核),里面会有数据,然后在开启一个服务(占用硬件资源,但是能完美运行一个服务)
Bins libs:驱动程序
Guestos:操作系统
虚拟化层(hypervisor):相当于vmware装的操作系统,用来分割硬件资源。
最底层的硬件:cpu、mem、disk

半解耦
直接在系统上安装一堆APP,旁边有一个docker(守护进程),直接从本机调取资源。
在硬件上安装完成操作系统后,但是并不直接产生操作系统,而是把lib、bin文件以软连接的形式加
Hypervisor:切割硬件,容器没有办法限制硬件。
不同操作系统公用内核(半解耦)
物理机的操作系统
[root@localhost ~]# uname -r
3.10.0-862.el7.x86_64

容器centos的操作系统
[root@localhost ~]# docker run -it -test centos /bin/bash
[root@4f3042fc61ba /]# uname -r
3.10.0-862.el7.x86_64

容器Ubuntu的操作系统
[root@localhost ~]# docker pull ubuntu
[root@localhost ~]# docker run -it -test ubuntu /bin/bash
root@13efc191805d:/# uname -r
3.10.0-862.el7.x86_64

如果物理机和容器的操作系统一样,物理机内容连上来,不相同则创建新的。

容器里不可以允许windwos,windows的内核不能用。
容器能解决的问题:软件的超强移植。
Namespace:半解耦状态下的虚拟化让一些软件不会有冲突
[root@localhost ~]# yum install -y httpd
查看进程PID
[root@localhost ~]# systemctl start httpd
[root@localhost ~]# netstat -anput|grep httpd
tcp6       0      0 :::80                   :::*                    LISTEN      2072/httpd          
运行的httpd内存大小就是proc的编号
/proc目录和/sys目录都是伪文件系统:程序在内存内的文件为0

[root@localhost ~]# cd /proc/2072/
[root@localhost 2072]# ls
attr        comm             fd        map_files   net            pagemap      schedstat  statm    wchan
autogroup   coredump_filter  fdinfo    maps        ns             patch_state  sessionid  status
auxv        cpuset           gid_map   mem         numa_maps      personality  setgroups  syscall
cgroup      cwd              io        mountinfo   oom_adj        projid_map   smaps      task
clear_refs  environ          limits    mounts      oom_score      root         stack      timers
cmdline     exe              loginuid  mountstats  oom_score_adj  sched        stat       uid_map


/proc
Linux系统上的/proc目录是一种文件系统,即proc文件系统,与其他常见的文件系统不同的是,/proc是一种伪文件系统(即:虚拟机文件系统),存储的是当前内核运行状态的一系列特许文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。


二、Docker核心——namespace(命名空间)


隔离的本质:不在统一的范围。

[root@localhost 2072]# pwd
/proc/2072
[root@localhost 2072]# cd ns/
[root@localhost ns]# ll
total 0
lrwxrwxrwx. 1 root root 0 Jul 23 21:30 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 Jul 23 21:30 mnt -> mnt:[4026532437]
lrwxrwxrwx. 1 root root 0 Jul 23 21:30 net -> net:[4026531956]
lrwxrwxrwx. 1 root root 0 Jul 23 21:30 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 Jul 23 21:30 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 Jul 23 21:30 uts -> uts:[4026531838]
  • ipc:信号量,消息队列和共享内存
  • mnt:挂载点和文件系统(解耦的时候使用私密挂载)
  • net:网络设备、端口
  • pid:编号
  • users:用户和组
  • uts:主机名和域名
进程隔离
[root@localhost ~]# vim fork_example.c
#include <unistd.h>
#include <stdio.h>
int main (){
  pid_t fpid;
  int count=0;
  fpid=fork();
  if (fpid < 0)printf("error in fork!");
  else if (fpid == 0) {
    printf("I am child.Process id is %d\n",getpid());
}
  else {
    printf("I am parent.Process id is %d\n",getpid());
}
  return 0;
}

运行——gcc编译(没有gcc环境自行安装)
[root@localhost ~]# gcc -Wall fork_example.c && ./a.out
fork_example.c: In function ‘main’:
fork_example.c:5:7: warning: unused variable ‘count’ [-Wunused-variable]
   int count=0;
       ^
I am parent.Process id is 3228
[root@localhost ~]# I am child.Process id is 3229

父进程比子进程小1
父进程和子进程的调取关系——父进程和子进程各开一个程序,模拟容器的打开。
主机名隔离
[root@localhost ~]# vim fork_example.c 
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

#define STACK_SIZE (1024 * 1024)

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

int child_main(void* args) {
  printf("在子进程中! \n");
  execv(child_args[0], child_args);
  return 1;
}

int main () {
  printf("程序开始:\n");
  int child_pid = clone(child_main, child_stack + STACK_SIZE, SIGCHLD, NULL);
  waitpid(child_pid, NULL, 0);
  printf("已退出\n");
  return 0;
}

[root@localhost ~]# gcc -Wall fork_example.c -o uts.o
运行
[root@localhost ~]# ./uts.o 
程序开始:
在子进程中! 

[root@localhost ~]# ps
   PID TTY          TIME CMD
  1486 pts/0    00:00:00 bash
  3238 pts/0    00:00:00 uts.o  #父进程
  3239 pts/0    00:00:00 bash   #子进程
  3250 pts/0    00:00:00 ps
两个程序无法区分
IPC消息队列隔离——验证隔离容器内外的隔离,消息互不影响物理机所有队列
二、Docker—Namespace隔离
两者的队列是相同的——程序无法产生真正的解耦
二、Docker—Namespace隔离
二、Docker—Namespace隔离
二、Docker—Namespace隔离
二、Docker—Namespace隔离
二、Docker—Namespace隔离
创建队列
二、Docker—Namespace隔离
容器里查看消息队列
二、Docker—Namespace隔离
查看物理机的消息队列——两者不同,说明隔离
PID隔离
二、Docker—Namespace隔离
第一次切割完,/bash的PID变成了1,但是1的PID应该是systemd,systemd是父进程,所有的进程都在systemd的基础下开启的,如果要切割PID编号就应该也要切割/proc目录,因为你的编号虽然变量但是/proc目录中的1的PID编号对应内荣是不对的,时候就需要对挂载进行特殊处理,不应该把现有的物理系统的挂载也挂载到容器里,或不让容器里的挂载出来、
/bin目录是单向传播
/lib驱动程序可以共享
/proc管理自己的内存状态,私有挂载
/root用户的家目录,属于隐私目录,个人的隐私状态
二、Docker—Namespace隔离
查看PID编号
1号进程没有彻底隔离出来父进程的原因,在容器内,yum、rpm安装包,无法启动程序因为PID不同
二、Docker—Namespace隔离
二、Docker—Namespace隔离
二、Docker—Namespace隔离
二、Docker—Namespace隔离
二、Docker—Namespace隔离
进程PID隔离
二、Docker—Namespace隔离
变为1号进程
但是这样隔离PID只是一个PID号,并没有实现真正的数据隔离。
mount隔离
  • Namespace挂载的四个模式
  • 主从挂载——master影响slave,slave不影响master
  • 共享挂载——share相互影响
  • 私密挂载——private互不影响
  • 不可绑定的挂载


容器里的/proc内容与物理机一样,这是因此此时容器的1号进程实际上是物理机的1号进程,但是容器运行的1号进程是/bin/bash,/bin/bash强行改为1号进程,没有权限生成新的子进程,所以需要解决的是容器1号进程把/proc挂载到独有的proc目录。

[root@Change name ~]# ls /proc/
1     15    264  3     368  392  544  685        devices      keys        net            timer_list
10    16    265  30    369  393  545  686        diskstats    key-users   pagetypeinfo   timer_stats
1013  17    266  3193  38   40   546  7          dma          kmsg        partitions     tty
1016  18    27   3202  382  41   547  8          driver       kpagecount  sched_debug    uptime
1018  19    274  3354  383  42   548  88         execdomains  kpageflags  schedstat      version
1021  2     275  3358  384  44   57   9          fb           loadavg     scsi           vmallocinfo
1022  20    276  3376  385  460  608  acpi       filesystems  locks       self           vmstat
11    2072  278  3380  386  485  634  buddyinfo  fs           mdstat      slabinfo       zoneinfo
1109  2073  279  3398  387  496  636  bus        interrupts   meminfo     softirqs
1110  2074  28   3416  388  5    637  cgroups    iomem        misc        stat
1111  2075  281  3417  389  510  638  cmdline    ioports      modules     swaps
13    2076  283  3418  39   541  640  consoles   irq          mounts      sys
14    2077  285  358   390  542  654  cpuinfo    kallsyms     mpt         sysrq-trigger
1439  21    29   359   391  543  661  crypto     kcore        mtrr        sysvipc
二、Docker—Namespace隔离
添加一个新的隔离选项
二、Docker—Namespace隔离
生成
二、Docker—Namespace隔离
两个主机的proc目录是相同的,容器内部做私有挂载进行隔离
proc类型,挂一个proc文件,proc设备是proc,/proc挂载到哪个目录
二、Docker—Namespace隔离
只有自己的进程
二、Docker—Namespace隔离
会影响到物理机
二、Docker—Namespace隔离
对物理机进行私有挂载
二、Docker—Namespace隔离
二、Docker—Namespace隔离
进入容器内,再次挂载
二、Docker—Namespace隔离
物理机


提权操作:
bug:没有真正的隔离,父进程不是systemd第一个进程,而容器开启以后启动的第一个进程是/bin/bash,此时容器的1号进程仍然是/bin/bash,无法启动systemd按照的程序,可以提权,让其变成/sbin/init
强行提权——不建议操作:
docker run --restart always -d --name test0 --privileged centos /sbin/init 容器内的1号进程
docker exec -it test0 /bin/bash 再用/bin/bash进去
yum install -y httpd
systemctl start httpd(可以执行,此时1号进程是int
exit
init是企业版6的进程
systemd是企业版7的进程

网络隔离
二、Docker—Namespace隔离
概念:
数据想要联通,用网线传输数据,网线一段是veth(虚拟以太网卡),另一端是peer,一个peer只能插一个veth,交换机,peer都插在交换机上,交换机负责转发数据,linux可以转发,需要打开路由。
配置路由转发功能
[root@localhost ~]# vim /etc/sysctl.conf 
net.ipv4.ip_forward = 1
打开路由转发
[root@localhost ~]# sysctl -p
net.ipv4.ip_forward = 1
创建虚拟空间
[root@localhost ~]# ip netns add test_ns
查看空间
[root@localhost ~]# ip netns exec test_ns ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
只有一块网卡,LOOPBACK没有开启

开启虚拟空间
[root@localhost ~]# ip netns exec test_ns ip link set dev lo up
网卡开启
[root@localhost ~]# ip netns exec test_ns ip link list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
此时可以ping通
[root@localhost ~]# ip netns exec test_ns ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.097 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.032 ms

创建一对veth peer
[root@localhost ~]# ip link add veth0 type veth peer name veth1
主机会多出两块网卡
[root@localhost ~]# ip a
veth1@veth0 #代表veth1连着veth0
veth0@veth1 

一端放在test_ns空间里
[root@localhost ~]# ip link set veth1 netns test_ns
物理机剩下veth0
veth0@if10

查看test_ns虚拟空间网卡IP
[root@localhost ~]# ip netns exec test_ns ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
10: veth1@if11: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 9a:60:bc:c8:2a:70 brd ff:ff:ff:ff:ff:ff link-netnsid 0
配置test_ns虚拟空间网卡IP
[root@localhost ~]# ip netns exec test_ns ifconfig veth1 10.1.1.1/24 up
[root@localhost ~]# ip netns exec test_ns ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
10: veth1@if11: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
    link/ether 9a:60:bc:c8:2a:70 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.1.1.1/24 brd 10.1.1.255 scope global veth1
       valid_lft forever preferred_lft forever

配置test_ns物理机网卡IP
[root@localhost ~]# ifconfig veth0 10.1.1.2/24 up

test_ns物理机ping虚拟空间IP
[root@localhost ~]# ping 10.1.1.1
PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.
64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.095 ms
64 bytes from 10.1.1.1: icmp_seq=2 ttl=64 time=0.037 ms

test_ns虚拟空间ping物理机IP
[root@localhost ~]# ip netns exec test_ns ping 10.1.1.2
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.103 ms
64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.041 ms
^C

test_ns空间的路由
[root@localhost ~]# ip netns exec test_ns route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.1.1.0        0.0.0.0         255.255.255.0   U     0      0        0 veth1
删除空间
[root@localhost ~]# ip netns delete test_ns
[root@localhost ~]# ip netns exec test_ns ip a
Cannot open network namespace "test_ns": No such file or directory

三、小实验


二、Docker—Namespace隔离
实现10网段ping通20网段
开启路由转发
[root@localhost ~]# sysctl -p
net.ipv4.ip_forward = 1
创建两个虚拟空间
[root@localhost ~]# ip netns add test_ns1
[root@localhost ~]# ip netns add test_ns2
开启两个虚拟空间
[root@localhost ~]# ip netns exec test_ns1 ip link set dev lo up
[root@localhost ~]# ip netns exec test_ns2 ip link set dev lo up 
test_ns1创建一堆veth-peer
[root@localhost ~]# ip link add veth1-1 type veth peer name veth1-2
test_ns1虚拟空间指定网卡
[root@localhost ~]# ip link set veth1-2 netns test_ns1
test_ns1虚拟空间网卡veth1-2配置IP
[root@localhost ~]# ip netns exec test_ns1 ifconfig veth1-2 10.1.1.10/24 up
test_ns1物理网卡veth1-1配置IP
[root@localhost ~]# ifconfig veth1-1 10.1.1.20/24
test_ns1空间配置网关
[root@localhost ~]# ip netns exec test_ns1 route add default gw 10.1.1.20

test_ns2创建一堆veth-peer
[root@localhost ~]# ip link add veth2-1 type veth peer name veth2-2
test_ns2虚拟空间指定网卡
[root@localhost ~]# ip link set veth2-2 netns test_ns2
test_ns2虚拟空间网卡veth2-2配置IP
[root@localhost ~]# ip netns exec test_ns2 ifconfig veth2-2 20.1.1.10/24
test_ns2物理网卡2-1配置IP
[root@localhost ~]# ifconfig veth2-1 20.1.1.20/24 up
test_ns2空间配置网关
[root@localhost ~]# ip netns exec test_ns2 route add default gw 20.1.1.20
两个虚拟空间的网卡进行互ping
[root@localhost ~]# ip netns exec test_ns1 ping 20.1.1.10
PING 20.1.1.10 (20.1.1.10) 56(84) bytes of data.
64 bytes from 20.1.1.10: icmp_seq=1 ttl=63 time=0.391 ms
64 bytes from 20.1.1.10: icmp_seq=2 ttl=63 time=0.050 ms
[root@localhost ~]# ip netns exec test_ns2 ping 10.1.1.10
PING 10.1.1.10 (10.1.1.10) 56(84) bytes of data.
64 bytes from 10.1.1.10: icmp_seq=1 ttl=63 time=0.090 ms
64 bytes from 10.1.1.10: icmp_seq=2 ttl=63 time=0.049 ms

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: