[docker]七、docker镜像的制作(超详细)、docker镜像结构原理、镜像的分享——harbor

2023-10-28

目录

1、镜像(images)

1.1、Dockerfile:是制作镜像的文件

2、镜像结构原理

2.1、base 镜像

2.1.1、rootfs和bootfs

2.1.2、base 镜像提供的是最小安装的 Linux 发行版

2.2、镜像分层

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

2.3、容器可写层

3、制作镜像

3.1、制作镜像的过程

3.1.1、练习

3.2、Minimize the number of layers,减少层数

3.3、制作镜像的流程中可能会出现的问题

3.4、copy和add的区别

4、若是今后开发人员开发了新的代码,容器里的代码是否会自动更新?

5、为什么有些容器启动的时候需要使用 -it 有些不需要?

5.1、daemon off

6、使用容器跑一个go程序

7、harbor


1、镜像(images)

是打包好的软件,由程序代码、基础系统、依赖关系的软件包、系统库和工具组成 。

官方解释:

A Docker container image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings.

Docker容器映像是一个轻量级的、独立的、可执行的软件包,包含了运行应用程序所需的一切:代码、运行时、系统工具、系统库和设置。

[区分一下之前我们提到的centos镜像]

我们之前在linux安装centos提到的镜像是镜像站点的centos镜像,也叫映像文件。因为centos的官方是在国外,若是我们国内用户访问到国外的话,网速就特别慢。这个时候中国的企业就在中国搞一个镜像站点,把国外的东西复制一份放到国内来。那么国内的这个服务器就成为了国外服务器的镜像站点了。而我们现在要讲的这个镜像叫images,是docker容器里的镜像可以理解为是打包好的软件,是一个app的封装。

1.1、Dockerfile:是制作镜像的文件

参考文献:Docker Hub

点开里边的任意一个版本就能看到它的dockerfile  

2、镜像结构原理

转载部分内容:第八篇:Docker镜像结构原理_Linux运维开发的技术博客_51CTO博客

2.1、base 镜像

base 镜像有两层含义:

  • 不依赖其他镜像,从 scratch 构建。

scratch:是最基础的docker镜像,相当于一个地基。

  • 其他镜像可以在此基础上进行扩展。

用别的镜像做基础镜像---》站在巨人的肩膀

base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS 等,以 CentOS 为例学习 base 镜像包含哪些内容。

这里拉了很多镜像,是因为这个镜像就是在一些基础镜像的基础上加一些镜像组成了python这个镜像

使用docker pull centos下载最新版本的Centos镜像也就207M左右,而我们平时下载一个原生的centos镜像都是4G,对于 Docker 初学者都会有这个疑问。

下载python镜像:

下面来了解下Linux 操作系统由内核空间和用户空间组成,如下图所示:


2.1.1、rootfs和bootfs

bootfs和rootfs里面就是一些程序和文件

bootfs:容器启动的时候需要的内容,是linux kernel 提供了bootfs

内核空间是 kernel,Linux 刚启动时会加载 bootfs 文件系统,之后 bootfs 会被卸载掉。用户空间的文件系统是 rootfs,包含我们熟悉的 /dev, /proc, /bin 等目录。对于 base 镜像来说,底层直接用 Host 的 kernel,自己只需要提供 rootfs 就行了。而对于一个精简的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了。相比其他 Linux 发行版,CentOS 的 rootfs 已经算臃肿的了,alpine 还不到 10MB。我们平时安装的 CentOS 除了 rootfs 还会选装很多软件、服务、图形桌面等,需要好几个 GB 就不足为奇了。

即我们的容器全部用宿主机的内核kernel,而容器提供的只是不同的系统文件。bootfs,容器启动的时候需要的内容;rootfs,容器内部的操作系统

2.1.2、base 镜像提供的是最小安装的 Linux 发行版

下面是 CentOS 镜像的 Dockerfile 的内容:

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

2.2、镜像分层

Docker 支持通过扩展现有镜像,创建新的镜像。
实际上,Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的。比如我们现在制作一个镜像

root@dockerserever:~# docker pull debian  # 下载一个debian的镜像
 


① 新镜像不再是从 scratch 开始,而是直接在 Debian base 镜像上构建。
② 安装 emacs 编辑器。
③ 安装 apache2。
④ 容器启动时运行 bash。

构建过程如下图所示:

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

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

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

比如:若是有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

这时可能就有人会问了:如果多个容器共享一份基础镜像,当某个容器修改了基础镜像的内容,比如 /etc 下的文件,这时其他容器的 /etc 是否也会被修改?
答:不会!因为修改会被限制在单个容器内。因为它有Copy-on-Write特性。

2.3、容器可写层

当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。

