-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
111 lines (53 loc) · 98.2 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Docker 原理</title>
<link href="/2023/07/09/Docker-%E5%8E%9F%E7%90%86/"/>
<url>/2023/07/09/Docker-%E5%8E%9F%E7%90%86/</url>
<content type="html"><![CDATA[<h1 id="Docker原理"><a href="#Docker原理" class="headerlink" title="Docker原理"></a>Docker原理</h1><h2 id="Image-and-Container"><a href="#Image-and-Container" class="headerlink" title="Image and Container"></a>Image and Container</h2><p><img src="/2023/07/09/Docker-%E5%8E%9F%E7%90%86/%E9%95%9C%E5%83%8F%E5%92%8C%E5%AE%B9%E5%99%A8.png" alt="镜像和容器"></p><h2 id="Image"><a href="#Image" class="headerlink" title="Image"></a>Image</h2><p><img src="/2023/07/09/Docker-%E5%8E%9F%E7%90%86/docker-image.png" alt="docker-image"></p><p>image可以被认为 “union view” of a stack of <strong>read-only layers</strong>.</p><p>在 Docker 镜像中,<strong>每个镜像由多个分层组成,每个分层都是一个只读的文件层</strong>。这些分层在构建镜像时创建,每个分层都包含了自己的文件系统变化。<strong>每个分层都可以指定一个父分层</strong>,以此形成一个分层树形结构。在 Docker 中,常用的分层文件系统包括 AUFS、OverlayFS、Btrfs 等。</p><p>在运行 Docker 容器时,<strong>Docker 引擎会根据镜像的分层结构创建一个容器文件系统</strong>。容器文件系统由多个分层组成,每个分层都是只读的,但是它们可以在联合文件系统中被合并成一个可写的文件系统。这个联合文件系统提供了一个“虚拟”的文件系统视图,将所有分层的文件系统变化合并成一个单一的文件系统。</p><p>在这个“虚拟”的文件系统视图中,<strong>所有的分层文件系统变化都是只读的,而容器本身的文件系统是可写的。因此,任何对容器中文件系统的修改都会被记录在容器的可写层中,而不会影响底层的只读层</strong>。这种设计使得容器可以快速启动和销毁,同时节省存储空间。</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># sudo tree -L 1 /var/lib/docker/</span>/var/lib/docker/├── aufs├── containers├── graph├── init├── linkgraph.db├── repositories-aufs├── tmp├── trust└── volumes<span class="token number">7</span> directories, <span class="token number">2</span> files<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="Container"><a href="#Container" class="headerlink" title="Container"></a>Container</h2><p><img src="/2023/07/09/Docker-%E5%8E%9F%E7%90%86/docker-container.png" alt="docker container"></p><p>A container is defined as a <strong>“union view” of a stack of layers the top of which is a read-write layer</strong>. At this point, some of you might notice that this definition says nothing about <strong>whether this container is running.</strong></p><p><strong>Takeaway</strong>: A container is defined <em>only</em> as a read-write layer atop an image (of read-only layers itself). <strong>It does not have to be running</strong>.</p><p>在 Docker 中,<strong>启动一个容器时会从镜像中创建一个新的可写层</strong>,这个可写层被称为容器层。容器层位于镜像的顶部,并包含了所有容器的修改和文件系统变化。容器层是可读写的,因此<strong>容器中的任何修改都会被记录在容器层中,而不会影响镜像本身</strong>。</p><p>需要注意的是,在容器中进行的修改不会影响镜像本身。每个容器都有自己的文件系统和运行环境,容器之间是独立的。<strong>当容器被删除时,容器层也会被删除,所有的修改都会被丢弃。因此,容器是一个临时的运行实例,而镜像则是一个静态的文件系统层次结构,它可以被多个容器共享和使用。</strong></p><h2 id="Running-Container"><a href="#Running-Container" class="headerlink" title="Running Container"></a>Running Container</h2><p><img src="/2023/07/09/Docker-%E5%8E%9F%E7%90%86/docker-running-container1.png" alt="docker running container1"></p><p>A running container is defined as <em><strong>a read-write “union view” and the the isolated process-space and processes within</strong></em></p><p>在 Docker 中,一个<strong>运行中的容器</strong>可以被看作是一个<em><strong>可写的“虚拟”文件系统和一个隔离的进程空间的组合</strong></em>。容器中的文件系统是由镜像的只读层和容器的可写层合并而成的。容器的进程空间是由容器中运行的进程和它们的子进程组成的。</p><figure><div class="code-wrapper"><pre class="line-numbers language-gherkin" data-language="gherkin"><code class="language-gherkin">+----------------------------+<span class="token table-head"><span class="token punctuation">|</span><span class="token th variable"> Docker host </span><span class="token punctuation">|</span></span><span class="token table-body"><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span><span class="token td string"> +------------------------+ </span><span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> Running container </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +--------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> Process 1 </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +--------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +--------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> Process 2 </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +--------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span><span class="token td string"> +------------------------+ </span><span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span></span>+----------------------------+<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p><img src="/2023/07/09/Docker-%E5%8E%9F%E7%90%86/docker-running-container2.png" alt="docker running container2"></p><p>在 Docker 中,容器的隔离机制是<em><strong>通过内核级别的技术</strong></em>实现的,包括 cgroups、namespaces 等。这些技术可以将容器中的进程和资源隔离开来,使得容器之间相互独立,同时也可以限制容器中的资源使用,以确保容器不会对主机系统造成影响。</p><figure><div class="code-wrapper"><pre class="line-numbers language-gherkin" data-language="gherkin"><code class="language-gherkin">+-----------------------------------------------------+<span class="token table-head"><span class="token punctuation">|</span><span class="token th variable"> Docker host </span><span class="token punctuation">|</span></span><span class="token table-body"><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span><span class="token td string"> +-------------------------------------------------+ </span><span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> Running container </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +---------------------------------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> Process 1 </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +-------------------+ +-------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> File A </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> File B </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +-------------------+ +-------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +---------------------------------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +---------------------------------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> Process 2 </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +-------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> File C </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +-------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +---------------------------------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +---------------------------------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> Process 3 </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +-------------------+ +-------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> File D </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> File E </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +-------------------+ +-------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token td string"> +---------------------------------------------+ </span><span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span> <span class="token punctuation">|</span><span class="token punctuation">|</span><span class="token td string"> +-------------------------------------------------+ </span><span class="token punctuation">|</span><span class="token punctuation">|</span> <span class="token punctuation">|</span></span>+-----------------------------------------------------+<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p> 未完…s</p>]]></content>
<categories>
<category>云计算</category>
</categories>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title>使用Docker</title>
<link href="/2023/07/06/Docker/"/>
<url>/2023/07/06/Docker/</url>
<content type="html"><![CDATA[<h1 id="使用Docker"><a href="#使用Docker" class="headerlink" title="使用Docker"></a>使用Docker</h1><p>使用docker的目的是让应用的<strong>部署、测试、分发变得高效轻松</strong>。</p><h2 id="一-Docker-架构"><a href="#一-Docker-架构" class="headerlink" title="一 Docker 架构"></a>一 Docker 架构</h2><p><img src="/2023/07/06/Docker/Docker%E6%A0%B8%E5%BF%83%E6%9E%B6%E6%9E%84%E5%9B%BE.jpg" alt="Docker核心架构图"></p><ol><li>用户使用Docker Client与Docker Daemon建立通信,并发送请求给后者。</li><li>Docker Daemon作为Docker架构中的主体部分,首先提供Server的功能使其可以接受Docker Client的请求;</li><li>Engine执行Docker内部的一系列工作,<strong>每一项工作都是以一个Job的形式的存在。</strong></li><li>Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过<strong>镜像管理驱动graphdriver</strong>将下载镜像以Graph的形式存储;</li><li>当需要为Docker创建网络环境时,通过<strong>网络管理驱动networkdriver</strong>创建并配置Docker容器网络环境;</li><li>当需要限制Docker容器运行资源或执行用户指令等操作时,则通过<strong>execdrive</strong>r来完成。</li><li><strong>libcontainer是一项独立的容器管理包</strong>,networkdriver以及execdriver都是<strong>通过libcontainer来实现具体对容器进行的操作</strong>。</li></ol><blockquote><p>参考: <a href="https://www.huweihuang.com/docker-notes/basics/docker-architecture.html">https://www.huweihuang.com/docker-notes/basics/docker-architecture.html</a></p></blockquote><h2 id="二-使用镜像"><a href="#二-使用镜像" class="headerlink" title="二 使用镜像"></a>二 使用镜像</h2><h3 id="2-1拉取镜像"><a href="#2-1拉取镜像" class="headerlink" title="2.1拉取镜像"></a>2.1拉取镜像</h3><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> pull <span class="token punctuation">[</span>选项<span class="token punctuation">]</span><span class="token punctuation">[</span>Docker Registry 地址<span class="token punctuation">[</span>:端口号<span class="token punctuation">]</span>/<span class="token punctuation">]</span>仓库名<span class="token punctuation">[</span>:标签<span class="token punctuation">]</span><span class="token comment">#############################################################</span><span class="token comment">#example:</span><span class="token function">docker</span> pull ubuntu:18.04<span class="token number">18.04</span>: Pulling from library/ubuntu92dc2a97ff99: Pull completebe13a9d27eb8: Pull completec8299583700a: Pull completeDigest: sha256:4bc3ae6596938cb0d9e5ac51a1152ec9dcac2a1c50829c74abd9c4361e321b26Status: Downloaded newer image <span class="token keyword">for</span> ubuntu:18.04docker.io/library/ubuntu:18.04<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li><code>[Docker Registry 地址[:端口号]/]</code> 是可选的 Docker Registry 地址和端口号,用于指定从哪个 Docker Registry 即((<code>docker.io</code>))下载镜像。如果不给出镜像仓库地址将默认从Docker Hub上进行下载。</li><li>仓库名是两段式名称,即 <code><用户名>/<软件名></code>。对于 Docker Hub,如果不给出用户名,则默认为 <code>library</code>,也就是官方镜像。</li></ul><p>如果要从私有 Docker Registry 中下载镜像,则可使用以下命令:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> pull myregistry.example.com:5000/myimage:latest<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>我们还可以使用<code>docker pull --help</code> 来查看选项:</p><p><img src="/2023/07/06/Docker/%E9%95%9C%E5%83%8F%E6%8B%89%E5%8F%96%E5%91%BD%E4%BB%A4.png" alt="镜像拉取命令"></p><p><strong>这里对tags做出解释:</strong>tags代表标签,一个 Docker 镜像可以有多个标签,每个标签代表了该镜像的一个版本或者一个变体。例如,ubuntu 镜像可能有 latest、18.04、20.04 等多个标签,分别代表了 Ubuntu 的不同版本。</p><h3 id="2-2-运行"><a href="#2-2-运行" class="headerlink" title="2.2 运行"></a>2.2 运行</h3><p><code>docker run</code>命令可以使用一个镜像运行出一个容器</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-it</span> <span class="token parameter variable">--rm</span> ubuntu:18.04 <span class="token function">bash</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>这个命令的参数含义如下:</p><ul><li>-it:将容器的<strong>标准输入和输出连接到当前终端</strong>,并启动一个交互式的终端。</li><li>–rm:在容器退出后自动删除容器;</li><li>ubuntu:18.04:指定要使用的镜像,这里使用的是 Ubuntu 18.04 版本的镜像;</li><li>bash:在容器中运行的命令,这里是启动一个交互式的 bash 终端。</li></ul><p>运行这个命令后,会启动一个新的容器,使用 Ubuntu 18.04 镜像,并在容器中启动一个交互式的 bash 终端。您可以在终端中执行任何命令,就像在本地的终端中一样。</p><p><strong>注:</strong>这里的bash 是在容器启动后要执行的命令,也可以是其他命令或脚本。在这个命令中,<u>bash 命令会启动一个交互式的 bash 终端</u><u>,允许用户在容器中进行交互式的操作。</u></p><p>如果想在容器启动后使用其他命令的话可以使用以下例子</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-it</span> <span class="token parameter variable">--rm</span> ubuntu:18.04 python /path/to/script.py<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>此例子在容器启动后会执行一个python脚本。</p><h3 id="2-3-列出镜像"><a href="#2-3-列出镜像" class="headerlink" title="2.3 列出镜像"></a>2.3 列出镜像</h3><p><code>docker image ls</code>命令可以列出本地已有镜像。</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> image <span class="token function">ls</span>REPOSITORY TAG IMAGE ID CREATED SIZEredis latest 5f515359c7f8 <span class="token number">5</span> days ago <span class="token number">183</span> MBnginx latest 05a60462f8ba <span class="token number">5</span> days ago <span class="token number">181</span> MBmongo <span class="token number">3.2</span> fe9198c04d62 <span class="token number">5</span> days ago <span class="token number">342</span> MB<span class="token operator"><</span>none<span class="token operator">></span> <span class="token operator"><</span>none<span class="token operator">></span> 00285df0df87 <span class="token number">5</span> days ago <span class="token number">342</span> MBubuntu <span class="token number">18.04</span> 329ed837d508 <span class="token number">3</span> days ago <span class="token number">63</span>.3MBubuntu bionic 329ed837d508 <span class="token number">3</span> days ago <span class="token number">63</span>.3MB<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>这里需要说明以下几点:</p><ul><li><code>REPOSTORY</code>:Repository 是 Docker 镜像的仓库名称,<u>表示 Docker 镜像所属的仓库或组织</u><ul><li><em>在 Docker Hub 上</em>,仓库名称通常是<strong>用户名或组织名,后面跟着斜杠和镜像名称</strong>。例如,ubuntu 镜像的仓库名称是 library/ubuntu,其中 library 是 Docker Hub 默认的命名空间,用于存储公共的、官方的镜像。</li><li><em>在本地计算机上</em>,Docker 镜像的仓库名称通常是在下载镜像时指定的。若使用<code>docker pull ubuntu</code>下载镜像,则 Repository 列将显示为 ubuntu。</li><li>如果从私有 Docker Registry 中下载镜像,则仓库名称将包括 Registry 的地址和端口号<code>myregistry.example.com:5000/myimage。</code>。</li></ul></li><li><code>IMAGE ID</code>:本地计算机中唯一标识一个镜像</li><li><code>CREATED</code>:镜像创建的时间;</li><li><code>SIZE</code>:镜像的大小。</li></ul><p>若不列出本地所有镜像,使用<code>docker image ls ubuntu</code>可以列出所有仓库名为ubuntu的镜像</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> image <span class="token function">ls</span> ubuntuREPOSITORY TAG IMAGE ID CREATED SIZEubuntu <span class="token number">20.04</span> 7e0aa2d69a15 <span class="token number">3</span> weeks ago <span class="token number">72</span>.8MBubuntu latest 7e0aa2d69a15 <span class="token number">3</span> weeks ago <span class="token number">72</span>.8MBubuntu <span class="token number">18.04</span> 2eb2d388e1a2 <span class="token number">2</span> months ago <span class="token number">63</span>.3MB<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p><code>docker image ls</code>还支持以下选项</p><ul><li><code>-a</code> 或 <code>--all</code>:包括本地计算机上所有的 Docker 镜像,包括中间镜像和被标记为 <code><none></code> 的镜像;</li><li><code>--digests</code>:显示镜像的摘要信息;</li><li><code>-f</code> 或 <code>--filter</code>:根据指定的条件过滤要显示的镜像。例如,使用 <code>docker image ls -f dangling=true</code> 命令可以只显示被标记为 <code><none></code> 的镜像;</li><li><code>--format</code>:使用 Go 模板语言自定义输出结果的格式;</li><li><code>-q</code> 或 <code>--quiet</code>:只显示镜像的 ID,而不显示其他信息。</li></ul><h3 id="2-4-删除镜像"><a href="#2-4-删除镜像" class="headerlink" title="2.4 删除镜像"></a>2.4 删除镜像</h3><p><code>docker image rm</code>命令用于删除本地镜像</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> image <span class="token function">rm</span> <span class="token punctuation">[</span>选项<span class="token punctuation">]</span> <span class="token operator"><</span>镜像<span class="token operator"><span class="token file-descriptor important">1</span>></span> <span class="token punctuation">[</span><span class="token operator"><</span>镜像<span class="token operator"><span class="token file-descriptor important">2</span>></span> <span class="token punctuation">..</span>.<span class="token punctuation">]</span>$ <span class="token function">docker</span> image <span class="token function">ls</span>REPOSITORY TAG IMAGE ID CREATED SIZEcentos latest 0584b3d2cf6d <span class="token number">3</span> weeks ago <span class="token number">196.5</span> MBredis alpine 501ad78535f0 <span class="token number">3</span> weeks ago <span class="token number">21.03</span> MB<span class="token function">docker</span> latest cf693ec9b5c7 <span class="token number">3</span> weeks ago <span class="token number">105.1</span> MBnginx latest e43d811ce2f4 <span class="token number">5</span> weeks ago <span class="token number">181.5</span> MB<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>此处<code><镜像></code>可以是<code>镜像短 ID</code>、<code>镜像长 ID</code>、<code>镜像名</code> 或者 <code>镜像摘要</code>。例如使用<strong>镜像短id</strong>来进行删除,可使用命令<code>docker image rm 501</code>。</li><li>要删除多个 Docker 镜像,可以在命令中列出多个镜像名称或 ID,以空格分隔。如<code>docker image rm myimage1 myimage2</code>。我们也可以用<code>镜像名</code>,也就是 <code><仓库名>:<标签></code>,来删除镜像,<code> docker image rm centos</code>。</li><li>可以使用 <code>-f</code> 或 <code>--force</code> 选项删除正在被容器使用的镜。</li></ul><p><strong>注意:</strong></p><p>镜像的<u>唯一标识是其 ID 和摘要,而一个镜像可以有多个标签</u>。使用上述命令对镜像删除的<strong>实质是将满足我们要求的所有镜像标签都取消</strong>,执行<code>Untagged</code>。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 <code>Delete</code> 行为就不会发生。所以并非所有的 <code>docker image rm</code> 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。</p><p><strong>镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。</strong>镜像的多层结构让镜像复用变得非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己 <code>docker pull</code> 看到的层数不一样的原因。</p><p>除了镜像依赖以外,<strong>还需要注意的是容器对镜像的依赖</strong>。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。之前讲过,<strong>容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。</strong>因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。</p><h3 id="2-4-理解镜像构成"><a href="#2-4-理解镜像构成" class="headerlink" title="2.4 理解镜像构成"></a>2.4 理解镜像构成</h3><p>镜像是多层存储,每一层是在前一层的基础上进行的修改;而容器同样也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层</p><p>以下以定制一个 Web 服务器为例子,来讲解镜像是如何构建的。</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> run <span class="token parameter variable">--name</span> webserver <span class="token parameter variable">-d</span> <span class="token parameter variable">-p</span> <span class="token number">80</span>:80 nginx<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>这条命令会用 <code>nginx</code> 镜像启动一个容器,命名为 <code>webserver</code>,并且映射了 80 端口。</p><p>该命令的各个参数含义如下:</p><ul><li><code>run</code>:启动一个新容器。</li><li><code>--name webserver</code>:指定容器的名称为 <code>webserver</code>。</li><li><code>-d</code>:指定容器在后台以守护进程的形式运行。</li><li><code>-p 80:80</code>:将主机的 80 端口映射到容器的 80 端口,以便可以通过主机的 IP 地址访问容器中的 Web 服务器。</li><li><code>nginx</code>:指定要在容器中运行的镜像名称,这里是 Nginx 镜像。</li></ul><p><img src="/2023/07/06/Docker/nginx%E6%9C%8D%E5%8A%A1%E5%BC%80%E5%90%AF.png" alt="nginx服务开启"></p><p><img src="/2023/07/06/Docker/nginx%E6%9C%8D%E5%8A%A1%E6%9F%A5%E7%9C%8B.png" alt="nginx服务查看"></p><p>使用<code>docker exc</code>命令进入容器内部,用 <code><h1>Hello, Docker!</h1></code> 覆盖了<code>/usr/share/nginx/html/index.html</code> 的内容。</p><p><img src="/2023/07/06/Docker/%E8%BF%9B%E5%85%A5%E5%AE%B9%E5%99%A8.png" alt="进入容器"></p><p>再次刷新,欢迎页已经发生改变。</p><p><img src="/2023/07/06/Docker/hello.png" alt="hello"></p><p>以上操作在实质上是改动了容器的存储层,使用<code>docker diff</code>命令可以看到这些改动。</p><p><img src="/2023/07/06/Docker/%E5%AE%B9%E5%99%A8%E5%B1%82%E6%94%B9%E5%8F%98.png" alt="容器层改变"></p><ul><li>/usr、/usr/share、/usr/share/nginx、/usr/share/nginx/html 和 /usr/share/nginx/html/index.html 都是容器创建时已经存在的文件或目录,但它们的状态已被更改了(<strong>C 表示“changed”</strong>)。</li><li>/var、/var/cache 和 /var/cache/nginx 也是容器创建时已经存在的文件或目录,但是 /var/cache/nginx/client_temp、/var/cache/nginx/fastcgi_temp、/var/cache/nginx/proxy_temp、/var/cache/nginx/scgi_temp 和 /var/cache/nginx/uwsgi_temp 都是新创建的目录(<strong>A 表示“added”</strong>)。</li><li>/etc、/etc/nginx 和 /etc/nginx/conf.d 都是容器创建时已经存在的文件或目录,但是 /etc/nginx/conf.d/default.conf 的内容已被更改(<strong>C 表示“changed”</strong>)。</li><li>/run/nginx.pid 和 /root/.bash_history 是新创建的文件(<strong>A 表示“added”</strong>)。</li></ul><p>将这些变化保存下来形成镜像。</p><p>在不使用卷的情况下,我们对与容器的任何修改都会<u>记录在容器的存储层里</u>,可以使用<code>docker commit</code> 将容器的<u>存储层</u>保存为一个新的镜像,即在<strong>原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。</strong>以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> commit <span class="token punctuation">[</span>选项<span class="token punctuation">]</span> <span class="token operator"><</span>容器ID或名称<span class="token operator">></span> <span class="token punctuation">[</span><span class="token operator"><</span>仓库名<span class="token operator">></span><span class="token punctuation">[</span>:<span class="token operator"><</span>标签<span class="token operator">></span><span class="token punctuation">]</span><span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>可以使用以下选项来指定镜像的属性:</p><ul><li><code>-a</code> 或 <code>--author</code>:指定新镜像的作者信息。</li><li><code>-c</code> 或 <code>--change</code>:在新镜像中应用 Dockerfile 指令,例如 <code>CMD</code>、<code>EXPOSE</code>、<code>ENV</code> 等。</li><li><code>-m</code> 或 <code>--message</code>:为新镜像添加一条说明信息。</li><li><code>-p</code> 或 <code>--pause</code>:在创建新镜像之前暂停容器的进程。</li></ul><p><img src="/2023/07/06/Docker/%E4%BF%9D%E5%AD%98%E9%95%9C%E5%83%8F.png" alt="保存镜像"></p><p>再使用<code>docker image ls nigix</code>命令查看这些新定制的nginx镜像</p><p>![新镜像 ](新镜像 .png)</p><p>使用<code>docker history</code>命令可以看到镜像的历史信息</p><p><img src="/2023/07/06/Docker/nginx-v2.png" alt="nginx-v2"></p><p><img src="/2023/07/06/Docker/nginx-latest.png" alt="nginx-latest"></p><p>通过<strong>对比nginx:v2 和nginx: latested内部的历史信息</strong>,发现nginx:v2这一层增加了我们刚刚提交的这一层。</p><p>输出显示了 Docker 镜像的历史记录,其中包含了每个镜像层的创建时间、创建方式、大小和注释。下面是每一行的含义:</p><ul><li>IMAGE:该镜像层所在的镜像的名称或 ID。</li><li>CREATED:该镜像层的创建时间。</li><li>CREATED BY:创建该镜像层的 Dockerfile 指令或命令。</li><li>SIZE:该镜像层的大小。</li><li>COMMENT:该镜像层的注释信息。</li></ul><p>在这个输出中,镜像层的顺序从上到下,越往下表示越早创建的层。可以看到,<strong>最后一行是完整镜像的 ID</strong>,而其他行都是中间层的 ID,因为 <strong>Docker 镜像是由多个只读的镜像层组成的</strong>。</p><p>要注意的是,如果一个镜像层被标记为<code> <missing></code>,则表示该层已被删除或不存在。</p><p>我们运行新定制的镜像。</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">--name</span> web2 <span class="token parameter variable">-d</span> <span class="token parameter variable">-p</span> <span class="token number">81</span>:80 nginx:v2<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>此时我们直接访问<a href="http://localhost:81/">http://localhost:81</a> 会直接出现更改后的页面。</p><p><img src="/2023/07/06/Docker/hello-docker2.png" alt="hello-docker2"></p><p>以上操作完成了定制镜像,使用的是 <code>docker commit</code> 命令,手动操作给旧的镜像添加了新的一层,形成新的镜像。</p><p>注意⚠️:</p><p>仔细观察 <code>docker diff webserver</code> 的结果,会发现除了真正想要修改的<code>index.html</code> 文件外还有很多文件被改动或添加了,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,将会导致镜像极为臃肿。</p><p>此外,使用 <code>docker commit</code> 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为 <strong>黑箱镜像</strong>,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。</p><p>而且,回顾之前提及的镜像所使用的分层存储的概念,除当前层外,之前的每一层都是不会发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添加、修改,而不会改动上一层。如果使用 <code>docker commit</code> 制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。</p><h3 id="2-5Dockerfile定制镜像"><a href="#2-5Dockerfile定制镜像" class="headerlink" title="2.5Dockerfile定制镜像"></a>2.5Dockerfile定制镜像</h3><p>Dockerfile 是一个文本文件,<strong>用于定义构建 Docker 镜像时所需的步骤和指令</strong>。它包含了一系列指令,用于告诉 Docker 引擎如何构建镜像。构建 Docker 镜像的过程中,Docker 引擎将从 Dockerfile 中按顺序读取每一条指令,并在其基础上构建新的镜像。</p><p>Dockerfile 使用一些关键字来定义每个构建步骤,例如:</p><ul><li><code>FROM</code>:指定该镜像将基于哪个基础镜像构建。</li><li><code>RUN</code>:在容器中执行一条命令。</li><li><code>COPY</code>:将文件从主机复制到容器中。</li><li><code>WORKDIR</code>:设置工作目录。</li><li><code>EXPOSE</code>:声明容器将使用的端口。</li><li><code>CMD</code>:设置容器启动时要执行的命令。</li></ul><p>使用Dockerfile 来定制niginx镜像</p><p>首先创建Dockerfile文件:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">mkdir</span> mynginx$ <span class="token builtin class-name">cd</span> mynginx$ <span class="token function">touch</span> Dockerfile<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure><p>在Dockerfile中写入</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">FROM nginxRUN <span class="token builtin class-name">echo</span> <span class="token string">'<h1>Hello, Docker!</h1>'</span> <span class="token operator">></span> /usr/share/nginx/html/index.html<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><p><img src="/2023/07/06/Docker/Dockerfile.png" alt="Dockerfile"></p><p>注意⚠️:</p><p>Docker 还存在一个特殊的镜像,名为 <code>scratch</code>。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。如果以 <code>scratch</code> 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">FROM debian:stretchRUN <span class="token function">apt-get</span> updateRUN <span class="token function">apt-get</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> gcc libc6-dev <span class="token function">make</span> <span class="token function">wget</span>RUN <span class="token function">wget</span> <span class="token parameter variable">-O</span> redis.tar.gz <span class="token string">"http://download.redis.io/releases/redis-5.0.3.tar.gz"</span>RUN <span class="token function">mkdir</span> <span class="token parameter variable">-p</span> /usr/src/redisRUN <span class="token function">tar</span> <span class="token parameter variable">-xzf</span> redis.tar.gz <span class="token parameter variable">-C</span> /usr/src/redis --strip-components<span class="token operator">=</span><span class="token number">1</span>RUN <span class="token function">make</span> <span class="token parameter variable">-C</span> /usr/src/redisRUN <span class="token function">make</span> <span class="token parameter variable">-C</span> /usr/src/redis <span class="token function">install</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>Dockerfile 中每一个指令都会建立一层,也就是每一层<code>RUN</code>会创建一层新镜像 。其实并不需要这样,以上所有的命令只有一个目的,就是<strong>编译、安装 redis 可执行文件</strong>。因此没有必要建立很多层,这只是一层的事情。可以以下修改:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">FROM debian:stretchRUN <span class="token builtin class-name">set</span> -x<span class="token punctuation">;</span> <span class="token assign-left variable">buildDeps</span><span class="token operator">=</span><span class="token string">'gcc libc6-dev make wget'</span> <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">apt-get</span> update <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">apt-get</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> <span class="token variable">$buildDeps</span> <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">wget</span> <span class="token parameter variable">-O</span> redis.tar.gz <span class="token string">"http://download.redis.io/releases/redis-5.0.3.tar.gz"</span> <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">mkdir</span> <span class="token parameter variable">-p</span> /usr/src/redis <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">tar</span> <span class="token parameter variable">-xzf</span> redis.tar.gz <span class="token parameter variable">-C</span> /usr/src/redis --strip-components<span class="token operator">=</span><span class="token number">1</span> <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">make</span> <span class="token parameter variable">-C</span> /usr/src/redis <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">make</span> <span class="token parameter variable">-C</span> /usr/src/redis <span class="token function">install</span> <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">rm</span> <span class="token parameter variable">-rf</span> /var/lib/apt/lists/* <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">rm</span> redis.tar.gz <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">rm</span> <span class="token parameter variable">-r</span> /usr/src/redis <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">apt-get</span> purge <span class="token parameter variable">-y</span> --auto-remove <span class="token variable">$buildDeps</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>使用<code>&&</code>将各个操作连接起来,将七层镜像压缩成了一层。<strong>在一组命令的最后添加清理工作的命令</strong>,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 <code>apt</code> 缓存文件。镜像是多层存储,每一层的东西并不会在下一层被删除,<strong>会一直跟随着镜像</strong>。因此镜像构建时,一<strong>定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉</strong>。</p><p>在 <code>Dockerfile</code> 文件所在目录执行<code>docker build -t nginx:v3 </code> 构建镜像。</p><p><img src="/2023/07/06/Docker/build.png" alt="build"></p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token punctuation">[</span>选项<span class="token punctuation">]</span> <span class="token operator"><</span>上下文路径/URL/-<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li><code>-t</code> 选项用于指定新镜像的名称和标签</li><li><code>nginx:v3</code> 意味着新镜像的名称为 <code>nginx</code>,标签为 <code>v3</code>。</li><li><code>.</code> 表示当前目录是 Docker 镜像构建的上下文。Docker 引擎会在构建过程中将当前目录及其所有子目录中的文件发送到 Docker 引擎中,以便在 Dockerfile 中使用这些文件。</li></ul><p>表面上我们好像是在本机执行各种 <code>docker</code> 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。在进行镜像构建是,还会有一些例如COPY,ADD等指令,这些指令会涉及到本地文件等复制等操作,而由于docker镜像构建操作是在服务端进行,<strong>为了服务端能够操作本地文件,引入了上下文的概念</strong>。<code>docker build</code> 命令会将上下文路径下的所有内容打包上传给docker服务器(引擎),以此获得构建时所需要的所有文件。</p><p>对于以下命令</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">COPY ./package.json /app/<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>并不是要复制执行 <code>docker build</code> 命令所在的目录下的 <code>package.json</code>,也不是复制 <code>Dockerfile</code> 所在目录下的 <code>package.json</code>,而是复制 <strong>上下文(context)</strong> 目录下的 <code>package.json</code>。</p><p>如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 <code>.gitignore</code> 一样的语法写一个 <code>.dockerignore</code>,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。</p><p>例如:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 忽略所有 .git 目录和文件</span>.git<span class="token comment"># 忽略所有 .DS_Store 文件</span>**/.DS_Store<span class="token comment"># 忽略 .env 文件</span>.env<span class="token comment"># 忽略 node_modules 目录</span>node_modules/<span class="token comment"># 忽略所有 .log 文件</span>**/*.log<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p> 还需要注意的是如果不额外指定 <code>Dockerfile</code> 的话,会将上下文目录下的名为 <code>Dockerfile</code> 的文件作为 Dockerfile。实际上 <code>Dockerfile</code> 的文件名并不要求必须为 <code>Dockerfile</code>,而且并不要求必须位于上下文目录中,比如可以用 <code>-f ../Dockerfile.php</code> 参数指定某个文件作为 <code>Dockerfile</code>。</p><h3 id="2-6-Dockerfile指令"><a href="#2-6-Dockerfile指令" class="headerlink" title="2.6 Dockerfile指令"></a>2.6 Dockerfile指令</h3><h4 id="COPY复制文件"><a href="#COPY复制文件" class="headerlink" title="COPY复制文件"></a>COPY复制文件</h4><ul><li><figure><div class="code-wrapper"><pre><code class="shell">COPY [--chown=<user>:<group>] <源路径>... <目标路径><pre class="line-numbers language-none"><code class="language-none">- ```shell COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure></code></pre></li></ul><p><code>COPY</code> 指令将从构建上下文目录中 <code><源路径></code> 的文件/目录复制到新的一层的镜像内的 <code><目标路径></code> 位置。比如:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">COPY package.json /usr/src/app/<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>使用该指令的时候还可以加上 <code>--chown=<user>:<group></code> 选项来改变文件的所属用户及所属组。</p><p>以下是一些示例:</p><ul><li><code>--chown=55:mygroup</code>:将复制后的文件或目录的所属用户设置为用户 ID 为 55,所属组设置为 mygroup。</li><li><code>--chown=bin</code>:将复制后的文件或目录的所属用户设置为 bin 用户,所属组保持不变。</li><li><code>--chown=1</code>:将复制后的文件或目录的所属用户设置为用户 ID 为 1,所属组保持不变。</li><li><code>--chown=10:11</code>:将复制后的文件或目录的所属用户设置为用户 ID 为 10,所属组设置为组 ID 为 11。</li></ul><h4 id="ADD-更高级的复制文件"><a href="#ADD-更高级的复制文件" class="headerlink" title="ADD 更高级的复制文件"></a>ADD 更高级的复制文件</h4><p><code>ADD</code> 指令在<code>COPY</code> 的基础上增加了一些新的功能,<code><源路径></code> 可以是一个 <code>URL</code>,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <code><目标路径></code> 去。下载后的文件权限自动设置为 <code>600</code>,如果这并不是想要的权限,那么还需要增加额外的一层 <code>RUN</code> 进行权限调整,</p><p><u>这里补充说明以下文件权限:</u></p><p>文件权限 <code>600</code> 表示该文件的所有者拥有<em>读写权限</em>,其他用户没有任何权限。具体来说,权限 600 表示:</p><ul><li>文件所有者拥有读权限(4)和写权限(2),即权限值为 6。</li><li>文件所属组和其他用户没有任何权限,即权限值为 0。</li></ul><h4 id="CMD容器启动命令"><a href="#CMD容器启动命令" class="headerlink" title="CMD容器启动命令"></a>CMD容器启动命令</h4><ul><li><p><code>shell</code> 格式:<code>CMD <命令></code></p></li><li><p><code>exec</code> 格式:<code>CMD ["可执行文件", "参数1", "参数2"...]</code></p></li></ul><p>Docker 不是虚拟机,容器就是进程。进程启动就需要指定运行程序的参数。<strong>CMD指令用于指定默认的容器主进程的启动命令</strong></p><p>当我们直接<code>docker run -it ubuntu</code> ,会直接进入bash。此时可使用 <code>docker run -it ubuntu cat /etc/os-release</code>,来运行其他命令。这里就是用<code> cat</code> <code>etc/os-release</code> 命令替换了默认的<code>/bin/bash</code>命令,输出了系统版本信息。</p><p>如果使用 <code>shell</code> 格式的话,实际的命令会被包装为 <code>sh -c</code> 的参数的形式进行执行。比如:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">CMD <span class="token builtin class-name">echo</span> <span class="token environment constant">$HOME</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>实际上执行时会将其改变为:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">CMD <span class="token punctuation">[</span> <span class="token string">"sh"</span>, <span class="token string">"-c"</span>, <span class="token string">"echo <span class="token environment constant">$HOME</span>"</span> <span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>这个命令包含了三个部分:</p><ul><li>sh:指定要使用的 shell 程序是 /bin/sh。</li><li>-c:指定要执行的命令是后面的字符串。</li><li>“echo $HOME”:实际要执行的命令,输出环境变量 HOME 的值。</li></ul><p>需要注意的是,<strong>Docker不是虚拟机,容器中的应用都应该以前台执行,</strong>而不是像虚拟机,物理机里面那样使用<code>systemd</code>去启动后台服务,容器内没有后台服务的概念。<strong>Docker 容器在启动时,会执行 Dockerfile 中指定的 <code>CMD</code> 或 <code>ENTRYPOINT</code> 指令所定义的主进程。</strong>这个主进程通常是一个应用程序,例如一个 Web 服务器或数据库,它会在容器中运行并提供服务。当主进程退出时,容器就会停止运行。在 Docker 中,容器的生命周期是由主进程的状态来决定的。如果主进程退出,容器就会被认为是已经停止,Docker 引擎会将其自动停止并删除。</p><p>与主进程不同的是,辅助进程(或守护进程)不是容器生命周期的关键因素。<strong>辅助进程是一些在容器内运行的后台进程</strong>,例如日志记录器、监控器或定时任务。它们通常由主进程启动,并在容器运行期间一直保持运行状态。但是,如果辅助进程退出,不会影响容器本身的运行状态。因此容器中的主进程应该是一个长时间运行的进程,不能是一个短暂运行的脚本或命令。否则,当主进程退出后,容器也会立即退出,导致服务中断。</p><p>以nginx启动为例,若用以下命令来启动nginx服务:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">CMD <span class="token function">service</span> nginx start<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>现容器执行后就立即退出了,因为用 <code>service nginx start</code> 命令,则是希望 upstart 来以后台守护进程形式启动 <code>nginx</code> 服务。而 <code>CMD service nginx start</code> 会被理解为 <code>CMD [ "sh", "-c", "service nginx start"]</code>,**因此主进程实际上是 <code>sh</code>**。那么当 <code>service nginx start</code> 命令结束后,<code>sh</code> 也就结束了,<code>sh</code> 作为主进程退出了,自然就会令容器退出。</p><p>正确的使用方式应该是直接执行 <code>nginx</code> 可执行文件,并且要求以前台形式运行。比如:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">CMD <span class="token punctuation">[</span><span class="token string">"nginx"</span>, <span class="token string">"-g"</span>, <span class="token string">"daemon off;"</span><span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>这个命令包含了三个部分:</p><ul><li><code>nginx</code>:指定要运行的程序是 Nginx。</li><li><code>-g</code>:指定要传递给 Nginx 的全局配置参数。</li><li><code>"daemon off;"</code>:传递给 Nginx 的全局配置参数,表示在前台运行 Nginx 并关闭守护进程模式。</li></ul><h4 id="ENTRYPOINT入口点"><a href="#ENTRYPOINT入口点" class="headerlink" title="ENTRYPOINT入口点"></a>ENTRYPOINT入口点</h4><p><code>ENTRYPOINT</code> 的目的和 <code>CMD</code> 一样,都是在<strong>指定容器启动程序及参数。</strong></p><p>当指定了 <code>ENTRYPOINT</code> 后,<code>CMD</code> 的含义就发生了改变,不再是直接的运行其命令,而是<strong>将 <code>CMD</code> 的内容作为参数传给 <code>ENTRYPOINT</code> 指令</strong>,换句话说实际执行时,将变为:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token operator"><</span>ENTRYPOINT<span class="token operator">></span> <span class="token string">"<CMD>"</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p><u>ENTRYPOINT支持以下特殊场景:</u></p><p><strong>场景一:让镜像变成像命令一样使用</strong></p><p>假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用 <code>CMD</code> 来实现:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">FROM ubuntu:18.04RUN <span class="token function">apt-get</span> update <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">apt-get</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> <span class="token function">curl</span> <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">rm</span> <span class="token parameter variable">-rf</span> /var/lib/apt/lists/*CMD <span class="token punctuation">[</span> <span class="token string">"curl"</span>, <span class="token string">"-s"</span>, <span class="token string">"http://myip.ipip.net"</span> <span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>这个 Dockerfile 包含以下指令:</p><ul><li><code>FROM ubuntu:18.04</code>:指定基础镜像为 Ubuntu 18.04</li><li><code>RUN apt-get update \ && apt-get install -y curl \ && rm -rf /var/lib/apt/lists/*</code>:使用 apt-get 工具安装 curl,并在安装完成后删除不必要的文件以减小镜像大小。</li><li><code>CMD [ "curl", "-s", "http://myip.ipip.net" ]</code>:指定容器启动时要执行的命令。<ul><li>curl:指定要执行的命令为 curl,一个用于从服务器上获取数据的工具。</li><li>-s:指定 curl 工具以静默模式(silent mode)运行,不输出任何进度或错误信息。</li><li><a href="http://myip.ipip.net:指定要查询的">http://myip.ipip.net:指定要查询的</a> URL,这个 URL 是一个公共的 IP 地址查询服务,可以通过访问该服务的网站来获取当前主机的 IP 地址。</li></ul></li></ul><p>我们使用 <code>docker build -t myip .</code> 来构建镜像的话,如果我们需要查询当前公网 IP,只需要执行:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> run myip当前 IP:61.148.226.66 来自:北京市 联通<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><p>这个示例使用了一个公共的 IP 地址查询服务 <a href="http://myip.ipip.net,在实际应用中,可能需要使用不同的服务或自定义脚本来获取容器的/">http://myip.ipip.net,在实际应用中,可能需要使用不同的服务或自定义脚本来获取容器的</a> IP 地址。另外,为了确保容器能够成功访问这些服务,需要确保容器网络配置正确,并且容器所在的主机具有正确的网络配置和访问权限。</p><p>如果我们希望加参数呢?比如从上面的 <code>CMD</code> 中可以看到实质的命令是 <code>curl</code>,那么如果我们希望显示 HTTP 头信息,就需要加上 <code>-i</code> 参数。那么我们可以直接加 <code>-i</code> 参数给 <code>docker run myip</code> 么?</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> run myip <span class="token parameter variable">-i</span>docker: Error response from daemon: invalid header field value <span class="token string">"oci runtime error: container_linux.go:247: starting container process caused <span class="token entity" title="\"">\"</span>exec: <span class="token entity" title="\\">\\</span><span class="token entity" title="\"">\"</span>-i<span class="token entity" title="\\">\\</span><span class="token entity" title="\"">\"</span>: executable file not found in <span class="token environment constant">$PATH</span><span class="token entity" title="\"">\"</span><span class="token entity" title="\n">\n</span>"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><p>可以看出会报<code>executable file not found</code>的错误,这是因为<strong>跟在镜像名后面的是 <code>command</code>,运行时会替换 <code>CMD</code> 的默认值。</strong>因此这里的 <code>-i</code> 替换了原来的 <code>CMD</code>,而不是添加在原来的 <code>curl -s http://myip.ipip.net</code> 后面。而 <code>-i</code> 根本不是命令,所以自然找不到。</p><p>如果我们希望加入 <code>-i</code> 这参数,我们就必须重新完整的输入这个命令:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> run myip <span class="token function">curl</span> <span class="token parameter variable">-s</span> http://myip.ipip.net <span class="token parameter variable">-i</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>这样并不是一个好的解决方案。</p><p>现在重新使用<code>ENTRYPOINT</code>来解决这个问题。</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">FROM ubuntu:18.04RUN <span class="token function">apt-get</span> update <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">apt-get</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> <span class="token function">curl</span> <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">rm</span> <span class="token parameter variable">-rf</span> /var/lib/apt/lists/*ENTRYPOINT <span class="token punctuation">[</span> <span class="token string">"curl"</span>, <span class="token string">"-s"</span>, <span class="token string">"http://myip.ipip.net"</span> <span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>再来尝试直接使用 <code>docker run myip -i</code>:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> run myip当前 IP:61.148.226.66 来自:北京市 联通$ <span class="token function">docker</span> run myip <span class="token parameter variable">-i</span>HTTP/1.1 <span class="token number">200</span> OKServer: nginx/1.8.0Date: Tue, <span class="token number">22</span> Nov <span class="token number">2016</span> 05:12:40 GMTContent-Type: text/html<span class="token punctuation">;</span> <span class="token assign-left variable">charset</span><span class="token operator">=</span>UTF-8Vary: Accept-EncodingX-Powered-By: PHP/5.6.24-1~dotdeb+7.1X-Cache: MISS from cache-2X-Cache-Lookup: MISS from cache-2:80X-Cache: MISS from proxy-2_6Transfer-Encoding: chunkedVia: <span class="token number">1.1</span> cache-2:80, <span class="token number">1.1</span> proxy-2_6:8006Connection: keep-alive当前 IP:61.148.226.66 来自:北京市 联通<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>这次成功了。这是因为当存在 <code>ENTRYPOINT</code> 后,<code>CMD</code> 的内容将会作为参数传给 <code>ENTRYPOINT</code>,而**这里 <code>-i</code> 就是新的 <code>CMD</code>**,因此会作为参数传给 <code>curl</code>,从而达到了我们预期的效果。</p><p><strong>场景二:应用前的准备工作</strong></p><p>启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。这些准备工作是和容器 <code>CMD</code> 无关的,无论 <code>CMD</code> 为什么,都需要事先进行一个预处理的工作。这种情况下,<strong>可以写一个脚本,然后放入 <code>ENTRYPOINT</code> 中去执行,而这个脚本会将接到的参数(也就是 <code><CMD></code>)作为命令,在脚本最后执行</strong>。</p><p>官方镜像 <code>redis</code> 中就是这么做的:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">FROM alpine:3.4<span class="token punctuation">..</span>.RUN addgroup <span class="token parameter variable">-S</span> redis <span class="token operator">&&</span> adduser <span class="token parameter variable">-S</span> <span class="token parameter variable">-G</span> redis redis<span class="token punctuation">..</span>.ENTRYPOINT <span class="token punctuation">[</span><span class="token string">"docker-entrypoint.sh"</span><span class="token punctuation">]</span>EXPOSE <span class="token number">6379</span>CMD <span class="token punctuation">[</span> <span class="token string">"redis-server"</span> <span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>这个 Dockerfile 包含以下指令:</p><ul><li><code>FROM alpine:3.4</code>:指定基础镜像为 Alpine Linux 3.4 版本。</li><li><code>RUN addgroup -S redis && adduser -S -G redis redis</code>:创建一个名为 <code>redis</code> 的用户和组。</li><li><code>ENTRYPOINT ["docker-entrypoint.sh"]</code>:<strong>指定容器启动时要执行的入口点脚本为 <code>docker-entrypoint.sh</code>。</strong></li><li><code>EXPOSE 6379</code>:指定容器监听的端口为 6379,用于 Redis 服务。</li><li><code>CMD [ "redis-server" ]</code>:指定容器启动时要执行的默认命令为 <code>redis-server</code>,即启动 Redis 服务。</li></ul><p><code>docker-entrypoint.sh</code>内容如下:</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token punctuation">..</span>.<span class="token comment"># allow the container to be started with `--user`</span><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token operator">=</span> <span class="token string">'redis-server'</span> <span class="token parameter variable">-a</span> <span class="token string">"<span class="token variable"><span class="token variable">$(</span><span class="token function">id</span> <span class="token parameter variable">-u</span><span class="token variable">)</span></span>"</span> <span class="token operator">=</span> <span class="token string">'0'</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span><span class="token function">find</span> <span class="token builtin class-name">.</span> <span class="token punctuation">\</span><span class="token operator">!</span> <span class="token parameter variable">-user</span> redis <span class="token parameter variable">-exec</span> <span class="token function">chown</span> redis <span class="token string">'{}'</span> +<span class="token builtin class-name">exec</span> gosu redis <span class="token string">"<span class="token variable">$0</span>"</span> <span class="token string">"<span class="token variable">$@</span>"</span><span class="token keyword">fi</span><span class="token builtin class-name">exec</span> <span class="token string">"<span class="token variable">$@</span>"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>该脚本的内容就是根据 <code>CMD</code> 的内容来判断,如果是 <code>redis-server</code> 的话,则切换到 <code>redis</code> 用户身份启动服务器,否则依旧使用 <code>root</code> 身份执行。</p><h4 id="ENV-设置环境变量"><a href="#ENV-设置环境变量" class="headerlink" title="ENV 设置环境变量"></a>ENV 设置环境变量</h4><ul><li><figure><div class="code-wrapper"><pre class="language-none"><code class="language-none">ENV <key> <value><pre class="line-numbers language-none"><code class="language-none">- ```shell ENV <key1>=<value1> <key2>=<value2>...<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure></code></pre></li></ul><p>设置环境变量后,后面的其它指令,如 <code>RUN</code>,还是运行时的应用,都可以直接使用这里定义的环境变量。</p><p>在 Dockerfile 中使用 <code>ENV</code> 指令设置的环境变量的作用域与容器中的所有进程和命令相关。也就是说,<strong>这些环境变量将在容器中的所有进程和命令中可用</strong>,包括容器启动时运行的命令、ENTRYPOINT 指令指定的入口点脚本以及使用 docker exec 命令进入容器后手动执行的命令。</p><h4 id="ARG构建参数"><a href="#ARG构建参数" class="headerlink" title="ARG构建参数"></a>ARG构建参数</h4><p>构建参数和 <code>ENV</code> 的效果一样,都是设置环境变量。所不同的是,<code>ARG</code> 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。<strong>但是不要因此就使用 <code>ARG</code> 保存密码之类的信息,因为 <code>docker history</code> 还是可以看到所有值的。</strong></p><h4 id="EXPOSE暴露端口"><a href="#EXPOSE暴露端口" class="headerlink" title="EXPOSE暴露端口"></a>EXPOSE暴露端口</h4><p><code>EXPOSE <端口1> [<端口2>...]</code></p><p><code>EXPOSE</code> 指令是<strong>声明容器运行时提供服务的端口</strong>,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。</p><p>当有端口声明时有以下好处:</p><ol><li>帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。</li><li>在运行时使用随机端口映射时,也就是 <code>docker run -P</code> 时,会自动随机映射 <code>EXPOSE</code> 的端口。</li></ol><p>要将 <code>EXPOSE</code> 和在运行时使用 <code>-p <宿主端口>:<容器端口></code> 区分开来。<code>-p</code>,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 <code>EXPOSE</code> 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。</p><h2 id="三-操作容器"><a href="#三-操作容器" class="headerlink" title="三 操作容器"></a>三 操作容器</h2>]]></content>
<categories>
<category>云计算</category>
</categories>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title>云原生安全</title>
<link href="/2023/07/05/%E4%BA%91%E5%8E%9F%E7%94%9F%E5%AE%89%E5%85%A8/"/>
<url>/2023/07/05/%E4%BA%91%E5%8E%9F%E7%94%9F%E5%AE%89%E5%85%A8/</url>
<content type="html"><![CDATA[<h1 id="云原生安全"><a href="#云原生安全" class="headerlink" title="云原生安全"></a>云原生安全</h1><p><strong>云原生</strong>:是一种软件开发和部署的方法论,旨在帮助开发者更好地在云端环境下构建和运行应用程序。云原生应用程序具有高度的可伸缩性、弹性和可靠性,能够更快地响应业务需求。(在云上设计应用程序)</p><p><strong>内生安全</strong>:在软件开发阶段就要注入安全的理念,不仅要确保软件的多有功能都是可预期的,还要努力做到不存在“可被利用的漏洞”。</p><p><strong>容器运行时(Container Runtime)</strong>是一种用于在容器内运行应用程序的<strong>软件</strong>,它提供了容器的<u>生命周期管理、资源隔离和安全性控制等功能</u>。容器运行时是容器化应用程序的基础设施,它可以帮助应用程序在不同的运行环境中实现一致的运行行为,提供更加便捷的部署和管理方式。</p><h2 id="第1章-云原生安全"><a href="#第1章-云原生安全" class="headerlink" title="第1章 云原生安全"></a>第1章 云原生安全</h2><h3 id="1-1-云原生:云计算下半场"><a href="#1-1-云原生:云计算下半场" class="headerlink" title="1.1 云原生:云计算下半场"></a>1.1 云原生:云计算下半场</h3><p>许多企业对基础设施进行了云化,但大多数都是<strong>利用开源IaaS系统构建的云平台</strong>,只是简单点地将传统物理主机、平台或应用转化为虚拟态。这样的云实践知识“形”上的改变,不是神上的改变。**<u>云计算下班上要解决业务在开发,运行整个生命周期中遇到问题。</u>**</p><p>云原生系统一般特征:</p><ol><li><p><strong>轻、快、不变的基础设施</strong></p><p>利用容器有轻和快的特点,在实践中去更新更为持久的化的镜像而<strong>不会在容器中安装或更新应用</strong>,通过编排系统下载新镜像并启动相应的容器即可,<strong>不改变容器运行时的模式</strong>。</p></li><li><p><strong>弹性服务编排</strong></p><p>云原生的焦点是业务而非基础设施</p></li><li><p><strong>开发运营一体化</strong></p><p>缩短软件开发周期</p></li><li><p>微服务架构</p></li><li><p>无服务模型</p></li></ol><h3 id="1-2-什么是云原生安全"><a href="#1-2-什么是云原生安全" class="headerlink" title="1.2 什么是云原生安全"></a>1.2 什么是云原生安全</h3><p><strong>云原生安全</strong>:面向云原生环境的安全和具有云原生特征的安全</p><p>原生安全:基于云原生且无处不在,即使用了云原生技术,适用于各类场景。</p><p>虚拟化安全关注的是资源,云原生安全关注的是应用。</p><p>容器安全和虚拟化安全的差别看似是隔离技术强度,但其实应该是生命周期。</p><p>容器逃逸等攻击手段往往利用操作系统层面隔离的漏洞,与终端上的恶意软件攻击手法不同。</p>]]></content>
<categories>
<category>安全</category>
</categories>
<tags>
<tag>云安全</tag>
</tags>
</entry>
<entry>
<title>Kubernetes</title>
<link href="/2023/07/05/Kubernetes/"/>
<url>/2023/07/05/Kubernetes/</url>
<content type="html"><![CDATA[<h2 id="Kubernetes"><a href="#Kubernetes" class="headerlink" title="Kubernetes"></a>Kubernetes</h2><h2 id="一-、应用部署三大阶段"><a href="#一-、应用部署三大阶段" class="headerlink" title="一 、应用部署三大阶段"></a>一 、应用部署三大阶段</h2><p><img src="/2023/07/05/Kubernetes/%E4%BC%A0%E7%BB%9F%E9%83%A8%E7%BD%B2%E4%B8%8E%E8%99%9A%E6%8B%9F%E5%8C%96%E9%83%A8%E7%BD%B2.png" alt="截屏2023-07-03 09.42.01"></p><p><img src="/2023/07/05/Kubernetes/%E5%AE%B9%E5%99%A8%E9%83%A8%E7%BD%B2.png" alt="截屏2023-07-03 09.52.24"></p><p>传统部署:环境不隔离</p><p>虚拟化部署:环境过度隔离,每个虚拟机内部又要重新装一个操作系统。</p><p>容器部署:容器内部没有硬件设备模拟,很好解决了以上两个问题 ,容器的启动效率更高</p><p><strong>一些问题:</strong></p><p>传统部署方式ip会很稳定,容器化部署中容器相当于一个壳子 ,意味着容器的生命周期很短。当容器删除时文件系统网络等都会改变,从而会影响一些服务。这些问题k8s都能够解决。</p><h3 id="K8s的特点"><a href="#K8s的特点" class="headerlink" title="K8s的特点"></a>K8s的特点</h3><ul><li><p>自我修复</p><p>自动检测容器是否发生错误,若出现错误会基于原来的容器重新copy一个出来</p></li><li><p>弹性伸缩</p><p>自动基于原来的容器进行扩容</p></li><li><p>自动部署和回滚</p><p>挨个更新,保证无论如何都有一个容器可用,用户不会感知这次更新</p></li><li><p>服务发现与负载均衡</p></li><li><p>机密和配置管理</p></li><li><p>存储编排</p><p> 把所有机器的存储资源管理成虚拟磁盘 ,容器访问虚拟磁盘,最后被映射成物理磁盘。</p></li></ul><hr><h2 id="二、集群架构与组件"><a href="#二、集群架构与组件" class="headerlink" title="二、集群架构与组件"></a>二、集群架构与组件</h2><h3 id="Borg架构"><a href="#Borg架构" class="headerlink" title="Borg架构"></a>Borg架构</h3><img src="Borg架构.png" alt="截屏2023-07-03 10.18.01" style="zoom:50%;" /><p>主从调度。Borglet是从节点,BrogMaster去协调各个节点。</p><h3 id="K8s架构"><a href="#K8s架构" class="headerlink" title="K8s架构"></a>K8s架构</h3><img src="Kubernetes Architecture.png" alt="截屏2023-07-03 11.07.35" style="zoom:50%;" /><p><strong>注:</strong></p><ol><li>节点分为主节点和从节点,只有从节点才部署对应的任务,主节点可以既作为主节点也可以作为从节点。</li><li>所有的操作有一个汇聚一点api-server,维护所有api功能。 </li><li>我们使用命令行进行操作,对k8s来说都是http请求。命令行都是操作api的。</li></ol><h3 id="相关组件"><a href="#相关组件" class="headerlink" title="相关组件"></a>相关组件</h3><img src="k8s架构.png" alt="截屏2023-07-03 10.36.11" /><p>通过UI或者命令行调用APi来操作master</p><h5 id="kubectl命令行工具"><a href="#kubectl命令行工具" class="headerlink" title="kubectl命令行工具"></a>kubectl命令行工具</h5><h5 id="Dashboad可视化界面UI"><a href="#Dashboad可视化界面UI" class="headerlink" title="Dashboad可视化界面UI"></a>Dashboad可视化界面UI</h5><h3 id="控制面板组件(Master)"><a href="#控制面板组件(Master)" class="headerlink" title="控制面板组件(Master)"></a>控制面板组件(Master)</h3><p><img src="/2023/07/05/Kubernetes/Master%E8%8A%82%E7%82%B9.png" alt="Master节点"></p><h5 id="api-server"><a href="#api-server" class="headerlink" title="api-server"></a>api-server</h5><p>接口服务:基于REST开放k8s接口服务</p><h5 id="controller-manager"><a href="#controller-manager" class="headerlink" title="controller-manager"></a>controller-manager</h5><p>控制管理器:负责运行控制器进程,每个控制器都是一个单独的进程,但是为<strong>了降低复杂性,他们都被编译到同一个可执行文件</strong>,并在同一个进程中运行管理各个类型的控制器。<em>实现节点控制,任务控制,端点分片,服务账号控制。</em>针对k8s中各种资源进行管理。</p><h5 id="cloud-controller-manager"><a href="#cloud-controller-manager" class="headerlink" title="cloud-controller-manager"></a>cloud-controller-manager</h5><p>云控制器管理器:第三方云平台提供的控制器API对接管理功能</p><h5 id="kube-scheduler"><a href="#kube-scheduler" class="headerlink" title="kube-scheduler"></a>kube-scheduler</h5><p>调度器:负责将Pod<strong>基于一定算法</strong>,将其调用到合适的节点上。</p><h5 id="etcd"><a href="#etcd" class="headerlink" title="etcd"></a>etcd</h5><p>理解为k8s的数据库,键值类型存储的分布式数据库,提供基于Raft算法实现自主的集群高可用。老版本:基于内存。新版本。</p><h3 id="普通节点"><a href="#普通节点" class="headerlink" title="普通节点"></a>普通节点</h3><p><img src="/2023/07/05/Kubernetes/%E6%99%AE%E9%80%9A%E8%8A%82%E7%82%B9.png" alt="普通节点"></p><h5 id="kubelet"><a href="#kubelet" class="headerlink" title="kubelet"></a>kubelet</h5><p>负责Pod生命周期、存储、网络。</p><h5 id="kube-proxy"><a href="#kube-proxy" class="headerlink" title="kube-proxy"></a>kube-proxy</h5><p>网络代理,负责Service的服务发展。负载均衡(4层负载)</p><h5 id="pod"><a href="#pod" class="headerlink" title="pod"></a>pod</h5><p>一个可运行多个容器</p><h5 id="container-runtime"><a href="#container-runtime" class="headerlink" title="container-runtime"></a>container-runtime</h5><p>容器运行时环境:docker、containerd、CRI-O</p><p><strong>注:所有节点都依赖于API-Server,最终操作会落到Pod上面。</strong></p><hr><h2 id="三、核心概念与专业术语"><a href="#三、核心概念与专业术语" class="headerlink" title="三、核心概念与专业术语"></a>三、核心概念与专业术语</h2><h3 id="有状态与无状态"><a href="#有状态与无状态" class="headerlink" title="有状态与无状态"></a>有状态与无状态</h3><p><img src="/2023/07/05/Kubernetes/%E6%9C%89%E7%8A%B6%E6%80%81%E4%B8%8E%E6%97%A0%E7%8A%B6%E6%80%81.png" alt="有状态与无状态"></p><p><strong>无状态:</strong></p><p>代表应用:Nginx,Apache</p><p>优点:对客户端透明,可以高效试下你扩容、迁移。</p><p>缺点:不能存储数据,需要额外的数据服务支持</p><h3 id="资源和对象"><a href="#资源和对象" class="headerlink" title="资源和对象"></a>资源和对象</h3><p>k8s中所有内容都被抽象为‘资源’,如Pod、Service、Node等都是资源。“对象”就是“资源”的实例,是持久化的实体 如某个Pod、某个具体的Node,k8s使用这些实体去表示集群状态,对象的创建、删除、修改都是通过k8s api来进行操作。</p><p><strong>资源的分类</strong></p><p><img src="/2023/07/05/Kubernetes/%E8%B5%84%E6%BA%90%E5%88%86%E7%B1%BB.png" alt="资源分类"></p><ol><li><p>元数据</p><p>对资源的元数据描述,每一个资源都可以使用元空间的数据</p></li><li><p>集群</p><p>集群级别的资源作用与集群之上,集群下所有资源可以享用。</p></li><li><p>命名空间</p><p>命名空间级别的资源,作用在命名空间之上,通常只能在该命名空间范围内使用</p></li></ol><h5 id="元数据类型级"><a href="#元数据类型级" class="headerlink" title="元数据类型级"></a>元数据类型级</h5><ol><li><p>Hori zontal Pod Autoscaler</p><p>Pod自动扩容,可以根据CPU使用率自定义指标自动对Pod进行扩容和缩容</p></li><li><p>PodTemplate</p></li><li><p>LimiteRange</p><p>可以对集群内Request和Limits的配置做一个全局的限制,相当于批量设置了某一范围内(某个命名空间)的Pod的资源使用限制</p></li></ol><h5 id="集群级"><a href="#集群级" class="headerlink" title="集群级"></a>集群级</h5><ol><li><p>Namespace</p></li><li><p>Node(相当于一个服务器)</p><p>不像其他的资源如(Pod个Namespace),Node本质上不是K8s来创建的,<strong>K8s只是管理Bode上的资源</strong>,虽然可以通过Mainfest创建一个Node对象但K8s也只是去检查是否是有3这么一个Node,如果检查失败,也不会向上调度Pod</p></li><li><p>ClusterRole</p><p>用于对集群权限进行管理</p></li><li><p>ClusterRoleBingding</p><p>让资源与权限进行绑定</p></li></ol><h5 id="命名空间级"><a href="#命名空间级" class="headerlink" title="命名空间级"></a>命名空间级</h5><h6 id="1-工作负载级"><a href="#1-工作负载级" class="headerlink" title="1. 工作负载级"></a>1. 工作负载级</h6><p><strong>Pod</strong></p><p><img src="/2023/07/05/Kubernetes/pod.png" alt="pod"></p><p>Pod(容器组)是K8s中最小的可部署单元,<strong>一个Pod(容器组)包含了一个应用程序容器(某些情况下是多个容器)、存储资源,一个唯一的网络ip地址</strong>,以及一些确定容器该如何运行的选项,Pod容器组代表了k8s中一个独立的应用程序运行实力,该实例可能由单个或几个紧耦合在一起的容器组成。</p><p><img src="/2023/07/05/Kubernetes/pause%E5%AE%B9%E5%99%A8.png" alt="pause容器"></p><p>通过pause容器来实现 pod内部多个容器对网络文件系统的共享</p><p><strong>一个pod内有相同的网络空间,相同的进程名,相同的主机名。</strong>*</p><p><strong><u>1. Pod副本</u></strong></p><p>引入‘副本’的概念——一个Pod可以被复制成多分,每一份被称之为一个“副本”,这些“副本”除了一些描述的信息(Pod的名字、uid等)不一样意外,其他信息都是一样的,譬如Pod内部的容器,容器数量,容器里面运行的应用等,这些信息都是一样的,这些副本提供同样的功能。</p><p><u><strong>2. Pod控制器</strong></u></p><p>Pod的”控制器“通常包含一个名为<strong>”replicas“的属性</strong>,”replicas“属性则指定了特定Pod的副本数量,当集群中该Pod的数量与该属性置顶的值不一致时,k8s会采用一些策略去使得当前状态满足配置的要求。每个控制器都是一个pod对象。控制器管理着pod,管理pod怎么去创建,控制器不止一种。</p><p>**<u>控制器分类</u>**:</p><p><img src="/2023/07/05/Kubernetes/Pod%E6%8E%A7%E5%88%B6%E5%99%A8.png" alt="Pod控制器"></p><p><u>a. 适用无状态服务:</u></p><ol><li><p>Replication Controller(RC):</p><p><em>帮助我们动态更新Pod副本数。基于原本模版创建,根据Rplace,需要与Pod进行绑定</em></p></li><li><p>Replica Set(RS):</p><p>帮助我们动态更新Pod的副本书,可以通过selector来选择对哪些pod生效</p></li><li><p><strong>Delopyment:</strong></p><p>RC,RS只有扩容和缩容,针对RS的更高层次的封装,提供了更丰富的部署相关的功能。</p><ul><li><p>不用描述RS,可以直接创建Replica Set/Pod</p></li><li><p>滚动升级/回滚</p><p><img src="/2023/07/05/Kubernetes/%E6%BB%9A%E5%8A%A8%E6%9B%B4%E6%96%B0.png" alt="滚动更新"></p><p>滚动升级会更根据RS1创建一个RS2,升级RS2里面的Pod再依此替换老旧的版本,不用将应用全部停掉。同时不删除旧版本保证可以回滚。 </p></li><li><p>平滑扩容和缩容</p></li><li><p>暂停与恢复</p></li></ul></li></ol><p><u>b. 适用于有状态服务</u></p><ol><li><p>statefulset</p><img src="statefulset.png" alt="statefulset" style="zoom:50%;" /><p>提供给稳定的持久化存储,稳定的网络标志,由徐部署,有序扩展,有收缩,有序删除。</p><p> 组成:(1)Headless Service:用于定义网络标志(Domain Name Service)Domain name server:域名服务,将域名与ip绑定映射关系,使用服务名来当域名,会自动绑定一个地址(2)volumeClaimTemplate。</p></li></ol><p><u>c. 守护进程</u></p><p><img src="/2023/07/05/Kubernetes/Daemonset.png" alt="Daemonset"></p><p>DaemonSet保<strong>证在每个Node上运行一个容器副本</strong>,常用来部署一些集群的日志、监控或者其他管理应用,典型的应用包括:</p><ul><li><p>日志收集:比如fluentd ,logstash等</p></li><li><p>系统监控,比如 Prometheus Node Exporter</p></li><li><p>系统程序,比如kube-proxy,kube-dns等</p><p><strong>注:以上三种控制器会持续运行,出错会重新运行。</strong></p></li></ul><p>d. 任务/定时任务</p><ol><li>job:一次性运行的任务,运行完成后Pod销毁,不再重新启动新容器</li><li>Cronjob:周期性执行的任务</li></ol><h6 id="2-服务发现"><a href="#2-服务发现" class="headerlink" title="2. 服务发现"></a>2. 服务发现</h6><p><img src="/2023/07/05/Kubernetes/%E7%BD%91%E7%BB%9C%E8%AE%BF%E9%97%AE%E6%A8%A1%E5%9E%8B.png" alt="网络访问模型"></p><p><img src="/2023/07/05/Kubernetes/%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%BA%94%E7%94%A8.png" alt="服务发现应用"></p><ul><li>Service :k8s集群内部的网络通信 ,解决横向流量东西流量的问题</li><li>Ingress:实现将K8s内部服务暴露给外网络访问的服务,ingress-nigx 反向代理,负载均衡(七层负载)解决纵向流量南北流量</li></ul><h6 id="3-存储"><a href="#3-存储" class="headerlink" title="3.存储"></a>3.存储</h6><p>Volume 数据卷,共享Pod中容器使用的数据,用来持久化的数据,比如数据库数据。</p><p>CSI:容器标准化接口。方便使用插件</p><h6 id="4-配置"><a href="#4-配置" class="headerlink" title="4. 配置"></a>4. 配置</h6><ol><li>ConfigMap 存储key-value,把configMap加载到容器里方便容器使用这些参数,这样将参数分离出来方便修改。</li><li>Secret 加密</li><li>DownwardAPI 把Pod的信息共享到容器里。可以通过环境变量或volume挂载的方式</li></ol><h6 id="5-其他"><a href="#5-其他" class="headerlink" title="5. 其他"></a>5. 其他</h6><ol><li>Role:定义一组命名空间的权限</li><li>RoleBinding</li></ol><h3 id="对象规约和状态"><a href="#对象规约和状态" class="headerlink" title="对象规约和状态"></a>对象规约和状态</h3><p><strong>Spec是规约</strong>,规格的意思,<strong>spec是必须的</strong>,他描述了对象期望状态(Desired State)———希望对象所具有的特征,当创建Kubernates对象时,必须提供对象的规约,用来描述对象的期望状态。</p><p>**状态 **:表示对象的实际状态,该属性由k8s自己维护,k8s会通过一系列的控制器对对应对象进行管理,让对象尽可能地让实际状态与期望状态重合。</p><h2 id="k8s集群搭建"><a href="#k8s集群搭建" class="headerlink" title="k8s集群搭建"></a>k8s集群搭建</h2><table><thead><tr><th align="center">角色</th><th align="center">IP地址</th><th align="center">实验画环境</th></tr></thead><tbody><tr><td align="center">Master</td><td align="center">192.168.2.131</td><td align="center">Cenos8.4</td></tr><tr><td align="center">Node1</td><td align="center">192.168.2.130</td><td align="center">Centos8.4</td></tr><tr><td align="center">Node2</td><td align="center">192.168.2.132</td><td align="center">Centos8.4</td></tr></tbody></table><p>未完待续……</p>]]></content>
<categories>
<category>云计算</category>
</categories>
<tags>
<tag>k8s</tag>
<tag>虚拟化</tag>
<tag>容器编排</tag>
</tags>
</entry>
</search>