Kong 的架构研究与探索

Kong作为比较受欢迎的API网关, 基于OpenRestry , 能充分发挥Nginx epoll 模型的强大并发支持, 并且使用Lua 来对模块进行配置与热插拔.

 

目前使用的是Kong的最新版 1.3.0 , 然后使用的是PostgreSQL用作配置存储, 目前是可以不在后端采用数据库的, 直接使用配置文件来组织和管理规则~

 

首先是Kong的几个关键的概念:

Kong是什么?

Kong的组成?

Kong能干什么?

Kong的扩展性与高可用?

 

第一个问题在开篇已经介绍了, 用作API 网关.

既然是做到网关的功能了, 那网关的关键的功能应该都一应俱全.

比如API转发请求, API注册, API权限控制, API后端负载均衡, API使用统计, API限流.

这些功能都是原生提供了的, 需要在配置的时候, 进行相应的配置并启用插件来实现.

 

Kong的组成, 首先从软件层面来看, 我的Kong 是采用rpm包进行安装的, 也就是预编译好的二进制包, 安装完成后, 进行对应的配置即可启动服务.

为了方便研究和管理, 我还搭建了一个Konga的 图形管理界面. 用于快捷的操作Admin的API来对Kong 进行管理.

新手最好也弄一个这个Konga ,因为光看文档的话, 不一定列出了所有的参数. 在Konga里面的话, 就把能配置的参数都一一列出来了. 虽然看起来上手难度更加高, 但是对于新手的操作的话, 更加友好.

服务启动之后, 主要还是Nginx, 还有用于存储配置的PostgreSQL 数据库.

使用默认的情况下, 进程会监听8000端口和8001端口, 8000端口主要处理API请求, 8001端口的话就是Admin API所监听的端口.

几乎所有的管理都是通过Admin API接口来进行处理的.

 

Kong的内部逻辑组成

主要由以下几个部分组成:

Service

Route

Consumer

Upstream

Plugin

Certificate

 

 

按照发起一次API请求

Client -> Gateway -> Back-end Service 的顺序来简要介绍一下这几个内部的逻辑组件

 

  1. Consumer/anonymous 用户/匿名用户

对于API的访问, 我们需要对无需验证的接口开放访问, 就需要开放匿名用户访问接口, 此处的anonymous 就是用作无需授权进行访问Route

而对于内部接口和计费接口, 需要验证身份进行访问的话, 就需要在内部设定好Consumer 这个角色, 配置好验证方式. 并授予合适的权限给Consumer.

确保Consumer 能访问获得授权的接口.

 

  1. Route: Client 通过匿名或者授权的方式访问到了 Gateway之后,  首先进到的是Route,

Route 定义了各种路由规则和权限来进行访问, 与它关联的组件有 Service, 在匹配到合适的路由规则之后, 会对请求进转发.

 

  1. Service: 匹配到Route之后, 会进入到Service里面, Service里定义了转发到后端的Host, Path, Port 等这些关键信息, 按照Service内定义好的规则将请求转发.
  2. Plugin: 在请求接受到转发全程可以通过插件来实现特定功能.
  3. Certificate: 验证凭据

 

 

Kong能干什么?

OpenRestry 的强大支持下, 能支持相当高的并发访问, 而且加上由Lua语言编写的Kong插件, 新加的配置能立马应用到Kong上面. 有了这些工具的加成,

对于服务API的管理如虎添翼.

 

Kong 服务搭建与图形界面安装

由于需要使用Kong来进行API治理, 在内网搭建了Kong的测试环境

 

使用的配置都是最基础的配置

 

主要步骤:

  1. 检查PostgreSQL 是否可用并服务启动
  2. 使用官方RPM包安装
  3. 配置基础配置进行启动
  4. 安装UI界面

 

使用官方RPM包安装

 

首先下载Kong的 安装包

 

https://bintray.com/kong/kong-rpm/download_file?file_path=centos/7/kong-1.3.0.el7.amd64.rpm

 

下载完成后, 直接使用yum 命令进行安装

yum install kong-1.3.0.el7.amd64.rpm

 

配置基础配置进行启动

 

安装完成后, 文件主要有如下几个目录需要注意和操作的

 

程序主要的目录

/usr/local/kong

配置文件目录

/etc/kong

 

/etc/kong

主要配置:

 

admin_access_log = logs/admin_access.log

admin_error_log = logs/error.log

admin_listen = 0.0.0.0:8001

anonymous_reports = on

log_level = notice

nginx_daemon = on

nginx_user = nginx nginx

nginx_worker_processes = auto

pg_database = kong

pg_host = 10.10.0.8

pg_password = kong1234

pg_port = 5432

pg_timeout = 5000

pg_user = kong

plugins = bundled

prefix = /usr/local/kong/

proxy_access_log = logs/access.log

proxy_error_log = logs/error.log

proxy_listen = 0.0.0.0:8000

real_ip_header = X-Real-IP

trusted_ips = 0.0.0.0/0,::/0

 

配置好之后, 使用 kong check 检查配置是否正常

 

没有问题的话 执行 kong migrations bootstrap 来初始化数据库

 