所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。
只有容器层是可写的,容器层下面的所有镜像层都是只读的。
下面我们深入讨论容器层的细节。
镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /a,上层的 /a 会覆盖下层的 /a,也就是说用户只能访问到上层中的文件 /a0.。在容器层中,用户看到的是一个叠加之后的文件系统。

1.添加文件
在容器中创建文件时,新文件被添加到容器层中。
2. 读取文件
在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,打开并读入内存。
3. 修改文件
在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。
4. 删除文件
在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。

只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。
这样就解释了我们前面提出的问题:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。

3、制作镜像

[为什么要制作镜像?docker hub上不是有很多镜像吗?]

  • 不能满足我们的需求
  • 不够安全,有安全隐患

[谁去制作镜像?]

一般都是由开发人员去制作,也可以运维人员去制作或者权力比较大的工作人员,因为镜像里边涉及了公司里的机密代码等。

3.1、制作镜像的过程

第一步:新建一个空文件夹并pull一个python镜像

[root@centos7-docker ~]# mkdir /mydocker
[root@centos7-docker ~]# cd /mydocker/

第二步:新建Dockerfile

这里要注意,不要把注释放在代码的后边,不然待会run的时候运行不出来。

FROM python
# 我们在docker容器的工作目录。即进入docker容器的时候,所在的目录
WORKDIR /app 

# 复制当前目录下的所有内容到容器里边/app目录下
ADD . /app 

# 定义一个数据卷
VOLUME ["/data_flask"]

# requirements.txt会写明我这个项目会需要一些什么python库;\
# 这条命令是告诉容器把requirements.txt里需要的库到pypi.python.org这个网站里边下载
# 这条命令是在制作镜像的时候要运行的,而不是在启动容器的时候运行的,这个要记住很容易混淆
RUN pip install  --trusted-host pypi.python.org -r requirements.txt

# 暴露我们的端口号,即容器对外开放哪个端口
EXPOSE 80

# 定义环境变量
ENV NAME World
ENV AUTHOR fan

# 容器起来之后运行的第一个程序,这个是容器启动的时候运行,要和上边区分开来
CMD ["python", "app.py"]

另外的一些解释

第三步:新建requirements.txt文件

Flask
Redis

第四步:新建app.py文件

from flask import Flask
from redis import Redis,RedisError
import os
import socket

# Connect to Redis   连接redis数据库
redis = Redis(host="redis", db=0,socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

# 访问这台机器的根"/"
@app.route("/")
def hello():
	try:
		# 若是有人访问,会往redis数据库里增加一个值
		visits = redis.incr("counter")
	except RedisError:
		# 这个visits是从redis数据库里获取的
		visits = "<i>cannot connect to REdis, counter disabled</i>"

	html = "<h3>Hello {name}!</h3>" \
			"<b>Hostname:</b>{hostname}<br/>" \
			"<b>Visits:</b> {visits}"
	# 返回这个主机名(socket.gethostname())和访问的次数(visits)
	return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(),visits=visits)

if __name__ == "__main__":
	app.run(host='0.0.0.0', port=80)

第五步:生成镜像,需要一点时间,因为要下载很多东西

# -t,打个标记,等于给这个镜像取个名字
[root@centos7-docker mydocker]# docker build -t friendlyhello_1 .

生成镜像的时候,背后会自动生成临时的容器去执行Dockerfile里的命令,检测这个命令是否可以运行 

[查看结果] 

[root@centos7-docker ~]# docker images
REPOSITORY          TAG       IMAGE ID       CREATED         SIZE
friendlyhello_1     latest    82dda8055795   7 hours ago     935MB

 第六步:运行容器

[root@centos7-docker docker]# docker run -d -p 5080:80 --name hello-1 friendlyhello_1
[root@centos7-docker docker]# docker ps
CONTAINER ID   IMAGE             COMMAND           CREATED         STATUS         PORTS                                   NAMES
c6dbd5383c7a   friendlyhello_1   "python app.py"   7 minutes ago   Up 7 minutes   0.0.0.0:5080->80/tcp, :::5080->80/tcp   hello-1
# 一定要做端口映射,不然到时候我们无法访问应用程序

[注意]: 若是我们使用"daocker ps"没有看到我们创建的容器在启动,那么就是我们之前编辑app.py  Dockerfile  requirements.txt,这三个文件有误。我们可以使用"docker logs + 容器名字"来看运行这个容器的时候出现了什么错误。

[root@centos7-docker mydocker]# docker logs hello-2
 * Serving Flask app 'app' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on all addresses (0.0.0.0)
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on http://127.0.0.1:80
 * Running on http://172.17.0.3:80 (Press CTRL+C to quit)
