Amazon Linux安装Mariadb

|默认输入yum -y mariadb mariadb-server

提示No package mariadb , mariadb-server

第一步,创建repo

vim /etc/yum.repos.d/MariaDB.repo

内容:

# http://downloads.mariadb.org/mariadb/repositories/
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.2/centos6-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

保存,同时更新yum

yum clean all
yum update

最后再次运行:

yum install -y MariaDB-server MariaDB-client

搞定,安装完成

Kubernetes调度之亲和性和反亲和性

背景

Kubernetes中的调度策略可以大致分为两种,一种是全局的调度策略,要在启动调度器时配置,包括kubernetes调度器自带的各种predicates和priorities算法,具体可以参看文章《Kubernetes调度详解》;另一种是运行时调度策略,包括nodeAffinity(主机亲和性),podAffinity(POD亲和性)以及podAntiAffinity(POD反亲和性)。

nodeAffinity 主要解决POD要部署在哪些主机,以及POD不能部署在哪些主机上的问题,处理的是POD和主机之间的关系。

podAffinity 主要解决POD可以和哪些POD部署在同一个拓扑域中的问题(拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的cluster、zone等。),podAntiAffinity主要解决POD不能和哪些POD部署在同一个拓扑域中的问题。它们处理的是Kubernetes集群内部POD和POD之间的关系。

三种亲和性和反亲和性策略的比较如下表所示:

未分类

本文主要介绍如何使用亲和性和反亲和性做资源调度。

使用场景

nodeAffinity使用场景 :

  • 将S1服务的所有Pod部署到指定的符合标签规则的主机上。
  • 将S1服务的所有Pod部署到除部分主机外的其他主机上。

podAffinity使用场景 :

  • 将某一特定服务的pod部署在同一拓扑域中,不用指定具体的拓扑域。
  • 如果S1服务使用S2服务,为了减少它们之间的网络延迟(或其它原因),把S1服务的POD和S2服务的pod部署在同一拓扑域中。

podAntiAffinity使用场 景:

  • 将一个服务的POD分散在不同的主机或者拓扑域中,提高服务本身的稳定性。
  • 给POD对于一个节点的独占访问权限来保证资源隔离,保证不会有其它pod来分享节点资源。
  • 把可能会相互影响的服务的POD分散在不同的主机上。

对于亲和性和反亲和性,每种都有三种规则可以设置:

RequiredDuringSchedulingRequiredDuringExecution :在调度期间要求满足亲和性或者反亲和性规则,如果不能满足规则,则POD不能被调度到对应的主机上。在之后的运行过程中,如果因为某些原因(比如修改label)导致规则不能满足,系统会尝试把POD从主机上删除(现在版本还不支持)。

RequiredDuringSchedulingIgnoredDuringExecution :在调度期间要求满足亲和性或者反亲和性规则,如果不能满足规则,则POD不能被调度到对应的主机上。在之后的运行过程中,系统不会再检查这些规则是否满足。

PreferredDuringSchedulingIgnoredDuringExecution :在调度期间尽量满足亲和性或者反亲和性规则,如果不能满足规则,POD也有可能被调度到对应的主机上。在之后的运行过程中,系统不会再检查这些规则是否满足。

使用示例

使用POD亲和性调度时要先开启Kubernetes调度器的MatchInterPodAffinity筛选功能,具体的操作方式是修改调度器的配置文件,在predicates中增加如下内容:

{"name": "MatchInterPodAffinity"}

测试环境的主机信息如下:

未分类

其中每个主机上都有 beta.kubernetes.io/arch、beta.kubernetes.io/os、kubernetes.io/hostname这几个标签,在测试过程中把这些标签当做拓扑域使用。

nodeAffinity 使用示例:

使用nodeAffinity把POD部署到主机mesos-slave1和mesos-slave2上,yaml定义如下:

{
  "nodeAffinity": {
    "requiredDuringSchedulingIgnoredDuringExecution": {
      "nodeSelectorTerms": [
        {
          "matchExpressions": [
            {
              "key": "kubernetes.io/hostname",
              "operator": "In",
              "values": [
                "mesos-slave1",
                "mesos-slave2"
              ]
            }
          ]
        }
      ]
    }
  }
}

创建一个有6个POD的RC,结果如下:

未分类

从结果可以看出POD被部署到了mesos-slave1和mesos-slave2上,mesos-slave3上没有部署POD。

podAffinity使用示例 :

使用kubernetes.io/hostname作为拓扑域,把pod创建在同一主机上。其中matchExpressions中填写内容对应到RC中POD自身的标签。可以通过修改需要匹配的标签内容来控制把一个服务中的POD和其它服务的POD部署在同一主机上。

yaml中的定义如下:

{
  "podAffinity": {
    "requiredDuringSchedulingIgnoredDuringExecution": [
      {
        "labelSelector": {
          "matchExpressions": [
            {
              "key": "name",
              "operator": "In",
              "values": [
                "node-rc"
              ]
            }
          ]
        },
        "topologyKey": "kubernetes.io/hostname"
      }
    ]
  }
}

