mirror of
https://github.com/KevinMidboe/infra-map.git
synced 2025-10-29 17:40:28 +00:00
proxy through varnish
sets up varnish cache server with: - custom cache per content type - gzip - configure app & hass backends - varnish debug headers & X-Cache verbose cache header also updates docker-compose, varnish/Dockerfile & drone.
This commit is contained in:
41
.drone.yml
41
.drone.yml
@@ -48,6 +48,44 @@ steps:
|
|||||||
- latest
|
- latest
|
||||||
- ${DRONE_COMMIT_SHA}
|
- ${DRONE_COMMIT_SHA}
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
include:
|
||||||
|
- push
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
branch:
|
||||||
|
- main
|
||||||
|
- update
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- Build
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: Publish
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: config-check
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: check-config
|
||||||
|
image: alpine/git
|
||||||
|
commands:
|
||||||
|
- git fetch --no-tags --depth=2
|
||||||
|
- |
|
||||||
|
if git diff --quiet HEAD^ HEAD -- varnish/default.vcl; then
|
||||||
|
echo "No changes in varnish config file, skipping..."
|
||||||
|
exit 78 # exit code 78 = skip in Drone
|
||||||
|
else
|
||||||
|
echo "Changes detected in varnish config"
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Publish varnish to ghcr
|
- name: Publish varnish to ghcr
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
settings:
|
settings:
|
||||||
@@ -72,7 +110,6 @@ trigger:
|
|||||||
branch:
|
branch:
|
||||||
- main
|
- main
|
||||||
- update
|
- update
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- Build
|
- Build
|
||||||
|
|
||||||
@@ -146,6 +183,6 @@ volumes:
|
|||||||
temp: {}
|
temp: {}
|
||||||
---
|
---
|
||||||
kind: signature
|
kind: signature
|
||||||
hmac: b3cc991813f340024e65c68d5509cb23025796914a2e2ac72f71657a347e0708
|
hmac: 01caa41521eac62356f6fc941cdd489dae8e2c4249bdb4e4dc1a32e101c639b7
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|||||||
21
docker-compose.yml
Normal file
21
docker-compose.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
container_name: infra-map
|
||||||
|
ports:
|
||||||
|
- '3000:3000' # svelte-kit preview HTTP
|
||||||
|
varnish:
|
||||||
|
build: varnish
|
||||||
|
container_name: varnish-cache
|
||||||
|
ports:
|
||||||
|
- '6081:6081' # Varnish HTTP
|
||||||
|
environment:
|
||||||
|
- VARNISH_LISTEN_PORT=6081
|
||||||
|
command: >
|
||||||
|
varnishd
|
||||||
|
-F
|
||||||
|
-f /etc/varnish/default.vcl
|
||||||
|
-s malloc,256m
|
||||||
|
-a :6081
|
||||||
@@ -33,7 +33,8 @@
|
|||||||
function refetchImage() {
|
function refetchImage() {
|
||||||
let url;
|
let url;
|
||||||
try {
|
try {
|
||||||
url = new URL(`${IMAGE_PROXY_URL}/image/${imageUrl}`);
|
const { protocol, host } = window.location;
|
||||||
|
url = new URL(`${protocol}//${host}/image-proxy/${imageUrl}`);
|
||||||
} catch {
|
} catch {
|
||||||
console.log('url not valid, returning');
|
console.log('url not valid, returning');
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ RUN git clone https://github.com/varnish/libvmod-digest.git /opt/libvmod-digest
|
|||||||
./configure VARNISHSRC=/usr/include/varnish && \
|
./configure VARNISHSRC=/usr/include/varnish && \
|
||||||
make && make install
|
make && make install
|
||||||
|
|
||||||
|
COPY . /etc/varnish/
|
||||||
|
|
||||||
EXPOSE 6081
|
EXPOSE 6081
|
||||||
|
|
||||||
CMD ["varnishd", "-F", "-f", "/etc/varnish/default.vcl", "-a", ":6081", "-s", "malloc,512m"]
|
CMD ["varnishd", "-F", "-f", "/etc/varnish/default.vcl", "-a", ":6081", "-s", "malloc,512m"]
|
||||||
|
|||||||
146
varnish/default.vcl
Normal file
146
varnish/default.vcl
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
vcl 4.0;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
import digest;
|
||||||
|
|
||||||
|
# include "handlers/ttl-override-handler.vcl";
|
||||||
|
include "includes/x-cache-header.vcl";
|
||||||
|
|
||||||
|
include "vcl_deliver.vcl";
|
||||||
|
|
||||||
|
# Define backend pointing to Home Assistant IP
|
||||||
|
backend hass_backend {
|
||||||
|
.host = "10.0.0.82";
|
||||||
|
.port = "8123";
|
||||||
|
}
|
||||||
|
|
||||||
|
backend app_frontend {
|
||||||
|
.host = "host.docker.internal";
|
||||||
|
.port = "5173";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_recv {
|
||||||
|
set req.backend_hint = app_frontend;
|
||||||
|
|
||||||
|
# Handle CORS preflight
|
||||||
|
if (req.method == "OPTIONS") {
|
||||||
|
return (synth(204, "Preflight"));
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rewrite image URL
|
||||||
|
if (req.url ~ "^/image-proxy/?") {
|
||||||
|
# Extract everything after /image-proxy/ and store it
|
||||||
|
set req.http.X-Image-URL = regsub(req.url, "^/image-proxy/(.*)", "\1");
|
||||||
|
|
||||||
|
# Rewrite req.url to match backend expectations
|
||||||
|
set req.url = regsub(req.http.X-Image-URL, "^http://[^/]+", "");
|
||||||
|
|
||||||
|
set req.backend_hint = hass_backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable debug headers through query param
|
||||||
|
if (req.url ~ "(?i)debug=(true|yes|1)") {
|
||||||
|
set req.http.X-debug = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove cookies so content is cacheable
|
||||||
|
unset req.http.Cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_synth {
|
||||||
|
if (resp.status == 204) {
|
||||||
|
set resp.http.Access-Control-Allow-Origin = "*";
|
||||||
|
set resp.http.Access-Control-Allow-Methods = "GET, OPTIONS";
|
||||||
|
set resp.http.Access-Control-Allow-Headers = "Content-Type, X-Cache-ID";
|
||||||
|
set resp.http.Content-Length = "0";
|
||||||
|
return (deliver);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.status == 304) {
|
||||||
|
set resp.http.ETag = req.http.If-None-Match;
|
||||||
|
set resp.http.Content-Length = "0";
|
||||||
|
return (deliver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_backend_response {
|
||||||
|
###################
|
||||||
|
# cache rules #
|
||||||
|
###################
|
||||||
|
# HTML pages → short cache or no cache
|
||||||
|
if (bereq.url ~ "\.html$") {
|
||||||
|
set beresp.ttl = 30s; # Cache briefly
|
||||||
|
set beresp.uncacheable = true; # Or disable cache entirely
|
||||||
|
}
|
||||||
|
|
||||||
|
# JavaScript & CSS → long cache
|
||||||
|
if (bereq.url ~ "\.(js|css)$") {
|
||||||
|
set beresp.ttl = 1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Images under /image/ → long cache
|
||||||
|
if (bereq.url ~ "^/images/.*\.(svg|png|jpe?g)$") {
|
||||||
|
set beresp.ttl = 1y;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Favicons → long cache
|
||||||
|
if (bereq.url ~ "^/favicons/") {
|
||||||
|
set beresp.ttl = 1y;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fallback: ensure some cache
|
||||||
|
if (beresp.ttl <= 0s) {
|
||||||
|
set beresp.ttl = 22s;
|
||||||
|
}
|
||||||
|
|
||||||
|
set beresp.http.X-TTL = beresp.ttl;
|
||||||
|
|
||||||
|
####################
|
||||||
|
# camera proxy #
|
||||||
|
####################
|
||||||
|
if (bereq.url ~ "^/api/camera_proxy/") {
|
||||||
|
set beresp.ttl = 0.3s;
|
||||||
|
set beresp.grace = 60s;
|
||||||
|
set beresp.keep = 60s;
|
||||||
|
|
||||||
|
# Ensure ETag is passed to client
|
||||||
|
if (beresp.http.ETag) {
|
||||||
|
set beresp.http.X-Cache-ETag = beresp.http.ETag;
|
||||||
|
} else {
|
||||||
|
# Optional: generate one if not provided
|
||||||
|
# set beresp.http.ETag = digest.hash_md5(beresp.body);
|
||||||
|
set beresp.http.ETag = beresp.http.Content-Length;
|
||||||
|
set beresp.http.X-Cache-ETag = beresp.http.ETag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
########################
|
||||||
|
# gzip compression #
|
||||||
|
########################
|
||||||
|
if (beresp.http.Content-Type ~ "^(text/|application/json|application/javascript|application/x-javascript|application/xml|application/xhtml+xml|image/svg\+xml)") {
|
||||||
|
set beresp.do_gzip = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (deliver);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_hit {
|
||||||
|
if (obj.ttl < 0s && std.healthy(req.backend_hint)) {
|
||||||
|
return (deliver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_deliver {
|
||||||
|
unset resp.http.X-Image-URL;
|
||||||
|
set resp.http.Access-Control-Allow-Origin = "*";
|
||||||
|
|
||||||
|
# Handle conditional request with ETag
|
||||||
|
if (
|
||||||
|
req.http.If-None-Match &&
|
||||||
|
req.http.If-None-Match == resp.http.ETag
|
||||||
|
) {
|
||||||
|
return (synth(304));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (deliver);
|
||||||
|
}
|
||||||
43
varnish/includes/x-cache-header.vcl
Normal file
43
varnish/includes/x-cache-header.vcl
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
sub vcl_recv {
|
||||||
|
unset req.http.X-Cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_hit {
|
||||||
|
set req.http.X-Cache = "HIT";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_miss {
|
||||||
|
set req.http.X-Cache = "MISS";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_pass {
|
||||||
|
set req.http.X-Cache = "PASS";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_pipe {
|
||||||
|
set req.http.X-Cache = "PIPE uncacheable";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_synth {
|
||||||
|
set resp.http.X-Cache = "SYNTH";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_deliver {
|
||||||
|
if (obj.uncacheable) {
|
||||||
|
set req.http.X-Cache = req.http.X-Cache + " uncacheable" ;
|
||||||
|
} else {
|
||||||
|
set req.http.X-Cache = req.http.X-Cache + " cached" + " (real age: " + resp.http.Age + ", hits: " + obj.hits + ", ttl: " + resp.http.x-ttl + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
# if we are gracing, make sure the browser doesn't cache things, and set our maxage to 1
|
||||||
|
# also log grace delivery
|
||||||
|
if (req.http.graceineffect) {
|
||||||
|
set resp.http.Cache-Control = regsub(resp.http.Cache-Control, "max-age=[0-9]*", "max-age=1");
|
||||||
|
set resp.http.Cache-Control = regsub(resp.http.Cache-Control, "channel-maxage=[0-9]*", "channel-maxage=1");
|
||||||
|
set req.http.X-Cache = req.http.X-Cache + " [grace: " + req.http.graceineffect + " " + req.http.grace + ", remaining: " + req.http.graceduration + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
# uncomment the following line to show the information in the response
|
||||||
|
set resp.http.X-Cache = req.http.X-Cache;
|
||||||
|
}
|
||||||
|
|
||||||
40
varnish/vcl_deliver.vcl
Normal file
40
varnish/vcl_deliver.vcl
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
sub vcl_deliver {
|
||||||
|
# Happens when we have all the pieces we need, and are about to send the
|
||||||
|
# response to the client.
|
||||||
|
|
||||||
|
if (resp.status == 503) {
|
||||||
|
set resp.http.failing-backend = "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Give some debug
|
||||||
|
if (req.http.X-debug && req.esi_level == 0) {
|
||||||
|
set resp.http.X-Backend = req.backend_hint;
|
||||||
|
set resp.http.X-Backend-Url = req.url;
|
||||||
|
} else {
|
||||||
|
# not debug, strip some headers
|
||||||
|
unset resp.http.X-Cache;
|
||||||
|
unset resp.http.X-Backend;
|
||||||
|
unset resp.http.x-upstream;
|
||||||
|
unset resp.http.x-request-uri;
|
||||||
|
unset resp.http.Via;
|
||||||
|
unset resp.http.xkey;
|
||||||
|
unset resp.http.x-goog-hash;
|
||||||
|
unset resp.http.x-goog-generation;
|
||||||
|
unset resp.http.X-GUploader-UploadID;
|
||||||
|
unset resp.http.x-goog-storage-class;
|
||||||
|
unset resp.http.x-goog-metageneration;
|
||||||
|
unset resp.http.x-goog-stored-content-length;
|
||||||
|
unset resp.http.x-goog-stored-content-encoding;
|
||||||
|
unset resp.http.x-goog-meta-goog-reserved-file-mtime;
|
||||||
|
unset resp.http.Server;
|
||||||
|
unset resp.http.X-Apache-Host;
|
||||||
|
unset resp.http.X-Varnish-Backend;
|
||||||
|
unset resp.http.X-Varnish-Host;
|
||||||
|
unset resp.http.X-Nginx-Host;
|
||||||
|
unset resp.http.X-Upstream-Age;
|
||||||
|
unset resp.http.X-Retries;
|
||||||
|
unset resp.http.X-TTL;
|
||||||
|
unset resp.http.X-Varnish;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,11 +4,6 @@ import { defineConfig } from 'vite';
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [sveltekit()],
|
plugins: [sveltekit()],
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
allowedHosts: ['host.docker.internal']
|
||||||
'/proxmox': {
|
|
||||||
target: 'https://apollo.schleppe:8006/api2/json',
|
|
||||||
secure: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user