apisix
This commit is contained in:
1 parent
b68632a50e
commit
31eb2d296d
9 files changed
+12022
No files matched your search
@@ -0,0 +1,248 @@
|
|||||||
|
Chỉnh sysctl trên máy chủ Host (máy Linux chạy Docker) để TCP Stack hoạt động tốt hơn.
|
||||||
|
|
||||||
|
# Cho phép tái sử dụng các socket đang ở trạng thái TIME_WAIT
|
||||||
|
sudo sysctl -w net.ipv4.tcp_tw_reuse=1
|
||||||
|
|
||||||
|
# Tăng hàng đợi kết nối (Backlog)
|
||||||
|
sudo sysctl -w net.core.somaxconn=65535
|
||||||
|
|
||||||
|
# Lưu lại cấu hình (để khi reboot không mất)
|
||||||
|
echo "net.ipv4.tcp_tw_reuse=1" | sudo tee -a /etc/sysctl.conf
|
||||||
|
echo "net.core.somaxconn=65535" | sudo tee -a /etc/sysctl.conf
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
# Hệ Thống Cổng Kết Nối API Trung Gian (API Gateway) Cho Dynamics 365 Dataverse
|
||||||
|
|
||||||
|
Tài liệu này hướng dẫn cách triển khai, cấu hình và vận hành hệ thống API Gateway tự host (Self-hosted) sử dụng **Apache APISIX** để cung cấp (expose) API của Microsoft Dynamics 365 Dataverse bảo mật cho bên thứ ba. Hệ thống tích hợp sẵn cụm quản lý log tập trung siêu nhẹ: **Grafana Loki + Promtail + Grafana**.
|
||||||
|
|
||||||
|
## 📌 Kiến Trúc Hệ Thống
|
||||||
|
|
||||||
|
```text
|
||||||
|
[Bên Thứ 3] ---> (Port: 9080 kèm API Key) ---> [APISIX Gateway] ---> (OAuth2 Token) ---> [Dataverse Web API]
|
||||||
|
|
|
||||||
|
(Docker Stdout JSON Log)
|
||||||
|
|
|
||||||
|
[Promtail] ---> [Grafana Loki] <--- [Grafana UI]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ Danh Sách Thành Phần & Cổng Kết Nối
|
||||||
|
|
||||||
|
| Thành phần | Cổng công khai (Host Port) | Vai trò | Tài khoản mặc định |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **APISIX Gateway** | `9080` (HTTP) / `9180` (Admin API) | Xử lý traffic, giới hạn lượt gọi, gán token bảo mật | - |
|
||||||
|
| **APISIX Dashboard** | `9090` | Giao diện cấu hình Route, Plugin trực quan | `admin` / `admin` |
|
||||||
|
| **Grafana** | `9300` | Giao diện truy vấn và giám sát log thời gian thực | `admin` / `admin` |
|
||||||
|
| **Loki** | `9100` | Cơ sở dữ liệu lưu trữ log tập trung (Siêu nhẹ) | - |
|
||||||
|
| **etcd** | *Không expose (Chỉ chạy nội bộ)* | Cơ sở dữ liệu phân tán lưu cấu hình của APISIX | Bảo mật nội bộ container |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Hướng Dẫn Triển Khai Nhanh
|
||||||
|
|
||||||
|
### Bước 1: Chuẩn bị cấu trúc thư mục
|
||||||
|
Tạo cấu trúc thư mục trên máy chủ của bạn như sau:
|
||||||
|
```text
|
||||||
|
apisix-stack/
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── apisix_conf/
|
||||||
|
│ ├── config.yaml
|
||||||
|
│ ├── dashboard_conf.yaml
|
||||||
|
│ └── schema.json
|
||||||
|
├── promtail_conf/
|
||||||
|
│ └── promtail-config.yaml
|
||||||
|
└── grafana_conf/
|
||||||
|
└── provisioning/
|
||||||
|
├── datasources/
|
||||||
|
│ └── datasources.yaml
|
||||||
|
└── dashboards/
|
||||||
|
├── dashboards.yaml
|
||||||
|
└── apisix_performance.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bước 2: Khởi chạy cụm Docker
|
||||||
|
Tại thư mục gốc (`apisix-stack/`), chạy lệnh sau để tải và chạy các dịch vụ:
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Cấu Hình Kết Nối Dynamics 365 Dataverse
|
||||||
|
|
||||||
|
### Bước 1: Đăng ký Ứng dụng trên Microsoft Entra ID (Azure AD)
|
||||||
|
1. Truy cập vào **Microsoft Entra ID** > **App registrations** > **New registration**.
|
||||||
|
2. Đặt tên (ví dụ: `APISIX-Dataverse-Proxy`) và bấm **Register**.
|
||||||
|
3. Lưu lại thông tin: `Application (client) ID` và `Directory (tenant) ID`.
|
||||||
|
4. Vào mục **Certificates & secrets** > **New client secret** > Tạo một Secret mới và sao chép lại **Value** của secret này.
|
||||||
|
5. Vào mục **API permissions** > **Add a permission** > Chọn **Dynamics CRM** > Tích chọn `user_impersonation` và bấm **Add permissions**. Sau đó bấm **Grant admin consent** cho tenant của bạn.
|
||||||
|
|
||||||
|
### Bước 2: Tạo Application User trong Dataverse
|
||||||
|
1. Truy cập vào [Power Platform Admin Center](https://microsoft.com).
|
||||||
|
2. Chọn Môi trường (Environment) của bạn > **Settings** > **Users + permissions** > **Application users**.
|
||||||
|
3. Bấm **New app user** > **Add an app** > Chọn ứng dụng bạn vừa đăng ký ở Bước 1.
|
||||||
|
4. Chọn **Business unit** và gán **Security Role** phù hợp để giới hạn quyền truy cập bảng dữ liệu (Table).
|
||||||
|
|
||||||
|
### Bước 3: Cấu hình Route trên APISIX Dashboard
|
||||||
|
Truy cập vào giao diện quản lý APISIX tại địa chỉ: `http://<IP-Cua-Ban>:9090`.
|
||||||
|
|
||||||
|
#### 1. Tạo Consumer (Cấp API Key cho bên thứ ba)
|
||||||
|
* Vào mục **Consumers** > Bấm **Create**.
|
||||||
|
* Đặt tên định danh (Username), ví dụ: `doi-tac-A`.
|
||||||
|
* Ở phần Plugin, bật plugin **`key-auth`** và điền một chuỗi bí mật vào ô `key` (ví dụ: `partner-secret-key-2026`).
|
||||||
|
|
||||||
|
#### 2. Tạo Route (Cấu hình luồng đi của API)
|
||||||
|
* Vào mục **Routes** > Bấm **Create**.
|
||||||
|
* **Định nghĩa đường dẫn (Request):** `Path`: `/ssm-vn-dev/*`
|
||||||
|
* **Cấu hình Đích đến (Upstream):** Chọn `Custom Upstream`. `Node URL`: `https://tngredev.crm5.dynamics.com` (Port: `443`).
|
||||||
|
* **Cấu hình Plugins:**
|
||||||
|
* **Cấu hình Plugins xác thực (Chọn một trong hai phương án sau tùy theo nhu cầu):**
|
||||||
|
* **Phương án A: Sử dụng OpenID Connect (Azure AD / Microsoft Entra ID) để bảo mật API đầu vào**
|
||||||
|
* Bật plugin **`openid-connect`** để APISIX đóng vai trò xác thực Token từ Client gửi lên:
|
||||||
|
* `client_id`: `<Mã Application (client) ID>`
|
||||||
|
* `client_secret`: `<Mã Client Secret Value>`
|
||||||
|
* `discovery`: `https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration`
|
||||||
|
* `bearer_only`: `true` (Chỉ chấp nhận Bearer Token ở header `Authorization`, không tự động redirect sang trang web login)
|
||||||
|
* **Phương án B: Xác thực Client bằng API Key (`key-auth`)**
|
||||||
|
* Bật plugin **`key-auth`** trên cấu hình Route. Giá trị cấu hình rất đơn giản, chỉ cần truyền một đối tượng rỗng:
|
||||||
|
```json
|
||||||
|
"key-auth": {}
|
||||||
|
```
|
||||||
|
*(Mặc định APISIX sẽ tìm header tên là `apikey` gửi từ Client. Nếu bạn muốn đổi tên header tùy biến, ví dụ `X-API-Key`, bạn nhập:* `{"header": "X-API-Key"}`*)*.
|
||||||
|
* *Lưu ý*: Vì plugin `openid-connect` trong APISIX chỉ hỗ trợ xác thực đầu vào (Incoming Auth), nếu bạn muốn APISIX tự động lấy Token từ Azure AD (qua Client Credentials Flow) để đính kèm khi gọi sang Dynamics 365 Dataverse (Outbound Token Injection), bạn cần sử dụng một cơ chế bổ trợ (như viết Custom Lua Script trong plugin `serverless-pre-function` hoặc dùng service phụ qua plugin `forward-auth`).
|
||||||
|
* **Bật plugin `rate-limiting`**: Thiết lập giới hạn request phù hợp để bảo vệ Dataverse.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🔑 Hướng dẫn cấu hình chi tiết Phương án B (API Key + Auto Inject Dynamics 365 Token)
|
||||||
|
|
||||||
|
Phương án này cho phép **Client** chỉ cần truyền API Key đơn giản khi gọi Gateway (qua header `apikey` của plugin `key-auth`), sau đó **APISIX Gateway** sẽ tự động thực hiện Client Credentials Flow với Azure AD để lấy Access Token, cache lại trong bộ nhớ RAM của Gateway và đính kèm vào header `Authorization: Bearer <token>` trước khi chuyển tiếp request tới Dynamics 365.
|
||||||
|
|
||||||
|
#### Bước 1: Khai báo bộ nhớ cache dùng chung (`lua_shared_dict`)
|
||||||
|
Để lưu trữ và tái sử dụng token (hạn dùng của Microsoft token là 1 tiếng), tránh việc mỗi request gửi lên lại phải gọi API xin token mới làm chậm hệ thống, ta cần cấp phát bộ nhớ cache trong `apisix_conf/config.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nginx_config:
|
||||||
|
custom_lua_shared_dict:
|
||||||
|
oauth_cache: 1m # Cấp 1MB bộ nhớ để cache access token
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Bước 2: Thiết lập Route với plugin `key-auth` và `serverless-pre-function`
|
||||||
|
Dưới đây là lệnh `curl` cấu hình Route `/ssm-vn-dev/*` tích hợp xác thực API Key đầu vào và tự lấy/đính kèm Token đầu ra thông qua Custom Lua Script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -i http://localhost:9180/apisix/admin/routes/ssm-vn-dev-route \
|
||||||
|
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-X PUT \
|
||||||
|
-d '{
|
||||||
|
"uri": "/ssm-vn-dev/*",
|
||||||
|
"name": "ssm-vn-dev-route",
|
||||||
|
"plugins": {
|
||||||
|
"key-auth": {},
|
||||||
|
"serverless-pre-function": {
|
||||||
|
"phase": "access",
|
||||||
|
"functions": [
|
||||||
|
"return function(conf, ctx) local core = require(\"apisix.core\"); local http = require(\"resty.http\"); local cache = ngx.shared.oauth_cache; if not cache then core.log.error(\"oauth_cache not defined in config.yaml\"); return end; local token = cache:get(\"dataverse_token\"); if not token then local httpc = http.new(); httpc:set_timeout(5000); local tenant_id = \"<YOUR_TENANT_ID>\"; local client_id = \"<YOUR_CLIENT_ID>\"; local client_secret = \"<YOUR_CLIENT_SECRET>\"; local scope = \"https://<YOUR_ORG>.crm.dynamics.com/.default\"; local body = \"grant_type=client_credentials&client_id=\" .. ngx.escape_uri(client_id) .. \"&client_secret=\" .. ngx.escape_uri(client_secret) .. \"&scope=\" .. ngx.escape_uri(scope); local res, err = httpc:request_uri(\"https://login.microsoftonline.com/\" .. tenant_id .. \"/oauth2/v2.0/token\", { method = \"POST\", body = body, headers = { [\"Content-Type\"] = \"application/x-www-form-urlencoded\" }, ssl_verify = false }); if not res or res.status ~= 200 then core.log.error(\"Failed to fetch Azure AD token: \", err or (res and res.body)); return core.response.exit(500, { error = \"Auth failed to upstream provider\" }) end; local data, decode_err = core.json.decode(res.body); if not data or not data.access_token then core.log.error(\"Failed to decode token response\"); return core.response.exit(500, { error = \"Invalid token response\" }) end; token = data.access_token; local expires_in = data.expires_in or 3600; cache:set(\"dataverse_token\", token, expires_in - 60) end; ngx.req.set_header(\"Authorization\", \"Bearer \" .. token) end"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rate-limiting": {
|
||||||
|
"count": 100,
|
||||||
|
"time_window": 60,
|
||||||
|
"rejected_code": 429
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"upstream": {
|
||||||
|
"type": "roundrobin",
|
||||||
|
"nodes": {
|
||||||
|
"tngredev.crm5.dynamics.com:443": 1
|
||||||
|
},
|
||||||
|
"pass_host": "node",
|
||||||
|
"scheme": "https"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> **Giải thích tham số cấu hình Upstream:**
|
||||||
|
> * `pass_host: "node"`: Bắt buộc cấu hình trường này để APISIX ghi đè giá trị header `Host` gửi tới Dynamics 365 trùng khớp với node URL (`<YOUR_ORG>.crm.dynamics.com`). Nếu thiếu, Dynamics 365 sẽ từ chối xử lý request vì host header không khớp.
|
||||||
|
> * Các tham số `<YOUR_TENANT_ID>`, `<YOUR_CLIENT_ID>`, `<YOUR_CLIENT_SECRET>`, `<YOUR_ORG>` trong đoạn mã Lua cần được thay thế bằng thông tin môi trường Azure AD và Dynamics 365 thực tế của bạn.
|
||||||
|
|
||||||
|
#### Chi tiết kịch bản script Lua chạy trong APISIX:
|
||||||
|
1. **Kiểm tra Cache:** Truy cập vào `ngx.shared.oauth_cache` tìm xem có token `dataverse_token` nào chưa hết hạn không.
|
||||||
|
2. **Nếu chưa có:** Kích hoạt một cuộc gọi HTTP POST không đồng bộ (non-blocking) tới Endpoint của Azure AD sử dụng thư viện `resty.http`.
|
||||||
|
3. **Lưu trữ:** Giải mã JSON trả về, lấy Access Token và lưu lại vào cache với thời gian sống (TTL) ngắn hơn thời hạn thật của token 60 giây (để tránh rủi ro token hết hạn trong lúc đang truyền tin).
|
||||||
|
4. **Gán Header:** Ghi đè header `Authorization: Bearer <token>` bằng lệnh `ngx.req.set_header`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Hướng Dẫn Giám Sát Và Xem Log Tập Trung (Grafana Loki)
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Hệ thống đã được cấu hình **tự động kết nối (provisioning)** giữa Grafana và Loki làm Data Source mặc định, đồng thời **tự động import sẵn Dashboard theo dõi hiệu năng APISIX**. Bạn không cần phải cấu hình thủ công sau khi khởi chạy container.
|
||||||
|
|
||||||
|
### Bước 1: Xem Dashboard giám sát hiệu năng APISIX (Real-time Dashboard)
|
||||||
|
1. Truy cập vào **Grafana UI**: `http://<IP-Cua-Ban>:9300` (Tài khoản mặc định: `admin` / `admin`).
|
||||||
|
2. Vào phần **Dashboards** từ menu bên trái.
|
||||||
|
3. Chọn dashboard có tên là **APISIX Gateway Performance** (trong thư mục `APISIX API Gateway`).
|
||||||
|
4. Tại đây bạn sẽ thấy các biểu đồ trực quan hóa thời gian thực được phân tích từ log của Loki:
|
||||||
|
- **Request Rate (RPS)**: Tốc độ xử lý request mỗi giây.
|
||||||
|
- **Average Response Time (Latency)**: Thời gian phản hồi trung bình (Độ trễ).
|
||||||
|
- **HTTP Status Distribution**: Phân bố mã trạng thái phản hồi (2xx, 3xx, 4xx, 5xx).
|
||||||
|
- **Data Egress (Throughput)**: Băng thông dữ liệu trả về từ Gateway.
|
||||||
|
- **APISIX Gateway Logs**: Dòng log thời gian thực chạy trực tiếp bên dưới.
|
||||||
|
|
||||||
|
### Bước 2: Xem log thời gian thực (Live Stream Log) trong Grafana Explore
|
||||||
|
1. Bấm vào biểu tượng **Explore** (Hình la bàn bên thanh công cụ trái).
|
||||||
|
2. Đảm bảo đã chọn Data Source là **Loki**.
|
||||||
|
3. Tại ô nhập câu lệnh truy vấn (Query) nhập:
|
||||||
|
```text
|
||||||
|
{container="apisix-gateway"}
|
||||||
|
```
|
||||||
|
4. Bấm **Run query** và chọn **Live** để bắt đầu theo dõi dòng log thời gian thực.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🛡️ Cấu hình ghi Log truy cập tự động kèm Request/Response Body sang Loki
|
||||||
|
|
||||||
|
Mặc định, log stdout của APISIX Gateway không chứa nội dung Request/Response Body để tối ưu hiệu năng. Nếu bạn muốn tự động ghi nhận lại toàn bộ Payload (Request/Response Body) gửi từ bên thứ ba và đẩy trực tiếp sang Loki, thực hiện cấu hình dưới đây:
|
||||||
|
|
||||||
|
#### Hướng dẫn thiết lập qua APISIX Admin API
|
||||||
|
*(Lưu ý: Vì phiên bản APISIX Dashboard (v3.0.1) ra đời trước khi plugin `loki-logger` có schema hiển thị đồ họa trên UI, nên plugin này sẽ không hiển thị trong danh sách Plugin Market của giao diện Dashboard. Tuy nhiên, APISIX Gateway (v3.11.0) hỗ trợ hoàn toàn plugin này. Bạn hãy cấu hình trực tiếp qua Admin API bằng lệnh `curl` dưới đây để kích hoạt ghi log toàn cục:)*
|
||||||
|
Chạy lệnh curl sau trực tiếp trên VPS để cấu hình plugin `loki-logger` toàn cục:
|
||||||
|
```bash
|
||||||
|
curl -i http://localhost:9180/apisix/admin/global_rules/1 \
|
||||||
|
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-X PUT \
|
||||||
|
-d '\''{
|
||||||
|
"plugins": {
|
||||||
|
"loki-logger": {
|
||||||
|
"endpoint_addrs": ["http://loki:3100"],
|
||||||
|
"include_req_body": true,
|
||||||
|
"include_resp_body": true,
|
||||||
|
"log_labels": {
|
||||||
|
"job": "apisix-gateway"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'\''
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📊 Xem và truy vấn Payload log (Request/Response Body) trong Grafana
|
||||||
|
1. Truy cập **Grafana UI** (`http://<IP-Cua-Ban>:9300`) > **Explore**.
|
||||||
|
2. Chọn Data Source là **Loki**.
|
||||||
|
3. Nhập câu lệnh LogQL sau để lọc log được gửi trực tiếp từ plugin `loki-logger`:
|
||||||
|
```text
|
||||||
|
{job="apisix-gateway"}
|
||||||
|
```
|
||||||
|
4. Để định dạng (parse) log JSON và hiển thị trực quan các trường Request/Response, bạn có thể thêm bộ phân tích cú pháp bằng cách nhập truy vấn sau:
|
||||||
|
```text
|
||||||
|
{job="apisix-gateway"} | json
|
||||||
|
```
|
||||||
|
5. Click vào bất kỳ dòng log nào bên dưới kết quả. Bạn sẽ thấy các trường dữ liệu được phân tích cấu trúc, bao gồm:
|
||||||
|
- `request.body`: Chứa toàn bộ nội dung Request payload từ Client.
|
||||||
|
- `response.body` hoặc `resp_body`: Chứa toàn bộ nội dung Response payload trả về từ phía Dataverse.
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
apisix:
|
||||||
|
id: "apisix-gateway"
|
||||||
|
|
||||||
|
deployment:
|
||||||
|
role: traditional
|
||||||
|
role_traditional:
|
||||||
|
config_provider: etcd
|
||||||
|
admin:
|
||||||
|
allow_admin:
|
||||||
|
- 0.0.0.0/0
|
||||||
|
admin_key:
|
||||||
|
- name: "admin"
|
||||||
|
key: edd1c9f034335f136f87ad84b625c8f1
|
||||||
|
role: admin
|
||||||
|
etcd:
|
||||||
|
host:
|
||||||
|
- "http://etcd:2379"
|
||||||
|
prefix: "/apisix"
|
||||||
|
|
||||||
|
nginx_config:
|
||||||
|
custom_lua_shared_dict:
|
||||||
|
oauth_cache: 1m
|
||||||
|
http:
|
||||||
|
access_log: "/dev/stdout"
|
||||||
|
access_log_format: '{"time_local":"$time_local","client_ip":"$remote_addr","request":"$request","status":$status,"body_bytes_sent":$body_bytes_sent,"request_time":$request_time,"upstream_response_time":"$upstream_response_time"}'
|
||||||
|
access_log_format_escape: json
|
||||||
|
error_log: "/dev/stderr"
|
||||||
|
error_log_level: "warn"
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
conf:
|
||||||
|
listen:
|
||||||
|
host: 0.0.0.0
|
||||||
|
port: 9090
|
||||||
|
allow_list:
|
||||||
|
- 0.0.0.0/0
|
||||||
|
etcd:
|
||||||
|
endpoints:
|
||||||
|
- etcd:2379
|
||||||
|
log:
|
||||||
|
error_log:
|
||||||
|
level: warn
|
||||||
|
file_path: /dev/stderr
|
||||||
|
access_log:
|
||||||
|
file_path: /dev/stdout
|
||||||
|
security:
|
||||||
|
content_security_policy: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-src *;"
|
||||||
|
|
||||||
|
authentication:
|
||||||
|
secret: "edd1c9f034335f136f87ad84b625c8f1"
|
||||||
|
expire_time: 3600
|
||||||
|
users:
|
||||||
|
- username: "admin"
|
||||||
|
password: "admin"
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- api-breaker
|
||||||
|
- authz-casbin
|
||||||
|
- authz-casdoor
|
||||||
|
- authz-keycloak
|
||||||
|
- aws-lambda
|
||||||
|
- azure-functions
|
||||||
|
- basic-auth
|
||||||
|
- clickhouse-logger
|
||||||
|
- client-control
|
||||||
|
- consumer-restriction
|
||||||
|
- cors
|
||||||
|
- csrf
|
||||||
|
- datadog
|
||||||
|
- echo
|
||||||
|
- error-log-logger
|
||||||
|
- ext-plugin-post-req
|
||||||
|
- ext-plugin-post-resp
|
||||||
|
- ext-plugin-pre-req
|
||||||
|
- fault-injection
|
||||||
|
- file-logger
|
||||||
|
- forward-auth
|
||||||
|
- google-cloud-logging
|
||||||
|
- grpc-transcode
|
||||||
|
- grpc-web
|
||||||
|
- gzip
|
||||||
|
- hmac-auth
|
||||||
|
- http-logger
|
||||||
|
- ip-restriction
|
||||||
|
- jwt-auth
|
||||||
|
- kafka-logger
|
||||||
|
- kafka-proxy
|
||||||
|
- key-auth
|
||||||
|
- ldap-auth
|
||||||
|
- limit-conn
|
||||||
|
- limit-count
|
||||||
|
- limit-req
|
||||||
|
- loggly
|
||||||
|
- mocking
|
||||||
|
- opa
|
||||||
|
- openid-connect
|
||||||
|
- opentelemetry
|
||||||
|
- openwhisk
|
||||||
|
- prometheus
|
||||||
|
- proxy-cache
|
||||||
|
- proxy-control
|
||||||
|
- proxy-mirror
|
||||||
|
- proxy-rewrite
|
||||||
|
- public-api
|
||||||
|
- real-ip
|
||||||
|
- redirect
|
||||||
|
- referer-restriction
|
||||||
|
- request-id
|
||||||
|
- request-validation
|
||||||
|
- response-rewrite
|
||||||
|
- rocketmq-logger
|
||||||
|
- server-info
|
||||||
|
- serverless-post-function
|
||||||
|
- serverless-pre-function
|
||||||
|
- skywalking
|
||||||
|
- skywalking-logger
|
||||||
|
- sls-logger
|
||||||
|
- splunk-hec-logging
|
||||||
|
- syslog
|
||||||
|
- tcp-logger
|
||||||
|
- traffic-split
|
||||||
|
- ua-restriction
|
||||||
|
- udp-logger
|
||||||
|
- uri-blocker
|
||||||
|
- wolf-rbac
|
||||||
|
- zipkin
|
||||||
|
- elasticsearch-logge
|
||||||
|
- openfunction
|
||||||
|
- tencent-cloud-cls
|
||||||
|
- ai
|
||||||
|
- cas-auth
|
||||||
|
- loki-logger
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,101 @@
|
|||||||
|
networks:
|
||||||
|
apisix_net:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
services:
|
||||||
|
# 1. Cơ sở dữ liệu cấu hình cho APISIX
|
||||||
|
etcd:
|
||||||
|
image: bitnamilegacy/etcd:3.5.11
|
||||||
|
container_name: apisix-etcd
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- etcd_data:/bitnami/etcd
|
||||||
|
environment:
|
||||||
|
ETCD_ENABLE_V2: "true"
|
||||||
|
ALLOW_NONE_AUTHENTICATION: "yes"
|
||||||
|
ETCD_ADVERTISE_CLIENT_URLS: "http://etcd:2379"
|
||||||
|
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
|
||||||
|
networks:
|
||||||
|
- apisix_net
|
||||||
|
|
||||||
|
# 2. Apache APISIX Gateway
|
||||||
|
apisix:
|
||||||
|
image: apache/apisix:3.16.0-debian
|
||||||
|
container_name: apisix-gateway
|
||||||
|
user: "root"
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml
|
||||||
|
depends_on:
|
||||||
|
- etcd
|
||||||
|
networks:
|
||||||
|
- apisix_net
|
||||||
|
ports:
|
||||||
|
- "9080:9080" # HTTP Traffic
|
||||||
|
- "9443:9443" # HTTPS Traffic
|
||||||
|
- "9180:9180" # Admin API
|
||||||
|
|
||||||
|
# 3. APISIX Dashboard (Đổi sang cổng 9090)
|
||||||
|
apisix-dashboard:
|
||||||
|
image: apache/apisix-dashboard:3.0.1-alpine
|
||||||
|
container_name: apisix-dashboard
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./apisix_conf/dashboard_conf.yaml:/usr/local/apisix-dashboard/conf/conf.yaml:ro
|
||||||
|
- ./apisix_conf/schema.json:/usr/local/apisix-dashboard/conf/schema.json:ro
|
||||||
|
depends_on:
|
||||||
|
- etcd
|
||||||
|
networks:
|
||||||
|
- apisix_net
|
||||||
|
ports:
|
||||||
|
- "9090:9090"
|
||||||
|
|
||||||
|
# 4. Grafana Loki (Đổi sang cổng 9100)
|
||||||
|
loki:
|
||||||
|
image: grafana/loki:3.0.0
|
||||||
|
container_name: apisix-loki
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- loki_data:/tmp/loki
|
||||||
|
command: -config.file=/etc/loki/local-config.yaml
|
||||||
|
networks:
|
||||||
|
- apisix_net
|
||||||
|
|
||||||
|
# 5. Promtail (Thu thập log gửi sang Loki)
|
||||||
|
promtail:
|
||||||
|
image: grafana/promtail:3.0.0
|
||||||
|
container_name: apisix-promtail
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./promtail_conf/promtail-config.yaml:/etc/promtail/config.yml:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
command: -config.file=/etc/promtail/config.yml
|
||||||
|
depends_on:
|
||||||
|
- loki
|
||||||
|
networks:
|
||||||
|
- apisix_net
|
||||||
|
|
||||||
|
# 6. Grafana (Đổi sang cổng 9300)
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:11.0.0
|
||||||
|
container_name: apisix-grafana
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- GF_SECURITY_ALLOW_EMBEDDING=true
|
||||||
|
ports:
|
||||||
|
- "9300:3000"
|
||||||
|
volumes:
|
||||||
|
- grafana_data:/var/lib/grafana
|
||||||
|
- ./grafana_conf/provisioning:/etc/grafana/provisioning:ro
|
||||||
|
depends_on:
|
||||||
|
- loki
|
||||||
|
networks:
|
||||||
|
- apisix_net
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
etcd_data:
|
||||||
|
driver: local
|
||||||
|
loki_data:
|
||||||
|
driver: local
|
||||||
|
grafana_data:
|
||||||
|
driver: local
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"fiscalYearStartMonth": 0,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"id": null,
|
||||||
|
"links": [],
|
||||||
|
"liveNow": true,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Request Rate (RPS)",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "sum(rate({container=\"apisix-gateway\"}[$__interval]))",
|
||||||
|
"legendFormat": "Requests / Sec",
|
||||||
|
"datasource": {
|
||||||
|
"type": "loki",
|
||||||
|
"uid": "Loki"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "reqps"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "Average Response Time (Latency)",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "avg(avg_over_time({container=\"apisix-gateway\"} | json | unwrap request_time [$__interval]))",
|
||||||
|
"legendFormat": "Avg Latency",
|
||||||
|
"datasource": {
|
||||||
|
"type": "loki",
|
||||||
|
"uid": "Loki"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "s",
|
||||||
|
"custom": {
|
||||||
|
"drawStyle": "line",
|
||||||
|
"lineInterpolation": "smooth"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"title": "HTTP Status Distribution",
|
||||||
|
"type": "bargauge",
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 8,
|
||||||
|
"x": 0,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "sum by(status) (count_over_time({container=\"apisix-gateway\"} | json [$__interval]))",
|
||||||
|
"legendFormat": "HTTP {{status}}",
|
||||||
|
"datasource": {
|
||||||
|
"type": "loki",
|
||||||
|
"uid": "Loki"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"orientation": "horizontal",
|
||||||
|
"displayMode": "basic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"title": "Data Egress (Throughput)",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 16,
|
||||||
|
"x": 8,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "sum(rate({container=\"apisix-gateway\"} | json | unwrap body_bytes_sent [$__interval]))",
|
||||||
|
"legendFormat": "Bytes / Sec",
|
||||||
|
"datasource": {
|
||||||
|
"type": "loki",
|
||||||
|
"uid": "Loki"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "Bps"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"title": "APISIX Gateway Logs",
|
||||||
|
"type": "logs",
|
||||||
|
"gridPos": {
|
||||||
|
"h": 10,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 14
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "{container=\"apisix-gateway\"}",
|
||||||
|
"datasource": {
|
||||||
|
"type": "loki",
|
||||||
|
"uid": "Loki"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"showLabels": false,
|
||||||
|
"wrapLogMessage": true,
|
||||||
|
"enableLogDetails": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schemaVersion": 39,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": ["apisix"],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-1h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "browser",
|
||||||
|
"title": "APISIX Gateway Performance",
|
||||||
|
"uid": "apisix-loki-perf",
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: 'APISIX Stack Dashboards'
|
||||||
|
orgId: 1
|
||||||
|
folder: 'APISIX API Gateway'
|
||||||
|
type: file
|
||||||
|
disableDeletion: false
|
||||||
|
editable: true
|
||||||
|
options:
|
||||||
|
path: /etc/grafana/provisioning/dashboards
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
datasources:
|
||||||
|
- name: Loki
|
||||||
|
type: loki
|
||||||
|
access: proxy
|
||||||
|
url: http://loki:3100
|
||||||
|
jsonData:
|
||||||
|
maxLines: 1000
|
||||||
|
isDefault: true
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
server:
|
||||||
|
http_listen_port: 9080
|
||||||
|
grpc_listen_port: 0
|
||||||
|
|
||||||
|
positions:
|
||||||
|
filename: /tmp/positions.yaml
|
||||||
|
|
||||||
|
clients:
|
||||||
|
- url: http://loki:3100/loki/api/v1/push
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: docker-containers
|
||||||
|
docker_sd_configs:
|
||||||
|
- host: unix:///var/run/docker.sock
|
||||||
|
refresh_interval: 5s
|
||||||
|
relabel_configs:
|
||||||
|
- source_labels: ['__meta_docker_container_name']
|
||||||
|
regex: '/(.*)'
|
||||||
|
target_label: 'container'
|
||||||
Reference in new issue
Block a user