当前位置: 首页 > news >正文

玩转Docker(一):基本概念

容器技术是继大数据和云计算之后又一炙手可热的技术,而且未来相当一段时间内都会非常流行。

本文将对其基本概念和基本使用做出介绍。包括容器生态系统、容器的原理、怎样运行第一个容器、容器技术的概念与实践、Docker镜像等等

目录

一. 鸟瞰容器生态系统

1. 容器生态系统包含哪些不同层次的技术?

2. 先了解下Docker

3. 容器、镜像、镜像仓库

4. Docker容器与虚拟机技术的区别和联系

二. 运行第一个容器

三. 容器技术

1. What - 什么是容器?

2. Why - 为什么需要容器?

3. How - 容器是如何工作的

Docker架构

Docker服务器:Docker Daemon

Docker客户端:Docker CLI

Docker镜像:Image

Docker容器:Container

Registry:存放Docker镜像的仓库

4. 小结

四. Docker镜像

1. 镜像的内部结构

hello-world -- 最小的镜像

base镜像

支持运行多种LinuxOS

2. 镜像的分层结构

3. 构建镜像

docker commit

Dockerfile

RUN vs CMD vs ENTRYPOINT


一. 鸟瞰容器生态系统

1. 容器生态系统包含哪些不同层次的技术?

容器生态系统是基于Docker等各种容器的生态系统,包含的技术层次

容器生态系统包含核心技术、平台技术和支持技术。

简而言之:

  • 核心技术:能够让 Container 在 host 上运行的那些技术。解决的是容器自身怎样运行。
  • 平台技术:关注一组容器如何组织运行,管理这组容器的生命周期。
  • 容器支持技术:支持基于容器的基础设施。例如,容器网络、服务发现、监控、数据管理、日志管理、安全性等等

2. 先了解下Docker

Docker是一种开源的容器化平台,旨在简化应用程序的开发、部署和运行过程。提供了一种轻量级、可移植和自包含的容器化环境,使开发人员能够在不同计算机上以一致的方式构建、打包和分发应用程序

3. 容器、镜像、镜像仓库

  • 容器(Container):容器是Docker中运行应用程序的运行时实例。它是一个轻量级、可执行的软件包,包含了运行某个应用程序所需的所有内容,包括代码、运行时、系统工具、系统库和设置等。容器基于镜像创建,是镜像的运行实例。容器可以处于以下几种状态:

    • 运行中:容器正在运行应用程序
    • 已停止:容器已经停止运行,但仍然存在于系统中,可以随时重新启动。
    • 已删除:容器被删除后,其所有数据和状态都会被清除。
  • 镜像(Image):镜像是容器的模板,是只读的。它包含了容器运行所需的文件系统和应用程序的初始状态。镜像是由一系列的分层文件系统组成的,每一层都包含了特定的修改或更新。

  • 镜像仓库(Image Registry):镜像仓库是用于存储和分发 Docker 镜像的地方。最常用的公共镜像仓库是 Docker Hub,上面有大量的官方和社区共享的镜像。此外,还可以搭建私有的镜像仓库,用于存放自己的镜像。

  • Dockerfile:Dockerfile 是一种文本文件,用于定义 Docker 镜像的构建过程。它包含了一系列的指令,用于指定基础镜像、安装软件、拷贝文件、配置环境等。通过 Dockerfile,可以自动化地构建镜像,确保镜像的一致性和可重复性。

4. Docker容器与虚拟机技术的区别和联系

Docker容器和虚拟机都是用于隔离和运行应用程序的技术,但它们在实现方式、性能、资源占用、启动速度等方面存在显著差异。以下是它们的主要区别:

  • Docker容器:容器是一种轻量级的隔离运行环境,基于宿主机的操作系统内核,共享内核但拥有独立的文件系统、网络接口和进程空间。容器启动快(通常几秒内完成),资源占用低,性能接近物理机,适合快速部署和运行应用程序,尤其适用于微服务架构和开发测试环境。
  • 虚拟机:虚拟机通过虚拟化技术创建一个完整的虚拟计算机环境,运行独立的操作系统和应用程序。它提供强隔离性,适合运行多个不同操作系统或需要高度安全隔离的场景。虚拟机启动慢(通常几分钟),资源占用高,性能损耗较大,但可移植性强,适合长期运行且对隔离性要求高的应用。

5. 运行第一个容器

首先,请安装Docker并配置Docker的apt源。

  • 配置Docker的APT源主要是为了确保能够从官方或可信的源中安装和更新Docker软件包,同时也能解决一些常见的问题,比如版本兼容性、软件更新以及网络访问速度等。
  • 由于Docker Hub的服务在国外,下载镜像比较慢,最好配置成为免费的国内镜像服务。

环境就绪,马上运行第一个容器,执行命令:

docker run -d -p 80:80 httpd

结果如图所示:

其过程可以简单描述为:

1. 从Docker Hub下载httpd镜像

2. 启动httpd容器,并将容器的80端口映射到host的80端口

3. 下面可以通过浏览器验证容器是否正常工作。在浏览器中输入:http://[your ubuntu host ip]/

结果如图所示:

二. 容器技术

容器技术,需要搞清楚 What、why 和 How三个方面:什么是容器、为什么使用容器、容器是如何工作的。其中容器是怎样工作的,是容器核心知识的最主要部分。

1. What - 什么是容器?

容器是一种轻量级、可移植、自包含的软件打包技术,使应用程序可以在几乎任何地方以相同的方式运行。

容器与虚拟机

谈到容器,就不得不将它与虚拟机进行对比,因为两者都是为应用提供封装和隔离。

  • 容器由两部分构成:应用程序本身、及其依赖(比如应用程序需要的库或者其他软件容器在Host操作系统的用户空间中运行,与操作系统的气压进程隔离,这一点显著区别于虚拟机)。
  • 传统的虚拟化技术:目标是创建完整的虚拟机。为了运行应用,除了部署应用本及其依赖,还得安装整个操作系统。

