0%

docker 总结

基本概念

docker是一个帮助开发者使用容器进行开发、部署项目的平台。

容器概念

容器是一种轻量级虚拟化,也称为操作系统级虚拟化。它是一个中间件和回显系统,允许在多个称为容器的实例之间共享操作系统。每个容器与其他容器分开,并且可以具有与其他容器和基本操作系统不同的Linux风格。

容器和虚拟机的区别

docker可以将应用程序封装在不同的容器中执行并为每个容器提供单独的运行环境,每个容器内部都可以视为一个完整的主机环境,其功能类似于虚拟机,但和虚拟机相比容器更加轻量。虚拟机和主机操作系统的关系是相互独立的,它们共享主机硬件但虚拟机之间以及和主机操作系统之间内核是相互隔离的,虚拟机拥有完整的系统内核。容器是运行在主机操作系统之上的,容器以及主机其他程序共享系统内核,但每个容器拥有自己独立的运行库以及其他运行环境

组成结构

docker采用客户端-服务器模式,其主要由以下部分构成:

  • daemon:是运行在主机上的守护进程,docker的所有操作指令通过该守护进程进行
  • API:docker提供的daemon的接口,通过API来控制daemon
  • CLI:是docker的客户端,通过命令行或者界面输入docker指令,输入的指令实际会传输给daemon执行
  • network:docker提供的网络服务,docker允许容器之间通过多种网络驱动方式进行通信
  • image:docker所管理的镜像,其组成结构类型于栈,由多个只读层叠加而成,是生成容器的模板
  • container:docker所管理的容器,应用程序运行在容器内部,容器通过镜像构建而成。
  • volumes:docker提供的数据存储功能,docker提供与容器独立的数据存储功能以减小容器大小,同时持久化数据,docker提供两种数据存储方式。

Dockerfile

dockerfile是一个包含构建镜像所需命令的文本文件,docker可以根据dockerfile文件构建镜像
dockerfile以from开始,表示使用另一个镜像作为生成当前镜像的基础,新创建的镜像会包含基础镜像的内容。dockerfile中包含了构建镜像所需的全部命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 使用python的运行环境作为parent image
FROM python:2.7-slim

# 设置工作路径
WORKDIR /app

# 将当前目录的内容复制到相应路径下
COPY . /app

# 下载requirements.txt中要求的包
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 暴露容器的80端口
EXPOSE 80

# 定义环境变量
ENV NAME World

#当docker开始运行时执行文件
CMD ["python", "app.py"]

镜像image

docker中的容器都是基于镜像构成的,镜像是由多个只读层构成,每个只读层记录了dockerfile的一条命令,docker每执行dockerfile的一条命令都会新建一个只读层记录该指令操作结果。

1
2
3
4
5
6
7
8
9
10
$ docker history yezh01/getstart:v1.0 
IMAGE CREATED CREATED BY SIZE COMMENT
7ecc82fd960a 7 days ago /bin/sh -c #(nop) CMD ["python" "app.py"] 0B
54b7ddd84299 7 days ago /bin/sh -c #(nop) ENV NAME=World 0B
9262b67607c5 7 days ago /bin/sh -c #(nop) EXPOSE 80 0B
f595bf8c2004 7 days ago /bin/sh -c pip install --trusted-host pypi.p… 10.9MB
baa7e092c815 7 days ago /bin/sh -c #(nop) COPY dir:c984b678f865c5dd8… 1.19kB
581ae7fc3818 7 days ago /bin/sh -c #(nop) WORKDIR /app 0B
5f759d36aead 7 days ago /bin/sh -c #(nop) CMD ["python2"] 0B
<missing> 7 days ago /bin/sh -c set -ex; savedAptMark="$(apt-ma… 7.39MB

docker可以在一个镜像的基础上生成新的镜像,新的镜像不会复制原有镜像的内容而是和原有镜像共享文件数据,这样可以减小镜像大小,提高存储和传输效率。

容器

容器内包含了应用程序和其所需的与运行环境,docker可以为每个容器设置不同的运行环境,而且彼此互不干扰。

容器是根据镜像生成的,容器与镜像很重要的一个区别在于,镜像都是由只读层构成的,而容器在镜像只读层之上构建了一个读写层用于记录对容器的修改操作,因此,容器是可修改的,而镜像是只读的。

