03|容器技术基本原理之Namespace

以前被面试或者面试别人的时候,出现频率比较高的一个问题是:看你简历上写熟悉容器技术,那你是怎么理解容器的?还算可以的回答是:容器技术本身不是新的技术,容器本质上就是宿主机操作系统上的一个特殊的进程,利用Namespace和Cgroup技术实现了资源的隔离与限制,并且实现了容器镜像,解决了应用程序分发的问题。答到这里,可以说对容器的理解还是比较全面的了。

Docker的安装:本不想写网上一搜一堆,且不需要理解的东西,但是看好多文章里写的安装过程,多少有点坑,所以还是写一下,安装过程就在ubuntu上操作,因为CentOS7将来也就不维护了,听说ebay生产环境系统也都用ubuntu了。

step1: 更新ubuntu apt-get源

 sudo apt-get update
 
 sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

step2: 添加Docker官方GPG key


 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

step3: 安装Docker稳定版仓库地址

 echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

step4: 安装Docker

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

通常安装完Docker就自动运行了,但是很多人想长期用这个Docker作为一个test或staging环境,那么直接用估计会有问题,Docker默认的存储目录在/var/lib/docker/下,有些主机的这个盘很小,尤其是云主机,用一段时间把系统盘占满了,还得迁走,就很麻烦,不如一步到位,把数据存储目录放到较大的盘上,比如/data/docker/。

step5: 编辑/lib/systemd/system/docker.service文件,在ExecStart选项后加上--data-root=/data/docker,这样就自定义了数据目录,结果如下所示:

ExecStart=/usr/bin/dockerd -H fd:// --data-root=/data/docker --containerd=/run/containerd/containerd.sock --log-level warn --log-opt max-size=100m --log-opt max-file=5

然后重新加载systemd并重启Docker进程即可。

systemctl daemon-reload
systemctl restart docker

step6: 验证容器已经安装成功

# docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

用Docker运行一个Nginx容器:Docker作为工具来用,其实没有什么复杂的东西,日常只要掌握Dockerfile,会用一些基本的命令就可以了,下面以Nginx为基础,制作我们的镜像并运行。

step1: 编写Dockerfile

# 这里指定你的基础镜像,建议写固定的版本号,不然默认的latest更新可能会导致你的代码不兼容
FROM nginx:1.21.3

# 进入容器后的默认目录
WORKDIR /usr/share/nginx/html

# 把index.html内容改成我们想要的内容
RUN echo "This is my first image base nginx!" > index.html

step2: 生成Docker镜像

docker build -t my_nginx:v1.0 -f Dockerfile .

step3: 查看生成的镜像,并使用该镜像启动容器

# docker images
my_nginx     v1.0      5d0547b2cc8d   3 hours ago              133MB

# docker run -d 5d0547b2cc8d

# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS     NAMES
6961dd4fce83   5d0547b2cc8d   "/docker-entrypoint.…"   18 minutes ago   Up 18 minutes   80/tcp    strange_jepsen

step4: 查看容器IP地址,并使用容器IP访问nginx服务,这里我们介绍使用nsenter命令,场景是我们的nginx镜像中并没有ifconfig命令,但是我们又想查看IP地址,这时就可以通过nsenter命令进入到容器的Namespace,并用宿主机上的命令来调整容器。

[root@iZ2zec5wzaupsrdu55oeaiZ docker]# docker inspect 6961dd4fce83 | grep -i pid
            "Pid": 30176,
            "PidMode": "",
            "PidsLimit": null,
[root@iZ2zec5wzaupsrdu55oeaiZ docker]# nsenter -t 30176 -n ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.18.0.2  netmask 255.255.0.0  broadcast 172.18.255.255
        ether 02:42:ac:12:00:02  txqueuelen 0  (Ethernet)
        RX packets 18  bytes 1256 (1.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 14  bytes 1386 (1.3 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@iZ2zec5wzaupsrdu55oeaiZ docker]# curl 172.18.0.2
This is my first image base nginx!

转入正题,细说Namespace: Namespace不是一项新技术,它是内核自带的一项功能,Docker之所以能实现资源隔离,就是调用了内核的Namespace功能。该功能对内核资源进行隔离,使得容器进程都可以在单独的命名空间中运行,并且只可以访问当前容器命名空间的资源。

Linux下的资源类型有很多,那么Namespace就也分为下面几类对资源进行限制:

  • PID namespace:用于隔离进程 ID。
  • Network namespace:隔离网络接口,在虚拟的 net namespace 内用户可以拥有自己独立的 IP、路由、端口等。
  • Mount namespace:文件系统挂载点隔离。
  • IPC namespace:信号量、消息队列和共享内存的隔离。
  • UTS namespace:主机名和域名的隔离。
  • User namespace: 用户和组的隔离。

上面列出了各种资源的隔离,下面就拿 PID namespace 来举例,看看这个在Linux操作系统里是如何实现的,Linux 系统中创建进程的系统调用是 clone(),如下代码所示:

int pid = clone(main_function, stack_size, SIGCHLD, NULL); 

但是一旦我们传入了 CLONE_NEWPID 参数,新创建的进程就进入了一个独立的空间,在这个空间里它自己的PID是1,在外面看来它就是一个正常的进程,进程号可能是xxx这种,但肯定不是1,因为1号进程是操作系统的systemd进程,如下代码就实现了进程隔离:

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL); 

通俗来讲,一个进程在启动后,Namespace就给他一通包装,然后这个进程就在自己的小天地里自由的玩耍了。但齐天大圣终穷逃不出如来的五指山,对于开发者来讲,它仍然是操作系统上的一个进程罢了。

对于大多数人来讲,我们只需要知道Docker是如何实现进程隔离的,如果以后有需要去深入挖掘,就按着操作系统的Namespace功能的方向去找就好了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值