learn.lianglianglee.com/专栏/由浅入深吃透 Docker-完/05 仓库访问:怎样搭建属于你的私有仓库?.md.html
2022-09-06 22:30:37 +08:00

377 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>05 仓库访问:怎样搭建属于你的私有仓库?.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/由浅入深吃透 Docker-完/00 溯本求源,吃透 Docker.md.html">00 溯本求源,吃透 Docker</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/01 Docker 安装:入门案例带你了解容器技术原理.md.html">01 Docker 安装:入门案例带你了解容器技术原理</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/02 核心概念:镜像、容器、仓库,彻底掌握 Docker 架构核心设计理念.md.html">02 核心概念:镜像、容器、仓库,彻底掌握 Docker 架构核心设计理念</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/03 镜像使用Docker 环境下如何配置你的镜像?.md.html">03 镜像使用Docker 环境下如何配置你的镜像?</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/04 容器操作:得心应手掌握 Docker 容器基本操作.md.html">04 容器操作:得心应手掌握 Docker 容器基本操作</a>
</li>
<li>
<a class="current-tab" href="/专栏/由浅入深吃透 Docker-完/05 仓库访问:怎样搭建属于你的私有仓库?.md.html">05 仓库访问:怎样搭建属于你的私有仓库?</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/06 最佳实践:如何在生产中编写最优 Dockerfile.md.html">06 最佳实践:如何在生产中编写最优 Dockerfile</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/07 Docker 安全:基于内核的弱隔离系统如何保障安全性?.md.html">07 Docker 安全:基于内核的弱隔离系统如何保障安全性?</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/08 容器监控:容器监控原理及 cAdvisor 的安装与使用.md.html">08 容器监控:容器监控原理及 cAdvisor 的安装与使用</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/09 资源隔离:为什么构建容器需要 Namespace .md.html">09 资源隔离:为什么构建容器需要 Namespace </a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/10 资源限制:如何通过 Cgroups 机制实现资源限制?.md.html">10 资源限制:如何通过 Cgroups 机制实现资源限制?</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/11 组件组成:剖析 Docker 组件作用及其底层工作原理.md.html">11 组件组成:剖析 Docker 组件作用及其底层工作原理</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/12 网络模型:剖析 Docker 网络实现及 Libnetwork 底层原理.md.html">12 网络模型:剖析 Docker 网络实现及 Libnetwork 底层原理</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/13 数据存储:剖析 Docker 卷与持久化数据存储的底层原理.md.html">13 数据存储:剖析 Docker 卷与持久化数据存储的底层原理</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/14 文件存储驱动AUFS 文件系统原理及生产环境的最佳配置.md.html">14 文件存储驱动AUFS 文件系统原理及生产环境的最佳配置</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/15 文件存储驱动Devicemapper 文件系统原理及生产环境的最佳配置.md.html">15 文件存储驱动Devicemapper 文件系统原理及生产环境的最佳配置</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/16 文件存储驱动OverlayFS 文件系统原理及生产环境的最佳配置.md.html">16 文件存储驱动OverlayFS 文件系统原理及生产环境的最佳配置</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/17 原理实践:自己动手使用 Golang 开发 Docker.md.html">17 原理实践:自己动手使用 Golang 开发 Docker</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/18 原理实践:自己动手使用 Golang 开发 Docker.md.html">18 原理实践:自己动手使用 Golang 开发 Docker</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/19 如何使用 Docker Compose 解决开发环境的依赖?.md.html">19 如何使用 Docker Compose 解决开发环境的依赖?</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/20 如何在生产环境中使用 Docker Swarm 调度容器?.md.html">20 如何在生产环境中使用 Docker Swarm 调度容器?</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/21 如何使 Docker 和 Kubernetes 结合发挥容器的最大价值?.md.html">21 如何使 Docker 和 Kubernetes 结合发挥容器的最大价值?</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/22 多阶级构建Docker 下如何实现镜像多阶级构建?.md.html">22 多阶级构建Docker 下如何实现镜像多阶级构建?</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/23 DevOps容器化后如何通过 DevOps 提高协作效能?.md.html">23 DevOps容器化后如何通过 DevOps 提高协作效能?</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/24 CICD容器化后如何实现持续集成与交付.md.html">24 CICD容器化后如何实现持续集成与交付</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/25 CICD容器化后如何实现持续集成与交付.md.html">25 CICD容器化后如何实现持续集成与交付</a>
</li>
<li>
<a href="/专栏/由浅入深吃透 Docker-完/26 结束语 展望未来Docker 的称霸之路.md.html">26 结束语 展望未来Docker 的称霸之路</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>05 仓库访问:怎样搭建属于你的私有仓库?</h1>
<p>在第三课时“镜像使用Docker 环境下如何配置你的镜像”里我介绍了镜像的基本操作和镜像的原理那么有了镜像我们应该如何更好地存储和分发镜像呢答案就是今天的主角——Docker 的镜像仓库。其实我们不仅可以使用公共镜像仓库存储和分发镜像,也可以自己搭建私有的镜像仓库,那在搭建之前,我们先回顾下仓库的基础知识。</p>
<h3>仓库是什么?</h3>
<p>仓库Repository是存储和分发 Docker 镜像的地方。镜像仓库类似于代码仓库Docker Hub 的命名来自 GitHubGithub 是我们常用的代码存储和分发的地方。同样 Docker Hub 是用来提供 Docker 镜像存储和分发的地方。</p>
<p>有的同学可能经常分不清注册服务器Registry和仓库Repository的概念。在这里我可以解释下这两个概念的区别注册服务器是存放仓库的实际服务器而仓库则可以被理解为一个具体的项目或者目录注册服务器可以包含很多个仓库每个仓库又可以包含多个镜像。例如我的镜像地址为 docker.io/centosdocker.io 是注册服务器centos 是仓库名。 它们之间的关系如图 1 所示。</p>
<p><img src="assets/Ciqc1F9bM-uAI6MDAADk1noY7ic639.png" alt="png" /></p>
<p>按照类型,我们将镜像仓库分为公共镜像仓库和私有镜像仓库。</p>
<h3>公共镜像仓库</h3>
<p>公共镜像仓库一般是 Docker 官方或者其他第三方组织(阿里云,腾讯云,网易云等)提供的,允许所有人注册和使用的镜像仓库。</p>
<p>Docker Hub 是全球最大的镜像市场,目前已经有超过 10w 个容器镜像,这些容器镜像主要来自软件供应商、开源组织和社区。大部分的操作系统镜像和软件镜像都可以直接在 Docker Hub 下载并使用。</p>
<p><img src="assets/Ciqc1F9bL9yAYd_LAAJW9Q4Ue2w855.png" alt="png" /></p>
<p>图 2 Docker Hub 镜像</p>
<p>下面我以 Docker Hub 为例,教你如何使用公共镜像仓库分发和存储镜像。</p>
<h4>注册 Docker Hub 账号</h4>
<p>我们首先访问<a href="https://hub.docker.com/">Docker Hub</a>官网,点击注册按钮进入注册账号界面。</p>
<p><img src="assets/CgqCHl9bL-aABPLiAABcwVxClDY261.png" alt="png" /></p>
<p>图 3 注册 Docker Hub 账号</p>
<p>注册完成后,我们可以点击创建仓库,新建一个仓库用于推送镜像。</p>
<p><img src="assets/Ciqc1F9bL--AYVIKAADWoafHnho359.png" alt="png" /></p>
<p>图 4 创建仓库</p>
<p>这里我的账号为 lagoudocker创建了一个名称为 busybox 的仓库,创建好仓库后我们就可以推送本地镜像到这个仓库里了。下面我通过一个实例来演示一下如何推送镜像到自己的仓库中。</p>
<p>首先我们使用以下命令拉取 busybox 镜像:</p>
<pre><code>$ docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
Digest: sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977
Status: Image is up to date for busybox:latest
docker.io/library/busybox:latest
</code></pre>
<p>在推送镜像仓库前,我们需要使用<code>docker login</code>命令先登录一下镜像服务器,因为只有已经登录的用户才可以推送镜像到仓库。</p>
<pre><code>$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: lagoudocker
Password:
Login Succeeded
</code></pre>
<p>使用<code>docker login</code>命令登录镜像服务器,这时 Docker 会要求我们输入用户名和密码,输入我们刚才注册的账号和密码,看到<code>Login Succeeded</code>表示登录成功。登录成功后就可以推送镜像到自己创建的仓库了。</p>
<blockquote>
<p><code>docker login</code>命令默认会请求 Docker Hub如果你想登录第三方镜像仓库或者自建的镜像仓库<code>docker login</code>后面加上注册服务器即可。例如我们想登录访问阿里云镜像服务器,则使用<code>docker login registry.cn-beijing.aliyuncs.com</code>,输入阿里云镜像服务的用户名密码即可。</p>
</blockquote>
<p>在本地镜像推送到自定义仓库前,我们需要先把镜像“重命名”一下,才能正确推送到自己创建的镜像仓库中,使用<code>docker tag</code>命令将镜像“重命名”:</p>
<pre><code>$ docker tag busybox lagoudocker/busybox
</code></pre>
<p>镜像“重命名”后使用<code>docker push</code>命令就可以推送镜像到自己创建的仓库中了。</p>
<pre><code>$ docker push lagoudocker/busybox
The push refers to repository [docker.io/lagoudocker/busybox]
514c3a3e64d4: Mounted from library/busybox
latest: digest: sha256:400ee2ed939df769d4681023810d2e4fb9479b8401d97003c710d0e20f7c49c6 size: 527
</code></pre>
<p>此时,<code>busybox</code>这个镜像就被推送到自定义的镜像仓库了。这里我们也可以新建其他的镜像仓库,然后把自己构建的镜像推送到仓库中。
有时候,出于安全或保密的需求,你可能想要搭建一个自己的镜像仓库,下面我带你一步一步构建一个私有的镜像仓库。</p>
<h3>搭建私有仓库</h3>
<h4>启动本地仓库</h4>
<p>Docker 官方提供了开源的镜像仓库 <a href="https://github.com/docker/distribution">Distribution</a>,并且镜像存放在 Docker Hub 的 <a href="https://hub.docker.com/_/registry">Registry</a> 仓库下供我们下载。</p>
<p>我们可以使用以下命令启动一个本地镜像仓库:</p>
<pre><code>$ docker run -d -p 5000:5000 --name registry registry:2.7
Unable to find image 'registry:2.7' locally
2.7: Pulling from library/registry
cbdbe7a5bc2a: Pull complete
47112e65547d: Pull complete
46bcb632e506: Pull complete
c1cc712bcecd: Pull complete
3db6272dcbfa: Pull complete
Digest: sha256:8be26f81ffea54106bae012c6f349df70f4d5e7e2ec01b143c46e2c03b9e551d
Status: Downloaded newer image for registry:2.7
d7e449a8a93e71c9a7d99c67470bd7e7a723eee5ae97b3f7a2a8a1cf25982cc3
</code></pre>
<p>使用<code>docker ps</code>命令查看一下刚才启动的容器:</p>
<pre><code>$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d7e449a8a93e registry:2.7 &quot;/entrypoint.sh /etc…&quot; 50 seconds ago Up 49 seconds 0.0.0.0:5000-&gt;5000/tcp registry
</code></pre>
<p>此时我们就拥有了一个私有镜像仓库,访问地址为<code>localhost</code>,端口号为 5000。</p>
<h4>推送镜像到本地仓库</h4>
<p>我们依旧使用 busybox 镜像举例。首先我们使用<code>docker tag</code>命令把 busybox 镜像&quot;重命名&quot;<code>localhost:5000/busybox</code></p>
<pre><code>$ docker tag busybox localhost:5000/busybox
</code></pre>
<p>此时 Docker 为<code>busybox</code>镜像创建了一个别名<code>localhost:5000/busybox</code><code>localhost:5000</code>为主机名和端口Docker 将会把镜像推送到这个地址。
使用<code>docker push</code>推送镜像到本地仓库:</p>
<pre><code>$ docker push localhost:5000/busybox
The push refers to repository [localhost:5000/busybox]
514c3a3e64d4: Layer already exists
latest: digest: sha256:400ee2ed939df769d4681023810d2e4fb9479b8401d97003c710d0e20f7c49c6 size: 527
</code></pre>
<p>这里可以看到,我们已经可以把<code>busybox</code>推送到了本地镜像仓库。</p>
<p>此时,我们验证一下从本地镜像仓库拉取镜像。首先,我们删除本地的<code>busybox</code><code>localhost:5000/busybox</code>镜像。</p>
<pre><code>$ docker rmi busybox localhost:5000/busybox
Untagged: busybox:latest
Untagged: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5032252329323f2810233831626566">[email&#160;protected]</a>:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977
Untagged: localhost:5000/busybox:latest
Untagged: localhost:5000/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="066473757f64697e46756e67343330">[email&#160;protected]</a>:400ee2ed939df769d4681023810d2e4fb9479b8401d97003c710d0e20f7c49c6
</code></pre>
<p>查看一下本地<code>busybox</code>镜像:</p>
<pre><code>$ docker image ls busybox
REPOSITORY TAG IMAGE ID CREATED SIZE
</code></pre>
<p>可以看到此时本地已经没有<code>busybox</code>这个镜像了。下面,我们从本地镜像仓库拉取<code>busybox</code>镜像:</p>
<pre><code>$ docker pull localhost:5000/busybox
Using default tag: latest
latest: Pulling from busybox
Digest: sha256:400ee2ed939df769d4681023810d2e4fb9479b8401d97003c710d0e20f7c49c6
Status: Downloaded newer image for localhost:5000/busybox:latest
localhost:5000/busybox:latest
</code></pre>
<p>然后再使用<code>docker image ls busybox</code>命令,这时可以看到我们已经成功从私有镜像仓库拉取<code>busybox</code>镜像到本地了</p>
<h4>持久化镜像存储</h4>
<p>我们知道,容器是无状态的。上面私有仓库的启动方式可能会导致镜像丢失,因为我们并没有把仓库的数据信息持久化到主机磁盘上,这在生产环境中是无法接受的。下面我们使用以下命令将镜像持久化到主机目录:</p>
<pre><code>$ docker run -v /var/lib/registry/data:/var/lib/registry -d -p 5000:5000 --name registry registry:2.7
</code></pre>
<p>我们在上面启动<code>registry</code>的命令中加入了<code>-v /var/lib/registry/data:/var/lib/registry</code><code>-v</code>的含义是把 Docker 容器的某个目录或文件挂载到主机上,保证容器被重建后数据不丢失。<code>-v</code>参数冒号前面为主机目录,冒号后面为容器内目录。</p>
<blockquote>
<p>事实上registry 的持久化存储除了支持本地文件系统还支持很多种类型,例如 S3、Google Cloud Platform、Microsoft Azure Blob Storage Service 等多种存储类型。</p>
</blockquote>
<p>到这里我们的镜像仓库虽然可以本地访问和拉取,但是如果你在另外一台机器上是无法通过 Docker 访问到这个镜像仓库的,因为 Docker 要求非<code>localhost</code>访问的镜像仓库必须使用 HTTPS这时候就需要构建外部可访问的镜像仓库。</p>
<h4>构建外部可访问的镜像仓库</h4>
<p>要构建一个支持 HTTPS 访问的安全镜像仓库,需要满足以下两个条件:</p>
<ul>
<li>拥有一个合法的域名,并且可以正确解析到镜像服务器;</li>
<li>从证书颁发机构CA获取一个证书。</li>
</ul>
<p>在准备好域名和证书后,就可以部署我们的镜像服务器了。这里我以<code>regisry.lagoudocker.io</code>这个域名为例。首先准备存放证书的目录<code>/var/lib/registry/certs</code>,然后把申请到的证书私钥和公钥分别放到该目录下。 假设我们申请到的证书文件分别为<code>regisry.lagoudocker.io.crt</code><code>regisry.lagoudocker.io.key</code></p>
<p>如果上一步启动的仓库容器还在运行,我们需要先停止并删除它。</p>
<pre><code>$ docker stop registry &amp;&amp; docker rm registry
</code></pre>
<p>然后使用以下命令启动新的镜像仓库:</p>
<pre><code>$ docker run -d \
--name registry \
-v &quot;/var/lib/registry/data:/var/lib/registry \
-v &quot;/var/lib/registry/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/regisry.lagoudocker.io.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/regisry.lagoudocker.io.key \
-p 443:443 \
registry:2.7
</code></pre>
<p>这里,我们使用 -v 参数把镜像数据持久化在<code>/var/lib/registry/data</code>目录中,同时把主机上的证书文件挂载到了容器的 /certs 目录下,同时通过 -e 参数设置 HTTPS 相关的环境变量参数,最后让仓库在主机上监听 443 端口。</p>
<p>仓库启动后,我们就可以远程推送镜像了。</p>
<pre><code>$ docker tag busybox regisry.lagoudocker.io/busybox
$ docker push regisry.lagoudocker.io/busybox
</code></pre>
<h4>私有仓库进阶</h4>
<p>Docker 官方开源的镜像仓库<code>Distribution</code>仅满足了镜像存储和管理的功能,用户权限管理相对较弱,并且没有管理界面。</p>
<p>如果你想要构建一个企业的镜像仓库,<a href="https://goharbor.io/">Harbor</a> 是一个非常不错的解决方案。Harbor 是一个基于<code>Distribution</code>项目开发的一款企业级镜像管理软件,拥有 RBAC (基于角色的访问控制)、管理用户界面以及审计等非常完善的功能。目前已经从 CNCF 毕业,这代表它已经有了非常高的软件成熟度。</p>
<p><img src="assets/CgqCHl9bMHCAFgcMAABNmNOujV4312.png" alt="png" /></p>
<p>图 5 Harbor 官网</p>
<p>Harbor 的使命是成为 Kubernetes 信任的云原生镜像仓库。 Harbor 需要结合 Kubernetes 才能发挥其最大价值,因此,在这里我就不展开介绍 Harbor 了。如果你对 Harbor 构建企业级镜像仓库感兴趣,可以到它的<a href="https://goharbor.io/">官网</a>了解更多。</p>
<h3>结语</h3>
<p>到此,相信你不仅可以使用公共镜像仓库存储和拉取镜像,还可以自己动手搭建一个私有的镜像仓库。那当你使用 Docker Hub 拉取镜像很慢的时候,你知道如何加快镜像的拉取速度吗?思考后,可以把你的想法写在留言区。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/由浅入深吃透 Docker-完/04 容器操作:得心应手掌握 Docker 容器基本操作.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/由浅入深吃透 Docker-完/06 最佳实践:如何在生产中编写最优 Dockerfile.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997b425f1f3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>