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

04_Docker 镜像和仓库

04_Docker 镜像和仓库

文章目录

  • 04_Docker 镜像和仓库
  • 4.1 什么是 Docker 镜像
  • 4.2 列出 Docker 镜像
  • 4.3 拉取镜像
  • 4.4 查找镜像
  • 4.5 构建镜像
    • 4.5.1 创建 Docker Hub 账号
    • 4.5.2 用 Docker 的 commit 命令创建镜像
    • 4.5.3 用 Dockerfile 构建镜像
    • 4.5.5 基于 Dockerfile 构建新镜像
    • 4.5.5 指令失败时会怎样
    • 4.5.6 Dockerfile 和构建缓存
    • 4.5.7 基于构建缓存的 Dockerfile 模板
    • 4.5.8 查看新镜像
    • 4.5.9 从新镜像启动容器
    • 4.5.10 Dockerfile 指令
      • 1.CMD
      • 2.ENTRYPOINT
      • 3.WORDIR
      • 4.ENV
      • 5.USER
      • 6.VOLUME
      • 7.ADD
      • 8.COPY
      • 9.LABEL
      • 10.STOPSIGNAL
      • 11.ARG
      • 12.ONBUILD
  • 4.6 将镜像推送到 Docker Hub
  • 4.7 删除镜像
  • 4.8 运行自己的 Docker Registry
  • 4.9 其他可选 Registry 服务

在第二章,学习了如何安装 Docker,在第三章学习了包括 docker run 在内的很多管理 Docker 容器的命令。

回顾一下 docker run 命令

sudo docker run -i -t --name my_container ubuntu /bin/bash

这条命令将会启动一个新的名为 my_container 的容器,这个容器基于 Ubuntu 镜像并且会启动 Bash shell。

在这章中,我们将主要探讨 Docker 镜像:用来启动容器的基石。将会学习很多关于 Docker 镜像的知识,比如,什么是镜像,如果对镜像进行管理,如何修改镜像,

以及如何创建、存储和共享自己的镜像。还会介绍用来存储镜像的仓库和用来存储仓库的 Registry。

4.1 什么是 Docker 镜像

Docker 镜像是由文件系统叠加而成。最底端是一个引导文件系统,即 bootfs,跟典型的 Linux/Unix 的引导文件系统很像。

Docker 用户几乎永远不会和引导文件系统有什么交互。当一个容器启动后,它将被移到内存中,而引导文件系统则被卸载,以留出更多的内存供 initrd 磁盘镜像使用。

到目前位置,Docker 看起来还很像一个典型的 Linux 虚拟化栈。实际上,Docker 镜像的第二层是 root 文件系统 rootfs,它位于引导文件系统之上。

rootfs 可以是一种或多种操作系统(如 Debain 或者 Ubuntu 文件系统)。

在传统的 Linux 引导过程中,root 文件系统会最先以只读的方式加载,当引导结束并完成了完整性检查后,它才会被切换为读写模式。但是在 Docker 里,root 文件系统

永远只能是只读状态,并且 Docker 利用联合加载技术又会在 root 文件系统层上加载更多的只读文件系统。

联合加载指的是一次同时加载多个文件系统,但是在外面看起来只能看到一个文件系统。联合加载会将各层文件系统叠加到一起,这样最终的文件系统会包含所有底层的文

件和目录。

Docker 将这样的文件系统称为镜像。一个镜像可以放到另一个镜像的顶部。位于下面的镜像称为父镜像,可以以此类推,直到镜像栈的最底部,最底部的镜像称为基础

镜像。最后,当从一个镜像启动时,Docker 会在该镜像的最顶层加载一个读写文件系统。我们在Docker 中运行的程序就是在这个读写层中执行的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uh0vyMPk-1676705738847)(images/VNQiip3c76rT6Cbt0UHt0mrxe0GvKHPslx2hocsungY.png)]

当 Docker 第一次启动一个容器时,初始的读写层是空的。当文件系统发生变化时,这些变化都会应用到这一层上。比如,如果想修改一个文件,这个文件首先会从该读写

下面的只读层复制到该读写层。该文件的只读版本依然存在,但是已经被读写层的该文件副本所隐藏。

通常这种机制被称为写时复制,这也是使 Docker 如此强大的技术之一。每个只读镜像层都是只读的,并且以后永远不会变化。

当创建一个新容器时,Docker 会构建出一个镜像栈,并在栈的对顶端添加一个读写层。这个读写层再加上其下面的镜像层以及一些配置数据,就构成了一个容器。

4.2 列出 Docker 镜像

可以使用 docker images 命令来列出 Docker 主机上可用的镜像

$ sudo docker images
REPOSITORY         TAG       IMAGE ID       CREATED       SIZE
ubuntu             latest    6b7dfa7e8fdb   5 weeks ago   77.8MB

可以看到已经获取了一个镜像列表,他们都源于名为 ubuntu 的仓库。那么,这些镜像是从何而来的呢?我们执行 docker run

命令时,同时进行了镜像下载。

注意:本地镜像都保存在 Docker 宿主机的 /var/lib/docker 目录下。每个镜像都保存在 Docker 所采用的存储驱动目录下。如 aufs 或者 devicemapper。也可以在 /var/lib/docker/containers 目录下面看到所有的容器

镜像从仓库下载下来。镜像保存在仓库中,而仓库存在于 Registry 中。默认的 Registry 是由 Docker 公司运营的公共 Registry 服务,即 Docker Hub。Docker Registry 的也是开源的,也可以允许自己私有的 Registry。

在 Docker Hub (或者用户自己运营的 Registry)中,镜像是保存仓库中的。可以将镜像仓库想象为类似 Git 仓库的东西。它包括镜像,层以及关于镜像的元数据。

每个镜像仓库都可以存放很多镜像(比如,Ubuntu 仓库报告了 Ubuntu 18.04、20.04 镜像)。

拉取镜像

$ sudo docker pull ubuntu:latest
latest: Pulling from library/ubuntu
6e3729cf69e0: Pull complete 
Digest: sha256:27cb6e6ccef575a4698b66f5de06c7ecd61589132d5a91d098f7f3f9285415a9
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest

这里使用 docker pull 命令来拉取 ubuntu 仓库中的 ubuntu:latest 镜像。

