前言
不想换个电脑重新配置下参数,这个配置可以说是相当的繁琐,比如说各种软件的配置,模块的兼容等等,以及交互式的界面对于提高自己的编程水平感觉并不高,因此有时需要一个无界面的命令行,强迫自己去使用纯命令行,简而言之,就是自虐。。。。。后来在学习过程中发现了个有意思的工具,没错,就是本文打算入坑的一个虚拟机。是一个属于Linux 容器的一种封装。不同操作系统的安装不太一样,这个过程比较简单,可以自行搜索。由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的,不用担心运行环境的变化导致应用无法正常运行的情况,就像后来为什么喜欢上了overleaf那样。。。。。,另外一点,这种虚拟机的性能,接近于原生,这就很爽了。。。
初识
Docker 包括三个基本概念,镜像(Image
) 容器(Container
) 仓库(Repository
)
镜像
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资 源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境 变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被 创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的
命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、 自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环 境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容 器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会把容器和虚拟机搞混。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此, 任何保存于容器存储层的信息都会随容器删除而丢失。
仓库
一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多 个标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的 各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版 本的镜像。如果不给出标签,将以 latest 作为默认标签。仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy ,前者往往意 味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这 并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。
安装Ubuntu 和 Debian
官方为了简化安装流程,提供了一套安装脚本,Ubuntu 和 Debian 系统可 以使用这套脚本安装:
curl -sSL https://get.docker.com/ | sh |
由于墙的原因,可能会存在安装不上的原因,可使用阿里云来进行安装
curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/ docker-engine/internet | sh - |
使用 Docker 镜像
Docker 运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker 会从 镜像仓库下载(默认是 Docker Hub 公共注册服务器中的仓库)。
从 Docker Registry 获取镜像的命令是 docker pull
。其命令格式为:
docker pull [选项] [Docker Registry地址]<仓库名>:<标签> |
Docker Registry地址:地址的格式一般是 <域名/IP>[端口号]。默认地址是 Docker Hub。仓库名是两段式名称,既 <用户名>/<软件名> 。 对于 Docker Hub,如果不给出用户名,则默认为 library ,也就是官方镜像。
比如
docker pull ubuntu:14.04 |
运行
有了镜像后,我们就可以以这个镜像为基础启动一个容器来运行。以上面的 ubuntu:14.04 为例,如果我们打算启动里面的 bash 并且进行交互式操作的话,可以执行下面的命令。
docker run -it --rm ubuntu:14.04 bash |
docker run
就是运行容器的命令,-it :这是两个参数,一个是-i:交互式操作,一个是 -t:终端。—rm :这个参数是说容器退出后随之将其删除。默认情况下,为了排障需
求,退出的容器并不会立即删除,除非手动 docker rm 。
列出镜像
要想列出已经下载下来的镜像,可以使用docker images
命令
列出部分镜像
不加任何参数的情况下, docker images 会列出所有顶级镜像,但是有时候我们 只希望列出部分镜像。 docker images 有好几个参数可以帮助做到这个事情。 根据仓库名列出镜像
docker images ubuntu |
列出特定的某个镜像,也就是说指定仓库名和标签
docker images ubuntu:16.04 |
利用 commit 理解镜像构成
镜像是容器的基础,每次执行 docker run
的时候都会指定哪个镜像作为容器运 行的基础。在之前的例子中,我们所使用的都是来自于 Docker Hub 的镜像。直接使用这些镜像是可以满足一定的需求,而当这些镜像无法直接满足需求时,我们就需要定制这些镜像。镜像是多层存储,每一层是在前一层的基础上进行的修改;而容器同样也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层。
定制一个 Web 服务器为例子,来讲解镜像是如何构建的。
docker run --name webserver -d -p 80:80 nginx |
这条命令会用 nginx
镜像启动一个容器,命名为 webserver ,并且映射了 80 端口,这样我们可以用浏览器去访问这个 nginx 服务器。
如果是在 Linux 本机运行的 Docker,或者如果使用的是 Docker for Mac、Docker for Windows,那么可以直接访问:http://localhost;如果使用的是 Docker Toolbox,或者是在虚拟机、云服务器上安装的 Docker,则需要将 localhost 换为虚拟机地址或者实际云服务器地址。
当运行一个容器的时候(如果不使用卷的话),做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
docker commit
的语法格式为:
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]] |
可以在 docker images 中看到这个新定制的镜像,新的镜像定制好后,可以来运行这个镜像。
docker run --name web2 -d -p 81:80 nginx:v2 |
这里命名为新的服务为 web2 ,并且映射到 81 端口。如果是 Docker for Mac/Windows 或 Linux 桌面的话,我们就可以直接访问 http://localhost:81 看到结果,其内容应该和之前修改后的 webserver 一样。
除当前层外,之前的每一层都是不会发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添 加、修改,而不会改动上一层。如果使用 docker commit 制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。
docker commit 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用 docker commit 定制镜像,定制行为应该使用 Dockerfile 来完成。
运行(windows 下)
如何在Windows下运行,可以首先使用docker images看看自己的镜像,然后在powershell里面输入类似的语法即可:
docker run -it lsqy_ubuntu:laster //bin/bash |
对于喜欢linux风格的,可以在git bash 下输入
winpty docker run -it lsqy_ubuntu:latest bash |
一开始也想通过powershell的方式,输入的,结果报了这样的错误the input device is not a TTY. If you are using mintty, try prefixing the comma
保存修改后的镜像
一开始很费解,为什么在里面安装了一些软件后,比如配置好了一些环境变量,再次打开的时候,发现之前修改的都不存在了。可以将容器打包成镜像,需要指定要提交保存容器的ID,类似于
docker commit 214e52618d54 lsqyrobot/lsqy_ubuntu:latest |
这里的214e52618d54
就是制定要提交保存的容器ID, 当然,从上面所说的 利用 commit 理解镜像构成
那部分内容,也容易理解为什么做了些修改后,再次重启就不复存在了,这当然有很多好处,比如自己在机器人上开发一些功能,开发后与开发前提升了哪些性能,当然是与开发前相比了,所以每次开机都会恢复成最初的状态,以便于对比和检查更新前后的差别,反复确保没有问题后,方可更新。
使用 Dockerfile 定制镜像
从 docker commit
的学习可以了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作 的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。以 nginx 镜像为例,使用 Dockerfile 来定制。 在一个空白目录中,建立一个文本文件,并命名为 Dockerfile :
mkdir mynginx |
其内容为:
FROM nginx RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index .html |
参考
[1] Docker 入门教程——阮一峰
[2] 《docker 从入门到实践》—杨宝华等