记录Docker的安装部署,以及一些常用操作,主要目的在于巩固学习,同时也希望对初学者起到一此借鉴作用。
PS:本次演练的系统环境为CentOS7,Docker版本为20.10.12,builde91ed57。
Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源。
Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类似iPhone的app)更重要的是容器性能开销极低。
Docker支持将软件编译成镜像;在镜像中支持各种软件配置好并发布,其他使用者可以直接使用配置好的镜像。运行中的镜像称之为容器,容器启动速度很快。类似于封装好的Windows系统,通过U盘直接安装即可,不需要进行系统配置软件。
Docker的应用场景:
Docker的优点:
Docker主机(Host):安装了Docker程序的机器(Docker直接安装在操作系统中)
Docker客户端(Client):连接Docker主机进行操作;
Docker容器(Container):镜像启动后的实例,独立运行的一个或一组应用;
Docker镜像(Image):打包好的软件,用于创建Docker容器的模板;
Docker仓库(Respository):用于保存打包好的软件镜像;
关系示意图:
Docker的基本使用方式:
①在机器中安装Docker;
②在Docker仓库中寻找这个软件对应的镜像;
③使用Docker运行镜像,生成一个Docker容器;
④容器的启动或停止相当于对软件的启动和停止。
操作系统:CentOS7
系统版本:3.10.0-1160.el7.x86_64
备注说明:本次演练基于VMwareWorkstation16Pro安装CentOS7虚拟机,安装时选择最小安装模式。
英语水平有限,为了便于查看,在CentOS安装过程中直接把系统语言设置为简体中文,系统时区设置为“亚洲”-“上海”。
CentOS7系统内核版本高于3.10,可以使用以下命令查看:
uname-r以下为执行结果,可以看出显示内核版本为:3.10.0-1160.el7.x86_64
[root@localhost~]#uname-r3.10.0-1160.el7.x86_64[root@localhost~]#2、配置yum安装dockerce即社区免费版,须先安装必要的软件包。安装yum-utils,它提供一个yum-config-manager单元。同时安装的device-mapper-persistent-data和lvm2用于储存设备映射(devicemapper)。
直接使用官方仓库进行Docker安装速度会有点慢,所以我们紧接着配置一个稳定(stable)的仓库,仓库配置会保存到/etc/yum.repos.d/docker-ce.repo文件中。此处我们使用阿里云。
执行以下命令,通过Vim查看镜像仓库配置:
vim/etc/yum.repos.d/docker-ce.repo#也可以使用cat命令查看#cat/etc/yum.repos.d/docker-ce.repo文件内容如下:
查看仓库版本:
通过以下命令查看Docker运行状态
systemctlstatusdocker查看输出结果:
[root@localhost~]#systemctlenabledockerCreatedsymlinkfrom/etc/systemd/system/multi-user.target.wants/docker.serviceto/usr/lib/systemd/system/docker.service.[root@localhost~]#5、验证Docker6、卸载Docker查看已安装的组件:
删除安装包:
yumremove<移除需要卸载的组件>#yumremovedocker-ce删除镜像、容器、配置文件等内容:
rm-rf/var/lib/dockerPS:以上为Docker在CentOS7上进行安装部署的一个简演练。供初学者借鉴,如有不当之处,也请大家指正。
当配置某一个加速器地址之后,若发现拉取不到镜像,请切换到另一个加速器地址。国内各大云服务商均提供了Docker镜像加速服务,建议根据运行Docker的云平台选择对应的镜像加速服务。
打开终端,使用vim打开/etc/docker/daemon.json文件,写入以下内容(如果文件不存在请新建该文件):
sudosystemctldaemon-reload&&sudosystemctlrestartdockerDocker简单示例Docker允许你在容器内运行应用程序,使用dockerrun命令来在容器内运行一个应用程序。此处以Nginx服务部署做一个演练。
PS:由于之前没有拉取过Nginx的镜像,所以会先自动下载Nginx镜像。拉取后自动运行容器。通过dockerps命令可以查看运行中的容器列表。
各个参数解析:
执行dockerps命令,会把当前运行的容器给列出来。结果如上图所示。
当运行容器时,使用的镜像如果在本地中不存在,docker就会自动从docker镜像仓库中下载,默认是从DockerHub公共镜像源下载。
我们可以使用dockerimages来列出本地主机上的镜像。
各列字段说明:
同一仓库源可以有多个TAG,代表这个仓库源的不同个版本,如ubuntu仓库源里,有15.10、14.04等多个不同的版本,我们使用REPOSITORY:TAG来定义不同的镜像。
所以,我们如果要使用版本为15.10的ubuntu系统镜像来运行容器时,命令如下:
dockerrun-itubuntu:15.10/bin/bash如果你不指定一个镜像的版本标签,例如你只使用ubuntu,docker将默认使用ubuntu:latest镜像。
NAME:镜像仓库源的名称
DESCRIPTION:镜像的描述
OFFICIAL:是否docker官方发布
STARS:类似Github里面的star,表示点赞、喜欢的意思。
AUTOMATED:自动构建。
当我们在本地主机上使用一个不存在的镜像时Docker就会自动下载这个镜像。如果我们想预先下载这个镜像,我们可以使用dockerpull命令来下载它。
#下载ubuntu镜像到本机dockerrunubuntu下载完成后,我们就可以使用这个镜像了。
#运行并进入容器,执行相应命令dockerrun-itubuntu/bin/bashPS:我运行到这里竟然失败了!出现以下提示:
WARNING:IPv4forwardingisdisabled.Networkingwillnotwork.
网上搜索了一下,通过以下办法解决“IPv4forwardingisdisabled”的问题。
第一步:在宿主机上执行echo"net.ipv4.ip_forward=1">>/usr/lib/sysctl.d/00-system.conf
#执行命令echo"net.ipv4.ip_forward=1">>/usr/lib/sysctl.d/00-system.conf#查看文件cat/usr/lib/sysctl.d/00-system.conf#以下为文件内容,可以看到net.ipv4.ip_forward=1的设置已追加到文件末尾。#Kernelsysctlconfigurationfile##Forbinaryvalues,0isdisabled,1isenabled.Seesysctl(8)and#sysctl.conf(5)formoredetails.#Disablenetfilteronbridges.net.bridge.bridge-nf-call-ip6tables=0net.bridge.bridge-nf-call-iptables=0net.bridge.bridge-nf-call-arptables=0net.ipv4.ip_forward=1第二步:重启network和docker服务
systemctlrestartnetwork&&systemctlrestartdocker第三步:验证是否成功
dockerrun-itubuntu/bin/bash完美解决。
镜像删除使用**dockerrmi**或dockerimagerm命令:
删除指定镜像,也可以使用镜像ID:
[root@localhost~]#dockerimagerm9b9cb95443b5Untagged:ubuntu:15.10Untagged:ubuntu@sha256:02521a2d079595241c6793b2044f02eecf294034f31d6e235ac4b2b54ffc41f3Deleted:sha256:9b9cb95443b5f846cd3c8cfa3f64e63b6ba68de2618a08875a119c81a8f96698Deleted:sha256:b616585738eaf78ff7d86c7526caf7c91a35bc4028ef63204e5bfee82f7494b5Deleted:sha256:dee1316f97acc7e1a5088b02fbc2b3078e0bfa038dd904b8072e2de5656e7bb8Deleted:sha256:e7d9ae1a69c53c9fefa1aef34348be5a5dbf2fe79e7dd647b3d4f4e927587ebcDeleted:sha256:f121afdbbd5dd49d4a88c402b1a1a4dca39c9ae75ed7f80a29ffd9739fc680a7[root@localhost~]##这里再列表所有镜像,可以看到ubuntu:15.10已不见了。[root@localhost~]#dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEnginxlatest605c77e624dd8daysago141MBubuntulatestba6acccedd292monthsago72.8MB[root@localhost~]#5、创建镜像当我们从docker镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行更改。
更新镜像之前,我们需要使用镜像来创建一个容器。
在完成操作之后,输入exit命令来退出这个容器。
此时ID为dd88dacb1af7的容器,是按我们的需求更改的容器。我们可以通过命令dockercommit来提交容器副本。
[root@localhost~]#dockercommit-m="Thishasbeenupdated."-a="jack"dd88dacb1af7jack/ubuntu:v2sha256:d1c2e19bdcdcb5f3ffb4c5b1c795e938defa9c316588ec49603a817c472f303b[root@localhost~]#各个参数说明:
我们可以使用dockerimages命令来查看我们的新镜像jack/ubuntu:v2:
[root@localhost~]#dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEjack/ubuntuv2d1c2e19bdcdcAboutaminuteago105MBnginxlatest605c77e624dd8daysago141MBubuntulatestba6acccedd292monthsago72.8MB5.2构建镜像使用VS2022,基于.NET6,创建一个控制台程序进行镜像发布。最简单的HelloWorld输出。
项目创建完成,整个程序目前仅一句代码:
Dockerfile文件代码如下:
第一条FROM,指定使用哪个镜像源
RUN指令告诉docker在镜像内执行命令,安装了什么。。。
后面,我们会使用Dockerfile文件,通过dockerbuild命令来构建一个镜像。
OK,到了这一步,我们的程序已准备好了。下面就选择把项目整体打包到Linux,在服务器上进行编辑打包,镜像构建。
dockerbuild-tmy-hello-world.参数说明:
最终完整输出如下:
我们可以使用dockertag命令,为镜像添加一个新的标签。
dockertag1add28a21d49my-hello-world:V1.0#参数说明:#dockertag<镜像ID><镜像源名><新的标签名>使用dockerimages命令可以看到,ID为1add28a21d49的镜像多一个标签V1.0。
[root@localhostHelloWorldDemo]#dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEmy-hello-worldV1.01add28a21d4915minutesago188MBmy-hello-worldlatest1add28a21d4915minutesago188MB
docker客户端非常简单,我们可以直接输入docker命令来查看到Docker客户端的所有命令选项。
[root@localhost~]#docker这个就不放图了,可以自己去试一下。可以通过命令dockercommand--help更深入的了解指定的Docker命令使用方法。
例如我们要查看dockerrun指令的具体使用方法:
PS:如果我们本地没有镜像,我们可以使用dockerpull命令来载入镜像:
dockerpullnginx#查看正在运行的容器列表dockerps#所有容器列表(包含存活和退出容器)dockerps–a2、启动容器dockerrun-d-p9999:80--namemy-nginxnginx参数说明:(见前面简单示例内容)
#启动已存在的容器dockerstart<容器ID/Name>#重启容器dockerrestart容器id1[容器id2][...]#启动所有容器dockerstart$(dockerps-aq)#查看容器的进程PIDdokertop
dockerps-a使用dockerstart启动一个已停止的容器:
在大部分的场景下,我们希望docker的服务是在后台运行的,我们可以过-d指定容器的运行模式。
dockerrun-d-p9999:80--namemy-nginxnginx注:加了-d参数默认不会进入容器,想要进入容器需要使用指令dockerexec。
停止容器的命令如下:
dockerstop<容器ID>停止的容器可以通过dockerrestart重启:
dockerrestart<容器ID>6、进入容器在使用-d参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入:
#进入容器dockerexec-it24054bd26a66/bin/bash#进入后就可以执行相应的命令了……#退出容器exit7、删除容器删除容器使用dockerrm命令:
#正常情况下,我们只能删除已停止的容器,添加-f参数,强制执行,不再考虑容器是否正在运行。[root@localhostHelloWorldDemo]#dockerrm-f70994ac442fa70994ac442fa[root@localhostHelloWorldDemo]#8、查看日志dockerlogs[ID或者名字]可以查看容器内部的标准输出。
[root@localhostHelloWorldDemo]#dockerstatsmy-nginx#================================CONTAINERIDNAMECPU%MEMUSAGE/LIMITMEM%NETI/OBLOCKI/OPIDS24054bd26a66my-nginx0.01%3.074MiB/1.777GiB0.17%656B/0B0B/14.3kB5CONTAINERIDNAMECPU%MEMUSAGE/LIMITMEM%NETI/OBLOCKI/OPIDS24054bd26a66my-nginx0.01%3.074MiB/1.777GiB0.17%656B/0B0B/14.3kB5CONTAINERIDNAMECPU%MEMUSAGE/LIMITMEM%NETI/OBLOCKI/OPIDS24054bd26a66my-nginx0.00%3.074MiB/1.777GiB0.17%656B/0B0B/14.3kB5…………10、查看配置使用dockerinspect来查看Docker的底层信息。它会返回一个JSON文件记录着Docker容器的配置和状态信息。
dockercpsentinel.confredis-master:/usr/local/redis#把文件sentinel.conf拷贝到容器redis-master的/usr/local/redis目录下面。dockercpredis-master:/usr/local/redis/sentinel.conf./#把容器redis-master中的文件/usr/local/redis/sentinel.con拷贝到当前操作目录下在。#注意:dockercp命令只可以宿主机上运行Docker数据存储1、什么是数据卷在Docker中,容器的数据读写默认发生在容器的存储层,当容器被删除时其上的数据将会丢失。如果想实现数据的持久化,就需要将容器和宿主机建立联系(将数据从宿主机挂载到容器内),通俗的说,数据卷就是在容器和宿主机之间实现数据共享。
Docker提供了三种不同的方式将数据从宿主机挂载到容器中:volume、bindmounts、tmpfsmounts
volume:Docker管理宿主机文件系统的一部分(/var/lib/docker/volumes)
bindmounts:可以存储在宿主机系统的任意位置
tmpfsmounts:挂载存储在宿主机系统的内存中,不会写入宿主机的文件系统
创建volume数据卷:
dockervolumecreatefor_nginx查看当前所有数据卷信息:
dockervolumels会显示一大堆名字为很长字符的数据卷为匿名数据卷,是因为之前创建容器的时候没有手动创建数据卷进行了文件挂载,Docker就会自动创建匿名数据卷。启动容器并指定数据卷:
dockerrun-d-p8088:80--namemynginx--mounttype=volume,source=for_nginx,target=/usr/share/nginx/htmlnginx可以查看下容器具体信息:
dockerinspectmynginx通过页面输出的信息,在Mounts节点上,我们可以看到Volume在主机中的文件路径。注意:Volume挂载的文件必须是在Docer在主机上的指定位置的目录。
"Mounts":[{"Type":"volume","Name":"for_nginx","Source":"/var/lib/docker/volumes/for_nginx/_data","Destination":"/usr/share/nginx/html","Driver":"local","Mode":"z","RW":true,"Propagation":""}],我们在宿主机数据卷里新增一个host.html
#在主机上面运行cd/var/lib/docker/volumes/for_nginx/_dataecho"Thisishostfile.">host.html进入容器内部查看是否也增加了host.html文件:
dockerrm-fmynginx#查看文件cd/var/lib/docker/volumes/for_nginx/_data&&ls4、bindmounts(绑定数据卷)bindmounts可以将宿主机任意目录挂载到容器内将宿主机/opt目录挂载到容器内:
dockerrun-d-p8088:80--namemynginx--mounttype=bind,source=/opt,target=/usr/share/nginx/htmlnginx进入容器查看nginx默认html页面:
dockerexec-itmynginx/bin/bash#已进入Docker容器mynginx内部cd/usr/share/nginx/html&&ls#可以查看到,在主机中创建的文件已在容器内部给列出来了。发现并没有nginx默认的index.html和50.html页面。
注:如果你使用Bindmounts挂载宿主机目录到一个容器中的非空目录,那么此容器中的非空目录中的文件会被隐藏,容器访问这个目录时能够访问到的文件均来自于宿主机目录。
那么如何挂载才不会覆盖容器中的文件呢?
知识点:可以挂载具体的文件,不要挂载目录。同时要挂载的文件必须要先存在,否则会挂载失败。
#示例dockerrun-d-p8088:80--namemynginx-v/opt/a.html:/usr/share/nginx/html/a.htmlnginx我们平时常用的挂载命令-v其实就是--mounttype=bind的简写。
dockerrun-d-p8088:80--namemynginx--mounttype=bind,source=/opt,target=/usr/share/nginx/htmlnginx#简写方式(常用方式)如下:dockerrun-d-p8088:80--namemynginx-v/opt:/usr/share/nginx/htmlnginx5、tmpfsmounts(临时数据卷)运行容器并绑定临时卷:
dockerrun-d--nametestnginx--mounttype=tmpfs,target=/usr/share/nginx/htmlnginx进入容器,创建文件并写入测试数据:
dockerexec-ittestnginx/bin/bashechotest>test.txt删除容器重新创建容器后发现数据丢失,可见临时卷无法持久化数据。
volumes:
bindmount:
tmpfsmount:
#使用ipaddr查看网卡信息ipaddr当Docker启动时,会自动在主机上创建一个docker0虚拟网桥,实际上是Linux的一个bridge,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。
当创建一个Docker容器的时候,同时会创建了一对vethpair接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即eth0;另一端在本地并被挂载到docker0网桥,名称以veth开头(例如vethAQI2QT)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker就创建了在主机和所有容器之间一个虚拟共享网络。
原理:
如上图所示,tomcat01和tomcat02是公用的一个路由器,docker0所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用IP。Docker中的所有网络接口都是虚拟的。虚拟的转发效率高!只要删除容器,对应的一对网桥就没了。
容器要想访问外部网络,需要本地系统的转发支持。在Linux系统中,检查转发是否打开。
sysctlnet.ipv4.ip_forwardnet.ipv4.ip_forward=1如果为0,说明没有开启转发,则需要手动打开。
sysctl-wnet.ipv4.ip_forward=1如果在启动Docker服务的时候设定--ip-forward=true,Docker就会自动设定系统的ip_forward参数为1。
link两个可实现两个容器之间互通
1、启动两个busybox容器实例:(注意要添加-itd参数)
dockerrun-itd--namet1busybox&&dockerrun-itd--namet2busybox2、进行ping测试
如图所示,上面的无法ping通!提示IP找不到。
重新启动一个实例,使用link命令
#启动t3容器dockerrun-itd--namet3--linkt1--linkt2busybox#进行ping测试dockerexec-itt3pingt1dockerexec-itt3pingt2#进入t3查看网络映射配置dockerexec-itt3cat/etc/hosts#原理:link命令就是在运行的设备中配置了hosts网络IP映射。执行过程如下图所示:
补充:以上配置,t3是可以ping通t1和t2,但t1、t2还是不能ping通t3的。
总结:
dockernetworklsdockernetworkinspect网络ID三种网络模式:
#dockernetworklsNETWORKIDNAMEDRIVERSCOPE97bfc37753c8bridgebridgelocal588bf06ad261hosthostlocal5efd1c6c22fdnonenulllocal#bridge: 桥接模式(默认)#host: 和宿主机共享网络#none: 不配置网络#container: 容器网络连通(用得少,局限大)bridge网络应用示例dockerrun-itd--nameb1-P--netbridgebusyboxdockerrun-itd--nameb2-P--netbridgebusyboxdockerrun-itd--nameb3-P--netbridgebusybox#以上可以一句代码执行dockerrun-itd--nameb1-P--netbridgebusybox&&dockerrun-itd--nameb2-P--netbridgebusybox&&dockerrun-itd--nameb3-P--netbridgebusybox#创建一个busybox容器,IP自动分配,指定网络名称为bridge,后台运行。#注意:这里使用busybox,使用的参数是-itd,为什么呢?#测试dockerexec-itb1pingb2#不通dockerexec-itdockerexec-itb3ping01a25b8ccc57#不通dockerexec-itb1ping172.17.0.4#通了#docker0特点:默认,域名不能访问,–-link可以打通查看以上容器的配置信息
dockerinspectb1以上示例,使用默认的网络,无法实现容器之间的互通,如何解决呢?自己建一个网络。
示例1:使用默认配置创建一个网络
dockernetworkcreatemynet#只是指定了网络名称为mynetwork,没有其它配置通过命令查看,发现默认就是bridge模式。
[root@localhost~]#dockernetworklsNETWORKIDNAMEDRIVERSCOPE02b391ca5efabridgebridgelocal47a2cadc245fhosthostlocaldcc742ccb288mynetbridgelocal640e91b35c09nonenulllocal[root@localhost~]#查看网络配置
[root@localhost~]#dockernetworkinspectmynet[{"Name":"mynet","Id":"dcc742ccb288333bb85cab8e0d2a0a2524afc3fed0a2a16a6af77dc021a72589","Created":"2022-01-21T21:28:23.316025804+08:00","Scope":"local","Driver":"bridge","EnableIPv6":false,"IPAM":{"Driver":"default","Options":{},"Config":[{"Subnet":"172.18.0.0/16","Gateway":"172.18.0.1"}]},"Internal":false,"Attachable":false,"Ingress":false,"ConfigFrom":{"Network":""},"ConfigOnly":false,"Containers":{},"Options":{},"Labels":{}}][root@localhost~]#示例2——指定子网和网关
dockernetworkcreate--subnet192.168.0.0/16--gateway192.168.0.1mynetwork#dockernetworkcreate--driverbridge--subnet192.168.0.0/16--gateway192.168.0.1mynetwork#解释:dockernetworkcreate桥接的网络模式(这个参数一般省略)子网网关名称查看网络配置
[root@localhost~]#dockernetworkinspectmynetwork[{"Name":"mynetwork","Id":"7aa4be7ee31f6821ae5d3a60a17c943cfba2df9ce53da77c867a6168cc719a58","Created":"2022-01-21T21:32:13.508461709+08:00","Scope":"local","Driver":"bridge","EnableIPv6":false,"IPAM":{"Driver":"default","Options":{},"Config":[{"Subnet":"192.168.0.0/16","Gateway":"192.168.0.1"}]},"Internal":false,"Attachable":false,"Ingress":false,"ConfigFrom":{"Network":""},"ConfigOnly":false,"Containers":{},"Options":{},"Labels":{}}][root@localhost~]#同一网络下的容器直接互相ping通
#测试网络以下测试全部通过dockerexec-ita1pinga2dockerexec-ita1pinga3dockerexec-ita2pinga1dockerexec-ita2pinga3dockerexec-ita3pinga1dockerexec-ita3pinga2运行结果就不放图了,通过以上测试,得到一个结论:
在同一网络下(不是默认的bridge),容器实例之间是互通的。
继续上一个测试:
其实,不同网络下的容器也能互相连通,执行下面这句代码:
Docker底层的核心技术包括Linux上的名字空间(Namespaces)、控制组(Controlgroups)、Union文件系统(Unionfilesystems)和容器格式(Containerformat)。
Docker的网络实现其实就是利用了Linux上的网络名字空间和虚拟网络设备(特别是vethpair)。
Docker创建容器时默认采用bridge网络,自行分配ip,不允许自己指定。
在实际部署中,我们需要指定容器ip,不允许其自行分配ip,尤其是搭建集群时,固定ip是必须的。
我们可以创建自己的bridge网络:mynet,创建容器的时候指定网络为mynet并指定ip即可。
通过一张图来了解Docker镜像、容器和Dockerfile三者之间的关系。
Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是Dockerfile。
Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了Dockerfile,当我们需要定制自己额外的需求时,只需在Dockerfile上添加或者修改指令,重新生成image即可,省去了敲命令的麻烦。
查看一个镜像的处理过程(Showthehistoryofanimage):
一个标准的dockerfile,注释是必须的。
#这是dockerfile注释,dockerfile中指令以"CMDargs"格式出现CMDargsCMDargs...一个Dockerfile第一个指令必须是FROM指令,用于指定基础镜像,那么基础镜像的父镜像从哪里来?答案是scratch带有该FROMscratch指令的Dockerfile会创建一个基本映像。
解析器指令是可选的,会影响aDockerfile中后续行的处理方式。解析器指令不会向构建添加层,也不会显示为构建步骤,单个指令只能使用一次。
dockerfile目前支持以下两个解析器指令:
我们可以在dockerfile文件开头指定此dockerfile语法解析器,如下:
#syntax=docker/dockerfile:1#syntax=docker.io/docker/dockerfile:1#syntax=example.com/user/repo:tag@sha256:abcdef...通过syntax自定义Dockerfile语法解析器可以实现如下:
官方dockerfile解析器:
比如我们使用1.2最新补丁版本,我们的Dockerfile如下:
#syntax=docker/dockerfile:1.2FROMbusyboxrunecho123我们启用buildkit构建
#DOCKER_BUILDKIT=1dockerbuild-tbusybox:v1.[+]Building5.8s(8/8)FINISHED=>[internal]loadbuilddefinitionfromDockerfile0.3s=>=>transferringdockerfile:150B0.0s=>[internal]load.dockerignore0.4s=>=>transferringcontext:2B0.0s=>resolveimageconfigfordocker.io/docker/dockerfile:1.22.6s=>CACHEDdocker-image://docker.io/docker/dockerfile:1.2@sha256:e2a8561e419ab1ba6b2fe6cbdf49fd92b950.0s=>[internal]loadmetadatafordocker.io/library/busybox:latest0.0s=>[1/2]FROMdocker.io/library/busybox0.3s=>[2/2]RUNecho1231.1s=>exportingtoimage0.3s=>=>exportinglayers0.3s=>=>writingimagesha256:bd66a3db9598d942b68450a7ac08117830b4d66b68180b6e9d63599d01bc8a040.0s=>=>namingtodocker.io/library/busybox:v12.2escape通过escape定义dockerfile的换行拼接转义符
#escape=\如果要构建一个window镜像就有大用处了,我们看下面dockerfile
FROMmicrosoft/nanoserverCOPYtestfile.txtc:\\RUNdirc:\由于默认转义符为\,则在构建的第二步step2会是这样COPYtestfile.txtc:\RUNdirc:显然与我们的预期不符。
我们把转义符换成`号即可
#escape=`FROMmicrosoft/nanoserverCOPYtestfile.txtc:\`RUNdirc:\3.类bash的环境变量FROMbusyboxENVFOO=/barWORKDIR${FOO}#WORKDIR/barADD.$FOO#ADD./barCOPY\$FOO/quux#COPY$FOO/quux${variable_name}语法还支持bash指定的一些标准修饰符:
.dockerignore用于忽略CLI发送到docker守护进程的文件或目录。以下是一个.dockerignore文件
#.dockeringre可以有注释*.md!README.mdtemp*/temp**/*/temp*规则行为*/temp*排除名称以temp根目录的任何直接子目录开头的文件和目录。例如,纯文件/somedir/temporary.txt被排除在外,目录/somedir/temp.*/*/temp*排除temp从根目录下两级的任何子目录开始的文件和目录。例如,/somedir/subdir/temporary.txt被排除在外。temp排除根目录中名称为一个字符扩展名的文件和目录temp。例如,/tempa和/tempb被排除在外。!不排除到文件DockerFile创建镜像1、dockerbuild以nginx镜像为例:
在一个空白目录中,建立一个文本文件,并命名为Dockerfile
mkdirmynginx&&cdmynginx&&touchDockerfile&&vimDockerfile其内容为:
FROMnginxRUNecho'
Hello,Docker!
'>/usr/share/nginx/html/index.html这个Dockerfile文件很简单,一共就两行。涉及了两条指令,FROM和RUN。运行命令:
dockerbuild-tnginx:v1.通过build指定了目标镜像的标签为nginx:v1,以及Dockerfile的上下文context。
什么是docker上下文?
一个面向服务端的目录夹结构,除了Dockerfile,你的一切构建资源都应该在这个目标(指定的上下文)中。
上面命令最后面的.表示当前目录中的所有文件。
上下文是递归处理的。因此,如果是PATH则包含任何子目录,如果是URL则包含存储库及其子模块。
关键点,构建是由Docker守护程序运行,而不是由CLI运行,所以docker会把上下文资源打包传输给守护进程进行构建,为了减少不必要的臃肿,最好是从一个空目录作为上下文开始,并将Dockerfile保存在该目录中。
强调:仅添加构建Dockerfile所需的文件。
我们可以使用-f选项指定dockerfile
dockerbuild-f../Dockerfile-tnginx:v1.使用多个-t选项保持多个tag。
dockerbuild-tnginx:v1-tnginx:v2.结果如下所示:
[root@localhostmynginx]#dockerbuild-tnginx:v1-tnginx:v2.SendingbuildcontexttoDockerdaemon2.048kBStep1/2:FROMnginx--->605c77e624ddStep2/2:RUNecho'
Hello,Docker!
'>/usr/share/nginx/html/index.html--->Runningin500c662be6bbRemovingintermediatecontainer500c662be6bb--->8c99d51744afSuccessfullybuilt8c99d51744afSuccessfullytaggednginx:v1Successfullytaggednginx:v2[root@localhostmynginx]#[root@localhostmynginx]#dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEnginxv18c99d51744af46secondsago141MBnginxv28c99d51744af46secondsago141MB【结果】构建了两个不同tag的同一镜像。PS:如果多次执行相同的构建命令,查看像像列表,会发现没有新的镜像生成,原因是使用了缓存。添加--no-cache命令可以解决。
[root@localhostmynginx]#dockerbuild--no-cache-tnginx:v1-tnginx:v2.SendingbuildcontexttoDockerdaemon2.048kBStep1/2:FROMnginx--->605c77e624ddStep2/2:RUNecho'
Hello,Docker!
'>/usr/share/nginx/html/index.html--->Runningin63ac8e9cf933Removingintermediatecontainer63ac8e9cf933--->413890de8389Successfullybuilt413890de8389Successfullytaggednginx:v1Successfullytaggednginx:v2[root@localhostmynginx]#dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEnginxv1413890de83895secondsago141MBnginxv2413890de83895secondsago141MB从版本18.09开始,Docker支持由moby/buildkit项目提供的用于执行构建的新后端。与旧的实现相比,BuildKit后端提供了许多好处。例如,BuildKit可以:
要使用BuildKit后端,只需要在调用DOCKER_BUILDKIT=1dockerbuild之前在CLI上设置环境变量DOCKER_BUILDKIT=1。或者配置/etc/docker/daemon.json启用。
环境变量
exportDOCKER_BUILDKIT=1exportCOMPOSE_DOCKER_CLI_BUILD=1注入到.bashrc文件
echo-e"exportDOCKER_BUILDKIT=1">>~/.bashrcecho-e"exportCOMPOSE_DOCKER_CLI_BUILD=1">>~/.bashrc修改/etc/docker/daemon.json
"features":{ "buildkit":true }重启docker-daemon和docker
sudosystemctldaemon-reload&&sudosystemctlrestartdocker演示如下:
FROM指令用于指定其后构建新镜像所使用的基础镜像。FROM指令必是Dockerfile文件中的首条命令,启动构建流程后,Docker将会基于该镜像构建新镜像,FROM后的命令也会基于这个基础镜像。
FROM语法格式为:
FROM
在镜像的构建过程中执行特定的命令,并生成一个中间镜像。
#shell格式RUN
COPYpackage.json/usr/src/app/<源路径>可以是多个,甚至可以是通配符,其通配符规则要满足Go的filepath.Match规则,如:
COPYhom*/mydir/COPYhom.txt/mydir/<目标路径>可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用WORKDIR指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
ADD指令和COPY的格式和性质基本一致。
另外需要注意的是,ADD指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
因此在COPY和ADD指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY指令,仅在需要自动解压缩的场合使用ADD。
在构建镜像时,复制上下文中的文件到镜像内,格式:
ADD<源路径>...<目标路径>ADD["<源路径>",..."<目标路径>"]5、ENV设置环境变量格式有两种:
ENV
ENVVERSION=1.0DEBUG=on\NAME="HappyFeet"这个例子中演示了如何换行(在Linux中,\表示换行),以及对含有空格的值用双引号括起来的办法,这和Shell下的行为是一致的。
为构建的镜像设置监听端口,使容器在运行时监听。格式:
EXPOSE
VOLUME用于创建挂载点,即向基于所构建镜像创始的容器添加卷:
VOLUME["/data"]一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
VOLUME让我们可以将源代码、数据或其它内容添加到镜像中,而又不并提交到镜像中,并使我们可以多个容器间共享这些内容。
CMD用于指定在容器启动时所要执行的命令。CMD有以下三种格式:
CMD["executable","param1","param2"]CMD["param1","param2"]CMDcommandparam1param2省略可执行文件的exec格式,这种写法使CMD中的参数当做ENTRYPOINT的默认参数,此时ENTRYPOINT也应该是exec格式,具体与ENTRYPOINT的组合使用,参考ENTRYPOINT。
注意与RUN指令的区别:RUN在构建的时候执行,并生成一个新的镜像,CMD在容器运行的时候执行,在构建时不进行任何操作。
ENTRYPOINT指定这个容器启动的时候要运行的命令,可以追加命令。
ENTRYPOINT用于给容器配置一个可执行程序。也就是说,每次使用镜像创建容器时,通过ENTRYPOINT指定的程序都会被设置为默认程序。ENTRYPOINT有以下两种形式:
ENTRYPOINT["executable","param1","param2"]ENTRYPOINTcommandparam1param2ENTRYPOINT与CMD非常类似,不同的是通过dockerrun执行的命令不会覆盖ENTRYPOINT,而dockerrun命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。
dockerrun运行容器时指定的参数都会被传递给ENTRYPOINT,且会覆盖CMD命令指定的参数。如,执行dockerrun
也可以通过dockerrun--entrypoint重写ENTRYPOINT入口点。如:可以像下面这样指定一个容器执行程序:
ENTRYPOINT["/usr/bin/nginx"]10、USER指定当前用户USER用于指定运行镜像所使用的用户:
USERdaemon使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。以下都是合法的指定试:
USERuserUSERuser:groupUSERuidUSERuid:gidUSERuser:gidUSERuid:group使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过dockerrun运行容器时,可以通过-u参数来覆盖所指定的用户。
WORKDIR用于在容器内设置一个工作目录:
WORKDIR/path/to/workdir通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。如,使用WORKDIR设置工作目录:
WORKDIR/aWORKDIRbWORKDIRcRUNpwd在以上示例中,pwd最终将会在/a/b/c目录中执行。在使用dockerrun运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。
LABEL用于为镜像添加元数据,元数以键值对的形式指定:
LABEL
LABELversion="1.0"description="这是一个Web服务器"by="Docker笔录"指定后可以通过dockerinspect查看:
dockerinspectitbilu/test"Labels":{"version":"1.0","description":"这是一个Web服务器","by":"Docker笔录"},13、ARG构建参数ARG用于指定传递给构建运行时的变量:
ARG
ARGsiteARGbuild_user=IT笔录以上我们指定了site和build_user两个变量,其中build_user指定了默认值。在使用dockerbuild构建镜像时,可以通过--build-arg
dockerbuild--build-argsite=itiblu.com-titbilu/test.这样我们构建了itbilu/test镜像,其中site会被设置为itbilu.com,由于没有指定build_user,其值将是默认值IT笔录。
ONBUILD用于设置镜像触发器:
ONBUILD[INSTRUCTION]当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被触发。如,当镜像被使用时,可能需要做一些处理:
[...]ONBUILDADD./app/srcONBUILDRUN/usr/local/bin/python-build--dir/app/src[...]15、STOPSIGNALSTOPSIGNAL用于设置停止容器所要发送的系统调用信号:
STOPSIGNALsignal所使用的信号必须是内核系统调用表中的合法的值,如:SIGKILL。
SHELL用于设置执行命令(shell式)所使用的的默认shell类型:
SHELL["executable","parameters"]SHELL在Windows环境下比较有用,Windows下通常会有cmd和powershell两种shell,可能还会有sh。这时就可以通过SHELL来指定所使用的shell类型:
所谓私有仓库,也就是在本地(局域网)搭建的一个类似公共仓库的东西,搭建好之后,我们可以将镜像提交到私有仓库中。这样我们既能使用Docker来运行我们的项目镜像,也避免了商业项目暴露出去的风险。
下面就是详细的基于Registry搭建私有仓库的步骤,首先我们可以准备两台服务器,这里我有两台Linux服务主机,他们的角色如下:
首先,下载Registry镜像并启动
dockerpullregistry然后,运行一个Registry镜像仓库的容器实例
dockerrun-d-v/opt/images/registry:/var/lib/registry\-p5000:5000\--restart=always\--namegerry-registryregistry\最后,在客户端查看镜像仓库中的所有镜像
加上下面这一句,这里的“your-server-ip”请换为你的服务器的外网IP地址:
为了使得配置生效,重新启动docker服务:
#systemctlrestartdocker其次,为要上传的镜像打Tag
dockertagyour-image-name:tagnameyour-server-ip:5000/your-image-name:tagname最后,开始正式上传镜像到服务端镜像仓库
dockerpushyour-registry-server-ip:5000/your-image-name:tagname这时我们可以再次通过访问API验证镜像仓库的内容:
dockerpullyour-server-ip:5000/your-image-name:tagname如果想要知道要下载的镜像都有哪些tag(或版本),可以通过下面这个api来获取:
dockertagxdp-service-runtime:2.2edisonsaonian/xdp-service-runtime:2.2打完Tag就可以推送到远程仓库啦:
dockerpushedisonsaonian/xdp-service-runtime:2.2这时,便可以到dockerhub上查看Repository的信息。当然,我们可以在另外的客户端上拉取这个刚刚上传的镜像了。
怎么样,是不是很Easy,Enjoy!
Harbor作为一个企业级私有Registry服务器,提供了更好的性能和安全,提升了用户使用Registry构建和运行环境传输镜像的效率。虽然Harbor和Registry都是私有镜像仓库的选择,但是Harbor的企业级特性更强,因此也是更多企业级用户的选择。
Harbor实现了基于角色的访问控制机制,并通过项目来对镜像进行组织和访问权限的控制,也常常和K8S中的namespace结合使用。此外,Harbor还提供了图形化的管理界面,我们可以通过浏览器来浏览,检索当前Docker镜像仓库,管理项目和命名空间。
下面列出了Harbor的搭建过程,主要参考自Harbor的github文档:
(1)下载离线安装包
这里选择版本为v2.4.1,下载完成后传输到你的服务器上并解压:
tarzvxfharbor-offline-installer-v2.4.1.tgz(2)安装docker
如果还没有安装docker,那么请先安装docker,已安装则跳过。
#yuminstalldocker#systemctlstartdocker.service(3)安装docker-compose
这里选择Github源:下载并设置权限
docker-compose-versionPS:如果想要卸载docker-compose,请执行以下命令
sudorm/usr/local/bin/docker-compose3.2自签TLS证书虽然对于所有要求配置HTTPS的要求我都是比较抵触的,不过考虑到去年公司官网被攻击并且还被Google列入黑名单导致公司官网好几天不可用,我们信息中心遭受了很大的压力,因此还是老老实实地为所有有需要的地方配上HTTPS吧。当然,这里演示的只是我们自己创建的证书,实际生产环境中我们切记还是需要去阿里云或者其他云服务器厂商申请免费或收费的证书。
(1)创建存放证书的目录
mkdir-p/data/cert/cd/data/cert/(2)创建自签名证书key文件并生成证书请求:
注意:这里reg.edisonedu.com需要替换为你的域名,我这里是随便取的,会在后面更改DNS。
opensslgenrsa-outreg.edisonedu.com.key4096opensslreq-x509-new-nodes-sha512-days365\-subj"/C=TW/ST=Taipei/L=Taipei/O=example/OU=Personal/CN=reg.edisonedu.com"\-keyreg.edisonedu.com.key\-outreg.edisonedu.com.crt3.3Harbor安装与配置在解压的harbor目录中编辑harbor.cfg文件,修改以下内容:
接下来就是执行准备这个harbor.cfg了,在harbor目录下执行以下命令:
./prepare然后再执行install这个shell脚本进行install:
./install.sh它会经历好几个步骤:加载Harbor镜像(初次安装耗时较长)、准备运行环境、通过docker-compose启动harbor(有兴趣的童鞋可以看看harbor目录下的docker-compose.yml文件)等。
我们可以通过docker-composeps命令查看启动起来的docker实例:
可以看到,整个harbor容器实例群包括了管理服务、数据库服务、Job服务、日志服务以及Portal网页入口(默认是80端口)服务等。
为了能在你的开发机上能够访问到我们这个域名,你需要改一下Windows的hosts文件(C:/Windows/System32/drivers/etc/hosts),如果你用的阿里云,那么你可能还需要开放一下端口号,80和443端口:
为了进行后面的演示,这里我们创建一个私有项目:
然后再创建一个项目管理员用户:
最后,为test项目添加新创建的这个用户作为项目管理员(由于我们后续会演示镜像上传,所以这里设为管理员,如果只是拉取镜像,可以设为开发人员角色,如果只是看看那可以只设置为游客角色):
接下来我们就会在另一台主机中访问这台服务器上部署的Harbor私有镜像仓库了。
(1)首先,由于我们这里是自签证书,不是受信任的,所以我们要做一些准备工作才能在普通主机上访问到刚刚部署的Harbor镜像仓库。(注意:这一部分的操作在另外的一台主机上,非我们刚刚部署的Harbor的服务器上面)
准备工作一:创建Harbor服务域名的证书文件夹
mkdir/etc/docker/certs.d/reg.edisonedu.com-p准备工作二:设置Hosts匹配我们设置的假域名
vim/etc/hosts加上一行:
47.22.232.200reg.edisonedu.com#替换为你的Harbor服务器外网IP准备工作三:将Harbor服务器上的证书拷贝要访问Harbor仓库的主机上
这一工作你可以选择直接通过SFTP软件将reg.edisonedu.com.crt从Harbor服务器上拷贝到客户机刚刚创建的文件夹中(/etc/docker/certs.d/reg.edisonedu.com),也可以通过scp命令去拷贝,总之拷贝过来就行。这里我通过scp去拷贝:
dockerloginreg.edisonedu.com(3)然后,就跟刚刚我们在dockerhub中的步骤一样了,假设我们要push一个镜像到镜像仓库,首先打个Tag:
dockertagxdp-service-runtime:2.2reg.edisonedu.com/test/xdp-service-runtime:2.2PS:这里我们打的tag加上了私有项目名test
打完Tag,就可以push到镜像仓库了:
dockerpushreg.edisonedu.com/test/xdp-service-runtime:2.2推送完后,我们可以到Harbor的Web管理界面中验证:
(4)推送完之后,我们想在其他docker主机中pull下来呢?
dockerpullreg.edisonedu.com/test/xdp-service-runtime:2.2(5)如果想退出我们的私有仓库
dockerlogoutreg.edisonedu.com3.5其他补充如果想要继续更改harbor配置,那么改完后需要重新初始化Harbor:
docker-composedown-v#暂停Harbor实例群./prepare#生成配置文件,根据harbor.cfg配置生成docker-compose文件。docker-composeup-d#后台启动Harbor实例群想要暂停和重启Harbor:
docker-composestop#暂停Harbordocker-composestart#启动Harbor不用Harbor了,那么可以彻底删除Harbor的数据和镜像文件:
123#彻底地删除Harbor的数据和镜像rm-r/data/databaserm-r/data/registry(四)小结本文总结了流行的几个镜像仓库的搭建步骤,并给出了基本使用示例。个人感觉:对于个人开发者或开源社区而言,dockerhub主要提供的是类似于github的共享公共仓库(当然dockerhub也有提供私有仓库)。对于小团队而言,官方提供的Registry项目可以帮助小团队快速地构建起自己的镜像仓库把精力更多放在快速迭代上面。而对于中大规模的团队,Harbor的企业级特性更加适合此类型的团队使用。
示例:
version:"3.8"services:redis:#服务名称image:redis:alpine#使用的镜像ports:-"6379"#指定的端口networks:-frontend#使用的网络deploy:replicas:2update_config:parallelism:2delay:10srestart_policy:condition:on-failuredb:image:postgres:9.4volumes:-db-data:/var/lib/postgresql/datanetworks:-backendresult:image:nginxports:-"5001:80"networks:-backenddepends_on:-dbdeploy:replicas:1update_config:parallelism:2delay:10srestart_policy:condition:on-failureworker:image:nginxnetworks:-frontend-backenddeploy:mode:replicatedreplicas:1labels:[APP=VOTING]restart_policy:condition:on-failuredelay:10smax_attempts:3window:120snetworks:frontend:backend:volumes:db-data:SERVICES配置指令1.container_name
指定容器名称
version:"3"services:redis:image:redis:alpinecontainer_name:redis_test2.image
指定为镜像名称或镜像ID。如果镜像在本地不存在,Compose将会尝试拉取这个镜像。
version:"3"services:redis:image:redis:alpine3.build
指定Dockerfile所在文件夹的路径(可以是绝对路径,或者相对docker-compose.yml文件的路径)。Compose将会利用它自动构建这个镜像,然后使用这个镜像。
version:'3'services:webapp:build:./dir也可以使用context指令指定Dockerfile所在文件夹的路径(或者是git仓库的URL)。同时使用dockerfile指令指定Dockerfile文件名。
version:'3'services:webapp:build:context:./dirdockerfile:Dockerfile-name注意:如果同时指定了image和build,image不在具有单独使用它的意义,而是指定了目前要构建的镜像的名称。也就是说Compose会使用build指令中指定的Dockerfile构建的镜像,之后构建的镜像名称使用image中指定的名字webapp:tag命名。
4.command
使用command可以覆盖容器启动后默认执行的命令。
#写成shell形式command:bundleexecthin-p3000#写成Dockerfile中的exec格式command:[bundle,exec,thin,-p,3000]5.depends_on
解决容器的依赖、启动先后的问题。
version:'3'services:web:image:redis:alpinecontainer_name:redis_testdepends_on:-db6.environment
设置环境变量。可以使用数组或字典两种格式。
只给定名称的变量会自动获取运行Compose主机上的对应变量的信息。
environment:RACK_ENV:developmentSHOW:'true'SESSION_SECRET:environment:-RACK_ENV=development-SHOW=true-SESSION_SECRET如果变量名称或者值中用到true|false,yes|no等表达布尔含义的词汇,最好放到引号里,避免YAML自动解析某些内容为对应的布尔语义。这些特定词汇。
y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF7.expose
暴露端口,但不映射到宿主机,只被连接的服务访问。
仅可以指定容器内部的端口为参数
expose:-"3000"-"8000"8.ports
映射端口信息。
宿主端口:容器端口(即:HOST:CONTAINER)的格式格式,或者仅仅指定容器的端口(宿主将会随机选择端口)。
ports:-"3000"-"3000-3005"-"8000:8000"-"9090-9091:8080-8081"-"49100:22"-"127.0.0.1:8001:8001"-"127.0.0.1:5000-5010:5000-5010"-"6060:6060/udp"注意:当使用HOST:CONTAINER格式来映射端口时,如果你使用的容器端口小于60并且没放到引号里,可能会得到错误结果,因为YAML会自动解析xx:yy这种数字格式为60进制。为避免出现这种问题,建议数字串都采用引号包括起来的字符串格式。
9.extra_hosts
类似Docker中的–add-host参数,指定额外的host名称映射信息。会在启动后的服务容器中/etc/hosts文件中添加host映射信息。
extra_hosts:-"somehost:162.242.195.82"-"otherhost:50.31.209.229"10.networks
要加入的网络,使用顶级networks定义下的条目。
services:some-service:networks:-some-network-other-networknetworks:some-network:other-network:11.entrypoint
指定服务容器启动后执行的入口文件。
12.user
指定容器中运行应用的用户名。
13.working_dir
指定容器中工作目录。
14.restart
指定容器退出后的重启策略为始终重启。该命令对保持服务始终运行十分有效,在生产环境中推荐配置为always或者unless-stopped。
restart:always15.alias
网络上此服务的别名(备用主机名)。同一网络上的其他容器可以使用服务名称或此别名连接到其中一个服务的容器。由于aliases是网络范围的,因此相同的服务可以在不同的网络上具有不同的别名。注意:网络范围的别名可以由多个容器共享,甚至可以由多个服务共享。如果是,则无法保证名称解析为的容器。
services:some-service:networks:some-network:aliases:-alias1-alias3other-network:aliases:-alias2VOLUMES配置指令数据卷所挂载路径设置。可以设置宿主机路径(HOST:CONTAINER)或加上访问模式(HOST:CONTAINER:ro)。
该指令中路径支持相对路径。
使用docker-composeup启动容器后,这些容器都会被加入app_default网络中。使用dockernetworkls可以查看网络列表,dockernetworkinspect可以查看对应网络的配置。
version:'3'services:web:mage:nginx:latestcontainer_name:webdepends_on:-dbports:-"9090:80"links:-dbdb:image:mysqlcontainer_name:db2.networks关键字指定自定义网络
例如下面的docker-compose.yml文件,定义了front和back网络,实现了网络隔离。其中proxy和db之间只能通过app来实现通信。其中,custom-driver-1并不能直接使用,你应该替换为host,bridge,overlay等选项中的一种。
version:'3'services:proxy:build:./proxynetworks:-frontapp:build:./appnetworks:-front-backdb:image:postgresnetworks:-backnetworks:front:#Useacustomdriverdriver:custom-driver-1back:#Useacustomdriverwhichtakesspecialoptionsdriver:custom-driver-2driver_opts:foo:"1"bar:"2"3.配置默认网络
version:'2'services:web:build:.ports:-"8000:8000"db:image:postgresnetworks:default:#Useacustomdriverdriver:custom-driver-14.使用已存在的网络
networks:default:external:name:my-pre-existing-networkDocker-Compose常用命令用于部署一个Compose应用。
默认情况下该命令会读取名为docker-compose.yml或docker-compose.yaml的文件。
当然用户也可以使用-f指定其他文件名。通常情况下,会使用-d参数令应用在后台启动。
被停止的应用可以很容易地通过docker-composerestart命令重新启动。
用于删除已停止的Compose应用。
它会删除容器和网络,但是不会删除卷和镜像。
重启已停止的Compose应用。
如果用户在停止该应用后对其进行了变更,那么变更的内容不会反映在重启后的应用中,这时需要重新部署应用使变更生效。
用于列出Compose应用中的各个容器。
输出内容包括当前状态、容器运行的命令以及网络端口。
停止并删除运行中的Compose应用。
Swarm是Docker的一个编排工具,在之前我们只是在一台机器来进行docker的管理,但是有时容器并不一定都在一台主机上,如果是分布式的处于多台主机上,这时就可以借助于Swarm,Swarm是Docker自带的编排工具,只要你安装了Docker就会存在DockerSwarm工具。
Swarm中的模式是有两大类节点,一类是manager节点,另一类是worker节点,manager节点相当于对服务的创建和调度,worker节点主要是运行容器服务(当然manager节点也是可以运行的)。
manager节点状态、信息的同步根据Raftconsensusgroup的网络进行通信同步的,而worker之间的通信依靠的是Gossipnetwork做到的。
另外一个重要的概念就是service,我们可以在一个manager节点上创建一个服务,但是可以根据这一个服务创建多个容器的任务,这些容器任务运行在不同的节点上。
那么如何进行Swarm集群的搭建呢?
[root@centos-7~]#dockerswarm--helpUsage:dockerswarmCOMMANDManageSwarmCommands:caDisplayandrotatetherootCAinitInitializeaswarmjoinJoinaswarmasanodeand/ormanagerjoin-tokenManagejointokensleaveLeavetheswarmunlockUnlockswarmunlock-keyManagetheunlockkeyupdateUpdatetheswarmRun'dockerswarmCOMMAND--help'formoreinformationonacommand.可以看到其中有一个init的命令,这就是初始化一个Swarm,我们再看看这个init的参数:
在此之前我们应该先知道自己本机的ip,以便于–advertise-addrstring这个参数使用,查看本机ip:
然后就可以初始化Manager节点:
[root@centos-7~]#dockerswarminit--advertise-addr=192.168.0.108Swarminitialized:currentnode(sz7bfcmk637qhqfvzqhdnk3t2)isnowamanager.Toaddaworkertothisswarm,runthefollowingcommand:dockerswarmjoin--tokenSWMTKN-1-0zzpk3xqq2ykb5d5jcd6hqeimckhrg5qu82xs7nw2wwnwyjmzg-9tmof9880m9r92vz5rygaa42e192.168.0.108:2377Toaddamanagertothisswarm,run'dockerswarmjoin-tokenmanager'andfollowtheinstructions.可以看到已经创建了一个swarmmanager节点–advertise-addr参数的值192.168.0.108是自己的ip,相当于将自己加入到swarm中。它也说明了其它worker加入的方式。
另外再开启一台虚拟机,将这台机器加入到swarm中成为worker节点:
[root@localhost~]#dockerswarmjoin--tokenSWMTKN-1-0zzpk3xqq2ykb5d5jcd6hqeimckhrg5qu82xs7nw2wwnwyjmzg-9tmof9880m9r92vz5rygaa42e192.168.0.108:2377Thisnodejoinedaswarmasaworker.可以看到已经成功了。
注意的是查看节点状态只能在manager节点那台机器上查看,普通节点无法执行查看命令:
[root@centos-7~]#dockernodelsIDHOSTNAMESTATUSAVAILABILITYMANAGERSTATUSENGINEVERSIONsz7bfcmk637qhqfvzqhdnk3t2*centos-7ReadyActiveLeader19.03.589tezea9ltn6mqek7b48gtve7localhost.localdomainReadyActive18.09.7可以看到有两台机器了。
上面我们已经将节点创建好了,现在可以在这些节点上创建容器了,那么就需要看看dockerservice的帮助信息:
[root@centos-7~]#dockerservice--helpUsage:dockerserviceCOMMANDManageservicesCommands:createCreateanewserviceinspectDisplaydetailedinformationononeormoreserviceslogsFetchthelogsofaserviceortasklsListservicespsListthetasksofoneormoreservicesrmRemoveoneormoreservicesrollbackRevertchangestoaservice'sconfigurationscaleScaleoneormultiplereplicatedservicesupdateUpdateaserviceRun'dockerserviceCOMMAND--help'formoreinformationonacommand.可以看到有create这个命令,可以这样理解,dockerservicecreate相当于dockerrun就是创建容器,只不过在swarm中是不同的表现方式。
[root@centos-7docker]#dockerservicecreate--namenginx-demonginximagebusybox:latestcouldnotbeaccessedonaregistrytorecorditsdigest.Eachnodewillaccessbusybox:latestindependently,possiblyleadingtodifferentnodesrunningdifferentversionsoftheimage.edqgwqjka7ceyym9tg4gr9uimoverallprogress:1outof1tasks1/1:runningverify:Serviceconverged可以查看是否创建成功:
[root@centos-7docker]#dockerservicelsIDNAMEMODEREPLICASIMAGEPORTSedqgwqjka7cedemoreplicated1/1busybox:latest可以具体看这个service的内容:
[root@centos-7docker]#dockerservicepsdemoIDNAMEIMAGENODEDESIREDSTATECURRENTSTATEERRORPORTSlshgq4gfi3cvdemo.1busybox:latestlocalhost.localdomainRunningRunning2minutesago2、水平扩展之前接触或scale,它可以帮助我们创建多个service,这里也是可行的:
[root@centos-7docker]#dockerservicescaledemo=5demoscaledto5overallprogress:5outof5tasks1/5:running2/5:running3/5:running4/5:running5/5:runningverify:Serviceconverged已经有5个demo服务的容器正在运行了。可以先看看serverice:
[root@centos-7docker]#dockerservicelsIDNAMEMODEREPLICASIMAGEPORTSedqgwqjka7cedemoreplicated5/5busybox:latest再看看具体的容器数量:
[root@centos-7docker]#dockerservicepsdemoIDNAMEIMAGENODEDESIREDSTATECURRENTSTATEERRORPORTSlshgq4gfi3cvdemo.1busybox:latestlocalhost.localdomainRunningRunning3hoursagovvxl3f5p1w40demo.2busybox:latestcentos-7RunningRunning3hoursagoq5imz8pqygbvdemo.3busybox:latestcentos-7RunningRunning3hoursagoih6e2bhzzru2demo.4busybox:latestlocalhost.localdomainRunningRunning3hoursagol32ziu7ygoqwdemo.5busybox:latestcentos-7RunningRunning3hoursago可以看到5个容器在不同的节点上,其中有3个容器在manager节点,有两个在worker节点上。
当然,每一个节点都可以自己查看自己目前运行的容器,比如worker节点查看自己运行容器的数量:
[root@centos-7docker]#dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMESfbf77922d24cbusybox:latest"/bin/sh-c'whilet…"3hoursagoUp3hoursdemo.5.l32ziu7ygoqwapu4rbazqhyej51e0e953688fbusybox:latest"/bin/sh-c'whilet…"3hoursagoUp3hoursdemo.3.q5imz8pqygbv5fw1r2wyt84ps6f2b6d5f670cbusybox:latest"/bin/sh-c'whilet…"3hoursagoUp3hoursdemo.2.vvxl3f5p1w40oc82wvm2yq01q只需要通过dockerps命令即可。
scale在swarm中除了水平扩展外,还有一个作用,那就是修复作用,比如将某一个节点中的容器删除掉,那么很快swarm中发觉并进行修复,数量保持原先的样子。
假如现在删除worker中的一个容器:
[root@localhost~]#dockerrm-fa8c8e6a95978a8c8e6a95978我们在manager节点中查看容器情况:
[root@centos-7docker]#dockerservicepsdemoIDNAMEIMAGENODEDESIREDSTATECURRENTSTATEERRORPORTSlshgq4gfi3cvdemo.1busybox:latestlocalhost.localdomainRunningRunning3hoursagovvxl3f5p1w40demo.2busybox:latestcentos-7RunningRunning3hoursagoq5imz8pqygbvdemo.3busybox:latestcentos-7RunningRunning3hoursagozkoywf8h19p1demo.4busybox:latestlocalhost.localdomainRunningStartinglessthanasecondagoih6e2bhzzru2\_demo.4busybox:latestlocalhost.localdomainShutdownFailed23secondsago"task:non-zeroexit(137)"l32ziu7ygoqwdemo.5busybox:latestcentos-7RunningRunning3hoursago可以看到红色就是我们删掉的容器,但是后面它又马上补充了一个,保持了整个系统的稳定性。
如何使用Swarm进行部署wordpress呢?wordpress涉及到数据库容器以及wordpress应用容器,那么这两个容器很有可能是不在一台机器的,也就是使用Swarm中两个容器是分配到不同的节点之上的。
首先,将之前的service进行移除:
[root@centos-7docker]#dockerservicermdemodemo1、创建overlay网络需要将这些service部署在一个overlay网络中,所以需要有个overlay网络。所以现在manager节点上创建这个网络:
[root@centos-7docker]#dockernetworkcreate-doverlaydemoxcjljjqcw26b2xukuirgpo6au可以查看:
[root@centos-7docker]#dockernetworklsNETWORKIDNAMEDRIVERSCOPExcjljjqcw26bdemooverlayswarm值得注意的是其他节点上现在并没有这个网络,那么其它节点又是怎么连上这个网络的呢?
[root@centos-7docker]#dockerservicecreate--namemysql--envMYSQL_ROOT_PASSWORD=root--envMYSQL_DATABASE=wordpress--networkdemo--mounttype=volume,source=mysql-data,destination=/var/lib/mysqlmysql:5.7.24eirarnex6suqa4uqzgowncigvoverallprogress:1outof1tasks1/1:running[==================================================>]verify:Serviceconverged可以看看这个服务的详情以及位于哪一个节点上:
[root@centos-7docker]#dockerservicelsIDNAMEMODEREPLICASIMAGEPORTSeirarnex6suqmysqlreplicated1/1mysql:5.7.24[root@centos-7docker]#dockerservicepsmysqlIDNAMEIMAGENODEDESIREDSTATECURRENTSTATEERRORPORTSc4a4lggjmvufmysql.1mysql:5.7.24centos-7RunningRunningaboutaminuteago3、启动wordpress服务接着在manager节点上启动wordpress服务
[root@centos-7docker]#dockerservicecreate--namewordpress-p80:80--envWORDPRESS_DB-PASSWORD=root\--envWORDPRESS_DB_HOST=mysql--networkdemo\--mounttype=volume,source=wordpress-config,destination=/var/www/htmlwordpress上述中–mount参数将容器中的配置等一系列的文件映射出来了,是方便修改容器中配置文件,修复数据库连接不上的错误,详情查看:
现在可以进行访问本机的80端口进行测试了
这种部署方式就是使用docker-compose.yml文件来进行部署应用,而上面那种方式就是每次手动单启容器。
[root@centos-7wordpress-stack]#dockerstack--helpUsage:dockerstack[OPTIONS]COMMANDManageDockerstacksOptions:--orchestratorstringOrchestratortouse(swarm|kubernetes|all)Commands:deployDeployanewstackorupdateanexistingstacklsListstackspsListthetasksinthestackrmRemoveoneormorestacksservicesListtheservicesinthestackRun'dockerstackCOMMAND--help'formoreinformationonacommand.可以看到dockerstack的命令不是很多,第一个就是部署服务,一个docker-compose.yml就是一个服务,当然这个文件里面可能包含多个service。下面以实例来说明:
dockerpullredis2、查看本地镜像
dockerrun-p6379:6379--nameredis-v/root/gerry/redis/redis.conf:/etc/redis/redis.conf-v/root/gerry/redis/data:/data-dredisredis-server/etc/redis/redis.conf--appendonlyyes4、查看redis状态
5、进入redis
dockerexec-itredis/bin/bash(二)MongoDB查询mongo镜像
dockersearchmongo拉取镜像
dockerpullmongo运行容器
dockerrun--namemongodb-p27017:27017-v$PWD/db:/data/db-dmongo:latest1.以admin用户身份进入mongo:
dockerexec-itmongodbmongoadmin2.创建一个admin管理员账号:
db.createUser({user:'admin',pwd:'admin123456',roles:[{role:"userAdminAnyDatabase",db:"admin"}]});3.对admin用户进行身份认证
db.auth("admin","admin123456");4.创建用户、密码和数据库
用户zero密码123456数据库app
db.createUser({user:'zero',pwd:'123456',roles:[{role:"readWrite",db:"app"}]});5.对zero进行身份认证
db.auth("zero","123456");6.切换数据库
useapp7.添加数据
向表test中添加数据
db.test.save({name:"zhangsan"});8.查询数据
db.test.find();(三)RabbitMQ查看仓库里的RabbitMQ
dockersearchrabbitmq拉取RabbitMQ
dockerpullrabbitmq这里是直接安装最新的,如果需要安装其他版本在rabbitmq后面跟上版本号即可
启动RabbitMQ
dockerrun-d--namerabbit-p15672:15672-p5672:5672rabbitmq安装插件
先执行dockerps拿到当前的镜像ID进入容器安装插件ctrl+p+q退出当前容器dockerpsdockerexec-it镜像ID/bin/bashrabbitmq-pluginsenablerabbitmq_management访问验证
第一步拉去zookeeper镜像
dockerpullwurstmeister/zookeeper第二步拉去kafka镜像
dockerpullwurstmeister/kafka第三步后台启动zookeeper
第四步后台启动kafka
dockerrun-d--namekafka-p9092:9092-eKAFKA_BROKER_ID=0-eKAFKA_ZOOKEEPER_CONNECT=192.168.3.249/kafka-eKAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.3.249:9092-eKAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092-v/etc/localtime:/etc/localtimewurstmeister/kafka-eKAFKA_BROKER_ID=0在kafka集群中,每个kafka都有一个BROKER_ID来区分自己
-eKAFKA_ZOOKEEPER_CONNECT=宿主机ip地址:2181/kafka配置zookeeper管理kafka的路径宿主机ip地址:2181/kafka
-eKAFKA_ADVERTISED_LISTENERS=PLAINTEXT://宿主机ip地址:9092把kafka的地址端口注册给zookeeper
-eKAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092配置kafka的监听端口
一直启动失败
问题解决使用-it查看容器内部
dockerrun-it–namekafka-p9092:9092-eKAFKA_BROKER_ID=0-eKAFKA_ZOOKEEPER_CONNECT=宿主机ip地址7/kafka-eKAFKA_ADVERTISED_LISTENERS=PLAINTEXT://宿主机ip地址:9092-eKAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092-v/etc/localtime:/etc/localtimewurstmeister/kafka发现启动内存不足
使用如下命令dockerrun-d–namekafka-p9092:9092-eKAFKA_BROKER_ID=0-eKAFKA_ZOOKEEPER_CONNECT=192.168.3.249/kafka-eKAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.3.249:9092-eKAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092-eKAFKA_HEAP_OPTS=“-Xmx256m-Xms256m”-v/etc/localtime:/etc/localtimewurstmeister/kafka
-eKAFKA_HEAP_OPTS=“-Xmx256m-Xms256m”配置容器的启动所用内存默认的是KAFKA_HEAP_OPTS=“-Xmx1G-Xms1G”
此时启动成功
拉取Nginx镜像
dockerpullnginx运行Nginx容器
dockerrun-d--namemynginx-p80:80-v/root/gerry/nginx/nginx.conf:/etc/nginx/nginx.conf-v/root/gerry/nginx/logs:/var/log/nginx-v/root/gerry/nginx/html:/usr/share/nginx/html-v/gerry/nginx/conf:/etc/nginx/conf.d--privileged=truenginx测试nginx部署结果
拉取镜像
dockerpulldocker.elastic.co/elasticsearch/elasticsearch:7.14.0启动elasticsearch
dockerrun-d-p9200:9200-p9300:9300-e"discovery.type=single-node"docker.elastic.co/elasticsearch/elasticsearch:7.14.0DockerJenkins前言有人问,为什么要用Jenkins?我说下我以前开发的痛点,在一些中小型企业,每次开发一个项目完成后,需要打包部署,可能没有专门的运维人员,只能开发人员去把项目打成一个war包,可能这个项目已经上线了,需要把服务关,在部署到服务器上,将项目启动起来,这个时候可能某个用户正在操作某些功能上的东西,如果你隔三差五的部署一下,这样的话对用户的体验也不好,自己也是烦的很,总是打包拖到服务器上。希望小型企业工作人员学习一下,配置可能复杂,但是你配置好了之后,你只需要把代码提交到Git或者Svn上,自动构建部署,非常方便。
Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。
1.需要准备一台服务器,大家可以在网上买,个人学习的话还是建议大家去安装一个虚拟机,去装一个Linux系统
2.需要准备一个远程连接工具,连接到Linux系统
3.机器上安装了Docker
1.启动docker,下载Jenkins镜像文件
12mkdir-p/var/jenkins_mountchmod777/var/jenkins_mount3.创建并启动Jenkins容器
-d后台运行镜像
-p10240:8080将镜像的8080端口映射到服务器的10240端口。
-p10241:50000将镜像的50000端口映射到服务器的10241端口
-v/var/jenkins_mount:/var/jenkins_mount/var/jenkins_home目录为容器jenkins工作目录,我们将硬盘上的一个目录挂载到这个位置,方便后续更新镜像后继续使用原来的工作目录。这里我们设置的就是上面我们创建的/var/jenkins_mount目录
–namemyjenkins给容器起一个别名
dockerrun-d-p10240:8080-p10241:50000-v/var/jenkins_mount:/var/jenkins_home-v/etc/localtime:/etc/localtime--privileged=true--namemyjenkinsjenkins/jenkins4.查看jenkins是否启动成功,如下图出现端口号,就为启动成功了
dockerps-l5.查看docker容器日志。
dockerlogsmyjenkins6.配置镜像加速,进入cd/var/jenkins_mount/目录。
cd/var/jenkins_mount/修改vihudson.model.UpdateCenter.xml里的内容
7.访问Jenkins页面,输入你的ip加上10240
vi/var/jenkins_mount/secrets/initialAdminPassword9.到此以全部安装成功,尽情的使用吧!
2、创建文件夹并初始化git仓库
3、提交代码到本地仓库
1、注册一个github账号【账号和密码要记得】
2、配置本地git与远程git的互信关系
12在本地生成一个密钥对ssh-keygen-trsa-C"inet_ygssoftware@163.com"3、创建一个仓库然后在Git仓库配置推送所需信息
123gitadd.##添加当前项目所有新的或者修改后的文件到暂存区gitcommit-m"首次提交"###把所有暂存区的数据提交到本地仓gitpush-uorginmaster###首次推送的时候需要,后面直接写gitpush即可配Jenkins拉取Github中的源码1、创建“自由风格的项目”
2、配置私人密钥
注意:首次生成的时候token能看见,但是后面就看不见了,生成后必须记录下token一遍后面直接使用
3、配置github仓库
1、在刚刚配置的项目中找到gitee的webhook地址
2、在gitee对应的项目中配置gitee的webhook
3、测试是否正确
1、安装Docker插件2、开启Docker的tcp访问端口
vi/lib/systemd/system/docker.service##修改ExecStart##修改前ExecStart=/usr/bin/dockerd-Hfd://--containerd/var/run/containerd/containerd.sock##修改后ExecStart=/usr/bin/dockerd-Hfd://-Htcp://0.0.0.0:2376-Hunix:///var/run/docker.sock--containerd/var/run/containerd/containerd.sock##重新加载docker配置文件及重启Dockersystemctldeamon-reload&&systemctlrestartdocker3、在Dokcer插件中配置连接2376端口
4、在我的视图中配置使用Docker容器
5、查看镜像是否推送到harbor仓库
1、在部署的服务器的docker配置文件添加镜像私服地址
在进行本次演练的过程中,我也是踩了不少的坑。比如自己构建镜像的那一块,我就是由于对Dockerfile的配置没有写正确,导致镜像打包过程中总是报错。后面还是向社区的朋友请教了之后才磕磕碰碰的发布成功。