再来看看 docker images 命令现在会显示什么结果

sudo docker images

4.3 拉取镜像

用 docker run 命令从镜像启动一个容器时,如果该镜像不在本地,Docker 会先从 Docker Hub 上下载该镜像。

如果没有指定具体的镜像标签,那么 Docker 会自动下载 latest 标签的镜像。

例如,如果本地宿主机上还没有 ubuntu:latest 镜像,如下所示代码将下载该镜像

**docker run 和 默认的 latest 标签 **

sudo docker run -t -i --name next_container ubuntu /bin/bash 

也可以通过 docker pull 命令将镜像拉到本地。

使用 docker pull 命令可以节省从一个新镜像启动一个容器所需的时间。

拉取 fedora 镜像

sudo docker pull fedora:20

可以使用 docker images 命令查看已经下载到本地 Docker 宿主机上

查看 fedora 镜像

sudo docker images fedora

拉取带标签的 fedora 镜像

sudo docker pull fedora:21

4.4 查找镜像

通过 docker search 命令查找所有 Docker Hub 上公共的可用镜像

查找镜像

sudo docker search puppet
NAME                                               DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
puppet/puppetserver                                A Docker Image for running Puppet Server. Wi…   113  
puppet/puppetdb                                    A Docker image for running PuppetDB             37         
puppet/puppetboard                                 The Puppet Board dashboard for PuppetDB         20
...  

提示
也可以在 Docker Hub 网站上在线查找可用镜像
docker search puppet 命令在 Docker Hub 上查找所有带 puppet 的镜像,该命令完成镜像查找并返回下面信息:

  • 仓库名
  • 镜像描述
  • 用户评价 ---- 反应一个镜像的受欢迎程度
  • 是否官方
  • 自动构建 ---- 表示该镜像是由 Docker Hub 的自动构建流程创建的

从 docker search puppet 的结果中拉取一个镜像

拉取 puppet/puppetserver 镜像

sudo docker pull puppet/puppetserver

上面命令将会下载 puppet/puppetserver 镜像到本地(这个镜像里预装了 puppet 服务器)

接着就可以用这个镜像构建一个容器了

从 puppetserver 镜像创建一个容器

sudo docker run -t -i puppet/puppetserver /bin/bash 

4.5 构建镜像

拉取已经构建好的带有定制内容的 Docker 镜像后,修改了镜像,如何更新和管理这些镜像呢?构建 Docker 镜像有以下两种方法:

  • 使用 docker commit 命令
  • 使用 docker build 命令和 Dockerfile 文件。

推荐使用 Dockerfile 的方式构建 Docker 镜像。

注意:一般来说,不是真正“创建”新镜像,而是基于一个已有的基础镜像,如 ubuntu 或 federa 等,构建新镜像而已。如果真的想从零构建一个全新的镜像,可以参考 https://docks.docker.com/articles/baseimages

4.5.1 创建 Docker Hub 账号

构建镜像中很重要的一环就是如何共享和发布镜像。可以将镜像推送到 Docker Hub 或者用户自己私有 Registry 中。需要在 Docker Hub 上创建一个账号再登录到 Docker Hub

登录到 Docker Hub

sudo docker login

4.5.2 用 Docker 的 commit 命令创建镜像

先创建一个容器,并在容器里做出修改,最后再将修改提交为一个新镜像

创建一个要进行修改的定制容器

sudo docker run -t -i ubuntu /bin/bash
root@0c038cfe9e18:/#

接下来,在容器中安装 Apache

root@0c038cfe9e18:/# apt-get -yqq update
...
root@0c038cfe9e18:/# apt-get -y install apache2
...

启动了一个容器,并在里面安装了 Apache。将这个容器作为一个 Web 服务器来运行,所以想把它的当前状态保存下来。这样就不必每次都创建一个新容器并再次在里面安装 Apache 了。为了完成此项工作,需要先使用 exit 命令从容器退出,在运行 docker commit 命令

提交定制容器

sudo docker commit 0c038cfe9e18 ubuntu/apache2
sha256:a982b4ba932a7be8eea058f63d76117f6bbb1bc6d7ba528aab7b7f7e8429d319

上面 docker commit 命令中,指定了要提交的修改过的容器 ID(可以通过 docker ps -l -q 命令得到刚创建的容器 ID)以及一个目标镜像仓库和镜像名,这里是 ubuntu/apache2。需要注意的是,docker commit 提交的只是创建容器的镜像与容器的当前状态之间的差异部分,这使得该更新非常轻量。

检测新检测的镜像

sudo docker images ubuntu/apache2
REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
ubuntu/apache2   latest    a982b4ba932a   43 seconds ago   227MB

也可以在提交镜像时指定更多的数据(包括标签)来详细描述所做的修改。

提交另一个新的定制容器

sudo docker commit -m "A new custom image" -a "sar" 0c038cfe9e18 ubuntu/apache2:webserver
sha256:f55a1c33c1d7ae02f2f85cd2e809724eca09419a89da799d49176c7f32acee56

上面命令中,指定了更多信息,-m 选项用来指定创建的镜像提交信息,-a 选项指定该镜像的作者信息,接着指定了想要提交的容器的ID。最后的 ubuntu/apache2:webserver 指定了镜像的用户名和仓库名,并为该镜像增加了一个 webserver 标签。

可以用 docker inspect 命令来查看新创建的镜像的详细信息

查看提交的镜像的详细信息

