docker 入门上篇

851 阅读19分钟

docker 这么火,我怎么能不了解下?

从我听到的关于 docker 的关键词,诸如 容器go语言编写虚拟化打包移植等等,就觉得它是一个逼格有点高的东西。而且这玩意似乎不是给前端准备的,毕竟我们前端只是写写页面( ̄∇ ̄),这东西太复杂了。

最后我当然还是去学习它,并慢慢尝试用它。你要问原因,那就是真香。

本文涉及以下内容:

  • Docker 用途理解
  • Docker 安装及加速器配置
  • Docker 相关概念理解,以及学习相关命令,如镜像命令和容器命令
  • Docker 数据卷和数据卷容器
  • Docker 安装 nginx 实践

理解 Docker 的用途

语言是苍白的,但我还是想去说些什么。如果去搜索 docker 的定义,给出的答案就是一种容器引擎,开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上。第一次看到这个解释时我是似懂非懂的,我把容器理解为一个盒子,然后把代码打包到这个盒子里,就可以移植到别处运行。作为页面仔,我也不知道理解的对不对,而且没弄懂这么做的具体意义,这种操作给我的感觉就像是打包一个app,然后可以下载到别人手机上用。不知道是不是只有我这么认为。后来在看过很多关于 docker 的文章后,我才了解到 docker 的用途。

docker 可以解决软件运行环境不一致所带来的一系列问题。你是否听过这样一个灵魂质问:“我明明在本地运行地好好的,怎么你线上就崩了???”

这是因为开发环境和生产环境基本不一样,代码在哪运行,就要重新配置一套环境,这样除了带来时间成本外,一个大问题就是生产环境可能部署了不止一个项目,它们所依赖的环境版本不一样。比如我本地用 nodejs 最新版本开发好了一个项目,想要部署到服务器,但是服务器上已经部署了一个 nodejs 项目,并且使用的是很久很久以前的一个版本,此时再直接部署新项目显然就有很大的隐患了。虽然有其他办法解决,但有了 docker 后,一切变的更优雅了。

啰嗦了这么多,其实 docker 的作用就是把应用代码和所依赖的环境一起打包成一个文件,运行这个文件,会生成一个虚拟容器,代码在这个容器里运行,就像在物理机上运行一样,不受外界环境的影响。还有一个概念要了解一下,就是容器本身所处的环境,通常成为“宿主机”,这可以是一个 Linux 或 Windows 的机器。

总结成一句话就是:docker 是一种容器技术,主要解决代码跨环境迁移的问题

安装 Docker

安装教程实在很多,我这里演示的是在一台 CentOS 7.5 64位 服务器上的安装,参考的 菜鸟教程。别的系统的安装也可以自行查看。

首先安装所需的软件包:

sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

使用以下命令来设置稳定的仓库:

sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

安装最新版本的 Docker Engine-Community 和 containerd:

sudo yum install docker-ce docker-ce-cli containerd.io

安装完成后输入 docker version 验证是否安装成功。

配置镜像加速器

跟 github 类似,docker也有一个镜像存储库 DockerHub(关于镜像,后面会说),由于国内网络原因,我们在拉取一些已有镜像时会遇到困难,所以需要配置加速器。加速器有很多种,这里推荐使用阿里云的加速器,它是免费的,不需要购买任何东西。

登录阿里云后搜索关键词 镜像服务,选择容器镜像服务:

第一次使用时,会让你设置登录密码,设置一下即可:

然后在左侧菜单点击镜像加速器,右边可以看到给你提供了一个加速地址,这个地址每个人的都不一样。阿里云很贴心地在下方给出直接运行的命令代码,选择你的平台,复制粘贴所有代码,直接运行在终端即可:

运行结束后,可以使用命令 cat /etc/docker/daemon.json 查看是否设置成功,结果如下:

{
  "registry-mirrors": ["https://55zqg9lk.mirror.aliyuncs.com"]
}

如果你想替换别的加速地址,直接修改后面的地址即可,修改完不要忘记运行:

sudo systemctl daemon-reload
sudo systemctl restart docker

了解 Docker 的基础概念

docker 有三个基本概念:

  • 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
  • 容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
  • 仓库(Repository):仓库可看着一个代码控制中心,用来保存镜像。

再说一下,镜像 其实就是打包出来的包含代码和依赖环境的文件,我们可以使用别人制作好的镜像,也可以自己制作镜像。而 容器 就是根据镜像创建的,同一个镜像可以创建很多容器,就像对象的关系。

另外,Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器,可以用一张图来了解:

