04_MySQL网络安装

说明

  1. 假设安装的服务器为:123.1.1.1

采购了百度云服务器,发现,体验太差了,推荐还是阿里云

安装 Mysql

安装过程使用root操作

  1. 安装依赖软件
sudo yum install -y cmake , make , gcc , gcc-c++ , bison , ncurses , ncurses-devel
  1. 添加库
    vim /etc/yum.repos.d/MariaDB.repo
  2. 添加数据源

系统及版本选择:https://downloads.mariadb.org/mariadb/repositories/#mirror=tuna

# MariaDB 10.3 CentOS repository list - created 2019-03-29 02:33 UTC
# http://downloads.mariadb.org/mariadb/repositories/
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.3/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

清理yum缓存

yum clean headers  #清理/var/cache/yum的headers
yum clean packages #清理/var/cache/yum下的软件包
yum clean metadata

yum clean all   #清除Yum缓存
yum makecache   #把服务器的包信息下载到本地电脑缓存起来
yum update   #升级包同时也升级软件和系统内核

释放内存

sync; echo 2 > /proc/sys/vm/drop_caches
sync; echo 3 > /proc/sys/vm/drop_caches

free -m    #查看内存
  1. 安装mariadb
yum -y install MariaDB-server MariaDB-client
  1. 启动数据库
systemctl restart mariadb
systemctl status mariadb

假如密码忘记:
修改/etc/my.cnf文件,在[mysqld]下添加 skip-grant-tables , 再启动mariadb

[client-server]

# [mysqld]
# skip-grant-tables

#
# include all files from the config directory
#
!includedir /etc/my.cnf.d
  1. 登陆
mysql

// 安全配置
mysql_secure_installation

添加防火墙

sudo firewall-cmd --zone=public --permanent --add-service=mysql
sudo systemctl restart firewalld
  1. 重置密码
// 重启服务
systemctl restart mysqld
systemctl status mysqld
  1. 重置密码
mysql -uroot -p
use mysql;
update user set password=password('HouGuiYu@123!@#') where user='root' ;
  1. 开启远程访问
mysql -uroot -p

// %表示是所有的外部机器,如果指定某一台机,就将%改为相应的机器名;‘root’则是指要使用的用户名
grant all privileges on *.* to 'root'@'%' identified by 'HouGuiYu@123!@#' with grant option;

// 运行此句才生效,或者重启MySQL
flush privileges;

安装phpadmin

mysql的界面管理工具,用于mysql的可视化管理

  1. 安装依赖包
yum -y install httpd php php-fpm php-mysql
  1. 升级php
rpm -Uvh https://mirror.webtatic.com/yum/el7/epel-release.rpm #更新源
rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
yum remove php-common -y  #移除系统自带的php-common
yum install -y php72w php72w-opcache php72w-xml php72w-mcrypt php72w-gd php72w-devel php72w-mysql php72w-intl php72w-mbstring  #安装依赖包
  1. 安装apache
// 安装
yum install httpd

// 重启
systemctl restart httpd

// 查看运行状态
systemctl status httpd

访问地址:http://123.1.1.1/

  1. 安装phpadmin

此需进入目录 /var/www/html 目录操作

// 下载phpadmin
wget https://files.phpmyadmin.net/phpMyAdmin/4.8.5/phpMyAdmin-4.8.5-all-languages.zip

// 解压
unzip phpMyAdmin-4.8.5-all-languages.zip
  1. 修改名称
mv phpMyAdmin-4.8.5-all-languages phpMyAdmin

SpringCloud微服务迁移至Kubernetes实践

前言
原SpringCloud基础上的微服务已稳定运行近1年,遗留了一些问题不太好处理。原SpringCloud的整理文章见基于SpringCloud微服务的服务平台搭建的一些总结
问题如下:

客户端侧负载均衡在服务实例故障下线时候,不能及时发现,导致请求到故障实例地址造成请求错误,若增加请求重试配置,对于非幂等接口处理困难。
基于SpringCloud Config的配置中心有时候会有不及时刷新svn上的配置信息的情况(需要重新config),没找到这个问题的原因。另外多环境(生产、测试环境独立的配置)、多级配置(本地、配置中心)的组合设置对于组内其他开发者而言,学习成本较大,经常出现用错配置的情况。
若要使用微服务,则必须使用SpringCloud技术栈开发,对开发人员的技术选型是一种不必要的约束,另外大量的SpringCloud应用对与机器内存资源的占用比较严重(一个应用动辄上百兆内存),拆分成大量微服务后对于机器内存的浪费尤为严重。
虽然SpringCloud有Eureka,但是由于各个服务不是在所有物理机器上都起着副本,当服务器故障需要恢复时,仍然需要留意当时机器上运行程序的情况,常常出现机器上残留的旧版本应用被其他人启动起来的情况。无法做到不关心实例的具体部署。
服务的升级较为麻烦,在缺乏有效的自动化部署程序,缺乏有效滚动升级方案,以达到不中断服务的同时逐个替换现有服务实例。
改造方式
部署Kubernetes集群
网上教程挺多的,这里就不详细写了,用的1.11。但是遇到过问题。我们集群用的3.10的kernel,在1.11的kubernetes中网络默认用的ipvs,经常导致kernel panic,后来重新部署时候把ipvs去掉,换回原来的iptables就好了。