sudo docker inspect ubuntu/apache2:webserver
[{"Id": "sha256:f55a1c33c1d7ae02f2f85cd2e809724eca09419a89da799d49176c7f32acee56","RepoTags": ["ubuntu/apache2:webserver"],"RepoDigests": [],"Parent": "sha256:6b7dfa7e8fdbe18ad425dd965a1049d984f31cf0ad57fa6d5377cca355e65f03","Comment": "A new custom image","Created": "2023-02-17T07:17:52.475010058Z","Container": "0c038cfe9e18236e0d11a30d821784a48359efdb70ac691a6d96ebc1cf282e2e","ContainerConfig": {"Hostname": "0c038cfe9e18","Domainname": "","User": "","AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Tty": true,"OpenStdin": true,"StdinOnce": true,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd": ["/bin/bash"],"Image": "ubuntu","Volumes": null,"WorkingDir": "","Entrypoint": null,"OnBuild": null,"Labels": {}},"DockerVersion": "20.10.21","Author": "sar","Config": {"Hostname": "0c038cfe9e18","Domainname": "","User": "","AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Tty": true,"OpenStdin": true,"StdinOnce": true,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd": ["/bin/bash"],"Image": "ubuntu","Volumes": null,"WorkingDir": "","Entrypoint": null,"OnBuild": null,"Labels": {}},"Architecture": "amd64","Os": "linux","Size": 227361419,"VirtualSize": 227361419,"GraphDriver": {"Data": {"LowerDir": "/var/lib/docker/overlay2/abbaa48a8255eb1b3fd747f983154ae6f572175c45f00c0def0acc5bac44f4f8/diff","MergedDir": "/var/lib/docker/overlay2/e010c1a5d80ea0e3442a34012bd396e11442350b62abf01a7d563042558f0f72/merged","UpperDir": "/var/lib/docker/overlay2/e010c1a5d80ea0e3442a34012bd396e11442350b62abf01a7d563042558f0f72/diff","WorkDir": "/var/lib/docker/overlay2/e010c1a5d80ea0e3442a34012bd396e11442350b62abf01a7d563042558f0f72/work"},"Name": "overlay2"},"RootFS": {"Type": "layers","Layers": ["sha256:6515074984c6f8bb1b8a9962c8fb5f310fc85e70b04c88442a3939c026dbfad3","sha256:5a9d75687e1be1431aab443449b39894d846dd50c1b2f33a2b55f638de6f34e5"]},"Metadata": {"LastTagTime": "2023-02-17T15:17:52.930905199+08:00"}}
]

如果想从刚创建对策新镜像运行一个容器,可以使用 docker run 命令

从提交的镜像运行一个新容器

sudo docker run -t -i ubuntu/apache2:webserver /bin/bash

上面命令用了完整的标签 ubuntu/apache2:webserver 来指定这个镜像。

4.5.3 用 Dockerfile 构建镜像

推荐使用 Dockerfile 文件和 docker build 命令来构建镜像。Dockerfile 使用基本的基于 DSL 语法的命令来构建一个 Docker 镜像。推荐使用 Dockerfile 方法来代替 docker commit 命令,因为通过前者来构建镜像更具备可重复性、透明性以及幂等性。一旦有了 Dockerfile,就可以使用 docker build 命令基于 Dockerfile中的指定构建一个新的镜像。

第一个 Dockerfile

创建一个目录并在里面创建初始的 Dockerfile。目的是创建一个包含简单 web 服务器的 Docker 镜像

创建一个示例仓库

mkdir static_web
cd static_web
touch Dockerfile

上面命令创建了一个名为 static_web 的目录用来保存 Dockerfile,这个目录就是我们的构建环境, Docker 则称此环境为上下文或者构建上下文。Docker 会在构建镜像时将构建上下文和该上下文的文件和目录上传到 Docker 守护进程。这样 Docker 守护进程就能直接访问用户想在镜像中存储的任何代码、文件或者其他数据。作为开始,还创建了一个空 Dockerfile,下面通过一个例子来看看如何通过 Dockerfile 构建一个能作为 Web 服务器的 Docker 镜像。

***我们的第一个 Dockerfile ***

# Version: 0.0.1
FROM ubuntu:20.04
MAINTAINER sar "6451085@qq.com"
RUN apt-get update && apt-get install -y nginx
RUN echo 'Hi, I am in your container' \>/usr/share/nginx/html/index.html
EXPOSE 80

该 Dockerfile 由一系列指令和参数组成,每条指令、如 FROM,都必须为大写字母,且后面要跟随一个参数:FROM ubuntu:20.04。Dockerfile 中的指令会按顺序从上到下指向,所以应该根据需要合理安排指令的顺序。每条指令都会创建一个新的镜像层并对镜像进行提交。Docker 大体上按照如下流程执行 Dockerfile 中的指令。

  • Docker 从基础镜像运行一个容器
  • 执行一条指令,对容器做出修改。
  • 执行类似 docker commit 的操作,提交一个新的镜像层次
  • Docker 基于刚提交的镜像运行一个新容器
  • 指向 Dockerfile 中的下一条指令,直到所有指令都执行完毕
    如果用户的 Dockerfile 由于某些原因(如某条指令失败了)没有正常结束,那么用户将得到一个可以使用的镜像。这对调试非常有帮助:可以基于该镜像运行一个具备交互功能的容器,使用最后创建的镜像对为什么用户的指令会失败进行调试。

注意:Dockerfile 也支持注释。以 # 开头的行都会被认为是注释

每个 Dockerfile 的第一条指令必须是 FROM。FROM 指令指定一个已经存在的镜像,后续指令都将基于该镜像进行,这个镜像被称为基础镜像。在上面的示例中,指定了 ubuntu:20.04 作为新镜像的基础镜像。基于这个 Dockerfile 构建的新镜像将以 Ubuntu 20.04 操作系统为基础。在运行一个容器时,必须要指明是基于哪个基础镜像进行构建。MAINTAINER 指令告诉 Docker 该镜像的作者是谁,以及作者的电子邮件地址。最后指定了两条 RUN 值令。RUN 指令会在当前镜像中运行指定的命令。默认情况下,RUN 指令会在 shell 里使用命令包装器 /bin/sh -c 来执行。如果是在一个不支持 shell 的平台上或者不希望在 shell中运行,也可以使用 exec 格式的 RUN 指令

*** exec 格式的 RUN 指令***

RUN [ “apt-get”, " install", “-y”, “nginx” ]

在上面这种方式中,使用一个数组来指定要运行的命令和传递给命令的每个参数。接着设置了 EXPOSE 指令,这条指令告诉 Docker 该容器内的应用程序将会使用容器的指定端口。这并不意味着可以自动访问任意容器运行中服务的端口。出于安全原因,Docker 并不会自动打开该端口,而是需要用户使用 docker run 运行容器时来指定需要打开哪些端口。可以指定多个 EXPOSE 指令来向外部公开多个端口。

4.5.5 基于 Dockerfile 构建新镜像

执行 docker build 命令时,Dockerfile 中的所有指令都会被执行并且提交,并且在该命令成功结束后返回一个新镜像。

***运行 Dockerfile ***

