【Docker深入浅出】【四】单体应用容器化与Dockerfile怎么写

news2024/9/20 5:38:47

文章目录

  • 一. 应用的容器化——简介
  • 二. 单体应用容器化
    • 1. 获取代码与分析Dockfile
    • 2. 容器化当前应用(构建具体的镜像)
    • 3.推送镜像到仓库
    • 4. 运行应用程序
    • 5. 小结
  • 三. 生产环境中的多阶段构建
  • 四. 应用容器化命令

本文介绍了如何容器化(Docker化)一个应用。

  • 介绍了Dockerfile基本的工作机制,并用docker image build命令创建了一个新的镜像。镜像创建后,基于该镜像启动了一个容器。
  • 多阶段构建提供了一种简单的方式,能够构建更加精简的生产环境镜像。
  • Dockerfile是一个将应用程序文档化的有力工具:首先编写应用代码,然后创建一个Dockerfile来定义这个应用,最后使用docker image build命令构建镜像。

一. 应用的容器化——简介

完整的应用容器化过程主要分为以下几个步骤。

  1. 创建Dockerfile,其中包括当前应用的描述、依赖以及该如何运行这个应用。
  2. 创建镜像:对Dockerfile执行docker image build命令构建镜像
  3. 应用容器化完成(即应用被打包为一个Docker镜像)​,就能以镜像的形式交付并以容器的方式运行了。

在这里插入图片描述

 

二. 单体应用容器化

1. 获取代码与分析Dockfile

代码地址:https://github.com/nigelpoulton/psweb.git 。如下代码结构:

$ cd psweb

$ ls -l
total 28
-rw-r--r-- 1 root root  341 Sep 29 16:26 app.js
-rw-r--r-- 1 root root  216 Sep 29 16:26 circle.yml
-rw-r--r-- 1 root root  338 Sep 29 16:26 Dockerfile
。。。

Dockerfile包含了对当前应用的描述,并且能指导Docker完成镜像的构建。在Docker当中,包含应用文件的目录通常被称为构建上下文(Build Context)​。通常将Dockerfile放到构建上下文的根目录下

有一点是必须的:文件开头字母是大写D,这里是一个单词。像“dockerfile”或者“Docker file”这种写法都是不允许的。

$ cat Dockerfile

FROM alpine                 # 以alpine镜像作为当前镜像基础,
LABEL maintainer="nigelpoulton@hotmail.com" # 维护者(maintainer)
RUN apk add --update nodejs nodejs-npm # 安装Node.js和NPM,
COPY . /src # 将应用的代码复制到镜像当中,设置新的工作目录,安装依赖包,
WORKDIR /src
RUN npm install
EXPOSE 8080 # 网络端口
ENTRYPOINT ["node", "./app.js"] # 将app.js设置为默认运行的应用。

 

Dockerfile主要包括两个用途

  • 描述当前应用
  • 指导Docker完成应用的容器化(创建一个包含当前应用的镜像)​

Dockerfile能实现开发和部署两个过程的无缝切换、帮助新手快速熟悉这个项目。如下Dockerfile配置描述:

配置说明
基础镜像层1. 每个Dockerfile文件第一行都是FROM指令。FROM指定的镜像,会作为当前镜像的基础镜像层,当前应用的剩余内容会作为新增镜像层添加到基础镜像层之上。
2. 本例中的应用基于Linux操作系统,所以在FROM指令当中引用Linux基础镜像;
label标签1. 一种描述信息,沟通方式。每个标签是一个键值对(Key-Value)​,在一个镜像当中可以通过增加标签的方式来为镜像添加自定义元数据。
2. 通过标签(LABEL)方式指定了当前镜像的维护者为“nigelpoulton@hotmail. com”​。
安装镜像层1. RUN apk add --update nodejs npm 指令使用alpine的apk包管理器将nodejs和nodejs-npm安装到当前镜像之中
2. RUN指令会在FROM指定的alpine基础镜像之上,新建一个镜像层来存储这些安装内容。
工作目录Dockerfile通过WORKDIR指令,为Dockerfile中尚未执行的指令设置工作目录。该目录与镜像相关,并且会作为元数据记录到镜像配置中,但不会创建新的镜像层。
安装相关依赖1. run npm install指令会根据package.json中的配置信息,使用npm来安装当前应用的相关依赖包。
2. npm命令会在工作目录中执行,并且在镜像中新建镜像层来保存相应的依赖文件。
暴露端口通过EXPOSE 8080设置应用端口
指定程序入口1. 通过ENTRYPOINT指令来指定当前镜像的入口程序。
2. ENTRYPOINT指定的配置信息也是通过镜像元数据的形式保存下来,而不是新增镜像层。

