一文扫盲Docker

输麻了,

隔壁组的大哥给实习生妹妹讲Docker

把实习生妹妹忽悠得一愣一愣的,

实习生妹妹看隔壁组大哥那崇拜的小眼神,

我属实是特别嫉妒,

为了扳回一局,

我决定好好整理一下Docker的文档。


前言

本文将对Docker的镜像,Dockerfile的关键指令,Docker的容器进行关键概念总结,整体内容不会很深,但也绝不是停留在指令介绍的层面。

本文将重点分析如下内容。

  1. 镜像的分层结构及大小;
  2. CMD指令和ENTRYPOINT指令的关系;
  3. 如何让容器中的主进程的PID是1;
  4. 容器的分层结构及大小。

参考:Docker官方文档

正文

一. Linux Namespace

1. 概念说明

NamespaceLinux资源隔离方案,用于将某种全局资源通过Namespace进行隔离,使得Namespace下的资源仅由该Namespace下的进程共享。

Linux内核实现了如下六种Namespace

Namespace类型 隔离的资源 容器下的效果
Mount 文件系统挂载点 不同容器中可以看到不同的目录结构
Network 例如网络设备,端口等网络相关资源 容器拥有独立的网络设备,IP地址,端口号等,这就使得同一宿主机上不同容器的进程可以绑定到相同端口上
UTS HostnameDomainname 容器可以拥有自己的主机名和域名
IPC 信号量,消息队列和共享内存等进程间通信资源 不同容器里的进程无法直接进行进程间通信
PID 进程编号 容器中的进程可以有独立的PID,以及容器中的进程会有两个PID:宿主机上一个PID和容器里一个PID
User 用户和用户组 宿主机和容器中可以有两套用户和用户组

2. Docker中的使用

Docker创建一个容器时,Docker就会去创建上述六种Namespace实例,然后容器中的所有进程就会被放到创建出来的Namespace中,从而容器中的进程只能使用当前容器的Namespace下的资源,不同容器之间实现了资源隔离。

二. Linux Control Groups

1. 概念说明

Namespace让容器中的进程拥有了独立的运行环境,现在还需要限制这些进程使用的CPU内存磁盘网络等资源,而cgroupControl Groups)就能实现这样的功能。

cgroupLinux提供的用于将进程分组管理的功能,针对每个组里的进程,提供如下能力。

  1. 限制组里进程使用的CPU核数和使用率;
  2. 限制组里进程使用的内存大小;
  3. 限制组里进程对物理设备的使用,例如磁盘
  4. 为组里进程分配网络带宽

可以进入到宿主机的/sys/fs/cgroup目录,观察到有如下目录。

每个目录都是cgroup可以控制的资源类型,说明如下。

  1. blkio。限制cgroup中的进程对块设备(例如磁盘)的IO速度;
  2. cpu。限制cgroup中的进程对CPU的使用率;
  3. cpuacct。统计cgroup中的进程对CPU的使用率;
  4. cpuset。为cgroup中的进程分配CPU核数;
  5. devices。限制cgroup中的进程使用物理设备的权限;
  6. freezer。挂起或恢复cgroup中的所有进程;
  7. hugetlb。限制cgroup中的进程对大页的使用数量(4KB称为小页,反之例如2MB或1GB称为大页);
  8. memory。限制cgroup中的进程使用的内存大小,并生成使用报告;
  9. net_cls。为cgroup中的进程的所有网络包添加classid标记符号,用于iptables(网络包过滤)和TCTraffic Control,流量控制);
  10. perf_event。监控cgroup中的进程的性能。

2. Docker中的使用

Docker中,启动一个容器后,会在宿主机的/sys/fs/cgroup目录下的相关资源目录下创建以容器id为名字的目录,如下所示。

容器中的进程的PID(容器进程在宿主机中的PID)会被记录到tasks文件中,如下所示。