我们先看 Hosts,它是 Docker 主机,用于执行 Docker 守护进程和容器。它分为本地主机和远程主机,主机下包含 daemon(守护程序)container(容器)image(镜像)daemon是一个常驻在后台的系统进程,当我们安装完成 Docker 后,它就存在。

Registries 是仓库,Docker 有一个官方的镜像仓库Docker Hub,与github类似,我们可以获取或上传镜像。我们也可以创建自己的私有镜像库。

Clients 是Docker客户端,通过命令行与守护进程通信。

Docker 相关命令

Docker 命令可以分三部分来说,即服务命令镜像命令容器命令

服务命令

服务命令指的是操作 daemon 服务的命令,操控的是 docker 进程。它主要包含5个:

  • 查看 docker 状态:systemctl status docker
  • 启动 docker 服务:systemctl start docker
  • 停止 docker 服务:systemctl stop docker
  • 重启 docker 服务:systemctl restart docker
  • 开机启动 docker 服务:systemctl enable docker

首先我们可以查看一下当前 docker 服务状态,使用 systemctl status docker

绿色部分的 active (running) 就表示 docker 正在运行,我们再来停止一次服务,并查看状态:

如图,inactive (dead) 就表示 docker 服务已停止。再试下重启服务:

开机启动命令这里就不演示了。

镜像命令

镜像命令用于操作镜像(images)相关。

查看镜像

查看本地所有镜像,可使用命令:

docker images

可以看到我的本地有一个 redis 的镜像,这是因为我提前安装了,如果没安装就是空的。显示结果的第一行是表头,分别表示为镜像名称版本号镜像 id创建时间镜像大小

查看所有镜像的 id,可以使用命令:

docker images -q

我这里只有一个镜像,所以只显示了一个 id,如果有多个镜像的话会显示所有的镜像 id。

搜索镜像

有时候我们并不知道库里是否有我们需要的镜像,这时候就需要先搜索,使用命令:

docker search 镜像名称

比如我们来搜索 redis

如图所示,会将所有 redis 相关源信息打印出来,第一行依旧是头,依次表示为:名称描述star 数是否是官方出的自动构建

拉取镜像

拉取镜像即从 docker 仓库下载镜像到本地,使用命令:

docker pull 镜像名称

或者下载指定版本:

docker pull 镜像名称:版本号

注意,当不指定版本号时,默认下载最新版本(latest版)。另外要指定下载某个版本时,可以去 DockerHub 查看镜像对应的一些版本号,不要随便自定义版本号。下载就不演示了。

删除镜像

删除指定镜像,使用命令:

docker rmi 镜像id

注意这里是要指定镜像的 id ,而不是名称。我们可以先使用 docker images 来查看镜像的id,然后再来删除。删除 redis 测试:

第一步查看所有镜像时,显示有两个镜像,删除 redis 后再查看时只有一个了,说明删除成功。

另外,当一次删除多个镜像时,可以将镜像id依次输入进去,这样:

docker rmi id1 id2 id3...

当要删除所有的镜像时,可以使用这样一个组合命令:

docker rmi `docker images -q`

这句命令的意思很明显,就是将 docker images -q 查询到的所有镜像 id 传给删除命令,以达到删除所有镜像的效果。

容器命令

容器命令用于操控容器相关,是比较重要的部分,我们最终直接用于生产的也就是容器。

查看容器

查看本地所有正在运行中的容器,使用命令:

docker ps

查看本地所有的容器(不管是否运行),使用命令:

docker ps -a

由于我本地暂未有容器,所以两个命令结果都为空。结果中的表头含义依次为:容器 ID使用的镜像启动容器时运行的命令容器的创建时间容器状态容器的端口信息和使用的连接类型容器名称

创建并启动容器

容器是根据镜像创建的,如果本地有镜像,可以直接使用,如果没有,会自动下载镜像,然后再创建容器。创建容器使用命令:

docker run [OPTIONS] IMAGE [COMMAND]

[OPTIONS] 是可选参数,IMAGE 是创建容器要根据的镜像,[COMMAND] 是启动容器后执行的命令,可选。

这里说一下 OPTIONS 参数,官方提供了很多可选参数,我们不需要一开始就去认识所有的参数,后面遇到的会再说,这里先只列举几个常用的了解一下:

  • -i:以交互模式运行容器,保持容器运行状态,通常与 -t 同时使用;
  • -t:为容器重新分配一个伪终端,通常与 -i 同时使用;
  • -d:以后台模式运行容器,创建一个容器在后台运行,需要使用 docker exec 进入容器,退出后容器不会关闭;
  • --name:为创建的容器命名(注意这里是长参数)。

现在举例来创建一个容器,选取镜像 centos,使用以下命令:

docker run -it --name=c1 centos /bin/bash

