Docker原理及使用

  

虚拟化系统:
  1. Type-I: 此种虚拟化是Hypervisor直接运行在硬件之上,来创建虚拟机.
  2. Type-II: 这种虚拟化类似与VMware Workstations。

  

  IPC: 在相同的名称空间中的进程才能通过IPC实现进程间通信。
  PID: 在每个名称空间中必须有一个进程ID为1的进程,它是所有进程的父进程,即init进程,所以要实现用户空间隔离,就需要让每个用户空间中进程,以为自己是运行在init进程下的.
  user: 要让每个用户空间中的进程以为自己是运行在独立的主机中,那就必须实现每个用户空间中都一个UID为0的用户, 但该用户在真实系统中,却是一个普通用户。

容器虚拟化:
 资源限制:
  通常有两种,一种是弹性百分比限制能使用全部资源的百分比.另一种是决对资源限制.

 实现资源限制的是Cgroups(Control Groups):
  1. blkio: 块设备IO
  2. cpu: CPU
  3. cpuacct: CPU资源使用报告
  4. cpuset: 多处理器平台上的CPU集合
  5. devices: 设备访问
  6. freezer: 挂起 或 恢复任务
  7. memory: 内存用量及报告
  8. perf_event: 对cgroup中的任务进行统一性能测试
  9. net_cls: cgroup中的任务创建的数据报文的类别标识符.


Docker、LXC、CNCF:
  Docker是一家商业公司开发的容器技术,早期是容器内核是基于LXC(LinuX Container),来实现容器的创建和管理的。
  LXC: 早期有一种技术叫 jail(监狱,或叫沙盒),这种技术可让一个应用程序运行在一个沙箱中,该应用程序无论做出任何对系统的破坏都不会影响到真实的系统,仅仅只是沙箱中的虚拟环境。后来这种技术在Linux也引入了,叫vserver,即现在我们比较熟悉的chroot功能。但是在Linux系统中早期内核支持名称空间,是需要通过自己编写代码,调用系统调用来创建出UTS,Network,IPC,等不同的名称空间,结合chroot实现在一个操作系统上创建出一个用户空间,但这对普通用户来说,是很困难的,而LXC则提供了一组工具,将这些功能整合成工具,用户若需要创建容器,只需要使用lxc-create等命令行工具,就可以很轻松的创建容器,但是LXC在创建容器时,它需要借助于template(模板),当允许lxc-create来创建一个容器时,你可以在CentOS的系统上,创建一个Ubuntu的容器,或FreeBSD的容器等,都可以只有你提供相应的template和这些系统的软件包安装源即可; 但LXC这种方式带来的问题是使用上依然非常困难,应用模板的创建并不容易,而且又产生了很多冗余数据;如:我想创建两个Ubuntu的容器,我就需要安装两份相同的数据; 而且若我需要将一个容器
迁移到另一台主机上,怎么做?LXC没有提供相应的工具。所以它并不适合大规模使用。Docker:它早期创建容器的内核是LXC,但它使用了比LXC更高效,更精巧的方式来创建容器,它不在使用模板来安装容器,而是将容器制作成镜像,当需要使用容器时,直接从镜像仓库中下载之前制作好的镜像到本地,直接启动容器即可。并且它的镜像采用一种叫做叠加镜像的方式解决数据冗余的问题,实际上它采用一种叫三层叠加镜像的方式来实现数据的高效利用 和 容器私有数据的保存。借助于共享存储来将容器的私有数据保存在宿主机的外部,当宿主机宕机时,只要在新的宿主机上下载镜像,启动容器,挂载数据即可恢复服务。具体如下图:
  注:
   Docker为了管理容器方便,它定义一个容器中只能运行一个进程, 但实际上容器跟虚拟机类似,它是可运行多个进程的,只是为了管理方便,限制容器中只能运行一个进程。

    

 

容器这种技术带来好处:
  对于开发人员来说,是天大的好处,因为Docker的出现真正解决代码一次编写到处运行,无论底层是什么系统,只要能运行docker,将镜像做好后,直接编排好,然后在宿主机上启动容器即可。对于运维人员来说,带来的问题是,系统构架更加复杂,原本的调试进程的方式,在容器时代变的异常困难,因为容器中很可能没有各种调试工具等。

