zoukankan      html  css  js  c++  java
  • 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资源的消耗情况。

  • 相关阅读:
    spark Kryo serialization failed: Buffer overflow 错误
    spark sql加载avro
    Java读写hdfs上的avro文件
    spark使用scala读取Avro数据(转)
    spark遇到的错误1-内存不足
    php数据表
    php商品详情页的修改
    php ecshop前台修改
    php的文件
    php文件的学习
  • 原文地址:https://www.cnblogs.com/wn1m/p/11284560.html
Copyright © 2011-2022 走看看