由于所有容器共享一个host os,这使得容器在体积上要比虚拟机小很多。另外,启动容器不需要启动整个操作系统,所以容器部署和启动更快、开销更小也更容易迁移。

2. Why - 为什么需要容器?

为什么需要容器,容器到底解决了什么问题?简要的答案是,容器使软件具备了超强的移植能力。

就像集装箱解决了货物运输过程中,不同种类的货物相互影响、干扰的难题。

任何货物,不论是钢琴和保时捷,都被放到各自的集装箱中。集装箱在整个运输过程中都是密封的,只有到达最终目的地才会被打开。集装箱被誉为运输界与世界贸易最重要的发明。(容器和集装箱的英文单词都是:Container)

Docker将集装箱思想用到软件打包上,为代码提供了一个基于容器的标准化运输系统。

Docker可以将任何应用以及依赖打包成一个轻量级、可移植、自包含的容器。容器可以运行在几乎所有的操作系统上。

3. How - 容器是如何工作的

Docker架构

Docker的核心组件包括:

  • Docker客户端:Client
  • Docker服务器:Docker daemon
  • Docker镜像:Image
  • Registry
  • Docker容器:Container

Docker架构如图所示

Docker采用的是Client / Server架构。客户端向服务器发送请求,服务器请求构建、运行和分发容器。客户端和服务器可以运行在同一个Host上,客户端也可以通过socket或REST API与远程服务器通信。

Docker服务器:Docker Daemon

Docker 服务器是容器管理的核心,负责实际的容器生命周期管理和资源分配。

Docker服务器是Docker的后台服务进程,通常定义为Docker守护进程。它负责管理Docker的生命周期,包括创建、运行、停止、删除容器等操作。

功能:

  • 容器管理:接收来自客户端的指令,执行容器的创建、启动、停止、删除等操作。
  • 镜像管理:管理Docker镜像的拉取、存储和推送
  • 网络管理:配置容器的网络环境,包括虚拟网络、端口映射等。
  • 存储管理:管理容器的存储卷,支持数据持久化。
  • 资源隔离:通过 Linux 内核的命名空间(Namespaces)和控制组(Cgroups)技术,为容器提供隔离的运行环境。

运行方式:Docker服务器通常在后台运行,监听来自客户端的请求,并执行相应的操作。

Docker客户端:Docker CLI

Docker 客户端是用户与 Docker 服务器之间的桥梁,提供用户友好的交互方式,方便用户发送命令和管理容器。最常用的Docker客户端是docker命令。通过docker我们可以方便地在Host上构建和运行容器。

  • Docker客户端是用户与Docker服务器交互的工具,通常是一个命令行界面(CLI),用户通过它发送命令来控制Docker服务器。

功能:

  • 命令执行:用户通过命令行输入指令,如 docker rundocker builddocker stop 等,将这些指令发送给 Docker 服务器。
  • 交互界面:提供用户友好的交互方式,方便用户管理容器和镜像。
  • 配置管理:允许用户配置 Docker 的运行参数,如网络设置、存储驱动等。

运行方式:Docker 客户端通常运行在用户的本地机器上,通过网络(默认是本地套接字)与 Docker 服务器通信。

Docker镜像:Image

可将Docker镜像看成只读模板,通过它可以创建Docker容器。

镜像有多种生成方法:

  • 从无到有开始创建镜像
  • 下载并使用别人创建好的现成的镜像
  • 在现有镜像上创建新的镜像

可以将镜像的内容和创建步骤描述在一个文本文件中,这个文件被称为Dockerfile,通过执行docker build <docker-file> 命令可以构建出Docker镜像。

Docker容器:Container

Docker容器就是Docker镜像的运行实例。

用户可以通过CLI(Docker)或是API启动、停止、移动或删除容器。可以这么认为:对于应用软件,镜像是软件生命周期的构建和打包阶段,而容器则是启动和运行阶段。

Registry:存放Docker镜像的仓库

Registry是存放Docker镜像的仓库,Registry分为私有和公有两种。

