<?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>Notion Blog</title>
        <link>https://ifrank.qzz.io/</link>
        <description>一个NotionNext搭建的博客</description>
        <lastBuildDate>Wed, 26 Aug 2026 14:51:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2026, Frank</copyright>
        <item>
            <title><![CDATA[如何优雅地导入导出数据？]]></title>
            <link>https://ifrank.qzz.io/article/export_excel_data</link>
            <guid>https://ifrank.qzz.io/article/export_excel_data</guid>
            <pubDate>Sun, 21 Jul 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[本文主要介绍如何优雅地导出数据。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-25c6f6b8b96740f2bcd130b69c3514c8"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-table-of-contents notion-gray notion-block-81f0c6e298444a20896d813d6269460a"><a href="#63b7fb269c224d569dc4be30563e16c7" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">背景</span></a><a href="#c26731f961bd42af810e336acaf3d67a" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">EasyExcel</span></a><a href="#8bcb5e9e35a34e75a21db2223bfb7f95" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">介绍</span></a><a href="#7b356c0ff1834ffe861df95db17253e8" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">EasyExcel中的注解</span></a><a href="#92d4aa9477614e529f28a1a470a2c899" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">@ExcelProperty</span></a><a href="#846c2abee17c4a168d41228aa40a2cb2" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">@ExcelIgnore</span></a><a href="#d3ac4b336a49469896beaa015945954c" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">@ExcelIgnoreUnannotated</span></a><a href="#3e79bd86dc734c1e9f317e3c969c1966" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">@DateTimeFormat</span></a><a href="#5fb9d3049ae1425cbbc8d7c2a8d93c56" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">@NumberFormat</span></a><a href="#47259470d52a485abdae17a05071ff01" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">@ColumnWith</span></a><a href="#1812725f497d4577888006dfaf7911a9" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">@ContentRowHeight</span></a><a href="#9be1565f4cef40e793e05b36bed56b4a" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">@ContentFontStyle</span></a><a href="#a6cc34f6bb7f4687b2aeb865a6bb4763" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">@ContentStyle</span></a><a href="#9ca0e50da4c7446b8cdef3ad21a1b8f3" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">数据导出（写Excel）</span></a><a href="#7828e92abc2f42c1855255a56700d0e4" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">controller</span></a><a href="#233ac41d84d544ea99cb0bb34b7846dc" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">使用UserRespVO实现数据导出</span></a><a href="#3c795116b84b43309cff27223faf58d1" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">ExcelUtils</span></a><a href="#6be32152e7cf4d32a54320c14cddaba7" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">数据导入（读Excel）</span></a><a href="#64e1374591494fd0bef4297fc238f3b3" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">controller</span></a><a href="#99182d965e6b4445800011cfa101dce5" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">使用UserImportExcelVO实现数据导入</span></a><a href="#5853fd24e0444a039e8d409fd40fe415" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">ExcelUtils</span></a><a href="#1bf4c604ffad4948a5497c2552f54848" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">字段转换器</span></a></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-63b7fb269c224d569dc4be30563e16c7" data-id="63b7fb269c224d569dc4be30563e16c7"><span><div id="63b7fb269c224d569dc4be30563e16c7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#63b7fb269c224d569dc4be30563e16c7" title="背景"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">背景</span></span></h2><div class="notion-text notion-block-0489ed3aed09420fa5ea88392dad29c3">在SpringBoot项目中，数据导入导出是非常常见的需求。无论是从数据库导出数据到Excel文件，还是从Excel文件导入数据到系统中，都会涉及到数据的格式转换、校验以及异常处理等多个方面。优雅地实现这些功能，能够提高系统的可靠性和用户体验。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-c26731f961bd42af810e336acaf3d67a" data-id="c26731f961bd42af810e336acaf3d67a"><span><div id="c26731f961bd42af810e336acaf3d67a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#c26731f961bd42af810e336acaf3d67a" title="EasyExcel"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">EasyExcel</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-8bcb5e9e35a34e75a21db2223bfb7f95" data-id="8bcb5e9e35a34e75a21db2223bfb7f95"><span><div id="8bcb5e9e35a34e75a21db2223bfb7f95" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8bcb5e9e35a34e75a21db2223bfb7f95" title="介绍"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">介绍</span></span></h3><ul class="notion-list notion-list-disc notion-block-38dce2d94dc74b9cb62f3656434b1367"><li>EasyExcel 是阿里开源的 Excel 工具库，具有简单易用、低内存、高性能的特点。</li></ul><ul class="notion-list notion-list-disc notion-block-79bb5d233084424fada97786bb2a7fd6"><li>在尽可用节约内存的情况下，支持百万行的 Excel 读写操作。例如说，仅使用 64M 内存，20 秒完成 75M（46 万行 25 列）Excel 的读取。并且，还有极速模式能更快，但是内存占用会在100M 多一点。</li></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-e100bbe2675c44b89d71f94f710cb003"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F511ac9ee-95ce-4dbe-ac27-5ef694ae8297%2FUntitled.png?table=block&amp;id=e100bbe2-675c-44b8-9d71-f94f710cb003&amp;t=e100bbe2-675c-44b8-9d71-f94f710cb003&amp;width=1434&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-7b356c0ff1834ffe861df95db17253e8" data-id="7b356c0ff1834ffe861df95db17253e8"><span><div id="7b356c0ff1834ffe861df95db17253e8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#7b356c0ff1834ffe861df95db17253e8" title="EasyExcel中的注解"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">EasyExcel中的注解</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-92d4aa9477614e529f28a1a470a2c899" data-id="92d4aa9477614e529f28a1a470a2c899"><span><div id="92d4aa9477614e529f28a1a470a2c899" class="notion-header-anchor"></div><a class="notion-hash-link" href="#92d4aa9477614e529f28a1a470a2c899" title="@ExcelProperty"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code">@</code><code class="notion-inline-code"><b>ExcelProperty</b></code></span></span></h4><div class="notion-text notion-block-282039aebd9643e98f92465eb8c33fbf">用于匹配excel和实体类的匹配，参数如下：</div><table class="notion-simple-table notion-block-4d64bb6a1e414b1795ae9a56563e738f"><tbody><tr class="notion-simple-table-row notion-block-8eec4b3551bc4f8d8da963e2e2ad7a8f"><td class="" style="width:120px"><div class="notion-simple-table-cell">名称</div></td><td class="" style="width:154.41250610351562px"><div class="notion-simple-table-cell">默认值</div></td><td class="" style="width:550px"><div class="notion-simple-table-cell">描述</div></td></tr><tr class="notion-simple-table-row notion-block-c9be787340724d44b6fc76476b9f5970"><td class="" style="width:120px"><div class="notion-simple-table-cell">value</div></td><td class="" style="width:154.41250610351562px"><div class="notion-simple-table-cell">空</div></td><td class="" style="width:550px"><div class="notion-simple-table-cell">列名，用于匹配excel中的头，必须全匹配，如果有多行头，会匹配最后一行头</div></td></tr><tr class="notion-simple-table-row notion-block-27d0985103624841bbe41442fb0f8430"><td class="" style="width:120px"><div class="notion-simple-table-cell">order</div></td><td class="" style="width:154.41250610351562px"><div class="notion-simple-table-cell">Integer.MAX_VALUE</div></td><td class="" style="width:550px"><div class="notion-simple-table-cell">列序号，优先级高于<code class="notion-inline-code">value</code>，会根据<code class="notion-inline-code">order</code>的顺序来匹配实体和excel中数据的顺序</div></td></tr><tr class="notion-simple-table-row notion-block-d7b1045c34c54831a8fd264574c2d049"><td class="" style="width:120px"><div class="notion-simple-table-cell">index</div></td><td class="" style="width:154.41250610351562px"><div class="notion-simple-table-cell">-1</div></td><td class="" style="width:550px"><div class="notion-simple-table-cell">优先级高于<code class="notion-inline-code">value</code>和<code class="notion-inline-code">order</code>，会根据<code class="notion-inline-code">index</code>直接指定到excel中具体的哪一列</div></td></tr><tr class="notion-simple-table-row notion-block-a1e89fe981894d619d2fe13aa0b7404c"><td class="" style="width:120px"><div class="notion-simple-table-cell">converter</div></td><td class="" style="width:154.41250610351562px"><div class="notion-simple-table-cell">自动选择</div></td><td class="" style="width:550px"><div class="notion-simple-table-cell">指定当前字段用什么转换器，默认会自动选择。写的情况下只要实现<code class="notion-inline-code">com.alibaba.excel.converters.Converter#convertToExcelData(com.alibaba.excel.converters.WriteConverterContext&lt;T&gt;)</code> 方法即可</div></td></tr></tbody></table><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-846c2abee17c4a168d41228aa40a2cb2" data-id="846c2abee17c4a168d41228aa40a2cb2"><span><div id="846c2abee17c4a168d41228aa40a2cb2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#846c2abee17c4a168d41228aa40a2cb2" title="@ExcelIgnore"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code">@ExcelIgnore</code></span></span></h4><div class="notion-text notion-block-7fd14a0a68e5407bb183465b5a5be542">默认所有字段都会和excel去匹配，加了这个注解会忽略该字段</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-d3ac4b336a49469896beaa015945954c" data-id="d3ac4b336a49469896beaa015945954c"><span><div id="d3ac4b336a49469896beaa015945954c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d3ac4b336a49469896beaa015945954c" title="@ExcelIgnoreUnannotated"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code">@ExcelIgnoreUnannotated</code></span></span></h4><div class="notion-text notion-block-6bb85915aa1943f799563ec14d76d0e1">默认不管加不加<code class="notion-inline-code">ExcelProperty</code>的注解的所有字段都会参与读写，加了<code class="notion-inline-code">ExcelIgnoreUnannotated</code>注解以后，不加<code class="notion-inline-code">ExcelProperty</code>注解的字段就不会参与</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-3e79bd86dc734c1e9f317e3c969c1966" data-id="3e79bd86dc734c1e9f317e3c969c1966"><span><div id="3e79bd86dc734c1e9f317e3c969c1966" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3e79bd86dc734c1e9f317e3c969c1966" title="@DateTimeFormat"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code">@DateTimeFormat</code></span></span></h4><div class="notion-text notion-block-2f1c4c5364ac4f0180da5abc6008c3a1">日期转换，用<code class="notion-inline-code">String</code>去接收excel日期格式的数据会调用这个注解，参数如下：</div><table class="notion-simple-table notion-block-940580dcd72e47ca9da5025beb31b68e"><tbody><tr class="notion-simple-table-row notion-block-aec8888259df491b9dd7760359e6824b"><td class="" style="width:120px"><div class="notion-simple-table-cell">名称</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">默认值</div></td><td class="" style="width:571px"><div class="notion-simple-table-cell">描述</div></td></tr><tr class="notion-simple-table-row notion-block-91fe52975ac74fd1aa3a22669f85ed89"><td class="" style="width:120px"><div class="notion-simple-table-cell">value</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">空</div></td><td class="" style="width:571px"><div class="notion-simple-table-cell">参照<code class="notion-inline-code">java.text.SimpleDateFormat</code>书写即可</div></td></tr><tr class="notion-simple-table-row notion-block-53e6d51117984743913aac35894fb7de"><td class="" style="width:120px"><div class="notion-simple-table-cell">use1904windowing</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">自动选择</div></td><td class="" style="width:571px"><div class="notion-simple-table-cell">excel中时间是存储1900年起的一个双精度浮点数，但是有时候默认开始日期是1904，所以设置这个值改成默认1904年开始</div></td></tr></tbody></table><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-5fb9d3049ae1425cbbc8d7c2a8d93c56" data-id="5fb9d3049ae1425cbbc8d7c2a8d93c56"><span><div id="5fb9d3049ae1425cbbc8d7c2a8d93c56" class="notion-header-anchor"></div><a class="notion-hash-link" href="#5fb9d3049ae1425cbbc8d7c2a8d93c56" title="@NumberFormat"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code">@NumberFormat</code></span></span></h4><div class="notion-text notion-block-93afa19d647a4b1597463a7900be025f">数字转换，用<code class="notion-inline-code">String</code>去接收excel数字格式的数据会调用这个注解。</div><table class="notion-simple-table notion-block-d3c5c164666b46feb282d3b1cdba66e2"><tbody><tr class="notion-simple-table-row notion-block-fe449d664e464409951163db85b48ae5"><td class="" style="width:120px"><div class="notion-simple-table-cell">名称</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">默认值</div></td><td class="" style="width:343px"><div class="notion-simple-table-cell">描述</div></td></tr><tr class="notion-simple-table-row notion-block-f3d777e9ba2047d78058abbd803118b5"><td class="" style="width:120px"><div class="notion-simple-table-cell">value</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">空</div></td><td class="" style="width:343px"><div class="notion-simple-table-cell">参照<code class="notion-inline-code">java.text.DecimalFormat</code>书写即可</div></td></tr><tr class="notion-simple-table-row notion-block-f3b1a2b1d92046399c31bb052f65ad32"><td class="" style="width:120px"><div class="notion-simple-table-cell">roundingMode</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">RoundingMode.HALF_UP</div></td><td class="" style="width:343px"><div class="notion-simple-table-cell">格式化的时候设置舍入模式</div></td></tr></tbody></table><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-47259470d52a485abdae17a05071ff01" data-id="47259470d52a485abdae17a05071ff01"><span><div id="47259470d52a485abdae17a05071ff01" class="notion-header-anchor"></div><a class="notion-hash-link" href="#47259470d52a485abdae17a05071ff01" title="@ColumnWith"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code"><b>@ColumnWith</b></code></span></span></h4><div class="notion-text notion-block-f7a70e1110614cf1a8edac5ab4f2675e">用于设置列宽度的注解，注解中只有一个参数value，value的单位是字符长度，最大可以设置255个字符，因为一个excel单元格最大可以写入的字符个数就是255个字符。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1812725f497d4577888006dfaf7911a9" data-id="1812725f497d4577888006dfaf7911a9"><span><div id="1812725f497d4577888006dfaf7911a9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1812725f497d4577888006dfaf7911a9" title="@ContentRowHeight"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code"><b>@ContentRowHeight</b></code></span></span></h4><div class="notion-text notion-block-95431eda85a54d948db40ee9129e37f3">用于设置行高，参数：value行高，<code class="notion-inline-code">-1</code>代表自动行高</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-9be1565f4cef40e793e05b36bed56b4a" data-id="9be1565f4cef40e793e05b36bed56b4a"><span><div id="9be1565f4cef40e793e05b36bed56b4a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9be1565f4cef40e793e05b36bed56b4a" title="@ContentFontStyle"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code"><b>@ContentFontStyle</b></code></span></span></h4><div class="notion-text notion-block-53a42fb7e8014c8caa751fe5ab39b269">用于设置单元格内容字体格式的注解</div><table class="notion-simple-table notion-block-93ad7fb34a154dc0bd6599f1e1e90239"><tbody><tr class="notion-simple-table-row notion-block-44f51775cb4f434081595a9fbc6c7364"><td class="" style="width:120px"><div class="notion-simple-table-cell">参数</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">含义</div></td></tr><tr class="notion-simple-table-row notion-block-c124b348aef2496e8a2165a17dcc067e"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">fontName</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">字体名称</div></td></tr><tr class="notion-simple-table-row notion-block-5f32b08bada44cda9ff6bc48ca7689ed"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">fontHeightInPoints</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">字体高度</div></td></tr><tr class="notion-simple-table-row notion-block-8c6fe843674f45cfa131cca4f998a452"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">italic</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">是否斜体</div></td></tr><tr class="notion-simple-table-row notion-block-43c4f90c0e6349eb8e60aa8b3ad679b4"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">strikeout</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">是否设置删除水平线</div></td></tr><tr class="notion-simple-table-row notion-block-67b37a740b074a8fb5319aed8fd17aaf"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">color</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">字体颜色</div></td></tr><tr class="notion-simple-table-row notion-block-c82680b068dd454cafe23e679c39197f"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">typeOffset</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">偏移量</div></td></tr><tr class="notion-simple-table-row notion-block-300a9d579b1a4d91a7b7e7f94397ba9f"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">underline</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">下划线</div></td></tr><tr class="notion-simple-table-row notion-block-a72ae0f02544439ab508795c24b9b4bb"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">bold</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">是否加粗</div></td></tr><tr class="notion-simple-table-row notion-block-6355a201246b414a9b88ecce2d6ceeb0"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">charset</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">编码格式</div></td></tr></tbody></table><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-a6cc34f6bb7f4687b2aeb865a6bb4763" data-id="a6cc34f6bb7f4687b2aeb865a6bb4763"><span><div id="a6cc34f6bb7f4687b2aeb865a6bb4763" class="notion-header-anchor"></div><a class="notion-hash-link" href="#a6cc34f6bb7f4687b2aeb865a6bb4763" title="@ContentStyle"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code"><b>@ContentStyle</b></code></span></span></h4><div class="notion-text notion-block-fa191b5cb69c462986fa69f26a17699a">设置内容格式注解</div><table class="notion-simple-table notion-block-a453f2d733a94f709dc1965f00548e18"><tbody><tr class="notion-simple-table-row notion-block-cf362620ef584fb7a98b7cd2b4e21566"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell">参数</div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">含义</div></td></tr><tr class="notion-simple-table-row notion-block-9f000035dbb147eab0c52f82a6ca5cb6"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">dataFormat</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">日期格式</div></td></tr><tr class="notion-simple-table-row notion-block-998b3734a84645b09ad650ac5b57aaab"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">hidden</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置单元格使用此样式隐藏</div></td></tr><tr class="notion-simple-table-row notion-block-631a80fc60b0447bad234a0aa94b2eab"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">locked</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置单元格使用此样式锁定</div></td></tr><tr class="notion-simple-table-row notion-block-12fb984b3a424598827300919af288e7"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">quotePrefix</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">在单元格前面增加`符号，数字或公式将以字符串形式展示</div></td></tr><tr class="notion-simple-table-row notion-block-1dfaa6100e2043e8a95686af5ffad3c7"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">horizontalAlignment</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置是否水平居中</div></td></tr><tr class="notion-simple-table-row notion-block-df73a95ffda84eb4bc8d542f2973cb14"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">wrapped</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置文本是否应换行。将此标志设置为<code class="notion-inline-code">true</code>通过在多行上显示使单元格中的所有内容可见</div></td></tr><tr class="notion-simple-table-row notion-block-fb58382636a04d6892fa1a3e702d452c"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">verticalAlignment</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置是否垂直居中</div></td></tr><tr class="notion-simple-table-row notion-block-686cffc83a0a4b84a52036f7b1a95d0d"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">rotation</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置单元格中文本旋转角度。03版本的Excel旋转角度区间为-90°~90°，07版本的Excel旋转角度区间为0°~180°</div></td></tr><tr class="notion-simple-table-row notion-block-9b8ce78198854613947861efd72df4b5"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">indent</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置单元格中缩进文本的空格数</div></td></tr><tr class="notion-simple-table-row notion-block-24b717be4f2a49ad8ae4526f9ace2ca0"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">borderLeft</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置左边框的样式</div></td></tr><tr class="notion-simple-table-row notion-block-01ecce4634c947908486270b924f9a00"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">borderRight</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置右边框样式</div></td></tr><tr class="notion-simple-table-row notion-block-3f2f362e13a9488eb85d0d56c2e79d73"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">borderTop</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置上边框样式</div></td></tr><tr class="notion-simple-table-row notion-block-3142d6ccef3d4a04a27af90881aaa279"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">borderBottom</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置下边框样式</div></td></tr><tr class="notion-simple-table-row notion-block-7b5052adb8054804b710b37f3ff3abbd"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">leftBorderColor</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置左边框颜色</div></td></tr><tr class="notion-simple-table-row notion-block-e835c6c7dd0e4b318b941d796cb4c27a"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">rightBorderColor</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置右边框颜色</div></td></tr><tr class="notion-simple-table-row notion-block-87628b451f8b4dab9eb837a5ab1cfc29"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">topBorderColor</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置上边框颜色</div></td></tr><tr class="notion-simple-table-row notion-block-ce058ce3c2514e3b95a882afa36ad897"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">bottomBorderColor</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置下边框颜色</div></td></tr><tr class="notion-simple-table-row notion-block-c140ee24cdfa4302ba15931a8c5e4f8f"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">fillPatternType</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置填充类型</div></td></tr><tr class="notion-simple-table-row notion-block-0cacc65ccc494f1383d2eb41380158d0"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">fillBackgroundColor</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置背景色</div></td></tr><tr class="notion-simple-table-row notion-block-92166ee92edd417080c5de08a051ebd2"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">fillForegroundColor</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置前景色</div></td></tr><tr class="notion-simple-table-row notion-block-338e8bf861274d7196e606f180068f57"><td class="" style="width:165.5749969482422px"><div class="notion-simple-table-cell"><code class="notion-inline-code">shrinkToFit</code></div></td><td class="" style="width:431px"><div class="notion-simple-table-cell">设置自动单元格自动大小</div></td></tr></tbody></table><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-9ca0e50da4c7446b8cdef3ad21a1b8f3" data-id="9ca0e50da4c7446b8cdef3ad21a1b8f3"><span><div id="9ca0e50da4c7446b8cdef3ad21a1b8f3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9ca0e50da4c7446b8cdef3ad21a1b8f3" title="数据导出（写Excel）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">数据导出（写Excel）</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-7828e92abc2f42c1855255a56700d0e4" data-id="7828e92abc2f42c1855255a56700d0e4"><span><div id="7828e92abc2f42c1855255a56700d0e4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#7828e92abc2f42c1855255a56700d0e4" title="controller"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">controller</span></span></h4><ul class="notion-list notion-list-disc notion-block-322985613bb74023bb8d278fa27e1198"><li>将从数据库中查询出来的列表，一般可以复用分页接口，需要设置 <code class="notion-inline-code">.setPageSize(PageParam.PAGE_SIZE_NONE)</code> 不过滤分页</li></ul><ul class="notion-list notion-list-disc notion-block-914a840ebf2442c8a8358cf4785ea2c7"><li>将 PostDO 列表，转换成 PostRespVO 列表，之后通过 ExcelUtils 转换成 Excel 文件，返回给前端。</li></ul><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-233ac41d84d544ea99cb0bb34b7846dc" data-id="233ac41d84d544ea99cb0bb34b7846dc"><span><div id="233ac41d84d544ea99cb0bb34b7846dc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#233ac41d84d544ea99cb0bb34b7846dc" title="使用UserRespVO实现数据导出"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用<span class="notion-default"><code class="notion-inline-code"><b>UserRespVO</b></code></span><span class="notion-default"><b>实现数据导出</b></span></span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-3c795116b84b43309cff27223faf58d1" data-id="3c795116b84b43309cff27223faf58d1"><span><div id="3c795116b84b43309cff27223faf58d1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3c795116b84b43309cff27223faf58d1" title="ExcelUtils"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code">ExcelUtils</code></span></span></h4><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-6be32152e7cf4d32a54320c14cddaba7" data-id="6be32152e7cf4d32a54320c14cddaba7"><span><div id="6be32152e7cf4d32a54320c14cddaba7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6be32152e7cf4d32a54320c14cddaba7" title="数据导入（读Excel）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">数据导入（读Excel）</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-64e1374591494fd0bef4297fc238f3b3" data-id="64e1374591494fd0bef4297fc238f3b3"><span><div id="64e1374591494fd0bef4297fc238f3b3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#64e1374591494fd0bef4297fc238f3b3" title="controller"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">controller</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-99182d965e6b4445800011cfa101dce5" data-id="99182d965e6b4445800011cfa101dce5"><span><div id="99182d965e6b4445800011cfa101dce5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#99182d965e6b4445800011cfa101dce5" title="使用UserImportExcelVO实现数据导入"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用<code class="notion-inline-code">UserImportExcelVO</code><span class="notion-default"><b>实现数据导入</b></span></span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-5853fd24e0444a039e8d409fd40fe415" data-id="5853fd24e0444a039e8d409fd40fe415"><span><div id="5853fd24e0444a039e8d409fd40fe415" class="notion-header-anchor"></div><a class="notion-hash-link" href="#5853fd24e0444a039e8d409fd40fe415" title="ExcelUtils"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code">ExcelUtils</code></span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1bf4c604ffad4948a5497c2552f54848" data-id="1bf4c604ffad4948a5497c2552f54848"><span><div id="1bf4c604ffad4948a5497c2552f54848" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1bf4c604ffad4948a5497c2552f54848" title="字段转换器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">字段转换器</span></span></h4><div class="notion-text notion-block-d1917a8e752b42d88b6e734e676386d4">如果字段的的注解 <code class="notion-inline-code">converter</code> 属性是 DictConvert 转换器，用于字典的转换。例如说，通过 <code class="notion-inline-code">status</code> 字段，将 <code class="notion-inline-code">status = 1</code> 转换成“开启”列，<code class="notion-inline-code">status = 0</code> 转换成”禁用”列。</div><div class="notion-text notion-block-a0a96505f0c347439ae67778cf8aea87">EasyExcel定义了 <a class="notion-link" href="https://github.com/alibaba/easyexcel/blob/master/easyexcel-core/src/main/java/com/alibaba/excel/converters/Converter.java" target="_blank" rel="noopener noreferrer">Convert</a> 接口，用于实现字段的转换。它有两个核心方法：</div><ol start="1" class="notion-list notion-list-numbered notion-block-299ed593c21a4f629bb0345f8b5fc96b" style="list-style-type:decimal"><li><code class="notion-inline-code">convertToJavaData(...)</code> 方法：将 Excel Row 对应表格的值，转换成 Java 内存中的值。例如说，Excel 的“状态”列，将“状态”列转换成 <code class="notion-inline-code">status = 1</code>，”禁用”列转换成 <code class="notion-inline-code">status = 0</code>。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-92edc0f1a12f4605a67d54d74159e6b3" style="list-style-type:decimal"><li><code class="notion-inline-code">convertToExcelData(...)</code> 方法：恰好相反，将 Java 内存中的值，转换成 Excel Row 对应表格的值。例如说，Excel 的“状态”列，将 <code class="notion-inline-code">status = 1</code> 转换成“开启”列，<code class="notion-inline-code">status = 0</code> 转换成”禁用”列</li></ol><div class="notion-blank notion-block-f8170e17f89647529d4470e4a9602496"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Docker]]></title>
            <link>https://ifrank.qzz.io/article/docker</link>
            <guid>https://ifrank.qzz.io/article/docker</guid>
            <pubDate>Fri, 02 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[本文主要介绍Docker相关的知识，主要包括Docker的架构和原理，镜像和容器相关的操作，如何使用Dockerfile和Docker-compose部署微服务应用等等。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-4f9244b396f2423cbd7570ce45aa175a"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-table-of-contents notion-gray notion-block-367c6e4538f4458192fb35f3446d5a7f"><a href="#9d1f5558ab0e4d42978d3686be2e7a7b" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">初识Docker</span></a><a href="#a77b328b553f45e1a9b24321b1d827c4" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">什么是Docker？</span></a><a href="#6a038eac381e422fa3b858054bd722e5" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">应用部署的环境问题</span></a><a href="#5bdf8b2a25214192acb7f304e2a47182" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">Docker解决依赖兼容问题</span></a><a href="#d5e4b49593cc4729bbb21f43231dd485" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">Docker解决操作系统环境差异</span></a><a href="#eb9f9d23b0db4ed2936750bd7f44178a" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">Docker和虚拟机的区别</span></a><a href="#ed32aa59fae142148e1641369e174634" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">Docker架构</span></a><a href="#ea161e58f24e442ba70507cc46e6c317" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">镜像和容器</span></a><a href="#096ba966e8c548f28220f5f0319398dc" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">DockerHub</span></a><a href="#b2c52fd09d784be7b39148b06e67ce48" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">Docker架构</span></a><a href="#ac7b8e15a5b348949b6eae2300fc3e43" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">安装Docker</span></a><a href="#a858854aa3624848ba79beeadf028104" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">卸载（可选）</span></a><a href="#f6a6c31103564fdca4bbf69b45dd1887" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">安装docker</span></a><a href="#9bb6b5a616a947fca31a1a6fe59abad2" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">启动docker</span></a><a href="#07093ad7a6c44a70afb68e056585685e" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">配置镜像加速</span></a><a href="#8a4366b1730b4eb08f1b72bac083de49" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">Docker的基本操作</span></a><a href="#98b99584711346468a047054acade2c4" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">镜像操作</span></a><a href="#b44de59885da49998d37d026b7827b51" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">镜像名称</span></a><a href="#31922b97f4cb4ecfbe37375d9c835570" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">镜像命令</span></a><a href="#9888c67c23b34bd493f4faea92f071ca" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">案例1：拉取、查看镜像</span></a><a href="#9ba3b6abee2c492ca117570f25cbdad9" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">案例2：保存、导入镜像</span></a><a href="#844744cf1ac24c66804bb4fe84aa7bec" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">容器操作</span></a><a href="#ea2d5acc88ac4a81b6b45530a9e479e9" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">容器相关命令</span></a><a href="#d9c3389128d24c2f8724c6a5ad15f83e" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">案例：创建并运行一个容器</span></a><a href="#d76684b25a0348509d57795dbe49e66e" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">案例：进入容器，修改文件</span></a><a href="#22b449d7f94b43918c612a3ce7954e32" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">其他命令</span></a><a href="#945ab6b79b3e4d0f91d4cd2824c7aed2" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">数据卷（容器数据管理）</span></a><a href="#35aa5a12eea241959a12bc51e5637be7" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">什么是数据卷</span></a><a href="#b01a4c6137bd4f91b837d59ebe60cb72" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">数据集操作命令</span></a><a href="#770bf6364f2943bfb49ec9a8a1e9060e" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">创建和查看数据卷</span></a><a href="#4555b4f1d5d2460fa0cae34d0dcb46ff" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">挂载数据卷</span></a><a href="#79b99c56daf041c69ea915f0a4ef3254" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">案例：给nginx挂载数据卷</span></a><a href="#6c585d069ccc4f008c05708ca9bf60f6" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">案例：给MySQL挂载本地目录</span></a><a href="#fe6834adfec448f18be0e133d426c2fa" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">Dockerfile自定义镜像</span></a><a href="#586d01bfc8e046b3abe52493200f6e60" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">镜像结构</span></a><a href="#fd71a2b8e5514f7984a3c075d209fa79" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">Dockerfile语法</span></a><a href="#007dcf17847c4568910fa5d928551ecd" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">构建Java项目</span></a><a href="#298e660d8f7c4f1d9e4a9d2883c8d235" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">基于Ubuntu构建Java项目</span></a><a href="#f5e48a6f3fc549f797c895b8072749f8" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">基于java8构建Java项目</span></a><a href="#c80fff9f7ab448828404910671f075bd" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">小结</span></a><a href="#1bf52d37281749a2a8d1e73b509b0a58" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">Docker-Compose</span></a><a href="#80d91ac4a0024c6a90f0fc323b113b7f" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">初识DockerCompose</span></a><a href="#94215cbd3eb3436babbfc386314033e8" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">安装DockerCompose</span></a><a href="#f4713196d18d45dbacb871b244037363" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">下载</span></a><a href="#1ee8b3ee8fe94efc9647591583c4b592" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">修改执行权限</span></a><a href="#96305a6e799548b9b1faa65bf923be47" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">查看docker-compose版本</span></a><a href="#ac35a4bd78bc4049befb9f1b5f5fd041" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">Base自动补全命令</span></a><a href="#0f63b81347c44e7eae32e522e6fc52c4" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">部署微服务集群</span></a><a href="#58695b53810a4193835ec9e56073c609" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">compose文件</span></a><a href="#f290c0ad83954e7c956c45c4173a3250" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">修改微服务配置</span></a><a href="#fd3fe3cb8ecf44f38602f3581b1f2606" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">打包</span></a><a href="#aac8050351954054a14eaefbc6a9f4ec" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">部署</span></a><a href="#b8150014298f4d36ac0b160f613dcd8c" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">Docker镜像仓库</span></a><a href="#22a7d829acb34ebe9e46c14871e22ff0" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">搭建私有镜像仓库</span></a><a href="#395a5875d1194ddcbad8cb6374a20a8c" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">简化版镜像仓库</span></a><a href="#4d160c29486d45d59f2190f0554243a7" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">带有图形化界面版本</span></a><a href="#0f717a3888ed41c8989d5cbdae5ce745" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:48px">配置Docker信任地址</span></a><a href="#275adf07f9f14f6ab3d259b824086be1" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">推送、拉取镜像</span></a></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-9d1f5558ab0e4d42978d3686be2e7a7b" data-id="9d1f5558ab0e4d42978d3686be2e7a7b"><span><div id="9d1f5558ab0e4d42978d3686be2e7a7b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9d1f5558ab0e4d42978d3686be2e7a7b" title="初识Docker"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>初识Docker</b></span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-a77b328b553f45e1a9b24321b1d827c4" data-id="a77b328b553f45e1a9b24321b1d827c4"><span><div id="a77b328b553f45e1a9b24321b1d827c4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#a77b328b553f45e1a9b24321b1d827c4" title="什么是Docker？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>什么是Docker？</b></span></span></h3><div class="notion-text notion-block-8cda221f95d14b7c97bd169872bc7992">微服务虽然具备各种各样的优势，但服务的拆分通用给部署带来了很大的麻烦。</div><ul class="notion-list notion-list-disc notion-block-abaaef8cb9ee4b7a9f587b0689c20dea"><li>分布式系统中，依赖的组件非常多，不同组件之间部署时往往会产生一些冲突。</li></ul><ul class="notion-list notion-list-disc notion-block-4bbdf4f9de244d09a5d504058622329c"><li>在数百上千台服务中重复部署，环境不一定一致，会遇到各种问题</li></ul><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-6a038eac381e422fa3b858054bd722e5" data-id="6a038eac381e422fa3b858054bd722e5"><span><div id="6a038eac381e422fa3b858054bd722e5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6a038eac381e422fa3b858054bd722e5" title="应用部署的环境问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>应用部署的环境问题</b></span></span></h4><div class="notion-text notion-block-d2e94255bf054975b609ddfd4c2b2cc5">大型项目组件较多，运行环境也较为复杂，部署时会碰到一些问题：</div><ul class="notion-list notion-list-disc notion-block-c7d3cc757d3d44beb892a34dd685be80"><li>依赖关系复杂，容易出现兼容性问题</li></ul><ul class="notion-list notion-list-disc notion-block-0ba850d09d21409a9ddbb3491b9127dc"><li>开发、测试、生产环境有差异</li></ul><div class="notion-text notion-block-f99d883875cc45379da2250695b6f61c">例如一个项目中，部署时需要依赖于node.js、Redis、RabbitMQ、MySQL等，这些服务部署时所需要的函数库、依赖项各不相同，甚至会有冲突。给部署带来了极大的困难。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-38dbc148aed4472a80849b5d46b71297"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:720px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F59800de7-478b-4ed2-928a-bba1fa0eca5b%2FUntitled.png?table=block&amp;id=38dbc148-aed4-472a-8084-9b5d46b71297&amp;t=38dbc148-aed4-472a-8084-9b5d46b71297&amp;width=720&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-5bdf8b2a25214192acb7f304e2a47182" data-id="5bdf8b2a25214192acb7f304e2a47182"><span><div id="5bdf8b2a25214192acb7f304e2a47182" class="notion-header-anchor"></div><a class="notion-hash-link" href="#5bdf8b2a25214192acb7f304e2a47182" title="Docker解决依赖兼容问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Docker解决依赖兼容问题</b></span></span></h4><div class="notion-text notion-block-7b6a849a9d454bd5946058063c4fdb46">而Docker确巧妙的解决了这些问题，Docker是如何实现的呢？</div><div class="notion-text notion-block-2dbd6f6ebd8c41bb885f2ddbbabed679">Docker为了解决依赖的兼容问题的，采用了两个手段：</div><ul class="notion-list notion-list-disc notion-block-f293bf2b8edf4155b3e795652b5c39c7"><li>将应用的Libs（函数库）、Deps（依赖）、配置与应用一起打包形成<span class="notion-orange"><b>镜像</b></span></li></ul><ul class="notion-list notion-list-disc notion-block-3b93d890924347379a4b158e4e1868d3"><li>将每个应用放到一个隔离<span class="notion-orange"><b>容器</b></span>去运行，避免互相干扰</li></ul><div class="notion-text notion-block-226fdf102aa4481a83c548c94822906e">这样打包好的应用包中，既包含应用本身，也保护应用所需要的Libs、Deps，无需再操作系统上安装这些，自然就不存在不同应用之间的兼容问题了。</div><div class="notion-text notion-block-ff094467faf242b48e6a2fded80760de">虽然解决了不同应用的兼容问题，但是开发、测试等环境会存在差异，操作系统版本也会有差异，怎么解决这些问题呢？</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-a990abfda39a41bfb5763880b3ef921f"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:816px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F3ef4c256-ad29-4dbb-81de-ad85a2c60dcc%2FUntitled.png?table=block&amp;id=a990abfd-a39a-41bf-b576-3880b3ef921f&amp;t=a990abfd-a39a-41bf-b576-3880b3ef921f&amp;width=816&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-d5e4b49593cc4729bbb21f43231dd485" data-id="d5e4b49593cc4729bbb21f43231dd485"><span><div id="d5e4b49593cc4729bbb21f43231dd485" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d5e4b49593cc4729bbb21f43231dd485" title="Docker解决操作系统环境差异"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Docker解决操作系统环境差异</b></span></span></h4><div class="notion-text notion-block-0609880e71514a5882b0a1e1ac653371">要解决不同操作系统环境差异问题，必须先了解操作系统结构。以一个Ubuntu操作系统为例，结构如下：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-56b05aa723fa44869695e4886adc5cfd"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:912px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F80e21002-203e-4bea-872b-ad848764ebe6%2FUntitled.png?table=block&amp;id=56b05aa7-23fa-4486-9695-e4886adc5cfd&amp;t=56b05aa7-23fa-4486-9695-e4886adc5cfd&amp;width=912&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><ul class="notion-list notion-list-disc notion-block-082b95cd6f2c4a58b0e5f0bfe48a2562"><li>结构包括：</li><ul class="notion-list notion-list-disc notion-block-082b95cd6f2c4a58b0e5f0bfe48a2562"><ol start="1" class="notion-list notion-list-numbered notion-block-6bcb28ea75d34428a28b5c516f234f91" style="list-style-type:decimal"><li>计算机硬件：例如CPU、内存、磁盘等</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-dde6bda14cb9465f80375ae04d9174f7" style="list-style-type:decimal"><li>系统内核：所有Linux发行版的内核都是Linux，例如CentOS、Ubuntu、Fedora等。内核可以与计算机硬件交互，对外提供<b>内核指令</b>，用于操作计算机硬件。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-62f223eb4f7d47e7b2a3503b5d4b9306" style="list-style-type:decimal"><li>系统应用：操作系统本身提供的应用、函数库。这些函数库是对内核指令的封装，使用更加方便。</li></ol></ul></ul><ul class="notion-list notion-list-disc notion-block-fc828cc61caf449eaf84d1b541eca230"><li>应用于计算机交互的流程如下：</li><ul class="notion-list notion-list-disc notion-block-fc828cc61caf449eaf84d1b541eca230"><ol start="1" class="notion-list notion-list-numbered notion-block-5c250eab2ad14127bc1977cc3197995f" style="list-style-type:decimal"><li>应用调用操作系统应用（函数库），实现各种功能</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-8ba52f5148114800b371bc4f41347cd3" style="list-style-type:decimal"><li>系统函数库是对内核指令集的封装，会调用内核指令</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-9d4015f065c14fd2826c46175deff00e" style="list-style-type:decimal"><li>内核指令操作计算机硬件</li></ol></ul></ul><ul class="notion-list notion-list-disc notion-block-c6b7543c2b3140a791c76318fef86cc1"><li>Ubuntu和CentOSpringBoot都是基于Linux内核，无非是系统应用不同，提供的函数库有差异：</li></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-09307dd56aa74fbba6550e491d0f0872"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1008px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2Ff5a95719-aac5-4994-a17a-22b2848ae8bc%2FUntitled.png?table=block&amp;id=09307dd5-6aa7-4fbb-a655-0e491d0f0872&amp;t=09307dd5-6aa7-4fbb-a655-0e491d0f0872&amp;width=1008&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-fe427f8030c44b2cbadcceb8cd5e3265">此时，如果将一个Ubuntu版本的MySQL应用安装到CentOS系统，MySQL在调用Ubuntu函数库时，会发现找不到或者不匹配，就会报错了：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-8989e6d2cc40490fb8d6f97a0ed0c915"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1008px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F8f414ad5-62cb-46fd-81a1-4ceaf8bf5e2f%2FUntitled.png?table=block&amp;id=8989e6d2-cc40-490f-b8d6-f97a0ed0c915&amp;t=8989e6d2-cc40-490f-b8d6-f97a0ed0c915&amp;width=1008&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><ul class="notion-list notion-list-disc notion-block-10a52bc94f064fe6b002bc2fecdcd789"><li>Docker如何解决不同系统环境的问题？</li><ul class="notion-list notion-list-disc notion-block-10a52bc94f064fe6b002bc2fecdcd789"><ol start="1" class="notion-list notion-list-numbered notion-block-69215a5647bc42638df7a8ce1adcaa46" style="list-style-type:decimal"><li>Docker将用户程序与所需要调用的系统(比如Ubuntu)函数库一起打包</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-cc9cb79edab54cbb9613b0701c6ea70e" style="list-style-type:decimal"><li>Docker运行到不同操作系统时，直接基于打包的函数库，借助于操作系统的Linux内核来运行</li></ol></ul></ul><div class="notion-text notion-block-e143493d46c44a5e800b3d4d94f57c13">如图：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2eadacda99e64adfb2351caa48bbfc5a"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1008px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F70e624a5-e8c4-4819-b197-b140e71c6ecc%2FUntitled.png?table=block&amp;id=2eadacda-99e6-4adf-b235-1caa48bbfc5a&amp;t=2eadacda-99e6-4adf-b235-1caa48bbfc5a&amp;width=1008&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-eb9f9d23b0db4ed2936750bd7f44178a" data-id="eb9f9d23b0db4ed2936750bd7f44178a"><span><div id="eb9f9d23b0db4ed2936750bd7f44178a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#eb9f9d23b0db4ed2936750bd7f44178a" title="Docker和虚拟机的区别"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Docker和虚拟机的区别</b></span></span></h3><ul class="notion-list notion-list-disc notion-block-fbff65e4dc9a4abd980d56b6d9665320"><li><span class="notion-orange"><b>虚拟机（virtual machine）</b></span>是在操作系统中<b>模拟</b>硬件设备，然后运行另一个操作系统，比如在 Windows 系统里面运行 Ubuntu 系统，这样就可以运行任意的Ubuntu应用了。</li></ul><ul class="notion-list notion-list-disc notion-block-7cd5d334191345e0885ec4c6de1d3734"><li><span class="notion-orange"><b>Docker</b></span>仅仅是封装函数库，并没有模拟完整的操作系统，如图：</li></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-6bdffa21146a4e709eea508c4abefd1e"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:960px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F3f3f9c92-802c-4831-9e40-21d919731b55%2FUntitled.png?table=block&amp;id=6bdffa21-146a-4e70-9eea-508c4abefd1e&amp;t=6bdffa21-146a-4e70-9eea-508c4abefd1e&amp;width=960&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-353011210a67452d9de1c0bb66b21ed3"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:960px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2Fa16fa7ff-d5e4-49dd-9979-e5e07d570d13%2FUntitled.png?table=block&amp;id=35301121-0a67-452d-9de1-c0bb66b21ed3&amp;t=35301121-0a67-452d-9de1-c0bb66b21ed3&amp;width=960&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-callout notion-gray_background_co notion-block-3d73eadc33c442ebbad5c471790cf9d7"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">Docker和虚拟机的差异：<ul class="notion-list notion-list-disc notion-block-8d734597db604758b9bf84803f5927f7"><li><span class="notion-orange"><b>Docker是一个系统进程</b></span>；<b>虚拟机是在操作系统中的操作系统</b></li></ul><ul class="notion-list notion-list-disc notion-block-89fd2c35ef944f948429f739e246493b"><li>Docker体积小、启动速度快、性能好；<b>虚拟机体积大、启动速度慢、性能一般</b></li></ul></div></div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-ed32aa59fae142148e1641369e174634" data-id="ed32aa59fae142148e1641369e174634"><span><div id="ed32aa59fae142148e1641369e174634" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ed32aa59fae142148e1641369e174634" title="Docker架构"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Docker架构</b></span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-ea161e58f24e442ba70507cc46e6c317" data-id="ea161e58f24e442ba70507cc46e6c317"><span><div id="ea161e58f24e442ba70507cc46e6c317" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ea161e58f24e442ba70507cc46e6c317" title="镜像和容器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>镜像和容器</b></span></span></h4><ul class="notion-list notion-list-disc notion-block-c457b5bb51724afc9d6b2d1f69bce7d4"><li>Docker中有几个重要的概念：</li><ul class="notion-list notion-list-disc notion-block-c457b5bb51724afc9d6b2d1f69bce7d4"><ol start="1" class="notion-list notion-list-numbered notion-block-42e9cb6170f84cbf934bbdb2b77812ce" style="list-style-type:decimal"><li><span class="notion-orange"><b>镜像（Image）</b></span>：Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起，称为<span class="notion-orange"><b>镜像</b></span>。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-d704033488bd43e698f8e12aae1fa143" style="list-style-type:decimal"><li><span class="notion-orange"><b>容器（Container）</b></span>：镜像中的应用程序运行后形成的进程就是<b>容器</b>，只是<b>Docker会给容器进程做隔离</b>，对外不可见。</li></ol></ul></ul><blockquote class="notion-quote notion-block-617c5db749864ec7a84996a3ef385a05"><div>一切应用最终都是代码组成，都是硬盘中的一个个的字节形成的<b>文件</b>。只有运行时，才会加载到内存，形成<b>进程</b>。</div><ul class="notion-list notion-list-disc notion-block-05dc4754d1924070acb894330c9023d1"><li>而<b>镜像</b>，就是把一个应用在硬盘上的文件、及其运行环境、部分系统函数库文件一起打包形成的文件包。<span class="notion-orange"><b>这个文件包是只读的</b></span>。</li></ul><ul class="notion-list notion-list-disc notion-block-dae217a867034874928aaf39c17b8372"><li><b>容器</b>呢，就是将这些文件中编写的程序、函数加载到内存中允许，形成进程，只不过要隔离起来。因此一个镜像可以启动多次，形成多个容器进程。</li></ul><ul class="notion-list notion-list-disc notion-block-68f5f929ad9b4900afdfdb7959ed0dac"><li>例如你下载了一个QQ，如果我们将QQ在磁盘上的运行<b>文件</b>及其运行的操作系统依赖打包，形成QQ镜像。然后你可以启动多次，双开、甚至三开QQ。</li></ul></blockquote><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-6d7f9ad14fd04b0c91d1812f9704bffb"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1008px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F2617747e-59a4-472a-a3ed-0213fb9d51e8%2FUntitled.png?table=block&amp;id=6d7f9ad1-4fd0-4b0c-91d1-812f9704bffb&amp;t=6d7f9ad1-4fd0-4b0c-91d1-812f9704bffb&amp;width=1008&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-096ba966e8c548f28220f5f0319398dc" data-id="096ba966e8c548f28220f5f0319398dc"><span><div id="096ba966e8c548f28220f5f0319398dc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#096ba966e8c548f28220f5f0319398dc" title="DockerHub"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>DockerHub</b></span></span></h4><div class="notion-text notion-block-a26183283a414002868a22247bc281d0">开源应用程序非常多，打包这些应用往往是重复的劳动。为了避免这些重复劳动，人们就会将自己打包的应用镜像，例如Redis、MySQL镜像放到网络上，共享使用，就像GitHub的代码共享一样。</div><ul class="notion-list notion-list-disc notion-block-dc50cb25aebc41b49ab866544d79e831"><li><a class="notion-link" href="https://hub.docker.com/" target="_blank" rel="noopener noreferrer">DockerHub</a>：DockerHub是一个官方的Docker镜像的托管平台。这样的平台称为<b>Docker Registry</b>。</li></ul><ul class="notion-list notion-list-disc notion-block-51fb69ba7d2142008e4d292c86b1deec"><li>国内也有类似于DockerHub 的公开服务，比如 <a class="notion-link" href="https://c.163yun.com/hub" target="_blank" rel="noopener noreferrer">网易云镜像服务</a>、<a class="notion-link" href="https://cr.console.aliyun.com/" target="_blank" rel="noopener noreferrer">阿里云镜像库</a>等。</li></ul><blockquote class="notion-quote notion-block-36854dbdb0fe4730abc9548ae4152d52"><div>我们一方面可以将自己的镜像共享到DockerHub，另一方面也可以从DockerHub拉取镜像。</div></blockquote><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-7b50bddf6f3240829e904a0744c5f881"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1008px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F1693ff77-01ad-4a50-92fa-33c3e925e178%2FUntitled.png?table=block&amp;id=7b50bddf-6f32-4082-9e90-4a0744c5f881&amp;t=7b50bddf-6f32-4082-9e90-4a0744c5f881&amp;width=1008&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-b2c52fd09d784be7b39148b06e67ce48" data-id="b2c52fd09d784be7b39148b06e67ce48"><span><div id="b2c52fd09d784be7b39148b06e67ce48" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b2c52fd09d784be7b39148b06e67ce48" title="Docker架构"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Docker架构</b></span></span></h4><div class="notion-text notion-block-bec7bcc837e54a74ad93961fd43d0011">我们要使用Docker来操作镜像、容器，就必须要安装Docker。</div><div class="notion-text notion-block-6c265e237df2405394cf6d7099b6a2aa">Docker是一个CS架构的程序，由两部分组成：</div><ul class="notion-list notion-list-disc notion-block-95f1543cc3ea467f8fbc566df5517afb"><li>服务端(server)：Docker守护进程，负责处理Docker指令，管理镜像、容器等</li></ul><ul class="notion-list notion-list-disc notion-block-dea078901f794a4fb7e3901a81ab5aa4"><li>客户端(client)：通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令。</li></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-68f0ae5888a042abaf39e03075a2c303"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1008px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F8503e639-3576-40da-a543-5336e86dc1f8%2FUntitled.png?table=block&amp;id=68f0ae58-88a0-42ab-af39-e03075a2c303&amp;t=68f0ae58-88a0-42ab-af39-e03075a2c303&amp;width=1008&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-ac7b8e15a5b348949b6eae2300fc3e43" data-id="ac7b8e15a5b348949b6eae2300fc3e43"><span><div id="ac7b8e15a5b348949b6eae2300fc3e43" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ac7b8e15a5b348949b6eae2300fc3e43" title="安装Docker"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>安装Docker</b></span></span></h3><ul class="notion-list notion-list-disc notion-block-bd3271ca62034320aef30c4622e3c21d"><li>企业部署一般都是采用Linux操作系统，而其中又数CentOS发行版占比最多，因此我们在CentOS下安装Docker。</li></ul><ul class="notion-list notion-list-disc notion-block-adfc35fe07b2482faca88fe5b038bf26"><li>Docker 分为 CE 和 EE 两大版本。CE 即社区版（免费，支持周期 7 个月），EE 即企业版，强调安全，付费使用，支持周期 24 个月。</li></ul><ul class="notion-list notion-list-disc notion-block-a8b9749358ce4d5c9c0b188520e4cc9f"><li>Docker CE 分为 <code class="notion-inline-code">stable</code> <code class="notion-inline-code">test</code> 和 <code class="notion-inline-code">nightly</code> 三个更新频道。</li></ul><ul class="notion-list notion-list-disc notion-block-66a24c042428427c9e2a42283d0fcf21"><li>官方网站上有各种环境下的 <a class="notion-link" href="https://docs.docker.com/install/" target="_blank" rel="noopener noreferrer">安装指南</a>，这里主要介绍 Docker CE 在 CentOS上的安装。</li></ul><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-a858854aa3624848ba79beeadf028104" data-id="a858854aa3624848ba79beeadf028104"><span><div id="a858854aa3624848ba79beeadf028104" class="notion-header-anchor"></div><a class="notion-hash-link" href="#a858854aa3624848ba79beeadf028104" title="卸载（可选）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">卸载（可选）</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-f6a6c31103564fdca4bbf69b45dd1887" data-id="f6a6c31103564fdca4bbf69b45dd1887"><span><div id="f6a6c31103564fdca4bbf69b45dd1887" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f6a6c31103564fdca4bbf69b45dd1887" title="安装docker"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">安装docker</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-9bb6b5a616a947fca31a1a6fe59abad2" data-id="9bb6b5a616a947fca31a1a6fe59abad2"><span><div id="9bb6b5a616a947fca31a1a6fe59abad2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9bb6b5a616a947fca31a1a6fe59abad2" title="启动docker"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">启动docker</span></span></h4><div class="notion-text notion-block-c939e437f1f24ddcab9af4f6f9fa39e3">Docker应用需要用到各种端口，逐一去修改防火墙设置。非常麻烦，因此建议大家直接关闭防火墙！</div><div class="notion-text notion-block-0b8600bc56b54319b86fdc835d92ea66">启动docker前，一定要关闭防火墙后！！</div><div class="notion-text notion-block-bd14871ef96c45caba8ca8877edd88b0">启动docker前，一定要关闭防火墙后！！</div><div class="notion-text notion-block-fa4a8dd9edf446858748c14ceb9a18ae">启动docker前，一定要关闭防火墙后！！</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-07093ad7a6c44a70afb68e056585685e" data-id="07093ad7a6c44a70afb68e056585685e"><span><div id="07093ad7a6c44a70afb68e056585685e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#07093ad7a6c44a70afb68e056585685e" title="配置镜像加速"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">配置镜像加速</span></span></h4><blockquote class="notion-quote notion-block-76703d5b917f451281ad3f301d5f4826"><div>docker官方镜像仓库网速较差，我们需要设置国内镜像服务：参考阿里云的镜像加速文档：<a class="notion-link" href="https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors" target="_blank" rel="noopener noreferrer">https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors</a></div></blockquote><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-8a4366b1730b4eb08f1b72bac083de49" data-id="8a4366b1730b4eb08f1b72bac083de49"><span><div id="8a4366b1730b4eb08f1b72bac083de49" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8a4366b1730b4eb08f1b72bac083de49" title="Docker的基本操作"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Docker的基本操作</b></span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-98b99584711346468a047054acade2c4" data-id="98b99584711346468a047054acade2c4"><span><div id="98b99584711346468a047054acade2c4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#98b99584711346468a047054acade2c4" title="镜像操作"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>镜像操作</b></span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-b44de59885da49998d37d026b7827b51" data-id="b44de59885da49998d37d026b7827b51"><span><div id="b44de59885da49998d37d026b7827b51" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b44de59885da49998d37d026b7827b51" title="镜像名称"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>镜像名称</b></span></span></h4><div class="notion-text notion-block-28141ed2c74d4dbeabce2302efd1efe3">首先来看下镜像的名称组成：</div><ul class="notion-list notion-list-disc notion-block-e3669b0232ab45c1abb97f1e46fe9e72"><li>镜名称一般分两部分组成：<code class="notion-inline-code">[repository]:[tag]</code></li></ul><ul class="notion-list notion-list-disc notion-block-8b25e30450634f4f9db3e8e2652639fb"><li>在没有指定tag时，默认是<code class="notion-inline-code">latest</code>，代表最新版本的镜像</li></ul><div class="notion-text notion-block-bed77b14d2a74292b1382a312020501d">如图：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-e0536c5d66c347fcba8885de7eef705a"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:912px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F2b463253-4e30-4771-91bc-8ed1c4a26e96%2FUntitled.png?table=block&amp;id=e0536c5d-66c3-47fc-ba88-85de7eef705a&amp;t=e0536c5d-66c3-47fc-ba88-85de7eef705a&amp;width=912&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-66c4caf344014590b24885f1d051b7fd">这里的mysql就是repository，5.7就是tag，合一起就是镜像名称，代表5.7版本的MySQL镜像。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-31922b97f4cb4ecfbe37375d9c835570" data-id="31922b97f4cb4ecfbe37375d9c835570"><span><div id="31922b97f4cb4ecfbe37375d9c835570" class="notion-header-anchor"></div><a class="notion-hash-link" href="#31922b97f4cb4ecfbe37375d9c835570" title="镜像命令"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>镜像命令</b></span></span></h4><div class="notion-text notion-block-a51c6c8deee046f083ef80e5e28d550e">常见的镜像操作命令如图：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-7310c788c8e04511aad756ed1e6e7db8"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:912px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2Feabd792e-f065-4176-a434-89051230e133%2FUntitled.png?table=block&amp;id=7310c788-c8e0-4511-aad7-56ed1e6e7db8&amp;t=7310c788-c8e0-4511-aad7-56ed1e6e7db8&amp;width=912&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-9888c67c23b34bd493f4faea92f071ca" data-id="9888c67c23b34bd493f4faea92f071ca"><span><div id="9888c67c23b34bd493f4faea92f071ca" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9888c67c23b34bd493f4faea92f071ca" title="案例1：拉取、查看镜像"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>案例1：拉取、查看镜像</b></span></span></h4><div class="notion-text notion-block-fe9be0991e454d6d81dd8a68fe40bbe8">需求：从DockerHub中拉取一个nginx镜像并查看</div><ol start="1" class="notion-list notion-list-numbered notion-block-06c694120f2b4a72815b1d525dc98d28" style="list-style-type:decimal"><li>首先去镜像仓库<a class="notion-link" href="https://hub.docker.com/" target="_blank" rel="noopener noreferrer">DockerHub</a>搜索nginx镜像。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-55e21db9f61b47188894a768c2097228" style="list-style-type:decimal"><li>根据查看到的镜像名称，拉取自己需要的镜像，通过命令：<code class="notion-inline-code">docker pull nginx</code></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-12f508a98f034f3eb93951edbc9c52c7" style="list-style-type:decimal"><li>通过命令：<code class="notion-inline-code">docker images</code> 查看拉取到的镜像</li></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-9ba3b6abee2c492ca117570f25cbdad9" data-id="9ba3b6abee2c492ca117570f25cbdad9"><span><div id="9ba3b6abee2c492ca117570f25cbdad9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9ba3b6abee2c492ca117570f25cbdad9" title="案例2：保存、导入镜像"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>案例2：保存、导入镜像</b></span></span></h4><div class="notion-text notion-block-b582b76444c143c98d15257ba796cb90">需求：利用<code class="notion-inline-code">docker save</code>将nginx镜像导出磁盘，然后再通过<code class="notion-inline-code">load</code>加载回来</div><ol start="1" class="notion-list notion-list-numbered notion-block-d6165654d012453588b59677b1fbc83c" style="list-style-type:decimal"><li>利用<code class="notion-inline-code">docker xx --help</code>命令查看<code class="notion-inline-code">docker save</code>和<code class="notion-inline-code">docker load</code>的语法</li><ol class="notion-list notion-list-numbered notion-block-d6165654d012453588b59677b1fbc83c" style="list-style-type:lower-alpha"><blockquote class="notion-quote notion-block-97b2b81be46e4745b94d30f318686d68"><div>例如，查看<code class="notion-inline-code">save</code>命令用法，可以输入命令：<code class="notion-inline-code">docker save --help</code>，可知命令格式：<code class="notion-inline-code">docker save -o [保存的目标文件名称] [镜像名称]</code></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0c9354aa66e2442fa94dcd1dedaf4005"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:720px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2Ff908a9d0-04f6-45f1-b5f7-96b2532dd70a%2FUntitled.png?table=block&amp;id=0c9354aa-66e2-442f-a94d-cd1dedaf4005&amp;t=0c9354aa-66e2-442f-a94d-cd1dedaf4005&amp;width=720&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></blockquote></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1e5609f06ab545e081fafade2cb4ca4d" style="list-style-type:decimal"><li>使用<code class="notion-inline-code">docker save</code>导出镜像到磁盘：运行命令：<code class="notion-inline-code">docker save -o nginx.tar nginx:latest</code>，</li><ol class="notion-list notion-list-numbered notion-block-1e5609f06ab545e081fafade2cb4ca4d" style="list-style-type:lower-alpha"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-a898db2009314de4837a4f61f2ea1f50"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:912px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F07a74a2f-ea29-4cde-84f8-0add108dcc1a%2FUntitled.png?table=block&amp;id=a898db20-0931-4de4-837a-4f61f2ea1f50&amp;t=a898db20-0931-4de4-837a-4f61f2ea1f50&amp;width=912&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-8cb6140950284e0789c487877935f44c" style="list-style-type:decimal"><li>使用<code class="notion-inline-code">docker load</code>加载镜像</li><ol class="notion-list notion-list-numbered notion-block-8cb6140950284e0789c487877935f44c" style="list-style-type:lower-alpha"><div class="notion-text notion-block-f07e86ee677f416e8b50cd2a78692d38">先删除本地的nginx镜像：<code class="notion-inline-code">docker </code><code class="notion-inline-code"><b>rmi</b></code><code class="notion-inline-code"> nginx:latest</code>，然后运行命令，加载本地文件：<code class="notion-inline-code">docker load -i nginx.tar</code></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-ee00b4f78eb446faaccbe7452b9c7fcf"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:960px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F2fca4102-f54f-4435-ad49-636e2dc25f46%2FUntitled.png?table=block&amp;id=ee00b4f7-8eb4-46fa-accb-e7452b9c7fcf&amp;t=ee00b4f7-8eb4-46fa-accb-e7452b9c7fcf&amp;width=960&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></ol></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-844744cf1ac24c66804bb4fe84aa7bec" data-id="844744cf1ac24c66804bb4fe84aa7bec"><span><div id="844744cf1ac24c66804bb4fe84aa7bec" class="notion-header-anchor"></div><a class="notion-hash-link" href="#844744cf1ac24c66804bb4fe84aa7bec" title="容器操作"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>容器操作</b></span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-ea2d5acc88ac4a81b6b45530a9e479e9" data-id="ea2d5acc88ac4a81b6b45530a9e479e9"><span><div id="ea2d5acc88ac4a81b6b45530a9e479e9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ea2d5acc88ac4a81b6b45530a9e479e9" title="容器相关命令"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>容器相关命令</b></span></span></h4><div class="notion-text notion-block-c4651d05986240e5ac82d2b1b8c16fc9">容器操作的命令如图：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-fb8ffdcb63474b768d6660d470bc4e94"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1008px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F014f0dda-b3b3-481a-bdad-7c90de417b5e%2FUntitled.png?table=block&amp;id=fb8ffdcb-6347-4b76-8d66-60d470bc4e94&amp;t=fb8ffdcb-6347-4b76-8d66-60d470bc4e94&amp;width=1008&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-b0d639b587d149d0b35c8bbfc4201245">容器保护三个状态：</div><ul class="notion-list notion-list-disc notion-block-e4a880aaaad147cb920e5f94c2223ed6"><li><span class="notion-orange"><b>运行</b></span>：进程正常运行</li></ul><ul class="notion-list notion-list-disc notion-block-46e5af1d0dfc4797aa1720db159b1ff6"><li><span class="notion-orange"><b>暂停</b></span>：进程暂停，CPU不再运行，并不释放内存</li></ul><ul class="notion-list notion-list-disc notion-block-06a4c09023a745b79d1984cfa0b7f358"><li><span class="notion-orange"><b>停止</b></span>：进程终止，回收进程占用的内存、CPU等资源</li></ul><div class="notion-text notion-block-b07d053eeb8142589dc0e028fab94043">其中：</div><ul class="notion-list notion-list-disc notion-block-e970f0b9d98f450fb1bcf7d7b7a5f776"><li><code class="notion-inline-code">docker run</code>：创建并运行一个容器，处于运行状态</li></ul><ul class="notion-list notion-list-disc notion-block-b39da0d68c794e2ba5bc236387f612d6"><li><code class="notion-inline-code">docker pause</code>：让一个运行的容器暂停</li></ul><ul class="notion-list notion-list-disc notion-block-6e717c86c62f4a1fa0c123564da45144"><li><code class="notion-inline-code">docker unpause</code>：让一个容器从暂停状态恢复运行</li></ul><ul class="notion-list notion-list-disc notion-block-35514d68a58c4f2dba662ead9f0dc35b"><li><code class="notion-inline-code">docker stop</code>：停止一个运行的容器</li></ul><ul class="notion-list notion-list-disc notion-block-25414add36ac48f3abe0f2d1e11dfe64"><li><code class="notion-inline-code">docker start</code>：让一个停止的容器再次运行</li></ul><ul class="notion-list notion-list-disc notion-block-ab2ab4172ce64420b6e2388bbdfef203"><li><code class="notion-inline-code">docker restart</code>：重启容器</li></ul><ul class="notion-list notion-list-disc notion-block-26352474c1334598a162952afd804717"><li><code class="notion-inline-code">docker rm</code>：删除一个容器</li></ul><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-d9c3389128d24c2f8724c6a5ad15f83e" data-id="d9c3389128d24c2f8724c6a5ad15f83e"><span><div id="d9c3389128d24c2f8724c6a5ad15f83e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d9c3389128d24c2f8724c6a5ad15f83e" title="案例：创建并运行一个容器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>案例：创建并运行一个容器</b></span></span></h4><div class="notion-text notion-block-00f485aacf604246a42f2246b69ca5e1">创建并运行nginx容器的命令：</div><blockquote class="notion-quote notion-block-203b7085fc7345d9bf3099ac2f42c282"><div>命令解读：</div><ul class="notion-list notion-list-disc notion-block-13de13cde378405b8ed6ff3173f9cca1"><li><code class="notion-inline-code">docker run</code>：创建并运行一个容器</li></ul><ul class="notion-list notion-list-disc notion-block-1eccd4f19e8f48eb890b10a9364d2eba"><li><code class="notion-inline-code">—name</code>：给容器起一个名字，比如叫做mynginx</li></ul><ul class="notion-list notion-list-disc notion-block-ffaa6ae116d84d3abf18db2f4ab291d2"><li><code class="notion-inline-code">-p</code>：将宿主机端口与容器端口映射，冒号左侧是宿主机端口，右侧是容器端口</li></ul><ul class="notion-list notion-list-disc notion-block-d68741ef558f4bb0b63092175407ad3b"><li><code class="notion-inline-code">-d</code>：后台运行容器</li></ul><ul class="notion-list notion-list-disc notion-block-8cb692fa65924a64b384f70a6a12bb7d"><li><code class="notion-inline-code">--restart</code>：随<code class="notion-inline-code">docker</code>的启动而启动</li></ul><ul class="notion-list notion-list-disc notion-block-c2d8cca994f44245b4579b5a2afebcd2"><li><code class="notion-inline-code">nginx</code>：镜像名称，例如nginx</li></ul></blockquote><blockquote class="notion-quote notion-block-a488fdafe6754a3e92852457a3d2cf4c"><div>这里的<code class="notion-inline-code">-p</code>参数，是将容器端口映射到宿主机端口。默认情况下，容器是隔离环境，我们直接访问宿主机的80端口，肯定访问不到容器中的nginx。现在，将容器的80与宿主机的80关联起来，当我们访问宿主机的80端口时，就会被映射到容器的80，这样就能访问到nginx了。</div></blockquote><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-461bd3914eb146ffa7c3d49ff75650be"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1008px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2Faf7e9d4d-4b90-4894-8f9e-49136e54b3a8%2FUntitled.png?table=block&amp;id=461bd391-4eb1-46ff-a7c3-d49ff75650be&amp;t=461bd391-4eb1-46ff-a7c3-d49ff75650be&amp;width=1008&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-d76684b25a0348509d57795dbe49e66e" data-id="d76684b25a0348509d57795dbe49e66e"><span><div id="d76684b25a0348509d57795dbe49e66e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d76684b25a0348509d57795dbe49e66e" title="案例：进入容器，修改文件"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>案例：进入容器，修改文件</b></span></span></h4><ul class="notion-list notion-list-disc notion-block-eeef46bfe504413888debff57aef3ba3"><li><b>需求</b>：进入Nginx容器，修改HTML文件内容，添加“Frank欢迎您”</li></ul><ul class="notion-list notion-list-disc notion-block-bfc5b317502a484cb9260b1bf2a88e64"><li><b>提示</b>：进入容器要用到<code class="notion-inline-code">docker exec</code>命令。</li></ul><ul class="notion-list notion-list-disc notion-block-538647c6ff84482a93c0a8ae5520a504"><li><b>步骤</b>：</li></ul><ol start="1" class="notion-list notion-list-numbered notion-block-480110b1198746879cba0c4754b70483" style="list-style-type:decimal"><li>进入容器。进入我们刚刚创建的nginx容器的命令为：<code class="notion-inline-code">docker exec -it mynginx bash</code></li><ol class="notion-list notion-list-numbered notion-block-480110b1198746879cba0c4754b70483" style="list-style-type:lower-alpha"><blockquote class="notion-quote notion-block-453cfab20ebc439a864cbb60d38bdfe3"><div>命令解读：</div><ul class="notion-list notion-list-disc notion-block-4b126d7d8033438f93429f9d8acc186f"><li><code class="notion-inline-code">docker exec</code>：进入容器内部，执行一个命令</li></ul><ul class="notion-list notion-list-disc notion-block-e0100c624e364f8699cc267880b3431e"><li><code class="notion-inline-code">-it</code>: 给当前进入的容器创建一个标准输入、输出终端，允许我们与容器交互</li></ul><ul class="notion-list notion-list-disc notion-block-b28e90f404cc43d398d061c787c56d22"><li><code class="notion-inline-code">mynginx</code>：要进入的容器的名称</li></ul><ul class="notion-list notion-list-disc notion-block-2a28f89c739e4d09833d17c4bf75f2fc"><li><code class="notion-inline-code">bash</code>：进入容器后执行的命令，bash是一个linux终端交互命令</li></ul></blockquote></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-8a9fb173a0de40c2a58d446db56bd8c7" style="list-style-type:decimal"><li>进入nginx的HTML所在目录 /usr/share/nginx/html</li><ol class="notion-list notion-list-numbered notion-block-8a9fb173a0de40c2a58d446db56bd8c7" style="list-style-type:lower-alpha"><div class="notion-text notion-block-51ade2e7c9414349b8b720f3f170b529">容器内部会模拟一个独立的Linux文件系统，看起来如同一个linux服务器一样：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-24da6e528cfd4b7baaa2811e461ae8b8"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:960px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F784d0ade-6bcb-406d-ae9f-513b90f18097%2FUntitled.png?table=block&amp;id=24da6e52-8cfd-4b7b-aaa2-811e461ae8b8&amp;t=24da6e52-8cfd-4b7b-aaa2-811e461ae8b8&amp;width=960&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-3e4d1b5f3732473dba9e069cd67c1210">nginx的环境、配置、运行文件全部都在这个文件系统中，包括我们要修改的html文件。</div><div class="notion-text notion-block-4a769c26186b4a9a8b16d1e6e9a1ebba">查看DockerHub网站中的nginx页面，可以知道nginx的html目录位置在<code class="notion-inline-code">/usr/share/nginx/html</code></div><div class="notion-text notion-block-7620cd104d2c455894c136fcd68f990b">我们执行命令，进入该目录：</div><div class="notion-text notion-block-ba0611d668b743fc946f24f1f090b0a8">查看目录下文件：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-c8a231db763748079928021e91b3ebf3"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1200px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F9c1b23b5-f84e-4a67-8350-da2502419b29%2FUntitled.png?table=block&amp;id=c8a231db-7637-4807-9928-021e91b3ebf3&amp;t=c8a231db-7637-4807-9928-021e91b3ebf3&amp;width=1200&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-439dcd4828a446779410a823650b53aa" style="list-style-type:decimal"><li>修改index.html的内容</li><ol class="notion-list notion-list-numbered notion-block-439dcd4828a446779410a823650b53aa" style="list-style-type:lower-alpha"><div class="notion-text notion-block-a7bb9ba2992f414b85e12036aabb94b6">容器内没有vi命令，无法直接修改，我们用下面的命令来修改：</div><div class="notion-text notion-block-a99ce93ce68b422aaa04f59bb5e7cf48">在浏览器访问自己的虚拟机地址，例如我的是：http://192.168.10.100:80，即可看到结果。</div></ol></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-22b449d7f94b43918c612a3ce7954e32" data-id="22b449d7f94b43918c612a3ce7954e32"><span><div id="22b449d7f94b43918c612a3ce7954e32" class="notion-header-anchor"></div><a class="notion-hash-link" href="#22b449d7f94b43918c612a3ce7954e32" title="其他命令"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">其他命令</span></span></h4><ul class="notion-list notion-list-disc notion-block-4147be291b9048afbd54bb4087c6568d"><li>查看容器日志的命令：</li><ul class="notion-list notion-list-disc notion-block-4147be291b9048afbd54bb4087c6568d"><ol start="1" class="notion-list notion-list-numbered notion-block-547092f1ac8e4e34bedbfe4a5b15ac0d" style="list-style-type:decimal"><li><code class="notion-inline-code">docker logs</code></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-89d296a54af64282954fea7f612d7055" style="list-style-type:decimal"><li>添加 <code class="notion-inline-code">-f</code> 参数可以持续查看日志</li></ol></ul></ul><ul class="notion-list notion-list-disc notion-block-9c5acf01cd164e5f8b82ed86f1261406"><li>查看容器状态：</li><ul class="notion-list notion-list-disc notion-block-9c5acf01cd164e5f8b82ed86f1261406"><ol start="1" class="notion-list notion-list-numbered notion-block-ec1683b924c3485f9f0bffa6d150aa43" style="list-style-type:decimal"><li><code class="notion-inline-code">docker ps</code>：只能查看目前正在运行的容器</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-fdebef0455a84966a08c6692824698ba" style="list-style-type:decimal"><li><code class="notion-inline-code">docker ps -a</code>：查看所有容器，包括已经停止的</li></ol></ul></ul><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-945ab6b79b3e4d0f91d4cd2824c7aed2" data-id="945ab6b79b3e4d0f91d4cd2824c7aed2"><span><div id="945ab6b79b3e4d0f91d4cd2824c7aed2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#945ab6b79b3e4d0f91d4cd2824c7aed2" title="数据卷（容器数据管理）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>数据卷（容器数据管理）</b></span></span></h3><blockquote class="notion-quote notion-block-33db2deae6764eee88d8e8cb9f2e5d5f"><div>在之前的nginx案例中，修改nginx的html页面时，需要进入nginx内部。并且因为没有编辑器，修改文件也很麻烦。这就是<span class="notion-orange"><b>因为容器与数据（容器内文件）耦合</b></span>带来的后果。要解决这个问题，必须将数据与容器解耦，这就要用到数据卷了。</div></blockquote><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0e2c81787ee444958888443abe5b7481"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1056px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2Fc633644c-bf7a-43a1-b8a0-1f0e97e0793e%2FUntitled.png?table=block&amp;id=0e2c8178-7ee4-4495-8888-443abe5b7481&amp;t=0e2c8178-7ee4-4495-8888-443abe5b7481&amp;width=1056&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-35aa5a12eea241959a12bc51e5637be7" data-id="35aa5a12eea241959a12bc51e5637be7"><span><div id="35aa5a12eea241959a12bc51e5637be7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#35aa5a12eea241959a12bc51e5637be7" title="什么是数据卷"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>什么是数据卷</b></span></span></h4><ul class="notion-list notion-list-disc notion-block-e5939638260942d4a2a3ad000c6ce536"><li><span class="notion-orange"><b>数据卷（volume）</b></span>是一个虚拟目录，指向宿主机文件系统中的某个目录。</li></ul><ul class="notion-list notion-list-disc notion-block-13f209a8638149c48214c30094e0b8db"><li>一旦完成数据卷挂载，对容器的一切操作都会作用在数据卷对应的宿主机目录了。</li></ul><ul class="notion-list notion-list-disc notion-block-dd3b9125f1de4c9a8dc1275d9cb2050e"><li>这样，我们操作<span class="notion-default"><b>宿主机</b></span>的<code class="notion-inline-code">/var/lib/docker/volumes/html</code>目录，就等于操作<b>容器</b>内的<code class="notion-inline-code">/usr/share/nginx/html</code>目录了。</li></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-7b5507e1ffe3497fbc21df334972f082"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1056px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F06e059c4-4829-49e8-9b3d-36ad4752823f%2FUntitled.png?table=block&amp;id=7b5507e1-ffe3-497f-bc21-df334972f082&amp;t=7b5507e1-ffe3-497f-bc21-df334972f082&amp;width=1056&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-b01a4c6137bd4f91b837d59ebe60cb72" data-id="b01a4c6137bd4f91b837d59ebe60cb72"><span><div id="b01a4c6137bd4f91b837d59ebe60cb72" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b01a4c6137bd4f91b837d59ebe60cb72" title="数据集操作命令"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>数据集操作命令</b></span></span></h4><ul class="notion-list notion-list-disc notion-block-528a35086a854c04a9ab94c3aaec6800"><li>数据卷操作的基本语法如下：</li><ul class="notion-list notion-list-disc notion-block-528a35086a854c04a9ab94c3aaec6800"></ul></ul><ul class="notion-list notion-list-disc notion-block-73cc872d08b540fd89b70188dfc7682d"><li><code class="notion-inline-code">docker volume</code>命令是数据卷操作，根据命令后跟随的<code class="notion-inline-code">command</code>来确定下一步的操作：</li><ul class="notion-list notion-list-disc notion-block-73cc872d08b540fd89b70188dfc7682d"><ol start="1" class="notion-list notion-list-numbered notion-block-dfcbd1a76397443b97d10466fac59ffa" style="list-style-type:decimal"><li><code class="notion-inline-code">create</code>：创建一个volume</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-e5bdf2cf563b4fc8a181708a68b0b54c" style="list-style-type:decimal"><li><code class="notion-inline-code">inspect</code>：显示一个或多个volume的信息</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-926344b179d949c39c8e3a0bb0566853" style="list-style-type:decimal"><li><code class="notion-inline-code">ls</code>：列出所有的volume</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-4317ca987a1f494d96810cba357ac502" style="list-style-type:decimal"><li><code class="notion-inline-code">prune</code>：删除未使用的volume</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-05d48f3cc4a541cda01610a6cc47e522" style="list-style-type:decimal"><li><code class="notion-inline-code">rm</code>：删除一个或多个指定的volume</li></ol></ul></ul><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-770bf6364f2943bfb49ec9a8a1e9060e" data-id="770bf6364f2943bfb49ec9a8a1e9060e"><span><div id="770bf6364f2943bfb49ec9a8a1e9060e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#770bf6364f2943bfb49ec9a8a1e9060e" title="创建和查看数据卷"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>创建和查看数据卷</b></span></span></h4><div class="notion-text notion-block-8eff4834df7049089dd6dd5ac25c2cf0"><b>需求</b>：创建一个数据卷，并查看数据卷在宿主机的目录位置</div><ol start="1" class="notion-list notion-list-numbered notion-block-1de8308fea8c41a8aaf70bc636ab76d5" style="list-style-type:decimal"><li>创建数据卷：<code class="notion-inline-code">docker volume create html</code></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-d37732d0b6f54038959af5aeb2297609" style="list-style-type:decimal"><li>查看所有数据：<code class="notion-inline-code">docker volume ls</code></li><ol class="notion-list notion-list-numbered notion-block-d37732d0b6f54038959af5aeb2297609" style="list-style-type:lower-alpha"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3a88392e653444f380f00f59d46e6509"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F907e5f5e-dc95-401c-9c43-eed593138b7d%2FUntitled.png?table=block&amp;id=3a88392e-6534-44f3-80f0-0f59d46e6509&amp;t=3a88392e-6534-44f3-80f0-0f59d46e6509&amp;width=681.9375&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-98f1d2258782433da1d71984a931cb04" style="list-style-type:decimal"><li>查看数据卷详细信息卷：<code class="notion-inline-code">docker volume inspect html</code></li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-c844361cddbc47edb1b92e5419ad1c6c"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:532.7625122070312px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2Fdc761349-685d-4c58-aa55-c1d1279e8ebf%2FUntitled.png?table=block&amp;id=c844361c-ddbc-47ed-b1b9-2e5419ad1c6c&amp;t=c844361c-ddbc-47ed-b1b9-2e5419ad1c6c&amp;width=532.7625122070312&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-f614bb2a65354e33b1c8b79747f0a504">可以看到，我们创建的html这个数据卷关联的宿主机目录为<code class="notion-inline-code">/var/lib/docker/volumes/html/_data</code>目录。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-4555b4f1d5d2460fa0cae34d0dcb46ff" data-id="4555b4f1d5d2460fa0cae34d0dcb46ff"><span><div id="4555b4f1d5d2460fa0cae34d0dcb46ff" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4555b4f1d5d2460fa0cae34d0dcb46ff" title="挂载数据卷"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>挂载数据卷</b></span></span></h4><div class="notion-text notion-block-c494cab6dd8046d999b73b167c002f86">我们在创建容器时，可以通过 <code class="notion-inline-code">-v</code> 参数来挂载一个数据卷到某个容器内目录，命令格式如下：</div><blockquote class="notion-quote notion-block-cdbb0e2ce4384aeea206f3e343a5aa1c"><div>这里的-v就是挂载数据卷的命令</div><ul class="notion-list notion-list-disc notion-block-181815e6d0e14516a177434878552a46"><li><code class="notion-inline-code">-v html:/root/htm</code> ：把<code class="notion-inline-code">html</code>数据卷挂载到<b>容器</b>内的<code class="notion-inline-code">/root/html</code>这个目录中</li></ul></blockquote><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-79b99c56daf041c69ea915f0a4ef3254" data-id="79b99c56daf041c69ea915f0a4ef3254"><span><div id="79b99c56daf041c69ea915f0a4ef3254" class="notion-header-anchor"></div><a class="notion-hash-link" href="#79b99c56daf041c69ea915f0a4ef3254" title="案例：给nginx挂载数据卷"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>案例：给nginx挂载数据卷</b></span></span></h4><ul class="notion-list notion-list-disc notion-block-36ba8ffa63ef45938abb2a31593c707e"><li><b>需求</b>：创建一个nginx容器，修改容器内的html目录内的index.html内容</li></ul><ul class="notion-list notion-list-disc notion-block-8f3f4fd3b2774716ac59e139f86597cd"><li><b>分析</b>：上个案例中，我们进入nginx容器内部，已经知道nginx的html目录所在位置/usr/share/nginx/html ，我们需要把这个目录挂载到html这个数据卷上，方便操作其中的内容。</li></ul><ul class="notion-list notion-list-disc notion-block-acb7f8befe964638a394779e0eb8ed09"><li><b>提示</b>：运行容器时使用 -v 参数挂载数据卷</li></ul><ul class="notion-list notion-list-disc notion-block-81453e9d0d5c416d95104a4441f2dc74"><li><b>步骤</b>：</li><ul class="notion-list notion-list-disc notion-block-81453e9d0d5c416d95104a4441f2dc74"><ol start="1" class="notion-list notion-list-numbered notion-block-15b844054ac3445b9677b9b16df04515" style="list-style-type:decimal"><li>创建容器并挂载数据卷到容器内的HTML目录</li><ol class="notion-list notion-list-numbered notion-block-15b844054ac3445b9677b9b16df04515" style="list-style-type:lower-alpha"></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-fecd1ca913324a7480c7fb0df82cfb3c" style="list-style-type:decimal"><li>进入html数据卷所在位置，并修改HTML内容</li><ol class="notion-list notion-list-numbered notion-block-fecd1ca913324a7480c7fb0df82cfb3c" style="list-style-type:lower-alpha"></ol></ol></ul></ul><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-6c585d069ccc4f008c05708ca9bf60f6" data-id="6c585d069ccc4f008c05708ca9bf60f6"><span><div id="6c585d069ccc4f008c05708ca9bf60f6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6c585d069ccc4f008c05708ca9bf60f6" title="案例：给MySQL挂载本地目录"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>案例：给MySQL挂载本地目录</b></span></span></h4><ul class="notion-list notion-list-disc notion-block-4b9745b6bd474f3cb98e8fbce45fdaf3"><li>容器不仅仅可以挂载数据卷，也可以直接挂载到宿主机目录上。关联关系如下：</li><ul class="notion-list notion-list-disc notion-block-4b9745b6bd474f3cb98e8fbce45fdaf3"><ol start="1" class="notion-list notion-list-numbered notion-block-38fa46e8f1f54eb787c13d8faa05241c" style="list-style-type:decimal"><li><b>带数据卷模式</b>：宿主机目录→数据卷→容器内目录</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-50dd5d03502648ca8ba301d9aec88356" style="list-style-type:decimal"><li><b>直接挂载模式</b>：宿主机目录→容器内目录</li></ol></ul></ul><ul class="notion-list notion-list-disc notion-block-758ac4ae21db4167889e51711d3fdc1d"><li>如图：</li></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0bed4ea9a4d54dadb83f60e3caf6738d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1008px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2Fa6fb9feb-319c-4115-9409-c3c83a203e4f%2FUntitled.png?table=block&amp;id=0bed4ea9-a4d5-4dad-b83f-60e3caf6738d&amp;t=0bed4ea9-a4d5-4dad-b83f-60e3caf6738d&amp;width=1008&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><ul class="notion-list notion-list-disc notion-block-2cfd023d97da43d9b5338f5cb49880e3"><li><b>语法</b>：</li><ul class="notion-list notion-list-disc notion-block-2cfd023d97da43d9b5338f5cb49880e3"><blockquote class="notion-quote notion-block-fb9b39ed12b448af887d1d52f6a0eff7"><div>目录挂载与数据卷挂载的语法是类似的：</div></blockquote><ol start="1" class="notion-list notion-list-numbered notion-block-6397e10f0f164377aa29c286f8e17d36" style="list-style-type:decimal"><li><code class="notion-inline-code">-v [宿主机目录]:[容器内目录]</code></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-57f9e402712a461cb594c40c3f091b7e" style="list-style-type:decimal"><li><code class="notion-inline-code">-v [宿主机文件]:[容器内文件]</code></li></ol></ul></ul><ul class="notion-list notion-list-disc notion-block-b2cb14f4150d449dbb5a29e37660fdfa"><li><b>需求</b>：创建并运行一个MySQL容器，将宿主机目录直接挂载到容器。实现思路如下：</li><ul class="notion-list notion-list-disc notion-block-b2cb14f4150d449dbb5a29e37660fdfa"><ol start="1" class="notion-list notion-list-numbered notion-block-0312b4d83ea84b99b0d71ae795deadec" style="list-style-type:decimal"><li>将mysql.tar文件上传到虚拟机，通过load命令加载为镜像</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-960045076046403aac165ba64802e238" style="list-style-type:decimal"><li>创建目录/tmp/mysql/data</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-ec4bbfc9efc7491cbc989ad9c0e56021" style="list-style-type:decimal"><li>创建目录/tmp/mysql/conf，将hmy.cnf文件上传到/tmp/mysql/conf</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-bf40ebe67d31451ab7c6861b89cd2669" style="list-style-type:decimal"><li>去DockerHub查阅资料，创建并运行MySQL容器，要求：</li><ol class="notion-list notion-list-numbered notion-block-bf40ebe67d31451ab7c6861b89cd2669" style="list-style-type:lower-alpha"><div class="notion-text notion-block-afb526160f3c47c6b2cd6be6f70f62dd">① 挂载<code class="notion-inline-code">/tmp/mysql/data</code>到mysql容器内数据存储目录</div><div class="notion-text notion-block-07c736a4442c406cb236cf15ce28a116">② 挂载<code class="notion-inline-code">/tmp/mysql/conf/hmy.cnf</code>到mysql容器的配置文件</div><div class="notion-text notion-block-4547972fcd7f45e6bf06dc122b441909">③ 设置MySQL密码</div></ol></ol></ul></ul><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-fe6834adfec448f18be0e133d426c2fa" data-id="fe6834adfec448f18be0e133d426c2fa"><span><div id="fe6834adfec448f18be0e133d426c2fa" class="notion-header-anchor"></div><a class="notion-hash-link" href="#fe6834adfec448f18be0e133d426c2fa" title="Dockerfile自定义镜像"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Dockerfile自定义镜像</b></span></span></h2><blockquote class="notion-quote notion-block-83ab178cce574cbaa1da28373faedc1e"><div>常见的镜像在DockerHub就能找到，但是我们自己写的项目就必须自己构建镜像了。而要自定义镜像，就必须先了解镜像的结构才行。</div></blockquote><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-586d01bfc8e046b3abe52493200f6e60" data-id="586d01bfc8e046b3abe52493200f6e60"><span><div id="586d01bfc8e046b3abe52493200f6e60" class="notion-header-anchor"></div><a class="notion-hash-link" href="#586d01bfc8e046b3abe52493200f6e60" title="镜像结构"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>镜像结构</b></span></span></h3><div class="notion-text notion-block-e40797c8d5624f309a428980312e093d"><span class="notion-orange"><b>镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。</b></span></div><div class="notion-text notion-block-edd9e70fbb754325ab336ebef72c6730">我们以MySQL为例，来看看镜像的组成结构：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-019378c2bae448a3b15329b60b8fef9d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1056px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F22483dd9-147b-4bf2-9381-5064d03680e1%2FUntitled.png?table=block&amp;id=019378c2-bae4-48a3-b153-29b60b8fef9d&amp;t=019378c2-bae4-48a3-b153-29b60b8fef9d&amp;width=1056&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-473ab9a088904ab1a2bc54b4a71c725f">简单来说，<span class="notion-orange"><b>镜像就是在系统函数库、运行环境基础上，添加应用程序文件、配置文件、依赖文件等组合，然后编写好启动脚本打包在一起形成的文件。</b></span></div><div class="notion-text notion-block-e376777c6c53448a8c6b79a6d1277cf5">我们要构建镜像，其实就是实现上述打包的过程。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-fd71a2b8e5514f7984a3c075d209fa79" data-id="fd71a2b8e5514f7984a3c075d209fa79"><span><div id="fd71a2b8e5514f7984a3c075d209fa79" class="notion-header-anchor"></div><a class="notion-hash-link" href="#fd71a2b8e5514f7984a3c075d209fa79" title="Dockerfile语法"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Dockerfile语法</b></span></span></h3><div class="notion-text notion-block-c74c5b88401d43a9808880066d7cfa33">构建自定义的镜像时，并不需要一个个文件去拷贝，打包。我们只需要告诉Docker，我们的镜像的组成，需要哪些BaseImage、需要拷贝什么文件、需要安装什么依赖、启动脚本是什么，将来Docker会帮助我们构建镜像。而<span class="notion-orange"><b>描述上述信息的文件就是Dockerfile文件</b></span>。</div><div class="notion-text notion-block-2f3514b3eacb4e23a6c1976f90ec6e2a"><span class="notion-orange"><b>Dockerfile就是一个文本文件，其中包含一个个的指令（Instruction），用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。</b></span></div><div class="notion-text notion-block-ea75ea28cf624ef5afaea3f2a712eb1b">更新详细语法说明，请参考官网文档： <a class="notion-link" href="https://docs.docker.com/engine/reference/builder" target="_blank" rel="noopener noreferrer">https://docs.docker.com/engine/reference/builder</a></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-9c9292a8ddd54cf1aea6d50ab51e1bb5"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1104px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F4f6d9b29-0056-41fd-a544-d798b56eebaa%2FUntitled.png?table=block&amp;id=9c9292a8-ddd5-4cf1-aea6-d50ab51e1bb5&amp;t=9c9292a8-ddd5-4cf1-aea6-d50ab51e1bb5&amp;width=1104&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-007dcf17847c4568910fa5d928551ecd" data-id="007dcf17847c4568910fa5d928551ecd"><span><div id="007dcf17847c4568910fa5d928551ecd" class="notion-header-anchor"></div><a class="notion-hash-link" href="#007dcf17847c4568910fa5d928551ecd" title="构建Java项目"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>构建Java项目</b></span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-298e660d8f7c4f1d9e4a9d2883c8d235" data-id="298e660d8f7c4f1d9e4a9d2883c8d235"><span><div id="298e660d8f7c4f1d9e4a9d2883c8d235" class="notion-header-anchor"></div><a class="notion-hash-link" href="#298e660d8f7c4f1d9e4a9d2883c8d235" title="基于Ubuntu构建Java项目"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>基于Ubuntu构建Java项目</b></span></span></h4><div class="notion-text notion-block-37f8758fd46d44c88edefcf15bba8180">需求：基于Ubuntu镜像构建一个新镜像，运行一个java项目</div><ul class="notion-list notion-list-disc notion-block-d9536e4e6d59474bb3ec3f367b0b4a9d"><li>步骤1：新建一个空文件夹docker-demo</li></ul><ul class="notion-list notion-list-disc notion-block-ca1464aeb6f14151bcecb9ebfac900b3"><li>步骤2：拷贝docker-demo.jar文件到docker-demo这个目录</li></ul><ul class="notion-list notion-list-disc notion-block-7ef1f38395954c47b9891257aa382940"><li>步骤3：拷贝jdk8.tar.gz文件到docker-demo这个目录</li></ul><ul class="notion-list notion-list-disc notion-block-b0a106486fc34e79bd6f15dc3d29dfb0"><li>步骤4：拷贝Dockerfile到docker-demo这个目录，其中Dockerfile的内容如下：</li><ul class="notion-list notion-list-disc notion-block-b0a106486fc34e79bd6f15dc3d29dfb0"></ul></ul><ul class="notion-list notion-list-disc notion-block-7048d5c77db84138b2358e1b0506bb96"><li>步骤5：进入docker-demo</li><ul class="notion-list notion-list-disc notion-block-7048d5c77db84138b2358e1b0506bb96"><div class="notion-text notion-block-b8b96ba5b2884d9cae67e058315d5d55">将准备好的docker-demo上传到虚拟机任意目录，然后进入docker-demo目录下</div></ul></ul><ul class="notion-list notion-list-disc notion-block-365842c4889c4caa9ff32fd52d71ccac"><li>步骤6：运行命令：</li><ul class="notion-list notion-list-disc notion-block-365842c4889c4caa9ff32fd52d71ccac"></ul></ul><div class="notion-text notion-block-0211ba9ad6f041b68b89e023c25eed5a">最后访问<a class="notion-link" href="http://192.168.10.100:8090/hello/count" target="_blank" rel="noopener noreferrer">http://192.168.10.100:8090/hello/count</a>，其中的ip改成你的虚拟机ip</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0a7eef71d3ba406bab3486249d9873f5"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:532.7125244140625px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F98bb90b4-cd52-4977-894b-16cd9e3a6e84%2FUntitled.png?table=block&amp;id=0a7eef71-d3ba-406b-ab34-86249d9873f5&amp;t=0a7eef71-d3ba-406b-ab34-86249d9873f5&amp;width=532.7125244140625&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-f5e48a6f3fc549f797c895b8072749f8" data-id="f5e48a6f3fc549f797c895b8072749f8"><span><div id="f5e48a6f3fc549f797c895b8072749f8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f5e48a6f3fc549f797c895b8072749f8" title="基于java8构建Java项目"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>基于java8构建Java项目</b></span></span></h4><blockquote class="notion-quote notion-block-2234863bde704b5d9814d7e23cfb112e"><div>虽然我们可以基于Ubuntu基础镜像，添加任意自己需要的安装包，构建镜像，但是却比较麻烦。所以大多数情况下，我们都可以在一些安装了部分软件的基础镜像上做改造。
例如，构建java项目的镜像，可以在已经准备了JDK的基础镜像基础上构建。</div></blockquote><ul class="notion-list notion-list-disc notion-block-ee5451fbfd314118949111c086ce5f16"><li>需求：基于<code class="notion-inline-code">java:8-alpine</code>镜像，将一个Java项目构建为镜像</li></ul><ul class="notion-list notion-list-disc notion-block-6b9a45be34004d16aab3d86eff6d4aa8"><li>实现思路如下：</li><ul class="notion-list notion-list-disc notion-block-6b9a45be34004d16aab3d86eff6d4aa8"><ol start="1" class="notion-list notion-list-numbered notion-block-d0366505214a4d19b48eca0904099c58" style="list-style-type:decimal"><li>主要是将Dockerfile改成下面的内容</li><ol class="notion-list notion-list-numbered notion-block-d0366505214a4d19b48eca0904099c58" style="list-style-type:lower-alpha"></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-346d12a96d7142e1bc762a036915e5ce" style="list-style-type:decimal"><li>使用<code class="notion-inline-code">docker build</code>命令<span class="notion-orange"><b>构建镜像</b></span>：<code class="notion-inline-code">docker build -t javaweb:2.0 .</code></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-5d3d7cec80a94befa154ec4064d74c05" style="list-style-type:decimal"><li>使用<code class="notion-inline-code">docker run</code>创建容器并运行：<code class="notion-inline-code">docker run —name web2 -p 8090:8090 -d javaweb:2.0</code></li></ol></ul></ul><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-c80fff9f7ab448828404910671f075bd" data-id="c80fff9f7ab448828404910671f075bd"><span><div id="c80fff9f7ab448828404910671f075bd" class="notion-header-anchor"></div><a class="notion-hash-link" href="#c80fff9f7ab448828404910671f075bd" title="小结"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>小结</b></span></span></h3><div class="notion-callout notion-gray_background_co notion-block-ac56755c54ba49dd86bca51ffb8435a3"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">小结：<ol start="1" class="notion-list notion-list-numbered notion-block-c2760ad627de41e783ca66df2a17133e" style="list-style-type:decimal"><li>Dockerfile的本质是一个文件，通过指令描述镜像的构建过程</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-7261a984d65e434db890c8709f502b5f" style="list-style-type:decimal"><li><b>Dockerfile的第一行必须是FROM，从一个基础镜像来构建</b></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-f34737a0c51841719046c7e3efb34b56" style="list-style-type:decimal"><li>基础镜像可以是基本操作系统，如Ubuntu。也可以是其他人制作好的镜像，例如：<code class="notion-inline-code">java:8-alpine</code></li></ol></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1bf52d37281749a2a8d1e73b509b0a58" data-id="1bf52d37281749a2a8d1e73b509b0a58"><span><div id="1bf52d37281749a2a8d1e73b509b0a58" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1bf52d37281749a2a8d1e73b509b0a58" title="Docker-Compose"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Docker-Compose</b></span></span></h2><blockquote class="notion-quote notion-block-c75b69c0163f436396449b1e668b177c"><div>Docker Compose可以基于Compose文件帮我们快速的部署分布式应用，而无需手动一个个创建和运行容器！</div></blockquote><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-80d91ac4a0024c6a90f0fc323b113b7f" data-id="80d91ac4a0024c6a90f0fc323b113b7f"><span><div id="80d91ac4a0024c6a90f0fc323b113b7f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#80d91ac4a0024c6a90f0fc323b113b7f" title="初识DockerCompose"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>初识DockerCompose</b></span></span></h3><div class="notion-text notion-block-f07c69af77124ff6b02fadbe196546da">Compose文件是一个文本文件，通过指令定义集群中的每个容器如何运行。格式如下：</div><div class="notion-text notion-block-1c016f36884f484a87139f117ad9e3a2">上面的Compose文件就描述一个项目，其中包含两个容器：</div><ul class="notion-list notion-list-disc notion-block-4a6cff99a0634d7ebe7c047db2ef8e71"><li>mysql：一个基于<code class="notion-inline-code">mysql:5.7.25</code>镜像构建的容器，并且挂载了两个目录</li></ul><ul class="notion-list notion-list-disc notion-block-a8c826d064f8466caccc24609689d27f"><li>web：一个基于<code class="notion-inline-code">docker build</code>临时构建的镜像容器，映射端口时8090</li></ul><div class="notion-text notion-block-8b2ecc3cb12a43e193805f44449a137f">DockerCompose的详细语法参考官网：<a class="notion-link" href="https://docs.docker.com/compose/compose-file/" target="_blank" rel="noopener noreferrer">https://docs.docker.com/compose/compose-file/</a></div><blockquote class="notion-quote notion-block-5a2ee507a4384a2db4047ff2da07d570"><div>其实DockerCompose文件可以看做是将多个<code class="notion-inline-code">docker run</code>命令写到一个文件，只是语法稍有差异。</div></blockquote><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-94215cbd3eb3436babbfc386314033e8" data-id="94215cbd3eb3436babbfc386314033e8"><span><div id="94215cbd3eb3436babbfc386314033e8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#94215cbd3eb3436babbfc386314033e8" title="安装DockerCompose"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>安装DockerCompose</b></span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-f4713196d18d45dbacb871b244037363" data-id="f4713196d18d45dbacb871b244037363"><span><div id="f4713196d18d45dbacb871b244037363" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f4713196d18d45dbacb871b244037363" title="下载"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">下载</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ee8b3ee8fe94efc9647591583c4b592" data-id="1ee8b3ee8fe94efc9647591583c4b592"><span><div id="1ee8b3ee8fe94efc9647591583c4b592" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ee8b3ee8fe94efc9647591583c4b592" title="修改执行权限"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">修改执行权限</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-96305a6e799548b9b1faa65bf923be47" data-id="96305a6e799548b9b1faa65bf923be47"><span><div id="96305a6e799548b9b1faa65bf923be47" class="notion-header-anchor"></div><a class="notion-hash-link" href="#96305a6e799548b9b1faa65bf923be47" title="查看docker-compose版本"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">查看docker-compose版本</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-ac35a4bd78bc4049befb9f1b5f5fd041" data-id="ac35a4bd78bc4049befb9f1b5f5fd041"><span><div id="ac35a4bd78bc4049befb9f1b5f5fd041" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ac35a4bd78bc4049befb9f1b5f5fd041" title="Base自动补全命令"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Base自动补全命令</span></span></h4><blockquote class="notion-quote notion-block-68f035a316f54271aa0c4ae64f9ab3f7"><div>如果这里出现错误，需要修改自己的hosts文件：</div></blockquote><div class="notion-blank notion-block-18a0334ca96a4c828e294b274f2cb8a6"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-0f63b81347c44e7eae32e522e6fc52c4" data-id="0f63b81347c44e7eae32e522e6fc52c4"><span><div id="0f63b81347c44e7eae32e522e6fc52c4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0f63b81347c44e7eae32e522e6fc52c4" title="部署微服务集群"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>部署微服务集群</b></span></span></h3><div class="notion-text notion-block-e3f46b027e564b5985d4816f53e09f36"><b>需求</b>：将之前学习的cloud-demo微服务集群利用DockerCompose部署</div><div class="notion-text notion-block-d94ebfca4d9942a3be2a6d3a90233009"><b>实现思路</b>：</div><div class="notion-text notion-block-cef5054b6d9e4c37b68c93eceffd06c1">① 查看cloud-demo文件夹，里面已经编写好了docker-compose文件</div><div class="notion-text notion-block-06d09d811b704ced8d9c68c7f53fb10a">② 修改自己的cloud-demo项目，将数据库、nacos地址都命名为docker-compose中的服务名</div><div class="notion-text notion-block-29497293a8914960a210db16901f41bd">③ 使用maven打包工具，将项目中的每个微服务都打包为app.jar</div><div class="notion-text notion-block-60d58565405e41c4b86336daaa51601e">④ 将打包好的app.jar拷贝到cloud-demo中的每一个对应的子目录中</div><div class="notion-text notion-block-75edf931c21f4539b82b3db930f74795">⑤ 将 cloud-demo上传至虚拟机，利用 <code class="notion-inline-code">docker-compose up -d</code> 来部署</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-58695b53810a4193835ec9e56073c609" data-id="58695b53810a4193835ec9e56073c609"><span><div id="58695b53810a4193835ec9e56073c609" class="notion-header-anchor"></div><a class="notion-hash-link" href="#58695b53810a4193835ec9e56073c609" title="compose文件"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>compose文件</b></span></span></h4><div class="notion-text notion-block-c084fc678b3e415f83f16486354ae8ed">查看cloud-demo文件夹，里面已经编写好了docker-compose文件，而且每个微服务都准备了一个独立的目录：</div><div class="notion-text notion-block-937a6b05a5084b61a5b88dfecca301d4">内容如下：</div><div class="notion-text notion-block-a0d35332752b482f8df9040f4fb5c749">可以看到，其中包含5个service服务：</div><ul class="notion-list notion-list-disc notion-block-e5e3ab6353e44822a86972741b41272d"><li><code class="notion-inline-code">nacos</code>：作为注册中心和配置中心</li><ul class="notion-list notion-list-disc notion-block-e5e3ab6353e44822a86972741b41272d"><li><code class="notion-inline-code">image: nacos/nacos-server</code>： 基于nacos/nacos-server镜像构建</li><li><code class="notion-inline-code">environment</code>：环境变量</li><ul class="notion-list notion-list-disc notion-block-0c1f8993233b4f618109bebfedc76028"><li><code class="notion-inline-code">MODE: standalone</code>：单点模式启动</li></ul><li><code class="notion-inline-code">ports</code>：端口映射，这里暴露了8848端口</li></ul></ul><ul class="notion-list notion-list-disc notion-block-929ada40e55243d093ec79b13773f24a"><li><code class="notion-inline-code">mysql</code>：数据库</li><ul class="notion-list notion-list-disc notion-block-929ada40e55243d093ec79b13773f24a"><li><code class="notion-inline-code">image: mysql:5.7.25</code>：镜像版本是mysql:5.7.25</li><li><code class="notion-inline-code">environment</code>：环境变量</li><ul class="notion-list notion-list-disc notion-block-0bd0947e66e040768e59d29b09be8f2d"><li><code class="notion-inline-code">MYSQL_ROOT_PASSWORD: ******</code>：设置数据库root账户的密码为******</li></ul><li><code class="notion-inline-code">volumes</code>：数据卷挂载，这里挂载了mysql的data、conf目录，其中有我提前准备好的数据</li></ul></ul><ul class="notion-list notion-list-disc notion-block-18b5121c3b404932a36c597d9abb70a9"><li><code class="notion-inline-code">userservice</code>、<code class="notion-inline-code">orderservice</code>、<code class="notion-inline-code">gateway</code>：都是基于Dockerfile临时构建的</li></ul><div class="notion-text notion-block-7b96fabc3cb44f229ad67c8fdfa6bb4a">内容如下：</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-f290c0ad83954e7c956c45c4173a3250" data-id="f290c0ad83954e7c956c45c4173a3250"><span><div id="f290c0ad83954e7c956c45c4173a3250" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f290c0ad83954e7c956c45c4173a3250" title="修改微服务配置"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>修改微服务配置</b></span></span></h4><div class="notion-text notion-block-337ad6d424a744919dd86b8ef1033b42">因为微服务将来要部署为docker容器，而<span class="notion-orange"><b>容器之间互联不是通过IP地址，而是通过容器名</b></span>。这里我们将order-service、user-service、gateway服务的mysql、nacos地址都修改为基于容器名的访问。如下所示：</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-fd3fe3cb8ecf44f38602f3581b1f2606" data-id="fd3fe3cb8ecf44f38602f3581b1f2606"><span><div id="fd3fe3cb8ecf44f38602f3581b1f2606" class="notion-header-anchor"></div><a class="notion-hash-link" href="#fd3fe3cb8ecf44f38602f3581b1f2606" title="打包"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>打包</b></span></span></h4><div class="notion-text notion-block-bf6c9998b1284f078665c7102526eb3f">接下来需要将我们的每个微服务都打包。因为之前查看到Dockerfile中的jar包名称都是app.jar，因此我们的每个微服务都需要用这个名称。</div><div class="notion-text notion-block-e7b7e19494f7417c86866a5721b7cbcf">可以通过修改pom.xml中的打包名称来实现，每个微服务都需要修改：</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-aac8050351954054a14eaefbc6a9f4ec" data-id="aac8050351954054a14eaefbc6a9f4ec"><span><div id="aac8050351954054a14eaefbc6a9f4ec" class="notion-header-anchor"></div><a class="notion-hash-link" href="#aac8050351954054a14eaefbc6a9f4ec" title="部署"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>部署</b></span></span></h4><div class="notion-text notion-block-cc6c1d8b77004e81891c7cd237bf9f95">最后，我们需要将文件整个cloud-demo文件夹上传到虚拟机中，使用DockerCompose部署。</div><div class="notion-text notion-block-2c3761af9ade4bc1bcb2ce6d6377a095">进入cloud-demo目录，然后运行下面的命令：</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-b8150014298f4d36ac0b160f613dcd8c" data-id="b8150014298f4d36ac0b160f613dcd8c"><span><div id="b8150014298f4d36ac0b160f613dcd8c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b8150014298f4d36ac0b160f613dcd8c" title="Docker镜像仓库"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Docker镜像仓库</b></span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-22a7d829acb34ebe9e46c14871e22ff0" data-id="22a7d829acb34ebe9e46c14871e22ff0"><span><div id="22a7d829acb34ebe9e46c14871e22ff0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#22a7d829acb34ebe9e46c14871e22ff0" title="搭建私有镜像仓库"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>搭建私有镜像仓库</b></span></span></h3><ul class="notion-list notion-list-disc notion-block-9609d8f052394cb5bcaba1827e7ef6ab"><li>搭建镜像仓库可以基于Docker官方提供的DockerRegistry来实现。</li></ul><ul class="notion-list notion-list-disc notion-block-e01a91fadbdc4368876a6ec655de857b"><li>官网地址：<a class="notion-link" href="https://hub.docker.com/_/registry" target="_blank" rel="noopener noreferrer">https://hub.docker.com/_/registry</a></li></ul><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-395a5875d1194ddcbad8cb6374a20a8c" data-id="395a5875d1194ddcbad8cb6374a20a8c"><span><div id="395a5875d1194ddcbad8cb6374a20a8c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#395a5875d1194ddcbad8cb6374a20a8c" title="简化版镜像仓库"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>简化版镜像仓库</b></span></span></h4><div class="notion-text notion-block-e28c3567531343be8fbef7c509f90ba7">Docker官方的Docker Registry是一个基础版本的Docker镜像仓库，具备仓库管理的完整功能，但是没有图形化界面。</div><div class="notion-text notion-block-04fb722c838147f093e96a554c59af7b">搭建方式比较简单，命令如下：</div><div class="notion-text notion-block-e0d491317fcf4c259b50c254d36ae96f">命令中挂载了一个数据卷registry-data到容器内的/var/lib/registry 目录，这是私有镜像库存放数据的目录。</div><div class="notion-text notion-block-e36c69505569452cb6544528dfd0afde">访问<a class="notion-link" href="http://192.168.10.100:5000/v2/_catalog" target="_blank" rel="noopener noreferrer">http://YourIp:5000/v2/_catalog</a>可以查看当前私有镜像服务中包含的镜像</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-4d160c29486d45d59f2190f0554243a7" data-id="4d160c29486d45d59f2190f0554243a7"><span><div id="4d160c29486d45d59f2190f0554243a7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4d160c29486d45d59f2190f0554243a7" title="带有图形化界面版本"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">带有图形化界面版本</span></span></h4><div class="notion-text notion-block-f51465dcee074a4198b9545ca3fc683b">使用DockerCompose部署带有图象界面的DockerRegistry，命令如下：</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-0f717a3888ed41c8989d5cbdae5ce745" data-id="0f717a3888ed41c8989d5cbdae5ce745"><span><div id="0f717a3888ed41c8989d5cbdae5ce745" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0f717a3888ed41c8989d5cbdae5ce745" title="配置Docker信任地址"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>配置Docker信任地址</b></span></span></h4><div class="notion-text notion-block-3a9582c8a1b04a26a8973275cf8bb673">我们的私服采用的是http协议，默认不被Docker信任，所以需要做一个配置：</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-275adf07f9f14f6ab3d259b824086be1" data-id="275adf07f9f14f6ab3d259b824086be1"><span><div id="275adf07f9f14f6ab3d259b824086be1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#275adf07f9f14f6ab3d259b824086be1" title="推送、拉取镜像"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>推送、拉取镜像</b></span></span></h3><div class="notion-text notion-block-82db206014ed4ab688c117ebb8d41c34">推送镜像到私有镜像服务必须先<code class="notion-inline-code">tag</code>，步骤如下：</div><ol start="1" class="notion-list notion-list-numbered notion-block-b194ad6ed4a34d6a897318bc5c2abcf4" style="list-style-type:decimal"><li>重新tag本地镜像，名称前缀为私有仓库的地址：192.168.10.100:8080/</li><ol class="notion-list notion-list-numbered notion-block-b194ad6ed4a34d6a897318bc5c2abcf4" style="list-style-type:lower-alpha"></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-32b76b859f05491f99d0ca865ee664f4" style="list-style-type:decimal"><li>推送镜像</li><ol class="notion-list notion-list-numbered notion-block-32b76b859f05491f99d0ca865ee664f4" style="list-style-type:lower-alpha"></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-e1de604599dd4475aef609eede79a18a" style="list-style-type:decimal"><li>拉取镜像</li><ol class="notion-list notion-list-numbered notion-block-e1de604599dd4475aef609eede79a18a" style="list-style-type:lower-alpha"></ol></ol><div class="notion-text notion-block-02e9da5b7d75475ea6f66522dd2205bd">命令中挂载了一个数据卷<code class="notion-inline-code">registry-data</code>到容器内的<code class="notion-inline-code">/var/lib/registry</code>目录，这是私有镜像库存放数据的目录。</div><div class="notion-text notion-block-b17ea8ee9e224bdbaf9883df310bbe90">访问<a class="notion-link" href="http://192.168.10.100:5000/v2/_catalog" target="_blank" rel="noopener noreferrer">http://192.168.10.100:5000/v2/_catalog </a>可以查看当前私有镜像服务中包含的镜像。</div><div class="notion-blank notion-block-f677df2317e64c418ccf4b027ebb4a5a"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[如何优雅地记录操作日志？]]></title>
            <link>https://ifrank.qzz.io/article/operatelog</link>
            <guid>https://ifrank.qzz.io/article/operatelog</guid>
            <pubDate>Sat, 20 Jul 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[本文主要介绍如何“优雅”地记录操作日志。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-f907cefff92643e8af1ca14491cccb49"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-table-of-contents notion-gray notion-block-f55f244cfe444233a576b5d96b3500ae"><a href="#1215de1764594aeb922f531b804156eb" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">背景</span></a><a href="#5784f1d7dbb9427081224d5fb53ee590" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">AOP+注解生成动态日志</span></a><a href="#1f7bac1c36b14e358eed0815a673f5eb" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">参考</span></a></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1215de1764594aeb922f531b804156eb" data-id="1215de1764594aeb922f531b804156eb"><span><div id="1215de1764594aeb922f531b804156eb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1215de1764594aeb922f531b804156eb" title="背景"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">背景</span></span></h2><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-5784f1d7dbb9427081224d5fb53ee590" data-id="5784f1d7dbb9427081224d5fb53ee590"><span><div id="5784f1d7dbb9427081224d5fb53ee590" class="notion-header-anchor"></div><a class="notion-hash-link" href="#5784f1d7dbb9427081224d5fb53ee590" title="AOP+注解生成动态日志"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">AOP+注解生成动态日志</span></span></h2><div class="notion-blank notion-block-68443dcfb30d4e27b419d2cdab4995b5"> </div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7bac1c36b14e358eed0815a673f5eb" data-id="1f7bac1c36b14e358eed0815a673f5eb"><span><div id="1f7bac1c36b14e358eed0815a673f5eb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7bac1c36b14e358eed0815a673f5eb" title="参考"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">参考</span></span></h2><ul class="notion-list notion-list-disc notion-block-c553219b0b384ac9b4ca5a04cf99ee74"><li></li></ul><ul class="notion-list notion-list-disc notion-block-b2679dbff0ab4be890c7bce0f225c235"><li><a class="notion-link" href="https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/expressions.html" target="_blank" rel="noopener noreferrer">Spring Expression Language (SpEL)</a></li></ul><ul class="notion-list notion-list-disc notion-block-449b9f0fa1374ffe984778b29054ec57"><li><a class="notion-link" href="https://mp.weixin.qq.com/s/JC51S_bI02npm4CE5NEEow" target="_blank" rel="noopener noreferrer">美团技术-如何优雅地记录操作日志？</a></li></ul><div class="notion-blank notion-block-56e12edd933a437b848e311725bed0f6"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spring Security OAuth2(1)]]></title>
            <link>https://ifrank.qzz.io/article/4a85c2eb-0d73-4bef-8459-fda246b8bb42</link>
            <guid>https://ifrank.qzz.io/article/4a85c2eb-0d73-4bef-8459-fda246b8bb42</guid>
            <pubDate>Thu, 25 Jul 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-4a85c2eb0d734bef8459fda246b8bb42"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-table-of-contents notion-gray notion-block-7547b5ba6db1439090792834f7ccb415"><a href="#dd688aaa9000450088a32af72cf0f2a5" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">前言</span></a><a href="#93b23b560b6f484ea242aa8542f2c807" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">概述</span></a><a href="#6e391cb012844800b3808ecbc6006513" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">项目准备</span></a><a href="#3b9758f2c14f48029a369b9ae1ccc9e0" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">Maven依赖</span></a><a href="#17dfea510f63494baed004c5a5d48560" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">创建资源</span></a><a href="#373d733063284785b08f517f073c1fce" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">配置资源服务器和授权服务器</span></a></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-dd688aaa9000450088a32af72cf0f2a5" data-id="dd688aaa9000450088a32af72cf0f2a5"><span><div id="dd688aaa9000450088a32af72cf0f2a5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#dd688aaa9000450088a32af72cf0f2a5" title="前言"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">前言</span></span></h2><div class="notion-text notion-block-f141b8ced27141f0b85edae8a294e57a">接口对接的场景在实际应用中很常见，A厂家有一套HTTP接口需要提供给B厂家使用，由于是外网环境，所以需要有一套安全机制保障，这个时候oauth2就可以作为一个方案。</div><div class="notion-text notion-block-58e35a3a1888432c9f7dabefc879eb09">关于oauth2，其实是一个授权规范。如果你还不清楚授权服务器，资源服务器，认证授权等基础概念，可以移步<a class="notion-link" href="http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html" target="_blank" rel="noopener noreferrer">理解OAuth 2.0 - 阮一峰</a>，这是一篇对于oauth2很好的科普文章。</div><div class="notion-text notion-block-ea12d119193142c0a680cc575fe16103">需要对spring security有一定的配置使用经验，用户认证这一块，spring security oauth2建立在spring security的基础之上。java中的安全框架如shrio，已经有<a class="notion-link" href="http://jinnianshilongnian.iteye.com/blog/2018936" target="_blank" rel="noopener noreferrer">跟我学shiro - 开涛</a>，非常成体系地，深入浅出地讲解了apache的这个开源安全框架。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-93b23b560b6f484ea242aa8542f2c807" data-id="93b23b560b6f484ea242aa8542f2c807"><span><div id="93b23b560b6f484ea242aa8542f2c807" class="notion-header-anchor"></div><a class="notion-hash-link" href="#93b23b560b6f484ea242aa8542f2c807" title="概述"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">概述</span></span></h2><div class="notion-text notion-block-df1322910c8346cd84620f778cf8a3f6">使用oauth2可以分为简易的分为三个步骤：</div><ul class="notion-list notion-list-disc notion-block-6945dd7d372942baaf62d9af11901b1a"><li>配置资源服务器</li></ul><ul class="notion-list notion-list-disc notion-block-953e8e070ab84414815b33b0cc003238"><li>配置认证服务器</li></ul><ul class="notion-list notion-list-disc notion-block-eb553312f1924b21901a0264f5b989cf"><li>配置spring security</li></ul><blockquote class="notion-quote notion-block-4ff00ce7565e4a50a38a9979c4dc4c17"><div>前两点是oauth2的主体内容，但前面我已经描述过了，spring security oauth2是建立在spring security基础之上的，所以有一些体系是公用的。</div></blockquote><div class="notion-text notion-block-85ee95c8c43842acb80d17e12c22c374">oauth2根据使用场景不同，分成了4种模式：</div><ul class="notion-list notion-list-disc notion-block-fbb6f11a9c4e408ea48fdb3deffacbbd"><li>授权码模式（authorization code）</li></ul><ul class="notion-list notion-list-disc notion-block-5d5c97ceffbd4b4d8c51344b8e5e8444"><li>简化模式（implicit）</li></ul><ul class="notion-list notion-list-disc notion-block-e109fad49e91453f98c5533b29014582"><li>密码模式（resource owner password credentials）</li></ul><ul class="notion-list notion-list-disc notion-block-3d80832bb6a44108afc4d28604cbe09e"><li>客户端模式（client credentials）</li></ul><div class="notion-text notion-block-7e0686c273a44039abe1b1713db58fb1">本文重点讲解接口对接中常使用的密码模式（以下简称password模式）和客户端模式（以下简称client模式）。授权码模式使用到了回调地址，是最为复杂的方式，通常网站中经常出现的微博，qq第三方登录，都会采用这个形式。简化模式不常用。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-6e391cb012844800b3808ecbc6006513" data-id="6e391cb012844800b3808ecbc6006513"><span><div id="6e391cb012844800b3808ecbc6006513" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6e391cb012844800b3808ecbc6006513" title="项目准备"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">项目准备</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3b9758f2c14f48029a369b9ae1ccc9e0" data-id="3b9758f2c14f48029a369b9ae1ccc9e0"><span><div id="3b9758f2c14f48029a369b9ae1ccc9e0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3b9758f2c14f48029a369b9ae1ccc9e0" title="Maven依赖"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Maven依赖</span></span></h3><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-17dfea510f63494baed004c5a5d48560" data-id="17dfea510f63494baed004c5a5d48560"><span><div id="17dfea510f63494baed004c5a5d48560" class="notion-header-anchor"></div><a class="notion-hash-link" href="#17dfea510f63494baed004c5a5d48560" title="创建资源"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">创建资源</span></span></h3><div class="notion-text notion-block-c0a117a6ba724923a3e72902392ea8a8">暴露一个商品查询接口（后续不做安全限制）和一个订单查询接口（后续添加访问控制）</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-373d733063284785b08f517f073c1fce" data-id="373d733063284785b08f517f073c1fce"><span><div id="373d733063284785b08f517f073c1fce" class="notion-header-anchor"></div><a class="notion-hash-link" href="#373d733063284785b08f517f073c1fce" title="配置资源服务器和授权服务器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>配置资源服务器和授权服务器</b></span></span></h3><div class="notion-blank notion-block-164b2968de4942fc978e1e325a6c780f"> </div><div class="notion-blank notion-block-b31e7b3b125e4db49d2d5f959621e8ba"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[从Seq2Seq到Attention]]></title>
            <link>https://ifrank.qzz.io/article/from-Seq2Seq-to-Attention</link>
            <guid>https://ifrank.qzz.io/article/from-Seq2Seq-to-Attention</guid>
            <pubDate>Mon, 20 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[本文先介绍基于 RNN 的 Seq2Seq：编码器将输入序列压缩成固定长度的 context 向量，解码器再逐步生成输出。其主要瓶颈在长序列场景：固定向量容量有限易丢信息，且 RNN 串行计算带来长距离依赖与遗忘。为此引入 Attention：编码器保留所有时间步隐状态，解码器在每一步计算权重并加权求和得到动态 context，从而更精准地关注关键信息并提升效果。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-6115ea170bbe8241bfb2015589186835"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-table-of-contents notion-gray notion-block-3c65ea170bbe808fab5de8af9de71c6b"><a href="#3c65ea170bbe80fa9fe0ecd4dd5872b7" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">从 Seq2Seq 到 Attention</span></a><a href="#3c65ea170bbe80fe9a05ef5b349ad9dc" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">前言</span></a><a href="#3c65ea170bbe80f2ba4ff6df5f4ff5c6" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">什么是 Seq2Seq 模型？</span></a><a href="#3c65ea170bbe80638125cdcc00fde0cd" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">基于 RNN 的 Seq2Seq 模型如何处理文本/长文本序列？</span></a><a href="#3c65ea170bbe80f89a09c8faf1ac7c1f" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">Seq2Seq 模型处理长文本序列时遇到了什么问题？有什么优缺点？</span></a><a href="#3c65ea170bbe8095b6e0ffafe5bf6c2a" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">基于 RNN 的 Seq2Seq 模型如何结合 Attention 来改善模型效果？</span></a><a href="#3c65ea170bbe80bf9debc5b0b7ab89e3" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:24px">参考文献</span></a></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-3c65ea170bbe80fa9fe0ecd4dd5872b7" data-id="3c65ea170bbe80fa9fe0ecd4dd5872b7"><span><div id="3c65ea170bbe80fa9fe0ecd4dd5872b7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3c65ea170bbe80fa9fe0ecd4dd5872b7" title="从 Seq2Seq 到 Attention"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">从 Seq2Seq 到 Attention</span></span></h2><div class="notion-blank notion-block-3c65ea170bbe8087a912d69b9f9b6abe"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3c65ea170bbe80fe9a05ef5b349ad9dc" data-id="3c65ea170bbe80fe9a05ef5b349ad9dc"><span><div id="3c65ea170bbe80fe9a05ef5b349ad9dc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3c65ea170bbe80fe9a05ef5b349ad9dc" title="前言"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">前言</span></span></h3><div class="notion-text notion-block-3c65ea170bbe80d49833f173877a3a0e">自然语言处理（NLP）或是大语言模型（LLM）的底层原理是 Attention（注意力机制），它不仅是如今如日中天的 Transformer 架构的灵魂核心，更是开启这轮 AI 狂飙时代的超级引擎。</div><div class="notion-text notion-block-3c65ea170bbe80878f14df4b1c2d015d">其实，技术的演进从来都不是凭空捏造的。要想真正吃透 Attention，必须先回到大模型爆发的“前夜”，去看看上一代霸主——传统 Seq2Seq 模型到底遇到了什么无法跨越的“致命瓶颈”。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3c65ea170bbe80f2ba4ff6df5f4ff5c6" data-id="3c65ea170bbe80f2ba4ff6df5f4ff5c6"><span><div id="3c65ea170bbe80f2ba4ff6df5f4ff5c6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3c65ea170bbe80f2ba4ff6df5f4ff5c6" title="什么是 Seq2Seq 模型？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">什么是 Seq2Seq 模型？</span></span></h3><div class="notion-text notion-block-3c65ea170bbe80ba913ffaa37bcecb45">Seq2Seq 是一种常见的 NLP 模型结构，全称是：sequence to sequence，翻译为“序列到序列”。顾名思义：从一个文本序列得到一个新的文本序列。这类任务的特点是：输入序列和输出序列的长度通常是不对等的。</div><div class="notion-text notion-block-3c65ea170bbe8003b6dcf52338250e98">Seq2Seq 模型架构由编码器（Encoder）和解码器（Decoder）两部分组成：</div><ol start="1" class="notion-list notion-list-numbered notion-block-3c65ea170bbe80dba4d8fc699c04deae" style="list-style-type:decimal"><li>编码器（Encoder）：负责把输入的文本序列（比如“我爱机器学习”）一个个读进去，通过循环神经网络（RNN）不断更新隐状态（hidden state），最终将整句话的语义压缩成一个固定长度的向量。这个向量我们称之为上下文向量（Context Vector）。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-3c65ea170bbe806b9d85d725c2a223d2" style="list-style-type:decimal"><li>解码器（Decoder）：拿到这个 context 向量后，像挤牙膏一样，一步步把目标文本（比如 “I love machine learning”）吐出来。</li></ol><div class="notion-text notion-block-3c65ea170bbe807ca687f99d5372969c">典型的任务有：机器翻译任务，文本摘要任务。</div><div class="notion-callout notion-gray_background_co notion-block-3c65ea170bbe80458ca9c235aa05208e"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text"><ol start="1" class="notion-list notion-list-numbered notion-block-3c65ea170bbe805aa964e62ee3fdee3b" style="list-style-type:decimal"><li>Seq2Seq 模型中的 Encoder和 Decoder 一般采用的是循环神经网络 RNN（Transformer 模型还没出现的过去时代）。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-3c65ea170bbe800ea9a1f39c2d08e1b1" style="list-style-type:decimal"><li><b>context 的数组长度是基于 Encoder RNN 的隐藏层神经元数量的</b>，在实际应用中，context 向量的长度是自定义的，比如可能是 256，512 或者 1024。</li></ol></div></div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3c65ea170bbe80638125cdcc00fde0cd" data-id="3c65ea170bbe80638125cdcc00fde0cd"><span><div id="3c65ea170bbe80638125cdcc00fde0cd" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3c65ea170bbe80638125cdcc00fde0cd" title="基于 RNN 的 Seq2Seq 模型如何处理文本/长文本序列？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">基于 RNN 的 Seq2Seq 模型如何处理文本/长文本序列？</span></span></h3><div class="notion-text notion-block-3c65ea170bbe8001bac7da1474f0f5e9">可以参考 <a class="notion-link" href="https://jalammar.github.io/visualizing-neural-machine-translation-mechanics-of-Seq2Seq-models-with-attention/" target="_blank" rel="noopener noreferrer">jalammar</a> 制作的动画对 Seq2Seq 处理文本/长文本序列有一个清晰的认识。</div><figure class="notion-asset-wrapper notion-asset-wrapper-video notion-block-3c65ea170bbe80839808e354cd744333"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1152px;max-width:100%;flex-direction:column;height:320px"><video playsinline="" controls="" preload="metadata" src="https://file.notion.so/f/f/c8c4cfcb-e2dc-41ce-ac48-d1a968389e65/88457a58-1814-41f3-b55a-88f7162eaf20/Seq2seq.mp4?table=block&amp;id=3c65ea17-0bbe-8083-9808-e354cd744333&amp;spaceId=c8c4cfcb-e2dc-41ce-ac48-d1a968389e65&amp;expirationTimestamp=1787781600000&amp;signature=85FFixoHjCuf6H2Xc5cpg_S5iSPaZI9tjhib8mJb3o0" title="video"></video></div></figure><div class="notion-text notion-block-3c65ea170bbe80dbb2bbe33f321e7bfd">我们用数学语言来更精确地还原这个过程。以机器翻译为例，假设序列输入是一个句子，这个句子由<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span>个单词组成：<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span></div><ol start="1" class="notion-list notion-list-numbered notion-block-3c65ea170bbe80d4880fdae9b4da4911" style="list-style-type:decimal"><li><b>词向量映射</b>：首先，RNN 将句子中的每一个单词通过一个嵌入层（Word Embedding）映射成为一个向量得到一个向量序列：<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span>。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-3c65ea170bbe8035bbb7e28715033f20" style="list-style-type:decimal"><li><b>编码器隐状态更新</b>：在时间步 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span>， Encoder RNN 的内部计算公式为：</li><ol class="notion-list notion-list-numbered notion-block-3c65ea170bbe8035bbb7e28715033f20" style="list-style-type:lower-alpha"><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><blockquote class="notion-quote notion-block-3c65ea170bbe8073a3f3d6373123ad2f"><div>这里，<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 是上一个时间步的隐状态，它像一个滚雪球一样，携带了从 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 到 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 的全部历史信息。<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 是 RNN 在时间步<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span>的输入单词<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span>经过映射得到的向量。</div></blockquote></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-3c65ea170bbe80abb39ff4c4a67b1e6a" style="list-style-type:decimal"><li><b>传递 context 向量</b>：当 Encoder 读完最后一个词 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 时，它输出的最后一个隐状态 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 就成为了代表整句话语义的 context 向量：</li><ol class="notion-list notion-list-numbered notion-block-3c65ea170bbe80abb39ff4c4a67b1e6a" style="list-style-type:lower-alpha"><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span></ol></ol><ol start="4" class="notion-list notion-list-numbered notion-block-3c65ea170bbe80c996e3c2f5468984d8" style="list-style-type:decimal"><li><b>解码器生成序列</b>：Decoder 接收到 context 后，将其作为初始隐状态 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span>，然后开始逐字预测：</li><ol class="notion-list notion-list-numbered notion-block-3c65ea170bbe80c996e3c2f5468984d8" style="list-style-type:lower-alpha"><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><blockquote class="notion-quote notion-block-3c65ea170bbe80b39ab4cc5558c34598"><div>其中 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 是解码器上一步输出的单词，<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 是解码器当前的隐状态。</div></blockquote></ol></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3c65ea170bbe80f89a09c8faf1ac7c1f" data-id="3c65ea170bbe80f89a09c8faf1ac7c1f"><span><div id="3c65ea170bbe80f89a09c8faf1ac7c1f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3c65ea170bbe80f89a09c8faf1ac7c1f" title="Seq2Seq 模型处理长文本序列时遇到了什么问题？有什么优缺点？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Seq2Seq 模型处理长文本序列时遇到了什么问题？有什么优缺点？</span></span></h3><div class="notion-text notion-block-3c65ea170bbe80438559d73fa7685a18">经典 Seq2Seq 看似完美，但在工程实践中却遇到了极大的挑战。它的致命缺点可以总结为一句话：成也 context，败也 context。</div><div class="notion-text notion-block-3c65ea170bbe80aa8624d27ee29b3ac3">打个比方：这就好比让你去读一本 500 页的长篇小说（输入长文本），读完之后不准你翻书，只允许你在脑子里提取出 100 个字 的故事大纲（固定长度的 context 向量）。然后，让你根据这 100 字的大纲，把整本书一字不漏地翻译成英文（解码器输出）。</div><div class="notion-text notion-block-3c65ea170bbe80a69debfb478343a52f">这显然是不可能的任务！</div><div class="notion-text notion-block-3c65ea170bbe80489024cc305c9691d6">在实际应用中，这种架构存在两个无法调和的矛盾：</div><div class="notion-text notion-block-3c65ea170bbe805386c7da38a6eda772">基于 RNN 的 Seq2Seq 模型中 <b>Encoder 把所有信息都编码到了一个context向量中</b>，便是这类模型的瓶颈。</div><ol start="1" class="notion-list notion-list-numbered notion-block-3c65ea170bbe80e1bb38eeb78e8a5408" style="list-style-type:decimal"><li><b>信息量与表达能力的矛盾</b>：单只固定维度的向量（比如维度是 512），其所能容纳的信息熵是极其有限的，很难包含所有文本序列的信息。强行将一段成百上千字的文本压缩进如此小的空间，必然会导致严重的信息丢失。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-3c65ea170bbe8057904ddb489b0a82e7" style="list-style-type:decimal"><li><b>梯度消失与记忆遗忘</b>：由于 RNN 是串行递归计算的，即使使用了 LSTM 或 GRU，在处理到第 500 个单词时，很难再包含1-499个单词中的所有信息了。这就是经典的<b>长距离依赖问题</b>（Long-range Dependency）。</li></ol><div class="notion-text notion-block-3c65ea170bbe80869644dd45edf13856">为了打破这个窒息的信息瓶颈，2014 年， Dzmitry Bahdanau 等人提出了一项开创性的技术——注意力机制（ Attention Mechanism）。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3c65ea170bbe8095b6e0ffafe5bf6c2a" data-id="3c65ea170bbe8095b6e0ffafe5bf6c2a"><span><div id="3c65ea170bbe8095b6e0ffafe5bf6c2a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3c65ea170bbe8095b6e0ffafe5bf6c2a" title="基于 RNN 的 Seq2Seq 模型如何结合 Attention 来改善模型效果？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">基于 RNN 的 Seq2Seq 模型如何结合 Attention 来改善模型效果？</span></span></h3><div class="notion-text notion-block-3c65ea170bbe80fd954af559d9703538">既然问题出在“<b>试图用一个固定向量代表整句话</b>”上，那我们索性不再强行压缩！</div><div class="notion-text notion-block-3c65ea170bbe806eb2cfe29f6327f3d5">Attention 的核心思想非常朴素：</div><ol start="1" class="notion-list notion-list-numbered notion-block-3c65ea170bbe80a7b0d1c59f9f2f4861" style="list-style-type:decimal"><li>Encoder 在处理输入时，把所有时间步的隐状态 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 全部保留并发送给 Decoder ，而不仅仅是最后一个隐状态 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span>。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-3c65ea170bbe8028b8c4e71f2b6932f3" style="list-style-type:decimal"><li>当 Decoder 在产生第 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 个输出单词时，它会动态地去计算当前输出状态与输入端所有隐状态的关联度（即注意力分配分数）。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-3c65ea170bbe80629eb3cb0afa8f2d76" style="list-style-type:decimal"><li>接着，根据这些分数对所有隐状态进行加权求和，融合成一个专属于当前时间步的动态 context 向量 <span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span>。</li></ol><div class="notion-text notion-block-3c65ea170bbe80418070ebdc1ac6e9e9">Attention 机制与经典的 Seq2Seq 模型主要有两点不同：</div><ul class="notion-list notion-list-disc notion-block-3c65ea170bbe80d99416e2a6efc46d64"><li>首先，编码器会把更多的数据传递给解码器。编码器把所有时间步的 hidden state（隐藏层状态）传递给解码器，而不是只传递最后一个 hidden state（隐藏层状态）</li></ul><ul class="notion-list notion-list-disc notion-block-3c65ea170bbe8004bceaf115d499167a"><li>注意力模型的解码器在产生输出之前，做了一个额外的 Attention 处理：</li></ul><div class="notion-callout notion-gray_background_co notion-block-3c65ea170bbe8039951ffcb583314d6f"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text"><div class="notion-text notion-block-3c65ea170bbe800da5d2e1509cefea1c">今天我们不关注 Attention 内部实现的细节，只是简单了解大致的作用，以及其与 Seq2Seq 的区别。</div></div></div><figure class="notion-asset-wrapper notion-asset-wrapper-video notion-block-3c65ea170bbe80dba483ef60c5011a35"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:1248px;max-width:100%;flex-direction:column;height:320px"><video playsinline="" controls="" preload="metadata" src="https://file.notion.so/f/f/c8c4cfcb-e2dc-41ce-ac48-d1a968389e65/4eedf601-dab4-4274-b8b4-6b2e148592d2/Mechanics_of_Seq2seq_Models_With_Attention.mp4?table=block&amp;id=3c65ea17-0bbe-80db-a483-ef60c5011a35&amp;spaceId=c8c4cfcb-e2dc-41ce-ac48-d1a968389e65&amp;expirationTimestamp=1787781600000&amp;signature=N77aFU0yNONo6qmEPz20uYcE6YBPc5fFWt-rUg5JQwI" title="video"></video></div></figure><ol start="1" class="notion-list notion-list-numbered notion-block-3c65ea170bbe800ba39fec8008141a8e" style="list-style-type:decimal"><li>由于编码器中每个 hidden state（隐藏层状态）都对应到输入句子中一个单词，那么<b>解码器要查看所有接收到的编码器的 hidden state</b>（隐藏层状态）。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-3c65ea170bbe805eaae4ece35e6f7f47" style="list-style-type:decimal"><li>给每个 hidden state（隐藏层状态）计算出一个分数（我们先忽略这个分数的计算过程）。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-3c65ea170bbe8081a847c3425a950bcc" style="list-style-type:decimal"><li>所有 hidden state（隐藏层状态）的分数经过softmax进行归一化。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-3c65ea170bbe8053b04ee35c8758365f" style="list-style-type:decimal"><li>将每个 hidden state（隐藏层状态）乘以所对应的分数，从而能够让高分对应的 hidden state（隐藏层状态）会被放大，而低分对应的 hidden state（隐藏层状态）会被缩小。</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-3c65ea170bbe8034951ff8b81a04df92" style="list-style-type:decimal"><li>将所有hidden state根据对应分数进行加权求和，得到对应时间步的context向量。</li></ol><div class="notion-callout notion-gray_background_co notion-block-3c65ea170bbe8077879ae92e4fcb5788"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text"><div class="notion-text notion-block-3c65ea170bbe80c69d1fe7478eab56f9">所以，Attention 可以简单理解为：一种有效的加权求和技术，其艺术在于如何获得权重。需要注意的是：注意力模型不是无意识地把输出的第一个单词对应到输入的第一个单词，它是在训练阶段学习到如何对两种语言的单词进行对应。</div></div></div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3c65ea170bbe80bf9debc5b0b7ab89e3" data-id="3c65ea170bbe80bf9debc5b0b7ab89e3"><span><div id="3c65ea170bbe80bf9debc5b0b7ab89e3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3c65ea170bbe80bf9debc5b0b7ab89e3" title="参考文献"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">参考文献</span></span></h3><div class="notion-text notion-block-3c65ea170bbe80a4b396e3d66fe9392a">[1] <a class="notion-link" href="https://jalammar.github.io/visualizing-neural-machine-translation-mechanics-of-Seq2Seq-models-with-attention/" target="_blank" rel="noopener noreferrer">Visualizing A Neural Machine Translation Model (Mechanics of Seq2Seq Models With Attention)</a>
[2] <a class="notion-link" href="https://arxiv.org/abs/1706.03762" target="_blank" rel="noopener noreferrer">Attention Is All You Need</a></div><div class="notion-blank notion-block-3c65ea170bbe808393cae10b989d2f4e"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spring Security(3)：如何设计PasswordEncoder？]]></title>
            <link>https://ifrank.qzz.io/article/ss_PasswordEncoder</link>
            <guid>https://ifrank.qzz.io/article/ss_PasswordEncoder</guid>
            <pubDate>Thu, 25 Jul 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[本文主要介绍PasswordEncoder的设计，密码的演进及相应的解决方案。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-3347f5e396d64396a11ba1e73c565463"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-table-of-contents notion-gray notion-block-28fcc34e0687424f8e6fa9805602c033"><a href="#04ada46471844e229d82d172e9130c22" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">密码存储演进史</span></a><a href="#1b9fdd5de6d24d74bb117e76ea5a451b" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">慢 hash 算法真的安全吗？</span></a><a href="#e3127678d7004173b9bda7230415e13d" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">spring security 废弃的接口</span></a><a href="#34bdb371f2544272b8a3eaeb646f4549" class="notion-table-of-contents-item"><span class="notion-table-of-contents-item-body" style="display:inline-block;margin-left:0">更深层的思考</span></a></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-04ada46471844e229d82d172e9130c22" data-id="04ada46471844e229d82d172e9130c22"><span><div id="04ada46471844e229d82d172e9130c22" class="notion-header-anchor"></div><a class="notion-hash-link" href="#04ada46471844e229d82d172e9130c22" title="密码存储演进史"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>密码存储演进史</b></span></span></h2><div class="notion-text notion-block-d2a121929c8c42e8945344fc20cece46">自从互联网有了用户的那一刻起，存储用户密码这件事便成为了一个健全的系统不得不面对的一件事。远古时期，明文存储密码可能还不被认为是一个很大的系统缺陷（事实上这是一件很恐怖的事）。提及明文存储密码，我立刻联想到的是 CSDN 社区在 2011 年末发生的 600 万用户密码泄露的事件，谁也不会想到这个和程序员密切相关的网站会犯如此低级的错误。明文存储密码使得恶意用户可以通过 sql 注入等攻击方式来获取用户名和密码，虽然安全框架和良好的编码规范可以规避很多类似的攻击，但依旧避免不了系统管理员，DBA 有途径获取用户密码这一事实。事实上，不用明文存储存储密码，程序员们早在 n 多年前就已经达成了共识。</div><div class="notion-text notion-block-0e3f6487f7fb473fb23fec29b9a1e67f">不能明文存储，一些 hash 算法便被广泛用做密码的编码器，对密码进行单向 hash 处理后存储数据库，当用户登录时，计算用户输入的密码的 hash 值，将两者进行比对。<span class="notion-orange_background"><b>单向 hash 算法</b></span>，顾名思义，它无法（或者用不能轻易更为合适）被反向解析还原出原密码。这杜绝了管理员直接获取密码的途径，可仅仅依赖于普通的 hash 算法（如 md5，sha256）是不合适的，他主要有 3 个特点：</div><ol start="1" class="notion-list notion-list-numbered notion-block-cec5283fb1d04de795ab1e8c7fc4e161" style="list-style-type:decimal"><li>同一密码生成的 hash 值一定相同</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-b46eba0fd13a4b908d086c2d757ca226" style="list-style-type:decimal"><li>不同密码的生成的 hash 值可能相同（md5 的碰撞问题相比 sha256 还要严重）</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-f73c6b06f0d5454889fc007d2321316c" style="list-style-type:decimal"><li>计算速度快。</li></ol><div class="notion-text notion-block-821c2c86947d4d5cbe73072d34824c30">以上三点结合在一起，破解此类算法成了不是那么困难的一件事，尤其是第三点，会在下文中再次提到，多快才算非常快？按照相关资料的说法：</div><blockquote class="notion-quote notion-block-786812fdd62c4affb980d434c4fc0a10"><div>modern hardware perform billions of hash calculations a second.</div></blockquote><div class="notion-text notion-block-84d3bf153a4d4b5ea382a67daa2f679c">考虑到大多数用户使用的密码多为数字+字母+特殊符号的组合，攻击者将常用的密码进行枚举，甚至通过排列组合来暴力破解，这被称为 rainbow table。算法爱好者能够立刻看懂到上述的方案，这被亲切地称之为—打表，一种暴力美学，这张表是可以被复用的。</div><div class="notion-text notion-block-6fac8d1a113a4ab592f1a92da44373ef">虽然仅仅依赖于传统 hash 算法的思路被否决了，但这种 hash 后比对的思路，几乎被后续所有的优化方案继承。</div><div class="notion-text notion-block-e4dd5b994b3540ad97443a48aa2356e1">hash 方案迎来的第一个改造是对引入一个“随机的因子”来掺杂进明文中进行 hash 计算，这样的随机因子通常被称之为<span class="notion-orange_background"><b>盐 （salt）</b></span>。salt 一般是用户相关的，每个用户持有各自的 salt。此时张三和李四的密码即使相同，由于 salt 的影响，存储在数据库中的密码也是不同的，除非…为每个用户单独建议一张 rainbow table。很明显 salted hash 相比普通的单向 hash 方案加大了 hacker 攻击的难度。但了解过 GPU 并行计算能力之强大的童鞋，都能够意识到，虽然破解 salted hash 比较麻烦，却并非不可行，安全专家似乎也对这个方案不够满意。</div><div class="notion-text notion-block-3915c1da02e340bbb2d7f9d0a351f547">为解决上述 salted hash 仍然存在的问题，一些新型的单向 hash 算法被研究了出来。其中就包括：<span class="notion-orange_background"><b>Bcrypt，PBKDF2，Scrypt，Argon2</b></span>。<b>为什么这些 hash 算法能保证密码存储的安全性？因为他们足够慢，恰到好处的慢</b>。这么说不严谨，只是为了给大家留个深刻的映像：慢。这类算法有一个特点，存在一个影响因子，可以用来控制计算强度，这直接决定了破解密码所需要的资源和时间，直观的体会可以见下图，在一年内破解如下算法所需要的硬件资源花费（折算成美元）</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-8a8a4a2441d34eb482d8789f269653f2"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:720px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fc8c4cfcb-e2dc-41ce-ac48-d1a968389e65%2F43757f51-27e2-4342-bf18-4e6ae99b352e%2FUntitled.png?table=block&amp;id=8a8a4a24-41d3-4eb4-82d8-789f269653f2&amp;t=8a8a4a24-41d3-4eb4-82d8-789f269653f2&amp;width=720&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-623b86c012dc420ea600548a2d1daac2">这使得破解成了一件极其困难的事，并且，其中的<span class="notion-orange_background"><b>计算强度因子【可以理解为加密复杂度，例如可以使用</b></span><span class="notion-orange_background"><code class="notion-inline-code"><b>private Integer passwordEncoderLength = 4</b></code></span><span class="notion-orange_background"><b>，数值越大，计算开销越大】</b></span>是可控的，这样，即使未来量子计算机的计算能力爆表，也可以通过其控制计算强度以防破解。注意，普通的验证过程只需要计算一次 hash 计算，使用此类 hash 算法并不会影响到用户体验。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1b9fdd5de6d24d74bb117e76ea5a451b" data-id="1b9fdd5de6d24d74bb117e76ea5a451b"><span><div id="1b9fdd5de6d24d74bb117e76ea5a451b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1b9fdd5de6d24d74bb117e76ea5a451b" title="慢 hash 算法真的安全吗？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">慢 hash 算法真的安全吗？</span></span></h2><div class="notion-text notion-block-819bae64b905457586a6544aa165a2b0">Bcrypt，Scrypt，PBKDF2 这些慢 hash 算法是目前最为推崇的 password encoding 方式，好奇心驱使我思考了这样一个问题：慢 hash 算法真的安全吗？</div><div class="notion-text notion-block-f1c3dfd50c3c48ffa76727d6645c7676">我暂时还没有精力仔细去研究他们中每一个算法的具体实现，只能通过一些文章来拾人牙慧，简单看看这几个算法的原理和安全性。</div><div class="notion-text notion-block-ce6961d0d97b45e5889894af03063bc3">PBKDF2 被设计的很简单，它的基本原理是通过一个伪随机函数（例如 HMAC 函数），把明文和一个盐值作为输入参数，然后按照设置的计算强度因子重复进行运算，并最终产生密钥。这样的重复 hash 已经被认为足够安全，但也有人提出了不同意见，此类算法对于传统的 CPU 来说的确是足够安全，但 GPU 被搬了出来，前文提到过 GPU 的并行计算能力非常强大。</div><div class="notion-text notion-block-d770c4c4c8e24b2486cd9dee70a3a135"><span class="notion-orange"><b>Bcrypt 强大的一点在于，其不仅仅是 CPU 密集型，还是 RAM 密集型！双重的限制因素，导致 GPU，ASIC（专用集成电路）无法应对 Bcrypt 带来的破解困境。</b></span></div><div class="notion-text notion-block-955bc26a31534fecbcc4a1e62aad9bc6">然后…看了 Scrypt 的相关资料之后我才意识到这个坑有多深。一个熟悉又陌生的词出现在了我面前：FPGA（现场可编程逻辑门阵列），这货就比较厉害了。现成的芯片指令结构如传统的 CPU，GPU，ASIC 都无法破解 Bcrypt，但是 FPGA 支持烧录逻辑门（如AND、OR、XOR、NOT），通过编程的方式烧录指令集的这一特性使得可以定制硬件来破解 Bcrypt。尽管我不认为懂这个技术的人会去想办法破解真正的系统，但，只要这是一个可能性，就总有方法会被发明出来与之对抗。Scrypt 比 Bcrypt 额外考虑到的就是大规模的<a class="notion-link" href="https://zh.wikipedia.org/w/index.php?title=%E5%AE%A2%E8%A3%BD%E7%A1%AC%E9%AB%94%E6%94%BB%E6%93%8A&amp;action=edit&amp;redlink=1" target="_blank" rel="noopener noreferrer">自定义硬件攻击</a> ，从而刻意设计需要大量内存运算。</div><div class="notion-text notion-block-36f2e5e6742e4754b96baac42cf67056">理论终归是理论，实际上 Bcrypt 算法被发明至今 18 年，使用范围广，且从未因为安全问题而被修改，其有限性是已经被验证过的，相比之下 Scrypt 据我看到的文章显示是 9 年的历史，没有 Bcrypt 使用的广泛。从破解成本和权威性的角度来看，Bcrypt 用作密码编码器是不错的选择。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-e3127678d7004173b9bda7230415e13d" data-id="e3127678d7004173b9bda7230415e13d"><span><div id="e3127678d7004173b9bda7230415e13d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e3127678d7004173b9bda7230415e13d" title="spring security 废弃的接口"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>spring security 废弃的接口</b></span></span></h2><div class="notion-text notion-block-05d4111cae2e4297af1c45ea2395602e">spring security 5 对 PasswordEncoder 做了相关的重构，原先默认配置的 PlainTextPasswordEncoder（明文密码）被移除了，想要做到明文存储密码，只能使用一个过期的类来过渡。</div><div class="notion-text notion-block-28f75e824f2f4e87acaaa30f1fcaeacd">实际上，spring security 提供了 BCryptPasswordEncoder 来进行密码编码，并作为了相关配置的默认配置，只不过没有暴露为全局的 Bean。使用明文存储的风险在文章一开始就已经强调过，NoOpPasswordEncoder 只能存在于 demo 中。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-34bdb371f2544272b8a3eaeb646f4549" data-id="34bdb371f2544272b8a3eaeb646f4549"><span><div id="34bdb371f2544272b8a3eaeb646f4549" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34bdb371f2544272b8a3eaeb646f4549" title="更深层的思考"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>更深层的思考</b></span></span></h2><div class="notion-text notion-block-14c6799da9874e1db04d2a317f991b8b">不知道有没有读者产生跟我相同的困扰：</div><ol start="1" class="notion-list notion-list-numbered notion-block-15594c3158ee41919eb9954d97d4ff53" style="list-style-type:decimal"><li>如果我要设计一个 QPS 很高的登录系统，使用 spring security 推荐的 BCrypt 会不会存在性能问题？</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-0c3e9ba92ed04ac789a2f02518d17aae" style="list-style-type:decimal"><li>spring security 怎么这么坑，原来的密码编码器都给改了，我需要怎么迁移旧密码编码的应用程序？</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-d8834f75fe2c4424b549c722ffb7d8f9" style="list-style-type:decimal"><li>万一以后出了更高效的加密算法，这种笨重的硬编码方式配置密码编码器是不是不够灵活？</li></ol><div class="notion-text notion-block-ffacdc1bbe9c42dfb8513f3915f7be43">在 spring security 5 提供了这样一个思路，应该<span class="notion-orange"><b>将密码编码之后的 hash 值和加密方式一起存储</b></span>，并提供了一个 <code class="notion-inline-code">DelegatingPasswordEncoder</code> 来作为众多<b>密码编码方式的集合</b>。</div><div class="notion-text notion-block-f5d1658c47164bf0bc7bac531cd76bde">负责生产 <code class="notion-inline-code">DelegatingPasswordEncoder</code>的工厂方法：</div><div class="notion-text notion-block-80eb9b62fc3e489ba9a0b123cf15dcad">如此注入 PasswordEncoder 之后，我们在数据库中需要这么存储数据：</div><blockquote class="notion-quote notion-block-c3d5ad8f13ce4dcfad04573594178cb7"><div>你会不会担心密码泄露后，{bcrypt}，{pbkdf2}，{scrypt}，{sha256} 此类前缀会直接暴露密码的编码方式？其实这个考虑是多余的，因为密码存储的依赖算法并不是一个秘密。大多数能搞到你密码的 hacker 都可以轻松的知道你用的是什么算法，例如，bcrypt 算法通常以 $2a$ 开头</div></blockquote><div class="notion-text notion-block-26db43fa463d4f91b001f8645abb9e9d">稍微思考下，前面的三个疑问就可以迎刃而解，这就是文档中所谓的：<b>能够自适应服务器性能的现代化密码编码方案</b>。</div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>