docker基础(看这一篇就够了)

docker基础

docker基础(看这一篇就够了)_第1张图片

一、Docker 是什么?

Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可抑制的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器完全使用沙盒机制,相互之间不会存在任何接口。几乎没有性能开销,可以很容易的在机器和数据中心运行。最重要的是,他们不依赖于任何语言、框架或者包装系统。

小知识:沙盒也叫沙箱(sandbox)。在计算机领域指一种虚拟技术,而且多用于计算机安全技术。安全软件可以让它在沙盒中运行,如果含有恶意行为,则禁止程序的进一步运行,而这不会对系统造成任何危害。

Docker是dotCloud公司开源的一个基于LXC的高级容器引擎,源码托管在Github上,基于go语言并且遵从Apache2.0协议开源。GitHub地址:https://github.com/moby/moby

小知识:LXC为Linux Container的简写。Linux Container 容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。LXC主要通过Kernel的namespace实现每个用户实例之间的项目隔离,通过cgroup实现对资源的配额和调度。

docker官网:https://www.docker.com
docker中文库:https://www.docker.org.cn/

常用命令一般分为docker信息,镜像管理,容器管理

二、Docker 容器技术与虚拟机的区别

Docker 到底是个什么东西呢?我们在理解 Docker 之前,首先得先区分清楚两个概念,容器和虚拟机。

虚拟机

虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。在实体计算机中能够完成的工作在虚拟机中都能够实现。在计算机中创建虚拟机时,需要将实体机的部分硬盘和内存容量作为虚拟机的硬盘和内存容量。每个虚拟机都有独立的CMOS、硬盘和操作系统,可以像使用实体机一样对虚拟机进行操作。

可能很多读者朋友都用过虚拟机,而对容器这个概念比较的陌生。我们用的传统虚拟机如 VMware , VisualBox 之类的需要模拟整台机器包括硬件。

每台虚拟机都需要有自己的操作系统,虚拟机一旦被开启,预分配给它的资源将全部被占用。

每一台虚拟机包括应用,必要的二进制和库,以及一个完整的用户操作系统。

容器

Docker 容器是一个开源的应用容器引擎,让开发者可以以统一的方式打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何安装了docker引擎的服务器上(包括流行的Linux机器、windows机器),也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app)。几乎没有性能开销,可以很容易地在机器和数据中心中运行。最重要的是,他们不依赖于任何语言、框架包括系统。

而容器技术是和我们的宿主机共享硬件资源及操作系统,可以实现资源的动态分配。

容器包含应用和其所有的依赖包,但是与其他容器共享内核。容器在宿主机操作系统中,在用户空间以分离的进程运行。

容器技术是实现操作系统虚拟化的一种途径,可以让您在资源受到隔离的进程中运行应用程序及其依赖关系。

通过使用容器,我们可以轻松打包应用程序的代码、配置和依赖关系,将其变成容易使用的构建块,从而实现环境一致性、运营效率、开发人员生产力和版本控制等诸多目标。

容器可以帮助保证应用程序快速、可靠、一致地部署,其间不受部署环境的影响。

容器还赋予我们对资源更多的精细化控制能力,让我们的基础设施效率更高。

对比

相同点

docker和容器技术和虚拟机技术,都是虚拟化技术。

不同点

通过下面这幅图,我们可以很直观的反映出这两者的区别所在:

docker基础(看这一篇就够了)_第2张图片

Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。

而 Linux 容器是 Linux 发展出的另一种虚拟化技术,简单来讲, Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离,相当于是在正常进程的外面套了一个保护层。

对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。

Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。

程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker ,就不用担心环境问题。

总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

Docker 的优势

Docker 相比于传统虚拟化方式具有更多的优势:

  • Docker 启动快速属于秒级别。虚拟机通常需要几分钟去启动。
  • Docker 需要的资源更少。Docker 在操作系统级别进行虚拟化,Docker 容器和内核交互,几乎没有性能损耗,性能优于通过 Hypervisor 层与内核层的虚拟化。
  • Docker 更轻量。Docker 的架构可以共用一个内核与共享应用程序库,所占内存极小。同样的硬件环境,Docker 运行的镜像数远多于虚拟机数量,对系统的利用率非常高。
  • 与虚拟机相比,Docker 隔离性更弱。Docker 属于进程之间的隔离,虚拟机可实现系统级别隔离。
  • 安全性。Docker 的安全性也更弱,Docker 的租户 Root 和宿主机 Root 等同,一旦容器内的用户从普通用户权限提升为 Root 权限,它就直接具备了宿主机的 Root 权限,进而可进行无限制的操作。
  • 虚拟机租户 Root 权限和宿主机的 Root 虚拟机权限是分离的,并且虚拟机利用如 Intel 的 VT-d 和 VT-x 的 ring-1 硬件隔离技术。
  • 这种隔离技术可以防止虚拟机突破和彼此交互,而容器至今还没有任何形式的硬件隔离,这使得容器容易受到攻击。
  • 可管理性。Docker 的集中化管理工具还不算成熟。各种虚拟化技术都有成熟的管理工具,例如 VMware vCenter 提供完备的虚拟机管理能力。
  • 高可用和可恢复性。Docker 对业务的高可用支持是通过快速重新部署实现的。
  • 虚拟化具备负载均衡,高可用,容错,迁移和数据保护等经过生产实践检验的成熟保障机制, VMware 可承诺虚拟机 99.999% 高可用,保证业务连续性。
  • 快速创建、删除。虚拟化创建是分钟级别的,Docker 容器创建是秒级别的,Docker 的快速迭代性,决定了无论是开发、测试、部署都可以节约大量时间
  • 交付、部署。虚拟机可以通过镜像实现环境交付的一致性,但镜像分发无法体系化。Docker 在 Dockerfile 中记录了容器构建过程,可在集群中实现快速分发和快速部署。

我们可以从下面这张表格很清楚地看到容器相比于传统虚拟机的特性的优势所在:

docker基础(看这一篇就够了)_第3张图片

Docker局限性

Docker用于应用程序时是最有用的,但并不包含数据。日志、数据库等通常放在Docker容器外。一个容器的镜像通常都很小,不用和存储大量数据,存储可以通过外部挂载等方式使用,比如:NFS、ipsan、MFS等 ,或者docker命令 ,-v映射磁盘分区。总之,docker只用于计算,存储交给别人。

三、Docker 特性

  • 文件系统隔离:每个进程容器运行在一个完全独立的根文件系统里。
  • 资源隔离:系统资源,像CPU和内存等可以分配到不同的容器中,使用cgroup。
  • 网络隔离:每个进程容器运行在自己的网路空间,虚拟接口和IP地址。
  • 日志记录:Docker将收集到和记录的每个进程容器的标准流(stdout/stderr/stdin),用于实时检索或者批量检索
  • 变更管理:容器文件系统的变更可以提交到新的镜像中,并可重复使用以创建更多的容器。无需使用模板或者手动配置。
  • 交互式shell:Docker可以分配一个虚拟终端并且关联到任何容器的标准输出上,例如运行一个一次性交互shell。

四、Docker 的三个基本概念

docker基础(看这一篇就够了)_第4张图片

从上图我们可以看到,Docker 中包括三个基本的概念:

  • Image(镜像)
  • Container(容器)
  • Repository(仓库)

镜像是 Docker 运行容器的前提,仓库是存放镜像的场所,可见镜像更是 Docker 的核心。

Image(镜像)

那么镜像到底是什么呢?Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。

镜像不包含任何动态数据,其内容在构建之后也不会被改变。镜像(Image)就是一堆只读层(read-only layer)的统一视角,也许这个定义有些难以理解,下面的这张图能够帮助读者理解镜像的定义:

docker基础(看这一篇就够了)_第5张图片

从左边我们看到了多个只读层,它们重叠在一起。除了最下面一层,其他层都会有一个指针指向下一层。这些层是 Docker 内部的实现细节,并且能够在主机的文件系统上访问到。

统一文件系统(Union File System)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角。

这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。我们可以在图片的右边看到这个视角的形式。

Container(容器)

容器(Container)的定义和镜像(Image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。

docker基础(看这一篇就够了)_第6张图片

由于容器的定义并没有提及是否要运行容器,所以实际上,容器 = 镜像 + 读写层。

Repository(仓库)

Docker 仓库是集中存放镜像文件的场所。镜像构建完成后,可以很容易的在当前宿主上运行。

但是, 如果需要在其他服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry(仓库注册服务器)就是这样的服务。

有时候会把仓库(Repository)和仓库注册服务器(Registry)混为一谈,并不严格区分。

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

实际上,一个 Docker Registry 中可以包含多个仓库(Repository),每个仓库可以包含多个标签(Tag),每个标签对应着一个镜像。

所以说,镜像仓库是 Docker 用来集中存放镜像文件的地方,类似于我们之前常用的代码仓库。

通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。

我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 Latest 作为默认标签。

仓库又可以分为两种形式:

  • Public(公有仓库)
  • Private(私有仓库)

Docker Registry 公有仓库是开放给用户使用、允许用户管理镜像的 Registry 服务。

一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。

当用户创建了自己的镜像之后就可以使用 Push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 Pull 下来就可以了。最大的公开仓库是 Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云 、网易云等。

我们主要把 Docker 的一些常见概念如 Image,Container,Repository 做了详细的阐述,也从传统虚拟化方式的角度阐述了 Docker 的优势。

五、Docker 架构

我们从下图可以直观地看到 Docker 的架构:
docker基础(看这一篇就够了)_第7张图片

Docker 使用 C/S 结构,即客户端/服务器体系结构。Docker 客户端与 Docker 服务器进行交互,Docker服务端负责构建、运行和分发 Docker 镜像。

Docker 客户端和服务端可以运行在一台机器上,也可以通过 RESTful 、 Stock 或网络接口与远程 Docker 服务端进行通信。

docker基础(看这一篇就够了)_第8张图片

这张图展示了 Docker 客户端、服务端和 Docker 仓库(即 Docker Hub 和 Docker Cloud ),默认情况下 Docker 会在 Docker 中央仓库寻找镜像文件。

这种利用仓库管理镜像的设计理念类似于 Git ,当然这个仓库是可以通过修改配置来指定的,甚至我们可以创建我们自己的私有仓库。

工作原理

Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上, 然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。容器,是一个运行时环境,就是我们前面说到的集装箱。

Docker 的核心组件包括:

  • Docker Client
  • Docker Daemon
  • Docker Image
  • Docker Registry
  • Docker Container

Docker 采用的是 Client/Server 架构。客户端向服务器发送请求,服务器负责构建、运行和分发容器。

客户端和服务器可以运行在同一个 Host 上,客户端也可以通过 Socket 或 REST API 与远程的服务器通信。

可能很多朋友暂时不太理解一些东西,比如 REST API 是什么东西等,不过没关系,在后面的文章中会一一给大家讲解清楚。

1、Docker Client

Docker Client ,也称 Docker 客户端。它其实就是 Docker 提供命令行界面(CLI)工具,是许多 Docker 用户与 Docker 进行交互的主要方式。

客户端可以构建,运行和停止应用程序,还可以远程与 Docker_Host 进行交互。

最常用的 Docker 客户端就是 Docker 命令,我们可以通过 Docker 命令很方便地在 Host 上构建和运行 Docker 容器。

2、Docker Daemon

Docker Daemon 是服务器组件,以 Linux 后台服务的方式运行,是 Docker 最核心的后台进程,我们也把它称为守护进程。

它负责响应来自 Docker Client 的请求,然后将这些请求翻译成系统调用完成容器管理操作。

该进程会在后台启动一个 API Server ,负责接收由 Docker Client 发送的请求,接收到的请求将通过 Docker Daemon 内部的一个路由分发调度,由具体的函数来执行请求。

#可用通过systemctl管理 docker服务
systemctl status docker

我们大致可以将其分为以下三部分:

  • Docker Server
  • Engine
  • Job

Docker Daemon 的架构如下所示:

docker基础(看这一篇就够了)_第9张图片

Docker Daemon 可以认为是通过 Docker Server 模块接受 Docker Client 的请求,并在 Engine 中处理请求,然后根据请求类型,创建出指定的 Job 并运行。

Docker Daemon 运行在 Docker Host 上,负责创建、运行、监控容器,构建、存储镜像。

运行过程的作用有以下几种可能:

  • 向 Docker Registry 获取镜像。
  • 通过 GraphDriver 执行容器镜像的本地化操作。
  • 通过 NetworkDriver 执行容器网络环境的配置。
  • 通过 ExecDriver 执行容器内部运行的执行工作。

由于 Docker Daemon 和 Docker Client 的启动都是通过可执行文件 Docker 来完成的,因此两者的启动流程非常相似。

Docker 可执行文件运行时,运行代码通过不同的命令行 Flag 参数,区分两者,并最终运行两者各自相应的部分。

启动 Docker Daemon 时,一般可以使用以下命令来完成:

docker --daemon = true docker –d
docker –d = true

再由 Docker 的 main() 函数来解析以上命令的相应 Flag 参数,并最终完成 Docker Daemon 的启动。

下图可以很直观地看到 Docker Daemon 的启动流程:

docker基础(看这一篇就够了)_第10张图片

默认配置下,Docker Daemon 只能响应来自本地 Host 的客户端请求。如果要允许远程客户端请求,需要在配置文件中打开 TCP 监听。

我们可以照着如下步骤进行配置:

1、编辑配置文件/etc/systemd/system/multi-user.target.wants/docker.service,在环境变量 ExecStart 后面添加 -H tcp://0.0.0.0,允许来自任意 IP 的客户端连接。

docker基础(看这一篇就够了)_第11张图片

2、重启 Docker Daemon:

systemctl daemon-reload
systemctl restart docker.service

3、Docker Image

Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。

镜像不包含任何动态数据,其内容在构建之后也不会被改变。我们可将 Docker 镜像看成只读模板,通过它可以创建 Docker 容器。

镜像有多种生成方法:

  • 从无到有开始创建镜像
  • 下载并使用别人创建好的现成的镜像
  • 在现有镜像上创建新的镜像

我们可以将镜像的内容和创建步骤描述在一个文本文件中,这个文件被称作 Dockerfile ,通过执行 docker build命令可以构建出 Docker 镜像。

4、Docker Container

Docker 容器就是 Docker 镜像的运行实例,是真正运行项目程序、消耗系统资源、提供服务的地方。

Docker Container 提供了系统硬件环境,我们可以使用 Docker Images 这些制作好的系统盘,再加上我们所编写好的项目代码,Run 一下就可以提供服务啦。

5、Docker Registry

Docker Registry 是存储 Docker Image 的仓库,运行 docker push、docker pull、docker search 时,实际上是通过 Docker Daemon 与 Docker Registry 通信。

六、Docker 网络

1.本机网络理解

我们使用ifconfig可以看到三组网络。首先是docker0,这是我们本节的重点,docker的网络。之后是enp3s0,本机的外网地址。lo口,本地环回地址,可以代表localhost。

root@odoo-pc:~# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:30ff:fe6d:b02c  prefixlen 64  scopeid 0x20<link>
        ether 02:42:30:6d:b0:2c  txqueuelen 0  (以太网)
        RX packets 1597  bytes 9884475 (9.8 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1929  bytes 298615 (298.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.78  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::caa3:5f79:6cb3:55e6  prefixlen 64  scopeid 0x20<link>
        ether f4:6b:8c:89:eb:aa  txqueuelen 1000  (以太网)
        RX packets 3633068  bytes 3293046833 (3.2 GB)
        RX errors 0  dropped 21  overruns 0  frame 0
        TX packets 1173019  bytes 107290429 (107.2 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (本地环回)
        RX packets 873743  bytes 365458953 (365.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 873743  bytes 365458953 (365.4 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

关于docker0呢,其实就是一个叫docker0的虚拟网桥。我们使用brctl命令来查看一下。(没有这个命令的下载yum -y install bridge-utils

root@odoo-pc:~# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242306db02c	no		veth8036047
							veth903acfc

2.清空本机docker环境

docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -aq)

3.veth-pair技术

什么是veth-pair技术?要理解它,我们首先来启动两个tomcat容器。

docker run -d -P --name=tomcat01 tomcat:7
docker run -d -P --name=tomcat02 tomcat:7
#提示:选择tomcat7是因为这个镜像包含了ip addr 等常用命令!
-P 表示随机映射端口
root@odoo-pc:~# docker ps
CONTAINER ID   IMAGE      COMMAND             CREATED              STATUS              PORTS                                         NAMES
4f1f396b6c3e   tomcat:7   "catalina.sh run"   About a minute ago   Up About a minute   0.0.0.0:49154->8080/tcp, :::49154->8080/tcp   tomcat02
45a14fe49479   tomcat:7   "catalina.sh run"   3 minutes ago        Up 3 minutes        0.0.0.0:49153->8080/tcp, :::49153->8080/tcp   tomcat01

启动机器之后,我们查看容器ip,通过容器的ip 去ping宿主机ip,发现是通的。

root@odoo-pc:~# docker exec -it tomcat01  ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

root@odoo-pc:~# docker exec -it tomcat02  ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

root@odoo-pc:~# docker exec -it tomcat02  ping 172.17.0.1
root@odoo-pc:~# docker exec -it tomcat02  ping 192.168.0.78
都是通的

理解:我们每启动一个docker容器,docker就会给docker容器分配一个ip,安装docker之后,会产生一个叫docker0的网卡,这里使用的就是veth-pair技术

使用ip addr命令,查看我们的网卡。

11: vethab64a85@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether b2:4d:4e:09:6a:df brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::b04d:4eff:fe09:6adf/64 scope link 
       valid_lft forever preferred_lft forever
13: vethadab078@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether a6:50:18:4f:57:6a brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::a450:18ff:fe4f:576a/64 scope link 
       valid_lft forever preferred_lft forever

我们发现多出来了两个网卡,到了这里,你已经知道这两张网卡是那里来的了吧。没错,是启动容器之后产生的!我们回过头来查看我们在启动的容器IP,就会很清晰的发现,这个网卡是成对存在的!容器内的11对应着宿主机的10,容器内的13对应宿主机的12。

什么是veth-pair?veth-pair 就是一堆的虚拟设备接口,他们都是成对出现的,一端连接着协议,一端连接着彼此。使得它充当了一个桥梁的作用。

4.docker网络详解

我们来绘制一个简单的网络模型,这样veth-pair的作用就清晰明了了。

docker基础(看这一篇就够了)_第12张图片

不难看出,tomcat01和tomcat02是共用的同一个路由器,即docker0。所有的容器在不指定网络的情况下,都是docker0路由的,docekr会给我们的容器分配一个默认IP。docker网络就是下面这个网络模型所描述的。(docker所有的网络接口都是虚拟的,虚拟的转发效率高)

docker基础(看这一篇就够了)_第13张图片

5.docker网络模式

5.1docker网络模式有以下几种:

Host:容器不会虚拟出自己的网卡,配置主机的IP等,而是使用宿主机的IP和端口

Container: 创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP。(一般不用)

None: 该模式关闭了容器的网络功能。(一般不用)

Bridge:默认为该模式(桥接,自己创建也是用它),此模式会为每一个容器分配,设置IP等,并将容器连接到一个docker0 的虚拟网桥,通过docker 0 网桥以及iptables nat 表配置与宿主机通信。
docker network ls   
#列出docker网卡
root@odoo-pc:~# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
89381d752f8f   bridge    bridge    local
4a08e9ad09d3   host      host      local
d38ee9516193   none      null      local

5.2创建自定义网络的容器:

#我们直接启动命令, --net bridge,就是docker0(默认)
docker run -d -P --name=tomcat01 --net bridge tomcat

#docker0特点:默认,域名不能访问,--link不建议使用

下面我们自己来创建一个bridge。

root@odoo-pc:~# docker network create --driver bridge --subnet 192.168.100.0/24 --gateway 192.168.100.1  testnet
e5c328386a2df17d0f763b4f3176eb6a20eb82a75980fff5110d5dd016a97433

root@odoo-pc:~# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
89381d752f8f   bridge    bridge    local
4a08e9ad09d3   host      host      local
d38ee9516193   none      null      local
e5c328386a2d   testnet   bridge    local

#docker network inspect 网卡名字  #查看网卡详细信息
root@odoo-pc:~# docker network inspect testnet
[
    {
        "Name": "testnet",
        "Id": "e5c328386a2df17d0f763b4f3176eb6a20eb82a75980fff5110d5dd016a97433",
        "Created": "2023-07-27T15:36:47.227337109+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.100.0/24",
                    "Gateway": "192.168.100.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

5.3发布两个在自己创建的网络里的容器。

docker run -d -P --name=tomcat01-net --net=testnet tomcat:7
docker run -d -P --name=tomcat02-net --net testnet tomcat:7

然后使用docker network inspect testnet,就可以看到刚才创建的这两个容器的IP了。

root@odoo-pc:~# docker network inspect testnet

        "Containers": {
            "0ca313c861f9920c3034d1a40aac48fb19a0de6ad43314037b6b17cc1bcc2e41": {
                "Name": "tomcat01-net",
                "EndpointID": "a61e68d2cda3c9f86e6ea3a8324390f3dc002cc9ec4e9bba02c76f35f05e6f94",
                "MacAddress": "02:42:c0:a8:64:02",
                "IPv4Address": "192.168.100.2/24",
                "IPv6Address": ""
            },
            "79807772d27de0b2c109b4baff98ce9c9933e6de176ebb12cb8cb3569f927c26": {
                "Name": "tomcat02-net",
                "EndpointID": "01ac7bb84098978c92c8134f7d46adb0938221ccd0c065883b91a1c3ab236a85",
                "MacAddress": "02:42:c0:a8:64:03",
                "IPv4Address": "192.168.100.3/24",
                "IPv6Address": ""
            }
        },


还记得我们前面说的docker0的缺点之一,不能通过域名访问吗?而我们自定义的网络,就修复了这个功能!

docker exec -it tomcat01-net ping -c 3 IP
docker exec -it tomcat01-net ping -c 3 tomcat02-net

#提示,ping -c可以自定义ping的次数
root@odoo-pc:~# docker exec -it tomcat01-net ping tomcat02-net
PING tomcat02-net (192.168.100.3) 56(84) bytes of data.
64 bytes from tomcat02-net.testnet (192.168.100.3): icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from tomcat02-net.testnet (192.168.100.3): icmp_seq=2 ttl=64 time=0.048 ms
64 bytes from tomcat02-net.testnet (192.168.100.3): icmp_seq=3 ttl=64 time=0.095 ms

七、Docker 安装

1、Docker 安装

Docker 的安装和使用有一些前提条件,主要体现在体系架构和内核的支持上。

对于体系架构,除了 Docker 一开始就支持的 X86-64 ,其他体系架构的支持则一直在不断地完善和推进中。

2017年初,docker公司将原先的docker开源项目改名为moby。

moby是集成了原先的docker项目,是社区维护的开源项目,谁都可以在moby的基础打造自己的容器产品。

Docker 分为 CE 和 EE 两大版本。CE 即社区版,免费支持周期 7 个月;EE 即企业版,强调安全,付费使用,支持周期 24 个月。我们经常使用的版本当然是docker-ce啦!

我们在安装前可以参看官方文档获取最新的 Docker 支持情况,官方文档在这里:

https://docs.docker.com/install/

Docker 对于内核支持的功能,即内核的配置选项也有一定的要求(比如必须开启 Cgroup 和 Namespace 相关选项,以及其他的网络和存储驱动等)。

Docker CE 的安装请参考官方文档:

  • MacOS:https://docs.docker.com/docker-for-mac/install/
  • Windows:https://docs.docker.com/docker-for-windows/install/
  • Ubuntu:https://docs.docker.com/install/linux/docker-ce/ubuntu/
  • Debian:https://docs.docker.com/install/linux/docker-ce/debian/
  • CentOS:https://docs.docker.com/install/linux/docker-ce/centos/
  • Fedora:https://docs.docker.com/install/linux/docker-ce/fedora/
  • 其他 Linux 发行版:https://docs.docker.com/install/linux/docker-ce/binaries/

CentOS/Ubuntu安装Docker和Docker Compose,这里我们以 CentOS 7 作为演示。

  • 当然在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS 系统上可以使用这套脚本安装:
## 一键安装 docker
curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh
# 或
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
# 或
curl -sSL https://get.docker.com/ | sh
# 或
wget -qO- https://get.docker.com/ | sh

具体可以参看 docker-install 的脚本:

https://github.com/docker/docker-install

执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker CE 的 Edge 版本安装在系统中。更多详见CentOS/Ubuntu安装Docker和Docker Compose。

安装完成后,运行下面的命令,验证是否安装成功:

启动 Docker-CE:

sudo systemctl start docker
sudo systemctl enable docker  #设置开机自启

验证

docker -vdocker version 或 docker --version
# 或
docker info

返回 Docker 的版本相关信息,证明 Docker 安装成功

2、Docker 的简单运用 Hello World

我们通过最简单的 Image 文件 Hello World,感受一下 Docker 的魅力吧!

我们直接运行下面的命令,将名为 hello-world 的 image 文件从仓库抓取到本地:

docker pull hello-world

docker pull images 是抓取 image 文件,library/hello-world 是 image 文件在仓库里面的位置,其中 library 是 image 文件所在的组,hello-world 是 image 文件的名字。

抓取成功以后,就可以在本机看到这个 image 文件了:

docker images

现在,我们可以运行 hello-world 这个 image 文件:

docker run hello-world

输出这段提示以后,hello world 就会停止运行,容器自动终止。有些容器不会自动终止,因为提供的是服务,比如 Nginx镜像等。

我们从上面可以看出,Docker 的功能是十分强大的,除此之外,我们还可以拉取一些 Centos,Ubuntu,Alpine等镜像,在未来的教程中我们将会一一提到。

Docker 提供了一套简单实用的命令来创建和更新镜像,我们可以通过网络直接下载一个已经创建好了的应用镜像,并通过 Docker RUN 命令就可以直接使用。

当镜像通过 RUN 命令运行成功后,这个运行的镜像就是一个 Docker 容器啦。

容器可以理解为一个轻量级的沙箱,Docker 利用容器来运行和隔离应用,容器是可以被启动、停止、删除的,这并不会影响 Docker 镜像。

我们可以看看下面这幅图:

docker基础(看这一篇就够了)_第14张图片

Docker 客户端是 Docker 用户与 Docker 交互的主要方式。当您使用 Docker 命令行运行命令时,Docker 客户端将这些命令发送给服务器端,服务端将执行这些命令。

Docker 命令使用 Docker API 。Docker 客户端可以与多个服务端进行通信。

我们将剖析一下 Docker 容器是如何工作的,学习好 Docker 容器工作的原理,我们就可以自己去管理我们的容器了。

3 Docker 配置镜像加速器

这是使用阿里云docker镜像加速器。控制台地址:https://cr.console.aliyun.com,登录后,左侧-镜像工具-镜像加速器,加速器帮助页面会为你显示独立的加速地址,这个加速地址每个人的都不同。
docker基础(看这一篇就够了)_第15张图片

针对Docker客户端版本大于 1.10.0 的用户

您可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://zjj1c410.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

4 Docker 组件是如何协作运行容器

看到这里,我相信各位读者朋友们应该已经对 Docker 基础架构熟悉的差不多了,我们还记得运行的第一个容器吗?

现在我们再通过 hello-world 这个例子来体会一下 Docker 各个组件是如何协作的。

容器启动过程如下:

  • Docker 客户端执行 docker run 命令。
  • Docker Daemon 发现本地没有 hello-world 镜像。
  • Daemon 从 Docker Hub 下载镜像。
  • 下载完成,镜像 hello-world 被保存到本地。
  • Docker Daemon 启动容器。

具体过程可以看如下这幅演示图:

docker基础(看这一篇就够了)_第16张图片

了解了这些过程以后,我们再来理解这些命令就不会觉得很突兀了,下面我来给大家讲讲 Docker 常用的一些命令操作吧。

八、Docker 常用命令

我们可以通过 docker -h 去查看命令的详细的帮助文档。在这里我只会讲一些日常我们可能会用的比较多的一些命令。

docker基础(看这一篇就够了)_第17张图片

1.帮助启动类命令

  • [ 查看版本] docker version
  • [ 查看Docker概要信息] docker info
  • [ 查看Docker总体帮助文档] docker--help
  • [ 查看docker具体命令帮助文档] docker具体命令--help
  • [ 启动Docker] systemctl start docker
  • [ 停止Docker] systemctl stop docker
  • [ 重启Docker] systemctl restart docker
  • [ 查看状态] systemctl status docker
  • [ 开机启动] systemctl enable docker

2.镜像命令

1)列出本地主机上的镜像

docker images

参数说明:

-a:列出所有镜像(含历史镜像)
-q:只显示镜像ID
-f:过滤

root@odoo-pc:~# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
tomcat       latest    fb5657adc892   19 months ago   680MB
tomcat       7         9dfd74e6bc2f   2 years ago     533MB

各个选项说明:

REPOSITORY:表示镜像的仓库源
TAG:镜像的标签版本号
IMAGE ID:镜像ID
CREATED:镜像创建时间
SIZE:镜像大小

2)在远程仓库中搜索镜像

执行命令,默认去docker hub中搜索

docker search 镜像名称

参数说明:

-f:过滤
--limit 数量:只列出N个镜像,默认25个
root@odoo-pc:~# docker search odoo
NAME                           DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
odoo                           Odoo (formerly known as OpenERP) is a suite …   1236      [OK]       
bitnami/odoo                   Bitnami Docker Image for Odoo                   59                   [OK]
elicocorp/odoo-china           Odoo related dockers specific for China.        32                   [OK]
camptocamp/odoo-project        An image we use as a base for our Odoo proje…   9                    [OK]
llacroix/odoo                  Odoo image support from odoo8 to odoo14(mast…   6                    
poonlap/odoo-th                Ready to use Odoo 14.0, 13.0 or 12.0  with O…   4                    
odoohost/odoo                  Integrate odoo, postgresql, wkhtmltopdf etc.…   3                    [OK]
camptocamp/odoo-nginx          An image that includes a configuration file1                    [OK]
reseaucerta/odooada            AdA;odoo-ada                                    0                    
reseaucerta/odoogenerik        Generik;odoo-generik                            0                    
reseaucerta/odoo16                                                             0                    
reseaucerta/odoo                                                               0                    
reseaucerta/odoopepinieres     Pépinières;odoo-pepinieres                      0                    
reseaucerta/odoo15                                                             0                    
reseaucerta/odoosweetybio                                                      0                    
openg2p/openg2p-odoo-package   All the Odoo add-ons packed in a single dock…   0                    
reseaucerta/odoosurplomb       Surplomb;odoo-surplomb                          0                    
reseaucerta/odooprimeur        Primeur;odoo-primeur                            0                    
reseaucerta/odoo12             version 12;odoo-12                              0                    
reseaucerta/odoo14             version 14;odoo-14                              0                    
vauxoo/odoo-140-image          Odoo 14.0 base image                            0                    
reseaucerta/odoo13             version 13;odoo-13                              0                    
lorissantamaria/odoo8          Odoo 8 on Centos 7 with systemd support         0                    [OK]
bitnamicharts/odoo                                                             0                    
odoobgnet/docker               This is odoo 9 docker built with the idea to…   0                    [OK]

3)下载镜像

docker pull 镜像名称[:tag]

不加 tag 时,默认下载最新的镜像(即tag为latest)。

4)查看占据的空间

查看镜像/容器/数据卷所占的空间:

docker system df

root@odoo-pc:~# docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          2         1         1.213GB   679.6MB (56%)
Containers      2         2         73.04kB   0B (0%)
Local Volumes   3         0         93.14MB   93.14MB (100%)
Build Cache     0         0         0B        0B

5)删除镜像

docker rmi 镜像名称/ID

可以使用空格分隔,删除多个镜像:

docker rmi 镜像1 镜像2 镜像3

删除全部镜像:

docker rmi -f ${docker images -qa}

6)虚悬镜像

仓库名、标签都是的镜像,俗称虚悬镜像(dangling image)。

fatux: what?

3.容器命令

1)新建+启动容器

新建容器,需要先下载镜像 docker pull ubuntu

执行命令 docker run[OPTIONS]IMAGE[COMMAND][ARG...]

参数【OPTIONS】说明:

  • --name:为容器指定一个名称
  • -d:后台运行容器并返回容器ID,也即启动守护式容器
  • -i:以交互模式(interactive)运行容器,通常与-t同时使用
  • -t:为容器重新分配一个伪输入终端(tty),通常与-i同时使用。也即启动交互式容器(前台有伪终端,等待交互)
  • -e:为容器添加环境变量
  • -P:随机端口映射。将容器内暴露的所有端口映射到宿主机随机端口
  • -p:指定端口映射

-p指定端口映射的几种不同形式

  • -p hostPort:containerPort:端口映射,例如-p 8080:80
  • -p ip:hostPort:containerPort:配置监听地址,例如 -p 10.0.0.1:8080:80
  • -p ip::containerPort:随机分配端口,例如 -p 10.0.0.1::80
  • -p hostPort1:containerPort1-p hostPort2:containerPort2:指定多个端口映射,例如-p 8080:80 -p 8888:3306

2)启动交互式容器(前台命令行)

执行命令,以交互方式启动ubuntu镜像

docker run -it ubuntu /bin/bash

参数说明:

-i: 交互式操作。-t: 终端。ubuntu : ubuntu 镜像。/bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。要退出终端,直接输入 exit:

两种方式退出交互模式

  1. exit; run进去容器,exit退出,容器停止
  2. 使用快捷键 ctrl+P+Q run进去容器,ctrl+p+q退出,容器不停止

3)列出当前所有正在运行的容器

