mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-25 20:56:42 +08:00
689 lines
22 KiB
HTML
689 lines
22 KiB
HTML
<!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>24 练习篇:K8s 集群配置测验.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="/专栏/Kubernetes 实践入门指南/00 为什么我们要学习 Kubernetes 技术.md.html">00 为什么我们要学习 Kubernetes 技术.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/01 重新认识 Kubernetes 的核心组件.md.html">01 重新认识 Kubernetes 的核心组件.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/02 深入理解 Kubernets 的编排对象.md.html">02 深入理解 Kubernets 的编排对象.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/03 DevOps 场景下落地 K8s 的困难分析.md.html">03 DevOps 场景下落地 K8s 的困难分析.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/04 微服务应用场景下落地 K8s 的困难分析.md.html">04 微服务应用场景下落地 K8s 的困难分析.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/05 解决 K8s 落地难题的方法论提炼.md.html">05 解决 K8s 落地难题的方法论提炼.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/06 练习篇:K8s 核心实践知识掌握.md.html">06 练习篇:K8s 核心实践知识掌握.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/07 容器引擎 containerd 落地实践.md.html">07 容器引擎 containerd 落地实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/08 K8s 集群安装工具 kubeadm 的落地实践.md.html">08 K8s 集群安装工具 kubeadm 的落地实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/09 南北向流量组件 IPVS 的落地实践.md.html">09 南北向流量组件 IPVS 的落地实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/10 东西向流量组件 Calico 的落地实践.md.html">10 东西向流量组件 Calico 的落地实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/11 服务发现 DNS 的落地实践.md.html">11 服务发现 DNS 的落地实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/12 练习篇:K8s 集群配置测验.md.html">12 练习篇:K8s 集群配置测验.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/13 理解对方暴露服务的对象 Ingress 和 Service.md.html">13 理解对方暴露服务的对象 Ingress 和 Service.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/14 应用网关 OpenResty 对接 K8s 实践.md.html">14 应用网关 OpenResty 对接 K8s 实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/15 Service 层引流技术实践.md.html">15 Service 层引流技术实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/16 Cilium 容器网络的落地实践.md.html">16 Cilium 容器网络的落地实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/17 应用流量的优雅无损切换实践.md.html">17 应用流量的优雅无损切换实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/18 练习篇:应用流量无损切换技术测验.md.html">18 练习篇:应用流量无损切换技术测验.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/19 使用 Rook 构建生产可用存储环境实践.md.html">19 使用 Rook 构建生产可用存储环境实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/20 有状态应用的默认特性落地分析.md.html">20 有状态应用的默认特性落地分析.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/21 案例:分布式 MySQL 集群工具 Vitess 实践分析.md.html">21 案例:分布式 MySQL 集群工具 Vitess 实践分析.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/22 存储对象 PV、PVC、Storage Classes 的管理落地实践.md.html">22 存储对象 PV、PVC、Storage Classes 的管理落地实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/23 K8s 集群中存储对象灾备的落地实践.md.html">23 K8s 集群中存储对象灾备的落地实践.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
<a class="current-tab" href="/专栏/Kubernetes 实践入门指南/24 练习篇:K8s 集群配置测验.md.html">24 练习篇:K8s 集群配置测验.md.html</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>24 练习篇:K8s 集群配置测验</h1>
|
||
|
||
<p>第二部分的内容围绕 Kubernetes 核心组件的安装配置一一给大家拆解了一遍,当前集群组件最主流的配置就是这些:Containerd、kubeadm、IPVS、Calico、kube-dns。读者通过官方文档就可以独立配置一套集群,只是笔者发现,因为集群配置的过度复杂,能获得的环境也是千差万别,很难得到统一的认知。本篇测验的目的就是带着大家一起校验一遍我们学习到的经验,一起搭建一套集群的全过程,以此来校验我们掌握的集群知识。</p>
|
||
|
||
<h3>环境</h3>
|
||
|
||
<p>从第一天接触容器技术之后,我们想解决的问题就是环境依赖问题。因为 Docker 是让环境包裹这应用一起制作成镜像分发的。等我们配置 Kubernetes 集群的时候,我们操作的最小单元是 Pod,你可以理解为是一个容器组,这个容器组并不是简单的把一组容器放一起就完事了。它的设计巧妙之处在于以 pause 为核心的基础容器把相关应用的所有环境依赖都掌握在自己的运行时里面。其它相关业务容器只是加入到这个运行时里面,这些业务容器出现问题并不会破坏环境。这是 Kubernetes 构建业务集群的核心设计,非常巧妙地解决了应用服务的可用性问题。</p>
|
||
|
||
<p>现在我们要选择操作系统的版本了。你会发现并没有任何官方文档说过,哪一个版本是指定的。其实官方并没有这样的约定。因为容器的目的就是解决环境的依赖,但是这么多年的演进,说得更清楚一点,我们仍然有一个核心依赖就是 Kernel 依赖搞不定。Kernel 的特性会决定容器的特性,我们一般在选择上会参考 Docker 的版本来定,主流的有 18.09、19.03 等。</p>
|
||
|
||
<p>你发现没有,你并不能保证在特定的环境下这些 Docker 版本没有问题,这就是我们在配置生产环境中出现问题自己埋下的坑。如果你是企业内部使用,最好的办法是建立基准线,明确版本号,在大量实践的基础上投入人力来维护这个版本的稳定性。因为容器技术发展很快,现在 Kubernetes 已经和 Docker 越来越规避,都在使用 containerd 来支持底层容器运行时的管理,作为用户我们是无法回避这个。</p>
|
||
|
||
<p>这里又突显一个问题,因为组件的变革,我到底应该选择哪个版本呢,它们稳定吗?因为 Kubernetes 是开源社区推动的软件,我们一定要遵循开源的方式来使用这些软件才能得到正确的经验。我总结出来的经验如下,方便大家参考:</p>
|
||
|
||
<p>x86-64 仍然是当前对容器最好的系统架构体系,目前主流的系统聚集在 RedHat/CentOS 7.x 系列、Ubuntu 16.04 系列。对于内核红帽系主要在 3.10 以上,Ubuntu 能到 4.4 以上。有些用户会通过开源 Kernel 仓库把红帽系的 Kernel 升级到 4.4,也比较常见。升级内核的代价就是引入很多未知的模块,让系统变得不稳定。ARM 系统架构会对整个 Kubernetes 组件的文件格式产生兼容性要求,在选择适配的时候,一定要注意有没有准备好 Kubernetes 相应的组件。总结下来,主流的操作系统主要是红帽的 7.x 系列和 Ubuntu LTS 系列 16.04。升级大版本操作系统对 Kubernetes 来说,需要做很多适配工作,目前开源社区是不太可能帮用户做的。一定注意。</p>
|
||
|
||
<p>Kubernetes 的版本更新很快,整个社区会维护 3 个主线版本,如现在主要为 1.16.x、1.17.x、1.18.x。这个 x 版本号差不多 2 周就一个迭代,主要是修复 Bug。很多团队在使用上总结了一些技巧,比如取奇数版本或者偶数版本作为自己的主力版本,这个做法的目的就是规避最新版本带来的不稳定性。并不是说奇数版本好或者是偶数版本稳定,这是纯属瞎猜。作为开源软件,它的质量是社区在维护,落实到用户这里,就是大家都是小白鼠,需要在自己的环境试验验证组件的可靠性。总结下来,主流的环境还是选择比最新版本低 1 个或者 2 个子版本作为周期来当做自己的软件来维护。维护开源软件不是免费的,它是通过大家的努力才能保证组件的使用可靠性的。</p>
|
||
|
||
<p>除了 Kubernetes 主线版本的选择我们应该延迟 1 到 2 个版本之外,对于其它附属组件如 Calico、kube-dns、Containerd 等,应该需要选择最新版本。主要原因在于它们是一线运行的组件,被调用的次数是更多的,发现问题的机会更突出。越早发现问题越快得到修复。这又是开源里面的原则,就是越早发现、越早修复,组件越稳定。很多用户在组件选择上,会比较保守,导致很多修复过的 Bug 还存在于你的集群中,让不确定性得到蔓延。总结下来,跑容器的一线组件应该使用最新版本,越早发现,你的程序越稳固。言下之意,当开源小白鼠,咱们也要有对策,通过自动化测试的环境,把这些组件多测测。</p>
|
||
|
||
<p>很多以为 Kubernetes 安装上之后就完事大吉,环境的事情就不用操心了。诚然,通过容器确实可以解决一部分运维的问题。但是应用架构的可靠性并不能依靠 Kubernetes。为什么在有了 容器之后,在 DevOps 领域开始引入了 SRE 的概念,就是说业务保障一直是业务核心能力,不能依赖 Kubernetes。用了 Kubernetes 之后,你更要关注架构的稳定性。</p>
|
||
|
||
<h4>kubeadm 的配置测验</h4>
|
||
|
||
<p>kubeadm 推出的初衷是为了用更平滑的方式来安装、升级 Kubernetes。在早期我是排斥的,因为二进制的安装方式好像更简洁,排错也更方便。但是随着安装经验的丰富,我发现二进制的安装还是无法标准化,配置起来手工操作的地方很多,无法满足一键安装的目的。kubeadm 是唯一被官方认可的安装项目,可以说明社区对它云原生的安装配置方式的认可。</p>
|
||
|
||
<p>这里引出的问题就是,kubeadm 是不是安装的结果都是一样呢?不是的。它考虑的单机模式、高可用模式、组件混合镜像模式、组件分组镜像模式,这些让用户在安装的时候会遇到很多选择,也会产生出一些不可知的问题。</p>
|
||
|
||
<p>因为 kubeadm 的版本的不同,它的安装过程和细节都会微调,我们应该尽量使用最新版本的 kubeadm 来安装,这样就可以得到很多一键部署的好处。很多原来手工需要做的事情,如自签名证书的签发都是 kubeadm 自动帮你做了,另外如安装之前的环境调优的参数也会自动帮你生成。这些操作都是之前需要手工考虑并自己手工执行的。目前笔者的经验就是:以官方文档为基准,选择最新版本的 kubeadm 为最佳组件,然后做 Kubernetes 的安装规划工作,目前还没有碰到什么难题。</p>
|
||
|
||
<h4>Calico、IPVS、kube-dns 组件的配置测验</h4>
|
||
|
||
<p>很多用户谈到容器网络就色变,因为之前容器网络的方案太多,让用户根本选择不清楚。每家都说自己的网络方案好,但是其实每家的解决方案都不是最完美的方案。目前,容器网络方案并没有官方推荐的方案。从 Kubernetes 官方文档中介绍到,网络这个范畴是不包括在集群组件中的。这是社区的选择,无可厚非。但是我们安装集群,如果没有配置网络,这个 Kubernetes 是无法承载业务容器的。</p>
|
||
|
||
<p>为了这个实际问题,我们要选择一个合适的方案。</p>
|
||
|
||
<p>为什么说 Calico 是当前最理想的方案,主要原因是它的配置简单,在 100 台物理机规模下通过 IPIP 模式创建的容器网络,性能已经接近主机网卡模式,损耗很小。这个是以前版本的 Calico 无法解决的,现在最新版本经过测试发现性能提升不少。对于 kube-dns,它对容器网络还是有依赖,只有你有了容器网络之后,kube-dns 才能正常工作。目前主流的是 CoreDns,在 100 台物理机下同样性能很好,目前运行上并没有遇到什么大问题。</p>
|
||
|
||
<p>IPVS 是 Kernel 的内核模块,主要代替 kube-proxy 的南北向流量。但是因为它的功能还是局限在 proxy 之上,对于 kube-proxy 的东西向流量的支持还是不行的,需要靠 iptables 来转换。华为通过压测发现之前用 iptables 来解决南北向流量的性能瓶颈,这才提出的 IPVS 的方案。随着现在 eBPF 可编程数据包的出现,让 IPVS 的方案开始进入到一个过度阶段。因为 eBPF 技术不仅仅支持南北向,也能支持东西向,可以完美替换到 iptables 的工作能力。毕竟 iptables 是为防火墙设计的,复杂并动态变更的规则会对系统带来影响,从而导致对业务的影响,这些都是用户不愿意看到的。</p>
|
||
|
||
<p>总结下来,这些组件的配置目前来看都有默认配置,基本上配置一次就不用更改,大家只要能验证能否正常工作就可以,没有必要花费太大精力在这些组件的配置上。</p>
|
||
|
||
<h3>总结</h3>
|
||
|
||
<p>所谓 Kubernetes 的配置测验,依靠的是大家动手去安装,单靠例子的示范很难让你获得一手的经验。但是测验的目的是让你能明白在安装过程中遇到的问题和解决方案。我们要感受到开源软件的不同之处,它利用社区的力量来维护版本的稳定性。</p>
|
||
|
||
<p>大家安装的时候肯定会遇到这样那样的问题,除了自己验证排错之外,你可以理直气壮的在社区的问题列表栏上写下自己的问题,方便大家互通有无。很多国内的用户并没有理解这种交互带来的价值,因为开源运动是一种社交活动,它依赖用户的互动,如果大家都不去反馈,这个软件只会变的越来越差。</p>
|
||
|
||
<p>大家要理解 kubeadm 的意义,就是未来可能更多的配置要被默认值替代,大家只需要一键执行就可以获得一套性能可观的 Kubernetes 环境。这个需要时间,我们可以期待。</p>
|
||
|
||
<p>参考:</p>
|
||
|
||
<ul>
|
||
|
||
<li><a href="https://kubernetes.io/blog/2017/01/stronger-foundation-for-creating-and-managing-kubernetes-clusters/">https://kubernetes.io/blog/2017/01/stronger-foundation-for-creating-and-managing-kubernetes-clusters/</a></li>
|
||
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/Kubernetes 实践入门指南/23 K8s 集群中存储对象灾备的落地实践.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
|
||
</div>
|
||
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099728aab283d60","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>
|
||
|