容器部署上的问题:
  如NMP环境的构建上, 通常我们需要先部署MySQL容器,接着是PHP容器,最后是Nginx容器,这样的顺序部署才能让环境都正常工作,否则可能出现Nginx代理PHP服务器,结果找不到PHP服务器,因为容器启动后,它的IP地址会改变,那通过DHCP根据MAC不是可以固定分配IP吗?但其实是不可以的,因为容器是没有固定MAC的,它的网卡是虚拟的,是通过Linux上的"虚拟网线”创建的两个虚拟网卡,一端接入容器,一端接入Docker0(默认桥)桥上,这样就实现了容器能接入网络中,因此每次启动容器时,是无法知道这一次创建的虚拟网卡的MAC地址是多少的,因此必须使用其它方法来获取启动完成后的容器的IP地址,这也就是为啥容器需要编排,对于LNMP环境,Nginx要依赖PHP,因为它要代理PHP,而PHP又依赖MySQL,因为PHP应用程序要从MySQL中获取数据,因此必须先启动MySQL容器,这样才能获取MySQL容器当前使用的IP,接着启动PHP容器,将MySQL容器的IP传入到PHP容器中,这样PHP容器就可以知道MySQL的IP,而Nginx也是一样,这样编排后,LNMP环境中每个容器就都能获取它所依赖的容器的IP,就可以正常通信了。

为了解决部署容器的顺序问题,容器技术,必须结合编排工具一起使用,才能实现大规模应用。
目前比较知名的编排工具:
  Docker: machine+swarm+compose,这三个工具配合使用来实现编排。
  第三方提供的编排工具: mesos+marathon
  Google: kubernetes-->简称k8s

libcontainer:
  又称为RunC,它是Docker官方提供的新Docker构建容器的核心程序,由于LXC在功能上,没有与时俱进,因此Docker官方,最终决定自己开发一个新的容器构建核心,它就是runC。

Docker,Moby,CNCF的历史故事:
  Docker我们知道它是一家商业公司,早期Docker刚出现时,以下轰动世界,为了让Docker技术,能够有更大的发展,Docker将自己的产品分成商业版和社区开源版,但是Docker的商业版在商业领域上始终没能活动 大佬们的信赖,没能得到太多融资,但是社区开源版确始终火热的不行,Docker为了能获得更多商业利益,它最终决定将社区版的Docker更名为Moby,希望能吸引更多关注的目光到它的商业版上,但事与愿违。 而此时Google发现自己秘密使用了十几年的容器技术竟然被Docker给开源了,而且还发展的如此红火,尽人皆知,但Google不希望自己在容器技术上的话语权落到Docker手中,它决定扶持一家小容器公司,但这家公司的产品在Google大力扶植下,依然是名落孙山,不是Docker的对手,最后,Google发现Docker在编排工具的开发上,好无建树,而自己拥有十几年的容器使用经验(Google内部的容器叫Borg(博格)),因此就组织了工程师开发了Kubernetes,又鉴于Docker一家公司为了商业目的,随意更改Docker社区版,改变Docker的性质,因此Google将Kubernetes捐给了CNCF,而CNCF是容器标准化基金会组织,它是Google联合多家
知名公司,如:IBM,微软,Facebook等联合成立的组织。向世人表态为不会左右Kubernetes的发展进程,加上Kubernetes从其诞生就是站在Bong的肩膀上成立的,所以它一经发布就成为世人聚焦的目标,短短3年,就已经从0.1发展到1.1版本,以每年4个版本迭代,2018年为止,其市场占有率达到80%以上.


OCI(Open Container Initiative):
  它由Linux基金会主导于2015年6月创立,皆在围绕容器格式和运行时制定了一个开放的工业标准.
  它制定了两个规范标准:
    1. 运行时标准(The Runtime Specifications (runtime-spec))
    2. 镜像格式标准(The Image Specification (image-spec))

OCF(Open Container Format):
  OCF开放容器格式就是OCI的倡导下产生的标准。

