Secure Production Ready Setup, Security Best Practices and Setup Monitoring Logging
This commit is contained in:
parent
66806e70d7
commit
f391d5355a
|
@ -1,48 +1,37 @@
|
||||||
FROM php:8.2-cli
|
FROM php:8.2-cli
|
||||||
|
|
||||||
|
# Buat non-root user
|
||||||
|
ARG USER=appuser
|
||||||
|
ARG UID=1000
|
||||||
|
RUN useradd -u $UID -m $USER
|
||||||
|
|
||||||
|
# Install dependencies & PostgreSQL driver
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libpq-dev \
|
||||||
|
postgresql-client \
|
||||||
|
unzip \
|
||||||
|
git \
|
||||||
|
libzip-dev \
|
||||||
|
libicu-dev \
|
||||||
|
&& docker-php-ext-install pdo pdo_pgsql pgsql zip intl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Composer
|
||||||
|
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
WORKDIR /var/www
|
WORKDIR /var/www
|
||||||
|
|
||||||
# Install build dependencies & PHP extensions
|
# Copy source code
|
||||||
RUN apk add --no-cache \
|
COPY . .
|
||||||
bash \
|
|
||||||
git \
|
|
||||||
unzip \
|
|
||||||
libpng-dev \
|
|
||||||
libjpeg-turbo-dev \
|
|
||||||
freetype-dev \
|
|
||||||
libzip-dev \
|
|
||||||
icu-dev \
|
|
||||||
zlib-dev \
|
|
||||||
postgresql-dev \
|
|
||||||
curl \
|
|
||||||
oniguruma-dev \
|
|
||||||
autoconf \
|
|
||||||
gcc \
|
|
||||||
g++ \
|
|
||||||
make \
|
|
||||||
pkgconfig \
|
|
||||||
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
|
|
||||||
&& docker-php-ext-install pdo pdo_pgsql mbstring zip intl bcmath gd \
|
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
# Install Composer
|
# Change owner to non-root user
|
||||||
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer
|
RUN chown -R $USER:$USER /var/www
|
||||||
|
|
||||||
# Copy project
|
# Switch to non-root user
|
||||||
COPY . /var/www
|
USER $USER
|
||||||
|
|
||||||
# Set permissions
|
# Install Laravel dependencies
|
||||||
RUN mkdir -p storage bootstrap/cache \
|
RUN composer install --no-dev --optimize-autoloader --prefer-dist
|
||||||
&& chown -R www-data:www-data storage bootstrap/cache
|
|
||||||
|
|
||||||
# Install Laravel dependencies & optimasi
|
|
||||||
RUN composer install --no-dev --optimize-autoloader
|
|
||||||
|
|
||||||
# Cache Laravel
|
|
||||||
RUN php artisan config:cache \
|
|
||||||
&& php artisan route:cache \
|
|
||||||
&& php artisan view:cache
|
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
|
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
|
|
@ -89,7 +89,7 @@ return [
|
||||||
'port' => env('DB_PORT', '5432'),
|
'port' => env('DB_PORT', '5432'),
|
||||||
'database' => env('DB_DATABASE', 'laravel'),
|
'database' => env('DB_DATABASE', 'laravel'),
|
||||||
'username' => env('DB_USERNAME', 'root'),
|
'username' => env('DB_USERNAME', 'root'),
|
||||||
'password' => env('DB_PASSWORD', ''),
|
'password' => env('DB_PASSWORD_FILE', ''),
|
||||||
'charset' => env('DB_CHARSET', 'utf8'),
|
'charset' => env('DB_CHARSET', 'utf8'),
|
||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'prefix_indexes' => true,
|
'prefix_indexes' => true,
|
||||||
|
|
|
@ -1,60 +1,96 @@
|
||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
db_password:
|
||||||
|
file: ./secrets/db_password.txt
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db-data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
sijago_net:
|
||||||
|
driver: overlay
|
||||||
|
monitoring_net:
|
||||||
|
external: true # pastikan monitoring stack network sudah dibuat sebelumnya
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile.prod
|
|
||||||
container_name: sijago_app
|
|
||||||
working_dir: /var/www
|
|
||||||
volumes:
|
|
||||||
- .:/var/www
|
|
||||||
ports:
|
|
||||||
- "8000:8000"
|
|
||||||
depends_on:
|
|
||||||
db:
|
|
||||||
condition: service_healthy
|
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
environment:
|
|
||||||
APP_ENV: production
|
|
||||||
APP_DEBUG: false
|
|
||||||
APP_KEY: base64:8LpKi2PtQ4nhCQr+u495ZRvfno7PjNrVnh0kyuoLTeE=
|
|
||||||
DB_CONNECTION: pgsql
|
|
||||||
DB_HOST: db
|
|
||||||
DB_PORT: 5432
|
|
||||||
DB_DATABASE: sijago
|
|
||||||
DB_USERNAME: sijago_user
|
|
||||||
DB_PASSWORD: secret
|
|
||||||
CACHE_DRIVER: redis
|
|
||||||
REDIS_HOST: redis
|
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: postgres:13
|
image: postgres:13
|
||||||
container_name: sijago_db
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: sijago
|
POSTGRES_DB: sijago
|
||||||
POSTGRES_USER: sijago_user
|
POSTGRES_USER: sijago_user
|
||||||
POSTGRES_PASSWORD: secret
|
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
|
||||||
volumes:
|
volumes:
|
||||||
- db-data-prod:/var/lib/postgresql/data
|
- db-data:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U sijago_user"]
|
test: ["CMD-SHELL", "pg_isready -U sijago_user"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 10
|
||||||
|
secrets:
|
||||||
|
- db_password
|
||||||
|
networks:
|
||||||
|
- sijago_net
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '0.5'
|
||||||
|
memory: 512M
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
container_name: sijago_redis
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
ports:
|
||||||
- "6379:6379" # beda port juga biar aman
|
- "6379:6379"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "redis-cli", "ping"]
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
networks:
|
||||||
|
- sijago_net
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '0.2'
|
||||||
|
memory: 256M
|
||||||
|
|
||||||
volumes:
|
app:
|
||||||
db-data
|
image: sijago_app:prod # build dulu: docker build -t sijago_app:prod -f Dockerfile.prod .
|
||||||
|
working_dir: /var/www
|
||||||
|
volumes:
|
||||||
|
- .:/var/www:cached
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
APP_ENV: production
|
||||||
|
APP_DEBUG: "false"
|
||||||
|
DB_CONNECTION: pgsql
|
||||||
|
DB_HOST: db
|
||||||
|
DB_PORT: 5432
|
||||||
|
DB_DATABASE: sijago
|
||||||
|
DB_USERNAME: sijago_user
|
||||||
|
DB_PASSWORD_FILE: /run/secrets/db_password
|
||||||
|
CACHE_DRIVER: redis
|
||||||
|
REDIS_HOST: redis
|
||||||
|
PROMETHEUS_URL: http://prometheus:9090
|
||||||
|
CADVISOR_URL: http://cadvisor:8080
|
||||||
|
GRAFANA_URL: http://grafana:3000
|
||||||
|
secrets:
|
||||||
|
- db_password
|
||||||
|
networks:
|
||||||
|
- sijago_net
|
||||||
|
- monitoring_net
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '1.0'
|
||||||
|
memory: 512M
|
||||||
|
reservations:
|
||||||
|
cpus: '0.5'
|
||||||
|
memory: 256M
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
monitoring_net:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
grafana-data:
|
||||||
|
loki-data:
|
||||||
|
|
||||||
|
services:
|
||||||
|
cadvisor:
|
||||||
|
image: gcr.io/cadvisor/cadvisor:latest
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- /:/rootfs:ro
|
||||||
|
- /var/run:/var/run:ro
|
||||||
|
- /sys:/sys:ro
|
||||||
|
- /var/lib/docker/:/var/lib/docker:ro
|
||||||
|
networks:
|
||||||
|
- monitoring_net
|
||||||
|
deploy:
|
||||||
|
mode: global
|
||||||
|
privileged: true # biar cadvisor bisa akses semua metric container
|
||||||
|
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:latest
|
||||||
|
ports:
|
||||||
|
- "9090:9090"
|
||||||
|
volumes:
|
||||||
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||||
|
networks:
|
||||||
|
- monitoring_net
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- grafana-data:/var/lib/grafana
|
||||||
|
- ./grafana/provisioning:/etc/grafana/provisioning
|
||||||
|
environment:
|
||||||
|
- GF_SECURITY_ADMIN_USER=syifa
|
||||||
|
- GF_SECURITY_ADMIN_PASSWORD=secret123
|
||||||
|
depends_on:
|
||||||
|
- prometheus
|
||||||
|
- loki
|
||||||
|
networks:
|
||||||
|
- monitoring_net
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
|
||||||
|
loki:
|
||||||
|
image: grafana/loki:2.8.2
|
||||||
|
ports:
|
||||||
|
- "3100:3100"
|
||||||
|
volumes:
|
||||||
|
- ./loki-config.yml:/etc/loki/local-config.yml:ro
|
||||||
|
- loki-data:/loki
|
||||||
|
command: -config.file=/etc/loki/local-config.yml
|
||||||
|
networks:
|
||||||
|
- monitoring_net
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
|
||||||
|
promtail:
|
||||||
|
image: grafana/promtail:2.8.2
|
||||||
|
volumes:
|
||||||
|
- /var/log:/var/log
|
||||||
|
- /var/lib/docker/containers:/var/lib/docker/containers:ro
|
||||||
|
- ./promtail-config.yml:/etc/promtail/config.yml:ro
|
||||||
|
command: -config.file=/etc/promtail/config.yml
|
||||||
|
depends_on:
|
||||||
|
- loki
|
||||||
|
networks:
|
||||||
|
- monitoring_net
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
|
@ -0,0 +1,9 @@
|
||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: 'Default'
|
||||||
|
orgId: 1
|
||||||
|
folder: ''
|
||||||
|
type: file
|
||||||
|
options:
|
||||||
|
path: /etc/grafana/provisioning/dashboards
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"id": null,
|
||||||
|
"title": "Docker Container Overview",
|
||||||
|
"tags": ["docker", "cadvisor"],
|
||||||
|
"timezone": "browser",
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"type": "graph",
|
||||||
|
"title": "Container CPU Usage",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "rate(container_cpu_usage_seconds_total[1m])",
|
||||||
|
"legendFormat": "{{container_label_com_docker_swarm_task_name}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "graph",
|
||||||
|
"title": "Container Memory Usage",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "container_memory_usage_bytes",
|
||||||
|
"legendFormat": "{{container_label_com_docker_swarm_task_name}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schemaVersion": 16,
|
||||||
|
"version": 1
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
datasources:
|
||||||
|
- name: Prometheus
|
||||||
|
type: prometheus
|
||||||
|
access: proxy
|
||||||
|
url: http://prometheus:9090
|
||||||
|
isDefault: true
|
||||||
|
|
||||||
|
- name: Loki
|
||||||
|
type: loki
|
||||||
|
access: proxy
|
||||||
|
url: http://loki:3100
|
|
@ -0,0 +1,54 @@
|
||||||
|
auth_enabled: false
|
||||||
|
|
||||||
|
server:
|
||||||
|
http_listen_port: 3100
|
||||||
|
grpc_listen_port: 9096
|
||||||
|
|
||||||
|
ingester:
|
||||||
|
lifecycler:
|
||||||
|
address: 0.0.0.0
|
||||||
|
ring:
|
||||||
|
kvstore:
|
||||||
|
store: inmemory
|
||||||
|
replication_factor: 1
|
||||||
|
chunk_idle_period: 5m
|
||||||
|
max_chunk_age: 1h
|
||||||
|
chunk_target_size: 1048576
|
||||||
|
chunk_retain_period: 30s
|
||||||
|
wal:
|
||||||
|
enabled: true
|
||||||
|
dir: /loki/wal
|
||||||
|
|
||||||
|
schema_config:
|
||||||
|
configs:
|
||||||
|
- from: 2020-10-24
|
||||||
|
store: boltdb-shipper
|
||||||
|
object_store: filesystem
|
||||||
|
schema: v11
|
||||||
|
index:
|
||||||
|
prefix: index_
|
||||||
|
period: 24h
|
||||||
|
|
||||||
|
storage_config:
|
||||||
|
boltdb_shipper:
|
||||||
|
active_index_directory: /loki/index
|
||||||
|
cache_location: /loki/cache
|
||||||
|
shared_store: filesystem
|
||||||
|
filesystem:
|
||||||
|
directory: /loki/chunks
|
||||||
|
|
||||||
|
compactor:
|
||||||
|
working_directory: /loki/compactor
|
||||||
|
shared_store: filesystem
|
||||||
|
|
||||||
|
limits_config:
|
||||||
|
ingestion_rate_mb: 8
|
||||||
|
ingestion_burst_size_mb: 16
|
||||||
|
max_concurrent_tail_requests: 20
|
||||||
|
|
||||||
|
chunk_store_config:
|
||||||
|
max_look_back_period: 0s
|
||||||
|
|
||||||
|
table_manager:
|
||||||
|
retention_deletes_enabled: true
|
||||||
|
retention_period: 168h # 7 hari
|
|
@ -0,0 +1,25 @@
|
||||||
|
global:
|
||||||
|
scrape_interval: 15s
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'prometheus'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['prometheus:9090']
|
||||||
|
|
||||||
|
- job_name: 'sijago_app'
|
||||||
|
#metrics_path: /metrics
|
||||||
|
static_configs:
|
||||||
|
- targets: ['sijago_stack_app:8000']
|
||||||
|
|
||||||
|
# Scrape semua cadvisor (global mode di swarm)
|
||||||
|
- job_name: 'cadvisor'
|
||||||
|
dns_sd_configs:
|
||||||
|
- names:
|
||||||
|
- 'tasks.monitoring_stack_cadvisor'
|
||||||
|
type: A
|
||||||
|
port: 8080
|
||||||
|
|
||||||
|
# Scrape Loki (opsional, buat healthcheck)
|
||||||
|
- job_name: 'loki'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['loki:3100']
|
|
@ -0,0 +1,34 @@
|
||||||
|
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:
|
||||||
|
# Ambil log khusus dari container "sijago_stack_app"
|
||||||
|
- job_name: sijago
|
||||||
|
docker_sd_configs:
|
||||||
|
- host: unix:///var/run/docker.sock
|
||||||
|
relabel_configs:
|
||||||
|
# Ambil hanya container dengan nama sijago_stack_app
|
||||||
|
- source_labels: [__meta_docker_container_name]
|
||||||
|
regex: /sijago_stack_app.*
|
||||||
|
action: keep
|
||||||
|
# Path log
|
||||||
|
- source_labels: [__meta_docker_container_log_stream]
|
||||||
|
target_label: stream
|
||||||
|
- source_labels: [__meta_docker_container_name]
|
||||||
|
target_label: container
|
||||||
|
|
||||||
|
# Ambil semua log dari semua container Docker
|
||||||
|
- job_name: docker
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
- localhost
|
||||||
|
labels:
|
||||||
|
job: docker
|
||||||
|
__path__: /var/lib/docker/containers/*/*-json.log
|
|
@ -0,0 +1,18 @@
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
root /var/www/public;
|
||||||
|
|
||||||
|
index index.php index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
fastcgi_pass 127.0.0.1:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
secret
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue