个人认为镜像是文件的一种封装,文件可以是操作系统、应用软件等等。
镜像从哪里来呢? 有一个镜像仓库的概念,就是存储和管理镜像的,可以是远程的公共镜像仓库,也可以是本地的镜像仓库, 我们可以从仓库中下载想要的镜像,也可以将我们本地生成的镜像上传到仓库中。
用docker官方的镜像仓库,非常慢,因为仓库位于国外,所以要用国内的。
1.1 设置从国内的daocloud 镜像仓库下载
在 https://hub.daocloud.io/网站中,即daocloud.io 免费注册一个账号,登录进去
看你安装docker的是什么系统,我的是Ubuntu(linux)系统,所以选择上图中的指令,然后在你的系统中执行指令。
然后重启docker,执行:sudo systemctl restart docker.service
之后,我们去下载镜像文件就快多了。
docker客户端:Docker架构是C/S结构 , 客户端好理解,直接用docker命令就行了。
docker服务器:docker daemon是服务器的组件,运行在docker服务器上,负责创建、运行、监控容器, 构建、存储镜像。默认设置下,docker服务器只接收来自本机的请求,如果要接收来自远程 客户端的请求,就需要做配置:
执行:gedit /etc/systemd/system/multi-user.target.wants/docker.service
在ExecStart 后面添加 -H tcp://0.0.0.0
表明可以接收任何ip的客户端连接。
配置完后,重启docker:systemctl daemon-reload 和 systemctl restart docker.service
镜像:上面已经解释过了。
容器:一个Docker平台上,可以运行多个容器,每个容器都是一个镜像的实例,好比是进程和程序,程序是死的代码(镜像),进程是程序跑起来的状态(容器)。每个容器都是独立的一整套结构,而Docker平台是包含了这些容器的一套完整结构。
查看Docker服务状态:systemctl status docker.service
docker pull xxxx: 从镜像仓库里下载镜像xxxx,镜像仓库默认是docker 官方的,比较慢,可以设置为自己的私有镜像仓库或者国内免费的镜像仓库,上文已经设置过了,执行设置指令,那么docker就会在配置文件中做相应的配置,之后的下载镜像就会去我们设置的那个镜像仓库下载。
docker run xxxx:运行xxxx镜像,首先去本地镜像仓库看有没有xxxx,如果没有,就先下载,然后运行。
docker images: 查看本地镜像仓库中的镜像,下图中没有镜像,这是显示出了表头,Repository是镜像名,tag是表示标签,image id就是镜像id,created是创建时间,size是镜像大小。 (其实镜像名:Repository : tag,当我们构建镜像时,docker build -t xxxx:yy,此时Repository为xxxx,tag为yy,如果没设置tag,docker默认是设置为latest),tag常用于描述版本信息。
demon例子 :hello world
执行:docker pull hello-world
如下图,hello-world镜像下载成功。
运行镜像: docker run hello-world
运行成功!
Base镜像的定义:不依赖于别的镜像,可以在base镜像上进行扩展。
什么意思呢?就是base镜像是运行的基础,包含有操作系统,其它的应用程序镜像可以被添加在base镜像上。
@@@@@@@@@@@@@有待写。
两种方法:docker commit 指令 和Dockerfile文件 方法。
5.1 docker commit 指令(不推荐此方法)
三个步骤:运行容器、修改容器、将容器保存为新的镜像。
以交互式的形式运行xxxx容器,并进入容器,进入容器之后,才能去安装应用软件:docker run -it xxxx
安装新应用软件 yyy:apt-get install -y yyy
安装完之后,形成一个新的容器(不是新镜像哦),查看我们新的容器:docker ps //会显示出我们新容器的名字,docker随机分配的名字,假如是gggg。
构建新的镜像,设置镜像名为kkkk:docker commit gggg kkkk
然后用docker images查看我们的新镜像。
5.2 Dockerfile文件构建镜像
Dockerfile文件就是个脚本,里面有语法,语义的规定,下面学习一个Dockerfile文件中常用的(大小写敏感):
注释:#
指定base镜像: FROM base镜像名
设置镜像的作者:MAIINTAINER 作者名
复制文件到镜像 ,src是原文件或者原目录, dest是目的路径:COPY src dest
添加文件到镜像,和COPY一样,不一样的地方是,如果src是归档文件(tar,zip,tgz,xz等),文件会被自动解压到dest中:ADD src dest
设置环境变量,$变量名 获取变量值,:ENV 变量名 变量值
暴露出端口,如果容器中的进程要监听某个接口的话,就要先将该接口暴露出来:EXPOSE
将文件或者目录声明为VOLUME:volume
设置目录为当前工作目录:WORKDIR
运行指定命令:RUN
容器启动时运行指定的指令,Dockerfile文件可以有多个CMD,但是只有最后一个CMD才是有效的:CMD
设置容器启动时运行的指令:ENTRYPOINT
1. 首先创建一个Dockerfile文件,写入指令(脚本),假如文件是在yyyy路径下。
构建镜像指令:docker build -t xxxx -f yyyy //xxxx是新镜像的名字, yyyy是Dockerfile文件的目录。
docker build -t xxxx . 默认的路径build context下的Dockerfile文件。
5.3 RUN CMD ENTRYPOINT的区别
RUN:是直接运行指令
CMD:是容器启动后默认执行的命令,但是CMD可以被docker run 后面的参数替代,docker run的末尾可以加指令,此指令是在容器启动后执行的,那么此时CMD就会被忽略。
ENTRYPOINT:是容器启动时执行的命令
5.4 TAG版本号管理方案
一个镜像可以有多个tag,多个Repository。因为只要镜像的镜像id不变,名称和版本可以有多个。
docker tag 原Repostitory[: 原tag] 新Repository[: 新tag] //等于是给镜像添加别名
//上面的指令是给原镜像添加新的镜像名和tag ,原tag可选,新tag可选,如果没有设置新tag,那么默认是latest。
Docker官方的免费仓库是公共的,也就意味着别人能访问你的镜像,可以花钱用私人的。quay.io是另一个公共免费的镜像仓库。
6.1 登录docker官方
执行:docker login -u zhanghao
接着输入密码,就ok了。注意zhanghao是指你自己的docker官方账号
6.2 修改镜像名
假如我们的镜像名是xxxx,要上传Docker官方仓库,需要规范化镜像名,规则 zhanghao/xxxx:tag ,前面需要有账号,只有docker官方的那些镜像,才是没有前缀的。 执行:docker tag xxxx zhanghao/xxxx:tag
6.3 发布镜像到远程仓库
docker push zhanghao/xxxx.tag
如果你从来没有修改过docker镜像源的话,那默认是发布的docker hub官方的开源镜像仓库去,如果你修改过docker镜像源配置文件的话,那就发布到你修改的那个仓库去,如果修改过的话,在/etc/docker/下面有个daemon.json文件,在里面进行配置。
由于本人用的是daocloud的镜像仓库,下载镜像是免费的,但是上传镜像却是收费的,而且公共的镜像仓库不安全,所以还是搭建一个本地的镜像仓库吧。
7.1 下载registry仓库
docker pull registry
查看一下,是否下载了: docker images
7.2 新建一个文件夹myDockerRegistry,路径自己定,这个文件夹就相当于仓库,用于存放镜像。
然后执行:docker run -d -p 5000:5000 -v /home/XXXX/myDockerRegistry:/var/lib/registry registry
执行后,本地镜像仓库就跑起来了,用docker ps查看是否已经运行起来了。
7.3 修改配置文件
我之前新建了一个脚本文件/etc/docker/daemon.json,里面定义了daocloud的镜像源:{"registry-mirrors": ["http://f1361db2.m.daocloud.io"]}
现在要修改为本地镜像仓库了:修改daemon.json:
{"insecure-registries":["IP:5000"]} IP是你本机的ip地址。
7.4 重启docker和registry仓库
service docker restart
docker stop registry
7.5 重新启动registry仓库
一定要执行下面的指令,/home/XXXX/myDockerRegistry是仓库镜像要挂载的文件夹,一定要有这个,不能直接就docker run registry。
docker run -d -p 5000:5000 -v /home/XXXX/myDockerRegistry:/var/lib/registry registry
7.8 发布一个镜像到你的本地仓库
docker push xxxx:1.0.1
本地仓库所在的位置是在/home/XXXX/myDockerRegistry/docker/registry/v2/repositories 下面,这个下面就有你发布的xxxx:1.0.1镜像了。
7.9 拉取本地仓库中的镜像
首先删除docker中已经有的xxxx:1.0.1 镜像:docker rmi xxxx:1.0.1
无法删除的原因是:已经有容器使用这个xxxx:1.0.1 镜像(我截图中的想要删除的是hello-world:latest),此时解决方法就是删除该容器,可能还不止一个,有多少删除多少,最后,再删除镜像,查看是否删除了:docker images
删除镜像后,在执行:docker pull xxxx:1.0.1
在查看:docker images
又有了。
有两种方法:attach 和 exec
8.1 attach方法
docker attach 容器ID
此方法是直接进入容器的启动命令终端,只能查看此容器启动后的指令(CMD指令)执行结果。
如果是要看容器启动命令的结果,可以用:docker logs -f 容器ID
8.2 exec方法
docker exec -it 容器ID bash
此方法以交互模式进入容器终端,结果就是打开了一个bash终端,我们可以输入指令执行的。
所以通常我们使用的更多的是exec。
9.1 停止
docker stop 容器ID
向该进程发送一个SIGTERM信号。
docker kill 容器ID
向该进程发送一个SIGKILL信号
9.2 启动
docker start 容器ID
此指令会保留容器第一次启动时的参数
9.3 重启
docker restart 容器ID
重启容器,等价于docker stop 和 docker start
如果我们的容器是服务器类型的容器,那么我们肯定希望:如果因为运行出错而导致容器停止,总是会自动尽快重启。所以可以添加参数
docker run --restart=always 容器ID
如果要设置这种故障停机,自动重启的最大次数(比如4次),可设置:docker run --restart=always:4 容器名
9.4 挂起
docker pause 容器ID
挂起之后,容器不运行,不占用CPU
9.5 唤醒
docker unpause 容器ID
将挂起的容器唤醒,继续运行
9.6 删除
使用一段时间docker后,可能会有很多容器,有些虽然已经退出了,但是仍然占用着一些资源,所以可以适当清理删除。
docker rm 容器1 容器2 //可以删除多个,也可以只删除一个,以空格隔开
docker rmi 镜像1 镜像2 //可是删除多个镜像,也可以只删除一个,以空格隔开
1、被创建状态 created
容器可以被先创建,再运行。
可以看到创建了hello-world容器,此时容器还没有运行,只有用docker ps -a 查看所有的容器,看到hello-world容器的状态为created。
2、运行状态 up
启动容器:docker start hello-world
由于我们创建的hello-world容器为空,所以一运行,就退出了,状态为退出状态exited。
docker run 容器 === docker created 容器 + docker start 容器
3、退出问题
如果是用docker stop 和 docker kill 退出的容器,那么容器设置的 --restart 策略就不起作用,如果是正常退出或者异常退出,那么才会根据 --restart 策略去执行重启。
docker平台上可能运行着多个容器,我们可以限制某个容器的资源使用量,避免某个容器占用太多的资源而导致其它docker的性能和整个平台的性能。
限制的情况下,我们可以用 progrium/stress 去测试资源压力。
11.1 内存限制
内存分为物理内存 memory 和 虚拟内存swap,在docker run一个容器的时候设置内存限制参数,有两个参数可供设置:memory(内存) 和 memory + swap (内存+虚拟内存),如果没有设置的话,两个参数默认为-1,即对内存使用没有限制。
设置hello容器的内存限制为10M,并运行:docker run -m 10M hello
或者:docker run --memory 10M hello //此时只设置了内存,那么swap默认大小为memory大小,memory + swap默认为 memory的2倍。
需要设置 memory + swap的大小限制: docker run -m 10M --memory-swap=20M hello //这样的话,swap就是10M
11.2 CPU限制
默认情况下,多个容器是可以平等地去占用CPU资源,没有限制。
-c 设置容器使用CPU的占比,是一个相对值,比如100,50,默认是1024;
docker run hello -c 100 然后接着 docker run hello2 -c 50 那么当hello 和 hello2 一起使用cpu时,hello将会得到2倍的资源(相对于hello2)。
注意:这种CPU抢占的情况只会发生在CPU资源不够用的时候,如果CPU资源足够容器使用,那即便是设置了这样的占比,也不会起作用,如果CPU不够用的时候,占比的设置才会起作用。
11.3 Block IO 带宽限额
Block IO指的是磁盘的读写,,可以通过控制权重、bps、iops来控制容器读写磁盘的带宽
1、权重设置:--blkio-weight
和cpu占比很类似,默认为500;
2、bps(每秒读写的字节数,具体分为读 和 写): --device-read-bps (每秒读的字节数), --device-write-bps(每秒写的字节数)。
3、iops(每秒读写的次数,具体分为读 和 写):--device-read-iops(每秒读的次数),--device-write-iops(每秒写的次数)。
注意:只用bps 和 iops 时都要指定读写的目标文件,表明容器读写哪个文件内容的带宽。比如:docker run --device-read-bps /home/ggg:30M hello , 意思是容器hello要读home目录下ggg文件的内容,以不能超过30M/s的速度读,并启动hello。
当我们要使用iops的时候,需要额外指定参数oflag=direct ,iops设置才会有效。
cgroup 和 namespace 分别实现对资源的限制 和 资源的隔离。
linux系统通过cgroup去控制进程的cpu、IO、内存的限制,11节中的三种限制的设置,就是通过cgroup来设置的。
12.1 cgroup
sys/fs/cgroup/cpu/docker/ 这个目录是docker容器的资源限制目录,此目录下还有很多文件夹,每一个容器都有这样的一个文件夹,名称为容器的长ID,进入此文件夹,里面有个cpu.shares 文件,就是cpu资源限制文件。
sys/fs/cgroup/memory/docker/ 是内存限制目录 sys/fs/cgroup/blkio/docker/ 是带宽限制目录。
12.2 namespace
对于容器来讲,一个容器包含了能够独立运行的整套资源,但是电脑的硬件都是唯一的,或者有限的,如果让每个容器都觉得自己用的资源是独立唯一的呢,这就是namespace的作用,namespace管理着这些资源。
linux使用了6中namespace,分别对应6中资源:Mount、UTS、IPC、PID、netWork、User。
Mount namespace:让容器感觉自己拥有整个系统;
UTS namespace:
13.1 none 网络
none网络是空网络,挂在none网络下的容器,只有loop,封闭的网络。 --network=? 来指定网络
指定容器使用网络:docker run --network=none hello
13.2 host 网络
就是Docker平台的网络,挂在host网络下的容器可以共享Docker 平台的网络,能使用Docker中的所有网卡, --nerwork=host。
13.3 bridge 网络
docker 在安装时会创建一个名为docker0 的linux bridge。如果容器不指定网络的话,默认是挂在这个bridge网络下的。
展示网桥信息:brctl show
可以看到docker0, 而interfaces 就是挂在docker0网络下的容器虚拟网卡。
docker0就相当于一个虚拟子网的网关,容器都是出于这个子网,当然ip会和docker0的ip出于同一网段。
13.4 user-defined 网络
自定义网络。Docker提供三种自定义网络驱动:bridge、overlay、macvlan。
1、bridge网络
docker network create --driver bridge my_net
查看一下,有了,网络的名字就是br-短ID。
查看bridge网络具体信息,参数只能是网络名字,本例中是my_net:docker network inspect my_net
下图中的网段是Docker自动分配的,也可以在创建网络的时候,自己设置subnet 和 Gateway。
比如:docker network create --driver bridge --subnet 172.22.12.0/16 gateway 172.22.12.1 my_net2
到此自定义的bridge网络就创建成功了,就差将容器挂在此网络下面了。
将hello容器挂在my-net2网络中去:docker run --network=my_net2 hello
此时hello运行起来,被docker自动分配一个my_net2网段中的ip;
但是我们可以自动设设置hello容器的IP地址,--ip:docker run network --ip 172.22.12.8 hello
注意:如果不是用subnet 来自定义网段的话,用--ip参数会报错。
2、bridge网络互通
我们假设定义了两个bridge网络:b1,b2, b1中有两个容器r1,r2; b2中有两个容器r3,r4.
在同一网段中的容器肯定是能够互通的,ping得通。
在不同网段中的容器肯定是ping不通的。
但是不同的bridge网络之间可以互通,设置一个虚拟路由,将这些要互通的bridge网络接到此路由上,即可互通(具体做法,暂时不做说明)。
13.5 容器间的通信
1、IP通信
这个就是需要容器出于同一网段中。
2、Docker 的DNS服务
我们对数字是不敏感的,IP地址记起来、用起来很麻烦,如果能够直接用名字取代替IP地址,就好了。
在启动容器的时候,为容器设置名字, --name:docker run network --network=my_net2 --name=name123 hello
这样,在容器通信的时候,就可以直接使用容器的名字,不用IP地址了。
注意:Docker 的DNS服务只用于自定义的网络中,默认的网络,DNS是不起作用的。
3、joined 容器方式通信
joined容器是一种概念,同一个joined容器可以包含若干个容器,这若干个容器共享网卡和配置信息。
举个例子就明白了。比如运行一个容器hello1,:docker run --network=my_net2 --name=web1 hello1 (my_net2网络下,DNS名字为web1) 在运行一个容器hello2:docker run --network=container:web1 hello2 (此时hello2 和 hello1 是同一个joined容器,共享网卡和IP,docker inspect 查看容器的网络信息,就可以发现mac地址 和 IP地址一样,相当于这两个容器是一台主机上的本地应用,直接可以使用127.0.0.1来通信)。
应用场景:1.适合希望通过loopback来高效通信的,2. 一个容器去监控另一个容器的流量等数据。
13.6 容器于外部世界通信
1、容器通向外部
容器默认是能够访问外部的。
过程:容器要去访问外部,首先容器请求访问的IP地址,容器所在的网桥发现是外网的地址,
2、外部访问容器
(等有机会,再实践)。
14.1 storage driver
上图所示,一个容器包括了镜像层 和 容器层,镜像层是一层一层堆起来的。动态数据永远都只能在最上面的容器层,而镜像层的数据只能是可读的,1. 如果有新数据,那么新数据就存在容器层,2. 如果要修改数据,那么就将镜像层中的数据复制到容器层,然后修改 ,3. 如果要查找数据,docker会从上网下查找,如果有重复的名字,那么上层的数据会覆盖掉下层的数据。
这种层次结构就是靠storage driver来实现的,Docker支持的storage driver有很多种,但是到底用哪种呢? 建议使用linux发行版的默认storage driver。 用 docker info 查看一下自己docker用的哪个。
storage driver的优点:数据都是在容器层临时存在的,容器一旦关闭,容器层就没有了,里面的数据也就不存在了,对于那么不需要数据持久化的应用来说,很适合。 缺点:但是对于需要数据持久化的应用来说,就不适合了,因为下次重启,就找不到以前的数据了。
14.2 data volume
那如何才能持久化数据呢?想想,根据14.1节中描述的容器特性,那肯定不能再容器的内部去持久化数据,只能将数据存储在Docker平台的文件目录下,这样,容器的消失与否,都不会影响到Docker平台下的数据。
1. data volume 是文件或者目录; 2、容器可以读写volume中的数据 ;
但是容器怎么去读写data volume呢? 假设将Docker平台下的volume挂载到容器中的XXX文件或者目录,容器直接对XXX的读写,就是对volume的读写,以此达到目的。
挂载指令, -v : docker run -v /home/xxx:/home/hehe/yyy hello (参数是 -v, 原目录或文件 : 目的目录或文件,将Docker平台下的/home/xxx目录 挂载成 容器hello中的/home/hehe/yyy, 如果yyy目录下事先有数据,那么这些数据会被xxx中的数据覆盖掉)。
默认情况下,容器对挂载的目录或者文件是可读可写,但是可以设置为只可读 ,添加 ro: docker run -v /home/xxx:/home/hehe/yyy:ro hello
data volume缺点:需要挂载,而且需要指定Docker平台的目录作为挂载源,如果需要移植容器的话,那么在新平台上就要出问题,新平台可能没有那个目录,导致出错。
14.3 docker managed volume
docker managed volume与data volume的区别就在于前者不需要指定挂载源,只需要指定挂载目的就行,这样,容器移植到新平台上时,根据配置去Docker平台参加需要的挂载源,即可解决问题。
docker run -v /home/hehe/yyy hello
挂载源在哪呢? docker平台会自动在/var/lib/docker/volumes 生成一个目录作为容器的挂载源。
如果挂载目的文件或者目录已经存在,那么会将文件或者目录里的数据复制到挂载源。
查看挂载信息:docker volume ls 查看挂载源
15.1 容器和host共享数据
使用volume挂载,可以实现。
15.2 容器间的共享数据
将Docker的被挂载目录或文件 挂载到 多个容器中,这样,容器之间就可以共享数据了。
15.3 volume container容器方式
待写
15.4 date-packed volume container方式
volume container的数据还是在Docker 平台下, date-packed volume container 原理就是将数据打包,生成一个数据镜像,这样移植性就很好,但是这个数据就是固定的,没法增、删、改,适用于固定的配置数据等等。
1、备份
volume就是Docker平台下的文件或者目录,在安装registry本地仓库的时候,所有镜像都保存在当时指定的目录下,假设这个文件夹为XXX,那么我们要做到就是备份这个XXX文件夹。
至此, Docker的基础知识就算学习了。