Docker的基本架构:

  

  注:
 Docker的镜像:
   Registry:在Docker中称为镜像仓库,Docker的镜像仓库有点类似与yum源仓库,但它的组织方式是这样的:
    官方提供的Docker镜像仓库地址: hup.docker.org,当然国内也有很多docker镜像站.
    Docker镜像仓库的组织格式为:
      应用程序名:版本号
      其中: 应用程序名为仓库名.
      版本号: 为仓库中镜像名.也是它的tag标签号.
    以Nginx为例:
      Nginx:为仓库名.
      nginx:1.15 这就是一个镜像名.同时一个镜像可能有多个名字,
        比如: 1.15是Nginx的最新发行版,则还会给它提供一个名字叫: nginx:lasted 即最新版,
        这个名字会随着Nginx发布的最新版而改变。
      nginx:1.14 另外我们知道,Nginx的发行版中,次号码为偶数表示,此版本为稳定版,
        为奇数则为开发版,所以这种版本通常也会有一个名字: nginx:stated 即稳定版的nginx镜像.

  docker的下载站有很多:
    若使用清华大学的repo仓库,下载其repo文件后,还需要修改其repo镜像的站点为清华大学的镜像站.
      yum install docker-ce

    安装完docker后,要下载其docker镜像,可使用国内的镜像加速器来下载:使用方式:
    mkdir /etc/docker
    vim /etc/docker/daemon.json
      {  "registry-mirrors": ["https://registry.docker-cn.com"]  }

  查看docker版本信息:
    docker version
    docker info

  docker的常用操作:
    docker search :搜索docker镜像仓库中指定的镜像名.
    docker pull : 下载指定镜像到本地.
    docker images : 列出本地已有的镜像

  下载docker镜像:
    docker image pull nginx:1.14-alpine
    注: alpine:这是一个小发行版的nginx,大小很小. 它仅集成了nginx运行所必须的环境,其它什么都没有,若你需要一些调试工具,它就不太适合了。
      另外,一般官方发行的镜像,一般都很少会带调试工具。
      另注: 我们搜索docker镜像时,会看到类似 tom/nginx 这类的镜像,它们是表示 tom这个人,在docker官方注册了账号,上传了自己制作的nginx镜像.

    docker pull busybox

    docker imgle ls --help    #查看ls子命令的帮助.
    docker imgle --help

  创建容器:
    docker container create    #仅创建容器,但不运行.
    docker container run     #创建容器,并运行。
    docker container start          #启动一个或多个已经创建好的容器。
    docker container stop |kill    #都可以停在一个或多个容器.
    docker container top      #查看那些容器最消耗资源.

  docker container run [options] Image [Command] [参数...]
   options:
    -t :启动镜像时,给其添加一个终端访问接口,以便能够启动后,连接登录.
    -i :启动为交互模式.
    --rm :停止后,直接删除该容器.
    -d : 运行到后台.
    --name <ImangeName> :为启动的容器名一个名字.
    --network <NetName> :将容器加入一个指定的网络中.

  docker network ls    #可列出当前本机有多少网络可用.
  ifconfig    可查看到,当docker安装完成后,它会自动创建一个docker0的桥,这个桥是NAT桥.
       它是直接桥接在物理网卡上的,可直接和物理网络通信。

  示例:
   docker pull busybox    #下载一个busybox的镜像.
   docker container run -it --name b1 busybox:latest    #这样就启动了一个叫b1的容器.
    / #ps      #这样就启动了busybox,当前显示的是它的sh终端.
    / #mkdir /data/httpd
    / #vim /data/httpd/index.html
    / #httpd -f -h /data/httpd      #在busybox中启动一个httpd进程,并指明其web根目录.
    / #exit              #执行exit就退出了sh终端,也就终止了容器.

  终端2:
    docker ps
    docker inspect b1    #可查看b1这个容器的详细信息.
    curl http://1.1.1.1/    #这样就可以访问容器中的httpd服务了。

    docker ps -a    #可查看停止的容器.

  示例2:
    终端1:
      docker start -a -i b1 #重新启动停止的容器b1

    终端2:
      docker ps
      docker kill b1    #可强制关闭b1容器. 正常情况下应该使用stop,可保障数据不丢失.
      docker rm b1    #删除停止的容器b1

  示例3:
    docker run --name nginx1 -d nginx:1.14-alpine #若本机能访问互联网,这里可直接运行,docker会自动下载镜像.
    注:
        在容器中运行应用程序,它必须工作在前台,否则容器已启动就会关闭,因为在容器中只有一个进程,该进程是支撑容器的骨架
    docker ps

    docker run --name redis1 -d redis:4-alpine    #启动一个redis容器,它将自动下载该镜像.并启动.

  示例4:
    docker exec -it redis1 /bin/sh    #在指定容器redis1上运行一个命令,/bin/sh,并使用交互式登录.
    /data # ps             #这样就可以登录到redis1这个容器上了.

    docker logs nginx1    #查看容器nginx1的日志信息.