现在再查看一下cpu.cfs_quota_us,如下所示。

-1表示无限制,可以在使用docker run时通过-c来指定CPU限制相关参数,参数说明如下所示。

三. 镜像Image

镜像,是Docker中的容器的静态表示。镜像中包含着容器运行时的代码运行依赖,是一个多层结构,且每一层都是只读的,所以镜像一旦创建,就无法被修改。

下面将从多个维度来说明镜像的概念。

1. 基础镜像

基础镜像,有如下两个特点。

  1. 不依赖其它镜像
  2. 其它镜像可基于基础镜像做扩展

那么可以联想到,通常基础镜像就是那些系统级镜像例如CentOS镜像,Ubuntu镜像等,这些镜像会作为地基,为容器中的程序运行提供操作系统(下面讨论的基础镜像均指系统级镜像)。

那么现在宿主机上有一个安装好的操作系统,宿主机上运行的一个容器中也有基础镜像提供的操作系统,这两个操作系统是否是一样的呢。说一样也行,说不一样也行,说明如下。

  1. 一样。是因为容器中由镜像提供的操作系统底层使用的内核(Kernel)其实就是宿主机安装好的操作系统的内核。那么试问Linux的虚拟机上能运行使用Windows作为基础镜像的容器吗,那自然是不能的;
  2. 不一样。是因为例如Red Hat系统的虚拟机上,可以运行使用Ubuntu作为基础镜像的容器,容器和宿主机上的Linux是不一样的发行版

Linux的不同发行版本都会采用相同的Linux内核,然后不同的发行版会在Linux内核的基础上加入各自的改动)

既然都能使用宿主机的操作系统内核了,那么为啥还需要基础镜像呢。这里其实就又会涉及到两个概念:bootfsrootfs

  1. bootfs,是Linux启动时需要加载的内容,由内核提供,属于内核空间。所以基础镜像中的操作系统需要依赖宿主机操作系统中的内核所提供的bootfs,才能将容器中的操作系统运行起来;
  2. rootfs,是基本的命令,类库或者工具(例如包管理工具yumapt-get等),属于用户空间。由于容器中的文件系统通常是和宿主机上的文件系统隔离的,所以容器中运行的应用程序是无法访问到宿主机上的相关类库或者工具,基于这样的情况,基础镜像中就需要包含rootfs相关的内容。

到这里,基本就理清了基础镜像中的操作系统和宿主机上的操作系统的关系,简单点说就是:基础镜像中的操作系统是不完整的,缺少内核,需要使用宿主机上的操作系统的内核。(一个Ubuntu镜像才201MB,一个Ubuntu 18.4的安装包2GB,少了啥自己想)

2. 镜像组成

现在通过docker save -o filename.tar imagename:version将一个镜像打为tar并解压,解压后目录如下所示。

每种文件说明如下。

  1. repositories。包含镜像名称,版本号,以及最上层的哈希值;
  2. manifest.json。镜像(假)元数据Json文件,包含镜像真正的元数据文件的文件名,以及镜像每一层的哈希值等;
  3. 各种文件夹。镜像每一层对应的文件夹,是一个镜像真正占用空间大小的内容;
  4. ac0f3e4255c186822ea64dcb267bea7c897d44ace98c61cd3faaffdb9479114f.json。镜像真正的元数据文件。

基于Dockerfile构建出来的镜像,真正占用磁盘空间的层,要么是基础镜像层,要么是使用了ADD,COPY或者RUN指令而构建出来的层,而像ENVCMD等指令,虽然也会构建镜像的层,但其实是一个空层,这些指令的真正作用是会修改构建出来的镜像的元数据。

3. RUN指令对镜像大小的影响

首先上一张图。

Dockerfile中要基于ubuntu:18.04基础镜像来构建镜像,其中会通过RUN指令执行两次api-get update,假设每次执行都会更改到ubuntu:18.04基础镜像中的File1</

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值