如下产生了四个镜像

在这里插入图片描述

 

2. 容器化当前应用(构建具体的镜像)

如下命令会构建并生成一个名为web:latest的镜像。命令最后的点(.)表示Docker在进行构建的时候,使用当前目录作为构建上下文


docker image build -t web:latest .                                        
[+] Building 55.2s (10/10) FINISHED                                                                                                            docker:desktop-linux
 => [internal] load build definition from Dockerfile              
 => => transferring dockerfile: 400B                              
 => [internal] load metadata for docker.io/library/alpine:latest  
 => [internal] load .dockerignore                                 
 => => transferring context: 2B                                   
 => CACHED [1/5] FROM docker.io/library/alpine:latest             
 => [internal] load build context                                 
 => => transferring context: 7.65kB                               
 => [2/5] RUN apk add --update nodejs npm curl                                                        
 => [3/5] COPY . /src                                             
 => [4/5] WORKDIR /src                                            
 => [5/5] RUN  npm install                                                 
 => exporting to image              
 => => exporting layers     
 => => writing image sha256:673850491a21f99a0f063f9e69f6649ba5dcdc
 => => naming to docker.io/library/web:latest                     

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/b80dnc9ketudf6y5nyoiob5vs

命令执行结束后,检查本地Docker镜像库是否包含了刚才构建的镜像。

$ docker image ls 
REPO TAG IMAGE ID CREATED SIZE web latest 
fc69fdc4c18e 10 seconds ago 64.4MB

恭喜,应用容器化已经成功了!

可以通过docker image inspect web:latest来确认刚刚构建的镜像配置是否正确。

 

3.推送镜像到仓库

Docker Hub是一个开放的公共镜像仓库服务,并且这也是docker image push命令默认的推送地址。

在推送镜像之前,需要先使用Docker ID登录Docker Hub。除此之外,还需要为待推送的镜像打上合适的标签。

登录

docker login 

Authenticating with existing credentials...
Login Succeeded

# 或

docker login
Login with **your** Docker ID to push and pull images from Docker Hub...
Username: nigelpoulton
Password:
Login Succeeded

Docker在镜像推送的过程中需要如下信息:

  1. ● Registry(镜像仓库服务)​● Repository(镜像仓库)​● Tag(镜像标签)​。
  2. 当Registry和Tag不指定值时,Docker会默认Registry=docker.io、Tag=latest。但是Docker并没有给Repository提供默认值,而是从被推送镜像中的REPOSITORY属性值获取。

 
push

执行docker image push命令,会尝试将镜像推送到docker.io/web:latest中。

二级命名空间:gaoliang00这个用户并没有web这个镜像仓库的访问权限,所以只能尝试推送到gaoliang00这个二级命名空间(Namespace)之下。因此需要使用这个ID,为当前镜像重新打一个标签。

在这里插入图片描述

额外的标签

docker image tag web:latest gaoliang00/web:latest

docker image list                         
REPOSITORY                           TAG         IMAGE ID       CREATED          SIZE
gaoliang00/web                              latest      673850491a21   17 minutes ago   94.9MB
web                                  latest      673850491a21   17 minutes ago   94.9MB

