Docker 入门

什么是容器

容器技术并不是一个全新的概念,它又称为容器虚拟化。显然它是虚拟化技术中的一种。虚拟化技术目前主要有硬件虚拟化、半虚拟化和操作系统虚拟化等。本书讲述的容器虚拟化属于操作系统虚拟 化,其相较于其他主流虚拟化技术更轻量。

什么是Docker

Docker是一个开源的应用容器引擎,开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到主流的Linux/macOS/Windows机器上,实现虚拟化。 Docker是一个重新定义了程序开发测试、交付和部署过程的开放平台。在Docker的世界里,容器就是集装箱,我们的代码都被打包到集装箱里;Docker就是集船坞、货轮、装卸、搬运于一体的平 台,帮你把应用软件运输到世界各地,并迅速部署。

为什么选择Docker

Docker作为一种新兴的虚拟化方式,与传统的虚拟化方式相比优势明显。 首先,Docker容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多。其次,Docker对系统资源的利用率很高,一台普通的主机上可以同时运行数千个Docker容器。 容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行10个不同的应用就要启动10个虚拟机,而Docker只需要启动10个隔离的 应用即可。 对开发和运维人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。开发人员使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。 而这正是Docker可以做到的,Docker容器的运行不需要额外的hypervisor支持,它是内核级的虚拟化,因此可以实现更高的性能和效率。 Docker容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人计算机、服务器等。这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个平台上。使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。

Docker与虚拟机的区别

虚拟机和Docker最明显的差别是虚拟机需要安装操作系统(安装Guest OS)才能执行应用程序,而Docker内不需要安装操作系统。Docker技术不是在OS外来建立虚拟环境,而是在OS内的核心系统 层来打造虚拟执行环境,通过共享宿主机OS的做法,取代一个一个Guest OS的功用。Docker也因此被称为是操作系统虚拟化技术。

Docker容器

在Docker的世界中,容器是一个核心,容器是一个基于Docker镜像创建,包含为了运行某一特定程序所有需要的OS、软件、配置文件和数据,是一个可移植的运行单元。不过在宿主机看来,它只是 一个简单的用户进程而已。关于容器的知识,在第8章会有详细介绍,在这里读者只需要知道容器是从镜像创建的运行实例,它是独立的一个沙盒即可。 容器很好地诠释了集装箱的理念,开发人员不用关心容器内部是什么应用,只管传输、运行即可,这是一种标准化的集装和运输方式,正是因为Docker把容器技术进行了体验友好的封装,才使得容器 技术迅速推广普及。

Docker仓库

相信大家对Github这个网站不会陌生,Github上有着海量的代码仓库,类似的,在Docker中,当开发者想要构建一个镜像或运行一个容器时,一般要先有一个现成的镜像才可以执行构建或者运行, 而本地又没有该特定镜像时怎么办呢?Docker提出了Registry的概念,用户可以上传自己的镜像到Registry上,如果是公开的,那么全世界的用户都可以拉取这个镜像来操作,可以说Registry就是一个“软 件商店”。类比传统运输业,Registry类似一种船坞、中转站一样,是一个集中存放“集装箱”(镜像)的地方。 Docker官方的Registry地址是https://hub.docker.com/。 除了这个官方的地址,用户还可以搭建自己的私有Registry用来存储非公开的镜像等。

安装Docker

Linux系统

Docker基于Linux容器技术,面向服务器端,所以对Linux的支持无疑是最好的,主流的Linux系统都可以安装Docker。 Docker只能安装运行在64位计算机上(社区有对32位的支持),Linux内核版本必须大于3.10,内核小于3.10的系统会因为缺少Docker容器运行所需的功能而有错误。

一键安装脚本

Docker官方提供了便捷的安装脚本,通过脚本自动完成安装。官网脚本一共有3种版本供用户选择,分别是最新的稳定版本、测试版本和实验版本。

(1)安装稳定版本

 curl -sSL https://get.docker.com/ | sh  