sudo docker build -t="sar/static_web" .
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM ubuntu:20.04
20.04: Pulling from library/ubuntu
b549f31133a9: Pull complete 
Digest: sha256:4a45212e9518f35983a976eead0de5eecc555a2f047134e9dd2cfc589076a00d
Status: Downloaded newer image for ubuntu:20.04---> e40cf56b4be3
Step 2/5 : MAINTAINER sar "6451085@qq.com"---> Running in 933215eb39e4
Removing intermediate container 933215eb39e4---> df2e0c365c98
Step 3/5 : RUN apt-get update && apt-get install -y nginx---> Running in 0410dc3d2945
Removing intermediate container 0410dc3d2945---> f3a5f48a8ccd
Step 4/5 : RUN echo 'Hi, I am in your container'       >/usr/share/nginx/html/index.html---> Running in 26abb5b57315
Removing intermediate container 26abb5b57315---> 1b29e2ec5648
Step 5/5 : EXPOSE 80---> Running in c43521c79c73
Removing intermediate container c43521c79c73---> 55acde2328f4
Successfully built 55acde2328f4
Successfully tagged sar/static_web:latest

使用 docker build 命令来构建新镜像。使用 -t 选项为新镜像设置了仓库和名称,本例中仓库为 sar 镜像名为 static_web。

强烈建议为自动的镜像设置合适的名字以方便追踪和管理。

也可以在构建镜像的过程中为镜像设置一个标签,其使用方法为“镜像名:标签”

在构建时为镜像设置标签

sudo docker build -t="sar/static_web:v1" .

提示:如果没有定制任何标签,Docker 会自动为镜像设置一个 latest 标签

上面命令中最后的 “.” 告诉 Docker 到本地目录中去找 Dockerfile 文件。也可以指定一个 Git 仓库的源地址来指定 Dockerfile 的位置。

从 git 仓库构建 Docker 镜像

sudo docker build -t="sar/static_web:v1" \
git@github.com:sar/docker-static_web

提示:如果在构建上下文的根目录下存在以.dockerignore 命名的文件的话,那么该文件内容会被按行进行分割,每一行都是一条文件过滤匹配模式。与.gitignore 文件类似,该文件用来设置哪些文件不会被当作构建上下文的一部分,因此可以防止它们被上传到 Docker 守护进程中去。

可以看到 Dockerfile 中的每条指令会被顺序执行,而且作为构建过程的最终结果,返回了新镜像的 ID,即 55acde2328f4。构建的每一步及其对应指令都会独立运行,并且在输出最终镜像 ID 之前,Docker 会提交每步的构建结果。

4.5.5 指令失败时会怎样

前面介绍了一个指令失败时将会怎样,下面看一个例子:假设在第 4 步中将软件包的名字弄错了,比如写成了 ngin

再次运行一遍构建过程并看看当指令失败时会怎样。

管理失败的指令

sudo docker build -t="sar/static_web" .
[sudo] password for sar: 
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM ubuntu:20.04---> e40cf56b4be3
Step 2/5 : MAINTAINER sar "6451085@qq.com"---> Using cache---> df2e0c365c98
Step 3/5 : RUN apt-get update && apt-get install -y ngin---> Running in 65031aca8e64
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package ngin
The command '/bin/sh -c apt-get update && apt-get install -y ngin' returned a non-zero code: 100

这时需要调试一下失败。用 docker run 命令来基于这次构建到目前为止已经成功的最后一步创建一个容器。

在上面例子中,使用的镜像 ID 是 df2e0c365c98,

基于最后的成功步骤创建新容器

sudo docker run -t -i df2e0c365c98 /bin/bash
root@c867c1c5d8b2:/# 

这时在这个容器中可以再次运行 apt-get install -y ngin,并指定正确的包名,或者通过进一步的调试来找出到底哪里出错了。

一旦解决了这个问题,就可以退出容器,使用正确的包名修改 Dockerfile 文件,之后再尝试进行构建。

4.5.6 Dockerfile 和构建缓存

由于每一步的构建过程都会将结果提交为镜像,所有 Docker 的构建镜像过程显得非常聪明。它会将之前的镜像层看作缓存。比如,在前面的调试例子中,我们不需要在第 1 步到第 3 步之间进行任何修改,因此 Docker 会将之前构建的镜像当做缓存并作为新的开始点。实际上,当再次进行构建时,Docker 会直接从第 4 步开始。当之前的构建步骤没有变化时,这会节省大量的时间。如果真的在第 1 步到第 3 步之间做了修改,Docker 则会从第一条发生了变化的指令开始。然而,有些时候需要确保构建过程不会使用缓存。比如,如果已经缓存了前面的第 3 步,即 apt-get update 那么 Docker 将不会在此刷新 APT 包缓存。这时用户可能需要取得每个包的最新版本。要想略过缓存功能,可以使用 docker build 的 --no-cache 标志,

*** 忽略 Dockerfile 的构建缓存 ***

sudo docker build --no-cache -t="sar/static_web" .

4.5.7 基于构建缓存的 Dockerfile 模板

构建缓存带来的一个好处就是,可以实现简单的 Dockerfile 模板(比如在 Dockerfile 文件顶部增加仓库或者更新包,从而尽可能确保缓存命中)。我们一般都会在自己的 Dockerfile 文件顶部使用相同的指令集模板,比如对 Ubuntu,使用下面所示模板。

Ubuntu 系统的Dockerfile模板

FROM ubuntu:20.04
MAINTAINER sar "6451085@qq.com"
ENV REFRESHED_AT 2023-02-18
RUN apt-get -qq update

上面 Dockerfile 首先通过 FROM 指令为新镜像设置了一个基础镜像 ubuntu:20.04。接着使用 MAINTAINER 指令添加维护者的联系信息;接下来使用 ENV 指令在镜像中设置环境变量。在上面例子中,通过 ENV 指令来设置一个名为 REFRESHED_AT 的环境变量,这个环境变量用来表明该镜像模板最后的更新时间。最后,使用了 RUN 指令来运行 apt-get -qq update 命令,该指令运行时将会刷新 APT 包缓存,用来确保能将要安装的每个软件包更新到最新版本中。有了这个模板,如果想刷新一个构建,只需要修改 ENV 指令中的日期。这使 Docker 在命中 ENV 指令时开始重置这个缓存,并运行后续指令而无须依赖该缓存。也就是说, RUN apt-get update 这条指令将会被再次执行,包缓存也将会被刷新为最新内容。可扩展次模板,比如适配到不同的平台或者添加额外的需求。比如,可将上面示例改为支持一个 fedora 镜像。