docker ps [OPTIONS]

常用参数说明:

-a:列出当前所有正在运行的容器+历史上运行过的
-l:显示最近创建的容器。

-n:显示最近n个创建的容器。

-q:静默模式,只显示容器编号。

4)启动守护式容器

大部分情况下,我们系统docker容器服务是在后台运行的,可以通过-d指定容器的后台运行模式:

docker run -d 容器名

**注意事项:**如果使用 docker run-d ubuntu尝试启动守护式的ubuntu,然后 docker ps-a 进行查看, 会发现容器已经退出了。

因为Docker容器如果在后台运行,就必须要有一个前台进程。容器运行的命令如果不是那些一直挂起的命令(例如 toptail),就会自动退出。

这个是docker的机制问题。比如你的web容器,我们以nginx为例,正常情况下,我们配置启动服务只需要启动响应的service即可。例如service nginx start但是这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用,这样的容器后台启动后,会立即自杀因为他觉得他没事可做了。所以最佳的解决方案是,将你要运行的程序以前台进程的形式运行,常见就是命令行模式,表示还有交互操作。

5)容器和宿主机文件拷贝

容器内文件拷贝到宿主机:

docker cp 容器ID:容器内路径 目的主机路径

宿主机文件拷贝到容器中:

docker cp 主机路径 容器ID:容器内路径