192.168.29.1 - - [22/Apr/2022 13:19:18] "GET / HTTP/1.1" 200 -
192.168.29.1 - - [22/Apr/2022 13:19:18] "GET /favicon.ico HTTP/1.1" 404 -

# 我这是执行成功了的情况

[结果查看]

 

因为redis服务没有安装,所以连接不到redis数据库,可以启动一个redis的容器,链接到现在这个容器

第七步:起一个redis容器

[root@centos7-docker docker]# docker run -d -p 6379:80 --name fan-redis-1 redis
a2b9a2ec08fa9efa1276efbefed4beaef25033bd56d8d888f3e61bfa46afad8b
[root@centos7-docker docker]# docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED          STATUS          PORTS                                             NAMES
a2b9a2ec08fa   redis             "docker-entrypoint.s…"   4 seconds ago    Up 3 seconds    6379/tcp, 0.0.0.0:6379->80/tcp, :::6379->80/tcp   fan-redis-1
c6dbd5383c7a   friendlyhello_1   "python app.py"          22 minutes ago   Up 22 minutes   0.0.0.0:5080->80/tcp, :::5080->80/tcp             hello-1

起完之后,页面还是没有变化。因为我们虽然起了reids容器,但是不知道redis服务的IP地址是多少,所以连不过去。

[root@centos7-docker docker]# docker exec -it hello-1 /bin/bash
root@c6dbd5383c7a:/app# cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	c6dbd5383c7a  # 这里可以看到这个机器只知道自己的IP地址

因为这个镜像的命令太过于少了,所以没有vim和vi命令能够直接修改这个文件。但是我感觉直接修改这个文件,添加一条标明redis容器的IP地址的内容就可以访问到了。这里我们直接新建一个容器吧。

[root@centos7-docker mydocker]# docker run  -d --name hello-2 -p  5081:80 --link fan-redis-1:redis friendlyhello_1
ad7d079f9aa87da741303fc366311166b020617d4b5729916444881c75983647
[root@centos7-docker mydocker]# docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED             STATUS         PORTS                                             NAMES
ad7d079f9aa8   friendlyhello_1   "python app.py"          6 seconds ago       Up 6 seconds   0.0.0.0:5081->80/tcp, :::5081->80/tcp             hello-2
a2b9a2ec08fa   redis             "docker-entrypoint.s…"   About an hour ago   Up 6 minutes   6379/tcp, 0.0.0.0:6379->80/tcp, :::6379->80/tcp   fan-redis-1

# 这里我们可以看到新建的hello-2有redis的IP地址
root@ad7d079f9aa8:/app# cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	redis a2b9a2ec08fa fan-redis-1
172.17.0.3	ad7d079f9aa8

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[docker]七、docker镜像的制作(超详细)、docker镜像结构原理、镜像的分享——harbor 的相关文章