*** Fedora Dockerfile 模板***

FROM ubuntu:20.04
MAINTAINER sar "6451085@qq.com"
ENV REFRESHED_AT 2023-02-18
RUN yum -q makecache

4.5.8 查看新镜像

可以使用 docker images 命令来查看新构建的镜像

列出新的 Docker 镜像

sudo docker images ubuntu:static_web
REPOSITORY   TAG          IMAGE ID       CREATED       SIZE
ubuntu       static_web   5a6d038755d2   4 weeks ago   174MB

如果想更深入探求镜像是如何构建出来的,可以使用 docker history m命令。

使用 docker history 命令

sudo docker history 5a6d038755d2
IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT
5a6d038755d2   4 weeks ago    /bin/sh -c #(nop)  EXPOSE 80                    0B        
c947b068c315   4 weeks ago    /bin/sh -c echo 'Hi, I am in your container'27B       
d1efd38f7785   4 weeks ago    /bin/sh -c apt-get update && apt-get install…   96.3MB    
efe03cf82c02   4 weeks ago    /bin/sh -c #(nop)  MAINTAINER sar "6451085@q…   0B        
6b7dfa7e8fdb   2 months ago   /bin/sh -c #(nop)  CMD ["bash"]                 0B        
<missing>      2 months ago   /bin/sh -c #(nop) ADD file:481dd2da6de715252…   77.8MB

4.5.9 从新镜像启动容器

可以基于新构建的镜像启动一个新容器,来检查构建工作是否一切正常。

从新镜像启动一个容器

sudo docker run -d -p 80 --name static_web ubuntu:static_web nginx -g "daemon off;"
bc3ec35dabb14c5a600dca86636680d525dbb05baf84ec0d7269e63c11485a33

使用 docker run 命令,基于刚才构建的镜像的名字,启动了一个名为 static_web 的新容器。同时指定了 -d 选项,告诉 Docker 以分离的方式在后台运行。这种方式非常时候运行类似 Nginx 守护进程这样需要长时间运行的进程。同时还指定了需要在容器中运行的命令:nginx -g “daemon off;”。这将以前台运行的方式启动 Nginx,来作为我们的 Web 服务器。还使用了一个新的 -p 标志,该标志用来控制 Docker 在运行时应该公开哪些网络端口给外部(宿主机)。运行一个容器时, Docker 可以通过两种方法来在宿主机上分配端口。

  • Docker 可以在宿主机上随机选择一个位于 32768 ~ 61000 的一个比较大的端口来映射到容器中的 80 端口上。
  • 可以在 Docker 宿主机中指定一个具体的端口号来映射到容器中的 80 端口上。

docker run 命令将在 Docker 宿主机上随机打开一个端口,这个端口会连接到容器中的 80 端口上。使用 docker ps 命令来看一下容器的端口分配情况。

***查看 Docker 端口映射情况 ***

sudo docker ps -l
CONTAINER ID   IMAGE               COMMAND                  CREATED          STATUS          PORTS                                     NAMES
bc3ec35dabb1   ubuntu:static_web   "nginx -g 'daemon of…"   22 seconds ago   Up 21 seconds   0.0.0.0:32768->80/tcp, :::32768->80/tcp   static_web

可以看到,容器中的 80 端口被映射到了宿主机的 49154 上。也可以通过 docker port 来查看容器的端口映射情况。

*** docker port 命令***

sudo docker port bc3ec35dabb1 80

-p 选项还为我们将容器端口向宿主机公开时提供了一定的灵活性。比如,可以指定将容器中的端口映射到 Docker 宿主机的某一特定端口上。

通过 -p 选项映射到特定端口

sudo docker run -d -p 80:80 --name static_web ubuntu:static_web nginx -g "daemon off;"

上面命令会将容器内的 80 端口绑定到本地宿主机的 80 端口上。必须非常小心地使用这种直接绑定的做法:如果要运行多个容器,只有一个容器能成功地将端口绑定到本地宿主机上,这将会限制 Docker 的灵活性。为了避免这个问题,可以将容器内的端口绑定到不同的宿主机端口上去,

绑定不同的端口

sudo docker run -d -p 8080:80 --name static_web ubuntu:static_web nginx -g "daemon off;"

这条命令会将容器中的80端口绑定到宿主机的8080端口上。也可以将端口绑定限制在特定的网络接口(即 IP 地址)上。

绑定到特定的网络接口

sudo docker run -d -p 127.0.0.1:8080:80 --name static_web ubuntu:static_web nginx -g "daemon off;"

将容器内的 80 端口绑定到了本地宿主机的 127.0.0.1 这个 IP 的 8080 端口上。也可以使用类似的方式将容器内的 80 端口绑定到宿主机的随机端口上.

绑定到特定的网络接口的随机端口

sudo docker run -d -p 127.0.0.1::80 --name static_web ubuntu:static_web nginx -g "daemon off;"

这里,并没有指定具体要绑定的宿主机上的端口号,只是指定了一个 IP 地址 127.0.0.1,这时我们可以使用 docker inspect 或者docker port 命令来查看容器内的 80 端口具体被绑定到了宿主机的哪个端口上。

注意:也可以通过在端口绑定时使用 /udp 后缀来指定 UDP 端口

Docker 还提供了一个更简单的方式,即 -P 参数,该参数可以用来对外公开在 Dockerfile 中通过 EXPOSE 指令公开的所有端口。

使用 docker run 命令对外公开端口

sudo docker run -d -P --name static_web ubuntu:static_web nginx -g "daemon off;"

该命令会将容器内的 80 端口对本地宿主机公开,并且绑定到宿主机的一个随机端口上。该命令会将用来构建该镜像的 Dockerfile 文件中的 EXPOSE 指令指定的其他端口也一并公开。有了这个端口号,就可以使用本地宿主机的 IP 地址或者 127.0.0.1 的 localhost 连接到运行中的容器,查看 web 服务器内容了。

*** 使用 curl 连接到容器***