为镜像打标签命令的格式是docker image tag <current-tag> <new-tag>,其作用是为指定的镜像添加一个额外的标签,并且不需要覆盖已经存在的标签。

docker image push gaoliang00/web                 
Using default tag: latest
The push refers to repository [docker.io/gaoliang00/web]
744f820bd5f4: Pushed 
。。。
9110f7b5208f: Pushed 
latest: digest: sha256:4c822e80a71c328a1f2aeaf11221c6c724dca7bd88822da35bb9160925823f56 size: 1366

在这里插入图片描述

 

4. 运行应用程序

从app.js这个文件内容中可以看出,这其实就是一个在8080端口提供Web服务的应用程序。下面的命令会基于web:latest这个镜像,启动一个名为c1的容器。

docker container run -d --name c1   -p 81:8080   web:latest

docker container ls
CONTAINER ID   IMAGE                              COMMAND                  CREATED              STATUS                 PORTS                    NAMES
9d9b6776cbe9   web:latest                         "node ./app.js"          About a minute ago   Up About a minute      0.0.0.0:8888->8080/tcp   c2
49ccc5e94a9a   web:latest                         "node ./app.js"          3 minutes ago        Up 3 minutes           0.0.0.0:81->8080/tcp     c1

在这里插入图片描述

这样应用程序已经容器化并成功运行了!
 

如果没有出现这样的界面,尝试执行下面的检查来确认原因所在。

  • 使用docker container ls指令来确认容器已经启动并且正常运行。容器名称是c1,并且从输出内容中能看到0.0.0.0:81->8080/tcp。
  • 确认防火墙或者其他网络安全设置没有阻止访问Docker主机的80端口。

 

5. 小结

1.如何区分命令是否会新建镜像层

一个基本的原则是,如果指令的作用是向镜像中增添新的文件或者程序,那么这条指令就会新建镜像层;如果只是告诉Docker如何完成构建或者如何运行应用程序,那么就只会增加镜像的元数据。

 

2.查看构建镜像过程中执行了哪些指令

docker image history web:latest

IMAGE     CREATED BY                                       SIZE
fc6..18e  /bin/sh -c #(nop)  ENTRYPOINT ["node" "./a...    0B
334.bf0  /bin/sh -c #(nop)  EXPOSE 8080/tcp               0B
b27..eae  /bin/sh -c npm install                           14.1MB
932.749  /bin/sh -c #(nop) WORKDIR /src                   0B
052.2dc  /bin/sh -c #(nop) COPY dir:2a6ed1703749e80...    22.5kB
c1d..81f  /bin/sh -c apk add --update nodejs nodejs-npm    46.1MB
336.b92  /bin/sh -c #(nop)  LABEL maintainer=nigelp...    0B
3fd..f02  /bin/sh -c #(nop)  CMD ["/bin/sh"]               0B
 /bin/sh -c #(nop) ADD file:093f0723fa46f6c...    4.15MB

可以观察到只有4条指令会新建镜像层(就是那些SIZE列对应的数值不为零的指令),其他指令只在镜像中新增了元数据信息。

docker image inspect指令来确认确实只有4个层被创建了。

docker image inspect web:latest

<Snip>
},
"RootFS": {
    "Type": "layers",
    "Layers": [
        "sha256:cd7100...1882bd56d263e02b6215",
        "sha256:b3f88e...cae0e290980576e24885",
        "sha256:3cfa21...cc819ef5e3246ec4fe16",
        "sha256:4408b4...d52c731ba0b205392567"
    ]
},

 

3.了解镜像构建的过程

运行临时容器>在该容器中运行Dockerfile中的指令>将指令运行结果保存为一个新的镜像层>删除临时容器。

Step 3/8 : RUN apk add --update nodejs nodejs-npm
 ---> Running in e690ddca785f    << Run inside of temp container