创建一个有3个POD的RC,结果如下:

未分类

所有创建的POD集中在同一个主机上,具体的主机是哪个不需要指定。

podAntiAffinity 使用示例:

使用kubernetes.io/hostname作为拓扑域,把pod创建在不同主机上,每个主机上最多只有一个同类型的POD(同类型用标签区分)。其中matchExpressions中填写内容对应到RC中POD自身的标签。可以通过修改需要匹配的标签内容来控制把一个服务中的POD和其它服务的POD部署在不同主机上。

yaml中的定义如下:

{
  "podAntiAffinity": {
    "requiredDuringSchedulingIgnoredDuringExecution": [
      {
        "labelSelector": {
          "matchExpressions": [
            {
              "key": "name",
              "operator": "In",
              "values": [
                "node-rc"
              ]
            }
          ]
        },
        "topologyKey": "kubernetes.io/hostname"
      }
    ]
  }
}

创建一个有4个POD的RC,结果如下:

未分类

三个主机上都有一个POD运行,因为每个主机上最多只能运行一个这种类型的POD,所以有一个POD一直处于Pending状态,不能调度到任何节点。

上边的例子中可以通过修改topologyKey来限制拓扑域的范围,实现把相关服务部署在不同的容灾域等其它功能。

总结

Kubernetes提供了丰富的调度策略,包括静态的全局调度策略,以及动态的运行时调度策略,用户可以根据需要自由组合使用这些策略来实现自己的需求。在调度过程中,使用nodeAffnity决定资源可以部署在哪些主机上,使用podAffinity和podAntiAffinity决定哪些资源需要部署在同一主机(拓扑域)或者不能部署在同一主机。

kubernetes集群使用Ceph

https://blog.csdn.net/aixiaoyang168/article/details/78999851

经实验,在一个node上多个Pod是可以以ReadWrite模式挂载同一个CephRBD,但是跨node则不行,会提示image xxx is locked by other nodes。而我们的应用场景是需要多个node挂载一个ceph的,在我们的应用场景需要使用CephFS。

使用cephfs的场景:创建一个fs,挂载的时候指定path。

kubernetes使用CephFS的两种方式:

1.直接通过pod挂载

apiVersion: v1
kind: Pod
metadata:
name: cephfs2
spec:
containers:
- name: cephfs-rw
image: busybox
command: ["sleep", "60000"]
volumeMounts:
- mountPath: "/mnt/cephfs"
name: cephfs
volumes:
- name: cephfs
cephfs:
monitors:
- '<your_etcd_ip>:6789'
user: admin
secretRef:
name: ceph-secret
readOnly: false

2.通过创建pv、pvc挂载

在ceph集群上找到key:

[cephd@<your_ceph_machine> ~]$ ceph auth get-key client.admin | base64
QVFBNEhnNWJpQmN1RWhBQUhWSmJKZTVtOG9jWUdkNmlYMnA5dmc9PQ==

创建secret:

apiVersion: v1
kind: Secret
metadata:
name: ceph-secret
data:
key: QVFBNEhnNWJpQmN1RWhBQUhWSmJKZTVtOG9jWUdkNmlYMnA5dmc9PQ==

PV:

apiVersion: v1
kind: PersistentVolume
metadata:
name: cephfs-pv
spec:
capacity:
storage: 1Gi
accessModes:
– ReadWriteMany
cephfs:
monitors:
– <your_etcd_ip>:6789

path: /sns
user: admin
secretRef:
name: ceph-secret
readOnly: false
persistentVolumeReclaimPolicy: Recycle

PVC:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: cephfs-pv-claim
spec:
accessModes:
– ReadWriteMany
resources:
requests:
storage: 1Gi

创建POD:

apiVersion: v1
kind: Pod
metadata:
labels:
test: cephfs-pvc-pod
name: cephfs-pv-pod1
spec:
containers:
– name: cephfs-pv-busybox1
image: busybox
command: [“sleep”, “60000”]
volumeMounts:
– mountPath: “/mnt/cephfs”
name: cephfs-vol1
readOnly: false
volumes:
– name: cephfs-vol1
persistentVolumeClaim:
claimName: cephfs-pv-claim

遇到的问题:

1.映射到内核的时候报错RBD image feature set mismatch

http://blog.51cto.com/hipzz/1888048

–image-format format-id

format-id取值为1或2,默认为 2。

format 1 – 新建 rbd 映像时使用最初的格式。此格式兼容所有版本的 librbd 和内核模块,但是不支持较新的功能,像克隆。

format 2 – 使用第二版 rbd 格式, librbd 和 3.11 版以上内核模块才支持(除非是分拆的模块)。此格式增加了克隆支持,使得扩展更容易,还允许以后增加新功能。

解决方案1:

更改为格式1,重新映射。

注意:需要重新建立镜像。