docker 所有状态转化示意

  

  Docker的镜像:

  

    Docker镜像包含了启动容器所需的文件系统及其内容,因此它用于创建并启动docker容器.
  docker镜像采用分层构建的机制,最底层为bootfs,其上为rootfs.
  bootfs: 用于系统引导文件系统,包括bootloader和kernel,容器启动完成后,bootfs会被卸载,以节省内存空间.
  rootfs: 它在运行中的docker容器中变现为根文件系统.
  如: docker exec -it nginx1 /bin/sh   #登录后, ls / 可以看到全部的根文件系统目录.
  传统OS:
    系统启动时,内核挂载rootfs时,为确保不会误删除文件,通常会先以只读模式挂载,完整性自检完成后,再重新以读写模式挂载rootfs.
    docker: 它在启动内核,挂载rootfs时,始终是以只读模式挂载, 而后在通过联合挂载技术额外挂载一个可写层.实现读写操作。

Docker Image Layer

  

  

  此镜像显示了Docker的镜像层级,从上到下分别为:
  可写层
  add Apache 和 add emacs(类似于vim的编辑器): 这两层为自己通过yum安装在镜像中的工具.
  Debian: 这是为安装的工具提供的基本的最小安装的根文件系统已共Apache能运行起来.这里使用的是Debian.还可以使用CentOS等.
  bootfs: 这一层则是启动Debian这个rootfs所需的bootloader和kernel,通常在将Debian启动起来后,它将自动被卸载.

Docker实现叠加挂载镜像的文件系统:
  overlayfs: 从kernel3.18以后被整合到Linux内核中了. 它叫 叠加文件系统, 再早期Docker实现叠加挂载是使用Aufs(Advanced multi-layered unification filesystem:高级多层统一文件系统), 而此文件系统是2006年开发,但因代码质量问题,一直未被整合到kernel中; 据说它的代码有3万行,而ext4这种文件系统总代码量4千多行。所以早期要使用docker有两种方式 一种手动给内核打aufs的补丁,另一种就是使用Ubuntu系统,因为它比较早的将Aufs整合到它的kernel中了。不过现在目前 CentOS系统已经能支持overlayfs的第2版了,所以可以直接使用docker即可.
  docker info
     #输出中Storage Driver: overlay2 ,overlay2是一种虚拟抽象层的文件系统,它是建构在Backing Filesystem: xfs ,即后端文件系统xfs之上的文件系统。

  docker pull quay.io/coreos/flannel:v0.10.0-amd64
     #下载quay.io 这个网站的coreos名称空间下的flannel仓库中的tag为v0.10.0-amd64 的镜像。默认docker使用https去下载. quay.io是docker镜像很有名的第三方提供者.

 示例1:
  #基于busybox创建一个测试镜像:
    docker run --name bx1 -it busybox
    / #mkdir -p /data/html
    / #vi /data/html/index.html
      <h1>Hello busybox HTTP Server</h1>

  终端2:
    docker commit -p bx1    #-p是让bx1容器先暂停,避免有人访问镜像,导致镜像创建不完成.
    docker image ls       #可看到刚创建的镜像.
    docker tag <IMAGE ID> tom/bbox:v0.1        #这样就给指定的镜像打上了tom/bbox的标签,并且tag为v0.1s
    docker tag tom/bbox:v0.1 tom/bbox:latest    #在tom/bbox:v0.1上再给其创建一个标签名.
    docker image rm tom/bbox:latest    #删除一个标签名, 它只是删除标签,就像删除硬链接,文件是不会被删除的。

 示例2:
  docker inspect nginx:v1.14    #查看其CMD,可以看到的其被docker启动后,容器中将启动的第一个进程为nginx
  docker inspect tom/bbox:latest

 示例3:
  #基于busybox制作一个启动后运行busybox中的httpd的镜像.

  终端1:
    docker run --name bx1 -it tom/bbox:latest    #注:这是基于示例1中的内容创建的.

  终端2:
    docker commit -a "tom <tom@test.com>" -c ‘CMD ["/bin/httpd","-f","-h","/data/html"]’ -p bx1 tom/bbox:v0.2
    注:
      CMD: 这是仅修改启动容器后,执行什么命令, 注意CMD必须大写!! 其次参数的顺序不能乱.
      -p : 制作时让容器处于暂停状态.
            制作完成后,直接给它加一个标签名为tom/bbox:v0.2
    docker image ls  #查看制作好的镜像
    

    docker  run  --name bx2  -d  tom/bbox:v02
  终端3:
    docker inspect bx2  #查看bx2的定义,主要看CMD定义
    curl http://1.1.1.3/    #这样就可以直接访问busybox启动的微型httpd了。


  示例4:
  #上传镜像到docker hup中的仓库中.
    1. 要创建docker hup的登录账户,并登录到后台,创建一个仓库.
       如: 上面创建的镜像名为: tom/bbox:v0.1
        那创建的仓库就叫 tom/bbox
    2. 上传镜像前,要先登录
      docker login -u tom #然后要求输入密码,Login Succeeded表示登录成功.
    3. 开始上传镜像:
      docker push tom/bbox #不加tag则表示将本地所有镜像都推送上去。

  示例5:
    #导出已有镜像,并在其它主机上导入镜像:
    docker image ls
    docker save -o myimages.gz tom/bbox:v0.1 tom/bbox:v0.2    #这样即可将两个镜像打包成myimages.gz。

    #登录到另一台docker上就可以执行:
    docker load -i myimages.gz #就可以导入镜像了
    docker image ls

  示例6:
    docker exec -it --name bx1
    / #wget -O - -q http://1.1.1.2/   # 假如Nginx容器启动后的地址为1.1.1.2,则通过wget测试访问.
           -O :加载完成页面后,不保存,直接输出当标准输出."-"