层次结构

一个镜像可以同时生成多个容器副本,容器副本之间共享同一个镜像,不同容器副本只保留自己容器的修改结果。

容器读写策略

docker采用写时复制(cop-on-write)策略,当容器需要读取底层层次的数据时,如果不需要对数据进行修改,则直接从底层读取数据;如果需要对数据进行修改,则将数据复制到当前层次进行修改并将修改后的数据保留在当前层次。这样可以减小容器体积同时提高读写效率。

应用程序的分级结构

docker对应用程序的执行方式可以分成三级层次结构:

  • 堆栈stack
  • 服务services
  • 容器container

容器

容器是应用的最底层,容器内部包含了一个应用程序或者应用程序的一个组件。应用程序都是在容器内部执行的。

服务

服务是指应用程序的其中一部分功能,服务可以理解为一群“运行中的容器”,服务中的容器都是根据同一个镜像生成的,服务定义了容器的行为方式,包括:容器对外开放的借口、生成的容器数量等。 服务是通过yml文件进行配置的。

集群

docker支持在集群上运行服务,即一个服务可以执行在多台主机或者虚拟机中。

docker可以将主机设置为集群模式或者加入一个集群,每个集群含有一个管理者manager和若干工作者worker,manager可以控制集群工作,而worker只提供自身的计算能力。集群模式下的控制命令与独立容器的控制命令一致,将命令发送给manager,manager会自动控制集群执行命令。
集群会自动进行负载均衡,即当manager收到一个提供服务的命令后,集群会自动将服务的容器实例均匀分布到各个集群主机中。

外界对任何一台集群主机的访问的结果都是一致的,外界可以通过一台主机访问集群内所有主机的服务。

堆栈

堆栈是一组相互关联的服务,它们共享依赖关系,并且可以协调和缩放在一起。单个堆栈能够定义和协调整个应用程序的功能

数据管理

docker使用union file system来管理数据,docker构建image和container也是采用了同样的技术。
docker支持多种数据存储驱动,官方推荐的最新驱动是overlay2

数据存储

由于容器读写层数据在容器结束后会丢失,同时,在容器中记录过多数据会导致容器体积过大,因此,docker提供了而外的数据存储方式:挂载主机文件、挂载docker volume、挂载tmpfs。

  • 挂载主机文件是直接将主机中的目录挂载在docker中,这中方式依赖于主机特定的文件系统。
  • 挂载docker volume。volume是docker提供的首选数据存储方式,volume独立于容器存在,容器结束后volum不会消失。
  • tmpfs是linux上的文件系统,其将数据保留在主机内存中,主机关机后,内存数据丢失,tmpfs上保留的数据也会丢失。

网络

docker提供多种网络驱动:

  • bridge: docker默认网络驱动,docker提供默认网桥也允许自定义网桥,官方推荐自定义网桥
  • overlay:支持不同daemon下的容器之间一个集群服务之间的通信,适用于集群环境
  • host: 容器与主机没有网络隔绝,与主机贡献网络,适用于需要隔绝除网络以外的其他部分的场景
  • macvlan:为容器分配MAC地址,daemon可以通过容器MAC地址将流量直接路由给容器而无需通过主机网络栈
  • none:禁用容器一切网络

windows10下实现在docker容器中访问docker daemon。

在docker desktop setting中选择 expose daemon on tcp://localhost:2375 without tls.

在容器应用中使用docker.for.win.localhost指向主机localhost

1
2
3
import docker
client = docker.DockerClient(base_url = 'tcp://docker.for.win.localhost:2375')
print(client.containers.run("alpine", ["echo", "hello", "world"]))

docker学习三:network

docker支持容器之间的网络通信,docker的网络通信方式有以下五种:

driver 说明 使用场景
bridge docker 默认的网络驱动,如果不指定网络驱动,docker就会创建一个bridge bridge适用于同一docker主机上的容器通信
overlay overlay支持不同daemon下的容器之间和集群服务之间的相互通信 overlay适用于不同主机的容器之间的通信,或者集群服务中的多个应用程序协调工作
host host消除了独立容器与主机之间的网络隔离,container直接使用主机的网络 host适用于需要隔离容器除网络以外的其他部分的情况
macvlan macvlan允许用户为容器分配MAC地址,daemon可以将流量通过容器MAC地址之间路由到容器而不需要经过docker host的网络栈 macvlan适用于从虚拟机配置移植或者需要让容器表现为一台包含mac地址的物理机的情况
none none禁止容器的所有网络,其一般和用户自定义网络驱动一起使用,集群服务不支持none