SpringCloud应用改造
首先要去除对于eureka的maven依赖和config的依赖,并去除@EnableEurekaClient的注解即可。

其次服务调用方面由于没有Eureka了,FeignClient也需要进行一些变动,但是又要尽量简化开发和修改步骤,如果还得由开发人员记住每个服务的IP和端口那就有点退化的厉害了。我们做如下设置。

1) 应用在Kubernetes内的封装
– 关于端口:每一个微服务在kubernetes内都有一个Deployment负责Pod的部署、端口暴露(Expose出Spring应用的server.port即可)
– 对服务的封装:每个应用还需要一个Service来做服务实例的负载均衡,这样服务之间调用就只要找服务对应的Service就好。Service的ip地址由Kubernetes内部DNS负责解析。服务对外统一用80端口,这样集群内部访问时候就只要写服务名称即可。同时Service的名称和微服务的应用名称一致。

2)FeignClient的改造
原SpringCloud内只要指定serviceName就能通过Ribbon自行负载到对应的实例上,现在则需要通过Kubernetes的service实现调用,由于1)已经约定了Service名称与原服务名称一致,同时端口是80。因此只要加上url就行。
如:

// 这里的url是新增参数
@FeignClient(name=”remote-service”, url=”remote-service”)
public interface RemoteServiceApi {}
环境改造
1)将数据库连接改为Service和Endpoint
这样应用里的数据库连接jdbc就从:

jdbc:10.2.3.2:5433/dbname
改为

jdbc:pg-master/dbname
Service和Endpoint的配置方式:

apiVersion: v1
kind: Endpoints
metadata:
name: pg-endpoint-loadbalancer
subsets:
– addresses:
– ip: 10.2.xxx.xxx
ports:
– port: 4432
protocol: TCP

apiVersion: v1
kind: Service
metadata:
name: pg-endpoint-loadbalancer
spec:
ports:
– port: 5432
targetPort: 4432
protocol: TCP

由此改变的好处在于,数据库切换无需逐个更换应用配置再重启,而是仅需修改集群内Endpoint设置即可。

2)部分通用配置改为ConfigMap
将通用配置,如zipkin配置、redis配置等全局范围内应用都会用到的参数提取到ConfigMap中,在通过EnvFrom的方式挂在在应用的镜像中。此时跑在容器内的镜像就可以通过环境变量获取到这些全局的设置。
比如 bootstrap.properties:

spring.rabbitmq.username=${rabbitmq_username}
环境变量挂载方式:

env:
– name: rabbit_mq_host
valueFrom:
configMapKeyRef:
name: cloud-env
key: rabbit_mq_host
– name: rabbit_mq_address
valueFrom:
configMapKeyRef:
name: cloud-env
key: rabbit_mq_address
– name: redis_cluster
valueFrom:
configMapKeyRef:
name: cloud-env
key: redis_cluster

日志改造
原应用日志统一写入${HOME}/logs/${application-name.log}里边,再有logstash采集后发送到elastic search。现在采用容器方式运行后则不再将日志文件与物理机器挂钩,而是在一个Pod内再启动一个filebeat日志采集容器,两个容器共同挂载一个/log/目录。

# 部分yaml
template:
spec:
volumes:
– name: app-logs
emptyDir: {}
containers:
– name: xxxx(application-image)
# ……
volumeMounts:
– name: app-logs
mountPath: /log
– name: filebeat-image
# ……
volumeMounts:
– name: app-logs
mountPath: /log
结构如图所示:

日志采集结构改变为如下图所示:

应用部署方式
考虑到减少其余开发人员的新增学习成本我们将应用的打包发布流程封装成部署脚本,提供给开发人员,开发人员仅需准备jar包、lib依赖(可选)以及配置文件即可。
通过统一的模板和配置脚本将:打包Docker image、上传私服、生成yaml文件等自动完成。
为自动化部署和升级需要,yaml中默认采用Alwayl的Image拉去imagePullPolicy: Always,同时增加rollout 升级的配置。这样研发人员在部署应用时就能自动进行滚动升级,并支持回滚到旧版本。

spec:
replicas: REPLICAS
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1

发布于部署的关系如下所示:
一键发布脚本会自动完成打包docker image、推送私服、生成yaml文件三件事。研发人员仅需应用生成的yaml文件即可完成部署任务。

集群服务入口
原服务: 互联网 -> CDN -> 防火墙 -> Nginx -> zuul网关 -> 微服务
改为: 互联网 -> CDN -> 防火墙 -> Nginx -> traefik Ingress -> zuul网关 -> 微服务。
从traefik之后就已经进入微服务集群了。当然这里nginx仍然保留是因为历史元英,有些端口和应用二级目录共用问题,暂时无法完全由traefik接管。

结束
综上完成全部改造流程,将应用迁移进Kubernetes,并适时切换nginx负载即可。

service 和 kube-proxy 原理

简介

在 kubernetes 集群中,网络是非常基础也非常重要的一部分。对于大规模的节点和容器来说,要保证网络的连通性、网络转发的高效,同时能做的 ip 和 port 自动化分配和管理,并让用户用直观简单的方式来访问需要的应用,这是需要复杂且细致设计的。

