《《容器化基础知识》课件.docx》由会员分享,可在线阅读,更多相关《《容器化基础知识》课件.docx(20页珍藏版)》请在课桌文档上搜索。
1、容器化基础知识1.基础知识随着云计算和大数据时代的到来,传统的服务器架构已经不能满足现代应用的需求,为了更好地支持应用程序的开发、测试、部署和运行,出现了很多新型的虚拟化技术,其中最流行的技术之一就是Docker容器技术。DoCker是一个开源的容器技术,它可以帮助开发者在物理机或虚拟机上创建、部署和运行应用程序,同时提供了更高的效率、可靠性和安全性。在Docker容器中,每个应用程序都是隔离的,可以依赖于不同的软件和库进行运行,这使得开发人员能够在同一台设备上运行多个应用程序,而不必担心它们之间的干扰、依赖和安全问题。1.1. Docker的核心概念和术语D。Cker容器技术是一个非常灵活和
2、可配置的系统,它包含了很多核心概念和术语,例如:镜像(image):Docker容器的基础模板,一个镜像包含了一个应用程序运行所需的所有文件、库和配置信息,可以用来创建一个或多个Docker容器。容器(Container):DOCker镜像的运行实例,一个容器可以被视为一个小型虚拟机,在其中运行单个应用程序,容器之间是独立且相互隔离的。仓库(registry):用于存储和管理Docker镜像的地方,DockerHub是最常用的公共仓库,但也可以搭建私有仓库。Dockerfile:通过编写一份Dockerfile文件,可以自动化构建一个Docker镜像,并配置应用程序的环境和依赖。DockerC
3、ompose:一种工具,可以通过编写一个docker-compose.yml文件来管理多个容器,并定义它们之间的网络和协作关系。1.2. 使用Docker的优势和注意事项D。Cker容器技术具有以下优势:1、容器具有不可变的特性。容器将操作系统、程序库、配置文件、路径和应用程序打包在一起运行,也就是说,我们在做QA测试的时候整个镜像是什么样,投入到产品环境以后就是什么样,其性能不会有彳不可差距。2、容器都非常轻量。单个容器的内存占用很小,不像其他进程动辄占用上万MB的内存空间,容器只会给主进程分配内存,可以有效降低系统开销。3、容器的速度更快。虚拟机的启动时间一般都在分钟级,容器的启动速度可以
4、达到秒级,启动容器就跟启动IinUX进程一样快。虽然容器的好处这么多,但是有很多用户还不了解,还认为容器跟一般的虚拟机没什么差别。实际上,容器是可销毁的,这是容器跟虚拟机之间最大的差别。容器的存在周期很短,只要用户使用完毕,就可以立即销毁容器,所以用朝生暮死来形容也不算过分。在使用Docker的过程中,需要注意以下事项:1)不要将数据储存在容器中。容器随时都可以停止、销毁或迁移,比方说,一个容器里运行的应用版本是1.0,我们分分钟就可以把这个应用升级到1.1同时还不会对数据造成任何影响。所以如果用户想要存数据的话,最好是用数据卷来存储。不过在用卷存数据的时候大家还是要注意一点,如果有两个容器共
5、用一个数据卷,都往里面写数据的话,是有可能造成程序崩溃的。我们在设计应用程序的时候应该考虑到这一点,为保万无一失,应用程序应该具备特定的机制,以确保在往共享数据存储区写入数据的时候不会出错。2)不要把应用程序分块交付。在部分用户看来,容器跟虚拟机没什么两样,所以有些人往往会把应用程序部署到当前运行的若干个容器中。这种做法在开发阶段没有太大的问题,因为做开发的时候我们会很频繁地进行部署和调试,但是到了持续交付(CD)阶段,下一步就是QA测试和正式投产了,这种做法就不太适合了。在这一阶段,我们应该充分考虑到容器的不可变特性,最好是将应用程序打包到一个镜像中交付。3)不要把镜像体积建得很大。镜像越大
6、,就越难发布。镜像中只包含必要的文件和Iibrary就可以了,能让应用或者进程运行起来就行。千万不要在镜像中安装些没必要的东西,在构建镜像的时候要避免使用yum这种update命令,免得系统自动下载很多不相干的文件到新镜像层中。4)建镜像的时候不要只建一层。大家都知道,Docker的文件系统是分层的,在建镜像的时候我们应该这么建,将操作系统单独建一层,作为基础镜像,然后用户名定义文件、运行时安装环境、配置文件都要分别建一层镜像,最后才是应用镜像层。这么做的话,我们以后重建、管理以及发布镜像的时候就要轻省得多了。换句话说就是创建镜像的时候不要用wdockercommit命令来创建。用这种办法建镜
7、像是完全不可取的,因为这种办法是不能重复的。我们在建镜像的时候应该从Dockerfile创建,或者用其他S2I(从源文件构建镜像)的方式来创建,这样镜像才具有可再生性,而且如果我们把镜像存在git之类提供版本控制能的系统里的话,还可以对Dockerfile的改动进行跟踪。6)给镜像打tag的时候不要只打TateSlatest其实就相当于Maven里头的快照。因为容器的文件系统是分层的,我们最好是给镜像多打几个tago如果只有latest的话,可能过段时间我们再来运行应用程序的时候就发现程序运行不起来了,因为应用的父层(就是Dockerfile里面的跟在FROM命令后面的那一层)被更新的版本覆盖
8、了,而新版本又不能向下兼容,还有可能就是从buildcache里面取镜像的时候取到了错的latest镜像。在产品环境中部署容器的时候也要避免使用latest,不然容易造成无法跟踪记录镜像版本的问题。7)不要在单个容器里面运行多个进程。容器本来就是用来运行单个应用的(比如httpdaemon,应用服务器,数据库等等),如果我们非要在一个容器里跑几个应用,那么在管理每个应用进程、存取日志、升级应用的时候就会很麻烦。8)不要把认证口令存在镜像中,用环境变量比较好。如果我们把用户名/密码值对存在镜像里的话,就只有采用硬编码的方式来挨个处理,估计这种麻烦事没人愿意去干。所以我们最好是用环境变量的方从容器
9、外部获取此类信息。Docker容器默认是以root权限运行的。不过随着技术的成熟,docker也会提供安全性更高的默认操作选项。在现有技术条件下,以root权限运行会对其他应用带来安全隐患,而且在有些运行环境下root权限是取不到的,所以我们在跑容器的时候应该用USER命令来指定非root权限的用户。10)不要过分依赖IP地址。每个容器都有一个内部IP,这个IP不是固定的,我们启动容器或者停止容器的时候IP都会变。如果我们要让应用或者微服务模块在容器之间进行通信的话,正确的做法是通过设置环境变量来传递主机名和端口号。其实看完这句话还是不明白究竟是啥的,下面就慢慢解释。不过长话短说的话,把他想象
10、成一个用了一种新颖方式实现的超轻量虚拟机,在大概效果上也是正确的。当然在实现的原理和应用上还是和VM有巨大差别的,并且专业的叫法是应用容器(ApplicationContainer2.最佳实践&容器编排在项目中应用最佳实践有以下优势: 提高安全性 优化Dockerimage的大小 充分利用Docker有用的功能编写易于维护的Dockerfile文件2L最佳实践1:使用官方的镜像尽可能使用官方和经过验证的Docker镜像作为基础镜像。如果你所在的团队技术比较强悍,有自己私有化的Dockerhubz保存了公司项目中使用的所有镜像,这些镜像包括构建项目使用的基础镜像以及使用中的项目镜像.基础镜像还是
11、建议使用Docker官方并经过验证的镜像,如果基于Dockerfile构建的项目镜像那么还是需要校验DOCkerimage的安全性等一系列的安全检查s.假设你正在开发一个NOde.js应用程序,并希望将其构建并作为DOCker镜像运行。最佳实践:不要使用基本操作系统(UbUntU、CentOS等)镜像并安装node.js、叩m和其他你的应用程序所需的工具,而是为你的应用程序使用官方的node镜像。不推荐FROMubunturunapt-getupdate&apt-getinstall-ynode&rm-rfvarlibaptlists*在这个Dockerfile中使用了官方的ubuntu镜像,
12、然后使用命令安装了node麟推荐FROMnode这个Dockerfile中我们使用官方提供的Node镜像说明:同样是官方的镜像,为什们不推荐使用ubuntu而是使用官方的node镜像?更干净的DOCkerfile,意味着dockerfile中的代码量更少,更清晰使用官方和经过验证的图像,这些镜像已经采用了最佳实践在官方DockerHub中,我们看到镜像名称后面带有DOCKEROFFICIALIMAGE标识的就是Docker官方的镜像22最佳实践2:使用特定的Docker镜像版本使用特定的Docker镜像版本在使用dockerimage的时候,我们已经选择了基础镜像,但是现在当我们从这个Dock
13、erfile构建应用程序镜像时,它将始终使用官方node镜像的latest标签O使用官方默认的latest标签会有问题呢?可能会得到一个与之前版本不同的图像版本,及时使用了latest标签,官方在不断的更新node镜像的内容,每次都构建了不同的镜像。新的镜像可能会有bug、或者不稳定的情况发生.软件开发中有个规则就是一般都不是用软件的最新版本(因为会有不同程度的问题)。latest标签是不可预测的,会导致意外的问题发生.所以,最好的做法就是使用固定版本的镜像,更好的做法是使用是我们的应用程序相匹配的镜像版本,规则就是:越具体越好不推荐FROMnode:latest在这个Dockerfile中使
14、用了官方的带有latest标签的镜像,latest意味着就是最新版本的镜像,存在不稳定,或者未发现的问题.推荐FROMnodexurrent-alpine3.18这个Dockerfile中我们使用官方提供的Node镜像并指定了版本号未node:slim这样我们在项目中使用的镜像就知道使用了镜像的那个版本,是否跟我们项目使用的node版本相匹配。2.3. 最佳实践3:使用更小的官方镜像使用更小的官方镜像选择Nodejs镜像时,我们会发现实际上有多个官方镜像可供选择。不仅版本号不同,而且还有不同的操作系统分发版:那问题是:我们应该选择哪一个镜像,它为什么很重要?61)镜像大小:如果镜像是基于像Ub
15、untu或Centos这样的完整操作系统发行版,那么镜像中已经打包了许多工具。因此,镜像大小会更大,但是在我们的应用程序镜像中并不需要大部分这些工具。相比之下,拥有较小的图像意味着在图像存储库中需要更少的存储空间,同时也需要更少的部署服务器空间。当从存储库拉取或推送图像时,当然可以更快地传输这些图像。2)安全问题:除此之外,由于内部安装了许多工具,我们需要考虑安全方面的问题。因为这样的基础镜像通常包含很多漏洞,从而给我们的应用镜像创建了一个更大的攻击面。这样一来,我们的应用中引入了不必要的安全问题!,正所谓要想少犯错,那么就让他少干活通过使用较小的图像和更精简的操作系统发行版进行比较,只安装必
16、要的系统工郸口库,可以最大限度地减少攻击面,并确保构建更安全的镜像。所以在这里最佳实践是选择一个基于更轻量级操作系统分发版本的图像,比如alpineoAlpine镜像具备启动容器应用所需的一切,但更加轻量级。对于大多数在DockerHUb上查看的镜像我们会看到一个带有alpine发行版标签的版本号。他是Docker容器中最常见和流行的基础镜像之一。2.4. 最佳实践4:优化构建镜像时的缓存优化构建镜像时的缓存在docker中,镜像层是什么,缓存和镜像层有什么关联呢?1)什么是镜像层(imagelayer)一个Docker镜像是基于Dockerfile构建的。在Dockerfile中,每个命令或
17、指令都会创建一个镜像层:FROMnode:CUrrem-alpine3.18WORKDIRappCOPYmyappappRUNnpminstallproductionCMDnnode,srcindex.jsw所以,当我们在上面的示例中使用nodealpine作为基础镜像时,它已经有了层级结构,因为它已经使用自己的Dockerfile进行构建。止匕外,在我们的Dockerfile中还有一些其他命令,每个命令都会向该镜像添加一个新的层级。2)什么是镜像缓存?在每一层中都会被Docker缓存。因此,当重新构建镜像时,如果Dockerfile没有更改,Docker将只使用缓存的层来构建镜像。这样构建的
18、速度就会更快,也会占用更少的存储空间.使用镜像缓存的优势有那些?:更快的构建镜像;更快的拉去和推送新的镜像到服务中.如果在拉取同一应用程序的新图像版本,并且假设在新版本中添加了1个新层:只有新增的层将被下载,其余部分已经由Docker本地缓存。3)优化缓存在Docker中一旦一个层发生变化,所有后续或下游的层也必须重新创建。换句话说:当我们改变了Dockerfile中的某一行内容时,所有后续行或层的缓存都会被破坏和失效。所以这里的规则和最佳实践是:在Dockerfile中,将我们的命令按照从最不经常变化到最经常变化的顺序进行排序,以利用缓存并优化镜像构建速度。2.5. 最佳实践5:使用doCk
19、erignore文件使用.dockerignore文件通常情况下,当我们构建镜像时,并不需要项目中的所有内容来运行应用程序。我们不需要自动生成的文件夹,比如targets或者build文件夹,也不需要readme文件等。那么我们如何防止这些内容出现在我们的应用程序图像中呢?答案就是使用dockerignore文件.这很简单。我们只需要创建一个名为.dockerignore的文件,然后列出所有要忽略的文件和文件夹,在构建镜像时,Docker会查看其内容并忽略其中指定的任何内容。我们在项目的跟目录中创建.d。Ckerignore文件,并添加以下内容到文件中:#忽略git目录和cache目录.git
20、.cache#忽略所有的markdown文件.md#忽略其他不想打包到镜像中的文件private.keySettingsjsonps:这样做的目的可以有效的减低镜像的大小2.6. 最佳实践6:使用doCkerignore文件使用Docker的多阶段构建现在假设我们的项目中有一些内容(如开发、测试工具和库),我们需要它们来构建镜像-在构建过程中,但是不需要它们在最终镜像本身中运行应用程序。如果我们在最终镜像中保留这些文物,它们对于运行应用程序是完全不必要的,那么它将导致镜像的大小增加以及被攻击的可能性增大。那么我们如何将构建阶段与运行阶段分离呢?换句话说,我们如何在镜像中排除构建依赖项,同时仍然
21、可以在构建镜像时使用它们??要解决这个问题我们可以使用Docker的多阶段构建技术多阶段构建功能允许我们在构建过程中使用多个临时镜像,但只保留最新的镜像作为最终产物:比如以下dockerfile中我们使用Docker的多阶段构建技术来构建golang应用程序:#多阶段构建第1步:FROMgolang:alpineASbuilder1.ABELstage=gobuilderENVCGO-ENABLED0ENVGOPROXYhttps,directRUNsed-i,sdl-cdn.alpinelinu.orggetcapkrepositoriesRUNapkupdate-no-cache&apka
22、dd-no-cachetzdataWORKDIR/buildADDgo.mod.ADDgo.sum.RUNgomoddownloadCOPY.COPY./etcappetcRUNgobuild-ldflags=-s-wn-oappportal.#多阶段构建第2步:FROMscratchCOPYfrom=builderetcsslcertsca-certificates.crtetcsslcertsca-certificates.crtCOPY-from=builderusrsharezoneinfoAsiaShanghaiusrsharezoneinfoAsiaShanghaiENVTZAsi
23、a/ShanghaiWORKDIRappCOPYfrom=builderappportalappportalCOPY-from=builderappetcappetcCMD./portal,h-fzetcenv.yam多阶段构建第1步主要是基于golang:alpine镜像并将当前项目拷贝到镜像中,目的是未了构建一个golang的编译环境,可以正常的构建golang应用程序未可执行的二进制程序.多阶段构建的第2步:将第一步构建完成的文件拷贝到基于scratch镜像中,目的是要达到镜像+可执行程序后镜像最小.这样做的好处是:将构建工具和依赖项与运行时所需的内容分离减少依赖项并减小镜像大小2.7.
24、 最佳实践7:使用最低权限的用户使用勒氐权限的用户当我们创建这个镜像并最终将其作为容器运行时,哪个操作系统用户将用于启动内部的应用程序呢?默认情况下,当Dockerfile没有指定用户时,它使用root用户。但实际上大多数情况下没有必要以root权限运行容器。这已经引入了一个安全问题,因为当容器在主机上启动运行时,它有可能具有Docker主机的root访问权限。因此,在容器内使用root用户运行应用程序将使攻击者更容易提升主机的权限,并基本上控制底层主机及其进程,而不仅仅是容器本身尤其是如果容器内的应用程序存在漏洞可供利用的情况下会更糟。为了避免这种情况,最佳做法是在DOCker镜像中创建一个
25、专用用户和专用组来运行应用程序,并且在容器内使用该用户来运行应用程序。在Dockerfile中我们添加以下代码:#创建demo组和demo用户RUNgroupadd-rdemo&useradd-gdemodemo#设置demo用户的权限RUNchown-Rdemo:demoapp#切换用户userdemocmdnodeindex.jsTip:一些镜像中已经包含了一个通用用户,我们可以使用它。因此,我们不需要创建新的用户。例如,node.js图像已经捆绑了一个名为node的通用用户,可以直接使用该用户在容器内运行应用程序。2.8. 最佳实践8:扫码镜像,查找是否存在漏洞扫码镜像,查找是否存在漏洞
26、我们如何确保我们的构建的镜像中有少量的或者不存在任何漏洞呢?我们在构建镜像之后可以使用docker官方提供的dockerscan命令来扫描安全漏洞。Docker如何发现我们的镜像是否存在漏洞呢?Docker实际上使用了一个名为snyk的服务来对镜像进行漏洞扫描。该扫描使用了一个不断更新的漏洞数据库。早期的Docker可以使用dockerscan命令:$dockerscanhello-worldTestinghello-world.Organization:docker-desktop-testPackagemanager:IinuxProjectname:docker-imagehello-w
27、orldDockerimage:hello-world1.icenses:enabled/Tested0dependenciesforknownissues,novulnerablepathsfound.Notethatwedonotcurrentlyhavevulnerabilitydataforyourimage.具体使用参考官方文档。除了在命令行界面上使用dockerscan命令手动扫描图像之外,还可以配置DockerHub以在图像被推送到存储库时自动扫描它们。当构建Docker镜像时,当然也可以将此检查与我们的CI/CD集成。2.9. 容器编排Docker容器编排是一种管理和协调多个D
28、ocker容器的技术,旨在简化容器化应用程序的部署、扩展和管理。在现代应用开发中,容器化已经成为一种流行的部署方式,DOCker是其中应用最广泛的容器化技术。然而,当应用由许多容器组成时,手动管理和协调这些容器可能会变得复利口耗时,这就是容器编排工具的用武之地。容器编排的主要目标是确保容器在集群中正确地部署、扩展和运行,同时提供高可用性、负载均衡和故障恢复机制。容器编排工具可以自动处理以下任务:自动化容器部署:容器编排工具可以根据定义的规则和配置自动在集群中创建和管理容器实例。负载均衡:容器编排工具可以分配流量并将请求路由到不同的容器实例,从而实现负载均衡,确保每个容器都能得到适当的资源分配。
29、自动伸缩:根据应用程序的负载,容器编排工具可以自动添加或删除容器实例,以确保应用程序的性能和可用性。服务发现:容器编排工具可以帮助应用程序在集群中自动发现其他服务的位置和地址,从而实现容器间的通信和协作。故障恢复:如果某个容器实例失败,容器编排工具可以自动重新启动或替换该容器,以保持应用程序的可用性。配置管理:容器编排工具可以管理容器的配置,确保容器使用正确的环境变量、数据卷等。两个最常用的容器编排工具是DockerSwarm和Kuberneteso它们都具备上述功能,并提供了强大的管理和监控功能,让您能够轻松管理复杂的容器化应用程序。容器编排在现代容器化部署中扮演着至关重要的角色,使得开发人
30、员和运维人员能够更高效地管理大规模和复杂的应用程序。composeDockerCompose是Docker官方提供的用于定义和运行多容器Docker应用程序的工具。它使用简单的YAML文件来配置应用程序的服务、网络、卷等,可以让你通过一个命令启动、停止和管理整个应用程序的容器群。DockerCompose非常适合用于本地开发环境、测试环境或者小规模的生产环境部署。下面是DockerCompose的一些关键特性和详细介绍:定义服务(SerVice):在DockerCompose中,你可以通过YAML文件定义一个或多个服务。每个服务代表一个容器。服务中可以指定使用的镜像、容器的名称、容器之间的网络
31、连接、环境变量、挂载的卷等。通过定义服务,你可以轻松构建多个容器组成的应用程序。多容器编排:DockerCompose支持在一个Compose文件中定义多个服务,这些服务可以同时启动、停止、销毁等。这使得编排多个相关容器变得更加简单,因为它们可以共享网络、卷等资源,实现容器之间的通信和数据共享。简化容器启动:使用DockerCompose,你可以通过一个简单的命令来启动整个应用程序,而不需要手动逐个启动每个容器。Compose会根据你的配置自动创建和连接容器,并按照指定的Jl质序启动它们。声明式配置:DockerCompose使用YAML文件来声明式地描述你的应用程序和服务配置。这样,你可以更
32、清楚地了解整个应用程序的结构和组件,以及如何运行它们。覆盖配置:Compose允许你使用覆盖文件(OVeITidefile)来覆盖或补充原始的Compose文件配置。这对于在不同环境中使用不同配置(例如:开发、测试、生产)非常有用。可移植性:DockerCompose的配置文件是YAML格式的,这使得它非常易于分享和版本控制。你可以轻松地在不同的环境中部署和运行应用程序,而不必担心环境的差异。扩展性:Compose不仅仅用于本地开发和测试,还可以与DockerSwarm等编排工具结合使用,以支持更大规模的容器编斗坏口部署。使用DockerCompose的一般步骤如下: 创建一个docker-compose.yml文件,定义你的服务和容器配置。 使用docker-composeup命令来启动应用程序的容器。 使用docker-composedown命令来停止并销毁所有相关容器。 可选:使用docker-composebuild命令来构建应用程序的镜像(如果需要I 可选:使用覆盖文件(如docker-compose.override.yml)来根据不同环境或需求覆盖Compose配置。总的来说,DockerComPOSe是一个强大且易于使用的工具,它简化了多容器应用程序的编艇口管理,使得在开发和测试环境中部署应用程序变得非常方便。它是DOCker生态系统。