参考

Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于 pod 上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上。可使用节点污点来控制允许工作负载在哪些节点上运行。

添加taint

查看节点taint在返回的节点说明中,查找 Taints 字段

1
kubectl  describe nodes  rancher-k8s-m1

可以使用命令 kubectl taint 给节点增加一个 taint。比如

1
kubectl taint nodes [NODE_NAME] [KEY]=[VALUE]:[EFFECT]

还可以向具有特定标签的节点添加污点:

1
2
kubectl taint node -l  node-role.kubernetes.io/etcd=tru dedicated=foo:PreferNoSchedule
#kubectl taint nodes dev-k8s-m1 node-role.kubernetes.io/controlplane=true:NoSchedule 给节点 dev-k8s-m1 增加一个 taint,它的 key 是 role.kubernetes.io/controlplane,value 是 true,effect 是 NoSchedule。除非pod有符合的容忍(toleration),否则不会被调度到dev-k8s-m1这个节点

删除taint

1
kubectl taint nodes dev-k8s-m1 role.kubernetes.io/controlplane:NoSchedule-

点污点是与“effect”相关联的键值对。以下是可用的effect:

1
2
3
NoSchedule:不会将不能容忍此污点的 Pod 调度到节点上。
PreferNoSchedule:Kubernetes 会避免将不能容忍此污点的 Pod 调度到节点上。这是一个优先选择或者软性版本的NoSchedule,调度系统会尽量避免调度不容忍这种污点的pod到带有此污点的节点上,但是并不是硬性要求
NoExecute:如果 Pod 已在节点上运行,则会将该 Pod 从节点中逐出;如果尚未在节点上运行,则不会将其调度到节点上。

toleration

可以在 PodSpec 中定义 pod 的 toleration。下面两个 toleration 均与上面例子中使用 kubectl taint 命令创建的 taint 相匹配,因此如果一个 pod 拥有其中的任何一个 toleration 都能够被分配到 dev-k8s-m1

1
2
3
4
5
tolerations:
- key: "role.kubernetes.io/controlplane"
operator: "Equal"
value: "true"
effect: "NoSchedule"

1
2
3
4
tolerations:
- key: "role.kubernetes.io/controlplane"
operator: "Exists"
effect: "NoSchedule"

只有pod的key和effect都和某一个污点的key与effect匹配,才被认为是匹配,并且要符合以下情形:
operator是Exists(这种情况下value不应当指定)
operator是 Equal 并且value相同
如果operator没有指定,则默认是Equal

1
2
3
4
5
6
tolerations:
- operator: "Exists"

tolerations:
- key: "role.kubernetes.io/controlplane"
operator: "Exists"

以上会匹配所有key为role.kubernetes.io/controlplane的所有taint节点
可以为一个节点(node)添加多个污点,也可以为一个pod添加多个容忍(toleration).kubernetes处理多个污点(taint)或者多个容忍(toleration)类似于过滤器:起初包含所有污点,然后忽略掉pod匹配的污点,剩下不可被忽略的污点决定此节点对pod的效果,特别地:
1 如果至少有一个不可忽略的NoSchedule类型的效果(effect),kubernetes不会调度pod到此节点上来.
2 如果没有不可忽略的NoSchedule类型的效果(effect),但是至少有一个PreferNoSchedule类型的效果,则kubernetes会尝试调度pod到此节点上
3 如果至少有一个NoExecute类型的效果(effect),则此pod会被驱离此节点(当然,前提此pod在此节点上),并且如果pod不在此节点上,也不会被调度到此节点上
假如你有一个以下类型的节点

1
2
3
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

类型的pod

1
2
3
4
5
6
7
8
9
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"

这种情况下,pod不会被调度到node1上,因为没有容忍(toleration)来匹配第三个taint.但是如果它运行在此节点上,它仍然可以继续运行在此节点上,因为它仅仅不匹配第三个taint.(而第三个taint的效果是NoSchedule,指示不要被调度到此节点)

tolerationSeconds

通常情况下,一个效果类型为NoExecute的taint被添加到一个节点上后,所有不容忍此taint的pod会被马上驱离,容忍的永远不会被驱离.但是效果类型NoExecute可以指定一个tolerationSeconds字段来指示当NoExecute效果类型的污点被添加到节点以后,pod仍然可以继续在在指定时间内留存在此节点上,优雅驱离

1
2
3
4
5
6
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600

在此段时间内如果污点被移除,则pod不会被驱离

业务场景:
cdn-LB-ingress-nginx
创建ingres-nginx的configmap文件

1
2
3
4
5
6
7
8
9
10
11
12
13
kubectl apply -f - <<EOF
apiVersion: v1
data:
compute-full-forwarded-for: "true"
forwarded-for-header: X-Forwarded-For
use-forwarded-headers: "true"
kind: ConfigMap
metadata:
labels:
app: ingress-nginx
name: nginx-configuration
namespace: ingress-nginx
EOF

主要参数https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#use-forwarded-headers

1
2
3
compute-full-forwarded-for: "true"
forwarded-for-header: X-Forwarded-For
use-forwarded-headers: "true"

为啥要用overlay2