6)导入和导出容器

export:导出容器的内容流作为一个tar归档文件(对应import命令);

import:从tar包中的内容创建一个新的文件系统再导入为镜像(对应export命令);

示例:

# 导出
# docker export 容器ID > tar文件名
docker export abc > aaa.tar

# 导入
# cat tar文件 | docker import - 自定义镜像用户/自定义镜像名:自定义镜像版本号
cat aaa.tar | docker import - test/mytest:1.0.1

7)将容器生成新镜像

docker commit提交容器副本使之成为一个新的镜像。

docker 启动一个镜像容器后, 可以在里面执行一些命令操作,然后使用docker commit将新的这个容器快照生成一个镜像。

docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[tag]
root@odoo-pc:~# docker commit -m="fatux test" -a="fatux" tomcat01-net tomcat7:fatux
sha256:f2b093d4b9cf92174c2a46535c15b0e178477b762b8a872767e25783799a879b

8)容器卷操作

Docker挂载主机目录,可能会出现报错:cannot open directory.:Perissiondenied。

解决方案:在命令中加入参数 --privileged=true

CentOS7安全模块比之前系统版本加强,不安全的会先禁止,目录挂载的情况被默认为不安全的行为,在SELinux里面挂载目录被禁止掉了。如果要开启,一般使用 --privileged=true,扩大容器的权限解决挂载没有权限的问题。也即使用该参数,容器内的root才拥有真正的root权限,否则容器内的root只是外部的一个普通用户权限。

卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过UnionFS,提供一些用于持续存储或共享数据。

特性:卷设计的目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。

特点:

  • 数据卷可以在容器之间共享或重用数据
  • 卷中的更改可以直接实施生效
  • 数据卷中的更改不会包含在镜像的更新中
  • 数据卷的生命周期一直持续到没有容器使用它为止

运行一个带有容器卷存储功能的容器实例:

docker run -it --privileged=true -v 宿主机绝对路径目录:容器内目录[rw | ro] 镜像名
root@odoo-pc:~# docker run -it --privileged=true -v /home/odoo:/home/codoo  tomcat7:fatux  /bin/bash

可以使用docker inspect查看容器绑定的数据卷。

docker inspect 308fbc437b77(容器id)

权限:

  • rw:读写
  • ro:只读。如果宿主机写入内容,可以同步给容器内,容器内可以读取。

容器卷的继承:

# 启动一个容器
docker run -it --privileged=true /tmp/test:/tmp/docker --name u1 ubuntu /bin/bash

# 使用 --volumes-from 继承 u1的容器卷映射配置
docker run -it --privileged=true --volumes-from u1 --name u2 ubuntu

9)其他命令

  • [ 启动已停止运行的容器] docker start容器ID或者容器名
  • [ 重启容器] docker restart容器ID或容器名
  • [ 停止容器] docker stop容器ID或容器名
  • [ 强制停止容器] docker kill容器ID或容器名
  • [ 删除已经停止的容器] docker rm容器ID或容器名
  • [ 强制删除正在运行的容器] docker rm-f容器ID或容器名
  • [ 一次删除多个容器实例] docker rm-f ${docker ps-a-q} 或者 docker ps-a-q|xargs docker rm
  • [ 查看容器日志] docker logs容器ID或容器名,更多查看容器日志使用技巧,docker logs 查看日志
  • [ 查看容器内部细节] docker inspect容器ID或容器名
  • [ 进入正在运行的容器] docker exec-it容器ID bashShell
  • [ 重新进入] docker attach 容器ID

docker exec 和 docker attach 区别

  • attach直接进入容器启动命令的终端,不会启动新的进程,用exit退出会导致容器的停止
  • exec是在容器中打开新的终端,并且可以启动新的进程,用exit退出不会导致容器的停止

如果有多个终端,都对同一个容器执行了 docker attach,就会出现类似投屏显示的效果。一个终端中输入输出的内容,在其他终端上也会同步的显示。

九、实战测试:部署Nginx

Alpine介绍

社区开发的面向安全应用的轻量级Linux发行版,Alpine 的意思是“高山的”,比如 Alpine plants高山植物,Alpine skiing高山滑雪、the alpine resort阿尔卑斯山胜地。

Alpine Linux 网站首页注明“Small!Simple!Secure!Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.”概括了以下特点:

1、小巧:基于Musl libc和busybox,和busybox一样小巧,最小的Docker镜像只有5MB;

2、安全:面向安全的轻量发行版;

3、简单:提供APK包管理工具,软件的搜索、安装、删除、升级都非常方便。

4、适合容器使用:由于小巧、功能完备,非常适合作为容器的基础镜像。

1.搜索镜像

docker search nginx:alpine

fatux: 这个搜索条件,出现一堆名字不叫nginx:alpine的列表, 好像描述里出现alpine就事,也不知道应该下载哪个?

2.下载镜像

docker pull nginx:alpine

确实下载了一个,而且不大

3.查看镜像

root@odoo-pc:~# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
nginx        alpine    cc44224bfe20   19 months ago   23.5MB

4.启动容器

docker run -d --name nginxd -p 8080:80 nginx:alpine

用浏览器打开8080端口,没有问题

5.查看容器

docker ps

6.测试访问

curl 127.0.0.1:8080

127.0.0.1是本地回环ip,就是本机啦,可以用localhost代替。

7.进入容器修改页面

docker exec -it 容器名称/容器ID/容器ID简写

docker exec -it nginxd ash
#或
docker exec -it de15a5e4c614 ash
#或,容器ID太长,我们用容器ID的前2位即可
docker exec -it de ash

#alpine没有装bash,可能是太大了吧

alpine是轻量级的操作系统,默认没有安装whereis,这是使用find来查找nginx安装目录。

root@odoo-pc:~# docker exec -it e4 ash
/ # find -name nginx
./usr/lib/nginx
./usr/share/nginx
./usr/share/licenses/nginx
./usr/sbin/nginx
./etc/logrotate.d/nginx
./etc/init.d/nginx
./etc/nginx
./var/cache/nginx
./var/log/nginx

/ # cd /usr/share/nginx/
/usr/share/nginx # ls
html
/usr/share/nginx # cd html
/usr/share/nginx/html # ls
50x.html    index.html
/usr/share/nginx/html # vi index.html 

修改完验证 http://localhost:8080/

十、Dockerfile 是什么

前面我们已经提到了 Docker 的一些基本概念。以 CTF 的角度来看,我们可以去使用 Dockerfile 定义镜像,依赖镜像来运行容器,可以去模拟出一个真实的漏洞场景。

因此毫无疑问的说, Dockerfile 是镜像和容器的关键,并且 Dockerfile 还可以很轻易的去定义镜像内容,说了这么多,那么 Dockerfile 到底是个什么东西呢?

Dockerfile 是自动构建 Docker 镜像的配置文件,用户可以使用 Dockerfile 快速创建自定义的镜像。Dockerfile 中的命令非常类似于 Linux 下的 Shell 命令。

我们可以通过下面这幅图来直观地感受下 Docker 镜像、容器和 Dockerfile 三者之间的关系:

docker基础(看这一篇就够了)_第18张图片

我们从上图中可以看到,Dockerfile 可以自定义镜像,通过 Docker 命令去运行镜像,从而达到启动容器的目的。Dockerfile 是由一行行命令语句组成,并且支持已 # 开头的注释行。

一般来说,我们可以将 Dockerfile 分为四个部分:

  • 基础镜像(父镜像)信息指令 FROM。
  • 维护者信息指令 MAINTAINER。
  • 镜像操作指令 RUN 、EVN 、ADD 和 WORKDIR 等。
  • 容器启动指令 CMD 、ENTRYPOINT 和 USER 等。

下面是一段简单的 Dockerfile 的例子:

FROM python:2.7
MAINTAINER 大技术 
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
EXPOSE 5000 ENTRYPOINT ["python"] CMD ["app.py"]

我们可以分析一下上面这个过程:

  • 从 Docker Hub 上 Pull 下 Python 2.7 的基础镜像。
  • 显示维护者的信息。
  • Copy 当前目录到容器中的 /App 目录下 复制本地主机的 ( Dockerfile 所在目录的相对路径)到容器里 。
  • 指定工作路径为 /App。
  • 安装依赖包。
  • 暴露 5000 端口。
  • 启动 App。

这个例子是启动一个 Python Flask App 的 Dockerfile(Flask 是 Python 的一个轻量的 Web 框架),相信大家从这个例子中能够稍微理解了 Dockfile 的组成以及指令的编写过程。

十一、Dockerfile 常用的指令

根据上面的例子,我们已经差不多知道了 Dockerfile 的组成以及指令的编写过程,我们再来理解一下这些常用命令就会得心应手了。

由于 Dockerfile 中所有的命令都是以下格式:INSTRUCTION argument ,指令(INSTRUCTION)不分大小写,但是推荐大写和 SQL 语句是不是很相似呢?下面我们正式来讲解一下这些指令集吧。

FROM

FROM 是用于指定基础的 images ,一般格式为 FROM or FORM :。

所有的 Dockerfile 都应该以 FROM 开头,FROM 命令指明 Dockerfile 所创建的镜像文件以什么镜像为基础,FROM 以后的所有指令都会在 FROM 的基础上进行创建镜像。

可以在同一个 Dockerfile 中多次使用 FROM 命令用于创建多个镜像。比如我们要指定 Python 2.7 的基础镜像,我们可以像如下写法一样:

FROM python:2.7

MAINTAINER

MAINTAINER 是用于指定镜像创建者和联系方式,一般格式为 MAINTAINER。

这里我设置成我的 ID 和邮箱:

MAINTAINER 大技术

COPY

COPY 是用于复制本地主机的(为 Dockerfile 所在目录的相对路径)到容器中的。

当使用本地目录为源目录时,推荐使用 COPY 。一般格式为 COPY。

例如我们要拷贝当前目录到容器中的 /app 目录下,我们可以这样操作:

COPY . /app

WORKDIR

WORKDIR 用于配合 RUN,CMD,ENTRYPOINT 命令设置当前工作路径。

可以设置多次,如果是相对路径,则相对前一个 WORKDIR 命令。默认路径为/。一般格式为 WORKDIR /path/to/work/dir。

例如我们设置 /app 路径,我们可以进行如下操作:

WORKDIR /app

RUN

RUN 用于容器内部执行命令。每个 RUN 命令相当于在原有的镜像基础上添加了一个改动层,原有的镜像不会有变化。一般格式为 RUN。