或者

  wget -qO- https://get.docker.com/ | sh 

(2)安装测试版本

  curl -fsSL https://test.docker.com/ | sh 

或者

  wget -qO- https://test.docker.com/ | sh 

(3)安装实验版本

  curl -fsSL https://experimental.docker.com/ | sh

或者

 wget -qO- https://experimental.docker.com/ | sh 

如果脚本在用户的Linux机器上无法运行,或者出现其他错误,可以继续往下看,下文有非脚本的手动安装教程。 如果安装时出现如下没有aufs的提示,用户可以安装内核扩展(Ubuntu系列)。

  sudo apt-get install linux-image-extra-'uname -r'

或者自己下载aufs编译安装,再或者等待10秒,安装脚本会使用替代方案安装Docker。

 sudo curl -sSL https://get.docker.com/ | sh 
modprobe: FATAL: Module aufs not found in directory /lib/modules/ 4.4.0-2-**-amd64
 Warning: current kernel is not supported by the linux-image-extra-virtual 
package. We have no AUFS support. Consider installing the packages 
linux-image-virtual kernel and linux-image-extra-virtual for AUFS support. 
+ sleep 10 

具体发行版安装会有细小差别,如果不能完成自动安装可以使用下面方法手动安装Docker。

Debian发行版

如前面所说,安装前确保机器为64位计算机,并且Linux内核在3.10以上

查看内核版本

要查看当前的内核版本,只需要打开一个终端,使用uname-r查看内核版本。

uname -r 

3.13.0-85-generic

如果内核版本不达到要求,需要升级内核。目前Debian一般都不用升级内核。

sudo apt-get update 
sudo apt-get dist-upgrade
sudo reboot 

更新APT源

打开一个终端,安装apt-transport-https包,使得APT支持HTTPS协议的源。

sudo apt-get update && sudo apt-get install apt-transport-https ca-certificates 

添加Docker源的gpg密钥。

sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D 

添加Docker的官方APT软件源

Debian每次发行都有一个代号,使用下面命令可查看当前操作系统的代号。这里以Debian 8.0为例,返回的系统代号是wheezy。

lsb_release -c 
Codename: jessie 

·Debian testing代号为stretch/Sid。

·Debian 8.0代号为Jessie。

·Debian 7.7代号为Wheezy。

获得系统代号之后,通过命令创建/etc/apt/sources.list.d/docker.list文件(如果不存在就新建),并写入源的地址内容。注意修改为自己操作系统对应的代号。 创建/etc/apt/sources.list.d/docker.list文件(这里使用编辑器Vim,如果不熟悉Vim的操作,读者可以选择自己喜欢的编辑器进行编辑)。

sudo vim /etc/apt/sources.list.d/docker.list 

Debian Wheezy如下:

deb https://apt.dockerproject.org/repo debian-wheezy main 

Debian Jessie如下:

deb https://apt.dockerproject.org/repo debian-jessie main

Debian Stretch/Sid如下:

deb https://apt.dockerproject.org/repo debian-stretch main 

添加成功后,更新APT软件包缓存。

sudo apt-get update 

校验软件包缓存结果。

apt-cache policy docker-engine 

安装Docker

安装Docker之前,如果用户以前装过Docker,那么需要先完全卸载Docker再安装。

apt-get purge "lxc-docker*" 
apt-get purge "docker.io*" 

一切没问题之后,执行安装。

sudo apt-get install docker-engine 

启动Docker

sudo service docker start 

确认Docker运行正常

sudo docker run --rm hello-world 

返回Helo World表示运行正常。

Ubuntu发行版

Docker目前支持的最低Ubuntu版本为12.04LTS,但推荐用户至少使用14.04LTS版本。

查看内核版本

要查看当前的内核版本,只需要打开一个终端,使用uname-r查看内核版本。

uname -r 
3.13.0-85-generic 

如果内核版本不达到要求,需要升级内核。

sudo apt-get update 
sudo apt-get install -y linux-images-generic-lts-raring linux-headers- generic-lts-raring 
sudo reboot 

