Docker详解

403 阅读16分钟

一,Docker概述

  • Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub上进行维护。

  • Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc。Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。

  • Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。 Docker 的基础是 Linux 容器(LXC)等技术。

    在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。

Docker 和传统虚拟化方式的不同之处:

  • 容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统
  • 传统方式则是在硬件层面实现。

Docker基本概念:

Docker 包括三个基本概念

  • 镜像(Image)

    Docker 镜像就是一个只读的模板。

    例如:一个镜像可以包含一个完整的 ubuntu 操作系统环境,里面仅安装了 Apache 或用户需要的其它应用程序。镜像可以用来创建 Docker 容器。

    Docker 提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。

    Docker就相当于Java面向对象里面的类

  • 容器(Container)

    Docker 利用容器来运行应用。

    **容器是从镜像创建的运行实例。**它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。

    可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。

    :镜像是只读的,容器在启动的时候创建一层可写层作为最上层。

  • 仓库(Repository)

    仓库是集中存放镜像文件的场所。有时候会把仓库和仓库注册服务器(Registry)混为一谈,并不严格区分。实际上,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。

    仓库分为公开仓库(Public)和私有仓库(Private)两种形式。

    最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。 国内的公开仓库包括 Docker Pool 等,可以提供大陆用户更稳定快速的访问。

    当然,用户也可以在本地网络内创建一个私有仓库。

    当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以了

    注:Docker 仓库的概念跟 Git 类似,注册服务器可以理解为 GitHub 这样的托管服务

    image-20231024101036502

二,镜像

镜像是 Docker 的三大组件之一。

Docker 运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker 会从镜像仓库下载(默认是 Docker Hub 公共注册服务器中的仓库)。

2.1 从仓库获取镜像

要获取镜像首先需要先查找需要获取的镜像。

docker search 镜像名

然后使用命令

docker pull 镜像名:镜像版本(tag)

如果不指定版本默认拉取最新的镜像版本

下载过程中,会输出获取镜像的每一层信息。

该命令实际上相当于 $ sudo docker pull registry.hub.docker.com/ubuntu:12.04命令,即从注册服务器 registry.hub.docker.com 中的 ubuntu 仓库来下载标记为 12.04 的镜像。

有时候官方仓库注册服务器下载较慢,可以从其他仓库下载。 从其它仓库下载时需要指定完整的仓库注册服务器地址。例如

sudo docker pull dl.dockerpool.com:5000/ubuntu:12.04

2.2 管理本地主机上的镜像

2.2.1 显示本地已有的镜像

使用 docker images 显示本地已有的镜像

$ sudo docker images
REPOSITORY       TAG      IMAGE ID      CREATED      VIRTUAL SIZE
ubuntu           12.04    74fe38d11401  4 weeks ago  209.6 MB
ubuntu           precise  74fe38d11401  4 weeks ago  209.6 MB

在列出信息中,可以看到几个字段信息

  • REPOSITORY :来自于哪个仓库,比如 ubuntu
  • TAG:镜像的标记,比如 14.04
  • IMAGE ID :它的 ID 号(唯一)
  • CREATED:创建时间
  • VIRTUAL SIZE:镜像大小

其中镜像的 ID 唯一标识了镜像,注意到 ubuntu:14.04ubuntu:trusty 具有相同的镜像 ID,说明它们实际上是同一镜像。

TAG 信息用来标记来自同一个仓库的不同镜像。例如 ubuntu 仓库中有多个镜像,通过 TAG 信息来区分发行版本,例如 10.0412.0412.1013.0414.04


2.2.2 创建镜像

创建镜像有很多方法,用户可以从 Docker Hub 获取已有镜像并更新,也可以利用本地文件系统创建一个。