kubernetes 在这方面下了很大的功夫,它通过 servicednsingress 等概念,解决了服务发现、负载均衡的问题,也大大简化了用户的使用和配置。

这篇文章就讲解如何配置 kubernetes 的网络,最终从集群内部和集群外部都能访问应用。

跨主机网络配置:flannel

一直以来,kubernetes 并没有专门的网络模块负责网络配置,它需要用户在主机上已经配置好网络。kubernetes 对网络的要求是:容器之间(包括同一台主机上的容器,和不同主机的容器)可以互相通信,容器和集群中所有的节点也能直接通信。

至于具体的网络方案,用户可以自己选择,目前使用比较多的是 flannel,因为它比较简单,而且刚好满足 kubernetes 对网络的要求。我们会使用 flannel vxlan 模式,具体的配置我在博客之前有文章介绍过,这里不再赘述。

以后 kubernetes 网络的发展方向是希望通过插件的方式来集成不同的网络方案, CNI 就是这一努力的结果,flannel 也能够通过 CNI 插件的形式使用。

kube-proxy 和 service

配置好网络之后,集群是什么情况呢?我们可以创建 pod,也能通过 ReplicationController 来创建特定副本的 pod(这是更推荐也是生产上要使用的方法,即使某个 rc 中只有一个 pod 实例)。可以从集群中获取每个 pod ip 地址,然后也能在集群内部直接通过 podIP:Port 来获取对应的服务。

但是还有一个问题:pod 是经常变化的,每次更新 ip 地址都可能会发生变化,如果直接访问容器 ip 的话,会有很大的问题。而且进行扩展的时候,rc 中会有新的 pod 创建出来,出现新的 ip 地址,我们需要一种更灵活的方式来访问 pod 的服务。

Service 和 cluster IP

针对这个问题,kubernetes 的解决方案是“服务”(service),每个服务都一个固定的虚拟 ip(这个 ip 也被称为 cluster IP),自动并且动态地绑定后面的 pod,所有的网络请求直接访问服务 ip,服务会自动向后端做转发。Service 除了提供稳定的对外访问方式之外,还能起到负载均衡(Load Balance)的功能,自动把请求流量分布到后端所有的服务上,服务可以做到对客户透明地进行水平扩展(scale)。

而实现 service 这一功能的关键,就是 kube-proxy。kube-proxy 运行在每个节点上,监听 API Server 中服务对象的变化,通过管理 iptables 来实现网络的转发。

NOTE: kube-proxy 要求 NODE 节点操作系统中要具备 /sys/module/br_netfilter 文件,而且还要设置 bridge-nf-call-iptables=1,如果不满足要求,那么 kube-proxy 只是将检查信息记录到日志中,kube-proxy 仍然会正常运行,但是这样通过 Kube-proxy 设置的某些 iptables 规则就不会工作。

kube-proxy 有两种实现 service 的方案:userspace 和 iptables

  • userspace 是在用户空间监听一个端口,所有的 service 都转发到这个端口,然后 kube-proxy 在内部应用层对其进行转发。因为是在用户空间进行转发,所以效率也不高
  • iptables 完全实现 iptables 来实现 service,是目前默认的方式,也是推荐的方式,效率很高(只有内核中 netfilter 一些损耗)。

这篇文章通过 iptables 模式运行 kube-proxy,后面的分析也是针对这个模式的,userspace 只是旧版本支持的模式,以后可能会放弃维护和支持。

kube-proxy 参数介绍

kube-proxy 的功能相对简单一些,也比较独立,需要的配置并不是很多,比较常用的启动参数包括:

参数 含义 默认值
–alsologtostderr 打印日志到标准输出 false
–bind-address HTTP 监听地址 0.0.0.0
–cleanup-iptables 如果设置为 true,会清理 proxy 设置的 iptables 选项并退出 false
–healthz-bind-address 健康检查 HTTP API 监听端口 127.0.0.1
–healthz-port 健康检查端口 10249
–iptables-masquerade-bit 使用 iptables 进行 SNAT 的掩码长度 14
–iptables-sync-period iptables 更新频率 30s
–kubeconfig kubeconfig 配置文件地址
–log-dir 日志文件目录/路径
–masquerade-all 如果使用 iptables 模式,对所有流量进行 SNAT 处理 false
–master kubernetes master API Server 地址
–proxy-mode 代理模式,userspace 或者 iptables, 目前默认是 iptables,如果系统或者 iptables 版本不够新,会 fallback 到 userspace 模式 iptables
–proxy-port-range 代理使用的端口范围, 格式为 beginPort-endPort,如果没有指定,会随机选择 0-0
–udp-timeout UDP 空连接 timeout 时间,只对 userspace 模式有用 250ms
–v 日志级别 0

kube-proxy 的工作模式可以通过 --proxy-mode 进行配置,可以选择 userspace 或者 iptables

实例启动和测试

我们可以在终端上启动 kube-proxy,也可以使用诸如 systemd 这样的工具来管理它,比如下面就是一个简单的 kube-proxy.service 配置文件