更新APT源

打开一个终端,安装apt-transport-https包,使得APT支持HTTPS协议的源。

sudo apt-get update && sudo apt-get install apt-transport-https ca-certificates 

添加Docker源的gpg密钥。

sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D 

添加Docker的官方APT软件源

Ubuntu/Debian每次发行都有一个代号,使用下面命令可查看当前操作系统的代号。这里以Ubuntu 14.04为例,返回的系统代号是trusty。

lsb_release -c 
Codename: trusty 

·Ubuntu 12.04(LTS)代号为precise。

·Ubuntu 14.04(LTS)代号为trusty。

·Ubuntu 15.04代号为vivid。

·Ubuntu 15.10代号为wily。

·Ubuntu 16.04(LTS)代号为xenial。

获得系统代号之后,通过命令创建/etc/apt/sources.list.d/docker.list文件(如果不存在就新建),并写入源的地址内容。注意修改为自己操作系统对应的代号。 创建/etc/apt/sources.list.d/docker.list文件(这里使用编辑器Vim,如果不熟悉Vim的操作,可以选择自己喜欢编辑器进行编辑)。

sudo vim /etc/apt/sources.list.d/docker.list 

Ubuntu 12.04(LTS)如下:

deb https://apt.dockerproject.org/repo ubuntu-precise main 

Ubuntu 14.04(LTS如下):

deb https://apt.dockerproject.org/repo ubuntu-trusty main 

Ubuntu 15.04如下:

deb https://apt.dockerproject.org/repo ubuntu-vivid main 

Ubuntu 15.10如下:

deb https://apt.dockerproject.org/repo ubuntu-wily main 

Ubuntu 16.04(LTS)如下:

deb https://apt.dockerproject.org/repo ubuntu-xenial main 

添加成功后,更新APT软件包缓存。

sudo apt-get update 

校验软件包缓存结果:

apt-cache policy docker-engine 

安装Docker

确保基本安装条件满足,如果用户以前装过Docker,那么需要先完全卸载Docker再安装.

apt-get purge "lxc-docker*" 
apt-get purge "docker.io*"

然后再执行如下安装步骤。

sudo apt-get update 
sudo apt-get install docker-engine

显示如下信息说明安装成功:

+ sudo -E sh -c docker version

Client:

Version: 1.11.2

API version: 1.23

Go version: go1.5.4

Git commit: b9f10c9

Built: Wed Jun 1 22:00:43 2016

OS/Arch: linux/amd64

Server:

Version: 1.11.2

API version: 1.23

Go version: go1.5.4

Git commit: b9f10c9

Built: Wed Jun 1 22:00:43 2016

OS/Arch: linux/amd64

If you would like to use Docker as a non-root user, you should now consider

adding your user to the "docker" group with something like:

sudo usermod -aG docker yourusername

Remember that you will have to log out and back in for this to take effect!

启动Docker

sudo service docker start

确认Docker运行正常

sudo docker run --rm hello-world

返回Hello World表示运行正常。

为非root用户授权

如果没有Docker用户组就建立一个Docker用户组(默认安装后自动创建)。

sudo groupadd docker

增加当前用户到Docker组,需要注销来生效:

sudo gpasswd -a${USER} docker

重启Docker服务:

sudo service docker restart

这样,执行Docker命令就不必使用sudo申请权限了。

Docker Compose

Compose可译为组合物。[12]Compose 是用于定义和运行 多个容器Docker应用程序 的工具。通过Compose,你可以使用YAML文件来配置应用程序需要的所有服务,然后通过使用一个命令,就可以创建并启动所有服务。[13][14]Compose对应的命令为docker-compose[15]

Docker Desktop

Docker Desktop 是适用于Windows、Linux、MacOS操作系统的一键式安装程序,提供了图形用户界面,便于管理Docker的容器、镜像以及应用等对象。Docker Desktop包含了:Docker Engine、CLI、构建以及扩展等组件。[16]

Swarm Mode