bridge

docker bridge与一般的网桥功能类似,能够让同一网桥下的容器相互通信。
docker提供默认bridge,若不指定容器的驱动,则自动将容器关联到默认bridge中。 官方推荐用户自定义bridge而不是使用默认bridge

自定义bridge和默认bridge的区别

自定义bridge 默认bridge
隔离性和连接性 位于同一bridge的容器之间暴露自身所有的端口,容器端口默认对外界关闭,需要使用-p--publish开启 需要手动打开端口,同时需要通过docker外的方式控制外界对端口的访问
容器之前的DNS服务 支持自动DNS解析,可以通过容器别名访问容器 容器之间只能通过ip地址访问或者为通信两端的容器分别使用--link选项
热插拔 支持在运行过程中断开或连接自定义bridge 容器与默认网桥断开时,需要关闭容器并使用新配置重建容器
全局配置 每个自定义bridge可以使用docker命令单独配置 对默认bridge的配置会影响到所有使用默认bridge的容器,且配置默认bridge需要使用docker外的命令
共享环境变量 自定义bridge无法直接共享环境变量,但可以通过
挂载卷或者目录
使用compos声明共享变量
使用集群服务代替独立容器,通过配置共享变量
使用--link flag共享环境变量

配置自定义bridge

创建和删除bridge的命令如下:

1
2
docker network create my-net
docker network rm my-net

创建bridge时可以配置bridge的子网范围、网关和其他选项,具体的配置参数可以使用docker network --help或者查看官方文档

连接bridge

可以在创建容器的同时连接bridge,如:

1
2
3
4
5
6
7
8
$ docker create --name my-nginx \
--network my-net \
--publish 8080:80 \
nginx:latest

$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a716400c6714 nginx:latest "nginx -g 'daemon of…" 7 minutes ago Created my-nginx

上面创建了一个容器my-nginx,同时将容器连接到my-net 网桥并将8080端口暴露出来供外界访问。
连接已存在的容器和bridge时可以使用以下命令

1
$ docker network connect my-net my-nginx

取消连接可以使用以下命令

1
$ docker network disconnect my-net my-nginx

overlay

overlay在多个docker主机上面创建一个分布式网络,该网络位于各个主机的具体网络之上,允许容器连接该网络进行通信。
当创建一个集群或者将docker主机加入到集群时,docker主机会创建两个网络:

  1. ingress:是一个overlay网络,用于处理和集群控制有关的命令和数据流量,如果创建集群时没有指定自定义overlay网络,集群默认连接到ingress
  2. docker_gwbridge:一个 bridge网络,用于连接集群中各个daemon守护进程

创建overlay

创建overlay的命令与bridge基本一致,但创建overlay的命令必须在位于集群中的主机执行,使用命令docker swarm initdocker swarm join将主机加入到集群中

1
$ docker network create -d overlay my-overlay

host

使用host网络相当于容器和主机共享一个网络,docker不会为容器分配ip地址,访问容器端口的方式改为直接访问主机对应的端口,-p-P等端口相关的指令会被忽略。
host网络只在linux下工作,不提供对mac和Windows的支持

macvlan

某些应用程序,尤其是后台应用程序或监视网络流量的应用程序,希望直接连接到物理网络。在这种情况下,您可以使用macvlan网络驱动程序为每个容器的虚拟网络接口分配MAC地址,使其看起来像是直接连接到物理网络的物理网络接口。在这种情况下,您需要在Docker主机上指定一个物理接口,用于macvlan,以及macvlan的子网和网关。您甚至可以使用不同的物理网络接口隔离您的macvlan网络。但需要记住以下事项:

  • 由于IP地址耗尽或“VLAN传播”,很容易无意中损坏您的网络,在这种情况下,网络中存在大量不合适的MAC地址。
  • 您的网络设备需要能够处理“混杂模式”,其中一个物理接口可以分配多个MAC地址。
  • 如果您的应用程序可以使用bridge(在单个Docker主机上)或overlay(跨多个Docker主机进行通信),那么从长远来看,这些解决方案可能会更好。