[root@localhost]# cat /usr/lib/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Proxy Service
Documentation=http://kubernetes.com
After=network.target
Wants=network.target

[Service]
Type=simple
EnvironmentFile=-/etc/sysconfig/kube-proxy
ExecStart=/usr/bin/kube-proxy \
    --master=http://172.17.8.100:8080 \
    --v=4 \
    --proxy-mode=iptables
TimeoutStartSec=0
Restart=on-abnormal

[Install]
WantedBy=multi-user.target

为了方便测试,我们创建一个 rc,里面有三个 pod。这个 pod 运行的是 cizixs/whoami 容器,它是一个简单的 HTTP 服务器,监听在 3000 端口,访问它会返回容器的 hostname。

[root@localhost ~]# cat whoami-rc.yml
apiVersion: v1
kind: ReplicationController
metadata:
  name: whoami
spec:
  replicas: 3
  selector:
    app: whoami
  template:
    metadata:
      name: whoami
      labels:
        app: whoami
        env: dev
    spec:
      containers:
      - name: whoami
        image: cizixs/whoami:v0.5
        ports:
        - containerPort: 3000
        env:
          - name: MESSAGE
            value: viola

我们为每个 pod 设置了两个 label:app=whoami 和 env=dev,这两个标签很重要,也是后面服务进行绑定 pod 的关键。

为了使用 service,我们还要定义另外一个文件,并通过 kubectl create -f ./whoami-svc.yml 来创建出来对象:

apiVersion: v1
kind: Service
metadata:
  labels:
    name: whoami
  name: whoami
spec:
  ports:
    - port: 3000
      targetPort: 3000
      protocol: TCP
  selector:
    app: whoami
    env: dev

其中 selector 告诉 kubernetes 这个 service 和后端哪些 pod 绑定在一起,这里包含的键值对会对所有 pod 的 labels 进行匹配,只要完全匹配,service 就会把 pod 作为后端。也就是说,service 和 rc 并不是对应的关系,一个 service 可能会使用多个 rc 管理的 pod 作为后端应用。

ports 字段指定服务的端口信息:

  • port:虚拟 ip 要绑定的 port,每个 service 会创建出来一个虚拟 ip,通过访问 vip:port 就能获取服务的内容。这个 port 可以用户随机选取,因为每个服务都有自己的 vip,也不用担心冲突的情况
  • targetPort:pod 中暴露出来的 port,这是运行的容器中具体暴露出来的端口,一定不能写错
  • protocol:提供服务的协议类型,可以是 TCP 或者 UDP

创建之后可以列出 service ,发现我们创建的 service 已经分配了一个虚拟 ip (10.10.10.28),这个虚拟 ip 地址是不会变化的(除非 service 被删除)。查看 service 的详情可以看到它的 endpoints 列出,对应了具体提供服务的 pod 地址和端口。

[root@localhost ~]# kubectl get svc
NAME         CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
kubernetes   10.10.10.1    <none>        443/TCP    19d
whoami       10.10.10.28   <none>        3000/TCP   1d

[root@localhost ~]# kubectl describe svc whoami
Name:                   whoami
Namespace:              default
Labels:                 name=whoami
Selector:               app=whoami
Type:                   ClusterIP
IP:                     10.10.10.28
Port:                   <unset> 3000/TCP
Endpoints:              10.11.32.6:3000,10.13.192.4:3000,10.16.192.3:3000
Session Affinity:       None
No events.

默认的 service 类型是 ClusterIP,这个也可以从上面输出看出来。在这种情况下,只能从集群内部访问这个 IP,不能直接从集群外部访问服务。如果想对外提供服务,我们后面会讲解决方案。

测试一下,访问 service 服务的时候可以看到它会随机地访问后端的 pod,给出不同的返回:

[root@localhost ~]# curl http://10.10.10.28:3000
viola from whoami-8fpqp
[root@localhost ~]# curl http://10.10.10.28:3000
viola from whoami-c0x6h
[root@localhost ~]# curl http://10.10.10.28:3000
viola from whoami-8fpqp
[root@localhost ~]# curl http://10.10.10.28:3000
viola from whoami-dc9ds

默认情况下,服务会随机转发到可用的后端。如果希望保持会话(同一个 client 永远都转发到相同的 pod),可以把 service.spec.sessionAffinity 设置为 ClientIP

NOTE: 需要注意的是,服务分配的 cluster IP 是一个虚拟 ip,如果你尝试 ping 这个 IP 会发现它没有任何响应,这也是刚接触 kubernetes service 的人经常会犯的错误。实际上,这个虚拟 IP 只有和它的 port 一起的时候才有作用,直接访问它,或者想访问该 IP 的其他端口都是徒劳。

外部能够访问的服务

上面创建的服务只能在集群内部访问,这在生产环境中还不能直接使用。如果希望有一个能直接对外使用的服务,可以使用 NodePort 或者 LoadBalancer 类型的 Service。我们先说说 NodePort ,它的意思是在所有 worker 节点上暴露一个端口,这样外部可以直接通过访问 nodeIP:Port 来访问应用。

我们先把刚才创建的服务删除:

[root@localhost ~]# kubectl delete rc whoami
replicationcontroller "whoami" deleted