当说到 Docker Swarm 时,一般是指单独项目 Docker Swarm。而在Docker 1.12时,将swarm mode集成到Docker 引擎中,可用Docker引擎API 和 CLI 命令直接使用。官方推荐用户使用集成的 swarm mode [17]

Swarm Mode 内置 kv 存储功能,提供了众多的新特性,比如:具有容错能力的去中心化设计、内置服务发现、负载均衡、路由网格、动态伸缩、滚动更新、安全传输等。使得 Docker 原生的 Swarm 集群具备与 Mesos、Kubernetes 竞争的实力。[18]

cluster(中文:集群),Docker将集群定义为:一群共同作业并提供高可用性的机器[5]。swarm(中文:群[19]),是指一个集群的Docker引擎以swarm mode形式运行[5]。swarm mode是指Docker引擎内嵌的集群管理和编排功能。当你初始化了一个swarm(cluster)或者将节点加入一个swarm时,其Docker引擎就会以swarm mode的形式运行。[5]

原理

swarm中的Docker机器中分为 managers(管理者) 和 workers(员工),管理者用于处理集群的关系和委派,员工则用于执行 swarm服务。[20]当你创建swarm服务时,你可以为其增加各种额外的状态(如:数量、网络、端口、存储资源等等)。Docker会去维持用户想要的状态。如:一个工作节点如果挂了,那么Docker会去把这个节点的任务给另外一个节点。此处的任务(task)是指:被swarm管理者管理的一个运行中的容器。[20]

swarm服务比单独容器好在,修改swarm服务的配置之后不用重启。同时,Docker以swarm mode形式运行时,也可以选择直接启动单独的容器。另外,swarm mode下,你也可以通过 docker stack deploy 使用 Compose file 部署应用栈。[20][21]swarm服务分为两种,一种是replicated services ,可以指定节点任务的总数量;global services,则是每个节点都会运行一个指定任务。[22]swarm管理员使用 ingress 负载均衡使服务可被外部接触。 swarm管理员会自动地给服务分配PublishedPort(或者手动配置)。外部组件,如云负载均衡器能通过集群中任何节点上的PublishedPort去接入服务(不管该服务是否启动)。另外 swarm mode有内部DNS组件,它会为每个服务分配一个DNS条目。swarm管理员使用 internal load balancing 去分发请求时,就是依靠这个DNS组件。[23]

swarm mode的功能是由swarmkit(一个独立项目)提供的,它实现了Docker的编排层。swarm可以直接被Docker使用。[20]

文件格式

Docker有两种文件格式,Dockerfile和Compose file。Dockerfile定义了单个容器的内容和启动时候的行为。Compose file定义了一个多容器应用。[24]

Dockerfile

Docker 可以依照 Dockerfile 的内容,自动化地构建镜像。 Dockerfile 是包含着用户想要如何构建镜像的所有命令的文本。[25]

FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py

关键词:

  • RUNRUN会在当前镜像的顶层上添加新的一层(layer),并在该层上执行命令,执行结果将会被提交。提交后的结果将会用于Dockerfile的下一步。[26]

  • ENTRYPOINT入口点ENTRYPOINT允许你配置容器,使之成为可执行程序。[27]即,ENTRYPOINT允许你为容器增加一个入口点ENTRYPOINTCMD类似,均在容器启动时执行,但是ENTRYPOINT为了提供稳定且不可被覆盖的操作。[28]通过在命令行中指定--entrypoint 命令的方式,可在运行时将Dockerfile文件中的ENTRYPOINT覆盖。

  • CMD,是command的缩写。CMD用于为已创建的镜像提供默认的操作,当不想要用默认操作时候,可用docker run IMAGE[:TAG|@DIGEST] [COMMAND] 进行替换 。但当Dockerfile拥有入口点时,CMD用于赋予入口点参数。[29]

Compose文件

