first commits

This commit is contained in:
David Young
2026-05-14 14:06:21 -06:00
parent d67dc1ad11
commit 015b3a8c5d
299 changed files with 87414 additions and 0 deletions

38
content/about/index-zh.md Normal file
View File

@@ -0,0 +1,38 @@
## 关于我
Hi我是赵化冰服务网格技术布道者及实践者[腾讯云](https://cloud.tencent.com/product/tcm)工程师,曾担任[中兴通讯](https://www.zte.com.cn/)技术专家Linux 基金会开源项目 [ONAP](https://www.onap.org/) 项目 leader甲骨文中间件高级顾问等。我创建了服务网格开源项目 [Aeraki Mesh](https://aeraki.net)CNCF Sandbox 项目),可以在 Istio 中管理 Dubbo、Thrift、Redis 以及任意私有协议的流量。
工作联系: zhaohuabing@gmail.com
## 出版物
| 标题 |类型 |出版社 |链接 |
| ----------- |----------- |----------- |----------- |
|[深入理解 Istio — 云原生服务网格进阶实战](https://www.zhaohuabing.com/post/2021-08-26-istio-handbook/)|实体书籍|电子工业出版社|[购买链接](https://item.jd.com/13200745.html)|
|[Distributed Tracing with Jaeger, Kubernetes, and Istio](https://www.zhaohuabing.com/post/2021-09-08-distributed-tracing-with-jaeger-kubernetes-and-istio/)|在线教程|[曼宁出版社(美)](https://www.manning.com/)|[30% 折扣](https://www.manning.com/liveprojectseries/distributed-tracing-ser)|
|[云原生数据中心网络](https://zhaohuabing.com/post/2021-08-27-cloud-native-data-center)|翻译书籍|中国电力出版社|[购买链接](https://item.jd.com/12929975.html)|
|[Istio 运维实战](https://istio-operation-bible.aeraki.net)|电子书籍||[在线阅读](https://istio-operation-bible.aeraki.net)|
## 演讲分享 (部分)
|年份 |城市 |会议 | 分享主题 |讲稿 |视频 |
| ----------- |----------- |----------- |----------- |----------- |----------- |
|2022|线上|[IstioCon](https://events.istio.io/istiocon-2022)|[Istio + Aeraki 在腾讯音乐的服务网格落地](https://events.istio.io/istiocon-2022/sessions/tencent-music-aeraki/)|[下载](/slides/tencent-music-service-mesh-practice-with-istio-and-aeraki.pdf)|[观看](https://www.youtube.com/watch?v=6t_yPsq4Pi4)|
|2022|线上|[A2M](https://a2m.msup.com.cn/course?aid=2699&cid=15382)|[全栈服务网格 - Aeraki Mesh 助你在 Istio 服务网格中管理任何七层流量](https://a2m.msup.com.cn/course?aid=2699&cid=15382)|[下载](/slides/full-stack-service-mesh-a2m-20220422.pdf)||
|2022|线上|[云原生正发声](https://cloud.tencent.com/developer/salon/live-1403)| [Areaki Mesh 在 2022 冬奥会视频直播应用中的服务网格实践](https://mp.weixin.qq.com/s/zp9q99mGyH2VD9Dij2owWg) | [下载](http://localhost:1313/img/2022-03-30-aeraki-mesh-winter-olympics-practice/slides.pdf)|[观看](https://youtu.be/uXxatQTKzW8)|
|2021|线上|[IstioCon](https://events.istio.io/istiocon-2021/)| [How to manage any layer-7 traffic in an Istio service mesh?](https://events.istio.io/istiocon-2021/sessions/how-to-manage-any-layer-7-traffic-in-an-istio-service-mesh/) | [下载](/slides/how-to-manage-any-layer-7-traffic-in-istio.pdf)|[观看](https://www.youtube.com/watch?v=sBS4utF68d8)|
|2020|线上|[CNBPS](https://www.cnbpa.org/)|[Istio 流量管理原理与协议扩展](https://cloud.tencent.com/developer/article/1723804)|[下载](/slides/cnbps2020-istio-aeraki.pdf)|[观看](https://www.youtube.com/watch?v=lB5d4qbZqzU)|
|2019|成都|[Service Mesher Meetup](https://cloudnative.to/blog/service-mesh-meetup-chengdu-20191028/)|[Service Mesh是下一代SDN吗](https://cloudnative.to/blog/service-mesh-meetup-chengdu-20191028/)|[下载](/slides/what-can-service-mesh-learn-from-sdn-servicemesher-meetup-20191026.pdf)|[观看](https://youtu.be/nGkxp-2OsKg)|
|2019|西安|ONAP Workshop|基于 5G 网络管理系统的服务网格实践|[下载](/slides/service-mesh-practice-with-5g-management-system-lfn.pdf)|
|2018|南京|[GNTC](https://www.bagevent.com/event/1624048?aId=)|[ONAP 服务网格实践](https://www.sdnlab.com/22596.html)|
|2017|圣克拉拉|[NAP Developer Forum](https://wiki.onap.org/display/DW/ONAP+Beijing+Release+Developer+Forum%2C+Dec.+11-13%2C+2017%2C+Santa+Clara%2C+CA+US)|[MSB to Support Carrier Grade ONAP Microservice Architecture with Service Mesh](https://onapbeijing2017.sched.com/event/D5q2)|[下载](https://wiki.onap.org/display/DW/MSB+Service+Mesh+Planning?preview=%2F20873876%2F20873874%2FMSB+to+Support+Carrier+Grade+ONAP+Microservice+Architecture+with+Service+Mesh.pdf)|
|2017|圣克拉拉|[ONS](https://wiki.onap.org/display/DW/ONAP@ONS2017)|Microservice Powered Orchestration|[下载](https://wiki.onap.org/display/DW/ONAP@ONS2017?preview=%2F3245268%2F3245309%2FMicroservice+Powered+Orchestration+Architecture.pdf)|
|2017|新泽西|[ONAP Developer Event](https://wiki.onap.org/display/DW/ONAP+Project+Developer+Event%3A+May+2+-+5%2C+2017%2C+Middletown%2C+NJ%2C+USA)|MSB Technical Deep Dive and ONAP Use Cases|[下载](https://www.slideshare.net/HuabingZhao/msb-depp-dive/)|
|2017|巴黎|[ONAP Developer Event](https://wiki.onap.org/display/DW/ONAP+Developer+Event+September+25-28%2C+2017%2C+Paris-Saclay%2C+France)|[Microservice Bus Tutorial](https://wiki.onap.org/display/DW/September+26-28+Topics#September2628Topics-M2)|[下载](https://www.slideshare.net/HuabingZhao/microservice-bus-tutorial)|
## 开源项目
|项目 |角色 | 网站 | GitHub |
| ----------- |----------- |----------- |----------- |
| Aeraki Mesh | 创建者 | https://aeraki.net | http://github.com/aeraki-mesh |
| Istio | Contributor| https://istio.io | https://github.com/istio/istio|
| Envoy | Contributor| https://www.envoyproxy.io |https://github.com/envoyproxy/envoy|
| ONAP | 项目 Leader | https://www.onap.org||
| hugo-theme-cleanwhite | 创建者 | https://themes.gohugo.io/themes/hugo-theme-cleanwhite | https://github.com/zhaohuabing/hugo-theme-cleanwhite |

55
content/about/index.md Normal file
View File

@@ -0,0 +1,55 @@
---
layout: page
multilingual: true
---
## About Me
**_Huabing Zhao_** is a software architect, an Istio Member and an ONAP PTL. He has a solid experience in the information and telecommunication technology industry for more than 17 years.
Throughout his career, he has built a number of large-scale, cross-country software systems, most of them are still running in production.
He loves open source and has been contributing to various open source projects, he is a member of Istio, previous PTL of ONAP, the author of the Hugo clean-white theme and the open source project Aeraki Mesh.
He also has strong interests in various technical topics such as Cloud Native, Artificial Intelligence, Cryptocurrencies, Smart Home, etc. He love sharing his ideas about these things in his blog.
Huabing holds a BSc in Computer Science and Technology from Chongqing University in China.
Currently, Huabing works as a senior engineer at [Tencent Cloud](https://cloud.tencent.com/) and also wears the hat of PTL in ONAP open source project. For now, his main focus is to build a managed service mesh solution on cloud.
While he is free, he likes writing technical blog posts, watching movies, swimming, hiking, travelling and learning languages.
Feel free to connect Huabing at Github and Linkedin, leave your thoughts in his blog or share your ideas by [writing him an email](mailto:zhaohuabing@zhaohuabing.com).
## Publications
| Title |Type |Publisher |Link |
| ----------- |----------- |----------- |----------- |
|[Istio Service Mesh Advanced Practical](https://www.zhaohuabing.com/post/2021-08-26-istio-handbook/)|Book|电子工业出版社|[Buy now](https://item.jd.com/13200745.html)|
|[Distributed Tracing with Jaeger, Kubernetes, and Istio](https://www.zhaohuabing.com/post/2021-09-08-distributed-tracing-with-jaeger-kubernetes-and-istio/)|Live Project|Manning|[30% off](https://www.manning.com/liveprojectseries/distributed-tracing-ser)|
|[Cloud Native Data Center Networking](https://zhaohuabing.com/post/2021-08-27-cloud-native-data-center)|Translation|中国电力出版社|[Buy now](https://item.jd.com/12929975.html)|
|[Istio Operation Bible](http://localhost:1313/post/2021-10-08-istio-operation-bible/)|ebook||[Read online](https://istio-operation-bible.aeraki.net/)|
## Presentations (Selected)
|Year |City |Conference | Title |Slides |Video |
| ----------- |----------- |----------- |----------- |----------- |----------- |
|2022|Virtual|[IstioCon](https://events.istio.io/istiocon-2022)|[Tencent Musics service mesh practice with Istio and Aeraki](https://events.istio.io/istiocon-2022/sessions/tencent-music-aeraki/)|[slides](/slides/tencent-music-service-mesh-practice-with-istio-and-aeraki.pdf)|[Video](https://www.youtube.com/watch?v=6t_yPsq4Pi4)|
|2022|Virtual|[A2M](https://a2m.msup.com.cn/course?aid=2699&cid=15382)|[全栈服务网格 - Aeraki Mesh 助你在 Istio 服务网格中管理任何七层流量](https://a2m.msup.com.cn/course?aid=2699&cid=15382)|[slides](/slides/full-stack-service-mesh-a2m-20220422.pdf)||
|2022|Virtual|[云原生正发声](https://cloud.tencent.com/developer/salon/live-1403)| [Areaki Mesh 在 2022 冬奥会视频直播应用中的服务网格实践](https://mp.weixin.qq.com/s/zp9q99mGyH2VD9Dij2owWg) | [Slides](http://localhost:1313/img/2022-03-30-aeraki-mesh-winter-olympics-practice/slides.pdf)|[Video](https://youtu.be/uXxatQTKzW8)|
|2021|Virtual|[IstioCon](https://events.istio.io/istiocon-2021/)| [How to manage any layer-7 traffic in an Istio service mesh?](https://events.istio.io/istiocon-2021/sessions/how-to-manage-any-layer-7-traffic-in-an-istio-service-mesh/) | [Slides](/slides/how-to-manage-any-layer-7-traffic-in-istio.pdf)|[Video](https://www.youtube.com/watch?v=sBS4utF68d8)|
|2020|Virtual|[CNBPS](https://www.cnbpa.org/)|[Istio 流量管理原理与协议扩展](https://cloud.tencent.com/developer/article/1723804)|[Slides](/slides/cnbps2020-istio-aeraki.pdf)|[Video](https://www.youtube.com/watch?v=lB5d4qbZqzU)|
|2019|Chengdu|[Service Mesher Meetup](https://cloudnative.to/blog/service-mesh-meetup-chengdu-20191028/)|[What Can Service Mesh Learn From SDN?](https://cloudnative.to/blog/service-mesh-meetup-chengdu-20191028/)|[Slides](/slides/what-can-service-mesh-learn-from-sdn-servicemesher-meetup-20191026.pdf)|[Video](https://youtu.be/nGkxp-2OsKg)|
|2019|Xi'an|ONAP Workshop|Service Mesh Practice with 5G Management System|[Slides](/slides/service-mesh-practice-with-5g-management-system-lfn.pdf)|
|2018|Nanjing|[GNTC](https://www.bagevent.com/event/1624048?aId=)|[Service Mesh in Action with ONAP](https://www.sdnlab.com/22596.html)|
|2017|Santa Clara|[NAP Developer Forum](https://wiki.onap.org/display/DW/ONAP+Beijing+Release+Developer+Forum%2C+Dec.+11-13%2C+2017%2C+Santa+Clara%2C+CA+US)|[MSB to Support Carrier Grade ONAP Microservice Architecture with Service Mesh](https://onapbeijing2017.sched.com/event/D5q2)|[Slides](https://wiki.onap.org/display/DW/MSB+Service+Mesh+Planning?preview=%2F20873876%2F20873874%2FMSB+to+Support+Carrier+Grade+ONAP+Microservice+Architecture+with+Service+Mesh.pdf)|
|2017|Santa Clara|[ONS](https://wiki.onap.org/display/DW/ONAP@ONS2017)|Microservice Powered Orchestration|[Slides](https://wiki.onap.org/display/DW/ONAP@ONS2017?preview=%2F3245268%2F3245309%2FMicroservice+Powered+Orchestration+Architecture.pdf)|
|2017|New Jersey|[ONAP Developer Event](https://wiki.onap.org/display/DW/ONAP+Project+Developer+Event%3A+May+2+-+5%2C+2017%2C+Middletown%2C+NJ%2C+USA)|MSB Technical Deep Dive and ONAP Use Cases|[Slides](https://www.slideshare.net/HuabingZhao/msb-depp-dive/)|
|2017|Paris|[ONAP Developer Event](https://wiki.onap.org/display/DW/ONAP+Developer+Event+September+25-28%2C+2017%2C+Paris-Saclay%2C+France)|[Microservice Bus Tutorial](https://wiki.onap.org/display/DW/September+26-28+Topics#September2628Topics-M2)|[Slides](https://www.slideshare.net/HuabingZhao/microservice-bus-tutorial)|
## Open Source Projects
|Project |Role | Website | GitHub |
| ----------- |----------- |----------- |----------- |
| Aeraki Mesh | Creator | https://aeraki.net | http://github.com/aeraki-mesh |
| Istio | Contributor| https://istio.io | https://github.com/istio/istio|
| Envoy | Contributor| https://www.envoyproxy.io |https://github.com/envoyproxy/envoy|
| ONAP | PTL | https://www.onap.org||
| hugo-theme-cleanwhite | Creator | https://themes.gohugo.io/themes/hugo-theme-cleanwhite | https://github.com/zhaohuabing/hugo-theme-cleanwhite |

6
content/archive/index.md Normal file
View File

@@ -0,0 +1,6 @@
---
title: "Posts Archive"
layout: archive
type: archive
description: Archive of historical posts.
---

8
content/notes/index.md Normal file
View File

@@ -0,0 +1,8 @@
---
layout: page
---
## [Go 语言学习笔记](https://zhaohuabing.com/learning-golang)
## [Envoy 学习笔记](https://zhaohuabing.com/learning-envoy)

View File

@@ -0,0 +1,14 @@
---
layout: post
title: "Welcome to Zhaohuabing Blog"
subtitle: "Hello World, Hello Blog"
date: 2017-11-04
author: "赵化冰"
URL: "/2017/11/03/hello-world/"
image: "https://img.zhaohuabing.com/post-bg-2015.jpg"
---
> “Yeah It's on. ”
## Hello World!

View File

@@ -0,0 +1,430 @@
---
layout: post
title: "Istio及Bookinfo示例程序安装试用笔记"
subtitle: "手把手教你从零搭建Istio及Bookinfo示例程序"
description: "Istio是来自GoogleIBM和Lyft的一个Service Mesh服务网格开源项目是Google继Kubernetes之后的又一大作,本文将演示如何从裸机开始从零搭建Istio及Bookinfo示例程序。"
excerpt: "Istio是来自GoogleIBM和Lyft的一个Service Mesh服务网格开源项目是Google继Kubernetes之后的又一大作,本文将演示如何从裸机开始从零搭建Istio及Bookinfo示例程序。"
date: 2017-11-04T12:00:00
author: "赵化冰"
image: "/img/istio-install_and_example/post-bg.jpg"
tags:
- Istio
URL: "/2017/11/04/istio-install_and_example/"
categories: [ Tech ]
---
## 服务网格简介
**服务网格**Service Mesh是为解决微服务的通信和治理而出现的一种**架构模式**。
服务网格将服务间通讯以及与此相关的管理控制功能从业务程序中下移到一个基础设施层,从而彻底隔离了业务逻辑和服务通讯两个关注点。采用服务网格后,应用开发者只需要关注并实现应用业务逻辑。服务之间的通信,包括服务发现,通讯的可靠性,通讯的安全性,服务路由等由服务网格层进行处理,并对应用程序透明。
<!--more-->
让我们来回顾一下微服务架构的发展过程。在出现服务网格之前,我们在微服务应用程序进程内处理服务通讯逻辑,包括服务发现,熔断,重试,超时等逻辑,如下图所示:
![](/img/istio-install_and_example/5-a.png)
通过对这部分负责服务通讯的逻辑进行抽象和归纳,可以形成一个代码库供应用程序调用。但应用程序还是需要处理和各种语言代码库的调用细节,并且各种代码库互不兼容,导致对应用程序使用的语言和代码框架有较大限制。
如果我们更进一步,将这部分逻辑从应用程序进程中抽取出来,作为一个单独的进程进行部署,并将其作为服务间的通信代理,如下图所示:
![](/img/istio-install_and_example/6-a.png)
因为通讯代理进程和应用进程一起部署因此形象地把这种部署方式称为“sidecar”三轮摩托的挎斗
![](/img/istio-install_and_example/sidecar.jpg)
应用间的所有流量都需要经过代理由于代理以sidecar方式和应用部署在同一台主机上应用和代理之间的通讯被认为是可靠的。然后由代理来负责找到目的服务并负责通讯的可靠性和安全等问题。
当服务大量部署时随着服务部署的sidecar代理之间的连接形成了一个如下图所示的网格被称之为Service Mesh服务网格从而得出如下的服务网格定义。
_服务网格是一个基础设施层,用于处理服务间通信。云原生应用有着复杂的服务拓扑,服务网格保证请求可以在这些拓扑中可靠地穿梭。在实际应用当中,服务网格通常是由一系列轻量级的网络代理组成的,它们与应用程序部署在一起,但应用程序不需要知道它们的存在。_
_William Morgan _[_WHATS A SERVICE MESH? AND WHY DO I NEED ONE?_](https://buoyant.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/)_ _
![](/img/istio-install_and_example/mesh1.png)
了解了服务网格的基本概念,下一步介绍一下[Istio](https://istio.io/)。Istio是来自GoogleIBM和Lyft的一个Service Mesh服务网格开源项目是Google继Kubernetes之后的又一大作Istio架构先进设计合理刚一宣布就获得了Linkerdnginmesh等其他Service Mesh项目的合作以及Red hat/Pivotal/Weaveworks/Tigera/Datawire等的积极响应。
![](/img/istio-install_and_example/Istio-Architecture.PNG)
可以设想在不久的将来微服务的标准基础设施将是采用kubernetes进行服务部署和集群管理采用Istio处理服务通讯和治理两者相辅相成缺一不可。
## 安装Kubernetes
Istio是微服务通讯和治理的基础设施层本身并不负责服务的部署和集群管理因此需要和Kubernetes等服务编排工具协同工作。
Istio在架构设计上支持各种服务部署平台包括kubernetescloud foundryMesos等但Istio作为Google亲儿子对自家兄弟Kubernetes的支持肯定是首先考虑的。目前版本的0.2版本的手册中也只有Kubernetes集成的安装说明其它部署平台和Istio的集成将在后续版本中支持。
从Istio控制面Pilot的架构图可以看到各种部署平台可以通过插件方式集成到Istio中为Istio提供服务注册和发现功能。
![](/img/istio-install_and_example/PilotAdapters.PNG)
kubernetes集群的部署较为复杂[Rancher](http://rancher.com)提供了kubernetes部署模板通过一键式安装可以大大简化kubernetes集群的安装部署过程。
本文的测试环境为两台虚机组成的集群操作系统是Ubuntu 16.04.3 LTS。两台虚机的地址分别为
Rancher Server: 10.12.25.60
工作节点: 10.12.25.116
通过Rancher安装Kubernetes集群的简要步骤如下
### 在server和工作节点上安装docker
因为k8s并不支持最新版本的docker因此需根据该页面安装指定版本的docker
[http://rancher.com/docs/rancher/v1.6/en/hosts/](http://rancher.com/docs/rancher/v1.6/en/hosts/) ,目前是1.12版本。
```
curl https://releases.rancher.com/install-docker/1.12.sh | sh
```
如果需要以非root用户执行docker命令参考[如何使用非root用户执行docker命令](http://zhaohuabing.com/2018/02/09/docker-without-sudo/)。
### 启动Rancher server
```
sudo docker run -d --restart=always -p 8080:8080 rancher/server
```
### 登录Rancher管理界面创建k8s集群
Rancher 管理界面的缺省端口为8080在浏览器中打开该界面通过菜单Default-&gt;Manage Environment-&gt;Add Environment添加一个kubernetes集群。这里需要输入名称kubernetes描述然后选择kubernetes template点击create创建Kubernetes环境。![](/img/istio-install_and_example/Rancher.PNG)
点击菜单切换到kubernetes Environment然后点击右上方的Add a host添加一台host到kubernetes集群中。注意添加到集群中的host上必须先安装好符合要求的docker版本。
然后根据Rancher页面上的提示在host上执行脚本启动Rancher agent以将host加入ranch cluster。注意脚本中包含了rancher server的地址在host上必须可以ping通该地址。![](/img/istio-install_and_example/Rancher-add-host.PNG)
host加入cluster后Rancher会在host上pull kubernetes的images并启动kubernetes相关服务根据安装环境所在网络情况不同需要等待几分钟到几十分钟不等。
### 安装并配置kubectl
待Rancher界面提示kubernetes创建成功后安装kubernetes命令行工具kubectl
```
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
```
登录Rancher管理界面, 将 All Environments-&gt;kubernetes-&gt;KUBERNETES-&gt;CLI create config 的内容拷贝到~/.kube/config 中以配置Kubectl和kubernetes server的连接信息。![](/img/istio-install_and_example/Rancher-kubectl.PNG)
## 安装Istio
Istio提供了安装脚本该脚本会根据操作系统下载相应的Istio安装包并解压到当前目录。
```
curl -L https://git.io/getLatestIstio | sh -
```
根据脚本的提示将Istio命令行所在路径加入到系统PATH环境变量中
```
export PATH="$PATH:/home/ubuntu/istio-0.2.10/bin"
```
在kubernetes集群中部署Istio控制面服务
```
kubectl apply -f istio-0.2.10/install/kubernetes/istio.yaml
```
确认Istio控制面服务已成功部署。Kubernetes会创建一个istio-system namespace将Istio相关服务部署在该namespace中。
确认Istio相关Service的部署状态
```
kubectl get svc -n istio-system
```
```
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-egress 10.43.192.74 <none> 80/TCP 25s
istio-ingress 10.43.16.24 10.12.25.116,... 80:30984/TCP,443:30254/TCP 25s
istio-mixer 10.43.215.250 <none> 9091/TCP,9093/TCP,9094/TCP,9102/TCP,9125/UDP,42422/TCP 26s
istio-pilot 10.43.211.140 <none> 8080/TCP,443/TCP 25s
```
确认Istio相关Pod的部署状态
```
kubectl get pods -n istio-system
```
```
NAME READY STATUS RESTARTS AGE
istio-ca-367485603-qvbfl 1/1 Running 0 2m
istio-egress-3571786535-gwbgk 1/1 Running 0 2m
istio-ingress-2270755287-phwvq 1/1 Running 0 2m
istio-mixer-1505455116-9hmcw 2/2 Running 0 2m
istio-pilot-2278433625-68l34 1/1 Running 0 2m
```
从上面的输出可以看到这里部署的主要是Istio控制面的服务而数据面的网络代理要如何部署呢
根据前面服务网格的架构介绍可以得知网络代理是随着应用程序以sidecar的方式部署的在下面部署Bookinfo示例程序时会演示如何部署网络代理。
## 部署Bookinfo示例程序
在下载的Istio安装包的samples目录中包含了示例应用程序。
通过下面的命令部署Bookinfo应用
```
kubectl apply -f <(istioctl kube-inject -f istio-0.2.10/samples/bookinfo/kube/bookinfo.yaml)
```
确认Bookinfo服务已经启动
```
kubectl get services
```
```
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
details 10.43.175.204 <none> 9080/TCP 6m
kubernetes 10.43.0.1 <none> 443/TCP 5d
productpage 10.43.19.154 <none> 9080/TCP 6m
ratings 10.43.50.160 <none> 9080/TCP 6m
reviews 10.43.219.248 <none> 9080/TCP 6m
```
在浏览器中打开应用程序页面地址为istio-ingress的External IP
`http://10.12.25.116/productpage`
![](/img/istio-install_and_example/Bookinfo.PNG)
## 理解Istio Proxy实现原理
服务网格相对于sprint cloud等微服务代码库的一大优势是其对应用程序无侵入在不修改应用程序代码的前提下对应用服务之间的通信进行接管Istio是如何做到这点的呢下面通过示例程序的部署剖析其中的原理。
如果熟悉kubernetes的应用部署过程我们知道Bookinfo应用程序的标准部署方式是这样的
```
kubectl apply -f istio-0.2.10/samples/bookinfo/kube/bookinfo.yaml
```
但从上面的部署过程可以看到kubectl apply命令的输入并不是一个kubernetes yaml文件而是`istioctl kube-inject -f istio-0.2.10/samples/bookinfo/kube/bookinfo.yaml`命令的输出。
这段命令在这里起到了什么作用呢通过单独运行该命令并将输出保存到文件中我们可以查看istioctl kube-inject命令到底在背后搞了什么小动作。
```
istioctl kube-inject -f istio-0.2.10/samples/bookinfo/kube/bookinfo.yaml >> bookinfo_with_sidecar.yaml
```
对比bookinfo/_with/_sidecar.yaml文件和bookinfo.yaml可以看到该命令在bookinfo.yaml的基础上做了如下改动
* 为每个pod增加了一个代理container该container用于处理应用container之间的通信包括服务发现路由规则处理等。从下面的配置文件中可以看到proxy container通过15001端口进行监听接收应用container的流量。
* 为每个pod增加了一个init-container该container用于配置iptable将应用container的流量导入到代理container中。
```
#注入istio 网络代理
image: docker.io/istio/proxy_debug:0.2.10
imagePullPolicy: IfNotPresent
name: istio-proxy
resources: {}
securityContext:
privileged: true
readOnlyRootFilesystem: false
runAsUser: 1337
volumeMounts:
- mountPath: /etc/istio/proxy
name: istio-envoy
- mountPath: /etc/certs/
name: istio-certs
readOnly: true
#使用init container修改iptable
initContainers:
- args:
- -p
- "15001"
- -u
- "1337"
image: docker.io/istio/proxy_init:0.2.10
imagePullPolicy: IfNotPresent
name: istio-init
```
从上面的分析我们可以看出Istio的kube-inject工具的用途即是将代理sidecar注入了Bookinfo的kubernetes yaml部署文件中。通过该方式不需要用户手动修改kubernetes的部署文件即可在部署服务时将sidecar和应用一起部署。
通过命令查看pod中部署的docker container确认是否部署了Istio代理
```
kubectl get pods
NAME READY STATUS RESTARTS AGE
details-v1-3688945616-8hv8x 2/2 Running 0 1d
productpage-v1-2055622944-cslw1 2/2 Running 0 1d
ratings-v1-233971408-8dcnp 2/2 Running 0 1d
reviews-v1-1360980140-474x6 2/2 Running 0 1d
reviews-v2-1193607610-cfhb5 2/2 Running 0 1d
reviews-v3-3340858212-b5c8k 2/2 Running 0 1d
```
查看reviews pod的中的container可以看到pod中除reviews container外还部署了一个istio-proxy container
```
kubectl get pod reviews-v3-3340858212-b5c8k -o jsonpath='{.spec.containers[*].name}'
reviews istio-proxy
```
而应用container的流量是如何被导入到istio-proxy中的呢
原理是Istio proxy在端口15001进行监听pod中应用container的流量通过iptables规则被重定向到15001端口中。下面我们进入pod内部通过相关命令来验证这一点。
先通过命令行找到ratings-v1-233971408-8dcnp pod的PID以用于查看其network namespace內的iptables规则。
```
CONTAINER_ID=$(kubectl get po ratings-v1-233971408-8dcnp -o jsonpath='{.status.containerStatuses[0].containerID}' | cut -c 10-21)
PID=$(sudo docker inspect --format '{{ .State.Pid }}' $CONTAINER_ID)
```
可以使用nsenter命令来进入pod的network namespace执行命令。
使用netstat命令可以看到istio proxy代理的监听端口15001
```
sudo nsenter -t ${PID} -n netstat -all | grep 15001
tcp 0 0 *:15001 *:* LISTEN
```
使用iptables命令可以查看到下面的规则
```
sudo nsenter -t ${PID} -n iptables -t nat -L -n -v
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
16 960 ISTIO_REDIRECT all -- * * 0.0.0.0/0 0.0.0.0/0 /* istio/install-istio-prerouting */
Chain INPUT (policy ACCEPT 16 packets, 960 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 84838 packets, 7963K bytes)
pkts bytes target prot opt in out source destination
1969 118K ISTIO_OUTPUT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio/install-istio-output */
Chain POSTROUTING (policy ACCEPT 84838 packets, 7963K bytes)
pkts bytes target prot opt in out source destination
Chain ISTIO_OUTPUT (1 references)
pkts bytes target prot opt in out source destination
0 0 ISTIO_REDIRECT all -- * lo 0.0.0.0/0 !127.0.0.1 /* istio/redirect-implicit-loopback */
1969 118K RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 owner UID match 1337 /* istio/bypass-envoy */
0 0 RETURN all -- * * 0.0.0.0/0 127.0.0.1 /* istio/bypass-explicit-loopback */
0 0 ISTIO_REDIRECT all -- * * 0.0.0.0/0 0.0.0.0/0 /* istio/redirect-default-outbound */
Chain ISTIO_REDIRECT (3 references)
pkts bytes target prot opt in out source destination
16 960 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio/redirect-to-envoy-port */ redir ports 15001
```
从pod所在network namespace的iptables规则中可以看到pod的入口和出口流量分别通过PREROUTING和OUTPUT chain指向了自定义的ISTIO/_REDIRECT chain而ISTIO/_REDIRECT chain中的规则将所有流量都重定向到了istio proxy正在监听的15001端口中。从而实现了对应用透明的通信代理。
## 测试路由规则
多次刷新Bookinfo应用的productpage页面我们会发现该页面中显示的Book Reviews有时候有带红星的评价信息有时有带黑星的评价信息有时只有文字评价信息。
这是因为Bookinfo应用程序部署了3个版本的Reviews服务每个版本的返回结果不同在没有设置路由规则时缺省的路由会将请求随机路由到每个版本的服务上如下图所示
![](/img/istio-install_and_example/withistio.svg)
通过创建一条路由规则route-rule.yaml将请求流量都引导到Reviews-v1服务上
```
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-default
spec:
destination:
name: reviews
precedence: 1
route:
- labels:
version: v1
```
启用该路由规则
```
istioctl create -f route-rule.yaml -n default
```
再次打开productpage页面, 无论刷新多少次显示的页面将始终是v1版本的输出即不带星的评价内容。
![](/img/istio-install_and_example/Bookinfo-no-star.PNG)
删除该路由规则
```
istioctl delete -f route_rule.yaml -n default
```
继续刷新productpage页面,将重新随机出现三个版本的评价内容页面。
## 分布式调用追踪
首先修改安装包中的 `istio-0.2.10/install/kubernetes/addons/zipkin.yaml` 部署文件增加Nodeport,以便能在kubernetes集群外部访问zipkin界面。
```
apiVersion: v1
kind: Service
metadata:
name: zipkin
namespace: istio-system
spec:
ports:
- name: http
port: 9411
nodePort: 30001
selector:
app: zipkin
type: NodePort
```
部署zipkin服务。
```
kubectl apply -f istio-0.2.10/install/kubernetes/addons/zipkin.yaml
```
在浏览器中打开zipkin页面可以追踪一个端到端调用经过了哪些服务以及各个服务花费的时间等详细信息如下图所示
`http://10.12.25.116:30001`
![](/img/istio-install_and_example/zipkin.PNG)
## 性能指标监控
首先修改安装包中的 `istio-0.2.10/install/kubernetes/addons/grafana.yaml` 部署文件增加Nodeport,以便能在kubernetes集群外部访问grafana界面。
```
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: istio-system
spec:
ports:
- port: 3000
protocol: TCP
name: http
nodePort: 30002
selector:
app: grafana
type: NodePort
```
prometheus用于收集和存储信息指标grafana用于将性能指标信息进行可视化呈现需要同时部署prometheus和grafana服务。
```
kubectl apply -f istio-0.2.10/install/kubernetes/addons/prometheus.yaml
kubectl apply -f istio-0.2.10/install/kubernetes/addons/grafana.yaml
```
首先在浏览器中打开Bookinfo的页面`http://10.12.25.116/productpage`,刷新几次,以制造一些性能指标数据。
然后打开grafana页面查看性能指标`http://10.12.25.116:30002/dashboard/db/istio-dashboard`,如下图所示:
![](/img/istio-install_and_example/grafana.PNG)
## 参考
* [Istio官方文档](https://istio.io/docs/)
* [Pattern: Service Mesh](http://philcalcado.com/2017/08/03/pattern_service_mesh.html)
* [WHATS A SERVICE MESH? AND WHY DO I NEED ONE?](https://buoyant.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/)
* [A Hackers Guide to Kubernetes Networking](https://thenewstack.io/hackers-guide-kubernetes-networking/)

View File

@@ -0,0 +1,94 @@
---
layout: post
title: "使用Istio实现应用流量转移"
subtitle:   "本文翻译自istio官方文档"
description: "本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio可以使用一系列不同权重的规则10%20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。"
excerpt: "本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio可以使用一系列不同权重的规则10%20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。"
date: 2017-11-07
author:     "赵化冰"
image: "/img/istio-traffic-shifting/crossroads.png"
categories: [ "Tech"]
tags:
- Istio
URL: "/2017/11/07/istio-traffic-shifting/"
---
关于Istio的更多内容请参考[istio中文文档](http://istio.doczh.cn/)。
原文参见[Traffic Shifting](https://istio.io/docs/tasks/traffic-management/traffic-shifting.html)。
本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio可以使用一系列不同权重的规则10%20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。
<!--more-->
为简单起见,本任务将采用两步将流量从`reviews:v1` 迁移到 `reviews:v3`权重分别为50%100%。
# 开始之前
* 参照文档[安装指南](http://istio.doczh.cn/docs/setup/kubernetes/index.html)中的步骤安装Istio。
* 部署[BookInfo](http://istio.doczh.cn/docs/guides/bookinfo.html) 示例应用程序。
> 请注意本文档假设示采用kubernetes部署示例应用程序。所有的示例命令行都采用规则yaml文件例如`samples/bookinfo/kube/route-rule-all-v1.yaml`指定的kubernetes版本。如果在不同的环境下运行本任务请将`kube`修改为运行环境中相应的目录例如对基于Consul的运行环境目录就是`samples/bookinfo/consul/route-rule-all-v1.yaml`)。
# 基于权重的版本路由
1. 将所有微服务的缺省版本设置为v1.
```bash
istioctl create -f samples/bookinfo/kube/route-rule-all-v1.yaml
```
1. 在浏览器中打开http://$GATEWAY_URL/productpage, 确认`reviews` 服务目前的活动版本是v1。
可以看到浏览器中出现BooInfo应用的productpage页面。
注意`productpage`显示的评价内容不带星级。这是由于`reviews:v1`不会访问`ratings`服务。
> 请注意:如果之前执行过 [配置请求路由](http://istio.doczh.cn/docs/tasks/traffic-management/request-routing.html)任务则需要先注销测试用户“jason”或者删除之前单独为该用户创建的测试规则
```bash
istioctl delete routerule reviews-test-v2
```
1. 首先使用下面的命令把50%的流量从`reviews:v1`转移到`reviews:v3`:
```bash
istioctl replace -f samples/bookinfo/kube/route-rule-reviews-50-v3.yaml
```
注意这里使用了`istioctl replace`而不是`create`。
1. 在浏览器中多次刷新`productpage`页面大约有50%的几率会看到页面中出现带红星的评价内容。
> 请注意在目前的Envoy sidecar实现中可能需要刷新`productpage`很多次才能看到流量分发的效果。在看到页面出现变化前有可能需要刷新15次或者更多。如果修改规则将90%的流量路由到v3可以看到更明显的效果。
1. 当v3版本的`reviews`服务已经稳定运行后可以将100%的流量路由到`reviews:v3`
```bash
istioctl replace -f samples/bookinfo/kube/route-rule-reviews-v3.yaml
```
此时,以任何用户登录到`productpage`页面,都可以看到带红星的评价信息。
# 理解原理
在这个任务中我们使用了Istio的带权重路由的特性将流量从老版本的`reviews`服务逐渐迁移到了新版本服务中。
注意该方式和使用容器编排平台的部署特性来进行版本迁移是完全不同的。容器编排平台使用了实例scaling来对流量进行管理。而通过Istio两个版本的`reviews`服务可以独立地进行scale up和scale down并不会影响这两个版本服务之间的流量分发。
想了解更多支持scaling的按版本路由功能请查看[Canary Deployments using Istio](https://istio.io/blog/canary-deployments-using-istio.html)。
# 清理
* 删除路由规则。
```bash
istioctl delete -f samples/bookinfo/kube/route-rule-all-v1.yaml
```
* 如果不打算尝试后面的任务,请参照[BookInfo cleanup](http://istio.doczh.cn/docs/guides/bookinfo.html#cleanup) 中的步骤关闭应用程序。
# 进阶阅读
* 更多的内容请参见[请求路由](http://istio.doczh.cn/docs/concepts/traffic-management/rules-configuration.html)。

View File

@@ -0,0 +1,298 @@
---
title: "采用Istio实现灰度发布(金丝雀发布)"
subtitle: "用户无感知的平滑业务升级"
description: "当应用上线以后运维面临的一大挑战是如何能在不影响已上线业务的情况下进行升级。本文将介绍如何使用Istio实现应用的灰度发布金丝雀发布"
excerpt: "当应用上线以后运维面临的一大挑战是如何能在不影响已上线业务的情况下进行升级。本文将介绍如何使用Istio实现应用的灰度发布金丝雀发布"
date: 2017-11-08 15:00:00
author: "赵化冰"
image: "/img/istio-canary-release/canary_bg.jpg"
publishDate: 2017-11-08 15:00:00
tags:
- Istio
URL: "/2017/11/08/istio-canary-release/"
categories: [ "Tech" ]
---
## 灰度发布(又名金丝雀发布)介绍
当应用上线以后,运维面临的一大挑战是如何能够在不影响已上线业务的情况下进行升级。做过产品的同学都清楚,不管在发布前做过多么完备的自动化和人工测试,在发布后都会出现或多或少的故障。根据墨菲定律,可能会出错的版本发布一定会出错。
"ANYTHING THAN CAN GO WRONG WILL GO WRONG" --MURPHY'S LAW
因此我们不能寄希望于在线下测试时发现所有潜在故障。在无法百分百避免版本升级故障的情况下,需要通过一种方式进行可控的版本发布,把故障影响控制在可以接受的范围内,并可以快速回退。
可以通过[灰度发布(又名金丝雀发布)](https://martinfowler.com/bliki/CanaryRelease.html)来实现业务从老版本到新版本的平滑过渡,并避免升级过程中出现的问题对用户造成的影响。
“金丝雀发布”的来源于矿工们用金丝雀对矿井进行空气测试的做法。以前矿工挖煤的时候,矿工下矿井前会先把金丝雀放进去,或者挖煤的时候一直带着金丝雀。金丝雀对甲烷和一氧化碳浓度比较敏感,会先报警。所以大家都用“金丝雀”来搞最先的测试。
下图中左下方的少部分用户就被当作“金丝雀”来用于测试新上线的1.1版本。如果新版本出现问题,“金丝雀”们会报警,但不会影响其他用户业务的正常运行。
![Istio灰度发布示意图](/img/istio-canary-release/canary-deployment.PNG)
灰度发布(金丝雀发布)的流程如下:
- 准备和生产环境隔离的“金丝雀”服务器。
- 将新版本的服务部署到“金丝雀”服务器上。
- 对“金丝雀”服务器上的服务进行自动化和人工测试。
- 测试通过后,将“金丝雀”服务器连接到生产环境,将少量生产流量导入到“金丝雀”服务器中。
- 如果在线测试出现问题,则通过把生产流量从“金丝雀”服务器中重新路由到老版本的服务的方式进行回退,修复问题后重新进行发布。
- 如果在线测试顺利,则逐渐把生产流量按一定策略逐渐导入到新版本服务器中。
- 待新版本服务稳定运行后,删除老版本服务。
## Istio实现灰度发布(金丝雀发布)的原理
从上面的流程可以看到,如果要实现一套灰度发布的流程,需要应用程序和运维流程对该发布过程进行支持,工作量和难度的挑战是非常大的。虽然面对的问题类似,但每个企业或组织一般采用不同的私有化实现方案来进行灰度发布,为解决该问题导致研发和运维花费了大量的成本。
Istio通过高度的抽象和良好的设计采用一致的方式解决了该问题采用sidecar对应用流量进行了转发通过Pilot下发路由规则可以在不修改应用程序的前提下实现应用的灰度发布。
备注采用kubernetes的[滚动升级(rolling update)](https://kubernetes.io/docs/tasks/run-application/rolling-update-replication-controller/)功能也可以实现不中断业务的应用升级,但滚动升级是通过逐渐使用新版本的服务来替换老版本服务的方式对应用进行升级,在滚动升级不能对应用的流量分发进行控制,因此无法采用受控地把生产流量逐渐导流到新版本服务中,也就无法控制服务升级对用户造成的影响。
采用Istio后可以通过定制路由规则将特定的流量如指定特征的用户导入新版本服务中在生产环境下进行测试同时通过渐进受控地导入生产流量可以最小化升级中出现的故障对用户的影响。并且在同时存在新老版本服务时还可根据应用压力对不同版本的服务进行独立的缩扩容非常灵活。采用Istio进行灰度发布的流程如下图所示
![Istio灰度发布示意图](/img/istio-canary-release/canary-deployments.gif)
## 操作步骤
下面采用Istion自带的BookinfoInfo示例程序来试验灰度发布的流程。
### 测试环境安装
首先参考[手把手教你从零搭建Istio及Bookinfo示例程序](http://zhaohuabing.com/2017/11/04/istio-install_and_example/)安装Kubernetes及Istio控制面。
因为本试验并不需要安装全部3个版本的reviews服务因此如果已经安装了该应用先采用下面的命令卸载。
```
istio-0.2.10/samples/bookinfo/kube/cleanup.sh
```
### 部署V1版本的服务
首先只部署V1版本的Bookinfo应用程序。由于示例中的yaml文件中包含了3个版本的reviews服务我们先将V2和V3版本的Deployment从yaml文件istio-0.2.10/samples/bookinfo/kube/bookinfo.yaml中删除。
从Bookinfo.yaml中删除这部分内容:
```
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: reviews-v2
spec:
replicas: 1
template:
metadata:
labels:
app: reviews
version: v2
spec:
containers:
- name: reviews
image: istio/examples-bookinfo-reviews-v2:0.2.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: reviews-v3
spec:
replicas: 1
template:
metadata:
labels:
app: reviews
version: v3
spec:
containers:
- name: reviews
image: istio/examples-bookinfo-reviews-v3:0.2.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
---
```
部署V1版本的Bookinfo程序。
```
kubectl apply -f <(istioctl kube-inject -f istio-0.2.10/samples/bookinfo/kube/bookinfo.yaml)
```
通过kubectl命令行确认pod部署可以看到只有V1版本的服务。
```
kubectl get pods
NAME READY STATUS RESTARTS AGE
details-v1-3688945616-nhkqk 2/2 Running 0 2m
productpage-v1-2055622944-m3fql 2/2 Running 0 2m
ratings-v1-233971408-0f3s9 2/2 Running 0 2m
reviews-v1-1360980140-0zs9z 2/2 Running 0 2m
```
在浏览器中打开应用程序页面地址为istio-ingress的External IP。由于V1版本的reviews服务并不会调用rating服务因此可以看到Product 页面显示的是不带星级的评价信息。
`http://10.12.25.116/productpage`
![](//img/istio-canary-release/product-page-default.PNG)
此时系统中微服务的部署情况如下图所示下面的示意图均忽略和本例关系不大的details和ratings服务
![](//img/istio-canary-release/canary-example-only-v1.PNG)
### 部署V2版本的reviews服务
在部署V2版本的reviews服务前需要先创建一条缺省路由规则route-rule-default-reviews.yaml将所有生产流量都导向V1版本避免对线上用户的影响。
```
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-default
spec:
destination:
name: reviews
precedence: 1
route:
- labels:
version: v1
```
启用该路由规则。
```
istioctl create -f route-rule-default-reviews.yaml -n default
```
创建一个V2版本的部署文件bookinfo-reviews-v2.yaml内容如下
```
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: reviews-v2
spec:
replicas: 1
template:
metadata:
labels:
app: reviews
version: v2
spec:
containers:
- name: reviews
image: istio/examples-bookinfo-reviews-v2:0.2.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
```
部署V2版本的reviews服务。
```
kubectl apply -f <(istioctl kube-inject -f bookinfo-reviews-v2.yaml)
```
此时系统中部署了V1和V2两个版本的reviews服务但所有的业务流量都被规则reviews-default导向了V1如下图所示
![](/img/istio-canary-release/canary-example-deploy-v2.PNG)
### 将测试流量导入到V2版本的reviews服务
在进行模拟测试时,由于测试环境和生产环境的网络,服务器,操作系统等环境存在差异,很难完全模拟生产环境进行测试。为了减少环境因素的对测试结果的影响,我们希望能在生产环境中进行上线前的测试,但如果没有很好的隔离措施,可能会导致测试影响已上线的业务,对企业造成损失。
通过采用Istio的路由规则可以在类生产环境中进行测试又完全隔离了线上用户的生产流量和测试流量最小化模拟测试对已上线业务的影响。如下图所示
![](/img/istio-canary-release/canary-example-route-test.PNG)
创建一条规则,将用户名为 test-user 的流量导入到V2
```
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-test-user
spec:
destination:
name: reviews
precedence: 2
match:
request:
headers:
cookie:
regex: "^(.*?;)?(user=test-user)(;.*)?$"
route:
- labels:
version: v2
```
注意precedence属性用于设置规则的优先级在同时存在多条规则的情况下优先级高的规则将先执行。这条规则的precedence设置为2以确保其在缺省规则之前运行将test-user用户的请求导流到V2版本reviews服务中。
启用该规则。
```
istioctl create -f route-rule-test-reviews-v2.yaml -n default
```
以test-user用户登录可以看到V2版本带星级的评价页面。
![](/img/istio-canary-release/product-page-test-user.PNG)
注销test-user只能看到V1版本不带星级的评价页面。如下图所示
![](/img/istio-canary-release/product-page-default.PNG)
### 将部分生产流量导入到V2版本的reviews服务
在线上模拟测试完成后如果系统测试情况良好可以通过规则将一部分用户流量导入到V2版本的服务中进行小规模的“金丝雀”测试。
修改规则route-rule-default-reviews.yaml将50%的流量导入V2版本。
> 备注本例只是描述原理因此为简单起见将50%流量导入V2版本在实际操作中更可能是先导入较少流量然后根据监控的新版本运行情况将流量逐渐导入如采用5%10%20%50% ...的比例逐渐导入。
```
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-default
spec:
destination:
name: reviews
precedence: 1
route:
- labels:
version: v1
weight: 50
- labels:
version: v2
weight: 50
```
```
istioctl replace -f route-rule-default-reviews.yaml -n default
```
此时系统部署如下图所示:
![](/img/istio-canary-release/canary-example-route-production-50.PNG)
### 将所有生产流量导入到到V2版本的reviews服务
如果新版本的服务运行正常则可以将所有流量导入到V2版本。
```
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-default
spec:
destination:
name: reviews
precedence: 1
route:
- labels:
version: v2
weight: 100
```
```
istioctl replace -f route-rule-default-reviews.yaml -n default
```
系统部署如下图所示:
![](/img/istio-canary-release/canary-example-route-production-100.PNG)
此时不管以任何用户登录都只能看到V2版本带星级的评价页面如下图所示
![](/img/istio-canary-release/product-page-default-v2.PNG)
> 备注如果灰度发布的过程中新版本的服务出现问题则可以通过修改路由规则将流量重新导入到V1版本的服务中将V2版本故障修复后再进行测试。
### 删除V1版本的reviews服务
待V2版本上线稳定运行后删除V1版本的reviews服务和测试规则。
```
kubectl delete pod reviews-v1-1360980140-0zs9z
istioctl delete -f route-rule-test-reviews-v2.yaml -n default
```
## 参考
* [Istio官方文档](https://istio.io/docs/)

View File

@@ -0,0 +1,256 @@
---
layout: post
title: "如何从外部访问Kubernetes集群中的应用"
subtitle: ""
description: "我们知道kubernetes的Cluster Network属于私有网络只能在cluster Network内部才能访问部署的应用那如何才能将Kubernetes集群中的应用暴露到外部网络为外部用户提供服务呢本文探讨了从外部网络访问kubernetes cluster中应用的几种实现方式。"
date: 2017-11-28 12:00:00
author: "赵化冰"
image: "https://img.zhaohuabing.com/post-bg-2015.jpg"
publishDate: 2017-11-28 12:00:00
tags:
- Kubernetes
URL: "/2017/11/28/access-application-from-outside/"
categories: [ Tech ]
---
## 前言
我们知道kubernetes的Cluster Network属于私有网络只能在cluster Network内部才能访问部署的应用那如何才能将Kubernetes集群中的应用暴露到外部网络为外部用户提供服务呢本文探讨了从外部网络访问kubernetes cluster中应用的几种实现方式。
<!--more-->
>本文尽量试着写得比较容易理解但要做到“深入浅出”把复杂的事情用通俗易懂的语言描述出来是非常需要功力的个人自认尚未达到此境界唯有不断努力。此外kubernetes本身是一个比较复杂的系统无法在本文中详细解释涉及的所有相关概念否则就可能脱离了文章的主题因此假设阅读此文之前读者对kubernetes的基本概念如dockercontainerpod已有所了解。
另外此文中的一些内容是自己的理解,由于个人的知识范围有限,可能有误,如果读者对文章中的内容有疑问或者勘误,欢迎大家指证。
## Pod和Service
我们首先来了解一下Kubernetes中的Pod和Service的概念。
Pod(容器组),英文中Pod是豆荚的意思从名字的含义可以看出Pod是一组有依赖关系的容器Pod包含的容器都会运行在同一个host节点上共享相同的volumes和network namespace空间。Kubernetes以Pod为基本操作单元可以同时启动多个相同的pod用于failover或者load balance。
![Pod](/img/access-application-from-outside/pod.PNG)
Pod的生命周期是短暂的Kubernetes根据应用的配置会对Pod进行创建销毁根据监控指标进行缩扩容。kubernetes在创建Pod时可以选择集群中的任何一台空闲的Host因此其网络地址是不固定的。由于Pod的这一特点一般不建议直接通过Pod的地址去访问应用。
为了解决访问Pod不方便直接访问的问题Kubernetes采用了Service的概念Service是对后端提供服务的一组Pod的抽象Service会绑定到一个固定的虚拟IP上该虚拟IP只在Kubernetes Cluster中可见但其实该IP并不对应一个虚拟或者物理设备而只是IPtable中的规则然后再通过IPtable将服务请求路由到后端的Pod中。通过这种方式可以确保服务消费者可以稳定地访问Pod提供的服务而不用关心Pod的创建、删除、迁移等变化以及如何用一组Pod来进行负载均衡。
Service的机制如下图所示Kube-proxy监听kubernetes master增加和删除Service以及Endpoint的消息对于每一个Servicekube proxy创建相应的iptables规则将发送到Service Cluster IP的流量转发到Service后端提供服务的Pod的相应端口上。
![Pod和Service的关系](/img/access-application-from-outside/services-iptables-overview.png)
>备注虽然可以通过Service的Cluster IP和服务端口访问到后端Pod提供的服务但该Cluster IP是Ping不通的原因是Cluster IP只是iptable中的规则并不对应到一个网络设备。
## Service的类型
Service的类型(ServiceType)决定了Service如何对外提供服务根据类型不同服务可以只在Kubernetes cluster中可见也可以暴露到Cluster外部。Service有三种类型ClusterIPNodePort和LoadBalancer。其中ClusterIP是Service的缺省类型这种类型的服务会提供一个只能在Cluster内才能访问的虚拟IP其实现机制如上面一节所述。
## 通过NodePort提供外部访问入口
通过将Service的类型设置为NodePort可以在Cluster中的主机上通过一个指定端口暴露服务。注意通过Cluster中每台主机上的该指定端口都可以访问到该服务发送到该主机端口的请求会被kubernetes路由到提供服务的Pod上。采用这种服务类型可以在kubernetes cluster网络外通过主机IP端口的方式访问到服务。
> 注意官方文档中说明了Kubernetes clusterIp的流量转发到后端Pod有Iptable和kube proxy两种方式。但对Nodeport如何转发流量却语焉不详。该图来自网络从图来看是通过kube proxy转发的我没有去研究过源码。欢迎了解的同学跟帖说明。
![Pod和Service的关系](/img/access-application-from-outside/nodeport.PNG)
下面是通过NodePort向外暴露服务的一个例子注意可以指定一个nodePort也可以不指定。在不指定的情况下kubernetes会从可用的端口范围内自动分配一个随机端口。
```
kind: Service
apiVersion: v1
metadata:
name: influxdb
spec:
type: NodePort
ports:
- port: 8086
nodePort: 30000
selector:
name: influxdb
```
通过NodePort从外部访问有下面的一些问题自己玩玩或者进行测试时可以使用该方案但不适宜用于生产环境。
* Kubernetes cluster host的IP必须是一个well-known IP即客户端必须知道该IP。但Cluster中的host是被作为资源池看待的可以增加删除每个host的IP一般也是动态分配的因此并不能认为host IP对客户端而言是well-known IP。
* 客户端访问某一个固定的host IP存在单点故障。假如一台host宕机了kubernetes cluster会把应用 reload到另一节点上但客户端就无法通过该host的nodeport访问应用了。
* 该方案假设客户端可以访问Kubernetes host所在网络。在生产环境中客户端和Kubernetes host网络可能是隔离的。例如客户端可能是公网中的一个手机APP是无法直接访问host所在的私有网络的。
因此需要通过一个网关来将外部客户端的请求导入到Cluster中的应用中在kubernetes中这个网关是一个4层的load balancer。
## 通过Load Balancer提供外部访问入口
通过将Service的类型设置为LoadBalancer可以为Service创建一个外部Load Balancer。Kubernetes的文档中声明该Service类型需要云服务提供商的支持其实这里只是在Kubernetes配置文件中提出了一个要求即为该Service创建Load Balancer至于如何创建则是由Google Cloud或Amazon Cloud等云服务商提供的创建的Load Balancer不在Kubernetes Cluster的管理范围中。kubernetes 1.6版本中WS, Azure, CloudStack, GCE and OpenStack等云提供商已经可以为Kubernetes提供Load Balancer.下面是一个Load balancer类型的Service例子
```
kind: Service
apiVersion: v1
metadata:
name: influxdb
spec:
type: LoadBalancer
ports:
- port: 8086
selector:
name: influxdb
```
部署该Service后我们来看一下Kubernetes创建的内容
```
$ kubectl get svc influxdb
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
influxdb 10.97.121.42 10.13.242.236 8086:30051/TCP 39s
```
Kubernetes首先为influxdb创建了一个集群内部可以访问的ClusterIP 10.97.121.42。由于没有指定nodeport端口kubernetes选择了一个空闲的30051主机端口将service暴露在主机的网络上然后通知cloud provider创建了一个load balancer上面输出中的EEXTERNAL-IP就是load balancer的IP。
测试使用的Cloud Provider是OpenStack我们通过neutron lb-vip-show可以查看创建的Load Balancer详细信息。
```
$ neutron lb-vip-show 9bf2a580-2ba4-4494-93fd-9b6969c55ac3
+---------------------+--------------------------------------------------------------+
| Field | Value |
+---------------------+--------------------------------------------------------------+
| address | 10.13.242.236 |
| admin_state_up | True |
| connection_limit | -1 |
| description | Kubernetes external service a6ffa4dadf99711e68ea2fa163e0b082 |
| id | 9bf2a580-2ba4-4494-93fd-9b6969c55ac3 |
| name | a6ffa4dadf99711e68ea2fa163e0b082 |
| pool_id | 392917a6-ed61-4924-acb2-026cd4181755 |
| port_id | e450b80b-6da1-4b31-a008-280abdc6400b |
| protocol | TCP |
| protocol_port | 8086 |
| session_persistence | |
| status | ACTIVE |
| status_description | |
| subnet_id | 73f8eb91-90cf-42f4-85d0-dcff44077313 |
| tenant_id | 4d68886fea6e45b0bc2e05cd302cccb9 |
+---------------------+--------------------------------------------------------------+
$ neutron lb-pool-show 392917a6-ed61-4924-acb2-026cd4181755
+------------------------+--------------------------------------+
| Field | Value |
+------------------------+--------------------------------------+
| admin_state_up | True |
| description | |
| health_monitors | |
| health_monitors_status | |
| id | 392917a6-ed61-4924-acb2-026cd4181755 |
| lb_method | ROUND_ROBIN |
| members | d0825cc2-46a3-43bd-af82-e9d8f1f85299 |
| | 3f73d3bb-bc40-478d-8d0e-df05cdfb9734 |
| name | a6ffa4dadf99711e68ea2fa163e0b082 |
| protocol | TCP |
| provider | haproxy |
| status | ACTIVE |
| status_description | |
| subnet_id | 73f8eb91-90cf-42f4-85d0-dcff44077313 |
| tenant_id | 4d68886fea6e45b0bc2e05cd302cccb9 |
| vip_id | 9bf2a580-2ba4-4494-93fd-9b6969c55ac3 |
+------------------------+--------------------------------------+
$ neutron lb-member-list
+--------------------------------------+--------------+---------------+--------+----------------+--------+
| id | address | protocol_port | weight | admin_state_up | status |
+--------------------------------------+--------------+---------------+--------+----------------+--------+
| 3f73d3bb-bc40-478d-8d0e-df05cdfb9734 | 10.13.241.89 | 30051 | 1 | True | ACTIVE |
| d0825cc2-46a3-43bd-af82-e9d8f1f85299 | 10.13.241.10 | 30051 | 1 | True | ACTIVE |
+--------------------------------------+--------------+---------------+--------+----------------+--------
```
可以看到OpenStack使用VIP 10.13.242.236在端口8086创建了一个Load BalancerLoad Balancer对应的Lb pool里面有两个成员10.13.241.89 和 10.13.241.10正是Kubernetes的host节点进入Load balancer流量被分发到这两个节点对应的Service Nodeport 30051上。
但是如果客户端不在Openstack Neutron的私有子网上则还需要在load balancer的VIP上关联一个floating IP以使外部客户端可以连接到load balancer。
部署Load balancer后应用的拓扑结构如下图所示本图假设Kubernetes Cluster部署在Openstack私有云上
![外部Load balancer](/img/access-application-from-outside/load-balancer.PNG)
>备注如果kubernetes环境在Public Cloud上Loadbalancer类型的Service创建出的外部Load Balancer已经带有公网IP地址是可以直接从外部网络进行访问的不需要绑定floating IP这个步骤。例如在AWS上创建的Elastic Load Balancing (ELB),有兴趣可以看一下这篇文章:[Expose Services on your AWS Quick Start Kubernetes cluster]( http://docs.heptio.com/content/tutorials/aws-qs-services-elb.html)。
如果Kubernetes Cluster是在不支持LoadBalancer特性的cloud provider或者裸机上创建的可以实现LoadBalancer类型的Service吗应该也是可以的。Kubernetes本身并不直接支持Loadbalancer但我们可以通过对Kubernetes进行扩展来实现可以监听kubernetes Master的service创建消息并根据消息部署相应的Load Balancer如Nginx或者HAProxy来实现Load balancer类型的Service。
通过设置Service类型提供的是四层Load Balancer当只需要向外暴露一个服务的时候可以直接采用这种方式。但在一个应用需要对外提供多个服务时采用该方式会为每一个服务IP+Port都创建一个外部load balancer。如下图所示
![创建多个Load balancer暴露应用的多个服务](/img/access-application-from-outside/multiple-load-balancer.PNG)
一般来说,同一个应用的多个服务/资源会放在同一个域名下在这种情况下创建多个Load balancer是完全没有必要的反而带来了额外的开销和管理成本。直接将服务暴露给外部用户也会导致了前端和后端的耦合影响了后端架构的灵活性如果以后由于业务需求对服务进行调整会直接影响到客户端。可以通过使用Kubernetes Ingress进行L7 load balancing来解决该问题。
## 采用Ingress作为七层load balancer
首先看一下引入Ingress后的应用拓扑示意图本图假设Kubernetes Cluster部署在Openstack私有云上
![采用Ingress暴露应用的多个服务](/img/access-application-from-outside/ingress.PNG)
这里Ingress起到了七层负载均衡器和Http方向代理的作用可以根据不同的url把入口流量分发到不同的后端Service。外部客户端只看到foo.bar.com这个服务器屏蔽了内部多个Service的实现方式。采用这种方式简化了客户端的访问并增加了后端实现和部署的灵活性可以在不影响客户端的情况下对后端的服务部署进行调整。
下面是Kubernetes Ingress配置文件的示例在虚拟主机foot.bar.com下面定义了两个Path其中/foo被分发到后端服务s1/bar被分发到后端服务s2。
```
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
backend:
serviceName: s1
servicePort: 80
- path: /bar
backend:
serviceName: s2
servicePort: 80
```
注意这里Ingress只描述了一个虚拟主机路径分发的要求可以定义多个Ingress描述不同的7层分发要求而这些要求需要由一个Ingress Controller来实现。Ingress Contorller会监听Kubernetes Master得到Ingress的定义并根据Ingress的定义对一个7层代理进行相应的配置以实现Ingress定义中要求的虚拟主机和路径分发规则。Ingress Controller有多种实现Kubernetes提供了一个[基于Nginx的Ingress Controller](https://github.com/kubernetes/ingress-nginx)。需要注意的是在部署Kubernetes集群时并不会缺省部署Ingress Controller需要我们自行部署。
下面是部署Nginx Ingress Controller的配置文件示例注意这里为Nginx Ingress Controller定义了一个LoadBalancer类型的Service以为Ingress Controller提供一个外部可以访问的公网IP。
```
apiVersion: v1
kind: Service
metadata:
name: nginx-ingress
spec:
type: LoadBalancer
ports:
- port: 80
name: http
- port: 443
name: https
selector:
k8s-app: nginx-ingress-lb
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ingress-controller
spec:
replicas: 2
revisionHistoryLimit: 3
template:
metadata:
labels:
k8s-app: nginx-ingress-lb
spec:
terminationGracePeriodSeconds: 60
containers:
- name: nginx-ingress-controller
image: gcr.io/google_containers/nginx-ingress-controller:0.8.3
imagePullPolicy: Always
//----omitted for brevity----
```
>备注Google Cloud直接支持Ingress资源如果应用部署在Google Cloud中Google Cloud会自动为Ingress资源创建一个7层load balancer并为之分配一个外部IP不需要自行部署Ingress Controller。
## 结论
采用Ingress加上Load balancer的方式可以将Kubernetes Cluster中的应用服务暴露给外部客户端。这种方式比较灵活基本可以满足大部分应用的需要。但如果需要在入口处提供更强大的功能如有更高的效率要求需求进行安全认证日志记录或者需要一些应用的定制逻辑则需要考虑采用微服务架构中的API Gateway模式采用一个更强大的API Gateway来作为应用的流量入口。
## 参考
* [Accessing Kubernetes Pods from Outside of the Cluster](http://alesnosek.com/blog/2017/02/14/accessing-kubernetes-pods-from-outside-of-the-cluster/)
* [Kubernetes nginx-ingress-controller](https://daemonza.github.io/2017/02/13/kubernetes-nginx-ingress-controller/)
* [Using Kubernetes external load balancer feature](https://docs.openstack.org/magnum/ocata/dev/kubernetes-load-balancer.html)
* [Expose Services on your AWS Quick Start Kubernetes cluster]( http://docs.heptio.com/content/tutorials/aws-qs-services-elb.html)

View File

@@ -0,0 +1,386 @@
---
layout: post
title: "Nginx开源Service Mesh组件Nginmesh安装指南"
subtitle: ""
description: "Nginmesh是NGINX的Service Mesh开源项目用于Istio服务网格平台中的数据面代理。它旨在提供七层负载均衡和服务路由功能与Istio集成作为sidecar部署并将以“标准可靠和安全的方式”使得服务间通信更容易。Nginmesh在今年底已经连续发布了0.2和0.3版本提供了服务发现请求转发路由规则性能指标收集等功能。本文介绍如何采用kubeadmin安装kubernetes集群并部署Nginmesh sidecar。"
date: 2018-01-02 12:00:00
author: "赵化冰"
image: "img/post-bg-2015.jpg"
publishDate: 2018-01-02 12:00:00
tags:
- Istio
- service Mesh
- nginmesh
URL: "/2018/01/02/nginmesh-install/"
categories: [ Tech ]
---
## 前言
Nginmesh是NGINX的Service Mesh开源项目用于Istio服务网格平台中的数据面代理。它旨在提供七层负载均衡和服务路由功能与Istio集成作为sidecar部署并将以“标准可靠和安全的方式”使得服务间通信更容易。Nginmesh在今年底已经连续发布了0.2和0.3版本,提供了服务发现,请求转发,路由规则,性能指标收集等功能。
<!--more-->
![Nginmesh sidecar proxy](https://raw.githubusercontent.com/nginmesh/nginmesh/master/images/nginx_sidecar.png)
> 备注本文安装指南基于Ubuntu 16.04在Centos上某些安装步骤的命令可能需要稍作改动。
## 安装Kubernetes Cluster
Kubernetes Cluster包含etcd, api server, schedulercontroller manager等多个组件组件之间的配置较为复杂如果要手动去逐个安装及配置各个组件需要了解kubernetes操作系统及网络等多方面的知识对安装人员的能力要求较高。kubeadm提供了一个简便快速安装Kubernetes Cluster的方式并且可以通过安装配置文件提供较高的灵活性因此我们采用kubeadm安装kubernetes cluster。
首先参照[kubeadm的说明文档](https://kubernetes.io/docs/setup/independent/install-kubeadm)在计划部署kubernetes cluster的每个节点上安装dockerkubeadm, kubelet 和 kubectl。
安装docker
```
apt-get update
apt-get install -y docker.io
```
使用google的源安装kubelet kubeadm和kubectl
```
apt-get update && apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm kubectl
```
使用kubeadmin安装kubernetes cluster
Nginmesh使用Kubernetes的[Initializer机制](https://kubernetes.io/docs/admin/extensible-admission-controllers/#initializers)来实现sidecar的自动注入。Initializer目前是kubernetes的一个Alpha feature缺省是未启用的需要[通过api server的参数](https://kubernetes.io/docs/admin/extensible-admission-controllers/#enable-initializers-alpha-feature)打开。因此我们先创建一个kubeadm-conf配置文件用于配置api server的启动参数
```
apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
apiServerExtraArgs:
admission-control: Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ValidatingAdmissionWebhook,ResourceQuota,DefaultTolerationSeconds,MutatingAdmissionWebhook
runtime-config: admissionregistration.k8s.io/v1alpha1
```
使用kubeadmin init命令创建kubernetes master节点。
可以先试用--dry-run参数验证一下配置文件。
```
kubeadm init --config kubeadm-conf --dry-run
```
如果一切正常kubeadm将提示Finished dry-running successfully. Above are the resources that would be created.
下面再实际执行创建命令
```
kubeadm init --config kubeadm-conf
```
kubeadm会花一点时间拉取docker image命令完成后会提示如何将一个work node加入cluster。如下所示
```
kubeadm join --token fffbf6.13bcb3563428cf23 10.12.5.15:6443 --discovery-token-ca-cert-hash sha256:27ad08b4cd9f02e522334979deaf09e3fae80507afde63acf88892c8b72f143f
```
> 备注目前kubeadm只能支持在一个节点上安装master支持高可用的安装将在后续版本实现。kubernetes官方给出的workaround建议是定期备份 etcd 数据[kubeadm limitations](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#limitations)。
Kubeadm并不会安装Pod需要的网络因此需要手动安装一个Pod网络这里采用的是Calico
```
kubectl apply -f https://docs.projectcalico.org/v2.6/getting-started/kubernetes/installation/hosted/kubeadm/1.6/calico.yaml
```
使用kubectl 命令检查master节点安装结果
```
ubuntu@kube-1:~$ kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12m
```
在每台工作节点上执行上述kubeadm join命令即可把工作节点加入集群中。使用kubectl 命令检查cluster中的节点情况。
```
ubuntu@kube-1:~$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kube-1 Ready master 21m v1.9.0
kube-2 Ready <none> 47s v1.9.0
```
## 安装Istio控制面和Bookinfo
参考[Nginmesh文档](https://github.com/nginmesh/nginmesh)安装Istio控制面和Bookinfo
该文档的步骤清晰明确,这里不再赘述。
需要注意的是在Niginmesh文档中建议通过Ingress的External IP访问bookinfo应用程序。但[Loadbalancer只在支持的云环境中才会生效](https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer)并且还需要进行一定的配置。如我在Openstack环境中创建的cluster则需要参照[该文档](https://docs.openstack.org/magnum/ocata/dev/kubernetes-load-balancer.html)对Openstack进行配置后Openstack才能够支持kubernetes的Loadbalancer service。如未进行配置通过命令查看Ingress External IP一直显示为pending状态。
```
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingress LoadBalancer 10.111.158.10 <pending> 80:32765/TCP,443:31969/TCP 11m
istio-mixer ClusterIP 10.107.135.31 <none> 9091/TCP,15004/TCP,9093/TCP,9094/TCP,9102/TCP,9125/UDP,42422/TCP 11m
istio-pilot ClusterIP 10.111.110.65 <none> 15003/TCP,443/TCP 11m
```
如不能配置云环境提供Loadbalancer特性, 我们可以直接使用集群中的一个节点IP:Nodeport访问Bookinfo应用程序。
```
http://10.12.5.31:32765/productpage
```
想要了解更多关于如何从集群外部进行访问的内容,可以参考[如何从外部访问Kubernetes集群中的应用](http://zhaohuabing.com/2017/11/28/access-application-from-outside/)
## 查看自动注入的sidecar
使用 kubectl get pod reviews-v3-5fff595d9b-zsb2q -o yaml 命令查看Bookinfo应用的reviews服务的Pod。
```
apiVersion: v1
kind: Pod
metadata:
annotations:
sidecar.istio.io/status: injected-version-0.2.12
creationTimestamp: 2018-01-02T02:33:36Z
generateName: reviews-v3-5fff595d9b-
labels:
app: reviews
pod-template-hash: "1999151856"
version: v3
name: reviews-v3-5fff595d9b-zsb2q
namespace: default
ownerReferences:
- apiVersion: extensions/v1beta1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: reviews-v3-5fff595d9b
uid: 5599688c-ef65-11e7-8be6-fa163e160c7d
resourceVersion: "3757"
selfLink: /api/v1/namespaces/default/pods/reviews-v3-5fff595d9b-zsb2q
uid: 559d8c6f-ef65-11e7-8be6-fa163e160c7d
spec:
containers:
- image: istio/examples-bookinfo-reviews-v3:0.2.3
imagePullPolicy: IfNotPresent
name: reviews
ports:
- containerPort: 9080
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-48vxx
readOnly: true
- args:
- proxy
- sidecar
- -v
- "2"
- --configPath
- /etc/istio/proxy
- --binaryPath
- /usr/local/bin/envoy
- --serviceCluster
- reviews
- --drainDuration
- 45s
- --parentShutdownDuration
- 1m0s
- --discoveryAddress
- istio-pilot.istio-system:15003
- --discoveryRefreshDelay
- 1s
- --zipkinAddress
- zipkin.istio-system:9411
- --connectTimeout
- 10s
- --statsdUdpAddress
- istio-mixer.istio-system:9125
- --proxyAdminPort
- "15000"
- --controlPlaneAuthPolicy
- NONE
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: INSTANCE_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
image: nginmesh/proxy_debug:0.2.12
imagePullPolicy: Always
name: istio-proxy
resources: {}
securityContext:
privileged: true
readOnlyRootFilesystem: false
runAsUser: 1337
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/istio/proxy
name: istio-envoy
- mountPath: /etc/certs/
name: istio-certs
readOnly: true
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-48vxx
readOnly: true
dnsPolicy: ClusterFirst
initContainers:
- args:
- -p
- "15001"
- -u
- "1337"
image: nginmesh/proxy_init:0.2.12
imagePullPolicy: Always
name: istio-init
resources: {}
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-48vxx
readOnly: true
nodeName: kube-2
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- emptyDir:
medium: Memory
name: istio-envoy
- name: istio-certs
secret:
defaultMode: 420
optional: true
secretName: istio.default
- name: default-token-48vxx
secret:
defaultMode: 420
secretName: default-token-48vxx
status:
conditions:
- lastProbeTime: null
lastTransitionTime: 2018-01-02T02:33:54Z
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: 2018-01-02T02:36:06Z
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: 2018-01-02T02:33:36Z
status: "True"
type: PodScheduled
containerStatuses:
- containerID: docker://5d0c189b9dde8e14af4c8065ee5cf007508c0bb2b3c9535598d99dc49f531370
image: nginmesh/proxy_debug:0.2.12
imageID: docker-pullable://nginmesh/proxy_debug@sha256:6275934ea3a1ce5592e728717c4973ac704237b06b78966a1d50de3bc9319c71
lastState: {}
name: istio-proxy
ready: true
restartCount: 0
state:
running:
startedAt: 2018-01-02T02:36:05Z
- containerID: docker://aba3e114ac1aa87c75e969dcc1b0725696de78d3407c5341691d9db579429f28
image: istio/examples-bookinfo-reviews-v3:0.2.3
imageID: docker-pullable://istio/examples-bookinfo-reviews-v3@sha256:6e100e4805a8c10c47040ea7b66f10ad619c7e0068696032546ad3e35ad46570
lastState: {}
name: reviews
ready: true
restartCount: 0
state:
running:
startedAt: 2018-01-02T02:35:47Z
hostIP: 10.12.5.31
initContainerStatuses:
- containerID: docker://b55108625832a3205a265e8b45e5487df10276d5ae35af572ea4f30583933c1f
image: nginmesh/proxy_init:0.2.12
imageID: docker-pullable://nginmesh/proxy_init@sha256:f73b68839f6ac1596d6286ca498e4478b8fcfa834e4884418d23f9f625cbe5f5
lastState: {}
name: istio-init
ready: true
restartCount: 0
state:
terminated:
containerID: docker://b55108625832a3205a265e8b45e5487df10276d5ae35af572ea4f30583933c1f
exitCode: 0
finishedAt: 2018-01-02T02:33:53Z
reason: Completed
startedAt: 2018-01-02T02:33:53Z
phase: Running
podIP: 192.168.79.138
qosClass: BestEffort
startTime: 2018-01-02T02:33:39Z
```
该命令行输出的内容相当长我们可以看到Pod中注入了一个 nginmesh/proxy_debug container,还增加了一个initContainer nginmesh/proxy_init。这两个容器是通过kubernetes initializer自动注入到pod中的。这两个container分别有什么作用呢让我们看一下[Nginmesh源代码中的说明](https://github.com/nginmesh/nginmesh/tree/49cd69a61d7d330685ef39ccd63fac06421c3da2/istio/agent)
* proxy_debug, which comes with the agent and NGINX.
* proxy_init, which is used for configuring iptables rules for transparently injecting an NGINX proxy from the proxy_debug image into an application pod.
proxy_debug就是sidecar代理proxy_init则用于配置iptable 规则以将应用的流量导入到sidecar代理中。
查看proxy_init的Dockerfile文件可以看到proxy_init其实是调用了[prepare_proxy.sh](https://github.com/nginmesh/nginmesh/blob/49cd69a61d7d330685ef39ccd63fac06421c3da2/istio/agent/docker-init/prepare_proxy.sh)这个脚本来创建iptable规则。
proxy_debug Dockerfile
```
FROM debian:stretch-slim
RUN apt-get update && apt-get install -y iptables
ADD prepare_proxy.sh /
ENTRYPOINT ["/prepare_proxy.sh"]
```
prepare_proxy.sh节选
```
...omitted for brevity
# Create a new chain for redirecting inbound and outbound traffic to
# the common Envoy port.
iptables -t nat -N ISTIO_REDIRECT -m comment --comment "istio/redirect-common-chain"
iptables -t nat -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-port ${ENVOY_PORT} -m comment --comment "istio/redirect-to-envoy-port"
# Redirect all inbound traffic to Envoy.
iptables -t nat -A PREROUTING -j ISTIO_REDIRECT -m comment --comment "istio/install-istio-prerouting"
# Create a new chain for selectively redirecting outbound packets to
# Envoy.
iptables -t nat -N ISTIO_OUTPUT -m comment --comment "istio/common-output-chain"
...omitted for brevity
```
## 关联阅读
[Istio及Bookinfo示例程序安装试用笔记](http://zhaohuabing.com/2017/11/04/istio-install_and_example/)
## 参考
* [Service Mesh with Istio and NGINX](https://github.com/nginmesh/nginmesh/)
* [Using kubeadm to Create a Cluster](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#14-installing-kubeadm-on-your-hosts)
* [Kubernetes Reference Documentation-Dynamic Admission Control](https://kubernetes.io/docs/admin/extensible-admission-controllers/#enable-initializers-alpha-feature)

View File

@@ -0,0 +1,200 @@
---
layout: post
title: "如何构建安全的微服务应用?"
subtitle: "微服务架构下的认证和鉴权方案探讨"
description: "微服务架构的引入为软件应用带来了诸多好处:包括小开发团队,缩短开发周期,语言选择灵活性,增强服务伸缩能力等。与此同时,也引入了分布式系统的诸多复杂问题。其中一个挑战就是如何在微服务架构中实现一个灵活,安全,高效的认证和鉴权方案。本文将尝试就此问题进行一次比较完整的探讨。"
excerpt: "微服务架构的引入为软件应用带来了诸多好处:包括小开发团队,缩短开发周期,语言选择灵活性,增强服务伸缩能力等。与此同时,也引入了分布式系统的诸多复杂问题。其中一个挑战就是如何在微服务架构中实现一个灵活,安全,高效的认证和鉴权方案。本文将尝试就此问题进行一次比较完整的探讨。"
date: 2018-02-03 12:00:00
author: "赵化冰"
image: "/img/2018-02-03-authentication-and-authorization-of-microservice/AuthenticationTrack.jpeg"
publishDate: 2018-02-03 12:00:00
tags:
- Microservice
- Security
URL: "/2018/05/22/user_authentication_authorization/"
categories: [ Tech ]
---
## 前言
微服务架构的引入为软件应用带来了诸多好处:包括小开发团队,缩短开发周期,语言选择灵活性,增强服务伸缩能力等。与此同时,也引入了分布式系统的诸多复杂问题。其中一个挑战就是如何在微服务架构中实现一个灵活,安全,高效的认证和鉴权方案。本文将尝试就此问题进行一次比较完整的探讨。
<!--more-->
## 单体应用的实现方式
在单体架构下,整个应用是一个进程,在应用中,一般会用一个安全模块来实现用户认证和鉴权。
用户登录时,应用的安全模块对用户身份进行验证,验证用户身份合法后,为该用户生成一个会话(Session)并为该Session关联一个唯一的编号(Session Id)。Session是应用中的一小块内存结构其中保存了登录用户的信息如User name, Role, Permission等。服务器把该Session的Session Id返回给客户端客户端将Session Id以cookie或者URL重写的方式记录下来并在后续请求中发送给应用这样应用在接收到客户端访问请求时可以使用Session Id验证用户身份不用每次请求时都输入用户名和密码进行身份验证。
> 备注为了避免Session Id被第三者截取和盗用客户端和应用之前应使用TLS加密通信session也会设置有过期时间。
![单体应用用户登录认证序列图](/img/2018-02-03-authentication-and-authorization-of-microservice/monolith-user-login.png)
<center>单体应用用户登录认证序列图</center>
客户端访问应用时Session Id随着HTTP请求发送到应用客户端请求一般会通过一个拦截器处理所有收到的客户端请求。拦截器首先判断Session Id是否存在如果该Session Id存在就知道该用户已经登录。然后再通过查询用户权限判断用户能否执行该此请求以实现操作鉴权。
![单体应用用户操作鉴权序列图](/img/2018-02-03-authentication-and-authorization-of-microservice/monolith-user-request.png)
<center>单体应用用户操作鉴权序列图</center>
## 微服务认证和鉴权面临的问题
在微服务架构下,一个应用被拆分为多个微服务进程,每个微服务实现原来单体应用中一个模块的业务功能。应用拆分后,对每个微服务的访问请求都需要进行认证和鉴权。如果参考单体应用的实现方式会遇到下述问题:
* 认证和鉴权逻辑需要在每个微服务中进行处理,需要在各个微服务中重复实现这部分公共逻辑。虽然我们可以使用代码库复用部分代码,但这又会导致所有微服务对特定代码库及其版本存在依赖,影响微服务语言/框架选择的灵活性。
* 微服务应遵循单一职责原理,一个微服务只处理单一的业务逻辑。认证和鉴权的公共逻辑不应该放到微服务实现中。
* 为了充分利用微服务架构的好处,实现微服务的水平扩展(Scalability)和弹性(Resiliency),微服务最好是无状态的。因此不建议使用session这种有状态的方案。
* 微服务架构下的认证和鉴权涉及到场景更为复杂,涉及到用户访问微服务应用,第三方应用访问微服务应用,应用内多个微服务之间相互访问等多种场景,每种场景下的认证和鉴权方案都需要考虑到,以保证应用程序的安全性。
![微服务认证和鉴权涉及到的三种场景](/img/2018-02-03-authentication-and-authorization-of-microservice/auth-scenarios.png)
<center>微服务认证和鉴权涉及到的三种场景</center>
## 微服务认证和鉴权的技术方案
### 用户身份认证
一个完整的微服务应用是由多个相互独立的微服务进程组成的对每个微服务的访问都需要进行用户认证。如果将用户认证的工作放到每个微服务中应用的认证逻辑将会非常复杂。因此需要考虑一个SSO单点登录的方案即用户只需要登录一次就可以访问所有微服务提供的服务。 由于在微服务架构中以API Gateway作为对外提供服务的入口因此可以考虑在API Gateway处提供统一的用户认证。
### 用户状态保持
HTTP是一个无状态的协议对服务器来说用户的每次HTTP请求是相互独立的。互联网是一个巨大的分布式系统HTTP协议作为互联网上的一个重要协议要考虑到大量应用访问的效率问题。无状态意味着服务端可以把客户端的请求根据需要发送到集群中的任何一个节点HTTP的无状态设计对负载均衡有明显的好处由于没有状态用户请求可以被分发到任意一个服务器应用也可以在靠近用户的网络边缘部署缓存服务器。对于不需要身份认证的服务例如浏览新闻网页等这是没有任何问题的。但很多服务如网络购物企业管理系统等都需要对用户的身份进行认证因此需要在HTTP协议基础上采用一种方式保存用户的登录状态避免用户每发起一次请求都需要进行验证。
传统方式是在服务器端采用Cookie来保存用户状态由于在服务器是有状态的对服务器的水平扩展有影响。在微服务架构下建议采用Token来记录用户登录状态。
Token和Seesion主要的不同点是存储的地方不同。Session是集中存储在服务器中的而Token是用户自己持有的一般以cookie的形式存储在浏览器中。Token中保存了用户的身份信息每次请求都会发送给服务器服务器因此可以判断访问者的身份并判断其对请求的资源有没有访问权限。
Token用于表明用户身份因此需要对其内容进行加密避免被请求方或者第三者篡改。[JWT(Json Web Token)](https://jwt.io)是一个定义Token格式的开放标准(RFC 7519),定义了Token的内容加密方式并提供了各种语言的lib。
JWT Token的结构非常简单包括三部分
* Header<BR>
头部包含类型,为固定值JWT。然后是JWT使用的Hash算法。
```
{
"alg": "HS256",
"typ": "JWT"
}
```
* Payload<BR>
包含发布者,过期时间,用户名等标准信息,也可以添加用户角色,用户自定义的信息。
```
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
```
* Signature<BR>
Token颁发方的签名用于客户端验证Token颁发方的身份也用于服务器防止Token被篡改。
签名算法
```
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
```
这三部分使用Base64编码后组合在一起成为最终返回给客户端的Token串每部分之间采用"."分隔。下图是上面例子最终形成的Token
![](https://cdn.auth0.com/content/jwt/encoded-jwt3.png)
采用Token进行用户认证服务器端不再保存用户状态客户端每次请求时都需要将Token发送到服务器端进行身份验证。Token发送的方式[rfc6750](https://tools.ietf.org/html/rfc6750)进行了规定,采用一个 Authorization: Bearer HHTP Header进行发送。
```
Authorization: Bearer mF_9.B5f-4.1JqM
```
采用Token方式进行用户认证的基本流程如下图所示
1. 用户输入用户名,密码等验证信息,向服务器发起登录请求
1. 服务器端验证用户登录信息生成JWT token
1. 服务器端将Token返回给客户端客户端保存在本地一般以Cookie的方式保存
1. 客户端向服务器端发送访问请求请求中携带之前颁发的Token
1. 服务器端验证Token确认用户的身份和对资源的访问权限并进行相应的处理拒绝或者允许访问
![](https://cdn.auth0.com/content/jwt/jwt-diagram.png)
<center>采用Token进行用户认证的流程图</center>
### 实现单点登录
单点登录的理念很简单即用户只需要登录应用一次就可以访问应用中所有的微服务。API Gateway提供了客户端访问微服务应用的入口Token实现了无状态的用户认证。结合这两种技术可以为微服务应用实现一个单点登录方案。
用户的认证流程和采用Token方式认证的基本流程类似不同之处是加入了API Gateway作为外部请求的入口。
用户登录
1. 客户端发送登录请求到API Gateway
2. API Gateway将登录请求转发到Security Service
3. Security Service验证用户身份并颁发Token
用户请求
1. 客户端请求发送到API Gateway
1. API Gateway调用的Security Service对请求中的Token进行验证检查用户的身份
2. 如果请求中没有TokenToken过期或者Token验证非法则拒绝用户请求。
3. Security Service检查用户是否具有该操作权
4. 如果用户具有该操作权限则把请求发送到后端的Business Service否则拒绝用户请求
![采用API Gateway实现微服务应用的SSO](/img/2018-02-03-authentication-and-authorization-of-microservice/api-gateway-sso.png)
<center>采用API Gateway和Token实现微服务应用的单点登录</center>
### 用户权限控制
用户权限控制有两种做法在API Gateway处统一处理或者在各个微服务中单独处理。
#### API Gateway处进行统一的权限控制
客户端发送的HTTP请求中包含有请求的Resource及HTTP Method。如果系统遵循REST规范以URI资源方式对访问对象进行建模则API Gateway可以从请求中直接截取到访问的资源及需要进行的操作然后调用Security Service进行权限判断根据判断结果决定用户是否有权限对该资源进行操作并转发到后端的Business Service。这种实现方式API Gateway处统一处理鉴权逻辑各个微服务不需要考虑用户鉴权只需要处理业务逻辑简化了各微服务的实现。
#### 由各个微服务单独进行权限控制
如果微服务未严格遵循REST规范对访问对象进行建模或者应用需要进行定制化的权限控制则需要在微服务中单独对用户权限进行判断和处理。这种情况下微服务的权限控制更为灵活但各个微服务需要单独维护用户的授权数据实现更复杂一些。
### 第三方应用接入
对于第三方应用接入的访问控制,有两种实现方式:
#### API Token
第三方使用一个应用颁发的API Token对应用的数据进行访问。该Token由用户在应用中生成并提供给第三方应用使用。在这种情况下一般只允许第三方应用访问该Token所属用户自身的数据而不能访问其他用户的敏感私有数据。
例如Github就提供了Personal API Token功能用户可以在[Github的开发者设置界面](https://github.com/settings/tokens)中创建Token然后使用该Token来访问Github的API。在创建Token时可以设置该Token可以访问用户的哪些数据如查看Repo信息删除Repo查看用户信息更新用户信息等。
使用API Token来访问Github API
```
curl -u zhaohuabing:fbdf8e8862252ed0f3ba9dba4e328c01ac93aeec https://api.github.com/user
```
使用API Token而不是直接使用用户名/密码来访问API的好处是降低了用户密码暴露的风险并且可以随时收回Token的权限而不用修改密码。
由于API Token只能访问指定用户的数据因此适合于用户自己开发一些脚本或小程序对应用中自己的数据进行操作。
#### OAuth
某些第三方应用需要访问不同用户的数据或者对多个用户的数据进行整合处理则可以考虑采用OAuth。采用OAuth当第三方应用访问服务时应用会提示用户授权第三方应用相应的访问权限根据用户的授权操作结果生成用于访问的Token以对第三方应用的操作请求进行访问控制。
同样以Github为例一些第三方应用如Travis CIGitBook等就是通过OAuth和Github进行集成的。
OAuth针对不同场景有不同的认证流程一个典型的认证流程如下图所示
* 用户向OAuth客户端程序发起一个请求OAuth客户端程序在处理该请求时发现需要访问用户在资源服务器中的数据。
* 客户端程序将用户请求重定向到认证服务器该请求中包含一个callback的URL。
* 认证服务器返回授权页面要求用户对OAuth客户端的资源请求进行授权。
* 用户对该操作进行授权后认证服务器将请求重定向到客户端程序的callback url将授权码返回给客户端程序。
* 客户端程序将授权码发送给认证服务器请求token。
* 认证服务器验证授权码后将token颁发给客户端程序。
* 客户端程序采用颁发的token访问资源完成用户请求。
>备注:
>1. OAuth中按照功能区分了资源服务器和认证服务器这两个角色在实现时这两个角色常常是同一个应用。将该流程图中的各个角色对应到Github的例子中资源服务器和认证服务器都是Github客户端程序是Travis CI或者GitBook用户则是使用Travis CI或者GitBook的直接用户。
>
>2. 有人可能会疑惑在该流程中为何要使用一个授权码(Authorization Code)来申请Token而不是由认证服务器直接返回Token给客户端。OAuth这样设计的原因是在重定向到客户端Callback URL的过程中会经过用户代理浏览器如果直接传递Token存在被窃取的风险。采用授权码的方式申请Token时客户端直接和认证服务器进行交互并且认证服务期在处理客户端的Token申请请求时还会对客户端进行身份认证避免其他人伪造客户端身份来使用认证码申请Token。
>下面是一个客户端程序采用Authorization Code来申请Token的示例client_id和client_secret被用来验证客户端的身份。
>
>```
>POST /oauth/token HTTP/1.1
>Host: authorization-server.com
>
>grant_type=authorization_code
>&code=xxxxxxxxxxx
>&redirect_uri=https://example-app.com/redirect
>&client_id=xxxxxxxxxx
>&client_secret=xxxxxxxxxx
>```
![OAuth认证流程](/img/2018-02-03-authentication-and-authorization-of-microservice/oauth_web_server_flow.png)
<center>OAuth认证流程</center>
另外在谈及OAuth时我们需要注意微服务应用作为OAuth客户端和OAuth服务器的两种不同场景:
在实现微服务自身的用户认证时也可以采用OAuth将微服务的用户认证委托给一个第三方的认证服务提供商例如很多应用都将用户登录和微信或者QQ的OAuth服务进行了集成。
第三方应用接入和微服务自身用户认证采用OAuth的目的是不同的前者是为了将微服务中用户的私有数据访问权限授权给第三方应用微服务在OAuth架构中是认证和资源服务器的角色而后者的目的是集成并利用知名认证提供服务商提供的OAuth认证服务简化繁琐的注册操作微服务在OAuth架构中是客户端的角色。
因此在我们需要区分这两种不同的场景,以免造成误解。
### 微服务之间的认证
除了来自用户和第三方的北向流量外,微服务之间还有大量的东西向流量,这些流量可能在同一个局域网中,也可能跨越不同的数据中心,这些服务间的流量存在被第三方的嗅探和攻击的危险,因此也需要进行安全控制。
通过双向SSL可以实现服务之间的相互身份认证并通过TLS加密服务间的数据传输。需要为每个服务生成一个证书服务之间通过彼此的证书进行身份验证。在微服务运行环境中可能存在大量的微服务实例并且微服务实例经常会动态变化例如随着水平扩展增加服务实例。在这种情况下为每个服务创建并分发证书变得非常困难。我们可以通过创建一个私有的证书中心(Internal PKI/CA)来为各个微服务提供证书管理如颁发、撤销、更新等。
## 参考
* [How We Solved Authentication and Authorization in Our Microservice Architecture](https://initiate.andela.com/how-we-solved-authentication-and-authorization-in-our-microservice-architecture-994539d1b6e6)
* [How to build your own public key infrastructure](https://blog.cloudflare.com/how-to-build-your-own-public-key-infrastructure/)
* [OAuth 2.0 Authorization Code Request](https://www.oauth.com/oauth2-servers/access-tokens/authorization-code-request/)
* [PKI/CA工作原理及架构](https://www.jianshu.com/p/c65fa3af1c01)
* [深入聊聊微服务架构的身份认证问题](http://www.primeton.com/read.php?id=2390)

View File

@@ -0,0 +1,27 @@
---
layout: post
title: "如何使用非root用户执行docker命令"
subtitle: ""
description: "如何使用非root用户执行docker命令"
excerpt: "如何使用非root用户执行docker命令"
date: 2018-02-09 10:00:00
author: "赵化冰"
image: "/img/docker.jpg"
publishDate: 2018-02-09 10:00:00
showtoc: false
tags:
- Tips
- Docker
URL: "/2018/02/09/docker-without-sudo/"
categories: [ Tips ]
---
### Add the docker group if it doesn't already exist:
sudo groupadd docker
### Add the connected user "$USER" to the docker group. Change the user name to match your preferred user if you do not want to use your current user:
sudo gpasswd -a $USER docker
### Either do a newgrp docker or log out/in to activate the changes to groups.

View File

@@ -0,0 +1,66 @@
---
layout: post
title: "Vim Tips"
subtitle: ""
description: "Vim Tips and tricks"
date: 2018-02-09 11:00:00
author: "赵化冰"
image: "/img/2018-02-09-vim-tips/matrix.jpg"
publishDate: 2018-02-09 11:00:00
tags:
- Tips
- Vim
URL: "/2018/02/09/vim-tips/"
categories: [ Tips ]
---
## vim graphical cheat sheet
![](//img/2018-02-09-vim-tips/vi-vim-cheat-sheet.svg)
<!--more-->
## Vim Jumps
* ^ — Move to start of line
* $ — Move to end of line
* b — Move back a word
* w — Move forward a word
* e — Move to the end of the next word
* Ctrl-o and Ctrl-i to go to the previous/next location you jumped to
* ``(two backticks) jump back to where you were
* gi go back to the last place you inserted a text and enter insert mode
## Vim Navigations
* { and } jump paragraph back and forth
* Ctrl-F/B move one screen back and forth
* Search the word under cursor, then n/p to jump to next/previous
## Enable Vim mode in bash
vi ~/.inputrc
set editing-mode vi
## Enable system clipboard support
See if system clipboard is supported:
```
$ vim --version | grep clipboard
-clipboard +iconv +path_extra -toolbar
+eval +mouse_dec +startuptime -xterm_clipboard
```
Rinstall vim as vim-gnome:
```
sudo apt-get install vim-gnome
```
Select what you want using the mouse - then type to copy to clipboard:
```
"+y
```
To paste to vim from clipboard type:
```
"+p
```
## Others
* Ex: open the current directory
* set number: show line number

View File

@@ -0,0 +1,46 @@
---
layout: post
title: "如何配置docker使用HTTP代理"
subtitle: ""
description: "如何配置docker使用HTTP代理"
date: 2018-03-13 18:00:00
author: "赵化冰"
image: "/img/docker.jpg"
publishDate: 2018-03-13 18:00:00
tags:
- Tips
- Docker
URL: "/2018/03/13/use-docker-behind-http-proxy/"
categories: [ Tips ]
---
## Ubuntu
### 设置docker使用http proxy
```
sudo /etc/default/docker
export http_proxy="http://127.0.0.1:3128/"
export https_proxy="http://127.0.0.1:3128/"
export HTTP_PROXY="http://127.0.0.1:3128/"
export HTTPS_PROXY="http://127.0.0.1:3128/"
```
<!--more-->
### 加载配置并重启docker
```
sudo service docker restart
```
## CentOS
### 设置docker使用http proxy
```
sudo mkdir -p /etc/systemd/system/docker.service.d
echo '
[Service]
Environment="HTTP_PROXY=http://proxy.foo.bar.com:80/"
' | sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf
```
### 加载配置并重启docker
```
sudo systemctl daemon-reload
sudo systemctl restart docker
```

View File

@@ -0,0 +1,251 @@
---
layout: post
title: "谈谈微服务架构中的基础设施Service Mesh与Istio"
subtitle: "Service Mesh模式及Istio开源项目介绍"
description: "作为一种架构模式,微服务将复杂系统切分为数十乃至上百个小服务,每个服务负责实现一个独立的业务逻辑。这些小服务易于被小型的软件工程师团队所理解和修改,并带来了语言和框架选择灵活性,缩短应用开发上线时间,可根据不同的工作负载和资源要求对服务进行独立缩扩容等优势。另一方面,当应用被拆分为多个微服务进程后,进程内的方法调用变成了了进程间的远程调用。引入了对大量服务的连接、管理和监控的复杂性,本文介绍了Service Mesh模式如何应对微服务架构的这些挑战以及Service Mesh的明星开源项目Istio。"
date: 2018-03-29 12:00:00
author: "赵化冰"
image: "/img/istio-install_and_example/post-bg.jpg"
publishDate: 2018-03-29 12:00:00
tags:
- Microservice
- Service Mesh
- Istio
URL: "/2018/03/29/what-is-service-mesh-and-istio/"
categories: [ Tech ]
---
## 微服务架构的演进
作为一种架构模式,微服务将复杂系统切分为数十乃至上百个小服务,每个服务负责实现一个独立的业务逻辑。这些小服务易于被小型的软件工程师团队所理解和修改,并带来了语言和框架选择灵活性,缩短应用开发上线时间,可根据不同的工作负载和资源要求对服务进行独立缩扩容等优势。
另一方面,当应用被拆分为多个微服务进程后,进程内的方法调用变成了了进程间的远程调用。引入了对大量服务的连接、管理和监控的复杂性。
<!--more-->
![](/img/2018-03-29-what-is-service-mesh-and-istio/microservice.PNG)
该变化带来了分布式系统的一系列问题,例如:
* 如何找到服务的提供方?
* 如何保证远程方法调用的可靠性?
* 如何保证服务调用的安全性?
* 如何降低服务调用的延迟?
* 如何进行端到端的调试?
另外生产部署中的微服务实例也增加了运维的难度,例如:
* 如何收集大量微服务的性能指标已进行分析?
* 如何在不影响上线业务的情况下对微服务进行升级?
* 如何测试一个微服务集群部署的容错和稳定性?
这些问题涉及到成百上千个服务的通信、管理、部署、版本、安全、故障转移、策略执行、遥测和监控等,要解决这些微服务架构引入的问题并非易事。
让我们来回顾一下微服务架构的发展过程。在出现服务网格之前,我们最开始在微服务应用程序内理服务之间的通讯逻辑,包括服务发现,熔断,重试,超时,加密,限流等逻辑。
![](/img/2018-03-29-what-is-service-mesh-and-istio/1.png)
在一个分布式系统中,这部分逻辑比较复杂,为了为微服务应用提供一个稳定、可靠的基础设施层,避免大家重复造轮子,并减少犯错的可能,一般会通过对这部分负责服务通讯的逻辑进行抽象和归纳,形成一个代码库供各个微服务应用程序使用,如下图所示:
![](/img/2018-03-29-what-is-service-mesh-and-istio/2.png)
公共的代码库减少了应用程序的开发和维护工作量,降低了由应用开发人员单独实现微服务通讯逻辑出现错误的机率,但还是存在下述问题:
* 微服务通讯逻辑对应用开发人员并不透明,应用开发人员需要理解并正确使用代码 库,不能将其全部精力聚焦于业务逻辑。
* 需要针对不同的语言/框架开发不同的代码库,反过来会影响微服务应用开发语言 和框架的选择,影响技术选择的灵活性。
* 随着时间的变化,代码库会存在不同的版本,不同版本代码库的兼容性和大量运行 环境中微服务的升级将成为一个难题。
可以将微服务之间的通讯基础设施层和TCP/IP协议栈进行类比。TCP/IP协议栈为操作系统中的所有应用提供基础通信服务但TCP/IP协议栈和应用程序之间并没有紧密的耦合关系应用只需要使用TCP/IP协议提供的底层通讯功能,并不关心TCP/IP协议的实现如IP如何进行路由TCP如何创建链接等。
同样地微服务应用也不应该需要关注服务发现Load balancingRetriesCircuit Breaker等微服务之间通信的底层细节。如果将为微服务提供通信服务的这部分逻辑从应用程序进程中抽取出来作为一个单独的进程进行部署并将其作为服务间的通信代理可以得到如下图所示的架构
![](/img/2018-03-29-what-is-service-mesh-and-istio/sidecar.png)
因为通讯代理进程伴随应用进程一起部署因此形象地把这种部署方式称为“sidecar”/边车(即三轮摩托的挎斗)。
应用间的所有流量都需要经过代理由于代理以sidecar方式和应用部署在同一台主机上应用和代理之间的通讯可以被认为是可靠的。由代理来负责找到目的服务并负责通讯的可靠性和安全等问题。
当服务大量部署时随着服务部署的sidecar代理之间的连接形成了一个如下图所示的网格该网格成为了微服务的通讯基础设施层承载了微服务之间的所有流量被称之为Service Mesh服务网格
![](/img/2018-03-29-what-is-service-mesh-and-istio/mesh.png)
_服务网格是一个基础设施层用于处理服务间通信。云原生应用有着复杂的服务拓扑服务网格保证请求可以在这些拓扑中可靠地穿梭。在实际应用当中服务网格通常是由一系列轻量级的网络代理组成的它们与应用程序部署在一起但应用程序不需要知道它们的存在。
_William Morgan _[_WHATS A SERVICE MESH? AND WHY DO I NEED ONE?_
](https://buoyant.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/)_
服务网格中有数量众多的Sidecar代理如果对每个代理分别进行设置工作量将非常巨大。为了更方便地对服务网格中的代理进行统一集中控制在服务网格上增加了控制面组件。
![](/img/2018-03-29-what-is-service-mesh-and-istio/controlplane.png)
这里我们可以类比SDN的概念控制面就类似于SDN网管中的控制器负责路由策略的指定和路由规则下发数据面类似于SDN网络中交换机负责数据包的转发。
由于微服务的所有通讯都由服务网格基础设施层提供,通过控制面板和数据面板的配合,可以对这些通讯进行监控、托管和控制,以实现微服务灰度发布,调用分布式追踪,故障注入模拟测试,动态路由规则,微服务闭环控制等管控功能。
## Istio服务网格
Istio是一个Service Mesh开源项目是Google继Kubernetes之后的又一力作主要参与的公司包括GoogleIBM和Lyft。
凭借kubernetes良好的架构设计及其强大的扩展性Google围绕kubernetes打造一个生态系统。Kubernetes用于微服务的编排编排是英文Orchestration的直译用大白话说就是描述一组微服务之间的关联关系并负责微服务的部署、终止、升级、缩扩容等。其向下用CNI(容器网络接口CRI容器运行时接口标准接口可以对接不同的网络和容器运行时实现提供微服务运行的基础设施。向上则用Istio提供了微服务治理功能。
由下图可见Istio补充了Kubernetes生态圈的重要一环是Google的微服务版图里一个里程碑式的扩张。
![](/img/2018-03-29-what-is-service-mesh-and-istio/k8s-ecosystem.PNG)
Google借Istio的力量推动微服务治理的事实标准对Google自身的产品Google Cloud有极其重大的意义。其他的云服务厂商如RedhatPivotalNginxBuoyant等看到大势所趋也纷纷跟进宣布自身产品和Istio进行集成以避免自己被落下丢失其中的市场机会。
可以预见不久的将来对于云原生应用而言采用kubernetes进行服务部署和集群管理采用Istio处理服务通讯和治理将成为微服务应用的标准配置。
Istio服务包括网格由数据面和控制面两部分。
* 数据面由一组智能代理Envoy组成代理部署为边车调解和控制微服务之间所有的网络通信。
* 控制面负责管理和配置代理来路由流量,以及在运行时执行策略。
![](/img/2018-03-29-what-is-service-mesh-and-istio/istio-architecture.png)
### Istio控制面
Istio控制面板包括3个组件:Pilot, Mixer和Istio-Auth。
#### Pilot
Pilot维护了网格中的服务的标准模型这个标准模型是独立于各种底层平台的。Pilot通过适配器和各底层平台对接以填充此标准模型。
例如Pilot中的Kubernetes适配器通过Kubernetes API服务器得到kubernetes中pod注册信息的更改入口资源以及存储流量管理规则等信息然后将该数据被翻译为标准模型提供给Pilot使用。通过适配器模式Pilot还可以从Mesos, Cloud Foundry, Consul中获取服务信息也可以开发适配器将其他提供服务发现的组件集成到Pilot中。
除此以外Pilo还定义了一套和数据面通信的标准APIAPI提供的接口内容包括服务发现 、负载均衡池和路由表的动态更新。通过该标准API将控制面和数据面进行了解耦简化了设计并提升了跨平台的可移植性。基于该标准API已经实现了多种Sidecar代理和Istio的集成除Istio目前集成的Envoy外还可以和Linkerd, Nginmesh等第三方通信代理进行集成也可以基于该API自己编写Sidecar实现。
Pilot还定义了一套DSLDomain Specific Language语言DSL语言提供了面向业务的高层抽象可以被运维人员理解和使用。运维人员使用该DSL定义流量规则并下发到Pilot这些规则被Pilot翻译成数据面的配置再通过标准API分发到Envoy实例可以在运行期对微服务的流量进行控制和调整。
![](/img/2018-03-29-what-is-service-mesh-and-istio/pilot.png)
#### Mixer
在微服务应用中通常需要部署一些基础的后端公共服务以用于支撑业务功能。这些基础设施包括策略类如访问控制配额管理以及遥测报告如APM日志等。微服务应用和这些后端支撑系统之间一般是直接集成的这导致了应用和基础设置之间的紧密耦合如果因为运维原因需要对基础设置进行升级或者改动则需要修改各个微服务的应用代码反之亦然。
为了解决该问题Mixer在应用程序代码和基础架构后端之间引入了一个通用中间层。该中间层解耦了应用和后端基础设施应用程序代码不再将应用程序代码与特定后端集成在一起而是与Mixer进行相当简单的集成然后Mixer负责与后端系统连接。
Mixer主要提供了三个核心功能
* 前提条件检查。允许服务在响应来自服务消费者的传入请求之前验证一些前提条件。前提条件可以包括服务使用者是否被正确认证是否在服务的白名单上是否通过ACL检查等等。
* 配额管理。 使服务能够在分配和释放多个维度上的配额配额这一简单的资源管理工具可以在服务消费者对有限资源发生争用时提供相对公平的竞争手段。Rate Limiting就是配额的一个例子。
* 遥测报告。使服务能够上报日志和监控。在未来,它还将启用针对服务运营商以及服务消费者的跟踪和计费流。
Mixer的架构如图所示:
![](/img/2018-03-29-what-is-service-mesh-and-istio/mixer2.png)
首先Sidecar会从每一次请求中收集相关信息如请求的路径时间源IP目地服务tracing头日志等并请这些属性上报给Mixer。Mixer和后端服务之间是通过适配器进行连接的Mixer将Sidecar上报的内容通过适配器发送给后端服务。
由于Sidecar只和Mixer进行对接和后端服务之间并没有耦合因此使用Mixer适配器机制可以接入不同的后端服务而不需要修改应用的代码例如通过不同的Mixer适配器可以把Metrics收集到Prometheus或者InfluxDB甚至可以在不停止应用服务的情况下动态切换后台服务。
其次Sidecar在进行每次请求处理时会通过Mixer进行策略判断并根据Mixer返回的结果决定是否继续处理该次调用。通过该方式Mixer将策略决策移出应用层使运维人员可以在运行期对策略进行配置动态控制应用的行为提高了策略控制的灵活性。例如可以配置每个微服务应用的访问白名单不同客户端的Rate limiting等等。
逻辑上微服务之间的每一次请求调用都会经过两次Mixer的处理调用前进行策略判断调用后进行遥测数据收集。Istio采用了一些机制来避免Mixer的处理影响Envoy的转发效率。
从上图可以看到Istio在Envoy中增加了一个Mixer Filter该Filter和控制面的Mixer组件进行通讯完成策略控制和遥测数据收集功能。Mixer Filter中保存有策略判断所需的数据缓存因此大部分策略判断在Envoy中就处理了不需要发送请求到Mixer。另外Envoy收集到的遥测数据会先保存在Envoy的缓存中每隔一段时间再通过批量的方式上报到Mixer。
#### Auth
Istio支持双向SSL认证Mutual SSL Authentication和基于角色的访问控制RBAC以提供端到端的安全解决方案。
##### 认证
Istio提供了一个内部的CA(证书机构),该CA为每个服务颁发证书提供服务间访问的双向SSL身份认证并进行通信加密其架构如下图所示
![](/img/2018-03-29-what-is-service-mesh-and-istio/auth.png)
其工作机制如下:
部署时:
* CA监听Kubernetes API Server, 为集群中的每一个Service Account创建一对密钥和证书并发送给Kubernetes API Server。注意这里不是为每个服务生成一个证书而是为每个Service Account生成一个证书。Service Account和kubernetes中部署的服务可以是一对多的关系。Service Account被保存在证书的SAN(Subject Alternative Name)字段中。
* 当Pod创建时Kubernetes根据该Pod关联的Service Account将密钥和证书以Kubernetes Secrets资源的方式加载为Pod的Volume以供Envoy使用。
* Pilot生成数据面的配置包括Envoy需使用的密钥和证书信息以及哪个Service Account可以允许运行哪些服务下发到Envoy。
>备注如果是虚机环境则采用一个Node Agent生成密钥向Istio CA申请证书然后将证书传递给Envoy。
运行时:
* 服务客户端的出站请求被Envoy接管。
* 客户端的Envoy和服务端的Envoy开始双向SSL握手。在握手阶段客户端Envoy会验证服务端Envoy证书中的Service Account有没有权限运行该请求的服务如没有权限则认为服务端不可信不能创建链接。
* 当加密TSL链接创建好后请求数据被发送到服务端的Envoy然后被Envoy通过一个本地的TCP链接发送到服务中。
##### 鉴权
Istio“基于角色的访问控制”RBAC提供了命名空间服务方法三个不同大小粒度的服务访问权限控制。其架构如下图所示
![](/img/2018-03-29-what-is-service-mesh-and-istio/authorization.png)
管理人员可以定制访问控制的安全策略这些安全策略保存在Istio Config Store中。 Istio RBAC Engine从Config Store中获取安全策略根据安全策略对客户端发起的请求进行判断并返回鉴权结果允许或者禁止
Istio RBAC Engine目前被实现为一个Mixer Adapter因此其可以从Mixer传递过来的上下文中获取到访问请求者的身份Subject和操作请求Action并通过Mixer对访问请求进行策略控制允许或者禁止某一次请求。
Istio Policy中包含两个基本概念
* ServiceRole定义一个角色并为该角色指定对网格中服务的访问权限。指定角色访问权限时可以在命名空间服务方法的不同粒度进行设置。
* ServiceRoleBinding将角色绑定到一个Subject可以是一个用户一组用户或者一个服务。
### Istio数据面
Istio数据面以“边车”(sidecar)的方式和微服务一起部署为微服务提供安全、快速、可靠的服务间通讯。由于Istio的控制面和数据面以标准接口进行交互因此数据可以有多种实现Istio缺省使用了Envoy代理的扩展版本。
Envoy是以C ++开发的高性能代理用于调解服务网格中所有服务的所有入站和出站流量。Envoy的许多内置功能被Istio发扬光大例如动态服务发现负载均衡TLS加密HTTP/2 & gRPC代理熔断器路由规则故障注入和遥测等。
Istio数据面支持的特性如下
| Outbound特性 | Inbound特性 |
|--------|--------|
| Service authentication服务认证|Service authentication服务认证|
|Load Balancing负载均衡 |Authorization鉴权|
|Retry and circuit breaker重试和断路器|Rate limits请求限流|
|Fine-grained routing细粒度的路由|Load shedding负载控制|
|Telemetry遥测|Telemetry遥测|
|Request Tracing分布式追踪|Request Tracing分布式追踪|
|Fault Injection故障注入|Fault Injection故障注入|
>备注Outbound特性是指服务请求侧的Sidecar提供的功能特性而Inbound特性是指服务提供侧Sidecar提供的功能特性。一些特性如遥测和分布式跟踪需要两侧的Sidecar都提供支持而另一些特性则只需要在一侧提供例如鉴权只需要在服务提供侧提供重试只需要在请求侧提供。
### 典型应用场景
Istio服务管控包括下列的典型应用场景
#### 分布式调用追踪
在微服务架构中,业务的调用链非常复杂,一个来自用户的请求可能涉及到几十个服务的协同处理。因此需要一个跟踪系统来记录和分析同一次请求在整个调用链上的相关事件,从而帮助研发和运维人员分析系统瓶颈,快速定位异常和优化调用链路。
Istio通过在Envoy代理上收集调用相关数据实现了对应用无侵入的分布式调用跟踪分析。 Istio实现分布式调用追踪的原理如下图所示:
![](/img/2018-03-29-what-is-service-mesh-and-istio/distributed-tracing.png)
Envoy收集一个端到端调用中的各个分段的数据并将这些调用追踪信息发送给MixerMixer Adapter 将追踪信息发送给相应的服务后端进行处理。整个调用追踪信息的生成流程不需要应用程序介入,因此不需要将分布式跟踪相关代码注入到应用程序中。
>注意应用仍需要在进行出口调用时将收到的入口请求中tracing相关的header转发出去传递给调用链中下一个边车进行处理。
#### 度量收集
Istio 实现度量收集的原理如下图所示:
![](/img/2018-03-29-what-is-service-mesh-and-istio/metrics-collecting.png)
Envoy收集指标相关的原始数据如请求的服务HTTP状态码调用时延等这些收集到的指标数据被送到Mixer通过Mixer Adapters 将指标信息转换后发送到后端的监控系统中。由于Mixer使用了插件机制后端监控系统可以根据需要在运行期进行动态切换。
#### 灰度发布
当应用上线以后,运维面临的一大挑战是如何能够在不影响已上线业务的情况下进行升级。无论进行了多么完善的测试,都无法保证线下测试时发现所有潜在故障。在无法百分百避免版本升级故障的情况下,需要通过一种方式进行可控的版本发布,把故障影响控制在可以接受的范围内,并可以快速回退。
可以通过灰度发布(又名金丝雀发布)来实现业务从老版本到新版本的平滑过渡,并避免升级过程中出现的问题对用户造成的影响。
Istio通过高度的抽象和良好的设计采用一致的方式实现了灰度发布。在发布新版本后运维人员可以通过定制路由规则将特定的流量如具有指定特征的测试用户导入新版本服务中以进行测试。通过渐进受控地向新版本导入生产流量可以最小化升级中出现的故障对用户的影响。
采用Istio进行灰度发布的流程如下图所示
首先,通过部署新版本的服务,并将通过路由规则将金丝雀用户的流量导入到新版本服务中
![](/img/2018-03-29-what-is-service-mesh-and-istio/canary-1.png)
测试稳定后使用路由规则将生产流量逐渐导入到新版本系统中如按5%10%50%80%逐渐导入。
![](/img/2018-03-29-what-is-service-mesh-and-istio/canary-2.png)
如果新版本工作正常,则最后将所有流量导入到新版本服务中,并将老版本服务下线;如中间出现问题,则可以将流量重新导回老版本,在新版本中修复故障后采用该流程重新发布。
![](/img/2018-03-29-what-is-service-mesh-and-istio/canary-3.png)
#### 断路器
在微服务架构中,存在着许许多多的服务单元,若一个服务出现故障,就会因依赖关系形成故障蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构就更加的不稳定。为了解决这样的问题,因此产生了断路器模式。
断路器模式指,在某个服务发生故障时,断路器的故障监控向调用放返回一个及时的错误响应,而不是长时间的等待。这样就不会使得调用线程因调用故障被长时间占用,从而避免了故障在整个系统中的蔓延。
Istio 实现断路器的原理如下图所示:
![](/img/2018-03-29-what-is-service-mesh-and-istio/circuitbreaker.png)
管理员通过destination policy设置断路触发条件断路时间等参数。例如设置服务B发生10次5XX错误后断路15分钟。则当服务B的某一实例满足断路条件后就会被从LB池中移除15分钟。在这段时间内Envoy将不再把客户端的请求转发到该服务实例。
Istio的断路器还支持配置最大链接数最大待处理请求数最大请求数每链接最大请求数重试次数等参数。当达到设置的最大请求数后新发起的请求会被Envoy直接拒绝。
![](/img/2018-03-29-what-is-service-mesh-and-istio/circuitbreaker-parameters.png)
#### 故障注入
对于一个大型微服务应用而言,系统的健壮性非常重要。在微服务系统中存在大量的服务实例,当部分服务实例出现问题时,微服务应用需要具有较高的容错性,通过重试,断路,自愈等手段保证系统能够继续对外正常提供服务。因此在应用发布到生产系统强需要对系统进行充分的健壮性测试。
对微服务应用进行健壮性测试的一个最大的困难是如何对系统故障进行模拟。在一个部署了成百上千微服务的测试环境中,如果想通过对应用,主机或者交换机进行设置来模拟微服务之间的通信故障是非常困难的。
Istio通过服务网格承载了微服务之间的通信流量因此可以在网格中通过规则进行故障注入模拟部分微服务出现故障的情况对整个应用的健壮性进行测试。
故障注入的原理如下图所示:
![](/img/2018-03-29-what-is-service-mesh-and-istio/fault-injection.png)
测试人员通过Pilot向Envoy注入了一个规则为发向服务MS-B的请求加入了指定时间的延迟。当客户端请求发向MSB-B时Envoy会根据该规则为该请求加入时延引起客户的请求超时。通过设置规则注入故障的方式测试人员可以很方便地模拟微服务之间的各种通信故障对微服务应用的健壮性进行较为完整的模拟测试。
## 总结
服务网格为微服务提供了一个对应用程序透明的安全、可靠的通信基础设施层。采用服务网格后,微服务应用开发人员可以专注于解决业务领域问题,将一些通用问题交给服务网格处理。采用服务网格后,避免了代码库带来的依赖,可以充分发挥微服务的异构优势,开发团队可以根据业务需求和开发人员能力自由选择技术栈。
Istio具有良好的架构设计提供了强大的二次开发扩展性和用户定制能力。虽然Istio目前还处于beta阶段但已经获得众多知名公司和产品的支持是一个非常具有前景的开源服务网格开源项目。
## 参考
* [Istio online documentation](https://istio.io/docs/)
* [Pattern: Service Mesh](http://philcalcado.com/2017/08/03/pattern_service_mesh.html)
* [Mixer and the SPOF Myth](https://istio.io/blog/2017/mixer-spof-myth.html)

View File

@@ -0,0 +1,79 @@
---
layout: post
title: "Service Mesh 和 API Gateway的关系探讨译文"
subtitle: ""
description: "API Gateway和Service Mesh的关系是我最近一直在思考的问题也和同事及社区的朋友之间进行了一些讨论。这篇短文很清晰地总结了两者之间的相似之处以及这两者在微服务架构中的不同用途。"
excerpt: "API Gateway和Service Mesh的关系是我最近一直在思考的问题也和同事及社区的朋友之间进行了一些讨论。这篇短文很清晰地总结了两者之间的相似之处以及这两者在微服务架构中的不同用途。"
date: 2018-04-11 09:32:00
author: "赵化冰"
image: "/img/2018-04-11-service-mesh-vs-api-gateway/background.jpg"
publishDate: 2018-04-11 09:32:00
tags:
- Microservice
- Service Mesh
- API Gateway
URL: "/2018/04/11/service-mesh-vs-api-gateway/"
categories: [ Tech ]
---
## Service Mesh vs API Gateway
在[前一篇关于Service Mesh的文章](https://medium.com/microservices-in-practice/service-mesh-for-microservices-2953109a3c9a)中,我提到了几个关于Service Mesh和API Gateway之间关系的问题在本篇文章中我打算就Service Mesh和API Gateway的用途进行进一步讨论。
为了区分API Gateway和Service Mesh让我们先分别看看两者各自的关键特征。
## API Gateway: 将服务作为被管理的API向外部暴露
使用API Gateway的主要目的是将微服务作为被管理的API暴露给外部系统。因此我们在API Gateway层开发的API或者边界服务对外提供了业务功能。
API/边界服务调用下游的组合或者原子微服务,通过组合/混装多个下游微服务的方式来提供业务逻辑。
在API/Edge服务调用下游服务时需要采用一种可靠的通信方式应用了断路器超时负载均衡/故障转移等可靠性模式。因此大部分的API Gateway解决方案都内置了这些特性。
API Gateway也内置了以下特性的支持包括服务发现分析可见性性能指标监控分布式日志分布式调用追踪和安全。
API Gateway和API管理生态系统的其他组件的关系紧密比如 API 市场/商店, API 发布门户。
## Service Mesh微服务的网络通信基础设施
现在我们来看看Service Mesh有哪些不同。
Service Mesh是一个网络通信基础设施 可以用于将应用层的网络通信功能从你的服务代码中剥离出来。
采用Service Mesh 你不用在服务代码中实现用于可靠通信的模式如断路超时等类似地Service Mesh也提供了服务发现服务可见性等其他功能。
## API Gateway和Service Mesh实践
API Gateway和Service Mesh之间的主要不同点API Gateway是暴露API/边界服务的关键组件而Service Mesh则仅仅是一个服务间通信的基础设施并不了解应用中的业务逻辑。
下图说明了API Gateway和Service Mesh的关系。如同前面所说这两者之间也有一些重叠的部分例如断路器等但重要的是需要理解这两者是用于完全不同的用途。
图1 API Gateway和Service Mesh实践
![](/img/2018-04-11-service-mesh-vs-api-gateway/service-mesh-vs-api-gateway.png)
如上图所示Service Mesh作为Sidecar边车和服务一起部署它是独立于服务的业务逻辑的。
另一方面API Gateway 提供了所有的API服务这些API服务有明确定义的业务功能它是应用业务逻辑的一部分。API Gateway可以具有内建的服务间通信能力但它也可以使用Service Mesh来调用下游服务API Gateway->Service Mesh->Microservices
在API管理层次你可以使用API Gateway内建的服务间通信能力也可以通过Service Mesh来调用下游服务以将应用网络通信功能从应用程序转移到Service Mesh中。
## 译者按
API Gateway和Service Mesh的关系是我最近一直在思考的问题也和同事及社区的朋友之间进行了一些讨论。这篇短文很清晰地总结了两者之间的相似之处以及这两者在微服务架构中的不同用途。
文章中提到“可以使用API Gateway内建的服务间通信能力也可以通过Service Mesh来调用下游服务”。在和同事讨论时大家提到一个比较重要的考虑因素是在API Gateway处引入一个Sidecar可能带来的额外延迟。
API Gateway作为微服务引用的流量入口其对效率要求较高如果随API Gateway部署一个Sidecar可能对效率有一定影响。
我对此未进行测试但从理论上来说服务发现重试断路等逻辑无论放到API Gateway还是Service Mesh中耗时应该是差不多的部署Sidecar只是增加了创建一个本地链接的消耗如下图所示:
![](/img/2018-04-11-service-mesh-vs-api-gateway/api-gateway-with-service-mesh.png)
将API Gateway和Service Mesh的功能进行清晰划分API Gateway负责应用逻辑Service Mesh负责服务通讯Metrics收集等微服务基础设施这样划分后在架构上更为清晰。对于效率问题我们可以考虑对API Gateway进行水平扩展来解决。
## 原文
本译文发表已征得原作者同意,原文参见 [Service Mesh vs API Gateway](https://medium.com/microservices-in-practice/service-mesh-vs-api-gateway-a6d814b9bf56)

View File

@@ -0,0 +1,373 @@
---
layout: post
title: "Helm介绍"
subtitle: "强大的Kubernetes包管理工具"
description: "Helm是Kubernetes生态系统中的一个软件包管理工具。本文将介绍为何要使用Helm进行Kubernetes软件包管理澄清Helm中使用到的相关概念并通过一个具体的示例学习如何使用Helm打包分发安装升级及回退Kubernetes应用。"
excerpt: "Helm是Kubernetes生态系统中的一个软件包管理工具。本文将介绍为何要使用Helm进行Kubernetes软件包管理澄清Helm中使用到的相关概念并通过一个具体的示例学习如何使用Helm打包分发安装升级及回退Kubernetes应用。"
date: 2018-04-16 15:00:00
author: "赵化冰"
image: "/img/2018-04-16-using-helm-to-deploy-to-kubernetes/buffalo.jpg"
publishDate: 2018-04-16 15:00:00
tags:
- Kubernetes
- Helm
categories: [ Tech ]
URL: "/2018/04/16/using-helm-to-deploy-to-kubernetes/"
---
## 前言
- - -
Helm是Kubernetes生态系统中的一个软件包管理工具。本文将介绍为何要使用Helm进行Kubernetes软件包管理澄清Helm中使用到的相关概念并通过一个具体的示例学习如何使用Helm打包分发安装升级及回退Kubernetes应用。
## Kubernetes应用部署的挑战
- - -
让我们首先来看看Kuberneteskubernetes提供了基于容器的应用集群管理为容器化应用提供了部署运行、资源调度、服务发现和动态伸缩等一系列完整功能。
kubernetes的核心设计理念是: 用户定义应用程序的规格而kubernetes则负责按照定义的规则部署并运行应用程序如果应用系统出现问题导致偏离了定义的规格kubernetes负责对其进行自动修正。例如应用规格要求部署两个实例其中一个实例异常终止了kubernetes会检查到并重新启动一个新的实例。
用户通过使用kubernetes API对象来描述应用程序规格包括PodServiceVolumeNamespaceReplicaSetDeploymentJob等等。一般这些对象需要写入一系列的yaml文件中然后通过kubernetes命令行工具kubectl进行部署。
以下面的wordpress应用程序为例涉及到多个kubernetes API对象这些kubernetes API对象分散在多个yaml文件中。
图1 Wordpress应用程序中涉及到的kubernetes API对象
![](/img/2018-04-16-using-helm-to-deploy-to-kubernetes/wordpress.png)
可以看到在进行kubernetes软件部署时我们面临下述问题
* 如何管理编辑和更新这些这些分散的kubernetes应用配置文件
* 如何把一套的相关配置文件作为一个应用进行管理?
* 如何分发和重用kubernetes的应用配置
Helm的引入很好地解决上面这些问题。
## Helm是什么
- - -
很多人都使用过Ubuntu下的ap-get或者CentOS下的yum, 这两者都是Linux系统下的包管理工具。采用apt-get/yum,应用开发者可以管理应用包之间的依赖关系,发布应用;用户则可以以简单的方式查找、安装、升级、卸载应用程序。
我们可以将Helm看作Kubernetes下的apt-get/yum。Helm是Deis (https://deis.com/) 开发的一个用于kubernetes的包管理器。
对于应用发布者而言可以通过Helm打包应用管理应用依赖关系管理应用版本并发布应用到软件仓库。
对于使用者而言使用Helm后不用需要了解Kubernetes的Yaml语法并编写应用部署文件可以通过Helm下载并在kubernetes上安装需要的应用。
除此以外Helm还提供了kubernetes上的软件部署删除升级回滚应用的强大功能。
## Helm组件及相关术语
- - -
开始接触Helm时遇到的一个常见问题就是Helm中的一些概念和术语非常让人迷惑我开始学习Helm就遇到这个问题。
因此我们先了解一下Helm的这些相关概念和术语。
* Helm
Kubernetes的应用打包工具也是命令行工具的名称。
* Tiller
Helm的服务端部署在Kubernetes集群中用于处理Helm的相关命令。
* Chart
Helm的打包格式内部包含了一组相关的kubernetes资源。
* Repoistory
Helm的软件仓库repository本质上是一个web服务器该服务器保存了chart软件包以供下载并有提供一个该repository的chart包的清单文件以供查询。在使用时Helm可以对接多个不同的Repository。
* Release
使用Helm install命令在Kubernetes集群中安装的Chart称为Release。
> 需要特别注意的是, Helm中提到的Release和我们通常概念中的版本有所不同这里的Release可以理解为Helm使用Chart包部署的一个应用实例。
>
> 其实Helm中的Release叫做Deployment更合适。估计因为Deployment这个概念已经被Kubernetes使用了因此Helm才采用了Release这个术语。
下面这张图描述了Helm的几个关键组件Helm客户端Tiller服务器RepositoryChart软件仓库Chart软件包之前的关系。
图2 Helm软件架构
![](/img/2018-04-16-using-helm-to-deploy-to-kubernetes/helm-architecture.png)
## 安装Helm
- - -
下面我们通过一个完整的示例来介绍Helm的相关概念并学习如何使用Helm打包分发安装升级及回退kubernetes应用。
可以参考Helm的帮助文档https://docs.helm.sh/using_helm/#installing-helm 安装Helm
采用二进制的方式安装Helm
1. 下载 Helm https://github.com/kubernetes/helm/releases
1. 解压 tar -zxvf helm-v2.0.0-linux-amd64.tgz
1. 拷贝到bin目录 mv linux-amd64/helm /usr/local/bin/helm
然后使用下面的命令安装服务器端组件Tiller
```bash
Helm init
```
## 构建一个Helm chart
- - -
让我们在实践中来了解Helm。这里将使用一个Go测试小程序让我们先为这个小程序创建一个Helm chart。
```
git clone https://github.com/zhaohuabing/testapi.git;
cd testapi
```
首先创建一个chart的骨架
```
helm create testapi-chart
```
该命令创建一个testapi-chart目录该目录结构如下所示我们主要关注目录中的这三个文件即可: Chart.yamlvalues.yaml 和 NOTES.txt。
```Bash
testapi-chart
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── NOTES.txt
│   └── service.yaml
└── values.yaml
```
* Chart.yaml 用于描述这个chart包括名字描述信息以及版本。
* values.yaml 用于存储templates目录中模板文件中用到的变量。 模板文件一般是Go模板。如果你需要了解更多关于Go模板的相关信息可以查看Hugo (https://gohugo.io) 的一个关于Go模板的介绍 (https://gohugo.io/templates/go-templates/)。
* NOTES.txt 用于向部署该chart的用于介绍chart部署后的一些信息。例如介绍如何使用这个chart列出缺省的设置等。
打开Chart.yaml, 填写你部署的应用的详细信息以testapi为例
```
apiVersion: v1
description: A simple api for testing and debugging
name: testapi-chart
version: 0.0.1
```
然后打开并根据需要编辑values.yaml。下面是testapi应用的values.yaml文件内容。
```
replicaCount: 2
image:
repository: daemonza/testapi
tag: latest
pullPolicy: IfNotPresent
service:
name: testapi
type: ClusterIP
externalPort: 80
internalPort: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
```
在 testapi_chart 目录下运行下面命令以对chart进行校验。
```
helm lint
==> Linting .
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, no failures
```
如果文件格式错误可以根据提示进行修改如果一切正常可以使用下面的命令对chart进行打包
```
helm package testapi-chart --debug
```
这里添加了 --debug 参数来查看打包的输出,输出应该类似于:
```
Saved /Users/daemonza/testapi/testapi-chart/testapi-chart-0.0.1.tgz to current directory
Saved /Users/daemonza/testapi/testapi-chart/testapi-chart-0.0.1.tgz to /Users/daemonza/.helm/repository/local
```
chart被打包为一个压缩包testapi-chart-0.0.1.tgz该压缩包被放到了当前目录下并同时被保存到了helm的本地缺省仓库目录中。
## Helm Repository
- - -
虽然我们已经打包了chart并发布到了helm的本地目录中但通过Helm search命令查找并不能找不到刚才生成的chart包。
```
helm search testapi
No results found
```
这是因为repository目录中的chart还没有被Helm管理。我们可以在本地启动一个Repository Server并将其加入到Helm repo列表中。
通过helm repo list命令可以看到目前helm中只配置了一个名为stable的repo该repo指向了google的一个服务器。
```Bash
helm repo list
NAME URL
stable https://kubernetes-charts.storage.googleapis.com
```
使用helm serve命令启动一个repo server该server缺省使用'$HELM_HOME/repository/local'目录作为chart存储并在8879端口上提供服务。
```Bash
helm serve&
Now serving you on 127.0.0.1:8879
```
启动本地repo server后将其加入helm的repo列表。
```Bash
helm repo add local http://127.0.0.1:8879
"local" has been added to your repositories
```
现在再查找testapi chart包就可以找到了。
```Bash
helm search testapi
NAME CHART VERSION APP VERSION DESCRIPTION
local/testapi-chart 0.0.1 A Helm chart for Kubernetes
```
## 在kubernetes中部署Chart
- - -
chart被发布到仓储后可以通过Helm instal命令部署chart部署时指定chart名及Release部署的实例
```
helm install local/testapi-chart --name testapi
```
该命令的输出应类似:
```
NAME: testapi
LAST DEPLOYED: Mon Apr 16 10:21:44 2018
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
testapi-testapi-chart ClusterIP 10.43.121.84 <none> 80/TCP 0s
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
testapi-testapi-chart 1 1 1 0 0s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
testapi-testapi-chart-9897d9f8c-nn6wd 0/1 Pending 0 0s
NOTES:
1. Get the application URL by running these commands:
export POD_NAME=$(kubectl get pods --namespace default -l "app=testapi-testapi-chart" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80
```
使用下面的命令列出所有已部署的Release以及其对应的Chart。
```
helm ls
```
该命令的输出应类似:
```
NAME REVISION UPDATED STATUS CHART NAMESPACE
testapi 1 Mon Apr 16 10:21:44 2018 DEPLOYED testapi-chart-0.0.1 default
```
可以看到在输出中有一个Revision更改历史字段该字段用于表示某一Release被更新的次数可以用该特性对已部署的Release进行回滚。
## 升级和回退
- - -
修改Chart.yaml将版本号从0.0.1 修改为 1.0.0, 然后使用Helm package命令打包并发布到本地仓库。
查看本地库中的Chart信息可以看到在本地仓库中testapi-chart有两个版本
```Bash
helm search testapi -l
NAME CHART VERSION APP VERSION DESCRIPTION
local/testapi-chart 0.0.1 A Helm chart for Kubernetes
local/testapi-chart 1.0.0 A Helm chart for Kubernetes
```
现在用helm upgrade将已部署的testapi升级到新版本。可以通过参数指定需要升级的版本号如果没有指定版本号则缺省使用最新版本。
```
helm upgrade testapi local/testapi-chart
```
已部署的testapi release被升级到1.0.0版本
```Bash
helm list
NAME REVISION UPDATED STATUS CHART NAMESPACE
testapi 2 Mon Apr 16 10:43:10 2018 DEPLOYED testapi-chart-1.0.0 default
```
可以通过Helm history查看一个Release的多次更改。
```Bash
helm history testapi
REVISION UPDATED STATUS CHART DESCRIPTION
1 Mon Apr 16 10:21:44 2018 SUPERSEDED testapi-chart-0.0.1 Install complete
2 Mon Apr 16 10:43:10 2018 DEPLOYED testapi-chart-1.0.0 Upgrade complete
```
如果更新后的程序由于某些原因运行有问题我们则需要回退到旧版本的应用可以采用下面的命令进行回退。其中的参数1是前面Helm history中查看到的Release的更改历史。
```Bash
helm rollback testapi 1
```
使用Helm list命令查看部署的testapi的版本已经回退到0.0.1
```Bash
helm list
NAME REVISION UPDATED STATUS CHART NAMESPACE
testapi 3 Mon Apr 16 10:48:20 2018 DEPLOYED testapi-chart-0.0.1 default
```
## 总结
- - -
Helm作为kubernetes应用的包管理以及部署工具提供了应用打包发布版本管理以及部署升级回退等功能。Helm以Chart软件包的形式简化Kubernetes的应用管理提高了对用户的友好性。
## Q&A
- - -
昨天在Docker.io技术微信群里面进行了Helm的分享下面是分享过程中得到的一些有意思的反馈进一步启发了我自己的一些思考。
**Q**: Helm结合CD有什么好的建议吗<BR>
**A**: 采用Helm可以把零散的Kubernetes应用配置文件作为一个chart管理chart源码可以和源代码一起放到git库中管理。Helm还简了在CI/CD pipeline的软件部署流程。通过把chart参数化可以在测试环境和生产环境可以采用不同的chart参数配置。
下图是采用了Helm的一个CI/CD流程
![](/img/2018-04-16-using-helm-to-deploy-to-kubernetes/ci-cd-jenkins-helm-k8s.png)
**Q**: 感谢分享,请问下多环境(test,stagingproduction)的业务配置如何管理呢通过heml打包configmap吗比如配置文件更新也要重新打chats包吗谢谢这块我比较乱<BR>
**A**Chart是支持参数替换的可以把业务配置相关的参数设置为模板变量。使用Helm install Chart的时候可以指定一个参数值文件这样就可以把业务参数从Chart中剥离了。例子 helm install --values=myvals.yaml wordpress
**Q**: helm能解决服务依赖吗<BR>
**A**可以的在chart可以通过requirements.yaml声明对其他chart的依赖关系。如下面声明表明chart依赖apache和mysql这两个第三方chart。
```yaml
dependencies:
- name: apache
version: 1.2.3
repository: http://example.com/charts
- name: mysql
version: 3.2.1
repository: http://another.example.com/charts
```
**Q**: chart的reversion 可以自定义吗比如跟git的tag<BR>
**A**: 这位朋友应该是把chart的version和Release的reversion搞混了呵呵。 Chart是没有reversion的Chart部署的一个实例Release才有ReversionReversion是Release被更新后自动生成的。
**Q**: 没有看到helm指向k8s的配置怎么确认在哪个K8s集群运行的<BR>
**A**: 使用和kubectl相同的配置在 ~/.kube/config 中。
**Q**: 这个简单例子并没有看出 Helm 相比 kubectl 有哪些优势,可以简要说一下吗?<BR>
**A** Helm将kubernetes应用作为一个软件包整体管理例如一个应用可能有前端服务器后端服务器数据库这样会涉及多个Kubernetes 部署配置文件Helm就整体管理了。另外Helm还提供了软件包版本一键安装升级回退。Kubectl和Helm就好比你手工下载安装一个应用 和 使用apt-get 安装一个应用的区别。
**Q**: 如何在helm install 时指定命名空间?<BR>
**A**: helm install local/testapi-chart --name testapi --namespace mynamespace
## 参考
- - -
* [Using Helm to deploy to Kubernetes](https://daemonza.github.io/2017/02/20/using-helm-to-deploy-to-kubernetes/)
* [Helm documentation](https://docs.helm.sh/helm/)
* [Helm - Application deployment management for Kubernetes](https://www.slideshare.net/alexLM/helm-application-deployment-management-for-kubernetes)

View File

@@ -0,0 +1,137 @@
---
layout: post
title: "川西秘境探险"
subtitle: "2018五一甘堡藏寨九龙湖自驾游记"
date: 2018-05-01
author: "赵化冰"
image: "/img/2018-05-01-may-day-jiulonghu/snowmountain.jpg"
publishDate: 2018-05-01
hide-in-home: true
tags:
- Travel
categories: [ Life ]
URL: "/2018/05/01/may-day-jiulonghu"
---
## 寻浮云牧场不遇
五一节前的一周内,几个朋友就纷纷坐不住了,一个二个不再安心上班,开始在微信群里讨论过节要到哪里耍。
大家思来想去,最后决定还是去理县方向。因为根据多年自驾的经验,只要出了汶川,沿途都是风景。
<!--more-->
放假第一天和第二天上午老婆加班,我在家里陪女儿做作业,提前把车油加好,准备路上的衣物。第二天中午老婆上完班,我迫不及待开着小狮子就向都汶高速出发了。虽然加班耽误了一天半,但我们这次也算错峰出行了,一路上畅通无阻,心情自然也比较愉快。 开车1个多点小时就赶到了汶川这时朋友一家刚在汶川县城吃完午饭我们在出汶川不远桃坪羌寨附近胜利会师了。
两位领导一起协商了一下,决定先开车去通化乡的“浮云牧场”看看。“浮云牧场”是最近的一个网红酒店,在通化乡山上的一个藏寨旁边。有道是:“浮云牧场”,不放牛羊,只牧浮云和姑娘。
“浮云牧场”走的是网红路线,马蜂窝,微信公众号的宣传做得好,知名度较高,房间比较紧俏,在五一期间更是一房难求,而且价格也比较感人。两位领导都持家有方,指示我们上去看看风景,然后下山再找住宿。
过了桃坪羌寨大概几公里317国道右边有一个比较明显的指路牌往右上山就是到浮云牧场的路。我们兴冲冲地开车上了山此时我们心中向往的浮云牧场是这样子的取图自网络
![](/img/2018-05-01-may-day-jiulonghu/fuyunmuchang.jpeg)
上山的路况还可以但比较窄回头弯较多需要注意对方来车。开了将近1小时后来到了半山腰对面来了好几辆下山的车。由于两方相遇的路面较窄开始堵车了。这时乘机向对方打听了山上的情况得知酒店封路了只有预定了房间的人才能进入浮云牧场。
得知这个消息,此时我们的内心是崩溃的,已经开了一大半的山路,现在却得知不能进去。没有办法,大家商量后还是决定下山。不过“塞翁失马,焉知非福”,这次没进入浮云牧场,为第二天探秘一个新景点埋下伏笔,现在暂时不表。于是我和朋友调转车头,悻悻下山,败意而回。
## 夜宿甘堡藏寨
下山后大家商量晚上的住宿。我觉得桃坪羌寨靠路边环境也一般提议去靠近理县的甘堡藏寨。朋友因为在桃坪羌寨住过了因此也想去另外的地方试试。于是一路向理县方向进发由于限速较低车辆也开始多了起来感觉没多远的距离开了接近1小时6点左右来到了甘堡藏寨。
最后一个靠小河边的藏家乐入住一个标间240元包3个人一顿晚饭一顿早饭。我和朋友两家分别在二楼和三楼的两间房间住下。这里得表扬一下领导每次出来耍选择的住宿都挺好性价比高住着也舒服。
这是一个河边的小院,有三层楼,院子里面种满了各种植物和花卉,老板是个很和气的中年人,把小院收拾得很舒服。房间里挺宽敞,床上套着雪白的床单,非常干净整洁。
院子里的洋槐树树冠上开满了白色的小花,配着嫩绿的树叶和攀缘的蔷薇,感觉非常的清新和惬意。
![](/img/2018-05-01-may-day-jiulonghu/nongjiale1.jpg)
老板的三层小楼,这里的修房的材料不是砖头,而是就地取材用山上的片状岩石修砌而成的,很有特色。
![](/img/2018-05-01-may-day-jiulonghu/nongjiale3.jpg)
窗户旁边挂着金黄色的玉米。
![](/img/2018-05-01-may-day-jiulonghu/nongjiale.jpg)
院子里种的玫瑰花。
![](/img/2018-05-01-may-day-jiulonghu/rose.jpg)
我们和老板闲聊,提到今天没得进到浮云牧场,老板笑道:这浮云牧场的景色我们这里到处都有,只是浮云牧场有老板投资,宣传做得好罢了。这后面山上就有草场,还有一个九龙湖,就挺好耍。我们听了,赶紧向老板仔细打听线路和路况,跃跃欲试,打算明天去探寻这个尚未开发的野景点。
不一会儿,麻利的老板和老板娘就把晚餐准备好了。我们和其他客人一起围桌吃晚饭。晚餐味道不错,好些是城里平时吃不到的东西。例如有老腊肉,核桃花,和一些不知名的野菜,非常爽口开胃。我带了一瓶红酒,和朋友们一起就着这山野美味,好不畅快。
晚上小朋友闹着要玩游戏,于是先一起玩了一会儿游戏,洗漱之后,便倒在床上酣然入梦。是夜,半梦半醒之间,窗外潺潺的河水声,院子里淡淡的槐花香仿佛也潜入梦来。
## 甘堡藏寨风情
昨晚虽然睡得不是很熟,但藏家院子里空气清新,精神恢复得很快,我没到七点就醒了。起床和大家一起吃了早饭,早饭是烤馍,鸡蛋,咸菜和稀饭。吃完饭后,陪孩子们去寨子里逛了一下。寨子不大,半个小时就能走完,街上摆着一些小摊,售卖一些民族特色的小饰品。
![](/img/2018-05-01-may-day-jiulonghu/village2.jpg)
两个小朋友在小摊上找自己喜欢的小饰品,摊主是一个十八九岁的小姑娘,她平时在成都读书,放假回来摆个小摊勤工俭学。最后照顾她生意,给每个小朋友买了一个十多块钱的小玩意。
![](/img/2018-05-01-may-day-jiulonghu/village3.jpg)
寨子墙上的石板画,画的是藏族传说中的英雄人物格萨尔王。
![](/img/2018-05-01-may-day-jiulonghu/geshaerwang.jpg)
## 探秘九龙湖
我逛完寨子,其余人也收拾妥当了。向老板告辞后,我们准备向九龙湖进发。细心的老板怕我们找不到地方,特意给我们画了一张地图。
![](/img/2018-05-01-may-day-jiulonghu/map.jpg)
开车顺着河边一路上山很快就上了盘山公路。路面是水泥的有护栏只是路比较窄很对地方只能容一车通过。川西山区的路基本都是这样之字形的回头弯很多这种回头弯一般有30到40度的坡度。我家的小狮子是1.6的,如果速度开慢点的话,过弯时得用一档。
![](/img/2018-05-01-may-day-jiulonghu/road1.jpg)
山路两边的风景很美,低海拔地区有很多樱桃树,核桃树以及开满了花的洋槐树。洋槐花蜜过一段时间就会上市了,很香的。我们摘了一些花带回家,杨槐花焯水后可以炒蛋,也可以和在面里面吃。
![](/img/2018-05-01-may-day-jiulonghu/yanghuaihua.jpg)
树上结满了樱桃,别看樱桃树不高,一棵树可以产两百斤樱桃。
![](/img/2018-05-01-may-day-jiulonghu/cherry.jpg)
再往上开,到了海拔高一点的地方,乔木就比较少了,路两边多是低矮的绿色灌木,以及不知名的小花。五月间的植物都是嫩绿嫩绿的,煞是好看。
![](/img/2018-05-01-may-day-jiulonghu/flower.jpg)
![](/img/2018-05-01-may-day-jiulonghu/flower1.jpg)
看到几头牛在路边吃草,看这淡定的眼神!山路边还时不时冒出一群小黑猪,目测就是一只就十多斤重,想给它们拍张照片,飞快地钻进灌木丛里面了,只好作罢。川西山里和草原上这种猪都是这样像牛羊一样放养的。我们流着口水说这个是资格的跑山猪,味道肯定巴适!
![](/img/2018-05-01-may-day-jiulonghu/cattle.jpg)
半山上的几户藏家。
![](/img/2018-05-01-may-day-jiulonghu/village1.jpg)
开到最高的一个寨子后,后面的路就是土路了。向寨子里一个大姐打听了一下,大姐信誓旦旦地说这两天没下大雨,轿车开上去没问题,于是我们就继续往上开了。
上土路后不久,遇到一个搭车的老爷子,他要去山顶的寺庙烧香。我们的运气也挺好,要不是老人家陪我们一起,后面我们不一定找得到地方。
![](/img/2018-05-01-may-day-jiulonghu/oldman.jpg)
开了一段土路后,发现路比较窄,路边就是悬崖,而且没有护栏。老婆娃儿都说还是停下来走路上去算了。于是和朋友找了一个路口把车停到路边,开始走路上山。朋友停车后说,在前面几个转弯的地方,开车时脚趾拇都抓紧了。
最后一段就是这种路,地面硬化程度不错,没有下雨的情况下,胆子大点的老师傅可以开上山。
![](/img/2018-05-01-may-day-jiulonghu/road.jpg)
老爷子说这个路开车完全没得问题嘛他看到别人都开车上去的。我们是不敢继续往前开了他也只好下车和我们一起走。我一边走一边和老人家聊天得知他已经高寿76了完全看不出来腰板硬朗牙齿健全走路比我们年轻人还快。老人家自豪地说他寨子阳光好地肥沃种什么粮食产量都高。
老人家所在的寨子,地里面已经种上了玉米。
![](/img/2018-05-01-may-day-jiulonghu/village.jpg)
我继续和老爷子边走边聊,老爷子告诉我,走山路要不紧不慢,快了容易呼吸不畅,引起高原反应。还给我介绍路边的各种植物,哪种可以食用。从聊天中得知,大爷姓何,祖上是从陕西迁移到这里的,到他这里已经是第九代了。家里有四个女儿,都在理县做生意或者打工,寨子的家里就他和老伴。他说他喜欢住在山里,一年也出不了几次山。
看得出老爷子很高兴有人能陪他说说话,住在山里虽然空气好,但子女不在身边,老人平时估计也比较寂寞。
老爷子说这种野菜煮汤喝很香。
![](/img/2018-05-01-may-day-jiulonghu/yecai.jpg)
转过一个弯,听见路边的灌木丛中“噗噗”的声音,飞出一只长尾巴的大鸟来,老爷子说那是野鸡。太快了没能拍到照片。
就这么慢慢地走了将近1小时来到了山顶上。
令人惊奇的是,虽然上山的路很陡,但山顶上却非常开阔,有一大片草坝子。从山顶上可以隐约看到对面巍峨的雪山,今天天气不是很好,能见度不高,如果是在晴天的话,肯定非常壮观。
![](/img/2018-05-01-may-day-jiulonghu/snowmountain.jpg)
山顶搭建了一个台子和一个草房。大爷说这是举行节日的时候的临时厨房。每年有三个时间山顶的草坝上会举行锅庄舞会。这个木板上标注了山顶上望过去的几座雪山可以看到最高的大黄峰有将近6000米高。
![](/img/2018-05-01-may-day-jiulonghu/snowmountain1.jpg)
山顶还有一个小秋千,两个小孩在上面玩得不亦乐乎。
![](/img/2018-05-01-may-day-jiulonghu/swing.jpg)
我对比了网上浮云牧场的图片,感觉这个山顶的雪山比浮云牧场更雄伟。这座山的风景也更好,树木,灌木,草地层次分明;而浮云牧场上山的路上很多地方是光秃秃的。
到山顶后再往前走大约500米翻过一个小山坡就到了九龙湖但是湖中长满了草没有水。大爷说现在水不多九龙湖一共有九个海子前面的海子还比较远。此时天空飘起了小雨由于担心下雨后下山的路湿滑不安全我们和大爷就此告别开始下山了。离别时大爷热情地给我说他家在寨子里面的位置让我们下次过来耍时再来找他。大爷钻进灌木很快就不见了看着他消失的背影我心想下次到这边来耍时希望还能遇到这个开朗乐观的何大爷。
下山的路比上山要轻松多了大家有说有笑很快就走到了停车的地方一路开下山。然后走317,都汶成灌回了成都。路上有一点小堵但一切都很顺利回到成都时也才6点左右。
> 请注意:川西地区山路路况复杂,请勿根据博客内容自行前往,否则一切后果自负。

View File

@@ -0,0 +1,180 @@
---
layout: post
title: "使用Algolia为Gitpage博客提供站内搜索"
subtitle: ""
date: 2018-05-21 11:00:00
author: "赵化冰"
image: "/img/2018-05-06-cryptocurrency_week1/bitcoin_header.jpg"
publishDate: 2018-05-21 11:00:00
tags:
- Jekyll:q
- Bitcoin
categories: [ Tech ]
URL: "/2018/05/21/algolia-integration-with-jekyll"
---
> This series of articles are my notes of "Bitcoin and Cryptocurrency Technologies" online course.
## Table of Content
{:.no_toc}
* Table of Content
{:toc}
## Scrooge Coin Transaction
Scrooge Coin programming assignment is a little bit tricky, the video of this lesson hasn't explained some implementation details. To help you understand the transaction data structure used in Scrooge Coin, I draw this diagram:
![Scrooge Coin](/img/2018-5-20-cryptocurrency_week1_scroogecoin/scroogecoin.png)
<!--more-->
Every transaction has a set of inputs and a set of outputs. An input in a transaction must use a hash pointer to refer to its corresponding output in the previous transaction, and it must be signed with the private key of the owner because the owner needs to prove he/she agrees to spend his/her coins.
Every output is correlated to the public key of the receiver, which is his/her ScroogeCoin address.
In the first transaction, we assume that Scrooge has created 10 coins and assigned them to himself, we don't doubt that because the system-Scroogecoin has a building rule which says that Scrooge has right to create coins.
In the second transaction, Scrooge transferred 3.9 coins to Alice and 5.9 coins to Bob. The sum of the two outputs is 0.2 less than the input because the transaction fee was 0.2 coin.
In the third transaction, there were two inputs and one output, Alice and Bob transferred 9.7 coins to mike, and the transaction fee was 0.1 coin.
## Unclaimed transaction outputs pool
Another trick we need to Note when doing the programming assignment is that an UTXOPool is introduced to track the unclaimed outputs (unspent coins), so we can know whether the corresponding output of an input of the transaction is available or not.
## TxHandler Java Code
```
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TxHandler {
private UTXOPool utxoPool;
/**
* Creates a public ledger whose current UTXOPool (collection of unspent
* transaction outputs) is {@code utxoPool}. This should make a copy of utxoPool
* by using the UTXOPool(UTXOPool uPool) constructor.
*/
public TxHandler(UTXOPool utxoPool) {
this.utxoPool = new UTXOPool(utxoPool);
}
/**
* @return true if: (1) all outputs claimed by {@code tx} are in the current
* UTXO pool, (2) the signatures on each input of {@code tx} are valid,
* (3) no UTXO is claimed multiple times by {@code tx}, (4) all of
* {@code tx}s output values are non-negative, and (5) the sum of
* {@code tx}s input values is greater than or equal to the sum of its
* output values; and false otherwise.
*/
public boolean isValidTx(Transaction tx) {
Set<UTXO> claimedUTXO = new HashSet<UTXO>();
double inputSum = 0;
double outputSum = 0;
List<Transaction.Input> inputs = tx.getInputs();
for (int i = 0; i < inputs.size(); i++) {
Transaction.Input input = inputs.get(i);
if (!isConsumedCoinAvailable(input)) {
return false;
}
if (!verifySignatureOfConsumeCoin(tx, i, input)) {
return false;
}
if (isCoinConsumedMultipleTimes(claimedUTXO, input)) {
return false;
}
UTXO utxo = new UTXO(input.prevTxHash, input.outputIndex);
Transaction.Output correspondingOutput = utxoPool.getTxOutput(utxo);
inputSum += correspondingOutput.value;
}
List<Transaction.Output> outputs = tx.getOutputs();
for (int i = 0; i < outputs.size(); i++) {
Transaction.Output output = outputs.get(i);
if (output.value <= 0) {
return false;
}
outputSum += output.value;
}
// Should the input value and output value be equal? Otherwise the ledger will
// become unbalanced.
// The difference between inputSum and outputSum is the transaction fee
if (outputSum > inputSum) {
return false;
}
return true;
}
private boolean isCoinConsumedMultipleTimes(Set<UTXO> claimedUTXO, Transaction.Input input) {
UTXO utxo = new UTXO(input.prevTxHash, input.outputIndex);
return !claimedUTXO.add(utxo);
}
private boolean verifySignatureOfConsumeCoin(Transaction tx, int index, Transaction.Input input) {
UTXO utxo = new UTXO(input.prevTxHash, input.outputIndex);
Transaction.Output correspondingOutput = utxoPool.getTxOutput(utxo);
PublicKey pk = correspondingOutput.address;
return Crypto.verifySignature(pk, tx.getRawDataToSign(index), input.signature);
}
private boolean isConsumedCoinAvailable(Transaction.Input input) {
UTXO utxo = new UTXO(input.prevTxHash, input.outputIndex);
return utxoPool.contains(utxo);
}
/**
* Handles each epoch by receiving an unordered array of proposed transactions,
* checking each transaction for correctness, returning a mutually valid array
* of accepted transactions, and updating the current UTXO pool as appropriate.
*/
public Transaction[] handleTxs(Transaction[] possibleTxs) {
List<Transaction> acceptedTx = new ArrayList<Transaction>();
for (int i = 0; i < possibleTxs.length; i++) {
Transaction tx = possibleTxs[i];
if (isValidTx(tx)) {
acceptedTx.add(tx);
removeConsumedCoinsFromPool(tx);
addCreatedCoinsToPool(tx);
}
}
Transaction[] result = new Transaction[acceptedTx.size()];
acceptedTx.toArray(result);
return result;
}
private void addCreatedCoinsToPool(Transaction tx) {
List<Transaction.Output> outputs = tx.getOutputs();
for (int j = 0; j < outputs.size(); j++) {
Transaction.Output output = outputs.get(j);
UTXO utxo = new UTXO(tx.getHash(), j);
utxoPool.addUTXO(utxo, output);
}
}
private void removeConsumedCoinsFromPool(Transaction tx) {
List<Transaction.Input> inputs = tx.getInputs();
for (int j = 0; j < inputs.size(); j++) {
Transaction.Input input = inputs.get(j);
UTXO utxo = new UTXO(input.prevTxHash, input.outputIndex);
utxoPool.removeUTXO(utxo);
}
}
}
```
## All the Example Codes on GitHub
I wrap the codes into a maven project, just run ```mvn test``` then the example codes will build and run all the test cases.
[Scrooge Coin example in Java](https://github.com/zhaohuabing/scroogecoin)

View File

@@ -0,0 +1,141 @@
---
layout: post
title: "微服务安全沉思录之一"
subtitle: "用户访问认证与鉴权"
description: "这段时间对之前微服务安全相关的一些想法进行了进一步总结和归纳,理清在之前文章里面没有想得太清楚的地方,例如服务间的认证与鉴权以及用户身份在服务调用链中的传递。在这一系列博客里面将分为三个部分对微服务安全进行系统阐述:用户访问认证与鉴权,服务间认证与鉴权,外部系统访问控制。"
excerpt: "这段时间对之前微服务安全相关的一些想法进行了进一步总结和归纳,理清在之前文章里面没有想得太清楚的地方,例如服务间的认证与鉴权以及用户身份在服务调用链中的传递。在这一系列博客里面将分为三个部分对微服务安全进行系统阐述:用户访问认证与鉴权,服务间认证与鉴权,外部系统访问控制。"
date: 2018-05-23T10:00:00
author:     "赵化冰"
image: "/img/2018-05-22-user_authentication_authorization/background.jpg"
publishDate: 2018-05-23T10:00:00
tags:
- Microservice
- Security
URL: "/2018/05/22/user_authentication_authorization"
categories: [ "Tech" ]
---
> 这段时间对之前微服务安全相关的一些想法进行了进一步总结和归纳,理清了在之前文章里面没有想得太清楚的地方,例如服务间的认证与鉴权以及用户身份在服务调用链中的传递。
>
> 在这一系列文章里,我将尝试分为三个部分对微服务安全进行系统阐述:用户访问认证与鉴权,服务间认证与鉴权,外部系统访问控制。
## 目录
{:.no_toc}
* 目录
{:toc}
## 前言
微服务架构的引入为软件应用带来了诸多好处:包括小开发团队,缩短开发周期,语言选择灵活性,增强服务伸缩能力等。与此同时,也引入了分布式系统的诸多复杂问题。其中一个挑战就是如何在微服务架构中实现一个灵活,安全,高效的认证和鉴权方案。
相对于传统单体应用,微服务架构下的认证和鉴权涉及到场景更为复杂,涉及到用户访问微服务应用,第三方应用访问微服务应用,应用内多个微服务之间相互访问等多种场景,每种场景下的认证和鉴权方案都需要考虑到,以保证应用程序的安全性。本系列博文将就此问题进行一次比较完整的探讨。
![微服务认证和鉴权涉及到的三种场景](//img/2018-02-03-authentication-and-authorization-of-microservice/auth-scenarios.png)
<center>微服务认证和鉴权涉及到的三种场景</center>
## 用户认证和鉴权
### 用户身份认证
一个完整的微服务应用是由多个相互独立的微服务进程组成的,对每个微服务的访问都需要进行用户认证。如果将用户认证的工作放到每个微服务中,存在下面一些问题:
* 需要在各个微服务中重复实现这部分公共逻辑。虽然我们可以使用代码库复用部分代码,但这又会导致所有微服务对特定代码库及其版本存在依赖,影响微服务语言/框架选择的灵活性。
* 将认证和鉴权的公共逻辑放到微服务实现中违背了单一职责原理,开发人员应重点关注微服务自身的业务逻辑。
* 用户需要分别登录以访问系统中不同的服务。
由于在微服务架构中以API Gateway作为对外提供服务的入口因此可以在API Gateway处提供统一的用户认证用户只需要登录一次就可以访问系统中所有微服务提供的服务。
### 用户状态保持
HTTP是一个无状态的协议对服务器来说用户的每次HTTP请求是相互独立的。互联网是一个巨大的分布式系统HTTP协议作为互联网上的一个重要协议在设计之初要考虑到大量应用访问的效率问题。无状态意味着服务端可以把客户端的请求根据需要发送到集群中的任何一个节点HTTP的无状态设计对负载均衡有明显的好处由于没有状态用户请求可以被分发到任意一个服务器应用也可以在靠近用户的网络边缘部署缓存服务器。对于不需要身份认证的服务例如浏览新闻网页等这是没有任何问题的。但HTTP成为企业应用的一个事实标准后企业应用需要保存用户的登录状态和身份以进行更严格的权限控制。因此需要在HTTP协议基础上采用一种方式保存用户的登录状态避免用户每发起一次请求都需要进行验证。
传统方式是在服务器端采用Cookie来保存用户状态由于在服务器是有状态的对服务器的水平扩展有影响。在微服务架构下建议采用Token来记录用户登录状态。
Token和Seesion主要的不同点是存储的地方不同。Session是集中存储在服务器中的而Token是用户自己持有的一般以cookie的形式存储在浏览器中。Token中保存了用户的身份信息每次请求都会发送给服务器服务器因此可以判断访问者的身份并判断其对请求的资源有没有访问权限。
Token用于表明用户身份因此需要对其内容进行加密避免被请求方或者第三者篡改。[JWT(Json Web Token)](https://jwt.io)是一个定义Token格式的开放标准(RFC 7519),定义了Token的内容加密方式并提供了各种语言的lib。
JWT Token的结构非常简单包括三部分
* Header<BR>
头部包含类型,为固定值JWT。然后是JWT使用的Hash算法。
```
{
"alg": "HS256",
"typ": "JWT"
}
```
* Payload<BR>
包含发布者,过期时间,用户名等标准信息,也可以添加用户角色,用户自定义的信息。
```
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
```
* Signature<BR>
Token颁发方的签名用于客户端验证Token颁发方的身份也用于服务器防止Token被篡改。
签名算法
```
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
```
这三部分使用Base64编码后组合在一起成为最终返回给客户端的Token串每部分之间采用"."分隔。下图是上面例子最终形成的Token
![xx](https://cdn.auth0.com/content/jwt/encoded-jwt3.png)
采用Token进行用户认证服务器端不再保存用户状态客户端每次请求时都需要将Token发送到服务器端进行身份验证。Token发送的方式[rfc6750](https://tools.ietf.org/html/rfc6750)进行了规定,采用一个 Authorization: Bearer HHTP Header进行发送。
```
Authorization: Bearer mF_9.B5f-4.1JqM
```
采用Token方式进行用户认证的基本流程如下图所示
1. 用户输入用户名,密码等验证信息,向服务器发起登录请求
1. 服务器端验证用户登录信息生成JWT token
1. 服务器端将Token返回给客户端客户端保存在本地一般以Cookie的方式保存
1. 客户端向服务器端发送访问请求请求中携带之前颁发的Token
1. 服务器端验证Token确认用户的身份和对资源的访问权限并进行相应的处理拒绝或者允许访问
![](https://cdn.auth0.com/content/jwt/jwt-diagram.png)
<center>采用Token进行用户认证的流程图</center>
### 实现单点登录
单点登录的理念很简单即用户只需要登录应用一次就可以访问应用中所有的微服务。API Gateway提供了客户端访问微服务应用的入口Token实现了无状态的用户认证。结合这两种技术可以为微服务应用实现一个单点登录方案。
用户的认证流程和采用Token方式认证的基本流程类似不同之处是加入了API Gateway作为外部请求的入口。
用户登录
1. 客户端发送登录请求到API Gateway
2. API Gateway将登录请求转发到Security Service
3. Security Service验证用户身份并颁发Token
用户请求
1. 客户端请求发送到API Gateway
1. API Gateway调用的Security Service对请求中的Token进行验证检查用户的身份
2. 如果请求中没有TokenToken过期或者Token验证非法则拒绝用户请求。
3. Security Service检查用户是否具有该操作权(可选,参见下一小节)
4. 如果用户具有该操作权限则把请求发送到后端的Business Service否则拒绝用户请求
![采用API Gateway实现微服务应用的SSO](/img/2018-05-22-user_authentication_authorization/api-gateway-sso.png)
<center>采用API Gateway和Token实现微服务应用的单点登录</center>
### 用户权限控制
用户权限控制有两种做法在API Gateway处统一处理或者在各个微服务中单独处理。
#### API Gateway处进行统一的权限控制
客户端发送的HTTP请求中包含有请求的Resource及HTTP Method。如果系统遵循REST规范以URI资源方式对访问对象进行建模则API Gateway可以从请求中直接截取到访问的资源及需要进行的操作然后调用Security Service进行权限判断根据判断结果决定用户是否有权限对该资源进行操作并转发到后端的Business Service。
假设系统中有三个角色:
* order_manager,可以查看,创建,修改,删除订单
* order_editor, 可以查看,创建,修改订单
* order_inspector只能查看订单
这些角色对资源的操作权限都可以映射到HTTP Verb上如下表所示。
| Role | Resource | Verbs |
|-----------------|----------|------------------------------------|
| order_manager | /orders | 'GET' 'POST' 'PUT' 'DELETE' |
| order_editor | /orders | 'GET' 'POST' 'PUT' |
| order_inspector | /orders | 'GET' |
这种实现方式在API Gateway处统一处理鉴权逻辑各个微服务不需要考虑用户鉴权只需要处理业务逻辑简化了各微服务的实现。
#### 由各个微服务单独进行权限控制
如果微服务未严格遵循REST规范对访问对象进行建模或者应用需要进行更细粒度的权限控制则需要在微服务中单独对用户权限进行判断和处理。这种情况下微服务的权限控制更为灵活但各个微服务需要单独维护用户的授权数据实现更复杂。
由于微服务进行权限判断时需要用户身份信息该方案需要处理的另一个问题是如何把登录用户的信息从API Gateway传递到微服务中。如果是基于Http可以采用Http header实现如果是其他协议则需要在消息体中增加用户身份相关的字段。

View File

@@ -0,0 +1,88 @@
---
layout: post
title: "微服务安全沉思录之三"
subtitle: "外部系统访问控制"
description: "一些外部的第三方系统可能需要访问系统内部的微服务。例如在网上商店的例子中,外部的推荐服务可能需要接入系统,以获取商店的商品目录信息。相对于内部服务之间的访问而言,外部系统的访问需要进行严格的安全控制。"
excerpt: "一些外部的第三方系统也可能需要访问系统内部的微服务。例如在网上商店的例子中,外部的推荐服务可能需要接入系统,以获取商店的商品目录信息。相对于内部服务之间的访问而言,外部系统的访问需要进行严格的安全控制。"
date: 2018-05-23T18:00:00
author:     "赵化冰"
image: "/img/2018-05-23-external_system_auth/background.jpg"
publishDate: 2018-05-23T18:00:00
tags:
- Microservice
- Security
URL: "/2018/05/23/external_system_auth/"
categories: [ "Tech" ]
---
## 外部系统访问控制
除用户访问和微服务之间的相互访问外,外部的第三方系统也可能需要访问系统内部的微服务。例如在上一篇博客的网上商店例子中,外部的推荐服务可能需要接入系统,以获取商店的商品目录信息。相对于内部服务之间的访问而言,外部系统的访问需要进行严格的安全控制。
### 使用账号进行控制
可以为外部系统创建一个用户账号,类似普通用户一样对外部系统的账号进行管理,并使用该账号对外部系统进行认证和权限控制。
采用这种方式的问题是难以处理用户相关的敏感数据。因为外部系统自身也是微服务系统中的一个用户账号,因此该外部系统只能访问该账号自身的数据和一些不敏感的公共数据,而不能访问和用户相关的数据。例如在网上商店的例子中,外部系统可以采用该方式访问商品目录信息,但不应允许访问用户历史购买记录,用户余额等信息。
### API Token
是一个API Token又称API Key可以控制对用户敏感数据的访问。微服务应用提供一个API Token的生成界面用户登录后可以生成自己的API Token并在第三方应用使用该API Token访问微服务的API。在这种情况下一般只允许第三方应用访问该Token所属用户自身的数据而不能访问其他用户的敏感私有数据。
例如Github就提供了Personal API Token功能用户可以在[Github的开发者设置界面](https://github.com/settings/tokens)中创建Token然后使用该Token来访问Github的API。在创建Token时可以设置该Token可以访问用户的哪些数据如查看Repo信息删除Repo查看用户信息更新用户信息等。
使用API Token来访问Github API
```
curl -u zhaohuabing:fbdf8e8862252ed0f3ba9dba4e328c01ac93aeec https://api.github.com/user
```
> 不用试了,这不是我的真实API Token, just for demonstration :-)
使用API Token而不是直接使用用户名/密码来访问API的好处是降低了用户密码暴露的风险并且可以随时收回Token的权限而不用修改密码。
由于API Token只能访问指定用户的数据因此适合于用户自己开发一些脚本或小程序对应用中自己的数据进行操作。
### OAuth
某些第三方应用需要访问不同用户的数据或者对多个用户的数据进行整合处理则可以考虑采用OAuth。采用OAuth当第三方应用访问服务时应用会提示用户授权第三方应用相应的访问权限根据用户的授权操作结果生成用于访问的Token以对第三方应用的操作请求进行访问控制。
同样以Github为例一些第三方应用如Travis CIGitBook等就是通过OAuth和Github进行集成的。
OAuth针对不同场景有不同的认证流程一个典型的认证流程如下图所示
* 用户向OAuth客户端程序发起一个请求OAuth客户端程序在处理该请求时发现需要访问用户在资源服务器中的数据。
* 客户端程序将用户请求重定向到认证服务器该请求中包含一个callback的URL。
* 认证服务器返回授权页面要求用户对OAuth客户端的资源请求进行授权。
* 用户对该操作进行授权后认证服务器将请求重定向到客户端程序的callback url将授权码返回给客户端程序。
* 客户端程序将授权码发送给认证服务器请求token。
* 认证服务器验证授权码后将token颁发给客户端程序。
* 客户端程序采用颁发的token访问资源完成用户请求。
>备注:
>1. OAuth中按照功能区分了资源服务器和认证服务器这两个角色在实现时这两个角色常常是同一个应用。将该流程图中的各个角色对应到Github的例子中资源服务器和认证服务器都是Github客户端程序是Travis CI或者GitBook用户则是使用Travis CI或者GitBook的直接用户。
>
>2. 有人可能会疑惑在该流程中为何要使用一个授权码(Authorization Code)来申请Token而不是由认证服务器直接返回Token给客户端。OAuth这样设计的原因是在重定向到客户端Callback URL的过程中会经过用户代理浏览器如果直接传递Token存在被窃取的风险。采用授权码的方式申请Token时客户端直接和认证服务器进行交互并且认证服务期在处理客户端的Token申请请求时还会对客户端进行身份认证避免其他人伪造客户端身份来使用认证码申请Token。
>下面是一个客户端程序采用Authorization Code来申请Token的示例client_id和client_secret被用来验证客户端的身份。
>
>```
>POST /oauth/token HTTP/1.1
>Host: authorization-server.com
>
>grant_type=authorization_code
>&code=xxxxxxxxxxx
>&redirect_uri=https://example-app.com/redirect
>&client_id=xxxxxxxxxx
>&client_secret=xxxxxxxxxx
>```
![OAuth认证流程](/img/2018-05-23-external_system_auth/oauth_web_server_flow.png)
<center>OAuth认证流程</center>
另外在谈及OAuth时我们需要注意微服务应用作为OAuth客户端和OAuth服务器的两种不同场景:
在实现微服务自身的用户认证时也可以采用OAuth将微服务的用户认证委托给一个第三方的认证服务提供商例如很多应用都将用户登录和微信或者QQ的OAuth服务进行了集成。
第三方应用接入和微服务自身用户认证采用OAuth的目的是不同的前者是为了将微服务中用户的私有数据访问权限授权给第三方应用微服务在OAuth架构中是认证和资源服务器的角色而后者的目的是集成并利用知名认证提供服务商提供的OAuth认证服务简化繁琐的注册操作微服务在OAuth架构中是客户端的角色。
因此在我们需要区分这两种不同的场景,以免造成误解。
## 后记
前两篇文章在在公众号发布后,有朋友提到还要注意登录密码明文问题、防止重放攻击、防止时间差攻击、防止脱裤后的彩虹表攻击...。的确安全是一个庞大的话题本系列文章只阐述了我关于微服务架构对应用安全带来的影响的一点小小思考。在产品开发和运维中还需要对安全进行全方面的考虑最好遵循一些业界的最佳实践如采用完善的防火墙对外部流量进行隔离采用加盐hash对用户密码进行存储采用tls进行加密传输对用户输入进行严格检查防止sql注入采用经过验证的通用加密算法等等。

View File

@@ -0,0 +1,111 @@
---
layout: post
title: "Istio Sidecar自动注入原理"
subtitle: "Kubernetes webhook扩展机制解析"
description: "Kubernets 1.9版本引入了Admission Webhook(web 回调)扩展机制通过Webhook,开发者可以非常灵活地对Kubernets API Server的功能进行扩展在API Server创建资源时对资源进行验证或者修改。 Istio 0.7版本就利用了Kubernets webhook实现了sidecar的自动注入。"
excerpt: "Kubernets 1.9版本引入了Admission Webhook(web 回调)扩展机制通过Webhook,开发者可以非常灵活地对Kubernets API Server的功能进行扩展在API Server创建资源时对资源进行验证或者修改。 Istio 0.7版本就利用了Kubernets webhook实现了sidecar的自动注入。"
date: 2018-05-23
author:     "赵化冰"
image: "/img/2018-4-25-istio-auto-injection-with-webhook/lion.jpg"
publishDate: 2018-05-23
tags:
- Kubernetes
- Istio
URL: "/2018/05/23/istio-auto-injection-with-webhook/"
categories: [ Tech ]
---
## 前言
- - -
Kubernets 1.9版本引入了Admission Webhook(web 回调)扩展机制通过Webhook,开发者可以非常灵活地对Kubernets API Server的功能进行扩展在API Server创建资源时对资源进行验证或者修改。
使用webhook的优势是不需要对API Server的源码进行修改和重新编译就可以扩展其功能。插入的逻辑实现为一个独立的web进程通过参数方式传入到kubernets中由kubernets在进行自身逻辑处理时对扩展逻辑进行回调。
Istio 0.7版本就利用了Kubernets webhook实现了sidecar的自动注入。
<!--more-->
## 什么是Admission
---
Admission是Kubernets中的一个术语指的是Kubernets API Server资源请求过程中的一个阶段。如下图所示在API Server接收到资源创建请求时首先会对请求进行认证和鉴权然后经过Admission处理最后再保存到etcd。
![](/img/2018-4-25-istio-auto-injection-with-webhook/admission-phase.png)
从图中看到Admission中有两个重要的阶段Mutation和Validation这两个阶段中执行的逻辑如下
* Mutation
Mutation是英文“突变”的意思,从字面上可以知道在Mutation阶段可以对请求内容进行修改。
* Validation
在Validation阶段不允许修改请求内容但可以根据请求的内容判断是继续执行该请求还是拒绝该请求。
## Admission webhook
---
通过Admission webhook,可以加入Mutation和Validation两种类型的webhook插件这些插件和Kubernets提供的预编译的Admission插件具有相同的能力。可以想到的用途包括
* 修改资源。例如Istio就通过Admin Webhook在Pod资源中增加了Envoy sidecar容器。
* 自定义校验逻辑,例如对资源名称有一些特殊要求。或者对自定义资源的合法性进行校验。
## 采用Webhook自动注入Istio Sidecar
---
### Kubernets版本要求
webhook支持需要Kubernets1.9或者更高的版本,使用下面的命令确认kube-apiserver的Admin webhook功能已启用。
```
kubectl api-versions | grep admissionregistration
admissionregistration.k8s.io/v1beta1
```
### 生成sidecar injection webhook的密钥和证书
Webhook使用数字证书向kube-apiserver进行身份认证因此需要先使用工具生成密钥对并向Istio CA申请数字证书。
```
./install/kubernetes/webhook-create-signed-cert.sh /
--service istio-sidecar-injector /
--namespace istio-system /
--secret sidecar-injector-certs
```
### 将生成的数字证书配置到webhook中
```
cat install/kubernetes/istio-sidecar-injector.yaml | /
./install/kubernetes/webhook-patch-ca-bundle.sh > /
install/kubernetes/istio-sidecar-injector-with-ca-bundle.yaml
```
### 创建sidecar injection configmap
```
kubectl apply -f install/kubernetes/istio-sidecar-injector-configmap-release.yaml
```
### 部署sidecar injection webhook
```
kubectl apply -f install/kubernetes/istio-sidecar-injector-with-ca-bundle.yaml
```
通过命令查看部署好的webhook injector
````
kubectl -n istio-system get deployment -listio=sidecar-injector
Copy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
istio-sidecar-injector 1 1 1 1 1d
```
### 开启需要自动注入sidecar的namespace
```
kubectl label namespace default istio-injection=enabled
kubectl get namespace -L istio-injection
NAME STATUS AGE ISTIO-INJECTION
default Active 1h enabled
istio-system Active 1h
kube-public Active 1h
kube-system Active 1h
```
## 参考
* [Extensible Admission is Beta](https://kubernetes.io/blog/2018/01/extensible-admission-is-beta)
* [Installing the Istio Sidecar](https://istio.io/docs/setup/kubernetes/sidecar-injection.html)

View File

@@ -0,0 +1,67 @@
---
layout: post
title: "微服务安全沉思录之二"
subtitle: "服务间认证与鉴权"
description: "除来自用户的访问请求以外,微服务应用中的各个微服务相互之间还有大量的访问,根据应用系统数据敏感程度不同,对于系统内微服务的访问也需要进行相应的安全控制。"
showonlyimage: false
excerpt: "除来自用户的访问请求以外,微服务应用中的各个微服务相互之间还有大量的访问,根据应用系统数据敏感程度不同,对于系统内微服务的访问也需要进行相应的安全控制。"
author:     "赵化冰"
date: 2018-05-23T15:00:00
image: "/img/2018-05-23-service_2_service_auth/background.jpg"
publishDate: 2018-05-23T15:00:00
tags:
- Microservice
- Security
URL: "/2018/05/23/service_2_service_auth/"
categories: [ Tech ]
---
## 服务间认证与鉴权
除来自用户的访问请求以外,微服务应用中的各个微服务相互之间还有大量的访问,包括下述场景:
* 用户间接触发的微服务之间的相互访问<BR>
例如在一个网上商店应用中,用户访问购物车微服务进行结算时,购物车微服务可能需要访问用户评级微服务获取用户的会员级别,以得到用户可以享受购物折扣。
* 非用户触发的微服务之间的相互访问<BR>
例如数据同步或者后台定时任务导致的微服务之间的相互访问。
根据应用系统的数据敏感程度的不同,对于系统内微服务的相互访问可能有不同的安全要求。
<!--more-->
### 对微服务之间的相互访问不进行安全控制
在某些场景下,可以假设同一应用中微服务之间的相互访问都是可信的。在这种情况下,应用依赖于内部网络的防火墙及其他网络安全措施来保证安全性。在这种情况对入侵者攻击进入内部网络后没有保护措施。入侵者可以对微服务间的通信进行典型的中间人攻击,例如窃听通信内容,伪造和修改通信数据,甚至假装为一个合法的微服务进行通信。
### 采用Service Account(服务账号)进行安全控制
“内部网络中微服务之间的所有通信都是可信的”这个假设在某些场景下是不成立的,特别是在微服务中保存有用户信息这种非常重要的数据的情况下。将敏感信息直接暴露在内部攻击下的做法是非常危险的。 解决该问题的一种方案是使用服务账号来对微服务之间的相互访问进行控制。
用户权限控制的一个普遍方法是使用”用户账号User Account”来标识一个系统用户并对其进行身份认证和操作鉴权。类似地可以为系统中每一个服务也创建一个账号称为”服务账号(Service Accout)“。 该服务账号表示了微服务的身份,以用于控制该微服务对系统中其它微服务的访问权限,如可以对哪些微服务的哪些资源进行何种操作。当一个微服务访问另一个微服务时,被访问的微服务需要验证访问者的服务账号,以确定其身份和资源操作权限。
#### SPIFEE标准
[Secure Production Identity Framework For Everyone (SPIFFE)](https://spiffe.io/)是一套服务之间相互进行身份识别的标准,主要包含以下内容:
* SPIFFE ID标准SPIFFE ID是服务的唯一标识实现为统一资源标识"Uniform Resource Identifier (URI)”符。
* SVID(SPIFFE Verifiable Identity Document)标准,将SPIFFE ID编码到一个加密的可验证文档中。
* 颁发/撤销 SVID的一套API标准。
SPIFFE SVID目前支持的实现方式是X.509数字证书在x.509 SVID中采用X.509数字证书的SAN(Subject Alternative Name)扩展字段来保存SPIFFE ID。
#### Istio Auth开源实现
Istio服务网格项目的Auth组件实现了SPIFFE标准可以为网格中服务颁发符合SPIFFE SVID标准的证书并为服务提供身份认证细粒度的操作鉴权以及通信加密。Istio的架构如下图所示
![](/img/2018-05-23-service_2_service_auth/auth.png)
Istio Auth采用了Kubernetes的service account来作为服务标识其SPIFFE ID的格式为spiffe://&lt;domain&gt;/ns/&lt;namespace&gt;/sa/&lt;serviceaccount&gt;,其中各组成部分如下:
* domain 域名
* namespace kubernetes service account所在的Namespace
* serviceaccount kubernetes中定义的service account名
Istio Auth提供了一个用于颁发证书的CA。在服务部署时CA监听Kubernetes API Server, 为集群中的每一个Service Account创建一对密钥和证书。当Pod创建时Kubernetes根据该Pod关联的Service Account将密钥和证书以Kubernetes Secrets资源的方式加载为Pod的Volume以供Envoy使用。
在服务运行时服务间的通信被Envoy接管Envoy使用证书在服务间进行双向SSL握手验证通信双方服务的身份并提供加密的通信通道。
### 采用用户身份进行安全控制
采用服务账号进行服务间交互的鉴权不能控制到用户粒度的访问权限,这在某些场景下可能出现数据泄露问题。
例如在网上商店应用中用户访问购物车微服务进行结算时购物车微服务需要访问另一个微服务中的用户历史购物数据。如果只采用服务账号对购物车微服务进行安全控制存在用户A通过购物车微服务向后端微服务发起一个获取用户B历史购物数据请求的风险。因为后端的微服务并不能得知发起请求的是哪一个用户因此会不加判断地返回购物车微服务请求的用户历史购物数据。
解决方案是将用户信息从用户直接访问的第一个微服务向后传递到调用链上的每一个微服务,调用链上的每一个微服务都使用该用户信息对用户能访问的资源进行判断。在一个大型的微服务系统中,一个调用链可能会非常长,导致该方案的实现比较复杂。
我们需要根据应用的使用场景,每个微服务中数据的敏感程度来决定选择哪一种服务间安全的实施方式。

View File

@@ -0,0 +1,85 @@
---
layout: post
title: "Everything about Setting Up My Ubuntu Desktop"
description: "Everything about setting up my own ubuntu desktop, it's just a Note in case I need it later"
excerpt: "Everything about setting up my own ubuntu desktop, it's just a Note in case I need it later"
date: 2018-05-24
author:     "赵化冰"
image: "/img/2018-05-23-service_2_service_auth/background.jpg"
publishDate: 2018-05-24
tags:
- ubuntu
URL: "/2018/05/24/set_up_my_ubuntu_desktop/"
categories: [ "Tips" ]
---
## Generate SSH Key Pair
```
ssh-keygen -C "zhaohuabing@gmail.com"
```
## Shadowsocks
Install shadowsokcs
```
sudo apt-get install python3-pip
sudo pip3 install shadowsocks
```
Create config at ```config/shadowsocks.json```, with the following content:
```
{
"server":"remote-shadowsocks-server-ip-addr",
"server_port":443,
"local_address":"127.0.0.1",
"local_port":1080,
"password":"your-passwd",
"timeout":300,
"method":"aes-256-cfb",
"fast_open":false,
"workers":1
}
```
Start a local socks proxy
```
sudo sslocal -c config/shadowsocks.json -d start
```
In case there is an openssl error, modify shadowsocks source file.
```
sudo vi /usr/local/lib/python3.6/dist-packages/shadowsocks/crypto/openssl.py
:%s/cleanup/reset/gc
```
Convert shadowsocks socks proxy to http proxy
```
sudo apt-get install polipo
echo "socksParentProxy = localhost:1080" | sudo tee -a /etc/polipo/config
sudo service polipo restart
```
Http proxy now is available at port 8123
# Set bing wallpaper as desktop background
```
sudo add-apt-repository ppa:whizzzkid/bingwallpaper
sudo apt-get update
sudo apt-get install bingwallpaper
```
# Use vim mode in bash
```
echo 'set -o vi'>> ~/.bashrc
```

View File

@@ -0,0 +1,126 @@
---
layout: post
title: "Istio 0.8 Release发布"
subtitle: "来自Istio的儿童节礼物"
excerpt: "Istio 0.8 Release新特性"
author:     "赵化冰"
date: 2018-06-02
description: "在6月1日这一天的早上Istio社区宣布发布0.8 Release除了常规的故障修复和性能改进外这个儿童节礼物里面还有什么值得期待内容呢让我们来看一看"
image: "/img/2018-06-02-istio08/background.jpg"
publishDate: 2018-06-02
tags:
- Istio
categories: [ Tech ]
URL: "/2018/06/02/istio08/"
---
> 在6月1日这一天的早上Istio社区宣布发布0.8 Release除了常规的故障修复和性能改进外这个儿童节礼物里面还有什么值得期待内容呢让我们来看一看
<!--more-->
## Networking
### 改进的流量管理模型
0.8版本采用了新的流量管理配置模型[v1alpha3 Route API](https://istio.io/blog/2018/v1alpha3-routing/)。新版本的模型添加了一些新的特性,并改善了之前版本模型中的可用性问题。主要的改动包括:
#### Gateway
新版本中不再使用K8s中的Ingress转而采用Gateway来统一配置Service Mesh中的各个HTTP/TCP负载均衡器。Gateway可以是处理入口流量的Ingress Gateway负责Service Mesh内部各个服务间通信的Sidecar Proxy也可以是负责出口流量的Egress Gateway。
Mesh中涉及到的三类Gateway:
![Gateway](/img/2018-06-02-istio08/gateways.svg)
该变化的原因是K8s中的Ingress对象功能过于简单不能满足Istio灵活的路由规则需求。在0.8版本中L4-L6的配置和L7的配置被分别处理Gateway中只配置L4-L6的功能例如暴露的端口TLS设置。然后用户可以采用VirtualService来配置标准的Istio规则并和Gateway进行绑定。
#### VirtualService
采用VirtualService代替了alpha2模型中的RouteRule。采用VirtualService有两个优势
**可以把一个服务相关的规则放在一起管理**
例如下面的路由规则发向reviews的请求流量缺省destination为v1如果user为jason则路由到v2。在v1模型中需要采用两条规则来实现采用VirtualService后放到一个规则下就可以实现。
```
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
cookie:
regex: "^(.*?;)?(user=jason)(;.*)?$"
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1
```
**可以对外暴露一个并不存在的“虚拟服务”然后将该“虚拟服务”映射到Istio中的Service上**
下面规则中的bookinfo.com是对外暴露的“虚拟服务”bookinfo.com/reviews被映射到了reviews服务bookinfo.com/ratings被映射到了ratings服务。通过采用VirtualService极大地增强了Istio路由规则的灵活性有利于Legacy系统和Istio的集成。
```
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- bookinfo.com
http:
- match:
- uri:
prefix: /reviews
route:
- destination:
host: reviews
- match:
- uri:
prefix: /ratings
route:
- destination:
host: ratings
...
```
### Envoy V2
控制面和数据面标准接口支持Envoy
### 用Gateway代替 Ingress/Engress
前面已经介绍到新的版本中不再支持将Kubernetes的Ingress和Istio路由规则一起使用。Istio 0.8支持平台无关的 Ingress/Egress Gateway,可以在KubernetesCloud Foundry中和Istio路由规则无缝集成。
### 对入站端口进行限制
0.8版本只允许访问Pod内已声明端口的入站流量。
## Security
### 安全组件Citadel
将Istio的安全组件Istio-Auth/Istio-CA正式命名为Citadel堡垒
### 跨集群支持
部署在多个Cluster中的Citadel可以共享同一Root Certificate以支持不同Cluster内的服务可以跨Cluster进行认证。
### 认证策略
认证策略既支持Service-to-Service认证也支持对终端用户进行认证。
## 遥测
Mixer和Pilot将上报自身的遥测数据其上报的流程和Mesh中的普通服务相同。
## 安装
按需安装部分组件支持只安装所需的组件如果只需要使用Istio的路由规则可以选择只安装Pilot而不安装Mixer和Citadel。
## Mixer
CloudWatch增加了一个CloudWatch插件可以向AWS CloudWatch上报度量数据。
## 已知故障:
* 如果Gateway绑定的VirtualService指向的是headless service则该规则不能正常工作。
* 0.8版本和Kubernetes1.10.2存在兼容问题目前建议采用1.9版本。
* convert-networking-config工具存在故障一个其它的namespace可能会被修改为istio-system namespace。可以在允许转换工具后手动修改文件来避免。
## 总结
0.8版本带来的最大变化是流量配置模型的重构重构后的模型整合了外部Gateway和内部Sidecar Proxy的路由配置。同时VirtualService的引入使路由规则的配置更为集中和灵活。

View File

@@ -0,0 +1,316 @@
---
showonlyimage: true
title: "Istio v1aplha3 routing API介绍(译文)"
subtitle: ""
excerpt: "介绍Istio v1alpha3 routing API及其设计原则"
description: "介绍Istio v1alpha3 routing API及其设计原则"
date: 2018-06-04
author:     "赵化冰"
image: "/img/2018-06-04-introducing-the-istio-v1alpha3-routing-api/background.jpg"
publishDate: 2018-06-04
tags:
- Istio
categories: [ Tech ]
URL: "/2018/06/04/introducing-the-istio-v1alpha3-routing-api/"
---
到目前为止Istio提供了一个简单的API来进行流量管理该API包括了四种资源RouteRuleDestinationPolicyEgressRule和Ingress直接使用了Kubernets的Ingress资源。借助此API用户可以轻松管理Istio服务网格中的流量。该API允许用户将请求路由到特定版本的服务为弹性测试注入延迟和失败添加超时和断路器等等所有这些功能都不必更改应用程序本身的代码。
<!--more-->
虽然目前API的功能已被证明是Istio非常引人注目的一部分但用户的反馈也表明这个API确实有一些缺点尤其是在使用它来管理包含数千个服务的非常大的应用程序以及使用HTTP以外的协议时。 此外使用Kubernetes Ingress资源来配置外部流量的方式已被证明不能满足需求。
为了解决上述缺陷和其他的一些问题Istio引入了新的流量管理API v1alpha3新版本的API将完全取代之前的API。 尽管v1alpha3和之前的模型在本质上是基本相同的但它并不向后兼容的基于旧API的模型需要进行手动转换。 Istio接下来的几个版本中会提供一个新旧模型的转换工具。
为了证明该非兼容升级的必要性v1alpha3 API经历了漫长而艰苦的社区评估过程以希望新的API能够大幅改进并经得起时间考验。 在本文中,我们将介绍新的配置模型,并试图解释其后面的一些动机和设计原则。
## 设计原则
路由模型的重构过程中遵循了一些关键的设计原则:
* 除支持声明式(意图)配置外,也支持显式指定模型依赖的基础设施。例如,除了配置入口网关(的功能特性)之外,负责实现 入口网关功能的组件Controller也可以在模型指定。
* 编写模型时应该“生产者导向”和“以Host为中心”而不是通过组合多个规则来编写模型。 例如所有与特定Host关联的规则被配置在一起而不是单独配置。
* 将路由与路由后行为清晰分开。
## v1alpha3中的配置资源
在一个典型的网格中通常有一个或多个用于终结外部TLS链接将流量引入网格的负载均衡器我们称之为gateway。 然后流量通过边车网关sidecar gateway流经内部服务。 应用程序使用外部服务的情况也很常见例如访问Google Maps API一些情况下这些外部服务可能被直接调用但在某些部署中网格中所有访问外部服务的流量可能被要求强制通过专用的出口网关Egress gateway。 下图描绘了网关在网格中的使用情况。
![Gateway](/img/2018-06-04-introducing-the-istio-v1alpha3-routing-api/gateways.svg)
考虑到上述因素,`v1alpha3`引入了以下这些新的配置资源来控制进入网格,网格内部和离开网格的流量路由。
1. `Gateway`
1. `VirtualService`
1. `DestinationRule`
1. `ServiceEntry`
`VirtualService``DestinationRule``ServiceEntry`分别替换了原API中的`RouteRule``DestinationPolicy``EgressRule``Gateway`是一个独立于平台的抽象,用于对流入专用中间设备的流量进行建模。
下图描述了跨多个配置资源的控制流程。
![不同配置资源之间的关系](/img/2018-06-04-introducing-the-istio-v1alpha3-routing-api/virtualservices-destrules.svg)
### Gateway
[Gateway](https://istio.io/docs/reference/config/istio.networking.v1alpha3/#Gateway)用于为HTTP / TCP流量配置负载均衡器并不管该负载均衡器将在哪里运行。 网格中可以存在任意数量的Gateway并且多个不同的Gateway实现可以共存。 实际上通过在配置中指定一组工作负载Pod标签可以将Gateway配置绑定到特定的工作负载从而允许用户通过编写简单的Gateway Controller来重用现成的网络设备。
对于入口流量管理,您可能会问: 为什么不直接使用Kubernetes Ingress API 原因是Ingress API无法表达Istio的路由需求。 Ingress试图在不同的HTTP代理之间取一个公共的交集因此只能支持最基本的HTTP路由最终导致需要将代理的其他高级功能放入到注解annotation而注解的方式在多个代理之间是不兼容的无法移植。
Istio `Gateway` 通过将L4-L6配置与L7配置分离的方式克服了`Ingress`的这些缺点。 `Gateway`只用于配置L4-L6功能例如对外公开的端口TLS配置所有主流的L7代理均以统一的方式实现了这些功能。 然后,通过在`Gateway`上绑定`VirtualService`的方式可以使用标准的Istio规则来控制进入`Gateway`的HTTP和TCP流量。
例如,下面这个简单的`Gateway`配置了一个Load Balancer以允许访问host bookinfo.com的https外部流量入mesh中
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- bookinfo.com
tls:
mode: SIMPLE
serverCertificate: /tmp/tls.crt
privateKey: /tmp/tls.key
```
要为进入上面的Gateway的流量配置相应的路由必须为同一个host定义一个`VirtualService`(在下一节中描述),并使用配置中的`gateways`字段绑定到前面定义的`Gateway` 上:
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- bookinfo.com
gateways:
- bookinfo-gateway # <---- bind to gateway
http:
- match:
- uri:
prefix: /reviews
route:
...
```
Gateway可以用于建模边缘代理或纯粹的内部代理如第一张图所示。 无论在哪个位置,所有网关都可以用相同的方式进行配置和控制。
### VirtualService
用一种叫做“Virtual services”的东西代替路由规则可能看起来有点奇怪但对于它配置的内容而言这事实上是一个更好的名称特别是在重新设计API以解决先前模型的可扩展性问题之后。
实际上发生的变化是在之前的模型中需要用一组相互独立的配置规则来为特定的目的服务设置路由规则并通过precedence字段来控制这些规则的顺序在新的API中则直接对虚拟服务进行配置该虚拟服务的所有规则以一个有序列表的方式配置在对应的[VirtualService](/docs/reference/config/istio.networking.v1alpha3/#VirtualService) 资源中。
例如,之前在[Bookinfo](/docs/guides/bookinfo/) 应用程序的reviews服务中有两个RouteRule资源如下所示
```yaml
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-default
spec:
destination:
name: reviews
precedence: 1
route:
- labels:
version: v1
---
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-test-v2
spec:
destination:
name: reviews
precedence: 2
match:
request:
headers:
cookie:
regex: "^(.*?;)?(user=jason)(;.*)?$"
route:
- labels:
version: v2
```
`v1alph3`,可以在单个`VirtualService`资源中提供相同的配置:
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
cookie:
regex: "^(.*?;)?(user=jason)(;.*)?$"
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1
```
正如你所看到的, 和reviews服务相关的两个规则集中写在了一个地方。这个改变乍一看可能觉得并没有什么特别的优势 然而如果仔细观察这个新模型会发现它和之前的API之间存在着根本的差异这使得v1alpha3功能更加强大。
首先,请注意`VirtualService`的目标服务是使用`hosts`字段(实际上是重复字段)指定的,然后再在每个路由的`destination`字段中指定。 这是与以前模型的重要区别。
`VirtualService`描述了一个或多个用户可寻址目标到网格内实际工作负载之间的映射。在上面的示例中这两个地址是相同的但实际上用户可寻址目标可以是任何用于定位服务的具有可选通配符前缀或CIDR前缀的DNS名称。
这对于应用从单体架构到微服务架构的迁移过程特别有用单体应用被拆分为多个独立的微服务后采用VirtaulService可以继续把多个微服务对外暴露为同一个目标地址而不需要服务消费者进行修改以适应该变化。
例如以下规则允许服务消费者访问Bookinfo应用程序的reviews和ratings服务就好像它们是`http://bookinfo.com/`(虚拟)服务的一部分:
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- bookinfo.com
http:
- match:
- uri:
prefix: /reviews
route:
- destination:
host: reviews
- match:
- uri:
prefix: /ratings
route:
- destination:
host: ratings
...
```
实际上在VirtualService中hosts部分设置只是虚拟的目的地,因此不一定是已在网格中注册的服务。这允许用户为在网格内没有可路由条目的虚拟主机的流量进行建模。 通过将`VirtualService`绑定到同一Host的`Gateway`配置(如前一节所述 可向网格外部暴露这些Host。
除了这个重大的重构之外, `VirtualService`还包括其他一些重要的改变:
1. 可以在`VirtualService`配置中表示多个匹配条件,从而减少对冗余的规则设置。
1. 每个服务版本都有一个名称(称为服务子集)。 属于某个子集的一组Pod/VM在`DestinationRule`定义,具体定义参见下节。
1. 通过使用带通配符前缀的DNS来指定`VirtualService`的host可以创建单个规则以作用于所有匹配的服务。 例如在Kubernetes中在'VirtualService'中使用*.foo.svc.cluster.local作为host,可以对`foo`命名空间中的所有服务应用相同的重写规则。
### DestinationRule
[DestinationRule](https://istio.io/docs/reference/config/istio.networking.v1alpha3/#DestinationRule)配置将流量转发到服务时应用的策略集。 这些策略应由由服务提供者撰写用于描述断路器负载均衡设置TLS设置等。
除了下述改变外,`DestinationRule`与其前身`DestinationPolicy`大致相同。
1. [DestinationRule](https://istio.io/docs/reference/config/istio.networking.v1alpha3/#DestinationRule)的`host`可以包含通配符前缀,以允许单个规则应用于多个服务。
1. `DestinationRule`定义了目的host的子集`subsets` (例如:命名版本)。 这些subset用于VirtualService的路由规则设置中可以将流量导向服务的某些特定版本。 通过这种方式为版本命名后可以在不同的virtual service中明确地引用这些命名版本的ubset简化Istio代理发出的统计数据并可以将subsets编码到SNI头中。
为reviews服务配置策略和subsets的`DestinationRule`可能如下所示:
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
trafficPolicy:
loadBalancer:
simple: RANDOM
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
- name: v3
labels:
version: v3
```
注意,与`DestinationPolicy`不同的是,可在单个`DestinationRule`中指定多个策略例如上面实例中的缺省策略和v2版本特定的策略
### ServiceEntry
[ServiceEntry](https://istio.io/docs/reference/config/istio.networking.v1alpha3/#ServiceEntry)用于将附加条目添加到Istio内部维护的服务注册表中。
它最常用于对访问网格外部依赖的流量进行建模例如访问Web上的API或遗留基础设施中的服务。
所有以前使用`EgressRule`进行配置的内容都可以通过`ServiceEntry`轻松完成。 例如,可以使用类似这样的配置来允许从网格内部访问一个简单的外部服务:
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: foo-ext
spec:
hosts:
- foo.com
ports:
- number: 80
name: http
protocol: HTTP
```
也就是说,`ServiceEntry`比它的前身具有更多的功能。首先,`ServiceEntry`不限于外部服务配置它可以有两种类型网格内部或网格外部。网格内部条目只是用于向网格显式添加服务添加的服务与其他内部服务一样。采用网格内部条目可以把原本未被网格管理的基础设施也纳入到网格中例如把虚机中的服务添加到基于Kubernetes的服务网格中。网格外部条目则代表了网格外部的服务。对于这些外部服务来说mTLS身份验证是禁用的并且策略是在客户端执行的而不是在像内部服务请求一样在服务器端执行策略。
由于`ServiceEntry`配置只是将服务添加到网格内部的服务注册表中,因此它可以像注册表中的任何其他服务一样,与`VirtualService`和/或`DestinationRule`一起使用。例如,以下`DestinationRule`可用于启动外部服务的mTLS连接
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: foo-ext
spec:
name: foo.com
trafficPolicy:
tls:
mode: MUTUAL
clientCertificate: /etc/certs/myclientcert.pem
privateKey: /etc/certs/client_private_key.pem
caCertificates: /etc/certs/rootcacerts.pem
```
除了扩展通用性以外,`ServiceEntry`还提供了其他一些有关`EgressRule`改进,其中包括:
1. 一个`ServiceEntry`可以配置多个服务端点,这在之前需要采用多个`EgressRules`来实现。
1. 现在可以配置服务端点的解析模式(`NONE``STATIC``DNS`)。
1. 此外,我们正在努力解决另一个难题:目前需要通过纯文本端口访问安全的外部服务(例如`http://google.com:443`)。该问题将会在未来几周内得到解决,届时将允许从应用程序直接访问`https://google.com`。请继续关注解决此限制的Istio补丁版本0.8.x
## 创建和删除v1alpha3路由规则
由于一个特定目的地的所有路由规则现在都存储在单个`VirtualService`资源的一个有序列表中,因此为该目的地添加新的规则不需要再创建新的`RouteRule`,而是通过更新该目的地的`VirtualService`资源来实现。
旧的路由规则:
```command
$ istioctl create -f my-second-rule-for-destination-abc.yaml
```
`v1alpha3`路由规则:
```command
$ istioctl replace -f my-updated-rules-for-destination-abc.yaml
```
删除路由规则也使用istioctl replace完成当然删除最后一个路由规则除外删除最后一个路由规则需要删除`VirtualService`)。
在添加或删除引用服务版本的路由时,需要在该服务相应的`DestinationRule`更新subsets 。 正如你可能猜到的,这也是使用`istioctl replace`完成的。
## 总结
Istio `v1alpha3`路由API具有比其前身更多的功能但不幸的是新的API并不向后兼容旧的模型升级需要一次手动转换。 Istio 0.9以后将不再支持`RouteRule``DesintationPolicy``EgressRule`这些以前的配置资源 。Kubernetes用户可以继续使用`Ingress`配置边缘负载均衡器来实现基本的路由。 但是,高级路由功能(例如,跨两个版本的流量分割)则需要使`用Gateway` 这是一种功能更强大Istio推荐的`Ingress`替代品。
## 致谢
感谢以下人员为新版本的路由模型重构和实现工作做出的贡献(按字母顺序)
* Frank Budinsky (IBM)
* Zack Butcher (Google)
* Greg Hanson (IBM)
* Costin Manolache (Google)
* Martin Ostrowski (Google)
* Shriram Rajagopalan (VMware)
* Louis Ryan (Google)
* Isaiah Snell-Feikema (IBM)
* Kuat Yessenov (Google)
## 原文
* [Introducing the Istio v1alpha3 routing API](https://kubernetes.io/blog/2018/01/extensible-admission-is-beta)

View File

@@ -0,0 +1,201 @@
---
layout: post
title: "Authoring mathematical formulae"
description: "Cleanwhite theme now has built-in support for authoring mathematical or chemical equations"
excerpt: "The theme uses Hugo's embedded instance of the KaTeX display engine to render mathematical markup to HTML at build time."
date: 2025-07-06
author: "Andreas Deininger"
image: "/img/2018-05-23-service_2_service_auth/background.jpg"
publishDate: 2025-07-06
tags:
- Math
- KaTeX
URL: "/2025/07/06/mathematical-formulae/"
categories: [ tips ]
---
## Authoring mathematical and chemical equations
Cleanwhite theme now has built-in \(\KaTeX\) support, so that you can easily include
complex mathematical formulae into your web page, either inline or centred
on its own line. The theme uses Hugo's embedded instance of the KaTeX
display engine to render mathematical markup to HTML at build time.
With this server side rendering of formulae, the same output is produced,
regardless of your browser or your environment.
[\(\LaTeX\)](https://www.latex-project.org/) is a high-quality typesetting
system for the production of technical and scientific documentation. Due to its
excellent math typesetting capabilities, \(\TeX\) became the de facto standard
for the communication and publication of scientific documents, especially if
these documents contain a lot of mathematical formulae. Designed and mostly
written by Donald Knuth, the initial version was released in 1978. Dating back
that far, \(\LaTeX\) has `pdf` as its primary output target and is not
particularly well suited for producing HTML output for the Web. Fortunately,
with [\(\KaTeX\)](https://katex.org/) there exists a fast and easy-to-use
JavaScript library for \(\TeX\) math rendering on the web, which is embedded
into Hugo as of Hugo version v0.132.0.
As already mentioned above, mathematical or chemical equations can be shown either inline or in display mode:
### Inline formulae
The following code sample produces a text line with three inline formulae:
```tex
When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c= 0\) and they are \(x = {-b \pm \sqrt{b^2-4ac} \over 2a}\).
```
When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c= 0\) and they are
\(x = {-b \pm \sqrt{b^2-4ac} \over 2a}\).
### Formulae in display mode
The following code sample produces an introductory text line followed by a
formula numbered as `(1)` residing on its own line:
````markdown
The probability of getting \(k\) heads when flipping \(n\) coins is:
\[
\tag*{(1)} P(E) = {n \choose k} p^k (1-p)^{n-k}
\]
````
As an alternative to the standard syntax used above, formulae can also be
authored using a [GLFM math block](https://docs.gitlab.com/ee/user/markdown.html#math):
````markdown
The probability of getting \(k\) heads when flipping \(n\) coins is:
```math
\tag*{(1)} P(E) = {n \choose k} p^k (1-p)^{n-k}
```
````
Both standard syntax and `math` block render to the same formula:
The probability of getting \(k\) heads when flipping \(n\) coins is:
```math
\tag*{(1)} P(E) = {n \choose k} p^k (1-p)^{n-k}
```
This [wiki page](https://en.wikibooks.org/wiki/LaTeX/Mathematics) provides in-depth
information about typesetting mathematical formulae using the \(\LaTeX\)
typesetting system.
### Activating KaTeX support
#### Enable `passthrough` extension
All you have to do is to enable and configure the goldmark `passthrough` extension
inside your `hugo.toml`/`hugo.yaml`/`hugo.json`. You may want to edit the definition of the delimiters to
meet your own needs. For details, see the official
[Hugo docs](https://gohugo.io/content-management/mathematics/#step-1).
```toml
[markup]
[markup.goldmark]
[markup.goldmark.extensions]
[markup.goldmark.extensions.passthrough]
enable = true
[markup.goldmark.extensions.passthrough.delimiters]
block = [['\[', '\]'], ['$$', '$$']]
inline = [['\(', '\)']]
```
Internally, cleanwhite theme creates and uses Hugo's `render-passthrough`
[hook](https://gohugo.io/render-hooks/passthrough/) when generating math
equations at build-time. This hook is part of the theme, no need for any user action.
#### Media types for download of KaTeX fonts
Just for your information, no need for any action from you as user:
KaTeX brings its own font files for rendering mathematical formulae.
In order to enable the download of these font files locally during build time, two
additional [media types](https://gohugo.io/configuration/media-types/#create-a-media-type)
had to be created by adding the lines below to the `hugo.toml` configuration file of the cleanwhite theme:
```toml
mediaTypes:
font/woff:
suffixes:
- woff
font/woff2:
suffixes:
- woff2
```
With the `passthrough` extension enabled and the media types defined, support
of \(\KaTeX\) is automatically enabled when you author a `math` code block on
your page or when you add a mathematical formula to your page using one of the
passthrough delimiter pairs defined above.
### Display of Chemical Equations and Physical Units
[mhchem](https://www.ctan.org/pkg/mhchem) is a \(\LaTeX\) package for
typesetting chemical molecular formulae and equations. Fortunately, \(\KaTeX\)
provides the `mhchem`
[extension](https://github.com/KaTeX/KaTeX/tree/main/contrib/mhchem) that makes
the `mhchem` package accessible when authoring content for the web. As of hugo
version v0.144.0, the `mhchem` extension is enabled in Hugo's embedded KaTeX
instance by default, therefore you can easily include chemical equations into
your page. An equation can be shown either inline or can reside on its own line.
The following code sample produces a text line including an inline chemical
equation:
```mhchem
*Precipitation of barium sulfate:* \(\ce{SO4^2- + Ba^2+ -> BaSO4 v}\)
```
_Precipitation of barium sulfate:_ \(\ce{SO4^2- + Ba^2+ -> BaSO4 v}\)
More complex equations can be displayed on their own line using the block
delimiters defined:
<!-- prettier-ignore-start -->
````markdown
\[
\tag*{(2)} \ce{Zn^2+ <=>[+ 2OH-][+ 2H+] $\underset{\text{amphoteric hydroxide}}{\ce{Zn(OH)2 v}}$ <=>[+ 2OH-][+ 2H+] $\underset{\text{tetrahydroxozincate}}{\ce{[Zn(OH)4]^2-}}$}
\]
````
<!-- prettier-ignore-end -->
Alternatively, you can use a code block adorned with `chem` in order to render
the equation:
````markdown
```chem
\tag*{(2)} \ce{Zn^2+ <=>[+ 2OH-][+ 2H+] $\underset{\text{amphoteric hydroxide}}{\ce{Zn(OH)2 v}}$ <=>[+ 2OH-][+ 2H+] $\underset{\text{tetrahydroxozincate}}{\ce{[Zn(OH)4]^2-}}$}
```
````
Both standard syntax and `chem` block renders to the same equation:
<!-- prettier-ignore-start -->
\[
\tag*{(2)} \ce{Zn^2+ <=>[+ 2OH-][+ 2H+] $\underset{\text{amphoteric hydroxide}}{\ce{Zn(OH)2 v}}$ <=>[+ 2OH-][+ 2H+] $\underset{\text{tetrahydroxozincate}}{\ce{[Zn(OH)4]^2-}}$}
\]
<!-- prettier-ignore-end -->
The [manual](https://mhchem.github.io/MathJax-mhchem/) for mchems input syntax
provides in-depth information about typesetting chemical formulae and physical
units using the `mhchem` tool.
Use of `mhchem` is not limited to the authoring of chemical equations. By using
the included `\pu` command, pretty looking physical units can be written with
ease, too. The following code sample produces two text lines with four numbers
plus their corresponding physical units:
```mhchem
* Scientific number notation: \(\pu{1.2e3 kJ}\) or \(\pu{1.2E3 kJ}\) \\
* Divisions: \(\pu{123 kJ/mol}\) or \(\pu{123 kJ//mol}\)
```
- Scientific number notation: \(\pu{1.2e3 kJ}\) or \(\pu{1.2E3 kJ}\)
- Divisions: \(\pu{123 kJ/mol}\) or \(\pu{123 kJ//mol}\)
For a complete list of options when authoring physical units, have a look at the
[section](https://mhchem.github.io/MathJax-mhchem/#pu) on physical units in the
`mhchem` documentation.

190
content/post/readme.md Normal file
View File

@@ -0,0 +1,190 @@
---
layout: post
title: "Clean White Theme for Hugo"
subtitle: "How to set up this theme"
date: 2019-01-09
author: "赵化冰"
image: "https://img.zhaohuabing.com/post-bg-2015.jpg"
---
# Clean White Theme for Hugo
CleanWhite is a clean, elegant, but fully functional blog theme for Hugo. Here is a live [demo site](https://zhaohuabing.com) using this theme.
It is based on [huxblog Jekyll Theme](https://github.com/Huxpro/huxpro.github.io)
and [Clean Blog Jekyll Theme](https://github.com/BlackrockDigital/startbootstrap-clean-blog-jekyll).
These two upstream projects have done awesome jobs to create a blog theme, what I'm doing here is porting it to Hugo, of which I like the simplicity and the much faster compiling speed. Some other features which I think could be useful, such as site search with algolia and proxy for Disqus access in China, have also been built in the CleanWhite theme. Other fancy features of upstream projects are not supported by this Hugo theme, I'd like to make it as simple as possible and only focus on blog purpose, at least for now.
While I created this theme, I followed the Hugo theme best practice and tried to make every part of the template as a replaceable partial html, so it could be much easier for you to make your customization based on it.
## Screenshots
**Home**
![screenshot](/img/fullscreenshot.png)
**Post**
![screenshot](/img/post.png)
**Search**
![screenshot](/img/sitesearch.png)
**Disqus**
![screenshot](/img/disqus.png)
**Wechat Pay & Alipay**
![screenshot](/img/rewards.png)
## Quick Start
Go to the directory where you have your Hugo site and run:
```
$ mkdir themes
$ cd themes
$ git clone https://github.com/zhaohuabing/hugo-theme-cleanwhite.git
```
If your site is already a git project, you may want to choose to add the cleanwhite theme as a submodule to avoid messing up your existing git repository.
```
$ mkdir themes
$ git submodule add https://github.com/zhaohuabing/hugo-theme-cleanwhite.git themes/hugo-theme-cleanwhite
```
Run Hugo Built-in Server Locally
```
$ hugo serve -t hugo-theme-cleanwhite
```
Now enter [`localhost:1313`](http://localhost:1313) in the address bar of your browser.
If you start from scratch, there is a working Hugo site configured with the CleanWhite theme in the [exampleSite](https://github.com/zhaohuabing/hugo-theme-cleanwhite/tree/master/exampleSite) directory. You can use it as a starting point for your site.
For more information read the official [setup guide](https://gohugo.io/overview/installing/) of Hugo
## Configuration
First, let's take a look at the [hugo.toml](https://github.com/zhaohuabing/hugo-cleanwhite-theme/tree/master/exampleSite/hugo.toml). It will be useful to learn how to customize your site. Feel free to play around with the settings.
### Comments
The optional comments system is powered by [Disqus](https://disqus.com). If you want to enable comments, create an account in Disqus and write down your shortname.
```toml
[services]
[services.disqus]
shortname = "your-disqus-short-name"
```
You can disable the comments system by leaving the `shortname` empty.
### Disqus in China
Disqus is inaccessible in China. To get it to work, we can set up a proxy with [disqus-php-api](https://github.com/zhaohuabing/disqus-php-api) in a host which sets between the client browser and the Disqus server. The idea is that if Disqus can be reached in the guest network, the blog page will show the original Disqus comments UI, otherwise, it will downgrade and use the proxy to access the Disqus, the UI will be a little different, but the visitors can still write their comments on the page.
The client side java script has already been integrated to CleanWhite them, but you need to set up a proxy server yourself.
The proxy is written in php, which can be found here: https://github.com/zhaohuabing/disqus-php-api/tree/master/api
You need to specify your Disqus account information in the config.php.
```
define('PUBLIC_KEY', '');
define('SECRET_KEY', '');
define('DISQUS_USERNAME', '');
define('DISQUS_EMAIL', '');
define('DISQUS_PASSWORD', '');
define('DISQUS_WEBSITE', '');
define('DISQUS_SHORTNAME', '');
```
Set the proxy server address in the site config file of your Hugo project.
```toml
disqus_proxy = "http://yourdisqusproxy.com"
```
### Site Search with Algolia
Follow this [tutorial](https://forestry.io/blog/search-with-algolia-in-hugo/#3-create-your-index-in-algolia) to create your index in Algolia. The index is just the storage of the indexing data of your site in the the cloud . The search page of CleanWhite theme will utilize this indexing data to do the search.
Go to the directory where you have your Hugo site and run the following commands:
```bash
$ npm init
$ npm install atomic-algolia --save
```
Next, open up the newly created package.json, where well add an NPM script to update your index at Algolia. Find "scripts", and add the following:
```josn
"algolia": "atomic-algolia"
```
Algolia index output format has already been supported by the CleanWhite theme, so you can just build your site, then youll find a file called algolia.json in the root, which we can use to update your index in Algolia.
Generate index file:
```bash
$ hugo
```
Create a new file in the root of your Hugo project called .env, and add the following contents:
```bash
ALGOLIA_APP_ID={{ YOUR_APP_ID }}
ALGOLIA_ADMIN_KEY={{ YOUR_ADMIN_KEY }}
ALGOLIA_INDEX_NAME={{ YOUR_INDEX_NAME }}
ALGOLIA_INDEX_FILE={{ PATH/TO/algolia.json }}
```
Now you can push your index to Algolia by simply running:
```bash
$ npm run algolia
```
Add the following variables to your hugo site config so the search page can get access to algolia index data in the cloud:
```
algolia_search = true
algolia_appId = {{ YOUR_APP_ID }}
algolia_indexName = {{ YOUR_INDEX_NAME }}
algolia_apiKey = {{ YOUR_SEARCH_ONLY_KEY }}
```
Open search page in your browser: http://localhost:1313/search
### Analytics
You can optionally enable Google or Baidu Analytics. Type your tracking code in the
```toml
[services]
[services.googleAnalytics]
id = "G-XXXXX"
```
Leave the `id` key empty to disable it.
### Baidu Analytics
You can optionally enable Baidu Analytics. Type your tracking code in the
```toml
ba_track_id = "XXXXXXXXXXXXXXXX"
```
Leave the 'ba_track_id ' key empty to disable it.
### Wechat Pay & Alipay Rewards
You can enable Wechat Pay & Alipay to allow readers send you money. So if they like your articles, you may even get rewards from your writing. Now you must be motivated to write more.
* Enable Wechat Pay & Alipay in the site config
```toml
reward = true
```
* Replace the QR codes of Wechat Pay & Alipay by overriding the photos in folder /static/img/reward/, otherwise the money will be sent to my accounts!
### Authoring mathematical and chemical equations with \(\KaTeX\)
If you want to make use if KaTeX on your site, enable and configure the goldmark `passthrough` extension
inside your `hugo.toml`/`hugo.yaml`/`hugo.json`. You may want to edit the definition of the delimiters to
meet your own needs. For details, see the official [Hugo docs](https://gohugo.io/content-management/mathematics/#step-1).
```toml
[markup]
[markup.goldmark]
[markup.goldmark.extensions]
[markup.goldmark.extensions.passthrough]
enable = true
[markup.goldmark.extensions.passthrough.delimiters]
block = [['\[', '\]'], ['$$', '$$']]
inline = [['\(', '\)']]
```
Afterwards, you can author mathematical and chemical equations on your site. Please read this [blog post] for more details on this subject.
## Thank
Thanks for the great jobs of [huxblog Jekyll Theme](https://github.com/Huxpro/huxpro.github.io) and [Clean Blog Jekyll Theme](https://github.com/BlackrockDigital/startbootstrap-clean-blog-jekyll) which are the the two upstream projects CleanWhite Hugo theme is based on.
## Feedback
If you find any problems, please feel free to [raise an issue](https://github.com/zhaohuabing/hugo-theme-cleanwhite/issues/new) or create a pull request to fix it.
If it's helpful for you, I would appreciate it if you could star this repository, thanks!

View File