Docker Hub(https://hub.docker.com/)是默认的Registry,由Docker公司维护,上面有数以万计的镜像,用户可以自由下载和使用。

出于对速度或安全的考虑,用户也可以创建自己的私有Registry。

  • docker pull 命令可以从Registry下载镜像。
  • docker run 命令则是先下载镜像(如果本地没有),然后再启动容器。

还记得我们运行的第一个容器吗?现在通过它来体会一下Docker各个组件是如何协作的。容器启动过程如下:

1. Docker客户端执行docker run命令

2. Docker Daemon发现本地没有httpd镜像

3. daemon从Docker Hub下载镜像

4. 下载完成,镜像httpd被保存到本地

5. Docker daemon启动容器。

docker images已经可以查看到httpd已经下载到本地

docker ps 或者 docker container ls 显示容器正在运行。

4. 小结

Docker借鉴了集装箱的概念。标准集装箱将货物运往世界各地,Docker将这个模型运用到自己的设计哲学中。唯一不同的是:集装箱运输货物,Docker运输软件。

每个容器都有一个软件镜像,相当于集装箱中的货物。容器可以被创建、启动、关闭和销毁。和集装箱一样,Docker在执行这些操作时,并不关心容器里面到底装的什么,它不管里面是Web Server,还是Data-base。用户不需要关心容器最终在哪里运行,因为在哪里都可以运行

开发人员可以在笔记本上构建镜像并上传到Registry,然后QA人员将镜像下载到物理或虚拟机做测试,最终容器会部署到生产环境。

使用Docker以及容器技术,我们可以快速的构建一个应用服务器、一个消息中间件、一个数据库、一个持续集成的环境。因为Docker Hub上有我们能想到的几乎所有的镜像。

三. Docker镜像

镜像是Docker容器的基石,容器是镜像的运行实例,有了镜像才能启动容器。

本章内容安排如下:首先通过研究几个典型的镜像,分析镜像的内部结构;然后学习如何构建自己的镜像;最后介绍怎样管理和分发镜像。

1. 镜像的内部结构

为什么我们要讨论镜像的内部结构?如果只是使用镜像,当然不需要了解,直接通过Docker命令下载和运行就可以了。

但如果我们想构建自己的镜像,或者想理解Docker为什么是轻量级的,就非常有必要学习这部分知识了。

我们从一个最小的镜像开始学习。

hello-world -- 最小的镜像

hello-world是Docker官方提供的一个镜像,通常用来检验Docker是否安装成功。我们先通过docker pull 从Docker Hub下载它,如图所示:

用 docker images 查看镜像的信息,如图所示:

hello-world竟然还不到2kb,通过docker run运行,如图所示:

其实我们更关心 hello-world 镜像包含了哪些内容。

Dockerfile是镜像的描述文件,定义了如何构建Docker镜像。Dockerfile的语法简洁且可读性强,后面我们会讨论如何编写Dockerfile。

hello-world 的 Dockerfile内容如下图所示:

只有短短三条指令。

(1)From scratch镜像是白手起家,从0开始构建

(2)COPY hello/ 将文件 “hello”复制到镜像的根目录

(3)CMD ["/hello"] 容器启动时,执行 /hello

镜像hello-world中就只有一个可执行文件 “hello”,其功能就是打印出 “Hello from Docker...”等信息。

/hello 就是文件系统的全部内容,连最基本的 /bin /usr /lib /dev 都没有

hello-world虽然是一个完整的镜像,但它并没有什么实际用途。通常来说,我们希望镜像能够提供一个基本的操作系统环境,用户可以根据需要安装和配置软件。

这样的镜像我们称作 base镜像。

base镜像

base镜像有两层含义:

  • 不依赖于其他镜像,从scratch构建
  • 其他镜像可以以之为基础进行扩展

所以,能称作base镜像的通常都是各种Linux发行版的Docker镜像,比如Ubuntu、Debian、CentOS等。

我们以CentOS为基础为例考察base镜像包含哪些内容。

下载镜像:

docker pull centos

查看镜像信息,如下图:

镜像大小不到200MB

等一下!

一个CentOS才200MB?

平时我们安装一个CentOS至少都有几个GB,怎么可能才200MB!

相信这是几乎所有 Docker 初学者都会有的疑问,包括我自己。下面我们来解释这个问题。

Linux操作系统由内核空间和用户空间组成,如下图:

1. rootfs

内核空间是kernel,Linux刚启动时会加载bootfs文件系统,之后bootfs会被卸载掉。

用户空间的文件系统是rootfs,包含我们熟悉的 /dev /proc /bin等目录

对于 base镜像 来说,底层直接用 Host 的 kernel,自己只需要提供rootfs就行了。

  • 而对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了。相比其他的Linux发行版,CentOS的rootfs已经算臃肿的了,alpine还不到10MB
  • 我们平时还会安装的CentOS除了rootfs之外,还会选装很多软件、服务、图形桌面等等,需要好几个GB就不足为奇了。

2. base镜像提供的是最小安装的Linux发行版

CentOS镜像的Dockerfile的内容如下图所示:

第二行ADD指令添加到镜像的 tar 包就是 CentOS 7 的rootfs,在制作镜像时,这个tar包会自动解压到 / 目录下,生成 /dev、/proc、/bin等目录。

支持运行多种LinuxOS

不同Linux发行版的区别主要就是rootfs,所以Docker可以同时支持多种Linux镜像,模拟出多种操作系统环境。

上图Debian和BusyBox(一种嵌入式Linux)上层提供各自的rootfs,底层共用Docker Host的kernel

2. 镜像的分层结构

Docker支持通过扩展现有镜像,创建新的镜像。

实际上,Docker Hub中99%的镜像都是通过在base镜像中安装和配置需要的软件构建出来的。比如我们构建一个新的镜像,Dockerfile 如下图所示:

(1)新镜像不再是从scratch开始,而是直接在Debian base镜像上创建

(2)安装 emacs 编辑器

(3)安装 Apache2

(4)容器启动时运行 bash

构建过程中如下图:

可以看到,新镜像是从base镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。

为什么Docker镜像要采用这种分层结构呢?

最大的一个好处就是:共享资源。

3. 构建镜像

对于Docker用户来说,最好的情况是不需要自己创建镜像。几乎所有常用的数据库、中间件、应用软件等都有现成Docker官方镜像或其他人和组织创建的镜像,我们只需要稍作配置就可以直接使用。

使用现成镜像的好处除了省去自己做镜像的工作量外,更重要的是可以利用前人的经验。特别是使用官方的镜像,因为Docker工程师更知道如何更好地在容器中运行软件。

当然,某些情况下我们也不得不自己构建镜像,比如:

(1)找不到现成的镜像,比如自己开发的应用程序。

(2)需要在镜像中加入特定的功能,比如官方镜像几乎都不提供ssh

所以本节我们将介绍构建镜像的方法。同时分析构建的过程也能够加深我们对前面镜像分层结构的理解。

Docker提供了两种构建镜像的方法:docker commit命令与Dockerfile构建文件。

docker commit

docker commit 命令是创建新镜像最直观的方法,其过程包含三个步骤:

  • 运行容器
  • 修改容器
  • 将容器保存为新的镜像

举个例子:在Ubuntu base镜像中安装vi并保存为新镜像。

(1)运行容器

如图所示:

-it的作用是以交互模式进入容器,并打开终端。

(2)安装vi

确认vi没有安装,如下图所示:

安装vi,如下图所示:

(3)保存为新镜像

在新窗口中查看当前运行的容器,如图所示:

silly_goldberg是Docker为我们容器随机分配的名字。

执行 docker commit 命令将容器保存为镜像,如图所示:

新镜像命名为ubuntu-with-vi

查看新镜像的属性,如图所示:

从size上看到镜像因为安装了软件而变大了。

从新镜像启动容器,验证 vi 已经可以使用,如图所示:

以上演示了如何用 docker commit创建新镜像。然而,Docker并不建议用户通过这种方式构建镜像。原因如下:

(1)这是一种手动创建镜像的方式,容易出错,效率低且可重复性若。比如要在 debian base镜像中也加入vi,还得重复前面所有的步骤。

(2)更重要的:使用者并不知道镜像是如何创建出来的,里面是否有恶意程序。也就是说无法对镜像进行审计,存在安全隐患。

既然 docker commit不是推荐的方法,我们为什么还要花时间学习呢?原因是,即便是用Dockerfile(推荐方法)构建镜像,底层也是docker commit一层一层构建新镜像的。学习docker commit 能够帮助我们更加深入地理解构建过程和镜像的分层结构。

Dockerfile

Dockerfile是一个文本文件,记录了镜像够简单的所有步骤。

1. 第一个Dockerfile

用 Dockerfile 创建上节的 ubuntu-with-vi,其内容如图所示:

下面我们运行 docker build 命令构建镜像并详细分析每个细节。

root@ubuntu:~# pwd ①/rootroot@ubuntu:~# ls ②Dockerfileroot@ubuntu:~# docker build -t ubuntu-with-vi-dockerfile . ③Sending build context to Docker daemon 32.26 kB ④Step 1 : FROM ubuntu ⑤---> f753707788c5Step 2 : RUN apt-get update && apt-get install -y vim ⑥---> Running in 9f4d4166f7e3 ⑦......Setting up vim (2:7.4.1689-3ubuntu1.1) ...---> 35ca89798937 ⑧Removing intermediate container 9f4d4166f7e3 ⑨Successfully built 35ca89798937 ⑩root@ubuntu:~#

① 当前目录为 /root。

② Dockerfile准备就绪。

③ 运行docker build命令,-t将新镜像命名为ubuntu-with-vi-dockerfile,命令末尾的.指明build context为当前目录。Docker默认会从build context中查找Dockerfile文件,我们也可以通过-f参数指定Dockerfile的位置。

④ 从这步开始就是镜像真正的构建过程。

首先Docker将build context中的所有文件发送给Docker daemon。build context为镜像构建提供所需要的文件或目录。 Dockerfile中的ADD、COPY等命令可以将build context中的文件添加到镜像。此例中,build context为当前目录 /root,该目录下的所有文件和子目录都会被发送给Docker daemon。 所以,使用build context就得小心了,不要将多余文件放到build context,特别不要把 /、/usr作为build context,否则构建过程会相当缓慢甚至失败。

⑤ Step 1:执行FROM,将Ubuntu作为base镜像。 Ubuntu镜像ID为f753707788c5。

⑥ Step 2:执行RUN,安装vim,具体步骤为 ⑦ ⑧ ⑨。

⑦ 启动ID为9f4d4166f7e3的临时容器,在容器中通过apt-get安装vim。

⑧ 安装成功后,将容器保存为镜像,其ID为35ca89798937。 这一步底层使用的是类似docker commit的命令。

⑨ 删除临时容器9f4d4166f7e3。

⑩ 镜像构建成功。

通过docker images查看镜像信息,如图3-21所示。 [插图] 图3-21 镜像ID为35ca89798937,与构建时的输出一致。 在上面的构建过程中,我们要特别注意指令RUN的执行过程 ⑦ ⑧ ⑨。Docker会在启动的临时容器中执行操作,并通过commit保存为新的镜像。

2. 查看镜像的分层结构

ubuntu-with-vi-dockerfile 是通过在base镜像的顶部增加一个新的镜像层而得到的,如图所示:

这个新镜像层的内容由 RUN apt-get update && apt-get in stall -y vim 生成。这一点我们可以通过docker history命令验证,如图所示:

docker history会显示镜像的构建历史,也就是Dockerfile的执行过程。

ubuntu-with-vi-dockerfile 与 Ubuntu 镜像相比,确实只是多了顶部的一层35ca89798937,由apt-get命令创建,大小为97.07MB。docker history向我们展示了镜像的分层结构,每一层由上至下排列。

注:missing表示无法获取IM-AGE ID,通常从 Docker Hub 下载的镜像会有这个问题。

3. 镜像的缓存特征

我们接下来看Docker镜像的缓存特征。

Docker会缓存已有镜像的镜像层,构建新镜像时,如果某镜像直接存在,就直接使用,无需重新创建。

例如,如果在Dockerfile中添加一点新内容,往镜像中复制一个文件:

(1)确保 testfile 已存在

(2)由于之前已经运行过相同的 RUN 命令,这次直接使用缓存中的镜像层 35ca89798937

(3)执行 COPY 命令

其过程是启动临时容器,复制 testfile,提交新的镜像层 8d02784a78f4,删除临时容器。

在ubuntu-with-vi-dockerfile 镜像上直接添加一层就得到了新的镜像 ubuntu-with-vi-docker-file-2,如图所示:

如果我们希望在构建镜像时不使用缓存,可以在 docker build 命令中加上 no-cache 参数。

Dockerfile每一个指令就会创建一个镜像层,上层是依赖于下层的。无论什么时候,只要某一层发生变化,其上面所有层的缓存都会失效。 

也就是说,如果我们改变 Dockerfile 的执行顺序,或者修改或者添加指令,都会使缓存失效。举例说明:

  • 如果交换前面 RUN 和 COPY 的顺序,如图所示:
  • 虽然在逻辑上这种改动对镜像的内容没有影响,但由于分层的结构特征,Docker必须重建受影响的镜像层。
  • 从执行过程可以看到,生成了新的镜像层,缓存已经失效。

除了构建时使用缓存,Docker在下载镜像时也会使用。

4. 调试Dockerfile

总结一下通过 Dockerfile 构建镜像的过程:

(1)从base镜像运行一个容器

(2)执行一条指令,对容器做修改

(3)执行类似docker commit的操作,生成一个新的镜像

(4)Docker再基于刚刚提交的镜像运行一个新容器

(5)重复2~~4步,直到Dockerfile中的所有指令执行完毕

5. Dockerfile 常用指令

是时候系统学习Dockerfile了

  • FROM:指定base镜像
  • MAINTAINER:设置镜像作者,可以是任意字符串
  • COPY:
    • 将文件从build context复制到镜像
    • COPY支持两种形式:COPY src dest 与 COPY ["src", "dest"]
    • 注意,src只能指定 build context 中的文件或目录
  • ADD:
    • 与COPY类似,从 build context 复制文件到镜像。不同的是,如果 src 是归档文件(tar、zip、tgz、xz等),文件会被自动解压到dest
  • ENV:
    • 设置环境变量,环境变量可以被后面的指令使用。例如:
    • ENV MY_VERSION 1.3 RUN apt-get install -y mypackage=$MY_VERSION

  • EXPOSE:

    • 指定容器中的进程会监听某一个端口,Docker可以将该端口暴露出来。

  • VOLUMN:

    • 将文件或者目录声明为volume

  • WORKDIR:

    • 为后面的RUN、CMD、ENTRY-POINT、ADD或COPY指令设置镜像中的当前工作目录

  • RUN

    • 在容器中执行指定的命令

  • CMD

    • 容器启动时运行指定的命令

    • Dockerfile可以有多个CMD指令,但只有最后一个生效。CMD可以被 docker run 之后的参数替换。

  • ENTRYPOINT

    • 设置容器启动时运行的命令。

    • Dockerfile中可以有多个ENTRYPOINT指令,但只有最后一个生效。CMD 或 docker run之后的参数会被当做参数传递给 ENRTYPOINT。

这是一个比较全面的Dockerfile(注:Dockerfile支持以 “#” 开头的注释)

构建镜像,如下图:

(1)构建前确保build context中存在需要的文件

(2)依次执行Dockerfile指令,完成构建

(3)运行容器,验证镜像内容,如下图

1. 进入容器,当前目录即为WORKDIR

如果WORKDIR不存在,Docker会自动帮我们创建

2. WORKDIR中保存了我们希望的文件和目录

目录bunch:由ADD指令从build context复制的归档文件bunch.tar.gz,已经自动解压。 文件tmpfile1:由RUN指令创建。 文件tmpfile2:由COPY指令从build context复制。

3. ENV指令定义的环境变量已经生效。

RUN vs CMD vs ENTRYPOINT

RUN、CMD和ENTRYPONIT这三个Dockerfile指令看上去很类似,很容易混淆。

简单地说:

(1)RUN:执行命令并创建新的镜像层,RUN经常用于安装软件包

(2)CMD:设置容器启动后默认执行的命令及其参数,但CMD能够被 docker run 后面接的命令行替换

(3)ENTRYPOINT:配置容器启动时运行的命令

最佳实践:

1. 使用RUN命令安装应用和软件包,构建镜像

2. 如果docker镜像的用途是运行应用程序或服务,比如运行一个MySQL,应该使用Exec格式的ENTRYPOINT指令。CMD可为ENTRYPONIT提供额外的默认参数,同时可以利用 docker run 命令行替换默认参数,同时可以利用 docker run 命令行替换默认参数。

3. 如果想为容器设置默认的启动命令,可使用CMD指令,用户可在 docker run 命令中替换此默认命令。

4. 小结

本章我们学习了Docker镜像。首先讨论了镜像的分层结构,然后学习了如何构建镜像,最后实践使用Docker Hub和本地registry。

下面是镜像的常用操作子命令:

  • images:显示镜像列表。
  • history:显示镜像构建历史。
  • commit:从容器创建新镜像。
  • build:从Dockerfile构建镜像。
  • tag:给镜像打tag。
  • pull:从registry下载镜像。
  • push:将镜像上传到registry。
  • rmi:删除Docker host中的镜像。
  • search:搜索Docker Hub中的镜像。

以上是 docker 技术的基本概念,其余知识明日继续连载

Good night~~

-- 2025年5月2日

相关文章:

玩转Docker(一):基本概念

容器技术是继大数据和云计算之后又一炙手可热的技术&#xff0c;而且未来相当一段时间内都会非常流行。 本文将对其基本概念和基本使用做出介绍。包括容器生态系统、容器的原理、怎样运行第一个容器、容器技术的概念与实践、Docker镜像等等 目录 一. 鸟瞰容器生态系统 1. 容器…...

Linux系统安装方式+适合初学者的发行版本

Linux系统安装方式适合初学者发行版—目录 一、Linux系统的安装方式1. 物理机直接安装2. 虚拟机安装3. 双系统安装4. Live USB试用5. 云服务器安装 二、适合初学者的Linux发行版1. Ubuntu2. Linux Mint3. Zorin OS4. Pop!_OS5. Elementary OS6. Fedora7. Manjaro 三、选择建议场…...

开启 Spring AI 之旅:从入门到实战

开启 Spring AI 之旅&#xff1a;从入门到实战 引言 在当今人工智能飞速发展的时代&#xff0c;Spring AI 为开发者们提供了一个强大而便捷的工具&#xff0c;用于在 Spring 生态系统中构建 AI 应用程序。本文将为你提供如何开始使用 Spring AI 的详细指南&#xff0c;帮助你…...

python数据分析(七):Pandas 数据变形与重塑

Pandas 数据变形与重塑全面指南 1. 引言 在数据分析过程中&#xff0c;我们经常需要将数据从一种结构转换为另一种结构&#xff0c;以适应不同的分析需求。Pandas 提供了丰富的数据变形与重塑功能&#xff0c;包括旋转(pivot)、堆叠(stack)、融合(melt)等多种操作。本文将详细…...

SX24C01.UG-PXI程控电阻桥板卡

PXI程控电阻桥板卡 概述 简介 程控电阻桥板卡采用4 个可程控精密调节的电阻臂组成桥式电路&#xff0c;通过计算机PXI总线控制继电器通断进行电阻调节&#xff1b;可根据具体情况&#xff0c;方便地选择不同桥路的连接&#xff1b;程控电阻桥板卡支持“1/4 桥”、“半桥”和…...

Python项目源码69:一键解析+csv保存通达信日线数据3.0

Python项目源码39&#xff1a;学生积分管理系统1.0&#xff08;命令行界面Json&#xff09; Python项目源码38&#xff1a;模拟炒股系统2.0&#xff08;tkinterJson&#xff09; Python项目源码35&#xff1a;音乐播放器2.0&#xff08;Tkintermutagen&#xff09; Python项…...

Conda 与 Spyder 环境管理

前言 作为 Python 科学计算领域的黄金搭档&#xff0c;Anaconda 和 Spyder 为研究人员和数据分析师提供了强大的工作环境。本文将详细介绍如何使用 Conda 管理 Python 环境&#xff0c;并在 Spyder IDE 中灵活切换这些环境&#xff0c;助你打造高效的 Python 开发工作流。 一…...

头皮理疗预约小程序开发实战指南

生活服务类小程序开发正成为互联网创业的热点领域,头皮理疗预约小程序作为其中的细分品类,具有广阔的市场前景和用户需求。基于微信小程序原生开发或uniapp框架,结合Java后端和MySQL数据库,可构建一个功能完善、性能稳定且易于维护的头皮理疗预约平台。本文将从零开始,详细…...

cPanel 的 Let’s Encrypt™ 插件

在 cPanel & WHM 中&#xff0c;推出了一个名为 AutoSSL 的功能。可能有些朋友还不了解 AutoSSL&#xff0c;它是一个能够自动为您的网站申请和安装免费 SSL 证书的工具&#xff0c;这些证书由 Comodo 签发&#xff0c;保证网站的安全性。 AutoSSL 与 Let’s Encrypt Let’…...

《Android 应用开发基础教程》——第十一章:Android 中的图片加载与缓存(Glide 使用详解)

目录 第十一章&#xff1a;Android 中的图片加载与缓存&#xff08;Glide 使用详解&#xff09; &#x1f539; 11.1 Glide 简介 &#x1f538; 11.2 添加 Glide 依赖 &#x1f538; 11.3 基本用法 ✦ 加载网络图片到 ImageView&#xff1a; ✦ 加载本地资源 / 文件 / UR…...

MySQL 中的游标(Cursor)

一、游标的作用 ​​逐行处理数据​​&#xff1a;当需要对查询结果集中的每一行进行特定操作&#xff08;如计算、条件判断、调用其他过程&#xff09;时使用。​​替代集合操作​​&#xff1a;在无法通过单一 SQL 语句完成复杂逻辑时&#xff0c;游标提供逐行处理的能力。​…...

【嵌入式Linux】基于ARM-Linux的zero2平台的智慧楼宇管理系统项目

目录 1. 需求及项目准备&#xff08;此项目对于虚拟机和香橙派的配置基于上一个垃圾分类项目&#xff0c;如初次开发&#xff0c;两个平台的环境变量&#xff0c;阿里云接入&#xff0c;摄像头配置可参考垃圾分类项目&#xff09;1.1 系统框图1.2 硬件接线1.3 语音模块配置1.4 …...

记忆翻牌游戏:认知科学与状态机的交响曲

目录 记忆翻牌游戏&#xff1a;认知科学与状态机的交响曲引言第一章 网格空间拓扑学1.1 自适应网格算法1.2 卡片排布原理 第二章 状态机设计2.1 状态跃迁矩阵2.2 时空关联模型 第三章 记忆强化机制3.1 认知衰减曲线3.2 注意力热力图 第四章 动画引擎设计4.1 翻牌运动方程4.2 粒…...

【业务领域】InfiniBand协议总结

InfiniBand协议总结 InfiniBand协议是什么&#xff1f;Infiniband产生的原因Mellanox公司介绍及其新闻基于TCP/IP的网络与IB网络的比较IB标准的优势什么是InfiniBand网络什么是InfiniBand架构Mellanox IB卡介绍InfiniBand速率发展介绍InfiniBand网络主要上层协议InfiniBand管理…...

使用Java正则表达式检查字符串是否匹配

在Java开发中&#xff0c;正则表达式&#xff08;Regular Expression&#xff0c;简称Regex&#xff09;是处理字符串的强大工具&#xff0c;广泛应用于模式匹配、数据验证和文本处理。Java通过java.util.regex包提供了对正则表达式的支持&#xff0c;包含Pattern和Matcher两个…...

个人健康中枢的多元化AI硬件革新与精准健康路径探析

在医疗信息化领域,个人健康中枢正经历着一场由硬件技术革新驱动的深刻变革。随着可穿戴设备、传感器技术和人工智能算法的快速发展,新一代健康监测硬件能够采集前所未有的多维度生物数据,并通过智能分析提供精准的健康建议。本文将深入探讨构成个人健康中枢的最新硬件技术,…...

Android基础控件用法介绍

Android基础控件用法详解 Android应用开发中,基础控件是构建用户界面的核心元素。本文将详细介绍Android中最常用的基础控件及其用法。 一、TextView(文本显示控件) TextView用于在界面上显示文本信息。 基本用法 <TextViewandroid:id="@+id/textView"andr…...

iO(不可区分混淆)是Web3隐私的圣杯?

1. 引言 iO 是区块链隐私的圣杯吗&#xff1f;本文将探讨&#xff1a; 不可区分混淆&#xff08;indistinguishability obfuscation, iO&#xff09;的局限性iO可能带来的变革iO为何重要&#xff1f;iO是否能真正成为可信硬件的替代方案&#xff1f; 区块链隐私面临的最大挑…...

文章三《机器学习基础概念与框架实践》

文章3&#xff1a;机器学习基础概念与框架实践 ——从理论到代码&#xff0c;用Scikit-learn构建你的第一个分类模型 一、机器学习基础理论&#xff1a;三大核心类型 机器学习是人工智能的核心&#xff0c;通过数据让计算机自动学习规律并做出预测或决策。根据学习方式&#…...

中小企业MES系统概要设计

版本&#xff1a;V1.0 日期&#xff1a;2025年5月2日 一、系统架构设计 1.1 整体架构模式 采用分层微服务架构&#xff0c;实现模块解耦与灵活扩展&#xff0c;支持混合云部署&#xff1a; #mermaid-svg-drxS3XaKEg8H8rAJ {font-family:"trebuchet ms",verdana,ari…...

自动化测试项目1 --- 唠嗑星球 [软件测试实战 Java 篇]

目录 项目介绍 项目源码库地址 项目功能测试 1.自动化实施步骤 1.1 编写测试用例 1.2 自动化测试脚本开发 1.2.1 配置相关环境, 添加相关依赖 1.2.2 相关代码编写 2. 自动化功能测试总结 2.1 弹窗的解决相关问题 2.2 断言的使用和说明 2.3 重新登录问题 项目性能…...

c语言 关键字--目录

1.c语言 关键字 2.typedef 关键字 3.volatile 关键字 4.register 关键字 5.const关键字用法 6.extern关键字...

C语言与指针3——基本数据类型

误区补充 char 的 表示范围0-127 signed char 127 unsigned char 0-255enum不常用&#xff0c;但是常见&#xff0c;这里记录一下。 enum Day {Monday 1,//范围是IntTuesday 2,Wednesday 3 }; enum Day d Monday; switch (d) {case Monday:{printf("%d",Monday);…...

[更新完毕]2025五一杯C题五一杯数学建模思路代码文章教学:社交媒体平台用户分析问题

完整内容请看文章最下面的推广群 社交媒体平台用户分析问题 在问题一中为解决博主在特定日期新增关注数的预测问题&#xff0c;本文构建了基于用户历史行为的二分类模型。首先&#xff0c;从用户对博主的观看、点赞、评论、关注等交互行为中提取统计与时序特征&#xff0c;形成…...

使用Vite创建vue3项目

什么是vite Vite 是新一代构建工具&#xff0c;由 Vue 核心团队开发&#xff0c;提供极快的开发体验。 它利用浏览器原生ES模块导入功能&#xff0c;提供了极快的热模块更新&#xff08;HMR&#xff09;和开发服务器启动速度。 官网&#xff1a;https://vitejs.cn/vite3-cn/…...

Linux管道识

深入理解管道 (Pipes)&#xff1a;连接命令的瑞士军刀 在 Linux 和类 Unix 系统&#xff08;包括 macOS&#xff09;的命令行世界里&#xff0c;管道&#xff08;Pipe&#xff09;是一个极其强大且基础的概念。它允许你将一个命令的输出直接作为另一个命令的输入&#xff0c;像…...

LabVIEW 中VI Server导出 VI 配置

该 LabVIEW VI 展示了在 VI Server 中配置和执行 Exported VIs 的过程&#xff0c;实现对服务器端导出 VI 的远程调用与操作。 ​ 具体过程及模块说明 前期配置&#xff1a;需确保在 LabVIEW 的 “Tools> Options > VI Server > Protocols” 路径下&#xff0c;启用 …...

map和set的遗留 + AVL树(1):

在讲解新的东西之前&#xff0c;我们把上节遗留的问题说一下&#xff1a; 遗留问题&#xff1a; 首先&#xff0c;我们的最上面的代码是一个隐式类型转换&#xff0c;我们插入了一对数据。 我们说了&#xff0c;我们的方括号的底层是insert&#xff0c;当我们调用operator的[…...

Java学习手册:Spring Security 安全框架

一、Spring Security 简介 Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架&#xff0c;用于保护 Java 应用程序&#xff0c;尤其是基于 Spring 的应用。它构建在 Spring 框架之上&#xff0c;能够轻松地集成到基于 Spring 的应用程序中&#xff0c;包括…...

pip 常用命令及配置

一、python -m pip install 和 pip install 的区别 在讲解 pip 的命令之前&#xff0c;我们有必要了解一下 python -m pip install 和 pip install 的区别&#xff0c;以便于我们在不同的场景使用不同的方式。 python -m pip install 命令使用 python 可执行文件将 pip 模块作…...

C++_STL

C 标准模板库&#xff08;Standard Template Library&#xff0c;STL&#xff09;是一套功能强大的 C 模板类和函数的集合&#xff0c;它提供了一系列通用的、可复用的算法和数据结构。 STL 的设计基于泛型编程&#xff0c;这意味着使用模板可以编写出独立于任何特定数据类型的…...

[FPGA Video] AXI4-Stream Remapper

Xilinx AXI4-Stream Remapper IP (PG379) 详细介绍 概述 Xilinx LogiCORE™ IP AXI4-Stream Remapper 核是一个专为视频处理设计的模块&#xff0c;用于在不同每时钟像素数&#xff08;Pixels Per Clock, PPC&#xff09;要求之间重新映射视频像素。它支持将输入 AXI4-Stream…...

【数据结构】励志大厂版·初阶(复习+刷题):栈与队列

前引&#xff1a;本篇将由小编与大家一起复习 栈 、队列 的知识点&#xff0c;栈、队列的顺序、链式结构各个缺点好处&#xff0c;如何实现、对于一般的增删查找此篇文章一定再详细不过&#xff01;对代码的注释、何时需要判断、特殊情况&#xff0c;白话文版一解到底&#xff…...

pytest——参数化

之前有说过&#xff0c;通过pytest测试框架标记参数化功能可以实现数据驱动测试。数据驱动测试使用的文件主要有以下类型&#xff1a; txt 文件 csv 文件excel 文件json 文件yaml 文件.... 本文主要讲的就是以上几种文件类型的读取和使用 一.txt 文件读取使用 首先创建一个 …...

第7篇:RESTful API设计与安全防护

在前后端分离架构中&#xff0c;RESTful API是系统交互的核心通道。本文将从接口规范设计到安全防护&#xff0c;全面讲解如何在EggJS中构建安全、规范、易用的API系统&#xff0c;并提供完整的解决方案和最佳实践。 一、标准化API接口规范设计 1. RESTful设计原则 核心要素&…...

CSS 架构与命名规范

CSS 架构与命名规范&#xff1a;BEM、SMACSS 与 OOCSS 实战 引言 在前端开发中&#xff0c;随着项目规模的扩大&#xff0c;CSS 代码往往会变得难以维护和扩展。无组织的样式表会导致命名冲突、权重覆盖问题和样式继承混乱&#xff0c;这些问题在团队协作的大型项目中尤为明显…...

实验二 软件白盒测试

实验二 软件白盒测试 某工资计算程序功能如下&#xff1a;若雇员月工作小时超过40小时&#xff0c;则超过部分按原小时工资的1.5倍的加班工资来计算。若雇员月工作小时超过50小时&#xff0c;则超过50的部分按原小时工资的3倍的加班工资来计算&#xff0c;而40到50小时的工资仍…...

【数据结构】String字符串的存储

目录 一、存储结构 1.字符串常量池 2.字符串哈希表 2.1结构 2.2基础存储单位 2.2.1键对象 2.2.2值对象 二、存储过程 1.搜索 2.创建 三、存储位置 四、存储操作 1.new新建 2.intern入池 这是String类的详解&#xff1a;String类变量 一、存储结构 1.字符串常量池…...

LLMs Tokenizer Byte-Pair Encoding(BPE)

1 Byte-Pair Encoding(BPE) 如何构建词典&#xff1f; 准备足够的训练语料;以及期望的词表大小&#xff1b;将单词拆分为字符粒度(字粒度)&#xff0c;并在末尾添加后缀“”&#xff0c;统计单词频率合并方式:统计每一个连续/相邻字节对的出现频率&#xff0c;将最高频的连续字…...

npm,yarn,pnpm,cnpm,nvm,npx包管理器常用命令

前端比较主流的包管理器主要有三个npm&#xff0c;yarn&#xff0c;pnpm 多层级依赖&#xff0c;通常发生在依赖之间存在复杂的版本要求时 包 A 依赖于包 B1.0.0 包 B 依赖于包 C2.0.0 另一个包 D 也依赖于 C3.0.0 一、NPM (Node Package Manager) https://www.npmjs.cn/…...

使用mybatis实例类和MySQL表的字段不一致怎么办

在 MyBatis 中&#xff0c;当 Java 实体类的属性名与数据库表的字段名不一致时&#xff0c;会导致查询结果无法正确映射。以下是几种常见解决方案及代码示例&#xff1a; 1. 使用 resultMap 显式映射&#xff08;推荐&#xff09; 场景&#xff1a;字段名与属性名差异较大&…...

阿里发布新一代通义千问 Qwen3模型

近日&#xff0c;阿里巴巴发布了新一代通义千问 Qwen3 模型&#xff0c;一举登顶全球最强开源模型。 这是国内首个“混合推理模型”&#xff0c;将“快思考”与“慢思考”集成进同一个模型&#xff0c;大大节省算力消耗。 旗舰模型 Qwen3-235B-A22B 在代码、数学、通用能力等…...

React pros比较机制

将 count1作为prop传递给Memoson组件 引用类型情况 虽然list值没有发生变化&#xff0c;但是仍旧重新渲染 解决方法使用useMemo()函数&#xff0c;传递一个空依赖项 // 传递数据为引用类型 比较的是引用 // 使用useMemo函数改写、const list useMemo(()>{return [1,2,3]},[…...

Flowable7.x学习笔记(十七)审批我的待办

前言 前文完成了我的待办的查询功能&#xff0c;本文就在此基础上从源码解读到完成审批任务的功能&#xff0c;审批界面我就先不带表单&#xff0c;直接单纯审批通过&#xff0c;这里需要注意的事&#xff0c;审批的表单其实每个节点都可能需要不同的表单内容&#xff0c;后续要…...

HTTP 状态码详解:用途与含义

HTTP 状态码详解&#xff1a;用途与含义 HTTP 状态码详解&#xff1a;用途与含义1. &#xff08;2xx&#xff09;成功类200 OK201 Created204 No Content206 Partial Content 2. &#xff08;3xx&#xff09;重定向类301 Moved Permanently302 Found&#xff08;临时重定向&…...

AI日报 · 2025年05月02日 | 再见GPT-4!OpenAI CEO 确认 GPT-4 已从 ChatGPT 界面正式移除

1、OpenAI CEO 确认 GPT-4 已从 ChatGPT 界面正式移除 在处理 GPT-4o 更新问题的同时&#xff0c;OpenAI CEO Sam Altman 于 5 月 1 日在 X 平台发文&#xff0c;正式确认初代 GPT-4 模型已从 ChatGPT 主用户界面中移除。此举遵循了 OpenAI 此前公布的计划&#xff0c;即在 4 …...

ppt设计美化公司_杰青_长江学者_优青_青年长江学者_万人计划青年拔尖人才答辩ppt模板

WordinPPT / 持续为双一流高校、科研院所、企业等提供PPT制作系统服务。 / 近期PPT美化案例 - 院士增选、科学技术奖、杰青、长江学者特聘教授、校企联聘长江、重点研发、优青、青长、青拔.. 杰青&#xff08;杰出青年科学基金&#xff09; 支持已取得突出成果的45岁以下学…...

文章四《深度学习核心概念与框架入门》

文章4&#xff1a;深度学习核心概念与框架入门——从大脑神经元到手写数字识别的奇幻之旅 引言&#xff1a;给大脑装个"GPU加速器"&#xff1f; 想象一下&#xff0c;你的大脑如果能像智能手机的GPU一样快速处理信息会怎样&#xff1f;这正是深度学习的终极目标&…...

HTML5+JavaScript实现连连看游戏之二

HTML5JavaScript实现连连看游戏之二 以前一篇&#xff0c;见 https://blog.csdn.net/cnds123/article/details/144220548 连连看游戏连接规则&#xff1a; 只能连接相同图案&#xff08;或图标、字符&#xff09;的方块。 连线路径必须是由直线段组成的&#xff0c;最多可以有…...

2025年- H19-Lc127-48.旋转矩阵(矩阵)---java版

1.题目描述 2.思路 画出矩阵&#xff0c;新的旋转矩阵的列坐标等于原始矩阵的矩阵长度-i-1&#xff08;也就是减去当前遍历的i&#xff09;&#xff0c;前后对调。然后新的旋转矩阵的横坐标&#xff0c;是原始矩阵的列坐标。 3.代码实现 public class H48 {public void rota…...