<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; dashboard</title>
	<atom:link href="http://tonybai.com/tag/dashboard/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Thu, 09 Apr 2026 00:20:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>跟上Go演进步伐，你只需要关注这几件事儿</title>
		<link>https://tonybai.com/2024/09/30/how-to-keep-up-with-go-evolution/</link>
		<comments>https://tonybai.com/2024/09/30/how-to-keep-up-with-go-evolution/#comments</comments>
		<pubDate>Mon, 30 Sep 2024 12:46:55 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AustinClements]]></category>
		<category><![CDATA[changelist]]></category>
		<category><![CDATA[CherryMui]]></category>
		<category><![CDATA[Cpp]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.24]]></category>
		<category><![CDATA[go1.x]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[golang-dev]]></category>
		<category><![CDATA[gomodule]]></category>
		<category><![CDATA[gonew]]></category>
		<category><![CDATA[google-group]]></category>
		<category><![CDATA[issue]]></category>
		<category><![CDATA[Iterator]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[loopvar]]></category>
		<category><![CDATA[math]]></category>
		<category><![CDATA[milestone]]></category>
		<category><![CDATA[proposal]]></category>
		<category><![CDATA[rand]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[roadmap]]></category>
		<category><![CDATA[RobertGriesemer]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[spec]]></category>
		<category><![CDATA[v2]]></category>
		<category><![CDATA[vgo]]></category>
		<category><![CDATA[提案]]></category>
		<category><![CDATA[看板]]></category>
		<category><![CDATA[语言规范]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=4306</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2024/09/30/how-to-keep-up-with-go-evolution Go语言以其简洁、高效和强大的特性赢得了众多开发者的青睐。与许多主流编程语言有着明确的演进Roadmap或下一个版本spec不同，Go的演进过程更加独特、灵活与开放。这种看起来不那么正式和严肃的演进方式却也能让Go快速响应开发者的需求，同时保持语言的稳定性和一致性。 作为一名Go开发者，跟上Go的演进步伐，甚至是参与到这个激动人心的过程中来，不仅能让你更好地利用语言的新特性，还能帮助你更深入地理解Go的设计哲学。 但很多Go开发者只知道每年Go有两次的大版本Release，并通过大版本的Release Notes来了解Go的演进。这无可厚非，但对于那些想及时跟进Go演进的Gopher来说，光有一年两次的Release Notes还是不够的的，很难及时跟进Go的演进决策。 但如果直接到Go语言项目的issue中去翻阅，面对Go丰富的社区讨论和频繁的更新，你可能会感到无从下手。别担心，本文将为你指明方向，让你只需关注几个关键点，就能轻松跟上Go的演进步伐。 1. 开发计划早知道 Go的版本规划具有很高的灵活性。每个Go 1.x版本在开发前，Go语言项目相关人员都会在golang-dev讨论组上发布一个帖子，这个帖子通常的标题为”Planning Go 1.x”，例如”Planning Go 1.23&#8243;，如下图： 很多contributor，无论是Go团队的，还是外部贡献者的，会在该帖子下面留下自己的plan(注意：这些plan中的特性可不一定会在最终的版本中发布)，然后等main tree开放后，就会将已经准备完毕的cl(changelist) merge到main tree中去，或开始提交cl，等待Go团队或社区的开发者进行评审。 当然对于Go 1.x这样的大版本，Go团队会在github建立专门的milestone跟踪，大家也可以在对应的milestone中看到该版本带来的新特性等，下图是目前正在积极开发的Go 1.24版本里程碑： 通过查看这些Plan或定期查看Go 1.x里程碑，你可以提前了解Go的发展方向，为新版本的到来做好准备。 当然如果要了解那些更早的Go演进的决策，我们还得关注和跟踪下面的Proposal Project看板和三个关键的issue。 2. Proposal Project看板和三个关键的Issue Go在早期并没有规范的proposal提案流程，更多是由Rob Pike、Robert Griesemer等三个Go语言之父，外加Ian Taylor和Russ Cox讨论确定，这一状态在Russ Cox建立明确的Go proposal提案流程后结束，提案流程是Go团队审查提案并决定接受或拒绝提案的过程。Russ Cox在提案流程中明确了Go项目的开发过程是设计驱动(design-driven)的，必须首先对语言、库或工具的重大更改进行讨论（包括Go语言项目主仓库和所有golang.org/x仓库中的API更改，以及对go command的命令行更改），并在实现这些设计之前进行正式记录。 Go团队目前使用Proposal Project看板和GitHub Issues来追踪语言的演进，下面我们来看看这个看板和值得关注的三个Issue。 2.1 Proposal Project看板 Proposal Project看板是Go团队跟踪proposal的全局视图，当然要理解该看板，我们需要先来简单看看Go的proposal流程以及每个提案的生命周期是怎样的。 Go Proposal流程并不复杂，可以概括为下面这个示意图： 该流程图展示了Go提案流程的几个主要步骤： 任何人都可以作为提案作者，在Go项目上创建一个简短的issue来描述提案。 Go团队成员以及任何Go社区成员在issue上进行初步讨论，由一组人组成的Go提案审核委员会决定是接受提案、拒绝提案，还是需要进一步的设计文档。 如果需要进一步的设计文档，提案作者会撰写一个详细的设计文档。 在设计文档的评论减少/收敛后(意见趋于一致后)，由Go提案审核委员会会进行最终讨论，决定接受或拒绝提案。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2024/09/30/how-to-keep-up-with-go-evolution">本文永久链接</a> &#8211; https://tonybai.com/2024/09/30/how-to-keep-up-with-go-evolution</p>
<p>Go语言以其简洁、高效和强大的特性赢得了众多开发者的青睐。与许多主流编程语言有着明确的演进Roadmap或下一个版本spec不同，Go的演进过程更加独特、灵活与开放。这种看起来不那么正式和严肃的演进方式却也能让Go快速响应开发者的需求，同时保持语言的稳定性和一致性。</p>
<p>作为一名Go开发者，跟上Go的演进步伐，甚至是参与到这个激动人心的过程中来，不仅能让你更好地利用语言的新特性，还能帮助你更深入地理解Go的设计哲学。</p>
<p>但很多Go开发者只知道每年Go有两次的大版本Release，并通过大版本的Release Notes来了解Go的演进。这无可厚非，但对于那些想及时跟进Go演进的Gopher来说，光有一年两次的Release Notes还是不够的的，很难及时跟进Go的演进决策。</p>
<p>但如果直接到Go语言项目的issue中去翻阅，面对Go丰富的社区讨论和频繁的更新，你可能会感到无从下手。别担心，本文将为你指明方向，让你只需关注几个关键点，就能轻松跟上Go的演进步伐。</p>
<h2>1. 开发计划早知道</h2>
<p>Go的版本规划具有很高的灵活性。每个Go 1.x版本在开发前，Go语言项目相关人员都会在<a href="https://groups.google.com/g/golang-dev/">golang-dev讨论组</a>上发布一个帖子，这个帖子通常的标题为”Planning Go 1.x”，例如”Planning Go 1.23&#8243;，如下图：</p>
<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-2.png" alt="" /></p>
<p>很多contributor，无论是Go团队的，还是外部贡献者的，会在该帖子下面留下自己的plan(注意：这些plan中的特性可不一定会在最终的版本中发布)，然后等main tree开放后，就会将已经准备完毕的<a href="https://google.github.io/eng-practices/review/developer/">cl(changelist)</a> merge到main tree中去，或开始提交cl，等待Go团队或社区的开发者进行评审。</p>
<p>当然对于Go 1.x这样的大版本，Go团队会在github建立专门的<a href="https://github.com/golang/go/milestones">milestone</a>跟踪，大家也可以在对应的milestone中看到该版本带来的新特性等，下图是目前正在积极开发的<a href="https://github.com/golang/go/milestone/322">Go 1.24版本里程碑</a>：</p>
<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-3.png" alt="" /></p>
<p>通过查看这些Plan或定期查看Go 1.x里程碑，你可以提前了解Go的发展方向，为新版本的到来做好准备。</p>
<p>当然如果要了解那些更早的Go演进的决策，我们还得关注和跟踪下面的Proposal Project看板和三个关键的issue。</p>
<h2>2. Proposal Project看板和三个关键的Issue</h2>
<p>Go在早期并没有规范的proposal提案流程，更多是由Rob Pike、Robert Griesemer等三个Go语言之父，外加Ian Taylor和Russ Cox讨论确定，这一状态在<a href="https://github.com/golang/proposal/">Russ Cox建立明确的Go proposal提案流程</a>后结束，提案流程是Go团队审查提案并决定接受或拒绝提案的过程。Russ Cox在提案流程中明确了Go项目的开发过程是设计驱动(design-driven)的，必须首先对语言、库或工具的重大更改进行讨论（包括Go语言项目主仓库和所有golang.org/x仓库中的API更改，以及对go command的命令行更改），并在实现这些设计之前进行正式记录。</p>
<p>Go团队目前使用Proposal Project看板和GitHub Issues来追踪语言的演进，下面我们来看看这个看板和值得关注的三个Issue。</p>
<h3>2.1 <a href="https://github.com/orgs/golang/projects/17">Proposal Project看板</a></h3>
<p>Proposal Project看板是Go团队跟踪proposal的全局视图，当然要理解该看板，我们需要先来简单看看Go的proposal流程以及每个提案的生命周期是怎样的。</p>
<p>Go Proposal流程并不复杂，可以概括为下面这个示意图：</p>
<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-7.png" alt="" /></p>
<p>该流程图展示了Go提案流程的几个主要步骤：</p>
<ul>
<li>任何人都可以作为提案作者，在Go项目上创建一个简短的issue来描述提案。</li>
<li>Go团队成员以及任何Go社区成员在issue上进行初步讨论，由一组人组成的<strong>Go提案审核委员会</strong>决定是接受提案、拒绝提案，还是需要进一步的设计文档。</li>
<li>如果需要进一步的设计文档，提案作者会撰写一个详细的设计文档。</li>
<li>在设计文档的评论减少/收敛后(意见趋于一致后)，由Go提案审核委员会会进行最终讨论，决定接受或拒绝提案。</li>
</ul>
<p>Go提案审查委员会使用GitHub项目看板来跟踪提案的状态并管理提案的生命周期(如下图所示)：</p>
<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-6.png" alt="" /></p>
<p>该看板针对每个提案issue设置了几个生命周期状态：</p>
<ul>
<li>Incoming：新提交的提案</li>
<li>Active：正在积极讨论的提案</li>
<li>Likely Accept / Likely Decline：可能被接受或拒绝的提案</li>
<li>Accepted / Declined：已被接受或拒绝的提案</li>
<li>Hold：需要设计修订或需要几周或更长时间才能获得附加信息的提案，这类提案一旦准备就绪，还会回到Active状态</li>
</ul>
<p>了解了上述Go提案与审核流程，再看下面的几个关键Issue就容易多了。</p>
<h3>2.2 <a href="https://golang.org/s/proposal-minutes">proposal: review meeting minutes(33502)</a></h3>
<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-4.png" alt="" /></p>
<p>该issue于2019年8月创建，其创建者为前Go团队技术负责人Russ Cox。这是目前Go语言项目最核心的追踪Issue，它记录了Go提案审查会议的纪要，通常每周更新一次(如下图所示)：</p>
<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-5.png" alt="" /></p>
<p>我们看到内容包括：</p>
<ul>
<li>发布当周已经决策为Accepted和Declined的proposal列表</li>
<li>后续Likely Accept和Likely Decline的proposal列表</li>
<li>正处于Active讨论的proposal列表</li>
<li>当前处于Hold状态的proposal列表</li>
</ul>
<p>和Go提案看板不同，该issue是对提案Issue的状态变更的记录，Gopher可以第一时间看到每周Go提案的状态更新。</p>
<p>由于<a href="https://mp.weixin.qq.com/s/2Sy6K_dU1j3tZZiyyfCTDQ">Russ Cox已经辞去了Go团队技术负责人的头衔</a>，从2024年9月下旬开始，Go团队新的技术负责人<a href="https://github.com/aclements">Austin Clements</a>将继续主持提案审核会议，并更新该Issue。</p>
<p>除了Review meeting minutes这个重要的issue外，还有两个issue值得我们关注，通过它们，我们可以及时了解到Go编译器和运行时的演进以及Go语法特性的演进。</p>
<h3>2.3 <a href="https://github.com/golang/go/issues/43930">Go compiler and runtime meeting notes(43930)</a></h3>
<p>Go编译器和运行时团队定期（大约每周）召开会议，讨论Go编译器和运行时的后续开发和演进事宜，该会议是Google Go团队的内部会议，但Go团队觉得Go社区有必要了解这个会议上的一些讨论议题、过程与会议结论，从而知道Go编译器和运行时团队正在以及将要做什么。</p>
<p>于是前Go团队成员<a href="https://github.com/jeremyfaller">Jeremy Faller</a>于2021年1月创建了该Issue，向Go社区发布Go编译器和运行时的最新演进动向。</p>
<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-8.png" alt="" /></p>
<p>之前Go编译器和运行时团队的负责人是Austin Clements，如今是<a href="https://github.com/cherrymui">CherryMui</a>。</p>
<h3>2.4 <a href="https://github.com/golang/go/issues/33892">spec: language change review meeting minutes(33892)</a></h3>
<p>编译器和运行时之外，Gopher最关心的就是Go语法的演进以及Go语言规范的变更，这个事儿是由Go语言之父之一的Robert Griesemer亲自抓的。在2019年8月，Robert Griesemer就建立了跟踪Go语法变化的issue，当然最初是要跟踪Go2的演进，后来Go泛型落地后，Go2彻底融入了Go1，该issue也就变成了跟踪Go语法演进的Issue。Robert Griesemer主持的Go语言变更审查会议每月举行一次，并将会议讨论的记录发布到该Issue上。</p>
<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-9.png" alt="" /></p>
<h2>3. Discussion与Russ Cox博客</h2>
<p>关于Go语言演进的动向，还有两个渠道可以关注，一个是<a href="https://github.com/golang/go/discussions">Go团队在github repo上发起的discussion</a>，<a href="https://github.com/golang/go/discussions/47139">Russ Cox在2021年7月启用了discussion</a>，旨在寻找一个地方来扩大许多人可能想要参与的讨论。当前，该discussion仅针对非常有限的事项添加讨论，并且只有少数Go核心团队的人才有发起discussion的权限。一些在前几个版本的重要语言特性变化以及标准库的变化，都在这里进行了充分的讨论，比如<a href="https://tonybai.com/2024/02/18/some-changes-in-go-1-22">loopvar语义修正</a>、<a href="https://tonybai.com/2024/06/24/range-over-func-and-package-iter-in-go-1-23">自定义iterator</a>、<a href="https://tonybai.com/2024/02/18/some-changes-in-go-1-22">开启标准库major版本更新的math/rand/v2</a>以及<a href="https://tonybai.com/2023/08/11/introduction-to-the-gonew-tool">gonew工具</a>等。</p>
<p><img src="https://tonybai.com/wp-content/uploads/how-to-keep-up-with-go-evolution-10.png" alt="" /></p>
<p>另外一个则是<a href="https://research.swtch.com/">Russ Cox的博客</a>，作为Go项目团队前技术负责人，作为Rob Pike的接班人，Russ Cox很好地完成了承上启下的作用，并为Go的演进和发展确立了演进框架、方法以及方向。Russ Cox经常在自己的博客上先“憋大招，做铺垫”！最典型的就是<a href="https://research.swtch.com/vgo">vgo</a>，也就是go module的前身，在短短几周内Russ Cox在博客上发表了7篇关于vgo的设计思路文章，为后来Go module的落地奠定了基础，至此基本上不再有Gopher抱怨Go依赖管理了。Russ Cox现已辞去Go技术负责人的头衔，后续是否还能在他的博客上看到Go相关的新特性的设计，让我们拭目以待！</p>
<h2>4. 小结</h2>
<p>在快速发展的技术环境中，Go语言以其独特的演进方式和灵活的开发计划，吸引了越来越多的开发者。本文介绍了如何及时有效地跟踪Go的演进的方法，包括关注大版本开发计划、Proposal Project看板和关键的issue，帮助Gopher及时了解语言的新特性与设计决策。通过参与讨论和关注Go团队的动态，开发者不仅能掌握最新的语言更新，还能深入理解Go的设计哲学和发展方向。希望每位Gopher都能抓住这些资源，与Go语言共同成长，提升自己的开发技能。</p>
<hr />
<p><a href="https://public.zsxq.com/groups/51284458844544">Gopher部落知识星球</a>在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时，我们也会加强代码质量和最佳实践的分享，包括如何编写简洁、可读、可测试的Go代码。此外，我们还会加强星友之间的交流和互动。欢迎大家踊跃提问，分享心得，讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落，享受coding的快乐! 欢迎大家踊跃加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻) &#8211; https://gopherdaily.tonybai.com</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>Gopher Daily归档 &#8211; https://github.com/bigwhite/gopherdaily</li>
<li>Gopher Daily Feed订阅 &#8211; https://gopherdaily.tonybai.com/feed</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2024, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2024/09/30/how-to-keep-up-with-go-evolution/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>官宣：慕课网课程“Kubernetes实战：高可用集群搭建、配置、运维与应用”上线了</title>
		<link>https://tonybai.com/2018/10/17/imooc-course-kubernetes-practice-go-online/</link>
		<comments>https://tonybai.com/2018/10/17/imooc-course-kubernetes-practice-go-online/#comments</comments>
		<pubDate>Wed, 17 Oct 2018 10:28:04 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[aliyun]]></category>
		<category><![CDATA[cloud-native]]></category>
		<category><![CDATA[CNCF]]></category>
		<category><![CDATA[cni]]></category>
		<category><![CDATA[container]]></category>
		<category><![CDATA[controller]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[EFK]]></category>
		<category><![CDATA[elastic]]></category>
		<category><![CDATA[ElasticSearch]]></category>
		<category><![CDATA[ELK]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[harbor]]></category>
		<category><![CDATA[heapster]]></category>
		<category><![CDATA[High-Available]]></category>
		<category><![CDATA[image]]></category>
		<category><![CDATA[imooc]]></category>
		<category><![CDATA[ingress]]></category>
		<category><![CDATA[istio]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[kubeadm]]></category>
		<category><![CDATA[kubectl]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[label]]></category>
		<category><![CDATA[logging]]></category>
		<category><![CDATA[Master]]></category>
		<category><![CDATA[microservice]]></category>
		<category><![CDATA[Namespace]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[pod]]></category>
		<category><![CDATA[PV]]></category>
		<category><![CDATA[PVC]]></category>
		<category><![CDATA[registry]]></category>
		<category><![CDATA[Service]]></category>
		<category><![CDATA[ServiceMesh]]></category>
		<category><![CDATA[storage]]></category>
		<category><![CDATA[StorageClass]]></category>
		<category><![CDATA[troubleshooting]]></category>
		<category><![CDATA[volume]]></category>
		<category><![CDATA[weave]]></category>
		<category><![CDATA[worker]]></category>
		<category><![CDATA[云原生]]></category>
		<category><![CDATA[云应用]]></category>
		<category><![CDATA[容器]]></category>
		<category><![CDATA[微服务]]></category>
		<category><![CDATA[慕课网]]></category>
		<category><![CDATA[服务]]></category>
		<category><![CDATA[服务治理]]></category>
		<category><![CDATA[服务网格]]></category>
		<category><![CDATA[网课]]></category>
		<category><![CDATA[高可用]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=2644</guid>
		<description><![CDATA[距离我的第一门网课《Kubernetes基础：开启云原生之门》上线已经过去5个多月了，我的实战课《Kubernetes实战：高可用集群搭建、配置、运维与应用》终于在9月27日正式上线了。 一. 课程介绍 《Kubernetes实战：高可用集群搭建、配置、运维与应用》的课程内容与最初课程设计时规划的内容大纲没有太多出入，基本就是根据我最初的想法拟定的内容，这也基本是我这两年学习k8s、积累的k8s实践的路线。整个课程基于kubernetes 1.10.2版本(docker 17.03.2ce)。课程内容大致分为七个部分（与课程主页的课程目录结构稍有差异，但课程内容是一致的）： 第一章 搭建你的第一个Kubernetes集群 本章介绍了一个使用kubeadm引导的Kubernetes集群的搭建和基本配置方法。 1-1: 导学 1-2: 安装准备 1-3: 初始化集群master节点 1-4: 向集群加入worker节点 1-5: 安装dashboard和heapster 1-6: 验证集群安装结果 第二章 Kubernetes集群探索 本章对kubeadm初始化集群的原理进行了讲解，并对已经建立的k8s集群中的各个组件进行详细介绍，包括功用、原理和配置等 2-1: kubeadm init流程揭秘 2-2: kubeadm join流程揭秘 2-3: kubernetes核心组件详解 2-4: kubectl详解 第三章 Kubernetes网络、安全与存储 本章讲解k8s集群的三个难点：网络、安全与存储的概念和运行原理。 3-1：kubernetes集群网络 3-1-1: kubernetes集群的“三个网络” 3-1-2: kubernetes网络的设计要求 3-1-3: kubernetes网络实现 3-1-4: pod网络实现原理 3-1-5: pod网络方案对比 3-1-6: service网络实现原理 3-2: kubernetes集群安全 3-2-1: kube-apiserver安全模型 3-2-2: [...]]]></description>
			<content:encoded><![CDATA[<p>距离我的第一门网课<a href="https://www.imooc.com/learn/978">《Kubernetes基础：开启云原生之门》</a>上线已经过去5个多月了，我的实战课<a href="https://coding.imooc.com/class/chapter/284.html">《Kubernetes实战：高可用集群搭建、配置、运维与应用》</a>终于在9月27日正式上线了。</p>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-frontpage.png" alt="img{512x368}" /></p>
<h3>一. 课程介绍</h3>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-content-1.png" alt="img{512x368}" /></p>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-content-2.png" alt="img{512x368}" /></p>
<p><a href="https://coding.imooc.com/class/chapter/284.html">《Kubernetes实战：高可用集群搭建、配置、运维与应用》</a>的课程内容与最初课程设计时规划的内容大纲没有太多出入，基本就是根据我最初的想法拟定的内容，<strong>这也基本是我这两年学习k8s、积累的k8s实践的路线</strong>。整个课程基于kubernetes 1.10.2版本(<a href="https://tonybai.com/tag/docker">docker</a> 17.03.2ce)。课程内容大致分为七个部分（与课程主页的课程目录结构稍有差异，但课程内容是一致的）：</p>
<p>第一章 搭建你的第一个<a href="https://tonybai.com/tag/kubernetes">Kubernetes集群</a></p>
<p>本章介绍了一个使用<a href="https://tonybai.com/2016/12/30/install-kubernetes-on-ubuntu-with-kubeadm/">kubeadm</a>引导的Kubernetes集群的搭建和基本配置方法。</p>
<ul>
<li>1-1: 导学</li>
<li>1-2: 安装准备</li>
<li>1-3: 初始化集群master节点</li>
<li>1-4: 向集群加入worker节点</li>
<li>1-5: <a href="https://tonybai.com/2017/09/26/some-notes-about-deploying-kubernetes-dashboard-1-7-0/">安装dashboard和heapster</a></li>
<li>1-6: 验证集群安装结果</li>
</ul>
<p>第二章 <a href="https://tonybai.com/2017/01/24/explore-kubernetes-cluster-installed-by-kubeadm/">Kubernetes集群探索</a></p>
<p>本章对kubeadm初始化集群的原理进行了讲解，并对已经建立的k8s集群中的各个组件进行详细介绍，包括功用、原理和配置等</p>
<ul>
<li>2-1: kubeadm init流程揭秘</li>
<li>2-2: kubeadm join流程揭秘</li>
<li>2-3: kubernetes核心组件详解</li>
<li>2-4: <a href="https://tonybai.com/2018/06/14/the-authentication-and-authorization-of-kubectl-when-accessing-k8s-cluster/">kubectl</a>详解</li>
</ul>
<p>第三章 Kubernetes网络、安全与存储<br />
本章讲解k8s集群的三个难点：网络、安全与存储的概念和运行原理。</p>
<p>3-1：kubernetes<a href="https://tonybai.com/2017/01/17/understanding-flannel-network-for-kubernetes/">集群网络</a></p>
<ul>
<li>3-1-1: kubernetes集群的“三个网络”</li>
<li>3-1-2: kubernetes网络的设计要求</li>
<li>3-1-3: kubernetes网络实现</li>
<li>3-1-4: pod网络实现原理</li>
<li>3-1-5: pod网络方案对比</li>
<li>3-1-6: service网络实现原理</li>
</ul>
<p>3-2: <a href="https://tonybai.com/2018/06/14/the-authentication-and-authorization-of-kubectl-when-accessing-k8s-cluster/">kubernetes集群安全</a></p>
<ul>
<li>3-2-1: kube-apiserver安全模型</li>
<li>3-2-2: 传输安全</li>
<li>3-2-3: <a href="https://tonybai.com/2016/11/25/the-security-settings-for-kubernetes-cluster/">身份验证</a></li>
<li>3-2-4: 授权</li>
<li>3-2-5: 准入控制</li>
</ul>
<p>3-3 kubernets集群存储</p>
<ul>
<li>3-3-1: Volume</li>
<li>3-3-2: <a href="https://tonybai.com/2016/11/07/integrate-kubernetes-with-ceph-rbd/">PV和PVC</a></li>
<li>3-3-3: StorageClass和动态PV供给</li>
<li>3-3-4: Kubernetes存储模型</li>
</ul>
<p>第四章 <a href="https://tonybai.com/2017/05/15/setup-a-ha-kubernetes-cluster-based-on-kubeadm-part1/">高可用Kubernetes集群</a>搭建方案<br />
本章介绍了什么是高可用k8s集群，并给出了一个可行的高可用Kubernetes集群的搭建方案。</p>
<ul>
<li>4-1: 什么是高可用Kubernetes集群</li>
<li>4-2: 高可用Kubernetes集群方案</li>
</ul>
<p>第五章 Kubernetes集群常见运维操作</p>
<p>本章讲解了Kubernetes集群的基本运维操作，包括node管理、service、pod管理、日志查看等。并讲解了面对k8s集群问题时如何做troubleshooting。</p>
<ul>
<li>5-1: 管理Node与Label</li>
<li>5-2: 管理Namespace、Service和Pod</li>
<li>5-3: <a href="https://tonybai.com/2017/10/16/out-of-node-resource-handling-in-kubernetes-cluster/">计算资源管理</a></li>
<li>5-4: 查看事件和容器日志</li>
<li>5-5: 常用TroubleShooting方法</li>
</ul>
<p>第六章 Kubernetes支撑<a href="https://www.cncf.io/">云原生应用</a>开发案例<br />
本章讲解了Kubernetes集群的应用：支撑云原生应用开发。并通过实际操作讲解了镜像仓库、集中日志以及云应用治理框架的搭建和使用。</p>
<ul>
<li>6-1: Kubernetes与云原生应用</li>
<li>6-2: <a href="https://tonybai.com/2017/12/08/deploy-high-availability-harbor-on-kubernetes-cluster/">高可用私有镜像仓库搭建</a></li>
<li>6-3: <a href="https://tonybai.com/2018/06/13/setup-efk-on-kubernetes-1-10-3-in-the-hard-way/">基于ElasticSearch Stack搭建集群Logging设施</a></li>
<li>6-4: <a href="https://tonybai.com/2018/01/03/an-intro-of-microservices-governance-by-istio/">基于istio service mesh实现服务治理</a></li>
</ul>
<p>第七章 课程回顾与总结</p>
<h3>二. 做网课目的与课程思路</h3>
<p>当初接下慕课商务的这门课主要是出于两个目的：</p>
<ul>
<li>通过这门课程对自己的k8s学习和实践做一个阶段性的系统总结</li>
<li>尝试一下网课这个“新鲜”事物</li>
</ul>
<p>现在看来，当初这两个“目的”都实现了。但是录制网课的确是件很“辛苦”的事情，不知道多少的夜晚和周末都留给了“网课资料编写和录制”。尤其是Kubernetes这个主题，讲起来“顾虑”很多：</p>
<ul>
<li>
<p>和编程语言课不同，Kubernetes平台是个复杂的平台，外延生态很庞大。k8s概念多，如果不把概念和原理交待清楚、讲透彻，直接就上手操作，那样学习后，对k8s的理解仍然不会很深刻，很多问题仍然无法自己去解决，尤其是中高级阶段。 这就导致很多小伙伴认为课程概念讲解“有些多”；</p>
</li>
<li>
<p>生产环境中k8s集群有大有小，使用目的也是大不相同，安装方式也是有很多种(官方就列了10多种)，所在的网络环境以及使用的pod网络插件也是区别很大，遇到的问题更是千差万别，这里在准备 课程时也是思来想去，无法覆盖所有生产环境的所有情况。最后决定使用kubeadm搭建一个4节点的集群(使用weave network plugin)，可能能更好的满足初学者的需求，学员们更容易获取搭建这样一个 k8s环境所需的资源。而关于课程中实际操作部分重点集中在前面的k8s搭建、集群探索以及后面的k8s对云应用支撑的环节。所以如果小伙伴们的环境与课程不同，可以在课程后提问，我会尽量第一时间、细致的回答各位的问题。</p>
</li>
<li>
<p>关于时长，我在课程里尽量做到没有”废话“。现在的网课多根据“时长”定价（虽然不赞同，但是目前也没有一个更好的量化课程质量的方法）：比如10个小时以上可能就会定到399元，但是不足10小时，可能就在199元这个价位。<strong>于是我努力地将课程做到了“199”这个价位上了</strong>。对于真正想学习k8s的小伙伴们，这也许是一个“好消息”:)。</p>
</li>
</ul>
<h3>三. 课程小结</h3>
<p>Kubernetes还在快速不断地演进！我个人觉得学完本门课程也仅仅是“Kubernetes实践之路”的一个开始而已！应用上云的趋势已经不可逆转，对于云应用开发人员来说，<strong>了解和学习Kubernetes就像当年单机时代开发人员要去了解PC操作系统一样重要</strong>！希望本门课程能给更多的开发者带去帮助！</p>
<p>下面是课程的自制海报，欢迎转发:)</p>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-with-qr-and-text.png" alt="img{512x368}" /></p>
<hr />
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/<br />
smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>我的联系方式：</p>
<p>微博：https://weibo.com/bigwhite20xx<br />
微信公众号：iamtonybai<br />
博客：tonybai.com<br />
github: https://github.com/bigwhite</p>
<p>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2018 &#8211; 2020, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2018/10/17/imooc-course-kubernetes-practice-go-online/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Kubernetes Dashboard 1.7.0部署二三事</title>
		<link>https://tonybai.com/2017/09/26/some-notes-about-deploying-kubernetes-dashboard-1-7-0/</link>
		<comments>https://tonybai.com/2017/09/26/some-notes-about-deploying-kubernetes-dashboard-1-7-0/#comments</comments>
		<pubDate>Tue, 26 Sep 2017 09:19:28 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[apiserver]]></category>
		<category><![CDATA[basicAuth]]></category>
		<category><![CDATA[container]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[grafana]]></category>
		<category><![CDATA[heapster]]></category>
		<category><![CDATA[InfluxDB]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[kube-apiserver]]></category>
		<category><![CDATA[kubeconfig]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[pod]]></category>
		<category><![CDATA[RBAC]]></category>
		<category><![CDATA[token]]></category>
		<category><![CDATA[权限]]></category>
		<category><![CDATA[角色]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=2417</guid>
		<description><![CDATA[由于开发的平台要进行内部公开测试，我们这周在公司内部私有云搭建了一套平台。涉及到Kubernetes相关的基础软件，由我来部署。Kubernetes以及其相关组件都在积极的开发中，版本更新也很快。截至本文撰写时，K8s发布最新稳定版是v1.7.6，而与之配套的Dashboard则是v1.7.0。 最初在部署规划时，我选择了Kubernetes v1.7.6+ dashboard v1.6.3的组合。之前K8s v1.7.3的稳定让我对使用最新Release版有一些信心，但dashboard v1.7.0则是三天前刚发布的，看dashboard的commit log，之前还大规模revert了一次。因此，我保守的选择了v1.6.3。 一、但Dashboard v1.6.3与Kubernetes 1.7.6似乎不匹配 在Kubernetes Dashboard的兼容性矩阵中，我们能看到dashboard 1.6.x与k8s 1.7.x的兼容性是一个问号。最新dashboard兼容性矩阵点击这里可以找到： 也就是说由于K8S API可能的变动，Dashboard 1.6.x的某些功能可能无法使用。之前我在阿里云上的测试环境中使用的是k8s 1.7.3+dashboard 1.6.3的组合，我需要的功能均可以使用。因此这里我首先尝试了dashboard v1.6.3。 安装过程不赘述。我依旧通过kube-apiserver暴露服务的方式来访问dasbboard，kube-apiserver采用basic auth的身份验证方式。我尝试在浏览器中访问下面路径： https://{kube-apiserver}:6443/ui 在浏览器弹出的身份验证对话框中输入user/password后，url跳转到： https://{kube-apiserver}:6443/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy 不过等了许久，浏览器页面依旧一片空白。Dashboard的内容并未鲜露出来。通过chrome浏览器自带的”检查”功能，发现一些静态资源（css、js）的get请求都返回404错误。由于时间有限，没有细致查问题所在。我打算用Dashboard 1.7.0试试。 二、采用Dashboard v1.7.0 1.7.0版本dashboard主要强化了安全性，增加了登录页面和相关菜单项，并增加了一个kubernetes-dashboard-init-amd64 init容器。我们无需再依赖浏览器弹框了。dashboard调整了源码目录结构，安装1.7.0需要执行下面命令： kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml 安装后，我们继续按原有方式访问dashboard，即访问https://{kube-apiserver}:6443/ui，但我们得到如下错误信息： Error: 'malformed HTTP response "\x15\x03\x01\x00\x02\x02"' Trying to reach: 'http://10.40.0.5:8443/' 回头再看dashboard的wiki，发现其告知的通过kube-apiserver访问dashboard的url如下： https://{kube-apiserver}:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy 访问该地址后，我们在浏览器中看到如下登录页面： dashboard v1.7.0默认支持两种身份校验登录方式：kubeconfig和token两种。我们说说token这种方式。点击选择:Token单选框，提示你输入token。token从哪里获取，我们从来没有生成过token？其实当前K8s中已经有了很多token： root@ubuntu-k8s-1:~# kubectl get secret [...]]]></description>
			<content:encoded><![CDATA[<p>由于开发的平台要进行内部公开测试，我们这周在公司内部私有云搭建了一套平台。涉及到<a href="http://tonybai.com/tag/kubernetes">Kubernetes</a>相关的基础软件，由我来部署。Kubernetes以及其相关组件都在积极的开发中，版本更新也很快。截至本文撰写时，K8s发布最新稳定版是<a href="https://github.com/kubernetes/kubernetes/releases/tag/v1.7.6">v1.7.6</a>，而与之配套的<a href="https://github.com/kubernetes/dashboard">Dashboard</a>则是<a href="https://github.com/kubernetes/dashboard/releases/tag/v1.7.0">v1.7.0</a>。</p>
<p>最初在部署规划时，我选择了Kubernetes v1.7.6+ <a href="https://github.com/kubernetes/dashboard/releases/tag/v1.6.3">dashboard v1.6.3</a>的组合。之前K8s v1.7.3的稳定让我对使用最新Release版有一些信心，但dashboard v1.7.0则是三天前刚发布的，看<a href="http://tonybai.com/tag/dashboard/">dashboard</a>的commit log，之前还大规模revert了一次。因此，我保守的选择了v1.6.3。</p>
<h2>一、但Dashboard v1.6.3与Kubernetes 1.7.6似乎不匹配</h2>
<p>在<a href="http://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/">Kubernetes Dashboard</a>的兼容性矩阵中，我们能看到dashboard 1.6.x与k8s 1.7.x的兼容性是一个问号。最新dashboard兼容性矩阵点击<a href="https://github.com/kubernetes/dashboard/wiki/Compatibility-matrix">这里</a>可以找到：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-dashboard-compatibility-matrix.png" alt="img{512x368}" /></p>
<p>也就是说由于K8S API可能的变动，Dashboard 1.6.x的某些功能可能无法使用。之前我在阿里云上的测试环境中使用的是<a href="http://tonybai.com/2017/08/09/fix-kube-apiserver-restart-exceptionally-in-k8s-1-7-3/">k8s 1.7.3+dashboard 1.6.3的组合</a>，我需要的功能均可以使用。因此这里我首先尝试了dashboard v1.6.3。</p>
<p>安装过程不赘述。我依旧通过kube-apiserver暴露服务的方式来访问dasbboard，kube-apiserver采用basic auth的身份验证方式。我尝试在浏览器中访问下面路径：</p>
<pre><code>https://{kube-apiserver}:6443/ui

</code></pre>
<p>在浏览器弹出的身份验证对话框中输入user/password后，url跳转到：</p>
<pre><code>https://{kube-apiserver}:6443/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy
</code></pre>
<p>不过等了许久，浏览器页面依旧一片空白。Dashboard的内容并未鲜露出来。通过chrome浏览器自带的”检查”功能，发现一些静态资源（css、js）的get请求都返回404错误。由于时间有限，没有细致查问题所在。我打算用Dashboard 1.7.0试试。</p>
<h2>二、采用Dashboard v1.7.0</h2>
<p>1.7.0版本dashboard主要强化了安全性，增加了登录页面和相关菜单项，并增加了一个kubernetes-dashboard-init-amd64 init容器。我们无需再依赖<a href="http://tonybai.com/2017/07/20/fix-cannot-access-dashboard-in-k8s-1-6-4/">浏览器弹框</a>了。dashboard调整了源码目录结构，安装1.7.0需要执行下面命令：</p>
<pre><code>kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
</code></pre>
<p>安装后，我们继续按原有方式访问dashboard，即访问https://{kube-apiserver}:6443/ui，但我们得到如下错误信息：</p>
<pre><code>Error: 'malformed HTTP response "\x15\x03\x01\x00\x02\x02"'
Trying to reach: 'http://10.40.0.5:8443/'
</code></pre>
<p>回头再看dashboard的wiki，发现其告知的通过kube-apiserver访问dashboard的url如下：</p>
<pre><code>https://{kube-apiserver}:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
</code></pre>
<p>访问该地址后，我们在浏览器中看到如下登录页面：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-dashboard-170-login.png" alt="img{512x368}" /></p>
<p>dashboard v1.7.0默认支持两种身份校验登录方式：kubeconfig和token两种。我们说说token这种方式。点击选择:Token单选框，提示你输入token。token从哪里获取，我们从来没有生成过token？其实当前K8s中已经有了很多token：</p>
<pre><code>root@ubuntu-k8s-1:~# kubectl  get secret -n kube-system
NAME                                     TYPE                                  DATA      AGE
attachdetach-controller-token-8pps2      kubernetes.io/service-account-token   3         4d
bootstrap-signer-token-jfj4q             kubernetes.io/service-account-token   3         4d
 ... ....

service-controller-token-9zqbz           kubernetes.io/service-account-token   3         4d
statefulset-controller-token-m7shd       kubernetes.io/service-account-token   3         4d
token-cleaner-token-sfvm8                kubernetes.io/service-account-token   3         4d
ttl-controller-token-dxjz9               kubernetes.io/service-account-token   3         4d
weave-net-token-zfgbp                    kubernetes.io/service-account-token   3         4d
</code></pre>
<p>想看那个secret对应的token，就执行kubectl describe secret/{token_name} -n kube-system。比如，我们查看一下service-controller-token-9zqbz 对应的token是多少：</p>
<pre><code>root@ubuntu-k8s-1:~# kubectl describe secret/service-controller-token-9zqbz -n kube-system
Name:        service-controller-token-9zqbz
Namespace:    kube-system
Labels:        &lt;none&gt;
Annotations:    kubernetes.io/service-account.name=service-controller
        kubernetes.io/service-account.uid=907b4a3b-9f59-11e7-a3ea-0650cc001a5b

Type:    kubernetes.io/service-account-token

Data
====
ca.crt:        1025 bytes
namespace:    11 bytes
token:        eyJhbG...QH9rfu7QI81QJg

</code></pre>
<p>现在你可以把上面token key对应那一长串copy到dashboard的token输入框中，点击：signin。即可登录。不过由于token对应的Service account的权限不同，即使进入dashboard，也干不了啥，甚至是啥也不能干。</p>
<h2>三、让Dashboard v1.7.0支持basic auth login方式</h2>
<p>我们要用basic auth方式登录dashboard，需要对kubernetes-dashboard.yaml进行如下修改：</p>
<pre><code>        args:
          - --tls-key-file=/certs/dashboard.key
          - --tls-cert-file=/certs/dashboard.crt
          - --authentication-mode=basic    &lt;---- 添加这一行

</code></pre>
<p>然后apply一下该yaml文件，等dashboard pod重新创建ok后，我们就可以user、password方式登录dashboard了：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-dashboard-170-login-by-basic.png" alt="img{512x368}" /></p>
<h2>四、集成heapster</h2>
<p>heapster当前最新版本v1.4.2，我们采用influxdb作为后端，因此使用的是下面的一些yaml文件：</p>
<pre><code>root@ubuntu-k8s-1:~/k8s176-install/dashboard/heapster-1.4.2/deploy/kube-config/influxdb# ls
grafana.yaml  heapster.yaml  influxdb.yaml
</code></pre>
<p>不过在创建这些pod之前，我们先要创建一些权限绑定：</p>
<pre><code>root@ubuntu-k8s-1:~/k8s176-install/dashboard/heapster-1.4.2/deploy/kube-config/rbac# kubectl create -f heapster-rbac.yaml
clusterrolebinding "heapster" created
</code></pre>
<p>heapster使用的grafana是v4.2.0版本，该版本有一个<a href="https://github.com/kubernetes/heapster/issues/1709">bug</a>，一旦运行后，会出现类似如下的错误：</p>
<pre><code># kubectl logs -f  monitoring-grafana-762361155-p9vwj  -n kube-system
Starting a utility program that will configure Grafana
Starting Grafana in foreground mode
t=2017-08-09T06:10:57+0000 lvl=crit msg="Failed to parse /etc/grafana/grafana.ini, open /etc/grafana/grafana.ini: no such file or directory%!(EXTRA []interface {}=[])"
</code></pre>
<p>我们需要将grafana升级到v4.4.1版本。修改上面的heapster-1.4.2/deploy/kube-config/influxdb/grafana.yaml:</p>
<pre><code>    spec:
      containers:
      - name: grafana
        image: gcr.io/google_containers/heapster-grafana-amd64:v4.4.1
</code></pre>
<p>创建heapster:</p>
<pre><code>root@ubuntu-k8s-1:~/k8s176-install/dashboard/heapster-1.4.2/deploy/kube-config# kubectl create -f influxdb/
deployment "monitoring-grafana" created
service "monitoring-grafana" created
serviceaccount "heapster" created
deployment "heapster" created
service "heapster" created
deployment "monitoring-influxdb" created
service "monitoring-influxdb" created
</code></pre>
<p>dashboard在页面上增加了一些新的展示组件，就像下面这样的：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-dashboard-170-new-feature.png" alt="img{512x368}" /></p>
<p><strong>更多内容可以通过我在慕课网开设的实战课程<a href="https://coding.imooc.com/class/284.html">《Kubernetes实战 高可用集群搭建、配置、运维与应用》</a>学习。</strong></p>
<hr />
<p>微博：<a href="http://weibo.com/bigwhite20xx">@tonybai_cn</a><br />
微信公众号：iamtonybai<br />
github.com: https://github.com/bigwhite</p>
<p style='text-align:left'>&copy; 2017 &#8211; 2018, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2017/09/26/some-notes-about-deploying-kubernetes-dashboard-1-7-0/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>解决Kubernetes 1.7.3 kube-apiserver频繁异常重启的问题</title>
		<link>https://tonybai.com/2017/08/09/fix-kube-apiserver-restart-exceptionally-in-k8s-1-7-3/</link>
		<comments>https://tonybai.com/2017/08/09/fix-kube-apiserver-restart-exceptionally-in-k8s-1-7-3/#comments</comments>
		<pubDate>Wed, 09 Aug 2017 14:51:19 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[addon]]></category>
		<category><![CDATA[Authorization]]></category>
		<category><![CDATA[cluster-admin]]></category>
		<category><![CDATA[clusterrole]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[etcd]]></category>
		<category><![CDATA[heapster]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[kube-apiserver]]></category>
		<category><![CDATA[kube-controller-manager]]></category>
		<category><![CDATA[kube-proxy]]></category>
		<category><![CDATA[kube-scheduler]]></category>
		<category><![CDATA[kubeadm]]></category>
		<category><![CDATA[kubelet]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[插件]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=2399</guid>
		<description><![CDATA[近期将之前的一个用kube-up.sh安装的Kubernetes 1.3.7的环境更换为最新发布的用kubeadm安装的Kubernetes 1.7.3版本。新版本的安装过程和之前的采用kubeadm安装的k8s 1.5.x、1.6.x版本类似，这里不赘述了。但在安装Dashboard后，发现了一些问题，这里记录一下解决的过程。 一、第一个问题 我们先来做一下回顾。在《解决Kubernetes 1.6.4 Dashboard无法访问的问题》一文中，我们通过把用户admin bind到cluster-admin这个clusterrole角色上使得dashboard得以正常访问。但访问几次后，我发现了一个问题：那就是用safari访问dashboard时，浏览器可以正常弹出鉴权对话框，让我输入用户名和密码；但用chrome访问时，总是无法弹出鉴权对话框，而直接显示如下错误： User "system:anonymous" cannot get at the cluster scope. kube-apiserver身份验证文档中对anonymous requests做了说明：对于没有被其他身份验证方法拒绝的requests，kube-apiserver会为这样的request赋予用户名: system:anonymous和用户group: system:unauthenticated，这个request将继续流向后面的环节：authorization和admission-control，直到被后面的环节拒绝，返回失败应答。这一些都源于k8s 1.6以后的版本中，kube-apiserver的命令行选项：&#8211;anonymous-auth的默认值改为了true，即允许anonymous request的存在，因此上面chrome在访问kube-apiserver时，不输入user、password也能继续下面的环节，这就是第一个问题及其原因。 二、关闭匿名请求的身份验证权 解决上面这个问题，最直接的方法就是关闭匿名请求的身份验证权，即不接受匿名请求。我们通过在/etc/kubernetes/manifests/kube-apiserver.yaml中添加下面一行来实现： spec: containers: - command: - kube-apiserver - --anonymous-auth=false /etc/kubernetes/manifests/kube-apiserver.yaml被修改后，kubelet会重启kube-apiserver。重启后，我再用chrome访问dashboard，身份验证对话框就出现在眼前了。 三、kube-apiserver周期性异常重启 一直以为问题到这里就解决了。但随后又发生了一个更为严重的问题，那就是：kube-apiserver定期重启，并牵连kube-controller-manager和kube-scheduler的status也不正常了。 通过kubectl describe查看状态异常的kube-apiserver pod，发现如下输出： root@yypdcom2:# kubectl describe pods/kube-apiserver-yypdcom2 -n kube-system&#124;grep health Liveness: http-get https://127.0.0.1:6443/healthz delay=15s timeout=15s period=10s #success=1 #failure=8 可以看到liveness [...]]]></description>
			<content:encoded><![CDATA[<p>近期将之前的一个<a href="http://tonybai.com/2016/10/18/learn-how-to-install-kubernetes-on-ubuntu/">用kube-up.sh安装的Kubernetes 1.3.7</a>的环境更换为最新发布的<a href="http://tonybai.com/2016/12/30/install-kubernetes-on-ubuntu-with-kubeadm/">用kubeadm安装</a>的<a href="https://github.com/kubernetes/kubernetes/releases/tag/v1.7.3">Kubernetes 1.7.3</a>版本。新版本的安装过程和之前的<a href="http://tonybai.com/2017/01/24/explore-kubernetes-cluster-installed-by-kubeadm/">采用kubeadm安装</a>的k8s 1.5.x、<a href="http://tonybai.com/2017/07/20/fix-cannot-access-dashboard-in-k8s-1-6-4/">1.6.x版本</a>类似，这里不赘述了。但在安装<a href="http://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/">Dashboard</a>后，发现了一些问题，这里记录一下解决的过程。</p>
<h2>一、第一个问题</h2>
<p>我们先来做一下回顾。在《<a href="http://tonybai.com/2017/07/20/fix-cannot-access-dashboard-in-k8s-1-6-4/">解决Kubernetes 1.6.4 Dashboard无法访问的问题</a>》一文中，我们通过把用户admin bind到cluster-admin这个clusterrole角色上使得dashboard得以正常访问。但访问几次后，我发现了一个问题：那就是用safari访问dashboard时，浏览器可以正常弹出鉴权对话框，让我输入用户名和密码；但用chrome访问时，总是无法弹出鉴权对话框，而直接显示如下错误：</p>
<pre><code>User "system:anonymous" cannot get  at the cluster scope.
</code></pre>
<p><a href="https://kubernetes.io/docs/admin/authentication/#anonymous-requests">kube-apiserver身份验证</a>文档中对anonymous requests做了说明：对于没有被其他身份验证方法拒绝的requests，kube-apiserver会为这样的request赋予用户名: <strong>system:anonymous</strong>和用户group: <strong>system:unauthenticated</strong>，这个request将继续流向后面的环节：<a href="http://tonybai.com/2016/11/25/the-security-settings-for-kubernetes-cluster/">authorization</a>和admission-control，直到被后面的环节拒绝，返回失败应答。这一些都源于k8s 1.6以后的版本中，kube-apiserver的命令行选项：&#8211;anonymous-auth的默认值改为了true，即允许anonymous request的存在，因此上面chrome在访问kube-apiserver时，不输入user、password也能继续下面的环节，这就是第一个问题及其原因。</p>
<h2>二、关闭匿名请求的身份验证权</h2>
<p>解决上面这个问题，最直接的方法就是关闭匿名请求的身份验证权，即不接受匿名请求。我们通过在/etc/kubernetes/manifests/kube-apiserver.yaml中添加下面一行来实现：</p>
<pre><code>spec:
  containers:
  - command:
    - kube-apiserver
    - --anonymous-auth=false
</code></pre>
<p>/etc/kubernetes/manifests/kube-apiserver.yaml被修改后，kubelet会重启kube-apiserver。重启后，我再用chrome访问dashboard，身份验证对话框就出现在眼前了。</p>
<h2>三、kube-apiserver周期性异常重启</h2>
<p>一直以为问题到这里就解决了。但随后又发生了一个更为严重的问题，那就是：<a href="https://kubernetes.io/docs/admin/kube-apiserver/">kube-apiserver</a>定期重启，并牵连kube-controller-manager和kube-scheduler的status也不正常了。</p>
<p>通过kubectl describe查看状态异常的kube-apiserver pod，发现如下输出：</p>
<pre><code>root@yypdcom2:# kubectl describe pods/kube-apiserver-yypdcom2 -n kube-system|grep health
    Liveness:        http-get https://127.0.0.1:6443/healthz delay=15s timeout=15s period=10s #success=1 #failure=8
</code></pre>
<p>可以看到liveness check有8次failure！8次是kube-apiserver的failure门槛值，这个值在/etc/kubernetes/manifests/kube-apiserver.yaml中我们可以看到：</p>
<pre><code>livenessProbe:
      failureThreshold: 8
      httpGet:
        host: 127.0.0.1
        path: /healthz
        port: 6443
        scheme: HTTPS
      initialDelaySeconds: 15
      timeoutSeconds: 15

</code></pre>
<p>这样，一旦failure次数超限，kubelet会尝试Restart kube-apiserver，这就是问题的原因。那么为什么kube-apiserver的<a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/">liveness check</a>会fail呢？这缘于我们关闭了匿名请求的身份验证权。还是来看/etc/kubernetes/manifests/kube-apiserver.yaml中的livenessProbe段，对于kube-apiserver来说，kubelet会通过访问: https://127.0.0.1:6443/healthz的方式去check是否ok？并且kubelet使用的是anonymous requests。由于上面我们已经关闭了对anonymous-requests的身份验证权，kubelet就会一直无法访问kube-apiserver的/healthz端点，导致kubelet认为kube-apiserver已经死亡，并尝试重启它。</p>
<h2>四、调整/healthz检测的端点</h2>
<p>我们既要保留 &#8211;anonymous-auth=false，还要保证kube-apiserver稳定运行不重启，我们就需要调整kube-apiserver的livenessProbe配置，将liveness probe的endpoint从</p>
<pre><code>https://127.0.0.1:6443/healthz
</code></pre>
<p>改为：</p>
<pre><code>http://127.0.0.1:8080/healthz

</code></pre>
<p>具体对/etc/kubernetes/manifests/kube-apiserver.yaml的修改是：</p>
<pre><code>spec:
  containers:
  - command:
    - kube-apiserver
    - --anonymous-auth=false
    ... ...
    - --insecure-bind-address=127.0.0.1
    - --insecure-port=8080

   livenessProbe:
      failureThreshold: 8
      httpGet:
        host: 127.0.0.1
        path: /healthz
        port: 8080
        scheme: HTTP
      initialDelaySeconds: 15
      timeoutSeconds: 15
... ...
</code></pre>
<p>我们不再用anonymous-requests，但我们可以利用&#8211;insecure-bind-address和&#8211;insecure-port。让kubelet的请求到insecure port，而不是secure port。由于insecure port的流量不会受到身份验证、授权等功能的限制，因此可以成功probe到kube-apiserver的liveness，kubelet不会再重启kube-apiserver了。</p>
<hr />
<p>微博：<a href="http://weibo.com/bigwhite20xx">@tonybai_cn</a><br />
微信公众号：iamtonybai<br />
github.com: https://github.com/bigwhite</p>
<p style='text-align:left'>&copy; 2017, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2017/08/09/fix-kube-apiserver-restart-exceptionally-in-k8s-1-7-3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>解决Kubernetes 1.6.4 Dashboard无法访问的问题</title>
		<link>https://tonybai.com/2017/07/20/fix-cannot-access-dashboard-in-k8s-1-6-4/</link>
		<comments>https://tonybai.com/2017/07/20/fix-cannot-access-dashboard-in-k8s-1-6-4/#comments</comments>
		<pubDate>Thu, 20 Jul 2017 12:54:01 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Admission-Control]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Authorization]]></category>
		<category><![CDATA[clusterrole]]></category>
		<category><![CDATA[ClusterRoleBinding]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[kube-apiserver]]></category>
		<category><![CDATA[kubeadm]]></category>
		<category><![CDATA[kubectl]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[nodeport]]></category>
		<category><![CDATA[role]]></category>
		<category><![CDATA[rolebinding]]></category>
		<category><![CDATA[weave]]></category>
		<category><![CDATA[yaml]]></category>
		<category><![CDATA[容器]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=2381</guid>
		<description><![CDATA[前一段时间将之前采用kubeadm安装的Kubernetes 1.5.1环境升级到了1.6.4版本，升级过程较为顺利。由于该k8s cluster是一个测试环境，当时并没有过于关注，就忙别的事情了。最近项目组打算在这个环境下做一些事情，而当我们重新“捡起”这个环境时，发现Kubernetes Dashboard无法访问了。 Kubernetes的dashboard可以有很多种访问方式，比如：可以通过暴露nodeport的方式(无身份验证，不安全)、可以通过访问apiserver的api服务的方式等。我们的Dashboard通过APIServer进行访问： https://apiserver_ip:secure_port/ui 正常情况下通过浏览器访问：https://apiserver_ip:secure_port/ui，浏览器会弹出身份验证对话框，待输入正确的用户名和密码后，便可成功进入Dashboard了。但当前，我们得到的结果却是： User "system:anonymous" cannot proxy services in the namespace "kube-system". 而访问apiserver(https://apiserver_ip:secure_port/)得到的结果如下： User "system:anonymous" cannot get at the cluster scope. 一、问题原因分析 k8s 1.6.x版本与1.5.x版本的一个很大不同在于1.6.x版本启用了RBAC的Authorization mode(授权模型)，这点在K8s master init的日志中可以得到证实： # kubeadm init --apiserver-advertise-address xx.xx.xx ... ... [init] Using Kubernetes version: v1.6.4 [init] Using Authorization mode: RBAC [preflight] Running pre-flight checks [preflight] Starting the [...]]]></description>
			<content:encoded><![CDATA[<p>前一段时间将之前<a href="http://tonybai.com/2016/12/30/install-kubernetes-on-ubuntu-with-kubeadm/">采用kubeadm安装的Kubernetes 1.5.1</a>环境升级到了<a href="https://github.com/kubernetes/kubernetes/releases/tag/v1.6.4">1.6.4版本</a>，升级过程较为顺利。由于该k8s cluster是一个测试环境，当时并没有过于关注，就忙别的事情了。最近项目组打算在这个环境下做一些事情，而当我们重新“捡起”这个环境时，发现<a href="http://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/">Kubernetes Dashboard</a>无法访问了。</p>
<p>Kubernetes的<a href="https://github.com/kubernetes/dashboard/">dashboard</a>可以有<a href="https://github.com/kubernetes/dashboard/blob/master/docs/user-guide/troubleshooting.md">很多种访问方式</a>，比如：可以通过暴露nodeport的方式(无身份验证，不安全)、可以通过访问<a href="http://tonybai.com/2017/03/03/access-api-server-from-a-pod-through-serviceaccount/">apiserver</a>的api服务的方式等。我们的Dashboard通过APIServer进行访问：</p>
<pre><code>https://apiserver_ip:secure_port/ui
</code></pre>
<p>正常情况下通过浏览器访问：https://apiserver_ip:secure_port/ui，浏览器会弹出<a href="http://tonybai.com/2016/11/25/the-security-settings-for-kubernetes-cluster/">身份验证</a>对话框，待输入正确的用户名和密码后，便可成功进入Dashboard了。但当前，我们得到的结果却是：</p>
<pre><code>User "system:anonymous" cannot proxy services in the namespace "kube-system".

</code></pre>
<p>而访问apiserver(https://apiserver_ip:secure_port/)得到的结果如下：</p>
<pre><code>User "system:anonymous" cannot get  at the cluster scope.
</code></pre>
<h2>一、问题原因分析</h2>
<p>k8s 1.6.x版本与1.5.x版本的一个很大不同在于1.6.x版本启用了<a href="https://kubernetes.io/docs/admin/authorization/rbac/">RBAC</a>的<a href="https://kubernetes.io/docs/admin/authorization/">Authorization mode</a>(授权模型)，这点在K8s master init的日志中可以得到证实：</p>
<pre><code># kubeadm init --apiserver-advertise-address xx.xx.xx
... ...
[init] Using Kubernetes version: v1.6.4
[init] Using Authorization mode: RBAC
[preflight] Running pre-flight checks
[preflight] Starting the kubelet service
[certificates] Generated CA certificate and key.
[certificates] Generated API server certificate and key
.... ...
[apiconfig] Created RBAC rules
[addons] Created essential addon: kube-proxy
[addons] Created essential addon: kube-dns

Your Kubernetes master has initialized successfully!
... ...
</code></pre>
<p>在<a href="http://tonybai.com/2016/11/25/the-security-settings-for-kubernetes-cluster/">《Kubernetes集群的安全配置》</a>一文中我们提到过Kubernetes API server的访问方法：</p>
<pre><code>Authentication(身份验证) -&gt; Authorization（授权）-&gt; Admission Control(入口条件控制)
</code></pre>
<p>只不过在Kubernetes 1.5.x及以前的版本中，Authorization的环节都采用了默认的配置，即”AlwaysAllow”，对访问APIServer并不产生什么影响：</p>
<pre><code># kube-apiserver -h
... ...
--authorization-mode="AlwaysAllow": Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: AlwaysAllow,AlwaysDeny,ABAC,Webhook,RBAC
... ...
</code></pre>
<p>但K8s 1.6.x版本中，&#8211;authorization-mode的值发生了变化：</p>
<pre><code># cat /etc/kubernetes/manifests/kube-apiserver.yaml

spec:
  containers:
  - command:
    - kube-apiserver
    - --allow-privileged=true
    ... ...
    - --basic-auth-file=/etc/kubernetes/basic_auth_file
    - --authorization-mode=RBAC
    ... ...
</code></pre>
<p>注：这里我们依旧通过basic auth方式进行apiserver的Authentication，而不是用<a href="http://tonybai.com/2015/04/30/go-and-https/">客户端数字证书校验</a>等其他方式。</p>
<p>显然问题的原因就在于这里<a href="https://en.wikipedia.org/wiki/Role-based_access_control">RBAC授权方式</a>的使用，让我们无法正常访问Dashboard了。</p>
<h2>二、Kubernetes RBAC Authorization简介</h2>
<p>RBAC Authorization的基本概念是Role和RoleBinding。Role是一些permission的集合；而RoleBinding则是将Role授权给某些User、某些Group或某些<a href="http://tonybai.com/2017/03/03/access-api-server-from-a-pod-through-serviceaccount/">ServiceAccount</a>。K8s官方博客<a href="http://blog.kubernetes.io/2017/04/rbac-support-in-kubernetes.html">《RBAC Support in Kubernetes》</a>一文的中的配图对此做了很生动的诠释：</p>
<p><img src="http://tonybai.com/wp-content/uploads/kubernetes-rbac.png" alt="img{512x368}" /></p>
<p>从上图中我们可以看到：</p>
<p>Role: pod-reader 拥有Pod的get和list permissions；<br />
RoleBinding: pod-reader 将Role: pod-reader授权给右边的User、Group和ServiceAccount。</p>
<p>和Role和RoleBinding对应的是，K8s还有ClusterRole和ClusterRoleBinding的概念，它们不同之处在于：ClusterRole和ClusterRoleBinding是针对整个Cluster范围内有效的，无论用户或资源所在的namespace是什么；而Role和RoleBinding的作用范围是局限在某个k8s namespace中的。</p>
<p>Kubernetes 1.6.4安装时内建了许多Role/ClusterRole和RoleBinds/ClusterRoleBindings：</p>
<pre><code># kubectl get role -n kube-system
NAME                                        AGE
extension-apiserver-authentication-reader   50d
system:controller:bootstrap-signer          50d
system:controller:token-cleaner             50d

# kubectl get rolebinding -n kube-system
NAME                                 AGE
system:controller:bootstrap-signer   50d
system:controller:token-cleaner      50d

# kubectl get clusterrole
NAME                                           AGE
admin                                          50d
cluster-admin                                  50d
edit                                           50d
system:auth-delegator                          50d
system:basic-user                              50d
system:controller:attachdetach-controller      50d
... ...
system:discovery                               50d
system:heapster                                50d
system:kube-aggregator                         50d
system:kube-controller-manager                 50d
system:kube-dns                                50d
system:kube-scheduler                          50d
system:node                                    50d
system:node-bootstrapper                       50d
system:node-problem-detector                   50d
system:node-proxier                            50d
system:persistent-volume-provisioner           50d
view                                           50d
weave-net                                      50d

# kubectl get clusterrolebinding
NAME                                           AGE
cluster-admin                                  50d
kubeadm:kubelet-bootstrap                      50d
kubeadm:node-proxier                           50d
kubernetes-dashboard                           50d
system:basic-user                              50d
system:controller:attachdetach-controller      50d
... ...
system:controller:statefulset-controller       50d
system:controller:ttl-controller               50d
system:discovery                               50d
system:kube-controller-manager                 50d
system:kube-dns                                50d
system:kube-scheduler                          50d
system:node                                    50d
system:node-proxier                            50d
weave-net                                      50d
</code></pre>
<h2>三、Dashboard的role和rolebinding</h2>
<p>Kubernetes 1.6.x启用RBAC后，诸多周边插件也都推出了适合K8s 1.6.x的manifest描述文件，比如：<a href="https://github.com/weaveworks/weave/releases/download/latest_release/weave-daemonset-k8s-1.6.yaml">weave-net</a>等。Dashboard的manifest文件中也增加了关于rolebinding的描述，我当初用的是<a href="https://raw.githubusercontent.com/kubernetes/dashboard/v1.6.1/src/deploy/kubernetes-dashboard.yaml">1.6.1版本</a>，文件内容摘录如下：</p>
<pre><code>// kubernetes-dashboard.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard
  labels:
    k8s-app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system
... ...
</code></pre>
<p>我们看到在kubernetes-dashboard.yaml中，描述文件新建了一个ClusterRoleBinding：<strong>kubernetes-dashboard</strong>。该binding将ClusterRole: <strong>cluster-admin</strong>授权给了一个ServiceAccount: <strong>kubernetes-dashboard</strong>。我们看看ClusterRole: <strong>cluster-admin</strong>都包含了哪些permission:</p>
<pre><code># kubectl get clusterrole/cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: 2017-05-30T14:06:39Z
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: cluster-admin
  resourceVersion: "11"
  selfLink: /apis/rbac.authorization.k8s.io/v1beta1/clusterrolescluster-admin
  uid: 331c79dc-4541-11e7-bc9a-12584ec3a8c9
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'
</code></pre>
<p>可以看到，在rules设定中，cluster-admin似乎拥有了“无限”权限。不过注意：这里仅仅授权给了一个service account，并没有授权给user或group。并且这里的<strong>kubernetes-dashboard</strong>是dashboard访问apiserver时使用的(下图右侧流程)，并不是user访问APIServer时使用的。</p>
<p><img src="http://tonybai.com/wp-content/uploads/kubernetes-dashboard-authentication.png" alt="img{512x368}" /></p>
<p>我们需要给登录dashboard或者说apiserver的user(图左侧)进行授权。</p>
<h2>四、为user: admin进行授权</h2>
<p>我们的kube-apiserver的启动参数中包含：</p>
<pre><code>    - --basic-auth-file=/etc/kubernetes/basic_auth_file
</code></pre>
<p>也就是说我们访问apiserver使用的是basic auth的身份验证方式，而user恰为admin。而从本文开头的错误现象来看，admin这个user并未得到足够的授权。这里我们要做的就是给admin选择一个合适的clusterrole。但kubectl并不支持查看user的信息，初始的clusterrolebinding又那么多，一一查看十分麻烦。我们知道cluster-admin这个clusterrole是全权限的，我们就来将admin这个user与clusterrole: <strong>cluster-admin</strong> bind到一起：</p>
<pre><code># kubectl create clusterrolebinding login-on-dashboard-with-cluster-admin --clusterrole=cluster-admin --user=admin
clusterrolebinding "login-on-dashboard-with-cluster-admin" created

# kubectl get clusterrolebinding/login-on-dashboard-with-cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  creationTimestamp: 2017-07-20T08:57:07Z
  name: login-on-dashboard-with-cluster-admin
  resourceVersion: "5363564"
  selfLink: /apis/rbac.authorization.k8s.io/v1beta1/clusterrolebindingslogin-on-dashboard-with-cluster-admin
  uid: 686a3f36-6d29-11e7-8f69-00163e1001d7
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: admin

</code></pre>
<p>binding后，我们再来访问一下dashboard UI，不出意外的话，熟悉的dashboard界面就会出现在你的眼前。</p>
<blockquote>
<p>注：Kubernetes API Server新增了&#8211;anonymous-auth选项，允许匿名请求访问secure port。没有被其他authentication方法拒绝的请求即Anonymous requests， 这样的匿名请求的username为”system:anonymous”, 归属的组为”system:unauthenticated”。并且该选线是默认的。这样一来，当采用chrome浏览器访问dashboard UI时很可能无法弹出用户名、密码输入对话框，导致后续authorization失败。为了保证用户名、密码输入对话框的弹出，需要将&#8211;anonymous-auth设置为false：</p>
</blockquote>
<pre><code>// /etc/kubernetes/manifests/kube-apiserver.yaml
    - --anonymous-auth=false
</code></pre>
<p>用curl测试结果如下：</p>
<pre><code>$curl -u admin:YOUR_PASSWORD -k https://apiserver_ip:secure_port/
{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "/apis/apps",
    "/apis/apps/v1beta1",
    "/apis/authentication.k8s.io",
    "/apis/authentication.k8s.io/v1",
    "/apis/authentication.k8s.io/v1beta1",
    "/apis/authorization.k8s.io",
    "/apis/authorization.k8s.io/v1",
    "/apis/authorization.k8s.io/v1beta1",
    "/apis/autoscaling",
    "/apis/autoscaling/v1",
    "/apis/autoscaling/v2alpha1",
    "/apis/batch",
    "/apis/batch/v1",
    "/apis/batch/v2alpha1",
    "/apis/certificates.k8s.io",
    "/apis/certificates.k8s.io/v1beta1",
    "/apis/extensions",
    "/apis/extensions/v1beta1",
    "/apis/policy",
    "/apis/policy/v1beta1",
    "/apis/rbac.authorization.k8s.io",
    "/apis/rbac.authorization.k8s.io/v1alpha1",
    "/apis/rbac.authorization.k8s.io/v1beta1",
    "/apis/settings.k8s.io",
    "/apis/settings.k8s.io/v1alpha1",
    "/apis/storage.k8s.io",
    "/apis/storage.k8s.io/v1",
    "/apis/storage.k8s.io/v1beta1",
    "/healthz",
    "/healthz/ping",
    "/healthz/poststarthook/bootstrap-controller",
    "/healthz/poststarthook/ca-registration",
    "/healthz/poststarthook/extensions/third-party-resources",
    "/healthz/poststarthook/rbac/bootstrap-roles",
    "/logs",
    "/metrics",
    "/swaggerapi/",
    "/ui/",
    "/version"
  ]
}

</code></pre>
<hr />
<p>微博：<a href="http://weibo.com/bigwhite20xx">@tonybai_cn</a><br />
微信公众号：iamtonybai<br />
github.com: https://github.com/bigwhite</p>
<p style='text-align:left'>&copy; 2017, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2017/07/20/fix-cannot-access-dashboard-in-k8s-1-6-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>搭建你自己的Go Runtime metrics环境</title>
		<link>https://tonybai.com/2017/07/04/setup-go-runtime-metrics-for-yourself/</link>
		<comments>https://tonybai.com/2017/07/04/setup-go-runtime-metrics-for-yourself/#comments</comments>
		<pubDate>Tue, 04 Jul 2017 09:43:29 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ab]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[apache2]]></category>
		<category><![CDATA[benchmarking]]></category>
		<category><![CDATA[carbon]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[GarbageCollection]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go-runtime-metrics]]></category>
		<category><![CDATA[Go1.5]]></category>
		<category><![CDATA[go1.5.4]]></category>
		<category><![CDATA[Go1.7]]></category>
		<category><![CDATA[go1.7.6]]></category>
		<category><![CDATA[go1.8]]></category>
		<category><![CDATA[go1.8.3]]></category>
		<category><![CDATA[go1.9]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[graphite]]></category>
		<category><![CDATA[graphite-web]]></category>
		<category><![CDATA[mod-wsgi]]></category>
		<category><![CDATA[monitoring]]></category>
		<category><![CDATA[node]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[npm]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[statsd]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[whisper]]></category>
		<category><![CDATA[wrk]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[基准测试]]></category>
		<category><![CDATA[度量]]></category>
		<category><![CDATA[时序数据库]]></category>
		<category><![CDATA[监控]]></category>
		<category><![CDATA[运行时]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=2369</guid>
		<description><![CDATA[自从Go 1.5开始，每次Go release, Gopher Brian Hatfield都会将自己对新版Go的runtime的性能数据（与之前Go版本的比较）在twitter上晒出来。就连Go team staff在世界各地做speaking时也在slide中引用Brian的图片。后来，Brian Hatfield将其用于度量runtime性能数据的代码打包成library并放在github上开源了，我们也可以使用这个library来建立我们自己的Go Runtime metrics设施了。这里简要说一下搭建的步骤。 一、环境与原理 Brian Hatfield的go-runtime-metrics library实现的很简单，其runtime data来自于Go runtime package中的MemStats、NumGoroutine和NumCgoCall等。被测试目标程序只需要import该library即可输出runtime states数据： import _ "github.com/bmhatfield/go-runtime-metrics" go-runtime-metrics library将启动一个单独的goroutine，并定时上报runtime数据。目前该library仅支持向statsD输出数据，用户可以通过配置将statsD的数据导入graphite并使用graphite web查看，流程如下图： 本次实验环境为ubuntu 16.04.1： $ uname -rmn tonybai-ThinkCentre-M6600s-N000 4.4.0-83-generic x86_64 二、搭建步骤 1、安装go-runtime-metrics library 我们直接go get就可以下载go-runtime-metrics library： $ go get github.com/bmhatfield/go-runtime-metrics 我们编写一个目标程序： //main.go package main import ( "flag" "log" "net/http" "os" _ "github.com/bmhatfield/go-runtime-metrics" [...]]]></description>
			<content:encoded><![CDATA[<p>自从<a href="http://tonybai.com/2015/07/10/some-changes-in-go-1-5/">Go 1.5</a>开始，每次<a href="https://golang.org/dl">Go release</a>, Gopher <a href="https://twitter.com/brianhatfield">Brian Hatfield</a>都会将自己对新版Go的runtime的性能数据（与之前Go版本的比较）在twitter上晒出来。就连Go team staff在世界各地做speaking时也在slide中引用Brian的图片。后来，Brian Hatfield将其用于度量runtime性能数据的代码打包成library并放在github上<a href="https://github.com/bmhatfield/go-runtime-metrics">开源了</a>，我们也可以使用这个<a href="https://github.com/bmhatfield/go-runtime-metrics">library</a>来建立我们自己的Go Runtime metrics设施了。这里简要说一下搭建的步骤。</p>
<h2>一、环境与原理</h2>
<p>Brian Hatfield的go-runtime-metrics library实现的很简单，其runtime data来自于Go runtime package中的MemStats、NumGoroutine和NumCgoCall等。被测试目标程序只需要import该library即可输出runtime states数据：</p>
<pre><code>import _ "github.com/bmhatfield/go-runtime-metrics"
</code></pre>
<p>go-runtime-metrics library将启动一个单独的<a href="http://tonybai.com/2017/06/23/an-intro-about-goroutine-scheduler/">goroutine</a>，并定时上报runtime数据。目前该library仅支持向<a href="https://github.com/etsy/statsd">statsD</a>输出数据，用户可以通过配置将statsD的数据导入<a href="http://graphiteapp.org/">graphite</a>并使用graphite web查看，流程如下图：</p>
<p><img src="http://tonybai.com/wp-content/uploads/go-runtime-metrics/go-runtime-metrics.png" alt="img{512x368}" /></p>
<p>本次实验环境为<a href="http://tonybai.com/tag/ubuntu">ubuntu</a> 16.04.1：</p>
<pre><code>$ uname -rmn
tonybai-ThinkCentre-M6600s-N000 4.4.0-83-generic x86_64
</code></pre>
<h2>二、搭建步骤</h2>
<h3>1、安装go-runtime-metrics library</h3>
<p>我们直接go get就可以下载go-runtime-metrics library：</p>
<pre><code>$ go get github.com/bmhatfield/go-runtime-metrics
</code></pre>
<p>我们编写一个目标程序：</p>
<pre><code>//main.go
package main

import (
    "flag"
    "log"
    "net/http"
    "os"

    _ "github.com/bmhatfield/go-runtime-metrics"
)

func main() {
    flag.Parse()

    cwd, err := os.Getwd()
    if err != nil {
        log.Fatal(err)
    }

    srv := &amp;http.Server{
        Addr:    ":8000", // Normally ":443"
        Handler: http.FileServer(http.Dir(cwd)),
    }
    log.Fatal(srv.ListenAndServe())
}
</code></pre>
<p>我的ubuntu主机上安装了四个go版本，它们分别是go 1.5.4、go 1.7.6、go 1.8.3和go1.9beta2，于是我们分别用这四个版本的server作为被测程序进行go runtime数据上报，以便对比。</p>
<pre><code>$ GOROOT=~/.bin/go154 ~/.bin/go154/bin/go build -o server-go154 main.go
$ GOROOT=~/.bin/go174 ~/.bin/go174/bin/go build -o server-go174 main.go
$ GOROOT=~/.bin/go183 ~/.bin/go183/bin/go build -o server-go183 main.go
$ GOROOT=~/.bin/go19beta2 ~/.bin/go19beta2/bin/go build -o server-go19beta2 main.go

$ ls -l

-rwxr-xr-x 1 tonybai tonybai 6861176 7月   4 13:49 server-go154
-rwxrwxr-x 1 tonybai tonybai 5901876 7月   4 13:50 server-go174
-rwxrwxr-x 1 tonybai tonybai 6102879 7月   4 13:51 server-go183
-rwxrwxr-x 1 tonybai tonybai 6365648 7月   4 13:51 server-go19beta2

</code></pre>
<h3>2、安装、配置和运行statsD</h3>
<p><a href="https://github.com/etsy/statsd">statsD</a>这个工具用于收集统计信息，并将聚合后的信息发给后端服务（比如：graphite）。statsD是采用js实现的服务，因此需要安装<a href="https://nodejs.org/en/">nodejs</a>、<a href="https://www.npmjs.com/">npm</a>和相关modules：</p>
<pre><code>$ sudo apt-get install nodejs
$ sudo apt-get install npm
</code></pre>
<p>接下来，我们将statsD项目clone到本地并根据exampleConfig.js模板配置一个我们自己用的goruntimemetricConfig.js（基本上就是保留默认配置）:</p>
<pre><code>// goruntimemetricConfig.js
{
  graphitePort: 2003
, graphiteHost: "127.0.0.1"
, port: 8125
, backends: [ "./backends/graphite" ]
}
</code></pre>
<p>启动statsD:</p>
<pre><code>$ nodejs stats.js goruntimemetricConfig.js
3 Jul 11:14:20 - [7939] reading config file: goruntimemetricConfig.js
3 Jul 11:14:20 - server is up INFO
</code></pre>
<p>启动成功！</p>
<h3>3、安装、配置和运行graphite</h3>
<p><a href="https://github.com/graphite-project">graphite</a>是一种存储时序监控数据，并可以按用户需求以图形化形式展示数据的工具，它包括三个组件：</p>
<ul>
<li><a href="https://github.com/graphite-project/whisper">whisper</a></li>
</ul>
<p>whisper是一种基于file的时序数据库格式，同时whisper也提供了相应的命令和API供其他组件调用以操作时序数据库；</p>
<ul>
<li><a href="https://github.com/graphite-project/carbon">carbon</a></li>
</ul>
<p>carbon用于读取外部推送的metrics信息，进行聚合并写入db，它还支持缓存热点数据，提升访问效率。</p>
<ul>
<li><a href="https://github.com/graphite-project/graphite-web">graphite-web</a>。</li>
</ul>
<p>graphite-web则是针对用户的图形化系统，用于定制展示监控数据的。</p>
<p>Graphite的安装和配置是略微繁琐的，我们一步一步慢慢来。</p>
<h4>a) 安装graphite</h4>
<pre><code>$sudo apt-get install graphite-web graphite-carbon

whisper将作为依赖自动被安装。
</code></pre>
<h4>b) local_settings.py</h4>
<p>graphite的主配置文件在/etc/graphite/local_settings.py，文件里面有很多配置项，这里仅列出有关的，且本次生效的配置：</p>
<pre><code>// /etc/graphite/local_settings.py

TIME_ZONE = 'Asia/Shanghai'

LOG_RENDERING_PERFORMANCE = True
LOG_CACHE_PERFORMANCE = True
LOG_METRIC_ACCESS = True

GRAPHITE_ROOT = '/usr/share/graphite-web'

CONF_DIR = '/etc/graphite'
STORAGE_DIR = '/var/lib/graphite/whisper'
CONTENT_DIR = '/usr/share/graphite-web/static'

WHISPER_DIR = '/var/lib/graphite/whisper'
LOG_DIR = '/var/log/graphite'
INDEX_FILE = '/var/lib/graphite/search_index'  # Search index file

DATABASES = {
    'default': {
        'NAME': '/var/lib/graphite/graphite.db',
        'ENGINE': 'django.db.backends.sqlite3',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': ''
    }
}
</code></pre>
<h4>c) 同步数据库</h4>
<p>接下来执行下面两个命令来做database sync(同步)：</p>
<pre><code>$ sudo graphite-manage migrate auth
.. ....
Operations to perform:
  Apply all migrations: auth
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK

$ sudo graphite-manage syncdb

Operations to perform:
  Synchronize unmigrated apps: account, cli, render, whitelist, metrics, url_shortener, dashboard, composer, events, browser
  Apply all migrations: admin, contenttypes, tagging, auth, sessions
Synchronizing apps without migrations:
  Creating tables...
    Creating table account_profile
    Creating table account_variable
    Creating table account_view
    Creating table account_window
    Creating table account_mygraph
    Creating table dashboard_dashboard
    Creating table events_event
    Creating table url_shortener_link
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying admin.0001_initial... OK
  Applying sessions.0001_initial... OK
  Applying tagging.0001_initial... OK

You have installed Django's auth system, and don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'root'):
Email address: xx@yy.com
Password:
Password (again):
Superuser created successfully.

</code></pre>
<p>这里我们创建一个superuser：root，用于登录graphite-web时使用。</p>
<h4>d) 配置carbon</h4>
<p>涉及carbon的配置文件如下，我们保持默认配置不动：</p>
<pre><code>/etc/carbon/carbon.conf（内容太多，这里不列出来了）

/etc/carbon/storage-schemas.conf
[carbon]
pattern = ^carbon\.
retentions = 60:90d

[default_1min_for_1day]
pattern = .*
retentions = 60s:1d

[stats]
pattern = ^stats.*
retentions = 10s:6h,1min:6d,10min:1800d
</code></pre>
<p>carbon有一个cache功能，我们通过下面步骤可以将其打开：</p>
<pre><code>打开carbon-cache使能开关：

$ vi /etc/default/graphite-carbon
CARBON_CACHE_ENABLED=true

启动carbon-cache：

$ sudo cp /usr/share/doc/graphite-carbon/examples/storage-aggregation.conf.example /etc/carbon/storage-aggregation.conf
$ systemctl start carbon-cache
</code></pre>
<h4>e) 启动graphite-web</h4>
<p>graphite-web支持多种主流web server，这里以<a href="http://tonybai.com/tag/apache">apache2</a>为例，graphite-web将mod-wsgi方式部署在apache2下面：</p>
<pre><code>$sudo apt-get install apache2 libapache2-mod-wsgi

$ sudo service apache2 start

$ sudo a2dissite 000-default
Site 000-default disabled.

$ sudo service apache2 reload

$ sudo cp /usr/share/graphite-web/apache2-graphite.conf /etc/apache2/sites-available

$ sudo  a2ensite apache2-graphite
Enabling site apache2-graphite.
To activate the new configuration, you need to run:
  service apache2 reload

$ sudo systemctl reload apache2
</code></pre>
<p>由于apache2的Worker process默认以www-data:www-data用户权限运行，但数据库文件的访问权限却是：_graphite:_graphite：</p>
<pre><code>$ ll /var/lib/graphite/graphite.db
-rw-r--r-- 1 _graphite _graphite 72704 7月   3 13:48 /var/lib/graphite/graphite.db
</code></pre>
<p>我们需要修改一下apache worker的user：</p>
<pre><code>$ sudo vi /etc/apache2/envvars

export APACHE_RUN_USER=_graphite
export APACHE_RUN_GROUP=_graphite
</code></pre>
<p>重启apache2生效！使用Browser打开：http://127.0.0.1，如无意外，你将看到下面graphite-web的首页：</p>
<p><img src="http://tonybai.com/wp-content/uploads/go-runtime-metrics/graphite-web-homepage.png" alt="img{512x368}" /></p>
<h2>三、执行benchmarking</h2>
<p>这里我将使用<a href="https://github.com/wg/wrk">wrk</a>这个http benchmarking tool分别对前面的四个版本的目标程序(server-go154  server-go174  server-go183  server-go19beta2)进行benchmarking test，每个目标程序接收10分钟的请求：</p>
<pre><code>$ ./server-go154
$ wrk -t12 -c400 -d10m http://127.0.0.1:8000

$ ./server-go174
$ wrk -t12 -c400 -d10m http://127.0.0.1:8000

$ ./server-go183
$ wrk -t12 -c400 -d10m http://127.0.0.1:8000

$ ./server-go19beta2
$ wrk -t12 -c400 -d10m http://127.0.0.1:8000

</code></pre>
<h2>四、结果展示</h2>
<p>用浏览器打开graphite-web，在左边的tree标签下以此打开树形结构：Metrics -> stats -> gauges -> go -> YOUR_HOST_NAME -> mem -> gc -> pause，如果顺利的话，你将会在Graphite Composer窗口看到折线图，我们也以GC pause为例，GC pause也是gopher们最为关心的：</p>
<p><img src="http://tonybai.com/wp-content/uploads/go-runtime-metrics/go-runtime-metrics-gc-pause.png" alt="img{512x368}" /></p>
<p>通过这幅图（左侧坐标轴的单位为nanoseconds），我们大致可以看出：</p>
<p>Go 1.5.4的GC pause约在600μs左右；<br />
Go 1.7.4的GC pause约在300μs左右；<br />
Go 1.8.3和Go 1.9beta2的GC pause基本都在100μs以下了。Go 1.9的GC改进似乎不大。不过这里我的程序也并不足够典型。</p>
<p>其他结果：</p>
<p>Go routines number：</p>
<p><img src="http://tonybai.com/wp-content/uploads/go-runtime-metrics/go-runtime-metrics-cpu-goroutines.png" alt="img{512x368}" /></p>
<p>GC count：</p>
<p><img src="http://tonybai.com/wp-content/uploads/go-runtime-metrics/go-runtime-metrics-gc-count.png" alt="img{512x368}" /></p>
<p>memory allocations：</p>
<p><img src="http://tonybai.com/wp-content/uploads/go-runtime-metrics/go-runtime-metrics-mem-allocs.png" alt="img{512x368}" /></p>
<p>除了查看单个指标曲线，你也可以通过graphite-web提供的dashboard功能定制你要monitor的面板，这里就不赘述了。</p>
<h2>五、参考资料</h2>
<ul>
<li><a href="https://linoxide.com/ubuntu-how-to/setup-graphite-statsd-ubuntu-16-04/">How to Setup Graphite with Statsd on an Ubuntu 16.04</a></li>
</ul>
<hr />
<p>微博：<a href="http://weibo.com/bigwhite20xx">@tonybai_cn</a><br />
微信公众号：iamtonybai<br />
github.com: https://github.com/bigwhite</p>
<p style='text-align:left'>&copy; 2017, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2017/07/04/setup-go-runtime-metrics-for-yourself/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用Fluentd和ElasticSearch Stack实现Kubernetes的集群Logging</title>
		<link>https://tonybai.com/2017/03/03/implement-kubernetes-cluster-level-logging-with-fluentd-and-elasticsearch-stack/</link>
		<comments>https://tonybai.com/2017/03/03/implement-kubernetes-cluster-level-logging-with-fluentd-and-elasticsearch-stack/#comments</comments>
		<pubDate>Fri, 03 Mar 2017 12:17:38 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[borg]]></category>
		<category><![CDATA[Ceph]]></category>
		<category><![CDATA[cephrbd]]></category>
		<category><![CDATA[container]]></category>
		<category><![CDATA[DaemonSet]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[EFK]]></category>
		<category><![CDATA[ElasticSearch]]></category>
		<category><![CDATA[ELK]]></category>
		<category><![CDATA[Fluentd]]></category>
		<category><![CDATA[gem]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[heapster]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[Kibana]]></category>
		<category><![CDATA[kube-up.sh]]></category>
		<category><![CDATA[kubeadm]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Logstash]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[serviceaccount]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=2200</guid>
		<description><![CDATA[在本篇文章中，我们继续来说Kubernetes。 经过一段时间的探索，我们先后完成了Kubernetes集群搭建，DNS、Dashboard、Heapster等插件安装，集群安全配置，搭建作为Persistent Volume的CephRBD，以及服务更新等探索和实现工作。现在Kubernetes集群层面的Logging需求逐渐浮上水面了。 随着一些小应用在我们的Kubernetes集群上的部署上线，集群的运行迈上了正轨。但问题随之而来，那就是如何查找和诊断集群自身的问题以及运行于Pod中应用的问题。日志，没错！我们也只能依赖Kubernetes组件以及Pod中应用输出的日志。不过目前我们仅能通过kubectl logs命令或Kubernetes Dashboard来查看Log。在没有cluster level logging的情况下，我们需要分别查看各个Pod的日志，操作繁琐，过程低效。我们迫切地需要为Kubernetes集群搭建一套集群级别的集中日志收集和分析设施。 对于任何基础设施或后端服务系统，日志都是极其重要的。对于受Google内部容器管理系统Borg启发而催生出的Kubernetes项目来说，自然少不了对Logging的支持。在“Logging Overview“中，官方概要介绍了Kubernetes上的几个层次的Logging方案，并给出Cluster-level logging的参考架构： Kubernetes还给出了参考实现： &#8211; Logging Backend：Elastic Search stack(包括：Kibana) &#8211; Logging-agent：fluentd ElasticSearch stack实现的cluster level logging的一个优势在于其对Kubernetes集群中的Pod没有侵入性，Pod无需做任何配合性改动。同时EFK/ELK方案在业内也是相对成熟稳定的。 在本文中，我将为我们的Kubernetes 1.3.7集群安装ElasticSearch、Fluentd和Kibana。由于1.3.7版本略有些old，EFK能否在其上面run起来，我也是心中未知。能否像《生化危机：终章》那样有一个完美的结局，我们还需要一步一步“打怪升级”慢慢看。 一、Kubernetes 1.3.7集群的 “漏网之鱼” Kubernetes 1.3.7集群是通过kube-up.sh搭建并初始化的。按照K8s官方文档有关elasticsearch logging的介绍，在kubernetes/cluster/ubuntu/config-default.sh中，我也发现了下面几个配置项： // kubernetes/cluster/ubuntu/config-default.sh # Optional: Enable node logging. ENABLE_NODE_LOGGING=false LOGGING_DESTINATION=${LOGGING_DESTINATION:-elasticsearch} # Optional: When set to true, Elasticsearch and Kibana will be setup as part of [...]]]></description>
			<content:encoded><![CDATA[<p>在本篇文章中，我们继续来说<a href="http://tonybai.com/tag/kubernetes">Kubernetes</a>。</p>
<p>经过一段时间的探索，我们先后完成了<a href="http://tonybai.com/2016/10/18/learn-how-to-install-kubernetes-on-ubuntu/">Kubernetes集群搭建</a>，<a href="http://tonybai.com/2016/10/23/install-dns-addon-for-k8s/">DNS</a>、<a href="http://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/">Dashboard</a>、<a href="http://tonybai.com/2017/01/20/integrate-heapster-for-kubernetes-dashboard/">Heapster</a>等插件安装，<a href="http://tonybai.com/2016/11/25/the-security-settings-for-kubernetes-cluster/">集群安全配置</a>，搭建<a href="http://tonybai.com/2016/11/07/integrate-kubernetes-with-ceph-rbd/">作为Persistent Volume的CephRBD</a>，以及<a href="http://tonybai.com/2017/02/09/rolling-update-for-services-in-kubernetes-cluster">服务更新</a>等<a href="http://tonybai.com/2017/01/24/explore-kubernetes-cluster-installed-by-kubeadm">探索</a>和实现工作。现在Kubernetes<a href="https://kubernetes.io/docs/user-guide/logging/overview/">集群层面的Logging</a>需求逐渐浮上水面了。</p>
<p>随着一些小应用在我们的Kubernetes集群上的部署上线，集群的运行迈上了正轨。但问题随之而来，那就是如何查找和诊断集群自身的问题以及运行于Pod中应用的问题。日志，没错！我们也只能依赖Kubernetes组件以及Pod中应用输出的日志。不过目前我们仅能通过kubectl logs命令或<a href="http://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/">Kubernetes Dashboard</a>来查看Log。在没有cluster level logging的情况下，我们需要分别查看各个Pod的日志，操作繁琐，过程低效。我们迫切地需要为Kubernetes集群搭建一套集群级别的集中日志收集和分析设施。</p>
<p>对于任何基础设施或后端服务系统，日志都是极其重要的。对于受Google内部容器管理系统Borg启发而催生出的Kubernetes项目来说，自然少不了对Logging的支持。在“<a href="https://kubernetes.io/docs/user-guide/logging/overview/">Logging Overview</a>“中，官方概要介绍了Kubernetes上的几个层次的Logging方案，并给出Cluster-level logging的参考架构：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-cluster-level-logging-architectures.png" alt="img{512x368}" /></p>
<p>Kubernetes还给出了参考实现：<br />
    &#8211; Logging Backend：<a href="https://www.elastic.co/">Elastic Search</a> stack(包括：<a href="https://www.elastic.co/products/kibana">Kibana</a>)<br />
    &#8211; Logging-agent：<a href="http://www.fluentd.org/">fluentd</a></p>
<p>ElasticSearch stack实现的cluster level logging的一个优势在于其对Kubernetes集群中的Pod没有侵入性，Pod无需做任何配合性改动。同时EFK/ELK方案在业内也是相对成熟稳定的。</p>
<p>在本文中，我将为我们的Kubernetes 1.3.7集群安装ElasticSearch、Fluentd和Kibana。由于1.3.7版本略有些old，EFK能否在其上面run起来，我也是心中未知。能否像《<a href="https://movie.douban.com/subject/20471852/">生化危机：终章</a>》那样有一个完美的结局，我们还需要一步一步“打怪升级”慢慢看。</p>
<h3>一、Kubernetes 1.3.7集群的 “漏网之鱼”</h3>
<p>Kubernetes 1.3.7集群是通过kube-up.sh搭建并初始化的。按照<a href="https://kubernetes.io/docs/user-guide/logging/elasticsearch/">K8s官方文档</a>有关elasticsearch logging的介绍，在kubernetes/cluster/ubuntu/config-default.sh中，我也发现了下面几个配置项：</p>
<pre><code>// kubernetes/cluster/ubuntu/config-default.sh
# Optional: Enable node logging.
ENABLE_NODE_LOGGING=false
LOGGING_DESTINATION=${LOGGING_DESTINATION:-elasticsearch}

# Optional: When set to true, Elasticsearch and Kibana will be setup as part of the cluster bring up.
ENABLE_CLUSTER_LOGGING=false
ELASTICSEARCH_LOGGING_REPLICAS=${ELASTICSEARCH_LOGGING_REPLICAS:-1}
</code></pre>
<p>显然，当初如果搭建集群伊始时要是知道这些配置的意义，可能那个时候就会将elastic logging集成到集群中了。现在为时已晚，集群上已经跑了很多应用，无法重新通过kube-up.sh中断集群运行并安装elastic logging了。我只能手工进行安装了！</p>
<h3>二、镜像准备</h3>
<p>1.3.7源码中kubernetes/cluster/addons/fluentd-elasticsearch下的manifest已经比较old了，我们直接使用kubernetes最新源码中的<a href="https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch">manifest文件</a>：</p>
<pre><code>k8s.io/kubernetes/cluster/addons/fluentd-elasticsearch$ ls *.yaml
es-controller.yaml  es-service.yaml  fluentd-es-ds.yaml  kibana-controller.yaml  kibana-service.yaml
</code></pre>
<p>分析这些yaml，我们需要三个镜像：</p>
<pre><code> gcr.io/google_containers/fluentd-elasticsearch:1.22
 gcr.io/google_containers/elasticsearch:v2.4.1-1
 gcr.io/google_containers/kibana:v4.6.1-1
</code></pre>
<p>显然镜像都在墙外。由于生产环境下的Docker引擎并没有配置加速器代理，因此我们需要手工下载一下这三个镜像。我采用的方法是通过另外一台配置了加速器的机器上的<a href="http://tonybai.com/tag/docker">Docker引擎</a>将三个image下载，并重新打tag，上传到我在hub.docker.com上的账号下，以elasticsearch:v2.4.1-1为例：</p>
<pre><code># docker pull  gcr.io/google_containers/elasticsearch:v2.4.1-1
# docker tag gcr.io/google_containers/elasticsearch:v2.4.1-1 bigwhite/elasticsearch:v2.4.1-1
# docker push bigwhite/elasticsearch:v2.4.1-1
</code></pre>
<p>下面是我们在后续安装过程中真正要使用到的镜像：</p>
<pre><code>bigwhite/fluentd-elasticsearch:1.22
bigwhite/elasticsearch:v2.4.1-1
bigwhite/kibana:v4.6.1-1
</code></pre>
<h3>三、启动fluentd</h3>
<p>fluentd是以<a href="https://kubernetes.io/docs/admin/daemons/">DaemonSet</a>的形式跑在K8s集群上的，这样k8s可以保证每个k8s cluster node上都会启动一个fluentd(注意：将image改为上述镜像地址，如果你配置了加速器，那自然就不必了)。</p>
<pre><code># kubectl create -f fluentd-es-ds.yaml --record
daemonset "fluentd-es-v1.22" created
</code></pre>
<p>查看daemonset中的Pod的启动情况，我们发现：</p>
<pre><code>kube-system                  fluentd-es-v1.22-as3s5                  0/1       CrashLoopBackOff   2          43s       172.16.99.6    10.47.136.60
kube-system                  fluentd-es-v1.22-qz193                  0/1       CrashLoopBackOff   2          43s       172.16.57.7    10.46.181.146
</code></pre>
<p>fluentd Pod启动失败，fluentd的日志可以通过/var/log/fluentd.log查看：</p>
<pre><code># tail -100f /var/log/fluentd.log

2017-03-02 02:27:01 +0000 [info]: reading config file path="/etc/td-agent/td-agent.conf"
2017-03-02 02:27:01 +0000 [info]: starting fluentd-0.12.31
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-mixin-config-placeholders' version '0.4.0'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-mixin-plaintextformatter' version '0.2.6'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-docker_metadata_filter' version '0.1.3'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-elasticsearch' version '1.5.0'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-kafka' version '0.4.1'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-kubernetes_metadata_filter' version '0.24.0'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-mongo' version '0.7.16'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-rewrite-tag-filter' version '1.5.5'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-s3' version '0.8.0'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-scribe' version '0.10.14'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-td' version '0.10.29'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-td-monitoring' version '0.2.2'
2017-03-02 02:27:01 +0000 [info]: gem 'fluent-plugin-webhdfs' version '0.4.2'
2017-03-02 02:27:01 +0000 [info]: gem 'fluentd' version '0.12.31'
2017-03-02 02:27:01 +0000 [info]: adding match pattern="fluent.**" type="null"
2017-03-02 02:27:01 +0000 [info]: adding filter pattern="kubernetes.**" type="kubernetes_metadata"
2017-03-02 02:27:02 +0000 [error]: config error file="/etc/td-agent/td-agent.conf" error="Invalid Kubernetes API v1 endpoint https://192.168.3.1:443/api: 401 Unauthorized"
2017-03-02 02:27:02 +0000 [info]: process finished code=256
2017-03-02 02:27:02 +0000 [warn]: process died within 1 second. exit.
</code></pre>
<p>从上述日志中的error来看：fluentd访问apiserver secure port(443)出错了：Unauthorized!  通过分析 cluster/addons/fluentd-elasticsearch/fluentd-es-image/build.sh和td-agent.conf，我们发现是fluentd image中的<a href="https://github.com/fabric8io/fluent-plugin-kubernetes_metadata_filter">fluent-plugin-kubernetes_metadata_filter</a>要去访问API Server以获取一些kubernetes的metadata信息。不过未做任何特殊配置的fluent-plugin-kubernetes_metadata_filter，我猜测它使用的是kubernetes为Pod传入的环境变量：KUBERNETES_SERVICE_HOST和KUBERNETES_SERVICE_PORT来得到API Server的访问信息的。但API Server在secure port上是开启了<a href="http://tonybai.com/2016/11/25/the-security-settings-for-kubernetes-cluster/">安全身份验证机制</a>的，fluentd直接访问必然是失败的。</p>
<p>我们找到了fluent-plugin-kubernetes_metadata_filter项目在github.com上的<a href="https://github.com/fabric8io/fluent-plugin-kubernetes_metadata_filter">主页</a>，在这个页面上我们看到了fluent-plugin-kubernetes_metadata_filter支持的其他配置，包括：ca_file、client_cert、client_key等，显然这些字眼非常眼熟。我们需要修改一下fluentd image中td-agent.conf的配置，为fluent-plugin-kubernetes_metadata_filter增加一些配置项，比如：</p>
<pre><code>// td-agent.conf
... ...
&lt;filter kubernetes.**&gt;
  type kubernetes_metadata
  ca_file /srv/kubernetes/ca.crt
  client_cert /srv/kubernetes/kubecfg.crt
  client_key /srv/kubernetes/kubecfg.key
&lt;/filter&gt;
... ...
</code></pre>
<p>这里我不想重新制作image，那么怎么办呢？Kubernetes提供了<a href="https://kubernetes.io/docs/user-guide/configmap/">ConfigMap</a>这一强大的武器，我们可以将新版td-agent.conf制作成kubernetes的configmap资源，并挂载到fluentd pod的相应位置以替换image中默认的td-agent.conf。</p>
<p>需要注意两点：<br />
 * 在基于td-agent.conf创建configmap资源之前，需要将td-agent.conf中的注释行都删掉，否则生成的configmap的内容可能不正确；<br />
 * fluentd pod将创建在kube-system下，因此configmap资源也需要创建在kube-system namespace下面，否则kubectl create无法找到对应的configmap。</p>
<pre><code># kubectl create configmap td-agent-config --from-file=./td-agent.conf -n kube-system
configmap "td-agent-config" created

# kubectl get configmaps -n kube-system
NAME              DATA      AGE
td-agent-config   1         9s

# kubectl get configmaps td-agent-config -o yaml
apiVersion: v1
data:
  td-agent.conf: |
    &lt;match fluent.**&gt;
      type null
    &lt;/match&gt;

    &lt;source&gt;
      type tail
      path /var/log/containers/*.log
      pos_file /var/log/es-containers.log.pos
      time_format %Y-%m-%dT%H:%M:%S.%NZ
      tag kubernetes.*
      format json
      read_from_head true
    &lt;/source&gt;
... ...
</code></pre>
<p>fluentd-es-ds.yaml也要随之做一些改动，主要是增加两个mount: 一个是mount 上面的configmap td-agent-config，另外一个就是mount hostpath：/srv/kubernetes以获取到相关client端的数字证书：</p>
<pre><code>  spec:
      containers:
      - name: fluentd-es
        image: bigwhite/fluentd-elasticsearch:1.22
        command:
          - '/bin/sh'
          - '-c'
          - '/usr/sbin/td-agent 2&gt;&amp;1 &gt;&gt; /var/log/fluentd.log'
        resources:
          limits:
            memory: 200Mi
          #requests:
            #cpu: 100m
            #memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: td-agent-config
          mountPath: /etc/td-agent
        - name: tls-files
          mountPath: /srv/kubernetes
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: td-agent-config
        configMap:
          name: td-agent-config
      - name: tls-files
        hostPath:
          path: /srv/kubernetes
</code></pre>
<p>接下来，我们重新创建fluentd ds，步骤不赘述。这回我们的创建成功了：</p>
<pre><code>kube-system                  fluentd-es-v1.22-adsrx                  1/1       Running    0          1s        172.16.99.6    10.47.136.60
kube-system                  fluentd-es-v1.22-rpme3                  1/1       Running    0          1s        172.16.57.7    10.46.181.146
</code></pre>
<p>但通过查看/var/log/fluentd.log，我们依然能看到“问题”：</p>
<pre><code>2017-03-02 03:57:58 +0000 [warn]: temporarily failed to flush the buffer. next_retry=2017-03-02 03:57:59 +0000 error_class="Fluent::ElasticsearchOutput::ConnectionFailure" error="Can not reach Elasticsearch cluster ({:host=&gt;\"elasticsearch-logging\", :port=&gt;9200, :scheme=&gt;\"http\"})!" plugin_id="object:3fd99fa857d8"
  2017-03-02 03:57:58 +0000 [warn]: suppressed same stacktrace
2017-03-02 03:58:00 +0000 [warn]: temporarily failed to flush the buffer. next_retry=2017-03-02 03:58:03 +0000 error_class="Fluent::ElasticsearchOutput::ConnectionFailure" error="Can not reach Elasticsearch cluster ({:host=&gt;\"elasticsearch-logging\", :port=&gt;9200, :scheme=&gt;\"http\"})!" plugin_id="object:3fd99fa857d8"
2017-03-02 03:58:00 +0000 [info]: process finished code=9
2017-03-02 03:58:00 +0000 [error]: fluentd main process died unexpectedly. restarting.
</code></pre>
<p>由于ElasticSearch logging还未创建，这是连不上elasticsearch所致。</p>
<h3>四、启动elasticsearch</h3>
<p>启动elasticsearch：</p>
<pre><code># kubectl create -f es-controller.yaml
replicationcontroller "elasticsearch-logging-v1" created

# kubectl create -f es-service.yaml
service "elasticsearch-logging" created

get pods：

kube-system                  elasticsearch-logging-v1-3bzt6          1/1       Running    0          7s        172.16.57.8    10.46.181.146
kube-system                  elasticsearch-logging-v1-nvbe1          1/1       Running    0          7s        172.16.99.10   10.47.136.60
</code></pre>
<p>elastic search logging启动成功后，上述fluentd的fail日志就没有了！</p>
<p>不过elastic search真的运行ok了么？我们查看一下elasticsearch相关Pod日志：</p>
<pre><code># kubectl logs -f elasticsearch-logging-v1-3bzt6 -n kube-system
F0302 03:59:41.036697       8 elasticsearch_logging_discovery.go:60] kube-system namespace doesn't exist: the server has asked for the client to provide credentials (get namespaces kube-system)
goroutine 1 [running]:
k8s.io/kubernetes/vendor/github.com/golang/glog.stacks(0x19a8100, 0xc400000000, 0xc2, 0x186)
... ...
main.main()
    elasticsearch_logging_discovery.go:60 +0xb53

[2017-03-02 03:59:42,587][INFO ][node                     ] [elasticsearch-logging-v1-3bzt6] version[2.4.1], pid[16], build[c67dc32/2016-09-27T18:57:55Z]
[2017-03-02 03:59:42,588][INFO ][node                     ] [elasticsearch-logging-v1-3bzt6] initializing ...
[2017-03-02 03:59:44,396][INFO ][plugins                  ] [elasticsearch-logging-v1-3bzt6] modules [reindex, lang-expression, lang-groovy], plugins [], sites []
... ...
[2017-03-02 03:59:44,441][INFO ][env                      ] [elasticsearch-logging-v1-3bzt6] heap size [1007.3mb], compressed ordinary object pointers [true]
[2017-03-02 03:59:48,355][INFO ][node                     ] [elasticsearch-logging-v1-3bzt6] initialized
[2017-03-02 03:59:48,355][INFO ][node                     ] [elasticsearch-logging-v1-3bzt6] starting ...
[2017-03-02 03:59:48,507][INFO ][transport                ] [elasticsearch-logging-v1-3bzt6] publish_address {172.16.57.8:9300}, bound_addresses {[::]:9300}
[2017-03-02 03:59:48,547][INFO ][discovery                ] [elasticsearch-logging-v1-3bzt6] kubernetes-logging/7_f_M2TKRZWOw4NhBc4EqA
[2017-03-02 04:00:18,552][WARN ][discovery                ] [elasticsearch-logging-v1-3bzt6] waited for 30s and no initial state was set by the discovery
[2017-03-02 04:00:18,562][INFO ][http                     ] [elasticsearch-logging-v1-3bzt6] publish_address {172.16.57.8:9200}, bound_addresses {[::]:9200}
[2017-03-02 04:00:18,562][INFO ][node                     ] [elasticsearch-logging-v1-3bzt6] started
[2017-03-02 04:01:15,754][WARN ][discovery.zen.ping.unicast] [elasticsearch-logging-v1-3bzt6] failed to send ping to [{#zen_unicast_1#}{127.0.0.1}{127.0.0.1:9300}]
SendRequestTransportException[[][127.0.0.1:9300][internal:discovery/zen/unicast]]; nested: NodeNotConnectedException[[][127.0.0.1:9300] Node not connected];
... ...
Caused by: NodeNotConnectedException[[][127.0.0.1:9300] Node not connected]
    at org.elasticsearch.transport.netty.NettyTransport.nodeChannel(NettyTransport.java:1141)
    at org.elasticsearch.transport.netty.NettyTransport.sendRequest(NettyTransport.java:830)
    at org.elasticsearch.transport.TransportService.sendRequest(TransportService.java:329)
    ... 12 more
</code></pre>
<p>总结了一下，日志中有两个错误：<br />
- 无法访问到API Server，这个似乎和fluentd最初的问题一样；<br />
- elasticsearch两个节点间互ping失败。</p>
<p>要想找到这两个问题的原因，还得回到源头，去分析elastic search image的组成。</p>
<p>通过cluster/addons/fluentd-elasticsearch/es-image/run.sh文件内容：</p>
<pre><code>/elasticsearch_logging_discovery &gt;&gt; /elasticsearch/config/elasticsearch.yml

chown -R elasticsearch:elasticsearch /data

/bin/su -c /elasticsearch/bin/elasticsearch elasticsearch
</code></pre>
<p>我们了解到image中，其实包含了两个程序，一个为/elasticsearch_logging_discovery，该程序执行后生成一个配置文件： /elasticsearch/config/elasticsearch.yml。该配置文件后续被另外一个程序：/elasticsearch/bin/elasticsearch使用。</p>
<p>我们查看一下已经运行的docker中的elasticsearch.yml文件内容：</p>
<pre><code># docker exec 3cad31f6eb08 cat /elasticsearch/config/elasticsearch.yml
cluster.name: kubernetes-logging

node.name: ${NODE_NAME}
node.master: ${NODE_MASTER}
node.data: ${NODE_DATA}

transport.tcp.port: ${TRANSPORT_PORT}
http.port: ${HTTP_PORT}

path.data: /data

network.host: 0.0.0.0

discovery.zen.minimum_master_nodes: ${MINIMUM_MASTER_NODES}
discovery.zen.ping.multicast.enabled: false
</code></pre>
<p>这个结果中缺少了一项：</p>
<pre><code>discovery.zen.ping.unicast.hosts: ["172.30.0.11", "172.30.192.15"]
</code></pre>
<p>这也是导致第二个问题的原因。综上，elasticsearch logging的错误其实都是由于/elasticsearch_logging_discovery无法访问API Server导致 /elasticsearch/config/elasticsearch.yml没有被正确生成造成的，我们就来解决这个问题。</p>
<p>我查看了一下/elasticsearch_logging_discovery的<a href="https://github.com/kubernetes/kubernetes/blob/master/cluster/addons/fluentd-elasticsearch/es-image/elasticsearch_logging_discovery.go">源码</a>，elasticsearch_logging_discovery是一个典型通过<a href="https://github.com/kubernetes/client-go">client-go</a>通过service account访问API Server的程序，很显然这就是我在《<a href="http://tonybai.com/2017/03/03/access-api-server-from-a-pod-through-serviceaccount">在Kubernetes Pod中使用Service Account访问API Server</a>》一文中提到的那个问题：默认的service account不好用。</p>
<p>解决方法：在kube-system namespace下创建一个新的service account资源，并在es-controller.yaml中显式使用该新创建的service account。</p>
<p>创建一个新的serviceaccount在kube-system namespace下：</p>
<pre><code>//serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: k8s-efk

# kubectl create -f serviceaccount.yaml -n kube-system
serviceaccount "k8s-efk" created

# kubectl get serviceaccount -n kube-system
NAME      SECRETS   AGE
default   1         139d
k8s-efk   1         17s
</code></pre>
<p>在es-controller.yaml中，使用service account “k8s-efk”：</p>
<pre><code>//es-controller.yaml
... ...
spec:
  replicas: 2
  selector:
    k8s-app: elasticsearch-logging
    version: v1
  template:
    metadata:
      labels:
        k8s-app: elasticsearch-logging
        version: v1
        kubernetes.io/cluster-service: "true"
    spec:
      serviceAccount: k8s-efk
      containers:
... ...
</code></pre>
<p>重新创建elasticsearch logging service后，我们再来查看elasticsearch-logging pod的日志：</p>
<pre><code># kubectl logs -f elasticsearch-logging-v1-dklui -n kube-system
[2017-03-02 08:26:46,500][INFO ][node                     ] [elasticsearch-logging-v1-dklui] version[2.4.1], pid[14], build[c67dc32/2016-09-27T18:57:55Z]
[2017-03-02 08:26:46,504][INFO ][node                     ] [elasticsearch-logging-v1-dklui] initializing ...
[2017-03-02 08:26:47,984][INFO ][plugins                  ] [elasticsearch-logging-v1-dklui] modules [reindex, lang-expression, lang-groovy], plugins [], sites []
[2017-03-02 08:26:48,073][INFO ][env                      ] [elasticsearch-logging-v1-dklui] using [1] data paths, mounts [[/data (/dev/vda1)]], net usable_space [16.9gb], net total_space [39.2gb], spins? [possibly], types [ext4]
[2017-03-02 08:26:48,073][INFO ][env                      ] [elasticsearch-logging-v1-dklui] heap size [1007.3mb], compressed ordinary object pointers [true]
[2017-03-02 08:26:53,241][INFO ][node                     ] [elasticsearch-logging-v1-dklui] initialized
[2017-03-02 08:26:53,241][INFO ][node                     ] [elasticsearch-logging-v1-dklui] starting ...
[2017-03-02 08:26:53,593][INFO ][transport                ] [elasticsearch-logging-v1-dklui] publish_address {172.16.57.8:9300}, bound_addresses {[::]:9300}
[2017-03-02 08:26:53,651][INFO ][discovery                ] [elasticsearch-logging-v1-dklui] kubernetes-logging/Ky_OuYqMRkm_918aHRtuLg
[2017-03-02 08:26:56,736][INFO ][cluster.service          ] [elasticsearch-logging-v1-dklui] new_master {elasticsearch-logging-v1-dklui}{Ky_OuYqMRkm_918aHRtuLg}{172.16.57.8}{172.16.57.8:9300}{master=true}, added {{elasticsearch-logging-v1-vjxm3}{cbzgrfZATyWkHfQYHZhs7Q}{172.16.99.10}{172.16.99.10:9300}{master=true},}, reason: zen-disco-join(elected_as_master, [1] joins received)
[2017-03-02 08:26:56,955][INFO ][http                     ] [elasticsearch-logging-v1-dklui] publish_address {172.16.57.8:9200}, bound_addresses {[::]:9200}
[2017-03-02 08:26:56,956][INFO ][node                     ] [elasticsearch-logging-v1-dklui] started
[2017-03-02 08:26:57,157][INFO ][gateway                  ] [elasticsearch-logging-v1-dklui] recovered [0] indices into cluster_state
[2017-03-02 08:27:05,378][INFO ][cluster.metadata         ] [elasticsearch-logging-v1-dklui] [logstash-2017.03.02] creating index, cause [auto(bulk api)], templates [], shards [5]/[1], mappings []
[2017-03-02 08:27:06,360][INFO ][cluster.metadata         ] [elasticsearch-logging-v1-dklui] [logstash-2017.03.01] creating index, cause [auto(bulk api)], templates [], shards [5]/[1], mappings []
[2017-03-02 08:27:07,163][INFO ][cluster.routing.allocation] [elasticsearch-logging-v1-dklui] Cluster health status changed from [RED] to [YELLOW] (reason: [shards started [[logstash-2017.03.01][3], [logstash-2017.03.01][3]] ...]).
[2017-03-02 08:27:07,354][INFO ][cluster.metadata         ] [elasticsearch-logging-v1-dklui] [logstash-2017.03.02] create_mapping [fluentd]
[2017-03-02 08:27:07,988][INFO ][cluster.metadata         ] [elasticsearch-logging-v1-dklui] [logstash-2017.03.01] create_mapping [fluentd]
[2017-03-02 08:27:09,578][INFO ][cluster.routing.allocation] [elasticsearch-logging-v1-dklui] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[logstash-2017.03.02][4]] ...]).

</code></pre>
<p>elasticsearch logging启动运行ok！</p>
<h3>五、启动kibana</h3>
<p>有了elasticsearch logging的“前车之鉴”，这次我们也把上面新创建的serviceaccount：k8s-efk显式赋值给kibana-controller.yaml:</p>
<pre><code>//kibana-controller.yaml
... ...
spec:
      serviceAccount: k8s-efk
      containers:
      - name: kibana-logging
        image: bigwhite/kibana:v4.6.1-1
        resources:
          # keep request = limit to keep this container in guaranteed class
          limits:
            cpu: 100m
          #requests:
          #  cpu: 100m
        env:
          - name: "ELASTICSEARCH_URL"
            value: "http://elasticsearch-logging:9200"
          - name: "KIBANA_BASE_URL"
            value: "/api/v1/proxy/namespaces/kube-system/services/kibana-logging"
        ports:
        - containerPort: 5601
          name: ui
          protocol: TCP
... ...
</code></pre>
<p>启动kibana，并观察pod日志：</p>
<pre><code># kubectl create -f kibana-controller.yaml
# kubectl create -f kibana-service.yaml
# kubectl logs -f kibana-logging-3604961973-jby53 -n kube-system
ELASTICSEARCH_URL=http://elasticsearch-logging:9200
server.basePath: /api/v1/proxy/namespaces/kube-system/services/kibana-logging
{"type":"log","@timestamp":"2017-03-02T08:30:15Z","tags":["info","optimize"],"pid":6,"message":"Optimizing and caching bundles for kibana and statusPage. This may take a few minutes"}
</code></pre>
<p>kibana缓存着实需要一段时间，请耐心等待！可能是几分钟。之后你将会看到如下日志：</p>
<pre><code># kubectl logs -f kibana-logging-3604961973-jby53 -n kube-system
ELASTICSEARCH_URL=http://elasticsearch-logging:9200
server.basePath: /api/v1/proxy/namespaces/kube-system/services/kibana-logging
{"type":"log","@timestamp":"2017-03-02T08:30:15Z","tags":["info","optimize"],"pid":6,"message":"Optimizing and caching bundles for kibana and statusPage. This may take a few minutes"}
{"type":"log","@timestamp":"2017-03-02T08:40:04Z","tags":["info","optimize"],"pid":6,"message":"Optimization of bundles for kibana and statusPage complete in 588.60 seconds"}
{"type":"log","@timestamp":"2017-03-02T08:40:04Z","tags":["status","plugin:kibana@1.0.0","info"],"pid":6,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-03-02T08:40:05Z","tags":["status","plugin:elasticsearch@1.0.0","info"],"pid":6,"state":"yellow","message":"Status changed from uninitialized to yellow - Waiting for Elasticsearch","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-03-02T08:40:05Z","tags":["status","plugin:kbn_vislib_vis_types@1.0.0","info"],"pid":6,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-03-02T08:40:05Z","tags":["status","plugin:markdown_vis@1.0.0","info"],"pid":6,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-03-02T08:40:05Z","tags":["status","plugin:metric_vis@1.0.0","info"],"pid":6,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-03-02T08:40:06Z","tags":["status","plugin:spyModes@1.0.0","info"],"pid":6,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-03-02T08:40:06Z","tags":["status","plugin:statusPage@1.0.0","info"],"pid":6,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-03-02T08:40:06Z","tags":["status","plugin:table_vis@1.0.0","info"],"pid":6,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-03-02T08:40:06Z","tags":["listening","info"],"pid":6,"message":"Server running at http://0.0.0.0:5601"}
{"type":"log","@timestamp":"2017-03-02T08:40:11Z","tags":["status","plugin:elasticsearch@1.0.0","info"],"pid":6,"state":"yellow","message":"Status changed from yellow to yellow - No existing Kibana index found","prevState":"yellow","prevMsg":"Waiting for Elasticsearch"}
{"type":"log","@timestamp":"2017-03-02T08:40:14Z","tags":["status","plugin:elasticsearch@1.0.0","info"],"pid":6,"state":"green","message":"Status changed from yellow to green - Kibana index ready","prevState":"yellow","prevMsg":"No existing Kibana index found"}
</code></pre>
<p>接下来，通过浏览器访问下面地址就可以访问kibana的web页面了，注意：Kinaba的web页面加载也需要一段时间。</p>
<pre><code>https://{API Server external IP}:{API Server secure port}/api/v1/proxy/namespaces/kube-system/services/kibana-logging/app/kibana#/settings/indices/
</code></pre>
<p>下面是创建一个index（相当于mysql中的一个database）页面：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-kibana-index-create.png" alt="img{512x368}" /></p>
<p>取消“Index contains time-based events”，然后点击“Create”即可创建一个Index。</p>
<p>点击页面上的”Setting” -> “Status”，可以查看当前elasticsearch logging的整体状态，如果一切ok，你将会看到下图这样的页面：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-kibana-status-ok.png" alt="img{512x368}" /></p>
<p>创建Index后，可以在Discover下看到ElasticSearch logging中汇聚的日志：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-kibana-discover.png" alt="img{512x368}" /></p>
<h3>六、小结</h3>
<p>以上就是在Kubernetes 1.3.7集群上安装Fluentd和ElasticSearch stack，实现kubernetes cluster level logging的过程。在<a href="http://tonybai.com/2016/12/30/install-kubernetes-on-ubuntu-with-kubeadm">使用kubeadm安装的Kubernetes 1.5.1环境</a>下安装这些，则基本不会遇到上述这些问题。</p>
<p>另外ElasticSearch logging默认挂载的volume是emptyDir，实验用可以。但要部署在生产环境，必须换成Persistent Volume，比如：<a href="http://tonybai.com/2016/11/07/integrate-kubernetes-with-ceph-rbd">CephRBD</a>。</p>
<p style='text-align:left'>&copy; 2017, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2017/03/03/implement-kubernetes-cluster-level-logging-with-fluentd-and-elasticsearch-stack/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>以Kubeadm方式安装的Kubernetes集群的探索</title>
		<link>https://tonybai.com/2017/01/24/explore-kubernetes-cluster-installed-by-kubeadm/</link>
		<comments>https://tonybai.com/2017/01/24/explore-kubernetes-cluster-installed-by-kubeadm/#comments</comments>
		<pubDate>Tue, 24 Jan 2017 08:28:36 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[aliyun]]></category>
		<category><![CDATA[CA]]></category>
		<category><![CDATA[coreos]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[etcd]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[kube-apiserver]]></category>
		<category><![CDATA[kube-controller-manager]]></category>
		<category><![CDATA[kube-proxy]]></category>
		<category><![CDATA[kube-scheduler]]></category>
		<category><![CDATA[kube-up.sh]]></category>
		<category><![CDATA[kubeadm]]></category>
		<category><![CDATA[kubelet]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[manifest]]></category>
		<category><![CDATA[Master]]></category>
		<category><![CDATA[minion]]></category>
		<category><![CDATA[nodeport]]></category>
		<category><![CDATA[pause]]></category>
		<category><![CDATA[systemd]]></category>
		<category><![CDATA[token]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[公钥]]></category>
		<category><![CDATA[公钥证书]]></category>
		<category><![CDATA[容器]]></category>
		<category><![CDATA[私钥]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=2139</guid>
		<description><![CDATA[当前手上有两个Kubernetes cluster，一个是采用kube-up.sh安装的k8s 1.3.7版本，另外一个则是采用kubeadm安装的k8s 1.5.1版本。由于1.3.7版本安装在前，并且目前它也是承载了我们PaaS平台的环境，因此对于这个版本的Kubernetes安装环境、配置操作、日志查看、集群操作等相对较为熟悉。而Kubeadm安装的1.5.1版本K8s集群在组件部署、配置、日志等诸多方面与1.3.7版本有了较大差异。刚上手的时候，你会发现你原来所熟知的1.3.7的东西都不在原先的位置上了。估计很多和我一样，采用kubeadm将集群升级到1.5.1版本的朋友们都会遇到这类问题，于是这里打算对Kubeadm方式安装的Kubernetes集群进行一些小小的探索，把一些变动较大的点列出来，供大家参考。 一、环境 这里使用的依然是文章《使用Kubeadm安装Kubernetes》中安装完毕的Kubernetes 1.5.1集群环境，底层是阿里云ECS，操作系统是Ubuntu 16.04.1。网络用的是weave network。 试验集群只有两个Node：一个master node和一个minion node。但Master node由于被taint了，因此它与minion node一样参与集群调度和承担负载。 二、核心组件的Pod化 Kubeadm安装的k8s集群与kube-up.sh安装集群相比，最大的不同应该算是kubernetes核心组件的Pod化，即：kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、kube-discovery以及etcd等核心组件都运行在集群中的Pod里的，这颇有些CoreOS的风格。只有一个组件是例外的，那就是负责在node上与本地容器引擎交互的Kubelet。 K8s的核心组件Pod均放在kube-system namespace中，通过kubectl(on master node)可以查看到： # kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE etcd-iz25beglnhtz 1/1 Running 2 26d kube-apiserver-iz25beglnhtz 1/1 Running 3 26d kube-controller-manager-iz25beglnhtz 1/1 Running 2 26d kube-scheduler-iz25beglnhtz 1/1 Running 4 26d ... ... 另外细心的朋友可能会发现，这些核心组建的Pod名字均以所在Node的主机名为结尾，比如：kube-apiserver-iz25beglnhtz中的”iz25beglnhtz”就是其所在Node的主机名。 不过，即便这些核心组件是一个容器的形式运行在集群中，组件所使用网络依然是所在Node的主机网络，而不是Pod [...]]]></description>
			<content:encoded><![CDATA[<p>当前手上有两个<a href="http://tonybai.com/tag/kubernetes">Kubernetes</a> cluster，一个是<a href="http://tonybai.com/2016/10/18/learn-how-to-install-kubernetes-on-ubuntu/">采用kube-up.sh安装的k8s 1.3.7版本</a>，另外一个则是<a href="http://tonybai.com/2016/12/30/install-kubernetes-on-ubuntu-with-kubeadm/">采用kubeadm安装的k8s 1.5.1版本</a>。由于1.3.7版本安装在前，并且目前它也是承载了我们<a href="http://tonybai.com/2017/01/03/2016-summary/">PaaS平台</a>的环境，因此对于这个版本的Kubernetes安装环境、配置操作、日志查看、集群操作等相对较为熟悉。而Kubeadm安装的1.5.1版本K8s集群在组件部署、配置、日志等诸多方面与1.3.7版本有了较大差异。刚上手的时候，你会发现你原来所熟知的1.3.7的东西都不在原先的位置上了。估计很多和我一样，采用<a href="https://github.com/kubernetes/kubeadm">kubeadm</a>将集群升级到1.5.1版本的朋友们都会遇到这类问题，于是这里打算对<a href="https://kubernetes.io/docs/getting-started-guides/kubeadm/">Kubeadm方式</a>安装的Kubernetes集群进行一些小小的探索，把一些变动较大的点列出来，供大家参考。</p>
<h3>一、环境</h3>
<p>这里使用的依然是文章《<a href="http://tonybai.com/2016/12/30/install-kubernetes-on-ubuntu-with-kubeadm/">使用Kubeadm安装Kubernetes</a>》中安装完毕的Kubernetes 1.5.1集群环境，底层是阿里云ECS，操作系统是<a href="http://tonybai.com/tag/ubuntu">Ubuntu</a> 16.04.1。网络用的是<a href="https://www.weave.works/">weave network</a>。</p>
<p>试验集群只有两个Node：一个master node和一个minion node。但<a href="https://kubernetes.io/docs/getting-started-guides/kubeadm/">Master node由于被taint</a>了，因此它与minion node一样参与集群调度和承担负载。</p>
<h3>二、核心组件的Pod化</h3>
<p>Kubeadm安装的k8s集群与kube-up.sh安装集群相比，最大的不同应该算是kubernetes核心组件的Pod化，即：kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、kube-discovery以及<a href="https://github.com/coreos/etcd/">etcd</a>等核心组件都运行在集群中的Pod里的，这颇有些<a href="https://github.com/coreos/">CoreOS</a>的风格。只有一个组件是例外的，那就是负责在node上与本地容器引擎交互的Kubelet。</p>
<p>K8s的核心组件Pod均放在kube-system namespace中，通过kubectl(on master node)可以查看到：</p>
<pre><code># kubectl get pods -n kube-system
NAME                                    READY     STATUS    RESTARTS   AGE
etcd-iz25beglnhtz                       1/1       Running   2          26d
kube-apiserver-iz25beglnhtz             1/1       Running   3          26d
kube-controller-manager-iz25beglnhtz    1/1       Running   2          26d
kube-scheduler-iz25beglnhtz             1/1       Running   4          26d
... ...
</code></pre>
<p>另外细心的朋友可能会发现，这些核心组建的Pod名字均以所在Node的主机名为结尾，比如：kube-apiserver-iz25beglnhtz中的”iz25beglnhtz”就是其所在Node的主机名。</p>
<p>不过，即便这些核心组件是一个容器的形式运行在集群中，组件所使用网络依然是所在Node的主机网络，而不是<a href="http://tonybai.com/2017/01/17/understanding-flannel-network-for-kubernetes/">Pod Network</a>：</p>
<pre><code># docker ps|grep apiserver
98ea64bbf6c8        gcr.io/google_containers/kube-apiserver-amd64:v1.5.1            "kube-apiserver --ins"   10 days ago         Up 10 days                              k8s_kube-apiserver.6c2e367b_kube-apiserver-iz25beglnhtz_kube-system_033de1afc0844729cff5e100eb700a81_557d1fb2
4f87d22b8334        gcr.io/google_containers/pause-amd64:3.0                        "/pause"                 10 days ago         Up 10 days                              k8s_POD.d8dbe16c_kube-apiserver-iz25beglnhtz_kube-system_033de1afc0844729cff5e100eb700a81_5931e490

# docker inspect 98ea64bbf6c8
... ...
"HostConfig": {
"NetworkMode": "container:4f87d22b833425082be55851d72268023d41b50649e46c738430d9dfd3abea11",
}
... ...

# docker inspect 4f87d22b833425082be55851d72268023d41b50649e46c738430d9dfd3abea11
... ...
"HostConfig": {
"NetworkMode": "host",
}
... ...
</code></pre>
<p>从上面docker inspect的输出可以看出kube-apiserver pod里面的pause容器采用的网络模式是host网络，而以pause容器网络为基础的kube-apiserver 容器显然就继承了这一<a href="http://tonybai.com/2017/01/11/understanding-linux-network-namespace-for-docker-network/">network namespace</a>。因此从外面看，访问Kube-apiserver这样的组件和以前没什么两样：在Master node上可以通过localhost:8080访问；在Node外，可以通过master_node_ip:6443端口访问。</p>
<h3>三、核心组件启动配置调整</h3>
<p>在kube-apiserver等核心组件还是以本地程序运行在物理机上的时代，修改kube-apiserver的启动参数，比如修改一下&#8211;service-node-port-range的范围、添加一个&#8211;basic-auth-file等，我们都可以通过直接修改/etc/default/kube-apiserver(以Ubuntu 14.04为例)文件的内容并重启kube-apiserver service(service restart kube-apiserver)的方式实现。其他核心组件：诸如：kube-controller-manager、kube-proxy和kube-scheduler均是如此。</p>
<p>但在kubeadm时代，这些配置文件不再存在，取而代之的是和用户Pod描述文件类似的manifest文件(都放置在/etc/kubernetes/manifests下面)：</p>
<pre><code>/etc/kubernetes/manifests# ls
etcd.json  kube-apiserver.json  kube-controller-manager.json  kube-scheduler.json
</code></pre>
<p>我们以为kube-apiserver增加一个启动参数：”&#8211;service-node-port-range=80-32767&#8243; 为例：</p>
<p>打开并编辑/etc/kubernetes/manifests/kube-apiserver.json，在“command字段对应的值中添加”&#8211;service-node-port-range=80-32767&#8243;：</p>
<pre><code>"containers": [
      {
        "name": "kube-apiserver",
        "image": "gcr.io/google_containers/kube-apiserver-amd64:v1.5.1",
        "command": [
          "kube-apiserver",
          "--insecure-bind-address=127.0.0.1",
          "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota",
          "--service-cluster-ip-range=10.96.0.0/12",
          "--service-account-key-file=/etc/kubernetes/pki/apiserver-key.pem",
          "--client-ca-file=/etc/kubernetes/pki/ca.pem",
          "--tls-cert-file=/etc/kubernetes/pki/apiserver.pem",
          "--tls-private-key-file=/etc/kubernetes/pki/apiserver-key.pem",
          "--token-auth-file=/etc/kubernetes/pki/tokens.csv",
          "--secure-port=6443",
          "--allow-privileged",
          "--advertise-address=10.47.217.91",
          "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
          "--anonymous-auth=false",
          "--etcd-servers=http://127.0.0.1:2379",
          "--service-node-port-range=80-32767"
        ],
</code></pre>
<p>注意：不要忘记在&#8211;etcd-servers这一行后面添加一个逗号，否则kube-apiserver会退出。</p>
<p>修改后，你会发现kube-apiserver会被自动重启。这是kubelet的功劳。kubelet在启动时监听/etc/kubernetes/manifests目录下的文件变化并做适当处理：</p>
<pre><code># ps -ef|grep kubelet
root      1633     1  5  2016 ?        1-09:24:47 /usr/bin/kubelet --kubeconfig=/etc/kubernetes/kubelet.conf --require-kubeconfig=true --pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin --cluster-dns=10.96.0.10 --cluster-domain=cluster.local
</code></pre>
<p>kubelet自身是一个<a href="http://tonybai.com/2016/12/27/when-docker-meets-systemd/">systemd</a>的service，它的启动配置可以通过下面文件修改：</p>
<pre><code># cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--kubeconfig=/etc/kubernetes/kubelet.conf --require-kubeconfig=true"
Environment="KUBELET_SYSTEM_PODS_ARGS=--pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true"
Environment="KUBELET_NETWORK_ARGS=--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
Environment="KUBELET_DNS_ARGS=--cluster-dns=10.96.0.10 --cluster-domain=cluster.local"
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_SYSTEM_PODS_ARGS $KUBELET_NETWORK_ARGS $KUBELET_DNS_ARGS $KUBELET_EXTRA_ARGS
</code></pre>
<h3>四、kubectl的配置</h3>
<p>kube-up.sh安装的k8s集群会在每个Node上的~/.kube/下创建config文件，用于kubectl访问apiserver和操作集群使用。但在kubeadm模式下，~/.kube/下面的内容变成了：</p>
<pre><code>~/.kube# ls
cache/  schema/
</code></pre>
<p>于是有了问题1：config哪里去了？</p>
<p>之所以在master node上我们的kubectl依旧可以工作，那是因为默认kubectl会访问localhost:8080来与kube-apiserver交互。如果kube-apiserver没有关闭&#8211;insecure-port，那么kubectl便可以正常与kube-apiserver交互，因为&#8211;insecure-port是没有任何校验机制的。</p>
<p>于是又了问题2：如果是其他node上的kubectl与kube-apiserver通信或者master node上的kubectl通过secure port与kube-apiserver通信，应该如何配置？</p>
<p>接下来，我们一并来回答上面两个问题。kubeadm在创建集群时，在master node的/etc/kubernetes下面创建了两个文件：</p>
<pre><code>/etc/kubernetes# ls -l
total 32
-rw------- 1 root root 9188 Dec 28 17:32 admin.conf
-rw------- 1 root root 9188 Dec 28 17:32 kubelet.conf
... ...
</code></pre>
<p>这两个文件的内容是完全一样的，仅从文件名可以看出是谁在使用。比如kubelet.conf这个文件，我们就在kubelet程序的启动参数中看到过：&#8211;kubeconfig=/etc/kubernetes/kubelet.conf</p>
<pre><code># ps -ef|grep kubelet
root      1633     1  5  2016 ?        1-09:26:41 /usr/bin/kubelet --kubeconfig=/etc/kubernetes/kubelet.conf --require-kubeconfig=true --pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin --cluster-dns=10.96.0.10 --cluster-domain=cluster.local

</code></pre>
<p>打开这个文件，你会发现这就是一个kubeconfig文件，文件内容较长，我们通过kubectl config view来查看一下这个文件的结构：</p>
<pre><code># kubectl --kubeconfig /etc/kubernetes/kubelet.conf config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: REDACTED
    server: https://{master node local ip}:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: admin
  name: admin@kubernetes
- context:
    cluster: kubernetes
    user: kubelet
  name: kubelet@kubernetes
current-context: admin@kubernetes
kind: Config
preferences: {}
users:
- name: admin
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
- name: kubelet
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
</code></pre>
<p>这和我们在《<a href="http://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/">Kubernetes集群Dashboard插件安装</a>》一文中介绍的kubeconfig文件内容并不二致。不同之处就是“REDACTED”这个字样的值，我们对应到kubelet.conf中，发现每个REDACTED字样对应的都是一段数据，这段数据是由对应的数字证书内容或密钥内容转换(base64)而来的，在访问apiserver时会用到。</p>
<p>我们在minion node上测试一下：</p>
<pre><code>minion node：

# kubectl get pods
The connection to the server localhost:8080 was refused - did you specify the right host or port?

# kubectl --kubeconfig /etc/kubernetes/kubelet.conf get pods
NAME                         READY     STATUS    RESTARTS   AGE
my-nginx-1948696469-359d6    1/1       Running   2          26d
my-nginx-1948696469-3g0n7    1/1       Running   3          26d
my-nginx-1948696469-xkzsh    1/1       Running   2          26d
my-ubuntu-2560993602-5q7q5   1/1       Running   2          26d
my-ubuntu-2560993602-lrrh0   1/1       Running   2          26d

</code></pre>
<p>kubeadm创建k8s集群时，会在master node上创建一些用于组件间访问的证书、密钥和token文件，上面的kubeconfig中的“REDACTED”所代表的内容就是从这些文件转化而来的：</p>
<pre><code>/etc/kubernetes/pki# ls
apiserver-key.pem  apiserver.pem  apiserver-pub.pem  ca-key.pem  ca.pem  ca-pub.pem  sa-key.pem  sa-pub.pem  tokens.csv
</code></pre>
<ul>
<li>apiserver-key.pem：kube-apiserver的私钥文件</li>
<li>apiserver.pem：kube-apiserver的公钥证书</li>
<li>apiserver-pub.pem  kube-apiserver的公钥文件</li>
<li>ca-key.pem：CA的私钥文件</li>
<li>ca.pem：CA的公钥证书</li>
<li>ca-pub.pem ：CA的公钥文件</li>
<li>sa-key.pem  ：serviceaccount私钥文件</li>
<li>sa-pub.pem  ：serviceaccount的公钥文件</li>
<li>tokens.csv：kube-apiserver用于校验的token文件</li>
</ul>
<p>在k8s各核心组件的启动参数中会看到上面文件的身影，比如：</p>
<pre><code> kube-apiserver --insecure-bind-address=127.0.0.1 --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota --service-cluster-ip-range=10.96.0.0/12 --service-account-key-file=/etc/kubernetes/pki/apiserver-key.pem --client-ca-file=/etc/kubernetes/pki/ca.pem --tls-cert-file=/etc/kubernetes/pki/apiserver.pem --tls-private-key-file=/etc/kubernetes/pki/apiserver-key.pem --token-auth-file=/etc/kubernetes/pki/tokens.csv --secure-port=6443 --allow-privileged --advertise-address={master node local ip} --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --anonymous-auth=false --etcd-servers=http://127.0.0.1:2379 --service-node-port-range=80-32767

</code></pre>
<p>我们还可以在minion node上通过curl还手工测试一下通过安全通道访问master node上的kube-apiserver。在《<a href="http://tonybai.com/2016/11/25/the-security-settings-for-kubernetes-cluster/">Kubernetes集群的安全配置</a>》一文中，我们提到过k8s的authentication（包括：客户端证书认证、basic auth、static token等）只要通过其中一个即可。当前kube-apiserver开启了客户端证书认证（&#8211;client-ca-file）和static token验证(&#8211;token-auth-file)，我们只要通过其中一个，就可以通过authentication，于是我们使用static token方式。static token file的内容格式：</p>
<pre><code>token,user,uid,"group1,group2,group3"
</code></pre>
<p>对应到master node上的tokens.csv</p>
<pre><code># cat /etc/kubernetes/pki/tokens.csv
{token},{user},812ffe41-cce0-11e6-9bd3-00163e1001d7,system:kubelet-bootstrap
</code></pre>
<p>我们用这个token通过curl与apiserver交互：</p>
<pre><code># curl --cacert /etc/kubernetes/pki/ca.pem -H "Authorization: Bearer {token}"  https://{master node local ip}:6443
{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "/apis/apps",
    "/apis/apps/v1beta1",
    "/apis/authentication.k8s.io",
    "/apis/authentication.k8s.io/v1beta1",
    "/apis/authorization.k8s.io",
    "/apis/authorization.k8s.io/v1beta1",
    "/apis/autoscaling",
    "/apis/autoscaling/v1",
    "/apis/batch",
    "/apis/batch/v1",
    "/apis/batch/v2alpha1",
    "/apis/certificates.k8s.io",
    "/apis/certificates.k8s.io/v1alpha1",
    "/apis/extensions",
    "/apis/extensions/v1beta1",
    "/apis/policy",
    "/apis/policy/v1beta1",
    "/apis/rbac.authorization.k8s.io",
    "/apis/rbac.authorization.k8s.io/v1alpha1",
    "/apis/storage.k8s.io",
    "/apis/storage.k8s.io/v1beta1",
    "/healthz",
    "/healthz/poststarthook/bootstrap-controller",
    "/healthz/poststarthook/extensions/third-party-resources",
    "/healthz/poststarthook/rbac/bootstrap-roles",
    "/logs",
    "/metrics",
    "/swaggerapi/",
    "/ui/",
    "/version"
  ]
}

</code></pre>
<p>交互成功！</p>
<p style='text-align:left'>&copy; 2017, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2017/01/24/explore-kubernetes-cluster-installed-by-kubeadm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Kubernetes Dashboard集成Heapster</title>
		<link>https://tonybai.com/2017/01/20/integrate-heapster-for-kubernetes-dashboard/</link>
		<comments>https://tonybai.com/2017/01/20/integrate-heapster-for-kubernetes-dashboard/#comments</comments>
		<pubDate>Fri, 20 Jan 2017 09:58:04 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[CA]]></category>
		<category><![CDATA[coreos]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[DNS]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[ElasticSearch]]></category>
		<category><![CDATA[grafana]]></category>
		<category><![CDATA[heapster]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[image]]></category>
		<category><![CDATA[InfluxDB]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[Kafka]]></category>
		<category><![CDATA[kube-apiserver]]></category>
		<category><![CDATA[kubeconfig]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[pod]]></category>
		<category><![CDATA[yaml]]></category>
		<category><![CDATA[容器]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=2135</guid>
		<description><![CDATA[默认安装后的Kubernetes dashboard如下图所示，是无法图形化展现集群度量指标信息的： 图形化展示度量指标的实现需要集成k8s的另外一个Addons组件：Heapster。 Heapster原生支持K8s(v1.0.6及以后版本)和CoreOS，并且支持多种存储后端，比如：InfluxDB、ElasticSearch、Kafka等，这个风格和k8s的确很像：功能先不管完善与否，先让自己在各个平台能用起来再说^0^。这里我们使用的数据存储后端是InfluxDB。 一、安装步骤 我们的Heapster也是要放在pod里运行的。当前，Heapster的最新stable版本是v1.2.0，我们可以下载其源码包到K8s cluster上的某个Node上。解压后，我们得到一个名为”heapster-1.2.0&#8243;的目录，进入该目录，我们可以看到如下内容： root@node1:~/k8stest/dashboardinstall/heapster-1.2.0# ls code-of-conduct.md CONTRIBUTING.md docs Godeps hooks integration LICENSE metrics riemann version common deploy events grafana influxdb kafka Makefile README.md vendor 以InfluxDB为存储后端的Heapster部署yaml在deploy/kube-config/influxdb下面： root@node1:~/k8stest/dashboardinstall/heapster-1.2.0# ls -l deploy/kube-config/influxdb/ total 28 -rw-r--r-- 1 root root 414 Sep 14 12:47 grafana-service.yaml -rw-r--r-- 1 root root 942 Jan 20 15:15 heapster-controller.yaml -rw-r--r-- 1 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/">默认安装后的Kubernetes dashboard</a>如下图所示，是无法图形化展现集群度量指标信息的：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-dashboard-without-heapster.png" alt="img{512x368}" /></p>
<p>图形化展示度量指标的实现需要集成k8s的另外一个Addons组件：<a href="https://github.com/kubernetes/heapster/">Heapster</a>。</p>
<p>Heapster原生支持K8s(v1.0.6及以后版本)和<a href="https://coreos.com/">CoreOS</a>，并且支持多种存储后端，比如：<a href="http://influxdb.com/">InfluxDB</a>、<a href="https://www.elastic.co/products/elasticsearch">ElasticSearch</a>、<a href="http://kafka.apache.org/">Kafka</a>等，这个风格和k8s的确很像：功能先不管完善与否，先让自己在各个平台能用起来再说^0^。这里我们使用的数据存储后端是InfluxDB。</p>
<h3>一、安装步骤</h3>
<p>我们的Heapster也是要放在pod里运行的。当前，Heapster的最新stable版本是<a href="https://github.com/kubernetes/heapster/releases/tag/v1.2.0">v1.2.0</a>，我们可以下载其<a href="https://github.com/kubernetes/heapster/archive/v1.2.0.zip">源码包</a>到<a href="http://tonybai.com/tag/k8s">K8s</a> cluster上的某个Node上。解压后，我们得到一个名为”heapster-1.2.0&#8243;的目录，进入该目录，我们可以看到如下内容：</p>
<pre><code>root@node1:~/k8stest/dashboardinstall/heapster-1.2.0# ls
code-of-conduct.md  CONTRIBUTING.md  docs    Godeps   hooks     integration  LICENSE   metrics    riemann  version
common              deploy           events  grafana  influxdb  kafka        Makefile  README.md  vendor
</code></pre>
<p>以<a href="https://github.com/kubernetes/heapster/blob/master/docs/influxdb.md">InfluxDB为存储后端的Heapster</a>部署yaml在deploy/kube-config/influxdb下面：</p>
<pre><code>root@node1:~/k8stest/dashboardinstall/heapster-1.2.0# ls -l deploy/kube-config/influxdb/
total 28
-rw-r--r-- 1 root root  414 Sep 14 12:47 grafana-service.yaml
-rw-r--r-- 1 root root  942 Jan 20 15:15 heapster-controller.yaml
-rw-r--r-- 1 root root  249 Sep 14 12:47 heapster-service.yaml
-rw-r--r-- 1 root root 1465 Jan 19 21:39 influxdb-grafana-controller.yaml
-rw-r--r-- 1 root root  259 Sep 14 12:47 influxdb-service.yaml
</code></pre>
<p>这里有五个yaml（注意：与heapster源码库中最新的代码已经有所不同，最新代码将influxdb和grafana从influxdb-grafana-controller.yaml拆分开了）。其中的一些docker image在墙外，如果你有加速器，那么你可以直接执行create命令；否则最好找到一些替代品： 比如：用signalive/heapster_grafana:2.6.0-2替换gcr.io/google_containers/heapster_grafana:v2.6.0-2。</p>
<p>创建pod的操作很简单：</p>
<pre><code>~/k8stest/dashboardinstall/heapster-1.2.0# kubectl create -f deploy/kube-config/influxdb/
service "monitoring-grafana" created
replicationcontroller "heapster" created
service "heapster" created
replicationcontroller "influxdb-grafana" created
service "monitoring-influxdb" created

</code></pre>
<p>如果image pull顺利的话，那么这些pod和service的启动是会很正常的。</p>
<pre><code>//kube get pods -n kube-system
... ...
kube-system                  heapster-b1dwa                          1/1       Running   0          1h        172.16.57.9    10.46.181.146   k8s-app=heapster,version=v6
kube-system                  influxdb-grafana-8c0e0                  2/2       Running   0          1h        172.16.57.10   10.46.181.146   name=influxGrafana
... ...
</code></pre>
<p>我们用浏览器打开kubernetes的Dashboard，期待中的图形化和集群度量指标信息到哪里去了呢？Dashboard还是一如既往的如上面图示中那样“简朴”，显然我们遇到问题了！</p>
<h3>二、TroubleShooting</h3>
<p>问题在哪？我们需要逐个检视相关Pod的日志：</p>
<pre><code># kubectl logs -f pods/influxdb-grafana-xxxxxx influxdb -n kube-system
# kubectl logs -f pods/influxdb-grafana-xxxxxx grafana -n kube-system
# kubectl logs -f pods/heapster-xxxxx -n kube-system
</code></pre>
<p>在heapster-xxxxx这个pod中，我们发现了大量失败日志：</p>
<pre><code>E0119 13:14:37.838900       1 reflector.go:203] k8s.io/heapster/metrics/heapster.go:319: Failed to list *api.Pod: the server has asked for the client to provide credentials (get pods)
E0119 13:14:37.838974       1 reflector.go:203] k8s.io/heapster/metrics/processors/node_autoscaling_enricher.go:100: Failed to list *api.Node: the server has asked for the client to provide credentials (get nodes)
E0119 13:14:37.839516       1 reflector.go:203] k8s.io/heapster/metrics/processors/namespace_based_enricher.go:84: Failed to list *api.Namespace: the server has asked for the client to provide credentials (get namespaces)
</code></pre>
<p>heapster无法连接apiserver，获取不要想要的信息。从kube-apiserver的日志(/var/log/upstart/kube-apiserver.log)也印证了这一点：</p>
<pre><code>E0120 09:15:30.833928   12902 handlers.go:54] Unable to authenticate the request due to an error: crypto/rsa: verification error
E0120 09:15:30.834032   12902 handlers.go:54] Unable to authenticate the request due to an error: crypto/rsa: verification error
E0120 09:15:30.835324   12902 handlers.go:54] Unable to authenticate the request due to an error: crypto/rsa: verification error

</code></pre>
<p>从apiserver的日志来看，heapster是通过apiserver的secure port连接的，由于我们的<a href="http://tonybai.com/2016/11/25/the-security-settings-for-kubernetes-cluster/">API server设置有https client端证书校验机制</a>，因此两者连接失败。</p>
<h3>三、通过insecure-port连接kube-apiserver</h3>
<p>现在我们就来解决上述问题。</p>
<p>首先，我们会想到：能否让heapster通过kube APIServer的insecure-port连接呢？在《<a href="http://tonybai.com/2016/11/25/the-security-settings-for-kubernetes-cluster/">Kubernetes集群的安全配置</a>》一文中我们提到过，kube-apiserver针对insecure-port接入的请求没有任何限制机制，这样heapster就可以获取到它所想获取到的所有有用信息。</p>
<p>在heapster doc中的“<a href="https://github.com/kubernetes/heapster/blob/master/docs/source-configuration.md">Configuring Source</a>”中，我们找到了连接kube-apiserver insecure-port的方法。不过在修改yaml之前，我们还是要先来看看当前heapster的一些启动配置的含义：</p>
<pre><code>//deploy/kube-config/influxdb/heapster-controller.yaml
command:
        - /heapster
        - --source=kubernetes:https://kubernetes.default
        - --sink=influxdb:http://monitoring-influxdb:8086
</code></pre>
<p>我们看到heapster启动时有两个启动参数：<br />
&#8211;source指示数据源，heapster是支持多种数据源的，这里用的是“kubernetes”类型的数据源，地址是：kubernetes.default。这个域名的全名是：kubernetes.default.svc.cluster.local，就是service “kubernetes”在cluster中的域名，而”kubernetes”服务就是kube-apiserver，它的信息如下：</p>
<pre><code># kubectl get services
NAME           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes     192.168.3.1     &lt;none&gt;        443/TCP        99d
... ...

# kubectl describe svc/kubernetes
Name:            kubernetes
Namespace:        default
Labels:            component=apiserver
            provider=kubernetes
Selector:        &lt;none&gt;
Type:            ClusterIP
IP:            192.168.3.1
Port:            https    443/TCP
Endpoints:        xxx.xxx.xxx.xxx:6443
Session Affinity:    ClientIP
No events.
</code></pre>
<p>因此，该域名在<a href="http://tonybai.com/2016/10/23/install-dns-addon-for-k8s/">k8s DNS</a>中会被resolve为clusterip:192.168.3.1。外加https的默认端口是443，因此实际上heapster试图访问的apiserver地址是：https://192.168.3.1:443。</p>
<p>heapster启动的另外一个参数是&#8211;sink，这个传入的就是存储后端，我们使用了InfluxDB，这里传入的就是上面创建的InfluxDB service的域名和端口号，我们在cluster中也能查找到该Service的信息：</p>
<pre><code># kubectl get services -n kube-system
NAME                   CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
monitoring-influxdb    192.168.3.228   &lt;none&gt;        8083/TCP,8086/TCP   1h
... ...
</code></pre>
<p>前面提到过，我们的APIServer在secure port上是有client端证书校验的，那么以这样的启动参数启动的heapster连接不上kube-apiserver就“合情合理”了。</p>
<p>接下来，我们按照”Configuring Source”中的方法，将heapster与kube-apiserver之间的连接方式改为通过insecure port进行：</p>
<pre><code>// kube-config/influxdb/heapster-controller.yaml
... ...
command:
        - /heapster
        - --source=kubernetes:http://10.47.136.60:8080?inClusterConfig=false
        - --sink=influxdb:http://monitoring-influxdb:8086
</code></pre>
<p>修改后重新create。重新启动后的heapster pod的日志输出如下：</p>
<pre><code># kubectl logs -f pod/heapster-hco5i  -n kube-system
I0120 02:03:46.014589       1 heapster.go:71] /heapster --source=kubernetes:http://10.47.136.60:8080?inClusterConfig=false --sink=influxdb:http://monitoring-influxdb:8086
I0120 02:03:46.014975       1 heapster.go:72] Heapster version v1.3.0-beta.0
I0120 02:03:46.015080       1 configs.go:60] Using Kubernetes client with master "http://10.47.136.60:8080" and version v1
I0120 02:03:46.015175       1 configs.go:61] Using kubelet port 10255
E0120 02:03:46.025962       1 influxdb.go:217] issues while creating an InfluxDB sink: failed to ping InfluxDB server at "monitoring-influxdb:8086" - Get http://monitoring-influxdb:8086/ping: dial tcp 192.168.3.239:8086: getsockopt: connection refused, will retry on use
I0120 02:03:46.026090       1 influxdb.go:231] created influxdb sink with options: host:monitoring-influxdb:8086 user:root db:k8s
I0120 02:03:46.026214       1 heapster.go:193] Starting with InfluxDB Sink
I0120 02:03:46.026286       1 heapster.go:193] Starting with Metric Sink
I0120 02:03:46.051096       1 heapster.go:105] Starting heapster on port 8082
I0120 02:04:05.211382       1 influxdb.go:209] Created database "k8s" on influxDB server at "monitoring-influxdb:8086"
</code></pre>
<p>之前的错误消失了！</p>
<p>我们再次打开Dashboard查看pod信息（这里需要等上一小会儿，因为采集cluster信息也是需要时间的），我们看到集群度量指标信息以图形化的方式展现在我们面前了(可对比本文开头那幅图示)：</p>
<p><img src="http://tonybai.com/wp-content/uploads/k8s-dashboard-with-heapster.png" alt="img{512x368}" /></p>
<h3>四、通过secure port连接kube-apiserver</h3>
<p>kube-apiserver的&#8211;insecure-port更多用来调试，生产环境下可是说关就关的，因此通过kube-apiserver的secure port才是“长治久安”之道。但要如何做呢？在heapster的”Configure Source”中给了一种使用serviceaccount的方法，但感觉略有些复杂啊。这里列出一下我自己探索到的方法: 使用kubeconfig文件！在《<a href="http://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/">Kubernetes集群Dashboard插件安装</a>》一文中，我们已经配置好了kubeconfig文件（默认位置：~/.kube/config），对于kubeconfig配置项还不是很了解的童鞋可以详细参考那篇文章，这里就不赘述了。</p>
<p>接下来，我们来修改heapster-controller.yaml：</p>
<pre><code>// deploy/kube-config/influxdb/heapster-controller.yaml

... ...
spec:
      containers:
      - name: heapster
        image: kubernetes/heapster:canary
        volumeMounts:
        - mountPath: /srv/kubernetes
          name: auth
        - mountPath: /root/.kube
          name: config
        imagePullPolicy: Always
        command:
        - /heapster
        - --source=kubernetes:https://kubernetes.default?inClusterConfig=false&amp;insecure=true&amp;auth=/root/.kube/config
        - --sink=influxdb:http://monitoring-influxdb:8086
      volumes:
      - name: auth
        hostPath:
          path: /srv/kubernetes
      - name: config
        hostPath:
          path: /root/.kube
... ...

</code></pre>
<p>从上述文件内容中&#8211;source的值我们可以看到，我们又恢复到初始kubernetes service的地址：https://kubernetes.default，但后面又跟了几个参数：</p>
<pre><code>inClusterConfig=false : 不使用service accounts中的kube config信息；
insecure=true：这里偷了个懒儿：选择对kube-apiserver发过来的服务端证书做信任处理，即不校验；
auth=/root/.kube/config：这个是关键！在不使用serviceaccount时，我们使用auth文件中的信息来对应kube-apiserver的校验。
</code></pre>
<p>上述yaml中，我们还挂载了两个path，以便pod可以访问到相应的配置文件(~/.kube/config）和/srv/kubernetes下的证书。</p>
<p>保存并重新创建相关pod后，Dashboard下的集群度量指标信息依然能以图形化的方式展现出来，可见这种方法是ok的！</p>
<p style='text-align:left'>&copy; 2017, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2017/01/20/integrate-heapster-for-kubernetes-dashboard/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Kubernetes集群Dashboard插件安装</title>
		<link>https://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/</link>
		<comments>https://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/#comments</comments>
		<pubDate>Wed, 18 Jan 2017 17:21:54 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[apiserver]]></category>
		<category><![CDATA[CA]]></category>
		<category><![CDATA[cluster]]></category>
		<category><![CDATA[container]]></category>
		<category><![CDATA[dashboard]]></category>
		<category><![CDATA[DNS]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[k8s]]></category>
		<category><![CDATA[kube-up.sh]]></category>
		<category><![CDATA[kubeconfig]]></category>
		<category><![CDATA[kubectl]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Master]]></category>
		<category><![CDATA[minion]]></category>
		<category><![CDATA[nodeport]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[数字证书]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=2131</guid>
		<description><![CDATA[第一次利用kube-up.sh脚本方式安装Kubernetes 1.3.7集群时，我就已经顺利地将kubernetes dashboard addon安装ok了。至今在这个环境下运行十分稳定。但是毕竟是一个试验环境，有些配置是无法满足生产环境要求的，比如：安全问题。今天有时间对Dashboard的配置进行一些调整，顺带将之前Dashboard插件的安装和配置过程也记录下来，供大家参考。 一、Dashboard的默认安装步骤 1、基于默认配置项的安装 采用kube-up.sh在Ubuntu上安装dashboard的原理与安装DNS插件大同小异，主要涉及的脚本文件和配置项包括： // kubernetes/cluster/config-default.sh ... ... # Optional: Install Kubernetes UI ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}" ... ... // kubernetes/cluster/ubuntu/deployAddons.sh ... ... function deploy_dashboard { if ${KUBECTL} get rc -l k8s-app=kubernetes-dashboard --namespace=kube-system &#124; grep kubernetes-dashboard-v &#38;&#62; /dev/null; then echo "Kubernetes Dashboard replicationController already exists" else echo "Creating Kubernetes Dashboard replicationController" ${KUBECTL} create -f ${KUBE_ROOT}/cluster/addons/dashboard/dashboard-controller.yaml [...]]]></description>
			<content:encoded><![CDATA[<p>第一次<a href="http://tonybai.com/2016/10/18/learn-how-to-install-kubernetes-on-ubuntu">利用kube-up.sh脚本方式安装Kubernetes 1.3.7集群</a>时，我就已经顺利地将<a href="https://github.com/kubernetes/dashboard">kubernetes dashboard</a> addon安装ok了。至今在这个环境下运行十分稳定。但是毕竟是一个试验环境，有些配置是无法满足生产环境要求的，比如：安全问题。今天有时间对Dashboard的配置进行一些调整，顺带将之前Dashboard插件的安装和配置过程也记录下来，供大家参考。</p>
<h3>一、Dashboard的默认安装步骤</h3>
<h4>1、基于默认配置项的安装</h4>
<p>采用kube-up.sh在<a href="http://tonybai.com/tag/ubuntu">Ubuntu</a>上安装dashboard的原理与<a href="http://tonybai.com/2016/10/23/install-dns-addon-for-k8s">安装DNS插件</a>大同小异，主要涉及的脚本文件和配置项包括：</p>
<pre><code>//  kubernetes/cluster/config-default.sh
... ...
# Optional: Install Kubernetes UI
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
... ...

// kubernetes/cluster/ubuntu/deployAddons.sh
... ...
function deploy_dashboard {
    if ${KUBECTL} get rc -l k8s-app=kubernetes-dashboard --namespace=kube-system | grep kubernetes-dashboard-v &amp;&gt; /dev/null; then
        echo "Kubernetes Dashboard replicationController already exists"
    else
        echo "Creating Kubernetes Dashboard replicationController"
        ${KUBECTL} create -f ${KUBE_ROOT}/cluster/addons/dashboard/dashboard-controller.yaml
    fi

    if ${KUBECTL} get service/kubernetes-dashboard --namespace=kube-system &amp;&gt; /dev/null; then
        echo "Kubernetes Dashboard service already exists"
    else
        echo "Creating Kubernetes Dashboard service"
        ${KUBECTL} create -f ${KUBE_ROOT}/cluster/addons/dashboard/dashboard-service.yaml
    fi

  echo
}

init

... ...

if [ "${ENABLE_CLUSTER_UI}" == true ]; then
  deploy_dashboard
fi
</code></pre>
<p>kube-up.sh会尝试创建”kube-system” namespace，并执行下面命令：</p>
<pre><code>kubectl create -f kubernetes/cluster/addons/dashboard/dashboard-controller.yaml
kubectl create -f kubernetes/cluster/addons/dashboard/dashboard-service.yaml
</code></pre>
<p>这和我们在cluster中创建一个rc和service没有多大区别。</p>
<p>当然上面的安装方式是伴随着k8s cluster的安装进行的，如果要单独安装Dashboard，那么Dashboard主页上的安装方式显然更为简单：</p>
<pre><code>kubectl create -f https://rawgit.com/kubernetes/dashboard/master/src/deploy/kubernetes-dashboard.yaml
</code></pre>
<h4>2、调整Dashboard容器启动参数</h4>
<p>dashboard-controller.yaml和dashboard-service.yaml两个文件内容如下：</p>
<pre><code>//dashboard-controller.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: kubernetes-dashboard-v1.1.1
  namespace: kube-system
  labels:
    k8s-app: kubernetes-dashboard
    version: v1.1.1
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    k8s-app: kubernetes-dashboard
  template:
    metadata:
      labels:
        k8s-app: kubernetes-dashboard
        version: v1.1.1
        kubernetes.io/cluster-service: "true"
    spec:
      containers:
      - name: kubernetes-dashboard
        image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.1.1
        resources:
          # keep request = limit to keep this container in guaranteed class
          limits:
            cpu: 100m
            memory: 50Mi
          requests:
            cpu: 100m
            memory: 50Mi
        ports:
        - containerPort: 9090
        livenessProbe:
          httpGet:
            path: /
            port: 9090
          initialDelaySeconds: 30
          timeoutSeconds: 30

// dashboard-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: kubernetes-dashboard
  namespace: kube-system
  labels:
    k8s-app: kubernetes-dashboard
    kubernetes.io/cluster-service: "true"
spec:
  selector:
    k8s-app: kubernetes-dashboard
  ports:
  - port: 80
    targetPort: 9090
</code></pre>
<p>这两个文件的内容略微陈旧些，用的还是目前已不推荐使用的ReplicationController。</p>
<p>不过这样默认安装后，你可能还会遇到如下问题：</p>
<p>（1） Dashboard pod创建失败：这是由于kubernetes-dashboard-amd64:v1.1.1 image在墙外，pull image失败导致的。</p>
<p>可以通过使用加速器或使用替代image的方式来解决，比如：mritd/kubernetes-dashboard-amd64:v1.4.0。修改一下dashboard-controller.yaml中image那一行即可。</p>
<p>（2）Dashboard无法连接到master node上的api server</p>
<p>如果唯一的dashboard pod（由于replicas=1）被调度到minion  node上，那么很可能无法连接上master node上api server(dashboard会在cluster中自动检测api server的存在，但有时候会失败)，导致页面无法正常显示。因此，需要指定一下api server的url，比如：我们在dashboard-controller.yaml中为container启动增加一个启动参数&#8211;apiserver-host：</p>
<pre><code>// dashboard-controller.yaml
... ...
spec:
      containers:
      - name: kubernetes-dashboard
        image: mritd/kubernetes-dashboard-amd64:v1.4.0
        imagePullPolicy: Always
        ports:
        - containerPort: 9090
          protocol: TCP
        args:
           - --apiserver-host=http://{api server host}:{api server insecure-port}
... ...
</code></pre>
<p>（3）增加nodeport，提供外部访问路径</p>
<p>dashboard以cluster service的角色运行在cluster中，我们虽然可以<a href="http://tonybai.com/2017/01/17/understanding-flannel-network-for-kubernetes/">在Node上访问该service或直接访问pod</a>，但要想在外部网络访问到dashboard，还需要另外设置，比如：设置nodeport。</p>
<p>在dashboard-service.yaml中，修改配置如下：</p>
<pre><code>spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 9090
    nodePort: 12345
</code></pre>
<p>这样你就可以通过node 的public ip+nodeport访问到dashboard了。</p>
<p>不过这时，你的dashboard算是在“裸奔”，没有任何安全可言：<br />
- dashboard ui没有访问管理机制，任何access都可以全面接管dashboard；<br />
- 同时在背后，dashboard通过insecure-port访问apiserver，没有使用加密机制。</p>
<h3>二、dashboard通过kubeconfig文件信息访问apiserver</h3>
<p>我们先来建立dashboard和apiserver之间的安全通信机制。</p>
<p>当前master上的kube-apiserver的启动参数如下：</p>
<pre><code>// /etc/default/kube-apiserver

KUBE_APISERVER_OPTS=" --insecure-bind-address=0.0.0.0 --insecure-port=8080 --etcd-servers=http://127.0.0.1:4001 --logtostderr=true --service-cluster-ip-range=192.168.3.0/24 --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,SecurityContextDeny,ResourceQuota --service-node-port-range=80-32767 --advertise-address={master node local ip} --basic-auth-file=/srv/kubernetes/basic_auth_file --client-ca-file=/srv/kubernetes/ca.crt --tls-cert-file=/srv/kubernetes/server.cert --tls-private-key-file=/srv/kubernetes/server.key"
</code></pre>
<p>dashboard要与apiserver建立安全通信机制，务必不能使用insecure port。kubernetes apiserver默认情况下secure port也是开启的，端口为6443。同时，apiserver开启了basic auth(&#8211;basic-auth-file=/srv/kubernetes/basic_auth_file)。这样一来，dashboard光靠传入的&#8211;apiserver-host参数将无法正常访问apiserver的secure port并通过basic auth。我们需要找到另外一个option：</p>
<p>我们来看一下dashboard还支持哪些cmdline options：</p>
<pre><code># docker run mritd/kubernetes-dashboard-amd64:v1.4.0 /dashboard -help
Usage of /dashboard:
      --alsologtostderr value          log to standard error as well as files
      --apiserver-host string          The address of the Kubernetes Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8080. If not specified, the assumption is that the binary runs inside aKubernetes cluster and local discovery is attempted.
      --heapster-host string           The address of the Heapster Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8082. If not specified, the assumption is that the binary runs inside aKubernetes cluster and service proxy will be used.
      --kubeconfig string              Path to kubeconfig file with authorization and master location information.
      --log-flush-frequency duration   Maximum number of seconds between log flushes (default 5s)
      --log_backtrace_at value         when logging hits line file:N, emit a stack trace (default :0)
      --log_dir value                  If non-empty, write log files in this directory
      --logtostderr value              log to standard error instead of files (default true)
      --port int                       The port to listen to for incoming HTTP requests (default 9090)
      --stderrthreshold value          logs at or above this threshold go to stderr (default 2)
  -v, --v value                        log level for V logs
      --vmodule value                  comma-separated list of pattern=N settings for file-filtered logging
</code></pre>
<p>从输出的options来看，只有&#8211;kubeconfig这个能够满足需求。</p>
<h4>1、kubeconfig文件介绍</h4>
<p>采用kube-up.sh脚本进行kubernetes默认安装后，脚本会在每个Cluster node上创建一个~/.kube/config文件，该<a href="https://kubernetes.io/docs/user-guide/kubeconfig-file/">kubeconfig文件</a>可为k8s cluster中的组件（比如kubectl等）、addons(比如dashboard等)提供跨全cluster的安全验证机制。</p>
<p>下面是我的minion node上的kubeconfig文件</p>
<pre><code># cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /srv/kubernetes/ca.crt
    server: https://{master node local ip}:6443
  name: ubuntu
contexts:
- context:
    cluster: ubuntu
    namespace: default
    user: admin
  name: ubuntu
current-context: ubuntu
kind: Config
preferences: {}
users:
- name: admin
  user:
    password: {apiserver_password}
    username: {apiserver_username}
    client-certificate: /srv/kubernetes/kubecfg.crt
    client-key: /srv/kubernetes/kubecfg.key
</code></pre>
<p>kubeconfig中存储了clusters、users、contexts信息，以及其他一些杂项，并通过current-context指定当前context。通过该配置文件，类似kubectl这样的cluster操作工具可以很容易的在各个cluster之间切换context。一个context就是一个三元组：{cluster、user、namespace}，current-context指定当前选定的context，比如上面的kubeconfig文件，当我们执行kubectl时，kubectl会读取该配置文件，并以current-context指定的那个context中的信息去查找user和cluster。这里current-context是ubuntu。</p>
<p>ubuntu这个context三元组中的信息是：</p>
<pre><code>{
    cluster = ubuntu
    namespace = default
    user = admin
}
</code></pre>
<p>之后kubectl到clusters中找到name为ubuntu的cluster，发现其server为https://{master node local ip}:6443，以及其CA信息；到users中找到name为admin的user，并使用该user下的信息：</p>
<pre><code>    password: {apiserver_password}
    username: {apiserver_username}
    client-certificate: /srv/kubernetes/kubecfg.crt
    client-key: /srv/kubernetes/kubecfg.key
</code></pre>
<p>通过kubectl config命令可以配置kubeconfig文件，具体命令可以参考<a href="https://kubernetes.io/docs/user-guide/kubeconfig-file/">这里</a>。</p>
<p>另外上面的/srv/kubernetes/ca.crt、/srv/kubernetes/kubecfg.crt和/srv/kubernetes/kubecfg.key都是kube-up.sh在安装k8s 1.3.7时在各个node上创建的，可以直接用来作为访问apiserver的参数传递给kubectl或其他要访问apiserver的组件或addons。</p>
<h4>2、修改dashboard启动参数，使用kubeconfig文件</h4>
<p>现在我们要让dashboard使用kubeconfig文件，我们需要修改dashboard-controller.yaml文件中涉及containers的配置信息：</p>
<pre><code>spec:
      containers:
      - name: kubernetes-dashboard
        image: mritd/kubernetes-dashboard-amd64:v1.4.0
        volumeMounts:
          - mountPath: /srv/kubernetes
            name: auth
          - mountPath: /root/.kube
            name: config
        imagePullPolicy: Always
        ports:
        - containerPort: 9090
          protocol: TCP
        args:
           - --kubeconfig=/root/.kube/config
        livenessProbe:
          httpGet:
            path: /
            port: 9090
          initialDelaySeconds: 30
          timeoutSeconds: 30
      volumes:
      - name: auth
        hostPath:
          path: /srv/kubernetes
      - name: config
        hostPath:
          path: /root/.kube
</code></pre>
<p>由于要用到各种证书以及kubeconfig，我们在pod里挂载了host主机的path： /root/.kube和/srv/kubernetes。</p>
<p>重新部署dashboard后，dashboard与kube-apiserver之间就有了安全保障了（https+basic_auth）。</p>
<h3>三、实现dashboard UI login</h3>
<p>虽然上面实现了dashboard与apiserver之间的安全通道和basic auth，但通过nodeport方式访问dashboard，我们依旧可以掌控dashboard，而dashboard依旧没有任何访问控制机制。而实际情况是dashboard目前还<a href="http://blog.kubernetes.io/2016/07/dashboard-web-interface-for-kubernetes.html">不支持identity and access management</a>，不过在不久的将来，<a href="https://github.com/kubernetes/dashboard/issues/964">dashboard将添加这方面的支持</a>。</p>
<p>那么在当前版本下，如何实现一个简易的login流程呢？除了前面提到的nodeport方式访问dashboard UI外，官方在<a href="https://github.com/kubernetes/dashboard/blob/master/docs/user-guide/troubleshooting.md">trouble shooting</a>里还提供了另外两种访问dashboard的方法，我们一起来看看是否能满足我们的最低级需求^0^。</p>
<h4>1、kubectl proxy方式</h4>
<p>kubectl proxy的方式默认只允许local network访问，但是kubectl proxy提供了若干flag options可以设置，我们来试试：</p>
<p>我们在minion node上执行：</p>
<pre><code># kubectl proxy --address='0.0.0.0' --port=30099
Starting to serve on [::]:30099
</code></pre>
<p>我们在minion node上的30099端口提供外网服务。打开浏览器，访问: http://{minion node public ip}:30099/ui，得到如下结果：</p>
<pre><code>&lt;h3&gt;Unauthorized&lt;/h3&gt;
</code></pre>
<p>到底哪没授权呢？我们查看kubectl proxy的flag options发现下面一个疑点：</p>
<pre><code>--accept-hosts='^localhost$,^127\.0\.0\.1$,^\[::1\]$': Regular expression for hosts that the proxy should accept.
</code></pre>
<p>显然&#8211;accept-hosts默认接受的host地址形式让我们的访问受限。重新调整配置再次执行：</p>
<pre><code># kubectl proxy --address='0.0.0.0' --port=30099 --accept-hosts='^*$'
Starting to serve on [::]:30099
</code></pre>
<p>再次打开浏览器，访问：http://{minion node public ip}:30099/ui</p>
<p>浏览器会跳转至下面的地址：</p>
<pre><code>http://{minion node public ip}:30099/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard/#/workload?namespace=default
</code></pre>
<p>dashboard ui访问成功！不过，这种方式依旧无需你输入user/password，这不符合我们的要求。</p>
<h4>2、直接访问apiserver方式</h4>
<p>trouble shooting文档提供的最后一种访问方式是直接访问apiserver方式：</p>
<pre><code>打开浏览器访问：

https://{master node public ip}:6443
</code></pre>
<p>这时浏览器会提示你：证书问题。忽略之（由于apiserver采用的是自签署的私有证书，浏览器端无法验证apiserver的server.crt），继续访问，浏览器弹出登录对话框，让你输入用户名和密码，这里我们输入apiserver &#8212;basic-auth-file中的用户名和密码，就可以成功登录apiserver，并在浏览器页面看到如下内容：</p>
<pre><code>{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "/apis/apps",
    "/apis/apps/v1alpha1",
    "/apis/autoscaling",
    "/apis/autoscaling/v1",
    "/apis/batch",
    "/apis/batch/v1",
    "/apis/batch/v2alpha1",
    "/apis/extensions",
    "/apis/extensions/v1beta1",
    "/apis/policy",
    "/apis/policy/v1alpha1",
    "/apis/rbac.authorization.k8s.io",
    "/apis/rbac.authorization.k8s.io/v1alpha1",
    "/healthz",
    "/healthz/ping",
    "/logs/",
    "/metrics",
    "/swaggerapi/",
    "/ui/",
    "/version"
  ]
}

</code></pre>
<p>接下来，我们访问下面地址：</p>
<pre><code>https://{master node public ip}:6443/ui
</code></pre>
<p>你会看到页面跳转到：</p>
<pre><code>https://101.201.78.51:6443/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard/

</code></pre>
<p>我们成功进入dashboard UI中! 显然这种访问方式满足了我们对dashboard UI采用登录访问的最低需求！</p>
<h3>三、小结</h3>
<p>到目前为止，dashboard已经可以使用。但它还缺少metric和类仪表盘图形展示功能，这两个功能需要额外安装<a href="https://github.com/kubernetes/heapster/">Heapster</a>才能实现，不过一般功能足以满足你对k8s cluster的管理需求。</p>
<p style='text-align:left'>&copy; 2017, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2017/01/19/install-dashboard-addon-for-k8s/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