方法一: 直接修改镜像

  1. 修改已有镜像: 先使用下载的镜像启动容器:

    docker run -t -i training/sinatra /bin/bash
    

    注意:记住容器的 ID,稍后还会用到。

  2. 在容器中添加 json 和 gem 两个应用。

    apt install json
    
  3. 使用 docker commit 命令来提交更新后的副本。

    sudo docker commit -m "Added json" -a "Docker Newbee" 0b2616b0e5a8 ouruser/sinatra:v2
    
    • 其中,-m 来指定提交的说明信息,跟我们使用的版本控制工具一样;
    • -a 可以指定更新的用户信息;之后是用来创建镜像的容器的 ID
    • 最后指定目标镜像的仓库名和 tag 信息。

    创建成功后会返回这个镜像的 ID 信息。

  4. 使用 docker images 来查看新创建的镜像

    sudo docker images
    
    REPOSITORY          TAG     IMAGE ID       CREATED       VIRTUAL SIZE
    training/sinatra    latest  5bc342fa0b91   10 hours ago  446.7 MB
    ouruser/sinatra     v2      3c59e02ddd1a   10 hours ago  446.7 MB
    ouruser/sinatra     latest  5db5f8471261   10 hours ago  446.7 MB
    
  5. 之后,可以使用新的镜像来启动容器

    sudo docker run -t -i ouruser/sinatra:v2 /bin/bash
    

方法二:利用 Dockerfile 来创建镜像

使用 docker commit 来扩展一个镜像比较简单,但是不方便在一个团队中分享。我们可以使用 docker build 来创建一个新的镜像。

首先需要创建一个 Dockerfile,里面包含一些如何创建镜像的指令。

  1. 首先创建Dockerfile文件

    新建一个目录和一个 Dockerfile

    mkdir sinatra
    cd sinatra
    touch Dockerfile
    
  2. Dockerfile 中每一条指令都创建镜像的一层,例如:

    # This is a comment
    FROM ubuntu:14.04
    MAINTAINER Docker Newbee <newbee@docker.com>
    RUN apt-get -qq update
    RUN apt-get -qqy install ruby ruby-dev
    RUN gem install sinatra
    
  3. 编写完成 Dockerfile 后可以使用 docker build 来生成镜像

    sudo docker build -t="ouruser/sinatra:v2" .
    

    其中 -t 标记来添加 tag,指定新的镜像的用户信息。

    . 是 Dockerfile 所在的路径(当前目录),也可以替换为一个具体的 Dockerfile 的路径。

    build 进程在执行操作。它要做的第一件事情就是上传这个 Dockerfile 内容,因为所有的操作都要依据 Dockerfile 来进行。 然后,Dockfile 中的指令被一条一条的执行。每一步都创建了一个新的容器,在容器中执行指令并提交修改(就跟之前介绍过的 docker commit 一样)。当所有的指令都执行完毕之后,返回了最终的镜像 id。所有的中间步骤所产生的容器都被删除和清理了。

    注意一个镜像不能超过 127 层

  4. 此外,还可以利用 ADD 命令复制本地文件到镜像;

    EXPOSE 命令来向外部开放端口;

    CMD 命令来描述容器启动后运行的程序等。

    # put my local web site in myApp folder to /var/www
    ADD myApp /var/www
    # expose httpd port
    EXPOSE 80
    # the command to run
    CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"]
    
  5. 现在可以利用新创建的镜像来启动一个容器

    sudo docker run -t -i ouruser/sinatra:v2 /bin/bash
    
  6. 还可以用 docker tag 命令来修改镜像的标签。

    sudo docker tag 5db5f8471261 ouruser/sinatra:devel
    sudo docker images ouruser/sinatra
    
    
    REPOSITORY          TAG     IMAGE ID      CREATED        VIRTUAL SIZE
    ouruser/sinatra     latest  5db5f8471261  11 hours ago   446.7 MB
    ouruser/sinatra     devel   5db5f8471261  11 hours ago   446.7 MB
    ouruser/sinatra     v2      5db5f8471261  11 hours ago   446.7 MB
    

2.2.3 上传镜像

用户可以通过 docker push 命令,把自己创建的镜像上传到仓库中来共享。例如,用户在 Docker Hub 上完成注册后,可以推送自己的镜像到仓库中。

sudo docker push ouruser/sinatra

2.2.4 存出和载入Docker镜像

导出镜像

如果要导出镜像到本地文件,可以使用 docker save 命令。

sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              14.04               c4ff7513909d        5 weeks ago         225.4 MB
...
sudo docker save -o ubuntu_14.04.tar ubuntu:14.04

载入镜像

可以使用 docker load 从导出的本地文件中再导入到本地镜像库,例如

sudo docker load --input ubuntu_14.04.tar

2.2.5 Docker 移除镜像

如果要移除本地的镜像,可以使用 docker rmi 命令。

sudo docker rmi training/sinatra

