docker

前言

不想换个电脑重新配置下参数,这个配置可以说是相当的繁琐,比如说各种软件的配置,模块的兼容等等,以及交互式的界面对于提高自己的编程水平感觉并不高,因此有时需要一个无界面的命令行,强迫自己去使用纯命令行,简而言之,就是自虐。。。。。后来在学习过程中发现了个有意思的工具,没错,就是本文打算入坑的一个虚拟机。是一个属于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 
$ cd mynginx
$ touch Dockerfile

其内容为:

FROM nginx RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index .html

参考

[1] Docker 入门教程——阮一峰

[2] 《docker 从入门到实践》—杨宝华等

CMakeLists Eigen FCPX GNU Gazebo Git Interest KDL Life Linux Matrix ODE ROS Ros UML Ubuntu VcXsrv algorithm algorithms axis-angle bode calibration chrome control cpp data_struct dots figure gdb latex launch life linux mac math matlab memory motor moveit operator optimal algorithm python robot robotics ros ros2 rtb simulation stl thread tools twist urdf velocity vim web work wsl
知识共享许可协议