Docker的四种网络模型:
  第一种是Closed Container(封闭式容器): 它主要是用来处理一些Docker计算任务的,完成后就自动退出了,因此它无需网络协议栈.
  第二种是Bridge Container(桥接式容器): 它是通过创建一根虚拟网线,一头接入容器中,也就是Container Virtual interface,另一头接在Docker bridge Virtual Interface上.
  第三种是Joined Container(联合容器): 它是让A和B两个容器都共享一组Net,UTS,IPC名称空间,这样的好处是A和B可以直接通过lo接口通信,并且还可以桥接到Docker桥上,共享网络接口。
  第四种是Open Container(开放容器): 它是第三种的扩展,它直接与物理机共享了Net,UTS,IPC名称空间,因此物理机上的网络接口它都可以看到,并且可以直接管理。

    

    示例1:
    #inspect 查看docker组件的详细信息.
    docker network ls
    docker network inspect bridge #查看bridge网络的详细信息。

  示例2:
    #创建网络模型3
    终端1:
      docker run --name bx1 -it --rm busybox
      / # wget -O - -q http://127.0.0.0.1/

    终端2:
      docker run --name bx2 -it --network container:bx1 --rm busybox
      / #echo "hello world." > /tmp/index.html
      / #httpd -f -h /tmp

  示例2:
    vim /etc/docker/daemon.json     #以下仅供参考,新版本有些参数以不支持!!
    {
      "bip": "1.1.1.1/24", #设置docker0的IP
      “fixed-cidr”:"10.20.0.0/16",
      "fixed-cidr-v6":"2001:db8::/64",
      "mtu":"1500",
      "default-gateway":"10.20.1.1",
      "default-gateway-v6":"2001:db8:dbcd::89",
      "dns":["10.20.1.2","8.8.8.8"],
      "hosts":["tcp://0.0.0.0:2375","unix:///var/run/docker.sock"] #启动docker监听在2375端口上.
    }

    systemctl restart docker      #新版本建议直接修改启动脚本,已参数形式传入

  在主机2:
    docker -H 1.1.1.1:2375 image ls #这样就可以查看1.1.1.1这台主机上docker的是否有镜像了。

  示例3:
    #创建docker网络
    docker network create -d bridge --subnet "1.1.1.0/16" --gateway "1.1.1.1" mybr0
    docker run -it --name bx1 --net mybr0 busybox:v0.1

  示例4:
    #启动容器时传递参数:
    docker run --name t1 -it --network bridge -h t1.test.com --rm busybox:latest

    docker run --name t1 -it --network bridge -h t1.test.com --dns 114.114.114.114 --rm busybox:latest
    docker run --name t1 -it --network bridge -h t1.test.com --dns 114.114.114.114 --add-host www.test.com:1.1.1.1 --rm busybox:latest

  示例5:
    docker run --name myweb --rm -p 80 tom/bbox:v0.2 #创建一个地址映射,允许外部访问此容器.
      #注: -p 80 ,实际是创建了一条 iptables DNAT规则.
    docker port myweb #查看docker创建的端口映射

    docker run --name myweb --rm -p 1.1.1.1::80 tom/bbox:v0.2
    docker run --name myweb --rm -p 80:80 tom/bbox:v0.2
    docker run --name myweb --rm -p 1.1.1.1:80:80 tom/bbox:v0.2

Docker的数据卷(Data Volume):
  为避免关闭再重启容器,其数据不受影响,但删除Docker容器,则其更改将会全部丢失.
  存在的问题:
    1. 存储于联合文件系统中的数据,宿主机不易访问.
    2. 容器间数据共享不方便.
    3. 删除容器其数据会丢失.
  解决方案:"卷(Volume)":
    卷是容器上的一个或多个目录(即:多个卷),此类目录可绕过联合文件系统,与宿主机上的某目录绑定(即关联)