来分析下这句命令的含义:

首先参数 -it-i -t 的合并写法,linux 的基础知识就不多说了,这两个参数同时使用表示创建的是一个交互式容器,创建完成后会自动进入容器,退出容器后,容器会关闭。开始这里可能不太明白什么意思,往后看。

--name=c1 表示给这个容器取名为 c1,也可以不加等号 --name c1,另外如果不主动取名,会自动生成一个名字。

centos 是镜像名称,在使用一个镜像前最好先搜索是否有这个镜像,同时我们也可以指定镜像的版本,即 centos:7

/bin/bash 是一个命令,他会在创建容器完成并进入容器时执行,非必须。

现在我们在终端执行这句命令:

首先看图中红框里,因为本地没有 centos 这个镜像,所以会自动在远程仓库拉取这个镜像,并且因为没指定版本,所以下载的是最新版的,之后就根据这个镜像创建完成了容器。

创建完成后会自动进入容器里,此时注意绿框中的命令提示符,显然已经不是在宿主机的终端了,而是容器内部的终端(因为 -t 参数),所以此时再执行的命令只对容器内部有效。退出容器,可执行 exit 命令:

退出后可以看到命令提示符中显示的又是宿主机了。需要知道的是,此时退出容器后,容器是关闭了的,再次强调一下使用 -it 参数创建的容器是交互式容器,退出即关闭,不信的话可以执行 docker ps 命令查看执行中的容器,它并不在列。

上面这种交互式容器意味着我们不能退出,不然就关闭了,所以很多时候我们需要另外一种方式创建容器,让它在后台一直运行。

我们使用另外一种方式创建容器,使用命令:

docker run -id --name=c2 centos

这条命令,使用 -d 参数替换掉了 -t,表示创建的是一个后台运行的容器,同时去除掉了 /bin/bash命令,因为创建后不会进入容器,不需要执行命令。需要注意的是使用的还是同一个镜像,同一个镜像可以创建很多容器。

运行结果:

可以看到,创建完成后只返回了容器id,并没有进入容器内部,命令提示符显示还是在宿主机终端上。但此时这个容器是在运行的,使用 docker ps 来验证:

我们可以进入容器来操作它,进入容器的命令是 docker exec,我们先执行 docker exec --help 看看它有什么要求:

可以看到它必须要跟上容器名和一个命令,所以我们可以执行:

docker exec c2 /bin/bash

但是这样执行,我们就拿不到一个可以在容器内部执行的终端,所以还需要加上 -it 参数,如下:

docker exec -it c2 /bin/bash

还是根据命令提示符,我们已经进入到容器内部,此时同样可以使用 exit 命令退出容器,但是此时退出并不会关闭容器,始终在后台运行,只能手动关闭(关闭容器命令后面说)。

需要注意的是, docker exec 进入容器操作不是仅针对 -id 形式创建的容器,使用 -it 形式创建的容器退出后,也是要用这个命令重新进入,并且进入之前需要手动启动容器。

小结:此小节我们知道创建容器有两种方式,使用 -it 参数创建的是交互式容器,创建完自动进入容器,退出时自动关闭容器。使用 -id 参数创建的是守护式容器,即创建完运行在后台,需要使用 docker exec 进入容器,使用 exit 退出,并且退出时不会关闭容器。

启动/停止容器

启动容器,使用命令:

docker start 容器ID或者容器名

停止容器,使用命令:

docker stop 容器ID或者容器名

在使用之前可以先用 docker ps -a 来查看容器的相关信息。

删除容器

删除容器使用命令:

docker rm 容器ID或者容器名

注意不能删除正在运行的容器,需要先关闭容器,才能删除。

如果要一次删除所有容器,可以使用这个命令:

docker rm `docker ps -aq`

docker ps -aq 表示查询所有容器的id,然后将结果传给 docker rm,以完成删除所有。需要注意这里删除所有的前提是所有的容器都处于关闭状态。

查看某容器信息

要查看某一个容器的信息,可以使用命令:

docker inspect 容器ID或者容器名

数据卷

数据卷是什么?

数据卷简单来说,就是宿主机中的一个特殊目录,它会与容器内目录形成映射关系。用一张图来表示层级关系如下:

数据卷有什么用?

有这样一个问题是否想过,就是当容器删除后,它产生的数据是否会销毁?答案是肯定的,因为容器是隔离运行的,数据也是产生在容器内部而不是在宿主机上,当容器被销毁时,所产生的数据也会被销毁。这显然是不安全的,我们需要对容器数据进行备份。而数据卷的作用就是用来做数据持久化的,它完全独立与容器的生命周期,容器删除时也不会删除其挂载的数据卷。

