配色: 字号:
Docker
2021-07-31 | 阅:  转:  |  分享 
  
Docker简单来说,Docker提供了一种程序运行的容器,同时保证这些容器相互隔离。不同于虚拟机的方式,Docker依赖于Linux自带的
LXC(LinuxContainers)技术。LXC利用了Linux可以对进程做内存、CPU、网络隔离的特性。Docker镜像不
需要新启动一个操作系统,因此提供了一种轻量级的打包和运行程序的方式。而且Docker能够直接访问硬件,从而使它的I/O操作比虚拟机
要快得多。正确看待OpenStack、KVM、Docker的方式应该是:OpenStack用于管理整个数据中心,KVM和Doc
ker作为相应的补充,KVM用于多租户的计算资源管理,DockerContainer用于应用程序的打包部署。容器和虚拟机。Lin
ux容器是Linux发展出的另一种虚拟化技术,简单来讲,Linux容器不是模拟一个完整的操作系统,而是对进程进行隔离,相
当于是在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。Docker
将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上
运行一样。有了Docker,就不用担心环境问题。总体来说,Docker的接口相当简单,用户可以方便地创建和使用容器,把自己的应
用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。Docker的优缺点Docker启动快速属于秒级
别。虚拟机通常需要几分钟去启动。Docker需要的资源更少。Docker在操作系统级别进行虚拟化,Docker容器和内核交互
,几乎没有性能损耗,性能优于通过Hypervisor层与内核层的虚拟化。Docker更轻量。Docker的架构可以共用一个
内核与共享应用程序库,所占内存极小。同样的硬件环境,Docker运行的镜像数远多于虚拟机数量,对系统的利用率非常高。与虚拟机相比
,Docker隔离性更弱。Docker属于进程之间的隔离,虚拟机可实现系统级别隔离。安全性。Docker的安全性也更弱,Do
cker的租户Root和宿主机Root等同,一旦容器内的用户从普通用户权限提升为Root权限,它就直接具备了宿主机的
Root权限,进而可进行无限制的操作。虚拟机租户Root权限和宿主机的Root虚拟机权限是分离的,并且虚拟机利用如I
ntel的VT-d和VT-x的ring-1硬件隔离技术。这种隔离技术可以防止虚拟机突破和彼此交互,而容器至今还没有任
何形式的硬件隔离,这使得容器容易受到攻击。可管理性。Docker的集中化管理工具还不算成熟。各种虚拟化技术都有成熟的管理工具,例如
VMwarevCenter提供完备的虚拟机管理能力。高可用和可恢复性。Docker对业务的高可用支持是通过快速重新部署实现
的。虚拟化具备负载均衡,高可用,容错,迁移和数据保护等经过生产实践检验的成熟保障机制,VMware可承诺虚拟机99.999%
高可用,保证业务连续性。快速创建、删除。虚拟化创建是分钟级别的,Docker容器创建是秒级别的,Docker的快速迭代性,决
定了无论是开发、测试、部署都可以节约大量时间交付、部署。虚拟机可以通过镜像实现环境交付的一致性,但镜像分发无法体系化。Docker
在Dockerfile中记录了容器构建过程,可在集群中实现快速分发和快速部署。Docker的三个基本概念Image(镜像)
Container(容器)Repository(仓库)镜像是Docker运行容器的前提,仓库是存放镜像的场所,可见镜像更是D
ocker的核心。Image(镜像):Docker镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配
置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被
改变。镜像就是一堆只读层的统一视角,也许这个定义有些难以理解,下面的这张图能够帮助读者理解镜像的定义:从左边我们看到了多个只读层,
它们重叠在一起。除了最下面一层,其他层都会有一个指针指向下一层。这些层是Docker内部的实现细节,并且能够在主机的文件系统上
访问到。统一文件系统(UnionFileSystem)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角。这样
就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。我们可以在图片的右边看到这个视角的形式。Container(容器):容器
(的定义和镜像乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。由于容器的定义并没有提及是否要运行容器
,所以实际上,容器=镜像+读写层。Repository(仓库):仓库是集中存放镜像文件的场所。镜像构建完成后,可以很容易的
在当前宿主上运行。但是,如果需要在其他服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,DockerRegistr
y(仓库注册服务器)就是这样的服务。有时候会把仓库(Repository)和仓库注册服务器(Registry)混为一谈,并不严格区
分。Docker仓库的概念跟Git类似,注册服务器可以理解为GitHub这样的托管服务。实际上,一个DockerRegi
stry中可以包含多个仓库(Repository),每个仓库可以包含多个标签(Tag),每个标签对应着一个镜像。所以说,镜像仓库
是Docker用来集中存放镜像文件的地方,类似于我们之前常用的代码仓库。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就
常用于对应该软件的各个版本。我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以La
test作为默认标签。仓库又可以分为两种形式:Public(公有仓库)Private(私有仓库)DockerRegistry
公有仓库是开放给用户使用、允许用户管理镜像的Registry服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供
收费服务供用户管理私有镜像。除了使用公开服务外,用户还可以在本地搭建私有DockerRegistry。Docker官方提供了
DockerRegistry镜像,可以直接使用做为私有Registry服务。当用户创建了自己的镜像之后就可以使用Pus
h命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上Pull下来就可以了。我们从下图可
以直观地看到Docker的架构:Docker使用C/S结构,即客户端/服务器体系结构。Docker客户端与Docker服
务器进行交互,Docker服务端负责构建、运行和分发Docker镜像。Docker客户端和服务端可以运行在一台机器上,也可以通
RESTful、Stock或网络接口与远程Docker服务端进行通信。这张图展示了Docker客户端、服务端和Dock
er仓库(即DockerHub和DockerCloud),默认情况下Docker会在Docker中央仓库寻找
镜像文件。这种利用仓库管理镜像的设计理念类似于Git,当然这个仓库是可以通过修改配置来指定的,甚至我们可以创建我们自己的私有仓
库。Docker的安装和使用Docker的安装和使用有一些前提条件,主要体现在体系架构和内核的支持上。对于体系架构,除了Doc
ker一开始就支持的X86-64,其他体系架构的支持则一直在不断地完善和推进中。Docker分为CE和EE两大版本。CE即社
区版,免费支持周期7个月;EE即企业版,强调安全,付费使用,支持周期24个月。我们在安装前可以参看官方文档获取最新的Docker
支持情况,官方文档在这里:https://docs.docker.com/install/Docker对于内核支持的功能,即内核
的配置选项也有一定的要求(比如必须开启Cgroup和Namespace相关选项,以及其他的网络和存储驱动等)。Docker源码中
提供了一个检测脚本来检测和指导内核的配置,脚本链接在这里:https://raw.githubusercontent.com/do
cker/docker/master/contrib/check-config.sh在满足前提条件后,安装就变得非常的简单了。Do
ckerCE的安装请参考官方文档:MacOS:https://docs.docker.com/docker-for-mac/i
nstall/Windows:https://docs.docker.com/docker-for-windows/install
/Ubuntu:https://docs.docker.com/install/linux/docker-ce/ubuntu/De
bian:https://docs.docker.com/install/linux/docker-ce/debian/CentO
S:https://docs.docker.com/install/linux/docker-ce/centos/Fedora:h
ttps://docs.docker.com/install/linux/docker-ce/fedora/其他Linux发行
版:https://docs.docker.com/install/linux/docker-ce/binaries/这里我们以
CentOS7作为演示。环境准备:阿里云服务器(1核2G,1M带宽)CentOS7.464位由于Docker-C
E支持64位版本的CentOS7,并且要求内核版本不低于3.10,首先我们需要卸载掉旧版本的Docker:$?sudo?yum
?remove?docker??docker-client??docker-client-latest??docker-commo
n??docker-latest??docker-latest-logrotate??docker-logrotate??dock
er-selinux??docker-engine-selinux??docker-engine我们执行以下安装命令去安装依赖包:
$?sudo?yum?install?-y?yum-utils??device-mapper-persistent-data??l
vm2这里我事先已经安装过了,所以提示我已经安装了最新版本:安装DockerDocker软件包已经包括在默认的CentOS-
Extras软件源里。因此想要安装Docker,只需要运行下面的yum命令:$?sudo?yum?install?dock
er当然在测试或开发环境中Docker官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS系统上可以使用这套脚本安装
:curl?-fsSL?get.docker.com?-o?get-docker.shsh?get-docker.sh具体可以参看
docker-install的脚本:https://github.com/docker/docker-install执行这个命
令后,脚本就会自动的将一切准备工作做好,并且把DockerCE的Edge版本安装在系统中。安装完成后,运行下面的命令,验
证是否安装成功:docker?versionordocker?info返回Docker的版本相关信息,证明Docker安装
成功:启动Docker-CE:$?sudo?systemctl?enable?docker$?sudo?systemctl?st
art?dockerDocker的简单运用HelloWorld由于服务器日常崩溃了,Docker出了点问题,所以以下案例
的演示是基于KaliLinux环境下进行的。我们通过最简单的Image文件HelloWorld,感受一下Docke
r的魅力吧!我们直接运行下面的命令,将名为hello-world的image文件从仓库抓取到本地:docker?pull
?library/hello-worlddockerpullimages是抓取image文件,library/hello
-world是image文件在仓库里面的位置,其中library是image文件所在的组,hello-world是
image文件的名字。抓取成功以后,就可以在本机看到这个image文件了:docker?images我们可以看到如下结果:
现在,我们可以运行hello-world这个image文件:docker?run?hello-world我们可以看到如下结
果:输出这段提示以后,helloworld就会停止运行,容器自动终止。有些容器不会自动终止,因为提供的是服务,比如MySQL
镜像等。是不是很Easy呢?我们从上面可以看出,Docker的功能是十分强大的,除此之外,我们还可以拉取一些Ubuntu
,Apache等镜像,在未来的教程中我们将会一一提到。Docker提供了一套简单实用的命令来创建和更新镜像,我们可以通过网络直
接下载一个已经创建好了的应用镜像,并通过DockerRUN命令就可以直接使用。当镜像通过RUN命令运行成功后,这个运行的
镜像就是一个Docker容器啦。容器可以理解为一个轻量级的沙箱,Docker利用容器来运行和隔离应用,容器是可以被启动、停止
、删除的,这并不会影响Docker镜像。我们可以看看下面这幅图:Docker客户端是Docker用户与Docker交
互的主要方式。当您使用Docker命令行运行命令时,Docker客户端将这些命令发送给服务器端,服务端将执行这些命令。Doc
ker命令使用DockerAPI。Docker客户端可以与多个服务端进行通信。我们将剖析一下Docker容器是如何工
作的,学习好Docker容器工作的原理,我们就可以自己去管理我们的容器了。Docker架构在上面的学习中,我们简单地讲解了
Docker的基本架构。了解到了Docker使用的是C/S结构,即客户端/服务器体系结构。明白了Docker客户端与
Docker服务器进行交互时,Docker服务端负责构建、运行和分发Docker镜像。知道了Docker客户端和服务
端可以运行在一台机器上,我们可以通过RESTful、Stock或网络接口与远程Docker服务端进行通信。我们从下图可以
很直观的了解到Docker的架构:Docker的核心组件包括:DockerClientDockerDaemonDocke
rImageDockerRegistryDockerContainerDocker采用的是Client/Server架
构。客户端向服务器发送请求,服务器负责构建、运行和分发容器。客户端和服务器可以运行在同一个Host上,客户端也可以通过Soc
ket或RESTAPI与远程的服务器通信。可能很多朋友暂时不太理解一些东西,比如RESTAPI是什么东西等,不过没关
系,在后面的文章中会一一给大家讲解清楚。DockerClientDockerClient,也称Docker客户端。它其实
就是Docker提供命令行界面(CLI)工具,是许多Docker用户与Docker进行交互的主要方式。客户端可以构建,
运行和停止应用程序,还可以远程与Docker_Host进行交互。最常用的Docker客户端就是Docker命令,我们可
以通过Docker命令很方便地在Host上构建和运行Docker容器。DockerDaemonDockerDaem
on是服务器组件,以Linux后台服务的方式运行,是Docker最核心的后台进程,我们也把它称为守护进程。它负责响应来自
DockerClient的请求,然后将这些请求翻译成系统调用完成容器管理操作。该进程会在后台启动一个APIServer
,负责接收由DockerClient发送的请求,接收到的请求将通过DockerDaemon内部的一个路由分发调度,由具
体的函数来执行请求。我们大致可以将其分为以下三部分:DockerServerEngineJobDockerDaemon的架构
如下所示:DockerDaemon可以认为是通过DockerServer模块接受DockerClient的请求,并
在Engine中处理请求,然后根据请求类型,创建出指定的Job并运行。?DockerDaemon运行在Docker
Host上,负责创建、运行、监控容器,构建、存储镜像。运行过程的作用有以下几种可能:向DockerRegistry获取镜像
。通过graphdriver执行容器镜像的本地化操作。通过networkdriver执行容器网络环境的配置。通过exec
driver执行容器内部运行的执行工作。由于DockerDaemon和DockerClient的启动都是通过可执行文
件Docker来完成的,因此两者的启动流程非常相似。Docker可执行文件运行时,运行代码通过不同的命令行Flag参数,
区分两者,并最终运行两者各自相应的部分。启动DockerDaemon时,一般可以使用以下命令来完成:docker?--dae
mon?=?truedocker?–ddocker?–d?=?true再由Docker的main()函数来解析以上命令的相
应Flag参数,并最终完成DockerDaemon的启动。下图可以很直观地看到DockerDaemon的启动流程:
默认配置下,DockerDaemon只能响应来自本地Host的客户端请求。如果要允许远程客户端请求,需要在配置文件中打开
TCP监听。我们可以照着如下步骤进行配置:①编辑配置文件/etc/systemd/system/multi-user.targe
t.wants/docker.service,在环境变量ExecStart后面添加-Htcp://0.0.0.0,允许来自
任意IP的客户端连接。②重启DockerDaemon:systemctl?daemon-reloadsystemctl?r
estart?docker.service③我们通过以下命令即可实现与远程服务器通信:docker?-H?服务器IP地址?info
-H是用来指定服务器主机,info子命令用于查看Docker服务器的信息。DockerImageDocker镜像可以看
作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环
境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。我们可将Docker镜像看成只读模板,通过它可以创建
Docker容器。镜像有多种生成方法:从无到有开始创建镜像下载并使用别人创建好的现成的镜像在现有镜像上创建新的镜像我们可以将镜像
的内容和创建步骤描述在一个文本文件中,这个文件被称作Dockerfile,通过执行dockerbuild命令可以构建出D
ocker镜像。DockerRegistryDockerRegistry是存储DockerImage的仓库,它在D
ocker生态环境中的位置如下图所示:运行dockerpush、dockerpull、dockersearch时,实际
上是通过DockerDaemon与DockerRegistry通信。DockerContainerDocker容器
就是Docker镜像的运行实例,是真正运行项目程序、消耗系统资源、提供服务的地方。DockerContainer提供了系统
硬件环境,我们可以使用DockerImages这些制作好的系统盘,再加上我们所编写好的项目代码,Run一下就可以提供服务啦
。Docker组件是如何协作运行容器看到这里,我相信各位读者朋友们应该已经对Docker基础架构熟悉的差不多了,我们还记得运
行的第一个容器吗?现在我们再通过hello-world这个例子来体会一下Docker各个组件是如何协作的。容器启动过程如下
:Docker客户端执行dockerrun命令。DockerDaemon发现本地没有hello-world镜像。D
aemon从DockerHub下载镜像。下载完成,镜像hello-world被保存到本地。DockerDaemon
启动容器。具体过程可以看如下这幅演示图:我们可以通过DockerImages可以查看到hello-world已经下载到本
地:我们可以通过DockerPs?或者DockerContainerls?显示正在运行的容器,我们可以看到,hello-
world在输出提示信息以后就会停止运行,容器自动终止,所以我们在查看的时候没有发现有容器在运行。我们把Docker容器的工
作流程剖析的十分清楚了,我们大体可以知道Docker组件协作运行容器可以分为以下几个过程:Docker客户端执行docke
rrun命令。DockerDaemon发现本地没有我们需要的镜像。Daemon从DockerHub下载镜像。下载完
成后,镜像被保存到本地。DockerDaemon启动容器。了解了这些过程以后,我们再来理解这些命令就不会觉得很突兀了,下面我来
给大家讲讲Docker常用的一些命令操作吧。Docker常用命令我们可以通过docker-h去查看命令的详细的帮助文档
。在这里我只会讲一些日常我们可能会用的比较多的一些命令。例如,我们需要拉取一个Docker镜像,我们可以用如下命令:docke
r?pull?image_nameimage_name为镜像的名称,而如果我们想从DockerHub上去下载某个镜像,我们
可以使用以下命令:docker?pull?centos:latestcento:lastest是镜像的名称,DockerDae
mon发现本地没有我们需要的镜像,会自动去DockerHub上去下载镜像,下载完成后,该镜像被默认保存到/var/lib
/docker目录下。接着我们如果想查看主机下存在多少镜像,我们可以用如下命令:docker?images我们要想知道当前有哪些
容器在运行,我们可以用如下命令:docker?ps?-a-a是查看当前所有的容器,包括未运行的。我们该如何去对一个容器进行启动,
重启和停止呢?我们可以用如下命令:docker?start?container_name/container_iddocker?r
estart?container_name/container_iddocker?stop?container_name/cont
ainer_id这个时候我们如果想进入到这个容器中,我们可以使用attach命令:docker?attach?containe
r_name/container_id那如果我们想运行这个容器中的镜像的话,并且调用镜像里面的bash,我们可以使用如下命令:
docker?run?-t?-i?container_name/container_id?/bin/bash那如果这个时候,我们想
删除指定镜像的话,由于Image被某个Container引用(拿来运行),如果不将这个引用的Container销毁(删
除),那Image肯定是不能被删除。我们首先得先去停止这个容器:docker?psdocker?stop?container_
name/container_id然后我们用如下命令去删除这个容器:docker?rm?container_name/contai
ner_id然后这个时候我们再去删除这个镜像:docker?rmi?image_name此时,常用的Docker相关的命令就讲
到这里为止了,我们在后续的文章中还会反复地提到这些命令。Dockerfile是什么前面我们已经提到了Docker的一些基本概
念。以CTF的角度来看,我们可以去使用Dockerfile定义镜像,依赖镜像来运行容器,可以去模拟出一个真实的漏洞场景。因
此毫无疑问的说,Dockerfile是镜像和容器的关键,并且Dockerfile还可以很轻易的去定义镜像内容,说了这么多,
那么Dockerfile到底是个什么东西呢?Dockerfile是自动构建Docker镜像的配置文件,用户可以使用Do
ckerfile快速创建自定义的镜像。Dockerfile中的命令非常类似于Linux下的Shell命令。我们可以通过
下面这幅图来直观地感受下Docker镜像、容器和Dockerfile三者之间的关系:我们从上图中可以看到,Dockerfi
le可以自定义镜像,通过Docker命令去运行镜像,从而达到启动容器的目的。Dockerfile是由一行行命令语句组成,并
且支持已#开头的注释行。一般来说,我们可以将Dockerfile分为四个部分:基础镜像(父镜像)信息指令FROM。维护者
信息指令MAINTAINER。镜像操作指令RUN、EVN、ADD和WORKDIR等。容器启动指令CMD、ENTR
YPOINT和USER等。下面是一段简单的Dockerfile的例子:FROM?python:2.7MAINTAINER
?Angel_Kitty?angelkitty6698@gmail.com>COPY?.?/appWORKDIR?/appRUN?
pip?install?-r?requirements.txtEXPOSE?5000ENTRYPOINT?[''python'']CM
D?[''app.py'']我们可以分析一下上面这个过程:从DockerHub上Pull下Python2.7的基础镜像
。显示维护者的信息。Copy当前目录到容器中的/App目录下复制本地主机的(Dockerfile所在目录的相对路径)到
容器里。指定工作路径为/App。安装依赖包。暴露5000端口。启动App。这个例子是启动一个PythonFlaskA
pp的Dockerfile(Flask是Python的一个轻量的Web框架),相信大家从这个例子中能够稍微理解了
Dockfile的组成以及指令的编写过程。Dockerfile常用的指令根据上面的例子,我们已经差不多知道了Dockerf
ile的组成以及指令的编写过程,我们再来理解一下这些常用命令就会得心应手了。由于Dockerfile中所有的命令都是以下格式
:INSTRUCTIONargument,指令(INSTRUCTION)不分大小写,但是推荐大写和SQL语句是不是很相似呢
?下面我们正式来讲解一下这些指令集吧。FROMFROM是用于指定基础的images,一般格式为FROMorFORM:
。所有的Dockerfile都应该以FROM开头,FROM命令指明Dockerfile所创建的镜像文件以什么镜像为基
础,FROM以后的所有指令都会在FROM的基础上进行创建镜像。可以在同一个Dockerfile中多次使用FROM命令
用于创建多个镜像。比如我们要指定Python2.7的基础镜像,我们可以像如下写法一样:FROM?python:2.7MAIN
TAINERMAINTAINER是用于指定镜像创建者和联系方式,一般格式为MAINTAINER。这里我设置成我的ID和邮箱
:MAINTAINER?Angel_Kitty?angelkitty6698@gmail.com>COPYCOPY是用于复制本地
主机的(为Dockerfile所在目录的相对路径)到容器中的。当使用本地目录为源目录时,推荐使用COPY。一般格式为CO
PY。例如我们要拷贝当前目录到容器中的/app目录下,我们可以这样操作:COPY?.?/appWORKDIRWORKDIR用
于配合RUN,CMD,ENTRYPOINT命令设置当前工作路径。可以设置多次,如果是相对路径,则相对前一个WORKDIR命
令。默认路径为/。一般格式为WORKDIR/path/to/work/dir。例如我们设置/app路径,我们可以进行如下操
作:WORKDIR?/appRUNRUN用于容器内部执行命令。每个RUN命令相当于在原有的镜像基础上添加了一个改动层,原有的
镜像不会有变化。一般格式为RUN。例如我们要安装Python依赖包,我们做法如下:RUN?pip?install?-r?re
quirements.txtEXPOSEEXPOSE命令用来指定对外开放的端口。一般格式为EXPOSE[...]。例如上面那个
例子,开放5000端口:EXPOSE?5000ENTRYPOINTENTRYPOINT可以让你的容器表现得像一个可执行程序一样。
一个Dockerfile中只能有一个ENTRYPOINT,如果有多个,则最后一个生效。ENTRYPOINT命令也有两种格式
:ENTRYPOINT[''executable'',''param1'',''param2'']:推荐使用的Exec?形式。ENT
RYPOINTcommandparam1param2:Shell形式。例如下面这个,我们要将Python镜像变成可执
行的程序,我们可以这样去做:ENTRYPOINT?[''python'']CMDCMD命令用于启动容器时默认执行的命令,CMD命令
可以包含可执行文件,也可以不包含可执行文件。不包含可执行文件的情况下就要用ENTRYPOINT指定一个,然后CMD命令的参
数就会作为ENTRYPOINT的参数。CMD命令有三种格式:CMD[''executable'',''param1'',''para
m2'']:推荐使用的exec形式。CMD[''param1'',''param2'']:无可执行程序形式。CMDcommandp
aram1param2:Shell形式。一个Dockerfile中只能有一个CMD,如果有多个,则最后一个生效。而CM
D的Shell形式默认调用/bin/sh-c执行命令。CMD命令会被Docker命令行传入的参数覆盖:docke
rrunbusybox/bin/echoHelloDocker?会把CMD里的命令覆盖。例如我们要启动/app,
我们可以用如下命令实现:CMD?[''app.py'']当然还有一些其他的命令,我们在用到的时候再去一一讲解一下。构建Dockerf
ile我们大体已经把Dockerfile的写法讲述完毕,我们可以自己动手写一个例子:mkdir?static_webcd?st
atic_webtouch?Dockerfile然后viDockerfile?开始编辑该文件,输入i开始编辑。以下是我们
构建的Dockerfile内容:FROM?nginxMAINTAINER?Angel_Kitty?RUN?echo?\\''He
llo,?Docker!\\''?>?/usr/share/nginx/html/index.html编辑完后按esc退出编辑,
然后?:wq写入,退出。我们在Dockerfile文件所在目录执行:docker?build?-t?angelkitty/n
ginx_web:v1?.我们解释一下:-t是为新镜像设置仓库和名称angelkitty为仓库名nginx_web为镜像名:
v1为标签(不添加为默认latest)我们构建完成之后,使用DockerImages命令查看所有镜像,如果存在REP
OSITORY为Nginx和TAG是v1的信息,就表示构建成功。接下来使用dockerrun命令来启动容器:d
ocker?run?--name?nginx_web?-d?-p?8080:80?angelkitty/nginx_web:v1这
条命令会用Nginx镜像启动一个容器,命名为nginx_web,并且映射了8080端口。这样我们可以用浏览器去访问这个
Nginx服务器:http://localhost:8080/或者http://本机的IP地址:8080/,页面返回信
息:数据库不适合容器化的7大原因数据不安全即使你要把Docker数据放在主机来存储,它依然不能保证不丢数据。Docker
volumes的设计围绕UnionFS镜像层提供持久存储,但它仍然缺乏保证。使用当前的存储驱动程序,Docker仍然存在
不可靠的风险。如果容器崩溃并数据库未正确关闭,则可能会损坏数据。运行数据库的环境需求常看到DBMS容器和其他服务运行在同一主
机上。然而这些服务对硬件要求是非常不同的。数据库(特别是关系型数据库)对IO的要求较高。一般数据库引擎为了避免并发资源竞争
而使用专用环境。如果将你的数据库放在容器中,那么将浪费你的项目的资源。因为你需要为该实例配置大量额外的资源。在公有云,当你需要
34G内存时,你启动的实例却必须开64G内存。在实践中,这些资源并未完全使用。怎么解决?您可以分层设计,并使用固定资源来
启动不同层次的多个实例。水平伸缩总是比垂直伸缩更好。网络问题要理解Docker网络,您必须对网络虚拟化有深入的了解。也必须准
备应付好意外情况。你可能需要在没有支持或没有额外工具的情况下,进行bug修复。我们知道:数据库需要专用的和持久的吞吐量,以实现
更高的负载。我们还知道容器是虚拟机管理程序和主机虚拟机背后的一个隔离层。然而网络对于数据库复制是至关重要的,其中需要主从数据库间
24/7的稳定连接。未解决的Docker网络问题在1.9版本依然没有得到解决。把这些问题放在一起,容器化使数据库容器很难管理
。我知道你是一个较高级的工程师,什么问题都可以得到解决。但是,你需要花多少时间解决Docker网络问题?将数据库放在专用环境不
会更好吗?节省时间来专注于真正重要的业务目标。状态在Docker中打包无状态服务是很酷的,可以实现编排容器并解决单点故障问题。
但是数据库呢?将数据库放在同一个环境中,它将会是有状态的,并使系统故障的范围更大。下次您的应用程序实例或应用程序崩溃,可能会影
响数据库。数据库不适合使用主要的Docker功能考虑容器中的数据库,我们来思考它的价值。我们先看看Docker官方对其的定义
:Docker是为开发人员和系统管理员构建,分发和运行分布式应用程序的开放平台。Docker包括DockerEngine
(便携式,轻量级运行时和打包工具)以及DockerHub(用于共享应用程序和自动化工作流的云服务),Docker使应用程序能
够以组件快速组装,并消除开发,QA和生产环境之间的不同。因此,IT可以更快地分发程序,并在笔记本电脑,数据中心虚拟机和任何云
上运行相同的应用程序。根据该答案,我们可以很容易定义Docker的主要特性:易于构建新环境易于重新部署(持续集成)容易水平伸缩
(从实践得出)易于维护环境一致让我们开始思考这些功能如何适应数据库世界。容易设置数据库?让我们看看,容器化或者在本地运行数据库,
在运行上是否有巨大的差异。dockerrun-dmongod:3.4对比:sudoapt-keyadv--keyser
verhkp://keyserver.ubuntu.com:80--recv0C49F3730359A14518585931
BC711F9BA15703C6echo''deb[arch=amd64,arm64]http://repo.mongod
b.org/apt/ubuntuxenial/mongodb-org/3.4multiverse''|sudotee/e
tc/apt/sources.list.d/mongodb-org-3.4.listsudoapt-getupdate&&
sudoapt-getinstall-ymongodb-org易于构建新环境?如果我们谈论是MongoDB集群-可能
容器化效率更高。但是配置管理系统呢?它们旨在通过运行一个命令来解决配置问题。使用Ansible你可以轻松设置几十个Mongo
实例。正如你所看到的,没有显著的价值增长。容易重新部署?您重新部署数据库升级到下一个版本的频率是多少呢?数据库升级不是可用性问题
,而是工程问题(即在群集中的可用性)。想想你的应用程序将如何使用新的数据库引擎版本。引擎更换时可能导致的问题。容易水平伸缩?是否要
在多个实例之间共享数据目录?你不害怕直接数据并发问题和可能的数据损坏吗?使用专用数据环境部署多个实例不会更安全吗?最后搞一个主从复
制?易于维护环境一致?数据库实例环境的变化频率如何?每天升级操作系统吗?还是数据库版本或依赖软件变化频繁?或者是不容易与工程团队达
成共识?最后看来,没有一个特性足以让我考虑数据库容器化。额外的隔离对数据库是不利的其实我在第二点和第三点原因中提到了这一点。但我
把这个列为单独的原因,因为我想再次强调这一事实。我们拥有的隔离级别越多,我们获得的资源开销就越多。相比专用环境而言,容易水平伸
缩可以使我们得到更多的好处。然而在Docker中水平伸缩只能用于无状态计算服务,而不是数据库。我们没有看到任何针对数据库的隔
离功能,那为什么我们应该把它放在容器中?云平台的不适用性大部分人通过共有云开始项目。云简化了虚拟机操作和替换的复杂性,因此不需要
在夜间或周末没有人工作时间来测试新的硬件环境。当我们可以迅速启动一个实例的时候,为什么我们需要担心这个实例运行的环境?这就是为什么
我们向云提供商支付很多费用的原因。当我们为实例放置数据库容器时,上面说的这些便利性就不存在了。因为数据不匹配,新实例不会与现有的
实例兼容,如果要限制实例使用单机服务,应该让DB使用非容器化环境,我们仅仅需要为计算服务层保留弹性扩展的能力。这7点适用于
所有数据库吗?也许不是全部,但是应该是一切需要持久化数据的数据库,以及所有具有特殊硬件环境要求的数据库。如果我们使用Redis
作为缓存或用户会话存储-使用容器就不应该有任何问题。因为不需要保证该数据落地,那么数据没有丢失的风险。但是如果我们考虑使用Re
dis作为一个持久的数据存储,那么你较好把数据放在容器外面,即使您不断刷新RDB快照,在快速变化的计算集群中找到这个快照也会
很复杂。我们还可以谈谈容器内的Elasticsearch。我们可以存储在ES中的索引,并且可以从持久性数据源重建它们。但是看
看要求!默认情况下,Elasticsearch需要2到3GB的内存。由于Java的GC,内存使用并不一致。您确定E
lasticsearch适合用于资源限制的容器吗?让不同的Elasticsearch实例使用不同的硬件配置不是更好吗?不要担
心本地开发环境的数据库容器化。将数据库放在本地环境的容器中,你将节省大量的时间和精力。你将能够复制生产环境操作系统。原生Postg
resforOSX或Windows不是100%兼容Linux版本。在主机操作系统上设置容器而不是软件包,你会克服这种问题。D
ocker简介Docker是开源应用容器引擎,轻量级容器技术。基于Go语言,并遵循Apache2.0协议开源Docker可以让开发
者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux系统上,也可以实现虚拟化容器完全使用沙箱技术
,相互之间不会有任何接口类似于虚拟机技术,但docker直接运行在操作系统上,而不是运行在虚拟机中,速度快,性能开销极低白话文,简
介就是:Docker支持将软件编译成一个镜像,然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以直接使用这个镜像。运行中
的这个镜像称为容器,容器启动是非常快速的。类似windows里面的ghost操作系统,安装好后什么都有了。Docker核心概念d
ocker镜像(Images):Docker镜像是用于创建Docker容器的模板docker容器(Container):镜像启动后
的一个实例称为容器,容器是独立运行的一个或一组应用,docker客户端(Client):客户端通过命令行或其他工具使用Docker
API(https://docs.docker.com/reference/api/docker_remote_api)与Doc
ker的守护进程进行通信docker主机(Host):一个物理或虚拟的机器用来执行Docker守护进程和容器docker仓库(Re
gistry):Docker仓库用来存储镜像,可以理解为代码控制中的代码仓库,DockerHub(https://hub.doc
ker.com)提供了庞大的镜像集合供使用Docker安装及启停查看centos版本Docker要求CentOS系统的内核
版本高于3.10通过命令:uname-r查看当前centos版本,如版本不符,需升级系统版本升级软件包及内核(可选)yumu
pdate安装dockeryuminstalldocker启动dockersystemctlstartdocker将doc
ker服务设为开机启动systemtctlenabledocker停止dockersystemtctlstopdocker
Docker常用命令及操作docker镜像命令通常情况下,Docker的镜像都放在Docker的官网DockerHub上,点此
前往官网镜像检索除了可以在DockerHub上搜索镜像外,还可以通过命令dockersearchxxx进行搜索,下面以
mysql为例:dockersearchmysql结果如下:镜像下载下载命名为:dockerpull镜像名:tag,其中
tag多为系统的版本,可选的,默认为least。dockerpullmysql镜像列表获取已下载镜像列表命令:dockeri
mages其中,RESPOSITORY为镜像名TAG为镜像版本,least代表最新版IMAGE_ID为该镜像唯一IDCREATED
为该镜像创建时间SIZE为该镜像大小镜像删除删除指定镜像:dockerrmiimage-id删除所有镜像:dockerrmi
$(dockerimages-q)容器操作可以理解为软件下载(下载QQ)-->安装(QQ)-->运行(QQ)的过程。下面以T
omcat为例搜索镜像dockersearchtomcat下载镜像dockerpulltomcat根据镜像启动容器最简单
的运行镜像为容器的命令如下:dockerrun--namecontainer-name-dimage-name运行一个容
器,使用dockerrun命令即可。--name:为容器起一个名称-d:detached,执行完这句命令后,控制台将不会阻塞,
可以继续输入命令操作image-name:要运行的镜像名称查看运行中容器可通过如下命令,查看运行中的容器列表:dockerpsC
ONTAINERID:启动时生成的IDIMAGE:该容器使用的镜像COMMAND:容器启动时执行的命令CREATED:容器创建时
间STATUS:当前容器状态PORTS:当前容器所使用的默认端口号NAMES:启动时给容器设置的名称停止运行中容器通过以下命令来停
止运行中的容器:dockerstopcontainer-name/container-id查看所有的容器通过以下命令可查看运行
和停止的所有容器:dockerps-a启动容器通过以下命令启动容器:dockerstartcontainer-name/c
ontainer-id删除容器删除单个容器:dockerrmcontainer-id删除所有容器:dockerrm$(do
ckerps-a-q)启动做端口映射的容器Docker运行容器之后却发现没IP,没端口,也就是说,启动容器的时候如果不指定
对应参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。所以需要通过Docker端口映射来实现网络访问。Docker的
端口映射通过-p参数实现,命令如下:dockerrun--nametomcat1-dtomcatdockerrun-
-nametomcat2-d-p8888:8080tomcat如上,就把主机端口8888请求映射到Docker容器内部端
口8080了。执行完这两条命令后,通过dockerps查看:通过PORTS可以看出,tomcat2是做了端口映射的,tomcat
1是没进行映射过的。分别通过浏览器访问:http://...:8080///tomcat1默认端口http://.
..:8888///做过端口映射的Tomcat2,8888会转发请求到tomcat2的8080结果如下:第一个请求是无法请
求到的,原因开篇处说过了。第二个请求是可以正常进行请求的,会由tomcat2容器进行处理端口映射格式:ip:hostport:co
ntainerport#指定ip、指定主机port、指定容器portip::containerport#指定ip、未指定主机por
t、指定容器porthostport:container#未指定ipport、指定主机port、指定容器port查看容器日志查看
当前容器日志,可通过如下命令:dockerlogscontainer-id/container-name查看端口映射可以通过如
下命令查看容器映射了哪些端口及协议:dockerportcontainer-id示例:[root@docker~]#dock
erport46114af6b44e8080/tcp->0.0.0.0:8888[root@docker~]#docke
rportcea668ee4db0如果返回空,则代表没进行端口映射。登录退出容器运行中的容器其实是一个功能完备的Linux操作
系统,所以我们可以像常规系统一样进行登陆及退出操作。登录命令为:dockerexec-itcontainer-id/cont
ainer-namebash退出命令为:exit更多操作命令更多命令可以参考:https://docs.docker.com/e
ngine/reference/commandline/docker/https://docs.docker.com/engine
/reference/commandline/docker/Dockerfile使用Dockerfile是Docker用来构建镜像
的文本文件,包括自定义的指令和格式。可以通过dockerbuild命令从Dockerfile中构建镜像。用户可以通过统一的语法命
令来根据需求进行配置,通过这份统一的配置文件,在不同的文件上进行分发,需要使用时就可以根据配置文件进行自动化构建,这解决了开发人员
构建镜像的复杂过程。Dockerfile描述了组装对象的步骤,其中每条指令都是单独运行的。除了FROM指令,其他每条命令都会在上一
条指令所生成镜像的基础上执行,执行完后会生成一个新的镜像层,新的镜像层覆盖在原来的镜像之上从而形成了新的镜像。Dockerfile
所生成的最终镜像就是在基础镜像上面叠加一层层的镜像层组建的。Dockerfile指令Dockerfile的基本格式如下:#?Com
mentINSTRUCTION?arguments在Dockerfile中,指令(INSTRUCTION)不区分大小写,但是为了与
参数区分,推荐大写。Docker会顺序执行Dockerfile中的指令,第一条指令必须是FROM指令,它用于指定构建镜像的基础镜像
。在Dockerfile中以#开头的行是注释,而在其他位置出现的#会被当成参数。Dockerfile中的指令有FROM、MAINT
AINER、RUN、CMD、EXPOSE、ENV、ADD、COPY、ENTRYPOING、VOLUME、USER、WORKDIR、
ONBUILD,错误的指令会被忽略。下面将详细讲解一些重要的Docker指令。FROM格式:FROM或者FR
OM:FROM指令的功能是为后面的指令提供基础镜像,因此Dockerfile必须以FROM指令作为第一条
非注释指令。从公共镜像库中拉取镜像很容易,基础镜像可以选择任何有效的镜像。在一个Dockerfile中FROM指令可以出现多次,这
样会构建多个镜像。tag的默认值是latest,如果参数image或者tag指定的镜像不存在,则返回错误。ENV格式:ENV<
key>或者ENV=...ENV指令可以为镜像创建出来的容器声明环境变量。并且在D
ockerfile中,ENV指令声明的环境变量会被后面的特定指令(即ENV、ADD、COPY、WORKDIR、EXPOSE、VOL
UME、USER)解释使用。其他指令使用环境变量时,使用格式为$variable_name或者${variable_name}。如
果在变量面前添加斜杠\可以转义。如\$foo或者\${foo}将会被转换为$foo和${foo},而不是环境变量所保存的值。另外,
ONBUILD指令不支持环境替换。COPY格式:COPYCOPY指令复制所指向的文件或目录,将它添加到
新镜像中,复制的文件或目录在镜像中的路径是所指定的源可以有多个,但必须是上下文根目录中的相对路径。不能只用
形如COPY../something/something这样的指令。此外,可以使用通配符指向所有匹配通配符的文件或
目录,例如,COPYhome/mydir/表示添加所有以''hom''开头的文件到目录/mydir/中。可以是文件
或目录,但必须是目标镜像中的绝对路径或者相对于WORKDIR的相对路径(WORKDIR即Dockerfile中WORKDIR指令指
定的路径,用来为其他指令设置工作目录)。若以反斜杠/结尾则其指向的是目录;否则指向文件。同理。若
是一个文件,则的内容会被写到中;否则指向的文件或目录中的内容会被复制添加到目录中。当<
src>指定多个源时,必须是目录。如果不存在,则路径中不存在的目录会被创建。ADD格式:ADD
ADD与COPY指令在功能上很相似,都支持复制本地文件到镜像的功能,但ADD指令还支持其他功能。可以是指向
网络文件的URL,此时若指向一个目录,则URL必须是完全路径,这样可以获得网络文件的文件名filename,该文件会被
复制添加到/。比如ADDhttp://example.com/config.property
/会创建文件/config.property。还可以指向一个本地压缩归档文件,该文件会在复制到容器时会被解压提取,如A
DDsxample.tar.xz/。但是若URL中的文件为归档文件则不会被解压提取。ADD和COPY指令虽然功能相似,但一
般推荐使用COPY,因为COPY只支持本地文件,相比ADD而言,它更加透明。EXPOSE格式:EXPOSE[ort>/...]EXPOSE指令通知Docker该容器在运行时侦听指定的网络端口。可以指定端口是侦听TCP还
是UDP,如果未指定协议,则默认值为TCP。这个指令仅仅是声明容器打算使用什么端口而已,并不会自动在宿主机进行端口映射,可以在运行
的时候通过docker-p指定。EXPOSE?80/tcpEXPOSE?80/udpUSER格式:USER[:<
group]或者USER[:]USER指令设置了username和usergroup(可选)。在它之
后的RUN,CMD以及ENTRYPOINT指令都会以设置的user来执行。WORKDIR格式:WORKDIR/path/to/
workdirWORKDIR指令设置工作目录,它之后的RUN、CMD、ENTRYPOINT、COPY以及ADD指令都会在这个工作目
录下运行。如果这个工作目录不存在,则会自动创建一个。WORKDIR指令可在Dockerfile中多次使用。如果提供了相对路径,则它
将相对于上一个WORKDIR指令的路径。例如WORKDIR?/aWORKDIR?bWORKDIR?cRUN?pwd输出结果是/a
/b/cRUN格式1:RUN(shell格式)格式2:RUN[''executable'',''param
1'',''param2''](exec格式,推荐使用)RUN指令会在前一条命令创建出的镜像的基础上创建一个容器,并在容器中运行命令
,在命令结束运行后提交容器为新镜像,新镜像被Dockerfile中的下一条指令使用。RUN指令的两种格式表示命令在容器中的两种运行
方式。当使用shell格式时,命令通过/bin/sh-c运行。当使用exec格式时,命令是直接运行的,容器不调用shell程序,
即容器中没有shell程序。exec格式中的参数会被当成JSON数组被Docker解析,故必须使用双引号而不能使用单引号。因为ex
ec格式不会在shell中执行,所以环境变量的参数不会被替换。比如执行RUN[''echo'',''$HOME'']指令时,$HOME
不会做变量替换。如果希望运行shell程序,指令可以写成RUN[''/bin/bash'',''-c'',''echo'',''$HO
ME'']。CMDCMD指令有3种格式。格式1:CMD(shell格式)格式2:CMD[''executabl
e'',''param1'',''param2''](exec格式,推荐使用)格式3:CMD[''param1'',''param2'']
(为ENTRYPOINT指令提供参数)CMD指令提供容器运行时的默认值,这些默认值可以是一条指令,也可以是一些参数。一个Dock
erfile中可以有多条CMD指令,但只有最后一条CMD指令有效。CMD[''param1'',''param2'']格式是在CMD
指令和ENTRYPOINT指令配合时使用的,CMD指令中的参数会添加到ENTRYPOING指令中.使用shell和exec格式时,
命令在容器中的运行方式与RUN指令相同。不同之处在于,RUN指令在构建镜像时执行命令,并生成新的镜像;CMD指令在构建镜像时并不执
行任何命令,而是在容器启动时默认将CMD指令作为第一条执行的命令。如果用户在命令行界面运行dockerrun命令时指定了命令参数
,则会覆盖CMD指令中的命令。ENTRYPOINTENTRYPOINT指令有两种格式。格式1:ENTRYPOINTd>(shell格式)格式2:ENTRYPOINT[''executable'',''param1'',''param2''](ex
ec格式,推荐格式)ENTRYPOINT指令和CMD指令类似,都可以让容器在每次启动时执行相同的命令,但它们之间又有不同。一个Do
ckerfile中可以有多条ENTRYPOINT指令,但只有最后一条ENTRYPOINT指令有效。当使用Shell格式时,ENTR
YPOINT指令会忽略任何CMD指令和dockerrun命令的参数,并且会运行在bin/sh-c中。这意味着ENTRYPOIN
T指令进程为bin/sh-c的子进程,进程在容器中的PID将不是1,且不能接受Unix信号。即当使用dockerstopontainer>命令时,命令进程接收不到SIGTERM信号。推荐使用exec格式,使用此格式时,dockerrun传入的命令参
数会覆盖CMD指令的内容并且附加到ENTRYPOINT指令的参数中。从ENTRYPOINT的使用中可以看出,CMD可以是参数,也可
以是指令,而ENTRYPOINT只能是命令;另外,dockerrun命令提供的运行命令参数可以覆盖CMD,但不能覆盖ENTRYP
OINT。Dockerfile实践心得使用标签给镜像打上标签,有利于帮助了解进镜像功能谨慎选择基础镜像选择基础镜像时,尽量选择当前
官方镜像库的肩宽,不同镜像的大小不同,目前Linux镜像大小由如下关系:busyboxbuntu同时在构建自己的Docker镜像时,只安装和更新必须使用的包。此外相比Ubuntu镜像,更推荐使用Debian镜像,因为
它非常轻量级(目前其大小是在100MB以下),并且仍然是一个完整的发布版本。充分利用缓存Dockerdaemon会顺序执行Doc
kerfile中的指令,而且一旦缓存失效,后续命令将不能使用缓存。为了有效地利用缓存,需要保证指令的连续性,尽量将所有Docker
file文件相同的部分都放在前面,而将不同的部分放到后面。正确使用ADD与COPY命令当在Dockerfile中的不同部分需要用到
不同的文件时,不要一次性地将这些文件都添加到镜像中去,而是在需要时添加,这样也有利于重复利用docker缓存。另外考虑到镜像大小问
题,使用ADD指令去获取远程URL中的压缩包不是推荐的做法。应该使用RUNwget或RUNcurl代替。这样可以删除解压后不在
需要的文件,并且不需要在镜像中在添加一层。错误做法:ADD?http://example.com/big.tar.xz?/usr/
src/things/RUN?tar?-xJf?/usr/src/things/big.tar.xz?-C?/usr/src/th
ingsRUN?make?-C?/usr/src/things?all正确的做法:RUN?mkdir?-p?/usr/src/th
ings?\&&?curl?-SL?http://example.com/big.tar.xz?\????|?tar?-xJC?/
usr/src/things?\&&?make?-C?/usr/src/things?allRUN指令在使用较长的RUN指令时可以
使用反斜杠\分隔多行。大部分使用RUN指令的常见是运行apt-wget命令,在该场景下请注意以下几点。不要在一行中单独使用指令RU
Napt-getupdate。当软件源更新后,这样做会引起缓存问题,导致RUNapt-getinstall指令运行失败。所
以,RUNapt-getupdate和RUNapt-getinstall应该写在同一行。比如RUNapt-getup
date&&apt-getinstall-ypackage-1package-2package-3避免使用指令RUN
apt-getupgrade和RUNapt-getdist-upgrade。因为在一个无特权的容器中,一些必要的包会更
新失败。如果需要更新一个包(如package-1),直接使用命令RUNapt-getinstall-ypackage-1。
CMD和ENTRYPOINT命令CMD和ENTRYPOINT命令指定是了容器运行的默认命令,推荐二者结合使用。使用exec格式的E
NTRYPOINT指令设置固定的默认命令和参数,然后使用CMD指令设置可变的参数。比如下面这个例子:FROM?busyboxWOR
KDIR?/appCOPY?run.sh?/appRUN?chmod?+x?run.shENTRYPOINT?[''/app/run
.sh'']CMD?[''param1'']run.sh内容如下:#!/bin/shecho?''$@''运行后输出结果为param1,D
ockerfile中CMD和ENTRYPOINT的顺序不重要(CMD写在ENTRYPOINT前后都可以)。当在windows系统下
builddockerfile你可能会遇到这个问题standard_init_linux.go:207:?exec?user?p
rocess?caused?''no?such?file?or?directory''这是因为sh文件的fileformat是dos,
这里需要修改为unix,不需要下载额外的工具,一般我们机器上安装了git会自带gitbash,进入gitbash,使用vi编
辑,在命令行模式下修改(:setff=unix)。不要再Dockerfile中做端口映射使用Dockerfile的EXPOSE指
令,虽然可以将容器端口映射在主机端口上,但会破坏Docker的可移植性,且这样的镜像在一台主机上只能启动一个容器。所以端口映射应在
dockerrun命令中用-p参数指定。#?不要再Dockerfile中做如下映射EXPOSE?80:8080#?仅暴露80端
口,需要另做映射EXPOSE?80实践Dockerfile的写法Java服务的DockerFileFROM?openjdk:8-
jre-alpineENV?spring_profiles_active=devENV?env_java_debug_enable
d=falseEXPOSE?8080WORKDIR?/appADD?target/smcp-web.jar?/app/target
/smcp-web.jarADD?run.sh?/appENTRYPOINT?./run.sh可以看到基础镜像是openjdk,然
后设置了两个环境变量,服务访问端口是9090(意味着springboot应用中指定了server.port=8080),设置了工作
目录是/app。通过ENTRYPOINT设定了启动镜像时要启动的命令(./run.sh)。这个脚本中的内容如下:#!/bin/sh
#?Set?debug?options?if?requiredif?[?x''${env_java_debug_enabled}''?
!=?x?]?&&?[?''${env_java_debug_enabled}''?!=?''false''?];?then????java_debug_args=''-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005''fi#?ex:?env_jvm_flags=''-Xmx1200m?-XX:MaxRAM=1500m''?for?productionjava?$java_debug_args?$env_jvm_flags?-XX:+UnlockExperimentalVMOptions?-XX:+UseCGroupMemoryLimitForHeap?-jar?target/smcp-web.jar如果我们要指定jvm的一些参数,可以通过在环境变量中设置env_jvm_flags来指定。MavenDockerfilemaven的Dockerfile也写的很好,这里我发上来也给大家参考下FROM?openjdk:8-jdkARG?MAVEN_VERSION=3.6.3ARG?USER_HOME_DIR=''/root''ARG?SHA=c35a1803a6e70a126e80b2b3ae33eed961f83ed74d18fcd16909b2d44d7dada3203f1ffe726c17ef8dcca2dcaa9fca676987befeadc9b9f759967a8cb77181c0ARG?BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binariesRUN?mkdir?-p?/usr/share/maven?/usr/share/maven/ref?\??&&?curl?-fsSL?-o?/tmp/apache-maven.tar.gz?${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz?\??&&?echo?''${SHA}??/tmp/apache-maven.tar.gz''?|?sha512sum?-c?-?\??&&?tar?-xzf?/tmp/apache-maven.tar.gz?-C?/usr/share/maven?--strip-components=1?\??&&?rm?-f?/tmp/apache-maven.tar.gz?\??&&?ln?-s?/usr/share/maven/bin/mvn?/usr/bin/mvnENV?MAVEN_HOME?/usr/share/mavenENV?MAVEN_CONFIG?''$USER_HOME_DIR/.m2''COPY?mvn-entrypoint.sh?/usr/local/bin/mvn-entrypoint.shCOPY?settings-docker.xml?/usr/share/maven/ref/ENTRYPOINT?[''/usr/local/bin/mvn-entrypoint.sh'']CMD?[''mvn'']可以看到它是基于openjdk这个基础镜像来创建的,先去下载maven的包,然后进行了安装。然后又设置了MAVEN_HOME和MAVEN_CONFIG这两个环境变量,最后通过mvn-entrypoing.sh来进行了启动。前端服务的两阶段构建我有一个前端服务,目录结构如下:$?ls?frontend/myaccount/??resources/??third_party/myaccount目录下是放置的js,vue等,resources放置的是css,images等。third_party放的是第三方应用。这里采用了两阶段构建,即采用上一阶段的构建结果作为下一阶段的构建数据FROM?node:alpine?as?builderWORKDIR?''/build''COPY?myaccount?./myaccountCOPY?resources?./resourcesCOPY?third_party?./third_partyWORKDIR?''/build/myaccount''RUN?npm?installRUN?npm?rebuild?node-sassRUN?npm?run?buildRUN?ls?/build/myaccount/distFROM?nginxEXPOSE?80COPY?--from=builder?/build/myaccount/dist?/usr/share/nginx/html需要注意结尾的--from=builder这里和开头是遥相呼应的。33
献花(0)
+1
(本文系汉无为首藏)