Docker Tutorial
Docker
Preliminaries
使用Docker之前必须要先安装docker!参考链接 Docker安装[1]。笔者实测在如下本地环境中可以正常安装:
1 |
|
Docker安装直接sudo apt install
会报错,需要先更新软件源之后再sudo apt update
刷新缓存之后,就可以正常的安装。
Introduction
Why We Need Docker?
依赖地狱问题[2],是指在计算机软件中,是指在操作系统中由于软件之间的依赖性不能被满足而引发的问题。一个软件包依赖于其它必要的软件包(且版本要符合要求),使得软件包系统形成了复杂的依赖关系网络,并可能引发一系列问题。一些软件包可能因为依赖性无法满足,需要安装大量软件包;另一方面,一个软件包的卸载可能引发数量众多的软件包无法工作。
这就会导致同一个软件在更换环境之后出现诸多异常的情况(对底层软件和工具链的依赖性过强),进而导致一系列的环境兼容问题。
如何解决,一种很常见的方法是只要高级的包管理工具,例如Debian
的apt
系列(apt install
就是在安装软件包)。而虚拟机(例如VMware)实现了另一种解决方案,即没有一致的环境就创造一致的环境,通过分系统的配置可以实现环境的统一。
但是虚拟机的一大痛点就是过于笨重,因为需要模拟完整的操作系统,Windows OS动辄几十GB,同时安装VM虚拟机需要直接使用主机的内存和CPU。而Docker实现了这一问题的轻量级实现,在低资源占用,快启动,高性能的基础之上完美的实现了版本和环境的隔离!
CI/CD
CI/CD(Continuous Integration / Continuous Delivery or Deployment)是现代软件开发的核心实践,旨在通过自动化流程快速、可靠地构建、测试和发布代码。它是 DevOps 文化的关键技术支撑。
在CI/CD中,从开发环境到测试环境中的build
就需要使用Docker镜像!这样有助于开发和测试人员统一环境配置,更好的对代码进行审查。
CI/CD是一个很有意思的东西~希望以后可以开个新坑讲一讲。
Quick Startup
我们当然可以直接上代码!但是笔者希望先讲一点点的原理和基本概念~
虚拟机笨重的很重要的一个原因就是他需要和硬件进行交互,并且模拟操作系统的运行。而Docker很聪明的绕过了这一点,将Docker Engine
建立在了操作系统之上,作为链接各种软件和底层操作系统的拦路虎,并在此之上建立相互隔离的Docker Container
,实现不同软件包的隔离。
Docker Image
在虚拟机中,存在快照(Snapshot)的概念,即在特定时间点保存虚拟机状态的技术。快照可以捕获虚拟机的整个运行状态,包括其内存、CPU 状态、硬盘内容和网络状态。这样,用户可以在将来恢复到这个状态,方便进行测试、备份或恢复操作。对于Docker来说,Docker Image就是一种更轻量级的快照(因为在操作系统的上层),一个只读的模板,包含运行应用所需的代码、运行时环境、库和配置。
Docker Container
在准备好镜像之后,将镜像实例化就是创建Docker Container的过程,并且不同的Container之间实现了隔离的功能,(可以把Docker Image看做抽象出来的类,而Docker Container就是实例化之后的对象),在Docker Container中,我们就有了修改的权限。
Docker File
如何构建镜像?我们不可能手动安装每一个依赖包(这还不如虚拟机),因此我们就需要编写自动化脚本来实现镜像的构建,有点类似于cmakelists.txt
。
Initial Project
在学习完Docker的这三个基本概念之后,就可以开始上手第一个Docker项目了!我们来看下面的示例:我们希望使用Docker“隔离”出一个环境来运行Helloworld,同时,为了增加复杂性,我们将会引入pip包管理系统,来看看Docker如何在创建image的时候自动的就把环境给配好了。
首先,我们需要创建一个working directory:
1 |
|
其中Dockerfile是脚本,而剩下两个文件就是实际Python开发中需要的文件。
1 |
|
1 |
|
接下来,我们需要创建一个Image,可以来支持运行我们基础的Python文件,我们可以使用如下的Dockerfile来实现这个功能:
1 |
|
FROM python:3.9-slim
代表下载Docker官方的Python镜像,可以在Docker Hub上找到你想要的镜像。WORKDIR /app
是设置容器的工作目录。(这一点在后面会讲到)COPY . .
代表将当前目录的文件复制到容器的/app目录,当然你也可以只复制你想要的。RUN
部分的指令是Docker的精髓:RUN
指令用于在构建镜像时执行命令(例如安装软件、配置环境等)- 他支持命令行脚本语句,具体就是使用
/bin/sh -c
- 也只是exec格式,相当于更灵活的命令行parser:
RUN ["/bin/bash", "-c", "echo Hello World"]
CMD
部分和RUN
非常想,但是实在容器启动(已经被实例化)之后需要执行的命令。
简单来说,
RUN
就是安装环境,而CMD
就是执行命令。
故意报点错
我们不妨先将RUN那一行注释掉,看会发生什么:
1 |
|
使用docker build
来实现构建docker镜像(Dockerfile -> Docker Image的过程),-t
后接后缀镜像的名字,注意最后的 .
表示当前目录是构建上下文。
1 |
|
Docker build成功了!接下来我们使用docker run
来实现镜像的实例化。
1 |
|
其中python-helloworld
就是镜像的名字,而--rm
代表创建临时镜像,适合作为临时的调试。
非常推荐安装Vscode的Docker插件,可以实时监控Docker的镜像和示例container:
貌似出现报错了…看看报错信息
1 |
|
也就是说,Docker已经成功设置好了一个Python的解释器的镜像(在Python-helloworld中),但是未安装相关包numpy
的依赖,导致脚本文件运行失败。
完整运行
现在我们把注释掉的命令恢复,docker build
again!
1 |
|
我们发现他成功执行了我们的脚本!这下再Docker run就可以正常输出信息了!
1 |
|
Some tips
我们也可以直接使用docker pull
来安装镜像!例如执行docker pull amd64/python
,就拉取了一个针对 AMD64 架构的官方 Python 镜像,相当于安装了一个Python解释器,同样,我们也可以针对这个docker image进行docker run
。
More Advanced Usage
到这里,Docker的基本命令就差不多了!个人感觉Docker的CLI做的还是比较清爽的,不像某些bash脚本一样要加上各种各样奇奇怪怪的后缀😅。
For more advanced usage, to be continued in the future.