Compose文件 是一个YAML文件,定义了服务(service)、网络、(volume)。

  • 服务(service)定义各容器的配置,定义内容将以命令行参数的方式 传给 docker run 命令。

  • 网络(network),类似地,将定义内容传给 docker network create 命令 。

  • 卷(volume),类似地,将定义内容传给 docker volume create 命令。

docker run 命令中有一些选项,和 Dockerfile文件中的指令效果一样(如:CMD, EXPOSE, VOLUME, ENV),如果Dockerfile文件中使用这些指令,那么这些指令就会被视为默认参数,所以开发者无需特意在 Compose文件中再指定一次。[30]

Compose文件 可使用 Shell变量(Variable),如:[31]

db:
  image: "postgres:${POSTGRES_VERSION}"

Compose文件 可通过自身的ARGS变量,将参数传给DockerfileARGS 指令。[32]

网络

参考文档:Docker文档-网络概要页面存档备份,存于互联网档案馆

bridge

在Docker里,网桥网络 使用的是 软件形式的网桥。使用相同的网桥的容器连接进入该网络,而非该网络的容器刷故无法接入。Docker网桥驱动会自动地在Docker主机上安装规则,这些规则让不同桥接网络之间不能直接通信。[33]桥接经常用于:在单独容器上运行应用时,可通过 网桥 进行通信。[34]网桥网络 适用于容器运行在相同地Docker守护进程的主机上。不同Docker守护进程主机上的容器,它们之间的通信需要依靠操作系统层次的路由,或者你应该使用 overlay网络 进行代替。[33]

bridge 是网桥驱动,是Docker默认的网络驱动(接口名为 docker0[35]),当你不为容器指定一个网络时候,Docker将会使用该驱动。[34]可通过 daemon.json 文件修改相关配置。[36]

自定义网桥可通过 brctl 命令进行配置。[37][38]

host

主机模式

host 用于单独容器,该网络下容器只能和Docker主机进行直接连接。host 只适用于 Docker 17.06或以上版本的swarm服务。

host网络和VirtualBox的 仅主机网络(Host-only Networking) 类似。[39]

overlay

overlay (中文:覆盖网络)网络驱动将会创建分布式网络,该网络可以覆盖若干个 Docker守护进程主机。该网络是基于 主机特定网络(host-specific networks),允许 swarm服务 和 容器 进行安全通信(当加密功能开启时)。在该网络下,Docker能够清晰地掌握 数据包的路由 以及 发送接收容器。[40]

overlay 有两种网络类型网络:[40]

  • ingress 网络,可掌控 swarm服务 的网络流量 。该网络是 overlay 的默认网络。

  • docker_gwbridge 网络是 网桥网络。该网络会将 单独的Docker守护进程 连接至 swarm里的另外一个守护进程。

overlay 网络下,单独的容器 和 swarm服务 的行为和配置概念 是不一样的。[40]

该策略不需要 容器们 具有操作系统级别的路由,因为Docker负责路由。[34]

macvlan

Macvlan网络配置提供了一种机制,允许单独的容器具有独立的MAC地址,使得这些容器在网络上表现得如同物理设备。

none

该策略下,容器不使用任何网络。none 常常用于连接自定义网络驱动的情况下。

其他

截止2023年5月18日,Docker官方仓库域名 https://hub.docker.com/页面存档备份,存于互联网档案馆) 在中国大陆被屏蔽,方式为DNS污染。随后短暂解封,2024年6月6日再次被封。

数据管理

Docker默认下,所有文件将会存储在容器里的可写的容器层(container layer)。[41]

  • 数据与容器为一体。随着容器消失,数据将消失;难以与其他程序(容器)共享。可以采用挂载文件的方式解决。

  • 由于容器的写入层是与宿主机器紧紧耦合。所以你难以移动数据到其他机器。

  • 容器的写入层的是通过 存储驱动页面存档备份,存于互联网档案馆)(storage driver) 管理文件系统。存储驱动页面存档备份,存于互联网档案馆) 会使用Linux内核的 链合文件系统(union filesystem)进行挂载。相比起直接操作于宿主机器文件系统的 数据卷,这额外的抽象层将会降低性能。