none:关闭容器的网络

使用--network none可以关闭容器内的网络栈,在容器内只剩下回环地址。

1
2
3
4
5
$ docker run --rm -dit \
--network none \
--name no-net-alpine \
alpine:latest \
ash

查看容器内部网络,里面只含有回环地址

1
2
3
$ docker exec no-net-alpine ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

dicker:数据管理

数据管理机制

docker使用union file system来管理数据,docker构建image和container也是采用了同样的技术。

image层次

iamge由多个层次构成,每个层次包含dockerfile的一条指令,除了最后一层外,其他层次都是只读的。

1
2
3
4
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py

其一共生成四个层次:

  • FROM 将ubuntu:18.04作为base image
  • copy将运行docker命令的目录下的文件复制到容器中的app文件中
  • run 使用make命令生成应用程序
  • cmd 在容器中运行指定命令

docker层次的管理方式类似于git版本管理,层次的结构类似于一个栈,每个层次只记录与之前的层次的差异。当创建一个container时,会在image层次的基础上再创建一个读写层,对容器的修改操作都记录在读写层上而不会修改底层记录的数据。

层次结构

container层次

container和image最大的区别在于container比image多了一层读写层用于记录对容器的修改,读写层在容器结束运行后销毁,其所记录的一切结果都不会保留,也不会改变image的数据。

container和image

由于container不会修改底层image的数据,所以多个container副本底层可以共享一个image。

copy-on-write策略

copy-on-write用于提高共享和复制文件的效率。
当需要对低层次数据进行读取时,如果不涉及数据修改,那么直接从低层读取现有数据;如果需要修改数据,则将数据拷贝到当前层次进行修改,下次读取相同数据时,从修改后的最新数据读取。

当我们将某个image作为base image构建新的image时,新的image不会复制base image的层次,而是共享相同的只读层。

存储驱动

docker支持多种存储驱动,官方推荐的驱动程序是overlay2。

使用docker info可以查看docker使用的是那种驱动

1
2
3
4
$ docker info 
...
Storage Driver: overlay2
...

数据存储方式

docker中存储用户数据有三种方式:记录在读写层、挂在主机文件系统、设置volume。
其中使用读写层记录数据会存在以下问题:

  • 数据无法持久化,当容器关闭后,读写层记录的数据就会消失
  • 数据不利于共享,其他容器或进程很难获取到容器读写层的数据
  • 读写效率低,读写层存储数据需要使用存储驱动,该驱动需要使用linux内核,运行效率低

如上图所示,
volume是将数据主机文件系统中docker所管理的区域,bind mount是将主机文件系统中的任何一部分挂载到container中,tmpfs mount是linux上的一个文件管理系统,他所有的数据都存储在内存中而不会保留在硬盘上,所以掉电后,其里面所有数据都会丢失。

相关链接

https://docs.docker.com/storage/storagedriver/
https://docs.docker.com/storage/storagedriver/select-storage-driver/
https://docs.docker.com/storage/storagedriver/overlayfs-driver$

docker 理解

docker是一个客户服务器结构的应用程序,其结构如下所示

其组成部分包括

  • container容器:是image的运行实例,一般container之间以及container与主机之间是相互隔离的,相当于一台轻量级的虚拟机
  • iamge镜像:是一个只读模板,是静态的,image通常是基于另一个image创建的
  • docker CLI 客户端:通过调用RESTAPI控制docker daemon
  • RESTAPI:是与docker daemon交互的API
  • docker daemon :docker daemon是一个始终运行的守护进程,docker的指令通过客户端传递给daemon执行
  • network: docker提供的网络功能,用于容器间的通信
  • datavolumes:docker提供的数据存储功能,用于将数据存储与容器分离

dockerfile

dockerfile是一个包含生成image所需命令的文本文件,使用docker build可以自动生成image。

Dockerfile文件是以FROM开头的,也就是说必须引用一个base image如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 使用python的运行环境作为parent image
FROM python:2.7-slim

# 设置工作路径
WORKDIR /app

# 将当前目录的内容复制到相应路径下
COPY . /app

# 下载requirements.txt中要求的包
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 暴露容器的80端口
EXPOSE 80

# 定义环境变量
ENV NAME World

#当docker开始运行时执行文件
CMD ["python", "app.py"]

