做者: 李佶澳 轉載請保留:原文地址 發佈時間:2018-09-29 15:41:50 +0800html
這是API網關Kong的系列教程中的一篇,使用過程當中遇到的問題和解決方法記錄在API網關Kong的使用過程當中遇到的問題以及解決方法。nginx
Nginx、OpenRestry、Kong這三個項目緊密相連: Nginx是模塊化設計的反向代理軟件,C語言開發; OpenResty是以Nginx爲核心的Web開發平臺,能夠解析執行Lua腳本(OpenResty與Lua的關係,相似於Jvm與Java,不過Java能夠作的事情太多了,OpenResty主要用來作Web、API等); Kong是一個OpenResty應用,是一個api gateway,具備API管理和請求代理的功能。git
Nginx是HTTP Server、反向代理服務器、郵件代理服務器、通用的TCP/UDP代理服務器。nginx features詳細列出了nginx的功能特性。github
Nginx的配置文件由單指令(simple directive)
和塊指令(block directive)
組成,單指令只有一行,以「;」結尾,塊指令後面是用「{ }」包裹的多行內容。web
有些塊指令後的花括號中能夠繼續包含單指令,這樣的塊指令被成爲配置上下文(context)
,這樣的指令有:events、http、server、location等。正則表達式
context是嵌套的,最外層的context是main context
,配置文件中不在{}
的中指令都是位於main context
中。sql
events和http指令位於main context,server位於http context,location位於server context:docker
main context
- events
- http
- server
- location
配置文件示例見: Beginner’s Guide,例如:數據庫
http {
server {
listen 8080; # server監聽端口,不指定默認80
root /data/up1; # 默認文件查找根目錄
# 將請求按照uri進行分組處理
location / { # 選擇最常匹配的location,若是不匹配任何location,返回404
root /data/www; # 文件查找根目錄,覆蓋server中的root配置
}
# uri路徑匹配,優先級低於下面的正則匹配!
location /images/ {
root /data;
}
# 使用正則表達式匹配(必須帶有"~ "前綴):匹配文件後綴名
location ~ \.(gif|jpg|png)$ { # 優先級高於uri路徑匹配
root /data/images;
}
# 做爲代理服務器的配置方法
location /proxy/ { # 將uri匹配的請求轉發到proxy_pass指定的地址
proxy_pass http://IP地址:8080;
}
# 將請求代理到FastCGI
# fastcgi_param是按照FastCGI的要求傳遞的參數,能夠有多個,後面的`$XXX`是Nginx變量,拼成了參數的值
location /fastcgi/ {
fastcgi_pass localhost:9000; # fastCGI服務的地址
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 傳給fastCGI服務的參數: SCRIPT_FILENAME
fastcgi_param QUERY_STRING $query_string; # 傳給fastCGI服務的參數: QUERY_STRING
}
}
}
上面的例子中的proxy_pass
和factcgi_pass
分別是nginx的http proxy module和http fastcgi moudle中指令。json
Nginx有不少的module,在Nginx Documents中能夠查看每一個modules的用法。
Nginx: Alphabetical index of directives中列出了Nginx的全部指令。
Nginx: Alphabetical index of variables中列出了能夠在配置文件中使用的全部變量。
在查看Nginx指令用法的時候,注意指令的context:
Syntax: gzip on | off;
Default: gzip off;
Context: http, server, location, if in location # 可使用gzip指令的地方
一個最經常使用的模塊是ngx_http_upstream_module,使用該模塊後,能夠用upstream指令選定一組server:
resolver 10.0.0.1;
upstream dynamic {
zone upstream_dynamic 64k;
server backend1.example.com weight=5;
server backend2.example.com:8080 fail_timeout=5s slow_start=30s;
server 192.0.2.1 max_fails=3;
server backend3.example.com resolve;
server backend4.example.com service=http resolve;
server backup1.example.com:8080 backup;
server backup2.example.com:8080 backup;
}
server {
location / {
proxy_pass http://dynamic;
health_check;
}
}
Nginx本來只能作7層(http)代理,在1.9.0版本中增長了4層(TCP/UDP)代理功能。
4層代理功能在Nginx的ngx_stream_core_module模塊中實現,但默認沒有編譯,須要在編譯時指定: –with-stream。
使用配置以下:
worker_processes auto;
error_log /var/log/nginx/error.log info;
events {
worker_connections 1024;
}
stream {
upstream backend {
hash $remote_addr consistent;
server backend1.example.com:12345 weight=5;
server 127.0.0.1:12345 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
}
upstream dns {
server 192.168.0.1:53535;
server dns.example.com:53;
}
server {
listen 12345;
proxy_connect_timeout 1s;
proxy_timeout 3s;
proxy_pass backend;
}
server {
listen 127.0.0.1:53 udp reuseport;
proxy_timeout 20s;
proxy_pass dns;
}
server {
listen [::1]:12345;
proxy_pass unix:/tmp/stream.socket;
}
}
理解Nginx Module很重要,由於後面的OpenResty就是標準的Nginx加上不少Nginx Module。
Nginx是用C語言開發軟件,採用模塊化設計,能夠經過開發模塊擴展Nginx的功能。
Nginx Development guide中介紹了Nginx模塊開發的方法Nginx Module develop。
插件能夠編譯成.so之後動態加載,也能夠直接編譯到nginx中,編譯是經過--add-module
指定要集成的模塊。
./configure --prefix=/opt/nginx \
--with-ld-opt="-Wl,-rpath,/path/to/luajit-or-lua/lib" \
--add-module=/path/to/ngx_devel_kit \
--add-module=/path/to/lua-nginx-module
OpenResty是一個集成了Nginx、LuaJIT和其它不少moudels的平臺,用來託管完整的web應用——包含業務邏輯,而不單純是靜態文件服務器:
OpenResty® aims to run your server-side web app completely in the Nginx server,
leveraging Nginx's event model to do non-blocking I/O not only with the HTTP
clients, but also with remote backends like MySQL, PostgreSQL, Memcached, and Redis.
OpenResty Components中列出了OpenResty集成的組件,數量很多,這裏就不列出來了。
先經過OpenResty Getting Started感覺一下OpenResty是咋回事。
OpenResty集成了LuaJit,一個Lua代碼的實時編譯器,支持使用Lua代碼。
Centos安裝方式:
sudo yum install yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
sudo yum install openresty
sudo yum install openresty-resty
經過源代碼編譯:
wget https://openresty.org/download/openresty-1.13.6.2.tar.gz
tar -xvf openresty-1.13.6.2.tar.gz
cd openresty-1.13.6.2/
./configure --with-pcre-jit --with-http_ssl_module --with-http_realip_module --with-http_stub_status_module --with-http_v2_module --prefix=/usr/local/bin/openresty
make -j2
make install //默認安裝在--prefix指定的目錄:/usr/local/bin/openresty
export PATH=/usr/local/openresty/bin:$PATH
爲了後面順利的使用kong,執行./configure時要指定kong依賴的模塊。
OpenResty的安裝目錄包含如下文件:
$ tree -L 2 /usr/local/openresty/
/usr/local/openresty/
|-- bin
| |-- md2pod.pl
| |-- nginx-xml2pod
| |-- openresty -> /usr/local/openresty/nginx/sbin/nginx
| |-- opm
| |-- resty
| |-- restydoc
| `-- restydoc-index
|-- COPYRIGHT
|-- luajit
| |-- bin
| |-- include
| |-- lib
| `-- share
...
注意openresty命令就是nginx命令
,OpenResty能夠理解爲一個集成了不少模塊的定製版nginx:
$ openresty -h
nginx version: openresty/1.13.6.2
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]
Options:
-?,-h : this help
-v : show version and exit
-V : show version and configure options then exit
-t : test configuration and exit
-T : test configuration, dump it and exit
-q : suppress non-error messages during configuration testing
-s signal : send signal to a master process: stop, quit, reopen, reload
-p prefix : set prefix path (default: /usr/local/openresty/nginx/)
-c filename : set configuration file (default: conf/nginx.conf)
-g directives : set global directives out of configuration file
nginx集成了不少模塊以後,能夠執行lua代碼。
OpenResty的安裝目錄中有一個resty
文件,它是一個perl腳本,能夠直接給它傳入lua代碼:
$ resty -e 'print("hello, world!")'
hello, world!
查看resty文件/usr/local/openresty/bin/resty
,會發現最後的執行者仍是nginx命令:
my $nginx_path = '/usr/local/openresty/nginx/sbin/nginx';
...
my @cmd = ($nginx_path, '-p', "$prefix_dir/", '-c', "conf/nginx.conf");
...
OpenResty本質上就是一個定製的nginx,經過支持在配置文件中使用lua代碼,成爲一個完善的應用開發平臺。
API網關Kong是一個典型的OpenResty應用,它的數據平面實現中,直接生成了一個使用了kong模塊的nginx.conf文件,而後直接給nginx指定這個配置啓動。
這時候的nginx有點相似於能夠加載執行lua代碼的解釋器。
OpenResty的配置文件中也能夠寫入lua代碼:
$ cat nginx.conf
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
location / {
default_type text/html;
content_by_lua '
ngx.say("<p>hello, world</p>")
';
}
}
}
啓動:
openresty -p `pwd` -c nginx.conf
而後訪問」127.0.0.1:8080」,能夠看到輸出:
$ curl 127.0.0.1:8080
<p>hello, world</p>
Kong是一個基於OpenResty的應用,是一個API網關。
Kong編譯安裝時須要先安裝OpenResty。
還須要lua包管理工具luarocks:
git clone git://github.com/luarocks/luarocks.git
./configure --lua-suffix=jit --with-lua=/usr/local/openresty/luajit --with-lua-include=/usr/local/openresty/luajit/include/luajit-2.1
make install
下載kong代碼編譯:
git clone https://github.com/Kong/kong.git
cd kong
make install
編譯完成以後會在當前目錄生成一個bin目錄:
$ ls bin/
busted kong
查看bin/kong的內容,能夠發現這是一個用resty
執行的腳本文件:
$ cat bin/kong
#!/usr/bin/env resty
require "luarocks.loader"
package.path = "./?.lua;./?/init.lua;" .. package.path
require("kong.cmd.init")(arg)
先準備數據庫,kong支持PostgreSQL和Cassandra 3.x.x,這裏使用PostgreSQL(須要版本在9.4及以上):
注意,若是使用其它版本的PostgreSQL,將下面的9.6換成對應版本號。
yum install https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm
yum install postgresql96
yum install postgresql96-server
export PATH=$PATH:/usr/pgsql-9.6/bin/
postgresql96-setup initdb
systemctl start postgresql-9.6
su - postgres
psql
CREATE USER kong; CREATE DATABASE kong OWNER kong;
alter user kong with encrypted password '123456';
\q
在/var/lib/pgsql/9.6/data/pg_hba.conf的開始處
添加規則下面規則:
host kong kong 127.0.0.1/32 md5
而後重啓PostgreSQL
,確保下面的命令能登錄PostgreSQL:
# psql -h 127.0.0.1 -U kong kong -W
Password for user kong:
psql (9.6.10)
Type "help" for help.
kong=>
PostgreSQL的部署使用和經過密碼登錄方式的設置參考:PostgresSQL數據庫的基本使用、PostgreSQL的用戶究竟是這麼回事?新用戶怎樣才能用密碼登錄?。
準備kong的配置文件,
cp kong.conf.default kong.conf
# 在kong.conf中填入數據地址、用戶、密碼等
建立kong的數據庫:
./bin/kong migrations up -c ./kong.conf
啓動kong:
./bin/kong start -c ./kong.conf
kong默認的代理地址是:
proxy_listen = 0.0.0.0:8000, 0.0.0.0:8443
默認的管理地址是:
admin_listen = 127.0.0.1:8001, 127.0.0.1:8444 ssl
返回的是json字符串:
$ curl -i http://localhost:8001/
HTTP/1.1 200 OK
Date: Sat, 29 Sep 2018 08:56:51 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/0.14.1
Content-Length: 5667
{"plugins":{"enabled_in_cluster":[],"availab...
PGBI/kong-dashboard是一個第三方的Dashboard。
docker run --rm -p 8080:8080 pgbi/kong-dashboard start \
--kong-url http://kong:8001
--basic-auth user1=password1 user2=password2
中止:
kong stop
從新加載:
kong reload
添加一個名爲example-service
的服務,服務地址是http://mockbin.org
:
curl -i -X POST \
--url http://localhost:8001/services/ \
--data 'name=example-service' \
--data 'url=http://mockbin.org'
執行後返回:
{
"connect_timeout": 60000,
"created_at": 1538213979,
"host": "mockbin.org",
"id": "ebed2707-e2fb-4694-9e8e-fb66fe9dd7c8",
"name": "example-service",
"path": null,
"port": 80,
"protocol": "http",
"read_timeout": 60000,
"retries": 5,
"updated_at": 1538213979,
"write_timeout": 60000
}
爲example-service
添加一個route
,知足route的請求將被轉發給example-service,執行:
curl -i -X POST \
--url http://localhost:8001/services/example-service/routes \
--data 'hosts[]=example.com'
這裏配置的route條件是:host爲example.com。
返回:
{
"created_at": 1538185340,
"hosts": [
"example.com"
],
"id": "4738ae2c-b64a-4fe5-9e2a-5855e769a9e8",
"methods": null,
"paths": null,
"preserve_host": false,
"protocols": [
"http",
"https"
],
"regex_priority": 0,
"service": {
"id": "ebed2707-e2fb-4694-9e8e-fb66fe9dd7c8"
},
"strip_path": true,
"updated_at": 1538185340
}
這時候訪問kong的proxy地址
時,若是host爲example.com
,請求被轉發到http://mockbin.org
:
curl -i -X GET \
--url http://localhost:8000/ \
--header 'Host: example.com'
能夠在/etc/hostsname中將example.com地址配置爲kong所在的機器的地址:
10.10.192.35 example.com
而後就能夠經過example.com:8000
打開http://mockbin.org。
插件是用來擴展API的,例如爲API添加認證、設置ACL、限制速率等、集成oauth、ldap等。
Kong Plugins中列出了已有的全部插件。
這裏演示key-auth插件的用法,Kong Enabling Plugins,。
curl -i -X POST \
--url http://localhost:8001/services/example-service/plugins/ \
--data 'name=key-auth'
返回:
{
"config": {
"anonymous": "",
"hide_credentials": false,
"key_in_body": false,
"key_names": [
"apikey"
],
"run_on_preflight": true
},
"created_at": 1538218948000,
"enabled": true,
"id": "f25f3952-d0d4-4923-baac-860554fc2fc1",
"name": "key-auth",
"service_id": "ebed2707-e2fb-4694-9e8e-fb66fe9dd7c8"
}
這時候直接訪問example.com,會返回401:
curl -i -X GET \
> --url http://localhost:8000/ \
> --header 'Host: example.com'
HTTP/1.1 401 Unauthorized
Date: Sat, 29 Sep 2018 11:03:55 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
WWW-Authenticate: Key realm="kong"
Server: kong/0.14.1
Content-Length: 41
在kong中建立一個名爲Jason的用戶:
curl -i -X POST \
--url http://localhost:8001/consumers/ \
--data "username=Jason"
返回:
{
"created_at": 1538219225,
"custom_id": null,
"id": "f2450962-e4bb-477f-8df6-85984eb94e09",
"username": "Jason"
}
將Jason的密碼設置爲123456:
curl -i -X POST \
--url http://localhost:8001/consumers/Jason/key-auth/ \
--data 'key=123456'
返回:
{
"consumer_id": "f2450962-e4bb-477f-8df6-85984eb94e09",
"created_at": 1538219311000,
"id": "0332d36f-61b9-425a-b563-510c11a85e85",
"key": "123456"
}
這時候能夠用Jason的key訪問API:
curl -i -X GET \
--url http://localhost:8000 \
--header "Host: example.com" \
--header "apikey: 123456"
返回的是mockbin.org的首頁。
key-auth插件的詳細用法參考Kong Plugin: key-auth。插件的做用範圍能夠是全局(global)、服務(service)、路由(router)。
啓用key-auth後,經過認證的請求被轉發給上游服務時,key-auth會增設下面的字段:
X-Consumer-ID, the ID of the Consumer on Kong
X-Consumer-Custom-ID, the custom_id of the Consumer (if set)
X-Consumer-Username, the username of the Consumer (if set)
X-Credential-Username, the username of the Credential (only if the consumer is not the 'anonymous' consumer)
X-Anonymous-Consumer, will be set to true when authentication failed, and the 'anonymous' consumer was set instead.
Kong Plugins中列出了已有的全部插件,有些插件只能在企業版使用,有些插件是社區成員開發的,大部分是Kong公司開發,並集成到社區版中。
下面是社區版集成的、Kong公司維護的插件(2018-09-30 14:33:03):
認證插件:
Basic Auth
HMAC Auth
JWT Auth
Key Auth
LDAP Auth
OAuth 2.0 Auth
安全插件:
Bot Detection (機器人檢測)
CORS (跨域請求)
IP Restriction (IP限制)
流控插件:
ACL (訪問控制)
Rate Limiting (限速)
Request Size Limiting
Request Termination
Response Rate Limiting
微服務插件:
AWS Lambda
Azure Functions
Apache OpenWhisk
Serverless Functions
分析和監控插件:
Datadog
Prometheus
Zipkin
內容修改插件(Transformations):
Correlation ID
Request Transformer
Response Transformer
日誌插件:
File Log
HTTP Log
Loggly
StatsD
Syslog
TCP Log
UDP Log
通過前面的學習,對Api網關是什麼,以及Kong可以作什麼已經有了足夠的瞭解。如今Kubernetes一統計算資源與應用發佈編排的趨勢已經造成,咱們更關心Kong可否和Kubernetes結合。
Kong是一個Api網關,也是一個特性更豐富的反向代理,既然它有代理流量的功能,那麼能不能直接成爲Kubernetes的流量入口?使Kubernetes內部的服務都經過Kong發佈。
Kong實現了一個Kubernetes Ingress Controller來作這件事。在Kubernetes中部署kong的方法見Kong CE or EE on Kubernetes。
這部份內容比較多,單獨開篇了: Kubernetes與API網關Kong的集成。
啓動的時候:
# ./bin/kong start -c ./kong.conf
...
ERROR: ./kong/globalpatches.lua:63: module 'socket' not found:No LuaRocks module found for socket
...
這是由於編譯kong以後,從新編譯了luarocks,而且將luarocks安裝在了其它位置。從新編譯kong以後解決。
建立數據庫的時候:
# kong migrations up -c ./kong.conf
...
[postgres error] could not retrieve current migrations: [postgres error] ERROR: function to_regclass(unknown) does not exist (8)
...
這是由於PostgreSQL的版本過低了,to_regclass
在PostgreSQL 9.4及以上的版本中才存在。
yum install https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm
yum install postgresql96
yum install postgresql96-server
nginx: [emerg] unknown directive "real_ip_header" in /usr/local/kong/nginx-kong.conf:73
這是由於編譯的openresty的時候,沒有指定--with-http_realip_module
,從新編譯安裝:
./configure --with-pcre-jit --with-http_ssl_module --with-http_realip_module --with-http_stub_status_module --with-http_v2_module
make -j2
make install //默認安裝在/usr/local/bin/openresty
export PATH=/usr/local/openresty/bin:$PATH