[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-detail-ssr-49617991133826060":3},{"id":4,"title":5,"tag":6,"createTime":7,"updateTime":7,"renderedHtml":8,"description":9,"ogImageUrl":10},"49617991133826060","etcd 数据管理","etcd","2023-12-09","\u003Ch2 id=\"h2-0-etcd-\">etcd 数据存储\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\n\u003Cp>etcd 的存储分为\u003Ccode>内存存储\u003C\u002Fcode>和\u003Ccode>持久化（硬盘）存储\u003C\u002Fcode>两部分。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>内存中的存储除了顺序化的记录下所有用户对节点数据变更的记录外，还会对用户数据进行索引、建堆等方便查询的操作。 而持久化则使用预写式日志（WAL：Write Ahead Log）进行记录存储。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>在 WAL 的体系中，所有的数据在提交之前都会进行日志记录。\u003C\u002Fstrong> 在 etcd 的持久化存储目录中，有两个子目录。一个是\u003Ccode>WAL\u003C\u002Fcode>，\u003Cstrong>存储着所有事务的变化记录\u003C\u002Fstrong>；另一个则是\u003Ccode>snapshot\u003C\u002Fcode>，\u003Cstrong>用于存储某一个时刻 etcd 所有目录的数据\u003C\u002Fstrong>。通过 WAL 和 snapshot 相结合的方式，etcd 可以有效的进行数据存储和节点故障恢复等操作。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>既然有了 WAL 实时存储了所有的变更，为什么还需要 snapshot 呢？随着使用量的增加，WAL 存储的数据会暴增，为了防止磁盘很快就爆满，etcd \u003Cstrong>默认每 10000 条记录做一次 snapshot\u003C\u002Fstrong>，\u003Cstrong>经过 snapshot 以后的 WAL 文件就可以删除\u003C\u002Fstrong>。而通过 API 可以查询的历史 etcd 操作默认为 1000 条。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>首次启动时，etcd 会把启动的配置信息存储到 data-dir 参数指定的数据目录中\u003C\u002Fstrong>。配置信息包括本地节点的 ID、集群 ID 和初始时集群信息。用户需要避免 etcd 从一个过期的数据目录中重新启动，因为使用过期的数据目录启动的节点会与集群中的其他节点产生不一致（如： 之前已经记录并同意 Leader 节点存储某个信息，重启后又向 Leader 节点申请这个信息）。所以，为了最大化集群的安全性，一旦有任何数据损坏或丢失的可能性，你就应该把这个节点从集群中移除，然后加入一个不带数据目录的新节点。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"h3-1--snapshot-wal\">磁盘中存储的 snapshot 和 wal\u003C\u002Fh3>\n\u003Cdiv class=\"ssr-code-block\" data-lang=\"text\">\u003Cdiv class=\"ssr-code-head\">\u003Cspan class=\"ssr-code-lang\">text\u003C\u002Fspan>\u003Cdiv class=\"ssr-code-actions\">\u003Cbutton class=\"ssr-code-fullscreen ssr-code-iconbtn\" type=\"button\" aria-label=\"全屏查看代码\" title=\"全屏\">\u003Csvg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M5 5h6v2H7v4H5V5zm14 0v6h-2V7h-4V5h6zM5 19v-6h2v4h4v2H5zm14 0h-6v-2h4v-4h2v6z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003Cbutton class=\"ssr-code-copy ssr-code-iconbtn\" type=\"button\" aria-label=\"复制代码\" title=\"复制\">\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--default\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--success\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--error\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"ssr-code-body\">\u003Cdiv class=\"ssr-code-pane\">\u003Cdiv class=\"ssr-code-gutter\" aria-hidden=\"true\">\u003Cspan class=\"ssr-code-num\">1\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">2\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">3\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">4\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">5\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">6\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">7\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">8\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">9\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">10\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cpre class=\"ssr-code-pre\">\u003Ccode class=\"hljs language-text\">\u003Cspan class=\"ssr-code-line\">[root@centos default.etcd]# tree\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">.\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">└── member\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">    ├── snap\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">    │   └── db\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">    └── wal\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">        ├── 0000000000000000-0000000000000000.wal\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">        └── 0.tmp\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">​\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">3 directories, 3 files\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Ch2 id=\"h2-2-\">灾难恢复\u003C\u002Fh2>\n\u003Cp>etcd 的灾难恢复主要依赖于备份和恢复机制。要执行灾难恢复，需要有一个最近的 etcd 数据快照，并且在必要时能够访问 WAL 日志文件。以下是灾难恢复的基本步骤：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\n\u003Cp>备份\u003C\u002Fp>\n\u003Cp>定期备份是灾难恢复的关键。etcd 提供了快照功能来帮助你创建数据的全量备份。\u003C\u002Fp>\n\u003Cdiv class=\"ssr-code-block\" data-lang=\"bash\">\u003Cdiv class=\"ssr-code-head\">\u003Cspan class=\"ssr-code-lang\">bash\u003C\u002Fspan>\u003Cdiv class=\"ssr-code-actions\">\u003Cbutton class=\"ssr-code-fullscreen ssr-code-iconbtn\" type=\"button\" aria-label=\"全屏查看代码\" title=\"全屏\">\u003Csvg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M5 5h6v2H7v4H5V5zm14 0v6h-2V7h-4V5h6zM5 19v-6h2v4h4v2H5zm14 0h-6v-2h4v-4h2v6z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003Cbutton class=\"ssr-code-copy ssr-code-iconbtn\" type=\"button\" aria-label=\"复制代码\" title=\"复制\">\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--default\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--success\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--error\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"ssr-code-body\">\u003Cdiv class=\"ssr-code-pane\">\u003Cdiv class=\"ssr-code-gutter\" aria-hidden=\"true\">\u003Cspan class=\"ssr-code-num\">1\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cpre class=\"ssr-code-pre\">\u003Ccode class=\"hljs language-bash\">\u003Cspan class=\"ssr-code-line\">\u003Cspan style=\"color:#B392F0\">etcdctl\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">snapshot\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">save\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">backup.db\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Cp>上述命令会创建当前 etcd 状态的快照，并将其保存到 backup.db 文件中。你可能还想要安全地存储 WAL 日志文件和配置文件，因为它们包含了重要的集群信息和更改历史。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>恢复\u003C\u002Fp>\n\u003Cp>如果遇到了灾难性的事件，比如数据丢失或损坏，可以使用快照文件来恢复 etcd 集群。使用 etcdctl 的 snapshot restore 命令来从快照文件中恢复数据。\u003C\u002Fp>\n\u003Cdiv class=\"ssr-code-block\" data-lang=\"bash\">\u003Cdiv class=\"ssr-code-head\">\u003Cspan class=\"ssr-code-lang\">bash\u003C\u002Fspan>\u003Cdiv class=\"ssr-code-actions\">\u003Cbutton class=\"ssr-code-fullscreen ssr-code-iconbtn\" type=\"button\" aria-label=\"全屏查看代码\" title=\"全屏\">\u003Csvg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M5 5h6v2H7v4H5V5zm14 0v6h-2V7h-4V5h6zM5 19v-6h2v4h4v2H5zm14 0h-6v-2h4v-4h2v6z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003Cbutton class=\"ssr-code-copy ssr-code-iconbtn\" type=\"button\" aria-label=\"复制代码\" title=\"复制\">\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--default\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--success\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--error\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"ssr-code-body\">\u003Cdiv class=\"ssr-code-pane\">\u003Cdiv class=\"ssr-code-gutter\" aria-hidden=\"true\">\u003Cspan class=\"ssr-code-num\">1\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cpre class=\"ssr-code-pre\">\u003Ccode class=\"hljs language-bash\">\u003Cspan class=\"ssr-code-line\">\u003Cspan style=\"color:#B392F0\">etcdutl\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">snapshot\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">restore\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">backup.db\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Cp>这个命令会从 backup.db 快照文件中读取数据，并初始化新的 etcd 数据目录。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cstrong>注意事项：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>当从快照恢复时，etcd 会创建一个新的集群 ID。这意味着恢复的数据不能用于加入到原有的集群中，而是会形成一个新的集群。\u003C\u002Fli>\n\u003Cli>在恢复过程中，所有的数据将回滚到快照时的状态，这意味着在快照之后的所有数据更改都将丢失。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"h2-3-\">实践\u003C\u002Fh2>\n\u003Cp>现在我使用 docker 部署了有三个节点的 docker 集群，目录结构如下：\u003C\u002Fp>\n\u003Cdiv class=\"ssr-code-block\" data-lang=\"text\">\u003Cdiv class=\"ssr-code-head\">\u003Cspan class=\"ssr-code-lang\">text\u003C\u002Fspan>\u003Cdiv class=\"ssr-code-actions\">\u003Cbutton class=\"ssr-code-fullscreen ssr-code-iconbtn\" type=\"button\" aria-label=\"全屏查看代码\" title=\"全屏\">\u003Csvg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M5 5h6v2H7v4H5V5zm14 0v6h-2V7h-4V5h6zM5 19v-6h2v4h4v2H5zm14 0h-6v-2h4v-4h2v6z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003Cbutton class=\"ssr-code-copy ssr-code-iconbtn\" type=\"button\" aria-label=\"复制代码\" title=\"复制\">\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--default\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--success\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--error\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"ssr-code-body\">\u003Cdiv class=\"ssr-code-pane\">\u003Cdiv class=\"ssr-code-gutter\" aria-hidden=\"true\">\u003Cspan class=\"ssr-code-num\">1\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">2\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">3\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">4\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">5\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">6\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">7\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">8\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">9\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">10\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">11\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">12\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">13\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">14\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">15\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">16\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">17\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">18\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">19\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">20\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">21\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">22\u003C\u002Fspan>\u003Cspan class=\"ssr-code-num\">23\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cpre class=\"ssr-code-pre\">\u003Ccode class=\"hljs language-text\">\u003Cspan class=\"ssr-code-line\">[root@centos docker]# tree\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">.\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">├── etcd01\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│   └── member\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│       ├── snap\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│       │   └── db\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│       └── wal\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│           ├── 0000000000000000-0000000000000000.wal\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│           └── 0.tmp\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">├── etcd02\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│   └── member\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│       ├── snap\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│       │   └── db\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│       └── wal\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│           ├── 0000000000000000-0000000000000000.wal\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">│           └── 0.tmp\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">└── etcd03\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">    └── member\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">        ├── snap\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">        │   └── db\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">        └── wal\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">            ├── 0000000000000000-0000000000000000.wal\u003C\u002Fspan>\u003Cspan class=\"ssr-code-line\">            └── 0.tmp\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Cp>在实际生成快照的过程中，可能会出现各种各样的问题。经过我各种失败的经验，最后能成功生成快照的命令是：\u003C\u002Fp>\n\u003Cdiv class=\"ssr-code-block\" data-lang=\"bash\">\u003Cdiv class=\"ssr-code-head\">\u003Cspan class=\"ssr-code-lang\">bash\u003C\u002Fspan>\u003Cdiv class=\"ssr-code-actions\">\u003Cbutton class=\"ssr-code-fullscreen ssr-code-iconbtn\" type=\"button\" aria-label=\"全屏查看代码\" title=\"全屏\">\u003Csvg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M5 5h6v2H7v4H5V5zm14 0v6h-2V7h-4V5h6zM5 19v-6h2v4h4v2H5zm14 0h-6v-2h4v-4h2v6z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003Cbutton class=\"ssr-code-copy ssr-code-iconbtn\" type=\"button\" aria-label=\"复制代码\" title=\"复制\">\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--default\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--success\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"\u002F>\u003C\u002Fsvg>\u003Csvg class=\"ssr-code-copy-icon ssr-code-copy-icon--error\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" aria-hidden=\"true\">\u003Cpath fill=\"currentColor\" d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"ssr-code-body\">\u003Cdiv class=\"ssr-code-pane\">\u003Cdiv class=\"ssr-code-gutter\" aria-hidden=\"true\">\u003Cspan class=\"ssr-code-num\">1\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cpre class=\"ssr-code-pre\">\u003Ccode class=\"hljs language-bash\">\u003Cspan class=\"ssr-code-line\">\u003Cspan style=\"color:#B392F0\">docker\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">exec\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">-it\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">-e\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">ETCDCTL_ENDPOINTS=http:\u002F\u002Fetcd-1:2379\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">etcd-1\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">etcdctl\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">snapshot\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">save\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\u002Fetcd-data\u002Fbackup.db\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Cp>结果如下所示：\u003C\u002Fp>\n\u003Cp>\u003Cimg class=\"article-img article-img-zoomable\" src=\"https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011191.png\" alt=\"image-20231205215945843\" loading=\"lazy\" decoding=\"async\" style=\"width: 100%; height: auto; max-width: 100%; display: block;\" srcset=\"https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011191.png?imageMogr2\u002Fthumbnail\u002F400x\u002Fformat\u002Fwebp\u002Fquality\u002F85 400w, https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011191.png?imageMogr2\u002Fthumbnail\u002F800x\u002Fformat\u002Fwebp\u002Fquality\u002F85 800w, https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011191.png?imageMogr2\u002Fthumbnail\u002F1200x\u002Fformat\u002Fwebp\u002Fquality\u002F85 1200w, https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011191.png?imageMogr2\u002Fthumbnail\u002F1920x\u002Fformat\u002Fwebp\u002Fquality\u002F85 1920w\" sizes=\"(max-width: 576px) 100vw, (max-width: 992px) 90vw, 720px\">\u003C\u002Fp>\n\u003Cp>这里有几点需要注意：\u003C\u002Fp>\n\u003Col>\n\u003Cli>环境变量问题：由于我在使用 docker 创建容器的时候就设置了一个环境变量（–env ETCDCTL_ENDPOINTS=\u003Ca href=\"http:\u002F\u002Fetcd-1:2379\">http:\u002F\u002Fetcd-1:2379\u003C\u002Fa>,\u003Ca href=\"http:\u002F\u002Fetcd-2:2379\">http:\u002F\u002Fetcd-2:2379\u003C\u002Fa>,\u003Ca href=\"http:\u002F\u002Fetcd-3:2379\">http:\u002F\u002Fetcd-3:2379\u003C\u002Fa>），又因为\u003Ccode>etcdctl snapshot save\u003C\u002Fcode>命令只能针对单个 etcd 节点执行快照操作，所以需要在执行命令之前，在同一行中设置环境变量的方式来覆盖容器中的环境变量，使用\u003Ccode>-e ETCDCTL_ENDPOINTS=http:\u002F\u002Fetcd-1:2379\u003C\u002Fcode>将 etcdctl 的连接对象变成单个节点，这样就可以正确执行快照操作了。当整条命令执行完成后，使用 -e 选项设置的环境变量就会失效。\u003C\u002Fli>\n\u003Cli>容器文件系统问题：刚开始我直接写了 backup.db 文件，但是执行完后我找不到 backup.db 文件在哪里，后来我发现整个命令是一个 docker 命令。对于 docker 命令，所有的文件路径都是相对于容器的文件系统的。所以需要做数据卷持久化将容器中的某个文件与宿主机进行同步。因为在创建容器的时候我已经指定了数据卷目录（–env ETCD_DATA_DIR=\u002Fetcd-data），所以需要将生成的 backup.db 文件放在 \u002Fetcd-data 目录下才能在宿主机中看到。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>下面是备份文件的恢复：\u003C\u002Fp>\n\u003Cp>使用 docker 部署 etcd 比较麻烦的地方就是做数据恢复，麻烦是因为：\u003C\u002Fp>\n\u003Col>\n\u003Cli>当删除 etcd 的数据目录后运行 etcd 的容器会自动停止，一旦容器停止就无法进入容器内部使用 etcdutl 目录在数据恢复。\u003C\u002Fli>\n\u003Cli>在做数据恢复的时候，etcd 的数据目录必需为一个空目录，不然无法恢复。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>所以解决以上问题的思路就是：\u003C\u002Fp>\n\u003Col>\n\u003Cli>首先在容器生成一个备份文件 backup.db，并可以在宿主机中拿到该备份文件（可以通过数据卷持久化完成）。\u003C\u002Fli>\n\u003Cli>在宿主机中删除 member 目录，这样容器会自动停止。\u003C\u002Fli>\n\u003Cli>用这个 backup.db 文件在宿主机中进行数据恢复，恢复完成后，会生成一个 member 目录。\u003C\u002Fli>\n\u003Cli>将该 member 目录拷贝到数据卷持久化的那个目录，然后重新运行容器，数据就成功恢复了。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>具体操作如下：\u003C\u002Fp>\n\u003Cp>\u003Cimg class=\"article-img article-img-zoomable\" src=\"https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011611.png\" alt=\"image-20231206180208958\" loading=\"lazy\" decoding=\"async\" style=\"width: 100%; height: auto; max-width: 100%; display: block;\" srcset=\"https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011611.png?imageMogr2\u002Fthumbnail\u002F400x\u002Fformat\u002Fwebp\u002Fquality\u002F85 400w, https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011611.png?imageMogr2\u002Fthumbnail\u002F800x\u002Fformat\u002Fwebp\u002Fquality\u002F85 800w, https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011611.png?imageMogr2\u002Fthumbnail\u002F1200x\u002Fformat\u002Fwebp\u002Fquality\u002F85 1200w, https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011611.png?imageMogr2\u002Fthumbnail\u002F1920x\u002Fformat\u002Fwebp\u002Fquality\u002F85 1920w\" sizes=\"(max-width: 576px) 100vw, (max-width: 992px) 90vw, 720px\">\u003C\u002Fp>\n","etcd 数据存储 etcd 的存储分为 和 两部分。 内存中的存储除了顺序化的记录下所有用户对节点数据变更的记录外，还会对用户数据进行索引、建堆等方便查询的操作。 而持久化则使用预写式日志（WAL：Write Ahead Log）进行记录存储。 在 WAL 的体系中，所有的数据在提交之前都会进行日志记录。 在 etc","https:\u002F\u002Fliubing-1314895948.cos.ap-chengdu.myqcloud.com\u002Fimg\u002F202312062011191.png"]