docker build后面可以接本地地址PATH也可以使用远程地址URL,如

1
$ docker build .

实际 执行build指令的是daemon而不是CLI(CLI相当输入命令的shell),也就是说输入docker build后,cli会将命令连同上下文环境一起发送给daemon, docker build后面接的PATH或者URL就是上下文环境。
注意:不要将\目录作为上下文环境,因为docker会将上下文环境下的所有文件和文件夹递归发送给daemon。 推荐的做法是新建一个空目录作为上下文环境

类似于github的.gitignore文件,docker也可以使用.dockerignore来忽略指定文件和目录

docker build后面的PATH并不代表的dockerfile所在的路径,但默认情况下docker会在PATH中寻找Dockerfile,如果要指定dockerfile ,可以使用参数-f,如

1
docker build -f /path/dockerfile   .

使用参数-t可以可以指定生成image的仓库名称和标签,如

1
2
3
docker  build  -t yezh01/getstart .
#  在仓库名称后面可以添加不同标签,同时build命令后面可以接多个 -t 参数
docker build -t yezh01/getstart:v1.1 -t yezh01/getstart:latest .

image

image是根据Dockerfile生成的由多个只读层组成的。对于下面的dockerfile,每条命令生成一个只读层

1
2
3
4
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py

其一共生成四个层次:

  • FROM 将ubuntu:18.04作为基础image
  • copy将运行docker命令的目录下的文件复制到容器中的app文件中
  • run 使用make命令生成应用程序
  • cmd 在容器中运行指定命令

container

container是根据image生成的,与image相比,container在image层次的基础上增加了一层读写层。所有对container的操作,例如增加、删除、修改文件等操作都会写入读写层。

container不会复制image,而是直接在image的上面叠加一层读写层,同时,同一image生成的不同container会共享image,由于container修改的所有内容都保存在读写层中而无法修改image的内容,因此不同container之间不会因为其他container产生干扰。

docker使用入门(四):集群swarm

swarm是一组位于同一集群且运行docker的机器,用户可以通过swarm manager向swarm输入命令,swarm中的机器可以是虚拟机也可以是物理机。
swarm中分为swarm manager和workers。用户通过swarm manager管理swarm,worker只提供处理能力,对worker的管理操作都是通过swarm manager进行的

创建swarm 

创建虚拟机 

linux下的docker-machine需要手动安装,安装方案见官方教程

此外还需要在linux上安装virtualbox,运行以下命令进行安装

1
sudo apt-get install virtualbox

运行以下命令创建两台虚拟机

1
2
docker-machine create --driver virtualbox myvm1
docker-machine create --driver virtualbox myvm2

第一次运行时,需要从github上下载文件,如果下载较慢,可以提前将boot2docker.iso下载到~/.docker/machine/cache/

使用docker-machine ls可以列出虚拟机的信息

1
2
3
4
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
myvm1 - virtualbox Running tcp://192.168.99.102:2376 v19.03.1
myvm2 - virtualbox Running tcp://192.168.99.100:2376 v19.03.1

配置swarm

使用docker-machine ssh可以向虚拟机传达指令,现在使用以下指令将vm1设置为swarm manager,令其能够执行swarm管理命令并且验证worker加入swarm。

1
docker-machine ssh myvm1 "docker swarm init --advertise-addr <myvm1 ip>"

其中,myvm1 ip为myvm1的ip地址

执行结果如下

1
2
3
4
5
6
7
8
$ docker-machine ssh myvm1 "docker swarm init --advertise-addr 192.168.99.102"
Swarm initialized: current node (th2pw563bvvfzvqkd98tsne52) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join --token SWMTKN-1-33l8tomuwv9cyt9ig54cztdpl4h9kyfc5vimlv58mb6zdn4fiw-dn3ic01jklavcvvsb8ltr86bu 192.168.99.102:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

注意:docker swarm initdocker swarm join命令通常使用端口2377执行,docker-machine ls使用端口2376执行,要确保这些端口不被占用

使用以下命令将vm2添加到swarm中

1
2
3
docker-machine ssh myvm2 "docker swarm join \
--token <token> \
<ip>:2377"

执行结果如下

1
2
$ docker-machine ssh myvm2 " docker swarm join --token SWMTKN-1-33l8tomuwv9cyt9ig54cztdpl4h9kyfc5vimlv58mb6zdn4fiw-dn3ic01jklavcvvsb8ltr86bu 192.168.99.102:2377"
This node joined a swarm as a worker.