[root@ceph1 ~]# rbd create block1 –image-format 1 –size 1024
rbd: image format 1 is deprecated
[root@ceph1 ~]# rbd ls
block1
block
[root@ceph1 ~]# rbd map block1
/dev/rbd0
[root@ceph1 ~]#

d.如上所示,映射正确。

解决方案2:

根据官网介绍,新建rbd默认格式2的rbd 块支持如下特性,并且默认全部开启:

–image-feature:

layering: 支持分层

striping: 支持条带化 v2

exclusive-lock: 支持独占锁

object-map: 支持对象映射(依赖 exclusive-lock )

fast-diff: 快速计算差异(依赖 object-map )

deep-flatten: 支持快照扁平化操作

journaling: 支持记录 IO 操作(依赖独占锁)

接下来尝试少开启一些特性:

[root@ceph1 ~]# rbd create block2 –image-feature layering –size 1024
[root@ceph1 ~]# rbd map block2
/dev/rbd1

2.创建pod挂载的时候遇到rbd: map failed executable file not found in $PATH

k8s集群内的节点上需要安装ceph-client:

yum install ceph–common

3.umount的时候出现target is busy

umount -l xxx

https://www.cnblogs.com/dkblog/archive/2012/07/18/2597192.html

https://blog.csdn.net/u012207077/article/details/21159339

4.如果k8s的node跟ceph集群的node不一样,则需要在k8s的node上部署ceph-common

yum install ceph-common

5.创建pod的时候提示,mount过去的时候提示libceph: bad option

k8s secret 认证 key 需要使用 base64 编码,有可能是secret文件里的key没有base64编码:

在ceph节点上ceph auth get-key client.admin |base64

填到secret文件里面。

6.如果mount fail,则去机器上查看kubelet的日志

7.多用户隔离

https://www.jianshu.com/p/96a34485f0fc

需要用pool,给user指定目录和权限,之后在pv中使用。

8.mount子目录

https://www.spinics.net/lists/ceph-devel/msg34698.html

mount -t ceph >> 172.24.0.4:6789:/volumes/kubernetes/test1 /tmp/mnt -o >> name=bar,secret=AQA+ln9Yfm6DKhAA10k7QkdkfIAKqmM6xeCsxA==

9.写入到共享存储的时候提示File Exists

目录权限问题,需要与Dockerfile中指定的USER的权限一样

HTTPS服务的Kubernetes ingress配置实践

在公有云被广泛接纳的今天,数据传输安全问题日益凸显,因为在公有云提供商的经典网络(二层互通)中,即便是内部网络通信也要考虑网络嗅探等hack手段,这也是公有云主推所谓“专用网络(二层隔离)”的原因之一。从应用的角度,我们应该尽量通过技术手段保证数据通信的安全性。而目前最常用的方式就是基于SSL/TLS的安全通信方式了,在七层,对应的就是https了。

这样,下面的仅在负载均衡/反向代理入口做加密通信的传统模型越来越无法满足数据安全性的需要了(nginx与backend service之间是基于明文的http通信):

传统安全通信模型:

client --- (via https) ---> nginx ---- (via http) ----> upstream backend services

我们需要下面的模型:

更为安全的通信模型:

client --- (via https) ---> nginx ---- (via https) ----> upstream backend services

在Kubernetes集群中,这种情况稍好些,首先,业务负载运行在集群的“虚拟网络”中,其次,一些K8s的网络插件实现是支持跨节点网络加密的(有一定的网络性能损耗),比如weave。但永远没有绝对的安全,作为业务应用的设计和实现人员,我们要尽可能的保证数据的通信安全,因此在面向七层的应用中,要尽可能的使用基于HTTPS的通信模型。本篇就来实践一下如何为Kubernetes集群内的HTTPS服务进行ingress的配置。

一. 例子概述与环境准备

在《实践kubernetes ingress controller的四个例子》一文中,我讲解了四种基本的kubernetes ingress配置方式。在这些例子中,有些例子的ingress controller(nginx)与backend service之间使用的是https,但client到ingress controller之间的通信却一直是基于http的。在本文中,我们的目标就是上面提到的那个更为安全的通信模型,即client与ingress controller(nginx)、nginx与backend service之间均使用的是https通信。这里在《实践kubernetes ingress controller的四个例子》一文例子的基础上,我们创建一个新的nginx ingress controller: nginx-ingress-controller-ic3,并将后端的svc7~svc9三个不同类型的服务暴露给client,如下图所示:

未分类

  • svc7: 是对传统通信模型的“复现”,即client与ingress controller(nginx)间采用https加密通信,但ingress controller(nginx)与svc7间则是明文的http通信;
  • svc8: 是ssl-termination的安全配置模型,即client与svc8的https通信分为“两段”,client与nginx建立https连接后,nginx将client提交的加密请求解密后,再向svc8发起https请求,并重新加密请求数据。这种client端ssl的过程在反向代理或负载均衡器终结的https通信方式被称为“ssl-termination”。
  • svc9: 是ssl-passthrough的安全配置模型,即nginx不会对client的https request进行解密,而是直接转发给backend的svc9服务,client端的ssl过程不会终结于nginx,而是在svc9对应的pod中终结。这种https通信方式被称为”ssl-passthrough”。这种配置模型尤其适合backend service对client端进行client certificate验证的情况,同时也降低了nginx加解密的性能负担。