上步正常运行后, 执行 kong start 运行 Kong

 

安装UI界面

 

由于启动了的Kong服务只是一个简单的接口形式的服务, 如果想方便的管理和操作API和路由策略的话,可以使用Konga 这个开源项目来进行方便快捷的管理

 

配置好软件的基础环境

 

Konga 依赖 NodeJS 环境, 所以需要提前下载下 NodeJS 的二进制包并安装到系统上

 

https://nodejs.org/dist/v12.10.0/node-v12.10.0-linux-x64.tar.xz

 

解压后配置好环境变量

 

这边我安装解压到了 /usr/local/nodejs/ 目录下

 

配置环境变量为:

 

export PATH=$PATH:/usr/local/nodejs/bin

 

安装完成后, 使用npm 安装下cnpm (为了后面进行环境初始化的时候顺利进行)

 

从github 上拉一份最新的代码下来

 

git clone https://github.com/pantsel/konga.git

 

 

克隆完成后, 进入konga 的目录下, 执行 cnpm i命令

 

命令正常执行完成后, 执行 cnpm start 启动 konga

 

首先进入会要注册一个管理员用户, 按照需要填写完成后再登录到konga里面, 配置好kong的admin 的url 就能, 就能愉快的使用Kong的图形管理界面了,

 

 

 

PostgreSQL 搭建与简单配置文档

由于需要使用Kong, 数据库存储需要选用PostgreSQL 来做数据存储, 所以需要在Linux服务器上部署PostgreSQL

 

下面是简单的PostgreSQL 部署步骤
主要分为如下几步:
1. 添加PostgreSQL 软件源
2. 下载安装PostgreSQL 12 客户端和服务端
3. 调整配置与启动服务
4. 创建用户和对应数据库
5. 登录验证数据库

添加PostgreSQL 软件源

yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

yum makecache

下载安装PostgreSQL 12 客户端和服务端

yum install postgresql12 postgresql12-server postgresql12-devel

调整配置与启动服务

由于默认PostgreSQL 都是监听的127.0.0.1的端口, 所以如果局域网需要访问PostgreSQL的话, 就需要修改本地监听端口然后然后设定好相应的权限

/var/lib/pgsql/12/data/postgresql.conf

listen_addresses = ‘0.0.0.0’

/var/lib/pgsql/12/data/pg_hba.conf

host all all 10.10.0.0/24 md5

创建用户和对应数据库

sudo su – postgres

createuser kong -P

createdb kong -O kong -E UTF8 -e

登录验证数据库

psql -h 10.10.0.8 -p 5432 -U kong -W kong

Prometheus

Prometheus是一个最初在SoundCloud上构建的开源系统监视和警报工具包

 

普罗米修斯的主要特点是:

 

具有由度量名称和键/值对标识的时间序列数据的多维数据模型

PromQL,一种灵活的查询语言, 可以利用这一维度

不依赖分布式存储; 单个服务器节点是自治的

时间序列集合通过HTTP上的拉模型发生

推送时间序列通过中间网关支持

通过服务发现或静态配置发现目标

多种图形和仪表板支持模式

 

 

 

安装

  1. 从官方github release 里面下载最新的二进制包
  2. 解压
  3. 启动 nohup ./prometheus –config.file=prometheus.yml &

需要启用网页刷新配置接口加上参数 –web.enable-lifecycle

  1. 访问网页监控页面 http://127.0.0.1:9090/graph
  2. 访问自身监控接口页面 http://127.0.0.1:9100/metrics

 

新增主机监控

  1. 下载node_explorer
  2. 后台运行 nohup ./node_explorer &
  3. 默认监听在9100端口, 将IP地址添加到Prometheus配置文件中
  1. 执行重载配置文件操作

python的requests库发出请求

requests.put(‘http://192.168.48.128:9090/-/reload’)

参考API文档

https://prometheus.io/docs/prometheus/latest/management_api/

  1. 再次查询已经无法查询到其他的node的信息了.

如下图所示

 

 

结合Grafana 的图形界面

  1. 下载Grafana

配置ius软件源

  1. 安装并且配置

sudo yum localinstall grafana-6.3.5-1.x86_64.rpm

  1. 启动Grafana

systemctl  start grafana-server.service

  1. 配置Prometheus数据源

 

在容器里, 应该使服务内置Prometheus 的组件, 并提供端口来拉取监控信息.

对于容器组件, 容器中的服务注册到CMDB或者Prometheus中就尤为重要, 因为服务的不确定性,

我们需要让服务主动来上报服务的IP地址和端口信息来进行动态监控.

Python-Django默认设定(避免踩坑)

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
"""
Django settings for my_plaform project.

Generated by 'django-admin startproject' using Django 2.1.7.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""


import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'jk^@fmftf6s6&!0yphdh9)eaf=i)%n!a8kbmrb^lbyp%)&1ixw'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'service_info'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'my_plaform.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'template'),
                 os.path.join(BASE_DIR, 'static'),],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'my_plaform.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'zh-Hans'

TIME_ZONE = 'PRC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/


STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, '')
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]