├── .DS_Store ├── 1.Docker技术原理.md ├── 10.Docker底层资源限制cgroups.md ├── 11.Harbor私有仓库搭建.md ├── 2.Docker容器、镜像、仓库.md ├── 3.Docker镜像配置与原理.md ├── 4.Docker容器的使用.md ├── 5.Dockerfile的编写规则.md ├── 6.docker架构设计.md ├── 7.Docker安全性能了解.md ├── 8.Docker安全性能提升.md ├── 9.Docker底层资源隔离namespace.md ├── README.md ├── images ├── .DS_Store ├── 11Docker-compose安装.png ├── 2docker-架构图.png ├── 2docker-组件.png ├── 2docker-进程数机构1.png ├── 2docker容器关系图.png ├── 3docker-bootfs-rootfs.png ├── 3docker-bootfs.png ├── 3docker-container1.png ├── 3docker-container2.png ├── 3docker-kernel.png ├── 3docker-镜像分层.png ├── 3docker-镜像操作.png ├── 4docker-容器层.png ├── 4docker-容器状态.png ├── 6.Docker架构.png ├── 7.docker与虚拟机的区别.png ├── 8.Docker with Kata架构设计.png ├── 8.docker的runtime架构图.png ├── 8.docker的runtime架构图2.png ├── 8.kata-container原理png.png ├── 9.Namespaces类型.png ├── cgroups-cpu-full-top.png ├── cgroups-cpu-quota-us.png ├── cgroups-cpu-top.png ├── cgroups-memory.png ├── cgroups1.png ├── cgroups2.png ├── chroot1.png ├── docker1.png ├── k8s架构图.jpg └── logo.png └── 实战演示 ├── .DS_Store ├── Docker-Compose编排技术.md └── images └── 1.proc文件.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/.DS_Store -------------------------------------------------------------------------------- /1.Docker技术原理.md: -------------------------------------------------------------------------------- 1 | # Docker技术原理 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-04 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏:> 18 | 19 | ------ 20 | 21 | #### **先聊聊Docker 的基础内容:Docker 能做什么,怎么安装 Docker,以及容器技术的原理。** 22 | 23 | ### 1. Docker 能做什么? 24 | 25 | ​ 众所周知,Docker 是一个用于开发,发布和运行应用程序的开放平台。通俗地讲,Docker 类似于集装箱。在一艘大船上,各种货物要想被整齐摆放并且相互不受到影响,我们就需要把各种货物进行集装箱标准化。有了集装箱,我们就不需要专门运输水果或者化学用品的船了。我们可以把各种货品通过集装箱打包,然后统一放到一艘船上运输。Docker 要做的就是把各种软件打包成一个集装箱(镜像),然后分发,且在运行的时候可以相互隔离。 26 | 27 | 到此,相信你已经迫不及待想要体验下了,下面就让我们来安装一个 Docker。 28 | 29 | ![](./images/docker1.png) 30 | 31 | #### **2. CentOS 下安装 Docker** 32 | 33 | ​ Docker 是跨平台的解决方案,它支持在当前主流的各大平台安装,包括 Ubuntu、RHEL、CentOS、Debian 等 Linux 发行版,同时也可以在 OSX 、Microsoft Windows 等非 Linux 平台下安装使用。 34 | 35 | ​ 因为 Linux 是 Docker 的原生支持平台,所以推荐你在 Linux 上使用 Docker。由于生产环境中我们使用 CentOS 较多,下面主要针对在 CentOS 平台下安装和使用 Docker 展开介绍。 36 | 37 | ##### 2.1 操作系统要求 38 | 39 | 要安装 Docker,我们需要 CentOS 7 及以上的发行版本。 40 | 41 | ##### 2.2 卸载已有 Docker 42 | 如果你已经安装过旧版的 Docker,可以先执行以下命令卸载旧版 Docker。 43 | 44 | ``` 45 | $ sudo yum remove docker \ 46 | docker-client \ 47 | docker-client-latest \ 48 | docker-common \ 49 | docker-latest \ 50 | docker-latest-logrotate \ 51 | docker-logrotate \ 52 | docker-engine 53 | ``` 54 | 55 | ##### 2.3 安装 Docker 56 | 57 | 首次安装 Docker 之前,需要添加 Docker 安装源。添加之后,我们就可以从已经配置好的源,安装和更新 Docker。添加 Docker 安装源的命令如下: 58 | 59 | ``` 60 | $ yum -y install yum-utils 61 | $ sudo yum-config-manager \ 62 | --add-repo \ 63 | https://download.docker.com/linux/centos/docker-ce.repo 64 | ``` 65 | 66 | 正常情况下,直接安装最新版本的 Docker 即可,因为最新版本的 Docker 有着更好的稳定性和安全性。你可以使用以下命令安装最新版本的 Docker。 67 | 68 | ``` 69 | $ sudo yum install docker-ce docker-ce-cli containerd.io 70 | ``` 71 | 72 | 安装完成后,使用以下命令启动 Docker。 73 | 74 | ``` 75 | $ sudo systemctl start docker 76 | ``` 77 | 78 | ##### 2.4 启动一个 hello world 的容器 79 | 80 | ``` 81 | $ sudo docker run hello-world 82 | Unable to find image 'hello-world:latest' locally 83 | latest: Pulling from library/hello-world 84 | 0e03bdcc26d7: Pull complete 85 | Digest: sha256:7f0a9f93b4aa3022c3a4c147a449bf11e0941a1fd0bf4a8e6c9408b2600777c5 86 | Status: Downloaded newer image for hello-world:latest 87 | Hello from Docker! 88 | ``` 89 | 90 | 运行上述命令,Docker 首先会检查本地是否有`hello-world`这个镜像,如果发现本地没有这个镜像,Docker 就会去 **Docker Hub** 官方仓库下载此镜像,然后运行它。最后我们看到该镜像输出 "Hello from Docker!" 并退出。 91 | 92 | > 安装完成后默认 docker 命令只能以 root 用户执行,如果想允许普通用户执行 docker 命令,需要执行以下命令 sudo groupadd docker && sudo gpasswd -a ${USER} docker && sudo systemctl restart docker ,执行完命令后,退出当前命令行窗口并打开新的窗口即可。 93 | 94 | 安装完 Docker,先不着急使用,先来了解下容器的技术原理,这样才能知其所以然。 95 | 96 | ### 3. 容器技术原理 97 | 98 | 必须先提一提chroot,因为 chroot 是最早的容器雏形。chroot 意味着切换根目录,有了 chroot 就意味着我们可以把任何目录更改为当前进程的根目录,这与容器非常相似,下面我们通过一个实例了解下 chroot。 99 | 100 | #### 3.1 chroot 即 change root directory (更改 root 目录) 101 | 102 | ​ 什么是 chroot 呢?下面是 chroot 维基百科定义: 103 | 104 | > chroot 是在 Unix 和 Linux 系统的一个操作,针对正在运作的软件行程和它的子进程,改变它外显的根目录。一个运行在这个环境下,经由 chroot 设置根目录的程序,它不能够对这个指定根目录之外的文件进行访问动作,不能读取,也不能更改它的内容。 105 | 106 | ​ 通俗地说 ,chroot 就是可以改变某进程的根目录,使这个程序不能访问目录之外的其他目录,这个跟我们在一个容器中是很相似的。 107 | 108 | ​ 在经过 chroot 之后,系统读取到的目录和文件将不在是旧系统根下的而是新根下(即被指定的新的位置)的目录结构和文件,因此它带来的好处大致有以下3个: 109 | 110 | 1. 增加了系统的安全性,限制了用户的权力; 111 | 112 | 在经过 chroot 之后,在新根下将访问不到旧系统的根目录结构和文件,这样就增强了系统的安全性。这个一般是在登录 (login) 前使用 chroot,以此达到用户不能访问一些特定的文件。 113 | 114 | 2. 建立一个与原系统隔离的系统目录结构,方便用户的开发; 115 | 116 | 使用 chroot 后,系统读取的是新根下的目录和文件,这是一个与原系统根下文件不相关的目录结构。在这个新的环境中,可以用来测试软件的静态编译以及一些与系统不相关的独立开发。 117 | 118 | 3. 切换系统的根目录位置,引导 Linux 系统启动以及急救系统等。 119 | 120 | chroot 的作用就是切换系统的根位置,而这个作用最为明显的是在系统初始引导磁盘的处理过程中使用,从初始 RAM 磁盘 (initrd) 切换系统的根位置并执行真正的 init。另外,当系统出现一些问题时,我们也可以使用 chroot 来切换到一个临时的系统。 121 | 122 | 123 | 124 | 下面我们**通过一个实例来演示下 chroot。** 125 | 126 | 首先我们在当前/home目录下创建一个 rootfs 目录: 127 | 128 | ``` 129 | $ mkdir rootfs 130 | ``` 131 | 132 | 这里为了方便演示,我使用现成的 busybox 镜像来创建一个系统,镜像的概念和组成后面我会详细讲解,如果你没有 Docker 基础可以把下面的操作命令理解成在 rootfs 下创建了一些目录和放置了一些二进制文件。 133 | 134 | 复制 135 | 136 | ``` 137 | $ cd rootfs 138 | $ docker export $(docker create busybox) -o busybox.tar 139 | $ tar -xf busybox.tar 140 | ``` 141 | 142 | 执行完上面的命令后,在 rootfs 目录下,我们会得到一些目录和文件。下面我们使用 ls 命令查看一下 rootfs 目录下的内容。 143 | 144 | 复制 145 | 146 | ``` 147 | $ ls 148 | bin busybox.tar dev etc home proc root sys tmp usr var 149 | ``` 150 | 151 | 可以看到我们在 rootfs 目录下初始化了一些目录,下面让我们通过一条命令来见证 chroot 的神奇之处。使用以下命令,可以启动一个 sh 进程,并且把 /home/centos/rootfs 作为 sh 进程的根目录。 152 | 153 | 复制 154 | 155 | ``` 156 | $ chroot /home/rootfs /bin/sh 157 | ``` 158 | 159 | 此时,我们的命令行窗口已经处于上述命令启动的 sh 进程中。在当前 sh 命令行窗口下,我们使用 ls 命令查看一下当前进程,看是否真的与主机上的其他目录隔离开了 160 | 161 | ``` 162 | / # ls / 163 | bin busybox.tar dev etc home proc root sys tmp usr var 164 | ``` 165 | 166 | 这里可以看到当前进程的根目录已经变成了主机上的 /home/rootfs 目录。这样就实现了当前进程与主机的隔离。到此为止,一个目录隔离的容器就完成了。操作图解如下图所示: 167 | 168 | ![](images/chroot1.png) 169 | 170 | 但是,此时还不能称之为一个容器,为什么呢?你可以在上一步(使用 chroot 启动命令行窗口)执行以下命令,查看如下路由信息: 171 | 172 | ``` 173 | /etc # ip route 174 | default via 172.20.1.1 dev eth0 175 | 172.17.0.0/16 dev docker0 scope link src 172.17.0.1 176 | 172.20.1.0/24 dev eth0 scope link src 172.20.1.3 177 | ``` 178 | 179 | 执行 ip route 命令后,你可以看到网络信息并没有隔离,实际上进程等信息此时也并未隔离。要想实现一个完整的容器,我们还需要 Linux 的其他三项技术: Namespace、Cgroups 和联合文件系统。 180 | 181 | Docker 是利用 Linux 的 Namespace 、Cgroups 和联合文件系统三大机制来保证实现的, 所以它的原理是使用 Namespace 做主机名、网络、PID 等资源的隔离,使用 Cgroups 对进程或者进程组做资源(例如:CPU、内存等)的限制,联合文件系统用于镜像构建和容器运行环境。 182 | 183 | 下面简单解释下它们的作用 184 | 185 | #### **3.2 Namespace** 186 | 187 | ​ Namespace 是 Linux 内核的一项功能,该功能对内核资源进行隔离,使得容器中的进程都可以在单独的命名空间中运行,并且只可以访问当前容器命名空间的资源。Namespace 可以隔离进程 ID、主机名、用户 ID、文件名、网络访问和进程间通信等相关资源。 188 | 189 | Docker 主要用到以下五种命名空间。 190 | 191 | - pid namespace:用于隔离进程 ID。 192 | - net namespace:隔离网络接口,在虚拟的 net namespace 内用户可以拥有自己独立的 IP、路由、端口等。 193 | - mnt namespace:文件系统挂载点隔离。 194 | - ipc namespace:信号量,消息队列和共享内存的隔离。 195 | - uts namespace:主机名和域名的隔离。 196 | 197 | #### 3.3 Cgroups 即:Linux Control Group 198 | 199 | ​ Cgroups 是一种 Linux 内核功能,可以限制和隔离进程的资源使用情况(CPU、内存、磁盘 I/O、网络等)。在容器的实现中,Cgroups 通常用来限制容器的 CPU 和内存等资源的使用。 200 | 201 | 参考地址: 202 | 203 | 首先,Linux把CGroup这个事实现成了一个file system,你可以mount。在我的Ubuntu 14.04下,你输入以下命令你就可以看到cgroup已为你mount好了。如下图所示: 204 | 205 | ![](images/cgroups1.png) 206 | 207 | 在**/sys/fs**下有一个cgroup的目录,这个目录下还有很多子目录,比如: cpu,cpuset,memory,blkio……这些,这些都是cgroup的子系统。分别用于干不同的事的。 208 | 209 | #### CPU限制 210 | 211 | **1) 先创建一个test.py文件** 212 | 213 | ```python 214 | def main(): 215 | i = 0 216 | while True: 217 | i += 1 218 | 219 | if __name__ == '__main__': 220 | 221 | main() 222 | ``` 223 | 224 | **2)执行test.py文件,并查询内存使用情况** 225 | 226 | ![](images/cgroups2.png) 227 | 228 | **3) 限制cpu使用效率** 229 | 230 | ![](images/cgroups-cpu-quota-us.png) 231 | 232 | 在/sys/fs/cgroup/cpu下创建了一个coco,而且你会发现,一旦你创建了一个子目录,这个子目录里又有很多文件了。其中**cpu.cfs_quota_us**表示该control group限制占用的时间(微秒),默认为-1,表示不限制。如果设为50000,表示占用50000/10000=50%的CPU。 233 | 234 | 这里,我们设置占用20%的CPU,即把cpu.cfs_quota_us设置为20000。执行命令就是: 235 | 236 | ``` 237 | echo 20000 > /sys/fs/cgroup/cpu/coco/cpu.cfs_quota_us 238 | ``` 239 | 240 | 然后执行死循环test.py文件,瞬间cpu就会跑到100%。如下图所示: 241 | 242 | ![](images/cgroups-cpu-full-top.png) 243 | 244 | 我们看到,这个进程的PID是4897,我们把这个进程加到这个cgroup中: 245 | 246 | ``` 247 | echo 4897 >> /sys/fs/cgroup/cpu/coco/tasks 248 | ``` 249 | 250 | 然后,就会在top中看到CPU的利用立马下降成20%了。(前面我们设置的20000就是20%的意思) 251 | 252 | 253 | 254 | **4) 查看内存使用情况** 255 | 256 | ![](images/cgroups-cpu-top.png) 257 | 258 | 259 | 260 | #### 内存限制 261 | 262 | ##### 1)创建test1.py文件 263 | 264 | ``` 265 | def main(): 266 | i = [] 267 | while True: 268 | i.append(1) 269 | 270 | 271 | if __name__ == '__main__': 272 | 273 | main() 274 | ``` 275 | 276 | 想要的效果为:i变量一直新增元素,导致i的内存空间会一直增大。 277 | 278 | **2) 执行test1.py文件,并通过top观察执行消耗内存情况** 279 | 280 | 执行test1.py文件,获取到进程PID 281 | 282 | ```Python 283 | python test1.py & 284 | ``` 285 | 286 | 然后可以通过top查看到对应进程的内存使用情况。 287 | 288 | **3)限制内存使用情况** 289 | 290 | ![](images/cgroups-memory.png) 291 | 292 | 在/sys/fs/cgroup/memory下创建了一个coco,而且你会发现,一旦你创建了一个子目录,这个子目录里又有很多文件了。其中**memory.limit_in_bytes** 或 **memory.memsw.limit_in_bytes** 限制进程内存占用的大小,避免在进程异常时耗尽系统资源 293 | 294 | 这里,我们设置占用300k的内存使用量,即把memory.limit_in_bytes设置为300k。执行命令就是: 295 | 296 | ``` 297 | echo 300k > /sys/fs/cgroup/memory/coco/memory.limit_in_bytes 298 | ``` 299 | 300 | 然后再把需要限制内存大小的进程加到这个cgroup中: 301 | 302 | ``` 303 | echo 23541 >> /sys/fs/cgroup/memory/coco/tasks 304 | ``` 305 | 306 | 最后你会看到,一会上面PID为 23541的进程将会被kill掉。 307 | 308 | **4)查看进程** 309 | 310 | 从上图中可以看出,当执行kill -9 23541时,提示无法找到PID为23541的进程。那就说明当PID为23541的进程消耗的内存超过了300k时,将主动被kill掉了。 311 | 312 | 313 | 314 | #### 3.4 联合文件系统 315 | 316 | ​ 联合文件系统,又叫 UnionFS,是一种通过创建文件层进程操作的文件系统,因此,联合文件系统非常轻快。Docker 使用联合文件系统为容器提供构建层,使得容器可以实现写时复制以及镜像的分层构建和存储。常用的联合文件系统有 AUFS、Overlay 和 Devicemapper 等。 -------------------------------------------------------------------------------- /10.Docker底层资源限制cgroups.md: -------------------------------------------------------------------------------- 1 | # Docker资源限制:Cgroups 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-28 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ### 1. 前言 20 | 21 | 22 | 23 | Cgroups 是一种 Linux 内核功能,可以限制和隔离进程的资源使用情况(CPU、内存、磁盘 I/O、网络等)。在容器的实现中,Cgroups 通常用来限制容器的 CPU 和内存等资源的使用。 24 | 25 | 参考地址: 26 | 27 | 首先,Linux把CGroup这个事实现成了一个file system,你可以mount。在我的Ubuntu 14.04下,你输入以下命令你就可以看到cgroup已为你mount好了。如下图所示: 28 | 29 | ![](images/cgroups1.png) 30 | 31 | 在**/sys/fs**下有一个cgroup的目录,这个目录下还有很多子目录,比如: cpu,cpuset,memory,blkio……这些,这些都是cgroup的子系统。分别用于干不同的事的。 32 | 33 | #### CPU限制 34 | 35 | **1) 先创建一个test.py文件** 36 | 37 | ```python 38 | def main(): 39 | i = 0 40 | while True: 41 | i += 1 42 | 43 | if __name__ == '__main__': 44 | 45 | main() 46 | ``` 47 | 48 | **2)执行test.py文件,并查询内存使用情况** 49 | 50 | ![](images/cgroups2.png) 51 | 52 | **3) 限制cpu使用效率** 53 | 54 | ![](images/cgroups-cpu-quota-us.png) 55 | 56 | 在/sys/fs/cgroup/cpu下创建了一个coco,而且你会发现,一旦你创建了一个子目录,这个子目录里又有很多文件了。其中**cpu.cfs_quota_us**表示该control group限制占用的时间(微秒),默认为-1,表示不限制。如果设为50000,表示占用50000/10000=50%的CPU。 57 | 58 | 这里,我们设置占用20%的CPU,即把cpu.cfs_quota_us设置为20000。执行命令就是: 59 | 60 | ``` 61 | echo 20000 > /sys/fs/cgroup/cpu/coco/cpu.cfs_quota_us 62 | ``` 63 | 64 | 然后执行死循环test.py文件,瞬间cpu就会跑到100%。如下图所示: 65 | 66 | ![](images/cgroups-cpu-full-top.png) 67 | 68 | 我们看到,这个进程的PID是4897,我们把这个进程加到这个cgroup中: 69 | 70 | ``` 71 | echo 4897 >> /sys/fs/cgroup/cpu/coco/tasks 72 | ``` 73 | 74 | 然后,就会在top中看到CPU的利用立马下降成20%了。(前面我们设置的20000就是20%的意思) 75 | 76 | 77 | 78 | **4) 查看内存使用情况** 79 | 80 | ![](images/cgroups-cpu-top.png) 81 | 82 | 83 | 84 | #### 内存限制 85 | 86 | ##### 1)创建test1.py文件 87 | 88 | ``` 89 | def main(): 90 | i = [] 91 | while True: 92 | i.append(1) 93 | 94 | 95 | if __name__ == '__main__': 96 | 97 | main() 98 | ``` 99 | 100 | 想要的效果为:i变量一直新增元素,导致i的内存空间会一直增大。 101 | 102 | **2) 执行test1.py文件,并通过top观察执行消耗内存情况** 103 | 104 | 执行test1.py文件,获取到进程PID 105 | 106 | ```Python 107 | python test1.py & 108 | ``` 109 | 110 | 然后可以通过top查看到对应进程的内存使用情况。 111 | 112 | **3)限制内存使用情况** 113 | 114 | ![](images/cgroups-memory.png) 115 | 116 | 在/sys/fs/cgroup/memory下创建了一个coco,而且你会发现,一旦你创建了一个子目录,这个子目录里又有很多文件了。其中**memory.limit_in_bytes** 或 **memory.memsw.limit_in_bytes** 限制进程内存占用的大小,避免在进程异常时耗尽系统资源 117 | 118 | 这里,我们设置占用300k的内存使用量,即把memory.limit_in_bytes设置为300k。执行命令就是: 119 | 120 | ``` 121 | echo 300k > /sys/fs/cgroup/memory/coco/memory.limit_in_bytes 122 | ``` 123 | 124 | 然后再把需要限制内存大小的进程加到这个cgroup中: 125 | 126 | ``` 127 | echo 23541 >> /sys/fs/cgroup/memory/coco/tasks 128 | ``` 129 | 130 | 最后你会看到,一会上面PID为 23541的进程将会被kill掉。 131 | 132 | **4)查看进程** 133 | 134 | 从上图中可以看出,当执行kill -9 23541时,提示无法找到PID为23541的进程。那就说明当PID为23541的进程消耗的内存超过了300k时,将主动被kill掉了。 135 | 136 | -------------------------------------------------------------------------------- /11.Harbor私有仓库搭建.md: -------------------------------------------------------------------------------- 1 | # Docker-Harbor:私有仓库搭建 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2021-01-04 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ### 1. 前言 20 | 21 | ​ Harbor是Vmvare中国团队开发的开源registry仓库,相比docker官方拥有更丰富的权限权利和完善的架构设计,适用大规模docker集群部署提供仓库服务。 22 | 23 | 24 | 25 | #### 2. 环境准备 26 | 27 | 1) Harbor是通过docker的compose项目部署的,需要安装compose,幸好compost 在git上提供了安装指令: 28 | 29 | ``` 30 | # 下载docker-compose 31 | curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose 32 | 33 | # 设置执行权限 34 | chmod +x /usr/local/bin/docker-compose #设置执行权限 35 | 36 | #查看安装是否程成功 37 | docker-compose --version 38 | ``` 39 | 40 |   这是2条命令,先执行curl再执行chmod指令,下载过程可能会出现网络问题,多尝试几次即可。 41 | 42 | ![](images\11Docker-compose安装.png) 43 | 44 | 45 | 46 | 2) Harbor软件安装 47 | 48 | ``` 49 | #下载离线安装软件 50 | wget http://harbor.orientsoft.cn/harbor-v1.3.0-rc4/harbor-offline-installer-v1.3.0-rc4.tgz 51 | 52 | #解压文件 53 | tar -zxf harbor-offline-installer-v1.3.0-rc4.tgz #解压后的文件夹是harbor 54 | ``` 55 | 56 | -------------------------------------------------------------------------------- /2.Docker容器、镜像、仓库.md: -------------------------------------------------------------------------------- 1 | # Docker技术原理 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-05 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ------ 20 | 21 | Docker 的操作围绕镜像、容器、仓库三大核心概念。在学架构设计之前,我们需要先了解 Docker 的三个核心概念。 22 | 23 | ### 1. Docker 核心概念 24 | 25 | #### 1.1 镜像 26 | 27 | 镜像是什么呢?通俗地讲,它是一个只读的文件和文件夹组合。它包含了容器运行时所需要的所有基础文件和配置信息,是容器启动的基础。所以你想启动一个容器,那首先必须要有一个镜像。**镜像是 Docker 容器启动的先决条件。** 28 | 29 | 如果你想要使用一个镜像,你可以用这两种方式: 30 | 31 | 1. 自己创建镜像。通常情况下,一个镜像是基于一个基础镜像构建的,你可以在基础镜像上添加一些用户自定义的内容。例如你可以基于`centos`镜像制作你自己的业务镜像,首先安装`nginx`服务,然后部署你的应用程序,最后做一些自定义配置,这样一个业务镜像就做好了。 32 | 2. 从功能镜像仓库拉取别人制作好的镜像。一些常用的软件或者系统都会有官方已经制作好的镜像,例如`nginx`、`ubuntu`、`centos`、`mysql`等,你可以到 [Docker Hub](https://hub.docker.com/) 搜索并下载它们。 33 | 34 | #### 1.2 容器 35 | 36 | 容器是什么呢?容器是 Docker 的另一个核心概念。通俗地讲,容器是镜像的运行实体。镜像是静态的只读文件,而容器带有运行时需要的可写文件层,并且容器中的进程属于运行状态。即**容器运行着真正的应用进程。容器有初建、运行、停止、暂停和删除五种状态。** 37 | 38 | 虽然容器的本质是主机上运行的一个进程,但是容器有自己独立的命名空间隔离和资源限制。也就是说,在容器内部,无法看到主机上的进程、环境变量、网络等信息,这是容器与直接运行在主机上进程的本质区别。 39 | 40 | #### 1.3 仓库 41 | 42 | Docker 的镜像仓库类似于代码仓库,用来存储和分发 Docker 镜像。镜像仓库分为公共镜像仓库和私有镜像仓库。 43 | 44 | 目前,[Docker Hub](https://hub.docker.com/) 是 Docker 官方的公开镜像仓库,它不仅有很多应用或者操作系统的官方镜像,还有很多组织或者个人开发的镜像供我们免费存放、下载、研究和使用。除了公开镜像仓库,你也可以构建自己的私有镜像仓库,在第 5 课时,我会带你搭建一个私有的镜像仓库。 45 | 46 | #### 1.4 镜像、容器、仓库,三者之间的联系 47 | 48 | ![Drawing 1.png](images/2docker容器关系图.png) 49 | 50 | 可以看到,镜像是容器的基石,容器是由镜像创建的。一个镜像可以创建多个容器,容器是镜像运行的实体。仓库就非常好理解了,就是用来存放和分发镜像的。 51 | 52 | 了解了 Docker 的三大核心概念,接下来认识下 Docker 的核心架构和一些重要的组件。 53 | 54 | # 2. Docker 架构 55 | 56 | 在了解 Docker 架构前,我先说下相关的背景知识——容器的发展史。 57 | 58 | 容器技术随着 Docker 的出现变得炙手可热,所有公司都在积极拥抱容器技术。此时市场上除了有 Docker 容器,还有很多其他的容器技术,比如 CoreOS 的 rkt、lxc 等。容器技术百花齐放是好事,但也出现了很多问题。比如容器技术的标准到底是什么?容器标准应该由谁来制定? 59 | 60 | 也许你可能会说, Docker 已经成为了事实标准,把 Docker 作为容器技术的标准不就好了?事实并没有想象的那么简单。因为那时候不仅有容器标准之争,编排技术之争也十分激烈。当时的编排技术有三大主力,**分别是 Docker Swarm、Kubernetes 和 Mesos** 。Swarm 毋庸置疑,肯定愿意把 Docker 作为唯一的容器运行时,但是 Kubernetes 和 Mesos 就不同意了,因为它们不希望调度的形式过度单一。 61 | 62 | 在这样的背景下,最终爆发了容器大战,`OCI`也正是在这样的背景下应运而生。 63 | 64 | **`OCI`全称为开放容器标准(Open Container Initiative)**,它是一个轻量级,开放的治理结构。`OCI`组织在 Linux 基金会的大力支持下,于 2015 年 6 月份正式注册成立。基金会旨在为用户围绕工业化容器的格式和镜像运行时,制定一个开放的容器标准。目前主要有两个标准文档:**容器运行时标准 (runtime spec)和容器镜像标准(image spec)**。 65 | 66 | 正是由于容器的战争,才导致 Docker 不得不在战争中改变一些技术架构。最终形成了下图所示的技术架构。 67 | 68 | ![](images/2docker-架构图.png) 69 | 70 | ​ Docker 架构图 71 | 72 | 73 | 74 | ​ 我们可以看到,Docker 整体架构采用 **C/S(客户端 / 服务器)模式**,主要由客户端和服务端两大部分组成。客户端负责发送操作指令,服务端负责接收和处理指令。客户端和服务端通信有多种方式,即可以在同一台机器上通过`UNIX`套接字通信,也可以通过网络连接远程通信。 75 | 76 | 下面我逐一介绍客户端和服务端。 77 | 78 | #### 2.1 Docker 客户端 79 | 80 | ​ Docker 客户端其实是一种泛称。其中 **docker 命令是 Docker 用户与 Docker 服务端交互的主要方式**。除了使用 docker 命令的方式,还可以使用**直接请求 REST API 的方式与 Docker 服务端交互**,甚至还可以使用各种语言的 SDK 与 Docker 服务端交互。目前社区维护着 Go、Java、Python、PHP 等数十种语言的 SDK,足以满足你的日常需求。如**Python中的docker-py库**。 81 | 82 | #### 2.2 Docker 服务端 83 | 84 | ​ Docker 服务端是 Docker 所有后台服务的统称。其中 **dockerd 是一个非常重要的后台管理进程**,默认的配置文件为/etc/docker/daemon.json。它负责响应和处理来自 Docker 客户端的请求,然后**将客户端的请求转化为 Docker 的具体操作**。例如镜像、容器、网络和挂载卷等具体对象的操作和管理。 85 | 86 | ​ Docker 从诞生到现在,服务端经历了多次架构重构。起初,服务端的组件是全部集成在 docker 二进制里。但是从 1.11 版本开始, dockerd 已经成了独立的二进制,此时的容器也不是直接由 dockerd 来启动了,而是集成了 containerd、runC 等多个组件。 87 | 88 | ​ 虽然 Docker 的架构在不停重构,但是各个模块的基本功能和定位并没有变化。它和一般的 C/S 架构系统一样,Docker 服务端模块负责和 Docker 客户端交互,并管理 Docker 的容器、镜像、网络等资源。 89 | 90 | #### 2.3 Docker 重要组件 91 | 92 | 看下 Docker 都有哪些工具和组件。在 Docker 安装路径下**/usr/bin** 中执行 ls 命令可以看到以下与 docker 有关的二进制文件。(containerd、containerd-shim、ctr、docker、docker-init、docker-proxy、dockerd、runc) 93 | 94 | ![](images/2docker-组件.png) 95 | 96 | 97 | 先简单介绍一下 Docker 的两个至关重要的组件:`runc`和`containerd`。 98 | 99 | - `runc`是 Docker 官方按照 OCI 容器运行时标准的一个实现。通俗地讲,runc是一个用来运行容器的轻量级工具,是真正用来运行容器的。 100 | - `containerd`是 Docker 服务端的一个核心组件,它是从`dockerd`中剥离出来的 ,它的诞生完全遵循 OCI 标准,是容器标准化后的产物。`containerd`通过 containerd-shim 启动并管理 runc,**可以说`containerd`真正管理了容器的生命周期。** 101 | 102 | ##### **首先谈谈dockerd,containerd,docker-shim,runc,每个组件是用来干嘛的** 103 | 104 | **2.3.1)dockerd** 105 | ​ dockerd本身实属是对容器相关操作的api的最上层封装,直接面向操作用户。 106 | 107 | **2.3.2)containerd** 108 | ​ dockerd实际真实调用的还是containerd的api接口(rpc方式实现),containerd是dockerd和runc之间的一个中间交流组件。 109 | 110 | **2.3.3)containerd-shim** 111 | ​ containerd-shim是一个真实**运行的容器的真实垫片载体**,每启动一个容器都会起一个新的containerd-shim的一个进程,他直接通过指定的三个参数:容器id,boundle目录(containerd的对应某个容器生成的目录,一般位于:/var/run/docker/libcontainerd/containerID),运行是二进制(默认为runc)来调用runc的api创建一个容器(比如创建容器:最后拼装的命令如下:runc create) 112 | 113 | **2.3.4)runc** 114 | ​ runc是一个命令行工具端,他根据`OCI`(开放容器组织)的标准来创建和运行容器。 115 | 116 | ### 3. Docker 各组件之间的关系 117 | 118 | 首先通过以下命令来启动一个 busybox 容器: 119 | 120 | ``` 121 | docker run -d busybox sleep 3600 122 | ``` 123 | 124 | 为了验证Docker 各组件之间的调用关系,下面使用 pstree 命令查看一下进程父子关系: 125 | 126 | ``` 127 | systemd(1)─┬─Xvfb(17214)─┬─{llvmpipe-0}(18510) 128 | │ ├─{llvmpipe-1}(18511) 129 | │ ├─{llvmpipe-2}(18512) 130 | │ └─{llvmpipe-3}(18513) 131 | ├─accounts-daemon(1327)─┬─{gdbus}(1495) 132 | │ └─{gmain}(1492) 133 | ├─acpid(1203) 134 | ├─agetty(23750) 135 | ├─atd(1223,daemon) 136 | ├─containerd(22211)─┬─containerd-shim(8473)─┬─sleep(8491) 137 | │ │ ├─{containerd-shim}(8474) 138 | │ │ ├─{containerd-shim}(8475) 139 | │ │ ├─{containerd-shim}(8476) 140 | │ │ ├─{containerd-shim}(8477) 141 | │ │ ├─{containerd-shim}(8478) 142 | │ │ ├─{containerd-shim}(8479) 143 | │ │ ├─{containerd-shim}(8480) 144 | │ │ └─{containerd-shim}(8482) 145 | │ ├─{containerd}(22216) 146 | │ ├─{containerd}(22217) 147 | │ ├─{containerd}(22218) 148 | │ ├─{containerd}(22219) 149 | │ ├─{containerd}(22220) 150 | │ ├─{containerd}(22227) 151 | │ ├─{containerd}(22228) 152 | │ ├─{containerd}(22229) 153 | │ ├─{containerd}(22231) 154 | │ ├─{containerd}(22232) 155 | │ ├─{containerd}(22233) 156 | │ ├─{containerd}(22234) 157 | │ ├─{containerd}(22369) 158 | │ ├─{containerd}(22375) 159 | │ ├─{containerd}(23023) 160 | │ ├─{containerd}(24119) 161 | │ ├─{containerd}(31016) 162 | │ ├─{containerd}(32704) 163 | │ ├─{containerd}(7339) 164 | │ ├─{containerd}(23491) 165 | │ ├─{containerd}(23596) 166 | │ ├─{containerd}(23889) 167 | │ ├─{containerd}(23894) 168 | │ ├─{containerd}(24535) 169 | │ ├─{containerd}(31378) 170 | │ ├─{containerd}(32403) 171 | │ ├─{containerd}(32404) 172 | │ ├─{containerd}(15398) 173 | │ ├─{containerd}(15399) 174 | │ ├─{containerd}(15805) 175 | │ └─{containerd}(16319) 176 | ├─cron(20629) 177 | ├─dbus-daemon(1227,messagebus) 178 | ├─dhclient(1023) 179 | ├─dockerd(6901)─┬─{dockerd}(6903) 180 | │ ├─{dockerd}(6904) 181 | │ ├─{dockerd}(6905) 182 | │ ├─{dockerd}(6906) 183 | │ ├─{dockerd}(6907) 184 | │ ├─{dockerd}(6908) 185 | │ ├─{dockerd}(6909) 186 | │ ├─{dockerd}(6910) 187 | │ ├─{dockerd}(6912) 188 | │ ├─{dockerd}(6913) 189 | │ ├─{dockerd}(6914) 190 | │ └─{dockerd}(6915) 191 | ├─irqbalance(1468) 192 | ├─iscsid(1140) 193 | ├─iscsid(1141) 194 | ├─lvmetad(479) 195 | ├─lxcfs(1215)─┬─{lxcfs}(1337) 196 | │ ├─{lxcfs}(1340) 197 | │ ├─{lxcfs}(9044) 198 | │ ├─{lxcfs}(23720) 199 | │ └─{lxcfs}(9313) 200 | ├─mdadm(1453) 201 | ├─mysqld(31859,mysql)─┬─{mysqld}(31871) 202 | │ ├─{mysqld}(31872) 203 | │ ├─{mysqld}(31873) 204 | │ ├─{mysqld}(31874) 205 | │ ├─{mysqld}(31875) 206 | │ ├─{mysqld}(31876) 207 | │ ├─{mysqld}(31877) 208 | │ ├─{mysqld}(31878) 209 | │ ├─{mysqld}(31879) 210 | │ ├─{mysqld}(31880) 211 | │ ├─{mysqld}(31881) 212 | │ ├─{mysqld}(31882) 213 | │ ├─{mysqld}(31884) 214 | │ ├─{mysqld}(31885) 215 | │ ├─{mysqld}(31886) 216 | │ ├─{mysqld}(31887) 217 | │ ├─{mysqld}(31888) 218 | │ ├─{mysqld}(31889) 219 | │ ├─{mysqld}(31890) 220 | │ ├─{mysqld}(31891) 221 | │ ├─{mysqld}(31892) 222 | │ ├─{mysqld}(31893) 223 | │ ├─{mysqld}(31894) 224 | │ ├─{mysqld}(31895) 225 | │ ├─{mysqld}(31896) 226 | │ ├─{mysqld}(31897) 227 | │ ├─{mysqld}(30641) 228 | │ ├─{mysqld}(15274) 229 | │ ├─{mysqld}(10442) 230 | │ ├─{mysqld}(26016) 231 | │ ├─{mysqld}(10137) 232 | │ ├─{mysqld}(1973) 233 | │ ├─{mysqld}(23981) 234 | │ └─{mysqld}(18314) 235 | ├─nginx(10152)─┬─nginx(10153,www-data) 236 | │ ├─nginx(10154,www-data) 237 | │ ├─nginx(10155,www-data) 238 | │ └─nginx(10156,www-data) 239 | ├─polkitd(1512)─┬─{gdbus}(1516) 240 | │ └─{gmain}(1514) 241 | ├─python(1190) 242 | ├─rsyslogd(1212,syslog)─┬─{in:imklog}(1440) 243 | │ ├─{in:imuxsock}(1439) 244 | │ └─{rs:main Q:Reg}(1441) 245 | ├─sshd(1499)───sshd(4622)─┬─bash(4678)─┬─pstree(8779) 246 | │ │ └─systemctl(7769)───pager(7774) 247 | │ ├─bash(8773)───sleep(8778) 248 | │ └─sftp-server(4693) 249 | ├─supervisord(29467) 250 | ├─supervisord(29644) 251 | ├─systemd(1700)───(sd-pam)(1704) 252 | ├─systemd-journal(424) 253 | ├─systemd-logind(1193) 254 | ├─systemd-timesyn(544,systemd-timesync)───{sd-resolve}(559) 255 | ├─systemd-udevd(501) 256 | ├─top(19260) 257 | ├─top(19447) 258 | ├─uwsgi(10063)───uwsgi(10077) 259 | └─wrapper(1490)─┬─java(1559)─┬─{java}(1567) 260 | │ ├─{java}(1570) 261 | │ ├─{java}(1572) 262 | │ ├─{java}(1574) 263 | │ ├─{java}(1576) 264 | │ ├─{java}(1584) 265 | │ ├─{java}(1586) 266 | │ ├─{java}(1589) 267 | │ ├─{java}(1603) 268 | │ ├─{java}(1605) 269 | │ ├─{java}(1607) 270 | │ ├─{java}(1610) 271 | │ ├─{java}(1612) 272 | │ ├─{java}(1614) 273 | │ ├─{java}(1628) 274 | │ ├─{java}(1651) 275 | │ └─{java}(1655) 276 | └─{wrapper}(1493) 277 | ``` 278 | 279 | 可以分别发现有两个进行,分别是:`dockerd`与`containerd`。而`dockerd`通过 gRPC 与`containerd`通信。当执行 **docker run 命令**(通过 busybox 镜像创建并启动容器)时,containerd 会创建 containerd-shim 充当 “垫片” 进程(进程PID为8473),然后启动容器的真正进程 sleep 3600 。这个过程和架构图是完全一致的。 280 | 281 | 282 | 283 | 再次创建一个容器,再来观察containerd进程的情况,下面先执行容器的创建: 284 | 285 | ``` 286 | docker run -d busybox sleep 3600 287 | ``` 288 | 289 | 下面使用 pstree 命令查看一下进程父子关系: 290 | 291 | ``` 292 | systemd(1)─┬─Xvfb(17214)─┬─{llvmpipe-0}(18510) 293 | │ ├─{llvmpipe-1}(18511) 294 | │ ├─{llvmpipe-2}(18512) 295 | │ └─{llvmpipe-3}(18513) 296 | ├─accounts-daemon(1327)─┬─{gdbus}(1495) 297 | │ └─{gmain}(1492) 298 | ├─acpid(1203) 299 | ├─agetty(23750) 300 | ├─atd(1223,daemon) 301 | ├─containerd(22211)─┬─containerd-shim(8473)─┬─sleep(8491) 302 | │ │ ├─{containerd-shim}(8474) 303 | │ │ ├─{containerd-shim}(8475) 304 | │ │ ├─{containerd-shim}(8476) 305 | │ │ ├─{containerd-shim}(8477) 306 | │ │ ├─{containerd-shim}(8478) 307 | │ │ ├─{containerd-shim}(8479) 308 | │ │ ├─{containerd-shim}(8480) 309 | │ │ ├─{containerd-shim}(8482) 310 | │ │ └─{containerd-shim}(8820) 311 | │ ├─containerd-shim(9297)─┬─sleep(9316) 312 | │ │ ├─{containerd-shim}(9298) 313 | │ │ ├─{containerd-shim}(9299) 314 | │ │ ├─{containerd-shim}(9300) 315 | │ │ ├─{containerd-shim}(9301) 316 | │ │ ├─{containerd-shim}(9302) 317 | │ │ ├─{containerd-shim}(9303) 318 | │ │ ├─{containerd-shim}(9304) 319 | │ │ ├─{containerd-shim}(9306) 320 | │ │ └─{containerd-shim}(9343) 321 | │ ├─{containerd}(22216) 322 | │ ├─{containerd}(22217) 323 | │ ├─{containerd}(22218) 324 | │ ├─{containerd}(22219) 325 | │ ├─{containerd}(22220) 326 | │ ├─{containerd}(22227) 327 | │ ├─{containerd}(22228) 328 | │ ├─{containerd}(22229) 329 | │ ├─{containerd}(22231) 330 | │ ├─{containerd}(22232) 331 | │ ├─{containerd}(22233) 332 | │ ├─{containerd}(22234) 333 | │ ├─{containerd}(22369) 334 | │ ├─{containerd}(22375) 335 | │ ├─{containerd}(23023) 336 | │ ├─{containerd}(24119) 337 | │ ├─{containerd}(31016) 338 | │ ├─{containerd}(32704) 339 | │ ├─{containerd}(7339) 340 | │ ├─{containerd}(23491) 341 | │ ├─{containerd}(23596) 342 | │ ├─{containerd}(23889) 343 | │ ├─{containerd}(23894) 344 | │ ├─{containerd}(24535) 345 | │ ├─{containerd}(31378) 346 | │ ├─{containerd}(32403) 347 | │ ├─{containerd}(32404) 348 | │ ├─{containerd}(15398) 349 | │ ├─{containerd}(15399) 350 | │ ├─{containerd}(15805) 351 | │ └─{containerd}(16319) 352 | ├─cron(20629) 353 | ├─dbus-daemon(1227,messagebus) 354 | ├─dhclient(1023) 355 | ├─dockerd(6901)─┬─{dockerd}(6903) 356 | │ ├─{dockerd}(6904) 357 | │ ├─{dockerd}(6905) 358 | │ ├─{dockerd}(6906) 359 | │ ├─{dockerd}(6907) 360 | │ ├─{dockerd}(6908) 361 | │ ├─{dockerd}(6909) 362 | │ ├─{dockerd}(6910) 363 | │ ├─{dockerd}(6912) 364 | │ ├─{dockerd}(6913) 365 | │ ├─{dockerd}(6914) 366 | │ └─{dockerd}(6915) 367 | ├─irqbalance(1468) 368 | ├─iscsid(1140) 369 | ├─iscsid(1141) 370 | ├─lvmetad(479) 371 | ├─lxcfs(1215)─┬─{lxcfs}(1337) 372 | │ ├─{lxcfs}(1340) 373 | │ ├─{lxcfs}(9044) 374 | │ ├─{lxcfs}(23720) 375 | │ └─{lxcfs}(9313) 376 | ├─mdadm(1453) 377 | ├─mysqld(31859,mysql)─┬─{mysqld}(31871) 378 | │ ├─{mysqld}(31872) 379 | │ ├─{mysqld}(31873) 380 | │ ├─{mysqld}(31874) 381 | │ ├─{mysqld}(31875) 382 | │ ├─{mysqld}(31876) 383 | │ ├─{mysqld}(31877) 384 | │ ├─{mysqld}(31878) 385 | │ ├─{mysqld}(31879) 386 | │ ├─{mysqld}(31880) 387 | │ ├─{mysqld}(31881) 388 | │ ├─{mysqld}(31882) 389 | │ ├─{mysqld}(31884) 390 | │ ├─{mysqld}(31885) 391 | │ ├─{mysqld}(31886) 392 | │ ├─{mysqld}(31887) 393 | │ ├─{mysqld}(31888) 394 | │ ├─{mysqld}(31889) 395 | │ ├─{mysqld}(31890) 396 | │ ├─{mysqld}(31891) 397 | │ ├─{mysqld}(31892) 398 | │ ├─{mysqld}(31893) 399 | │ ├─{mysqld}(31894) 400 | │ ├─{mysqld}(31895) 401 | │ ├─{mysqld}(31896) 402 | │ ├─{mysqld}(31897) 403 | │ ├─{mysqld}(30641) 404 | │ ├─{mysqld}(15274) 405 | │ ├─{mysqld}(10442) 406 | │ ├─{mysqld}(26016) 407 | │ ├─{mysqld}(10137) 408 | │ ├─{mysqld}(1973) 409 | │ ├─{mysqld}(23981) 410 | │ └─{mysqld}(18314) 411 | ├─nginx(10152)─┬─nginx(10153,www-data) 412 | │ ├─nginx(10154,www-data) 413 | │ ├─nginx(10155,www-data) 414 | 415 | ``` 416 | 417 | 不难发现,当执行 **docker run 命令**时,containerd 会创建 containerd-shim 充当 “垫片” 进程(进程PID为9297),然后启动容器的真正进程 sleep 3600 。 418 | 419 | 420 | 421 | 在当前的宿主机器上,可能就存在由上述的不同进程构成的进程树,如下图所示: 422 | 423 | ![](images/2docker-进程数机构1.png) 424 | 425 | 426 | 427 | -------------------------------------------------------------------------------- /3.Docker镜像配置与原理.md: -------------------------------------------------------------------------------- 1 | # Docker镜像配置与原理 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-011 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ------ 20 | 21 | ​ Docker 核心:镜像,首先重点讲解一下镜像的基本操作,然后介绍一下镜像的实现原理。 22 | 23 | ### 1. 镜像 24 | 25 | ​ 镜像是一个只读的 Docker 容器模板,包含启动容器所需要的所有文件系统结构和内容。简单来讲,**镜像是一个特殊的文件系统**,它提供了容器运行时所需的程序、软件库、资源、配置等静态数据。即镜像不包含任何动态数据,镜像内容在构建后不会被改变。 26 | 27 | 如何操作镜像: 28 | 29 | ![](images/3docker-镜像操作.png) 30 | 31 | 从图中可知,镜像的操作可分为: 32 | 33 | ​ 拉取镜像,使用docker pull命令拉取远程仓库的镜像到本地 ; 34 | 35 | ​ 重命名镜像,使用docker tag命令“重命名”镜像 ; 36 | 37 | ​ 查看镜像,使用docker image ls或docker images命令查看本地已经存在的镜像 ; 38 | 39 | ​ 删除镜像,使用docker rmi命令删除无用镜像 ; 40 | 41 | ​ 构建镜像,构建镜像有两种方式。第一种方式是使用docker build命令基于 Dockerfile 构建镜像,也是我比较推荐的镜像构建方式;第二种方式是使用docker commit命令基于已经运行的容器提交为镜像。 42 | 43 | #### **1.1 拉取镜像** 44 | 45 | Docker 镜像的拉取使用**docker pull命令**, 命令格式一般为 **docker pull [Registry]/[Repository]/[Image]:[Tag]**。 46 | 47 | - Registry 为注册服务器,Docker 默认会从 docker.io 拉取镜像,如果你有自己的镜像仓库,可以把 Registry 替换为自己的注册服务器。 48 | 49 | - Repository 为镜像仓库,通常把一组相关联的镜像归为一个镜像仓库,library为 Docker 默认的镜像仓库。 50 | 51 | - Image 为镜像名称。 52 | 53 | - Tag 为镜像的标签,如果你不指定拉取镜像的标签,默认为latest。 54 | 55 | 56 | 57 | 58 | 例如,我们需要获取一个 busybox 镜像,可以执行以下命令: 59 | 60 | busybox 是一个集成了数百个 Linux 命令(例如 curl、grep、mount、telnet 等)的精简工具箱,只有几兆大小,被誉为 Linux 系统的瑞士军刀。我经常会使用 busybox 做调试来查找生产环境中遇到的问题。 61 | 62 | ``` 63 | $ docker pull busybox 64 | Using default tag: latest 65 | latest: Pulling from library/busybox 66 | 61c5ed1cbdf8: Pull complete 67 | Digest: sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977 68 | Status: Downloaded newer image for busybox:latest 69 | docker.io/library/busybox:latest 70 | ``` 71 | 72 | ​ 实际上执行docker pull busybox命令,都是先从本地搜索,如果本地搜索不到busybox镜像则从 Docker Hub 下载镜像。 73 | 74 | #### 1.2 操作镜像 75 | 76 | ##### 查看镜像 77 | 78 | Docker 镜像查看使用**docker images或者docker image ls**命令。 79 | 80 | 下面我们使用docker images命令列出本地所有的镜像。 81 | 82 | ``` 83 | $ docker images 84 | REPOSITORY TAG IMAGE ID CREATED SIZE 85 | nginx latest 4bb46517cac3 9 days ago 133MB 86 | nginx 1.15 53f3fd8007f7 15 months ago 109MB 87 | busybox latest 018c9d7b792b 3 weeks ago 1.22MB 88 | ``` 89 | 90 | 如果我们想要查询指定的镜像,可以使用docker image ls命令来查询。 91 | 92 | ``` 93 | $ docker image ls busybox 94 | REPOSITORY TAG IMAGE ID CREATED SIZE 95 | busybox latest 018c9d7b792b 3 weeks ago 1.22MB 96 | ``` 97 | 98 | 当然你也可以使用docker images命令列出所有镜像,然后使用grep命令进行过滤。使用方法如下: 99 | 100 | ``` 101 | $ docker images |grep busybox 102 | busybox latest 018c9d7b792b 3 weeks ago 1.22MB 103 | ``` 104 | 105 | 106 | 107 | **“重命名”镜像** 108 | 109 | 如果你想要自定义镜像名称或者推送镜像到其他镜像仓库,你可以使用docker tag命令将镜像重命名。docker tag的命令格式为 docker tag [SOURCE_IMAGE][:TAG] [TARGET_IMAGE][:TAG]。 110 | 111 | ``` 112 | $ docker tag busybox:latest mybusybox:latest 113 | ``` 114 | 115 | 执行完docker tag命令后,可以使用查询镜像命令查看一下镜像列表: 116 | 117 | ``` 118 | docker images 119 | REPOSITORY TAG IMAGE ID CREATED SIZE 120 | busybox latest 018c9d7b792b 3 weeks ago 1.22MB 121 | mybusybox latest 018c9d7b792b 3 weeks ago 1.22MB 122 | ``` 123 | 124 | 可以看到,镜像列表中多了一个mybusybox的镜像。busybox和mybusybox这两个镜像的 IMAGE ID 是完全一样的。为什么呢?实际上它们指向了同一个镜像文件,只是别名不同而已。 125 | 126 | ##### 删除镜像 127 | 128 | 你可以使用**docker rmi或者docker image rm**命令删除镜像。 129 | 130 | 例:删除mybusybox镜像 131 | 132 | ``` 133 | $ docker rmi mybusybox 134 | 此时,再次使用docker images命令查看一下我们机器上的镜像列表。 135 | 136 | $ docker images 137 | REPOSITORY TAG IMAGE ID CREATED SIZE 138 | busybox latest 018c9d7b792b 3 weeks ago 1.22MB 139 | 通过上面的输出,我们可以看到,mybusybox镜像已经被删除。 140 | ``` 141 | 142 | ##### **构建镜像** 143 | 144 | 构建镜像主要有两种方式: 145 | 146 | - **docker commit**命令从运行中的容器提交为镜像; 147 | - **docker build**命令从 Dockerfile 构建镜像。 148 | 149 | 150 | 151 | **Docker commit命令** 152 | 153 | 首先介绍下如何从运行中的容器提交为镜像。我依旧使用 busybox 镜像举例,使用以下命令创建一个名为 busybox 的容器并进入 busybox 容器。 154 | 155 | ``` 156 | # 创建容器 157 | $ docker run -itd busybox 158 | 159 | # 进入容器 160 | $ docker exec -it 容器id /binn/bash 161 | ``` 162 | 163 | 执行完上面的命令后,当前窗口会启动一个 busybox 容器并且进入容器中。在容器中,执行以下命令创建一个文件并写入内容: 164 | 165 | ``` 166 | touch hello.txt && echo "I love Docker. " > hello.txt 167 | ``` 168 | 169 | 此时在容器的根目录下,已经创建了一个 hello.txt 文件,并写入了 "I love Docker. "。下面,我们新打开另一个命令行窗口,运行以下命令提交镜像: 170 | 171 | ``` 172 | $ docker commit busybox busybox:hello 173 | sha256:cbc6406aaef080d1dd3087d4ea1e6c6c9915ee0ee0f5dd9e0a90b03e2215e81c 174 | ``` 175 | 176 | 然后使用上面讲到的docker image ls命令查看镜像: 177 | 178 | ``` 179 | $ docker image ls busybox 180 | REPOSITORY TAG IMAGE ID CREATED SIZE 181 | busybox hello cbc6406aaef0 2 minutes ago 1.22MB 182 | busybox latest 018c9d7b792b 4 weeks ago 1.22MB 183 | ``` 184 | 185 | 此时我们可以看到主机上新生成了 busybox:hello 这个镜像。 186 | 187 | 188 | 189 | **Docker build命令** 190 | 191 | 最常用的镜像构建方式:Dockerfile。Dockerfile 是一个包含了用户所有构建命令的文本。**通过docker build命令可以从 Dockerfile 生成镜像。** 192 | 193 | 使用 Dockerfile 构建镜像具有以下特性: 194 | 195 | - Dockerfile 的每一行命令都会生成一个独立的镜像层,并且拥有唯一的 ID; 196 | 197 | - Dockerfile 的命令是完全透明的,通过查看 Dockerfile 的内容,就可以知道镜像是如何一步步构建的; 198 | 199 | - Dockerfile 是纯文本的,方便跟随代码一起存放在代码仓库并做版本管理。 200 | 201 | 202 | 203 | 204 | 先学习下 Dockerfile 常用的指令。 205 | 206 | Dockerfile 指令 指令简介 207 | 208 | - FROM Dockerfile 除了注释第一行必须是 FROM ,FROM 后面跟镜像名称,代表我们要基于哪个基础镜像构建我们的容器。( **默认会先从本地去查找镜像**) 209 | - RUN RUN 后面跟一个具体的命令,类似于 Linux 命令行执行命令。 210 | - ADD 拷贝本机文件或者远程文件到镜像内 211 | - COPY 拷贝本机文件到镜像内 212 | - USER 指定容器启动的用户 213 | - ENTRYPOINT 容器的启动命令 214 | - CMD CMD 为 ENTRYPOINT 指令提供默认参数,也可以单独使用 CMD 指定容器启动参数 215 | - ENV 指定容器运行时的环境变量,格式为 key=value 216 | - ARG 定义外部变量,构建镜像时可以使用 build-arg = 的格式传递参数用于构建 217 | - EXPOSE 指定容器监听的端口,格式为 [port]/tcp 或者 [port]/udp 218 | - WORKDIR 为 Dockerfile 中跟在其后的所有 RUN、CMD、ENTRYPOINT、COPY 和 ADD 命令设置工作目录。 219 | 220 | 221 | 222 | 先分析下如下Dockerfile文件中的含义: 223 | 224 | ```dockerfile 225 | FROM centos:7 226 | COPY nginx.repo /etc/yum.repos.d/nginx.repo 227 | RUN yum install -y nginx 228 | EXPOSE 80 229 | ENV HOST=mynginx 230 | CMD ["nginx","-g","daemon off;"] 231 | ``` 232 | 233 | - 第一行表示我要基于 centos:7 这个镜像来构建自定义镜像。这里需要注意,每个 Dockerfile 的第一行除了注释都必须以 FROM 开头。 234 | 235 | - 第二行表示拷贝本地文件 nginx.repo 文件到容器内的 /etc/yum.repos.d 目录下。这里拷贝 nginx.repo 文件是为了添加 nginx 的安装源。 236 | 237 | - 第三行表示在容器内运行yum install -y nginx命令,安装 nginx 服务到容器内,执行完第三行命令,容器内的 nginx 已经安装完成。 238 | 239 | - 第四行声明容器内业务(nginx)使用 80 端口对外提供服务。 240 | 241 | - 第五行定义容器启动时的环境变量 HOST=mynginx,容器启动后可以获取到环境变量 HOST 的值为 mynginx。 242 | 243 | - 第六行定义容器的启动命令,命令格式为 json 数组。这里设置了容器的启动命令为 nginx ,并且添加了 nginx 的启动参数 -g 'daemon off;' ,使得 nginx 以前台的方式启动。 244 | 245 | 246 | 上面这个 Dockerfile 的例子基本涵盖了常用的镜像构建指令。 247 | 248 | ### 2. 镜像的实现原理 249 | 250 | #### 2.1 Bootfs和Rootfs 251 | 252 | 先下拉ubuntu镜像和python:3镜像: 253 | 254 | ```python 255 | root@iZ2ze1lioikou185atzucgZ:~# docker pull ubuntu 256 | 257 | Using default tag: latest 258 | 259 | latest: Pulling from library/ubuntu 260 | 261 | 54ee1f796a1e: Pull complete 262 | 263 | f7bfea53ad12: Pull complete 264 | 265 | 46d371e02073: Pull complete 266 | 267 | b66c17bbf772: Pull complete 268 | 269 | Digest: sha256:31dfb10d52ce76c5ca0aa19d10b3e6424b830729e32a89a7c6eee2cda2be67a5 270 | 271 | Status: Downloaded newer image for ubuntu:latest 272 | 273 | docker.io/library/ubuntu:latest 274 | 275 | root@iZ2ze1lioikou185atzucgZ:~# docker images 276 | 277 | REPOSITORY TAG IMAGE ID CREATED SIZE 278 | 279 | python 3 28a4c88cdbbf 15 hours ago 882MB 280 | 281 | ubuntu latest 4e2eef94cd6b 3 weeks ago 73.9MB 282 | ``` 283 | 284 | 大家可能注意到上面命令输出的最后一列, 它显示 ubuntu 这个镜像才 73.9MB !但是大家应该都知道,安装一个 ubuntu 系统怎么也不可能就几十兆,那么 Docker 是怎么做到的呢? 285 | 286 | 这是因为典型的 Linux 运行需要两个 FS: **bootfs 和 rootfs**,Linux 刚启动时会加载 bootfs 文件系统,之后 bootfs 会被卸载掉。对于不同的 Linux 发行版, bootfs 基本是一致的, 但 rootfs 会有差别,其包含我们熟悉的 /dev, /proc, /bin 等目录。对于 ubuntu 镜像来说,其底层直接使用 Host 的 **kernel内核**,自己只需要提供 rootfs 就行了。如下图所示: 287 | 288 | ![](images/3docker-bootfs-rootfs.png) 289 | 290 | 291 | 292 | 为了确定 ubuntu 镜像使用的是 Host 的Kernel内核,我们可以使用**uname -a命令查看内核版本**: 293 | 294 | ![](images/3docker-kernel.png) 295 | 296 | 说明如下: 297 | 298 | ``` 299 | 第一个组数字:4, 主版本号 300 | 第二个组数字:15, 次版本号,当前为稳定版本 301 | 第三个组数字:0, 修订版本号 302 | 第四个组数字:111,当前内核版本(4.15.0)的第111次微调patch 303 | generic:当前内核版本为通用版本,另有表示不同含义的server(针对服务器)、i386(针对老式英特尔处理器) 304 | pae(Physical Address Extension):物理地址扩展,为了弥补32位地址在PC服务器应用上的不足而推出,表示此32位系统可以支持超过4G的内存 305 | x86_64:采用的是64位的CPU 306 | SMP:对称多处理机,表示内核支持多核、多处理器 307 | Tue Jul 15 17:46:11 UTC 2014:内核的编译时间(build date)为 2014/07/15 17:46:11 308 | ``` 309 | 310 | **重点:**如果我们在同一个 host 上下载不同 Linux 发行版镜像,如python:3和Ubuntu,可以从上图中发现其都是使用的公用 host 的 bootfs。抽象一下,如下图所示: 311 | 312 | ![](images/3docker-bootfs.png) 313 | 314 | 理解到bootfs和rootfs后,就可以解释什么是镜像分层了。 315 | 316 | #### 2.1 镜像分层 317 | 318 | ​ 每个镜像都是通过 DockerFile 文本文件定义的,Dockerfile 中的每条指令最终都会成为镜像中的 Layer。Layer 是按顺序构成的,**最底层的 Layer 是基础镜像(base image),最上层是最终镜像(final image)**。当一个镜像被更新或重新构建时,只有更新的层需要修改,其他没有更新的层可以直接复用本地缓存。这就是 Docker 镜像如此快速和轻量级的部分原因,每一层的大小加起来等于最终镜像的大小。 319 | 320 | ​ 理解上面的设计之后,我们现在来解释最上面关于 Layer 这个概念。假设我们的 Dockerfile 定义如下: 321 | 322 | ``` 323 | FROM debian 324 | RUN apt-get update && apt-get -y -f install emacs 325 | RUN apt-get update && apt-get -y -f install apache2 326 | ``` 327 | 328 | 上面一共有三条指令,如果编译这个 Dockerfile,其会生成三个镜像: 329 | 330 | ``` 331 | [root@iteblog.com ~]$ docker build -t iteblog-docker ./ 332 | Sending build context to Docker daemon 2.048kB 333 | Step 1/3: FROM debian 334 | ---> a8797652cfd9 335 | Step 2/3: RUN apt-get update && apt-get -y -f install emacs 336 | ---> Using cache 337 | ---> 4b2cc711d0f1 338 | Step 3/3: RUN apt-get update && apt-get -y -f installapache2 339 | ---> Using cache 340 | ---> 48ec647c89a1 341 | Successfully built 48ec647c89a1 342 | Successfully tagged iteblog-docker:latest 343 | ``` 344 | 345 | 如果用图片表示的话,这个过程如下: 346 | 347 | ![](images/3docker-镜像分层.png) 348 | 349 | 350 | 351 | ### 3. 容器层 352 | 353 | ​ 如果多个镜像共用一个基础镜像,内存中也只需加载一份基础镜像,就可以为所有容器服务了。那么问题来了,如果我们需要修改基础镜像里面的东西咋办呢?Docker 很好的处理了这个问题,**当容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称作容器层(container layer),容器层之下的都叫镜像层(image layer)**,所有的修改(比如删除文件、添加文件)都是在容器层进行的,如下图所示: 354 | 355 | ![](images/3docker-container1.png) 356 | 357 | ​ 可以从图中看出Container layer容器可读层和Image layers镜像层。用户对容器中的添加、修改等数据都存放在container layer层,当容器被删除时,也只是container layer层被删除。底层的image layers镜像层是不动的。因此**不同容器都有自己独有的容器层,所有的修改只会存在自己的容器层,也就是说不同容器之间的修改都互不影响,这也就使得不同容器可以共享一个镜像层**,具体图解如下: 358 | 359 | ![](images/3docker-container2.png) 360 | 361 | **重点:不同容器之间的修改是互不影响的,但是如果不同容器之间需要做数据传递,可以使用network进行网络通信。** 362 | 363 | -------------------------------------------------------------------------------- /4.Docker容器的使用.md: -------------------------------------------------------------------------------- 1 | # Docker容器的操作 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-12 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ------ 20 | 21 | ​ 镜像包含了容器运行所需要的文件系统结构和内容,是静态的只读文件,而容器则是在镜像的只读层上创建了可写层,并且容器中的进程属于运行状态,容器是真正的应用载体。接下来讲讲Docker 核心:容器,重点讲解一下容器的基本操作。 22 | 23 | ### 1. 容器(Container) 24 | 25 | 容器是基于镜像创建的可运行实例,并且单独存在,一个镜像可以创建出多个容器。 26 | 27 | ![](images\4docker-容器层.png) 28 | 29 | ##### 1.1 容器的生命周期 30 | 容器的生命周期是容器可能处于的状态,容器的生命周期分为 5 种。 31 | 32 | - created:初建状态 33 | 34 | - running:运行状态 35 | - stopped:停止状态 36 | - paused: 暂停状态 37 | - deleted:删除状态 38 | 39 | 各生命周期之前的转换关系如图所示: 40 | 41 | ![](images\4docker-容器状态.png) 42 | 43 | 44 | 45 | docker create命令: 生成的容器状态为初建状态,初建状态通过 46 | 47 | docker start命令:可以将初建状态转化为运行状态 48 | 49 | docker stop命令:运行状态的容器转化为停止状态 50 | 51 | docker start命令:可以将处于停止状态的容器转化为运行状态 52 | 53 | docker pause命令:运行状态的容器转化为暂停状态 54 | 55 | docker unpause命令:将处于暂停状态的容器转化为运行状态 。 56 | 57 | **重点:处于初建状态、运行状态、停止状态、暂停状态的容器都可以直接删除。** 58 | 59 | 60 | 61 | ### 2. 容器命令 62 | 63 | 容器的操作可以分为五个步骤:创建并启动容器、终止容器、进入容器、删除容器、导入和导出容器。 64 | 65 | ##### 2.1 创建并启动容器 66 | 67 | 容器启动有两种方式: 68 | 69 | 1. docker create命令用于创建容器,创建后的容器处于停止状态,然后可以使用docker start命令来启动它。 70 | 71 | ``` 72 | # --name 指定容器的名称 73 | # 最后面的busybox为镜像 74 | docker create -it --name=new_busybox busybox 75 | 76 | # 启动刚create创建的容器 77 | docker start new_busybox 78 | ``` 79 | 80 | 81 | 82 | 2. 使用docker run命令直接基于镜像新建一个容器并启动,相当于先执行docker create命令从镜像创建容器,然后再执行docker start命令启动容器。 83 | 84 | ``` 85 | docker run -it --name=new_busybox busybox 86 | ``` 87 | 88 | 89 | 90 | 当使用`docker run`创建并启动容器时,Docker 后台执行的流程为: 91 | 92 | - Docker 会检查本地是否存在 busybox 镜像,如果镜像不存在则从 Docker Hub 拉取 busybox 镜像 93 | - 使用 busybox 镜像创建并启动一个容器, 容器名为new_busybox 94 | - 分配文件系统,并且在镜像只读层外创建一个读写层 95 | - 从 Docker IP 池中分配一个 IP 给容器(在下面会讲解原理) 96 | - 执行用户的启动命令运行镜像 97 | 98 | 99 | 100 | ##### 2.2 终止容器 101 | 102 | `docker stop`命令:停止运行中的容器。命令格式为 docker stop [-t|--time[=10]]。 103 | 104 | 该命令首先会向运行中的容器发送 SIGTERM 信号,如果容器内 1 号进程接受并能够处理 SIGTERM,则等待 1 号进程处理完毕后退出,如果等待一段时间后,容器仍然没有退出,则会发送 SIGKILL 强制终止容器。 105 | 106 | ``` 107 | docker stop new_busybox 108 | ``` 109 | 110 | 如果你想查看停止状态的容器信息,你可以使用 docker ps -a 命令。 111 | 112 | ``` 113 | docker ps -a 114 | ``` 115 | 116 | 处于终止状态的容器也可以通过`docker start`命令和`docker restart`命令来重新启动。 117 | 118 | ``` 119 | docker start new_busybox 120 | docker restart new_busybox 121 | ``` 122 | 123 | 124 | 125 | ##### 2.3 进入容器 126 | 127 | 处于运行状态的容器可以通过`docker attach`、`docker exec`、`nsenter`等多种方式进入容器。 128 | 129 | - **使用**`docker attach`命令**进入容器** 130 | 131 | 使用 docker attach ,进入我们上一步创建好的容器,如下所示。 132 | 133 | ``` 134 | docker attach new_busybox 135 | ``` 136 | 137 | **注意:**当我们同时使用`docker attach`命令同时在多个终端运行时,所有的终端窗口将同步显示相同内容,当某个命令行窗口的命令阻塞时,其他命令行窗口同样也无法操作。 138 | 由于`docker attach`命令不够灵活,因此我们一般不会使用`docker attach`进入容器 139 | 140 | - **使用 docker exec 命令进入容器** 141 | 142 | 通过`docker exec -it CONTAINER /bin/bash`的方式进入到一个已经运行中的容器 143 | 144 | ``` 145 | docker exec -it 容器id /bin/bash 146 | ``` 147 | 148 | 149 | 150 | ##### 2.4 删除容器 151 | 152 | 删除容器命令的使用方式如下:`docker rm [OPTIONS] CONTAINER [CONTAINER...]` 153 | 154 | ``` 155 | # 删除已经暂停的容器 156 | docker rm 容器名或者容器id 157 | 158 | # 删除还在运行中的容器 159 | docker rm -f 容器名或者容器id 160 | ``` 161 | 162 | 163 | 164 | ##### 2.5 导出容器 165 | 166 | - **导出容器** 167 | 168 | 使用`docker export CONTAINER`命令导出一个容器到文件,不管此时该容器是否处于运行中的状态。 169 | 170 | 执行导出命令: 171 | 172 | ``` 173 | docker export new_busybox > new_busybox.tar 174 | ``` 175 | 176 | 执行以上命令后会在当前文件夹下生成 new_busybox.tar 文件,我们可以将该文件拷贝到其他机器上,通过导入命令实现容器的迁移。 177 | 178 | - **导入容器** 179 | 180 | 通过`docker export`命令导出的文件,可以使用`docker import`命令导入,执行完`docker import`后会变为本地镜像,最后再使用`docker run`命令启动该镜像,这样我们就实现了容器的迁移。 181 | 182 | 导入容器的命令格式为 docker import [OPTIONS] file|URL [REPOSITORY[:TAG]]。接下来将上一步导出的镜像文件导入到其他机器的 Docker 中并启动它。 183 | 184 | 使用`docker import`命令导入上一步导出的容器 185 | 186 | ``` 187 | docker import new_busybox.tar new_busybox:test 188 | ``` 189 | 190 | 此时,new_busybox.tar 被导入成为新的镜像,镜像名称为 new_busybox:test 。下面,我们使用`docker run`命令启动并进入容器,查看上一步创建的临时文件 191 | 192 | ``` 193 | docker run -it busybox:test sh 194 | ``` 195 | 196 | **重点:**通过`docker export`和`docker import`命令配合实现了容器的迁移 197 | 198 | ### 3. 网络动态IP分配 199 | 200 | 刚讲到docker run命令的执行时需要从Docker IP 池中分配一个 IP 给容器,接下来重点讲解下该内容。 201 | 202 | ##### 一、Docker的四种网络模式 203 | 204 | Docker在创建容器时有四种网络模式,bridge为默认不需要用--net去指定,其他三种模式需要在创建容器时使用--net去指定。 205 | 206 | 1. bridge模式,使用--net=bridge指定,默认设置。 207 | 2. none模式,使用--net=none指定。 208 | 3. host模式,使用--net=host指定。 209 | 4. container模式,使用--net=container:容器名称或ID指定。(如:--net=container:30b668ccb630) 210 | 211 | **bridge模式:**docker网络隔离基于网络命名空间,在物理机上创建docker容器时会为每一个docker容器分配网络命名空间,并且把容器IP桥接到物理机的虚拟网桥上。 212 | 213 | **none模式:**此模式下创建容器是不会为容器配置任何网络参数的,如:容器网卡、IP、通信路由等,全部需要自己去配置。 214 | 215 | **host模式:**此模式创建的容器没有自己独立的网络命名空间,是和物理机共享一个Network Namespace,并且共享物理机的所有端口与IP,并且这个模式认为是不安全的。 216 | 217 | **container模式:**此模式和host模式很类似,只是此模式创建容器共享的是其他容器的IP和端口而不是物理机,此模式容器自身是不会配置网络和端口,创建此模式容器进去后,你会发现里边的IP是你所指定的那个容器IP并且端口也是共享的,而且其它还是互相隔离的,如进程等。 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /5.Dockerfile的编写规则.md: -------------------------------------------------------------------------------- 1 | # Dockerfile的编写规则 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-18 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ### 1. 前言 20 | 21 | ​ 生产实践中一定优先使用 Dockerfile 的方式构建镜像。 因为使用 Dockerfile 构建镜像可以带来很多好处: 22 | 23 | - 易于版本化管理,Dockerfile 本身是一个文本文件,方便存放在代码仓库做版本管理,可以很方便地找到各个版本之间的变更历史; 24 | - 过程可追溯,Dockerfile 的每一行指令代表一个镜像层,根据 Dockerfile 的内容即可很明确地查看镜像的完整构建过程; 25 | 26 | - 屏蔽构建环境异构,使用 Dockerfile 构建镜像无须考虑构建环境,基于相同 Dockerfile 无论在哪里运行,构建结果都一致; 27 | 28 | - 虽然有这么多好处,但是如果你 Dockerfile 使用不当也会引发很多问题。比如镜像构建时间过长,甚至镜像构建失败;镜像层数过多,导致镜像文件过大。所以,这一课时我就教你如何在生产环境中编写最优的 Dockerfile; 29 | 30 | 31 | ### 2. Dockerfile编写遵循的原则 32 | 33 | ​ 遵循以下 Dockerfile 书写原则,不仅可以使得我们的 Dockerfile 简洁明了,让协作者清楚地了解镜像的完整构建流程,还可以帮助我们减少镜像的体积,加快镜像构建的速度和分发速度。 34 | 35 | **(1)单一职责** 36 | ​ 由于容器的本质是进程,一个容器代表一个进程,因此不同功能的应用应该尽量拆分为不同的容器,每个容器只负责单一业务进程。 37 | 38 | **(2)提供注释信息** 39 | ​ Dockerfile 也是一种代码,我们应该保持良好的代码编写习惯,晦涩难懂的代码尽量添加注释,让协作者可以一目了然地知道每一行代码的作用,并且方便扩展和使用。 40 | 41 | **(3)保持容器最小化** 42 | ​ 应该避免安装无用的软件包,比如在一个 nginx 镜像中,我并不需要安装 vim 、gcc 等开发编译工具。这样不仅可以加快容器构建速度,而且可以避免镜像体积过大。 43 | 44 | **(4)合理选择基础镜像** 45 | ​ 容器的核心是应用,因此只要基础镜像能够满足应用的运行环境即可。例如一个Java类型的应用运行时只需要JRE,并不需要JDK,因此我们的基础镜像只需要安装JRE环境即可。 46 | 47 | **(5)使用 .dockerignore 文件** 48 | ​ 在使用git时,我们可以使用.gitignore文件忽略一些不需要做版本管理的文件。同理,使用.dockerignore文件允许我们在构建时,忽略一些不需要参与构建的文件,从而提升构建效率。.dockerignore的定义类似于.gitignore。 49 | 50 | .dockerignore的本质是文本文件,Docker 构建时可以使用换行符来解析文件定义,每一行可以忽略一些文件或者文件夹。具体使用方式如下: 51 | 52 | ``` 53 | 规则 含义 54 | # 开头的表示注释,# 后面所有内容将会被忽略 55 | /tmp 匹配当前目录下任何以 tmp 开头的文件或者文件夹 56 | *.md 匹配以 .md 为后缀的任意文件 57 | tem? 匹配以 tem 开头并且以任意字符结尾的文件,?代表任意一个字符 58 | !README.md ! 表示排除忽略。 59 | ``` 60 | 61 | **(6)尽量使用构建缓存** 62 | ​ Docker 构建过程中,每一条 Dockerfile 指令都会提交为一个镜像层,下一条指令都是基于上一条指令构建的。如果构建时发现要构建的镜像层的父镜像层已经存在,并且下一条命令使用了相同的指令,即可命中构建缓存。 63 | 64 | Docker 构建时判断是否需要使用缓存的规则如下: 65 | 66 | - 从当前构建层开始,比较所有的子镜像,检查所有的构建指令是否与当前完全一致,如果不一致,则不使用缓存; 67 | 68 | - 一般情况下,只需要比较构建指令即可判断是否需要使用缓存,但是有些指令除外(例如ADD和COPY); 69 | 70 | - 对于ADD和COPY指令不仅要校验命令是否一致,还要为即将拷贝到容器的文件计算校验和(根据文件内容计算出的一个数值,如果两个文件计算的数值一致,表示两个文件内容一致 ),命令和校验和完全一致,才认为命中缓存。 71 | 72 | - 因此,基于 Docker 构建时的缓存特性,我们可以把不轻易改变的指令放到 Dockerfile 前面(例如安装软件包),而可能经常发生改变的指令放在 Dockerfile 末尾(例如编译应用程序)。 73 | 74 | - 例如,我们想要定义一些环境变量并且安装一些软件包,可以按照如下顺序编写 Dockerfile: 75 | 76 | ``` 77 | FROM centos:7 78 | # 设置环境变量指令放前面 79 | ENV PATH /usr/local/bin:$PATH 80 | # 安装软件指令放前面 81 | RUN yum install -y make 82 | # 把业务软件的配置,版本等经常变动的步骤放最后 83 | ``` 84 | 85 | 按照上面原则编写的 Dockerfile 在构建镜像时,前面步骤命中缓存的概率会增加,可以大大缩短镜像构建时间。 86 | 87 | **(7)正确设置时区** 88 | ​ 我们从 Docker Hub 拉取的官方操作系统镜像大多数都是 UTC 时间(世界标准时间)。如果你想要在容器中使用中国区标准时间(东八区),请根据使用的操作系统修改相应的时区信息,下面我介绍几种常用操作系统的修改方式: 89 | 90 | **Ubuntu 和Debian 系统** 91 | 92 | Ubuntu 和Debian 系统可以向 Dockerfile 中添加以下指令: 93 | 94 | ``` 95 | RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 96 | RUN echo "Asia/Shanghai" >> /etc/timezone 97 | ``` 98 | 99 | **CentOS系统** 100 | 101 | CentOS 系统则向 Dockerfile 中添加以下指令: 102 | 103 | ``` 104 | RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 105 | ``` 106 | 107 | **(8)使用国内软件源加快镜像构建速度** 108 | ​ 由于我们常用的官方操作系统镜像基本都是国外的,软件服务器大部分也在国外,所以我们构建镜像的时候想要安装一些软件包可能会非常慢。 109 | 110 | **(9)最小化镜像层数** 111 | 在构建镜像时尽可能地减少 Dockerfile 指令行数。例如我们要在 CentOS 系统中安装make和net-tools两个软件包,应该在 Dockerfile 中使用以下指令: 112 | 113 | ``` 114 | RUN yum install -y make net-tools 115 | ``` 116 | 117 | 而不应该写成这样: 118 | 119 | ``` 120 | RUN yum install -y make 121 | RUN yum install -y make 122 | ``` 123 | 124 | 了解完 Dockerfile 的书写原则后,我们再来具体了解下这些原则落实到具体的 Dockerfile 指令应该如何书写。 125 | 126 | ### 3. Dockerfile 指令书写建议 127 | 128 | 下面是我们常用的一些指令: 129 | 130 | **(1)RUN** 131 | RUN指令在构建时**将会生成一个新的镜像层**并且执行RUN指令后面的内容。 132 | 133 | 使用RUN指令时应该尽量遵循以下原则: 134 | 135 | - 当RUN指令后面跟的内容比较复杂时,建议使用反斜杠(\) 结尾并且换行; 136 | 137 | - RUN指令后面的内容尽量按照字母顺序排序,提高可读性。 138 | 139 | 140 | 例如,在官方的 CentOS 镜像下安装一些软件,一个建议的 Dockerfile 指令如下: 141 | 142 | ``` 143 | FROM centos:7 144 | RUN yum install -y automake \ 145 | curl \ 146 | python \ 147 | vim 148 | ``` 149 | 150 | **(2)CMD 和 ENTRYPOINT** 151 | 152 | ​ **CMD和ENTRYPOINT指令都是容器运行的命令入口**。 153 | 154 | 这两个指令的相同之处,CMD和ENTRYPOINT的基本使用格式分为两种。 155 | 156 | - 第一种为CMD/ENTRYPOINT["command" , "param"]。这种格式是使用 **Linux 的exec**实现的, 一般称为exec模式,这种书写格式为CMD/ENTRYPOINT后面跟 json 数组,也是Docker 推荐的使用格式。 157 | 158 | - 另外一种格式为CMD/ENTRYPOINTcommand param ,这种格式是**基于 shell 实现**的, 通常称为shell模式。当使用shell模式时,Docker 会以 /bin/sh -c command 的方式执行命令。 159 | 160 | 161 | 区别: 162 | 163 | - Dockerfile 中如果使用了ENTRYPOINT指令,启动 Docker 容器时需要使用 --entrypoint 参数才能覆盖 Dockerfile 中的ENTRYPOINT指令 164 | - 使用CMD设置的命令则可以被docker run后面的参数直接覆盖。 165 | - ENTRYPOINT指令可以结合CMD指令使用,也可以单独使用,而CMD指令只能单独使用。 166 | 167 | 例如:dockerfiler如下定义 168 | 169 | ``` 170 | FROM python:3 171 | 172 | CMD ["python3", "hello.py"] 173 | ``` 174 | 175 | 执行启动命令为:docker run python3 xxx.py -itd xxxx,表示下拉python:3镜像,并执行python3 xxx.py命令,如果用docker run -itd xxxx,表示下拉python:3镜像,并执行Dockerfile中定义的python3 hello.py命令。 176 | 177 | #### **疑问🤔️:** 178 | 179 | 什么时候应该使用ENTRYPOINT,什么时候使用CMD呢? 180 | 181 | 如果你希望你的镜像足够灵活,推荐使用CMD指令。如果你的镜像只执行单一的具体程序,并且不希望用户在执行docker run时覆盖默认程序,建议使用ENTRYPOINT。 182 | 183 | 最后再强调一下,无论使用CMD还是ENTRYPOINT,都**尽量使用exec模式**。 184 | 185 | 如果**在Dockerfile中有多条CMD指令,那么永远只会执行最后一条CMD指令,最好将执行的命令通过&&进行连接。** 186 | 187 | **(3)ADD 和 COPY** 188 | ​ ADD和COPY指令功能类似,都是从外部往容器内添加文件。 189 | 190 | - COPY指令只支持**基本的文件和文件夹**拷贝功能 191 | - ADD则支持更多**文件来源类型**,比如自动提取 tar 包,并且可以支持源文件为 URL 格式。 192 | 193 | 那么在日常应用中,我们应该使用哪个命令向容器里添加文件呢?既然ADD指令支持的功能更多,当然应该使用ADD指令了。然而事实恰恰相反,其实更推荐使用COPY指令,因为COPY指令更加透明,仅支持本地文件向容器拷贝,而且使用COPY指令可以更好地利用构建缓存,有效减小镜像体积。 194 | 195 | 如果使用ADD向容器中添加 URL 文件时,请尽量考虑使用其他方式替代。例如你想要在容器中安装 memtester(一种内存压测工具),你应该避免使用以下格式: 196 | 197 | ``` 198 | ADD http://pyropus.ca/software/memtester/old-versions/memtester-4.3.0.tar.gz /tmp/ 199 | RUN tar -xvf /tmp/memtester-4.3.0.tar.gz -C /tmp 200 | RUN make -C /tmp/memtester-4.3.0 && make -C /tmp/memtester-4.3.0 install 201 | ``` 202 | 203 | 下面是推荐写法: 204 | 205 | ``` 206 | RUN wget -O /tmp/memtester-4.3.0.tar.gz http://pyropus.ca/software/memtester/old-versions/memtester-4.3.0.tar.gz \ 207 | && tar -xvf /tmp/memtester-4.3.0.tar.gz -C /tmp \ 208 | && make -C /tmp/memtester-4.3.0 && make -C /tmp/memtester-4.3.0 install 209 | ``` 210 | 211 | **(4)WORKDIR** 212 | ​ WORKDIR: 指定容器的工作路径,应该尽量避免使用 RUN cd /work/path && do some work 这样的指令。 -------------------------------------------------------------------------------- /6.docker架构设计.md: -------------------------------------------------------------------------------- 1 | # Dockerfile的架构设计 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-18 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ### 1. 前言 20 | 21 | docker架构设计参考文章进行整理:地址为 22 | 23 | 这一篇文章源于2015年出版的《docker源码分析》,书中以docker1.2.0版本进行分析。但是在docker1.11以后,docker的架构就在不断的优化和拆分。下面整理出了docker框架的变迁史,可以稍作参考。 24 | 25 | ### 2. 架构设计 26 | 27 | ![](images/6.Docker架构.png) 28 | 29 | -------------------------------------------------------------------------------- /7.Docker安全性能了解.md: -------------------------------------------------------------------------------- 1 | # Docker安全性能了解 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-22 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ### 1. 前言 20 | 21 | ​ Docker 是基于 Linux 内核的 **Namespace** 技术实现资源隔离的,**所有的容器都共享主机的内核**。其实这与以虚拟机为代表的云计算时代还是有很多区别的,比如虚拟机有着更好的隔离性和安全性,而容器的隔离性和安全性则相对较弱。 22 | 23 | ​ 在讨论容器的安全性之前,我们先了解下容器与虚拟机的区别,这样可以帮助我们更好地了解容器的安全隐患以及如何加固容器安全。 24 | 25 | ### 2. docker与虚拟机的区别 26 | 27 | ​ 虚拟机是通过**管理系统(Hypervisor)**模拟出 CPU、内存、网络等硬件,然后在这些模拟的硬件上创建客户内核和操作系统。这样做的好处**就是虚拟机有自己的内核和操作系统,并且硬件都是通过虚拟机管理系统模拟出来的,用户程序无法直接使用到主机的操作系统和硬件资源,**因此虚拟机也对隔离性和安全性有着更好的保证。 28 | 29 | ​ 而 Docker 容器则是通过 Linux 内核(**kernel**)的 **Namespace 技术实现了文件系统、进程、设备以及网络的隔离**,然后再通过 **Cgroups 对 CPU、 内存等资源进行限制**,最终实现了容器之间相互不受影响,由于容器的隔离性仅仅依靠内核来提供,因此容器的隔离性也远弱于虚拟机。 30 | 31 | ![](images/7.docker与虚拟机的区别.png) 32 | 33 | ​ 容器与虚拟机相比,**容器的性能损耗非常小,并且镜像也非常小**,而且在业务快速开发和迭代的今天,容器秒级的启动等特性也非常匹配业务快速迭代的业务场景。 34 | 35 | ​ 既然我们要利用容器的优点,那就需要尽量弥补**容器弱隔离的安全性缺点**呢?要了解如何解决容器的安全问题,我们首先需要了解下容器目前存在的安全问题。 36 | 37 | ### **3. docker的安全性问题** 38 | 39 | ##### 1)docker自身安全 40 | 41 | ​ Docker 作为一款容器引擎,本身也会存在一些安全漏洞。安全漏洞可以通过CVE(Common Vulnerabilities and Exposures)又称常见漏洞与披露,是一个与信息安全有关的数据库,收集各种信息安全弱点及漏洞并给予编号以便于公众查阅。 42 | 43 | **2)镜像安全** 44 | ​ 由于 Docker 容器是基于镜像创建并启动,因此镜像的安全直接影响到容器的安全。具体影响镜像安全的总结如下。 45 | 46 | - 镜像软件存在安全漏洞:由于容器需要安装基础的软件包,如果软件包存在漏洞,则可能会被不法分子利用并且侵入容器,影响其他容器或主机安全。 47 | 48 | - 仓库漏洞:无论是 Docker 官方的镜像仓库还是我们私有的镜像仓库,都有可能被攻击,然后篡改镜像,当我们使用镜像时,就可能成为攻击者的目标对象。 49 | 50 | - 用户程序漏洞:用户自己构建的软件包可能存在漏洞或者被植入恶意脚本,这样会导致运行时提权影响其他容器或主机安全。 51 | 52 | **3)Linux 内核隔离性不够** 53 | 54 | ​ 尽管目前 **Namespace** 已经提供了非常多的资源隔离类型,但是仍有部分关键内容没有被完全隔离,其中包括一些系统的关键性目录(如 /sys、/proc 等),这些关键性的目录可能会泄露主机上一些关键性的信息,让攻击者利用这些信息对整个主机甚至云计算中心发起攻击。 55 | 56 | ​ 而且仅仅依靠 Namespace 的隔离是远远不够的,因为一旦内核的 Namespace 被突破,使用者就有可能直接提权获取到主机的超级权限,从而影响主机安全。 57 | 58 | **4) 所有容器共享主机内核** 59 | 60 | ​ 由于**同一宿主机上所有容器共享主机内核**,所以攻击者可以利用一些特殊手段导致内核崩溃,进而导致主机宕机影响主机上其他服务。 61 | 62 | ### 4. Docker安全问题解决 63 | 64 | 65 | **1) Docker 自身安全性改进** 66 | ​ 事实上,Docker 从 2013 年诞生到现在,在安全性上面已经做了非常多的努力。目前 Docker 在默认配置和默认行为下是足够安全的。 67 | 68 | ​ Docker 自身是基于 Linux 的多种 Namespace 实现的,其中有一个**很重要的 Namespace 叫作 User Namespace**,User Namespace 主要是用来做容器内用户和主机的用户隔离的。在过去容器里的 root 用户就是主机上的 root 用户,如果容器受到攻击,或者容器本身含有恶意程序,在容器内就可以直接获取到主机 root 权限。Docker 从 1.10 版本开始,使用 **User Namespace 做用户隔离,实现了容器中的 root 用户映射到主机上的非 root 用户,从而大大减轻了容器被突破的风险。** 69 | 70 | 因此,我们尽可能地使用 Docker 最新版本就可以得到更好的安全保障。 71 | 72 | **2) 保障镜像安全** 73 | 74 | ​ 为保障镜像安全,我们可以在**私有镜像仓库安装镜像安全扫描组件**,对上传的镜像进行检查,通过与 CVE 数据库对比,一旦发现有漏洞的镜像及时通知用户或阻止非安全镜像继续构建和分发。同时为了确保我们使用的镜像足够安全,在拉取镜像时,要确保只从受信任的镜像仓库拉取,并且与镜像仓库通信一定要使用 HTTPS 协议。 75 | 76 | **3) 加强内核安全和管理** 77 | 78 | ​ 由于仅仅依赖内核的隔离可能会引发安全问题,因此我们对于内核的安全应该更加重视。可以从以下几个方面进行加强。 79 | 80 | - 宿主机及时升级内核漏洞 81 | 82 | - 宿主机内核应该尽量安装最新补丁,因为更新的内核补丁往往有着更好的安全性和稳定性。 83 | 84 | - 使用 Capabilities 划分权限 85 | 86 | 87 | Capabilities 是 Linux 内核的概念,Linux 将系统权限分为了多个 Capabilities,它们都可以单独地开启或关闭,Capabilities 实现了系统更细粒度的访问控制。在执行docker run命令启动容器时,如非特殊可控情况,--privileged 参数不允许设置为 true,其他特殊权限可以使用 --cap-add 参数,根据使用场景适当添加相应的权限。 88 | 89 | **4) 使用安全加固组件** 90 | 91 | ​ Linux 的 **SELinux、AppArmor、GRSecurity组件都是 Docker 官方推荐的安全加固组件**。 92 | 93 | - SELinux (Secure Enhanced Linux): 是 Linux 的一个内核安全模块,提供了安全访问的策略机制,通过设置 SELinux 策略可以实现某些进程允许访问某些文件。 94 | 95 | - AppArmor: 类似于 SELinux,也是一个 Linux 的内核安全模块,普通的访问控制仅能控制到用户的访问权限,而 AppArmor 可以控制到用户程序的访问权限。 96 | 97 | - GRSecurity: 是一个对内核的安全扩展,可通过智能访问控制,提供内存破坏防御,文件系统增强等多种防御形式。 98 | 99 | 100 | 这三个组件可以限制一个容器对主机的内核或其他资源的访问控制。目前,容器报告的一些安全漏洞中,很多都是通过对内核进行加强访问和隔离来实现的。 101 | 102 | **5) 资源限制** 103 | 104 | ​ 在生产环境中,建议每个容器都添加相应的资源限制。下面给出一些执行docker run命令启动容器时可以传递的资源限制参数: 105 | 106 | ``` 107 | --cpus 限制 CPU 配额 108 | -m, --memory 限制内存配额 109 | --pids-limit 限制容器的 PID 个数 110 | ``` 111 | 112 | 例如我想要启动一个 1 核 2G 的容器,并且限制在容器内最多只能创建 1000 个 PID,启动命令如下: 113 | 114 | ``` 115 | $ docker run -it --cpus=1 -m=2048m --pids-limit=1000 busybox /bin/bash 116 | ``` 117 | 118 | 推荐在生产环境中限制 CPU、内存、PID 等资源,这样即便应用程序有漏洞,也不会导致主机的资源完全耗尽,最大限度降低安全风险。 119 | 120 | **6) 使用安全容器** 121 | 122 | ​ 容器有着轻便快速启动的优点,虚拟机有着安全隔离的优点,有没有一种技术可以兼顾两者的优点,做到既轻量又安全呢? 123 | 124 | ​ 答案是有,那就是**安全容器。安全容器是相较于普通容器的,安全容器与普通容器的主要区别在于,安全容器中的每个容器都运行在一个单独的微型虚拟机中,拥有独立的操作系统和内核,并且有虚拟化层的安全隔离。** 125 | 126 | 安全容器目前推荐的技术方案是 : **Kata Containers** 127 | 128 | -------------------------------------------------------------------------------- /8.Docker安全性能提升.md: -------------------------------------------------------------------------------- 1 | # Docker安全性能提升 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-22 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ### 1. 前言 20 | 21 | ​ Docker容器安全目前推荐的技术方案是 : **Kata Containers**,那先来认识一下什么是kata containers。 22 | 23 | ​ Kata Containers是一个开源项目和社区,致力于**构建轻量级虚拟机(VM)的标准实现——这些虚拟机的感知和执行类似容器**,但提供VM的工作负载隔离和安全优势,内核级别的隔离宿主机的内核!Kata Containers 项目最初是基于 **QEMU** 的,但它的设计从一开始就以支持多种管理程序解决方案为出发点。 24 | 25 | ### 2. kata-containers 26 | 27 |   kata containers是由OpenStack基金会管理,但独立于OpenStack项目之外的容器项目。它是一个**可以使用容器镜像以超轻量级虚机的形式创建容器的运行时工具**。 kata containers整合了Intel的 Clear Containers 和 Hyper.sh 的 runV,能够支持不同平台的硬件 (x86-64,arm等),并符合**OCI(Open Container Initiative)**规范,同时还可以兼容k8s的 **CRI(Container Runtime Interface)**接口规范。目前项目包含几个配套组件,即Runtime,Agent, Proxy,Shim,Kernel等 28 | 29 |   真正启动Docker容器的命令工具是RunC,它是OCI运行时规范 (runtime-spec)的默认实现。Kata containers其实跟RunC类似,也是一个符合OCI运行时规范的一种 实现(即**Clear Container和runV 都符OCI规范**),不同之处是,它给每个容器(在Docker容器的 角度)或每个Pod(k8s的角度)增加了一个独立的linux内核(不共享宿主机的内核),使容器有更好 的隔离性,安全性。 30 | 31 | 32 | 33 | **2.1)docker的runtime模式架构图:** 34 | 35 | ![](images/8.docker的runtime架构图.png) 36 | 37 | ​ 可以看到runC处于docker组件图的最底端,runC下面就是容器。目前docker已经不是一个专一的容器 管理组件,而真正的容器管理组件是containerd,而containerd本身也不会直接跟操作系统交互,去创建、删除容器,而是借助runC来对容器生命周期进行管理,因此runC作为必须遵循OCI规范。 38 | 39 | **2.2)kata containers定义位置:** 40 | 41 | kata containers在容器的什么位置,应该就显而易见了。它符合OCI运行时规 范,因此,可以作为runC的替代组件,如红色虚线框内所示: 42 | 43 | ![](images/8.docker的runtime架构图2.png) 44 | 45 | 46 | 47 | **2.3)kata container原理** 48 | 49 | ​ kata container实质上是在虚拟机内部使用container(基于runc的实现)。 kata-container使用虚拟化软件(qemu-lite优化过的qemu), 通过已经将kata-agent 安装的kernel & intrd image,启动过一个轻量级的虚拟机, 使用nvdimm将initrd image映射到guest vm中。然后由kata-agent为container创建对应的namespace和资源。 Guest VM作为实质上的sandbox可以完全与host kernel进行隔离。 50 | 51 | ![](images/8.kata-container原理png.png) 52 | 53 | **组件拆分介绍** 54 | 55 | - **kata-runtime**:实现OCI接口,可以通过CRI-O 与kubelet对接作为k8s runtime server, containerd对接docker engine,创建运行container/pod的VM 56 | - **kata-proxy**: 每一个container都会由一个kata-proxy进程,kata-proxy负责与kata-agent通讯,当guest vm启动后,kata-agent会随之启动并使用qemu virtio serial console 进行通讯 57 | - **kata-agent**: 运行在guest vm中的进程, 主要依赖于**libcontainer**项目,重用了大部分的runc代码,为container创建namespace(NS, UTS, IPC and PID) 58 | - **kata-shim**: 作为guest vm标准输入输出的接口,exec命令就是同kata-shim实现的。(其实相当于是container-shim的一个适配) 59 | 60 | **2.4)classic Docker与Docker with Kata的架构** 61 | 62 | ![](images/8.Docker with Kata架构设计.png) 63 | 64 | -------------------------------------------------------------------------------- /9.Docker底层资源隔离namespace.md: -------------------------------------------------------------------------------- 1 | # Docker资源隔离:Namespace 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-09-28 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ### 1. 前言 20 | 21 | ​ 在上一章Docker安全性能中已有讲解Namespace的功能作用。 22 | 23 | ​ 概念回顾:Docker 是基于 Linux 内核的 **Namespace** 技术实现资源隔离的,**所有的容器都共享主机的内核**。其实这与以虚拟机为代表的云计算时代还是有很多区别的,比如虚拟机有着更好的隔离性和安全性,而容器的隔离性和安全性则相对较弱。 24 | 25 | ​ 虽然Namespace的技术的隔离和安全性相对较弱,那么究竟什么是 Namespace,各种 Namespace 都有什么作用,为什么 Docker 需要 Namespace呢? 26 | 27 | ### 2.什么是Namespace 28 | 29 | ​ **维基百科定义**:Namespace 是 Linux 内核的一项功能,该功能对内核资源进行分区,以使一组进程看到一组资源,而另一组进程看到另一组资源。Namespace 的工作方式通过为一组资源和进程设置相同的 Namespace 而起作用,但是这些 Namespace 引用了不同的资源。资源可能存在于多个 Namespace 中。这些资源可以是进程 ID、主机名、用户 ID、文件名、与网络访问相关的名称和进程间通信。 30 | 31 | ​ **简单来说,**Namespace 是 Linux 内核的一个特性,该**特性可以实现在同一主机系统中,对进程 ID、主机名、用户 ID、文件名、网络和进程间通信等资源的隔离**。Docker 利用 Linux 内核的 Namespace 特性,**实现了每个容器的资源相互隔离,从而保证容器内部只能访问到自己 Namespace 的资源**。 32 | 33 | ![](images/9.Namespaces类型.png) 34 | 35 | 36 | 37 | ### 3.Namespace的作用 38 | 39 | 下面分别讲解六种Namespace的运用: 40 | **1)Mount Namespace** 41 | ​ **Mount Namespace 是 Linux 内核实现的第一个 Namespace,**从内核的 2.4.19 版本开始加入。它可以用来隔离不同的进程或进程组看到的挂载点。通俗地说,就是可以实现在不同的进程中看到不同的挂载目录。使用 Mount Namespace 可以实现容器内只能看到自己的挂载信息,在容器内的挂载操作不会影响主机的挂载目录。 42 | 43 | ​ **unshare** 是 util-linux 工具包中的一个工具,CentOS 7 系统默认已经集成了该工具,使用 unshare 命令可以实现创建并访问不同类型的 Namespace。 44 | 45 | 首先我们使用以下命令创建一个 bash 进程并且新建一个 Mount Namespace: 46 | 47 | ``` 48 | $ sudo unshare --mount --fork /bin/bash 49 | ``` 50 | 51 | 执行完上述命令后,这时我们已经在主机上创建了一个新的 Mount Namespace,并且当前命令行窗口加入了新创建的 Mount Namespace。下面我通过一个例子来验证下,**在独立的 Mount Namespace 内创建挂载目录是不影响主机的挂载目录的。** 52 | 53 | 首先在 /tmp 目录下创建一个目录。 54 | 55 | ``` 56 | [root@centos7 centos]# mkdir /tmp/tmpfs 57 | ``` 58 | 59 | 创建好目录后使用 mount 命令挂载一个 tmpfs 类型的目录。命令如下: 60 | 61 | ``` 62 | [root@centos7 centos]# mount -t tmpfs -o size=20m tmpfs /tmp/tmpfs 63 | ``` 64 | 65 | 然后使用 df 命令查看一下已经挂载的目录信息: 66 | 67 | ``` 68 | [root@centos7 centos]# df -h 69 | Filesystem Size Used Avail Use% Mounted on 70 | /dev/vda1 500G 1.4G 499G 1% / 71 | devtmpfs 16G 0 16G 0% /dev 72 | tmpfs 16G 0 16G 0% /dev/shm 73 | tmpfs 16G 0 16G 0% /sys/fs/cgroup 74 | tmpfs 16G 57M 16G 1% /run 75 | tmpfs 3.2G 0 3.2G 0% /run/user/1000 76 | tmpfs 20M 0 20M 0% /tmp/tmpfs 77 | ``` 78 | 79 | 可以看到 /tmp/tmpfs 目录已经被正确挂载。为了验证主机上并没有挂载此目录,我们新打开一个命令行窗口,同样执行 df 命令查看主机的挂载信息: 80 | 81 | ``` 82 | [centos@centos7 ~]$ df -h 83 | Filesystem Size Used Avail Use% Mounted on 84 | devtmpfs 16G 0 16G 0% /dev 85 | tmpfs 16G 0 16G 0% /dev/shm 86 | tmpfs 16G 57M 16G 1% /run 87 | tmpfs 16G 0 16G 0% /sys/fs/cgroup 88 | /dev/vda1 500G 1.4G 499G 1% / 89 | tmpfs 3.2G 0 3.2G 0% /run/user/1000 90 | ``` 91 | 92 | 通过上面输出可以看到主机上并没有挂载 /tmp/tmpfs,可见我们独立的 Mount Namespace 中执行 mount 操作并不会影响主机。 93 | 94 | 为了进一步验证我们的想法,我们继续在当前命令行窗口查看一下当前进程的 Namespace 信息,命令如下: 95 | 96 | ``` 97 | [root@centos7 centos]# ls -l /proc/self/ns/ 98 | total 0 99 | lrwxrwxrwx. 1 root root 0 Sep 4 08:20 ipc -> ipc:[4026531839] 100 | lrwxrwxrwx. 1 root root 0 Sep 4 08:20 mnt -> mnt:[4026532239] 101 | lrwxrwxrwx. 1 root root 0 Sep 4 08:20 net -> net:[4026531956] 102 | lrwxrwxrwx. 1 root root 0 Sep 4 08:20 pid -> pid:[4026531836] 103 | lrwxrwxrwx. 1 root root 0 Sep 4 08:20 user -> user:[4026531837] 104 | lrwxrwxrwx. 1 root root 0 Sep 4 08:20 uts -> uts:[4026531838] 105 | ``` 106 | 107 | 然后新打开一个命令行窗口,使用相同的命令查看一下主机上的 Namespace 信息: 108 | 109 | ``` 110 | [centos@centos7 ~]$ ls -l /proc/self/ns/ 111 | total 0 112 | lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 ipc -> ipc:[4026531839] 113 | lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 mnt -> mnt:[4026531840] 114 | lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 net -> net:[4026531956] 115 | lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 pid -> pid:[4026531836] 116 | lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 user -> user:[4026531837] 117 | lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 uts -> uts:[4026531838] 118 | ``` 119 | 120 | 通过对比两次命令的输出结果,我们可以看到,除了 Mount Namespace 的 ID 值不一样外,其他Namespace 的 ID 值均一致。 121 | 122 | 通过以上结果我们可以得出结论,使用 unshare 命令可以新建 Mount Namespace,并且在新建的 Mount Namespace 内 mount 是和外部完全隔离的。 123 | 124 | **2)PID Namespace** 125 | ​ **PID Namespace 的作用是用来隔离进程**。在不同的 PID Namespace 中,进程可以拥有相同的 PID 号,利用 PID Namespace 可以实现每个容器的主进程为 1 号进程,而容器内的进程在主机上却拥有不同的PID。例如一个进程在主机上 PID 为 122,使用 PID Namespace 可以实现该进程在容器内看到的 PID 为 1。 126 | 127 | 下面演示PID Namespace的作用。创建一个 bash 进程,并且新建一个 PID Namespace: 128 | 129 | ``` 130 | $ sudo unshare --pid --fork --mount-proc /bin/bash 131 | ``` 132 | 133 | 执行完上述命令后,我们在主机上创建了一个新的 PID Namespace,并且当前命令行窗口加入了新创建的 PID Namespace。在当前的命令行窗口使用 ps aux 命令查看一下进程信息: 134 | 135 | ``` 136 | [root@centos7 centos]# ps aux 137 | USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND 138 | root 1 0.0 0.0 115544 2004 pts/0 S 10:57 0:00 bash 139 | root 10 0.0 0.0 155444 1764 pts/0 R+ 10:59 0:00 ps aux 140 | ``` 141 | 142 | 通过上述命令输出结果可以看到当前 Namespace 下 bash 为 1 号进程,而且我们也看不到主机上的其他进程信息。 143 | 144 | **3)UTS Namespace** 145 | ​ **UTS Namespace 主要是用来隔离主机名的**,它允许每个 UTS Namespace 拥有一个独立的主机名。例如我们的主机名称为 docker,使用 UTS Namespace 可以实现在容器内的主机名称为 utsdocker 或者其他任意自定义主机名。 146 | 147 | 同样我们通过一个实例来验证下 UTS Namespace 的作用,首先我们使用 unshare 命令来创建一个 UTS Namespace: 148 | 149 | ``` 150 | $ sudo unshare --uts --fork /bin/bash 151 | ``` 152 | 153 | 创建好 UTS Namespace 后,当前命令行窗口已经处于一个独立的 UTS Namespace 中,下面我们使用 hostname 命令(hostname 可以用来查看主机名称)设置一下主机名: 154 | 155 | ``` 156 | root@centos7 centos]# hostname -b utsdocker 157 | ``` 158 | 159 | 然后再查看一下主机名: 160 | 161 | ``` 162 | [root@centos7 centos]# hostname 163 | utsdocker 164 | ``` 165 | 166 | 通过上面命令的输出,我们可以看到当前UTS Namespace 内的主机名已经被修改为 utsdocker。然后我们新打开一个命令行窗口,使用相同的命令查看一下主机的 hostname: 167 | 168 | ``` 169 | [centos@centos7 ~]$ hostname 170 | centos7 171 | ``` 172 | 173 | 可以看到主机的名称仍然为 centos7,并没有被修改。由此,可以验证 UTS Namespace 可以用来隔离主机名。 174 | 175 | **4)IPC Namespace** 176 | ​ **IPC Namespace 主要是用来隔离进程间通信的**。**PID Namespace 和 IPC Namespace 一起使用可以实现同一 IPC Namespace 内的进程彼此可以通信**,不同 IPC Namespace 的进程却不能通信。 177 | 178 | 使用 unshare 命令来创建一个 IPC Namespace: 179 | 180 | ``` 181 | $ sudo unshare --ipc --fork /bin/bash 182 | ``` 183 | 184 | 下面我们需要借助两个命令来实现对 IPC Namespace 的验证。 185 | 186 | - ipcs -q 命令:用来查看系统间通信队列列表。 187 | 188 | - ipcmk -Q 命令:用来创建系统间通信队列。 189 | 190 | 191 | 我们首先使用 ipcs -q 命令查看一下当前 IPC Namespace 下的系统通信队列列表: 192 | 193 | ``` 194 | [centos@centos7 ~]$ ipcs -q 195 | ------ Message Queues -------- 196 | key msqid owner perms used-bytes messages 197 | ``` 198 | 199 | 由上可以看到当前无任何系统通信队列,然后我们使用 ipcmk -Q 命令创建一个系统通信队列: 200 | 201 | ``` 202 | [root@centos7 centos]# ipcmk -Q 203 | Message queue id: 0 204 | ``` 205 | 206 | 再次使用 ipcs -q 命令查看当前 IPC Namespace 下的系统通信队列列表: 207 | 208 | ``` 209 | [root@centos7 centos]# ipcs -q 210 | ------ Message Queues -------- 211 | key msqid owner perms used-bytes messages 212 | 0x73682a32 0 root 644 0 0 213 | ``` 214 | 215 | 可以看到我们已经成功创建了一个系统通信队列。然后我们新打开一个命令行窗口,使用ipcs -q 命令查看一下主机的系统通信队列: 216 | 217 | ``` 218 | [centos@centos7 ~]$ ipcs -q 219 | ------ Message Queues -------- 220 | key msqid owner perms used-bytes messages 221 | ``` 222 | 223 | 通过上面的实验,可以发现,在单独的 IPC Namespace 内创建的系统通信队列在主机上无法看到。即 IPC Namespace 实现了系统通信队列的隔离。 224 | 225 | **5)User Namespace** 226 | ​ **User Namespace 主要是用来隔离用户和用户组的**。一个比较典型的应用场景就是在主机上以非 root 用户运行的进程可以在一个单独的 User Namespace 中映射成 root 用户。使用 User Namespace 可以实现进程在容器内拥有 root 权限,而在主机上却只是普通用户。 227 | 228 | User Namesapce 的创建是可以不使用 root 权限的。下面我们以普通用户的身份创建一个 User Namespace,命令如下: 229 | 230 | ``` 231 | [centos@centos7 ~]$ unshare --user -r /bin/bash 232 | ``` 233 | 234 | CentOS7 默认允许创建的 User Namespace 为 0,如果执行上述命令失败( unshare 命令返回的错误为 unshare: unshare failed: Invalid argument ),需要使用以下命令修改系统允许创建的 User Namespace 数量,命令为:echo 65535 > /proc/sys/user/max_user_namespaces,然后再次尝试创建 User Namespace。 235 | 236 | 然后执行 id 命令查看一下当前的用户信息: 237 | 238 | ``` 239 | [root@centos7 ~]# id 240 | uid=0(root) gid=0(root) groups=0(root),65534(nfsnobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 241 | ``` 242 | 243 | 通过上面的输出可以看到我们在新的 User Namespace 内已经是 root 用户了。下面我们使用只有主机 root 用户才可以执行的 reboot 命令来验证一下,在当前命令行窗口执行 reboot 命令: 244 | 245 | ``` 246 | [root@centos7 ~]# reboot 247 | Failed to open /dev/initctl: Permission denied 248 | Failed to talk to init daemon. 249 | ``` 250 | 251 | 可以看到,我们在新创建的 User Namespace 内虽然是 root 用户,但是并没有权限执行 reboot 命令。这说明在隔离的 User Namespace 中,并不能获取到主机的 root 权限,也就是说 User Namespace 实现了用户和用户组的隔离。 252 | 253 | **6)Net Namespace** 254 | ​ **Net Namespace 是用来隔离网络设备、IP 地址和端口等信息的**。Net Namespace 可以让每个进程拥有自己独立 的 IP 地址,端口和网卡信息。例如主机 IP 地址为 172.16.4.1 ,容器内可以设置独立的 IP 地址为 192.168.1.1。 255 | 256 | 同样用实例验证,我们首先使用 ip a 命令查看一下主机上的网络信息: 257 | 258 | ``` 259 | $ ip a 260 | 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 261 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 262 | inet 127.0.0.1/8 scope host lo 263 | valid_lft forever preferred_lft forever 264 | inet6 ::1/128 scope host 265 | valid_lft forever preferred_lft forever 266 | 2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 267 | link/ether 02:11:b0:14:01:0c brd ff:ff:ff:ff:ff:ff 268 | inet 172.20.1.11/24 brd 172.20.1.255 scope global dynamic eth0 269 | valid_lft 86063337sec preferred_lft 86063337sec 270 | inet6 fe80::11:b0ff:fe14:10c/64 scope link 271 | valid_lft forever preferred_lft forever 272 | 3: docker0: mtu 1500 qdisc noqueue state DOWN group default 273 | link/ether 02:42:82:8d:a0:df brd ff:ff:ff:ff:ff:ff 274 | inet 172.17.0.1/16 scope global docker0 275 | valid_lft forever preferred_lft forever 276 | inet6 fe80::42:82ff:fe8d:a0df/64 scope link 277 | valid_lft forever preferred_lft forever 278 | ``` 279 | 280 | 然后我们使用以下命令创建一个 Net Namespace: 281 | 282 | ``` 283 | $ sudo unshare --net --fork /bin/bash 284 | ``` 285 | 286 | 同样的我们使用 ip a 命令查看一下网络信息: 287 | 288 | ``` 289 | [root@centos7 centos]# ip a 290 | 1: lo: mtu 65536 qdisc noop state DOWN group default qlen 1000 291 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 292 | ``` 293 | 294 | 可以看到,宿主机上有 lo、eth0、docker0 等网络设备,而我们新建的 Net Namespace 内则与主机上的网络设备不同。 295 | 296 | 297 | 298 | ### 4. 总结 299 | 300 | ​ Linux 内核从 2002 年 2.4.19 版本开始加入了 Mount Namespace,而直到内核 3.8 版本加入了 User Namespace 才为容器提供了足够的支持功能。 301 | 302 | ​ 当 Docker 新建一个容器时, 它会创建这六种 Namespace,然后将容器中的进程加入这些 Namespace 之中,使得 Docker 容器中的进程只能看到当前 Namespace 中的系统资源。 303 | 304 | ​ 正是由于 Docker 使用了 Linux 的这些 Namespace 技术,才实现了 Docker 容器的隔离,可以说没有 Namespace,就没有 Docker 容器。 305 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker深度学习 2 | ​ ![](images/logo.png) 3 | 4 | Docker深入了解与掌握: ![]() ![]() 5 | 6 | Docker的学习大致分为5个阶段:Docker基础内容、Docker底层技术、Docker框架设计、Docker容器编排、Docker实战案例 7 | 8 | **目录:** 9 | 10 | #### 1. Docker基础内容: 11 | 12 | - [docker技术原理了解](1.Docker技术原理.md) 13 | - [docker核心设计概念:镜像,容器,仓库](2.Docker容器、镜像、仓库.md) 14 | - [docker镜像配置与原理](3.Docker镜像配置与原理.md) 15 | - [docker容器的使用](4.Docker容器的使用.md) 16 | - [Dockerfile的编写规则](5.Dockerfile的编写规则.md) 17 | - [docker安全性能了解](7.Docker安全性能了解.md) 18 | 19 | #### **2. Docker底层技术:** 20 | 21 | - [docker底层资源隔离:Namespace](9.Docker底层资源隔离namespace.md) 22 | - [docker底层资源限制:Cgroups](10.Docker底层资源限制cgroups.md) 23 | 24 | #### 3. Docker架构设计: 25 | 26 | - [docker架构设计](6.Docker架构设计.md) 27 | - [docker安全性能提升(kata container)](8.Docker安全性能提升.md) 28 | 29 | #### 4. Docker容器编排 30 | 31 | #### 5. Docker实战案例 32 | 33 | - [Docker Compose编排](./实战演示/Docker-Compose编排技术.md) 34 | 35 | #### 6. Harbor搭建 36 | 37 | - [Docker Harbor](./Harbor私有仓库搭建.md) 38 | 39 | -------------------------------------------------------------------------------- /images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/.DS_Store -------------------------------------------------------------------------------- /images/11Docker-compose安装.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/11Docker-compose安装.png -------------------------------------------------------------------------------- /images/2docker-架构图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/2docker-架构图.png -------------------------------------------------------------------------------- /images/2docker-组件.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/2docker-组件.png -------------------------------------------------------------------------------- /images/2docker-进程数机构1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/2docker-进程数机构1.png -------------------------------------------------------------------------------- /images/2docker容器关系图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/2docker容器关系图.png -------------------------------------------------------------------------------- /images/3docker-bootfs-rootfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/3docker-bootfs-rootfs.png -------------------------------------------------------------------------------- /images/3docker-bootfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/3docker-bootfs.png -------------------------------------------------------------------------------- /images/3docker-container1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/3docker-container1.png -------------------------------------------------------------------------------- /images/3docker-container2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/3docker-container2.png -------------------------------------------------------------------------------- /images/3docker-kernel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/3docker-kernel.png -------------------------------------------------------------------------------- /images/3docker-镜像分层.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/3docker-镜像分层.png -------------------------------------------------------------------------------- /images/3docker-镜像操作.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/3docker-镜像操作.png -------------------------------------------------------------------------------- /images/4docker-容器层.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/4docker-容器层.png -------------------------------------------------------------------------------- /images/4docker-容器状态.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/4docker-容器状态.png -------------------------------------------------------------------------------- /images/6.Docker架构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/6.Docker架构.png -------------------------------------------------------------------------------- /images/7.docker与虚拟机的区别.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/7.docker与虚拟机的区别.png -------------------------------------------------------------------------------- /images/8.Docker with Kata架构设计.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/8.Docker with Kata架构设计.png -------------------------------------------------------------------------------- /images/8.docker的runtime架构图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/8.docker的runtime架构图.png -------------------------------------------------------------------------------- /images/8.docker的runtime架构图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/8.docker的runtime架构图2.png -------------------------------------------------------------------------------- /images/8.kata-container原理png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/8.kata-container原理png.png -------------------------------------------------------------------------------- /images/9.Namespaces类型.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/9.Namespaces类型.png -------------------------------------------------------------------------------- /images/cgroups-cpu-full-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/cgroups-cpu-full-top.png -------------------------------------------------------------------------------- /images/cgroups-cpu-quota-us.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/cgroups-cpu-quota-us.png -------------------------------------------------------------------------------- /images/cgroups-cpu-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/cgroups-cpu-top.png -------------------------------------------------------------------------------- /images/cgroups-memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/cgroups-memory.png -------------------------------------------------------------------------------- /images/cgroups1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/cgroups1.png -------------------------------------------------------------------------------- /images/cgroups2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/cgroups2.png -------------------------------------------------------------------------------- /images/chroot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/chroot1.png -------------------------------------------------------------------------------- /images/docker1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/docker1.png -------------------------------------------------------------------------------- /images/k8s架构图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/k8s架构图.jpg -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/images/logo.png -------------------------------------------------------------------------------- /实战演示/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/实战演示/.DS_Store -------------------------------------------------------------------------------- /实战演示/Docker-Compose编排技术.md: -------------------------------------------------------------------------------- 1 | # (实战)Docker-Compose编排技术 2 | 3 | > Auth: 王海飞 4 | > 5 | > Data:2020-11-30 6 | > 7 | > QQ群:223058292 8 | > 9 | > WX:wanghaifeige 10 | > 11 | > Email:779598160@qq.com 12 | > 13 | > github:https://github.com/coco369/docker-depth-learning 14 | > 15 | > 知乎Docker专栏: 16 | > 17 | > 知乎Python入门专栏: 18 | 19 | ### 1. 前言 20 | 21 | -------------------------------------------------------------------------------- /实战演示/images/1.proc文件.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coco369/docker-depth-learning/02625440902b71bb5784c4aa105307e98285b0d2/实战演示/images/1.proc文件.png --------------------------------------------------------------------------------