myvm2中执行的命令可以直接复制添加myvm1时返回的命令提示信息

在manager中运行docker node ls可以查看节点信息

1
2
3
4
$ docker-machine ssh myvm1 "docker node ls"
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
th2pw563bvvfzvqkd98tsne52 * myvm1 Ready Active Leader 19.03.1
figr3d9s2dhwff1fjxuzo1jvw myvm2 Ready Active 19.03.1

将应用程序部署到swarm上

关联manager和shell

运行以下指令将当前shell与vm1关联到一起

1
2
3
4
5
6
7
$ docker-machine  env myvm1
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.102:2376"
export DOCKER_CERT_PATH="/home/yzh/.docker/machine/machines/myvm1"
export DOCKER_MACHINE_NAME="myvm1"
# Run this command to configure your shell:
# eval $(docker-machine env myvm1)

执行以下命令使配置生效

1
eval $(docker-machine env myvm1)

执行成功后,在当前shell中输入的docker命令都会在myvm1中执行,执行docker-machine ls可以看到myvm1的状态已经改变

1
2
3
4
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
myvm1 * virtualbox Running tcp://192.168.99.102:2376 v19.03.1
myvm2 - virtualbox Running tcp://192.168.99.100:2376 v19.03.1

以上配置仅在当前shell有效,在其他shell中需要重新配置,也可在当前shell重新执行以上指令覆盖原有的设置。

部署应用程序

将应用程序部署到swarm的命令与之前一样,不过现在该命令执行在与manager关联的shell上,通过manager在swarm上执行

1
docker stack deploy -c docker-compose.yml hello

执行docker stack ps getstartedlab可以发现实例分布在不同的虚拟机上,实现负载均衡

1
2
3
4
5
6
7
$ docker stack ps hello
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
cm0siz2mmwbt hello_web.1 yezh01/getstart:v1.0 myvm2 Running Preparing 19 seconds ago
8a6nlkz0b0di hello_web.2 yezh01/getstart:v1.0 myvm2 Running Preparing 19 seconds ago
dx3g10vswgs8 hello_web.3 yezh01/getstart:v1.0 myvm1 Running Preparing 19 seconds ago
lr2mi3gpjgcz hello_web.4 yezh01/getstart:v1.0 myvm2 Running Preparing 20 seconds ago
sax7rtkm3ro4 hello_web.5 yezh01/getstart:v1.0 myvm1 Running Preparing 20 seconds ago

测试应用程序

可以通过任意一个虚拟机的ip地址访问应用程序,而且访问效果相同,都可以访问到swarm中的所有运行实例,在swarm中的示意图如下所示

相关命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker-machine create --driver virtualbox myvm1 # Create a VM (Mac, Win7, Linux)
docker-machine create -d hyperv --hyperv-virtual-switch "myswitch" myvm1 # Win10
docker-machine env myvm1 # View basic information about your node
docker-machine ssh myvm1 "docker node ls" # List the nodes in your swarm
docker-machine ssh myvm1 "docker node inspect <node ID>" # Inspect a node
docker-machine ssh myvm1 "docker swarm join-token -q worker" # View join token
docker-machine ssh myvm1 # Open an SSH session with the VM; type "exit" to end
docker node ls # View nodes in swarm (while logged on to manager)
docker-machine ssh myvm2 "docker swarm leave" # Make the worker leave the swarm
docker-machine ssh myvm1 "docker swarm leave -f" # Make master leave, kill swarm
docker-machine ls # list VMs, asterisk shows which VM this shell is talking to
docker-machine start myvm1 # Start a VM that is currently not running
docker-machine env myvm1 # show environment variables and command for myvm1
eval $(docker-machine env myvm1) # Mac command to connect shell to myvm1
& "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env myvm1 | Invoke-Expression # Windows command to connect shell to myvm1
docker stack deploy -c <file> <app> # Deploy an app; command shell must be set to talk to manager (myvm1), uses local Compose file
docker-machine scp docker-compose.yml myvm1:~ # Copy file to node's home dir (only required if you use ssh to connect to manager and deploy the app)
docker-machine ssh myvm1 "docker stack deploy -c <file> <app>" # Deploy an app using ssh (you must have first copied the Compose file to myvm1)
eval $(docker-machine env -u) # Disconnect shell from VMs, use native docker
docker-machine stop $(docker-machine ls -q) # Stop all running VMs
docker-machine rm $(docker-machine ls -q) # Delete all VMs and their disk images