本文基于下面环境进行实验:kubernetes 1.10.3、weave networks 2.3.0、nginx-ingress-controller:0.15.0。关于本文涉及的例子的源码、chart包以及ingress controllers的yaml源文件可以在这里下载到。

二. 建立新的ingress-nginx-controller:nginx-ingress-controller-ic3

为了更好地进行例子说明,我们建立一个新的ingress-nginx-controller:nginx-ingress-controller-ic3,svc7~svc9都通过该ingress controller进行服务入口的暴露管理。要创建nginx-ingress-controller-ic3,我们首先需要在ic-common.yaml中为Role: nginx-ingress-role添加一个resourceName: “ingress-controller-leader-ic3″,并apply生效:

// ic-common.yaml
... ...
    resourceNames:
      # Defaults to "<election-id>-<ingress-class>"
      # Here: "<ingress-controller-leader>-<nginx>"
      # This has to be adapted if you change either parameter
      # when launching the nginx-ingress-controller.
      - "ingress-controller-leader-ic1"
      - "ingress-controller-leader-ic2"
      - "ingress-controller-leader-ic3"
... ...

# kubectl apply -f ic-common.yaml

我们为nginx-ingress-controller-ic3创建nodeport service,新nodeport为:30092:

// ic3-service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-ic3
  namespace: ingress-nginx-demo
spec:
  type: NodePort
  ports:
  - name: https
    port: 443
    targetPort: 443
    nodePort: 30092
    protocol: TCP
  selector:
    app: ingress-nginx-ic3

注意:ingress-nginx-ic3 service的nodeport映射到ic3 ingress controller的443端口,也就是支持安全通信的端口,而不是明文的80端口。

最后创建nginx-ingress-controller-ic3 pod,可以复制一份ic2-mandatory.yaml,然后将内容中的ic2全部修改为ic3即可:

# kubectl apply -f ic3-mandatory.yaml

如无意外,nginx-ingress-controller-ic3应该已经正常地运行在你的k8s cluster中了。

三. svc7: 使用ssl termination,但nginx与backend服务之间采用明文传输(http)

加密Web流量有两个主要配置方案:SSL termination和SSL passthrough。

使用SSL termination时,客户端的SSL请求在负载均衡器/反向代理中解密,解密操作将增加负载均衡器的工作负担,较为耗费CPU,但简化了SSL证书的管理。至于负载均衡器和后端之间的流量是否加密,需要nginx另行配置。

SSL Passthrough,意味着client端将直接将SSL连接发送到后端(backend)。与SSL termination不同,请求始终保持加密,并且解密负载分布在后端服务器上。但是,这种情况的SSL证书管理略复杂,证书必须在每台服务器上自行管理。另外,在这种方式下可能无法添加或修改HTTP header,可能会丢失X-forwarded-* header中包含的客户端的IP地址,端口和其他信息。

我们先来看一种并不那么“安全”的“传统模型”:在nginx上暴露https,但nginx到backend service(svc7)采用http。

我们先来创建相关的密钥和公钥证书,并以一个Secret:ingress-controller-demo-tls-secret存储密钥和证书数据:

// ingress-controller-demo/manifests下面

# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ic3.key -out ic3.crt -subj "/CN=*.tonybai.com/O=tonybai.com"
# kubectl create secret tls ingress-controller-demo-tls-secret --key  ic3.key --cert ic3.crt

svc7几乎是和svc1一样的程序(输出的字符串标识不同),但svc7的ingress与svc1大不相同,因为我们需要通过https访问svc7的ingress:

// svc7的values.yaml
... ...
replicaCount: 1

image:
  repository: bigwhite/ingress-controller-demo-svc7
  tag: v0.1
  pullPolicy: Always

service:
  type: ClusterIP
  port: 443

ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: ic3
  path: /
  hosts:
    - svc7.tonybai.com
  tls:
    - secretName: ingress-controller-demo-tls-secret
      hosts:
        - svc7.tonybai.com
... ...

与svc1的values.yaml不同的是,我们使用的ingress controller是ic3,我们开启了tls,secret用的就是我们上面创建的那个secret:ingress-controller-demo-tls-secret。创建ic3-svc7后,我们看到ingress controller内部的nginx.conf中有关svc7的配置输出如下:

# kubectl exec nginx-ingress-controller-ic3-67f7cf7845-2tnc9 -n ingress-nginx-demo -- cat /etc/nginx/nginx.conf

        # map port 442 to 443 for header X-Forwarded-Port
        map $pass_server_port $pass_port {
                442              443;
                default          $pass_server_port;
        }

        upstream default-ic3-svc7-http {
                least_conn;

                keepalive 32;

                server 192.168.28.13:8080 max_fails=0 fail_timeout=0;

        }