[root@localhost ~]# kubectl delete svc whoami
service "whoami" deleted

[root@localhost ~]# kubectl get pods,svc,rc
NAME         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.10.10.1   <none>        443/TCP   14h

对我们原来的 Service 配置文件进行修改,把 spec.type 写成 NodePort 类型:

[root@localhost ~]# cat whoami-svc.yml
apiVersion: v1
kind: Service
metadata:
  labels:
    name: whoami
  name: whoami
spec:
  ports:
    - port: 3000
      protocol: TCP
      # nodePort: 31000
  selector:
    app: whoami
  type: NodePort

因为我们的应用比较简单,只有一个端口。如果 pod 有多个端口,也可以在 spec.ports中继续添加,只有保证多个 port 之间不冲突就行。

重新创建 rc 和 svc:

[root@localhost ~]# kubectl create -f ./whoami-svc.yml
service "whoami" created
[root@localhost ~]# kubectl get rc,pods,svc
NAME        DESIRED   CURRENT   READY     AGE
rc/whoami   3         3         3         10s

NAME              READY     STATUS    RESTARTS   AGE
po/whoami-8zc3d   1/1       Running   0          10s
po/whoami-mc2fg   1/1       Running   0          10s
po/whoami-z6skj   1/1       Running   0          10s

NAME             CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
svc/kubernetes   10.10.10.1     <none>        443/TCP          14h
svc/whoami       10.10.10.163   <nodes>       3000:31647/TCP   7s

需要注意的是,因为我们没有指定 nodePort 的值,kubernetes 会自动给我们分配一个,比如这里的 31647(默认的取值范围是 30000-32767)。当然我们也可以删除配置中 # nodePort: 31000 的注释,这样会使用 31000 端口。

nodePort 类型的服务会在所有的 worker 节点(运行了 kube-proxy)上统一暴露出端口对外提供服务,也就是说外部可以任意选择一个节点进行访问。比如我本地集群有三个节点:172.17.8.100172.17.8.101 和 172.17.8.102

[root@localhost ~]# curl http://172.17.8.100:31647
viola from whoami-mc2fg
[root@localhost ~]# curl http://172.17.8.101:31647
viola from whoami-8zc3d
[root@localhost ~]# curl http://172.17.8.102:31647
viola from whoami-z6skj

有了 nodePort,用户可以通过外部的 Load Balance 或者路由器把流量转发到任意的节点,对外提供服务的同时,也可以做到负载均衡的效果。

nodePort 类型的服务并不影响原来虚拟 IP 的访问方式,内部节点依然可以通过 vip:port 的方式进行访问。

LoadBalancer 类型的服务需要公有云支持,如果你的集群部署在公有云(GCE、AWS等)可以考虑这种方式。

service 原理解析

目前 kube-proxy 默认使用 iptables 模式,上述展现的 service 功能都是通过修改 iptables 实现的。

我们来看一下从主机上访问 service:port 的时候发生了什么(通过 iptables-save 命令打印出来当前机器上的 iptables 规则)。

所有发送出去的报文会进入 KUBE-SERVICES 进行处理

*nat
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

KUBE-SERVICES 每条规则对应了一个 service,它告诉继续进入到某个具体的 service chain 进行处理,比如这里的 KUBE-SVC-OQCLJJ5GLLNFY3XB

-A KUBE-SERVICES -d 10.10.10.28/32 -p tcp -m comment --comment "default/whoami: cluster IP" -m tcp --dport 3000 -j KUBE-SVC-OQCLJJ5GLLNFY3XB

更具体的 chain 中定义了怎么转发到对应 endpoint 的规则,比如我们的 rc 有三个 pods,这里也就会生成三个规则。这里利用了 iptables 随机和概率转发的功能

-A KUBE-SVC-OQCLJJ5GLLNFY3XB -m comment --comment "default/whoami:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-VN72UHNM6XOXLRPW
-A KUBE-SVC-OQCLJJ5GLLNFY3XB -m comment --comment "default/whoami:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-YXCSPWPTUFI5WI5Y
-A KUBE-SVC-OQCLJJ5GLLNFY3XB -m comment --comment "default/whoami:" -j KUBE-SEP-FN74S3YUBFMWHBLF

我们来看第一个 chain,这个 chain 有两个规则,第一个表示给报文打上 mark;第二个是进行 DNAT(修改报文的目的地址),转发到某个 pod 地址和端口。

-A KUBE-SEP-VN72UHNM6XOXLRPW -s 10.11.32.6/32 -m comment --comment "default/whoami:" -j KUBE-MARK-MASQ
-A KUBE-SEP-VN72UHNM6XOXLRPW -p tcp -m comment --comment "default/whoami:" -m tcp -j DNAT --to-destination 10.11.32.6:3000

因为地址是发送出去的,报文会根据路由规则进行处理,后续的报文就是通过 flannel 的网络路径发送出去的。

nodePort 类型的 service 原理也是类似的,在 KUBE-SERVICES chain 的最后,如果目标地址不是 VIP 则会通过 KUBE-NODEPORTS :