Docker有两种类型的卷:
  1. Bind mount volume(绑定挂载卷):这是有用户自定义指定docker中的某目录挂载到宿主机的某个指定目录.
  2. Docker-managed volume(docker管理的卷): 这是由docker自己维护,用户需要在/etc/docker/daemon.json中指定启用docker自动启动容器时,关联宿主机上由docker默认自动创建一个目录,并与容器中指定目录关联的方式。

  示例:
    #由docker管理的卷:
    docker run --rm --name b2 -it -v /data busybox:latest

    终端2:
    docker inspect b2   #可查看其“Mounts”段,其中有说明容器中/data 目录与宿主机上的那个目录建立的关联.
              接着可测试在宿主机上目录中创建文件,在容器中编辑,是否可行。

  示例2:
    docker run --name b2 --rm -it -v /data/volumes/b2:/data busybox:latest

    终端2:
    echo “Hello Container busybox.” >> /data/volumes/b2/index.html
      #接着在容器中查看.
      # 再尝试删除容器,再关联相同目录,查看数据是否依然可访问。

    docker inspect -f {{.Mounts}} b2    #简单过滤.
     #注:
      JSON中使用"[]": 表示这是一个列表, {}: 表示这是一个关联数组(也叫Hash映射),它可嵌套.
      在关联数组中的格式:
      {
        "Key":"Value",    #每个键 值对要使用 逗号分隔.
        "Key":"Value",
        "key0": {
          "key1":"Value1",    #key1的上层是“key0”, 而key0上层是 根(通常用"."表示.)
          ..
        },
      ..
      }

  docker inspect -f  {{.Mounts}}  b2
     #最外层{} 为引用格式, 内层{.Mounts} 为引用数组中.Mounts
      . :表示根,.Mounts:表示根下的Mounts.
  docker inspect -f {{.NetworkSettings.IPAddress}} b2 #这是简单的Go模板,go模板还支持循环,判断等.

  示例3:
   #创建一个基础容器,让其它容器共享它的UTS,IPC,Network名称空间 已经它的卷映射.
  终端1:
    docker run --name infraCon -it -v /data/volumes/b2:/data/web/html busybox
    / #ifconfig

  终端2:
    docker run --name nginx --network container:infraCon --volumes-from infraCon -it busybox
    / #ifconfig

  终端3:
    docker inspect infraCon
    docker inspect nginx    #查看“Mounts” 和 Networks是否与上面的一样。