docker centos(内核3.10)上默认存储驱动是devicemapper 的loop-lvm模式,这种模式是用文件模拟块设备,不推荐生产使用
direct lvm又不是一个开箱即用的模式,懒得配置
最关键的是 docker in docker的情况下 device mapper是行不通的,典型的场景就是用drone时,构建docker镜像就不能正常工作
overlay存储驱动层数过多时会导致文件链接数过多可能会耗尽inode
所以当前overlay2是个比较好的选择

内核
你需要一个高版本的内核推荐4.9以上,我们用的是4.14,如果使用低内核可能你一些FROM别的基础镜像就跑不了,如用overlay2在centos系统上跑FROM ubuntu的镜像(不是必现)

我们这里提供了一个免费的内核rpm包 这个在我们生产环境跑了将近一年没出任何问题

监控
overlay2如果不做一些特殊操作,cadvisor是监控不到容器内实际使用多少磁盘的,经过xfs和配额配置才能正常监控到
kubernetes
kubelet默认一次拉取一个镜像,设置为false可以同时拉取多个镜像,
前提是存储驱动要为overlay2,对应的Dokcer也需要增加下载并发数
serialize-image-pulls: ‘false’

使用xfs文件系统
不使用xfs就无法做到给每个容器限制10G的大小,就可能出现一个容器的误操作导致把机器盘全占完

我们使用了lvm去弄个分区出来做xfs文件系统,当然你也可以不用lvm

1
2
3
4
5
6
7
8
9
if which lvs &>/dev/null; then
echo ""; echo -e "Remove last docker lv and mount ......"
lvremove k8s/docker -y
lvcreate -y -n docker k8s -L 100G
mkfs.xfs -n ftype=1 -f /dev/mapper/k8s-docker
mkdir -p /var/lib/docker
mount -o pquota,uqnoenforce /dev/mapper/k8s-docker /var/lib/docker
echo -e "/dev/mapper/k8s-docker /var/lib/docker xfs defaults,pquota 0 0" >> /etc/fstab
fi

配置使用overlay2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# cat /etc/docker/daemon.json
{
"registry-mirrors": ["https://registry.docker-cn.com"],
"insecure-registries":["192.168.1.0/24"],
"storage-opts": [
"overlay2.override_kernel_check=true",
"overlay2.size=10G"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
systemctl daemon-reload
systemctl restart docker

这样就可以把每个容器磁盘大小限制在10G了

location匹配规则及优先级

  1. = 严格匹配这个查询。如果找到,停止搜索。
  2. ^~ 匹配路径的前缀,如果找到,停止搜索。
  3. ~ 为区分大小写的正则匹配
  4. ~ 为不区分大小写匹配
    优先级: =, ^~, ~/~
    , 无

Nginx禁止未绑定域名访问
nginx通过host配置确认转发到那台服务器处理。如果未匹配上就会转发到default_server节点来处理。例如配置如下:

1
2
3
4
server {
listen 80 default_server;
server_name nginx.net;
}

对于所有请求的HOST未匹配上的都会转发到该server处理。
通过如下配置,所有未匹配到server_name的请求都会返回403

1
2
3
4
5
server{
listen 80 default;
server_name _ ;
return 403;
}

其中_是无效域名的代表,同样可以写成其他例如”-” “@”等等。

Nginx rewrite配置

1)last
重新将rewrite后的地址在server标签中执行
2)break
将rewrite后的地址在当前location标签中执行
3)redirect
返回302临时重定向,浏览器地址会显示跳转后的URL地址。
4)permanent
返回301永久重定向,浏览器地址会显示跳转后的URL地址。
使用last和break,浏览器中的地址不会改变,其中需要注意的是last和break的区别:
使用alias指令必须用last标记;使用proxy_pass指令时,需要使用break标记。Last标记在本条rewrite规则执行完毕后,会对其所在server{……}标签重新发起请求,而break标记则在本条规则匹配完成后,终止匹配。

将url中以/wap/开头的请求转发到后台对应的某台server上,可以再Nginx里设置一个变量,来临时保存/wap/后面的路径信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
location ^~ /wap/
{
if ($request_uri ~ /wap/(\d+)/(.+))
{
set $bucketid $1;
set $params $2;
}
proxy_pass http://mx$bucketid.test.com:6601/$params;
}
也可以首先rewrite一下,然后再代理:
location ^~ /wap/{
rewrite /wap/(\d+)/(.+) /$2?$args break;
proxy_pass http://mx$1.test.com:6601;
}
或者
location ~* /wap/(\d+)/(.+)
{
proxy_pass http://mx$1.test.com:6601/$2?$args;
}

注意上面最后的?$args,表明把原始url最后的get参数也给代理到后台
如果在proxy_pass中使用了变量(不管是主机名变量$1或后面的$2变量),则必须得加这段代码
但如果proxy_pass没用任何变量,则不需要加,它默认会把所有的url都给代理到后台,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
location ~* /wap/(\d+)/(.+)
{
proxy_pass http://mx.test.com:6601;
}
另外还需要注意url的/问题
下面四种情况分别用http://192.168.1.4/proxy/test.html 进行访问。
第一种:
location /proxy/ {
proxy_pass http://127.0.0.1:81/;
}
会被代理到http://127.0.0.1:81/test.html 这个url
第二种(相对于第一种,最后少一个 /)
location /proxy/ {
proxy_pass http://127.0.0.1:81;
}
会被代理到http://127.0.0.1:81/proxy/test.html 这个url
第三种:
location /proxy/ {
proxy_pass http://127.0.0.1:81/ftlynx/;
}
会被代理到http://127.0.0.1:81/ftlynx/test.html 这个url。
第四种情况(相对于第三种,最后少一个 / ):
location /proxy/ {
proxy_pass http://127.0.0.1:81/ftlynx;
}
会被代理到http://127.0.0.1:81/ftlynxtest.html 这个url
也就是说如果proxy_pass只是后端服务器的IP,最后没有/的话,就会将全uri带过去。
而如果proxy_pass带了/的话,只是带最后访问的文件。