容器有两种永久化存储方式:卷(volumes)绑定挂载(bind mounts)。另外,Linux用户还可使用 tmpfs 进行挂载;Window用户还可以使用 命名管道(named pipe)。在容器中,不管是哪种永久化存储,表现形式都是一样的。[41]

卷(volumes)是宿主机器的文件系统的一部分,由Docker进行管理( 在Linux,存储于/var/lib/docker/volumes/)。非Docker程序不应该去修改这些文件。Docker推荐使用 卷 进行持久化数据。 卷 可支持 卷驱动(volume drivers),该驱动允许用户将数据存储到 远程主机 或 云服务商(cloud provider)或 其他。[41]

没有名字的卷叫匿名卷(anonymous volume),有名字的卷叫命名卷(named volume)。匿名卷没有明确的名字,当被初始化时,会被赋予一个随机名字。[41]

绑定挂载

绑定挂载(bind mounts)通过将宿主机器的路径挂载到容器里的这种方式,从而数据持续化,因此绑定挂载可将数据存储在宿主机器的文件系统的任何地方。非Docker程序可修改这些文件。 绑定挂载是Docker早期就存在的,相比起卷,绑定挂载十分简单明了。[41]在开发Docker应用时,应使用命名卷(named volume)代替绑定挂载,因为用户不能对绑定挂载进行 Docker CLI 命令操作。[41]

绑定挂载常用于:[42]

  • 同步配置文件,如: 将 宿主主机的DNS配置文件/etc/resolv.conf)同步至容器中

  • 在开发程序时,将 源代码 或 Artifact 同步至容器中。[42]这种用法与 Vagrant 类似。

tmpfs

tmpfs 挂载(tmpfs mounts),仅仅存储于内存中,并不操作 宿主机器的文件系统(不持久化于磁盘)。它可用于存储一些 非持久化状态、敏感数据。 举例,swarm服务 通过tmpfssecrets页面存档备份,存于互联网档案馆)(密码、密钥、证书等)存储到swarm服务。 [41]

命名管道

命名管道(named pipes),通过 npipe 挂载的形式,使 Docker主机 和 容器 之间能互相通讯。常见用例是在容器内运行第三方工具,并使用命名管道连接到Docker Engine API。[41][43]

覆盖问题

当挂载 空的卷 至一个目录中,目录中的内容会被复制于卷中(不会覆盖)。如果挂载 非空的卷 或 绑定挂载 至一个目录中,那么该目录的内容将会被隐藏(obscured ),当卸载后内容将会恢复显示。[44]

日志

UNIX类Unix系统中,常见的 I/O流(英语:I/O streams) 分为三种:STDIN(输入 )、 STDOUT(正常输出)、STDERR(错误输出)。[45]

默认配置下,Docker的日志(如:docker logsdocker service log)所记载的是命令行的输出结果(STDOUTSTDERR)。而STDOUTSTDERR 对应的文件路径分别是 /dev/stderr/dev/stdout[45]另外,也可以在宿主主机上查看容器的日志,使用以下命令可以查看到容器的日志位置。[46]

$ docker inspect --format='{{.LogPath}}' $INSTANCE_ID

Kubernetes in docker

kind(全称:Kubernetes IN Docker)是部署本地Kubernetes集群的工具,而集群的节点是由Docker生成的。[47]

操作细节

在安装完kind之后,通过kind create cluster命令生成集群。[47]生成Kubernetes集群后,可以通过Docker命令进行查看节点概览:

$ docker container ls

CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                       NAMES

99c96c1f21ab        kindest/node:v1.17.0   "/usr/local/bin/entr…"   4 minutes ago       Up 4 minutes        127.0.0.1:32769->6443/tcp   kind-control-plane

✍️ 作者:Tom

📄 共享协议: CC 4.0协议

🔗 原文链接: https://22.3344567.xyz/archives/6fb28c6e-3fcb-4242-ad25-bc5cbffe5002

评论