什么是Docker? 在Docker之前,我们肯定要先了解Docker是什么。官网的介绍是Dockeristheworld’sleadingsoftwarecontainerplatform。官方给Docker的定位是一个应用容器平台。至于为什么要做这个Docker,官网上还有这么一句话Dockerisanopenplatformfordevelopersandsysadminstobuild,ship,andrundistributedapplications,whetheronlaptops,datacenterVMs,orthecloud。这句话用一句非常简单的话去概括就是Buildonce,RunanyWhere。这一点跟Java很像。那么它这样做是要解决现实中什么问题,我列举几个情况。 1。合作开发的时候,在本机可以跑,别人的电脑跑不起来 这里我们拿javaWeb应用程序举例,我们一个javaWeb应用程序涉及很多东西,比如jdk、tomcat、spring等等。当这些其中某一项版本不一致的时候,可能就会导致应用程序跑不起来这种情况。Docker则将程序直接打包成镜像,直接运行在容器中即可。 2。服务器自己的程序挂了,结果发现是别人程序出了问题把内存吃完了,自己程序因为内存不够就挂了 这种也是一种比较常见的情况,如果你的程序重要性不是特别高的话,公司基本上不可能让你的程序独享一台服务器的,这时候你的服务器就会跟公司其他人的程序共享一台服务器,所以不可避免地就会受到其他程序的干扰,导致自己的程序出现问题。Docker就很好解决了环境隔离的问题,别人程序不会影响到自己的程序。 3。公司要弄一个活动,可能会有大量的流量进来,公司需要再多部署几十台服务器 在没有Docker的情况下,要在几天内部署几十台服务器,这对运维来说是一件非常折磨人的事,而且每台服务器的环境还不一定一样,就会出现各种问题,最后部署地头皮发麻。用Docker的话,我只需要将程序打包到镜像,你要多少台服务,我就给力跑多少容器,极大地提高了部署效率。Docker与虚拟机的区别 关于Docker与虚拟机的区别,我在网上找到的一张图,非常直观形象地展示出来,话不多说,直接上图。 比较上面两张图,我们发现虚拟机是携带操作系统,本身很小的应用程序却因为携带了操作系统而变得非常大,很笨重。Docker是不携带操作系统的,所以Docker的应用就非常的轻巧。另外在调用宿主机的CPU、磁盘等等这些资源的时候,拿内存举例,虚拟机是利用Hypervisor去虚拟化内存,整个调用过程是虚拟内存虚拟物理内存真正物理内存,但是Docker是利用DockerEngine去调用宿主的的资源,这时候过程是虚拟内存真正物理内存。Docker安装 Docker的安装非常简单,官网上都给出了具体的安装步骤,都是可视化的安装,三步就搞定了。这里我就不细说了,附上Docker的官网安装教程地址。 Mac:https:docs。docker。comdocke。。。 Windows:https:docs。docker。comdocke。。。 linux的话,官网有不同版本的安装的教程,这里放上ubuntu的安装教程,其他小伙们自行查看。 ubuntu:https:docs。docker。comengin。。。Docker三个基本概念 下面这张图非常的经典,很形象地展示了,什么是容器,什么是镜像,什么是仓库,以及三者之间的联系。 接下来我们来解释一下这张图。现在我们要造一间厨房,在造之前我们首先要干的一件事,就是先列举出我们造厨房需要的东西。我们可能需要一个通了水电煤的房子以及一些必需的厨房用具诸如锅碗瓢勺、煤气灶、冰箱、水槽等等这些东西。现在我们知道需要了什么东西之后,我们就去找这些东西。首先我们先去京东购买一些厨房用具,这些用具就好比我们的Docker镜像,我们厨房的用具到了之后得找个地方把它们放在,不可能随处丢吧,不然后面用的时候就找不到它了,那么我们Docker镜像也是这样,需要一个Docker仓库去存储这些镜像。现在我们有了这些厨房用具之后就可以做饭了吗?答案当然是不能,没水没电没火啊!这时候我们得把厨房用具给装到一个通了水电煤的房子才行,那么Docker镜像也是这样,单纯的Docker镜像是不能用的,它得装到Docker容器中通了水电煤才能使用。等我们装好了厨房用具之后我们就可以开始做饭,那么我们的Docker镜像装到Docker容器之后,我们应用就可以跑起来了。我的第一个Docker镜像 上面的文章中,我们了解了一下Docker,接下来我们学着做一个属于自己的Docker镜像。 第一步:从镜像中心下载一个Node镜像a)。去http:hub。daocloud。io上找到Node镜像地址并执行如下命令dockerpulldaocloud。iolibrarynode:标签b)。查看本地库的Docker镜像,是否下载完成dockerimages 结果如下: 第二步:写一个app。js app。js的内容如下,内容很简单,作用也很简单,起一个80端口的服务,页面显示HelloDocker。varhttprequire(http);http。createServer(function(request,response){发送HTTP头部HTTP状态值:200:OK内容类型:textplainresponse。writeHead(200,{ContentType:textplain});发送响应数据HelloWorldresponse。end(HelloDocker);})。listen(3000);终端打印如下信息console。log(Serverrunningathttp:127。0。0。1:3000); 第三步:写一个Dockerfile,用于构建镜像依赖的镜像FROMdaocloud。iolibrarynode:latest镜像创建者的信息MAINTAINERwumingwumingmaihaoche。com执行mkdirhelloDocker创建一个helloDocker文件夹RUNmkdirhelloDocker将app。js添加到helloDocker文件夹中ADDapp。jshelloDocker容器运行时启动的命令,下面命令等价于CMDnodehelloDockerapp。jsCMD〔node,helloDockerapp。js〕 第四步:执行命令,构建对象dockerbuildthellodocker。t用于指定镜像的name:tag,没有指定tag则是latest。。代表Dockerfile所在的路径,这里是当前目录 结果如下: 如果镜像仓库或者标签错了的话,可以使用如下命令进行修改dockertagIMAGEIDname:tag例:dockertag1786dad83d25hellodocker:1。0。0 结果如下: 这里需要注意的是dockertag是新建一个标签用于目标镜像指向源镜像,所以我们记得删除原来的标签dockerrminame:tag例:dockerrmihellodocker:latest 温馨提示:当前目录应该有Dockerfile和app。js才可用上面命令构建成功 我的第一个Docker容器dockerrundp8003:3000IMAGEID 注意事项: p IP:hostport:containerport或pIP::port来指定允许访问容器的主机上的IP、接口等,我这里只允许外部8080端口来访问3000端口 d就是在后台运行容器,并返回容器ID。有兴趣的小伙伴可以体验一下有d和没d的区别 这时我们输入http:localhost:8003就可以访问了我们刚刚启的服务了。 另外我们可以通过dockerps来查看当前所有启动的容器。 接下来我们进入容器中看看这个容器到底有什么东西!dockerexecitCONTAINERID或者NAMESbinbash 进入容器中我们就看到了刚刚我们之前在Dockerfile建的helloDocker文件夹,进去之后发现了我们的app。js。执行exit命令就可以退出容器。Docker数据卷的使用 到这里我们已经掌握了如何去制作一个镜像以及如何启动一个镜像了,接下来我们要说点难一点的东西Docker数据卷的使用。首先说一下什么是卷,为什么要引入卷这个东西。 这边我们拿前端开发来说,我们在前端开发中,我们的js、css以及一些图标等等这些静态资源,这些文件都是可以直接在url输入地址直接访问到的,本身这些东西不会影响项目的运行,说点通俗点就是我们的这些资源怎么整不会让服务器报500错误。按照我们前面的方法去做的话,肯定之间把这些文件全部打到镜像里面,但是这个方法有个很不好的地方的。就是我们每次修改他们都需要重新打一次镜像,就感觉很繁琐。于是我们就想,我们能不能把这次静态资源从镜像中抽离出来,我们让容器指向这个目录,然后我们的服务就可以访问这些资源,每次改变完之后我们就不需要重新打镜像了,这样岂不是很好。正因为如此我们的数据卷就出来了,用来解决这个问题,那么什么是数据卷呢?我们用一句话来阐述就是数据卷是一个可供一个或多个容器使用的特殊目录。接下来我们实际操作一把。 数据卷 第一步:我们建立一个myDocker文件,里面包含两个文件app。js和package。json app。js的内容如下:varexpressrequire(express);varappexpress();设置public目录为静态目录app。use(express。static(public));监听3000端口app。listen(3000); package。json内容如下(主要目的是安装express,自己可以npminit自己生成一下,不必抄我的):{name:docker,version:1。0。0,description:dockertest,main:app。js,scripts:{test:echoError:notestspecifiedexit1},author:,license:ISC,dependencies:{express:4。15。3}} 第二步:在myDocker同级目录下,新建一个public目录,放张图片进去,这里我放了一张美女图片(hhh)。 第三步:在myDocker同级目录下新建一个Dockerfile,内容如下:设置基础镜像FROMdaocloud。iolibrarynode:latest维护者信息MAINTAINERwumingwumingmaihaoche。com在容器中新建一个myDocker文件中RUNmkdirmyDocker将Dockerfile所在目录中myDocker文件夹的内容加到目标容器中的myDocker文件夹中ADDmyDockermyDocker设置工作目录WORKDIRmyDocker执行安装项目依赖包的命令RUNnpminstall容器启动时,执行nodeapp。jsCMDnodeapp。js 第四步:构建一个名为mydocker的镜像 当前目录结构为: myDocker Dockerfile myDocker app。js package。json public 构建命令dockerbuildtmydocker:latest。 第五步:以挂载方式启动容器dockerrundp8003:3000vUserswumingdockerWorkpacemyDockerpublic:myDockerpublicmydocker:latest 这里我将本机的myDocker下的public挂载到容器中的myDockerpublic下,这里我们可以登入容器中看一下,会发现我们镜像并没有将我们的public目录打包进去,但是我们的容器中却有这个public目录。 第六步:访问我们的图片 在游览器地址栏中输入 http:localhost:8003test。jpg便可看见,如下效果: 大家可以往自己的本机的public丢图片进去,再去访问它试试看,以及看看容器中的public没有动态改变,答案是可以的。 数据卷容器上面我们已经实现了数据卷,但是我们发现加入如果我要起多个容器服务,时间短还能记得这个容器挂载目录,要是时间多了岂不是都忘了,而且每次这样去挂载也挺麻烦的,我们能不能把我们已经配好数据卷的容器作为一个数据卷的提供者呢?答案是可以的,这就是我们要说的数据卷容器,接下来我们操作一下。dockerrundp8005:3000volumesfromNAMES或者CONTAINERIDmydocker:latest上面的NAMES或者CONTAINERID为我们配好的数据卷的容器。启动完容器之后,我们可以访问试一下,并且往挂载目录丢点其他图片进去,看看两个容器服务是不是都可以访问到。答案是可以的。使用网络 外部访问容器 1。映射所有接口地址p端口号(8003):端口号(3000) 任何ip地址的指定端口号(8003)都可以访问到服务(前提是那ip地址对应的主机要起了这个服务) 2。映射到指定地址的指定端口pip地址(127。0。0。1)端口号(8003):端口号(3000) 指定ip地址的端口号(8003)都可以访问到服务(前提是那ip地址对应的主机要起了这个服务),比如指定127。0。0。1,只有本机输入127。0。0。1:8003或者localhost:8003可以访问自己的服务,但是其他同一个局域网的手机输入本机的对应局域网的ip地址:8003是访问不到的,小伙们可以拿自己的手机试试。Docker常用命令 1。杀死所有正在运行的容器dockerkill(dockerpsaq) 2。删除所有已经停止的容器dockerrm(dockerpsaq) 3。删除所有镜像dockerrmi(dockerimagesq) 4。关闭容器dockerstopCONTAINERID或者NAMES 5。重新启动关闭的容器dockerstartCONTAINERID或者NAMES 6。移除本地容器dockerrmCONTAINERID或者NAMES 7。查看本地容器dockerps查看正在运行的容器dockerpsa查看所有容器 8。查看本地镜像dockerimages 9。创建镜像dockerbuildtname:tagDockerfile路径 10。修改本地镜像标记dockertagIMAGEIDname:tagdockerrminame:tag 11。删除本地镜像dockerrminame:tag或者IMAGEID 12。进入容器dockerexecitIMAGEID或者NAMESbinbash 13。获取镜像中心的镜像dockerpullname:tag 14。获取容器的端口映射配置dockerportCONTAINERID或者NAMES 持续更新更新。。。。 Dockerfile命令速查表 FROM命令FROMimage:tag 用于设置基础镜像,一般是Dockerfile的第一句。 如果没有指定tag,则默认tag是latest。 MAINTAINERMAINTAINERname 用来指定维护者的姓名和联系方式。 RUNRUNcommand或RUN〔executable,param1,param2〕 每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像。 ADDADDsrcdest 将文件复制到文件:是相对被构建的源目录的相对路径,可以是文件或目录的路径,也可以是一个远程的文件url,是容器中的绝对路径。 COPYCOPYsrcdest 复制本地主机的(为Dockerfile所在目录的相对路径)到容器中的,与ADD指令差不多 ENTRYPOINTENTRYPOINT〔executable,param1,param2〕:推荐使用的exec形式ENTRYPOINTcommandparam1param2:shell形式 配置容器启动后执行的命令,并且不可被dockerrun提供的参数覆盖。 一个Dockerfile中只能有一个ENTRYPOINT,如果有多个,则最后一个生效。 CMDCMD〔executable,param1,param2〕使用exec执行,推荐方式;CMDcommandparam1param2在binsh中执行,提供给需要交互的应用;CMD〔param1,param2〕提供给ENTRYPOINT的默认参数; 指定启动容器时执行的命令,每个Dockerfile只能有一条CMD命令。如果指定了多条命令,只有最后一条会被执行。 如果用户启动容器时候指定了运行的命令,则会覆盖掉CMD指定的命令。 WORKDIRWORKDIRpathtoworkdir 为后续的RUN、CMD、ENTRYPOINT指令配置工作目录。 可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如WORKDIRaWORKDIRbWORKDIRcRUNpwd 则最终路径为abc。 EXPOSEEXPOSEport〔port。。。〕 告诉Docker服务端容器暴露的端口号,供互联系统使用。 例如EXPOSE80803000,开放8080和3000端口。 ENVENVkeyvalue 指定一个环境变量,会被后续RUN指令使用,并在容器运行时保持。 VOLUMEVOLUME〔data〕 创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。 USERUSERUIDUsername 为容器内指定CMDRUNENTRYPOINT命令运行时的用户名或UID。 ONBUILDONBUILD〔INSTRUCTION〕 配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。 例如,利用Dockerfile创建了一个镜像A,其中Dockerfile中有这么几个命令ONBUILDRUNmkdirtestONBUILDADDapp。jstestapp。js 那么镜像B基于镜像A去构建的时候,默认会在最后加上这两行命令FROM镜像ARUNmkdirtestADDapp。jstestapp。js 原文: https:segmentfault。coma1190000009544565 分享基于群晖Docker部署kafka案例思路 其实操作很简单:群晖带有docker服务,用docker来部署mysql即可,所以,本文其实也就是群晖docker的基本操作记录,以下几处是要注意的地方:容器端口登录容器操作docker的link操作环境信息群晖系统:DSM6。2。224922Update4Zookeeper:3。4。13Kafka:2。4。0(scala:2。12)下载镜像在浏览器登录群晖网页,按照下图中数字的顺序操作,即可下载到zookeeper的镜像,第七步时您可以按自己需要来选择合适的版本: kafka镜像下载的操作如下图中数字的顺序,第七步时您可以按自己需要来选择合适的版本: 镜像下载完成即可启动容器;启动zookeeper容器按照下图操作,启动zookeeper容器: 如下图,将容器名设为zookeeper,再点击高级设置: 将容器的2181端口和宿主机的2181端口绑定: 勾选向导完成后运行此容器,再点击应用就会启动容器: 可以在容器页查看启动情况: 接下来启动kafka;启动kafka容器在映像页面操作如下: kafka的容器设置比zookeeper略复杂一些,如下图,设置好名字后点击高级设置: 设置端口映射,这样内网环境都可以访问这个kafka了,用9092端口: 设置链接,如下图,这样的设置相当于docker的link参数,其实就是在kafka容器的etchosts中配置了一条记录,hostname是zookeeper,IP地址是zookeeper容器的IP地址: 接下来需要增加三个环境变量,如下图: KAFKAZOOKEEPERCONNECT:zookeeper的访问地址,这里的zookeeper其实就是hostname,由于设置了link参数,因此直接用hostname就能访问到; KAFKALISTENERS:内网连接方式,注意这里说的内网是指容器的网络; KAFKAADVERTISEDLISTENERS:群晖服务器所在的家庭网络,对kafka而言其实是外部网络(kafka容器的IP和群晖的IP是不同网段),所以家庭网络中其他机器要想连接kafka,对kafka而言就是外网机器要来访问,因此要配置KAFKAADVERTISEDLISTENERS,这里配置的就是群晖自己的IP;设置完毕后,勾选向导完成后运行此容器,再点击应用就会启动容器:: 收到弹窗提示(挺有道理的。。。): 现在kafka部署和启动都成功了,接下来验证这个服务在家庭内网是否可用;验证最直接的验证方式就是命令行操作,先设置群晖允许SSH登录,如下图操作: 如下图,勾选启用SSH功能,端口就用22: 现在用SSH终端即可登录群晖了,我这里是在MacBookPro电脑上用iTerm软件登录的,您可以选用任意SSH终端工具,账号密码就是能登录群晖的账号密码,如下图,登录后,就可以使用日常的linux命令了: 查看当前topic,命令是:sudodockerexeckafkabashccdoptkafkabin。kafkatopics。shbootstrapserver127。0。0。1:9092list 如上图所示,一个Topic都没有;创建topic,命令是:sudodockerexeckafkabashccdoptkafkabin。kafkatopics。shcreatebootstrapserver127。0。0。1:9092replicationfactor1partitions1topictest001再次查看topic,这回有数据了: 有了topic,来试试消息的创建和消费,执行以下命令,进入创建消息的对话模式(注意要带it参数,才能继续输入信息):sudodockerexecitkafkabashccdoptkafkabin。kafkaconsoleproducer。shbrokerlistkafka:9092topictest001再打开一个窗口,ssh登录群晖,执行以下命令,用来消费test001这个主题,一旦这个主题有了消息会立即在此打印出来:sudodockerexecitkafkabashccdoptkafkabin。kafkaconsoleconsumer。shbootstrapserver127。0。0。1:9092topictest001consumerpropertygroup。idoldconsumertestconsumerpropertyconsumer。idoldconsumerclfrombeginning回到发送消息的窗口,输入一些字符串,每次输入回车就会将当前行的字符串作为消息内容发送出去,此时在消费消息的窗口可以立即看到消息内容,如下图: 再来试试远程连接是否成功,我在MacBookPro上安装了kafka客户端工具KafkaTool2。0。7,成功连接到群晖上的kafka,能看到所有消息和主题: 另外zookeeper也可以用了,以下是在MacBookPro上远程连接zookeeper容器的操作: 至此,我的群晖上已经部署了nexus3、mysql、zookeeper、kafka,在家撸代码时数据库、消息这些服务随时想用就用,算是为自己的开发环境创造了更多便利。