## start server svc7.tonybai.com
        server {
                server_name svc7.tonybai.com ;

                listen 80;

                listen [::]:80;

                set $proxy_upstream_name "-";

                listen 442 proxy_protocol   ssl http2;

                listen [::]:442 proxy_protocol  ssl http2;

                # PEM sha: 248951b75535e0824c1a7f74dc382be3447057b7
                ssl_certificate                         /ingress-controller/ssl/default-ingress-controller-demo-tls-secret.pem;
                ssl_certificate_key                     /ingress-controller/ssl/default-ingress-controller-demo-tls-secret.pem;

                ssl_trusted_certificate                 /ingress-controller/ssl/default-ingress-controller-demo-tls-secret-full-chain.pem;
                ssl_stapling                            on;
                ssl_stapling_verify                     on;

                location / {
                        ... ...
                        proxy_pass http://default-ic3-svc7-http;

                        proxy_redirect                          off;

                }
           ... ...
        }
        ## end server svc7.tonybai.com

可以看到30092(nodeport) 映射的ingress controller的443端口在svc7.tonybai.com这个server域名下已经有了ssl标识,并且ssl_certificate和ssl_certificate_key对应的值就是我们之前创建的ingress-controller-demo-tls-secret。

我们通过curl访问以下svc7服务:

# curl -k https://svc7.tonybai.com:30092
Hello, I am svc7 for ingress-controller demo!

此时,如果再用http方式去访问svc7,你会得到下面错误结果:

# curl http://svc7.tonybai.com:30092
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>nginx/1.13.12</center>
</body>
</html>

四. svc8: 使用ssl termination,但nginx与backend服务之间采用加密传输(https)

前面说过,SSL termination配置场景中,负载均衡器和后端之间的流量是否加密,需要nginx另行配置。svc7采用了未加密的方式,nginx -> backend service存在安全风险,我们要将其改造为也通过https进行数据加密传输,于是有了svc8这个例子。

svc8对应的程序本身其实是上一篇文章《实践kubernetes ingress controller的四个例子》中的svc2的clone(唯一修改就是输出的log中的标识)。

在svc8对应的chart中,我们将values.yaml改为:

// ingress-controller-demo/charts/svc8/values.yaml

replicaCount: 1

image:
  repository: bigwhite/ingress-controller-demo-svc8
  tag: v0.1
  pullPolicy: Always

service:
  type: ClusterIP
  port: 443

ingress:
  enabled: true
  annotations:
    # kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/secure-backends: "true"
    kubernetes.io/ingress.class: ic3
  path: /
  hosts:
    - svc8.tonybai.com
  tls:
    - secretName: ingress-controller-demo-tls-secret
      hosts:
        - svc8.tonybai.com

... ...

与svc7不同点在于values.yaml中的新annotation: nginx.ingress.kubernetes.io/secure-backends: “true”。这个annotation让nginx以https的方式去访问backend service: svc8。安装svc8 chart后,ingress nginx controller为svc8生成的配置如下:

## start server svc8.tonybai.com
        server {
                server_name svc8.tonybai.com ;

                listen 80;

                listen [::]:80;

                set $proxy_upstream_name "-";

                listen 442 proxy_protocol   ssl http2;

                listen [::]:442 proxy_protocol  ssl http2;

                # PEM sha: 248951b75535e0824c1a7f74dc382be3447057b7
                ssl_certificate                         /ingress-controller/ssl/default-ingress-controller-demo-tls-secret.pem;
                ssl_certificate_key                     /ingress-controller/ssl/default-ingress-controller-demo-tls-secret.pem;

                ssl_trusted_certificate                 /ingress-controller/ssl/default-ingress-controller-demo-tls-secret-full-chain.pem;
                ssl_stapling                            on;
                ssl_stapling_verify                     on;

                location / {
                     ... ...
                        proxy_pass https://default-ic3-svc8-https;

                        proxy_redirect                          off;

                }

        }
        ## end server svc8.tonybai.com

        upstream default-ic3-svc8-https {
                least_conn;

                keepalive 32;

                server 192.168.28.14:8080 max_fails=0 fail_timeout=0;

        }

使用curl访问svc8服务(-k: 忽略对server端证书的校验):

# curl -k https://svc8.tonybai.com:30092
Hello, I am svc8 for ingress-controller demo!

五. svc9: 使用ssl passthrough, termination at pod

某些服务需要通过对client端的证书进行校验的方式,进行身份验证和授权,svc9就是这样一个对client certification进行校验的双向https校验的service。针对这种情况,ssl termination的配置方法无法满足需求,我们需要使用ssl passthrough的方案。

在ingress nginx controller开启ssl passthrough方案需要在ingress controller和ingress中都做一些改动。

首先我们需要为nginx-ingress-controller-ic3添加一个新的命令行参数:–enable-ssl-passthrough,并重新apply生效:

// ic3-mandatory.yaml
... ...
spec:
      serviceAccountName: nginx-ingress-serviceaccount
      containers:
        - name: nginx-ingress-controller-ic3
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.15.0
          args:
            - /nginx-ingress-controller
            - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
            - --configmap=$(POD_NAMESPACE)/nginx-configuration-ic3
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services-ic3
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services-ic3
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx-ic3
            - --annotations-prefix=nginx.ingress.kubernetes.io
            - --enable-ssl-passthrough
            - --ingress-class=ic3
... ...

然后在svc9的chart中,为ingress添加新的annotation
nginx.ingress.kubernetes.io/ssl-passthrough: “true”

// ingress-controller-demo/charts/svc9/values.yaml

replicaCount: 1

image:
  repository: bigwhite/ingress-controller-demo-svc9
  tag: v0.1
  pullPolicy: Always

service:
  type: ClusterIP
  port: 443

ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: ic3
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"

  path: /
  hosts:
    - svc9.tonybai.com
  tls:
    - secretName: ingress-controller-demo-tls-secret
      hosts:
        - svc9.tonybai.com
... ...

isntall svc9 chart之后,我们用curl来访问以下svc9:

# curl -k  https://svc9.tonybai.com:30092
curl: (35) gnutls_handshake() failed: Certificate is bad

由于svc9程序对client端的certificate进行验证,没有提供client certificate的curl请求被拒绝了!svc9 pod的日志也证实了这一点:

2018/06/25 05:36:29 http: TLS handshake error from 192.168.31.10:38634: tls: client didn't provide a certificate

我们进入到ingress-controller-demo/src/svc9/client路径下,执行:

# curl -k --key ./client.key --cert ./client.crt https://svc9.tonybai.com:30092
Hello, I am svc9 for ingress-controller demo!

带上client.crt后,svc9通过了验证,返回了正确的应答。

client路径下是一个svc9专用的客户端,我们也可以执行该程序去访问svc9:

# go run client.go
Hello, I am svc9 for ingress-controller demo!

我们再看看采用ssl-passthrough方式下ingress-nginx controller的访问日志,当curl请求发出时,ingress-nginx controller并未有日志输出,因为没有在nginx处ssl termnination,从此也可以证实:nginx将client的ssl过程转发到pod中去了,即passthrough了。

51短信平台:企业级短信平台定制开发专家 https://51smspush.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

K8S Ingress环境下,Http Redirect端口丢失问题

近日发现一个问题:应用程序在返回Http Redirect的时候丢失了原先访问的端口。比如,我们这样访问http://IP-A:Port-A/app/delete,这个url会响应302,但是它返回的Response header Location里丢失了端口,正确的结果应该是这样:http://IP-A:Port-A/app/index,但返回的却是:http://IP-A/app/index,把端口丢失了。

基本情况

我们的部署情况是这样的:

  • 部署了Nginx Ingress,并使用NodePort的方式把Nginx Ingress Service暴露出来
  • 配置了App的Ingress

服务器信息:

未分类

其实以上也不全是服务器,其中有两个K8S Service不是服务器,它们是VIP,关于这个请看K8S – Using Source IP一文,当访问http://IP-A:Port-A/app/delete的时候,这个请求从左到右贯穿了这些服务器。

顺便一提上面的NAT Server是一台普通的服务器,我们用它做了PAT使我们的Nginx Ingress能够被外网访问到。

观察

我们使用之前提到过的Echo Server来观察透过Ingress访问Echo Server时传递给Echo Server的Request header:http://IP-A:Port-A/echo-server,得到了这些有趣的Request header:

host=IP-A:Port-A
x-original-uri=/echo-server
x-forwarded-for=IP-B
x-forwarded-host=IP-A:Port-A
x-forwarded-port=80
x-forwarded-proto=http

然后直接访问Echo Server Svc,发现是没有上面提到的x-*Request header的。于是怀疑问题出在这几个header上。

名词解释

来讲一下这些头各自代表什么意思。

  • x-forwarded-for,client访问proxy的时候,client的ip。
    在这里之所以是K8S Node的IP,是因为在Nginx Ingress看来请求是来自K8S Node的(好好看看之前提到的K8S – Using Source IP一文),在这之前的NAT它是不知道的。
  • x-forwarded-host,client访问proxy的时候,访问的原始host。
  • x-forwarded-proto,client访问proxy的时候,访问的原始http scheme。
  • x-forwarded-port,client访问proxy的时候,访问的port。
  • x-original-uri,查不到权威资料。

注意,前三个是事实标准,MDN有收录,x-forwarded-port和x-original-uri似乎是私有扩展。

实验

找一个趁手的Http Request工具(我用的是Postman),记得把Follow redirect关掉,然后模拟Nginx请求的方式(就是把上面提到的x-* header带上/去掉/修改值)直接请求App Svc。

结果发现x-forwarded-port是Response header Location的关键,即如果x-forwarded-port=Port-A的话,Location就会带上正确的端口。

分析

Redirect url是如何构造的

可以推测,App利用了host和x-forwarded-*这些header来构造redirect url。