root与alias区别

一句话概括,root对应的目录会加上location部分去找文件,而alias则不会
Nginx 配置文件 server 中指定两个 location 执行,分别为root 和 alias 指令:
alisa:

1
2
3
location /static/ {
alias /www/test/;
}

按照上述配置,则访问 /static/ 目录里面的文件时,nginx 会去 /www/test/ 目录找文件
请求 http://idcsec.com/static/a.gif 时,在服务器查找的资源路径是:/www/test/a.gif

root:

1
2
3
location /static/ {
root /www/test;
}

按照这种配置,则访问 /static/ 目录下的文件时,nginx 会去 /www/test/static/ 目录下找文件
请求 http://idcsec.com/static/a.gif 这个地址时,那么在服务器里面对应的真正的资源是 /www/test/static/a.gif文件,真实的路径是root指定的值加上location指定的值

  • alias 是一个目录别名的定义,root 则是最上层目录的定义。
  • 另一个区别是 alias 后面必须要用 “/” 结束,否则会找不到文件,而 root 则对 ”/” 可有可无。
  • 误区:认为 root 是指 /www/test目录下,而应该是 /www/test/static 目录 。

Nginx静动分离

1
2
3
4
5
6
location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ 
{
root /usr/local/nginx/html/website/static/;
#expires定义用户浏览器缓存的时间为7天,如果静态页面不常更新,可以设置更长,这样可以节省带宽和缓解服务器的压力
expires 1d;
}

Nginx日志 切割

1
2
3
4
5
6
7
8
9
#nginx日志切割脚本
#!/bin/bash
logs_path="/usr/local/nginx/logs/"
pid_path="/usr/local/nginx/logs/nginx.pid"
mv ${logs_path}access.log ${logs_path}access_$(date -d "yesterday" +"%Y%m%d").log
kill -USR1 `cat ${pid_path}`
crontab -e

0 0 * * * bash /usr/local/nginx/nginx_log.sh

Nginx 443强制跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
server {
listen 443;
server_name idcsec.com.com;
ssl on;
ssl_certificate /etc/nginx/ssl/idcsec.com.crt;
ssl_certificate_key /etc/nginx/ssl/idcsec.com.com.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://test_domain;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;

}
}
server {
listen 80;
server_name idcsec.com.com;
location / {
return 301 https://$server_name$request_uri;
}
}

集群环境查看之前的文档

单机版搭建

安装依赖

1
# yum install git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl-devel -y

创建数据目录

1
# mkdir -p /data/fastdfs/{tracker,storage}

下载安装包

安装libfatscommon

1
2
git clone https://github.com/happyfish100/libfastcommon.git --depth 1
./make.sh && ./make.sh install

安装FastDFS

1
2
3
git clone https://github.com/happyfish100/fastdfs.git --depth 1
cd fastdfs/
./make.sh && ./make.sh install

配置文件生成

1
2
3
4
5
cp /etc/fdfs/tracker.conf.sample /etc/fdfs/tracker.conf #tarcker服务配置文件
cp /etc/fdfs/storage.conf.sample /etc/fdfs/storage.conf #storage服务配置文件
cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf #客户端文件,测试用
cp conf/http.conf /etc/fdfs/ #供nginx访问使用
cp conf/mime.types /etc/fdfs/ #供nginx访问使用

安装fastdfs-nginx-module需要在编译nginx时候添加这个模块

1
2
git clone https://github.com/happyfish100/fastdfs-nginx-module.git --depth 1
cp fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/

安装nginx

1
2
3
4
5
~]# wget http://nginx.org/download/nginx-1.14.0.tar.gz
tar -zxvf nginx-1.14.0.tar.gz
cd nginx-1.14.0
./configure --add-module=/root/fastdfs-nginx-module/src/
make && make instal

tracker配置

1
2
3
4
5
6
7
vim /etc/fdfs/tracker.conf
#需要修改的内容如下
port=22122 # tracker服务器端口(默认22122,一般不修改)
base_path=/data/fastdfs/tracker # 存储日志和数据的根目录
#保存后启动
/etc/init.d/fdfs_trackerd start #启动tracker服务
chkconfig fdfs_trackerd on #自启动tracker服务

storage配置

1
2
3
4
5
6
7
8
9
10
vim /etc/fdfs/storage.conf
#需要修改的内容如下
port=23000 # storage服务端口(默认23000,一般不修改)
base_path=/data/fastdfs/storage # 数据和日志文件存储根目录
store_path0=/data/fastdfs/storage/ # 第一个存储目录
tracker_server=192.168.1.38:22122 # tracker服务器IP和端口
http.server_port=8888 # http访问文件的端口(默认8888,看情况修改,和nginx中保持一致)其实现在这版本不需要
#保存后启动
/etc/init.d/fdfs_storaged start #启动storage服务
chkconfig fdfs_storaged on