注意:在删除镜像之前要先用 docker rm 删掉依赖于这个镜像的所有容器。

三,容器

容器是 Docker 又一核心概念。

简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。

3.1 启动容器

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。


3.1.1 新建并启动

所需要的命令主要为 docker run

例如,下面的命令输出一个 “Hello World”,之后终止容器。

sudo docker run ubuntu:14.04 /bin/echo 'Hello world'

这跟在本地直接执行 /bin/echo 'hello world' 几乎感觉不出任何区别。

下面的命令则启动一个 bash 终端,允许用户进行交互

sudo docker run -t -i --name ubuntu ubuntu:14.04 /bin/bash

其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开,--name用来给容器命名。

在交互模式下,用户可以通过所创建的终端来输入命令,例如

root@af8bae53bdd3:/# pwd
/

root@af8bae53bdd3:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

3.1.2 守护态运行Docker容器

更多的时候,需要让 Docker 容器在后台以守护态(Daemonized)形式运行。此时,可以通过添加 -d 参数来实现。

例如下面的命令会在后台运行容器。

sudo docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"

3.1.3 启动已终止容器

可以利用 docker start 命令,直接将一个已经终止的容器启动运行。

容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 pstop 来查看进程信息。

root@ba267838cc1b:/# ps

  PID TTY          TIME CMD
    1 ?        00:00:00 bash
   11 ?        00:00:00 ps

可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。

3.2 终止Docker容器

使用 docker stop 来终止一个运行中的容器,此外,当Docker容器中指定的应用终结时,容器也自动终止

docker restart 命令会将一个运行态的容器终止,然后再重新启动它。

3.3 查看容器信息

通过 docker ps 命令来查看容器信息。

sudo docker ps

CONTAINER ID  IMAGE         COMMAND               CREATED        STATUS       PORTS NAMES
1e5535038e28  ubuntu:14.04  /bin/sh -c 'while tr  2 minutes ago  Up 1 minute        insane_babbage

要获取容器的输出信息,可以通过 docker logs 命令。

sudo docker logs 容器id或容器名

hello world
hello world
hello world
. . .

终止状态的容器可以用 docker ps -a 命令看到。例如

sudo docker ps -a 

通过docker inspect命令来查看容器配置文件

sudo docker inspect 容器id或容器名

3.4 Docker 删除容器

可以使用 docker rm 来删除一个处于终止状态的容器。 例如

sudo docker rm  trusting_newton

如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器。

3.5 导出和导入Docker容器

3.5.1 导出容器

如果要导出本地某个容器,可以使用 docker export 命令。

sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                    PORTS               NAMES
7691a814370e        ubuntu:14.04        "/bin/bash"         36 hours ago        Exited (0) 21 hours ago                       test

sudo docker export 7691a814370e > ubuntu.tar

这样将导出容器快照到本地文件。

3.5.2 导入容器快照

可以使用 docker import 从容器快照文件中再导入为镜像,例如

cat ubuntu.tar | sudo docker import - test/ubuntu:v1.0
sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
test/ubuntu         v1.0                9d37a6082e97        About a minute ago   171.3 MB

此外,也可以通过指定 URL 或者某个目录来导入,例如

sudo docker import http://example.com/exampleimage.tgz example/imagerepo

注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

3.6 进入Docker容器

在使用 -d 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入:

  • docker attach
  • docker exec:推荐大家使用 docker exec 命令,因为此命令退出容器终端时不会导致容器的停止。

attach 命令

docker attach 1e560fca3906 

exec 命令

docker exec -it 243c32535da7 /bin/bash

3.7,Docker容器网络

默认情况下,所有容器都是以bridge方式连接到Docker的一个虚拟网桥上

image-20231024092139201

加入自定义网络的容器才可以通过容器名互相访问,Docker的网络操作命令如下

image-20231024093649970

3.7.1 新建网络

docker network create -d bridge test-net

参数说明:

-d:参数指定 Docker 网络类型,有 bridge、overlay,其中 overlay 网络类型用于 Swarm mode,不需要考虑这玩意,默认bridge类型。

3.7.2 加入网络

运行一个容器并连接到新建的 test-net 网络

docker run -it --name test1 --network test-net ubuntu /bin/bash

也可以将正在运行的容器加入指定网络

docker network connect test-net test1