fetch http://dl-cdn...APKINDEX.tar.gz
fetch http://dl-cdn...APKINDEX.tar.gz
(1/10) Installing ca-certificates (20171114-r0)
<Snip>
OK: 61 MiB in 21 packages
 ---> c1d31d36b81f                << Create new layer
Removing intermediate container   << Remove temp container
Step 4/8 : COPY . /src

 

三. 生产环境中的多阶段构建

在传统的 Docker 构建中,你可能会将所有构建依赖和工具保留在最终镜像中。这通常会导致镜像体积较大。多阶段构建允许你在多个阶段中进行构建,最终只将所需的部分复制到最终镜像中。这可以显著减少镜像的体积。

多阶段构建方式使用一个Dockerfile,其中包含多个FROM指令。每一个FROM指令都是一个新的构建阶段(Build Stage),并且可以方便地复制之前阶段的构件。

使用仓库如下来举例说明,https://github.com/david-gao1/atsea-sample-shop-app/blob/master/app/Dockerfile

FROM node:latest AS storefront
WORKDIR /usr/src/atsea/app/react-app
COPY react-app .
RUN npm install
RUN npm run build

FROM maven:latest AS appserver
WORKDIR /usr/src/atsea
COPY pom.xml .
RUN mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency
\:resolve
COPY . .
RUN mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests

FROM java:8-jdk-alpine AS production
RUN adduser -Dh /home/gordon gordon
WORKDIR /static
COPY --from=storefront /usr/src/atsea/app/react-app/build/ .
WORKDIR /app
COPY --from=appserver /usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar .
ENTRYPOINT ["java", "-jar", "/app/AtSea-0.0.1-SNAPSHOT.jar"]
CMD ["--spring.profiles.active=postgres"]

首先注意到,Dockerfile中有3个FROM指令。每一个FROM指令构成一个单独的构建阶段。

● 阶段0叫作storefront。● 阶段1叫作appserver。● 阶段2叫作production。

  1. storefront阶段拉取了大小超过600MB的node:latest镜像,然后设置了工作目录,复制一些应用代码进去,然后使用2个RUN指令来执行npm操作。这会生成3个镜像层并显著增加镜像大小。

  2. appserver阶段拉取了大小超过700MB的maven:latest镜像。然后通过2个COPY指令和2个RUN指令生成了4个镜像层。

  3. production阶段拉取java:8-jdk-alpine镜像,这个镜像大约150MB,明显小于前两个构建阶段用到的node和maven镜像。这个阶段会创建一个用户,设置工作目录,从store front阶段生成的镜像中复制一些应用代码过来。之后,设置一个不同的工作目录,然后从appserver阶段生成的镜像中复制应用相关的代码。最后,production设置当前应用程序为容器启动时的主程序。

重点在于COPY --from指令,它从之前的阶段构建的镜像中仅(how?)复制生产环境相关的应用代码,而不会复制生产环境不需要的构件。

可见它明显比之前阶段拉取和生成的镜像要小。最终,无须额外的脚本,仅对一个单独的Dockerfile执行docker image build命令,就创建了一个精简的生产环境镜像。多阶段构建是随Docker 17.05版本新增的一个特性,用于构建精简的生产环境镜像。

 

四. 应用容器化命令

docker image build 。命令会读取Dockerfile,并将应用程序容器化。使用-t参数为镜像打标签,使用-f参数指定Dockerfile的路径和名称,使用-f参数可以指定位于任意路径下的任意名称的Dockerfile。构建上下文是指应用文件存放的位置,可能是本地Docker主机上的一个目录或一个远程的Git库。
Dockerfile中的FROM指令用于指定要构建的镜像的基础镜像。它通常是Dockerfile中的第一条指令。
Dockerfile中的RUN指令用于在镜像中执行命令,这会创建新的镜像层。每个 RUN指令创建一个新的镜像层。
Dockerfile中的COPY指令用于将文件作为一个新的层添加到镜像中。通常使用 COPY指令将应用代码赋值到镜像中。
Dockerfile中的EXPOSE指令用于记录应用所使用的网络端口。
Dockerfile中的ENTRYPOINT指令用于指定镜像以容器方式启动后默认运行的程序。
其他的Dockerfile指令还有LABEL、ENV、ONBUILD、HEALTHCHECK、CMD等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2056867.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot的学习(三):开发相关