在Java Servlet API中,在描述HttpServletResponse#sendRedirect的时候提到,其返回的URL必须是Absolute URL。

Tomcat的org.apache.catalina.connector.Response的toAbsolute方法负责构造Absolute URL。

那么它又是如何知道选用什么Port的呢?这个和RemoteIPValve有关,有兴趣的话你可以查阅相关文档。

上面只是讲了Tomcat是如何构造redirect url的,但这个方法不是标准的,不同的容器有各自的实现,毕竟Java Servlet API也没有规定如何构造Absolute URL。

我之前也写过一篇相关话题的文章《反向代理使用https协议,后台tomcat使用http,redirect时使用错误协议的解决办法》,你可以看一看。

为何x-forwarded-port是80

那么问题来了,我明明访问的是IP-A:Port-A,为何Nginx取到的值是80?

这是因为在整个请求链路的前段:NAT Server > K8S Node > Nginx Ingress Svc 都是在第4层工作的,可以认为它们干的事情都是NAT,Nginx Ingress Pod是不知道这些服务器/网络节点的端口,因此它只能把自己的端口80(容器内Port)给x-forwarded-port。

关于这个逻辑你可以查看Nginx Ingress的配置文件就能够知道了:

kubectl -n kube-system exec -it <nginx-ingress-controller-pod-name> -- cat /etc/nginx/nginx.conf

解决办法

请求时带上x-forwarded-port(不靠谱)

查看Nginx Ingress配置文件发现如果最初请求的时候带上x-forwarded-port的话,就能够改变它传递到后面的值,但是这有两个问题:

  • 通过浏览器访问时,你没有办法加上这个header
  • 这个header一般都是反向代理加的,也就是在我们的Nginx Ingress之前还得有一个反向代理

所以这个方法不好。

修改tomcat的代码(不靠谱)

虽然可以通过修改tomcat的代码,让它从x-forward-host/host header来取port,但是这个不现实。

修改NAT Server的端口为80(靠谱)

这个方法比较靠谱,只要将NAT Server的端口改成80就没有问题了。

事实上,如果你直接访问K8S Node的话(NodePort方式),也是要将NodePort设置为80,记得前面说的吗?Nginx Ingress无法知道上层NAT的端口。

总而言之,就是你最初请求的URL不能是80之外的端口,必须是http://some-ip/app才可以。

使用Nginx Ingress Annotations(靠谱)