例如我们要安装 Python 依赖包,我们做法如下:

RUN pip install -r requirements.txt

EXPOSE

EXPOSE 命令用来指定对外开放的端口。一般格式为 EXPOSE[…]。

例如上面那个例子,开放5000端口:

EXPOSE 5000

ENTRYPOINT

ENTRYPOINT 可以让你的容器表现得像一个可执行程序一样。一个 Dockerfile 中只能有一个 ENTRYPOINT,如果有多个,则最后一个生效。

ENTRYPOINT 命令也有两种格式:

  • ENTRYPOINT [“executable”, “param1”, “param2”] :推荐使用的 Exec 形式。
  • ENTRYPOINT command param1 param2 :Shell 形式。

例如下面这个,我们要将 Python 镜像变成可执行的程序,我们可以这样去做:

ENTRYPOINT ["python"]

CMD

CMD 命令用于启动容器时默认执行的命令,CMD 命令可以包含可执行文件,也可以不包含可执行文件。

不包含可执行文件的情况下就要用 ENTRYPOINT 指定一个,然后 CMD 命令的参数就会作为 ENTRYPOINT 的参数。

CMD 命令有三种格式:

  • CMD [“executable”,“param1”,“param2”]:推荐使用的 exec 形式。
  • CMD [“param1”,“param2”]:无可执行程序形式。
  • CMD command param1 param2:Shell 形式。

一个 Dockerfile 中只能有一个 CMD,如果有多个,则最后一个生效。而 CMD 的 Shell 形式默认调用 /bin/sh -c 执行命令。

CMD 命令会被 Docker 命令行传入的参数覆盖:docker run busybox /bin/echo Hello Docker 会把 CMD 里的命令覆盖。

例如我们要启动 /app ,我们可以用如下命令实现:

CMD ["app.py"]

当然还有一些其他的命令,我们在用到的时候再去一一讲解一下。

十二、构建 Dockerfile

我们大体已经把 Dockerfile 的写法讲述完毕,我们可以自己动手写一个例子:

vi Dockerfile 开始编辑该文件,输入 i 开始编辑。以下是我们构建的 Dockerfile 内容:

FROM nginx:alpine
MAINTAINER 大技术<[email protected]>
RUN echo '

hello docker nginx

'
> /usr/share/nginx/html/index.html

编辑完后按 esc 退出编辑,然后 :wq写入,退出。

我们在 Dockerfile 文件所在目录执行:

docker build -t mynginx:1.0 .
root@odoo-pc:~/docker_mynginx# docker build -t myningx:1.0 .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM nginx:alpine
 ---> cc44224bfe20
Step 2/3 : MAINTAINER jiafei:<156277468@qq.com>
 ---> Running in 42d4d9ae4cf9
Removing intermediate container 42d4d9ae4cf9
 ---> 87b6b8f60ed9
Step 3/3 : RUN echo '

hello docker nginx

'
> /usr/share/nginx/html/index.html ---> Running in e1901c29bd3f Removing intermediate container e1901c29bd3f ---> d325de422f1d Successfully built d325de422f1d Successfully tagged myningx:1.0

我们解释一下:

  • -t 是为新镜像设置仓库和名称
  • mynginx 为镜像名
  • :1.0 为标签(不添加为默认 latest )

我们构建完成之后,使用 Docker Images 命令查看所有镜像,如果存在 REPOSITORY 为 Nginx 和 TAG 是 1.0 的信息,就表示构建成功。

root@odoo-pc:~/docker_mynginx# docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
myningx      1.0       d325de422f1d   59 seconds ago   23.5MB
nginx        alpine    cc44224bfe20   19 months ago    23.5MB
tomcat       7         9dfd74e6bc2f   2 years ago      533MB

接下来使用 docker run 命令来启动容器:

docker run --name mynginx -d -p 8080:80 mynginx:1.0

这条命令会用 Nginx 镜像启动一个容器,命名为 mynginx ,并且映射了 8080 端口。

这样我们可以用浏览器去访问这个 Nginx 服务器:

http://localhost:8080/

十三、实战测试:制作镜像并且提交到镜像仓库

1、Docker Hub仓库

1.注册docker hub 账号

网址:https://hub.docker.com/

2.服务器上使用命令行登录

docker login -u [账号名字]   #登陆命令
docker out                  #退出命令
docker push 账号/容器名字:版本号

看到Lgin Succeeded,就表示登录成功了。

3.构建镜像1)编写dockerfile

alpine官方默认镜像,没有vim命令,我们就基于此,创建一个镜像。

vim 
Dockerfile
FROM alpine
MAINTAINER 大技术
#更新Alpine的软件源为阿里云,默认官源拉取实在太慢
RUN echo http://mirrors.aliyun.com/alpine/v3.10/main/ > /etc/apk/repositories && echo http://mirrors.aliyun.com/alpine/v3.10/community/ >> /etc/apk/repositories
RUN apk update && apk upgrade
RUN apk --no-cache add vim
CMD vim --version

2)构建dockerfile

docker build 命令:

docker build -t myalpine:1.0 .
#或
docker build -f myDockerfile -t myalpine:1.0 .
root@odoo-pc:~/docker_alpine# docker build -t myalpine:1.0 .
Sending build context to Docker daemon  2.048kB
Step 1/6 : FROM alpine
 ---> c059bfaa849c
Step 2/6 : MAINTAINER fatux<156277468@qq.com>
 ---> Running in 8ec171e0b0c8
Removing intermediate container 8ec171e0b0c8
 ---> a36170d39797
Step 3/6 : RUN echo http://mirrors.aliyun.com/alpine/v3.10/main/ > /etc/apk/repositories && echo http://mirrors.aliyun.com/alpine/v3.10/community/ >> /etc/apk/repositories
 ---> Running in d8f7c62da8b0
Removing intermediate container d8f7c62da8b0
 ---> 683ea8107d90
Step 4/6 : RUN apk update && apk upgrade
 ---> Running in fd3b2b37d1cb