配置数据卷的方式其实就是在创建容器的同时,使用 -v 参数来配置,方式如下:

docker run ... -v 宿主机文件夹:容器内文件夹 ...

注意事项:

  • 文件夹路径要是绝对路径
  • 如果文件夹不存在,会自动创建
  • 可以挂载多个数据卷,几多次使用 -v 参数

来举个例子:

docker run -it --name=c1 -v ~/data:/root/data_container centos /bin/bash

创建完成后,可以在容器的 /root 中看到 data_container 这个文件夹。同时宿主机的 ~/data 文件夹也肯定存在了,它就是数据卷。

怎么使用数据卷呢?其实当配置好了数据卷后,不管是在宿主机上的操作还是在容器中的操作,都会自动同步。来举个例子,我们在容器 /root/data_container 目录中创建一个文件 test.txt

接下来,在新窗口查看宿主机的 ~/data 目录下是否有 test.txt 文件:

显然,文件是被同步的。反过来在宿主机上的操作,也是同步到容器中的,这里就不多演示了。

正是因为数据卷的这种同步功能,所以可以用来进行两个容器之间的间接通信,原理无非就是两个容器同时挂载到一个数据卷中,谁修改了,另一个也会收到更新。

另外,你也可以挂载多个数据卷,像这样:

docker run -it --name=c2 -v ~/data1:/root/data1 -v ~/data2:/root/data2 centos /bin/bash

数据卷容器

数据卷容器首先是一个容器,它挂载数据卷,然后其他容器通过挂载这个容器实现数据共享。同样用一张图表示:

数据卷容器相当于起了一个桥梁作用,它挂载数据卷,然后其他容器间要通信时就不需要一一再与数据卷挂载,直接挂载到数据卷容器上即可,简化了操作。

首先我们来创建数据卷容器 c3,(注意我每次举例前都清空了现有容器,所以一直能用重复的名称,容器名大家随意~)

docker run -it --name=c3 -v /volume centos /bin/bash

与之前配置数据卷不同,这里的 -v 参数后面只接了一个目录,这个目录是我们自定义的,它是属于容器内的。强调一下,这个目录是属于容器内的,不是宿主机上的。此时创建后,宿主机上会自动生成一个目录来映射(图中的数据卷是自动生成的,不是手动配置的)。

当运行命令后,容器内部已经生成了 volume 目录,现在我们回到宿主机环境,使用 docker inspect c3 命令来查看此容器的一些信息:

Mounts 数组中,“Source” 后面的就是宿主机上的数据卷,它是创建数据卷容器时自动创建的,“Destination” 表示目标地址,就是我们创建数据卷容器的自定义的 volume 目录。

然后我们在创建两个容器:

docker run -it --name=c1 --volumes-from c3 centos /bin/bash

docker run -it --name=c2 --volumes-from c3 centos /bin/bash

使用到了 --volumes-from 参数,使得新创建的容器可以访问 c3数据卷容器 的数据卷,说白了就是授权 c1,c2 可以访问 c3 的数据卷,实现数据共享。

当我们按照上面创建 c1 后,会发现目录下存在 volume 这个目录,说明绑定 c3 成功了,创建 c2 也是同理。

那么现在就来验证一下数据共享。我们在 c2 的 volume 目录下创建一个 hello.txt 文件,然后去查看 c1,c3 以及宿主机上的目录是否同步。

如图所示,一切没毛病。

使用 docker 安装 nginx

这里简单演示一下使用 docker 来安装 nginx。

拉取现有 nginx 镜像,正常情况下载一个镜像前应该去先搜索,这里略过,默认安装最新版本:

docker pull nginx

根据镜像创建 nginx 的容器:

docker run -id --name=nginx_c -p 8080:80 nginx

这里又出现了一个新的参数 -p ,它的作用是端口映射,8080:80 的意思是将容器的 80 端口映射到宿主机的 8080 端口。

为什么要端口映射?这是因为外部机器是不能直接访问到容器,但是外部机器与宿主机是可以连接的,同时容器也可以与宿主机连接,所以为了在外部机器上可以远程访问到容器,我们需要把容器的服务端口映射到宿主机上。

执行命令之后,使用服务器的 ip 加 8080 端口访问测试,如下:

可以看到使用 docker 部署 nginx 是如此便捷。当然用于实际生产还需要一些配置,这里先不多讲了,以后慢慢来。

结尾

由于本人也是刚刚入门 docker ,所以文中有不对的地方还是希望各位童鞋们指出,先谢谢了。

docker 文章还会继续出,一来是为了加深的印象,二来我喜欢分享学会的知识,如果对你有帮助,务必要给个赞哦~