简介 一些开发测试时用到的技术。 springboot 热部署 修改了代码&#xff0c;服务器不需要重启可以直接看到新的修改的效果。仅仅加载当前开发者自定义开发的资源&#xff0c;不加载jar资源。 在pom.xml配置文件中添加&#xff1a; <dependency><groupId>org.s…

AI可以写毕业论文吗?6款亲测好用人工智能写论文网站

AI写作工具在学术界的应用已经逐渐成为一种趋势&#xff0c;特别是在毕业论文的撰写过程中。这些工具不仅能够提高写作效率&#xff0c;还能帮助学生更好地组织和规划他们的研究内容。以下是六款经过亲测且好用的人工智能写论文网站推荐&#xff1a; 一、千笔-AIPassPaper 千笔…

【自动驾驶】控制算法(三)轮胎侧偏与车辆动力学模型

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

苍鹰来啦!快来看呀!NGO-BiTCN-BiGRU-Attention北方苍鹰算法优化多重双向深度学习回归预测

苍鹰来啦!快来看呀&#xff01;NGO-BiTCN-BiGRU-Attention北方苍鹰算法优化多重双向深度学习回归预测 目录 苍鹰来啦!快来看呀&#xff01;NGO-BiTCN-BiGRU-Attention北方苍鹰算法优化多重双向深度学习回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…

Java实现MQTT通信(发布订阅消息)

文章目录 前言一、相关pom依赖二、相关代码1.MQTT工具类2.MQTT回调函数3.订阅消息4.发布消息 三、安装mosquitto1.mosquitto简介2.下载 四、安装MQTT.fx1.MQTT.fx简介2.下载3.使用 五、java订阅消息六、java发布消息 前言 MQTT是一种轻量级的物联网通信协议&#xff0c;基于客…

[Meachines] [Easy] Blue MS17-010永恒之蓝

信息收集 IP AddressOpening Ports10.10.10.40TCP:135/tcp msrpc, 139/tcp netbios-ssn, 445/tcp microsoft-ds, 49152/tcp msrpc, 49153/tcp msrpc, 49154/tcp msrpc, 49155/tcp msrpc, 49156/tcp msrpc, 49157/tcp msrpc $ nmap -p- 10.10.10.40 --min-rate 1000 -sC -sV …

YOLOV8 POSE姿态检测对图片绘制矩形和和关节点序号

代码如下 import cv2 import torchfrom ultralytics import YOLO# Load a model # model YOLO("yolov8n-pose.yaml") # build a new model from YAML model YOLO("yolov8n-pose.pt") # load a pretrained model (recommended for training) # model …

SQL— DQL语句学习【后端 11】

DQL语句 引言 DQL&#xff08;Data Query Language&#xff0c;即数据查询语言&#xff09;是SQL&#xff08;Structured Query Language&#xff09;中用于从数据库中检索数据的重要部分。在数据库管理中&#xff0c;DQL语句是日常工作中最常用的工具之一。通过DQL&#xff0…

leetcode-538. 把二叉搜索树转换为累加树

题目描述 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下&#xff0c;二叉搜索树满足下列约束…

C++11:右值引用、移动语义和完美转发

目录 前言 1. 左值引用和右值引用 2. 引用范围 3. 左值引用的缺陷 4. 右值引用的作用 5. 右值引用的深入场景 6. 完美转发 总结 前言 C11作为一次重大的更新&#xff0c;引入了许多革命性的特性&#xff0c;其中之一便是右值引用和移动语义。本文将深入探讨其中引入的…

Unity抖音直播玩法开发流程

