feat(monitoring): replace Dozzle with full observability stack
Replace the single-purpose Dozzle log viewer with a comprehensive monitoring stack covering metrics, container telemetry, and persistent log aggregation. Stack changes (docker-stack-service.yml -> docker-stack-monitoring.yml): - remove Dozzle service and dozzle_users Docker secret - add Portainer CE + portainer-agent (Swarm management UI) - add node-exporter (global) — host CPU, memory, disk, network metrics - add cAdvisor (global) — per-container resource usage metrics - add Loki (replicated, service node) — persistent log storage, 31-day retention - add Promtail (global) — Docker service discovery; ships logs with service, stack, container, and project labels; sends to Loki - rename stack to iklimco-monitoring; add loki-vl persistent volume Workflow (.gitea/workflows/deploy-prod.yml -> deploy-monitoring-prod.yml): - rename file and add paths filter (Environment_Monitoring/**) - remove Dozzle secret creation and auth handling - add IMAGE_LOKI / IMAGE_PROMTAIL; clean up legacy dozzle_users Docker secret - update SWAG step to loop swag/site-confs/*.conf.tpl (portainer only) - remove DOZZLE_SUBDOMAIN; remove dozzle DNS record; keep portainer DNS - replace "Wait for Dozzle" with "Wait for Loki" SWAG: - remove swag/dozzle.conf.tpl (Dozzle no longer in stack) - add swag/site-confs/portainer.conf.tpl (moved from main repo template dir; monitoring stack manages its own SWAG configs independently) - remove init/apisix-dozzle.sh (superseded by SWAG reverse proxy) README: - rewrite in Turkish; document Portainer, node-exporter, cAdvisor, Loki, Promtail - add Grafana log viewing guide: datasource setup, label filter table, LogQL examples, metric-log correlation workflow, adding log panels to dashboards Requires IMAGE_LOKI and IMAGE_PROMTAIL to be defined in .env and corresponding custom images (build/loki/, build/promtail/) pushed to Harbor.
This commit is contained in:
parent
94dc1d2fe3
commit
735d957dfa
@ -4,6 +4,8 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- prod-env
|
- prod-env
|
||||||
|
paths:
|
||||||
|
- 'Environment_Monitoring/**'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: prod-monitoring-deploy
|
group: prod-monitoring-deploy
|
||||||
@ -47,104 +49,81 @@ jobs:
|
|||||||
test -s .env
|
test -s .env
|
||||||
test -s .env.secrets.swag
|
test -s .env.secrets.swag
|
||||||
|
|
||||||
- name: Create Dozzle Auth Secret
|
- name: Deploy Monitoring Stack
|
||||||
env:
|
|
||||||
DOZZLE_USERS_YML_B64: ${{ secrets.DOZZLE_USERS_YML_B64 }}
|
|
||||||
DOZZLE_USERS_YML: ${{ secrets.DOZZLE_USERS_YML }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
if [ -n "${DOZZLE_USERS_YML_B64:-}" ]; then
|
|
||||||
printf '%s' "$DOZZLE_USERS_YML_B64" | base64 -d > /tmp/dozzle-users.yml
|
|
||||||
elif [ -n "${DOZZLE_USERS_YML:-}" ]; then
|
|
||||||
printf '%s\n' "$DOZZLE_USERS_YML" > /tmp/dozzle-users.yml
|
|
||||||
else
|
|
||||||
echo "DOZZLE_USERS_YML_B64 or DOZZLE_USERS_YML secret is required"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
grep -q '^users:' /tmp/dozzle-users.yml
|
|
||||||
docker service rm iklimco_dozzle || true
|
|
||||||
for i in $(seq 1 24); do
|
|
||||||
if ! docker service inspect iklimco_dozzle >/dev/null 2>&1; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo "Waiting for old iklimco_dozzle service to be removed..."
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
docker secret rm dozzle_users || true
|
|
||||||
docker secret create dozzle_users /tmp/dozzle-users.yml >/dev/null
|
|
||||||
shred -u /tmp/dozzle-users.yml || rm -f /tmp/dozzle-users.yml
|
|
||||||
|
|
||||||
- name: Deploy Dozzle Stack
|
|
||||||
run: |
|
run: |
|
||||||
set -a; . ./.env; set +a
|
set -a; . ./.env; set +a
|
||||||
export IMAGE_DOZZLE="${IMAGE_DOZZLE:-amir20/dozzle:v10.6.6}"
|
export IMAGE_LOKI="${IMAGE_LOKI}"
|
||||||
export DOZZLE_USERS_SECRET_NAME=dozzle_users
|
export IMAGE_PROMTAIL="${IMAGE_PROMTAIL}"
|
||||||
|
|
||||||
|
# Remove leftover dozzle_users Docker secret from previous setup
|
||||||
|
docker secret rm dozzle_users 2>/dev/null || true
|
||||||
|
|
||||||
docker stack deploy \
|
docker stack deploy \
|
||||||
|
--with-registry-auth \
|
||||||
--resolve-image changed \
|
--resolve-image changed \
|
||||||
-c docker-stack-service.yml \
|
-c Environment_Monitoring/docker-stack-monitoring.yml \
|
||||||
iklimco
|
iklimco-monitoring
|
||||||
|
|
||||||
- name: Wait for Dozzle
|
- name: Wait for Loki
|
||||||
run: |
|
run: |
|
||||||
for i in $(seq 1 36); do
|
for i in $(seq 1 36); do
|
||||||
REPLICAS=$(docker service ls --filter name=iklimco_dozzle --format "{{.Replicas}}" | head -1)
|
REPLICAS=$(docker service ls --filter name=iklimco-monitoring_loki --format "{{.Replicas}}" | head -1)
|
||||||
if echo "$REPLICAS" | awk -F'[/ ]' '$1>0 && $1==$2{found=1} END{exit !found}'; then
|
if echo "$REPLICAS" | awk -F'[/ ]' '$1>0 && $1==$2{found=1} END{exit !found}'; then
|
||||||
echo "Dozzle is ready: $REPLICAS"
|
echo "Loki is ready: $REPLICAS"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
echo "Dozzle not ready yet (${REPLICAS:-missing}), waiting 5s..."
|
echo "Loki not ready yet (${REPLICAS:-missing}), waiting 5s..."
|
||||||
sleep 5
|
sleep 5
|
||||||
done
|
done
|
||||||
docker service ps iklimco_dozzle || true
|
docker service ps iklimco-monitoring_loki || true
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Configure SWAG Reverse Proxy
|
- name: Configure SWAG Reverse Proxy
|
||||||
run: |
|
run: |
|
||||||
set -a; . ./.env; . ./.env.secrets.swag; set +a
|
set -a; . ./.env; . ./.env.secrets.swag; set +a
|
||||||
export DOZZLE_SUBDOMAIN="${DOZZLE_SUBDOMAIN:-dozzle.iklim.co}"
|
export PORTAINER_SUBDOMAIN="${PORTAINER_SUBDOMAIN:-portainer.iklim.co}"
|
||||||
envsubst '${DOZZLE_SUBDOMAIN}' < swag/dozzle.conf.tpl | docker run --rm -i \
|
export RESTRICTED_IPS_BLOCK="$(echo "$RESTRICTED_IPS" | tr ',' '\n' | sed 's|.*| allow &;|')"
|
||||||
-v "${SWAG_SITE_CONFS_DIR}:/output" \
|
|
||||||
alpine sh -c "cat > /output/dozzle.conf"
|
SWAG_VARS='${PORTAINER_SUBDOMAIN}${RESTRICTED_IPS_BLOCK}'
|
||||||
|
for tpl in Environment_Monitoring/swag/site-confs/*.conf.tpl; do
|
||||||
|
fname=$(basename "${tpl%.tpl}")
|
||||||
|
envsubst "$SWAG_VARS" < "$tpl" | docker run --rm -i \
|
||||||
|
-v "${SWAG_SITE_CONFS_DIR}:/output" \
|
||||||
|
alpine sh -c "cat > /output/${fname}"
|
||||||
|
echo "${fname} written"
|
||||||
|
done
|
||||||
|
|
||||||
SWAG_CTR=$(docker ps -q -f name=iklimco_swag 2>/dev/null | head -1)
|
SWAG_CTR=$(docker ps -q -f name=iklimco_swag 2>/dev/null | head -1)
|
||||||
if [ -n "$SWAG_CTR" ]; then
|
if [ -n "$SWAG_CTR" ]; then
|
||||||
docker exec "$SWAG_CTR" nginx -t && docker exec "$SWAG_CTR" nginx -s reload
|
docker exec "$SWAG_CTR" nginx -t && docker exec "$SWAG_CTR" nginx -s reload
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Configure APISIX Reverse Proxy
|
- name: Update DNS Records
|
||||||
run: |
|
|
||||||
set -a; . ./.env; set +a
|
|
||||||
export SPRING_PROFILES_ACTIVE=prod
|
|
||||||
export DOZZLE_SUBDOMAIN="${DOZZLE_SUBDOMAIN:-dozzle.iklim.co}"
|
|
||||||
/bin/bash init/apisix-dozzle.sh
|
|
||||||
|
|
||||||
- name: Update DNS Record
|
|
||||||
run: |
|
run: |
|
||||||
set -a; . ./.env; . ./.env.secrets.swag; set +a
|
set -a; . ./.env; . ./.env.secrets.swag; set +a
|
||||||
FLOATING_IP="${{ vars.PROD_FLOATING_IP }}"
|
FLOATING_IP="${{ vars.PROD_FLOATING_IP }}"
|
||||||
DOMAIN="iklim.co"
|
DOMAIN="iklim.co"
|
||||||
RECORD="${DOZZLE_DNS_RECORD:-dozzle}"
|
|
||||||
|
|
||||||
CURRENT=$(curl -s \
|
for record in portainer; do
|
||||||
-H "Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}" \
|
CURRENT=$(curl -s \
|
||||||
"https://api.godaddy.com/v1/domains/${DOMAIN}/records/A/${RECORD}" \
|
|
||||||
2>/dev/null | jq -r '.[0].data // empty' 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ "$CURRENT" = "$FLOATING_IP" ]; then
|
|
||||||
echo "${RECORD}.${DOMAIN} -> ${FLOATING_IP} exists, skipping"
|
|
||||||
else
|
|
||||||
curl -sf -X PUT \
|
|
||||||
-H "Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}" \
|
-H "Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}" \
|
||||||
-H "Content-Type: application/json" \
|
"https://api.godaddy.com/v1/domains/${DOMAIN}/records/A/${record}" \
|
||||||
"https://api.godaddy.com/v1/domains/${DOMAIN}/records/A/${RECORD}" \
|
2>/dev/null | jq -r '.[0].data // empty' 2>/dev/null || true)
|
||||||
-d "[{\"data\":\"${FLOATING_IP}\",\"ttl\":600}]"
|
|
||||||
echo "${RECORD}.${DOMAIN} -> ${FLOATING_IP} added/updated"
|
if [ "$CURRENT" = "$FLOATING_IP" ]; then
|
||||||
fi
|
echo "${record}.${DOMAIN} -> ${FLOATING_IP} exists, skipping"
|
||||||
|
else
|
||||||
|
curl -sf -X PUT \
|
||||||
|
-H "Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"https://api.godaddy.com/v1/domains/${DOMAIN}/records/A/${record}" \
|
||||||
|
-d "[{\"data\":\"${FLOATING_IP}\",\"ttl\":600}]"
|
||||||
|
echo "${record}.${DOMAIN} -> ${FLOATING_IP} added/updated"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
- name: Verify Deployment
|
- name: Verify Deployment
|
||||||
run: |
|
run: |
|
||||||
docker service ps iklimco_dozzle \
|
docker service ps iklimco-monitoring_loki \
|
||||||
--filter "desired-state=running" \
|
--filter "desired-state=running" \
|
||||||
--format "table {{.Name}}\t{{.Node}}\t{{.CurrentState}}\t{{.Image}}" | head -20
|
--format "table {{.Name}}\t{{.Node}}\t{{.CurrentState}}\t{{.Image}}" | head -20
|
||||||
151
README.md
151
README.md
@ -1,36 +1,143 @@
|
|||||||
# Environment Monitoring
|
# Environment Monitoring
|
||||||
|
|
||||||
Dozzle is deployed as a separate Swarm monitoring service.
|
Tüm izleme servisleri `docker-stack-monitoring.yml` stack'inde yönetilir: Portainer, node-exporter, cAdvisor, Loki ve Promtail.
|
||||||
|
|
||||||
## Production
|
## Servisler
|
||||||
|
|
||||||
- Swarm mode is enabled with `DOZZLE_MODE=swarm`.
|
### Portainer
|
||||||
- The service uses `deploy.mode: global` so one Dozzle task runs on every Swarm node.
|
|
||||||
- Dozzle is attached to both `dozzle` and `iklimco-net`.
|
|
||||||
- Docker socket is mounted read-only.
|
|
||||||
- Simple authentication is enabled with `DOZZLE_AUTH_PROVIDER=simple`.
|
|
||||||
- The real `users.yml` must be supplied through Gitea secret `DOZZLE_USERS_YML_B64` or `DOZZLE_USERS_YML`.
|
|
||||||
- Prefer `roles: none` for read-only log viewing. Dozzle defaults to full action access when roles are omitted.
|
|
||||||
|
|
||||||
Generate a bcrypt-backed users file with Dozzle itself:
|
Docker Swarm yönetim arayüzü.
|
||||||
|
|
||||||
|
- `portainer-agent` — global mode, tüm node'larda çalışır; Docker socket ve volume bilgisini Portainer CE'ye aktarır
|
||||||
|
- `portainer` — tek replica, manager node'da; `portainer-net` overlay üzerinden agent'lara bağlanır
|
||||||
|
- Dış erişim SWAG üzerinden: `portainer.iklim.co`
|
||||||
|
|
||||||
|
### node-exporter
|
||||||
|
|
||||||
|
Host / işletim sistemi metriklerini Prometheus'a aktarır. Her node'da çalışır (`deploy.mode: global`).
|
||||||
|
|
||||||
|
Toplanan metrikler: CPU kullanımı, bellek, disk I/O, ağ trafiği, sistem yükü, dosya sistemi doluluk oranları.
|
||||||
|
|
||||||
|
### cAdvisor
|
||||||
|
|
||||||
|
Container ve Swarm servis bazlı kaynak tüketimini Prometheus'a aktarır. Her node'da çalışır (`deploy.mode: global`).
|
||||||
|
|
||||||
|
Toplanan metrikler: container başına CPU/bellek/ağ/disk I/O.
|
||||||
|
|
||||||
|
### Loki
|
||||||
|
|
||||||
|
Container loglarını toplayan ve saklayan log aggregation servisi. Grafana'nın native Loki datasource entegrasyonu ile metriklerle birlikte sorgulanabilir.
|
||||||
|
|
||||||
|
- Tek replica, `node.labels.type == service` node'unda çalışır
|
||||||
|
- Log saklama süresi: 31 gün (`limits_config.retention_period`)
|
||||||
|
- Konfigürasyon: `build/loki/loki.yml`
|
||||||
|
|
||||||
|
### Promtail
|
||||||
|
|
||||||
|
Her node'daki container loglarını Docker API üzerinden toplayarak Loki'ye gönderir. Her node'da çalışır (`deploy.mode: global`).
|
||||||
|
|
||||||
|
- Docker service discovery: container adı, servis adı, stack adı ve proje label'larını otomatik etiket olarak ekler
|
||||||
|
- Konfigürasyon: `build/promtail/promtail.yml`
|
||||||
|
|
||||||
|
### Health Agent
|
||||||
|
|
||||||
|
Cluster içi servis sağlığını Uptime Kuma'ya push eden Python servisi. Ayrıntılar için `health-agent/README.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Grafana'da Log Görüntüleme
|
||||||
|
|
||||||
|
### İlk Kurulum — Loki Datasource Ekleme
|
||||||
|
|
||||||
|
Loki deploy edildikten sonra tek seferlik yapılır:
|
||||||
|
|
||||||
|
1. Grafana UI → Sol menü → **Connections → Data sources**
|
||||||
|
2. **Add new data source** → **Loki** seç
|
||||||
|
3. URL: `http://loki:3100`
|
||||||
|
4. **Save & test** — "Data source connected" mesajı görünmeli
|
||||||
|
|
||||||
|
Alternatif olarak `template/grafana/provisioning/datasources/loki.yaml` dosyasını Grafana'nın provisioning dizinine kopyalayarak otomatik yüklenebilir:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it --rm amir20/dozzle:v10.6.6 generate admin \
|
# Grafana veri dizinine provisioning dosyasını kopyala
|
||||||
--password '<strong-password>' \
|
cp template/grafana/provisioning/datasources/loki.yaml \
|
||||||
--email admin@iklim.co \
|
<GRAFANA_DATA_DIR>/provisioning/datasources/loki.yaml
|
||||||
--name 'Admin' \
|
# Grafana'yı yeniden başlat
|
||||||
--user-roles none > users.yml
|
docker service update --force iklimco_grafana
|
||||||
```
|
```
|
||||||
|
|
||||||
Store the result as either:
|
### Log Görüntüleme — Explore
|
||||||
|
|
||||||
- `DOZZLE_USERS_YML_B64`: `base64 -w0 users.yml`
|
1. Sol menü → **Explore** (pusula ikonu)
|
||||||
- `DOZZLE_USERS_YML`: raw multiline file content
|
2. Üst sol köşeden datasource olarak **Loki** seç
|
||||||
|
3. **Label filters** ile filtrele:
|
||||||
|
|
||||||
Default public hostname:
|
| Etiket | Açıklama | Örnek |
|
||||||
|
|--------|----------|-------|
|
||||||
|
| `service` | Swarm servis adı | `iklimco_vault` |
|
||||||
|
| `stack` | Stack adı | `iklimco` |
|
||||||
|
| `container` | Container adı | `iklimco_vault.1.xxx` |
|
||||||
|
| `project` | project label değeri | `co.iklim` |
|
||||||
|
| `logstream` | stdout / stderr | `stderr` |
|
||||||
|
|
||||||
```text
|
### LogQL Örnekleri
|
||||||
dozzle.iklim.co
|
|
||||||
|
```logql
|
||||||
|
# Belirli bir servisin tüm logları
|
||||||
|
{service="iklimco_vault"}
|
||||||
|
|
||||||
|
# Tüm stack'teki hata logları
|
||||||
|
{stack="iklimco"} |= "ERROR"
|
||||||
|
|
||||||
|
# Exception içeren loglar (tüm servisler)
|
||||||
|
{project="co.iklim"} |= "Exception"
|
||||||
|
|
||||||
|
# APISIX erişim logları — sadece 5xx'ler
|
||||||
|
{service="iklimco_apisix"} | json | status >= 500
|
||||||
|
|
||||||
|
# Belirli zaman aralığında servis logları (UI'dan zaman seçimi de yapılabilir)
|
||||||
|
{service="iklimco_rabbitmq"} |= "error" | line_format "{{.line}}"
|
||||||
```
|
```
|
||||||
|
|
||||||
The workflow writes SWAG reverse proxy config, configures APISIX, updates DNS, deploys the Swarm stack, and verifies `iklimco_dozzle`.
|
### Metrik — Log Korelasyonu
|
||||||
|
|
||||||
|
Grafana'nın en güçlü özelliklerinden biri: bir Prometheus metriğinde anomali gördüğünde aynı zaman aralığındaki logları yan yana inceleyebilirsin.
|
||||||
|
|
||||||
|
1. Prometheus dashboard panelinde anomali noktasına tıkla
|
||||||
|
2. **Explore** linkine tıkla → aynı zaman aralığı Explore'da açılır
|
||||||
|
3. Datasource'u Loki'ye geçir, ilgili servisi filtrele
|
||||||
|
4. Metrik spike'ı ile log hataları aynı anda görünür
|
||||||
|
|
||||||
|
### Dashboard'a Log Paneli Ekleme
|
||||||
|
|
||||||
|
Mevcut dashboard'lara log paneli eklemek için:
|
||||||
|
|
||||||
|
1. Dashboard → **Edit** → **Add visualization**
|
||||||
|
2. Panel tipi: **Logs** seç
|
||||||
|
3. Datasource: **Loki**
|
||||||
|
4. Query: `{service="<servis_adı>"}`
|
||||||
|
5. **Deduplication** seçeneğini açabilirsin (tekrar eden satırları gizler)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ağ Yapısı
|
||||||
|
|
||||||
|
| Network | Tür | Kullananlar |
|
||||||
|
|---------|-----|-------------|
|
||||||
|
| `iklimco-net` | external overlay | Portainer, node-exporter, cAdvisor, Loki, Promtail, Health Agent |
|
||||||
|
| `portainer-net` | stack overlay | portainer-agent ↔ portainer iletişimi |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker stack deploy \
|
||||||
|
--with-registry-auth \
|
||||||
|
-c Environment_Monitoring/docker-stack-monitoring.yml \
|
||||||
|
iklimco-monitoring
|
||||||
|
```
|
||||||
|
|
||||||
|
Prod için Gitea workflow'u: `Environment_Monitoring/.gitea/workflows/deploy-monitoring-prod.yml`
|
||||||
|
|
||||||
|
> **Not:** Loki ve Promtail custom image kullanır (`build/loki/`, `build/promtail/`). Deploy öncesinde imajların Harbor'a build edilip push edilmesi gerekir. `.env` dosyasında `IMAGE_LOKI` ve `IMAGE_PROMTAIL` değişkenlerinin tanımlı olması zorunludur.
|
||||||
|
|||||||
118
docker-stack-monitoring.yml
Normal file
118
docker-stack-monitoring.yml
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
services:
|
||||||
|
portainer-agent:
|
||||||
|
image: portainer/agent:lts
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /var/lib/docker/volumes:/var/lib/docker/volumes
|
||||||
|
networks:
|
||||||
|
- portainer-net
|
||||||
|
deploy:
|
||||||
|
mode: global
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.platform.os == linux
|
||||||
|
restart_policy:
|
||||||
|
condition: any
|
||||||
|
delay: 5s
|
||||||
|
labels:
|
||||||
|
project: co.iklim
|
||||||
|
|
||||||
|
portainer:
|
||||||
|
image: portainer/portainer-ce:lts
|
||||||
|
command: -H tcp://tasks.portainer-agent:9001 --tlsskipverify
|
||||||
|
volumes:
|
||||||
|
- portainer_data:/data
|
||||||
|
networks:
|
||||||
|
- portainer-net
|
||||||
|
- iklimco-net
|
||||||
|
deploy:
|
||||||
|
mode: replicated
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.role == manager
|
||||||
|
restart_policy:
|
||||||
|
condition: any
|
||||||
|
delay: 5s
|
||||||
|
labels:
|
||||||
|
project: co.iklim
|
||||||
|
|
||||||
|
node-exporter:
|
||||||
|
image: prom/node-exporter:latest
|
||||||
|
command:
|
||||||
|
- '--path.procfs=/host/proc'
|
||||||
|
- '--path.sysfs=/host/sys'
|
||||||
|
- '--path.rootfs=/rootfs'
|
||||||
|
volumes:
|
||||||
|
- /:/rootfs:ro
|
||||||
|
- /proc:/host/proc:ro
|
||||||
|
- /sys:/host/sys:ro
|
||||||
|
networks:
|
||||||
|
- iklimco-net
|
||||||
|
deploy:
|
||||||
|
mode: global
|
||||||
|
restart_policy:
|
||||||
|
condition: any
|
||||||
|
delay: 5s
|
||||||
|
labels:
|
||||||
|
project: co.iklim
|
||||||
|
|
||||||
|
cadvisor:
|
||||||
|
image: gcr.io/cadvisor/cadvisor:latest
|
||||||
|
volumes:
|
||||||
|
- /:/rootfs:ro
|
||||||
|
- /var/run:/var/run:ro
|
||||||
|
- /sys:/sys:ro
|
||||||
|
- /var/lib/docker:/var/lib/docker:ro
|
||||||
|
networks:
|
||||||
|
- iklimco-net
|
||||||
|
deploy:
|
||||||
|
mode: global
|
||||||
|
restart_policy:
|
||||||
|
condition: any
|
||||||
|
delay: 5s
|
||||||
|
labels:
|
||||||
|
project: co.iklim
|
||||||
|
|
||||||
|
loki:
|
||||||
|
image: ${CUSTOM_IMAGE_REGISTRY}${IMAGE_LOKI}
|
||||||
|
volumes:
|
||||||
|
- loki-vl:/loki
|
||||||
|
networks:
|
||||||
|
- iklimco-net
|
||||||
|
deploy:
|
||||||
|
mode: replicated
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.labels.type == service
|
||||||
|
restart_policy:
|
||||||
|
condition: any
|
||||||
|
delay: 5s
|
||||||
|
labels:
|
||||||
|
project: co.iklim
|
||||||
|
|
||||||
|
promtail:
|
||||||
|
image: ${CUSTOM_IMAGE_REGISTRY}${IMAGE_PROMTAIL}
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
networks:
|
||||||
|
- iklimco-net
|
||||||
|
deploy:
|
||||||
|
mode: global
|
||||||
|
restart_policy:
|
||||||
|
condition: any
|
||||||
|
delay: 5s
|
||||||
|
labels:
|
||||||
|
project: co.iklim
|
||||||
|
|
||||||
|
networks:
|
||||||
|
portainer-net:
|
||||||
|
driver: overlay
|
||||||
|
attachable: true
|
||||||
|
iklimco-net:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
portainer_data:
|
||||||
|
loki-vl:
|
||||||
@ -1,38 +0,0 @@
|
|||||||
services:
|
|
||||||
dozzle:
|
|
||||||
image: ${IMAGE_DOZZLE:-amir20/dozzle:v10.6.6}
|
|
||||||
environment:
|
|
||||||
- DOZZLE_MODE=swarm
|
|
||||||
- DOZZLE_AUTH_PROVIDER=simple
|
|
||||||
- DOZZLE_NO_ANALYTICS=true
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
secrets:
|
|
||||||
- source: dozzle_users
|
|
||||||
target: /data/users.yml
|
|
||||||
mode: 0400
|
|
||||||
networks:
|
|
||||||
- dozzle
|
|
||||||
- iklimco-net
|
|
||||||
deploy:
|
|
||||||
mode: global
|
|
||||||
restart_policy:
|
|
||||||
condition: any
|
|
||||||
delay: 5s
|
|
||||||
update_config:
|
|
||||||
parallelism: 1
|
|
||||||
order: start-first
|
|
||||||
labels:
|
|
||||||
project: co.iklim
|
|
||||||
|
|
||||||
secrets:
|
|
||||||
dozzle_users:
|
|
||||||
external: true
|
|
||||||
name: ${DOZZLE_USERS_SECRET_NAME:-dozzle_users}
|
|
||||||
|
|
||||||
networks:
|
|
||||||
dozzle:
|
|
||||||
driver: overlay
|
|
||||||
attachable: true
|
|
||||||
iklimco-net:
|
|
||||||
external: true
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
PROFILE=${SPRING_PROFILES_ACTIVE:-prod}
|
|
||||||
|
|
||||||
if [[ "$PROFILE" == "dev" ]]; then
|
|
||||||
APISIX_ADMIN_URL=http://${LAN_IP:-127.0.0.1}:9180/apisix/admin
|
|
||||||
else
|
|
||||||
APISIX_ADMIN_URL=http://apisix:9180/apisix/admin
|
|
||||||
fi
|
|
||||||
|
|
||||||
API_KEY=${APISIX_ADMIN_KEY:?APISIX_ADMIN_KEY is required}
|
|
||||||
DOZZLE_HOST=${DOZZLE_SUBDOMAIN:-dozzle.iklim.co}
|
|
||||||
DOZZLE_NODE=${DOZZLE_NODE:-dozzle:8080}
|
|
||||||
ERRORS=0
|
|
||||||
|
|
||||||
call_api() {
|
|
||||||
local label="$1"; shift
|
|
||||||
local http_code
|
|
||||||
http_code=$(curl -sS -o /tmp/apisix_dozzle_resp.json -w "%{http_code}" "$@")
|
|
||||||
if [[ "$http_code" -ge 400 ]]; then
|
|
||||||
echo "ERROR: $label (HTTP $http_code)"
|
|
||||||
cat /tmp/apisix_dozzle_resp.json
|
|
||||||
echo
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
until curl -sf -o /dev/null -H "X-API-KEY: $API_KEY" "$APISIX_ADMIN_URL/upstreams"; do
|
|
||||||
echo "APISIX not ready, retrying in 3s..."
|
|
||||||
sleep 3
|
|
||||||
done
|
|
||||||
|
|
||||||
HC='"checks":{"active":{"type":"http","http_path":"/","timeout":5,"healthy":{"interval":10,"successes":1},"unhealthy":{"interval":5,"http_failures":3}},"passive":{"healthy":{"http_statuses":[200,201,204,302],"successes":2},"unhealthy":{"http_statuses":[429,500,502,503,504],"http_failures":3,"tcp_failures":3}}}'
|
|
||||||
|
|
||||||
if [[ "$PROFILE" != "dev" ]]; then
|
|
||||||
DOZZLE_ROUTE_PLUGINS=',"plugins":{"limit-count":{"count":120,"time_window":60,"key":"remote_addr","rejected_code":429,"policy":"local"}}'
|
|
||||||
else
|
|
||||||
DOZZLE_ROUTE_PLUGINS=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
call_api "upstream dozzle" -X PUT "$APISIX_ADMIN_URL/upstreams/dozzle-upstream" \
|
|
||||||
-H "X-API-KEY: $API_KEY" -H "Content-Type: application/json" \
|
|
||||||
-d '{"name":"dozzle-upstream","type":"roundrobin","nodes":{"'"$DOZZLE_NODE"'":1},'"$HC"'}'
|
|
||||||
|
|
||||||
call_api "service dozzle" -X PUT "$APISIX_ADMIN_URL/services/dozzle-service" \
|
|
||||||
-H "X-API-KEY: $API_KEY" -H "Content-Type: application/json" \
|
|
||||||
-d '{"name":"dozzle-service","upstream_id":"dozzle-upstream","enable_websocket":true}'
|
|
||||||
|
|
||||||
call_api "route dozzle" -X PUT "$APISIX_ADMIN_URL/routes/dozzle-route" \
|
|
||||||
-H "X-API-KEY: $API_KEY" -H "Content-Type: application/json" \
|
|
||||||
-d '{"name":"dozzle-route","hosts":["'"$DOZZLE_HOST"'"],"uri":"/*","methods":["GET","POST","PUT","DELETE","PATCH","OPTIONS"],"service_id":"dozzle-service","enable_websocket":true'"$DOZZLE_ROUTE_PLUGINS"'}'
|
|
||||||
|
|
||||||
if [ "$ERRORS" -gt 0 ]; then
|
|
||||||
echo "Dozzle APISIX init completed with $ERRORS error(s)."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Dozzle APISIX route configured for https://${DOZZLE_HOST}"
|
|
||||||
@ -2,7 +2,7 @@ server {
|
|||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
listen [::]:443 ssl;
|
listen [::]:443 ssl;
|
||||||
http2 on;
|
http2 on;
|
||||||
server_name ${DOZZLE_SUBDOMAIN};
|
server_name ${PORTAINER_SUBDOMAIN};
|
||||||
|
|
||||||
include /config/nginx/ssl.conf;
|
include /config/nginx/ssl.conf;
|
||||||
include /config/nginx/resolver.conf;
|
include /config/nginx/resolver.conf;
|
||||||
@ -10,10 +10,13 @@ server {
|
|||||||
client_max_body_size 0;
|
client_max_body_size 0;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
${RESTRICTED_IPS_BLOCK}
|
||||||
|
deny all;
|
||||||
|
|
||||||
include /config/nginx/proxy.conf;
|
include /config/nginx/proxy.conf;
|
||||||
include /config/nginx/resolver.conf;
|
include /config/nginx/resolver.conf;
|
||||||
set $upstream_app apisix;
|
set $upstream_app portainer;
|
||||||
set $upstream_port 9080;
|
set $upstream_port 9000;
|
||||||
set $upstream_proto http;
|
set $upstream_proto http;
|
||||||
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
|
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user