Chain KUBE-SERVICES (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 KUBE-NODEPORTS  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL

而 KUBE-NODEPORTS chain 和 KUBE-SERVICES chain 其他规则一样,都是转发到更具体的 service chain,然后转发到某个 pod 上面。

-A KUBE-NODEPORTS -p tcp -m comment --comment "default/whoami:" -m tcp --dport 31647 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/whoami:" -m tcp --dport 31647 -j KUBE-SVC-OQCLJJ5GLLNFY3XB

不足之处

看起来 service 是个完美的方案,可以解决服务访问的所有问题,但是 service 这个方案(iptables 模式)也有自己的缺点。

首先,如果转发的 pod 不能正常提供服务,它不会自动尝试另一个 pod,当然这个可以通过 readiness probes 来解决。每个 pod 都有一个健康检查的机制,当有 pod 健康状况有问题时,kube-proxy 会删除对应的转发规则。

另外,nodePort 类型的服务也无法添加 TLS 或者更复杂的报文路由机制。

基于 Docker for MAC 的 Kubernetes 本地环境搭建与应用部署

下载最新的 Docker for Mac 或者 Edge 版本,即可以看到内置的 Kubernetes 集群,直接点击安装即可在本地搭建好单节点的 Kubernetes 环境:

安装完毕后,如果我们也勾选了 Show system containers 选项,那么使用如下的 Docker 命令,能看到自动安装的 Kubernetes 相关容器:

➜  ~ docker container ls --format "table{{.Names}}\t{{.Image }}\t{{.Command}}"
NAMES                                                                                                                   IMAGE                                                    COMMAND
k8s_compose_compose-75f8bb4779-stxv9_docker_3c963862-f9f4-11e7-93cc-025000000001_0                                      docker/kube-compose-controller                           "/compose-controller…"
k8s_POD_compose-75f8bb4779-stxv9_docker_3c963862-f9f4-11e7-93cc-025000000001_0                                          gcr.io/google_containers/pause-amd64:3.0                 "/pause"
k8s_sidecar_kube-dns-545bc4bfd4-799pr_kube-system_139bf000-f9f4-11e7-93cc-025000000001_0                                gcr.io/google_containers/k8s-dns-sidecar-amd64           "/sidecar --v=2 --lo…"
k8s_dnsmasq_kube-dns-545bc4bfd4-799pr_kube-system_139bf000-f9f4-11e7-93cc-025000000001_0                                gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64     "/dnsmasq-nanny -v=2…"
k8s_kubedns_kube-dns-545bc4bfd4-799pr_kube-system_139bf000-f9f4-11e7-93cc-025000000001_0                                gcr.io/google_containers/k8s-dns-kube-dns-amd64          "/kube-dns --domain=…"
k8s_kube-proxy_kube-proxy-rrd8t_kube-system_139b00df-f9f4-11e7-93cc-025000000001_0                                      gcr.io/google_containers/kube-proxy-amd64                "/usr/local/bin/kube…"
k8s_POD_kube-dns-545bc4bfd4-799pr_kube-system_139bf000-f9f4-11e7-93cc-025000000001_0                                    gcr.io/google_containers/pause-amd64:3.0                 "/pause"
k8s_POD_kube-proxy-rrd8t_kube-system_139b00df-f9f4-11e7-93cc-025000000001_0                                             gcr.io/google_containers/pause-amd64:3.0                 "/pause"
k8s_kube-scheduler_kube-scheduler-docker-for-desktop_kube-system_972d74c9fc2f4ebd8ab673058e386a65_0                     gcr.io/google_containers/kube-scheduler-amd64            "kube-scheduler --ad…"
k8s_kube-apiserver_kube-apiserver-docker-for-desktop_kube-system_f7a81e8fe624bd46059fc6084e86bb81_0                     gcr.io/google_containers/kube-apiserver-amd64            "kube-apiserver --ad…"
k8s_etcd_etcd-docker-for-desktop_kube-system_56a21c0a5f545c0cca5388c457bb1b3b_0                                         gcr.io/google_containers/etcd-amd64                      "etcd --advertise-cl…"
k8s_kube-controller-manager_kube-controller-manager-docker-for-desktop_kube-system_8d1848c1e562e35a225e402988eadcd1_0   gcr.io/google_containers/kube-controller-manager-amd64   "kube-controller-man…"
k8s_POD_kube-apiserver-docker-for-desktop_kube-system_f7a81e8fe624bd46059fc6084e86bb81_0                                gcr.io/google_containers/pause-amd64:3.0                 "/pause"
k8s_POD_kube-controller-manager-docker-for-desktop_kube-system_8d1848c1e562e35a225e402988eadcd1_0                       gcr.io/google_containers/pause-amd64:3.0                 "/pause"
k8s_POD_kube-scheduler-docker-for-desktop_kube-system_972d74c9fc2f4ebd8ab673058e386a65_0                                gcr.io/google_containers/pause-amd64:3.0                 "/pause"
k8s_POD_etcd-docker-for-desktop_kube-system_56a21c0a5f545c0cca5388c457bb1b3b_0                                          gcr.io/google_containers/pause-amd64:3.0                 "/pause"

关于各个容器的作用,可以参阅 这里。在安装过程中,Docker 也为我们安装了 kubectl 控制命令:

$ kubectl get namespaces
$ kubectl get posts --namespace kube-system

接下来我们可以使用 kubectl 命令来创建简单的 kubernetes-dashboard 服务:

➜  ~ kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
secret "kubernetes-dashboard-certs" created
serviceaccount "kubernetes-dashboard" created
role "kubernetes-dashboard-minimal" created
rolebinding "kubernetes-dashboard-minimal" created
deployment "kubernetes-dashboard" created
service "kubernetes-dashboard" created

服务安装完毕后可以查看部署的容器与服务:

➜  ~ kubectl get deployments --namespace kube-system
NAME                   DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kube-dns               1         1         1            1           22m
kubernetes-dashboard   1         1         1            0           26s
➜  ~ kubectl get services --namespace kube-system
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
kube-dns               ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP   22m
kubernetes-dashboard   ClusterIP   10.111.242.95   <none>        443/TCP         30s

在 Dashboard 启动完毕后,可以使用 kubectl 提供的 Proxy 服务来访问该面板:

$ kubectl proxy

# 打开如下地址:
# http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/

如果访问报错,可以尝试编辑 kubernetes-dashboard 服务,或者参阅这里

$ kubectl -n kube-system edit service kubernetes-dashboard

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
...
  name: kubernetes-dashboard
  namespace: kube-system
  resourceVersion: "343478"
  selfLink: /api/v1/namespaces/kube-system/services/kubernetes-dashboard-head
  uid: 8e48f478-993d-11e7-87e0-901b0e532516
spec:
  clusterIP: 10.100.124.90
  externalTrafficPolicy: Cluster
  ports:
  - port: 443
    protocol: TCP
    targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard
  sessionAffinity: None
  type: ClusterIP ->> NodePort
status:
  loadBalancer: {}

访问上述地址,我们可以看到登录界面:

此时可暂时直接跳过,进入到控制面板中:

Docker 同样为我们提供了简单的应用示范,可以直接使用如下的 Docker Compose 配置文件:

version: '3.3'

services:
  web:
    build: web
    image: dockerdemos/lab-web
    volumes:
     - "./web/static:/static"
    ports:
     - "80:80"

  words:
    build: words
    image: dockerdemos/lab-words
    deploy:
      replicas: 5
      endpoint_mode: dnsrr
      resources:
        limits:
          memory: 16M
        reservations:
          memory: 16M

  db:
    build: db
    image: dockerdemos/lab-db

然后使用 stack 命令创建应用栈:

$ docker stack deploy --compose-file stack.yml demo

Stack demo was created
Waiting for the stack to be stable and running...
 - Service web has one container running

应用栈创建完毕后,可以使用 kubectl 查看创建的 Pods:

$ kubectl get pods

NAME                     READY     STATUS    RESTARTS   AGE
db-7f99cc64b7-cbd9t      1/1       Running   0          2m
web-758c6998f8-tmxfm     1/1       Running   0          2m
words-54bf6c5d57-8bxc8   1/1       Running   0          2m
words-54bf6c5d57-dzxm8   1/1       Running   0          2m
words-54bf6c5d57-k2448   1/1       Running   0          2m
words-54bf6c5d57-mhh4p   1/1       Running   0          2m
words-54bf6c5d57-w2q82   1/1       Running   0          2m

也可以来查看部署的集群与服务:

$ kubectl get deployments
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
db        1         1         1            1           3m
web       1         1         1            1           3m
words     5         5         5            5           3m

$ kubectl get services
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
db           ClusterIP      None           <none>        55555/TCP      3m
kubernetes   ClusterIP      10.96.0.1      <none>        443/TCP        52m
web          LoadBalancer   10.97.154.28   <pending>     80:30577/TCP   3m
words        ClusterIP      None           <none>        55555/TCP      3m

可以看到这里的 web 有所谓的 LoadBalancer 类型,即可以对外提供服务。最后我们还可以用 stack 与 kubectl 命令来删除应用:

$ docker stack remove demo
$ kubectl delete deployment kubernetes-dashboard --namespace kube-system

ISTJ 检查员型——细致、谨慎地执行好现有规则

一、基本描述

才储分析:您的性格类型倾向为“ ISTJ ”(内向 实感 思维 判断 倾向度: I57 S71 T57 J71  不假思索指数:6)

沉静,认真;贯彻始终、得人信赖而取得成功。讲求实际,注重事实和有责任感。能够合情合理地去决定应做的事情,而且坚定不移地把它完成,不会因外界事物而分散精神。以做事有次序、有条理为乐—不论在工作上, 家庭上或者生活上。重视传统和忠诚。

ISTJ型的人是严肃的、有责任心的和通情达理的社会坚定分子。他们值得信赖,他们重视承诺,对他们来说,言语就是庄严的宣誓。 ISTJ型的人工作缜密,讲求实际,很有头脑也很现实。他们具有很强的集中力、条理性和 准确性。无论他们做什么,都相当有条理和可靠。他们具有坚定不移、深思熟虑的思想,一旦他们着手自己相信是最好的行动方法时,就很难转变或变得沮丧。ISTJ型的人特别安静和勤奋,对于细节有很强的记忆和判断。 他们能够引证准确的事实支持自己的观点,把过去的经历运用到现在的决策中。他们重视和利用符合逻辑、客观的分析,以坚持不懈的态度准时地完成工作,并且总是安排有序,很有条理。他们重视必要的理论体系和传统 惯例,对于那些不是如此做事的人则很不耐烦。ISTJ型的人总是很传统、谨小甚微。他们聆听和喜欢确实、清晰地陈述事物。ISTJ型的人天生不喜欢显露,即使危机之时,也显得很平静。他们总是显得责无旁贷、坚定不变 、但是在他们冷静的外表之下,也许有强烈却很少表露的反应。

二、气质类型

根据大卫.凯尔西(David Keirsey)气质与性情理论,你属于“传统主义者”,下面是对“传统主义者”的描述:

“传统主义者”相信事实、已证实的数据、过去的经验和“五官”所带给他们的信息,喜欢有结构有条理的世界,喜欢做决定,是一 种既现实又有明确目标的人。

“传统主义者”是最传统的一类人,他们坚定、可靠、可信。他们重视法律、秩序、安全、得体、规则和本分。他们被一种 为社会服务的动机所驱使。他们尊重权威、等级制度和权力,而且一般具有保守的价值观。他们很有责任感,而且经常努力去做正确 的事情,这使他们可以信赖和依靠。

“传统主义者”需要有归属感,需要服务于别人,需要做正确的事情。他们注重安稳、秩序、合作、前后一致和可靠,而且他们严肃 认真,工作努力。“传统主义者”在工作中对自己要求十分严格,而且他们希望别人也是如此。“传统主义者”喜欢那些与他们一样 具有奉献精神、尊重权威和尽自己的本分的同事。

“传统主义者”往往是组织机构的主要支持者,不论他们是在领导层还是处于被领导的位置上。他们最常扮演的角色就是“稳定器” ——传统和现状的维护者。 大多数“传统主义者”(不论他们是属于什么判断偏好)最喜欢的是组织结构稳定、清楚、目标明确 的岗位,不喜欢处在不断变化和杂乱状况之中的职位或组织。

“传统主义者”包括思维型传统主义者和情感型传统主义者,这两种传统主义者之间又有很明显的不同。情感型传统主义者常常不象 思维型传统主义者那样明显地表现出传统主义者的一般特征。在做决定时,情感型传统主义者把与别人的关系和人放在首要位置,本 能地努力寻求与他人更和睦的关系,同时不断寻找着使他们能够通过有形的方式帮助他人的机会。

对于不同的性格类型而言,没有“好”与“坏”之分,每一个人都是一个独一无二的个体,都有其特别的优势和劣势,但问题的关键 在于如何认识这些优势和劣势。基于MBTI模型职业规划的核心法则是:“扬长避短”,学会了这一点将会影响到你的成败及你对工作的正确选择。

对你的总体描述

1. 实际,有条理,认真仔细。
2. 注重规则、政策、契约、例行习惯和时间要求。
3. 一旦他们承诺一件事情,总会坚持完成它。
4. 在跟进、规范方面做得很好。
5. 以第一次和每一次都做了正确的事情为荣。
6. 对需要注意的事情有敏锐的洞察力。
7. 善于尽可能有效地利用现有资源完成工作。

你潜在的弱点 

1. 容易只看到事情有黑和白两种情况,而看不到中间的灰色地带。
2. 可能不能很快地做出改变和适应。
3. 有些此类型成员不擅长变通、缺乏想象力。

三、优势与劣势

ISTJ的特质: 检查员型——细致、谨慎地执行好现有规则

优势:

你是一个认真而严谨的人,勤奋而负有责任感,认准的事情很少会改变或气馁,做事深思熟虑,信守承诺并值得信赖。

你依靠理智的思考来做决定,总是采取客观、合乎逻辑的步骤,不会感情用事,甚至在遇到危机时都能够表现得平静。

你谨慎而传统,重视稳定性、合理性;你天生独立,需要把大量的精力倾注到工作中,并希望其它人也是如此,善于聆听并喜欢将事情清晰而条理的安排好。

你喜欢先充分收集各种信息,然后根据信息去综合考虑实际的解决方法,而不是运用理论去解决。你对细节非常敏感,有很实际的判断力,决定时能够运用精确的证据和过去的经验来支持自己的观点,并且非常系统有条不紊,对那些不这样做的人没有耐心。

劣势:

你非常固执,一旦决定的事情,会对其他的观点置之不理,并经常沉浸于具体的细节和日常的操作中。

你看问题有很强的批判性,通常持怀疑态度,你需要时常的换位思考,更广泛的收集信息,并理智的评估自己的行为带来的可能后果。

你非常独立,我行我素,不能理解不合逻辑的事情,忽视他人的情感,并对与你风格不同的人不能理解,非常挑剔;你要学会欣赏他人的优点并及时表达出来。

你非常有主见,时常会将自己的观点和标准强加给别人,而且无视那些不自信的人的建议。在处理问题时,强求别人按照自己的想法来做,对于未经检验或非常规的方法不加考虑。若能在以后多尝试和接受新颖的、有创造性的方法,你就能做出更有效的决策。