Dockerfile:
 格式:
  # : #号开头为注释.
  FROM : 第一个指令必须是FROM,它用来指明,依据那个基础镜像做镜像。
      一般指令建议使用大写以区分参数, 但指令不区分大小写.
     格式:
      FROM <仓库名>[:<tag>] 或 FROM <仓库名>@<Hash摘要值>

  使用dockerfile制作镜像的注意事项:
   1. 必须把镜像文件都放到一个指定目录中, docker build在打包时,只打包该目录中的文件.超出此目录的文件将不会被打包到镜像中.
   2. .dockeringore : 这个隐藏文件在指定目录的中,用于设定打包时排除的文件列表,可使用通配符。
   3. docker build 在进行镜像打包时,它实际上类似于通过启动一个容器,然后,在容器中运行一些命令,创建一些文件,这些数据都保存在可写层中, 再将可写层打包到镜像中一样,不过,docker build是隐藏式启动基础镜像来自动完成这些操作,所以,需要知道你要使用某些命令,这些命令必须在基础镜像中存在才能使用。
   4. 变量替换:
    变量格式: $VarName 或 ${VarName}
    变量替换: ${VarName:-String}    #若VarName为空或未定义,则使用String做为值. 否则使用VarName的值.
          ${VarName:+String}   #若VarName有值,则使用String替换该值.
      5. dockerfile中的指令:
    1. copy <Src> <Dest> 或 COPY ["<Src>",..."<Dest>"]    #注:Src若为目录,复制时,是仅复制目录中文件,而不复制目录本身的.

  示例:
    mkdir /dockerfile/
    cd /dockerfile
    vim Dockerfile    #注: 文件名首字母必须大写.
      #注: Dockerfile中的每条指令都是一层,层数越多挂载效率越差!!
      #Description: test image
      FROM busybox:latest
      MAINTAINER "Tom <tom@test.com>"
      # LABEL maintainer="Tom <tom@test.com>"   #功能和上面一样.
      COPY index.html /data/web/html/        #需要将index.html放到/dockerfile中
      COPY yum.repos.d /etc/yum.repos.d/      #注: 先将yum.repos.d 复制党/dcokerfile中, 然后在写此命令.
            
      ADD http://nginx.org/download/nginx-1.15.2.tar.gz /usr/local/src/    #它会自动在打包时下载该URL指定的文件.
      ADD nginx-1.15.2.tar.gz /usr/local/src      #打包时,会自动将tar.gz展开放入/usr/local/src/的文件.

      WORKDIR /usr/local/src    #将它做为工作目录.
      ADD test.tar.gz ./       #这里引用的相对路径就是WORRDIR所设定的目录.

      WORKDIR /etc/yum.repos.d/
      ADD yum.repos.d.tar.gz ./      #这里的相对路径就是/etc/yum.repos.d/了。

      #VOLUME ["/dir_1", "/dir_2" ..]    #设置容器挂载主机目录
      VOLUME /data/mysql/        #指定Docker自动管理的卷。

      #EXPOSE <Port>/<Protocol>
      EXPOSE 80/tcp     
        #指定要对外暴露TCP的80端口,但这里指定暴露,实际并不会直接暴露,而是需要在启动容器时,使用 “-P” 才会真正暴露此端口给外部用户.
           # docker port 容器名 #在不加"-P"时,可使用它来验证是否暴露了80端口.
      #ENV <KeyName> <Value>        #此格式定义的变量,会将空格后面的所有字符串都当做keyname的值.
      #ENV <Key>=<Value> <Key2>=<V2> ...    #此格式可创建多个环节变量. 若值中有空格,需要用"\"转义, 或用引号引起来.
      ENV DOC_ROOT /data/web/html
      COPY *.html $DOC_ROOT
      COPY *.jsp ${JSP_ROOT:-/data/web/jsp/}  #注:这些环境变量,是可以在启动容器时,指定"-e JSP_ROOT=/data/jsp" 来修改的.


      #RUN <Command1> && <Command2> ...      #RUN执行命令是在docker build时执行命令.
      WORKDIR /usr/local/src
      ADD http://test.org/test.tar.gz ./
      RUN cd ./ && \
      tar xf test.tar.gz     #仅做多行写命令的测试

     #RUN ["<Executable>","<Param1>",....]
     #RUN的第二种语法格式.它使用JSON数组,Executable为要运行的命令,后面为命令的参数,可有多个.此格式启动的命令不会以/bin/sh -c 的方式启动,因此它不能使用sh的命令语法,如:变量替换,通配符等. 若想使用shell特性的话,可用以下方式:
    RUN ["/bin/sh","-c","<Executable>","Param1",...]
    注:
      RUN 和 CMD 及 ENTRYPOINT 若使用中括号来表示数组时,其中不能使用单引号.必须使用双引号.


    docker build --tag tinyhttpd:v0.1 ./

  测试:
    docker run --name tinyweb1 --rm tinyhttpd:v0.1 cat /data/web/html/index.html
    docker run --name tinyweb1 --rm tinyhttpd:v0.1 ls /etc/yum.repos.d/

  #启动镜像时,传递参数:
    Dockerfile:
    FROM java:8
    ADD microsoft.jar /root
    ENV PARAMS=""
    ENTRYPOINT ["sh","-c","java $PARAMS -jar /root/microsoft.jar"]

    测试:
      docker run -d -e PARAMS="-Dserver.port=8080" -p 2000:8080 镜像名称


Docker私有仓库的构建:
  1. 安装docker的仓库构建程序包.
    yum install docker-distribution
  2. vim /etc/docker-distribution/registry/config.yml
    services: registry #表示运行的服务为registry
    layerinfo: inmemory #表示缓存在内存中.
    addr: :5000 #没有指定地址,表示使用0.0.0.0:5000

  3.启动服务:
    systemctl start docker-distribution

  4. 给自己做的镜像打上自己的仓库地址的标签.
    docker tag myweb:v0.3.11 node2.test.com:5000/myweb:v0.3.11

  5. 将自己打好标签的镜像push上去.
    docker push node2.test.com:5000/myweb:v0.3.11    #这里会报错,说我们使用了不安全的http.

  6. 若我们是内网使用, 不想使用HTTPS,可修改配置文件为:
    vim /etc/docker/daemon.conf
      {
      ...
      "insecure-registries": ["node2.test.com:5000"]
      }

    systemctl restart docker

  7. 再次push

  8. 可到其它节点上测试下载.
    docker pull node2.test.com:5000/myweb:v0.3.11