fetch http://mirrors.aliyun.com/alpine/v3.10/main/x86_64/APKINDEX.tar.gz
fetch http://mirrors.aliyun.com/alpine/v3.10/community/x86_64/APKINDEX.tar.gz
v3.10.9-43-g3feb769ea3 [http://mirrors.aliyun.com/alpine/v3.10/main/]
v3.10.6-10-ged79a86de3 [http://mirrors.aliyun.com/alpine/v3.10/community/]
OK: 10357 distinct packages available
OK: 6 MiB in 14 packages
Removing intermediate container fd3b2b37d1cb
 ---> e430706b76df
Step 5/6 : RUN apk --no-cache add vim
 ---> Running in d1132d081ce0
fetch http://mirrors.aliyun.com/alpine/v3.10/main/x86_64/APKINDEX.tar.gz
fetch http://mirrors.aliyun.com/alpine/v3.10/community/x86_64/APKINDEX.tar.gz
(1/4) Installing lua5.3-libs (5.3.5-r2)
(2/4) Installing ncurses-terminfo-base (6.1_p20190518-r2)
(3/4) Installing ncurses-libs (6.1_p20190518-r2)
(4/4) Installing vim (8.1.1365-r0)
Executing busybox-1.34.1-r3.trigger
OK: 34 MiB in 18 packages
Removing intermediate container d1132d081ce0
 ---> 9e32632a95a6
Step 6/6 : CMD vim --version
 ---> Running in 1116f78a3c09
Removing intermediate container 1116f78a3c09
 ---> e112e8f37470
Successfully built e112e8f37470
Successfully tagged myalpine:1.0

#查看刚构建的镜像
docker images

到这里,我们就制作好了我们自己的镜像,虽然它并没有什么用。这里我们再启动我们自己制作的镜像,进去看看我们写的dockerfile都生效了没有。注:不加标签默认是latest,所以docker run的时候要带上镜像标签。

docker run -it myalpine:1.0
#查看dockerfile的构建过程
docker history myalpine:1.0
root@odoo-pc:~/docker_alpine# docker history myalpine:1.0
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
e112e8f37470   6 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "vim …   0B        
9e32632a95a6   6 minutes ago   /bin/sh -c apk --no-cache add vim               25.6MB    
e430706b76df   6 minutes ago   /bin/sh -c apk update && apk upgrade            1.45MB    
683ea8107d90   6 minutes ago   /bin/sh -c echo http://mirrors.aliyun.com/al…   95B       
a36170d39797   6 minutes ago   /bin/sh -c #(nop)  MAINTAINER fatux<15627746…   0B        
c059bfaa849c   20 months ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
      20 months ago   /bin/sh -c #(nop) ADD file:9233f6f2237d79659…   5.59MB   

4.推送镜像至docker hub

需要先登录(根据提示输入用户名和密码即可),已登录请忽略。

docker login

看到Lgin Succeeded,就表示登录成功了。

官方文档要求,我们推送的镜像名字必须是YOURDOCKERHUB_ID/XXXX,所以我们需要给镜像换一个名字

docker tag local-image:tagname new-repo:tagname

docker push new-repo:tagname

root@odoo-pc:~/docker_alpine# docker tag myalpine:1.0 fatux/myalpine:1.0

推送到docker hub

root@odoo-pc:~/docker_alpine# docker push fatux/myalpine:1.0
The push refers to repository [docker.io/fatux/myalpine]
7c675ce87cc0: Pushed 
9e72e1f60058: Pushed 
b71f8698d81c: Pushed 
8d3ac3489996: Mounted from library/alpine 
1.0: digest: sha256:d7e0fa19089a20019a299d5e93abbe071b59b801ba911afa406b165987cdbede size: 1157

等了一会之后,就推送到docker hub

我们登陆docker hub就可以看到我们刚刚推送上去的镜像啦,这个镜像可是全世界人民都看得到的哦,是不是有点小激动呢!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ocxZmjtm-1690509603154)(file:///home/odoo/Downloads/Docker%E5%85%A5%E9%97%A8%EF%BC%8C%E8%BF%99%E4%B8%80%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86%EF%BC%8C%E5%BB%BA%E8%AE%AE%E6%94%B6%E8%97%8F%20-%20%E7%9F%A5%E4%B9%8E_files/v2-d104bf21b6cc22c724090bedaacbc913_720w.webp)]

2 阿里云仓库

1. 登录阿里云Docker Registry

$ docker login --username=zhaoj*****@163.com registry.cn-hangzhou.aliyuncs.com

用于登录的用户名为阿里云账号全名,密码为开通服务时设置的密码。

您可以在访问凭证页面修改凭证密码。

2. 从Registry中拉取镜像

$ docker pull registry.cn-hangzhou.aliyuncs.com/aliyun-fatux/aliyun:[镜像版本号]

3. 将镜像推送到Registry

$ docker login --username=zhaoj*****@163.com registry.cn-hangzhou.aliyuncs.com$ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/aliyun-fatux/aliyun:[镜像版本号]$ docker push registry.cn-hangzhou.aliyuncs.com/aliyun-fatux/aliyun:[镜像版本号]

请根据实际镜像信息替换示例中的[ImageId]和[镜像版本号]参数。

4. 选择合适的镜像仓库地址

从ECS推送镜像时,可以选择使用镜像仓库内网地址。推送速度将得到提升并且将不会损耗您的公网流量。

如果您使用的机器位于VPC网络,请使用 registry-vpc.cn-hangzhou.aliyuncs.com 作为Registry的域名登录。

5. 示例

使用"docker tag"命令重命名镜像,并将它通过专有网络地址推送至Registry。

$ docker imagesREPOSITORY                                                         TAG                 IMAGE ID            CREATED             VIRTUAL SIZEregistry.aliyuncs.com/acs/agent                                    0.7-dfb6816         37bb9c63c8b2        7 days ago          37.89 MB$ docker tag 37bb9c63c8b2 registry-vpc.cn-hangzhou.aliyuncs.com/acs/agent:0.7-dfb6816

使用 “docker push” 命令将该镜像推送至远程。

$ docker push registry-vpc.cn-hangzhou.aliyuncs.com/acs/agent:0.7-dfb6816

十四、私有化部署镜像仓库

Docker 仓库是用来包含镜像的位置,Docker提供一个注册服务器(Register)来保存多个仓库,每个仓库又可以包含多个具备不同tag的镜像。Docker运行中使用的默认仓库是 Docker Hub 公共仓库。

私有化部署镜像仓库,一般使用Docker Register、Nexus、Harbor,下面分别介绍一下。

十五、Docker desktop是啥?

安装docker时,官网有docker desktop和docker engine两种,这两种有什么不同?应该安装哪种呢?

docker engine vs docker desktop

  • docker desktop包含虚拟机、图形界面及其他特性比如带了一个单节点的kubernetes集群,虚拟机里有一个Docker CE (Docker Community Edition)守护进程。

  • docker engine,根据

    官方文档

    包含三部分,

    • 守护进程dockerd
    • api,程序可通过api与dockerd交互
    • 命令行工具客户端docker,命令docker command中的docker

在docker desktop里,docker客户端是在宿主机中,守护进程在虚拟机里。当要访问docker desktop的ip时,要谨记一条-docker network存在于虚拟机中,即使使用docker run --net host那也是使用虚拟机的host network,而不是物理机的network。docker container运行在虚拟机中,其他一切都是结果。
在Windows和MacOS中,要想运行linux容器,必须有虚拟机,在linux中是不需要的;不过,为了一致体验,如果在linux中安装desktop也会安装一个虚拟机。

在这里插入图片描述

  • 如果装在macOS、windows、linux等有图形的桌面电脑,则用docker desktop。比如windows电脑、macos电脑、ubuntu、fedora电脑。
  • 如果装在没有图形的电脑,则用docker engine,比如公司的centos服务器、阿里云的centos服务器等

十六 Docker compose简单介绍

Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。使用前面介绍的Dockerfile我们很容易定义一个单独的应用容器。然而在日常开发工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器;再比如在分布式应用一般包含若干个服务,每个服务一般都会部署多个实例。如果每个服务都要手动启停,那么效率之低、维护量之大可想而知。这时候就需要一个工具能够管理一组相关联的的应用容器,这就是Docker Compose。

Compose有2个重要的概念:

  • 项目(Project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。
  • 服务(Service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。

docker-compose.yml 案例

version: '3.3'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.8.5
    container_name: elasticsearch
    restart: always
    ports:
      - 9200:9200
      - 9300:9300
    environment:
      discovery.type: single-node
    ulimits:
      memlock:
        soft: -1
        hard: -1
  oap:
    image: skywalking/oap
    container_name: oap
    depends_on:
      - elasticsearch
    links:
      - elasticsearch
    restart: always
    ports:
      - 11800:11800
      - 12800:12800
    environment:
      SW_STORAGE: elasticsearch
      SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200
  ui:
    image: skywalking/ui
    container_name: ui
    depends_on:
      - oap
    links:
      - oap
    restart: always
    ports:
      - 8080:8080
    environment:
      SW_OAP_ADDRESS: oap:12800

部署完成后将其上传至服务器,执行docker-compose -f /app/skywalking.yml up -d即可。

未完待续。

参考网址: https://zhuanlan.zhihu.com/p/600034612

你可能感兴趣的:(docker,docker,容器,运维)