前言 近两年直播玩法逐渐新兴起来了&#xff0c;也出现不少质量还不错的作品&#xff0c;比如下列《红蓝对决》《三国全战》等。近期我们也做了一款直播玩法&#xff0c;就此记录下开发流程。 1&#xff0c;申请应用 进入抖音开发者平台&#xff0c;在首页入驻平台。 如果是…

Unity的粒子系统

目录 基础参数与模块 创建与编辑 功能与应用 实例与教程 结论 Unity粒子系统的最新功能和更新有哪些&#xff1f; 如何在Unity中使用Visual Effect Graph创建复杂粒子效果&#xff1f; Unity粒子系统的高级应用技巧有哪些&#xff1f; 在Unity中实现粒子系统时的性能优…

回溯算法(基于Python)

递归 递归(recursion)是一种算法策略&#xff0c;通过函数调用自身来解决问题。"递"指程序不断深入地调用自身&#xff0c;通常传入更小或更简化的参数&#xff0c;直到达到“终止条件”。"归"指触发终止条件后&#xff0c;程序从最深层的递归函数开始逐层…

代码块分类

局部代码块 public class Test {public static void main(String[] args) {{int a 10;}// 执行到此处时候,变量a已经从内存中消失了。 // System.out.println(a);} } 构造代码块 public class Test {private String name;private int age;{// 构造代码块System.out.…

【STM32 Blue Pill编程】-定时器与中断

定时器与中断 文章目录 定时器与中断1、硬件准备及接线2、GPIO配置3、代码实现STM32F103C8 配有四个定时器,分别为 TIM1、TIM2、TIM3 和 TIM4。 它们充当时钟并用于跟踪基于时间的事件。 我们将展示如何使用 HAL 库在 STM32Cube IDE 中对这些定时器进行编程。 本文将涉及如下内…

【网络】抓包工具的使用

抓包工具 文章目录 1.tcpdump抓包1.1安装 tcpdump1.2常见使用 2.wireshark抓包 1.tcpdump抓包 TCPDump 是一款强大的网络分析工具&#xff0c; 主要用于捕获和分析网络上传输的数据包。 1.1安装 tcpdump tcpdump 通常已经预装在大多数 Linux 发行版中。 如果没有安装&#…

常见java OOM异常分析排查思路分析

Java 虚拟机&#xff08;JVM&#xff09;发生 OutOfMemoryError&#xff08;OOM&#xff09;异常时&#xff0c;表示 JVM 在尝试分配内存时无法找到足够的内存资源。以下是几种常见的导致 OOM 异常的情况&#xff1a; 1. Java 堆空间不足 (Java Heap Space) 这种情况发生在 J…

【小球下落反弹】小球自由落下,每次落地后反跳回原高度的一半

一小球从100米高度自由落下&#xff0c;每次落地后反跳回原高度的一半&#xff1b;再落下&#xff0c;求它在第10次落地时&#xff0c;共经过多少米&#xff1f;第10次反弹多高&#xff1f; 使用C语言实现&#xff0c;具体代码&#xff1a; #include<stdio.h>int main(…

wo是如何克服编程学习中的挫折感的?

你是如何克服编程学习中的挫折感的&#xff1f; 编程学习之路上&#xff0c;挫折感就像一道道难以逾越的高墙&#xff0c;让许多人望而却步。然而&#xff0c;真正的编程高手都曾在这条路上跌倒过、迷茫过&#xff0c;却最终找到了突破的方法。你是如何在Bug的迷宫中找到出口的…

HarmonyOs透明弹窗(选择照片弹窗样式)

1.鸿蒙中需要实现一个如下图的弹窗 2.由上图中可以得出&#xff0c;只需要三个Text组件依次向下排列&#xff0c;弹窗背景设置透明即可&#xff0c;弹窗代码如下(仅展示弹窗样式)&#xff1a; /**** 自定义选择图片弹窗** 外部定义需要导出*/ CustomDialog //自定义弹窗 export…