Docker的资源限制:
  Linux Capabilities: Linux Kernel相关的知识

  Memory:
  OOM: 对于比较重要的容器,在启动时就要调整其OOM_adj, 其值调整越小, OOM_score的评分就越低,当内核发现内存不足,发生OOM时,就优先将OOM_score得分高的优先kill掉.
  默认: docker daemon会自动将自己的OOM_adj调整很低,避免内核发生OOM时,被内核kill掉.

可调整容器使用内存的限制:
  -m | --memory=    #限制容器能使用的物理内存大小.单位:k, m, g; 若设置一个容器可用内存为4m,若容器使用超过,将被kill掉.
  --memory-swap=    #要使用它,必须先使用 -m 选项,否则它不生效.
    若设置: -m 7G --memory-swap=10G, 则表示Swap+Ram=10G, Swap=10-7=3G,即可使用3G的Swap内存.
    若设置: -m 7G --memory-swap=7G, 则表示不使用Swap内存.即 若设置这两个参数值相同,表示禁用SWAP.
    若设置: -m 7G --memory-swap=0, 则表示SWAP未设置, 则若DockerHost启用了SWAP,则表示容器可用的SWAP大小=2*RAM的大小.
    若设置: -m 7G --memory-swap=-1, 则表示DockerHost上启用了Swap,则容器可使用的全部的SWAP大小.
   注:
    free : 在容器中使用free看到的swap空间使用情况并非上面这些参数计算出来的实际情况, 也就是说free查看容器使用的内存空间是非常不准确的。
    --memory-swappiness: 设置容器使用SWAP内存的倾向性. 值的范围: 0~100, 0:能不用就不用, 100:只要能需要就使用.
    --memory-reservation: 设置要预留多少内存空间。
    --oom-kill-disable 设置当发生OOM时,是否禁止此容器被kill掉,它要可-m一起使用.

CPU:
  默认所有容器使用CPU资源都没有限制。
  --cpu-shares  #CPU是可压缩性资源,若当前有A,B,C三个容器, 假如分配给他们CPU占用的比例为:1024:512:2048.
        则表示,将CPU资源分2+1+4,即分成7份, 三个容器都需要CPU,则A使用2份CPU时间, B使用1份
        CPU时间, C使用4份CPU时间, 若B和C当前都空闲,则A可使用全部CPU资源; 若此时A和B都需要
        用CPU资源,则按照A使用2份CPU资源, B使用1份CPU时间, 若又新加了D,则再重新分割CPU时间片.
  --cpus=   #限制单个容器最多可使用几核CPU, 即: 假如有4个核心, 还是A,B,C, 若此时B和C都不使用CPU
        在不限制的情况下,A可使用4个核心,即最大可使用400%的计算能力.若设置了单个容器的
        最多可使用CPU核心数,则基本当前所有容器都不用CPU,你也只能使用限制个数的核心数.
  --cpus=1.5 #表示最多可用150%,注意容器最多可使用150%的CPU计算能力,指的是
        只有能限制它最多使用这么多计算能力即可,至于容器在那个CPU核心上
        运行无所谓,随机分配,分到那个核心上就在那个核心上运行。但总量是不会超过150%的。
  --cpuset-cpus=     #这是限制容器能够运行在那个CPU核心上的, 假如:有4核心, 则用0~3来表示4个核心的编号.
  --cpuset-cpus=0,1   # 则表示容器只能运行在核心0和1上,其它上面不行。
  --cpu-period=        #限制最多可用CPU的时间.
  --cpu-quota=


以上这些选项在docker run 时可以设置。

docker压测工具:
  docker pull lorel/docker-stress-ng
  docker run --name stress -it --rm -m 256m lorel/docker-stress-ng:latest stress --help #可查看帮助.
  docker run --name stress -it --rm -m 256m lorel/docker-stress-ng:latest stress --vm 2 #限制其最多使用256M内存.
  docker run --name stress -it --rm --cpus 2 lorel/docker-stress-ng:latest stress --cpu 8 #限制最多使用2个CPU核心的计算资源.

  终端1:
    docker run --name stress -it --rm --cpu-shares 1024 lorel/docker-stress-ng:latest stress --cpu 8

  终端3:
    docker run --name stress -it --rm --cpu-shares 512 lorel/docker-stress-ng:latest stress --cpu 8


  终端2:
    docker top stress #查看容器进程数
    docker stats #查看docker资源的消耗情况。

posted @ 2019-08-01 18:47  张朝锋  阅读(22044)  评论(0编辑  收藏  举报