first commits
This commit is contained in:
741
public/2017/11/03/hello-world/index.html
Normal file
741
public/2017/11/03/hello-world/index.html
Normal file
@@ -0,0 +1,741 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head><script src="/livereload.js?mindelay=10&v=2&port=1313&path=livereload" data-no-instant defer></script>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
|
||||
const autoTheme = false;
|
||||
if (autoTheme) {
|
||||
document.documentElement.setAttribute('data-auto-theme', 'true');
|
||||
}
|
||||
|
||||
const theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<meta property="og:site_name" content="David Young">
|
||||
<meta property="og:type" content="article">
|
||||
|
||||
|
||||
<meta property="og:image" content="http://localhost:1313/https://img.zhaohuabing.com/post-bg-2015.jpg">
|
||||
<meta property="twitter:image" content="http://localhost:1313/https://img.zhaohuabing.com/post-bg-2015.jpg" />
|
||||
|
||||
|
||||
|
||||
<meta name="title" content="Welcome to Zhaohuabing Blog" />
|
||||
<meta property="og:title" content="Welcome to Zhaohuabing Blog" />
|
||||
<meta property="twitter:title" content="Welcome to Zhaohuabing Blog" />
|
||||
|
||||
|
||||
|
||||
<meta name="description" content="Just About Everything">
|
||||
<meta property="og:description" content="Just About Everything" />
|
||||
<meta property="twitter:description" content="Just About Everything" />
|
||||
|
||||
|
||||
<meta property="og:url" content="http://localhost:1313/2017/11/03/hello-world/" />
|
||||
|
||||
|
||||
<meta property="twitter:card" content="summary" />
|
||||
|
||||
|
||||
|
||||
<meta name="keyword" content="Von Balthasar, Scripture, Gravel Riding, Ham Radio, Divine Office, Open Source">
|
||||
<link rel="shortcut icon" href="/img/favicon.ico">
|
||||
|
||||
<title>Welcome to Zhaohuabing Blog | David Young Blog</title>
|
||||
|
||||
<link rel="canonical" href="/2017/11/03/hello-world/">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/hugo-theme-cleanwhite.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/theme-variables.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/zanshang.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/font-awesome.all.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/hux-blog.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/lazysizes.min.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="navbar-header page-scroll">
|
||||
<button type="button" class="navbar-toggle">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">David Young</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="huxblog_navbar">
|
||||
<div class="navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="/">All Posts</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="/categories/life/">life</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tech/">tech</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tips/">tips</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li><a href="/archive//">ARCHIVE</a></li>
|
||||
|
||||
<li><a href="/notes//">NOTES</a></li>
|
||||
|
||||
<li><a href="/about//">ABOUT</a></li>
|
||||
|
||||
<li>
|
||||
<a href="/search"><i class="fa fa-search"></i></a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#" id="theme-toggle" title="Toggle dark mode" style="opacity: 0;">
|
||||
<i class="fa fa-moon"></i>
|
||||
<i class="fa fa-sun" style="display: none;"></i>
|
||||
</a>
|
||||
</li>
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
var theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
var toggleBtn = document.getElementById('theme-toggle');
|
||||
if (toggleBtn) {
|
||||
var moonIcon = toggleBtn.querySelector('.fa-moon');
|
||||
var sunIcon = toggleBtn.querySelector('.fa-sun');
|
||||
|
||||
if (theme === 'dark') {
|
||||
if (moonIcon) moonIcon.style.display = 'none';
|
||||
if (sunIcon) sunIcon.style.display = 'inline';
|
||||
toggleBtn.setAttribute('title', 'Switch to light mode');
|
||||
} else {
|
||||
if (moonIcon) moonIcon.style.display = 'inline';
|
||||
if (sunIcon) sunIcon.style.display = 'none';
|
||||
toggleBtn.setAttribute('title', 'Switch to dark mode');
|
||||
}
|
||||
|
||||
|
||||
requestAnimationFrame(function() {
|
||||
toggleBtn.style.transition = 'opacity 0.2s ease';
|
||||
toggleBtn.style.opacity = '1';
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
var $body = document.body;
|
||||
var $toggle = document.querySelector('.navbar-toggle');
|
||||
var $navbar = document.querySelector('#huxblog_navbar');
|
||||
var $collapse = document.querySelector('.navbar-collapse');
|
||||
|
||||
$toggle.addEventListener('click', handleMagic)
|
||||
function handleMagic(e){
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
|
||||
setTimeout(function(){
|
||||
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}else{
|
||||
|
||||
$collapse.style.height = "auto"
|
||||
$navbar.className += " in";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var navLinks = document.querySelectorAll('.navbar-collapse a');
|
||||
navLinks.forEach(function(link) {
|
||||
link.addEventListener('click', function() {
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
setTimeout(function(){
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
header.intro-header {
|
||||
background-image: url('https://img.zhaohuabing.com/post-bg-2015.jpg')
|
||||
}
|
||||
</style>
|
||||
|
||||
<header class="intro-header" >
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<div class="post-heading">
|
||||
<div class="tags">
|
||||
|
||||
</div>
|
||||
<h1>Welcome to Zhaohuabing Blog</h1>
|
||||
<h2 class="subheading">Hello World, Hello Blog</h2>
|
||||
<span class="meta">
|
||||
|
||||
Posted by
|
||||
|
||||
赵化冰
|
||||
|
||||
on
|
||||
Saturday, November 4, 2017
|
||||
|
||||
|
||||
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
|
||||
<article>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-8 col-lg-offset-2
|
||||
col-md-10 col-md-offset-1
|
||||
post-container">
|
||||
|
||||
|
||||
<blockquote>
|
||||
<p>“Yeah It’s on. ”</p>
|
||||
</blockquote>
|
||||
<h2 id="hello-world">Hello World!</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="entry-shang text-center">
|
||||
|
||||
<p>「真诚赞赏,手留余香」</p>
|
||||
|
||||
<button class="zs show-zs btn btn-bred">赞赏支持</button>
|
||||
</div>
|
||||
<div class="zs-modal-bg"></div>
|
||||
<div class="zs-modal-box">
|
||||
<div class="zs-modal-head">
|
||||
<button type="button" class="close">×</button>
|
||||
<span class="author"><a href="http://localhost:1313/"><img src="/img/favicon.png" />David Young</a></span>
|
||||
|
||||
<p class="tip"><i></i><span>真诚赞赏,手留余香</span></p>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="zs-modal-body">
|
||||
<div class="zs-modal-btns">
|
||||
<button class="btn btn-blink" data-num="2">2元</button>
|
||||
<button class="btn btn-blink" data-num="5">5元</button>
|
||||
<button class="btn btn-blink" data-num="10">10元</button>
|
||||
<button class="btn btn-blink" data-num="50">50元</button>
|
||||
<button class="btn btn-blink" data-num="100">100元</button>
|
||||
<button class="btn btn-blink" data-num="1">任意金额</button>
|
||||
</div>
|
||||
<div class="zs-modal-pay">
|
||||
<button class="btn btn-bred" id="pay-text">2元</button>
|
||||
<p>使用<span id="pay-type">微信</span>扫描二维码完成支付</p>
|
||||
<img src="/img/reward/wechat-2.png" id="pay-image"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zs-modal-footer">
|
||||
<label><input type="radio" name="zs-type" value="wechat" class="zs-type" checked="checked"><span ><span class="zs-wechat"><img src="/img/reward/wechat-btn.png"/></span></label>
|
||||
<label><input type="radio" name="zs-type" value="alipay" class="zs-type" class="zs-alipay"><img src="/img/reward/alipay-btn.png"/></span></label>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/js/reward.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
<ul class="pager">
|
||||
|
||||
|
||||
<li class="next">
|
||||
<a href="/2017/11/04/istio-install_and_example/" data-toggle="tooltip" data-placement="top" title="Istio及Bookinfo示例程序安装试用笔记">Next
|
||||
Post →</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link href="https://xxx.xxx.com/dist/Artalk.css" rel="stylesheet" />
|
||||
<script src="https://xxx.xxx.com/dist/Artalk.js"></script>
|
||||
|
||||
|
||||
<div id="Comments"></div>
|
||||
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: 'http:\/\/localhost:1313\/2017\/11\/03\/hello-world\/',
|
||||
pageTitle: 'Welcome to Zhaohuabing Blog',
|
||||
server: 'https:\/\/xxx.xxx.com',
|
||||
site: 'xxx blog',
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-2 col-lg-offset-0
|
||||
visible-lg-block
|
||||
sidebar-container
|
||||
catalog-container">
|
||||
<div class="side-catalog">
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5>
|
||||
<a class="catalog-toggle" href="#">CATALOG</a>
|
||||
</h5>
|
||||
<ul class="catalog-body"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-8 col-lg-offset-2
|
||||
col-md-10 col-md-offset-1
|
||||
sidebar-container">
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5><a href="/tags/">FEATURED TAGS</a></h5>
|
||||
<div class="tags">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/docker" title="docker">
|
||||
docker
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/istio" title="istio">
|
||||
istio
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/kubernetes" title="kubernetes">
|
||||
kubernetes
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/microservice" title="microservice">
|
||||
microservice
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/security" title="security">
|
||||
security
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/service-mesh" title="service mesh">
|
||||
service mesh
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/tips" title="tips">
|
||||
tips
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr>
|
||||
<h5>FRIENDS</h5>
|
||||
<ul class="list-inline">
|
||||
|
||||
<li><a target="_blank" href="https://zhaozhihan.com">Linda的博客</a></li>
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
|
||||
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<ul class="list-inline text-center">
|
||||
|
||||
<li>
|
||||
<a href="mailto:youremail@gmail.com">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-envelope fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="/your%20wechat%20qr%20code%20image">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-weixin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://github.com/yourgithub">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://www.linkedin.com/in/yourlinkedinid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-linkedin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://stackoverflow.com/users/yourstackoverflowid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-stack-overflow fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a href='' rel="alternate" type="application/rss+xml" title="David Young" >
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-rss fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<p class="copyright text-muted">
|
||||
Copyright © David Young 2026
|
||||
|
||||
<br>
|
||||
<a href="https://themes.gohugo.io/hugo-theme-cleanwhite">CleanWhite Hugo Theme</a> by <a href="https://zhaohuabing.com">Huabing</a> |
|
||||
<iframe
|
||||
style="margin-left: 2px; margin-bottom:-5px;"
|
||||
frameborder="0" scrolling="0" width="100px" height="20px"
|
||||
src="https://ghbtns.com/github-btn.html?user=zhaohuabing&repo=hugo-theme-cleanwhite&type=star&count=true" >
|
||||
</iframe>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
function loadAsync(u, c) {
|
||||
var d = document, t = 'script',
|
||||
o = d.createElement(t),
|
||||
s = d.getElementsByTagName(t)[0];
|
||||
o.src = u;
|
||||
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
|
||||
s.parentNode.insertBefore(o, s);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
if($('#tag_cloud').length !== 0){
|
||||
loadAsync("/js/jquery.tagcloud.js",function(){
|
||||
$.fn.tagcloud.defaults = {
|
||||
|
||||
color: {start: '#bbbbee', end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
function updateTagcloudColors() {
|
||||
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
const startColor = isDark ? '#808080' : '#bbbbee';
|
||||
if($('#tag_cloud').length !== 0 && $.fn.tagcloud) {
|
||||
$.fn.tagcloud.defaults = {
|
||||
color: {start: startColor, end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
updateTagcloudColors();
|
||||
});
|
||||
|
||||
|
||||
const observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.attributeName === 'data-theme') {
|
||||
updateTagcloudColors();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-theme']
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
loadAsync("https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js", function(){
|
||||
var $nav = document.querySelector("nav");
|
||||
if($nav) FastClick.attach($nav);
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<script src="/js/theme-toggle.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
function generateCatalog(selector) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_containerSelector = 'div.post-container'
|
||||
|
||||
|
||||
|
||||
var P = $(_containerSelector), a, n, t, l, i, c;
|
||||
a = P.find('h1,h2,h3,h4,h5,h6');
|
||||
|
||||
|
||||
$(selector).html('')
|
||||
|
||||
|
||||
a.each(function () {
|
||||
n = $(this).prop('tagName').toLowerCase();
|
||||
i = "#" + $(this).prop('id');
|
||||
t = $(this).text();
|
||||
c = $('<a href="' + i + '" rel="nofollow" title="' + t + '">' + t + '</a>');
|
||||
l = $('<li class="' + n + '_nav"></li>').append(c);
|
||||
$(selector).append(l);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
generateCatalog(".catalog-body");
|
||||
|
||||
|
||||
$(".catalog-toggle").click((function (e) {
|
||||
e.preventDefault();
|
||||
$('.side-catalog').toggleClass("fold")
|
||||
}))
|
||||
|
||||
|
||||
|
||||
|
||||
loadAsync("\/js\/jquery.nav.js", function () {
|
||||
$('.catalog-body').onePageNav({
|
||||
currentClass: "active",
|
||||
changeHash: !1,
|
||||
easing: "swing",
|
||||
filter: "",
|
||||
scrollSpeed: 700,
|
||||
scrollOffset: 0,
|
||||
scrollThreshold: .2,
|
||||
begin: null,
|
||||
end: null,
|
||||
scrollChange: null,
|
||||
padding: 80
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
1043
public/2017/11/04/istio-install_and_example/index.html
Normal file
1043
public/2017/11/04/istio-install_and_example/index.html
Normal file
File diff suppressed because it is too large
Load Diff
811
public/2017/11/07/istio-traffic-shifting/index.html
Normal file
811
public/2017/11/07/istio-traffic-shifting/index.html
Normal file
@@ -0,0 +1,811 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head><script src="/livereload.js?mindelay=10&v=2&port=1313&path=livereload" data-no-instant defer></script>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
|
||||
const autoTheme = false;
|
||||
if (autoTheme) {
|
||||
document.documentElement.setAttribute('data-auto-theme', 'true');
|
||||
}
|
||||
|
||||
const theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<meta property="og:site_name" content="David Young">
|
||||
<meta property="og:type" content="article">
|
||||
|
||||
|
||||
<meta property="og:image" content="http://localhost:1313//img/istio-traffic-shifting/crossroads.png">
|
||||
<meta property="twitter:image" content="http://localhost:1313//img/istio-traffic-shifting/crossroads.png" />
|
||||
|
||||
|
||||
|
||||
<meta name="title" content="使用Istio实现应用流量转移" />
|
||||
<meta property="og:title" content="使用Istio实现应用流量转移" />
|
||||
<meta property="twitter:title" content="使用Istio实现应用流量转移" />
|
||||
|
||||
|
||||
|
||||
<meta name="description" content="本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio,可以使用一系列不同权重的规则(10%,20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。">
|
||||
<meta property="og:description" content="本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio,可以使用一系列不同权重的规则(10%,20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。" />
|
||||
<meta property="twitter:description" content="本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio,可以使用一系列不同权重的规则(10%,20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。" />
|
||||
|
||||
|
||||
<meta property="og:url" content="http://localhost:1313/2017/11/07/istio-traffic-shifting/" />
|
||||
|
||||
|
||||
<meta property="twitter:card" content="summary" />
|
||||
|
||||
|
||||
|
||||
<meta name="keyword" content="Von Balthasar, Scripture, Gravel Riding, Ham Radio, Divine Office, Open Source">
|
||||
<link rel="shortcut icon" href="/img/favicon.ico">
|
||||
|
||||
<title>使用Istio实现应用流量转移 | David Young Blog</title>
|
||||
|
||||
<link rel="canonical" href="/2017/11/07/istio-traffic-shifting/">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/hugo-theme-cleanwhite.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/theme-variables.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/zanshang.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/font-awesome.all.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/hux-blog.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/lazysizes.min.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="navbar-header page-scroll">
|
||||
<button type="button" class="navbar-toggle">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">David Young</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="huxblog_navbar">
|
||||
<div class="navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="/">All Posts</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="/categories/life/">life</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tech/">tech</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tips/">tips</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li><a href="/archive//">ARCHIVE</a></li>
|
||||
|
||||
<li><a href="/notes//">NOTES</a></li>
|
||||
|
||||
<li><a href="/about//">ABOUT</a></li>
|
||||
|
||||
<li>
|
||||
<a href="/search"><i class="fa fa-search"></i></a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#" id="theme-toggle" title="Toggle dark mode" style="opacity: 0;">
|
||||
<i class="fa fa-moon"></i>
|
||||
<i class="fa fa-sun" style="display: none;"></i>
|
||||
</a>
|
||||
</li>
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
var theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
var toggleBtn = document.getElementById('theme-toggle');
|
||||
if (toggleBtn) {
|
||||
var moonIcon = toggleBtn.querySelector('.fa-moon');
|
||||
var sunIcon = toggleBtn.querySelector('.fa-sun');
|
||||
|
||||
if (theme === 'dark') {
|
||||
if (moonIcon) moonIcon.style.display = 'none';
|
||||
if (sunIcon) sunIcon.style.display = 'inline';
|
||||
toggleBtn.setAttribute('title', 'Switch to light mode');
|
||||
} else {
|
||||
if (moonIcon) moonIcon.style.display = 'inline';
|
||||
if (sunIcon) sunIcon.style.display = 'none';
|
||||
toggleBtn.setAttribute('title', 'Switch to dark mode');
|
||||
}
|
||||
|
||||
|
||||
requestAnimationFrame(function() {
|
||||
toggleBtn.style.transition = 'opacity 0.2s ease';
|
||||
toggleBtn.style.opacity = '1';
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
var $body = document.body;
|
||||
var $toggle = document.querySelector('.navbar-toggle');
|
||||
var $navbar = document.querySelector('#huxblog_navbar');
|
||||
var $collapse = document.querySelector('.navbar-collapse');
|
||||
|
||||
$toggle.addEventListener('click', handleMagic)
|
||||
function handleMagic(e){
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
|
||||
setTimeout(function(){
|
||||
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}else{
|
||||
|
||||
$collapse.style.height = "auto"
|
||||
$navbar.className += " in";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var navLinks = document.querySelectorAll('.navbar-collapse a');
|
||||
navLinks.forEach(function(link) {
|
||||
link.addEventListener('click', function() {
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
setTimeout(function(){
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
header.intro-header {
|
||||
background-image: url('/img/istio-traffic-shifting/crossroads.png')
|
||||
}
|
||||
</style>
|
||||
|
||||
<header class="intro-header" >
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<div class="post-heading">
|
||||
<div class="tags">
|
||||
|
||||
<a class="tag" href="/tags/istio" title="Istio">
|
||||
Istio
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<h1>使用Istio实现应用流量转移</h1>
|
||||
<h2 class="subheading"> "本文翻译自istio官方文档"</h2>
|
||||
<span class="meta">
|
||||
|
||||
Posted by
|
||||
|
||||
"赵化冰"
|
||||
|
||||
on
|
||||
Tuesday, November 7, 2017
|
||||
|
||||
|
||||
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
|
||||
<article>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-8 col-lg-offset-2
|
||||
col-md-10 col-md-offset-1
|
||||
post-container">
|
||||
|
||||
|
||||
<p>关于Istio的更多内容请参考<a href="http://istio.doczh.cn/">istio中文文档</a>。</p>
|
||||
<p>原文参见<a href="https://istio.io/docs/tasks/traffic-management/traffic-shifting.html">Traffic Shifting</a>。</p>
|
||||
<p>本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio,可以使用一系列不同权重的规则(10%,20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。</p>
|
||||
<p>为简单起见,本任务将采用两步将流量从<code>reviews:v1</code> 迁移到 <code>reviews:v3</code>,权重分别为50%,100%。</p>
|
||||
<h1 id="开始之前">开始之前</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<p>参照文档<a href="http://istio.doczh.cn/docs/setup/kubernetes/index.html">安装指南</a>中的步骤安装Istio。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>部署<a href="http://istio.doczh.cn/docs/guides/bookinfo.html">BookInfo</a> 示例应用程序。</p>
|
||||
</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>请注意:本文档假设示采用kubernetes部署示例应用程序。所有的示例命令行都采用规则yaml文件(例如<code>samples/bookinfo/kube/route-rule-all-v1.yaml</code>)指定的kubernetes版本。如果在不同的环境下运行本任务,请将<code>kube</code>修改为运行环境中相应的目录(例如,对基于Consul的运行环境,目录就是<code>samples/bookinfo/consul/route-rule-all-v1.yaml</code>)。</p>
|
||||
</blockquote>
|
||||
<h1 id="基于权重的版本路由">基于权重的版本路由</h1>
|
||||
<ol>
|
||||
<li>
|
||||
<p>将所有微服务的缺省版本设置为v1.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl create -f samples/bookinfo/kube/route-rule-all-v1.yaml
|
||||
</span></span></code></pre></div></li>
|
||||
<li>
|
||||
<p>在浏览器中打开http://$GATEWAY_URL/productpage, 确认<code>reviews</code> 服务目前的活动版本是v1。</p>
|
||||
<p>可以看到浏览器中出现BooInfo应用的productpage页面。
|
||||
注意<code>productpage</code>显示的评价内容不带星级。这是由于<code>reviews:v1</code>不会访问<code>ratings</code>服务。</p>
|
||||
<blockquote>
|
||||
<p>请注意:如果之前执行过 <a href="http://istio.doczh.cn/docs/tasks/traffic-management/request-routing.html">配置请求路由</a>任务,则需要先注销测试用户“jason”或者删除之前单独为该用户创建的测试规则:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl delete routerule reviews-test-v2
|
||||
</span></span></code></pre></div></li>
|
||||
<li>
|
||||
<p>首先,使用下面的命令把50%的流量从<code>reviews:v1</code>转移到<code>reviews:v3</code>:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl replace -f samples/bookinfo/kube/route-rule-reviews-50-v3.yaml
|
||||
</span></span></code></pre></div><p>注意这里使用了<code>istioctl replace</code>而不是<code>create</code>。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>在浏览器中多次刷新<code>productpage</code>页面,大约有50%的几率会看到页面中出现带红星的评价内容。</p>
|
||||
<blockquote>
|
||||
<p>请注意:在目前的Envoy sidecar实现中,可能需要刷新<code>productpage</code>很多次才能看到流量分发的效果。在看到页面出现变化前,有可能需要刷新15次或者更多。如果修改规则,将90%的流量路由到v3,可以看到更明显的效果。</p>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li>
|
||||
<p>当v3版本的<code>reviews</code>服务已经稳定运行后,可以将100%的流量路由到<code>reviews:v3</code>:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl replace -f samples/bookinfo/kube/route-rule-reviews-v3.yaml
|
||||
</span></span></code></pre></div><p>此时,以任何用户登录到<code>productpage</code>页面,都可以看到带红星的评价信息。</p>
|
||||
</li>
|
||||
</ol>
|
||||
<h1 id="理解原理">理解原理</h1>
|
||||
<p>在这个任务中,我们使用了Istio的带权重路由的特性将流量从老版本的<code>reviews</code>服务逐渐迁移到了新版本服务中。</p>
|
||||
<p>注意该方式和使用容器编排平台的部署特性来进行版本迁移是完全不同的。容器编排平台使用了实例scaling来对流量进行管理。而通过Istio,两个版本的<code>reviews</code>服务可以独立地进行scale up和scale down,并不会影响这两个版本服务之间的流量分发。</p>
|
||||
<p>想了解更多支持scaling的按版本路由功能,请查看<a href="https://istio.io/blog/canary-deployments-using-istio.html">Canary Deployments using Istio</a>。</p>
|
||||
<h1 id="清理">清理</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<p>删除路由规则。</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl delete -f samples/bookinfo/kube/route-rule-all-v1.yaml
|
||||
</span></span></code></pre></div></li>
|
||||
<li>
|
||||
<p>如果不打算尝试后面的任务,请参照<a href="http://istio.doczh.cn/docs/guides/bookinfo.html#cleanup">BookInfo cleanup</a> 中的步骤关闭应用程序。</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h1 id="进阶阅读">进阶阅读</h1>
|
||||
<ul>
|
||||
<li>更多的内容请参见<a href="http://istio.doczh.cn/docs/concepts/traffic-management/rules-configuration.html">请求路由</a>。</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<div class="entry-shang text-center">
|
||||
|
||||
<p>「真诚赞赏,手留余香」</p>
|
||||
|
||||
<button class="zs show-zs btn btn-bred">赞赏支持</button>
|
||||
</div>
|
||||
<div class="zs-modal-bg"></div>
|
||||
<div class="zs-modal-box">
|
||||
<div class="zs-modal-head">
|
||||
<button type="button" class="close">×</button>
|
||||
<span class="author"><a href="http://localhost:1313/"><img src="/img/favicon.png" />David Young</a></span>
|
||||
|
||||
<p class="tip"><i></i><span>真诚赞赏,手留余香</span></p>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="zs-modal-body">
|
||||
<div class="zs-modal-btns">
|
||||
<button class="btn btn-blink" data-num="2">2元</button>
|
||||
<button class="btn btn-blink" data-num="5">5元</button>
|
||||
<button class="btn btn-blink" data-num="10">10元</button>
|
||||
<button class="btn btn-blink" data-num="50">50元</button>
|
||||
<button class="btn btn-blink" data-num="100">100元</button>
|
||||
<button class="btn btn-blink" data-num="1">任意金额</button>
|
||||
</div>
|
||||
<div class="zs-modal-pay">
|
||||
<button class="btn btn-bred" id="pay-text">2元</button>
|
||||
<p>使用<span id="pay-type">微信</span>扫描二维码完成支付</p>
|
||||
<img src="/img/reward/wechat-2.png" id="pay-image"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zs-modal-footer">
|
||||
<label><input type="radio" name="zs-type" value="wechat" class="zs-type" checked="checked"><span ><span class="zs-wechat"><img src="/img/reward/wechat-btn.png"/></span></label>
|
||||
<label><input type="radio" name="zs-type" value="alipay" class="zs-type" class="zs-alipay"><img src="/img/reward/alipay-btn.png"/></span></label>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/js/reward.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
<ul class="pager">
|
||||
|
||||
<li class="previous">
|
||||
<a href="/2017/11/04/istio-install_and_example/" data-toggle="tooltip" data-placement="top" title="Istio及Bookinfo示例程序安装试用笔记">←
|
||||
Previous Post</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="next">
|
||||
<a href="/2017/11/08/istio-canary-release/" data-toggle="tooltip" data-placement="top" title="采用Istio实现灰度发布(金丝雀发布)">Next
|
||||
Post →</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link href="https://xxx.xxx.com/dist/Artalk.css" rel="stylesheet" />
|
||||
<script src="https://xxx.xxx.com/dist/Artalk.js"></script>
|
||||
|
||||
|
||||
<div id="Comments"></div>
|
||||
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: 'http:\/\/localhost:1313\/2017\/11\/07\/istio-traffic-shifting\/',
|
||||
pageTitle: '使用Istio实现应用流量转移',
|
||||
server: 'https:\/\/xxx.xxx.com',
|
||||
site: 'xxx blog',
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-2 col-lg-offset-0
|
||||
visible-lg-block
|
||||
sidebar-container
|
||||
catalog-container">
|
||||
<div class="side-catalog">
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5>
|
||||
<a class="catalog-toggle" href="#">CATALOG</a>
|
||||
</h5>
|
||||
<ul class="catalog-body"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-8 col-lg-offset-2
|
||||
col-md-10 col-md-offset-1
|
||||
sidebar-container">
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5><a href="/tags/">FEATURED TAGS</a></h5>
|
||||
<div class="tags">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/docker" title="docker">
|
||||
docker
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/istio" title="istio">
|
||||
istio
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/kubernetes" title="kubernetes">
|
||||
kubernetes
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/microservice" title="microservice">
|
||||
microservice
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/security" title="security">
|
||||
security
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/service-mesh" title="service mesh">
|
||||
service mesh
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/tips" title="tips">
|
||||
tips
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr>
|
||||
<h5>FRIENDS</h5>
|
||||
<ul class="list-inline">
|
||||
|
||||
<li><a target="_blank" href="https://zhaozhihan.com">Linda的博客</a></li>
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
|
||||
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<ul class="list-inline text-center">
|
||||
|
||||
<li>
|
||||
<a href="mailto:youremail@gmail.com">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-envelope fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="/your%20wechat%20qr%20code%20image">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-weixin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://github.com/yourgithub">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://www.linkedin.com/in/yourlinkedinid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-linkedin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://stackoverflow.com/users/yourstackoverflowid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-stack-overflow fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a href='' rel="alternate" type="application/rss+xml" title="David Young" >
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-rss fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<p class="copyright text-muted">
|
||||
Copyright © David Young 2026
|
||||
|
||||
<br>
|
||||
<a href="https://themes.gohugo.io/hugo-theme-cleanwhite">CleanWhite Hugo Theme</a> by <a href="https://zhaohuabing.com">Huabing</a> |
|
||||
<iframe
|
||||
style="margin-left: 2px; margin-bottom:-5px;"
|
||||
frameborder="0" scrolling="0" width="100px" height="20px"
|
||||
src="https://ghbtns.com/github-btn.html?user=zhaohuabing&repo=hugo-theme-cleanwhite&type=star&count=true" >
|
||||
</iframe>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
function loadAsync(u, c) {
|
||||
var d = document, t = 'script',
|
||||
o = d.createElement(t),
|
||||
s = d.getElementsByTagName(t)[0];
|
||||
o.src = u;
|
||||
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
|
||||
s.parentNode.insertBefore(o, s);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
if($('#tag_cloud').length !== 0){
|
||||
loadAsync("/js/jquery.tagcloud.js",function(){
|
||||
$.fn.tagcloud.defaults = {
|
||||
|
||||
color: {start: '#bbbbee', end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
function updateTagcloudColors() {
|
||||
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
const startColor = isDark ? '#808080' : '#bbbbee';
|
||||
if($('#tag_cloud').length !== 0 && $.fn.tagcloud) {
|
||||
$.fn.tagcloud.defaults = {
|
||||
color: {start: startColor, end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
updateTagcloudColors();
|
||||
});
|
||||
|
||||
|
||||
const observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.attributeName === 'data-theme') {
|
||||
updateTagcloudColors();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-theme']
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
loadAsync("https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js", function(){
|
||||
var $nav = document.querySelector("nav");
|
||||
if($nav) FastClick.attach($nav);
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<script src="/js/theme-toggle.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
function generateCatalog(selector) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_containerSelector = 'div.post-container'
|
||||
|
||||
|
||||
|
||||
var P = $(_containerSelector), a, n, t, l, i, c;
|
||||
a = P.find('h1,h2,h3,h4,h5,h6');
|
||||
|
||||
|
||||
$(selector).html('')
|
||||
|
||||
|
||||
a.each(function () {
|
||||
n = $(this).prop('tagName').toLowerCase();
|
||||
i = "#" + $(this).prop('id');
|
||||
t = $(this).text();
|
||||
c = $('<a href="' + i + '" rel="nofollow" title="' + t + '">' + t + '</a>');
|
||||
l = $('<li class="' + n + '_nav"></li>').append(c);
|
||||
$(selector).append(l);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
generateCatalog(".catalog-body");
|
||||
|
||||
|
||||
$(".catalog-toggle").click((function (e) {
|
||||
e.preventDefault();
|
||||
$('.side-catalog').toggleClass("fold")
|
||||
}))
|
||||
|
||||
|
||||
|
||||
|
||||
loadAsync("\/js\/jquery.nav.js", function () {
|
||||
$('.catalog-body').onePageNav({
|
||||
currentClass: "active",
|
||||
changeHash: !1,
|
||||
easing: "swing",
|
||||
filter: "",
|
||||
scrollSpeed: 700,
|
||||
scrollOffset: 0,
|
||||
scrollThreshold: .2,
|
||||
begin: null,
|
||||
end: null,
|
||||
scrollChange: null,
|
||||
padding: 80
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
992
public/2017/11/08/istio-canary-release/index.html
Normal file
992
public/2017/11/08/istio-canary-release/index.html
Normal file
@@ -0,0 +1,992 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head><script src="/livereload.js?mindelay=10&v=2&port=1313&path=livereload" data-no-instant defer></script>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
|
||||
const autoTheme = false;
|
||||
if (autoTheme) {
|
||||
document.documentElement.setAttribute('data-auto-theme', 'true');
|
||||
}
|
||||
|
||||
const theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<meta property="og:site_name" content="David Young">
|
||||
<meta property="og:type" content="article">
|
||||
|
||||
|
||||
<meta property="og:image" content="http://localhost:1313//img/istio-canary-release/canary_bg.jpg">
|
||||
<meta property="twitter:image" content="http://localhost:1313//img/istio-canary-release/canary_bg.jpg" />
|
||||
|
||||
|
||||
|
||||
<meta name="title" content="采用Istio实现灰度发布(金丝雀发布)" />
|
||||
<meta property="og:title" content="采用Istio实现灰度发布(金丝雀发布)" />
|
||||
<meta property="twitter:title" content="采用Istio实现灰度发布(金丝雀发布)" />
|
||||
|
||||
|
||||
|
||||
<meta name="description" content="当应用上线以后,运维面临的一大挑战是如何能在不影响已上线业务的情况下进行升级。本文将介绍如何使用Istio实现应用的灰度发布(金丝雀发布)">
|
||||
<meta property="og:description" content="当应用上线以后,运维面临的一大挑战是如何能在不影响已上线业务的情况下进行升级。本文将介绍如何使用Istio实现应用的灰度发布(金丝雀发布)" />
|
||||
<meta property="twitter:description" content="当应用上线以后,运维面临的一大挑战是如何能在不影响已上线业务的情况下进行升级。本文将介绍如何使用Istio实现应用的灰度发布(金丝雀发布)" />
|
||||
|
||||
|
||||
<meta property="og:url" content="http://localhost:1313/2017/11/08/istio-canary-release/" />
|
||||
|
||||
|
||||
<meta property="twitter:card" content="summary" />
|
||||
|
||||
|
||||
|
||||
<meta name="keyword" content="Von Balthasar, Scripture, Gravel Riding, Ham Radio, Divine Office, Open Source">
|
||||
<link rel="shortcut icon" href="/img/favicon.ico">
|
||||
|
||||
<title>采用Istio实现灰度发布(金丝雀发布) | David Young Blog</title>
|
||||
|
||||
<link rel="canonical" href="/2017/11/08/istio-canary-release/">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/hugo-theme-cleanwhite.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/theme-variables.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/zanshang.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/font-awesome.all.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/hux-blog.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/lazysizes.min.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="navbar-header page-scroll">
|
||||
<button type="button" class="navbar-toggle">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">David Young</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="huxblog_navbar">
|
||||
<div class="navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="/">All Posts</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="/categories/life/">life</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tech/">tech</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tips/">tips</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li><a href="/archive//">ARCHIVE</a></li>
|
||||
|
||||
<li><a href="/notes//">NOTES</a></li>
|
||||
|
||||
<li><a href="/about//">ABOUT</a></li>
|
||||
|
||||
<li>
|
||||
<a href="/search"><i class="fa fa-search"></i></a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#" id="theme-toggle" title="Toggle dark mode" style="opacity: 0;">
|
||||
<i class="fa fa-moon"></i>
|
||||
<i class="fa fa-sun" style="display: none;"></i>
|
||||
</a>
|
||||
</li>
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
var theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
var toggleBtn = document.getElementById('theme-toggle');
|
||||
if (toggleBtn) {
|
||||
var moonIcon = toggleBtn.querySelector('.fa-moon');
|
||||
var sunIcon = toggleBtn.querySelector('.fa-sun');
|
||||
|
||||
if (theme === 'dark') {
|
||||
if (moonIcon) moonIcon.style.display = 'none';
|
||||
if (sunIcon) sunIcon.style.display = 'inline';
|
||||
toggleBtn.setAttribute('title', 'Switch to light mode');
|
||||
} else {
|
||||
if (moonIcon) moonIcon.style.display = 'inline';
|
||||
if (sunIcon) sunIcon.style.display = 'none';
|
||||
toggleBtn.setAttribute('title', 'Switch to dark mode');
|
||||
}
|
||||
|
||||
|
||||
requestAnimationFrame(function() {
|
||||
toggleBtn.style.transition = 'opacity 0.2s ease';
|
||||
toggleBtn.style.opacity = '1';
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
var $body = document.body;
|
||||
var $toggle = document.querySelector('.navbar-toggle');
|
||||
var $navbar = document.querySelector('#huxblog_navbar');
|
||||
var $collapse = document.querySelector('.navbar-collapse');
|
||||
|
||||
$toggle.addEventListener('click', handleMagic)
|
||||
function handleMagic(e){
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
|
||||
setTimeout(function(){
|
||||
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}else{
|
||||
|
||||
$collapse.style.height = "auto"
|
||||
$navbar.className += " in";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var navLinks = document.querySelectorAll('.navbar-collapse a');
|
||||
navLinks.forEach(function(link) {
|
||||
link.addEventListener('click', function() {
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
setTimeout(function(){
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<header class="intro-header" style="background-image: url('/img/home-bg-jeep.jpg')">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 ">
|
||||
<div class="site-heading">
|
||||
<h1>David Young </h1>
|
||||
|
||||
<span class="subheading">Bevonovo</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
|
||||
<article>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
<div class="
|
||||
col-lg-8 col-lg-offset-1
|
||||
col-md-8 col-md-offset-1
|
||||
col-sm-12
|
||||
col-xs-12
|
||||
post-container">
|
||||
|
||||
<h2 id="灰度发布又名金丝雀发布介绍">灰度发布(又名金丝雀发布)介绍</h2>
|
||||
<p>当应用上线以后,运维面临的一大挑战是如何能够在不影响已上线业务的情况下进行升级。做过产品的同学都清楚,不管在发布前做过多么完备的自动化和人工测试,在发布后都会出现或多或少的故障。根据墨菲定律,可能会出错的版本发布一定会出错。</p>
|
||||
<p>“ANYTHING THAN CAN GO WRONG WILL GO WRONG” –MURPHY’S LAW</p>
|
||||
<p>因此我们不能寄希望于在线下测试时发现所有潜在故障。在无法百分百避免版本升级故障的情况下,需要通过一种方式进行可控的版本发布,把故障影响控制在可以接受的范围内,并可以快速回退。</p>
|
||||
<p>可以通过<a href="https://martinfowler.com/bliki/CanaryRelease.html">灰度发布(又名金丝雀发布)</a>来实现业务从老版本到新版本的平滑过渡,并避免升级过程中出现的问题对用户造成的影响。</p>
|
||||
<p>“金丝雀发布”的来源于矿工们用金丝雀对矿井进行空气测试的做法。以前矿工挖煤的时候,矿工下矿井前会先把金丝雀放进去,或者挖煤的时候一直带着金丝雀。金丝雀对甲烷和一氧化碳浓度比较敏感,会先报警。所以大家都用“金丝雀”来搞最先的测试。</p>
|
||||
<p>下图中,左下方的少部分用户就被当作“金丝雀”来用于测试新上线的1.1版本。如果新版本出现问题,“金丝雀”们会报警,但不会影响其他用户业务的正常运行。
|
||||
|
||||
<img src="/img/istio-canary-release/canary-deployment.PNG" alt="Istio灰度发布示意图">
|
||||
|
||||
</p>
|
||||
<p>灰度发布(金丝雀发布)的流程如下:</p>
|
||||
<ul>
|
||||
<li>准备和生产环境隔离的“金丝雀”服务器。</li>
|
||||
<li>将新版本的服务部署到“金丝雀”服务器上。</li>
|
||||
<li>对“金丝雀”服务器上的服务进行自动化和人工测试。</li>
|
||||
<li>测试通过后,将“金丝雀”服务器连接到生产环境,将少量生产流量导入到“金丝雀”服务器中。</li>
|
||||
<li>如果在线测试出现问题,则通过把生产流量从“金丝雀”服务器中重新路由到老版本的服务的方式进行回退,修复问题后重新进行发布。</li>
|
||||
<li>如果在线测试顺利,则逐渐把生产流量按一定策略逐渐导入到新版本服务器中。</li>
|
||||
<li>待新版本服务稳定运行后,删除老版本服务。</li>
|
||||
</ul>
|
||||
<h2 id="istio实现灰度发布金丝雀发布的原理">Istio实现灰度发布(金丝雀发布)的原理</h2>
|
||||
<p>从上面的流程可以看到,如果要实现一套灰度发布的流程,需要应用程序和运维流程对该发布过程进行支持,工作量和难度的挑战是非常大的。虽然面对的问题类似,但每个企业或组织一般采用不同的私有化实现方案来进行灰度发布,为解决该问题导致研发和运维花费了大量的成本。</p>
|
||||
<p>Istio通过高度的抽象和良好的设计采用一致的方式解决了该问题,采用sidecar对应用流量进行了转发,通过Pilot下发路由规则,可以在不修改应用程序的前提下实现应用的灰度发布。</p>
|
||||
<p>备注:采用kubernetes的<a href="https://kubernetes.io/docs/tasks/run-application/rolling-update-replication-controller/">滚动升级(rolling update)</a>功能也可以实现不中断业务的应用升级,但滚动升级是通过逐渐使用新版本的服务来替换老版本服务的方式对应用进行升级,在滚动升级不能对应用的流量分发进行控制,因此无法采用受控地把生产流量逐渐导流到新版本服务中,也就无法控制服务升级对用户造成的影响。</p>
|
||||
<p>采用Istio后,可以通过定制路由规则将特定的流量(如指定特征的用户)导入新版本服务中,在生产环境下进行测试,同时通过渐进受控地导入生产流量,可以最小化升级中出现的故障对用户的影响。并且在同时存在新老版本服务时,还可根据应用压力对不同版本的服务进行独立的缩扩容,非常灵活。采用Istio进行灰度发布的流程如下图所示:
|
||||
|
||||
<img src="/img/istio-canary-release/canary-deployments.gif" alt="Istio灰度发布示意图">
|
||||
|
||||
</p>
|
||||
<h2 id="操作步骤">操作步骤</h2>
|
||||
<p>下面采用Istion自带的BookinfoInfo示例程序来试验灰度发布的流程。</p>
|
||||
<h3 id="测试环境安装">测试环境安装</h3>
|
||||
<p>首先参考<a href="http://zhaohuabing.com/2017/11/04/istio-install_and_example/">手把手教你从零搭建Istio及Bookinfo示例程序</a>安装Kubernetes及Istio控制面。</p>
|
||||
<p>因为本试验并不需要安装全部3个版本的reviews服务,因此如果已经安装了该应用,先采用下面的命令卸载。</p>
|
||||
<pre tabindex="0"><code>istio-0.2.10/samples/bookinfo/kube/cleanup.sh
|
||||
</code></pre><h3 id="部署v1版本的服务">部署V1版本的服务</h3>
|
||||
<p>首先只部署V1版本的Bookinfo应用程序。由于示例中的yaml文件中包含了3个版本的reviews服务,我们先将V2和V3版本的Deployment从yaml文件istio-0.2.10/samples/bookinfo/kube/bookinfo.yaml中删除。</p>
|
||||
<p>从Bookinfo.yaml中删除这部分内容:</p>
|
||||
<pre tabindex="0"><code>apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: reviews-v2
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: reviews
|
||||
version: v2
|
||||
spec:
|
||||
containers:
|
||||
- name: reviews
|
||||
image: istio/examples-bookinfo-reviews-v2:0.2.3
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 9080
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: reviews-v3
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: reviews
|
||||
version: v3
|
||||
spec:
|
||||
containers:
|
||||
- name: reviews
|
||||
image: istio/examples-bookinfo-reviews-v3:0.2.3
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 9080
|
||||
---
|
||||
</code></pre><p>部署V1版本的Bookinfo程序。</p>
|
||||
<pre tabindex="0"><code>kubectl apply -f <(istioctl kube-inject -f istio-0.2.10/samples/bookinfo/kube/bookinfo.yaml)
|
||||
</code></pre><p>通过kubectl命令行确认pod部署,可以看到只有V1版本的服务。</p>
|
||||
<pre tabindex="0"><code>kubectl get pods
|
||||
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
details-v1-3688945616-nhkqk 2/2 Running 0 2m
|
||||
productpage-v1-2055622944-m3fql 2/2 Running 0 2m
|
||||
ratings-v1-233971408-0f3s9 2/2 Running 0 2m
|
||||
reviews-v1-1360980140-0zs9z 2/2 Running 0 2m
|
||||
</code></pre><p>在浏览器中打开应用程序页面,地址为istio-ingress的External IP。由于V1版本的reviews服务并不会调用rating服务,因此可以看到Product 页面显示的是不带星级的评价信息。</p>
|
||||
<p><code>http://10.12.25.116/productpage</code><br>
|
||||
|
||||
<img src="//img/istio-canary-release/product-page-default.PNG" alt="">
|
||||
|
||||
</p>
|
||||
<p>此时系统中微服务的部署情况如下图所示(下面的示意图均忽略和本例关系不大的details和ratings服务):
|
||||
|
||||
<img src="//img/istio-canary-release/canary-example-only-v1.PNG" alt="">
|
||||
|
||||
</p>
|
||||
<h3 id="部署v2版本的reviews服务">部署V2版本的reviews服务</h3>
|
||||
<p>在部署V2版本的reviews服务前,需要先创建一条缺省路由规则route-rule-default-reviews.yaml,将所有生产流量都导向V1版本,避免对线上用户的影响。</p>
|
||||
<pre tabindex="0"><code>apiVersion: config.istio.io/v1alpha2
|
||||
kind: RouteRule
|
||||
metadata:
|
||||
name: reviews-default
|
||||
spec:
|
||||
destination:
|
||||
name: reviews
|
||||
precedence: 1
|
||||
route:
|
||||
- labels:
|
||||
version: v1
|
||||
</code></pre><p>启用该路由规则。</p>
|
||||
<pre tabindex="0"><code>istioctl create -f route-rule-default-reviews.yaml -n default
|
||||
</code></pre><p>创建一个V2版本的部署文件bookinfo-reviews-v2.yaml,内容如下</p>
|
||||
<pre tabindex="0"><code>apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: reviews-v2
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: reviews
|
||||
version: v2
|
||||
spec:
|
||||
containers:
|
||||
- name: reviews
|
||||
image: istio/examples-bookinfo-reviews-v2:0.2.3
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 9080
|
||||
</code></pre><p>部署V2版本的reviews服务。</p>
|
||||
<pre tabindex="0"><code>kubectl apply -f <(istioctl kube-inject -f bookinfo-reviews-v2.yaml)
|
||||
</code></pre><p>此时系统中部署了V1和V2两个版本的reviews服务,但所有的业务流量都被规则reviews-default导向了V1,如下图所示:
|
||||
|
||||
<img src="/img/istio-canary-release/canary-example-deploy-v2.PNG" alt="">
|
||||
|
||||
</p>
|
||||
<h3 id="将测试流量导入到v2版本的reviews服务">将测试流量导入到V2版本的reviews服务</h3>
|
||||
<p>在进行模拟测试时,由于测试环境和生产环境的网络,服务器,操作系统等环境存在差异,很难完全模拟生产环境进行测试。为了减少环境因素的对测试结果的影响,我们希望能在生产环境中进行上线前的测试,但如果没有很好的隔离措施,可能会导致测试影响已上线的业务,对企业造成损失。</p>
|
||||
<p>通过采用Istio的路由规则,可以在类生产环境中进行测试,又完全隔离了线上用户的生产流量和测试流量,最小化模拟测试对已上线业务的影响。如下图所示:
|
||||
|
||||
<img src="/img/istio-canary-release/canary-example-route-test.PNG" alt="">
|
||||
|
||||
</p>
|
||||
<p>创建一条规则,将用户名为 test-user 的流量导入到V2</p>
|
||||
<pre tabindex="0"><code>apiVersion: config.istio.io/v1alpha2
|
||||
kind: RouteRule
|
||||
metadata:
|
||||
name: reviews-test-user
|
||||
spec:
|
||||
destination:
|
||||
name: reviews
|
||||
precedence: 2
|
||||
match:
|
||||
request:
|
||||
headers:
|
||||
cookie:
|
||||
regex: "^(.*?;)?(user=test-user)(;.*)?$"
|
||||
route:
|
||||
- labels:
|
||||
version: v2
|
||||
</code></pre><p>注意:precedence属性用于设置规则的优先级,在同时存在多条规则的情况下,优先级高的规则将先执行。这条规则的precedence设置为2,以确保其在缺省规则之前运行,将test-user用户的请求导流到V2版本reviews服务中。</p>
|
||||
<p>启用该规则。</p>
|
||||
<pre tabindex="0"><code>istioctl create -f route-rule-test-reviews-v2.yaml -n default
|
||||
</code></pre><p>以test-user用户登录,可以看到V2版本带星级的评价页面。
|
||||
|
||||
<img src="/img/istio-canary-release/product-page-test-user.PNG" alt="">
|
||||
|
||||
</p>
|
||||
<p>注销test-user,只能看到V1版本不带星级的评价页面。如下图所示:
|
||||
|
||||
<img src="/img/istio-canary-release/product-page-default.PNG" alt="">
|
||||
|
||||
</p>
|
||||
<h3 id="将部分生产流量导入到v2版本的reviews服务">将部分生产流量导入到V2版本的reviews服务</h3>
|
||||
<p>在线上模拟测试完成后,如果系统测试情况良好,可以通过规则将一部分用户流量导入到V2版本的服务中,进行小规模的“金丝雀”测试。</p>
|
||||
<p>修改规则route-rule-default-reviews.yaml,将50%的流量导入V2版本。</p>
|
||||
<blockquote>
|
||||
<p>备注:本例只是描述原理,因此为简单起见,将50%流量导入V2版本,在实际操作中,更可能是先导入较少流量,然后根据监控的新版本运行情况将流量逐渐导入,如采用5%,10%,20%,50% …的比例逐渐导入。</p>
|
||||
</blockquote>
|
||||
<pre tabindex="0"><code>apiVersion: config.istio.io/v1alpha2
|
||||
kind: RouteRule
|
||||
metadata:
|
||||
name: reviews-default
|
||||
spec:
|
||||
destination:
|
||||
name: reviews
|
||||
precedence: 1
|
||||
route:
|
||||
- labels:
|
||||
version: v1
|
||||
weight: 50
|
||||
- labels:
|
||||
version: v2
|
||||
weight: 50
|
||||
</code></pre><pre tabindex="0"><code>istioctl replace -f route-rule-default-reviews.yaml -n default
|
||||
</code></pre><p>此时系统部署如下图所示:
|
||||
|
||||
<img src="/img/istio-canary-release/canary-example-route-production-50.PNG" alt="">
|
||||
|
||||
</p>
|
||||
<h3 id="将所有生产流量导入到到v2版本的reviews服务">将所有生产流量导入到到V2版本的reviews服务</h3>
|
||||
<p>如果新版本的服务运行正常,则可以将所有流量导入到V2版本。</p>
|
||||
<pre tabindex="0"><code>apiVersion: config.istio.io/v1alpha2
|
||||
kind: RouteRule
|
||||
metadata:
|
||||
name: reviews-default
|
||||
spec:
|
||||
destination:
|
||||
name: reviews
|
||||
precedence: 1
|
||||
route:
|
||||
- labels:
|
||||
version: v2
|
||||
weight: 100
|
||||
</code></pre><pre tabindex="0"><code>istioctl replace -f route-rule-default-reviews.yaml -n default
|
||||
</code></pre><p>系统部署如下图所示:
|
||||
|
||||
<img src="/img/istio-canary-release/canary-example-route-production-100.PNG" alt="">
|
||||
|
||||
</p>
|
||||
<p>此时不管以任何用户登录,都只能看到V2版本带星级的评价页面,如下图所示:
|
||||
|
||||
<img src="/img/istio-canary-release/product-page-default-v2.PNG" alt="">
|
||||
|
||||
</p>
|
||||
<blockquote>
|
||||
<p>备注:如果灰度发布的过程中新版本的服务出现问题,则可以通过修改路由规则,将流量重新导入到V1版本的服务中,将V2版本故障修复后再进行测试。</p>
|
||||
</blockquote>
|
||||
<h3 id="删除v1版本的reviews服务">删除V1版本的reviews服务</h3>
|
||||
<p>待V2版本上线稳定运行后,删除V1版本的reviews服务和测试规则。</p>
|
||||
<pre tabindex="0"><code>kubectl delete pod reviews-v1-1360980140-0zs9z
|
||||
|
||||
istioctl delete -f route-rule-test-reviews-v2.yaml -n default
|
||||
</code></pre><h2 id="参考">参考</h2>
|
||||
<ul>
|
||||
<li><a href="https://istio.io/docs/">Istio官方文档</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link href="https://xxx.xxx.com/dist/Artalk.css" rel="stylesheet" />
|
||||
<script src="https://xxx.xxx.com/dist/Artalk.js"></script>
|
||||
|
||||
|
||||
<div id="Comments"></div>
|
||||
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: 'http:\/\/localhost:1313\/2017\/11\/08\/istio-canary-release\/',
|
||||
pageTitle: '采用Istio实现灰度发布(金丝雀发布)',
|
||||
server: 'https:\/\/xxx.xxx.com',
|
||||
site: 'xxx blog',
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-3 col-lg-offset-0
|
||||
col-md-3 col-md-offset-0
|
||||
col-sm-12
|
||||
col-xs-12
|
||||
sidebar-container
|
||||
">
|
||||
|
||||
|
||||
<section class="visible-md visible-lg">
|
||||
|
||||
<div class="short-about">
|
||||
|
||||
<a href="/about">
|
||||
<img src="/img/zhaohuabing.png" alt="avatar" style="cursor: pointer" />
|
||||
</a>
|
||||
|
||||
|
||||
<p>Open Source Enthusiast</p>
|
||||
|
||||
|
||||
<ul class="list-inline">
|
||||
|
||||
<li>
|
||||
<a href="mailto:youremail@gmail.com">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fa fa-circle fa-stack-2x"></i>
|
||||
<i class="fa fa-envelope fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="/your%20wechat%20qr%20code%20image">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-weixin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://github.com/yourgithub">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://www.linkedin.com/in/yourlinkedinid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-linkedin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://stackoverflow.com/users/yourstackoverflowid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-stack-overflow fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5>FEATURED TAGS</h5>
|
||||
<div class="tags">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/docker" title="docker">
|
||||
docker
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/istio" title="istio">
|
||||
istio
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/kubernetes" title="kubernetes">
|
||||
kubernetes
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/microservice" title="microservice">
|
||||
microservice
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/security" title="security">
|
||||
security
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/service-mesh" title="service mesh">
|
||||
service mesh
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/tips" title="tips">
|
||||
tips
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5>FRIENDS</h5>
|
||||
<ul class="list-inline">
|
||||
|
||||
<li><a target="_blank" href="https://zhaozhihan.com">Linda的博客</a></li>
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5>LAST POSTS</h5>
|
||||
<ul>
|
||||
|
||||
<li><a href="/2025/07/06/mathematical-formulae/">Authoring mathematical formulae</a></li>
|
||||
|
||||
<li><a href="/post/readme/">Clean White Theme for Hugo</a></li>
|
||||
|
||||
<li><a href="/2018/06/04/introducing-the-istio-v1alpha3-routing-api/">Istio v1aplha3 routing API介绍(译文)</a></li>
|
||||
|
||||
<li><a href="/2018/06/02/istio08/">Istio 0.8 Release发布</a></li>
|
||||
|
||||
<li><a href="/2018/05/24/set_up_my_ubuntu_desktop/">Everything about Setting Up My Ubuntu Desktop</a></li>
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
|
||||
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<ul class="list-inline text-center">
|
||||
|
||||
<li>
|
||||
<a href="mailto:youremail@gmail.com">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-envelope fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="/your%20wechat%20qr%20code%20image">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-weixin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://github.com/yourgithub">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://www.linkedin.com/in/yourlinkedinid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-linkedin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://stackoverflow.com/users/yourstackoverflowid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-stack-overflow fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a href='' rel="alternate" type="application/rss+xml" title="David Young" >
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-rss fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<p class="copyright text-muted">
|
||||
Copyright © David Young 2026
|
||||
|
||||
<br>
|
||||
<a href="https://themes.gohugo.io/hugo-theme-cleanwhite">CleanWhite Hugo Theme</a> by <a href="https://zhaohuabing.com">Huabing</a> |
|
||||
<iframe
|
||||
style="margin-left: 2px; margin-bottom:-5px;"
|
||||
frameborder="0" scrolling="0" width="100px" height="20px"
|
||||
src="https://ghbtns.com/github-btn.html?user=zhaohuabing&repo=hugo-theme-cleanwhite&type=star&count=true" >
|
||||
</iframe>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
function loadAsync(u, c) {
|
||||
var d = document, t = 'script',
|
||||
o = d.createElement(t),
|
||||
s = d.getElementsByTagName(t)[0];
|
||||
o.src = u;
|
||||
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
|
||||
s.parentNode.insertBefore(o, s);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
if($('#tag_cloud').length !== 0){
|
||||
loadAsync("/js/jquery.tagcloud.js",function(){
|
||||
$.fn.tagcloud.defaults = {
|
||||
|
||||
color: {start: '#bbbbee', end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
function updateTagcloudColors() {
|
||||
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
const startColor = isDark ? '#808080' : '#bbbbee';
|
||||
if($('#tag_cloud').length !== 0 && $.fn.tagcloud) {
|
||||
$.fn.tagcloud.defaults = {
|
||||
color: {start: startColor, end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
updateTagcloudColors();
|
||||
});
|
||||
|
||||
|
||||
const observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.attributeName === 'data-theme') {
|
||||
updateTagcloudColors();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-theme']
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
loadAsync("https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js", function(){
|
||||
var $nav = document.querySelector("nav");
|
||||
if($nav) FastClick.attach($nav);
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<script src="/js/theme-toggle.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
function generateCatalog(selector) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_containerSelector = 'div.post-container'
|
||||
|
||||
|
||||
|
||||
var P = $(_containerSelector), a, n, t, l, i, c;
|
||||
a = P.find('h1,h2,h3,h4,h5,h6');
|
||||
|
||||
|
||||
$(selector).html('')
|
||||
|
||||
|
||||
a.each(function () {
|
||||
n = $(this).prop('tagName').toLowerCase();
|
||||
i = "#" + $(this).prop('id');
|
||||
t = $(this).text();
|
||||
c = $('<a href="' + i + '" rel="nofollow" title="' + t + '">' + t + '</a>');
|
||||
l = $('<li class="' + n + '_nav"></li>').append(c);
|
||||
$(selector).append(l);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
generateCatalog(".catalog-body");
|
||||
|
||||
|
||||
$(".catalog-toggle").click((function (e) {
|
||||
e.preventDefault();
|
||||
$('.side-catalog').toggleClass("fold")
|
||||
}))
|
||||
|
||||
|
||||
|
||||
|
||||
loadAsync("\/js\/jquery.nav.js", function () {
|
||||
$('.catalog-body').onePageNav({
|
||||
currentClass: "active",
|
||||
changeHash: !1,
|
||||
easing: "swing",
|
||||
filter: "",
|
||||
scrollSpeed: 700,
|
||||
scrollOffset: 0,
|
||||
scrollThreshold: .2,
|
||||
begin: null,
|
||||
end: null,
|
||||
scrollChange: null,
|
||||
padding: 80
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
970
public/2017/11/28/access-application-from-outside/index.html
Normal file
970
public/2017/11/28/access-application-from-outside/index.html
Normal file
@@ -0,0 +1,970 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head><script src="/livereload.js?mindelay=10&v=2&port=1313&path=livereload" data-no-instant defer></script>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
|
||||
const autoTheme = false;
|
||||
if (autoTheme) {
|
||||
document.documentElement.setAttribute('data-auto-theme', 'true');
|
||||
}
|
||||
|
||||
const theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<meta property="og:site_name" content="David Young">
|
||||
<meta property="og:type" content="article">
|
||||
|
||||
|
||||
<meta property="og:image" content="http://localhost:1313/https://img.zhaohuabing.com/post-bg-2015.jpg">
|
||||
<meta property="twitter:image" content="http://localhost:1313/https://img.zhaohuabing.com/post-bg-2015.jpg" />
|
||||
|
||||
|
||||
|
||||
<meta name="title" content="如何从外部访问Kubernetes集群中的应用?" />
|
||||
<meta property="og:title" content="如何从外部访问Kubernetes集群中的应用?" />
|
||||
<meta property="twitter:title" content="如何从外部访问Kubernetes集群中的应用?" />
|
||||
|
||||
|
||||
|
||||
<meta name="description" content="我们知道,kubernetes的Cluster Network属于私有网络,只能在cluster Network内部才能访问部署的应用,那如何才能将Kubernetes集群中的应用暴露到外部网络,为外部用户提供服务呢?本文探讨了从外部网络访问kubernetes cluster中应用的几种实现方式。">
|
||||
<meta property="og:description" content="我们知道,kubernetes的Cluster Network属于私有网络,只能在cluster Network内部才能访问部署的应用,那如何才能将Kubernetes集群中的应用暴露到外部网络,为外部用户提供服务呢?本文探讨了从外部网络访问kubernetes cluster中应用的几种实现方式。" />
|
||||
<meta property="twitter:description" content="我们知道,kubernetes的Cluster Network属于私有网络,只能在cluster Network内部才能访问部署的应用,那如何才能将Kubernetes集群中的应用暴露到外部网络,为外部用户提供服务呢?本文探讨了从外部网络访问kubernetes cluster中应用的几种实现方式。" />
|
||||
|
||||
|
||||
<meta property="og:url" content="http://localhost:1313/2017/11/28/access-application-from-outside/" />
|
||||
|
||||
|
||||
<meta property="twitter:card" content="summary" />
|
||||
|
||||
|
||||
|
||||
<meta name="keyword" content="Von Balthasar, Scripture, Gravel Riding, Ham Radio, Divine Office, Open Source">
|
||||
<link rel="shortcut icon" href="/img/favicon.ico">
|
||||
|
||||
<title>如何从外部访问Kubernetes集群中的应用? | David Young Blog</title>
|
||||
|
||||
<link rel="canonical" href="/2017/11/28/access-application-from-outside/">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/hugo-theme-cleanwhite.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/theme-variables.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/zanshang.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/font-awesome.all.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/hux-blog.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/lazysizes.min.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="navbar-header page-scroll">
|
||||
<button type="button" class="navbar-toggle">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">David Young</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="huxblog_navbar">
|
||||
<div class="navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="/">All Posts</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="/categories/life/">life</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tech/">tech</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tips/">tips</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li><a href="/archive//">ARCHIVE</a></li>
|
||||
|
||||
<li><a href="/notes//">NOTES</a></li>
|
||||
|
||||
<li><a href="/about//">ABOUT</a></li>
|
||||
|
||||
<li>
|
||||
<a href="/search"><i class="fa fa-search"></i></a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#" id="theme-toggle" title="Toggle dark mode" style="opacity: 0;">
|
||||
<i class="fa fa-moon"></i>
|
||||
<i class="fa fa-sun" style="display: none;"></i>
|
||||
</a>
|
||||
</li>
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
var theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
var toggleBtn = document.getElementById('theme-toggle');
|
||||
if (toggleBtn) {
|
||||
var moonIcon = toggleBtn.querySelector('.fa-moon');
|
||||
var sunIcon = toggleBtn.querySelector('.fa-sun');
|
||||
|
||||
if (theme === 'dark') {
|
||||
if (moonIcon) moonIcon.style.display = 'none';
|
||||
if (sunIcon) sunIcon.style.display = 'inline';
|
||||
toggleBtn.setAttribute('title', 'Switch to light mode');
|
||||
} else {
|
||||
if (moonIcon) moonIcon.style.display = 'inline';
|
||||
if (sunIcon) sunIcon.style.display = 'none';
|
||||
toggleBtn.setAttribute('title', 'Switch to dark mode');
|
||||
}
|
||||
|
||||
|
||||
requestAnimationFrame(function() {
|
||||
toggleBtn.style.transition = 'opacity 0.2s ease';
|
||||
toggleBtn.style.opacity = '1';
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
var $body = document.body;
|
||||
var $toggle = document.querySelector('.navbar-toggle');
|
||||
var $navbar = document.querySelector('#huxblog_navbar');
|
||||
var $collapse = document.querySelector('.navbar-collapse');
|
||||
|
||||
$toggle.addEventListener('click', handleMagic)
|
||||
function handleMagic(e){
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
|
||||
setTimeout(function(){
|
||||
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}else{
|
||||
|
||||
$collapse.style.height = "auto"
|
||||
$navbar.className += " in";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var navLinks = document.querySelectorAll('.navbar-collapse a');
|
||||
navLinks.forEach(function(link) {
|
||||
link.addEventListener('click', function() {
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
setTimeout(function(){
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
header.intro-header {
|
||||
background-image: url('https://img.zhaohuabing.com/post-bg-2015.jpg')
|
||||
}
|
||||
</style>
|
||||
|
||||
<header class="intro-header" >
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<div class="post-heading">
|
||||
<div class="tags">
|
||||
|
||||
<a class="tag" href="/tags/kubernetes" title="Kubernetes">
|
||||
Kubernetes
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<h1>如何从外部访问Kubernetes集群中的应用?</h1>
|
||||
<h2 class="subheading"></h2>
|
||||
<span class="meta">
|
||||
|
||||
Posted by
|
||||
|
||||
赵化冰
|
||||
|
||||
on
|
||||
Tuesday, November 28, 2017
|
||||
|
||||
|
||||
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
|
||||
<article>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-8 col-lg-offset-2
|
||||
col-md-10 col-md-offset-1
|
||||
post-container">
|
||||
|
||||
|
||||
<h2 id="前言">前言</h2>
|
||||
<p>我们知道,kubernetes的Cluster Network属于私有网络,只能在cluster Network内部才能访问部署的应用,那如何才能将Kubernetes集群中的应用暴露到外部网络,为外部用户提供服务呢?本文探讨了从外部网络访问kubernetes cluster中应用的几种实现方式。</p>
|
||||
<blockquote>
|
||||
<p>本文尽量试着写得比较容易理解,但要做到“深入浅出”,把复杂的事情用通俗易懂的语言描述出来是非常需要功力的,个人自认尚未达到此境界,唯有不断努力。此外,kubernetes本身是一个比较复杂的系统,无法在本文中详细解释涉及的所有相关概念,否则就可能脱离了文章的主题,因此假设阅读此文之前读者对kubernetes的基本概念如docker,container,pod已有所了解。</p>
|
||||
</blockquote>
|
||||
<p>另外此文中的一些内容是自己的理解,由于个人的知识范围有限,可能有误,如果读者对文章中的内容有疑问或者勘误,欢迎大家指证。</p>
|
||||
<h2 id="pod和service">Pod和Service</h2>
|
||||
<p>我们首先来了解一下Kubernetes中的Pod和Service的概念。</p>
|
||||
<p>Pod(容器组),英文中Pod是豆荚的意思,从名字的含义可以看出,Pod是一组有依赖关系的容器,Pod包含的容器都会运行在同一个host节点上,共享相同的volumes和network namespace空间。Kubernetes以Pod为基本操作单元,可以同时启动多个相同的pod用于failover或者load balance。</p>
|
||||
<p>
|
||||
<img src="/img/access-application-from-outside/pod.PNG" alt="Pod">
|
||||
|
||||
</p>
|
||||
<p>Pod的生命周期是短暂的,Kubernetes根据应用的配置,会对Pod进行创建,销毁,根据监控指标进行缩扩容。kubernetes在创建Pod时可以选择集群中的任何一台空闲的Host,因此其网络地址是不固定的。由于Pod的这一特点,一般不建议直接通过Pod的地址去访问应用。</p>
|
||||
<p>为了解决访问Pod不方便直接访问的问题,Kubernetes采用了Service的概念,Service是对后端提供服务的一组Pod的抽象,Service会绑定到一个固定的虚拟IP上,该虚拟IP只在Kubernetes Cluster中可见,但其实该IP并不对应一个虚拟或者物理设备,而只是IPtable中的规则,然后再通过IPtable将服务请求路由到后端的Pod中。通过这种方式,可以确保服务消费者可以稳定地访问Pod提供的服务,而不用关心Pod的创建、删除、迁移等变化以及如何用一组Pod来进行负载均衡。</p>
|
||||
<p>Service的机制如下图所示,Kube-proxy监听kubernetes master增加和删除Service以及Endpoint的消息,对于每一个Service,kube proxy创建相应的iptables规则,将发送到Service Cluster IP的流量转发到Service后端提供服务的Pod的相应端口上。
|
||||
|
||||
<img src="/img/access-application-from-outside/services-iptables-overview.png" alt="Pod和Service的关系">
|
||||
|
||||
</p>
|
||||
<blockquote>
|
||||
<p>备注:虽然可以通过Service的Cluster IP和服务端口访问到后端Pod提供的服务,但该Cluster IP是Ping不通的,原因是Cluster IP只是iptable中的规则,并不对应到一个网络设备。</p>
|
||||
</blockquote>
|
||||
<h2 id="service的类型">Service的类型</h2>
|
||||
<p>Service的类型(ServiceType)决定了Service如何对外提供服务,根据类型不同,服务可以只在Kubernetes cluster中可见,也可以暴露到Cluster外部。Service有三种类型,ClusterIP,NodePort和LoadBalancer。其中ClusterIP是Service的缺省类型,这种类型的服务会提供一个只能在Cluster内才能访问的虚拟IP,其实现机制如上面一节所述。</p>
|
||||
<h2 id="通过nodeport提供外部访问入口">通过NodePort提供外部访问入口</h2>
|
||||
<p>通过将Service的类型设置为NodePort,可以在Cluster中的主机上通过一个指定端口暴露服务。注意通过Cluster中每台主机上的该指定端口都可以访问到该服务,发送到该主机端口的请求会被kubernetes路由到提供服务的Pod上。采用这种服务类型,可以在kubernetes cluster网络外通过主机IP:端口的方式访问到服务。</p>
|
||||
<blockquote>
|
||||
<p>注意:官方文档中说明了Kubernetes clusterIp的流量转发到后端Pod有Iptable和kube proxy两种方式。但对Nodeport如何转发流量却语焉不详。该图来自网络,从图来看是通过kube proxy转发的,我没有去研究过源码。欢迎了解的同学跟帖说明。</p>
|
||||
</blockquote>
|
||||
<p>
|
||||
<img src="/img/access-application-from-outside/nodeport.PNG" alt="Pod和Service的关系">
|
||||
|
||||
</p>
|
||||
<p>下面是通过NodePort向外暴露服务的一个例子,注意可以指定一个nodePort,也可以不指定。在不指定的情况下,kubernetes会从可用的端口范围内自动分配一个随机端口。</p>
|
||||
<pre tabindex="0"><code>kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: influxdb
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 8086
|
||||
nodePort: 30000
|
||||
selector:
|
||||
name: influxdb
|
||||
</code></pre><p>通过NodePort从外部访问有下面的一些问题,自己玩玩或者进行测试时可以使用该方案,但不适宜用于生产环境。</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Kubernetes cluster host的IP必须是一个well-known IP,即客户端必须知道该IP。但Cluster中的host是被作为资源池看待的,可以增加删除,每个host的IP一般也是动态分配的,因此并不能认为host IP对客户端而言是well-known IP。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>客户端访问某一个固定的host IP存在单点故障。假如一台host宕机了,kubernetes cluster会把应用 reload到另一节点上,但客户端就无法通过该host的nodeport访问应用了。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>该方案假设客户端可以访问Kubernetes host所在网络。在生产环境中,客户端和Kubernetes host网络可能是隔离的。例如客户端可能是公网中的一个手机APP,是无法直接访问host所在的私有网络的。</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>因此,需要通过一个网关来将外部客户端的请求导入到Cluster中的应用中,在kubernetes中,这个网关是一个4层的load balancer。</p>
|
||||
<h2 id="通过load-balancer提供外部访问入口">通过Load Balancer提供外部访问入口</h2>
|
||||
<p>通过将Service的类型设置为LoadBalancer,可以为Service创建一个外部Load Balancer。Kubernetes的文档中声明该Service类型需要云服务提供商的支持,其实这里只是在Kubernetes配置文件中提出了一个要求,即为该Service创建Load Balancer,至于如何创建则是由Google Cloud或Amazon Cloud等云服务商提供的,创建的Load Balancer不在Kubernetes Cluster的管理范围中。kubernetes 1.6版本中,WS, Azure, CloudStack, GCE and OpenStack等云提供商已经可以为Kubernetes提供Load Balancer.下面是一个Load balancer类型的Service例子:</p>
|
||||
<pre tabindex="0"><code>kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: influxdb
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 8086
|
||||
selector:
|
||||
name: influxdb
|
||||
</code></pre><p>部署该Service后,我们来看一下Kubernetes创建的内容</p>
|
||||
<pre tabindex="0"><code>$ kubectl get svc influxdb
|
||||
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
influxdb 10.97.121.42 10.13.242.236 8086:30051/TCP 39s
|
||||
</code></pre><p>Kubernetes首先为influxdb创建了一个集群内部可以访问的ClusterIP 10.97.121.42。由于没有指定nodeport端口,kubernetes选择了一个空闲的30051主机端口将service暴露在主机的网络上,然后通知cloud provider创建了一个load balancer,上面输出中的EEXTERNAL-IP就是load balancer的IP。</p>
|
||||
<p>测试使用的Cloud Provider是OpenStack,我们通过neutron lb-vip-show可以查看创建的Load Balancer详细信息。</p>
|
||||
<pre tabindex="0"><code>$ neutron lb-vip-show 9bf2a580-2ba4-4494-93fd-9b6969c55ac3
|
||||
+---------------------+--------------------------------------------------------------+
|
||||
| Field | Value |
|
||||
+---------------------+--------------------------------------------------------------+
|
||||
| address | 10.13.242.236 |
|
||||
| admin_state_up | True |
|
||||
| connection_limit | -1 |
|
||||
| description | Kubernetes external service a6ffa4dadf99711e68ea2fa163e0b082 |
|
||||
| id | 9bf2a580-2ba4-4494-93fd-9b6969c55ac3 |
|
||||
| name | a6ffa4dadf99711e68ea2fa163e0b082 |
|
||||
| pool_id | 392917a6-ed61-4924-acb2-026cd4181755 |
|
||||
| port_id | e450b80b-6da1-4b31-a008-280abdc6400b |
|
||||
| protocol | TCP |
|
||||
| protocol_port | 8086 |
|
||||
| session_persistence | |
|
||||
| status | ACTIVE |
|
||||
| status_description | |
|
||||
| subnet_id | 73f8eb91-90cf-42f4-85d0-dcff44077313 |
|
||||
| tenant_id | 4d68886fea6e45b0bc2e05cd302cccb9 |
|
||||
+---------------------+--------------------------------------------------------------+
|
||||
|
||||
$ neutron lb-pool-show 392917a6-ed61-4924-acb2-026cd4181755
|
||||
+------------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+------------------------+--------------------------------------+
|
||||
| admin_state_up | True |
|
||||
| description | |
|
||||
| health_monitors | |
|
||||
| health_monitors_status | |
|
||||
| id | 392917a6-ed61-4924-acb2-026cd4181755 |
|
||||
| lb_method | ROUND_ROBIN |
|
||||
| members | d0825cc2-46a3-43bd-af82-e9d8f1f85299 |
|
||||
| | 3f73d3bb-bc40-478d-8d0e-df05cdfb9734 |
|
||||
| name | a6ffa4dadf99711e68ea2fa163e0b082 |
|
||||
| protocol | TCP |
|
||||
| provider | haproxy |
|
||||
| status | ACTIVE |
|
||||
| status_description | |
|
||||
| subnet_id | 73f8eb91-90cf-42f4-85d0-dcff44077313 |
|
||||
| tenant_id | 4d68886fea6e45b0bc2e05cd302cccb9 |
|
||||
| vip_id | 9bf2a580-2ba4-4494-93fd-9b6969c55ac3 |
|
||||
+------------------------+--------------------------------------+
|
||||
|
||||
$ neutron lb-member-list
|
||||
+--------------------------------------+--------------+---------------+--------+----------------+--------+
|
||||
| id | address | protocol_port | weight | admin_state_up | status |
|
||||
+--------------------------------------+--------------+---------------+--------+----------------+--------+
|
||||
| 3f73d3bb-bc40-478d-8d0e-df05cdfb9734 | 10.13.241.89 | 30051 | 1 | True | ACTIVE |
|
||||
| d0825cc2-46a3-43bd-af82-e9d8f1f85299 | 10.13.241.10 | 30051 | 1 | True | ACTIVE |
|
||||
+--------------------------------------+--------------+---------------+--------+----------------+--------
|
||||
</code></pre><p>可以看到OpenStack使用VIP 10.13.242.236在端口8086创建了一个Load Balancer,Load Balancer对应的Lb pool里面有两个成员10.13.241.89 和 10.13.241.10,正是Kubernetes的host节点,进入Load balancer流量被分发到这两个节点对应的Service Nodeport 30051上。</p>
|
||||
<p>但是如果客户端不在Openstack Neutron的私有子网上,则还需要在load balancer的VIP上关联一个floating IP,以使外部客户端可以连接到load balancer。</p>
|
||||
<p>部署Load balancer后,应用的拓扑结构如下图所示(注:本图假设Kubernetes Cluster部署在Openstack私有云上)。
|
||||
|
||||
<img src="/img/access-application-from-outside/load-balancer.PNG" alt="外部Load balancer">
|
||||
|
||||
</p>
|
||||
<blockquote>
|
||||
<p>备注:如果kubernetes环境在Public Cloud上,Loadbalancer类型的Service创建出的外部Load Balancer已经带有公网IP地址,是可以直接从外部网络进行访问的,不需要绑定floating IP这个步骤。例如在AWS上创建的Elastic Load Balancing (ELB),有兴趣可以看一下这篇文章:<a href="http://docs.heptio.com/content/tutorials/aws-qs-services-elb.html">Expose Services on your AWS Quick Start Kubernetes cluster</a>。</p>
|
||||
</blockquote>
|
||||
<p>如果Kubernetes Cluster是在不支持LoadBalancer特性的cloud provider或者裸机上创建的,可以实现LoadBalancer类型的Service吗?应该也是可以的。Kubernetes本身并不直接支持Loadbalancer,但我们可以通过对Kubernetes进行扩展来实现,可以监听kubernetes Master的service创建消息,并根据消息部署相应的Load Balancer(如Nginx或者HAProxy),来实现Load balancer类型的Service。</p>
|
||||
<p>通过设置Service类型提供的是四层Load Balancer,当只需要向外暴露一个服务的时候,可以直接采用这种方式。但在一个应用需要对外提供多个服务时,采用该方式会为每一个服务(IP+Port)都创建一个外部load balancer。如下图所示
|
||||
|
||||
<img src="/img/access-application-from-outside/multiple-load-balancer.PNG" alt="创建多个Load balancer暴露应用的多个服务">
|
||||
|
||||
|
||||
一般来说,同一个应用的多个服务/资源会放在同一个域名下,在这种情况下,创建多个Load balancer是完全没有必要的,反而带来了额外的开销和管理成本。直接将服务暴露给外部用户也会导致了前端和后端的耦合,影响了后端架构的灵活性,如果以后由于业务需求对服务进行调整会直接影响到客户端。可以通过使用Kubernetes Ingress进行L7 load balancing来解决该问题。</p>
|
||||
<h2 id="采用ingress作为七层load-balancer">采用Ingress作为七层load balancer</h2>
|
||||
<p>首先看一下引入Ingress后的应用拓扑示意图(注:本图假设Kubernetes Cluster部署在Openstack私有云上)。
|
||||
|
||||
<img src="/img/access-application-from-outside/ingress.PNG" alt="采用Ingress暴露应用的多个服务">
|
||||
|
||||
|
||||
这里Ingress起到了七层负载均衡器和Http方向代理的作用,可以根据不同的url把入口流量分发到不同的后端Service。外部客户端只看到foo.bar.com这个服务器,屏蔽了内部多个Service的实现方式。采用这种方式,简化了客户端的访问,并增加了后端实现和部署的灵活性,可以在不影响客户端的情况下对后端的服务部署进行调整。</p>
|
||||
<p>下面是Kubernetes Ingress配置文件的示例,在虚拟主机foot.bar.com下面定义了两个Path,其中/foo被分发到后端服务s1,/bar被分发到后端服务s2。</p>
|
||||
<pre tabindex="0"><code>apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: test
|
||||
annotations:
|
||||
ingress.kubernetes.io/rewrite-target: /
|
||||
spec:
|
||||
rules:
|
||||
- host: foo.bar.com
|
||||
http:
|
||||
paths:
|
||||
- path: /foo
|
||||
backend:
|
||||
serviceName: s1
|
||||
servicePort: 80
|
||||
- path: /bar
|
||||
backend:
|
||||
serviceName: s2
|
||||
servicePort: 80
|
||||
</code></pre><p>注意这里Ingress只描述了一个虚拟主机路径分发的要求,可以定义多个Ingress,描述不同的7层分发要求,而这些要求需要由一个Ingress Controller来实现。Ingress Contorller会监听Kubernetes Master得到Ingress的定义,并根据Ingress的定义对一个7层代理进行相应的配置,以实现Ingress定义中要求的虚拟主机和路径分发规则。Ingress Controller有多种实现,Kubernetes提供了一个<a href="https://github.com/kubernetes/ingress-nginx">基于Nginx的Ingress Controller</a>。需要注意的是,在部署Kubernetes集群时并不会缺省部署Ingress Controller,需要我们自行部署。</p>
|
||||
<p>下面是部署Nginx Ingress Controller的配置文件示例,注意这里为Nginx Ingress Controller定义了一个LoadBalancer类型的Service,以为Ingress Controller提供一个外部可以访问的公网IP。</p>
|
||||
<pre tabindex="0"><code>apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-ingress
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 80
|
||||
name: http
|
||||
- port: 443
|
||||
name: https
|
||||
selector:
|
||||
k8s-app: nginx-ingress-lb
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-ingress-controller
|
||||
spec:
|
||||
replicas: 2
|
||||
revisionHistoryLimit: 3
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: nginx-ingress-lb
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- name: nginx-ingress-controller
|
||||
image: gcr.io/google_containers/nginx-ingress-controller:0.8.3
|
||||
imagePullPolicy: Always
|
||||
//----omitted for brevity----
|
||||
</code></pre><blockquote>
|
||||
<p>备注:Google Cloud直接支持Ingress资源,如果应用部署在Google Cloud中,Google Cloud会自动为Ingress资源创建一个7层load balancer,并为之分配一个外部IP,不需要自行部署Ingress Controller。</p>
|
||||
</blockquote>
|
||||
<h2 id="结论">结论</h2>
|
||||
<p>采用Ingress加上Load balancer的方式可以将Kubernetes Cluster中的应用服务暴露给外部客户端。这种方式比较灵活,基本可以满足大部分应用的需要。但如果需要在入口处提供更强大的功能,如有更高的效率要求,需求进行安全认证,日志记录,或者需要一些应用的定制逻辑,则需要考虑采用微服务架构中的API Gateway模式,采用一个更强大的API Gateway来作为应用的流量入口。</p>
|
||||
<h2 id="参考">参考</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="http://alesnosek.com/blog/2017/02/14/accessing-kubernetes-pods-from-outside-of-the-cluster/">Accessing Kubernetes Pods from Outside of the Cluster</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://daemonza.github.io/2017/02/13/kubernetes-nginx-ingress-controller/">Kubernetes nginx-ingress-controller</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://docs.openstack.org/magnum/ocata/dev/kubernetes-load-balancer.html">Using Kubernetes external load balancer feature</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="http://docs.heptio.com/content/tutorials/aws-qs-services-elb.html">Expose Services on your AWS Quick Start Kubernetes cluster</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<div class="entry-shang text-center">
|
||||
|
||||
<p>「真诚赞赏,手留余香」</p>
|
||||
|
||||
<button class="zs show-zs btn btn-bred">赞赏支持</button>
|
||||
</div>
|
||||
<div class="zs-modal-bg"></div>
|
||||
<div class="zs-modal-box">
|
||||
<div class="zs-modal-head">
|
||||
<button type="button" class="close">×</button>
|
||||
<span class="author"><a href="http://localhost:1313/"><img src="/img/favicon.png" />David Young</a></span>
|
||||
|
||||
<p class="tip"><i></i><span>真诚赞赏,手留余香</span></p>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="zs-modal-body">
|
||||
<div class="zs-modal-btns">
|
||||
<button class="btn btn-blink" data-num="2">2元</button>
|
||||
<button class="btn btn-blink" data-num="5">5元</button>
|
||||
<button class="btn btn-blink" data-num="10">10元</button>
|
||||
<button class="btn btn-blink" data-num="50">50元</button>
|
||||
<button class="btn btn-blink" data-num="100">100元</button>
|
||||
<button class="btn btn-blink" data-num="1">任意金额</button>
|
||||
</div>
|
||||
<div class="zs-modal-pay">
|
||||
<button class="btn btn-bred" id="pay-text">2元</button>
|
||||
<p>使用<span id="pay-type">微信</span>扫描二维码完成支付</p>
|
||||
<img src="/img/reward/wechat-2.png" id="pay-image"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zs-modal-footer">
|
||||
<label><input type="radio" name="zs-type" value="wechat" class="zs-type" checked="checked"><span ><span class="zs-wechat"><img src="/img/reward/wechat-btn.png"/></span></label>
|
||||
<label><input type="radio" name="zs-type" value="alipay" class="zs-type" class="zs-alipay"><img src="/img/reward/alipay-btn.png"/></span></label>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/js/reward.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
<ul class="pager">
|
||||
|
||||
<li class="previous">
|
||||
<a href="/2017/11/08/istio-canary-release/" data-toggle="tooltip" data-placement="top" title="采用Istio实现灰度发布(金丝雀发布)">←
|
||||
Previous Post</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="next">
|
||||
<a href="/2018/01/02/nginmesh-install/" data-toggle="tooltip" data-placement="top" title="Nginx开源Service Mesh组件Nginmesh安装指南">Next
|
||||
Post →</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link href="https://xxx.xxx.com/dist/Artalk.css" rel="stylesheet" />
|
||||
<script src="https://xxx.xxx.com/dist/Artalk.js"></script>
|
||||
|
||||
|
||||
<div id="Comments"></div>
|
||||
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: 'http:\/\/localhost:1313\/2017\/11\/28\/access-application-from-outside\/',
|
||||
pageTitle: '如何从外部访问Kubernetes集群中的应用?',
|
||||
server: 'https:\/\/xxx.xxx.com',
|
||||
site: 'xxx blog',
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-2 col-lg-offset-0
|
||||
visible-lg-block
|
||||
sidebar-container
|
||||
catalog-container">
|
||||
<div class="side-catalog">
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5>
|
||||
<a class="catalog-toggle" href="#">CATALOG</a>
|
||||
</h5>
|
||||
<ul class="catalog-body"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-8 col-lg-offset-2
|
||||
col-md-10 col-md-offset-1
|
||||
sidebar-container">
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5><a href="/tags/">FEATURED TAGS</a></h5>
|
||||
<div class="tags">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/docker" title="docker">
|
||||
docker
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/istio" title="istio">
|
||||
istio
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/kubernetes" title="kubernetes">
|
||||
kubernetes
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/microservice" title="microservice">
|
||||
microservice
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/security" title="security">
|
||||
security
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/service-mesh" title="service mesh">
|
||||
service mesh
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/tips" title="tips">
|
||||
tips
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr>
|
||||
<h5>FRIENDS</h5>
|
||||
<ul class="list-inline">
|
||||
|
||||
<li><a target="_blank" href="https://zhaozhihan.com">Linda的博客</a></li>
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
|
||||
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<ul class="list-inline text-center">
|
||||
|
||||
<li>
|
||||
<a href="mailto:youremail@gmail.com">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-envelope fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="/your%20wechat%20qr%20code%20image">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-weixin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://github.com/yourgithub">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://www.linkedin.com/in/yourlinkedinid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-linkedin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://stackoverflow.com/users/yourstackoverflowid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-stack-overflow fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a href='' rel="alternate" type="application/rss+xml" title="David Young" >
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-rss fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<p class="copyright text-muted">
|
||||
Copyright © David Young 2026
|
||||
|
||||
<br>
|
||||
<a href="https://themes.gohugo.io/hugo-theme-cleanwhite">CleanWhite Hugo Theme</a> by <a href="https://zhaohuabing.com">Huabing</a> |
|
||||
<iframe
|
||||
style="margin-left: 2px; margin-bottom:-5px;"
|
||||
frameborder="0" scrolling="0" width="100px" height="20px"
|
||||
src="https://ghbtns.com/github-btn.html?user=zhaohuabing&repo=hugo-theme-cleanwhite&type=star&count=true" >
|
||||
</iframe>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
function loadAsync(u, c) {
|
||||
var d = document, t = 'script',
|
||||
o = d.createElement(t),
|
||||
s = d.getElementsByTagName(t)[0];
|
||||
o.src = u;
|
||||
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
|
||||
s.parentNode.insertBefore(o, s);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
if($('#tag_cloud').length !== 0){
|
||||
loadAsync("/js/jquery.tagcloud.js",function(){
|
||||
$.fn.tagcloud.defaults = {
|
||||
|
||||
color: {start: '#bbbbee', end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
function updateTagcloudColors() {
|
||||
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
const startColor = isDark ? '#808080' : '#bbbbee';
|
||||
if($('#tag_cloud').length !== 0 && $.fn.tagcloud) {
|
||||
$.fn.tagcloud.defaults = {
|
||||
color: {start: startColor, end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
updateTagcloudColors();
|
||||
});
|
||||
|
||||
|
||||
const observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.attributeName === 'data-theme') {
|
||||
updateTagcloudColors();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-theme']
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
loadAsync("https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js", function(){
|
||||
var $nav = document.querySelector("nav");
|
||||
if($nav) FastClick.attach($nav);
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<script src="/js/theme-toggle.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
function generateCatalog(selector) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_containerSelector = 'div.post-container'
|
||||
|
||||
|
||||
|
||||
var P = $(_containerSelector), a, n, t, l, i, c;
|
||||
a = P.find('h1,h2,h3,h4,h5,h6');
|
||||
|
||||
|
||||
$(selector).html('')
|
||||
|
||||
|
||||
a.each(function () {
|
||||
n = $(this).prop('tagName').toLowerCase();
|
||||
i = "#" + $(this).prop('id');
|
||||
t = $(this).text();
|
||||
c = $('<a href="' + i + '" rel="nofollow" title="' + t + '">' + t + '</a>');
|
||||
l = $('<li class="' + n + '_nav"></li>').append(c);
|
||||
$(selector).append(l);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
generateCatalog(".catalog-body");
|
||||
|
||||
|
||||
$(".catalog-toggle").click((function (e) {
|
||||
e.preventDefault();
|
||||
$('.side-catalog').toggleClass("fold")
|
||||
}))
|
||||
|
||||
|
||||
|
||||
|
||||
loadAsync("\/js\/jquery.nav.js", function () {
|
||||
$('.catalog-body').onePageNav({
|
||||
currentClass: "active",
|
||||
changeHash: !1,
|
||||
easing: "swing",
|
||||
filter: "",
|
||||
scrollSpeed: 700,
|
||||
scrollOffset: 0,
|
||||
scrollThreshold: .2,
|
||||
begin: null,
|
||||
end: null,
|
||||
scrollChange: null,
|
||||
padding: 80
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user