使用Nginx Ingress提供的Proxy redirect annotations(https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#proxy-redirect),将Location的值做文本替换。

深入玩转K8S之利用Label控制Pod位置

首先介绍下什么是Label?

Label是Kubernetes系列中一个核心概念。是一组绑定到K8s资源对象上的key/value对。同一个对象的labels属性的key必须唯一。label可以附加到各种资源对象上,如Node,Pod,Service,RC等。

通过给指定的资源对象捆绑一个或多个不用的label来实现多维度的资源分组管理功能,以便于灵活,方便地进行资源分配,调度,配置,部署等管理工作。

默认配置下,Scheduler 会将 Pod 调度到所有可用的 Node。不过有些实际情况我们希望将 Pod 部署到指定的 Node,比如将有大量磁盘 I/O 的 Pod 部署到配置了 SSD 的 Node;或者 Pod 需要 GPU,需要运行在配置了 GPU 的节点上。

下面我们来实际的操作下,比如执行如下命令标注 k8s-node1 是配置了 SSD的节点。

kubectl label node k8snode1 disktype=ssd

然后通过 kubectl get node –show-labels 查看节点的 label。

未分类

可以看到disktype=ssd 已经成功添加到 k8snode1,除了 disktype,Node 还有几个 Kubernetes 自己维护的 label。有了 disktype 这个自定义 label,接下来就可以指定将 Pod 部署到 k8snod1。比如我编辑nginx.yml,增加nodeSelector标签,指定将此Pod部署到具有ssd属性的Node上去。

未分类

最后通过kubectl get pod -o wide。

如果要删除 label disktype,就执行如下命令删除即可:

kubectl label node k8s-node1 disktype-

但是要注意已经部署的 Pod 并不会重新部署,依然在 k8snode1 上运行。可能会有人说了,那怎么让Pod变回原样呢也就是分配到多个node上,那就需要一个笨方法了(至少在目前我学习的方法里面只会这样操作),就是在刚才编辑的那个nginx.yml文件里面删除nodeSelector标签,然后在利用kubectl apply重新部署,Kubernetes 会删除之前的 Pod 并调度和运行新的 Pod。

未分类

好了本次的Label标签的实践讨论到此结束,本文参考了Kubernetes 官网和每天5分钟玩转K8S。

【Linux】执行 service iptables save 命令异常解决

遇到问题

  博主在 CentOS7 安装 Redis 的过程中,使用 iptables 命令添加完 iptables规则以后,需要保存规则永久生效,当执行 service iptables save 命令时提示以下错误信息:
  
未分类

问题原因

  遇到此问题是因为没有安装 iptables 服务,因此需要先安装 iptables 服务。

解决方案

1. 关闭防火墙

systemctl stop firewalld
systemctl mask firewalld

2. 安装 iptables 服务

yum install iptables-services

3. 设置 iptables 服务开机启动

systemctl enable iptables

 

4. 重启 iptables 服务

systemctl restart iptables

 

5. 执行保存配置命令

service iptables save

Docker使用zookeeper

Apache ZooKeeper是一个开源的服务器,可以实现高度可靠的分布式协调。
记录Docker里面使用zookeeper的方法

镜像

docker pull zookeeper

启动一个Zookeeper服务器实例

启动一个zookeeper实例很简单:

docker run --name some-zookeeper --restart always -d zookeeper

由于Zookeeper “fails fast”,最好始终重新启动它。

这里可以加上-p参数把端口映射到主机端口:

docker run --name some-zookeeper -p 2181:2181 --restart always -d zookeeper

这样, 就把容器的2181端口映射到宿主机器的2181端口上了, java程序等可以直接连接(127.0.0.1:2181)

从另一个Docker容器中的应用程序连接到Zookeeper

docker run --name some-app --link some-zookeeper:zookeeper -d application-that-uses-zookeeper

从Zookeeper命令行客户端连接到Zookeeper

docker run -it --rm --link some-zookeeper:zookeeper zookeeper zkCli.sh -server zookeeper

查看日志

docker logs -f e36790ea5c5e

其中e36790ea5c5e是容器的ID, 可以通过docker container ls 来查看.

END

解决 error creating overlay mount to /var/lib/docker/overlay2

最近在centos7.1使用docker运行redis镜像,出现下面的错误:

/usr/bin/docker-current: Error response from daemon: error creating overlay mount to /var/lib/docker/overlay2/65f3c109fb903539820f84856d2725af784f2f03f95b1f0214e34184e4d61ff7-init/merged: invalid argument.
See '/usr/bin/docker-current run --help'.

在网上搜索一番后,一个可行的方案如下(改变storage driver类型, 禁用selinux):

停止docker服务

systemctl stop docker

清理镜像

rm -rf /var/lib/docker

修改存储类型

vi /etc/sysconfig/docker-storage

把空的DOCKER_STORAGE_OPTIONS参数改为overlay:

DOCKER_STORAGE_OPTIONS="--storage-driver overlay"

禁用selinux

vi /etc/sysconfig/docker

去掉option的–selinux-enabled

启动docker应该就可以了

systemctl start docker

方案抄自 Ysssssssssssssss的博客 和 redis的讨论: error creating overlay mount to …/merged: invalid argument., 基本可以确定是启用selinux导致的。

Ubuntu中apt命令自动补全设置

在Ubuntu中安装软件和包,apt命令是必不可少的,虽然类似于Python的pip一样十分方便,但安装的包名却是一个比较烦人的问题。 需要安装的报名太长、单词记不住、版本号不对(对于有些依赖库而言,要求是版本号是完全对应的,低的高的都不行,不适用向下兼容原则)等等问题,所以如果可以有包名的自动补全功能就好了。 而事实上也是有的,但似乎默认并没有开启。因为在新安装的系统上试了一下,是不会自动补全的。 所以下面记录一下设置步骤。

1. 安装bash-completion

在终端输入命令

apt-get install bash-completion

一般情况下这个包应该系统都自动装好了,如果没有就装一下。

2.修改配置文件

在终端中输入以下命令

gedit /etc/bash.bashrc

这样就能用Gedit打开配置文件,找到被注释掉的蓝色的一段代码,如下所示。

未分类

把这段代码取消注释,并保存,如下所示,即可使自动补全功能生效了。

未分类

3.测试

之前一直需要安装一个libcholmod的库,但是按照书上给的libcholmod-dev提示找不到包,在网上找了找,说是版本不对。 有说装libcholmod2.1.2成功的,有说装libcholmod3.0.14成功的,可惜的是在我电脑上两个都不行。 而且之前没有设置apt的自动补全,所以完全不知道我的电脑应该装哪个版本。但在设置了自动补全后终于可以了,原来我的电脑对应的版本是3.0.6,如下所示。

未分类

这样配置apt自动补全就大功告成了,成功解决了一个我的问题。

4. 题外话

关于apt自动补全,我看网上也有博客说修改~/.bashrc文件的,如这篇和这篇博客,但经过我的测试,在我的电脑上无效,而且设置完后还报了.:command not found的错误。 而且我看了~/.bashrc的文件内容如下。

未分类

上面博客中说的添加的内容其实就是这里被注释掉的蓝色部分。而稍微阅读一下注释就会发现,人家说,你不需要在这里开启这段代码,如果它已经在/etc/bash.bashrc中开启了的话。 而我们上面其实是按照系统的提示,修改了/etc/bash.bashrc,所以就不需要再改这里了。 当然如果你按照我上面的方法修改不起作用,那你可以按照刚提到的这两篇博客的内容试试,应该也是可以的。