690418:1638 Refactor Infra gitea
CI / CD Pipeline / build (push) Has been cancelled
CI / CD Pipeline / deploy (push) Has been cancelled

This commit is contained in:
2026-04-18 16:38:04 +07:00
parent 8b658e8530
commit 29a6509c58
36 changed files with 1824 additions and 157 deletions
@@ -0,0 +1,68 @@
# File: /volume1/np-dms/gitea-runner/docker-compose.yml
# Deploy on: ASUSTOR AS5403T
# เชื่อมต่อกับ Gitea บน QNAP ผ่าน Domain URL
#
# Setup:
# 1. cp .env.example .env
# 2. แก้ค่าใน .env
# 3. docker compose up -d
x-logging: &default_logging
logging:
driver: 'json-file'
options:
max-size: '10m'
max-file: '5'
services:
runner:
<<: *default_logging
image: gitea/act_runner:0.2.11
container_name: gitea-runner
restart: unless-stopped
extra_hosts:
- "git.np-dms.work:192.168.10.8"
environment:
- TZ=Asia/Bangkok
- CONFIG_FILE=/config.yaml
# Gitea connection
- GITEA_INSTANCE_URL=${GITEA_INSTANCE_URL}
- GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_REGISTRATION_TOKEN}
- GITEA_RUNNER_NAME=${GITEA_RUNNER_NAME:-asustor-runner}
# Label: ubuntu:22.04 แทน node:18-bullseye
# setup-node จะ install Node version ที่ถูกต้องได้เอง
- GITEA_RUNNER_LABELS=ubuntu-latest:docker://ubuntu:22.04,self-hosted:docker://ubuntu:22.04
# pnpm store path — ชี้ไปที่ volume ด้านล่าง
- PNPM_HOME=/root/.local/share/pnpm
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /volume1/np-dms/gitea-runner/data:/data
# pnpm global store — persist ข้ามทุก run, ไม่ถูกลบตอน Checkout
- /volume1/np-dms/gitea-runner/pnpm-store:/root/.local/share/pnpm
# Node.js tool cache — setup-node ไม่ต้อง download ซ้ำ
- /volume1/np-dms/gitea-runner/tool-cache:/opt/hostedtoolcache
# config
- /volume1/np-dms/gitea-runner/data:/data
- /volume1/np-dms/gitea-runner/config/config.yaml:/config.yaml
healthcheck:
test: ["CMD", "pgrep", "act_runner"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
cpus: '2.0'
memory: 4G
reservations:
cpus: '0.5'
memory: 1G
@@ -0,0 +1 @@
GRAFANA_ADMIN_PASSWORD=
@@ -0,0 +1,233 @@
# File: /volume1/np-dms/monitoring/docker-compose.yml
# DMS Container v1.8.6: Application name: lcbp3-monitoring
# Deploy on: ASUSTOR AS5403T
# Services: prometheus, grafana, node-exporter, cadvisor, uptime-kuma, loki, promtail
x-restart: &restart_policy
restart: unless-stopped
x-logging: &default_logging
logging:
driver: 'json-file'
options:
max-size: '10m'
max-file: '5'
networks:
lcbp3:
external: true
services:
# ----------------------------------------------------------------
# 1. Prometheus (Metrics Collection & Storage)
# ----------------------------------------------------------------
prometheus:
<<: [*restart_policy, *default_logging]
image: prom/prometheus:v2.48.0
container_name: prometheus
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
environment:
TZ: 'Asia/Bangkok'
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
ports:
- '9090:9090'
networks:
- lcbp3
volumes:
- '/volume1/np-dms/monitoring/prometheus/config:/etc/prometheus:ro'
- '/volume1/np-dms/monitoring/prometheus/data:/prometheus'
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:9090/-/healthy']
interval: 30s
timeout: 10s
retries: 3
# ----------------------------------------------------------------
# 2. Grafana (Dashboard & Visualization)
# ----------------------------------------------------------------
grafana:
<<: [*restart_policy, *default_logging]
image: grafana/grafana:10.2.2
container_name: grafana
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
env_file:
- .env
environment:
TZ: 'Asia/Bangkok'
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:?GRAFANA_ADMIN_PASSWORD required}
GF_SERVER_ROOT_URL: 'https://grafana.np-dms.work'
GF_INSTALL_PLUGINS: grafana-clock-panel,grafana-piechart-panel
ports:
- '3000:3000'
networks:
- lcbp3
volumes:
- '/volume1/np-dms/monitoring/grafana/data:/var/lib/grafana'
depends_on:
- prometheus
healthcheck:
test: ['CMD-SHELL', 'wget --spider -q http://localhost:3000/api/health || exit 1']
interval: 30s
timeout: 10s
retries: 3
# ----------------------------------------------------------------
# 3. Uptime Kuma (Service Availability Monitoring)
# ----------------------------------------------------------------
uptime-kuma:
<<: [*restart_policy, *default_logging]
image: louislam/uptime-kuma:1
container_name: uptime-kuma
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
environment:
TZ: 'Asia/Bangkok'
ports:
- '3001:3001'
networks:
- lcbp3
volumes:
- '/volume1/np-dms/monitoring/uptime-kuma/data:/app/data'
healthcheck:
test: ['CMD-SHELL', 'curl -f http://localhost:3001/api/entry-page || exit 1']
interval: 30s
timeout: 10s
retries: 3
# ----------------------------------------------------------------
# 4. Node Exporter (Host Metrics - ASUSTOR)
# ----------------------------------------------------------------
node-exporter:
<<: [*restart_policy, *default_logging]
image: prom/node-exporter:v1.7.0
container_name: node-exporter
deploy:
resources:
limits:
cpus: '0.5'
memory: 128M
environment:
TZ: 'Asia/Bangkok'
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
ports:
- '9100:9100'
networks:
- lcbp3
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:9100/metrics']
interval: 30s
timeout: 10s
retries: 3
# ----------------------------------------------------------------
# 5. cAdvisor (Container Metrics - ASUSTOR)
# ----------------------------------------------------------------
cadvisor:
<<: [*restart_policy, *default_logging]
image: gcr.io/cadvisor/cadvisor:v0.47.2
container_name: cadvisor
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
environment:
TZ: 'Asia/Bangkok'
# H4: cAdvisor binds 8080 ภายใน container — map เป็น 8088 บน host
ports:
- '8088:8080'
networks:
- lcbp3
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:8080/healthz']
interval: 30s
timeout: 10s
retries: 3
# ----------------------------------------------------------------
# 6. Loki (Log Aggregation)
# ----------------------------------------------------------------
loki:
<<: [*restart_policy, *default_logging]
image: grafana/loki:2.9.0
container_name: loki
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
environment:
TZ: 'Asia/Bangkok'
command: -config.file=/etc/loki/local-config.yaml
ports:
- '3100:3100'
networks:
- lcbp3
volumes:
- '/volume1/np-dms/monitoring/loki/data:/loki'
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:3100/ready']
interval: 30s
timeout: 10s
retries: 3
# ----------------------------------------------------------------
# 7. Promtail (Log Shipper)
# ----------------------------------------------------------------
promtail:
<<: [*restart_policy, *default_logging]
image: grafana/promtail:2.9.0
container_name: promtail
# L5: รันในฐานะ root เพราะต้องอ่าน /var/lib/docker/containers
# ที่ mount เข้ามาแบบ read-only
user: '0:0'
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
environment:
TZ: 'Asia/Bangkok'
command: -config.file=/etc/promtail/promtail-config.yml
networks:
- lcbp3
volumes:
- '/volume1/np-dms/monitoring/promtail/config:/etc/promtail:ro'
- '/var/run/docker.sock:/var/run/docker.sock:ro'
- '/var/lib/docker/containers:/var/lib/docker/containers:ro'
depends_on:
- loki
@@ -0,0 +1,800 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 18,
"links": [],
"liveNow": false,
"panels": [
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 18,
"panels": [],
"title": "Logs",
"type": "row"
},
{
"datasource": {
"type": "loki",
"uid": "a78950eb-fe7b-48c5-bbb5-7ef22a250c29"
},
"gridPos": {
"h": 41,
"w": 24,
"x": 0,
"y": 1
},
"id": 6,
"options": {
"dedupStrategy": "none",
"enableLogDetails": true,
"prettifyLogMessage": false,
"showCommonLabels": false,
"showLabels": false,
"showTime": true,
"sortOrder": "Descending",
"wrapLogMessage": false
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "a78950eb-fe7b-48c5-bbb5-7ef22a250c29"
},
"editorMode": "code",
"expr": "{container=~\"$container\"} |= ``",
"queryType": "range",
"refId": "A"
}
],
"title": "Container Logs",
"type": "logs"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 42
},
"id": 16,
"panels": [],
"title": "CPU",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "percent"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 43
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"editorMode": "code",
"expr": "rate(container_cpu_usage_seconds_total{image!=\"\",name=~\"$container\",instance=~\"$host\"}[5m]) * 100",
"legendFormat": "{{name}}",
"range": true,
"refId": "A"
}
],
"title": "CPU Usage (%)",
"type": "timeseries"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 51
},
"id": 22,
"panels": [],
"title": "Memory",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 52
},
"hiddenSeries": false,
"id": 23,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "10.2.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": true,
"steppedLine": false,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)",
"hide": false,
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Memory Usage",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:606",
"format": "bytes",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:607",
"format": "short",
"logBase": 1,
"show": true
}
],
"yaxis": {
"align": false
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 52
},
"hiddenSeries": false,
"id": 24,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "10.2.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": true,
"steppedLine": false,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)",
"hide": false,
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Memory Cached",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:606",
"format": "bytes",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:607",
"format": "short",
"logBase": 1,
"show": true
}
],
"yaxis": {
"align": false
}
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 60
},
"id": 21,
"panels": [],
"title": "Network",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 61
},
"hiddenSeries": false,
"id": 25,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"hideEmpty": false,
"hideZero": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "10.2.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"expr": "sum(rate(container_network_receive_bytes_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name)",
"hide": false,
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Received Network Traffic",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:674",
"format": "Bps",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:675",
"format": "short",
"logBase": 1,
"show": true
}
],
"yaxis": {
"align": false
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 61
},
"hiddenSeries": false,
"id": 26,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "10.2.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"expr": "sum(rate(container_network_transmit_bytes_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name)",
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Sent Network Traffic",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:832",
"format": "Bps",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:833",
"format": "short",
"logBase": 1,
"show": true
}
],
"yaxis": {
"align": false
}
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 69
},
"id": 19,
"panels": [],
"title": "Misc",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"filterable": false,
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "id"
},
"properties": [
{
"id": "custom.width",
"value": 260
}
]
},
{
"matcher": {
"id": "byName",
"options": "Running"
},
"properties": [
{
"id": "unit",
"value": "d"
},
{
"id": "decimals",
"value": 1
},
{
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
},
{
"id": "color",
"value": {
"fixedColor": "dark-green",
"mode": "fixed"
}
}
]
}
]
},
"gridPos": {
"h": 10,
"w": 24,
"x": 0,
"y": 70
},
"id": 20,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": ["sum"],
"show": false
},
"showHeader": true,
"sortBy": []
},
"pluginVersion": "10.2.2",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400",
"format": "table",
"instant": true,
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"title": "Containers Info",
"transformations": [
{
"id": "filterFieldsByName",
"options": {
"include": {
"names": [
"container_label_com_docker_compose_project",
"container_label_com_docker_compose_project_working_dir",
"image",
"instance",
"name",
"Value",
"container_label_com_docker_compose_service"
]
}
}
},
{
"id": "organize",
"options": {
"excludeByName": {},
"indexByName": {},
"renameByName": {
"Value": "Running",
"container_label_com_docker_compose_project": "Label",
"container_label_com_docker_compose_project_working_dir": "Working dir",
"container_label_com_docker_compose_service": "Service",
"image": "Registry Image",
"instance": "Instance",
"name": "Name"
}
}
}
],
"type": "table"
}
],
"refresh": "10s",
"schemaVersion": 38,
"tags": ["docker", "monitoring", "lcbp3"],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"definition": "label_values(container_cpu_usage_seconds_total{image!=\"\"}, instance)",
"hide": 0,
"includeAll": true,
"label": "Host",
"multi": false,
"name": "host",
"options": [],
"query": {
"query": "label_values(container_cpu_usage_seconds_total{image!=\"\"}, instance)",
"refId": "StandardVariableQuery"
},
"refresh": 1,
"regex": "/(.*)/",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"allValue": ".+",
"current": {
"selected": true,
"text": ["All"],
"value": ["$__all"]
},
"datasource": {
"type": "prometheus",
"uid": "bd6668d2-9dc4-40a7-8cd2-8b8c9719b1fb"
},
"definition": "label_values(container_cpu_usage_seconds_total{instance=~\"$host\", image!=\"\"}, name)",
"hide": 0,
"includeAll": true,
"label": "Container",
"multi": true,
"name": "container",
"options": [],
"query": {
"query": "label_values(container_cpu_usage_seconds_total{instance=~\"$host\", image!=\"\"}, name)",
"refId": "StandardVariableQuery"
},
"refresh": 1,
"regex": "/^\\/?(.*)$/",
"skipUrlSync": false,
"sort": 1,
"type": "query"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Data Center Overview (Loki + Prometheus)",
"uid": "lcbp3-docker-metrics-logs",
"version": 5,
"weekStart": ""
}
@@ -0,0 +1,2 @@
REGISTRY_ADMIN_USER=admin
REGISTRY_ADMIN_PASSWORD=
@@ -0,0 +1,58 @@
# File: /volume1/np-dms/registry/docker-compose.yml
# DMS Container v1.8.0: Application name: lcbp3-registry
# Deploy on: ASUSTOR AS5403T
# Services: registry
# ============================================================
# ⚠️ ข้อกำหนด:
# - ต้องสร้าง Docker Network ก่อน: docker network create lcbp3
# - Registry ใช้ Port 5000 (domain: registry.np-dms.work)
# - Portainer มีอยู่แล้วบน ASUSTOR (ไม่ต้อง deploy ซ้ำ)
# ============================================================
x-restart: &restart_policy
restart: unless-stopped
x-logging: &default_logging
logging:
driver: 'json-file'
options:
max-size: '10m'
max-file: '5'
networks:
lcbp3:
external: true
services:
# ----------------------------------------------------------------
# 1. Docker Registry (Private Container Image Storage)
# ----------------------------------------------------------------
registry:
<<: [*restart_policy, *default_logging]
image: registry:2
container_name: registry
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
reservations:
cpus: '0.1'
memory: 64M
environment:
TZ: 'Asia/Bangkok'
# --- Storage ---
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
# --- Garbage Collection (ลบ layer ที่ไม่ใช้) ---
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
ports:
- '5000:5000'
networks:
- lcbp3
volumes:
- '/volume1/np-dms/registry/data:/var/lib/registry'
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:5000/v2/']
interval: 30s
timeout: 10s
retries: 3
@@ -0,0 +1,104 @@
# File: /volume1/np-dms/registry/docker-compose.yml
# DMS Container v1.8.6: Application name: lcbp3-registry
# Deploy on: ASUSTOR AS5403T
# Services: registry, portainer
# ============================================================
# ⚠️ ข้อกำหนด:
# - ต้องสร้าง Docker Network ก่อน: docker network create lcbp3
# - Registry ใช้ Port 5000 (domain: registry.np-dms.work)
# - Portainer ใช้ Port 9443 (domain: portainer.np-dms.work)
# ============================================================
# 🔒 SECURITY (M6):
# Registry เปิด htpasswd auth (ADR-016)
# Prerequisite (ทำครั้งเดียวก่อน deploy):
# docker run --rm --entrypoint htpasswd httpd:2 -Bbn \
# "$REGISTRY_ADMIN_USER" "$REGISTRY_ADMIN_PASSWORD" \
# > /volume1/np-dms/registry/auth/htpasswd
# Env (.env): REGISTRY_ADMIN_USER, REGISTRY_ADMIN_PASSWORD
# ============================================================
x-restart: &restart_policy
restart: unless-stopped
x-logging: &default_logging
logging:
driver: 'json-file'
options:
max-size: '10m'
max-file: '5'
networks:
lcbp3:
external: true
services:
# 1. ตัวเก็บ Image (Docker Registry)
registry:
<<: [*restart_policy, *default_logging]
image: registry:2
container_name: registry
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
reservations:
cpus: '0.1'
memory: 64M
env_file:
- .env
environment:
TZ: 'Asia/Bangkok'
# --- Storage ---
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
# --- M6: htpasswd auth ---
REGISTRY_AUTH: 'htpasswd'
REGISTRY_AUTH_HTPASSWD_REALM: 'NP-DMS Registry'
REGISTRY_AUTH_HTPASSWD_PATH: '/auth/htpasswd'
security_opt:
- no-new-privileges:true
ports:
- '5000:5000'
volumes:
- '/volume1/np-dms/registry/data:/var/lib/registry'
- '/volume1/np-dms/registry/auth:/auth:ro'
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:5000/v2/']
interval: 30s
timeout: 10s
retries: 3
networks:
- lcbp3
# 2. UI สำหรับส่องดู Image
registry-ui:
<<: [*restart_policy, *default_logging]
image: joxit/docker-registry-ui:2.5.7
container_name: registry-ui
deploy:
resources:
limits:
cpus: '0.25'
memory: 128M
security_opt:
- no-new-privileges:true
ports:
- '8880:80'
environment:
TZ: 'Asia/Bangkok'
REGISTRY_TITLE: 'NP-DMS Registry'
REGISTRY_URL: 'http://registry:5000'
SINGLE_REGISTRY: 'true'
DELETE_IMAGES: 'true'
depends_on:
registry:
condition: service_healthy
networks:
- lcbp3
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:80/']
interval: 30s
timeout: 10s
retries: 3