curl localhost:49154

4.5.10 Dockerfile 指令

Dockerfile 中常用的指令有:CMD、ENTRYPOINT、ADD、COPY、VOLUME、WORKDIR、USER、ONBUILD、LABEL、STOPSIGNAL、ARG 和 ENV 等。

可以在 http://docs.docker.com/reference/builder 查看 Dockerfile 中可以使用的全部指令清单。

1.CMD

CMD 指令用于指定一个容器启动时要运行的命令。这有点儿类似于 RUN 指令,只是 RUN 指令是指定镜像被构建时要运行的命令,而 CMD 是指定容器被启动时要运行的命令。这和使用 docker run 命令启动容器时指定要运行的命令非常类似。

指定要运行的特定命令

sudo docker run -i -t ubuntu:static_web /bin/true

可以认为上面所示命令和在 Dockerfile 中使用下面 CMD 指令是等效的

使用 CMD 指令

CMD ["/bin/true"]

当然也可以为要运行的命令指定参数

***给 CMD 指令传递参数 ***

CMD ["/bin/bash", "-l"]

上面将 -l 标志传递给了 /bin/bash 命令

警告:需要注意的是,要运行的命令是存放在一个数组结构中的。这将告诉 Docker 按指定的原样来运行命令。当然也可以不使用数组而是指定 CMD 指令,这时候 Docker 会在指定的命令钱加上/bin/sh -c 。这在执行该命令的时候可能会导致意料之外的行为,所以 Docker 推荐一直使用以数组语法来设置要执行的命令。

2.ENTRYPOINT

ENTRYPOINT 指令与 CMD 指令非常类似,也很容易和 CMD 指令弄混。这两条指令有什么区别呢?我们可以在 docker run 命令行中覆盖 CMD 指令。有时候,我们希望容器会按照我们想象的那样去工作,这时候 CMD 就不太合适了。而 ENTRYPOINT 指令提供的命令则不容易在启动容器时被覆盖。实际上,docker run 命令行中指定的任何参数都会被当做参数再次传递给 ENTRYPOINT 指令中指定的命令。

指定ENTRYPOINT指令

ENTRYPOINT ["/usr/sbin/nginx"]

类似 CMD 指令,也可以在该指令中通过数组的方式为命令指定相应的参数

ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]

3.WORDIR

WORKDIR 指令用来在从镜像创建一个新容器时,在容器内部设置一个工作目录,ENTRYPOINT 和/或 CMD 指定的程序会在这个目录下执行。

我们可以使用该指令为 Dockerfile 中后续的一系列指令设置工作目录,也可以为最终的容器设置工作目录。如下代码为特定的指令设置不同的工作目录。

使用WORKDIR指令

WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT [ "rackup" ]

上面代码将工作目录切换为 /opt/webapp/db 后运行了 boundle install 命令,之后又将工作目录设置为 /opt/webapp,最后设置了 ENTRYPOINT 指令来启动 rackup 命令。

可以通过 -w 标志在运行时覆盖工作目录

覆盖工作目录

sudo docker run -ti -w /var/log ubuntu pwd /var/log

上面命令会将容器内的工作目录设置为 /var/log

4.ENV

ENV 命令用来在镜像构建过程中设置环境变量

在 Dockerfile文件中设置环境变量

ENV RVM_PATH /home/rvm

这个新的环境变量可以在后续的任何 RUN 指令中使用,这就如同在命令前面指定了环境变量前缀一样。

为 RUN 指令设置前缀

RUN gem install unicorn

5.USER

6.VOLUME

7.ADD

8.COPY

9.LABEL

10.STOPSIGNAL

11.ARG

12.ONBUILD

4.6 将镜像推送到 Docker Hub

镜像构建完毕后,可以将它上传到 Docker Hub 上去。通过 docker push 命令将镜像推送到 Docker Hub。

尝试推送 root 镜像

sudo docker push static_web

4.7 删除镜像

如果不需要一个镜像了,可以将它删除。可以使用 docker rmi 命令来删除一个镜像。

删除 Docker 镜像

sudo docker rmi ubuntu:static_web

删除了 ubuntu:static_web 镜像。在这里也可以看到 Docker 的分层文件系统:每一个 Deleted: 行都代表一个镜像层被删除。

同时删除多个 Docker 镜像

sudo docker rmi ubuntu:static_web ubuntu:vim 

删除所有镜像

sudo docker rmi `docker images -a -q`

4.8 运行自己的 Docker Registry

4.9 其他可选 Registry 服务

相关文章:

04_Docker 镜像和仓库

04_Docker 镜像和仓库 文章目录04_Docker 镜像和仓库4.1 什么是 Docker 镜像4.2 列出 Docker 镜像4.3 拉取镜像4.4 查找镜像4.5 构建镜像4.5.1 创建 Docker Hub 账号4.5.2 用 Docker 的 commit 命令创建镜像4.5.3 用 Dockerfile 构建镜像4.5.5 基于 Dockerfile 构建新镜像4.5.5…...

postman-enterprise-API

Postman 是一个用于构建和使用 API 的 API 平台。Postman 简化了 API 生命周期的每个步骤并简化了协作&#xff0c;因此您可以更快地创建更好的 API。 API存储库 在一个中央平台上围绕您的所有 API 工件轻松存储、编目和协作。Postman 可以存储和管理 API 规范、文档、工作流配…...

【ESP 保姆级教程】玩转emqx MQTT篇② ——保留消息和遗嘱消息

忘记过去,超越自己 ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-02-18 ❤️❤️ 本篇更新记录 2023-02-18 ❤️🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝🙏 此博客均由博主单独编写,不存在任何商业团队运营,如发现错误,请…...

开启慢查询日志方法

步骤 开启慢查询日志 SET GLOBAL slow_query_log on;SHOW VARIABLES like slow_query_log;设置时间限制 SET GLOBAL long_query_time 1; -- 单位sSHOW VARIABLES LIKE %long_query_time%;因为long_query_time参数只对新的数据库连接生效&#xff0c;所以还需要重启msql客户端…...

宝塔搭建实战人才求职管理系统admin前端vue源码(二)

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 上一期给大家分享骑士cms后台端在宝塔的搭建部署方式&#xff0c;这套系统是前后端分离的架构&#xff0c;前端是用vue2开发的&#xff0c;还需要在本地打包手动发布上宝塔&#xff0c;所以本期给大家分享&#x…...