检查fdfs状态
fdfs_monitor /etc/fdfs/storage.conf

配置nginx访问

vim /etc/fdfs/mod_fastdfs.conf

1
2
3
4
需要修改的内容如下
tracker_server=192.168.1.38:22122
url_have_group_name=true
store_path0=/data/fastdfs/storage

1
2
3
4
5
6
7
8
9
10
11
12
13
vi /usr/local/nginx/conf/nginx.conf
#添加如下配置
server {
listen 8888; ## 该端口为storage.conf中的http.server_port相同 其实没必要
server_name localhost;
location ~/group[0-9]/ {
ngx_fastdfs_module;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

vim /etc/fdfs/client.conf

修改的内容如下

1
2
base_path=/data/fastdfs/tracker
tracker_server=192.168.1.38:22122 #tracker IP地址
1
2
3
4
[root@ecs-1d0e ~]# fdfs_upload_file  /etc/fdfs/client.conf /root/1.txt 
group1/M00/00/00/wKgBJlyrRtWAR5dVAAAAB4upymk232.txt
[root@ecs-1d0e ~]# curl http://192.168.1.38:8888/group1/M00/00/00/wKgBJlyrRtWAR5dVAAAAB4upymk232.txt
111111

FastDFS 防盗链开启

但是这样是不安全的,因为只要知道ip和文件路径,就能下载所需文件。因此采用Token方式防盗链。
cp /root/fastdfs/conf/anti-steal.jpg /etc/fdfs/
vim /etc/fdfs/http.conf

1
2
3
4
5
#开启token校验  
http.anti_steal.check_token=true
#设置校验失败后显示的警告图片
http.anti_steal.token_check_fail=/etc/fdfs/anti-steal.jpg
重启nginx

注意:此任务使用新的 v1alpha3 流量管理 API。旧的 API 已被弃用,ngress Gateway 组件替代了符合 Kubernetes 规范的 Ingress Controller,因此对入站流量具有了更大的控制能力。

1、创建pod应用,确保namespace开启自动注入Pod所在的namespace包含istio-injection=enabled的Label
否则就必须在部署 tomcatapp应用程序之前手动注入 Sidecar

1
kubectl apply -f <(istioctl kube-inject -f tomcat-demo.yaml) //namespaceswei

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
kubectl apply -f tomcat-demo.yaml
[root@k8s-master ~]# cat tomcat-demo.yaml
apiVersion: v1
kind: Service
metadata:
annotations:
labels:
app: tomcat-istio
name: tomcat-istio
namespace: default
spec:
ports:
- name: 8080-8080
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: tomcat-istio
type: NodePort
status:
loadBalancer: {}
---
apiVersion: v1
kind: Pod
metadata:
name: tomcat-istio
annotations:
spec:
replicas: 1
template:
metadata:
labels:
app: tomcat-istio
spec:
containers:
- name: tomcat-istio
image: toamcat:demo
env:
- name: JAVA_OPTS
value: "-server -Xms4096M -Xmx4096M -Xss256K -Dmy.pod.name=$MY_POD_NAME -Djava.awt.headless=true -Dfile.encoding=utf-8 -XX:MaxPermSize=256M -XX:PermSize=128M"
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name

创建Gateway

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kubectl get svc -n istio-system -l istio=ingressgateway
cat <<EOF | istioctl create -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: tomcatapp-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "tomcat.idcsec.com"
EOF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
创建VirtualService组绑定网关
cat <<EOF | istioctl create -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: tomcat-istio
spec:
hosts:
- "tomcat.idcsec.com"
gateways:

- tomcatapp-gateway
http:

- match:
- uri:
exact: /

true- uri:
prefix: /

route:
- destination:
host: tomcat-istio

port:
number: 8080
EOF

接下来就可以在浏览器的访问域名自行修改host或者使用curl
一个简单使用IstioGateway 配置资源允许外部流量进入 Istio 服务网就完成

清理

删除 Gateway 和 VirtualService,并关闭 tomcat-demo 服务:

1
2
3
$ istioctl delete gateway tomcatapp-gateway
$ istioctl delete virtualservice tomcat-istio
$ kubectl delete --ignore-not-found=true -f tomcat-demo.yaml

安装Istio

1
curl -L https://git.io/getLatestIstio | sh -

将在运行上述命令的同一目录中找到一个文件夹istio-1.0.6。将位置istio-1.0.6/ bin添加到PATH变量,以便于访问Istio二进制文件。
将Ingress Gateway服务从LoadBalancer类型更改为NodePort
Istio为Kubernetes提供了许多自定义资源定义(CRD)。它们帮助我们从kubectl操纵虚拟服务,规则,网关和其他特定于Istio的对象。让我们在部署实际服务网格之前安装CRD。

1
kubectl apply -f install/kubernetes/helm/istio/templates/crds.yaml

查看安装的CRD:

1
kubectl get CustomResourceDefinition

Kubernetes中安装Istio核心组件

1
kubectl apply -f install / kubernetes / istio-demo.yaml

1
kubectl get svc -n istio-system -l istio=ingressgateway

*当前EXTERNAL-IP处于pending状态,为了使得可以从外部访问,修改istio-ingressgateway这个Service的externalIps,所以这个指定一个192.168
.19.223作为externalIp。也是可以设置NodePort 通过节点的nodeip:PORT访问

1
2
3
[root@k8s-master istio-1.0.6]# kubectl get svc  -n istio-system -l istio=ingressgateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway LoadBalancer 10.102.137.201 192.168.19.223 80:31380/TCP,443:31390/TCP,31400:31400/TCP,15011:31211/TCP,8060:31598/TCP,853:31745/TCP,15030:30600/TCP,15031:30012/TCP 2m21s

验证安装

1
2
kubectl get svc -n istio-system
kubectl get pod -n istio-system

部署Bookinfo应用自动注入sidecar
kubectl get pod -n istio-system | grep injector
istio-sidecar-injector可以自动将Envoy容器作为sidecar注入到Pod中,Pod所在的namespace包含istio-injection=enabled的Label。

1
2
3
kubectl label namespace default istio-injection=enabled
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
kubectl apply -f samples/bookinfo/platform/kube/bookinfo-ingress.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@k8s-master istio-1.0.6]# kubectl get pod,svc   | grep -vE "tomcat|iperf"
NAME READY STATUS RESTARTS AGE
pod/details-v1-68c7c8666d-l9qsc 2/2 Running 2 18m

pod/productpage-v1-54d799c966-8x6p8 2/2 Running 2 18m
pod/ratings-v1-8558d4458d-jzx2t 2/2 Running 0 18m
pod/reviews-v1-cb8655c75-ssnmm 2/2 Running 0 18m
pod/reviews-v2-7fc9bb6dcf-7rq98 2/2 Running 0 18m
pod/reviews-v3-c995979bc-qckj7 2/2 Running 0 18m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/details ClusterIP 10.105.255.93 <none> 9080/TCP 18m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d4h
service/productpage ClusterIP 10.96.39.50 <none> 9080/TCP 18m
service/ratings ClusterIP 10.98.120.231 <none> 9080/TCP 18m
service/reviews ClusterIP 10.98.253.153 <none> 9080/TCP 18m

通过Istio Ingress Gateway暴露Bookinfo应用
创建bookinfo的Gateway和VirtualService:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[root@k8s-master istio-1.0.6]# cat samples/bookinfo/networking/bookinfo-gateway.yaml 
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80

name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3

kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- "*"
gateways:

- bookinfo-gateway #对应上面的网关名称
http:

- match:
- uri:
exact: /productpage

- uri:
exact: /login

- uri:
exact: /logout

- uri:
prefix: /api/v1/products

route:
- destination:
host: productpage

port:
number: 9080
kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

1
2
3
[root@k8s-master istio-1.0.6]# kubectl  get gateway
NAME AGE
bookinfo-gateway 1m

访问http://192.168.19.223/productpage
临时映射grafana端口

1
kubectl -n istio-system port-forward --address 0.0.0.0  $(kubectl -n istio-system get pod -l app=grafana -o jsonpath='{.items[0].metadata.name}') 3333:3000 &

使用 Istio 网关配置 Ingress暴露grafana服务

Ingress Gateway描述了在网格边缘操作的负载平衡器,用于接收传入的 HTTP/TCP 连接。它配置暴露的端口,协议等,但与 Kubernetes Ingress Resources 不同,它不包括任何流量路由配置。流入流量的流量路由使用 Istio 路由规则进行配置,与内部服务请求完全相同。

创建一个 Istio Gateway:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cat <<EOF | istioctl create -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: grafana-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "grafana.idcsec.com"
EOF

为通过 Gateway 进入的流量配置路由创建VirtualService:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cat <<EOF | istioctl create -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: grafana
namespace: istio-system
spec:
hosts:
- "grafana.idcsec.com"
gateways:

- grafana-gateway
http:

- match:
- uri:
prefix: /

route:
- destination:
port:

number: 3000
host: grafana
EOF

使用浏览器访问 Ingress 服务
在浏览器中输入 grafana 服务的地址是不会生效的,这是因为因为我们没有办法让浏览器像 curl 一样装作访问grafana.idcse.com。因为有正常配置的主机和 DNS 记录,这种做法就能够成功了——只要简单的在浏览器中访问由域名构成的 URL 即可,例如 http://grafana.idcse.com/。
要解决此问题以进行简单的测试和演示,我们可以在 Gateway 和 VirutualService 配置中为hosts:使用通配符值 *
20190301110526.png
20190301110556.png
删除 Gateway 和 VirtualService,并关闭 httpbin 服务:
·····
$istioctl delete gateway grafana-gateway
$ istioctl delete virtualservice grafana
····
卸载Istio
···
kubectl delete -f install/kubernetes/istio-demo.yaml
kubectl delete -f install/kubernetes/helm/istio/templates/crds.yaml -n istio-system
···

tomcat获取kubernetes自定义变量
1、设置kubernetes自定义变量MY_POD_NAME
2、设置tomcat的JAVA_OPTS -D自定义变量名称:值(yaml里面定义的变量)
3、tomcat配置文件引用变量,获取kubernetes的pod主机名,处理多个pod日志按照主机名命名解决日志交叉写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@k8s-master ~]# cat tomcat-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
name: tomcat-istio
annotations:
spec:
containers:
- name: tomcat-istio
image: toamcat:demo
env:
- name: JAVA_OPTS
value: "-server -Xms4096M -Xmx4096M -Xss256K -Dmy.pod.name=$MY_POD_NAME -Djava.awt.headless=true -Dfile.encoding=utf-8 -XX:MaxPermSize=256M -XX:PermSize=128M"
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
1
2
DOckerfile
sed -i "prefix="localhost_access_log." suffix=".txt"/prefix="$\{my.pod.name\}__access_log" suffix=".txt"/g" server.xml