相关链接

https://docs.docker.com/get-started/part4/

docker(三):服务services

docker中services位于container上面,services可以控制container的运行方式(包括container运行时所分配的资源以及container实例数量)

创建yml文件

yml文件定义了容器运行时的行为。我们先创建一个docker-compose.yml文件,其包含以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: username/repo:tag
deploy:
replicas: 5
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "4000:80"
networks:
- webnet
networks:
webnet:

其中,各个字段含义如下

  • image是容器仓库的名字
  • replicas表示生成的实例个数,上述表示5个实例
  • cpus表示占用cpu的时间,上述表示每个实例占用单核cpu10%的运行时间
  • memory表示占用内存大小
  • condition: on-failure表示当一个实例故障时,重启容器
  • ports将web的80端口映射到主机的4000端口
  • network使用默认设置配置webnet

运行服务

执行以下指令运行服务

1
2
docker swarm init
docker stack deploy -c docker-compose.yml hello

其中hello是自行设置的应用程序名

使用以下命令可以获取服务id

1
2
3
4
5
6
7
8
 $ docker service  ls
ID NAME MODE REPLICAS IMAGE PORTS
kv6p22irsbds hello_web replicated 5/5 friendlyhello:latest *:4000->80/tcp

#or
$ docker stack services hello
ID NAME MODE REPLICAS IMAGE PORTS
kv6p22irsbds hello_web replicated 5/5 friendlyhello:latest *:4000->80/tcp

使用以下命令可以获得各个实例的信息

1
2
3
4
5
6
7
$ docker stack ps hello
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
g77x3nxbyeim hello_web.1 friendlyhello:latest pc Running Running 25 minutes ago
a329cldr272o hello_web.2 friendlyhello:latest pc Running Running 25 minutes ago
zr8putqyx4nd hello_web.3 friendlyhello:latest pc Running Running 25 minutes ago
x7cun5z3kuu3 hello_web.4 friendlyhello:latest pc Running Running 25 minutes ago
kc5lb0ni8y5k hello_web.5 friendlyhello:latest pc Running Running 25 minutes ago

拓展应用程序

docker支持即时更新部署,可以在运行时更新应用程序而无需停止服务,更新语句与运行语句一致

1
docker stack deploy -c docker-compose.yml hello

关闭应用程序

1
2
docker stack rm hello
docker swarm leave --force

相关命令

1
2
3
4
5
6
7
8
docker stack ls                                            # List stacks or apps
docker stack deploy -c <composefile> <appname> # Run the specified Compose file
docker service ls # List running services associated with an app
docker service ps <service> # List tasks associated with an app
docker inspect <task or container> # Inspect task or container
docker container ls -q # List container IDs
docker stack rm <appname> # Tear down an application
docker swarm leave --force # Take down a single node swarm from the manager

docker (二):容器container

docker层次结构可以分为三层,从下往上是:容器(container)、服务(services)、堆栈(stack),其中services定义了容器的行为,stack 定义了services的交互
接下来是尝试如何使用docker在容器中启动一个应用程序

创建容器

  1. 创建一个空的文件夹,其中包含Dockerfile、app.py、requirements.txt三个文件,文件内容分别如下

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 使用python的运行环境作为parent image
FROM python:2.7-slim

# 设置工作路径
WORKDIR /app

# 将当前目录的内容复制到相应路径下
COPY . /app

# 下载requirements.txt中要求的包
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 暴露容器的80端口
EXPOSE 80

# 定义环境变量
ENV NAME World

#当docker开始运行时执行文件
CMD ["python", "app.py"]

requirements.txt

1
2
Flask
Redis

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"

html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)

构建应用程序

使用以下命令build应用程序

1
2
docker build --tag=friendlyhello  .
# or docker build -t=friendlyhello .

其中TAG默认为latest,可以使用--tag=friendlyhello:v0.0.1.来指定tag.表示上下文环境。

使用docker image ls可以查看构建的image

1
2
3
4
5
 $  docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