SpringMVC——基础知识

基本概念 SpringMVC是基于servlet api构造的原始web框架&#xff0c;全称是Spring Web MVC 而MVC的全称是Model View Controller&#xff0c;翻译成中文分别是“模型”&#xff0c;“视图”&#xff0c;“控制器”&#xff0c;这是一种软件的架构模式 Model&#xff1a;用来…...

论文浅尝 | SpCQL: 一个自然语言转换Cypher的语义解析数据集

笔记整理&#xff1a;郭爱博&#xff0c;国防科技大学博士论文发表会议&#xff1a;The 31th ACM International Conference on Information and Knowledge Management&#xff0c;CIKM 2022动机随着社交、电子商务、金融等行业的快速发展&#xff0c;现实世界编织出一张庞大而…...

MongoDB 使用规范与限制及最佳实践

MongoDB 灵活文档的优势 灵活库/集合命名及字段增减同一字段可存储不同类型数据Json 文档可多层次嵌套文档对于开发而言最自然的表达 MongoDB 灵活文档的烦恼 数据库集合字段名千奇百怪同一字段数据类型各不一样业务异常可能写入“脏”数据 1.1 库命名规范 不能为空字符串 &…...

第五十六章 树状数组(一)

第五十六章 树状数组一、前缀和的缺陷二、树状数组1、作用2、算法分析3、算法实现&#xff08;1&#xff09;lowbits()&#xff08;2&#xff09;插入&#xff08;3&#xff09;查询三、例题1、问题题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示2、代码一、前缀和…...

kubernetes教程 --Pod控制器详解

Pod控制器详解 介绍 Pod是kubernetes的最小管理单元&#xff0c;在kubernetes中&#xff0c;按照pod的创建方式可以将其分为两类&#xff1a; 自主式pod&#xff1a;kubernetes直接创建出来的Pod&#xff0c;这种pod删除后就没有了&#xff0c;也不会重建控制器创建的pod&am…...

N2750A Agilent Keysight HP 差分探头1.5GHz

N2750A Agilent Keysight HP 差分探头13554860890 N2750A 是 Agilent Keysight HP 的 1.5 GHz 差分探头。 特征&#xff1a; N2750A&#xff1a;1.5 GHz 衰减比&#xff1a;2:1 或 10:1&#xff08;可切换&#xff09; 动态范围&#xff1a; 5 V 或 10 Vpp&#xff08;10:1 时…...

一文搞懂Linux内核进程CPU调度基本原理

为什么需要调度 进程调度的概念比较简单&#xff0c;我们假设在一个单核处理器的系统中&#xff0c;同一时刻只有一个进程可以拥有处理器资源&#xff0c;那么其他的进程只能在就绪队列中等待&#xff0c;等到处理器空闲之后才有计划获得处理器资源来运行。在这种场景下&#…...

java ssm爱宠宠物医院挂号预约系统管理系统设计与实现

本课题所实现的宠物医院网站是基于网页&#xff0c;它可以实现网上预约挂号&#xff0c;评价等基本功能。用户只要手边有一部手机或者一台电脑&#xff0c;可以上网浏览网页&#xff0c;便可以使用本系统&#xff0c;没有时间和地点的限制&#xff0c;使得就医预约&#xff0c;…...

自动化测试工具_Jmeter

【课程简介】 接口测试是测试系统组件间接口的一种测试,接口测试天生为高复杂性的平台带来高效的缺陷监测和质量监督能力,平台越复杂&#xff0c;系统越庞大&#xff0c;接口测试的效果越明显。在接口测试大行其道的今天,测试工具也愈发重要,Jmeter作为一款纯 Java 开发的测试…...

不是所有人都适合职场

一个读者的提问&#xff1a; 洋哥&#xff0c;我目前工作五年在一家大厂&#xff0c;属于那种什么事情上手都很快的人&#xff0c;并且搞定新问题能产生沉浸般的快感。我的本职是程序员&#xff0c;但运营思路产品方法也都会一些&#xff0c;甚至有时候提出的方案效果比产品&a…...

JSP 和 JSTL

文章目录&#x1f353;摘要&#x1f353;一、JSP&#x1f349;1.1 JSP的基础语法&#x1f36b;1.1.1 简介&#x1f36b;1.1.2 依赖&#x1f36b;1.1.3 注释&#x1f36b;1.1.4 Scriptlet 脚本&#x1f349;1.2 JSP的指令标签&#x1f36b;1.2.1 include 静态包含&#x1f36b;1…...

数据分析| Pandas200道练习题,使用Pandas连接MySQL数据库

文章目录使用Pandas连接数据库编码环境依赖包read_sql_query()的使用read_sql_table()的使用read_sql() 函数的使用to_sql()写入数据库的操作删除操作更新操作总结&#xff1a;使用Pandas连接数据库 通过pandas实现数据库的读&#xff0c;写操作时&#xff0c;首先需要进行数据…...

【Node.js】全局可用变量、函数和对象