20190228230738.png

节点规划

master 192.168.19.222
node1 192.168.19.223
node2 192.168.19.224

软件版本

操作系统:CentOS Linux release7 4.4.174-1.el7.elrepo.x86_64
Docker版本:18.06.2-ce
kubernetes版本:1.13.3

环境准备

配置SSH免密登录

关闭所有节点防火墙

1
[root@k8s-master ~]# service firewalld stop && systemctl disable firewalld

关闭所有节点selinux

1
2
[root@k8s-master ~]# setenforce 0
[root@k8s-master ~]# sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config

设置所有节点/etc/hosts文件

1
2
3
4
5
6
[root@k8s-master ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.19.222 k8s-master
192.168.19.223 node1
192.168.19.224 node2

haproxy安装脚本centos7.x

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/bin/bash
#description: configure and install haproxy software

SOFTDIR=/usr/local/src
H_SOFT="haproxy-1.7.10.tar.gz"
H_SOFT_DIR="haproxy-1.7.10"
H_PREFIX="/usr/local/haproxy"
H_CONFIG_DIR="/etc/haproxy"
H_WORK_DIR="/var/lib/haproxy"
USER="haproxy"
GROUP="haproxy"

if ! id $USER &>/dev/null;then
groupadd -g 3320 -r $GROUP
useradd -u 3320 -g $GROUP -M -s /sbin/nologin $USER
fi

#install haproxy
yum groupinstall -y "Development tools"
cd $SOFTDIR && [ ! -f ${H_SOFT} ] && wget https://www.haproxy.org/download/1.7/src/${H_SOFT}
[ ! -d ${H_SOFT_DIR} ] && tar xf ${H_SOFT}
cd ${SOFTDIR}/${H_SOFT_DIR} && make TARGET=linux2628 ARCH=x86_64 PREFIX=${H_PREFIX} && make install PREFIX=${H_PREFIX}
if [ $? -ne 0 ];then
echo "install haproxy fail"
exit 1
fi

#create haproxy configure file
[ ! -d ${H_WORK_DIR} ] && mkdir -p ${H_WORK_DIR} && chown -R ${USER}.${GROUP} ${H_WORK_DIR}
[ ! -d ${H_CONFIG_DIR} ] && mkdir -p ${H_CONFIG_DIR}
cat > /etc/haproxy/haproxy.cfg <<EOF
global
log 127.0.0.1 local2
chroot ${H_WORK_DIR}
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
stats socket ${H_WORK_DIR}/stats

defaults
mode tcp
log global
option tcplog
option dontlognull
option redispatch
retries 3
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout check 10s
maxconn 3000

listen stats
bind *:10800
mode http
option httplog
log 127.0.0.1 local3
stats refresh 30s
stats enable
stats uri /haproxyadmin?stats
stats realm Haproxy\ Statistics
stats auth Haproxyadmin:Aa123456
stats hide-version
stats admin if TRUE

frontend k8s-https-api
bind *:8443
mode tcp
option tcplog
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
default_backend k8s-https-api

backend k8s-https-api
mode tcp
option tcplog
option tcp-check
balance roundrobin
default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 2000 maxqueue 256 weight 100
server k8s-https-api-1 192.168.1.137:6443 check
server k8s-https-api-2 192.168.1.138:6443 check

frontend k8s-http-api
bind *:80
mode tcp
option tcplog
default_backend k8s-http-api

backend k8s-http-api
mode tcp
option tcplog
option tcp-check
balance roundrobin
default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 2000 maxqueue 256 weight 100
server k8s-http-api-1 192.168.1.137:8080 check
server k8s-http-api-2 192.168.1.138:8080 check
EOF

#copy haproxy start script
cd ${SOFTDIR}/${H_SOFT_DIR}
cp examples/haproxy.init /etc/init.d/haproxy
cd /etc/init.d/ && sed -i 's/\/usr\/sbin\/'\$BASENAME'/\/usr\/local\/haproxy\/sbin\/'\$BASENAME'/g' haproxy
chmod u+x /etc/init.d/haproxy
chkconfig --add haproxy
chkconfig haproxy on
ln -s /usr/local/haproxy/sbin/haproxy /usr/sbin/haproxy
if [ "ss -tunlp|grep 80|awk -F '[ :]+' '{print $6}'" = "80" ];then
echo "haproxy start fail,port 80"
exit 1
else
service haproxy start
fi
#configure log
sed -i 's/^#$ModLoad imudp/$ModLoad imudp/g' /etc/rsyslog.conf
sed -i 's/^#$UDPServerRun 514/$UDPServerRun 514/g' /etc/rsyslog.conf
echo "local2.* /var/log/haproxy.log" >> /etc/rsyslog.conf
echo "local3.* /var/log/haproxy_stats.log" >> /etc/rsyslog.conf
systemctl restart rsyslog

基本配置说明记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
global               #全局设置
log 127.0.0.1 local2 #日志输出配置,所有日志都记录在本机,通过local2输出
#log loghost local0 info
maxconn 4096 #最大连接数
chroot /usr/local/haproxy
uid 99 #所属运行的用户uid
gid 99 #所属运行的用户组
group haproxy #用户组
daemon #后台运行haproxy
nbproc 1 #启动1个haproxy实例
pidfile /usr/local/haproxy/haproxy.pid #将所有进程PID写入pid文件
#debug
#quiet

defaults #默认设置
#log global
log 127.0.0.1 local3 #日志文件的输出定向

#默认的模式:tcp|http|health
mode http #所处理的类别,默认采用http模式

option httplog #日志类别,采用http日志格式`
option dontlognull
option forwardfor #将客户端真实ip加到HTTP Header中供后端服务器读取
option retries 3 #三次连接失败则认证服务器不可用
option httpclose #每次请求完毕后主动关闭http通道,haproxy不支持keep-alive,只>能模拟这种模式的实现
retries 3 #3次连接失败就认为服务器不可用,主要通过后面的check检查
option redispatch #当serverid对应的服务器挂掉后,强制定向到其他健康服务器
option abortonclose #当服务器负载很高时,自动结束掉当前队列中处理比较久的链接
maxconn 2000 #默认最大连接数

timeout connect 5000 #连接超时时间
timeout client 50000 #客户端连接超时时间
timeout server 50000 #服务器端连接超时时间

stats enable
stats uri /haproxy-stats #haproxy监控页面的访问地址
stats auth test:test123 #设置监控页面的用户和密码
stats hide-version #隐藏统计页面的HAproxy版本信息

frontend http-in #前台
bind *:81
mode http
option httplog
log global
default_backend htmpool #静态服务器池

backend htmpool #后台
balance leastconn #负载均衡算法
option httpchk HEAD /index.html HTTP/1.0 #健康检查
server web1 192.168.2.10:80 cookie 1 weight 5 check inter 2000 rise 2 fall 3
server web2 192.168.2.11:80 cookie 2 weight 3 check inter 2000 rise 2 fall 3
# web1/web2:自定义服务器别名
# 192.168.2.10:80:服务器IP:Port
# cookie 1/2:表示serverid
# weight: 服务器权重,数字越大分配到的请求数越高
# check: 接受定时健康检查
# inter 2000: 检查频率
# rise 2: 两次检测正确认为服务器可用
# fall 3: 三次失败认为服务器不可用

listen w.gdu.me 0.0.0.0:80
option httpchk GET /index.html
server s1 192.168.2.10:80 weight 3 check
server s3 192.168.2.11:80 weight 3 check

# Haproxy统计页面
# --------------------------------------------------------------------------------------------
listen haproxy_stats
bind 0.0.0.0:1080 #侦听IP:Port
mode http
log 127.0.0.1 local 0 err #err|warning|info|debug]
stats refresh 30s
stats uri /haproxy-stats
stats realm Haproxy\ Statistics
stats auth admin:admin
stats auth test:test
stats hide-version
stats admin if TRUE #手工启用/禁用后端服务器


# 网站检测listen配置
# --------------------------------------------------------------------------------------------
listen site_status
bind 0.0.0.0:1081
mode http
log 127.0.0.1 local0 err

#网站健康检查URI,用来检测Haproxy管理的网站是否可能,正常返回200、异常返回500
monitor-uri /site_status

#定义网站down时的策略
#当backend中的有效服务器数<1时,返回true
acl site_dead nbsrv(denali_server) lt 1
acl site_dead nbsrv(tm_server) lt 1
acl site_dead nbsrv(mms_server) lt 1

#当满足策略的时候返回http-500,否则返回http-200
monitor fail if site_dead

#声名一个监测请求的来源网络
monitor-net 192.168.0.252/31


# https的配置方法
# --------------------------------------------------------------------------------------------
listen login_https_server
bind 0.0.0.0:443 #绑定HTTPS的443端口
mode tcp #https必须使用tcp模式
log global
balance roundrobin
option httpchk GET /member/login.jhtml HTTP/1.1\r\nHost:login.daily.taobao.net
#回送给server的端口也必须是443
server vm94f.sqa 192.168.212.94:443 check port 80 inter 6000 rise 3 fall 3
server v215120.sqa 192.168.215.120:443 check port 80 inter 6000 rise 3 fall 3


# frontend配置
# --------------------------------------------------------------------------------------------
frontend http_80_in
bind 0.0.0.0:80 #监听端口
mode http #http的7层模式
log global #使用全局的日志配置
option httplog #启用http的log
option httpclose #每次请求完毕后主动关闭http通道,HA-Proxy不支持keep-alive模式
option forwardfor ##如果后端服务器需要获得客户端的真实IP需要配置次参数,将可以从Http Header中获得客户端IP

#HAProxy的日志记录内容配置
capture request header Host len 40 # 请求中的主机名
capture request header Content-Length len 10 # 请求中的内容长度
capture request header Referer len 200 # 请求中的引用地址
capture response header Server len 40 # 响应中的server name
capture response header Content-Length len 10 # 响应中的内容长度(可配合option logasap使用)
capture response header Cache-Control len 8 # 响应中的cache控制
capture response header Location len 20 # 响应中的重定向地址


#ACL策略规则定义
#-------------------------------------------------
#如果请求的域名满足正则表达式返回true(-i:忽略大小写)
acl denali_policy hdr_reg(host) -i ^(www.gemini.taobao.net|my.gemini.taobao.net|auction1.gemini.taobao.net)$

#如果请求域名满足trade.gemini.taobao.net返回true
acl tm_policy hdr_dom(host) -i trade.gemini.taobao.net

#在请求url中包含sip_apiname=,则此控制策略返回true,否则为false
acl invalid_req url_sub -i sip_apiname=

#在请求url中存在timetask作为部分地址路径,则此控制策略返回true,否则返回false
acl timetask_req url_dir -i timetask

#当请求的header中Content-length等于0时返回true
acl missing_cl hdr_cnt(Content-length) eq 0


#ACL策略匹配相应
#-------------------------------------------------
#当请求中header中Content-length等于0阻止请求返回403
#block表示阻止请求,返回403错误
block if missing_cl

#如果不满足策略invalid_req,或者满足策略timetask_req,则阻止请求
block if !invalid_req || timetask_req

#当满足denali_policy的策略时使用denali_server的backend
use_backend denali_server if denali_policy

#当满足tm_policy的策略时使用tm_server的backend
use_backend tm_server if tm_policy

#reqisetbe关键字定义,根据定义的关键字选择backend
reqisetbe ^Host:\ img dynamic
reqisetbe ^[^\ ]*\ /(img|css)/ dynamic
reqisetbe ^[^\ ]*\ /admin/stats stats

#以上都不满足的时候使用默认mms_server的backend
default_backend mms_server

#HAProxy错误页面设置
errorfile 400 /home/admin/haproxy/errorfiles/400.http
errorfile 403 /home/admin/haproxy/errorfiles/403.http
errorfile 408 /home/admin/haproxy/errorfiles/408.http
errorfile 500 /home/admin/haproxy/errorfiles/500.http
errorfile 502 /home/admin/haproxy/errorfiles/502.http
errorfile 503 /home/admin/haproxy/errorfiles/503.http
errorfile 504 /home/admin/haproxy/errorfiles/504.http


# backend的设置
# --------------------------------------------------------------------------------------------
backend mms_server
mode http #http的7层模式
balance roundrobin #负载均衡的方式,roundrobin平均方式
cookie SERVERID #允许插入serverid到cookie中,serverid后面可以定义

#心跳检测的URL,HTTP/1.1¥r¥nHost:XXXX,指定了心跳检测HTTP的版本,XXX为检测时请求
#服务器的request中的域名是什么,这个在应用的检测URL对应的功能有对域名依赖的话需要设置
option httpchk GET /member/login.jhtml HTTP/1.1\r\nHost:member1.gemini.taobao.net

#服务器定义,cookie 1表示serverid为1,check inter 1500 是检测心跳频率
#rise 3是3次正确认为服务器可用,fall 3是3次失败认为服务器不可用,weight代表权重
server mms1 10.1.5.134:80 cookie 1 check inter 1500 rise 3 fall 3 weight 1
server mms2 10.1.6.118:80 cookie 2 check inter 1500 rise 3 fall 3 weight 2


backend denali_server
mode http
balance source #负载均衡的方式,source根据客户端IP进行哈希的方式
option allbackups #设置了backup的时候,默认第一个backup会优先,设置option allbackups后所有备份服务器权重一样

#心跳检测URL设置
option httpchk GET /mytaobao/home/my_taobao.jhtml HTTP/1.1\r\nHost:my.gemini.taobao.net

#可以根据机器的性能不同,指定连接数配置,如minconn 10 maxconn 20
server denlai1 10.1.5.114:80 minconn 4 maxconn 12 check inter 1500 rise 3 fall 3
server denlai2 10.1.6.104:80 minconn 10 maxconn 20 check inter 1500 rise 3 fall 3
#备份机器配置,正常情况下备机不会使用,当主机的全部服务器都down的时候备机会启用
server dnali-back1 10.1.7.114:80 check backup inter 1500 rise 3 fall 3
server dnali-back2 10.1.7.114:80 check backup inter 1500 rise 3 fall 3


backend tm_server
mode http
balance leastconn #负载均衡的方式,leastcon选择当前请求数最少的服务器
option httpchk GET /trade/itemlist/prepayCard.htm HTTP/1.1\r\nHost:trade.gemini.taobao.net
server tm1 10.1.5.115:80 check inter 1500 rise 3 fall 3
server tm2 10.1.6.105:80 check inter 1500 rise 3 fall 3


#reqisetbe自定义关键字匹配backend部分
backend dynamic
mode http
balance source
option httpchk GET /welcome.html
server denlai1 10.3.5.114:80 check inter 1500 rise 3 fall 3
server denlai2 10.4.6.104:80 check inter 1500 rise 3 fall 3

backend stats
mode http
balance source
option httpchk GET /welcome.html
server denlai1 10.5.5.114:80 check inter 1500 rise 3 fall 3
server denlai2 10.6.6.104:80 check inter 1500 rise 3 fall 3