3.8 Docker外部访问容器

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P-p 参数来指定端口映射。

当使用 -P 标记时,Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。

使用 docker ps 可以看到,本地主机的 49155 被映射到了容器的 5000 端口。此时访问本机的 49155 端口即可访问容器内 web 应用提供的界面。

$ sudo docker run -d -P ImageName

-p(小写的)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。

使用 hostPort:containerPort 格式本地的 5000 端口映射到容器的 5000 端口,可以执行

sudo docker run -d -p 5000:5000 ImageName

3.9 使用安全隧道进行容器之间的访问

使用 --link 参数可以让容器之间安全的进行交互。

  1. 下面先创建一个新的数据库容器。

    sudo docker run -d --name db ImageName
    
  2. 然后创建一个新的 web 容器,并将它连接到 db 容器

    sudo docker run -d -P --name web --link db:LinkName ImageName
    

    此时,db 容器和 web 容器建立互联关系。

    --link 参数的格式为 --link name:alias,其中 name 是要链接的容器的名称,alias 是这个连接的别名。

四,仓库

仓库(Repository)是集中存放镜像的地方。

一个容易混淆的概念是注册服务器(Registry)。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址 dl.dockerpool.com/ubuntu 来说,dl.dockerpool.com 是注册服务器地址,ubuntu 是仓库名。

大部分时候,并不需要严格区分这两者的概念。

4.1 Docker Hub

目前 Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了超过 15,000 的镜像。大部分需求,都可以通过在 Docker Hub 中直接下载镜像来实现。

4.2 阿里云仓库

和Docker Hub差不多的东西,容器镜像服务 (aliyun.com)

4.2 登录

可以通过执行 docker login 命令来输入用户名、密码和邮箱来完成注册和登录。 注册成功后,本地用户目录的 .dockercfg 中将保存用户的认证信息。

docker login --username=xxxx registry.cn-hangzhou.aliyuncs.com

4.3 拉取镜像

$ docker pull 仓库地址:镜像版本号

这个是指定拉取镜像地址,如果设置了镜像假设就不需要指定拉取镜像仓库的地址了

docker pull 镜像:镜像版本

4.4 推送镜像

在拉取镜像之前需要对本地镜像进行一个tag标记

docker tag 本地镜像:版本 远程仓库地址/本地镜像:版本

版本号不写默认latest

docker push 你打的tag名

注意的是:tag名是远程仓库地址/本地镜像:版本

举例:

docker login --username=alyName registry.cn-hangzhou.aliyuncs.com
docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/nxxxx/project:7
docker push registry.cn-hangzhou.aliyuncs.com/nxxxx/project:7

4.6 私有仓库

Docker官方的Dockerhub (hub.docker.com)是一个用于管理公共镜像的仓库,我们可以上面拉取镜像 到本地,也可以把我们自己的镜像推送上去。但是,有时候我们的服务器无法访问互联网,或者你不希望将自己的镜 像放到公网当中,那么我们就需要搭建自己的私有仓库来存储和管理自己的镜像

  1. 拉取私有仓库镜像

    docker pull registry
    
  2. 启动私有仓库容器

    docker run -id --name=registry -p 5000:5000 registry
    
  3. 打开浏览器 输入地址http://私有仓库服务器ip:5000/v2/_catalog,看到"repositories":[]》 表示私 有仓库 搭建成功

  4. 修改daemon.json

    image-20231024101441437

    ["insecure-registries":["私有仓库服务器ip:5000"]}
    
  5. 重启docker 服务

    systemct] restart docker
    docker start registry
    
  6. 列出私有仓库的所有镜像

    curl http://ip:5000/v2/_catalog
    
  7. ·列出私有仓库的centos镜像有哪些tag

    curl http://ip:5000/v2/centos/tags/list
    

五,Docker数据管理

5.1 数据卷

数据卷

  • 数据卷是宿主机中的一个目录或文件
  • 当容器目录和数据卷目录绑定后,对方的修改会立即同步
  • 一个数据卷可以被多个容器同时挂载
  • 一个容器也可以被挂载多个数据卷
image-20231024102947135

数据卷作用

  • 容器数据持久化
  • 外部机器和容器间接通信
  • 容器之间数据交换

配置数据卷

image-20231024103122402

5.2 数据卷容器

image-20231024103151863

image-20231024103211819