friendlyhello latest 7ecc82fd960a 53 minutes ago 148MB
python 2.7-slim 5f759d36aead 7 hours ago 137MB
hello-world latest fce289e99eb9 7 months ago 1.84kB

运行应用程序

使用以下语句运行程序

1
docker run -p 4000:80 friendlyhello

其中-p 4000:80 表示将image的80端口映射到主机的4000端口。
打开http://localhost:4000可以查看程序运行结果
加入参数-d可以让程序后台运行

1
2
3
4
5
6
7
8
9
10
11
12
$ docker run -d -p 4000:80 friendlyhello
c21b81020e77e9f15df5fafbfdaf2791599c6233b4169615ea9226f243ff68b8

$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c21b81020e77 friendlyhello "python app.py" About a minute ago Up About a minute 0.0.0.0:4000->80/tcp elegant_raman

# 关闭程序
$ docker container stop c21b

$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

相关命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
docker build -t friendlyhello .  # Create image using this directory's Dockerfile
docker run -p 4000:80 friendlyhello # Run "friendlyhello" mapping port 4000 to 80
docker run -d -p 4000:80 friendlyhello # Same thing, but in detached mode
docker container ls # List all running containers
docker container ls -a # List all containers, even those not running
docker container stop <hash> # Gracefully stop the specified container
docker container kill <hash> # Force shutdown of the specified container
docker container rm <hash> # Remove specified container from this machine
docker container rm $(docker container ls -a -q) # Remove all containers
docker image ls -a # List all images on this machine
docker image rm <image id> # Remove specified image from this machine
docker image rm $(docker image ls -a -q) # Remove all images from this machine
docker login # Log in this CLI session using your Docker credentials
docker tag <image> username/repository:tag # Tag <image> for upload to registry
docker push username/repository:tag # Upload tagged image to registry
docker run username/repository:tag # Run image from a registry

docker(一): 安装

开始

docker是一个可以帮助开发者使用容器(containers)开发、部署和运行应用程序的平台。llinux下使用容器部署系统称为containerization。
container 是映像(image)的运行实例,image包括应用的源文件、运行库、配置文件等运行应用程序时所需要的一切资源。
container与其他进程一样共享主机的内核。而虚拟机则是一个完整的操作系统,相当于拥有自己独立的系统内核,container比虚拟机更加轻量。

对比container与虚拟机的结构可以发现,其在应用程序和系统之间增加docker层,而container与其一般进程行为相似。虚拟机是在硬件与操作系统之间增加了hypervisor层,hypervisor层往上是主机的操作系统和虚拟机的操作系统,主机操作系统无法之间调用虚拟机的应用程序。

安装docker

下载网页
如果之前安装过旧版本的docker,需要先卸载docker

1
sudo apt-get remove docker docker-engine docker.io containerd runc

执行以下命令以允许通过https安装

1
2
3
4
5
6
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common

添加docker官方密钥(国内连接官方网站较慢,可以跳过这步使用下面的国内镜向网站下载)

1
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

验证密钥获取是否成功

1
2
3
4
5
6
$ sudo apt-key fingerprint 0EBFCD88

pub rsa4096 2017-02-22 [SCEA]
9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
uid [ unknown] Docker Release (CE deb) <docker@docker.com>
sub rsa4096 2017-02-22 [S]

根据电脑系统选择合适仓库,具体信息可以查看官网

1
2
3
4
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"

国外的网站连接速度慢,可以改用国内的镜像网站,下面是中科大的源

1
2
3
curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu \
$(lsb_release -cs) stable"

安装docker

1
2
 sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

验证是否安装成功

1
sudo docker run hello-world

docker配置

使用一般用户允许docker

安装玩docker后,想要运行docker必须要有管理员权限,通过为docker创建分组,可以让一般权限使用docker

1
2
sudo groupadd docker        # 添加docker分组
sudo usermod -aG docker $USER # 将当前用户添加到分组

通过newgrp docker命令更新分组
验证一般权限允许docker

1
docker run hello-world

相关命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
## List Docker CLI commands
docker
docker container --help

## Display Docker version and info
docker --version
docker version
docker info

## Execute Docker image
docker run hello-world

## List Docker images
docker image ls

## List Docker containers (running, all, all in quiet mode)
docker container ls
docker container ls --all
docker container ls -aq

相关连接

官网开始文档