Docker基础篇(包括遇到的各种坑)

1. 镜像是什么呢?

个人认为镜像是文件的一种封装,文件可以是操作系统、应用软件等等。

镜像从哪里来呢? 有一个镜像仓库的概念,就是存储和管理镜像的,可以是远程的公共镜像仓库,也可以是本地的镜像仓库, 我们可以从仓库中下载想要的镜像,也可以将我们本地生成的镜像上传到仓库中。

用docker官方的镜像仓库,非常慢,因为仓库位于国外,所以要用国内的。

1.1 设置从国内的daocloud 镜像仓库下载

 在 https://hub.daocloud.io/网站中,即daocloud.io 免费注册一个账号,登录进去

右上角有一个加速图标,,点击,进入新页面:

看你安装docker的是什么系统,我的是Ubuntu(linux)系统,所以选择上图中的指令,然后在你的系统中执行指令。

然后重启docker,执行:sudo systemctl restart docker.service

之后,我们去下载镜像文件就快多了。

2. 我们安装好了Docker之后,了解几个概念。

   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

3. docker常用指令 和 demon例子

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 

运行成功!

 

4. Base镜像

Base镜像的定义:不依赖于别的镜像,可以在base镜像上进行扩展。

什么意思呢?就是base镜像是运行的基础,包含有操作系统,其它的应用程序镜像可以被添加在base镜像上。

@@@@@@@@@@@@@有待写。

5. 构建镜像

两种方法: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。

6 公共镜像仓库(发布和获取镜像)

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文件,在里面进行配置。

7 本地镜像仓库的搭建

由于本人用的是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

                        又有了。

8 进入容器

有两种方法:attach  和 exec

8.1 attach方法

docker attach 容器ID

此方法是直接进入容器的启动命令终端,只能查看此容器启动后的指令(CMD指令)执行结果。

如果是要看容器启动命令的结果,可以用:docker logs -f  容器ID

8.2 exec方法

docker exec -it  容器ID bash

此方法以交互模式进入容器终端,结果就是打开了一个bash终端,我们可以输入指令执行的。

所以通常我们使用的更多的是exec。

9 停止  启动  重启 挂起  唤醒  删除容器

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             //可是删除多个镜像,也可以只删除一个,以空格隔开

10 容器的生命周期

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 策略去执行重启。

11、限制容器的资源

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设置才会有效。

12 实现容器的底层技术

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 Docker网络

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 Docker存储

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 数据共享

15.1 容器和host共享数据

使用volume挂载,可以实现。

15.2 容器间的共享数据

将Docker的被挂载目录或文件 挂载到 多个容器中,这样,容器之间就可以共享数据了。

15.3 volume container容器方式

待写

15.4 date-packed volume container方式

volume container的数据还是在Docker 平台下, date-packed volume container 原理就是将数据打包,生成一个数据镜像,这样移植性就很好,但是这个数据就是固定的,没法增、删、改,适用于固定的配置数据等等。

16 data volume 备份、恢复、迁移、销毁

1、备份

volume就是Docker平台下的文件或者目录,在安装registry本地仓库的时候,所有镜像都保存在当时指定的目录下,假设这个文件夹为XXX,那么我们要做到就是备份这个XXX文件夹。

                            

至此, Docker的基础知识就算学习了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值