随机推荐

  • 算法基础复盘笔记Day10【动态规划】—— 线性DP

    作者主页 欢迎来到我的技术博客 个人介绍 大家好 本人热衷于Java后端开发 欢迎来交流学习哦 如果文章对您有帮助 记得关注 点赞 收藏 评论 您的支持将是我创作的动力 让我们一起加油进步吧 第一章 线性DP 一 数字三角形 1 题目描述
  • 如何使用 AWS 和 ChatGPT 创建最智能的多语言虚拟助手

    上周ChatGPT发布了 每个人都在尝试令人惊奇的事情 我也开始使用它并想尝试它如何使用AWS的AI 服务进行集成 结果非常棒 在这篇文章中 我将逐步解释我是如何创建这个项目的 这样你也可以做到 最重要的是 您无需成为AI 专家即可创建它
  • openvino是啥

    英特尔发布的开源框架 用于深度学习的推理优化与模型部署 openvino具体使用方法还是看官方文档比较好https docs openvino ai 支持多种框架 tensorflow caffe pytorch mxnet keras o
  • Wireshark的抓包和分析,看这篇就够了!

    点击上方蓝字 关注 程序IT圈 WireShark是一个网络封包分析软件 网络封包分析软件的功能是撷取网络封包 并尽可能显示出最为详细的网络封包资料 Wireshark使用WinPCAP作为接口 直接与网卡进行数据报文交换 在网络封包和流量
  • 【DDD架构】

    DDD domain driven design 领域驱动设计模型 一 DP domain primitive 1 什么是DP 2 为什么要用DP 2 1 API接口清晰度 2 2 数据验证和错误处理 2 3 业务代码的清晰度 3 DP原则
  • 彻底搞懂JDBC的运行过程

    转载自 https blog csdn net y277an article details 96937010 JDBC的作用 JDBC的全称是Java DataBase Connection 也就是Java数据库连接 我们可以用它来操作关
  • Qt 工程

    Qt 工程 工程文件 项目文件 QMake 添加模块 添加与特定平台有关的文件 文件不存在时停止 qmake 控制台输出调试信息 变量 Variables CONFIG HEADERS SOURCES FORMS INCLUDEPATH T
  • 测试用例设计方法2——边界值

    介于有效等价类和无效等价类之间 一 边界值 三点 上点 边界上的点 离点 离上点最近的点 根据上点的精度确定 内点 边界有效范围内的任意一点 如何确定离点 若边界是闭区间 则离点在外 如用户名长度 6 18 之间 上点为6 18 离点为5
  • 浅谈C++元编程

    随着 C 11 14 17 标准的不断更新 C 语言得到了极大的完善和补充 元编程作为一种新兴的编程方式 受到了越来越多的广泛关注 结合已有文献和个人实践 对有关 C 元编程进行了系统的分析 首先介绍了 C 元编程中的相关概念和背景 然后利
  • VS019创建C++工程基本步骤

    1 打开VS2019新建工程 2 选择C 空项目 点击下一步 3 设置项目名称和项目位置 然后点击创建 4 设置自己喜欢的布局方式 关于背景网上有很多设置方法 5 新建入口Main c工程 右键源文件 添加 新项目 6 选择C 文件 取个名
  • 持续集成与持续交付(CI/CD):探讨在云计算中实现快速软件交付的最佳实践

    文章目录 持续集成 CI 的最佳实践 持续交付 CD 的最佳实践 云计算环境下的特别注意事项 个人主页 程序员 小侯 CSDN新晋作者 欢迎 点赞 评论 收藏 收录专栏 云计算 文章内容 软件交付 希望作者的文章能对你有所帮助 有不足的地方
  • python爬取站长素材上的图片

    Python爬取站长素材上的图片 罗纳尔康 首先这是一个学习的案例 我将其记录下来 因为所学的内容有点多 爬取这个图片 我是用的xpath来解析网页 当然也可以用bs4来进行解析 看个人喜好 该案例比较简单 但涉及的内容并不少 1 分析每页
  • Web3域名,会是新的应用场景么?

    在互联网时代 域名一直都是一个十分有价值的资产 无论是个人还是企业 想要在互联网中建立一个交互性的平台 网站 都需要一个域名来与 IP 地址进行映射 域名是具有唯一性的 因此 它遵循 先到先得 的原则 即一旦有用户注册了一个域名之后 其他人
  • 【序列比对】Needleman-Wunsch(全局)和Smith-Waterman(局部)算法py实现(多条回溯路径,三叉树思路,超详细注释)

    Needleman Wunsch和Smith Waterman算法py实现 多条回溯路径 话不多说 直接上结果图 多条回溯路径 原理 代码详解 以NW为例 导入包以及参数设置 import numpy as np sequence 1 AA
  • cmd命令提示符使用功能大全

    cmd命令提示符使用功能大全 winver 检查windows版本 arch 显示机器的处理器架构 1 uname m 显示机器的处理器架构 2 uname r 显示正在使用的内核版本 dmidecode q 显示硬件系统部件 SMBIOS
  • 串口收数数码管显示(串口带协议带校验)

    将上位机采集到的数据通过串口发给单片机 数码管显示收到的信息 数码管显示程序见上篇文章 串口带协议收发在我的文章中也可以找到 mian函数 BYTE CODE 10 0x3f 0x06 0x5b 0x4f 0x66 0x6d 0x7d 0x
  • ArgoCD的安装与部署

    一 简介 Argo CD 是一个为 Kubernetes 而生的 遵循声明式 GitOps 理念的持续部署 CD 工具 它的配置和使用非常简单 并且自带一个简单易用的 Dashboard 页面 并且支持多种配置管理 模板工具 例如 Kust
  • 区块链开发入门之一

    基本概念 区块链的核心理念 一个分布式数据库 其基本单元为区块 取款用来存储数据 区块之间前后关联 通过时间排序 基于PKI 摘要算法实现集体验证 维护 区块链提供了一个分布式总账 让用户对总账的数据实现共同治理 因而建立互信 区块链基本逻
  • mscrm导入其他组织的数据库

    在部署mscrm的时候 有时候会有这样的问题 新建一个组织 如何让这个组织使用已经存在的另一个组织的数据库 即多个组织公用一个crm数据库 1 打开部署管理器 开始 所有程序 Microsoft Dynamics CRM 部署管理器 2 点
  • [docker]七、docker镜像的制作(超详细)、docker镜像结构原理、镜像的分享——harbor

    目录 1 镜像 images 1 1 Dockerfile 是制作镜像的文件 2 镜像结构原理 2 1 base 镜像 2 1 1 rootfs和bootfs 2 1 2 base 镜像提供的是最小安装的 Linux 发行版 2 2 镜像分