文章目录前言_dirname和_filename变量全局函数setTimeout(cb,ms)clearTimeout(t)setInterval(cb,ms)clearInterval(t)setImmediate(cb)clearImmediate()console对象console.info([data][,...])console.error([data][,...])console.warn([data][,...])console.dir(obj[,options]…...

package.json 开发依赖与运行时依赖

文章目录前言一、生产环境与开发环境二、dependencies二、devDependencies总结前言 我已经使用npm接近两年了, 但对于package.json内的dependencies 和devDependencies也只是知道什么依赖该放什么部分, 至于为什么放到这个部分, 我不是很了解… 呃, 还是去了解一下. 一、生产环…...

关于最短路径算法中边的权值的思考

关于最短路径算法中边的权值的思考 不管是单源最短路径算法&#xff1a;Dijkstra Bellman-ford 还是多源最短路径算法&#xff1a;floyed Johnson 我们都绕不开的一件事就是&#xff0c;边的权值wi,jw_{i,j}wi,j​ 下面我们从多个角度谈边的权值 1.权值恒定 它是指对于每条边…...

LVGL开发教程:二、ESP-IDF 使用CmakeList管理自己的文件以及文件夹

本文需要已经安装了Vscode+IDF插件没有安装的请提前安装一下,IDF插件为乐鑫的插件不需要翻墙。需要环境搭建请看下面链接。 环境搭建: VScode+platformIO和Vscode+ESP-IDF两种开发环境搭建 项目例程下载地址: IDF-CmakeTes,密码:8888 另外,由于你和我的路径不一致,下载的工…...

与感受野相关的几种网络结构

一、Inception 1. Inception v1 目的 通过设计一个稀疏网络结构&#xff0c;但是能够产生稠密的数据&#xff0c;既能增加神经网络表现&#xff0c;又能保证计算资源的使用效率。 结构 图1-1 Inception v1结构图 特点 共4个通道&#xff0c;其中3个卷积通道分别使用111111…...

day19_抽象类丶接口

由来 当我们声明一个几何图形类&#xff1a;圆、矩形、三角形类等&#xff0c;发现这些类都有共同特征&#xff1a;求面积、求周长、获取图形详细信息。那么这些共同特征应该抽取到一个公共父类中。但是这些方法在父类中又无法给出具体的实现&#xff0c;而是应该交给子类各自…...

【网安神器篇】——系统指纹探测工具finger

作者名&#xff1a;白昼安全主页面链接&#xff1a; 主页传送门创作初心&#xff1a; 以后赚大钱座右铭&#xff1a; 不要让时代的悲哀成为你的悲哀专研方向&#xff1a; web安全&#xff0c;后渗透技术每日鸡汤&#xff1a; 我不想停下&#xff0c;因为这次出发的感觉太好了一…...

Prometheus离线tar包安装

Prometheus离线tar包安装实验环境一、部署前操作二、Master2.1下载2.2解压2.3更改服务目录名称2.4创建系统服务启动文件2.5配置修改2.6启动并设置开机自启2.7访问2.8添加node节点2.8.1 添加方法2.8.2修改Prometheus配置&#xff08;Master&#xff09;————————————…...

PostgreSQL查询引擎——SELECT STATEMENTS SelectStmt

SelectStmt: select_no_parens %prec UMINUS| select_with_parens %prec UMINUS select_with_parens:( select_no_parens ) { $$ $2; }| ( select_with_parens ) { $$ $2; } 该规则返回单个SelectStmt节点或它们的树&#xff0c;表示集合操作树(set-operation tree…...

零信任-易安联零信任介绍(11)

​目录 ​易安联零信任公司介绍 易安联零信任发展路线 易安联零信任产品介绍 易安联零信任架构 易安联零信任解决方案 易安联零信任发展展望 易安联零信任公司介绍 易安联是一家专业从事网络信息安全产品研发与销售&#xff0c;是行业内领先的“零信任”解决方案提供商&…...

C++ STL——map和set的使用

文章目录1. 关联式容器1.1 键值对1.2 树形结构的关联式容器2. set2.1 set的介绍2.2 set的插入2.3 set的删除和查找2.4 lower_bound和upper_bound3. multiset3.1 count4. map4.1 map的介绍4.2 map的插入4.3 map的遍历4.4 map的[ ]5. multimap1. 关联式容器 我们之前学的vector、…...

【Python】thread使用

目录1、Condition条件变量使用2、event通信3、Semaphore信号量使用4、setDaemon设置守护线程5、threadPool_map使用6、threadPool使用7、threadingTimer1、Condition条件变量使用 # encoding:utf-8 Condition 提供了一种多线程通信机制&#xff0c; 假如线程 1 需要数据&#…...

计网传输层协议:UDP和TCP

文章目录一. 应用层和传输层的联系二. UDP协议三. TCP协议1. TCP报头介绍2. TCP实现可靠传输的核心机制2.1 确认应答2.2 超时重传3. 连接管理(三次握手, 四次挥手)3.1 建立连接(三次握手)3.2 断开连接(四次挥手)4. 滑动窗口5. 流量控制6.拥塞控制7. 延时应答8. 捎带应答9. 面向…...

交互式网站的发展/优化大师下载

一、本节课教学内容的本质、地位、作用分析分类加法计数原理与分步乘法计数原理是人类在大量的实践经验的基础上归纳出的基本规律&#xff0c;它们不仅是推导排列数、组合数计算公式的依据&#xff0c;而且其基本思想方法也贯穿在解决本章应用问题的始终&#xff0c;在本章中是…...

网站建设的步骤/今日头条官网

如果要给C11颁一个“最令人困惑新词”奖&#xff0c;constexpr十有八九会折桂。当用于对象上面&#xff0c;它本质上就是const的加强形式&#xff0c;但是当它用于函数上&#xff0c;意思就大不相同了。有必要消除困惑&#xff0c;因为你绝对会用它的&#xff0c;特别是当你发现…...

张家口建设委员会网站/舆情报告

1.打开网站http://125.74.125.69:8091/&#xff0c; 2.输入用户名&#xff0c;密码 3.选择页面上右上角的菜单 4.点击人口库 5.选择人口信息&#xff0c; 6.根据图中提示 7.跟据图中提示完成下表 转载于:https://www.cnblogs.com/Happy-Eric-1/p/10396847.html...

公司做网站费用会计处理/新冠疫情最新情况最新消息

将已有项目代码加入svn版本控制 - TortoiseSVN入门篇Windows下SVN实用教程(以TortoiseSVN作为客户端(client)) 翻译&#xff1a; Bravo Young Next: 版本库的备份与存储 目录 导引安装Subversion安装TortoiseSVN一步步地操作 步骤0. 设置全局忽略文件类型(此步骤为可选)步骤1. …...

广西建设厅官网站/北京seo优化

第1条、考虑用静态工厂方法替代构造器 在基本类型的包装类中&#xff0c;如Boolean存在静态工厂方法&#xff1a; public static Boolean valueOf(boolean b){return b ? Boolean.TRUE : Boolean.FALSE; } 考虑静态工厂方法的优势&#xff1a; 静态工厂方法有自己的名…...

ps海报素材网/seo网站优化知识

写一个脚本/root/bin/yesorno.sh&#xff0c;提示用户输入yes或no,并判断用户输入的是yes还是no,或是其它信息#!/bin/bash read -p"put your answer(yes or no):" Answer case $Answer in y|Y|[yY][eE][sS]) //判断用户输入yes时的一切可能性echo &q…...