<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>samehada</title>
        <link>https://paragraph.com/@samehada</link>
        <description>I am a postgraduate from BUTP. I am learning web3 knowledge.</description>
        <lastBuildDate>Mon, 01 Jun 2026 20:10:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>samehada</title>
            <url>https://storage.googleapis.com/papyrus_images/c583ef131a38aaad5194d51d4541dcea4b2f2dd10280e5942046bde6cff866a1.jpg</url>
            <link>https://paragraph.com/@samehada</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Docker学习与使用]]></title>
            <link>https://paragraph.com/@samehada/docker</link>
            <guid>vb5Xxqr6pCbbf0ktxlc7</guid>
            <pubDate>Mon, 20 Mar 2023 14:51:16 GMT</pubDate>
            <description><![CDATA[Docker—学习与应用1.Docker概述Docker是一种轻量级的容器化技术，可以将应用和其依赖项封装成一个可移植的容器，这使得在不同的环境中部署和运行应用程序变得更加容易。Docker容器技术的核心思想是隔离和打包装箱，每个容器是互相隔离的。 Docker诞生于2010年，最初是由一家名为dotCloud的公司开发的，该公司提供了一些pass的云计算服务。Docker最初并没有受到行业的关注，直到2013年它成为了开源项目，越来越多的人开始认识到它的优点，Docker也因此变得越来越流行。docker-logo与传统的虚拟机技术相比，Docker的优点在于轻量级、资源占用少、启动快速等方面。Docker容器化技术也是一种虚拟化技术，但与虚拟机不同的是，容器内的应用直接运行在宿主机的内核上，没有自己的内核和虚拟硬件，每个容器是互相隔离的，因此可以更快速地部署和运行应用程序。docker-structureDocker的作用在于实现DevOps，即开发和运维的快速协作，将应用程序的构建、测试、部署等过程自动化，提高效率和质量，降低成本。Docker提供了一整套的工具和服务，包括...]]></description>
            <content:encoded><![CDATA[<h1 id="h-docker" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Docker—学习与应用</h1><h2 id="h-1docker" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1.Docker概述</h2><p>Docker是一种轻量级的容器化技术，可以将应用和其依赖项封装成一个可移植的容器，这使得在不同的环境中部署和运行应用程序变得更加容易。Docker容器技术的核心思想是隔离和打包装箱，每个容器是互相隔离的。</p><p>Docker诞生于2010年，最初是由一家名为dotCloud的公司开发的，该公司提供了一些pass的云计算服务。Docker最初并没有受到行业的关注，直到2013年它成为了开源项目，越来越多的人开始认识到它的优点，Docker也因此变得越来越流行。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/28c55a135ab51f67a05f972c415a48b8569a7de5eead886fe676a72bcc8ceec8.webp" alt="docker-logo" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">docker-logo</figcaption></figure><p>与传统的虚拟机技术相比，Docker的优点在于轻量级、资源占用少、启动快速等方面。Docker容器化技术也是一种虚拟化技术，但与虚拟机不同的是，容器内的应用直接运行在宿主机的内核上，没有自己的内核和虚拟硬件，每个容器是互相隔离的，因此可以更快速地部署和运行应用程序。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d893976101365049640742776bdac5452b53ce8946e9453dc85882b77a6018ed.png" alt="docker-structure" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">docker-structure</figcaption></figure><p>Docker的作用在于实现DevOps，即开发和运维的快速协作，将应用程序的构建、测试、部署等过程自动化，提高效率和质量，降低成本。Docker提供了一整套的工具和服务，包括Docker引擎、Docker Hub、Docker Compose等，可以方便地管理和维护容器化应用程序。</p><blockquote><p>Docker是基于Golang开发的开源项目</p><p>官网地址：<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.docker.com/">https://www.docker.com/</a></p><p>文档地址：<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.docker.com/">https://docs.docker.com/</a></p><p>仓库地址：<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hub.docker.com">https://hub.docker.com</a></p></blockquote><hr><h2 id="h-2docker" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.Docker安装</h2><h3 id="h-21-docker" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.1 Docker的基本组成</h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9f14d2e252b48614f16737f3452c00b9f505b7097ffb7ec5c194c5003104d6c3.png" alt="docker" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">docker</figcaption></figure><p>Docker是一种轻量级容器技术，它的核心组成部分有镜像、容器和仓库。本文将在下面介绍这三个组成部分的概念和作用。</p><ul><li><p><strong>镜像</strong>：镜像就像是一个模板，可以通过它来创建容器服务。例如，使用tomcat镜像可以创建一个tomcat01容器，容器中可以运行最终服务或项目。</p></li><li><p><strong>容器</strong>：容器则是通过镜像来创建的，利用容器技术独立运行一个或多个应用程序。启动、停止、删除等基本命令可以操作容器。可以把容器理解为一个简易的Linux系统。</p></li><li><p><strong>仓库</strong>：仓库是存放镜像的地方，分为公有仓库和私有仓库。DockerHub和阿里云都有容器服务器，可以配置镜像加速。</p></li></ul><p>通过使用Docker，可以方便地创建、部署和管理应用程序，加速开发和部署流程，提高开发效率和应用程序的可移植性。</p><h3 id="h-22-linux" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.2 搭建Linux环境</h3><p>本文，笔者使用的是Unbuntu18.04版本的虚拟机，使用XShell连接工具对虚拟机进行操作，配置完成Linux环境后确保Xshell可以正常访问Ubuntu文件。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f1a5035c7d7ce62c25b687766b284bd2b4ff44c25db53ee3b4790f63b138adfe.png" alt="ubuntu" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">ubuntu</figcaption></figure><h3 id="h-23-docker" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.3 安装Docker</h3><ol><li><p>首先需要先卸载掉Docker的旧版本文件数据。</p><pre data-type="codeBlock" text="sudo apt-get remove docker docker-engine docker.io containerd runc
"><code>sudo apt<span class="hljs-operator">-</span>get remove docker docker<span class="hljs-operator">-</span>engine docker.io containerd runc
</code></pre></li><li><p>通过仓库的方式安装。</p><pre data-type="codeBlock" text=" sudo apt-get update

 sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
"><code> sudo apt<span class="hljs-operator">-</span><span class="hljs-keyword">get</span> <span class="hljs-keyword">update</span>

 sudo apt<span class="hljs-operator">-</span><span class="hljs-keyword">get</span> install \
    ca<span class="hljs-operator">-</span>certificates \
    curl \
    gnupg \
    lsb<span class="hljs-operator">-</span><span class="hljs-keyword">release</span>
</code></pre></li><li><p>下面添加Docker的官方GPG key。</p><pre data-type="codeBlock" text=" curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

 # 国内阿里云版
 curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add -
"><code> curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

 <span class="hljs-comment"># 国内阿里云版</span>
 curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add -
</code></pre></li><li><p>接下来验证key的指纹</p><pre data-type="codeBlock" text="sudo apt-key fingerprint 0EBFCD88
"><code>sudo apt-<span class="hljs-keyword">key</span> fingerprint <span class="hljs-number">0</span>EBFCD88
</code></pre><p>正常的输出为：</p><pre data-type="codeBlock" text="root@camile:/# sudo apt-key fingerprint 0EBFCD88
pub   rsa4096 2017-02-22 [SCEA]
      9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid           [ 未知 ] Docker Release (CE deb) &lt;docker@docker.com&gt;
sub   rsa4096 2017-02-22 [S]
"><code>root@camile:<span class="hljs-operator">/</span># sudo apt<span class="hljs-operator">-</span>key fingerprint 0EBFCD88
pub   rsa4096 <span class="hljs-number">2017</span><span class="hljs-operator">-</span>02<span class="hljs-number">-22</span> [SCEA]
      9DC8 <span class="hljs-number">5822</span> 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid           [ 未知 ] Docker Release (CE deb) <span class="hljs-operator">&#x3C;</span>docker@docker.com>
sub   rsa4096 <span class="hljs-number">2017</span><span class="hljs-operator">-</span>02<span class="hljs-number">-22</span> [S]
</code></pre></li><li><p>下面我们添加稳定版的docker仓库或者国内阿里云版本。</p><pre data-type="codeBlock" text="# 官方版本
sudo add-apt-repository \
   &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable&quot;
# 国内阿里云版
sudo add-apt-repository \
   &quot;deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
   $(lsb_release -cs) \
   stable&quot;
"><code><span class="hljs-comment"># 官方版本</span>
sudo add-apt-repository \
   <span class="hljs-string">"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   <span class="hljs-subst">$(lsb_release -cs)</span> \
   stable"</span>
<span class="hljs-comment"># 国内阿里云版</span>
sudo add-apt-repository \
   <span class="hljs-string">"deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
   <span class="hljs-subst">$(lsb_release -cs)</span> \
   stable"</span>
</code></pre></li><li><p>安装我们最新版本的docker ce和container。</p><pre data-type="codeBlock" text=" sudo apt-get update
 sudo apt-get install docker-ce docker-ce-cli containerd.io
"><code> sudo apt<span class="hljs-operator">-</span>get update
 sudo apt<span class="hljs-operator">-</span>get install docker<span class="hljs-operator">-</span>ce docker<span class="hljs-operator">-</span>ce<span class="hljs-operator">-</span>cli containerd.io
</code></pre></li><li><p>查看我们安装的docker版本信息。</p><pre data-type="codeBlock" text="docker --version
"><code>docker <span class="hljs-operator">-</span><span class="hljs-operator">-</span>version
</code></pre></li><li><p>将非root用户加入docker组，以允许免<code>sudo</code>执行<code>docker</code>.</p><pre data-type="codeBlock" text="sudo gpasswd -a 用户名 docker
"><code>sudo gpasswd -<span class="hljs-selector-tag">a</span> 用户名 docker
</code></pre><p>重启服务并刷新docker组成员：</p><pre data-type="codeBlock" text="sudo service docker restart
newgrp - docker
"><code>sudo service docker restart
newgrp <span class="hljs-operator">-</span> docker
</code></pre><p>设置开机自启动并启动 Docker-ce（安装成功后默认已设置并启动，可忽略）</p><pre data-type="codeBlock" text="sudo systemctl enable docker
sudo systemctl start docker
"><code>sudo systemctl <span class="hljs-built_in">enable</span> docker
sudo systemctl start docker
</code></pre></li><li><p>接下来我们安装docker-compose <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.runoob.com/docker/docker-compose.html">https://www.runoob.com/docker/docker-compose.html</a></p><pre data-type="codeBlock" text="sudo curl -L https://github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

docker-compose --version
"><code>sudo curl <span class="hljs-operator">-</span>L https:<span class="hljs-comment">//github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose</span>

sudo chmod <span class="hljs-operator">+</span>x <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>docker<span class="hljs-operator">-</span>compose

sudo ln <span class="hljs-operator">-</span>s <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>docker<span class="hljs-operator">-</span>compose <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>docker<span class="hljs-operator">-</span>compose

docker<span class="hljs-operator">-</span>compose <span class="hljs-operator">-</span><span class="hljs-operator">-</span>version
</code></pre></li><li><p>测试我们的第一个镜像hello-world。</p><pre data-type="codeBlock" text="docker run hello-world
"><code>docker run hello-world
</code></pre></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/76f4ed993e4c00f62eab19c71290f1084bdbd3a9a1ecc9ab128706012370869a.png" alt="hello-world" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">hello-world</figcaption></figure><ol><li><p>卸载docker命令如下所示：</p><pre data-type="codeBlock" text=" sudo apt-get purge docker-ce docker-ce-cli containerd.io
 sudo rm -rf /var/lib/docker
 sudo rm -rf /var/lib/containerd
"><code> sudo apt<span class="hljs-operator">-</span>get purge docker<span class="hljs-operator">-</span>ce docker<span class="hljs-operator">-</span>ce<span class="hljs-operator">-</span>cli containerd.io
 sudo rm <span class="hljs-operator">-</span>rf <span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>lib<span class="hljs-operator">/</span>docker
 sudo rm <span class="hljs-operator">-</span>rf <span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>lib<span class="hljs-operator">/</span>containerd
</code></pre></li></ol><h3 id="h-24" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.4 配置阿里云镜像加速器</h3><p>在阿里云上，提供了一些免费的容器镜像服务，我们找到控制台-&gt;左侧菜单-&gt;容器镜像服务-&gt;镜像加速器，其中Ubuntu配置镜像加速器的命令已给出:</p><pre data-type="codeBlock" text="sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json &lt;&lt;-&apos;EOF&apos;
{
  &quot;registry-mirrors&quot;: [&quot;https://alvnh3pv.mirror.aliyuncs.com&quot;]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
"><code>sudo mkdir <span class="hljs-operator">-</span>p <span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>docker
sudo tee <span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>docker<span class="hljs-operator">/</span>daemon.json <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">-</span><span class="hljs-string">'EOF'</span>
{
  <span class="hljs-string">"registry-mirrors"</span>: [<span class="hljs-string">"https://alvnh3pv.mirror.aliyuncs.com"</span>]
}
EOF
sudo systemctl daemon<span class="hljs-operator">-</span>reload
sudo systemctl restart docker
</code></pre><h3 id="h-25" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.5 回顾</h3><p>上面我们运行了一个docker run的命令，笔者绘制了一个流程图供大家参考</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4d307bf1fb96a415a8356a96c2db4e87549006de709be85eb4dda7c5ad86559d.png" alt="process" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">process</figcaption></figure><p>那么Docker究竟是怎么工作的呢？这里笔者简单的介绍一下，Dcoker是一个 Client-Server结构的系统，Docker的守护进程运行在主机上，通过Socker从客户端访问。而DockerServer 接收到 Docker-Client 的指令，就会执行这个命令。如下所示</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/38ae305828730a86e78bb78afd761e4286941a57b54b7afde71857b16658cb01.png" alt="工作方式" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">工作方式</figcaption></figure><hr><h2 id="h-3docker" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3.Docker常用命令</h2><p>这一节，笔者总结了一些Docker的常用命令，速览以下有助于后续的使用。对于一些命令，笔者会在虚拟机上试验并给出响应结果。</p><h3 id="h-31" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.1 帮助命令</h3><pre data-type="codeBlock" text="docker version        # 版本信息
docker info           # 显示docker的系统信息，包括容器和镜像的信息
docker [命令] --help   # 帮助文档，官网在 doc下的reference 下的 command-line reference
"><code>docker version        <span class="hljs-comment"># 版本信息</span>
docker info           <span class="hljs-comment"># 显示docker的系统信息，包括容器和镜像的信息</span>
docker [命令] --<span class="hljs-built_in">help</span>   <span class="hljs-comment"># 帮助文档，官网在 doc下的reference 下的 command-line reference</span>
</code></pre><h3 id="h-32" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.2 镜像命令</h3><p><strong>docker images</strong>：查看本机所有的镜像</p><pre data-type="codeBlock" text="root@camile:~# docker images
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
hello-world   latest    feb5d9fea6a5   5 months ago   13.3kB

# 解释
REPOSITORY：镜像的仓库源
TAG：镜像的标签
IMAGE ID：镜像的ID
CREATED：镜像的创建时间
SIZE：镜像的大小

# 可选项
Options:
  -a, --all             # 列出所有镜像 
  -f, --filter          # 过滤镜像
  -q, --quiet           # 只显示镜像的id
  -aq                   # 列出所有镜像id
"><code>root@camile:<span class="hljs-operator">~</span># docker images
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
hello<span class="hljs-operator">-</span>world   latest    feb5d9fea6a5   <span class="hljs-number">5</span> months ago   <span class="hljs-number">13</span>.3kB

# 解释
REPOSITORY：镜像的仓库源
TAG：镜像的标签
IMAGE ID：镜像的ID
CREATED：镜像的创建时间
SIZE：镜像的大小

# 可选项
Options:
  <span class="hljs-operator">-</span>a, <span class="hljs-operator">-</span><span class="hljs-operator">-</span>all             # 列出所有镜像 
  <span class="hljs-operator">-</span>f, <span class="hljs-operator">-</span><span class="hljs-operator">-</span>filter          # 过滤镜像
  <span class="hljs-operator">-</span>q, <span class="hljs-operator">-</span><span class="hljs-operator">-</span>quiet           # 只显示镜像的id
  <span class="hljs-operator">-</span>aq                   # 列出所有镜像id
</code></pre><p>**docker search：**搜索镜像</p><pre data-type="codeBlock" text="root@camile:~# docker search mysql
NAME                             DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql/mysql-server               Optimized MySQL Server Docker images. Create…   907                  [OK]

# 可选项
Options:
  -f=STARS=3000 --filter=STARS=3000
"><code>root@camile:<span class="hljs-operator">~</span># docker search mysql
NAME                             DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql<span class="hljs-operator">/</span>mysql<span class="hljs-operator">-</span>server               Optimized MySQL Server Docker images. Create…   <span class="hljs-number">907</span>                  [OK]

# 可选项
Options:
  <span class="hljs-operator">-</span>f<span class="hljs-operator">=</span>STARS<span class="hljs-operator">=</span><span class="hljs-number">3000</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>filter<span class="hljs-operator">=</span>STARS<span class="hljs-operator">=</span><span class="hljs-number">3000</span>
</code></pre><p><strong>docker pull</strong>：下载镜像</p><pre data-type="codeBlock" text="# 下载镜像 docker pull 镜像名[:tag]
root@camile:~# docker pull mysql
Using default tag: latest # 默认是最新版本
latest: Pulling from library/mysql
72a69066d2fe: Pull complete  # 分层下载，docker image 的核心 联合文件系统
93619dbc5b36: Pull complete 
99da31dd6142: Pull complete 
626033c43d70: Pull complete 
37d5d7efb64e: Pull complete 
ac563158d721: Pull complete 
d2ba16033dad: Pull complete 
688ba7d5c01a: Pull complete 
00e060b6d11d: Pull complete 
1c04857f594f: Pull complete 
4d7cfa90e6ea: Pull complete 
e0431212d27d: Pull complete 
Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709  #签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址

# 等价于以下命令
docker pull docker.io/library/mysql:latest

# 指定版本下载
root@camile:~# docker pull mysql:5.7
5.7: Pulling from library/mysql
72a69066d2fe: Already exists 
93619dbc5b36: Already exists 
99da31dd6142: Already exists 
626033c43d70: Already exists 
37d5d7efb64e: Already exists 
ac563158d721: Already exists 
d2ba16033dad: Already exists 
0ceb82207cd7: Pull complete 
37f2405cae96: Pull complete 
e2482e017e53: Pull complete 
70deed891d42: Pull complete 
Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
"><code><span class="hljs-comment"># 下载镜像 docker pull 镜像名[:tag]</span>
<span class="hljs-section">root@camile:~# docker pull mysql</span>
Using default tag: latest <span class="hljs-comment"># 默认是最新版本</span>
<span class="hljs-section">latest: Pulling from library/mysql</span>
<span class="hljs-section">72a69066d2fe: Pull complete  # 分层下载，docker image 的核心 联合文件系统</span>
<span class="hljs-section">93619dbc5b36: Pull complete </span>
<span class="hljs-section">99da31dd6142: Pull complete </span>
<span class="hljs-section">626033c43d70: Pull complete </span>
<span class="hljs-section">37d5d7efb64e: Pull complete </span>
<span class="hljs-section">ac563158d721: Pull complete </span>
<span class="hljs-section">d2ba16033dad: Pull complete </span>
<span class="hljs-section">688ba7d5c01a: Pull complete </span>
<span class="hljs-section">00e060b6d11d: Pull complete </span>
<span class="hljs-section">1c04857f594f: Pull complete </span>
<span class="hljs-section">4d7cfa90e6ea: Pull complete </span>
<span class="hljs-section">e0431212d27d: Pull complete </span>
<span class="hljs-section">Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709  #签名</span>
<span class="hljs-section">Status: Downloaded newer image for mysql:latest</span>
<span class="hljs-section">docker.io/library/mysql:latest # 真实地址</span>

<span class="hljs-comment"># 等价于以下命令</span>
docker pull docker.io/library/mysql:latest

<span class="hljs-comment"># 指定版本下载</span>
<span class="hljs-section">root@camile:~# docker pull mysql:5.7</span>
<span class="hljs-section">5.7: Pulling from library/mysql</span>
<span class="hljs-section">72a69066d2fe: Already exists </span>
<span class="hljs-section">93619dbc5b36: Already exists </span>
<span class="hljs-section">99da31dd6142: Already exists </span>
<span class="hljs-section">626033c43d70: Already exists </span>
<span class="hljs-section">37d5d7efb64e: Already exists </span>
<span class="hljs-section">ac563158d721: Already exists </span>
<span class="hljs-section">d2ba16033dad: Already exists </span>
<span class="hljs-section">0ceb82207cd7: Pull complete </span>
<span class="hljs-section">37f2405cae96: Pull complete </span>
<span class="hljs-section">e2482e017e53: Pull complete </span>
<span class="hljs-section">70deed891d42: Pull complete </span>
<span class="hljs-section">Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94</span>
<span class="hljs-section">Status: Downloaded newer image for mysql:5.7</span>
<span class="hljs-section">docker.io/library/mysql:5.7</span>
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6e228f72656679ba7b9f641bdc4d99e47b3f3ac7205b037e73e47c0cf91dc20f.png" alt="images" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">images</figcaption></figure><p><strong>docker rmi</strong>：删除镜像</p><pre data-type="codeBlock" text="# 按照id删除镜像
docker rmi -f 容器id # 删除指定的镜像
docker rmi -f 容器id 容器id 容器id # 删除多个镜像
docker rmi -f $(docker images -aq) # 删除全部镜像
"><code><span class="hljs-comment"># 按照id删除镜像</span>
docker rmi -f 容器<span class="hljs-built_in">id</span> <span class="hljs-comment"># 删除指定的镜像</span>
docker rmi -f 容器<span class="hljs-built_in">id</span> 容器<span class="hljs-built_in">id</span> 容器<span class="hljs-built_in">id</span> <span class="hljs-comment"># 删除多个镜像</span>
docker rmi -f $(docker images -aq) <span class="hljs-comment"># 删除全部镜像</span>
</code></pre><h3 id="h-33" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.3 容器命令</h3><p>有了镜像，我们才可以创建容器，我们下载一个centos镜像来测试学习。</p><pre data-type="codeBlock" text="docker pull centos
"><code></code></pre><p><strong>新建容器并启动</strong></p><pre data-type="codeBlock" text="docker run [可选参数] image

# 参数说明
--name=“Name”     容器名字 tomcat01 tomcat02 用来区分容器
-d                后台方式运行
-i -t -it         使用交互方式运行，进入容器查看内容
-p                指定容器的端口 -p 8080：8080
-p ip:主机端口：容器端口
-p 主机端口：容器端口（常用）
-p 容器端口
容器端口
-p    随机指定端口

# 测试
root@camile:~# docker run -it centos /bin/bash # 启动容器并进入容器内部
[root@f2e7722031c0 /] # 进入容器内部 root@后面就是容器的id
[root@f2e7722031c0 /]# ls # 查看容器内的centos目录
bin  etc   lib      lost+found  mnt  proc  run   srv  tmp  var
dev  home  lib64  media       opt  root  sbin  sys  usr
[root@f2e7722031c0 /]# exit  # 从容器中退回主机
exit
root@camile:~# 
"><code>docker run [可选参数] image

# 参数说明
<span class="hljs-operator">-</span><span class="hljs-operator">-</span>name<span class="hljs-operator">=</span>“Name”     容器名字 tomcat01 tomcat02 用来区分容器
<span class="hljs-operator">-</span>d                后台方式运行
<span class="hljs-operator">-</span>i <span class="hljs-operator">-</span>t <span class="hljs-operator">-</span>it         使用交互方式运行，进入容器查看内容
<span class="hljs-operator">-</span>p                指定容器的端口 <span class="hljs-operator">-</span>p <span class="hljs-number">8080</span>：<span class="hljs-number">8080</span>
<span class="hljs-operator">-</span>p ip:主机端口：容器端口
<span class="hljs-operator">-</span>p 主机端口：容器端口（常用）
<span class="hljs-operator">-</span>p 容器端口
容器端口
<span class="hljs-operator">-</span>p    随机指定端口

# 测试
root@camile:<span class="hljs-operator">~</span># docker run <span class="hljs-operator">-</span>it centos <span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>bash # 启动容器并进入容器内部
[root@f2e7722031c0 <span class="hljs-operator">/</span>] # 进入容器内部 root@后面就是容器的id
[root@f2e7722031c0 <span class="hljs-operator">/</span>]# ls # 查看容器内的centos目录
bin  etc   lib      lost<span class="hljs-operator">+</span>found  mnt  proc  run   srv  tmp  <span class="hljs-keyword">var</span>
dev  home  lib64  media       opt  root  sbin  sys  usr
[root@f2e7722031c0 <span class="hljs-operator">/</span>]# exit  # 从容器中退回主机
exit
root@camile:<span class="hljs-operator">~</span># 
</code></pre><p><strong>列出所有运行中的容器</strong></p><pre data-type="codeBlock" text="# docker ps 命令
    # 列出当前正在运行的容器
-a    # 列出当前正在进行的容器+带出历史运行的容器
-n=？    # 显示最近几个创建的容器
-q  # 只显示容器的ID
root@camile:~# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED       STATUS                   PORTS     NAMES
f2e7722031c0   centos    &quot;/bin/bash&quot;   4 hours ago   Exited (0) 4 hours ago             amazing_rosalind
"><code># docker ps 命令
    # 列出当前正在运行的容器
<span class="hljs-operator">-</span>a    # 列出当前正在进行的容器<span class="hljs-operator">+</span>带出历史运行的容器
<span class="hljs-operator">-</span>n<span class="hljs-operator">=</span>？    # 显示最近几个创建的容器
<span class="hljs-operator">-</span>q  # 只显示容器的ID
root@camile:<span class="hljs-operator">~</span># docker ps <span class="hljs-operator">-</span>a
CONTAINER ID   IMAGE     COMMAND       CREATED       STATUS                   PORTS     NAMES
f2e7722031c0   centos    <span class="hljs-string">"/bin/bash"</span>   <span class="hljs-number">4</span> <span class="hljs-literal">hours</span> ago   Exited (<span class="hljs-number">0</span>) <span class="hljs-number">4</span> <span class="hljs-literal">hours</span> ago             amazing_rosalind
</code></pre><p><strong>退出容器</strong></p><pre data-type="codeBlock" text=" exit # 直接容器停止并退出
 CTRL + P + Q # 容器不停止并tui&apos;chu
"><code> <span class="hljs-built_in">exit</span> <span class="hljs-comment"># 直接容器停止并退出</span>
 CTRL + P + Q <span class="hljs-comment"># 容器不停止并tui'chu</span>
</code></pre><p><strong>删除容器</strong></p><pre data-type="codeBlock" text="docker rm 容器id                 # 删除指定的容器，不能删除正在运行的容器
docker rm -f $(docker ps -aq)   # 删除所有的容器
docker ps -aq | xargs docker rm # 删除所有的容器
"><code>docker <span class="hljs-built_in">rm</span> 容器<span class="hljs-built_in">id</span>                 <span class="hljs-comment"># 删除指定的容器，不能删除正在运行的容器</span>
docker <span class="hljs-built_in">rm</span> -f $(docker ps -aq)   <span class="hljs-comment"># 删除所有的容器</span>
docker ps -aq | xargs docker <span class="hljs-built_in">rm</span> <span class="hljs-comment"># 删除所有的容器</span>
</code></pre><p><strong>启动和停止容器的操作</strong></p><pre data-type="codeBlock" text="docker start 容器 id
docker restart 容器id
docker stop 容器id
docker kill 容器id
"><code>docker start 容器 <span class="hljs-built_in">id</span>
docker restart 容器<span class="hljs-built_in">id</span>
docker stop 容器<span class="hljs-built_in">id</span>
docker <span class="hljs-built_in">kill</span> 容器<span class="hljs-built_in">id</span>
</code></pre><h3 id="h-34" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.4 其他命令</h3><p><strong>后台启动容器</strong></p><pre data-type="codeBlock" text="# 命令 docker run -d 镜像名;
root@camile:~# docker run -d centos
# 问题 docker ps 发现，centos 停止了

# 常见的坑， docker 容器使用后台运行，就必须有一个前台进程，docker容器发现没有应用，就会自动停止
# 容器启动后，发现自己没有提供服务，就会立刻停止，就是没有程序了
"><code><span class="hljs-comment"># 命令 docker run -d 镜像名;</span>
root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:~</span><span class="hljs-comment"># docker run -d centos</span>
<span class="hljs-comment"># 问题 docker ps 发现，centos 停止了</span>

<span class="hljs-comment"># 常见的坑， docker 容器使用后台运行，就必须有一个前台进程，docker容器发现没有应用，就会自动停止</span>
<span class="hljs-comment"># 容器启动后，发现自己没有提供服务，就会立刻停止，就是没有程序了</span>
</code></pre><p><strong>查看日志</strong></p><pre data-type="codeBlock" text="docker logs -f -t --tail 容器，没有日志

# 自己编写一段shell脚本
docker run -d centos /bin/bash -c &quot;while true;do echo qiaowei;sleep 1;done&quot;

# 显示日志
root@camile:~# docker logs -tf --tail 10 397eabe2c023
2022-02-26T09:21:25.266776907Z qiaowei
2022-02-26T09:21:26.268727710Z qiaowei
2022-02-26T09:21:27.270738576Z qiaowei

-tf # 显示日志
--tail  # 显示日志条数
"><code>docker logs <span class="hljs-operator">-</span>f <span class="hljs-operator">-</span>t <span class="hljs-operator">-</span><span class="hljs-operator">-</span>tail 容器，没有日志

# 自己编写一段shell脚本
docker run <span class="hljs-operator">-</span>d centos <span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>bash <span class="hljs-operator">-</span>c <span class="hljs-string">"while true;do echo qiaowei;sleep 1;done"</span>

# 显示日志
root@camile:<span class="hljs-operator">~</span># docker logs <span class="hljs-operator">-</span>tf <span class="hljs-operator">-</span><span class="hljs-operator">-</span>tail <span class="hljs-number">10</span> 397eabe2c023
<span class="hljs-number">2022</span><span class="hljs-operator">-</span>02<span class="hljs-operator">-</span>26T09:<span class="hljs-number">21</span>:<span class="hljs-number">25</span>.266776907Z qiaowei
<span class="hljs-number">2022</span><span class="hljs-operator">-</span>02<span class="hljs-operator">-</span>26T09:<span class="hljs-number">21</span>:<span class="hljs-number">26</span>.268727710Z qiaowei
<span class="hljs-number">2022</span><span class="hljs-operator">-</span>02<span class="hljs-operator">-</span>26T09:<span class="hljs-number">21</span>:<span class="hljs-number">27</span>.270738576Z qiaowei

<span class="hljs-operator">-</span>tf # 显示日志
<span class="hljs-operator">-</span><span class="hljs-operator">-</span>tail  # 显示日志条数
</code></pre><p><strong>查看容器中的进程信息</strong></p><pre data-type="codeBlock" text="# 命令docker top 容器id
root@camile:~# docker top 19334600a733
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                14960               14931               0                   17:27               pts/0               00:00:00            /bin/bash
"><code><span class="hljs-comment"># 命令docker top 容器id</span>
root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:~</span><span class="hljs-comment"># docker top 19334600a733</span>
<span class="hljs-variable constant_">UID</span>                 <span class="hljs-variable constant_">PID</span>                 <span class="hljs-variable constant_">PPID</span>                C                   <span class="hljs-variable constant_">STIME</span>               <span class="hljs-variable constant_">TTY</span>                 <span class="hljs-variable constant_">TIME</span>                <span class="hljs-variable constant_">CMD</span>
root                <span class="hljs-number">14960</span>               <span class="hljs-number">14931</span>               <span class="hljs-number">0</span>                   <span class="hljs-number">17</span><span class="hljs-symbol">:</span><span class="hljs-number">27</span>               pts/<span class="hljs-number">0</span>               <span class="hljs-number">00</span><span class="hljs-symbol">:</span><span class="hljs-number">00</span><span class="hljs-symbol">:</span><span class="hljs-number">00</span>            /bin/bash
</code></pre><p><strong>查看镜像的元数据</strong></p><pre data-type="codeBlock" text="# 命令
docker inspect 容器id
"><code><span class="hljs-comment"># 命令</span>
docker inspect 容器<span class="hljs-built_in">id</span>
</code></pre><p><strong>进入当前正在运行的容器</strong></p><pre data-type="codeBlock" text="# 我们通常容器都是使用后台方式运行的，需要进入容器，修改一些配置

# 命令
docker exec -it 容器id 路径

# 测试
root@camile:~# docker exec -it 19334600a733 /bin/bash
[root@19334600a733 /]# ls
bin  etc   lib      lost+found  mnt  proc  run   srv  tmp  var
dev  home  lib64  media       opt  root  sbin  sys  usr
[root@19334600a733 /]# ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 09:27 pts/0    00:00:00 /bin/bash
root         15      0  0 09:46 pts/1    00:00:00 /bin/bash
root         31     15  0 09:46 pts/1    00:00:00 ps -ef

# 方式二
docker attach 容器id
root@camile:~# docker attach 19334600a733          
[root@19334600a733 /]# ls
bin  etc   lib      lost+found  mnt  proc  run   srv  tmp  var
dev  home  lib64  media       opt  root  sbin  sys  usr

# docker exec     # 进入容器后开启一个新的终端，可以在里面操作（常用）
# docker attach   # 进入容器正在执行的终端，不会启动新的进程
"><code># 我们通常容器都是使用后台方式运行的，需要进入容器，修改一些配置

# 命令
docker exec <span class="hljs-operator">-</span>it 容器id 路径

# 测试
root@camile:<span class="hljs-operator">~</span># docker exec <span class="hljs-operator">-</span>it 19334600a733 <span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>bash
[root@19334600a733 <span class="hljs-operator">/</span>]# ls
bin  etc   lib      lost<span class="hljs-operator">+</span>found  mnt  proc  run   srv  tmp  <span class="hljs-keyword">var</span>
dev  home  lib64  media       opt  root  sbin  sys  usr
[root@19334600a733 <span class="hljs-operator">/</span>]# ps <span class="hljs-operator">-</span>ef
UID         PID   PPID  C STIME TTY          TIME CMD
root          <span class="hljs-number">1</span>      <span class="hljs-number">0</span>  <span class="hljs-number">0</span> 09:<span class="hljs-number">27</span> pts<span class="hljs-operator">/</span><span class="hljs-number">0</span>    00:00:00 <span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>bash
root         <span class="hljs-number">15</span>      <span class="hljs-number">0</span>  <span class="hljs-number">0</span> 09:<span class="hljs-number">46</span> pts<span class="hljs-operator">/</span><span class="hljs-number">1</span>    00:00:00 <span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>bash
root         <span class="hljs-number">31</span>     <span class="hljs-number">15</span>  <span class="hljs-number">0</span> 09:<span class="hljs-number">46</span> pts<span class="hljs-operator">/</span><span class="hljs-number">1</span>    00:00:00 ps <span class="hljs-operator">-</span>ef

# 方式二
docker attach 容器id
root@camile:<span class="hljs-operator">~</span># docker attach 19334600a733          
[root@19334600a733 <span class="hljs-operator">/</span>]# ls
bin  etc   lib      lost<span class="hljs-operator">+</span>found  mnt  proc  run   srv  tmp  <span class="hljs-keyword">var</span>
dev  home  lib64  media       opt  root  sbin  sys  usr

# docker exec     # 进入容器后开启一个新的终端，可以在里面操作（常用）
# docker attach   # 进入容器正在执行的终端，不会启动新的进程
</code></pre><p><strong>从容器内拷贝文件到主机上</strong></p><pre data-type="codeBlock" text="docker cp 容器id：容器内路径 目前的主机路径

# 测试
[root@19334600a733 /]# cd /home
[root@19334600a733 home]# ls
[root@19334600a733 home]# touch test.java
[root@19334600a733 home]# exit
exit
root@camile:~# docker ps 
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@camile:~# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                        PORTS     NAMES
19334600a733   centos    &quot;/bin/bash&quot;              32 minutes ago   Exited (0) 12 seconds ago               admiring_bohr
397eabe2c023   centos    &quot;/bin/bash -c &apos;while…&quot;   39 minutes ago   Exited (137) 37 minutes ago             focused_ellis
cd940d7abbb2   centos    &quot;/bin/bash -C &apos;while…&quot;   40 minutes ago   Exited (127) 40 minutes ago             ecstatic_galileo
23db6f7950f8   centos    &quot;/bin/bash -C &apos;while…&quot;   40 minutes ago   Exited (127) 40 minutes ago             keen_sanderson
8f9ce6c66f30   centos    &quot;/bin/bash&quot;              43 minutes ago   Exited (0) 40 minutes ago               flamboyant_moser
root@camile:~# docker cp 19334600a733:/home/test.java /home
root@camile:~# cd home
-bash: cd: home: No such file or directory
root@camile:~# cd /home
root@camile:/home# ls
camile  test.java

# 拷贝是一个手动过程，未来我们使用 -v 卷的技术，可以实现
"><code>docker cp 容器id：容器内路径 目前的主机路径

<span class="hljs-comment"># 测试</span>
[root<span class="hljs-variable">@19334600a733</span> /]<span class="hljs-comment"># cd /home</span>
[root<span class="hljs-variable">@19334600a733</span> home]<span class="hljs-comment"># ls</span>
[root<span class="hljs-variable">@19334600a733</span> home]<span class="hljs-comment"># touch test.java</span>
[root<span class="hljs-variable">@19334600a733</span> home]<span class="hljs-comment"># exit</span>
exit
root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:~</span><span class="hljs-comment"># docker ps </span>
<span class="hljs-variable constant_">CONTAINER</span> <span class="hljs-variable constant_">ID</span>   <span class="hljs-variable constant_">IMAGE</span>     <span class="hljs-variable constant_">COMMAND</span>   <span class="hljs-variable constant_">CREATED</span>   <span class="hljs-variable constant_">STATUS</span>    <span class="hljs-variable constant_">PORTS</span>     <span class="hljs-variable constant_">NAMES</span>
root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:~</span><span class="hljs-comment"># docker ps -a</span>
<span class="hljs-variable constant_">CONTAINER</span> <span class="hljs-variable constant_">ID</span>   <span class="hljs-variable constant_">IMAGE</span>     <span class="hljs-variable constant_">COMMAND</span>                  <span class="hljs-variable constant_">CREATED</span>          <span class="hljs-variable constant_">STATUS</span>                        <span class="hljs-variable constant_">PORTS</span>     <span class="hljs-variable constant_">NAMES</span>
19334600a733   centos    <span class="hljs-string">"/bin/bash"</span>              <span class="hljs-number">32</span> minutes ago   <span class="hljs-title class_">Exited</span> (<span class="hljs-number">0</span>) <span class="hljs-number">12</span> seconds ago               admiring_bohr
397eabe2c023   centos    <span class="hljs-string">"/bin/bash -c 'while…"</span>   <span class="hljs-number">39</span> minutes ago   <span class="hljs-title class_">Exited</span> (<span class="hljs-number">137</span>) <span class="hljs-number">37</span> minutes ago             focused_ellis
cd940d7abbb2   centos    <span class="hljs-string">"/bin/bash -C 'while…"</span>   <span class="hljs-number">40</span> minutes ago   <span class="hljs-title class_">Exited</span> (<span class="hljs-number">127</span>) <span class="hljs-number">40</span> minutes ago             ecstatic_galileo
23db6f7950f8   centos    <span class="hljs-string">"/bin/bash -C 'while…"</span>   <span class="hljs-number">40</span> minutes ago   <span class="hljs-title class_">Exited</span> (<span class="hljs-number">127</span>) <span class="hljs-number">40</span> minutes ago             keen_sanderson
8f9ce6c66f30   centos    <span class="hljs-string">"/bin/bash"</span>              <span class="hljs-number">43</span> minutes ago   <span class="hljs-title class_">Exited</span> (<span class="hljs-number">0</span>) <span class="hljs-number">40</span> minutes ago               flamboyant_moser
root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:~</span><span class="hljs-comment"># docker cp 19334600a733:/home/test.java /home</span>
root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:~</span><span class="hljs-comment"># cd home</span>
-<span class="hljs-symbol">bash:</span> <span class="hljs-symbol">cd:</span> <span class="hljs-symbol">home:</span> <span class="hljs-title class_">No</span> such file <span class="hljs-keyword">or</span> directory
root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:~</span><span class="hljs-comment"># cd /home</span>
root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:/home</span><span class="hljs-comment"># ls</span>
camile  test.java

<span class="hljs-comment"># 拷贝是一个手动过程，未来我们使用 -v 卷的技术，可以实现</span>
</code></pre><hr><h2 id="h-4docker" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">4.Docker部署简单环境</h2><p>接下来，笔者将带领大家安装Nginx和Tomcat这两个常见的镜像。</p><h3 id="h-41-nginx" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.1 安装Nginx</h3><pre data-type="codeBlock" text="# 1.搜索镜像 search 建议大家去docker搜索，可以看到帮助文档

# 2.下载nginx镜像
docker pull nginx

# 3.运行测试
root@camile:/# docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
nginx        latest    605c77e624dd   8 weeks ago    141MB
centos       latest    5d0da3dc9764   5 months ago   231MB

# -d 后台运行
# --name 给容器命名
# -p 宿主机端口：容器内端口
root@camile:/# docker run -d --name nginx01 -p 3344:80 nginx
586cda0f86a49f103522efcf0300a8592639ae2da23c8317745a487d8373a555
root@camile:/# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                   NAMES
586cda0f86a4   nginx     &quot;/docker-entrypoint.…&quot;   4 seconds ago   Up 4 seconds   0.0.0.0:3344-&gt;80/tcp, :::3344-&gt;80/tcp   nginx01
root@camile:/# curl localhost:3344
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Welcome to nginx!&lt;/title&gt;
&lt;style&gt;
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Welcome to nginx!&lt;/h1&gt;
&lt;p&gt;If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.&lt;/p&gt;

&lt;p&gt;For online documentation and support please refer to
&lt;a href=&quot;http://nginx.org/&quot;&gt;nginx.org&lt;/a&gt;.&lt;br/&gt;
Commercial support is available at
&lt;a href=&quot;http://nginx.com/&quot;&gt;nginx.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thank you for using nginx.&lt;/em&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
"><code># <span class="hljs-number">1.</span>搜索镜像 search 建议大家去docker搜索，可以看到帮助文档

# <span class="hljs-number">2.</span>下载nginx镜像
docker pull nginx

# <span class="hljs-number">3.</span>运行测试
root@camile:<span class="hljs-operator">/</span># docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
nginx        latest    605c77e624dd   <span class="hljs-number">8</span> <span class="hljs-literal">weeks</span> ago    141MB
centos       latest    5d0da3dc9764   <span class="hljs-number">5</span> months ago   231MB

# <span class="hljs-operator">-</span>d 后台运行
# <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name 给容器命名
# <span class="hljs-operator">-</span>p 宿主机端口：容器内端口
root@camile:<span class="hljs-operator">/</span># docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name nginx01 <span class="hljs-operator">-</span>p <span class="hljs-number">3344</span>:<span class="hljs-number">80</span> nginx
586cda0f86a49f103522efcf0300a8592639ae2da23c8317745a487d8373a555
root@camile:<span class="hljs-operator">/</span># docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                   NAMES
586cda0f86a4   nginx     <span class="hljs-string">"/docker-entrypoint.…"</span>   <span class="hljs-number">4</span> <span class="hljs-literal">seconds</span> ago   Up <span class="hljs-number">4</span> <span class="hljs-literal">seconds</span>   <span class="hljs-number">0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span>:<span class="hljs-number">3344</span><span class="hljs-operator">-</span><span class="hljs-operator">></span><span class="hljs-number">80</span><span class="hljs-operator">/</span>tcp, :::<span class="hljs-number">3344</span><span class="hljs-operator">-</span><span class="hljs-operator">></span><span class="hljs-number">80</span><span class="hljs-operator">/</span>tcp   nginx01
root@camile:<span class="hljs-operator">/</span># curl localhost:<span class="hljs-number">3344</span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">!</span>DOCTYPE html<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>html<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>head<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>title<span class="hljs-operator">></span>Welcome to nginx<span class="hljs-operator">!</span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>title<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>style<span class="hljs-operator">></span>
html { color<span class="hljs-operator">-</span>scheme: light dark; }
body { width: 35em; margin: <span class="hljs-number">0</span> auto;
font<span class="hljs-operator">-</span>family: Tahoma, Verdana, Arial, sans<span class="hljs-operator">-</span>serif; }
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>style<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>head<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>body<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>h1<span class="hljs-operator">></span>Welcome to nginx<span class="hljs-operator">!</span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>If you see <span class="hljs-built_in">this</span> page, the nginx web server <span class="hljs-keyword">is</span> successfully installed and
working. Further configuration <span class="hljs-keyword">is</span> required.&#x3C;<span class="hljs-operator">/</span>p<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>For online documentation and support please refer to
<span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"http://nginx.org/"</span><span class="hljs-operator">></span>nginx.org&#x3C;<span class="hljs-operator">/</span>a<span class="hljs-operator">></span>.&#x3C;br<span class="hljs-operator">/</span><span class="hljs-operator">></span>
Commercial support <span class="hljs-keyword">is</span> available at
<span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"http://nginx.com/"</span><span class="hljs-operator">></span>nginx.com&#x3C;<span class="hljs-operator">/</span>a<span class="hljs-operator">></span>.&#x3C;<span class="hljs-operator">/</span>p<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>em<span class="hljs-operator">></span>Thank you <span class="hljs-keyword">for</span> <span class="hljs-keyword">using</span> <span class="hljs-title">nginx</span>.<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span><span class="hljs-title">em</span><span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span><span class="hljs-title">p</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span><span class="hljs-title">body</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span><span class="hljs-title">html</span><span class="hljs-operator">></span>
</code></pre><h3 id="h-42-tomcat" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.2 部署Tomcat</h3><pre data-type="codeBlock" text="# 官方的使用
docker run -it --rm tomcat:9.0

# 我们之前的启动都是后台，停止了容器之后，容器还是可以查到，官方的方法一般是用完及删

# 我们的逻辑是先下载再启动
docker pull tomcat:9.0

# 启动运行
root@camile:/# docker run -d -p 3355:8080 --name tomcat01 tomcat

# 发现问腿：1.Linux命令少了 2.没有webapps，阿里云镜像的原因，默认是最小的镜像，所有不必要的都删除吊
# 保证最小的运行环境
"><code># 官方的使用
docker run <span class="hljs-operator">-</span>it <span class="hljs-operator">-</span><span class="hljs-operator">-</span>rm tomcat:<span class="hljs-number">9.0</span>

# 我们之前的启动都是后台，停止了容器之后，容器还是可以查到，官方的方法一般是用完及删

# 我们的逻辑是先下载再启动
docker pull tomcat:<span class="hljs-number">9.0</span>

# 启动运行
root@camile:<span class="hljs-operator">/</span># docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span>p <span class="hljs-number">3355</span>:<span class="hljs-number">8080</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name tomcat01 tomcat

# 发现问腿：<span class="hljs-number">1</span>.Linux命令少了 <span class="hljs-number">2.</span>没有webapps，阿里云镜像的原因，默认是最小的镜像，所有不必要的都删除吊
# 保证最小的运行环境
</code></pre><h3 id="h-43-eskibana" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.3 部署ES+kibana</h3><pre data-type="codeBlock" text="# es 暴露的端口很多！
# es 十分的耗内存
# es 的数据一般需要放置在安全目录！挂载
# --net somenetwork ? 网络配置

# 启动 elasticsearch
docker run -d --name elasticsearch --net somenetwork -p 9200:9200  -e &quot;discovery.type=single-node&quot; elasticsearch:7.6.2

# 启动了 linux就卡住了 docker stats 查看 cpu状态

# es 是十分耗内存的  1核2G

# 查看 docker stats

# 测试以下 es 是否成功了

# 赶紧关闭，增加内存的限制 -e环境配置修改
docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e &quot;discovery.type=single-node&quot; elasticsearch:tag
docker run -d --name elasticsearch --net somenetwork -p 9200:9200  -e ES_JAVA_OPTS=&quot;-Xms64m -Xmx12m&quot; elasticsearch:7.6.2
"><code># es 暴露的端口很多！
# es 十分的耗内存
# es 的数据一般需要放置在安全目录！挂载
# <span class="hljs-operator">-</span><span class="hljs-operator">-</span>net somenetwork ? 网络配置

# 启动 elasticsearch
docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name elasticsearch <span class="hljs-operator">-</span><span class="hljs-operator">-</span>net somenetwork <span class="hljs-operator">-</span>p <span class="hljs-number">9200</span>:<span class="hljs-number">9200</span>  <span class="hljs-operator">-</span>e <span class="hljs-string">"discovery.type=single-node"</span> elasticsearch:<span class="hljs-number">7.6</span><span class="hljs-number">.2</span>

# 启动了 linux就卡住了 docker stats 查看 cpu状态

# es 是十分耗内存的  <span class="hljs-number">1</span>核2G

# 查看 docker stats

# 测试以下 es 是否成功了

# 赶紧关闭，增加内存的限制 <span class="hljs-operator">-</span>e环境配置修改
docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name elasticsearch <span class="hljs-operator">-</span><span class="hljs-operator">-</span>net somenetwork <span class="hljs-operator">-</span>p <span class="hljs-number">9200</span>:<span class="hljs-number">9200</span> <span class="hljs-operator">-</span>p <span class="hljs-number">9300</span>:<span class="hljs-number">9300</span> <span class="hljs-operator">-</span>e <span class="hljs-string">"discovery.type=single-node"</span> elasticsearch:tag
docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name elasticsearch <span class="hljs-operator">-</span><span class="hljs-operator">-</span>net somenetwork <span class="hljs-operator">-</span>p <span class="hljs-number">9200</span>:<span class="hljs-number">9200</span>  <span class="hljs-operator">-</span>e ES_JAVA_OPTS<span class="hljs-operator">=</span><span class="hljs-string">"-Xms64m -Xmx12m"</span> elasticsearch:<span class="hljs-number">7.6</span><span class="hljs-number">.2</span>
</code></pre><h3 id="h-44-docker" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.4 Docker可视化工具</h3><p>这里我们可以使用一个portainer，它是一个Docker的图形化界面管理工具，提供一个后台面板供我们操作，登陆进入可以管理我们的容器。</p><pre data-type="codeBlock" text="docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true   portainer/portaioner
"><code>docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span>p <span class="hljs-number">8088</span>:<span class="hljs-number">9000</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>restart<span class="hljs-operator">=</span>always <span class="hljs-operator">-</span>v <span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>run<span class="hljs-operator">/</span>docker.sock:<span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>run<span class="hljs-operator">/</span>docker.sock <span class="hljs-operator">-</span><span class="hljs-operator">-</span>privileged<span class="hljs-operator">=</span><span class="hljs-literal">true</span>   portainer<span class="hljs-operator">/</span>portaioner
</code></pre><p>在虚拟机中可以使用：localhost:8080来访问界面</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6ba3aff68875990170b5a585c60399b0d6f2db9e2815a2f2ea16d1848401e45d.png" alt="portainer" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">portainer</figcaption></figure><hr><h2 id="h-5docker" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">5.Docker镜像理解</h2><h3 id="h-51" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">5.1 镜像是什么</h3><p>镜像是一种轻量级、可执行的独立软件包，用来打包软件运行环境和基于运行环境开发的软件，它包含运行某个软件所需的所有内容，包括代码、运行时、库、环境变量和配置文件。</p><p>如何获得镜像：</p><ul><li><p>从远程仓库下载</p></li><li><p>朋友拷贝</p></li><li><p>自己制作一个镜像DockerFile</p></li></ul><h3 id="h-52" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">5.2 镜像加载原理</h3><p><strong>UnionFS(联合文件系统)</strong></p><p>我们下载的时候看到的一层一层的数据就是这个。</p><p>UnionFS（联合文件系统）：Union文件系统是一种分层、轻量级并且高性能的文件系统，它支持对文件系统的修改作为一次提交来一层层地叠加，同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承，基于基础镜像（没有父镜像），可以制作各种具体的应用镜像。</p><p>特性：一次同时加载多个文件系统，但从外表看起来，只能看到一个文件系统，联合加载会把各层文件系统叠加起来，这样最终的文件系统会包含所有的底层的文件和目录。</p><blockquote><p>Docker镜像加载原理</p></blockquote><p>docker的镜像实际上由一层一层的文件系统组成，这种层级的文件系统UnionFS。</p><p>bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统，在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的，包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了，此时内存的使用权已由bootfs转交给内核，此时系统也会卸载bootfs。</p><p>rootfs (root file system)，在bootfs之上。包含的就是典型Linux系统中的/dev,/proc, /bin, /etc等标准目录和文件。各种不同的操作系统发行版，比如Ubuntu , Centos等等</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/dd4ad3d75ae672c6b1d6cb64b6fe57a89cc281068c7ba6bb461b3003d2621182.png" alt="镜像分层" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">镜像分层</figcaption></figure><blockquote><p>平时我们安装虚拟机的CentOS都是好几个G，为什么Docker这里才200M？</p></blockquote><p>对于一个精简的OS，rootfs可以很小，只需要包含最基本的命令、工具和程序库就可以了，因为底层直接用Host的kernel，自己只需要提供rootfs就可以了。因此可见对于不同的linux发行版，bootfs基本是一致的，rootfs会有差别，因此不同的发行版可以公用bootfs。</p><h3 id="h-53" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">5.3 分层理解</h3><blockquote><p>分层的镜像</p></blockquote><p>我们可以去下载一个镜像，注意观察下载的日志输出，可以看到是一层一层地在下载。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/90f2433ac890df94945ecd2aaaf2dd40d0299b302fb7e7c3ec67b4bcf5c24716.png" alt="下载时分层" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">下载时分层</figcaption></figure><p>思考：为什么Docker镜像要采用这种分层结构？</p><p>最大的好处，我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来，那么宿主机只需在磁盘上保留一份base镜像，同时内存中也只需要加载一份base镜像，这样就可以为所有的容器服务了，而且镜像的每一层都可以被共享。</p><p>查看镜像分层的方式可以通过docker image inspect命令!</p><pre data-type="codeBlock" text="[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker image inspect nginx:latest
[
    {
        &quot;Id&quot;: &quot;sha256:ae2feff98a0cc5095d97c6c283dcd33090770c76d63877caa99aefbbe4343bdd&quot;,
        &quot;RepoTags&quot;: [
            &quot;nginx:latest&quot;
        ],
        &quot;RepoDigests&quot;: [
            &quot;nginx@sha256:4cf620a5c81390ee209398ecc18e5fb9dd0f5155cd82adcbae532fec94006fb9&quot;
        ],
        &quot;Parent&quot;: &quot;&quot;,
        &quot;Comment&quot;: &quot;&quot;,
        &quot;Created&quot;: &quot;2020-12-15T20:21:00.007674532Z&quot;,
        &quot;Container&quot;: &quot;4cc5da85f27ca0d200407f0593422676a3bab482227daee044d797d1798c96c9&quot;,
        &quot;ContainerConfig&quot;: {
            &quot;Hostname&quot;: &quot;4cc5da85f27c&quot;,
            &quot;Domainname&quot;: &quot;&quot;,
            &quot;User&quot;: &quot;&quot;,
            &quot;AttachStdin&quot;: false,
            &quot;AttachStdout&quot;: false,
            &quot;AttachStderr&quot;: false,
            &quot;ExposedPorts&quot;: {
                &quot;80/tcp&quot;: {}
            },
            &quot;Tty&quot;: false,
            &quot;OpenStdin&quot;: false,
            &quot;StdinOnce&quot;: false,
            &quot;Env&quot;: [
                &quot;PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&quot;,
                &quot;NGINX_VERSION=1.19.6&quot;,
                &quot;NJS_VERSION=0.5.0&quot;,
                &quot;PKG_RELEASE=1~buster&quot;
            ],
            &quot;Cmd&quot;: [
                &quot;/bin/sh&quot;,
                &quot;-c&quot;,
                &quot;#(nop) &quot;,
                &quot;CMD [\&quot;nginx\&quot; \&quot;-g\&quot; \&quot;daemon off;\&quot;]&quot;
            ],
            &quot;Image&quot;: &quot;sha256:13bffe371b56f4aeed88218ec17d0c6f653a83b49bd3e211fc8cfa2ca5d7a3d3&quot;,
            &quot;Volumes&quot;: null,
            &quot;WorkingDir&quot;: &quot;&quot;,
            &quot;Entrypoint&quot;: [
                &quot;/docker-entrypoint.sh&quot;
            ],
            &quot;OnBuild&quot;: null,
            &quot;Labels&quot;: {
                &quot;maintainer&quot;: &quot;NGINX Docker Maintainers &lt;docker-maint@nginx.com&gt;&quot;
            },
            &quot;StopSignal&quot;: &quot;SIGQUIT&quot;
        },
        &quot;DockerVersion&quot;: &quot;19.03.12&quot;,
        &quot;Author&quot;: &quot;&quot;,
        &quot;Config&quot;: {
            &quot;Hostname&quot;: &quot;&quot;,
            &quot;Domainname&quot;: &quot;&quot;,
            &quot;User&quot;: &quot;&quot;,
            &quot;AttachStdin&quot;: false,
            &quot;AttachStdout&quot;: false,
            &quot;AttachStderr&quot;: false,
            &quot;ExposedPorts&quot;: {
                &quot;80/tcp&quot;: {}
            },
            &quot;Tty&quot;: false,
            &quot;OpenStdin&quot;: false,
            &quot;StdinOnce&quot;: false,
            &quot;Env&quot;: [
                &quot;PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&quot;,
                &quot;NGINX_VERSION=1.19.6&quot;,
                &quot;NJS_VERSION=0.5.0&quot;,
                &quot;PKG_RELEASE=1~buster&quot;
            ],
            &quot;Cmd&quot;: [
                &quot;nginx&quot;,
                &quot;-g&quot;,
                &quot;daemon off;&quot;
            ],
            &quot;Image&quot;: &quot;sha256:13bffe371b56f4aeed88218ec17d0c6f653a83b49bd3e211fc8cfa2ca5d7a3d3&quot;,
            &quot;Volumes&quot;: null,
            &quot;WorkingDir&quot;: &quot;&quot;,
            &quot;Entrypoint&quot;: [
                &quot;/docker-entrypoint.sh&quot;
            ],
            &quot;OnBuild&quot;: null,
            &quot;Labels&quot;: {
                &quot;maintainer&quot;: &quot;NGINX Docker Maintainers &lt;docker-maint@nginx.com&gt;&quot;
            },
            &quot;StopSignal&quot;: &quot;SIGQUIT&quot;
        },
        &quot;Architecture&quot;: &quot;amd64&quot;,
        &quot;Os&quot;: &quot;linux&quot;,
        &quot;Size&quot;: 132935043,
        &quot;VirtualSize&quot;: 132935043,
        &quot;GraphDriver&quot;: {
            &quot;Data&quot;: {
                &quot;LowerDir&quot;: &quot;/var/lib/docker/overlay2/cb791e78a08db7091bf2ce1d78603f1758f52199e57f1805156fe30e39067aae/diff:/var/lib/docker/overlay2/1e73a72b25af68ee9abf4eb443f778d31226e12e9af428fcc14c7b044c83b258/diff:/var/lib/docker/overlay2/88c9c01762f2af8327db65d0b0d4a64785e87c9c2ab76c62e7d03619db03a985/diff:/var/lib/docker/overlay2/7304ab112ac4a9cb91fc6f74730be28fecbe19f042e92d321aa9181424cc4b2e/diff&quot;,
                &quot;MergedDir&quot;: &quot;/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/merged&quot;,
                &quot;UpperDir&quot;: &quot;/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/diff&quot;,
                &quot;WorkDir&quot;: &quot;/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/work&quot;
            },
            &quot;Name&quot;: &quot;overlay2&quot;
        },
        &quot;RootFS&quot;: {
            &quot;Type&quot;: &quot;layers&quot;,
            &quot;Layers&quot;: [
                &quot;sha256:87c8a1d8f54f3aa4e05569e8919397b65056aa71cdf48b7f061432c98475eee9&quot;,
                &quot;sha256:5c4e5adc71a82a96f02632433de31c998c5a9e2fccdcbaee780ae83158fac4fa&quot;,
                &quot;sha256:7d2b207c26790f693ab1942bbe26af8e2b6a14248969e542416155a912fec30d&quot;,
                &quot;sha256:2c7498eef94aef8c40d106f3e42f7da62b3eee8fd36012bf7379becc4cd639a2&quot;,
                &quot;sha256:4eaf0ea085df254fd5d2beba4e2c11db70a620dfa411a8ad44149e26428caee4&quot;
            ]
        },
        &quot;Metadata&quot;: {
            &quot;LastTagTime&quot;: &quot;0001-01-01T00:00:00Z&quot;
        }
    }
]
"><code>[root<span class="hljs-variable">@iZwz99sm8v95sckz8bd2c4Z</span> <span class="hljs-operator">~</span>]# docker image inspect nginx:latest
[
    {
        "Id": "sha256:ae2feff98a0cc5095d97c6c283dcd33090770c76d63877caa99aefbbe4343bdd",
        "RepoTags": [
            "nginx:latest"
        ],
        "RepoDigests": [
            "nginx@sha256:4cf620a5c81390ee209398ecc18e5fb9dd0f5155cd82adcbae532fec94006fb9"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2020-12-15T20:21:00.007674532Z",
        "Container": "4cc5da85f27ca0d200407f0593422676a3bab482227daee044d797d1798c96c9",
        "ContainerConfig": {
            "Hostname": "4cc5da85f27c",
            "Domainname": "",
            "User": "",
            "AttachStdin": <span class="hljs-literal">false</span>,
            "AttachStdout": <span class="hljs-literal">false</span>,
            "AttachStderr": <span class="hljs-literal">false</span>,
            "ExposedPorts": {
                "80/tcp": {}
            },
            "Tty": <span class="hljs-literal">false</span>,
            "OpenStdin": <span class="hljs-literal">false</span>,
            "StdinOnce": <span class="hljs-literal">false</span>,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "NGINX_VERSION=1.19.6",
                "NJS_VERSION=0.5.0",
                "PKG_RELEASE=1~buster"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "CMD [\"nginx\" \"<span class="hljs-operator">-</span>g\" \"daemon off;\"]"
            ],
            "Image": "sha256:13bffe371b56f4aeed88218ec17d0c6f653a83b49bd3e211fc8cfa2ca5d7a3d3",
            "Volumes": <span class="hljs-keyword">null</span>,
            "WorkingDir": "",
            "Entrypoint": [
                "/docker-entrypoint.sh"
            ],
            "OnBuild": <span class="hljs-keyword">null</span>,
            "Labels": {
                "maintainer": "NGINX Docker Maintainers &#x3C;docker-maint@nginx.com>"
            },
            "StopSignal": "SIGQUIT"
        },
        "DockerVersion": "19.03.12",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": <span class="hljs-literal">false</span>,
            "AttachStdout": <span class="hljs-literal">false</span>,
            "AttachStderr": <span class="hljs-literal">false</span>,
            "ExposedPorts": {
                "80/tcp": {}
            },
            "Tty": <span class="hljs-literal">false</span>,
            "OpenStdin": <span class="hljs-literal">false</span>,
            "StdinOnce": <span class="hljs-literal">false</span>,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "NGINX_VERSION=1.19.6",
                "NJS_VERSION=0.5.0",
                "PKG_RELEASE=1~buster"
            ],
            "Cmd": [
                "nginx",
                "-g",
                "daemon off;"
            ],
            "Image": "sha256:13bffe371b56f4aeed88218ec17d0c6f653a83b49bd3e211fc8cfa2ca5d7a3d3",
            "Volumes": <span class="hljs-keyword">null</span>,
            "WorkingDir": "",
            "Entrypoint": [
                "/docker-entrypoint.sh"
            ],
            "OnBuild": <span class="hljs-keyword">null</span>,
            "Labels": {
                "maintainer": "NGINX Docker Maintainers &#x3C;docker-maint@nginx.com>"
            },
            "StopSignal": "SIGQUIT"
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": <span class="hljs-number">132935043</span>,
        "VirtualSize": <span class="hljs-number">132935043</span>,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/cb791e78a08db7091bf2ce1d78603f1758f52199e57f1805156fe30e39067aae/diff:/var/lib/docker/overlay2/1e73a72b25af68ee9abf4eb443f778d31226e12e9af428fcc14c7b044c83b258/diff:/var/lib/docker/overlay2/88c9c01762f2af8327db65d0b0d4a64785e87c9c2ab76c62e7d03619db03a985/diff:/var/lib/docker/overlay2/7304ab112ac4a9cb91fc6f74730be28fecbe19f042e92d321aa9181424cc4b2e/diff",
                "MergedDir": "/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/merged",
                "UpperDir": "/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/diff",
                "WorkDir": "/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:87c8a1d8f54f3aa4e05569e8919397b65056aa71cdf48b7f061432c98475eee9",
                "sha256:5c4e5adc71a82a96f02632433de31c998c5a9e2fccdcbaee780ae83158fac4fa",
                "sha256:7d2b207c26790f693ab1942bbe26af8e2b6a14248969e542416155a912fec30d",
                "sha256:2c7498eef94aef8c40d106f3e42f7da62b3eee8fd36012bf7379becc4cd639a2",
                "sha256:4eaf0ea085df254fd5d2beba4e2c11db70a620dfa411a8ad44149e26428caee4"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]
</code></pre><p>这里指示了分层信息:</p><pre data-type="codeBlock" text="&quot;RootFS&quot;: {
    &quot;Type&quot;: &quot;layers&quot;,
    &quot;Layers&quot;: [
        &quot;sha256:87c8a1d8f54f3aa4e05569e8919397b65056aa71cdf48b7f061432c98475eee9&quot;,
        &quot;sha256:5c4e5adc71a82a96f02632433de31c998c5a9e2fccdcbaee780ae83158fac4fa&quot;,
        &quot;sha256:7d2b207c26790f693ab1942bbe26af8e2b6a14248969e542416155a912fec30d&quot;,
        &quot;sha256:2c7498eef94aef8c40d106f3e42f7da62b3eee8fd36012bf7379becc4cd639a2&quot;,
        &quot;sha256:4eaf0ea085df254fd5d2beba4e2c11db70a620dfa411a8ad44149e26428caee4&quot;
    ]
},
"><code><span class="hljs-attr">"RootFS"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"Type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"layers"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"Layers"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
        <span class="hljs-string">"sha256:87c8a1d8f54f3aa4e05569e8919397b65056aa71cdf48b7f061432c98475eee9"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"sha256:5c4e5adc71a82a96f02632433de31c998c5a9e2fccdcbaee780ae83158fac4fa"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"sha256:7d2b207c26790f693ab1942bbe26af8e2b6a14248969e542416155a912fec30d"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"sha256:2c7498eef94aef8c40d106f3e42f7da62b3eee8fd36012bf7379becc4cd639a2"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"sha256:4eaf0ea085df254fd5d2beba4e2c11db70a620dfa411a8ad44149e26428caee4"</span>
    <span class="hljs-punctuation">]</span>
<span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
</code></pre><p>所有的Docker镜像都起始于一个基础镜像层，当进行修改或增加新的内容时，就会在当前镜像层之上，创建新的镜像层。</p><p>举一个简单的例子，假如基于Ubuntu Linux 16.04创建一个新的镜像，这就是新镜像的第一层;如果在该镜像中添加Python包，就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁，就会创建第三个镜像层。</p><p>该镜像当前已经包含3个镜像层，如下图所示(这只是一个用于演示的很简单的例子)。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c50d0eebcf7aeb20a03e9b2ab9f2cedcf289359c93f613704d2ebfac7822a8cd.png" alt="3个镜像层" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">3个镜像层</figcaption></figure><p>在添加额外的镜像层的同时，镜像始终保持是当前所有镜像的组合，理解这一点非常重要。下图中举了一个简单的例子，每个层包含3个文件，而镜像包含了来自两个镜像层的6个文件。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0731c89b86e32d117479b3bd360518e9e0aac22f88a468c454c88d28c9da8292.png" alt="6个文件" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">6个文件</figcaption></figure><p>这种情况下，上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。Docker通过存储引擎（新版本采用快照机制)的方式来实现镜像层堆栈，并保证多镜像层对外展示为统一的文件系统。</p><p>Linux上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义，每种存储引擎都基于Linux中对应的文件系统或者块设备技术，并且每种存储引擎都有其独有的性能特点。</p><p>Docker在Windows 上仅支持windowsfilter一种存储引擎，该引擎基于NTFS文件系统之上实现了分层和CoW[1].下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并，对外提供统一的视图。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b09d9535d9e8ca81bc449376faf2f1942335ad63c37662130f98c5530b24a765.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>特点</p><p>Docker镜像都是只读地，当容器启动时，一个新的可写层被加载到镜像的顶部！</p><p>这一层就是我们通常说的容器层，容器之下的都是镜像层！</p><h3 id="h-54-commit" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">5.4 commit镜像</h3><p>当我们想要保存当前容器的状态，就可以通过commit来提交，获得一个镜像：</p><pre data-type="codeBlock" text="docker commit 提交容器成为一个新的副本

docker commit -m=&quot;提交的描述信息&quot; -a=&quot;作者&quot; 容器id 目标镜像名：[tag]
"><code>docker commit 提交容器成为一个新的副本

docker commit <span class="hljs-operator">-</span>m<span class="hljs-operator">=</span><span class="hljs-string">"提交的描述信息"</span> <span class="hljs-operator">-</span>a<span class="hljs-operator">=</span><span class="hljs-string">"作者"</span> 容器id 目标镜像名：[tag]
</code></pre><p>实战测试</p><pre data-type="codeBlock" text="# 启动一个默认的tomcat

# 发现这个默认的 tomcat 没有webapps应用，镜像的原因，官方的镜像默认 webapps 下是没有文件的

# 自己拷贝进去基本的文件

# 将我们操作过的容器通过commit提交为一个镜像！我们以后就可以使用我们修改过的镜像即可，这也就是我们自己的一个修改镜像。
"><code><span class="hljs-comment"># 启动一个默认的tomcat</span>

<span class="hljs-comment"># 发现这个默认的 tomcat 没有webapps应用，镜像的原因，官方的镜像默认 webapps 下是没有文件的</span>

<span class="hljs-comment"># 自己拷贝进去基本的文件</span>

<span class="hljs-comment"># 将我们操作过的容器通过commit提交为一个镜像！我们以后就可以使用我们修改过的镜像即可，这也就是我们自己的一个修改镜像。</span>
</code></pre><hr><h2 id="h-6docker" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">6.Docker容器数据卷</h2><h3 id="h-61" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">6.1 什么是容器数据卷</h3><p>容器数据卷是Docker中一种用于持久化数据的技术，它可以让容器中的数据在容器删除后不会丢失。Docker的理念是将应用和环境打包成一个镜像，但如果数据都在容器中，那么容器删除后数据也会消失。因此，需要使用数据卷技术实现数据持久化和同步操作。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2125282a414ab5a2e72a9fbe5891fa75ba840de8a43f4b17df0265ecaf60dd76.png" alt="挂载" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">挂载</figcaption></figure><p>例如，在使用MySQL时，如果容器删除，则数据库中的数据也会丢失，因此需要将MySQL数据存储在本地并使用卷技术实现容器和本地数据的同步。卷技术通过目录挂载实现，将容器内的目录挂载到Linux系统上。因此，容器间也可以共享数据，实现数据共享。容器数据卷是Docker中非常重要的一部分，可以实现容器的持久化和同步操作，为容器应用提供了强大的数据管理功能。</p><h3 id="h-62" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">6.2 使用数据卷</h3><blockquote><p>方式一：直接使用命令来挂载 -v</p></blockquote><pre data-type="codeBlock" text=" docker run -it -v 主机目录:容器目录

 # 测试
 docker run -it -v /home/ceshi:/home centos /bin/bash

 # 容器起来时候我们可以通过 docker inspect 容器id 来查看挂载状态
"><code> docker run <span class="hljs-operator">-</span>it <span class="hljs-operator">-</span>v 主机目录:容器目录

 # 测试
 docker run <span class="hljs-operator">-</span>it <span class="hljs-operator">-</span>v <span class="hljs-operator">/</span>home<span class="hljs-operator">/</span>ceshi:<span class="hljs-operator">/</span>home centos <span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>bash

 # 容器起来时候我们可以通过 docker inspect 容器id 来查看挂载状态
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3efcc95eef99b55b2836346578fa70673d048545cd3f97aa4bbd90d140d10142.png" alt="挂载状态" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">挂载状态</figcaption></figure><p>在容器内简历一个test.java测试文件。测试文件的同步，是一个双向绑定的过程。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/39027bcb1890e8de57012137bb40dbba9a8da1c299f222321f165983ec5bee7d.png" alt="双向绑定" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">双向绑定</figcaption></figure><h3 id="h-63-mysql" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">6.3 实战：安装MySQL</h3><pre data-type="codeBlock" text="# 获取镜像
camile@camile:/$ docker pull mysql:5.7

# 运行容器，需要做数据卷挂载 # 安装启动mysql -e表示配置环境
# 官方测试：docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

# 启动我们的mysql
-d 后台运行
-p 端口映射
-v 卷挂载
-n 容器名字
camile@camile:~$ docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=zuiweizhiming666 --name mysql01 mysql:5.7

# 启动成功之后，我们在本地使用 navicat 来测试以下
# navicat 连接到服务器的3310 -3310和容器内的3306映射，这个时候我们就可以连接上了

# 在本地测试创建一个数据库，查看一下我们创建的路径是否ok
"><code># 获取镜像
camile@camile:<span class="hljs-operator">/</span>$ docker pull mysql:<span class="hljs-number">5.7</span>

# 运行容器，需要做数据卷挂载 # 安装启动mysql <span class="hljs-operator">-</span>e表示配置环境
# 官方测试：docker run <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name some<span class="hljs-operator">-</span>mysql <span class="hljs-operator">-</span>e MYSQL_ROOT_PASSWORD<span class="hljs-operator">=</span>my<span class="hljs-operator">-</span>secret<span class="hljs-operator">-</span>pw <span class="hljs-operator">-</span>d mysql:tag

# 启动我们的mysql
<span class="hljs-operator">-</span>d 后台运行
<span class="hljs-operator">-</span>p 端口映射
<span class="hljs-operator">-</span>v 卷挂载
<span class="hljs-operator">-</span>n 容器名字
camile@camile:<span class="hljs-operator">~</span>$ docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span>p <span class="hljs-number">3310</span>:<span class="hljs-number">3306</span> <span class="hljs-operator">-</span>v <span class="hljs-operator">/</span>home<span class="hljs-operator">/</span>mysql<span class="hljs-operator">/</span>conf:<span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>mysql<span class="hljs-operator">/</span>conf.d <span class="hljs-operator">-</span>v <span class="hljs-operator">/</span>home<span class="hljs-operator">/</span>mysql<span class="hljs-operator">/</span>data:<span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>lib<span class="hljs-operator">/</span>mysql <span class="hljs-operator">-</span>e MYSQL_ROOT_PASSWORD<span class="hljs-operator">=</span>zuiweizhiming666 <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name mysql01 mysql:<span class="hljs-number">5.7</span>

# 启动成功之后，我们在本地使用 navicat 来测试以下
# navicat 连接到服务器的<span class="hljs-number">3310</span> <span class="hljs-number">-3310</span>和容器内的<span class="hljs-number">3306</span>映射，这个时候我们就可以连接上了

# 在本地测试创建一个数据库，查看一下我们创建的路径是否ok
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6eae297368b70d41701d579b50cd044432dc8daaa7098e9f6440cd2c4b636d30.png" alt="test" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">test</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/63809284db2379c9bcd176cc5775445cb49fc7e2a81808819d005aff7b8146dd.png" alt="test" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">test</figcaption></figure><h3 id="h-64" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">6.4 具名和匿名挂载</h3><p>Docker中的匿名挂载和具名挂载是两种不同的数据卷挂载方式。</p><p>匿名挂载是指在启动容器时，只指定容器内的路径，而不指定宿主机的目录。这样会生成一个随机的宿主机挂载目录，可以通过 <code>docker volume ls</code> 命令查看。</p><p>具名挂载是指在启动容器时，既指定容器内的路径，又指定一个卷名作为宿主机的目录。 这样可以方便地找到和管理数据卷，也可以通过 <code>docker volume inspect 卷名</code> 命令查看详细信息。</p><p>匿名挂载和具名挂载的语法如下：</p><ul><li><p><code>-v 容器内路径</code> # 匿名挂载</p></li><li><p><code>-v 卷名:容器内路径</code> # 具名挂载</p></li></ul><p>下面是具名挂载和匿名挂载的相关命令代码块：</p><pre data-type="codeBlock" text="# 启动一个nginx容器，使用匿名挂载，将容器内的/etc/nginx目录挂载到宿主机上
docker run -d -P --name nginx01 -v /etc/nginx nginx

# 查看所有的数据卷
docker volume ls

# 启动一个nginx容器，使用具名挂载，将容器内的/etc/nginx目录挂载到宿主机上，并命名为juming-nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx

# 查看具体的数据卷信息
docker volume inspect juming-nginx
"><code># 启动一个nginx容器，使用匿名挂载，将容器内的<span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>nginx目录挂载到宿主机上
docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span>P <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name nginx01 <span class="hljs-operator">-</span>v <span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>nginx nginx

# 查看所有的数据卷
docker volume ls

# 启动一个nginx容器，使用具名挂载，将容器内的<span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>nginx目录挂载到宿主机上，并命名为juming<span class="hljs-operator">-</span>nginx
docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span>P <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name nginx02 <span class="hljs-operator">-</span>v juming<span class="hljs-operator">-</span>nginx:<span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>nginx nginx

# 查看具体的数据卷信息
docker volume inspect juming<span class="hljs-operator">-</span>nginx
</code></pre><h3 id="h-65-dockerfile" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">6.5 DockerFile</h3><p>DockerFile 就是用来构建Docker镜像的构建文件、命令脚本，下面先体验一下！</p><p>通过这个脚本可以生成镜像，镜像是一层一层的，脚本是一个个的命令。</p><pre data-type="codeBlock" text="# 创建一个DockerFile文件，名字可以随机 ，建议 Dockerfile

# 文件中的内容
FROM centos

VOLUME [&quot;volume01&quot;,&quot;volume02&quot;]

CMD echo &quot;---end---&quot;
CMD /bin/bash

# 这里的每个命令，就是镜像的一层
"><code><span class="hljs-comment"># 创建一个DockerFile文件，名字可以随机 ，建议 Dockerfile</span>

<span class="hljs-comment"># 文件中的内容</span>
FROM centos

VOLUME [<span class="hljs-string">"volume01"</span>,<span class="hljs-string">"volume02"</span>]

CMD <span class="hljs-built_in">echo</span> <span class="hljs-string">"---end---"</span>
CMD /bin/bash

<span class="hljs-comment"># 这里的每个命令，就是镜像的一层</span>
</code></pre><pre data-type="codeBlock" text="# 启动自己写的容器
"><code><span class="hljs-comment"># 启动自己写的容器</span>
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e87d1eade445895b9db3856c4754318263f2f3577d07caf3bb4dc0da191fb7d8.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>这个目录就是我们生成镜像的时候自动挂载的数据卷目录。这个卷和外部一定有一个同步的目录。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4b900d5f73fd5401a19c3103e614f5e37f929a36d6e8ea18cc6316acf060bbad.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>查看一下卷挂载的路径。</p><p>测试一下刚才的文件是否同步了。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6f3629e426e0d954ec278692359fb0e20c296fe29cf2fee4dc56f86dd350fb9a.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>这种方式我们未来使用的十分多，因为我们通常会构建自己的镜像！假设构建镜像的时候没有挂载卷，要手动镜像挂载 -v 卷名:容器内路径。</p><hr><h2 id="h-7dockerfile" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">7.DockerFile</h2><h3 id="h-71-dockerfile" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">7.1 DockerFile</h3><p>dockerFile 是用来docker镜像的文件，是命令参数脚本。</p><p>构建步骤：编写一个dockerfile文件</p><ol><li><p>docker build 构建成一个镜像</p></li><li><p>docker run 运行镜像</p></li><li><p>docker push 发布镜像</p></li></ol><h3 id="h-72-dockerfile" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">7.2 dockerFile构建过程</h3><p><strong>基础知识</strong>：</p><ol><li><p>每个保留关键字都是大写字母</p></li><li><p>执行从上到下执行</p></li><li><p><code>#</code>表示注释</p></li><li><p>每一个指令都会创建提交一个新的镜像层，并提交！</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4b2a047a7246b78dc60b70f2339a0609aeaf01622ee38c09076fe135bc0ef764.png" alt="dockerfile" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">dockerfile</figcaption></figure><p>dockerfile是面向开发的，我们以后要发布项目，做镜像，就需要编写dockerfile文件，这个文件十分简单。</p><p>Docker镜像逐渐成为企业交付的标准，必须要掌握。</p><p>步骤：</p><p>DockerFile：构建文件，定义了一切的步骤，源代码</p><p>DockerImages：DockerFile构建生成的镜像，最终发布和运行的产品，原来是 jar war。</p><p>Docker容器：容器就是镜像运行起来提供服务的。</p><h3 id="h-73-dockerfile" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">7.3 DockerFile指令</h3><p>知晓了DockerFile命令，我们就可以自己制作镜像：</p><pre data-type="codeBlock" text="FROM          # 基础镜像，一切从这里开始构建
MANITAINER    # 镜像是谁写的 姓名+邮箱
RUN           # 镜像构建的时候需要运行的命令
ADD           # 步骤，tomcat镜像，这个tomcat压缩包！添加内容
WORKDIR       # 镜像的工作目录
VOLUME        # 挂载的目录
EXPOSE        # 指定暴露端口
CMD           # 指定这个容器启动的时候要运行的命令，只有最后一个会生效，可被替代
ENTRYPOINT    # 指定这个容器启动的时候要运行的命令，可以追加命令
ONBUILD       # 当个构建一个被继承dockerfile 的时候，会运行这个指令，触发指令
COPY          # 类似ADD，将我们文件拷贝到镜像中
ENV           # 构建的时候设置环境变量
"><code>FROM          <span class="hljs-comment"># 基础镜像，一切从这里开始构建</span>
MANITAINER    <span class="hljs-comment"># 镜像是谁写的 姓名+邮箱</span>
RUN           <span class="hljs-comment"># 镜像构建的时候需要运行的命令</span>
ADD           <span class="hljs-comment"># 步骤，tomcat镜像，这个tomcat压缩包！添加内容</span>
WORKDIR       <span class="hljs-comment"># 镜像的工作目录</span>
VOLUME        <span class="hljs-comment"># 挂载的目录</span>
EXPOSE        <span class="hljs-comment"># 指定暴露端口</span>
CMD           <span class="hljs-comment"># 指定这个容器启动的时候要运行的命令，只有最后一个会生效，可被替代</span>
ENTRYPOINT    <span class="hljs-comment"># 指定这个容器启动的时候要运行的命令，可以追加命令</span>
ONBUILD       <span class="hljs-comment"># 当个构建一个被继承dockerfile 的时候，会运行这个指令，触发指令</span>
COPY          <span class="hljs-comment"># 类似ADD，将我们文件拷贝到镜像中</span>
ENV           <span class="hljs-comment"># 构建的时候设置环境变量</span>
</code></pre><h3 id="h-74" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">7.4 实战测试</h3><p>Docker Hub 中绝大多数镜像都是从这个基础镜像过来的 <code>FROM scratch</code>，然后配置需要的软件和配置来进行的构建。</p><blockquote><p>创建一个自己的Centos</p></blockquote><pre data-type="codeBlock" text="# 1.编写DockerFile的文件
FROM centos:7

MAINTAINER qiaowei&lt;454921269@qq.com&gt;

ENV MYPATH /usr/local

WORKDIR $MYPATH

RUN yum -y install vim
RUN yum -y install net-tools

EXPOSE 80

CMD echo $MYPATH
CMD echo &quot;---end---&quot;
CMD /bin/bash

# 2.通过这个文件构建镜像
# 命令 docker build -f dockerfile文件路径 -t 镜像名:[tag]
Successfully built 11a9535c2896
Successfully tagged mycentos:0.1

# 3.测试运行
root@camile:/home/dockerfile# docker run -it 11a9535c2896
[root@f9c5dbcee829 local]# pwd
/usr/local
[root@f9c5dbcee829 local]# ifconfig
eth0: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 172.17.0.6  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:06  txqueuelen 0  (Ethernet)
        RX packets 23  bytes 2804 (2.7 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73&lt;UP,LOOPBACK,RUNNING&gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@f9c5dbcee829 local]# vim test
"><code># <span class="hljs-number">1.</span>编写DockerFile的文件
FROM centos:<span class="hljs-number">7</span>

MAINTAINER qiaowei<span class="hljs-operator">&#x3C;</span><span class="hljs-number">454921269</span>@qq.com>

ENV MYPATH <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local

WORKDIR $MYPATH

RUN yum <span class="hljs-operator">-</span>y install vim
RUN yum <span class="hljs-operator">-</span>y install net<span class="hljs-operator">-</span>tools

EXPOSE <span class="hljs-number">80</span>

CMD echo $MYPATH
CMD echo <span class="hljs-string">"---end---"</span>
CMD <span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>bash

# <span class="hljs-number">2.</span>通过这个文件构建镜像
# 命令 docker build <span class="hljs-operator">-</span>f dockerfile文件路径 <span class="hljs-operator">-</span>t 镜像名:[tag]
Successfully built 11a9535c2896
Successfully tagged mycentos:<span class="hljs-number">0</span><span class="hljs-number">.1</span>

# <span class="hljs-number">3.</span>测试运行
root@camile:<span class="hljs-operator">/</span>home<span class="hljs-operator">/</span>dockerfile# docker run <span class="hljs-operator">-</span>it 11a9535c2896
[root@f9c5dbcee829 local]# pwd
<span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local
[root@f9c5dbcee829 local]# ifconfig
eth0: flags<span class="hljs-operator">=</span><span class="hljs-number">4163</span><span class="hljs-operator">&#x3C;</span>UP,BROADCAST,RUNNING,MULTICAST<span class="hljs-operator">></span>  mtu <span class="hljs-number">1500</span>
        inet <span class="hljs-number">172.17</span><span class="hljs-number">.0</span><span class="hljs-number">.6</span>  netmask <span class="hljs-number">255.255</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span>  broadcast <span class="hljs-number">172.17</span><span class="hljs-number">.255</span><span class="hljs-number">.255</span>
        <span class="hljs-literal">ether</span> 02:<span class="hljs-number">42</span>:ac:<span class="hljs-number">11</span>:00:06  txqueuelen <span class="hljs-number">0</span>  (Ethernet)
        RX packets <span class="hljs-number">23</span>  <span class="hljs-keyword">bytes</span> <span class="hljs-number">2804</span> (<span class="hljs-number">2.7</span> KiB)
        RX errors <span class="hljs-number">0</span>  dropped <span class="hljs-number">0</span>  overruns <span class="hljs-number">0</span>  frame <span class="hljs-number">0</span>
        TX packets <span class="hljs-number">0</span>  <span class="hljs-keyword">bytes</span> <span class="hljs-number">0</span> (<span class="hljs-number">0</span><span class="hljs-number">.0</span> B)
        TX errors <span class="hljs-number">0</span>  dropped <span class="hljs-number">0</span> overruns <span class="hljs-number">0</span>  carrier <span class="hljs-number">0</span>  collisions <span class="hljs-number">0</span>

lo: flags<span class="hljs-operator">=</span><span class="hljs-number">73</span><span class="hljs-operator">&#x3C;</span>UP,LOOPBACK,RUNNING<span class="hljs-operator">></span>  mtu <span class="hljs-number">65536</span>
        inet <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>  netmask <span class="hljs-number">255.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span>
        loop  txqueuelen <span class="hljs-number">1000</span>  (Local Loopback)
        RX packets <span class="hljs-number">0</span>  <span class="hljs-keyword">bytes</span> <span class="hljs-number">0</span> (<span class="hljs-number">0</span><span class="hljs-number">.0</span> B)
        RX errors <span class="hljs-number">0</span>  dropped <span class="hljs-number">0</span>  overruns <span class="hljs-number">0</span>  frame <span class="hljs-number">0</span>
        TX packets <span class="hljs-number">0</span>  <span class="hljs-keyword">bytes</span> <span class="hljs-number">0</span> (<span class="hljs-number">0</span><span class="hljs-number">.0</span> B)
        TX errors <span class="hljs-number">0</span>  dropped <span class="hljs-number">0</span> overruns <span class="hljs-number">0</span>  carrier <span class="hljs-number">0</span>  collisions <span class="hljs-number">0</span>

[root@f9c5dbcee829 local]# vim test
</code></pre><p>自己写的centos 添加了vim 和 net-tools ，测试成功！</p><p>我们可以列出本地镜像的变更历史</p><pre data-type="codeBlock" text="root@camile:/home/dockerfile# docker history 11a9535c2896
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
11a9535c2896   4 minutes ago   /bin/sh -c #(nop)  CMD [&quot;/bin/sh&quot; &quot;-c&quot; &quot;/bin…   0B        
5005cf751c81   4 minutes ago   /bin/sh -c #(nop)  CMD [&quot;/bin/sh&quot; &quot;-c&quot; &quot;echo…   0B        
21407997234d   4 minutes ago   /bin/sh -c #(nop)  CMD [&quot;/bin/sh&quot; &quot;-c&quot; &quot;echo…   0B        
a567cd94200e   4 minutes ago   /bin/sh -c #(nop)  EXPOSE 80                    0B        
48ad5b128e9a   4 minutes ago   /bin/sh -c yum -y install net-tools             161MB     
da7d8a18d88e   4 minutes ago   /bin/sh -c yum -y install vim                   216MB     
b1524097a173   4 minutes ago   /bin/sh -c #(nop) WORKDIR /usr/local            0B        
01282534605d   4 minutes ago   /bin/sh -c #(nop)  ENV MYPATH=/usr/local        0B        
9bb0de5c2898   4 minutes ago   /bin/sh -c #(nop)  MAINTAINER qiaowei&lt;454921…   0B        
eeb6ee3f44bd   5 months ago    /bin/sh -c #(nop)  CMD [&quot;/bin/bash&quot;]            0B        
&lt;missing&gt;      5 months ago    /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
"><code>root@camile:<span class="hljs-operator">/</span>home<span class="hljs-operator">/</span>dockerfile# docker history 11a9535c2896
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
11a9535c2896   <span class="hljs-number">4</span> <span class="hljs-literal">minutes</span> ago   <span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>sh <span class="hljs-operator">-</span>c #(nop)  CMD [<span class="hljs-string">"/bin/sh"</span> <span class="hljs-string">"-c"</span> <span class="hljs-string">"/bin…   0B        
5005cf751c81   4 minutes ago   /bin/sh -c #(nop)  CMD ["</span><span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>sh<span class="hljs-string">" "</span><span class="hljs-operator">-</span>c<span class="hljs-string">" "</span>echo…   0B        
21407997234d   <span class="hljs-number">4</span> <span class="hljs-literal">minutes</span> ago   <span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>sh <span class="hljs-operator">-</span>c #(nop)  CMD [<span class="hljs-string">"/bin/sh"</span> <span class="hljs-string">"-c"</span> <span class="hljs-string">"echo…   0B        
a567cd94200e   4 minutes ago   /bin/sh -c #(nop)  EXPOSE 80                    0B        
48ad5b128e9a   4 minutes ago   /bin/sh -c yum -y install net-tools             161MB     
da7d8a18d88e   4 minutes ago   /bin/sh -c yum -y install vim                   216MB     
b1524097a173   4 minutes ago   /bin/sh -c #(nop) WORKDIR /usr/local            0B        
01282534605d   4 minutes ago   /bin/sh -c #(nop)  ENV MYPATH=/usr/local        0B        
9bb0de5c2898   4 minutes ago   /bin/sh -c #(nop)  MAINTAINER qiaowei&#x3C;454921…   0B        
eeb6ee3f44bd   5 months ago    /bin/sh -c #(nop)  CMD ["</span><span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>bash<span class="hljs-string">"]            0B        
&#x3C;missing>      5 months ago    /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
</span></code></pre><h3 id="h-75-tomcat" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">7.5 tomcat镜像</h3><ol><li><p>准备镜像文件 tomcat 压缩包，jdk的压缩包。</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/584570e9d5bb90b06b12bf49e7dd71866adef04a4dded7f19eb3138b9f91522f.png" alt="jdk" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">jdk</figcaption></figure><ol><li><p>编写dockerfile文件，官方命名“Dockerfile”，build会自动寻找这个文件，就不需要 -f 指定了。</p><pre data-type="codeBlock" text="root@camile:/home/qiaowei/build/tomcat# touch readme.txt
root@camile:/home/qiaowei/build/tomcat# vim Dockerfile
"><code>root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:/home/qiaowei/build/tomcat</span><span class="hljs-comment"># touch readme.txt</span>
root<span class="hljs-variable">@camile</span><span class="hljs-symbol">:/home/qiaowei/build/tomcat</span><span class="hljs-comment"># vim Dockerfile</span>
</code></pre><pre data-type="codeBlock" text="# 自己制作Dockerfile
FROM centos:7
MAINTAINER qiaowei&lt;454921269@qq.com&gt;

COPY readme.txt /usr/local/readme.txt

ADD jdk-17_linux-x64_bin.tar.gz /usr/local/
ADD apache-tomcat-9.0.59.tar.gz /usr/local/

RUN yum -y install vim

ENV MYPATH /usr/local
WORKDIR $MYPATH

ENV JAVA_HOME /usr/local/jdk-17.0.2
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.59
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.59
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin

EXPOSE 8080

CMD /usr/local/apache-tomcat-9.0.59/bin/startup.sh &amp;&amp; tail -F /usr/local/apache-tomcat-9.0.59/bin.logs/catalina.out
"><code># 自己制作Dockerfile
FROM centos:<span class="hljs-number">7</span>
MAINTAINER qiaowei<span class="hljs-operator">&#x3C;</span><span class="hljs-number">454921269</span>@qq.com>

COPY readme.txt <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>readme.txt

ADD jdk<span class="hljs-operator">-</span>17_linux<span class="hljs-operator">-</span>x64_bin.tar.gz <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>
ADD apache<span class="hljs-operator">-</span>tomcat<span class="hljs-number">-9.0</span><span class="hljs-number">.59</span>.tar.gz <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>

RUN yum <span class="hljs-operator">-</span>y install vim

ENV MYPATH <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local
WORKDIR $MYPATH

ENV JAVA_HOME <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>jdk<span class="hljs-number">-17.0</span><span class="hljs-number">.2</span>
ENV CLASSPATH $JAVA_HOME<span class="hljs-operator">/</span>lib<span class="hljs-operator">/</span>dt.jar:$JAVA_HOME<span class="hljs-operator">/</span>lib<span class="hljs-operator">/</span>tools.jar
ENV CATALINA_HOME <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>apache<span class="hljs-operator">-</span>tomcat<span class="hljs-number">-9.0</span><span class="hljs-number">.59</span>
ENV CATALINA_BASH <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>apache<span class="hljs-operator">-</span>tomcat<span class="hljs-number">-9.0</span><span class="hljs-number">.59</span>
ENV PATH $PATH:$JAVA_HOME<span class="hljs-operator">/</span>bin:$CATALINA_HOME<span class="hljs-operator">/</span>lib:$CATALINA_HOME<span class="hljs-operator">/</span>bin

EXPOSE <span class="hljs-number">8080</span>

CMD <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>apache<span class="hljs-operator">-</span>tomcat<span class="hljs-number">-9.0</span><span class="hljs-number">.59</span><span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>startup.sh <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> tail <span class="hljs-operator">-</span>F <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>apache<span class="hljs-operator">-</span>tomcat<span class="hljs-number">-9.0</span><span class="hljs-number">.59</span><span class="hljs-operator">/</span>bin.logs/catalina.out
</code></pre></li><li><p>构建镜像</p><pre data-type="codeBlock" text="# docker build -t diytomcat .
"><code><span class="hljs-meta"># docker build -t diytomcat .</span>
</code></pre></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/56ed7d104bb597fba3ae51a2145143692c849e85c8624829b41d65540a12f4bb.png" alt="构建镜像" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">构建镜像</figcaption></figure><p>1.</p><ol start="2"><li><p>运行容器：</p><pre data-type="codeBlock" text="docker run -d -p 9090:8080 --name qiaoweitomcat -v /home/qiaowei/build/tomcat/test:/usr/local/apache-tomcat-9.0.59/webapps/test -v /home/qiaowei/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.59/logs diytomcat
"><code>docker run <span class="hljs-operator">-</span>d <span class="hljs-operator">-</span>p <span class="hljs-number">9090</span>:<span class="hljs-number">8080</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name qiaoweitomcat <span class="hljs-operator">-</span>v <span class="hljs-operator">/</span>home<span class="hljs-operator">/</span>qiaowei<span class="hljs-operator">/</span>build<span class="hljs-operator">/</span>tomcat<span class="hljs-operator">/</span>test:<span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>apache<span class="hljs-operator">-</span>tomcat<span class="hljs-number">-9.0</span><span class="hljs-number">.59</span><span class="hljs-operator">/</span>webapps<span class="hljs-operator">/</span>test <span class="hljs-operator">-</span>v <span class="hljs-operator">/</span>home<span class="hljs-operator">/</span>qiaowei<span class="hljs-operator">/</span>build<span class="hljs-operator">/</span>tomcat<span class="hljs-operator">/</span>tomcatlogs<span class="hljs-operator">/</span>:<span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>local<span class="hljs-operator">/</span>apache<span class="hljs-operator">-</span>tomcat<span class="hljs-number">-9.0</span><span class="hljs-number">.59</span><span class="hljs-operator">/</span>logs diytomcat
</code></pre></li><li><p>访问测试</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/17f974d07deedc1d0953370ad579fc17650788b2b840e9fd2aa43b096e52fd8c.png" alt="tomcat" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">tomcat</figcaption></figure><ol><li><p>发布项目（由于做了卷挂载，我们直接在本地编写项目就可以）</p><pre data-type="codeBlock" text="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;web-app xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;
           xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
           xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_4_0.xsd&quot;
           version=&quot;4.0&quot;&gt;
&lt;/web-app&gt;
"><code><span class="hljs-operator">&#x3C;</span>?xml version<span class="hljs-operator">=</span><span class="hljs-string">"1.0"</span> encoding<span class="hljs-operator">=</span><span class="hljs-string">"UTF-8"</span>?<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>web<span class="hljs-operator">-</span>app xmlns<span class="hljs-operator">=</span><span class="hljs-string">"http://java.sun.com/xml/ns/javaee"</span>
           xmlns:xsi<span class="hljs-operator">=</span><span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>
           xsi:schemaLocation<span class="hljs-operator">=</span><span class="hljs-string">"http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_4_0.xsd"</span>
           version<span class="hljs-operator">=</span><span class="hljs-string">"4.0"</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>web<span class="hljs-operator">-</span>app<span class="hljs-operator">></span>
</code></pre></li><li><p>随便写一个html文件，测试访问</p><pre data-type="codeBlock" text="&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
         &lt;meta charset=&quot;UTF-8&quot;/&gt;
        &lt;title&gt;这是个标题&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;这是一个一个简单的HTML&lt;/h1&gt;
        &lt;p&gt;Hello World！&lt;/p&gt;
    &lt;/body&gt;
&lt;/html&gt;
"><code><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">!</span>DOCTYPE html<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>html<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>head<span class="hljs-operator">></span>
         <span class="hljs-operator">&#x3C;</span>meta charset<span class="hljs-operator">=</span><span class="hljs-string">"UTF-8"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>title<span class="hljs-operator">></span>这是个标题<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>title<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>head<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>body<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>h1<span class="hljs-operator">></span>这是一个一个简单的HTML<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>Hello World！<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>body<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>html<span class="hljs-operator">></span>
</code></pre></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1aedf5e691a39e6f69295cca6cd0b112d50b314bbda3164825b5f57fddf675db.png" alt="html" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">html</figcaption></figure><h3 id="h-76" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">7.6 发布自己的镜像</h3><blockquote><p>Dockerhub</p></blockquote><ol><li><p>地址：<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hub.docker.com/%EF%BC%8C%E6%B3%A8%E5%86%8C%E8%87%AA%E5%B7%B1%E7%9A%84%E8%B4%A6%E5%8F%B7">https://hub.docker.com/，注册自己的账号</a></p></li><li><p>确定这个账号可以登录</p></li><li><p>在我们服务器上提交自己的镜像</p><pre data-type="codeBlock" text="Usage:  docker login [OPTIONS] [SERVER]

Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.

Options:
  -p, --password string   Password
      --password-stdin    Take the password from stdin
  -u, --username string   Username
"><code>Usage:  docker login [OPTIONS] [SERVER]

Log in to a Docker registry.
If no server <span class="hljs-keyword">is</span> specified, the default <span class="hljs-keyword">is</span> defined by the daemon.

Options:
  <span class="hljs-operator">-</span>p, <span class="hljs-operator">-</span><span class="hljs-operator">-</span>password <span class="hljs-keyword">string</span>   Password
      <span class="hljs-operator">-</span><span class="hljs-operator">-</span>password<span class="hljs-operator">-</span>stdin    Take the password <span class="hljs-keyword">from</span> stdin
  <span class="hljs-operator">-</span>u, <span class="hljs-operator">-</span><span class="hljs-operator">-</span>username <span class="hljs-keyword">string</span>   Username
</code></pre></li><li><p>登录完毕后可以提交镜像了</p><pre data-type="codeBlock" text="# 先给镜像打一个标签
docker tag 1ba62e6aa598 wulegekong/tomcat:1.0

# 提交镜像
docker push wulegekong/tomcat:1.0

# 注意：镜像名前一定是自己dockerhub的用户名，否则会拒绝push请求
"><code><span class="hljs-comment"># 先给镜像打一个标签</span>
docker tag 1ba62e6aa598 wulegekong/tomcat:1.0

<span class="hljs-comment"># 提交镜像</span>
docker push wulegekong/tomcat:1.0

<span class="hljs-comment"># 注意：镜像名前一定是自己dockerhub的用户名，否则会拒绝push请求</span>
</code></pre></li></ol><h3 id="h-77" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">7.7 发布镜像到阿里云容器服务</h3><ul><li><p>登录阿里云</p></li><li><p>找到容器镜像服务</p></li><li><p>创建命名空间</p></li><li><p>根据官方提供的操作指南操作即可。</p></li></ul><hr><h2 id="h-8docker" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">8.Docker网络</h2><p>我们使用 ip addr 命令查看本机的所有网络信息：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1eb21eb5f47160787f9512996d88c7b3ab281d1d4fb1a2a8c64a633891d18bab.png" alt="ip addr" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">ip addr</figcaption></figure><p>这里出现了docker0地址，下面简单理解一下docker网络的原理：</p><ol><li><p>我们每安装一个docker容器，docker就会给docker容器分配一个ip，只要电脑安装了docker，就会有一个网卡docker0桥接模式，使用的是evth-pair技术。</p></li><li><p>再次测试ip addr</p></li><li><p>再启动一个容器测试，发现又多了一对网卡</p></li></ol><pre data-type="codeBlock" text="# 我们发现这个容器带来网卡，都是一对一对的 
# evth-pair 就是一对的虚拟设备接口，它们是成对出现的，一端连着协议，一段彼此相连。 
# 正因为有这个特性，evth-pair充当一个桥梁，连接各种虚拟网络设备 
# openstac,Docker容器之间的连接 ovs的连接都是使用evth-pair技术
"><code><span class="hljs-comment"># 我们发现这个容器带来网卡，都是一对一对的 </span>
<span class="hljs-comment"># evth-pair 就是一对的虚拟设备接口，它们是成对出现的，一端连着协议，一段彼此相连。 </span>
<span class="hljs-comment"># 正因为有这个特性，evth-pair充当一个桥梁，连接各种虚拟网络设备 </span>
<span class="hljs-comment"># openstac,Docker容器之间的连接 ovs的连接都是使用evth-pair技术</span>
</code></pre><ol><li><p>我们来测试tomcat01和tomcat02这两个之前创建的容器是否可以相互ping通，能够ping通！</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d2eb12e021de9113b6df13ede82eb37865fc13a0961a716e5327ff16ae8d25cc.png" alt="网络" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">网络</figcaption></figure><p>结论：tomcat01和tomcat02是公用的一个路由器，docker0.</p><p>所有的容器不指定网络的情况下，都是docker0路由的，docker会给我们的容器分配一个默认的可用IP。</p><blockquote><p>小结</p></blockquote><p>Docker使用的是Linux的桥接，宿主机中是一个Docker容器的网桥 docker0。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2aa2a7753319e27030c6d1cffb527f88764c8e2c039052ccd524ba0354ffbd5b.png" alt="网络" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">网络</figcaption></figure><p>Docker中的所有的网络接口都是虚拟的，虚拟的转发效率高，只要容器删除，对应网桥一对就没了。</p><hr><h2 id="h-9" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">9.总结</h2><p>笔者仅仅简单地学习和应用了Docker的一些基本内容，勉强算是入门，后续会学习Docker-Compose等进阶内容，敬请期待。</p>]]></content:encoded>
            <author>samehada@newsletter.paragraph.com (samehada)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/93cf59855f898a2ce73fac6a18c309a68d8734af00d6b5c6a260b49da984d6b0.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[IPFS白皮书阅读]]></title>
            <link>https://paragraph.com/@samehada/ipfs</link>
            <guid>vDXlJop7hxH21w8Bmrep</guid>
            <pubDate>Sat, 18 Mar 2023 11:51:07 GMT</pubDate>
            <description><![CDATA[作者：Samehada 时间：2023/03/18 文章: 《IPFS - Content Addressed, Versioned, P2P File System》 版权声明：文章采用 BY-NC-SA 许可协议，转载请注明出处！前言今天，我们要阅读的是一篇白皮书。这是一个非常有名的去中心化项目—IPFS 星际文件系统，该白皮书于2014年7月发布，此时IPFS项目还没有上线，其中许多内容都尚且是技术构想阶段。虽然目前最新的IPFS技术规范有一些更新，但总体设计和白皮书的内容是基本一致的。 这里简单介绍一下白皮书作者Juan Bennet，他毕业于斯坦福大学的计算机科学专业。低调的胡安并不像中本聪，V神那样被世人所熟知，但他所创建的IPFS在这几年确实风头很劲，很多人知道IPFS，但是对胡安一无所知，这也很正常，主要因为IPFS或者说IPFS所成就的分布式存储可能成为区块链未来发展的重要支撑。 2014年5月胡安就自己成立了公司，起名协议实验室，并且在当年夏天得到了YC的资助，这个YC就是全球著名的创业孵化器，投资了非常非常多优秀的早期创业项目。现在在中国YC掌舵的正是百度前...]]></description>
            <content:encoded><![CDATA[<blockquote><p>作者：Samehada</p><p>时间：2023/03/18</p><p>文章: 《IPFS - Content Addressed, Versioned, P2P File System》</p><p>版权声明：文章采用 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">BY-NC-SA</a> 许可协议，转载请注明出处！</p></blockquote><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">前言</h1><p>今天，我们要阅读的是一篇白皮书。这是一个非常有名的去中心化项目—IPFS 星际文件系统，该白皮书于2014年7月发布，此时IPFS项目还没有上线，其中许多内容都尚且是技术构想阶段。虽然目前最新的IPFS技术规范有一些更新，但总体设计和白皮书的内容是基本一致的。</p><p>这里简单介绍一下白皮书作者Juan Bennet，他毕业于斯坦福大学的计算机科学专业。低调的胡安并不像中本聪，V神那样被世人所熟知，但他所创建的IPFS在这几年确实风头很劲，很多人知道IPFS，但是对胡安一无所知，这也很正常，主要因为IPFS或者说IPFS所成就的分布式存储可能成为区块链未来发展的重要支撑。</p><p>2014年5月胡安就自己成立了公司，起名协议实验室，并且在当年夏天得到了YC的资助，这个YC就是全球著名的创业孵化器，投资了非常非常多优秀的早期创业项目。现在在中国YC掌舵的正是百度前COO陆奇。在胡安拿到投资之后，开始对IPFS、LIBP2P以及其他项目的进行开发。</p><h1 id="h-0" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">0️⃣摘要</h1><p>首先，Juan对IPFS进行了定义——一个点对点的分布式文件系统，旨在用同一个文件系统连接所有的计算设备。</p><p>随后，Juan称，在某些方面，IPFS对标于WEB互联网，但更像是单一的BitTorrent集群，使用Git作文件存储和数据交换。换句话说，IPFS使用基于内容寻址的超链接，提供了一个高吞吐量的、基于内容寻址的块存储模型。这种模型构成了一个广义的Merkle DAG（Directed Acyclic Graph，有向无环图）数据结构，可用来构建版本控制文件系统、区块链甚至永恒的互联网体系。IPFS整合了三大技术，分布式哈希表、带有奖励机制的块交换协议和自认证的命名系统。IPFS还解决了单点故障问题，可在节点无需相互信任的情况下正常运行。</p><h1 id="h-1" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1️⃣概述</h1><p>第一段，Juan说已经有很多人尝试构建全球化的分布式文件系统，并列举了学术界和商业界的一些案例。在学术届，AFS获得了比较广泛的成功并至今仍在使用，而能获得如此成功的其他产品则寥寥无几。在商业界，分布式文件系统主要应用于点对点文件共享，尤其是音视频的共享。并列举了Napster、KaZaA和BitTorrent这些分布式文件应用</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d207e24fb260e389d27d21342c1a9eee73031a69da21cb45d4a2e9b8c9039125.png" alt="BitTorrent" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">BitTorrent</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5bcc2bd350da73524b3ab8e5074cfe26e0c7fe0741e4a2ba817de88b96ee14e3.png" alt="Naspter" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Naspter</figcaption></figure><p>这些分布式文件应用承载了上亿的用户规模，至今，BitTorrent仍保持着上千万的活跃节点。     但是Juan认为这些商业系统虽然用户量很大，但是都在应用层。这些软件没有专注于底层协议框架以便于基于基础设施进行开发，没能实现全球开发者共享这些商业系统的分布式和低延迟的优势。</p><p>HTTP协议，作为互联网传输文件的公认标准，在大多数场景下已经足够成功。和浏览器伴生，HTTP协议已经积攒了很强的技术和社会声望。但是HTTP协议实在是太老了，并没能利用好近十几年出现的优秀的新文件分发技术。这些新技术与HTTP协议难以兼容，并且许多相关方都在现有的HTTP模型上有很多投资，所以WEB网络的发展陷入了停滞。但是从另一个角度，HTTP协议之后许多优秀的新协议出现并得到了广泛使用，这是令人兴奋的。目前所需要升级的是：在不降低用户体验的情况下引入新的文件分发技术，从而加强如今的HTTP的网络。</p><p>第二段，Juan谈到HTTP的成功在于处理小文件的表现很好，但是目前新网络时代的数据分发有以下5点挑战：</p><ol><li><p>托管和分发数据量达到PB（1PB = 1024TB）级别</p></li><li><p>跨组织大数据运算</p></li><li><p>分发海量的高清晰度媒体流</p></li><li><p>海量数据的连接与版本控制</p></li><li><p>防止重要文件丢失</p></li></ol><p>Juan总结说，新时代的数据量激增，“海量数据，无处不在”，这时候Juan团队不再对HTTP协议抱有幻想了，转而努力开发新的数据分发协议。</p><pre data-type="codeBlock" text="Many of these can be boiled down to “lots of data, accessible everywhere.”
"><code>Many of these can be boiled down to “lots of data, accessible everywhere.”
</code></pre><p>第三段，Juan说分布式系统要解决的两大问题就是数据同步和版本控制，而Git则是一套非常实用的源码版本控制系统。现在完全可以讲Git技术作为启发，尤其是将它基于内容寻址的Merkle DAG数据模型应用到分布式文件系统中。当然，还需解决高吞吐量和升级WEB网络的问题。</p><p>白皮书提出了IPFS：一种点对点的、基于版本控制的文件系统可以解决前面的问题。IPFS的核心原则就是将所有数据作为同一Merkle DAG的一部分来建模。</p><pre data-type="codeBlock" text="The central IPFS principle is modeling all data as part of the same Merkle DAG.
"><code>The central IPFS principle <span class="hljs-keyword">is</span> modeling all <span class="hljs-keyword">data</span> <span class="hljs-keyword">as</span> part of the same Merkle DAG.
</code></pre><h1 id="h-2" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2️⃣背景</h1><p>这一章，Juan回顾了IPFS系统所整合的一系列技术，这些技术对构建一个优秀的点对点系统非常重要。</p><h2 id="h-21-dhtdistributed-hash-tables" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.1 分布式哈希表DHT（Distributed Hash Tables）</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b5fe0bca192a4edad79797a267fbf9a138577877bbc62e75ca7b5ca3808ae1be.png" alt="DHT" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">DHT</figcaption></figure><p>分布式哈希表，即为分散存储在不同位置的哈希表，而哈希表就是存储文件哈希值和文件地址的表结构，一般是结构。DHT在点对点系统中广泛用于存储和同步元数据。下面，Juan介绍了三种DHT应用，分别是 Kademlia，Coral DSHT 和 S/Kademlia。 KAD Kademlia是非常流行的一种DHT算法（简称KAD），在eMule，BitTorrent，Guntella这些软件中都有使用。KAD算法的优点是查询高效(log(n))，同步开销低，抗攻击性强，应用广泛。 Coral DSHT，是对KAD的改进。在KAD协议中，使用XOR距离，信息永远存储在XOR距离最近的节点中，但是这些节点可能并不需要这些数据，这样会浪费存储和带宽资源。所以Coral将地址存储在可以提供数据的节点上，放宽了get_value(key)函数，改成了get_any_values(key)，将DSHT根据位置和大小分成不同的cluster，优先查询低延时的节点。 S/KAD算法扩展了KAD来防止恶意的攻击。首先，它通过一些机制生成NodeId防止女巫攻击。第二，S/KAD算法的节点在不相交的路径上查找值，即使网络中存在一般不诚实节点也能达到85%的成功率。 2.2 块交换技术 BigTorrent是一个广泛应用的P2P文件共享系统，它的一些特性给IPFS带来了3点启发： BitTorrent的数据交换协议使用了一种针锋相对的激励策略，奖励多做贡献的节点，惩罚只会索取资源的节点。 BitTorrent追踪文件片段的有效性，优先传输稀有资源。 对于一些消耗带宽的共享策略，BitTorrent的tit-for-tat策略表现脆弱，而PropShare表现非常好。 2.3 版本控制系统-Git Git是当前最流行的文件版本控制系统，它可以对文件内容的更新进行建模和分发。Git提供了一种强大的Merkle DAG对象模型，可以将文件版本更新记录在文件树中，同时对分布式环境也非常友好。Juan列举了Git的6种特性： 模型中有3种对象：文件、目录和commit更新 基于对象内容的哈希来寻址 对象之间的链接是嵌入式的，形成有向无环图 DAG 大部分版本化的元数据基于指针引用，创建和更新资源更高效 版本变化只需要更新引用或者增加对象 分布式的版本更新只需要传输文件和更新远程引用。 2.4 自认证文件系统 这里提到了SFS实现了分布式信任链和平等共享的全局命名空间。此外，Juan还提到了一种SFS远程文件寻址机制： # Addressing remote filesystems using the following scheme:
/sfs/&lt;Location&gt;:&lt;HostID&gt;

# where Location is the server network address, and:
HostID = hash(public_key || Location)
3️⃣IPFS模型设计 Juan在第三章提到IPFS融合了DHT，BitTorrent，Git以及SFS等系统的思想。IPFS的贡献就是将这些技术简化、优化和整合，并凝聚成一套更完整优秀的系统。IPFS也具备平台能力供开发者在平台上部署各类应用。IPFS所有的节点都是平等的，没有特殊的节点。这些节点需要在本地存储IPFS对象，并且互相通信和传递数据。 接着，Juan将IPFS协议按照功能划分为7个子协议栈： Identities（身份标识），负责节点的身份生成和身份验证。 Network（网络），负责管理节点之间的通信。 Routing（路由），负责维护数据寻址的结构。 Exchange（交换），建立一种新的数据交换协议（BitSwap）来管理数据块的分发。 Objects（对象），采用Merkle DAG连接内容寻址的对象。 Files（文件），借鉴了Git的文件版本控制模型。 Naming（命名），建立了一种自认证的、可变更的命名系统。 这些子协议最终融合一体，构建整个IPFS模型，下面Juan分别介绍了这7个协议。 3.1 Identities（身份标识） IPFS使用NodeId标识节点，它是由生成的公钥进行hash后的结果。用户可以在每次启动前，实例化一个新的节点标识。但是系统不鼓励节点经常更新NodeId，因为这样会消耗网络资源。 type NodeId Multihash
type Multihash []byte
// self-describing cryptographic hash digest
type PublicKey []byte
type PrivateKey []byte
// self-describing keys
type Node struct {
    NodeId NodeID
    PubKey PublicKey
    PriKey PrivateKey
}
上面是节点身份标识的数据结构，下面是节点身份生成的生成算法。 // S/Kademlia based IPFS identity generation
difficulty = &lt;integer parameter&gt;
n = Node{}
do {
    n.PubKey, n.PrivKey = PKI.genKeyPair()
    n.NodeId = hash(n.PubKey)
    p = count_preceding_zero_bits(hash(n.NodeId))
} while (p &lt; difficulty)
假设首次连接新节点other时，要判断它的公钥哈希hash(other.PubKey)和NodeId(other.NodeId)是否相等。如果不相等则节点无效，断开连接。 3.2 Network（网络） IPFS的节点要连接上百个来自整个互联网的其他节点，Juan给出了IPFS网络的5种特性： Transport（数据传输层），IPFS会使用多种传输层写有，这里Juan推荐WebRTC DataChannels和uTP协议。 Reliability（可靠性），即便底层网络不可靠，IPFS也可以使用uTP或SCTP来提升可靠性。 Connectivity（连接性）：IPFS使用ICE NAT穿透技术。 Integrity（完整性）：IPFS可以通过hash checksum，来校验消息完整性。 Authenticity（权限可验证性）：通过用发送者的私钥对消息进行数字签名，来验证消息的权限。 在最后，Juan补充了关于节点地址结构的说明。由于IPFS支持多种协议，所以可以应用在多种网络层。这种情况下，IPFS用一种多层地址multiaddr的结构来存储地址数据。 # an SCTP/IPv4 connection
/ip4/10.20.30.40/sctp/1234/
# an SCTP/IPv4 connection proxied over TCP/IPv4
/ip4/5.6.7.8/tcp/5678/ip4/1.2.3.4/sctp/1234/
3.3 Routing(路由) IPFS需要一个路由系统来查找目标节点，这里用到了基于S/Kademlia和Coral的DSHT技术。IPFS的DHT，就是分布式哈希表，是基于value值的大小区别存储的。如果value比较小，小于1kb，那么直接存在DHT上，如果value比较大，那么DHT上只存储引用，就是能够提供对象内容节点的NodeId。下面是Juan给出的IPFS的DSHT接口设计代码： type IPFSRouting interface {
    FindPeer(node NodeId)
    // gets a particular peer’s network address
    SetValue(key []bytes, value []bytes)
    // stores a small metadata value in DHT
    GetValue(key []bytes)
    // retrieves small metadata value from DHT
    ProvideValue(key Multihash)
    // announces this node can serve a large value
    FindValuePeers(key Multihash, min int)
    // gets a number of peers serving a large value
}
3.4 Block Exchange - BitSwap Protocal（块交换） IPFS使用的BitSwap协议收到了BitTorrent的启发，通过节点间交换数据完成数据的分发。像BitTorrent一样，每个节点向其他节点下载需要的数据want_list，同时提供已有的数据have_list，用这种方式实现数据交换。与BitTorrent不同的是，BitSwap不局限于一个torrent种子文件中所记录的数据块，它更像一个永久的交易市场，节点可以获取任意想要的数据块，这些数据碎片可能来自不相关的文件。所有节点都要在这个市场交易数据。交易市场的概念往往需要一个流通的虚拟货币支撑，这就需要一个全局账本来记录交易。 BitSwap 常规情况下，BitSwap节点之间直接能各取所需是最好的。但是，很多情况下，一方拥有对方需要的数据，而另一方没有。那可以，以较低的优先级去获取其他节点需要的数据。这就激励每个节点，能缓存和传播一些冷门的数据，即便并不是该节点所需要的。 下面，Juan介绍了BitSwap信用、策略、账本和规范四部分内容。 <strong>3.4.1 BitSwap信用</strong> BitSwap鼓励节点多存储和传播数据，即便自己不需要但是其他节点可能需要。节点乐观地相信，预先付出以后会得到回报，可以后面催其他节点还债。这就可以防止只免费索取不愿分享的节点，可以建立信用系统（credit-like system）来解决这些问题： 追踪节点之间相互可用的信用额度。 节点给另一个债务节点发送数据的可能性与债务大小成反比。 换句话说，对方欠我数据越多，我想它发送数据的可能性越小，并且设计一个函数来计算这个概率。但如果一个节点不打算理睬另一个节点，可以给出一个ignore_cooldown超时，表达断交的通知。 3.4.2 BitSwap策略 IPFS每个节点可以采用不同的BitSwap策略，不同的策略对块交换的性能会产生很大影响。BitTorrent指定tit-for-tat作为标准默认策略，但也有许多第三方非标准策略，如BitTyrant、BitThief和ProShare。BitSwap节点也应支持多种协议，不论是善意的还是恶意的。Juan给BitSwap的协议提出4点目标： 最大化节点和网络的交易性能 防止吃白食的负载节点消耗和破坏交易 谨慎对待或拒绝使用未知策略的节点 宽仁地对待信任的节点 Juan提到实践中，sigmoid函数是一个被证实有效的选择。这个函数可以根据负债率(r)来调节。 负债率 发送给负债节点的概率公式如下所示： 概率 概率曲线如下所示： 曲线 这种策略对长期交易的节点宽松，对未知节点严格，这样有几点好处： 抵制攻击者创建大量新节点发起女巫攻击 对长期合作的节点，即便偶尔不稳定也能维持友好关系 不跟表现恶劣的节点合作，知道对方有所改变 3.4.3 BitSwap账本 BitSwap节点保存一个记录与其他节点交易的账本，这样可以追踪历史记录、避免篡改。每建立一次连接，节点之间就要交换账本信息。如果双方的帐对不上，就要创建一个空账本初始化数据，同步给对方。所有不同步的借贷数据就会丢失。当然恶意节点有可能会故意制造这种债务丢失，不过这种数据不匹配的情况，很快就会发现，不会让恶意节点占太多便宜。如果发生次数较多，其他节点就不会再信任它了。 type Ledger struct {
    owner NodeId
    partner NodeId
    bytes_sent int
    bytes_recv int
    timestamp Timestamp
}
节点可以自由处理自己的账本数据，一般只有新的数据是有用的，无用的数据可以进行垃圾回收。 3.4.4 BitSwap协议规范 下面是BitSwap协议的数据结构，以及节点Peer的数据结构和4个接口： // Additional state kept
type BitSwap struct {
    ledgers map[NodeId]Ledger
    // Ledgers known to this node, inc inactive
    active map[NodeId]Peer
    // currently open connections to other nodes
    need_list []Multihash
    // checksums of blocks this node needs
    have_list []Multihash
    // checksums of blocks this node has
}
type Peer struct {
    nodeid NodeId
    ledger Ledger
    // Ledger between the node and this peer
    last_seen Timestamp
    // timestamp of last received message
    want_list []Multihash
    // checksums of all blocks wanted by peer
    // includes blocks wanted by peer’s peers
    }
    // Protocol interface:
    interface Peer {
    open (nodeid :NodeId, ledger :Ledger);
    send_want_list (want_list :WantList);
    send_block (block :Block) -&gt; (complete :Bool);
    close (final :Bool);
}
BitSwap结构的字段包括：当前节点的账本ledger、当前有效连接active，需要的block的校验码need_list，拥有的block的校验码have_list。 Peer结构的字段包括：NodeId，节点与当前peer之间的账本ledger，上一条消息时间last_seen，当前peer节点需要的block数据块校验码want_list，其中包括当前peer的peer所需要的数据块。 Peer协议接口包括：open，send_want_list，send_block，close四个接口。 Peer节点连接有4种声明状态： Open：对方节点请求建立连接并且发送账本 Sending：节点间交换需求块列表和数据块 Close：节点之间关闭连接 Ignored：策略认为该节点可以忽略，返回超时 后面，Juan详细介绍了Peer接口4个函数的流程，这里不再多做赘述。 3.5 Object（对象） Merkle DAG 前面提到的分布式哈希表DHT和块交换协议BitSwap，能够使IPFS构建点对点系统，高效稳定地存储和分发数据，但是这些数据是如何存储的呢？在这一章，Juan介绍IPFS会构建有向无环图Merkle DAG将对象进行连接。 Merkle DAG对IPFS来说，有3种重要特性： 内容寻址，所有内容都是由多重哈希校验来唯一标识 无法篡改，所有内容都用checksum来验证，数据被篡改，IPFS会检测到 有效去重，所有拥有一致内容的对象被IPFS看作同一个对象并只存储一次 IPFS数据格式如下： type IPFSLink struct {
    Name string
    // name or alias of this link
    Hash Multihash
    // cryptographic hash of target
    Size int
    // total size of target
    }
    type IPFSObject struct {
    links []IPFSLink
    // array of links
    data []byte
    // opaque content data
}
IPFS Merkle DAG 可以很灵活地处理数据，他只需要满足内容索引，以及上面的对象格式。IPFS不会干涉每个应用如何管理自己的数据，或是使用什么数据格式。Juan列举了一些IPFS对象连接表地操作指令： 列出一个对象的引用对象。 &lt;object multihash&gt; &lt;object size&gt; &lt;link name&gt;
解决了路径查找问题，如foo/bar/baz这样的路径，可以很容易通过link表来遍历。 递归遍历所有对象的引用，这就是遍历对象的指令。 &gt; ipfs refs --recursive \
/XLZ1625Jjn7SubMDgEyeaynFuR84ginqvzb
XLLxhdgJcXzLbtsLRL1twCHA2NrURp4H38s
XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x
XLHBNmRQ5sJJrdMPuu48pzeyTtRo39tNDR5
XLWVQDqxo9Km9zLyquoC9gAP8CL1gWnHZ7z
...
3.5.1 Paths IPFS对象可以通过路径来查找，格式与UNIX文件系统或者WEB路径差不多。Merkle DAG link结构让这种查找非常简单：下面是路径的例子： # format
/ipfs/&lt;hash-of-object&gt;/&lt;name-path-to-object&gt;

# example
/ipfs/XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x/foo.txt
假设一个对象路径是：/bar/baz，则以下三种方式都可以访问baz： /ipfs//bar/baz /ipfs//baz /ipfs/ 3.5.2 本地化存储 IPFS客户端需要一定的本地存储空间，一般存储于磁盘上或者内存种，用来存储一部分对象和原始数据。 3.5.3 对象锁定 IPFS节点可以将对象进行锁定，这样对象就可以一直保存在本地，也可以通过递归，将对象的所有关联对象全部锁定在本地。 3.5.4 发布对象 IPFS是全球化的分布式网络，需要满足海量文件同步工作。通过DHT技术，可以更安全、公平、分布式地分发对象。每个人都有权分发对象，只需要把对象的key，添加到DHT表中，把自己添加成为peer节点，也就是value中，再将路径分享给其他用户，就完成了发布。对象是永远不可变的，如果数据更新版本了，hash也会随之更新。 3.5.5 对象级密码学 IPFS支持对象级的密码学操作，加密或者签名的对象，可以用下面的数据结构： type EncryptedObject struct {
    Object []bytes
    // raw object data encrypted
    Tag []bytes
    // optional tag for encryption groups
}
type SignedObject struct {
    Object []bytes
    // raw object data signed
    Signature []bytes
    // hmac signature
    PublicKey []multihash
    // multihash identifying key
}
对象加密以后，hash发生变化成为了一个新的对象。所谓对象级加密，就是一个父节点对象，可以用一个密钥加密，另一个子对象可以用其他对象加密或者不加密，这即是对象级别的保护。 3.6 Files（文件） 3.5中的对象是相对抽象的数据结构，这一章，Juan在Merkle DAG上定义了四种对象类型。建立了一种可版本化的文件系统： block：大小可变的数据块 list：block的集合或者list的集合 tree：block、list或者tree的集合 commit：tree的版本历史快照 相比于Git模型，IPFS做了一些改进，如增加了总字节数字段（方便快速查询 ），增加了list对象（方便去重大文件）。下图是论文中的Object Merkle DAG样例图： DAG样例 样例图中有4中不同颜色的节点，代表4种不同的对象类型，接下来论文重点介绍这4种类型： 🕛<strong>blob对象</strong> blob对象是可寻址的文件数据单元，用来存储文件数据. {
    &quot;data&quot;: &quot;some data here&quot;,
    // blobs have no links
}
这里是blob对象bbb222的数据样例: &gt; ipfs file-cat &lt;bbb222-hash&gt; --json
{
    &quot;data&quot;: &quot;blob222 data&quot;,
    &quot;links&quot;: []
}
blob对象没有link字段，它是树的叶节点。IPFS的文件既可以用blob也可以用list。 🕛<strong>list对象</strong> list对象一般代表由多个blob组成的大文件，list可以包含blob和list。 🕛<strong>tree对象</strong> tree对象和Git一样，代表目录，是一组名称和哈希的映射，下面是tree对象的数据结构： {
    &quot;data&quot;: [&quot;blob&quot;, &quot;list&quot;, &quot;blob&quot;],
    // trees have an array of object types as data
    &quot;links&quot;: [
        { &quot;hash&quot;: &quot;XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x&quot;,
        &quot;name&quot;: &quot;less&quot;, &quot;size&quot;: 189458 },
        { &quot;hash&quot;: &quot;XLHBNmRQ5sJJrdMPuu48pzeyTtRo39tNDR5&quot;,
        &quot;name&quot;: &quot;script&quot;, &quot;size&quot;: 19441 },
        { &quot;hash&quot;: &quot;XLWVQDqxo9Km9zLyquoC9gAP8CL1gWnHZ7z&quot;,
        &quot;name&quot;: &quot;template&quot;, &quot;size&quot;: 5286 }
        // trees do have names
    ]
}
这里的哈希指向连接的blob，list和其它的tree或者commit，下面是ttt111对象的数据结构： &gt; ipfs file-cat &lt;ttt111-hash&gt; --json
{
    &quot;data&quot;: [&quot;tree&quot;, &quot;tree&quot;, &quot;blob&quot;],
    &quot;links&quot;: [
        { &quot;hash&quot;: &quot;&lt;ttt222-hash&gt;&quot;,
        &quot;name&quot;: &quot;ttt222-name&quot;, &quot;size&quot;: 1234 },
        { &quot;hash&quot;: &quot;&lt;ttt333-hash&gt;&quot;,
        &quot;name&quot;: &quot;ttt333-name&quot;, &quot;size&quot;: 3456 },
        { &quot;hash&quot;: &quot;&lt;bbb222-hash&gt;&quot;,
        &quot;name&quot;: &quot;bbb222-name&quot;, &quot;size&quot;: 22 }
    ]
}
ttt111对象link了两个tree和一个blob对象。 🕛<strong>commit对象</strong> commit对象表示对象的历史版本快照，它可以指向任何类型的对象。下面是commit对象ccc111的数据结构： {
    &quot;data&quot;: {
        &quot;type&quot;: &quot;tree&quot;,
        &quot;date&quot;: &quot;2014-09-20 12:44:06Z&quot;,
        &quot;message&quot;: &quot;This is a commit message.&quot;
    },
    &quot;links&quot;: [
        { &quot;hash&quot;: &quot;&lt;ccc000-hash&gt;&quot;,
        &quot;name&quot;: &quot;parent&quot;, &quot;size&quot;: 25309 },
        { &quot;hash&quot;: &quot;&lt;ttt111-hash&gt;&quot;,
        &quot;name&quot;: &quot;object&quot;, &quot;size&quot;: 5198 },
        { &quot;hash&quot;: &quot;&lt;aaa111-hash&gt;&quot;,
        &quot;name&quot;: &quot;author&quot;, &quot;size&quot;: 109 }
    ]
}
这一章最后，Juan讲了IPFS文件的一些特性。IPFS和Git的对象模型略有差别，但基本兼容。很多Git上的文件管理工具稍加修改也可以用在IPFS上。IPFS文件系统路径的格式和UNIX也是兼容的，tree代表unix目录，commit既可以表示目录也可以隐藏。对于一些大文件的管理和分发，主要的挑战是如何将它们分割成一些独立的数据块，这里Juan刚给了三种大致思路。紧接着Juan谈到了路径查询的新性能问题。通过一个路径去查询数据，要在DHT中找到key，再连接映射的peer节点拉取数据。如果路径比较深，效率可能比较低。Juan提出了两个方案：tree cacheing（树缓存）、flattened trees（扁平树）。 3.7 IPNS: Naming and Mutable State（命名系统） 目前，IPFS系统已经构建了点对点的块交换协议以及基于内容寻址的对象DAG。但目前的系统，发布的对象都是不可变的，这样会有什么问题呢？基于内容寻址很大的问题是，内容一变，之前的地址就失效了。文件都是通过hash来寻址的，但是有一些文件是经常变化的，每变化一次它的连接地址也会发生变化。而且哈希地址这种字符串，对人类识别很不友好，所以希望建立一种当文件变化时，路径可以保持不变的机制，这就是IPNS。 IPFS借鉴了自认命名系统SFS的命名机制： 计算node id 给每个用户分配一个可变的空间 用户可以在路径空间下添加对象，用其私钥签名 其他用户找到这个对象后，通过该用户公钥验证是否是其本人发布 IPNS虽然能够帮助重新命名，但长串的hash对用户很不友好，Juan列举了4种技术提升体验： 节点链接，Peer Links，通过这种方法可以将其他用户的对象连接到我的对象，当然也要通过权限验证，这就建立了一个可信网络。 # Alice links to bob Bob
ipfs link /&lt;alice-pk-hash&gt;/friends/bob /&lt;bob-pk-hash&gt;
# Eve links to Alice
ipfs link /&lt;eve-pk-hash/friends/alice /&lt;alice-pk-hash&gt;
# Eve also has access to Bob
/&lt;eve-pk-hash/friends/alice/friends/bob
# access Verisign certified domains
/&lt;verisign-pk-hash&gt;/foo.com
DNS TXT记录，对于一个有效的域名，IPFS要在DNS TXT记录查找，找到对应映射的IPNS路径 # this DNS TXT record
ipfs.benet.ai. TXT &quot;ipfs=XLF2ipQ4jD3U ...&quot;

# behaves as symlink
ln -s /ipns/XLF2ipQ4jD3U /ipns/fs.benet.ai
转换成可读的标识符，就是把一些无规则的加密字符串，变成一组可识别的单词 # this proquint phrase
/ipns/dahih-dolij-sozuk-vosah-luvar-fuluh

# will resolve to corresponding
/ipns/KhAwNprxYVxKqpDZ
短域名服务 # User can get a link from
/ipns/shorten.er/foobar

# To her own namespace
/ipns/XLF2ipQ4jD3UdeX5xp1KBgeHRhemUtaA8Vm
3.8 Using IPFS（应用） 最后的一小节，Juan列举了12种期望的应用场景，还有3个需要实现的目标 ： 建立IPFS库，可以导入到开发者的应用程序 通过命令行工具直接操作对象 实现可挂载的文件系统 4️⃣展望 Juan认为，IPFS沿着前人的成功足迹，结合了许多优秀的分布式系统的思想精华。除了BitSwap是一个新的协议，其他的都是各种现有技术的整合。IPFS有一个伟大的构想，构建分布式的互联网基础设施，并且支持各种应用程序。</p>]]></content:encoded>
            <author>samehada@newsletter.paragraph.com (samehada)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/2e073284fa530aa83bc0d3c65443a161d7a6aaf3d154ff97c100224a3cde4024.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[比特币白皮书阅读]]></title>
            <link>https://paragraph.com/@samehada/IZYyfLoNvj6ocl078uZs</link>
            <guid>IZYyfLoNvj6ocl078uZs</guid>
            <pubDate>Tue, 07 Mar 2023 08:58:43 GMT</pubDate>
            <description><![CDATA[作者：Samehada 时间：2023/01/07 文章: 《Bitcoin: A Peer-to-Peer Electronic Cash System》 版权声明：文章采用 BY-NC-SA 许可协议，转载请注明出处！1.前言2008年11月1日，比特币白皮书—《比特币:一种点对点的电子现金系统》横空出世。该文章向世人介绍了全新的货币系统-比特币。15年过去了，如今白皮书有了些被神话的味道，它成了许多人心中圣经般的存在，仿佛天不生比特币，加密货币领域万古如长夜。如此认知对于我们了解比特币是不利的，比特币白皮书更像是一种科学文献或是产品的说明文档，仅此而已，只是未曾料到它的出现影响了世界的走向。bitcoin_title在中本聪之前，对于点对点通信的信任问题，人们在密码学、时间戳、哈希现金等技术层面不断推陈出新，彼时科研人员也发表了一些带有相应技术特点的电子货币。2008年，中本聪站到了前人的肩膀上，提取了前人的智慧，做出了关键性的突破，打造出了自己心目中理想的电子现金系统。其发布时间恰逢美国政府滥发钞票造成了全国通胀。比特币生不逢时，却也来得刚刚好。2.摘要在摘要中，中本聪开...]]></description>
            <content:encoded><![CDATA[<blockquote><p>作者：Samehada</p><p>时间：2023/01/07</p><p>文章: 《Bitcoin: A Peer-to-Peer Electronic Cash System》</p><p>版权声明：文章采用 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">BY-NC-SA</a> 许可协议，转载请注明出处！</p></blockquote><h2 id="h-1" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1.前言</h2><p>2008年11月1日，比特币白皮书—《比特币:一种点对点的电子现金系统》横空出世。该文章向世人介绍了全新的货币系统-<strong>比特币</strong>。15年过去了，如今白皮书有了些被神话的味道，它成了许多人心中圣经般的存在，仿佛天不生比特币，加密货币领域万古如长夜。如此认知对于我们了解比特币是不利的，比特币白皮书更像是一种科学文献或是产品的说明文档，仅此而已，只是未曾料到它的出现影响了世界的走向。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d2d9f0e191e4a72b3bba6a6c680d96ff785b1b2fb4ed703186437b9f034e7601.png" alt="bitcoin_title" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_title</figcaption></figure><p>在中本聪之前，对于点对点通信的信任问题，人们在密码学、时间戳、哈希现金等技术层面不断推陈出新，彼时科研人员也发表了一些带有相应技术特点的电子货币。2008年，<strong>中本聪</strong>站到了前人的肩膀上，提取了前人的智慧，做出了关键性的突破，打造出了自己心目中理想的电子现金系统。其发布时间恰逢美国政府滥发钞票造成了全国通胀。比特币生不逢时，却也来得刚刚好。</p><h2 id="h-2" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.摘要</h2><p>在摘要中，中本聪开门见山，道出设计比特币系统的<strong>目的</strong>：</p><pre data-type="codeBlock" text="A purely peer-to-peer version of electronic cash would allow online payments to be sent directly from one party to another without going through a financial institution.
"><code><span class="hljs-selector-tag">A</span> purely peer-<span class="hljs-selector-tag">to</span>-peer version of electronic cash would allow online payments <span class="hljs-selector-tag">to</span> be sent directly <span class="hljs-selector-tag">from</span> one party <span class="hljs-selector-tag">to</span> another without going through <span class="hljs-selector-tag">a</span> financial institution.
</code></pre><p>比特币的目标是通过构建<strong>点对点</strong>的技术，实现一种电子现金系统，达到<strong>无需金融中介</strong>，也<strong>无需双方信任</strong>的情况下实现在线电子化支付。</p><p>紧接着中本聪提出了比特币系统要解决的核心问题—<strong>双重支付</strong>，此处由于篇幅问题不予展开：</p><pre data-type="codeBlock" text="Digital signatures provide part of the solution, but the main benefits are lost if a trusted third party is still required to prevent double-spending.
"><code>Digital signatures provide part of the solution, but the <span class="hljs-selector-tag">main</span> benefits are lost if <span class="hljs-selector-tag">a</span> trusted third party is still required <span class="hljs-selector-tag">to</span> prevent double-spending.
</code></pre><p>这里文章说数字签名技术为双花交易提供了部分解决方案，而比特币系统是也采用<strong>数字签名</strong>这一方法，但是如此还是需要第三方机构介入，那系统的设计就有些无趣了。事实上，日常的数字资产常依赖于第三方信任机构进行，如银行、支付宝、微信，这些机构通过中心化管理，并通过实时修改账户余额的方法来防止双重支付的出现。</p><p>现实世界中，人们需要为交易中的<strong>第三方验证</strong>支付巨额的费用，全球总共超过75亿人，每天的交易量更高达万亿级别，中心化机构管理的费用在交易中被提取出来。这也是文章中说的 <strong>the main benefits are lost</strong>。</p><pre data-type="codeBlock" text="We propose a solution to the double-spending problem using a peer-to-peer network.
"><code>We propose <span class="hljs-selector-tag">a</span> solution <span class="hljs-selector-tag">to</span> the double-spending problem using <span class="hljs-selector-tag">a</span> peer-<span class="hljs-selector-tag">to</span>-peer network.
</code></pre><p>所以比特币系统要做的就是<strong>去中心化</strong>和<strong>解决双重支付</strong>，在不需要第三方信任机构的情况下，点对点转移数字资产，中本聪提出了基于点对点的网络解决方案。</p><pre data-type="codeBlock" text="The network timestamps transactions by hashing them into an ongoing chain of hash-based proof-of-work, forming a record that cannot be changed without redoing the proof-of-work.
"><code>The network timestamps transactions by hashing them into an ongoing chain of hash<span class="hljs-operator">-</span>based proof<span class="hljs-operator">-</span>of<span class="hljs-operator">-</span>work, forming a record that cannot be changed without redoing the proof<span class="hljs-operator">-</span>of<span class="hljs-operator">-</span>work.
</code></pre><p>中本聪设计的点对点网络为交易打上了<strong>时间戳</strong>，通过取这些交易的哈希值，将它们编入一条持续增长的<strong>工作量证明</strong>链，这样的一条链除非重做已经做的大量工作否则将无法改变，这里的主要工作量是计算一个符合条件的哈希值。</p><pre data-type="codeBlock" text="The longest chain not only serves as proof of the sequence of events witnessed, but proof that it came from the largest pool of CPU power.
"><code>The longest chain <span class="hljs-keyword">not</span> only serves <span class="hljs-keyword">as</span> proof of the sequence of events witnessed, but proof that it came <span class="hljs-keyword">from</span> the largest pool of CPU power.
</code></pre><p>由于设计的工作量证明机制，比特币系统最长的链不仅仅是交易执行的事件序列，而且也代表着这条链占有者<strong>最大的CPU计算资源</strong>。</p><pre data-type="codeBlock" text="As long as a majority of CPU power is controllerd by nodes that are not cooperating to attack the network, they&apos;ll generate the longest chain and outpace attackers.
"><code><span class="hljs-keyword">As</span> <span class="hljs-type">long</span> <span class="hljs-keyword">as</span> a majority <span class="hljs-keyword">of</span> CPU power <span class="hljs-built_in">is</span> controllerd <span class="hljs-keyword">by</span> nodes that are <span class="hljs-built_in">not</span> cooperating <span class="hljs-keyword">to</span> attack the network, they<span class="hljs-comment">'ll generate the longest chain and outpace attackers.</span>
</code></pre><p>因为系统最长链是最大CPU计算资源工作形成的，所以只要大部分的CPU算力不被恶意节点所控制，那么这些诚实的节点就能一直生成最长的合法链并且延长链的速度会超过恶意节点所工作的另一条链，从而使得攻击失效，这里即是后面大名鼎鼎<strong>51%攻击</strong>的雏形，但是引起笔者反思“多数人即正义”在有些突发事件下真的合理吗？</p><pre data-type="codeBlock" text="The network itself requires minimal structure. Messages are broadcast on a best effort basis, and nodes can leave and rejoin the network at will, accepting the longest proof-of-work chain as proof of what happened while they were gone.
"><code>The network itself requires minimal structure. Messages are broadcast on a best effort basis, and nodes can leave and rejoin the network at will, accepting the longest proof<span class="hljs-operator">-</span>of<span class="hljs-operator">-</span>work chain <span class="hljs-keyword">as</span> proof of what happened <span class="hljs-keyword">while</span> they were gone.
</code></pre><p>比特币网络结构很简单，因为没有中心化服务器，所以它的结构非常小巧。每个节点只管尽可能广泛的打包并向最长合法链传播交易信息即可。<strong>每个节点也可以随时离开系统</strong>，因为有其他节点同时也在打包这些交易并试图传播到链上，而且可以随时回到系统，只需要继续<strong>接受最长链继续工作</strong>即可。</p><p><strong>总结：中本聪通过数字签名，随机散列，哈希算法，工作量证明，动态链式存储等技术，设计了一系列的机制，使比特币能够不依附于第三方信任机构，解决了电子支付中的双花、记录篡改等问题，并证明该系统安全有效、稳定可行。</strong></p><h2 id="h-3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3.概述</h2><p>文章第一章概述，中本聪在文章中描述了传统<strong>线上交易模式的弊病</strong>，并正式提出本文的比特币系统。</p><pre data-type="codeBlock" text="Commerce on the Internet has come to rely almost exclusively on financial institutions serving as trusted third parties to process electronic payments. While the system works well enough for most transactions, it still suffers from the inherent weaknesses of the trust based model.
"><code>Commerce <span class="hljs-keyword">on</span> the Internet has come <span class="hljs-keyword">to</span> rely almost exclusively <span class="hljs-keyword">on</span> financial institutions serving <span class="hljs-keyword">as</span> trusted third parties <span class="hljs-keyword">to</span> process electronic payments. <span class="hljs-keyword">While</span> the system works well enough <span class="hljs-keyword">for</span> most transactions, it still suffers <span class="hljs-keyword">from</span> the inherent weaknesses <span class="hljs-keyword">of</span> the trust based model.
</code></pre><p>中本聪指出当前的线上交易往往非常依赖于金融机构充当可信任的第三方。但这种交易模式有着先天性的缺点，那就是下面要说的<strong>交易成本高</strong>和<strong>交易可逆性</strong>。</p><pre data-type="codeBlock" text="Completely non-reversible transactions are not really possible, since financial institutions cannot avoid mediating disputes.
"><code>Completely non<span class="hljs-operator">-</span>reversible transactions <span class="hljs-keyword">are</span> <span class="hljs-keyword">not</span> really possible, since financial institutions cannot avoid mediating disputes.
</code></pre><p>文章这里谈到，由于金融机构不可避免地需要调节买卖双方的争端，所以现有的模式下实现完全不可逆的交易是不可能的。这里如何理解呢，交易双方往往由于<strong>没有充足的信任</strong>，交易完成后如果一方不满意，此时金融机构就要承担<strong>仲裁</strong>的角色，来平息买卖双方的争议，买家退货商家退款，这样的线上交易时可逆的。但是有些服务业和餐饮业的交易是需要一种不可逆性的，享用了服务再退款难免会出现扯皮的现象。</p><pre data-type="codeBlock" text="With the possibility of reversal, the need for trust spreads. Merchants must be wary of their customers, hassling them for more information than they would otherwise need. A certain percentage of fraud is accepted as unavoidable.
"><code>With the possibility of reversal, the need <span class="hljs-keyword">for</span> trust spreads. Merchants must be wary of their customers, hassling them <span class="hljs-keyword">for</span> more information than they would otherwise need. A certain percentage of fraud <span class="hljs-keyword">is</span> accepted <span class="hljs-keyword">as</span> unavoidable.
</code></pre><p>由于买卖双方不够信任，恶意退货这种行为使得交易存在被撤销的可能性。因此，商家有着很强的不安感，所以他们向客户<strong>索要大量的信息</strong>来建立脆弱的信任，降低交易撤回的可能性。这里说明了传统线上交易的第一个弊病——<strong>交易可逆性</strong>。</p><pre data-type="codeBlock" text="These costs and payment uncertainties can be avoided in person by using physical currency, but no mechanism exists to make payments over a communications channel without a trusted party.
"><code>These costs <span class="hljs-keyword">and</span> payment uncertainties can be avoided <span class="hljs-keyword">in</span> person <span class="hljs-keyword">by</span> <span class="hljs-keyword">using</span> physical currency, but <span class="hljs-keyword">no</span> mechanism <span class="hljs-keyword">exists</span> <span class="hljs-keyword">to</span> make payments <span class="hljs-keyword">over</span> a communications channel <span class="hljs-keyword">without</span> a trusted party.
</code></pre><p>文章紧接着说，这些协调争端的成本和支付的不确定性提高了整体交易的成本，而且目前不存在不引入第三方信任机构就能进行支付的机制，除非当面一手交钱一手交货，这就是传统交易的第二个弊病——<strong>交易成本高</strong>。</p><pre data-type="codeBlock" text="What is needed is an electronic payment system based on cryptographic proof instead of trust, allowing any two willing parties to transact directly with each other without the need for a trusted third party.
"><code>What <span class="hljs-built_in">is</span> needed <span class="hljs-built_in">is</span> an electronic payment system based <span class="hljs-keyword">on</span> cryptographic proof instead <span class="hljs-keyword">of</span> trust, allowing any two willing parties <span class="hljs-keyword">to</span> transact directly <span class="hljs-keyword">with</span> <span class="hljs-keyword">each</span> other without the need <span class="hljs-keyword">for</span> a trusted third party.
</code></pre><p>传统线上交易的两个弊病根源都在于<strong>互不信任</strong>，因此需要的一个电子支付系统，基于<strong>密码学</strong>来建立一种<strong>共识</strong>，这种具有公信力的共识可以允许双方互不信任并直接交易，而不需要第三方。</p><pre data-type="codeBlock" text="Transactions that are computationally impractical to reverse would protect sellers from fraud, and routine escrow mechanisms could easily be implemented to protect buyers.
"><code>Transactions that are computationally impractical <span class="hljs-selector-tag">to</span> reverse would protect sellers <span class="hljs-selector-tag">from</span> fraud, and routine escrow mechanisms could easily be implemented <span class="hljs-selector-tag">to</span> protect buyers.
</code></pre><p>从计算上的不可逆转性可以保护卖家的利益，而常规的托管机制也很容易实现来保护买家的利益。</p><pre data-type="codeBlock" text="In this paper, we propose a solution to the double-spending problem using a peer-to-peer distributed timestamp server to generate computational proof of the chronological order of transactions.
"><code>In this paper, we propose <span class="hljs-selector-tag">a</span> solution <span class="hljs-selector-tag">to</span> the double-spending problem using <span class="hljs-selector-tag">a</span> peer-<span class="hljs-selector-tag">to</span>-peer distributed timestamp server <span class="hljs-selector-tag">to</span> generate computational proof of the chronological <span class="hljs-attribute">order</span> of transactions.
</code></pre><p>下面文章又将矛头转向了多次提及的双重支付问题，中本聪提出的是一种<strong>点对点</strong>的<strong>分布式</strong>时间戳服务器，生成按交易顺序先后顺序排列的可计算的证明。</p><pre data-type="codeBlock" text="The system is secure as long as honest nodes collectively control more CPU power than any cooperating group of attacker nodes.
"><code>The system <span class="hljs-keyword">is</span> secure <span class="hljs-keyword">as</span> <span class="hljs-built_in">long</span> <span class="hljs-keyword">as</span> honest nodes collectively control more CPU power than any cooperating <span class="hljs-keyword">group</span> of attacker nodes.
</code></pre><p>只要节点和算力足够分散，诚实节点算力大于恶意节点算力，系统总是安全的。</p><p><strong>总结</strong>：<strong>目前的电子交易机制存在两点弊端：</strong></p><ol><li><p><strong>基于第三方机构的交易提高了交易成本。</strong></p></li><li><p><strong>由于不存在不可逆的交易，交易对信用要求高，买方会收集过多的信息。</strong></p></li></ol><p><strong>所以，文章提出的电子交易系统，建立在密码学共识而非新人之上，这让交易在不需要第三方机构的前提下可以完成。</strong></p><h2 id="h-4" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">4.交易</h2><p>文章第二章，中本聪提出了比特币系统中非常重要的概念，<strong>交易</strong>和<strong>记账</strong>。对任何一个电子货币系统来说，如何完成<strong>收付款以及存储交易记录</strong>，是头等大事。</p><pre data-type="codeBlock" text="We define an electronic coin as a chain of digital signatures. Each owner transfers the coin to the next by digitally signing a hash of the previous transaction and the public key of the next owner and adding these to the end of the coin. A payee can verify the signatures to verify the chain of ownership.
"><code>We define an electronic coin <span class="hljs-keyword">as</span> a chain <span class="hljs-keyword">of</span> digital signatures. <span class="hljs-keyword">Each</span> owner transfers the coin <span class="hljs-keyword">to</span> the <span class="hljs-keyword">next</span> <span class="hljs-keyword">by</span> digitally signing a hash <span class="hljs-keyword">of</span> the previous transaction <span class="hljs-built_in">and</span> the <span class="hljs-keyword">public</span> <span class="hljs-keyword">key</span> <span class="hljs-keyword">of</span> the <span class="hljs-keyword">next</span> owner <span class="hljs-built_in">and</span> adding these <span class="hljs-keyword">to</span> the <span class="hljs-keyword">end</span> <span class="hljs-keyword">of</span> the coin. A payee can verify the signatures <span class="hljs-keyword">to</span> verify the chain <span class="hljs-keyword">of</span> ownership.
</code></pre><p>中本聪说，我们定义电子货币是一条数字签名链，每个拥有者通过将<strong>上一次交易</strong>和<strong>下一个拥有者的公钥</strong>的哈希值的数字签名添加到货币末尾的方式，将货币转移给下一个拥有者。这样收款人就可以通过验证数字签名来证实该链的拥有者。</p><p>在上一笔交易的Hash值上进行数字签名，表明拥有者确实收到过这些货币，这些收到的货币是可溯源的。在下一位拥有者的公钥Hash上进行数字签名，表示是当前所有者转给下一位拥有者的。</p><p>下面将注意力放到中间的这笔交易上，这是Owner 1向Owner 2转账的一笔交易。从上向下看，首先这笔交易包含了上一笔交易的Hash，同时包含了Owner 2公钥的Hash，实际上就是Owner 2 的收款地址，最后在使用Owner 1的私钥对这笔转账进行签名。而Owner 2可以使用Owner 1的公钥来验证Owner 1的签名是否是其本人。</p><pre data-type="codeBlock" text="The problem of course is the payee can&apos;t verify that one of the owners did not double-spend the coin. A common solution is to introduce a trusted central authority, or mint, that checks every transaction for double spending. After each transaction, the coin must be returned to the mint to issue a new coin, and only coins issued directly from the mint are trusted not to be double-spent. The problem with this solution is that the fate of the entire money system depends on the company running the mint, with every transaction having to go through them, just like a bank.
"><code>The problem <span class="hljs-keyword">of</span> course <span class="hljs-built_in">is</span> the payee can<span class="hljs-comment">'t verify that one of the owners did not double-spend the coin. A common solution is to introduce a trusted central authority, or mint, that checks every transaction for double spending. After each transaction, the coin must be returned to the mint to issue a new coin, and only coins issued directly from the mint are trusted not to be double-spent. The problem with this solution is that the fate of the entire money system depends on the company running the mint, with every transaction having to go through them, just like a bank.</span>
</code></pre><p>通过数字签名技术，收款方已经能够验证付款方<strong>身份的真实性</strong>，但是它还<strong>不能验证付款方是否进行了双重支付</strong>，也就是付款方是否是否给其他人支付了这些电子货币。通常情况会引入可靠的第三方机构，比如央行或铸币厂，由他们负责发行、结算和审核交易。</p><pre data-type="codeBlock" text="We need a way for the payee to know that the previous owners did not sign any earlier transactions. For our purposes, the earliest transaction is the one that counts, so we don&apos;t care about later attempts to double-spend. The only way to confirm the absence of a transaction is to be aware of all transactions.
"><code>We need <span class="hljs-selector-tag">a</span> way for the payee <span class="hljs-selector-tag">to</span> know that the previous owners did not sign any earlier transactions. For our purposes, the earliest transaction is the one that counts, so we don't care about later attempts <span class="hljs-selector-tag">to</span> double-spend. The only way <span class="hljs-selector-tag">to</span> confirm the absence of <span class="hljs-selector-tag">a</span> transaction is <span class="hljs-selector-tag">to</span> be aware of <span class="hljs-attribute">all</span> transactions.
</code></pre><p>这样可以解决双花问题，但是比特币的使命就是不再依赖第三方金融中介，所以另一种方案就是不如<strong>让收款人洞悉到付款人在此次交易之前的其他交易</strong>。这样可以判断在这笔交易之前，付款人有没有会造成双重支付的其他可疑交易。</p><pre data-type="codeBlock" text="To accomplish this without a trusted party, transactions must be publicly announced , and we need a system for participants to agree on a single history of the order in which they were received.
"><code><span class="hljs-selector-tag">To</span> accomplish this without <span class="hljs-selector-tag">a</span> trusted party, transactions must be publicly announced , and we need <span class="hljs-selector-tag">a</span> system for participants <span class="hljs-selector-tag">to</span> agree on <span class="hljs-selector-tag">a</span> single history of the <span class="hljs-attribute">order</span> in which they were received.
</code></pre><p>如果系统不依赖任何第三方来审核账本，那么就需要公开所有的交易数据。所有的节点都来监督和维护一个<strong>共同的账本</strong>，并达成一种对交易收到顺序的单一历史性共识，最终只有一个<strong>有效的、带时间顺序的记账数据</strong>并公之于众，这就是比特币的<strong>分布式记账系统</strong>。</p><p><strong>总结：数字签名技术可以验证付款人身份，但无法验证双花问题。对于双重支付，去中心化的解决方案时靠公开数据并全网广播，所有人维护同一个有效账本。</strong></p><h2 id="h-5" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">5.时间戳服务器</h2><p>第三章，时间戳服务器，这一章只有短短一段话和一幅图。</p><pre data-type="codeBlock" text="The solution we propose begins with a timestamp server. A timestamp server works by taking a hash of a block of items to be timestamped and widely publishing the hash, such as in a newspaper or Usenet post. The timestamp proves that the data must have existed at the time, obviously, in order to get into the hash.
"><code>The solution we propose begins with <span class="hljs-selector-tag">a</span> timestamp server. <span class="hljs-selector-tag">A</span> timestamp server works by taking <span class="hljs-selector-tag">a</span> hash of <span class="hljs-selector-tag">a</span> block of items <span class="hljs-selector-tag">to</span> be timestamped and widely publishing the hash, such as in <span class="hljs-selector-tag">a</span> newspaper or Usenet post. The timestamp proves that the data must have existed at the <span class="hljs-selector-tag">time</span>, obviously, in <span class="hljs-attribute">order</span> <span class="hljs-selector-tag">to</span> get into the hash.
</code></pre><p>文章说时间戳服务器对包含交易数据的区块的哈希值打上时间戳，将<strong>时间信息纳入区块信息</strong>中，然后将这些哈希值广播到网络中。</p><pre data-type="codeBlock" text="Each timestamp includes the previous timestamp in its hash, forming a chain, with each additional timestamp reinforcing the ones before it.
"><code><span class="hljs-keyword">Each</span> <span class="hljs-type">timestamp</span> includes the previous <span class="hljs-type">timestamp</span> <span class="hljs-keyword">in</span> its hash, forming a chain, <span class="hljs-keyword">with</span> <span class="hljs-keyword">each</span> additional <span class="hljs-type">timestamp</span> reinforcing the ones before it.
</code></pre><p>同时文章提到，而每一个打上时间戳的区块Hash中包含着前一个打赏时间戳的区块的信息，即每打上一个时间戳就会加强前一个时间戳的真实存在性。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e3d575fab10bea33702af8273566182c5b06974f844804cb516544821c280e68.png" alt="bitcoin_tx" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_tx</figcaption></figure><p><strong>总结：时间见证一切，所有的区块像编年史一样，按照时序紧密连接，并且同步到全部的节点。因此，历史区块信息几乎无法篡改。因为。如果要修改区块数据，就需要修改其后所有已生成区块的哈希数据，还要让大多数节点形成共识 。</strong></p><h2 id="h-6" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">6.工作量证明</h2><p>第四章，来到了Proof-of-Work工作量证明。前面提到账本是由全体分布式节点共同维护的，接下来就面临<strong>谁拥有记账权</strong>的问题。</p><pre data-type="codeBlock" text="To implement a distributed timestamp server on a peer-to-peer basis, we will need to use a proof-of-work system similar to Adam Back&apos;s Hashcash , rather than newspaper or Usenet posts.
"><code><span class="hljs-selector-tag">To</span> implement <span class="hljs-selector-tag">a</span> distributed timestamp server on <span class="hljs-selector-tag">a</span> peer-<span class="hljs-selector-tag">to</span>-peer basis, we will need <span class="hljs-selector-tag">to</span> use <span class="hljs-selector-tag">a</span> proof-of-work system similar <span class="hljs-selector-tag">to</span> Adam Back's Hashcash , rather than newspaper or Usenet posts.
</code></pre><p>POW的理念早在1997年Adam Back发明的哈希现金(hashcash)中就提到过，原型本来是用于解决互联网的垃圾信息问题。</p><pre data-type="codeBlock" text="The proof-of-work involves scanning for a value that when hashed, such as with SHA-256, the hash begins with a number of zero bits. The average work required is exponential in the number of zero bits required and can be verified by executing a single hash.
"><code>The proof-<span class="hljs-keyword">of</span>-work involves scanning <span class="hljs-keyword">for</span> a value that <span class="hljs-keyword">when</span> hashed, such <span class="hljs-keyword">as</span> <span class="hljs-keyword">with</span> SHA-<span class="hljs-number">256</span>, the hash begins <span class="hljs-keyword">with</span> a number <span class="hljs-keyword">of</span> zero bits. The average work required <span class="hljs-built_in">is</span> exponential <span class="hljs-keyword">in</span> the number <span class="hljs-keyword">of</span> zero bits required <span class="hljs-built_in">and</span> can be verified <span class="hljs-keyword">by</span> executing a <span class="hljs-type">single</span> hash.
</code></pre><p>在进行哈希运算时，工作量证明机制将扫描寻找一个符合条件的值。例如通过SHA-256，遍历这些SHA-256的哈希值将从一些0开始。寻找这个值所需的工作量随着0的增长将呈指数级增长，而<strong>验证这个值是否是正确的仅需一次哈希运算</strong>。</p><pre data-type="codeBlock" text="For our timestamp network, we implement the proof-of-work by incrementing a nonce in the block until a value is found that gives the block&apos;s hash the required zero bits.
"><code><span class="hljs-keyword">For</span> our timestamp network, we implement the proof-<span class="hljs-keyword">of</span>-work <span class="hljs-keyword">by</span> incrementing a nonce <span class="hljs-keyword">in</span> the block <span class="hljs-keyword">until</span> a value <span class="hljs-built_in">is</span> found that gives the block<span class="hljs-comment">'s hash the required zero bits.</span>
</code></pre><p>为了保证每次工作量证明都是重新计算的，中本聪在区块中添加了一个Nonce随机数。这<strong>个随机数要使得区块的哈希值出现所需的那么多个0</strong>。通过反复运算来找到这个随机数，找这个随机数的过程就是在做工作，同时将难度动态调整，大约每10分钟出一个块。</p><pre data-type="codeBlock" text="Once the CPU effort has been expended to make it satisfy the proof-of-work, the block cannot be changed without redoing the work. As later blocks are chained after it, the work to change the block would include redoing all the blocks after it.
"><code>Once the CPU effort has been expended to make it satisfy the proof<span class="hljs-operator">-</span>of<span class="hljs-operator">-</span>work, the <span class="hljs-built_in">block</span> cannot be changed without redoing the work. As later blocks are chained after it, the work to change the <span class="hljs-built_in">block</span> would include redoing all the blocks after it.
</code></pre><p>一旦节点使用CPU工作满足了POW机制获得了记账权，区块发布后信息就不能再更改，除非重做一遍这些工作。而一旦有新的区块内链接到了一个区块后面，要<strong>修改这个区块的信息，就需要将这个区块已经做的所有工作全部重做一遍</strong>。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/33d6fc572c2a2a22a178a4b9f938608919b63736803fb1f7399820f3d1ad76f9.png" alt="bitcoin_nonce" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_nonce</figcaption></figure><p>文章给出的插图中可以看出，一个区块包含着上一个区块的哈希值。一旦修改某个区块的内容，其哈希值必然改变，则下一个区块就要重新寻找Nonce值，以此类推，一直追赶后面在不断延伸的链条。所以，<strong>如果没有足够强的算力，几乎不可能修改区块上的信息</strong>。</p><pre data-type="codeBlock" text="The proof-of-work also solves the problem of determining representation in majority decision making. 
"><code>The proof<span class="hljs-operator">-</span>of<span class="hljs-operator">-</span>work also solves the problem of determining representation in majority decision making. 
</code></pre><p>中本聪还提到了，POW机制解决了在多数决定中决定代表的问题。</p><pre data-type="codeBlock" text="If the majority were based on one-IP-address-one-vote, it could be subverted by anyone able to allocate many IPs. Proof-of-work is essentially one-CPU-one-vote. The majority decision is represented by the longest chain, which has the greatest proof-of-work effort invested in it.
"><code>If the majority were based on one<span class="hljs-operator">-</span>IP<span class="hljs-operator">-</span><span class="hljs-keyword">address</span><span class="hljs-operator">-</span>one<span class="hljs-operator">-</span>vote, it could be subverted by anyone able to allocate many IPs. Proof-of<span class="hljs-operator">-</span>work <span class="hljs-keyword">is</span> essentially one<span class="hljs-operator">-</span>CPU<span class="hljs-operator">-</span>one<span class="hljs-operator">-</span>vote. The majority decision <span class="hljs-keyword">is</span> represented by the longest chain, which has the greatest proof<span class="hljs-operator">-</span>of<span class="hljs-operator">-</span>work effort invested in it.
</code></pre><p>如果是基于一个IP一票的方式，那些恶意控制众多IP的人将会代表大多数，而<strong>POW机制本质是按CPU算力投票，最长链代表了最多数的投票结果</strong>，因为这条链上有最多的算力投票。</p><pre data-type="codeBlock" text="If a majority of CPU power is controlled by honest nodes, the honest chain will grow the fastest and outpace any competing chains.
"><code>If a majority of CPU power <span class="hljs-keyword">is</span> controlled <span class="hljs-keyword">by</span> honest nodes, the honest chain will grow the fastest <span class="hljs-keyword">and</span> outpace any competing chains.
</code></pre><p>如果多数的CPU算力被<strong>诚实的节点</strong>所控制，那么诚实的链增长得会最快，并超过其他的竞争链。</p><pre data-type="codeBlock" text="To modify a past block, an attacker would have to redo the proof-of-work of the block and all blocks after it and then catch up with and surpass the work of the honest nodes.
"><code>To modify a past <span class="hljs-built_in">block</span>, an attacker would have to redo the proof<span class="hljs-operator">-</span>of<span class="hljs-operator">-</span>work of the <span class="hljs-built_in">block</span> and all blocks after it and then <span class="hljs-keyword">catch</span> up with and surpass the work of the honest nodes.
</code></pre><p>前面已经提到，如果要修改某一个区块内容，那么其后已产生的区块信息都要修改，需要重做这些工作。攻击者需要重新一遍这个区块的工作量以及这个区块后所有区块的工作量，才能弯道超车赶上诚实节点的工作量，这个成本极高。</p><pre data-type="codeBlock" text="We will show later that the probability of a slower attacker catching up diminishes exponentially as subsequent blocks are added.
"><code>We will <span class="hljs-keyword">show</span> later that the probability <span class="hljs-keyword">of</span> a slower attacker catching up diminishes exponentially <span class="hljs-keyword">as</span> subsequent blocks <span class="hljs-keyword">are</span> added.
</code></pre><p>这种攻击者赶上诚实节点的概率会随着后续区块的增长而大大减小，成本也会大大增加。</p><pre data-type="codeBlock" text="To compensate for increasing hardware speed and varying interest in running nodes over time, the proof-of-work difficulty is determined by a moving average targeting an average number of blocks per hour. If they&apos;re generated too fast, the difficulty increases.
"><code><span class="hljs-keyword">To</span> compensate <span class="hljs-keyword">for</span> increasing hardware speed <span class="hljs-keyword">and</span> <span class="hljs-type">varying</span> interest <span class="hljs-keyword">in</span> <span class="hljs-keyword">running</span> nodes <span class="hljs-keyword">over</span> <span class="hljs-type">time</span>, the proof<span class="hljs-operator">-</span><span class="hljs-keyword">of</span><span class="hljs-operator">-</span>work difficulty <span class="hljs-keyword">is</span> determined <span class="hljs-keyword">by</span> a moving average targeting an average number <span class="hljs-keyword">of</span> blocks <span class="hljs-keyword">per</span> hour. If they<span class="hljs-string">'re generated too fast, the difficulty increases.
</span></code></pre><p>为了抵消硬件速度提升和平衡节点利益，工作量证明将使用<strong>移动平均数法</strong>来确定每小时生成区块的平均个数。如果出块过快，系统将会让出块的难度增加，最终保持在10分钟的水平。</p><p><strong>总结：中本聪在前人基础上提出了工作量证明机制，并介绍了每个节点工作量的体现方式。同时提出工作量证明机制本质上是基于算力的投票，拥有最大工作量的最长链代表着大多数节点认可的链。</strong></p><h2 id="h-7" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">7.网络</h2><p>第五章，中本聪设计了有效的机制，来部署整个<strong>分布式网络</strong>。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f7eb4088209902187dedfc89a587991ef839624d3214189f6714286058b5c1e5.png" alt="bitcoin_network" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_network</figcaption></figure><p>运行整个网络分为六个步骤：</p><ol><li><p>每一笔新交易都要向所有节点广播。</p></li><li><p>每个节点将新交易打包收集到一个区块中。</p></li><li><p>每个节点要为这个区块进行一定难度的工作量证明。</p></li><li><p>当一个节点找到了工作量证明，向其他节点广播这个区块。</p></li><li><p>节点需要验证区块内所有交易有效且没有双重支付的区块下接受这个区块。</p></li><li><p>节点使用这个区块的哈希值作为previous hash，记录在新区块中，表示节点已经认可这个区块。</p></li></ol><pre data-type="codeBlock" text="Nodes always consider the longest chain to be the correct one and will keep working on extending it.
"><code>Nodes always consider the longest chain <span class="hljs-keyword">to</span> be the correct one <span class="hljs-built_in">and</span> will keep working <span class="hljs-keyword">on</span> extending it.
</code></pre><p>节点总是认为<strong>最长的链</strong>是正确的，并共同维护和延长它。</p><pre data-type="codeBlock" text="If two nodes broadcast different versions of the next block simultaneously, some nodes may receive one or the other first.
"><code>If two nodes broadcast different versions <span class="hljs-keyword">of</span> the next block simultaneously, <span class="hljs-keyword">some</span> nodes may receive <span class="hljs-keyword">one</span> <span class="hljs-keyword">or</span> the other first.
</code></pre><p>有这样一种情况，<strong>两个节点同时 广播了不同的区块</strong>，有些节点可能先收到其中一个，其他节点先收到另一个。</p><pre data-type="codeBlock" text="In that case, they work on the first one they received, but save the other branch in case it becomes longer. The tie will be broken when the next proof-of-work is found and one branch becomes longer; the nodes that were working on the other branch will then switch to the longer one.
"><code><span class="hljs-keyword">In</span> that <span class="hljs-keyword">case</span>, they work <span class="hljs-keyword">on</span> the first one they received, but save the other branch <span class="hljs-keyword">in</span> <span class="hljs-keyword">case</span> it becomes longer. The tie will be broken <span class="hljs-keyword">when</span> the <span class="hljs-keyword">next</span> proof-<span class="hljs-keyword">of</span>-work <span class="hljs-built_in">is</span> found <span class="hljs-built_in">and</span> one branch becomes longer; the nodes that were working <span class="hljs-keyword">on</span> the other branch will <span class="hljs-keyword">then</span> switch <span class="hljs-keyword">to</span> the longer one.
</code></pre><p>这种情况下，节点基于他们先收到的区块继续工作，但也保存另一个分支，以防其变成更长的链。当下一个工作量证明被找到后，僵局就会被打破，从而其中一个分支变得更长，在另一个分支上工作的节点，将要转到更长链上工作。</p><pre data-type="codeBlock" text="New transaction broadcasts do not necessarily need to reach all nodes. As long as they reach many nodes, they will get into a block before long.
"><code><span class="hljs-built_in">New</span> transaction broadcasts <span class="hljs-keyword">do</span> <span class="hljs-built_in">not</span> necessarily need <span class="hljs-keyword">to</span> reach all nodes. <span class="hljs-keyword">As</span> <span class="hljs-type">long</span> <span class="hljs-keyword">as</span> they reach many nodes, they will <span class="hljs-keyword">get</span> <span class="hljs-keyword">into</span> a block before <span class="hljs-type">long</span>.
</code></pre><p>新交易的广播不必到达所有的节点，只要它们到达足够多的节点，它们很快就会被打包进入一个新区块。</p><pre data-type="codeBlock" text="Block broadcasts are also tolerant of dropped messages. If a node does not receive a block, it will request it when it receives the next block and realizes it missed one.
"><code>Block broadcasts are also tolerant of dropped messages. If a node does not <span class="hljs-function"><span class="hljs-keyword">receive</span> <span class="hljs-title">a</span> <span class="hljs-title"><span class="hljs-built_in">block</span></span>, <span class="hljs-title">it</span> <span class="hljs-title">will</span> <span class="hljs-title">request</span> <span class="hljs-title">it</span> <span class="hljs-title">when</span> <span class="hljs-title">it</span> <span class="hljs-title">receives</span> <span class="hljs-title">the</span> <span class="hljs-title">next</span> <span class="hljs-title"><span class="hljs-built_in">block</span></span> <span class="hljs-title">and</span> <span class="hljs-title">realizes</span> <span class="hljs-title">it</span> <span class="hljs-title">missed</span> <span class="hljs-title">one</span>.
</span></code></pre><p>区块广播是容忍消息丢失的。如果一个节点没有接收到一个区块，他会请求下载这个缺失的区块信息。</p><p><strong>总结：中本聪给出了比特币网络是如何运行的，并对网络中区块和交易的广播进行了解释</strong>。</p><h2 id="h-8" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">8.激励</h2><p>想要完成复杂的工作量证明，显然需要大量诚实的网络节点支持，并将一个个新区块数据写入网络中。这样的过程需要很大的成本，显然各个节点不会情愿一直为爱发电。</p><p>第六章，中本聪介绍了比特币网络的<strong>激励政策</strong>。</p><pre data-type="codeBlock" text="By convention, the first transaction in a block is a special transaction that starts a new coin owned by the creator of the block.
"><code><span class="hljs-keyword">By</span> convention, the first transaction <span class="hljs-keyword">in</span> a block <span class="hljs-built_in">is</span> a special transaction that starts a <span class="hljs-built_in">new</span> coin owned <span class="hljs-keyword">by</span> the creator <span class="hljs-keyword">of</span> the block.
</code></pre><p>比特币系统做了约定，每一个区块里的第一笔交易是<strong>特殊交易</strong>，是系统专门奖励给区块创造者的奖励。</p><pre data-type="codeBlock" text="This adds an incentive for nodes to support the network, and provides a way to initially distribute coins into circulation, since there is no central authority to issue them.
"><code>This adds an incentive <span class="hljs-keyword">for</span> nodes <span class="hljs-keyword">to</span> support the network, <span class="hljs-built_in">and</span> provides a way <span class="hljs-keyword">to</span> initially distribute coins <span class="hljs-keyword">into</span> circulation, since there <span class="hljs-built_in">is</span> no central authority <span class="hljs-keyword">to</span> issue them.
</code></pre><p>这样不仅增加了对网络节点的激励，也提供了一种分发新货币到流通领域的方法。</p><pre data-type="codeBlock" text="The incentive can also be funded with transaction fees. If the output value of a transaction is less than its input value, the difference is a transaction fee that is added to the incentive value of the block containing the transaction.
"><code>The incentive can also be funded with transaction fees. If the output value of a transaction <span class="hljs-keyword">is</span> less than its input value, the difference <span class="hljs-keyword">is</span> a transaction fee that <span class="hljs-keyword">is</span> added to the incentive value of the <span class="hljs-built_in">block</span> containing the transaction.
</code></pre><p>还有一部分激励是交易的手续费。如果一笔交易输出值小于输入值，那么<strong>差额就作为交易费</strong>，被打包到包含此交易的区块激励中。</p><pre data-type="codeBlock" text="Once a predetermined number of coins have entered circulation, the incentive can transition entirely to transaction fees and be completely inflation free.
"><code>Once <span class="hljs-selector-tag">a</span> predetermined number of coins have entered circulation, the incentive can <span class="hljs-attribute">transition</span> entirely <span class="hljs-selector-tag">to</span> transaction fees and be completely inflation free.
</code></pre><p>一旦<strong>预先量</strong>的货币都进入了流通域，激励将只剩交易费，如此防止通货膨胀。</p><pre data-type="codeBlock" text="The incentive may help encourage nodes to stay honest. If a greedy attacker is able to assemble more CPU power than all the honest nodes, he would have to choose between using it to defraud people by stealing back his payments, or using it to generate new coins. 
"><code>The incentive may help encourage nodes <span class="hljs-keyword">to</span> stay honest. <span class="hljs-keyword">If</span> a greedy attacker <span class="hljs-built_in">is</span> able <span class="hljs-keyword">to</span> assemble more CPU power than all the honest nodes, he would have <span class="hljs-keyword">to</span> choose between <span class="hljs-keyword">using</span> it <span class="hljs-keyword">to</span> defraud people <span class="hljs-keyword">by</span> stealing back his payments, <span class="hljs-built_in">or</span> <span class="hljs-keyword">using</span> it <span class="hljs-keyword">to</span> generate <span class="hljs-built_in">new</span> coins. 
</code></pre><p>同时，激励有助于<strong>鼓励节点保持诚实</strong>。比如一个贪婪的攻击者，如果他有能力聚集比所有诚实节点更多的CPU算力，而他的目的仅仅是为了完成一次双花攻击或欺骗交易，比起诚实地做矿工拿激励，这是不值得的。</p><pre data-type="codeBlock" text="He ought to find it more profitable to play by the rules, such rules that favour him with more new coins than everyone else combined, than to undermine the system and the validity of his own wealth.
"><code>He ought <span class="hljs-keyword">to</span> find it more profitable <span class="hljs-keyword">to</span> play <span class="hljs-keyword">by</span> the rules, such rules that favour him <span class="hljs-keyword">with</span> more <span class="hljs-built_in">new</span> coins than everyone <span class="hljs-keyword">else</span> combined, than <span class="hljs-keyword">to</span> undermine the system <span class="hljs-built_in">and</span> the validity <span class="hljs-keyword">of</span> his own wealth.
</code></pre><p>使用这些CPU算力做矿工获取稳定收益，这样做比他攻击链条、修改区块信息、盗取货币的利益更大。</p><p><strong>总结：比特币的激励 = 挖矿奖励 + 交易手续费。</strong></p><h2 id="h-9" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">9.回收磁盘空间</h2><p>第七章，回收磁盘空间，中本聪讲了区块链交易数据的存储结构。</p><pre data-type="codeBlock" text="Once the latest transaction in a coin is buried under enough blocks, the spent transactions before it can be discarded to save disk space.
"><code>Once the latest transaction in <span class="hljs-selector-tag">a</span> coin is buried under enough blocks, the spent transactions before it can be discarded <span class="hljs-selector-tag">to</span> save disk space.
</code></pre><p>如果某个币的最新交易信息被纳入了足够多的区块，那么可以丢弃掉这笔交易<strong>之前的交易</strong>，一词节省磁盘空间。</p><pre data-type="codeBlock" text="To facilitate this without breaking the block&apos;s hash, transactions are hashed in a Merkle Tree, with only the root included in the block&apos;s hash.
"><code><span class="hljs-keyword">To</span> facilitate this without breaking the block<span class="hljs-comment">'s hash, transactions are hashed in a Merkle Tree, with only the root included in the block's hash.</span>
</code></pre><p>区块中交易的存储结构是默克尔树，它有一个根节点、一组中间节点和一组叶节点组成。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2e33ad4770c6f8df734aab105449665ab6a84d3d522e108f329a0c2be5180818.png" alt="bitcoin_merkle_tree" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_merkle_tree</figcaption></figure><p>叶节点包含存储数据或是它的哈希值，中间节点是包含两个子节点内容的哈希值，根节点也是由下面两个子节点的哈希值构成。</p><pre data-type="codeBlock" text="Old blocks can then be compacted by stubbing off branches of the tree. The interior hashes do not need to be stored.
"><code>Old blocks can <span class="hljs-keyword">then</span> be compacted <span class="hljs-keyword">by</span> stubbing <span class="hljs-keyword">off</span> branches <span class="hljs-keyword">of</span> the tree. The interior hashes <span class="hljs-keyword">do</span> <span class="hljs-built_in">not</span> need <span class="hljs-keyword">to</span> be stored.
</code></pre><p>当一笔交易被足够多区块覆盖，即形成共识的时候。那么在区块中，它过去拥有者的交易数据就可以丢弃，<strong>只保留最新的数据</strong>以及之前数据根节点的哈希值，同时哈希值不会被破坏，依然可以来验证数据有效性。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4e078ceca46c9936d27544274391d88305eda1b5bc006045035fc360dc0d68a6.png" alt="bitcoin_merkle_remove" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_merkle_remove</figcaption></figure><p>如此验证，也就是<strong>零知识证明</strong>。也就是说，验证一个时间正确与否，并不需要验证者重现整个事件。默克尔树里的哈希值，即不占空间，又能提升索引效率，还能用于零知识证明。</p><pre data-type="codeBlock" text="A block header with no transactions would be about 80 bytes.
"><code><span class="hljs-selector-tag">A</span> block <span class="hljs-selector-tag">header</span> with no transactions would be about <span class="hljs-number">80</span> bytes.
</code></pre><p>中本聪还通过距离做了一笔估算，他说每个不包含交易的区块头大概是80个字节。</p><pre data-type="codeBlock" text="If we suppose blocks are generated every 10 minutes, 80 bytes * 6 * 24 * 365 = 4.2MB per year. With computer systems typically selling with 2GB of RAM as of 2008, and Moore&apos;s Law predicting current growth of 1.2GB per year, storage should not be a problem even if the block headers must be kept in memory.
"><code>If we suppose blocks are generated every <span class="hljs-number">10</span> <span class="hljs-literal">minutes</span>, <span class="hljs-number">80</span> <span class="hljs-keyword">bytes</span> <span class="hljs-operator">*</span> <span class="hljs-number">6</span> <span class="hljs-operator">*</span> <span class="hljs-number">24</span> <span class="hljs-operator">*</span> <span class="hljs-number">365</span> <span class="hljs-operator">=</span> <span class="hljs-number">4</span>.2MB per year. With computer systems typically selling with 2GB of RAM <span class="hljs-keyword">as</span> of <span class="hljs-number">2008</span>, and Moore<span class="hljs-string">'s Law predicting current growth of 1.2GB per year, storage should not be a problem even if the block headers must be kept in memory.
</span></code></pre><p>假设10分钟挖出一个区块，那么每年会生成4.2MB的数据。在2008年的计算机有2G的内存，根据摩尔定律预测，每年的内存会增长1.2GB。所以，就算区块头全部保存到内存中，存储也不是问题。</p><p><strong>总结：使用默克尔树这种数据结构可以压缩数据，节约交易数据的存储成本。默克尔树里的哈希值，即不占空间，又能提升索引效率，还能用于零知识证明。</strong></p><h2 id="h-10" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">10.简化的支付验证</h2><p>第八章，Simplified Payment Verification，简称SPV。</p><pre data-type="codeBlock" text="It is possible to verify payments without running a full network node.
"><code>It <span class="hljs-keyword">is</span> possible <span class="hljs-keyword">to</span> verify payments <span class="hljs-keyword">without</span> <span class="hljs-keyword">running</span> a <span class="hljs-keyword">full</span> network node.
</code></pre><p>SPV指的是<strong>不运行一个完整的网络节点</strong>，也可以进行支付验证的方式。我们经常看到的比特币钱包SPV Wallet或者轻钱包，往往运行的是SPV节点。</p><p>一个用户运行全节点需要几百G的磁盘空间，成本太高。因此，中本聪介绍了简化支付验证的可能性。</p><pre data-type="codeBlock" text="A user only needs to keep a copy of the block headers of the longest proof-of-work chain, which he can get by querying network nodes until he&apos;s convinced he has the longest chain, and obtain the Merkle branch linking the transaction to the block it&apos;s timestamped in.
"><code>A user only needs <span class="hljs-keyword">to</span> keep a copy <span class="hljs-keyword">of</span> the block headers <span class="hljs-keyword">of</span> the longest proof-<span class="hljs-keyword">of</span>-work chain, which he can <span class="hljs-keyword">get</span> <span class="hljs-keyword">by</span> querying network nodes <span class="hljs-keyword">until</span> he<span class="hljs-comment">'s convinced he has the longest chain, and obtain the Merkle branch linking the transaction to the block it's timestamped in.</span>
</code></pre><p>实际上用户只需保存<strong>最长共识链的区块头</strong>数据就足够了，他可以通过向其他网络节点查询来确保获取到了最长链的数据，然后将需要验证的交易链接到对应的默克尔分支。</p><pre data-type="codeBlock" text="He can&apos;t check the transaction for himself, but by linking it to a place in the chain, he can see that a network node has accepted it, and blocks added after it further confirm the network has accepted it.
"><code>He can<span class="hljs-comment">'t check the transaction for himself, but by linking it to a place in the chain, he can see that a network node has accepted it, and blocks added after it further confirm the network has accepted it.</span>
</code></pre><p>SPV节点原本不能自行验证交易，但是通过将交易连接到链上的某个位置，它可以看到网络节点认可了这笔交易，而在此之后增加的区块进一步确认了该交易被整个网络接受。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/aeb5ebf39d5d9732120d2dba1884dd6e4681e7c05c6d13876614756537baef9e.png" alt="bitcoin_spv" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_spv</figcaption></figure><p>实际上，这种简化认证的钱包客户端抗攻击能力较差。</p><pre data-type="codeBlock" text="As such, the verification is reliable as long as honest nodes control the network, but is more vulnerable if the network is overpowered by an attacker. While network nodes can verify transactions for themselves, the simplified method can be fooled by an attacker&apos;s fabricated transactions for as long as the attacker can continue to overpower the network.
"><code><span class="hljs-keyword">As</span> such, the verification <span class="hljs-built_in">is</span> reliable <span class="hljs-keyword">as</span> <span class="hljs-type">long</span> <span class="hljs-keyword">as</span> honest nodes control the network, but <span class="hljs-built_in">is</span> more vulnerable <span class="hljs-keyword">if</span> the network <span class="hljs-built_in">is</span> overpowered <span class="hljs-keyword">by</span> an attacker. <span class="hljs-keyword">While</span> network nodes can verify transactions <span class="hljs-keyword">for</span> themselves, the simplified method can be fooled <span class="hljs-keyword">by</span> an attacker<span class="hljs-comment">'s fabricated transactions for as long as the attacker can continue to overpower the network.</span>
</code></pre><p>如果面临攻击，这种简化验证就会有较高的风险。这种简化的节点，也需要设计一定的机制保证安全性。</p><pre data-type="codeBlock" text="One strategy to protect against this would be to accept alerts from network nodes when they detect an invalid block, prompting the user&apos;s software to download the full block and alerted transactions to confirm the inconsistency.
"><code>One strategy <span class="hljs-keyword">to</span> protect against this would be <span class="hljs-keyword">to</span> accept alerts <span class="hljs-keyword">from</span> network nodes <span class="hljs-keyword">when</span> they detect an invalid block, prompting the user<span class="hljs-comment">'s software to download the full block and alerted transactions to confirm the inconsistency.</span>
</code></pre><p>一种对策是接受其他网络节点发现一个无效区块时发出的警告，提醒用户软件下载整个区块和被警告的交易来检查一致性。</p><pre data-type="codeBlock" text="Businesses that receive frequent payments will probably still want to run their own nodes for more independent security and quicker verification.
"><code>Businesses that receive frequent payments will probably still want to run their own nodes <span class="hljs-keyword">for</span> more independent security <span class="hljs-keyword">and</span> quicker verification.
</code></pre><p>对于高频首付款的公司或者企业级服务商，中本聪还是建议维护一个自己的全节点。</p><p><strong>总结：SPV节点可以方便地验证交易，但容易被攻击者控制。</strong></p><h2 id="h-11" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">11.合并和分割交易额</h2><p>第九章，合并和分割交易额。</p><pre data-type="codeBlock" text="Although it would be possible to handle coins individually, it would be unwieldy to make a separate transaction for every cent in a transfer. To allow value to be split and combined, transactions contain multiple inputs and outputs.
"><code>Although it would be possible <span class="hljs-selector-tag">to</span> handle coins individually, it would be unwieldy <span class="hljs-selector-tag">to</span> make <span class="hljs-selector-tag">a</span> separate transaction for every cent in <span class="hljs-selector-tag">a</span> transfer. <span class="hljs-selector-tag">To</span> allow value <span class="hljs-selector-tag">to</span> be split and combined, transactions <span class="hljs-attribute">contain</span> multiple inputs and outputs.
</code></pre><p>实际上每笔比特币交易可以有<strong>多个输入方和多个输出方</strong>，即多个付款方和多个收款方。同时，收款方还可以包括付款方自己，这就是找零的过程。</p><pre data-type="codeBlock" text="Normally there will be either a single input from a larger previous transaction or multiple inputs combining smaller amounts, and at most two outputs: one for the payment, and one returning the change, if any, back to the sender.
"><code>Normally there will be either <span class="hljs-selector-tag">a</span> single <span class="hljs-selector-tag">input</span> <span class="hljs-selector-tag">from</span> <span class="hljs-selector-tag">a</span> larger previous transaction or multiple inputs combining smaller amounts, and at most two outputs: one for the payment, and one returning the change, if any, back to the sender.
</code></pre><p>同时，收款方还可以包括付款方自己，这就是找零的过程。</p><h2 id="h-12" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">12.隐私</h2><p>第十章，论文提到了隐私问题。</p><pre data-type="codeBlock" text="The traditional banking model achieves a level of privacy by limiting access to information to the parties involved and the trusted third party.
"><code>The traditional banking model achieves <span class="hljs-selector-tag">a</span> level of privacy by limiting access <span class="hljs-selector-tag">to</span> information <span class="hljs-selector-tag">to</span> the parties involved and the trusted third party.
</code></pre><p>传统的银行模式，是通过限制参与各方和第三方机构对信息的访问来达到一定的隐私保护。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/eb40f926f82d06eaaaf839110a79220985d488577b945d03b7c28e888172c4d2.png" alt="bitcoin_traditional_privacy" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_traditional_privacy</figcaption></figure><p>也就是说，用户交易信息只有用户本人和银行可以获取和提供。</p><pre data-type="codeBlock" text="The necessity to announce all transactions publicly precludes this method, but privacy can still be maintained by breaking the flow of information in another place: by keeping public keys anonymous.
"><code>The necessity to announce all transactions publicly precludes <span class="hljs-keyword">this</span> method, but privacy can still be maintained <span class="hljs-keyword">by</span> breaking the flow of information <span class="hljs-keyword">in</span> another place: <span class="hljs-keyword">by</span> keeping <span class="hljs-keyword">public</span> keys anonymous.
</code></pre><p>而如果需要广播全部交易就排除了这种方式，但比特币系统仍可以通过保持<strong>公钥的匿名性</strong>这种阻断信息流的方式保护用户隐私。用户无需注册和实名审核，在匿名的状态下就可以获得比特币账户，这样就打破了<strong>交易信息和真实身份之间的关联</strong>。</p><pre data-type="codeBlock" text="The public can see that someone is sending an amount to someone else, but without information linking the transaction to anyone. This is similar to the level of information released by stock exchanges, where the time and size of individual trades, the &quot;tape&quot;, is made public, but without telling who the parties were.
"><code>The <span class="hljs-keyword">public</span> can see that someone <span class="hljs-built_in">is</span> sending an amount <span class="hljs-keyword">to</span> someone <span class="hljs-keyword">else</span>, but without information linking the transaction <span class="hljs-keyword">to</span> anyone. This <span class="hljs-built_in">is</span> similar <span class="hljs-keyword">to</span> the level <span class="hljs-keyword">of</span> information released <span class="hljs-keyword">by</span> stock exchanges, <span class="hljs-keyword">where</span> the time <span class="hljs-built_in">and</span> size <span class="hljs-keyword">of</span> individual trades, the <span class="hljs-string">"tape"</span>, <span class="hljs-built_in">is</span> made <span class="hljs-keyword">public</span>, but without telling who the parties were.
</code></pre><p>公众能看到有人正在发送一定量货币给别人，但是他不能将交易关联到某个人。就像股票交易，每笔交易的时间和交易量，也就是行情是公开的，但是看不到交易双方是谁。</p><h2 id="h-13" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">13.计算</h2><p>第十一章计算，中本聪通过公式和代码论证：在实践中，比特币网络被攻击成功的概率非常低。</p><pre data-type="codeBlock" text="We consider the scenario of an attacker trying to generate an alternate chain faster than the honest chain.
"><code>We consider the scenario of an attacker trying <span class="hljs-selector-tag">to</span> generate an alternate chain faster than the honest chain.
</code></pre><p>假设攻击者试图生成一条比诚实链更快的替代链，如果目的达到会怎样呢？</p><pre data-type="codeBlock" text="Even if this is accomplished, it does not throw the system open to arbitrary changes, such as creating value out of thin air or taking money that never belonged to the attacker.
"><code>Even <span class="hljs-keyword">if</span> <span class="hljs-keyword">this</span> <span class="hljs-keyword">is</span> accomplished, it does not <span class="hljs-keyword">throw</span> the system <span class="hljs-keyword">open</span> to arbitrary changes, such <span class="hljs-keyword">as</span> creating value <span class="hljs-keyword">out</span> of thin air or taking money that never belonged to the attacker.
</code></pre><p>即使这种情况可以实现，攻击者也不能凭空创造比特币或者拿走其他人的钱。因为，这需要破解别人的私钥，这是一个难度极高的数学问题。如果没有私钥，这就是一笔<strong>无效交易</strong>。</p><pre data-type="codeBlock" text="Nodes are not going to accept an invalid transaction as payment, and honest nodes will never accept a block containing them.
"><code>Nodes are not going <span class="hljs-selector-tag">to</span> accept an invalid transaction as payment, and honest nodes will never accept <span class="hljs-selector-tag">a</span> block containing them.
</code></pre><p>诚实的节点不会接受无效的交易作为支付，而且永远不会接受包含无效交易的区块。</p><pre data-type="codeBlock" text="An attacker can only try to change one of his own transactions to take back money he recently spent.
"><code>An attacker can only <span class="hljs-keyword">try</span> <span class="hljs-keyword">to</span> change one <span class="hljs-keyword">of</span> his own transactions <span class="hljs-keyword">to</span> <span class="hljs-keyword">take</span> back money he recently spent.
</code></pre><p>所以，攻击者只能试图改变他自己的某笔交易来拿回不久前已经支出的钱。</p><pre data-type="codeBlock" text="The race between the honest chain and an attacker chain can be characterized as a Binomial Random Walk.
"><code>The race between the honest chain <span class="hljs-keyword">and</span> an attacker chain can be characterized <span class="hljs-keyword">as</span> a Binomial Random Walk.
</code></pre><p>中本聪说，诚实链于攻击者链之间的竞争，可以描述为<strong>二项随机游走模型</strong>。</p><pre data-type="codeBlock" text="The success event is the honest chain being extended by one block, increasing its lead by +1, and the failure event is the attacker&apos;s chain being extended by one block, reducing the gap by -1.
"><code>The success <span class="hljs-keyword">event</span> <span class="hljs-built_in">is</span> the honest chain being extended <span class="hljs-keyword">by</span> one block, increasing its lead <span class="hljs-keyword">by</span> +<span class="hljs-number">1</span>, <span class="hljs-built_in">and</span> the failure <span class="hljs-keyword">event</span> <span class="hljs-built_in">is</span> the attacker<span class="hljs-comment">'s chain being extended by one block, reducing the gap by -1.</span>
</code></pre><p>这里约定成功事件是诚实节点的链延长一个区块，也就是两条链的差距加1.失败事件是攻击者的链延长一个区块，两条链的差距减1。</p><pre data-type="codeBlock" text="The probability of an attacker catching up from a given deficit is analogous to a Gambler&apos;s Ruin problem.Suppose a gambler with unlimited credit starts at a deficit and plays potentially an infinite number of trials to try to reach breakeven.
"><code>The probability of an attacker catching up <span class="hljs-selector-tag">from</span> <span class="hljs-selector-tag">a</span> given deficit is analogous <span class="hljs-selector-tag">to</span> <span class="hljs-selector-tag">a</span> Gambler's Ruin problem<span class="hljs-selector-class">.Suppose</span> <span class="hljs-selector-tag">a</span> gambler with unlimited credit starts at <span class="hljs-selector-tag">a</span> deficit and plays potentially an infinite number of trials <span class="hljs-selector-tag">to</span> try <span class="hljs-selector-tag">to</span> reach breakeven.
</code></pre><p>那么攻击者从某一落后位置赶上诚实链的概率类似于<strong>赌徒破产理论</strong>。假设一个赌徒拥有无限的信任额度，从一定的亏损开始，玩潜在无限次的测试来试图弥补亏空。</p><pre data-type="codeBlock" text="We can calculate the probability he ever reaches breakeven, or that an attacker ever catches up with the honest chain, as follows :
"><code>We can calculate the probability he ever reaches breakeven, <span class="hljs-keyword">or</span> that an attacker ever catches up <span class="hljs-keyword">with</span> the honest chain, <span class="hljs-keyword">as</span> follows :
</code></pre><p>我们要计算的是赌徒达到盈亏平衡的概率，也就是攻击者赶上诚实链的概率。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c7db08aedac3301da7dab537a86a29680702ada75e7488d81d449619590a6d80.png" alt="bitcoin_probability" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_probability</figcaption></figure><p>首先做出一些假设：</p><ul><li><p>p = 诚实节点找到下一个区块概率</p></li><li><p>q = 攻击者找到下一个区块的概率</p></li><li><p>$q_z$ = 攻击者从落后 z 个区块赶上诚实链的概率</p></li></ul><p>当 p &lt; q，$q_z$ = 1，因为攻击者总是能够更快找到下一个区块</p><p>当 p &gt; q，$q_z = (q/p)^Z$，因为有z个独立事件。</p><pre data-type="codeBlock" text="Given our assumption that p &gt; q, the probability drops exponentially as the number of blocks the attacker has to catch up with increases.
"><code><span class="hljs-title class_">Given</span> our assumption that p > q, the probability drops exponentially <span class="hljs-keyword">as</span> the <span class="hljs-built_in">number</span> <span class="hljs-keyword">of</span> blocks the attacker has to <span class="hljs-keyword">catch</span> up <span class="hljs-keyword">with</span> increases.
</code></pre><p>假设 p &gt; q，概率$q_z$将会随着攻击者需要赶上的区块呈指数级下降。</p><pre data-type="codeBlock" text="With the odds against him, if he doesn&apos;t make a lucky lunge forward early on, his chances become vanishingly small as he falls further behind.
"><code><span class="hljs-keyword">With</span> the odds against him, <span class="hljs-keyword">if</span> he doesn<span class="hljs-comment">'t make a lucky lunge forward early on, his chances become vanishingly small as he falls further behind.</span>
</code></pre><p>因为这种形式对他非常不利，如果攻击者没有在早期快速地赶上，那么他落后越远，赶上的机会就越渺茫。</p><pre data-type="codeBlock" text="We now consider how long the recipient of a new transaction needs to wait before being sufficiently certain the sender can&apos;t change the transaction.
"><code>We now consider how <span class="hljs-type">long</span> the recipient <span class="hljs-keyword">of</span> a <span class="hljs-built_in">new</span> transaction needs <span class="hljs-keyword">to</span> wait before being sufficiently certain the sender can<span class="hljs-comment">'t change the transaction.</span>
</code></pre><p>接下来，中本聪考虑一个新交易的收款人要等多久，才能确信付款人不能再修改交易了。</p><pre data-type="codeBlock" text="We assume the sender is an attacker who wants to make the recipient believe he paid him for a while, then switch it to pay back to himself after some time has passed. The receiver will be alerted when that happens, but the sender hopes it will be too late.
"><code>We assume the sender <span class="hljs-built_in">is</span> an attacker who wants <span class="hljs-keyword">to</span> make the recipient believe he paid him <span class="hljs-keyword">for</span> a <span class="hljs-keyword">while</span>, <span class="hljs-keyword">then</span> switch it <span class="hljs-keyword">to</span> pay back <span class="hljs-keyword">to</span> himself after some time has passed. The receiver will be alerted <span class="hljs-keyword">when</span> that happens, but the sender hopes it will be too late.
</code></pre><p>假设付款人是攻击者，他想让收款人相信他已经付完款好一阵了，然后他又向自己打回这笔钱。当这个事件发生后，收款人将收到竞购，但是付款人希望付款人收到警告时为时已晚。</p><pre data-type="codeBlock" text="The receiver generates a new key pair and gives the public key to the sender shortly before signing.
"><code>The receiver generates a <span class="hljs-built_in">new</span> <span class="hljs-keyword">key</span> pair <span class="hljs-built_in">and</span> gives the <span class="hljs-keyword">public</span> <span class="hljs-keyword">key</span> <span class="hljs-keyword">to</span> the sender shortly before signing.
</code></pre><p>其实，收款人可以临时生成一对新密钥，并将公钥给付款人。这样付款人就不会提前指导收款人的常用收款地址，也就无法提前对交易进行签名。</p><pre data-type="codeBlock" text="This prevents the sender from preparing a chain of blocks ahead of time by working on it continuously until he is lucky enough to get far enough ahead, then executing the transaction at that moment.
"><code>This prevents the sender <span class="hljs-keyword">from</span> preparing a chain <span class="hljs-keyword">of</span> blocks ahead <span class="hljs-keyword">of</span> time <span class="hljs-keyword">by</span> working <span class="hljs-keyword">on</span> it continuously <span class="hljs-keyword">until</span> he <span class="hljs-built_in">is</span> lucky enough <span class="hljs-keyword">to</span> <span class="hljs-keyword">get</span> far enough ahead, <span class="hljs-keyword">then</span> executing the transaction at that moment.
</code></pre><p>这样能防止付款人预先准备好一条区块链，然后执行交易。</p><pre data-type="codeBlock" text="Once the transaction is sent, the dishonest sender starts working in secret on a parallel chain containing an alternate version of his transaction.
"><code>Once the transaction <span class="hljs-keyword">is</span> sent, the dishonest sender starts working <span class="hljs-keyword">in</span> secret <span class="hljs-keyword">on</span> a parallel chain containing an alternate version of his transaction.
</code></pre><p>但不管怎么样，一旦交易发出，不诚实的付款人就开始秘密地在一条包含他替换版交易的链上搞动作了。</p><pre data-type="codeBlock" text="The recipient waits until the transaction has been added to a block and z blocks have been linked after it. He doesn&apos;t know the exact amount of progress the attacker has made, but assuming the honest blocks took the average expected time per block, the attacker&apos;s potential progress will be a Poisson distribution with expected value:
"><code>The recipient waits <span class="hljs-keyword">until</span> the transaction has been added <span class="hljs-keyword">to</span> a block <span class="hljs-built_in">and</span> z blocks have been linked after it. He doesn<span class="hljs-comment">'t know the exact amount of progress the attacker has made, but assuming the honest blocks took the average expected time per block, the attacker's potential progress will be a Poisson distribution with expected value:</span>
</code></pre><p>假设收款人等到了交易被加到区块中，而且后面追加了 z 个诚实区块。他虽然不知道攻击者的确切进度，但假设诚实的区块按平均时间生成，攻击者可能的进度将会是一个泊松分布。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/47d37b73d0313ef39165df987a85ec913dfff0cd94d66c4c5f513925911a2d86.png" alt="bitcoin_poission" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_poission</figcaption></figure><p>期望值 λ 就是攻击者在这个平均时间内取得进展的区块数量的期望值。</p><pre data-type="codeBlock" text="To get the probability the attacker could still catch up now, we multiply the Poisson density for each amount of progress he could have made by the probability he could catch up from that point:
"><code><span class="hljs-keyword">To</span> <span class="hljs-keyword">get</span> the probability the attacker could still <span class="hljs-keyword">catch</span> up now, we multiply the Poisson density <span class="hljs-keyword">for</span> <span class="hljs-keyword">each</span> amount <span class="hljs-keyword">of</span> progress he could have made <span class="hljs-keyword">by</span> the probability he could <span class="hljs-keyword">catch</span> up <span class="hljs-keyword">from</span> that point:
</code></pre><p>为了计算攻击者当前仍然能赶上的概率，就是攻击者能到达 k 个区块进度的概率，下面列出了攻击者当前达到 k 个区块时，最终还能赶上 z 个诚实区块的概率。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fb9e92c8f2aab60e1e81cecfe35fed6deda549cfb532dbe4a99a177637f00d9b.png" alt="bitcoin_expression" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_expression</figcaption></figure><p>然后对所有事件概率积分得到总的概率值，再通过变换来避免无限尾部求和，最终得到下面公式：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/65c54d17b2ed9d6094065c8adb5ddb61ae79dd2d9ef3f8ff25123f50af34d7f1.png" alt="bitcoin_expression" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_expression</figcaption></figure><p>后面中本聪还给出了一段C语言代码和运行结果：</p><pre data-type="codeBlock" text="#include &lt;math.h&gt;
double AttackerSuccessProbability(double q, int z)
{
    double p = 1.0 - q;
    double lambda = z * (q / p);
    double sum = 1.0;
    int i, k;
    for (k = 0; k &lt;= z; k++)
    {
        double poisson = exp(-lambda);
        for (i = 1; i &lt;= k; i++)
            poisson *= lambda / i;
        sum -= poisson * (1 - pow(q / p, z - k));
    }
    return sum;
}
"><code>#include <span class="hljs-operator">&#x3C;</span>math.h>
double AttackerSuccessProbability(double q, <span class="hljs-keyword">int</span> z)
{
    double p <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span> <span class="hljs-operator">-</span> q;
    double lambda <span class="hljs-operator">=</span> z <span class="hljs-operator">*</span> (q <span class="hljs-operator">/</span> p);
    double sum <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span>;
    <span class="hljs-keyword">int</span> i, k;
    <span class="hljs-keyword">for</span> (k <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; k <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> z; k<span class="hljs-operator">+</span><span class="hljs-operator">+</span>)
    {
        double poisson <span class="hljs-operator">=</span> exp(<span class="hljs-operator">-</span>lambda);
        <span class="hljs-keyword">for</span> (i <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> k; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>)
            poisson <span class="hljs-operator">*</span><span class="hljs-operator">=</span> lambda <span class="hljs-operator">/</span> i;
        sum <span class="hljs-operator">-</span><span class="hljs-operator">=</span> poisson <span class="hljs-operator">*</span> (<span class="hljs-number">1</span> <span class="hljs-operator">-</span> pow(q <span class="hljs-operator">/</span> p, z <span class="hljs-operator">-</span> k));
    }
    <span class="hljs-keyword">return</span> sum;
}
</code></pre><p>结论就是当收款人被攻击的交易后面增加了z个区块后，攻击者的虚假链仍然能赶上诚实链的概率随着 z 的增长呈指数级下降。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/073f7b93bc9e7b22183fa59e7ee4e5f1a5f765a14599936a12e19b4af7f69191.png" alt="bitcoin_result" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">bitcoin_result</figcaption></figure><h2 id="h-14" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">14.总结</h2><p>论文最后一章，总结。</p><pre data-type="codeBlock" text="We have proposed a system for electronic transactions without relying on trust.
"><code>We have proposed a <span class="hljs-keyword">system</span> <span class="hljs-keyword">for</span> electronic transactions <span class="hljs-keyword">without</span> relying <span class="hljs-keyword">on</span> trust.
</code></pre><p>中本聪说我们提出了一种<strong>不依赖信任的电子交易系统</strong>。</p><pre data-type="codeBlock" text="We started with the usual framework of coins made from digital signatures, which provides strong control of ownership, but is incomplete without a way to prevent double-spending.
"><code>We started <span class="hljs-keyword">with</span> the usual framework <span class="hljs-keyword">of</span> coins made <span class="hljs-keyword">from</span> digital signatures, which provides strong control <span class="hljs-keyword">of</span> ownership, but <span class="hljs-built_in">is</span> incomplete without a way <span class="hljs-keyword">to</span> prevent <span class="hljs-type">double</span>-spending.
</code></pre><p>参考了通用的数字签名货币体系，这种体系虽然提供了强有力的所有权控制，但是由于缺乏防止双重支付的方法而不够完善。</p><pre data-type="codeBlock" text="To solve this, we proposed a peer-to-peer network using proof-of-work to record a public history of transactions that quickly becomes computationally impractical for an attacker to change if honest nodes control a majority of CPU power.
"><code><span class="hljs-selector-tag">To</span> solve this, we proposed <span class="hljs-selector-tag">a</span> peer-<span class="hljs-selector-tag">to</span>-peer network using proof-of-work <span class="hljs-selector-tag">to</span> record <span class="hljs-selector-tag">a</span> public history of transactions that quickly becomes computationally impractical for an attacker <span class="hljs-selector-tag">to</span> change if honest nodes control <span class="hljs-selector-tag">a</span> majority of CPU power.
</code></pre><p>为了解决这个问题，中本聪提出了工作量证明机制，并在点对点网络中记录公开的交易数据。只要诚实的节点掌控大多数CPU算力，对于攻击者来说，交易历史在计算上几乎变得不可更改。</p><pre data-type="codeBlock" text="Nodes work all at once with little coordination. They do not need to be identified, since messages are not routed to any particular place and only need to be delivered on a best effort basis. Nodes can leave and rejoin the network at will, accepting the proof-of-work chain as proof of what happened while they were gone.
"><code>Nodes work all at once <span class="hljs-keyword">with</span> little coordination. They <span class="hljs-keyword">do</span> <span class="hljs-built_in">not</span> need <span class="hljs-keyword">to</span> be identified, since messages are <span class="hljs-built_in">not</span> routed <span class="hljs-keyword">to</span> any particular place <span class="hljs-built_in">and</span> only need <span class="hljs-keyword">to</span> be delivered <span class="hljs-keyword">on</span> a best effort basis. Nodes can leave <span class="hljs-built_in">and</span> rejoin the network at will, accepting the proof-<span class="hljs-keyword">of</span>-work chain <span class="hljs-keyword">as</span> proof <span class="hljs-keyword">of</span> what happened <span class="hljs-keyword">while</span> they were gone.
</code></pre><p>中本聪认为这是一个<strong>结构简介而健壮</strong>的网络。节点只需要很少的协同就能同时工作。它们不需要被认证，因为信息不会发送到某个中心位置，只需要广泛传播。节点可以随时离开和重新加入网络。</p><pre data-type="codeBlock" text="They vote with their CPU power, expressing their acceptance of valid blocks by working on extending them and rejecting invalid blocks by refusing to work on them. Any needed rules and incentives can be enforced with this consensus mechanism
"><code>They vote <span class="hljs-keyword">with</span> their CPU power, expressing their acceptance of valid blocks <span class="hljs-keyword">by</span> working <span class="hljs-keyword">on</span> extending them <span class="hljs-keyword">and</span> rejecting invalid blocks <span class="hljs-keyword">by</span> refusing to work <span class="hljs-keyword">on</span> them. Any needed rules <span class="hljs-keyword">and</span> incentives can be enforced <span class="hljs-keyword">with</span> <span class="hljs-keyword">this</span> consensus mechanism
</code></pre><p>节点使用CPU算力来投票，通过努力延长有效区块来表达接受，通过拒绝在无效区块上工作来表达抵制。任何必要的规则和激励，都可以通过这个共识机制来加强。</p>]]></content:encoded>
            <author>samehada@newsletter.paragraph.com (samehada)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/e5f00463330e0179db6f60c5485542adefcb4e92edd425e199d435b5deef3e20.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>