Add opensocial.at as example

This commit is contained in:
Philipp Holzer 2024-03-01 23:00:03 +01:00
parent 87724f2688
commit 5417ffaa00
Signed by: nupplaPhil
GPG Key ID: 24A7501396EB5432
21 changed files with 600 additions and 0 deletions

View File

@ -0,0 +1 @@
./friendica.conf

View File

@ -0,0 +1,50 @@
# Opensocial.at setup
This example of the current opensocial.at configuration has to be seen as a possible "production-ready" environment.
The focus of this configuration is on performance and scalability.
## Prerequisites
This setup needs some configuration first to be fully usable.
1. It uses an external, dedicated database, which is not included here (you can just add a `mariadb` service directly)
2. avatar caching needs to be enabled
1. Enable the system-config `system.avatar_cache`
2. Set `avatar_cache_path` to `/var/www/avatar`
3. It uses a traefik docker service as overall reverse proxy for the whole docker environment
1. Otherwise, adaptations of the two services `web` and `avatar` are necessary
## The setup
The setup splits Friendica in as much (micro)services as possible.
### Split Frontend & Daemon
This setup splits the frontend services from the background Daemon.
So it's possible to scale different aspect from the frontend without harming states of the cronjob forks of the Daemon.
### Redis
Redis is a highly optimized, in-memory key-value storage.
The current setup uses redis for two use-cases:
- Redis as PHP overall session handler
- Redis for Friendica specific session-state handling
### [app](./app) (php-fpm)
The frontend logic of each user-request is computed by a php-fpm instance.
Because of the distributed session handling, it's possible to scale as much php-fpm app-instances as you need.
### [web](./web) (nginx)
This nginx instance is a reverse proxy for the frontend logic to avoid direct access to the php-fpm.
And it delivers static resources directly without passing the request to the php-fpm instance.
### [avatar](./avatar) (nginx)
This stateless nginx instance delivers all avatar-pictures of this instance.
### [cron](./app) (php-fpm)
The background daemon, which is based on the same image as the app-image.

View File

@ -0,0 +1,15 @@
FROM friendica:fpm-alpine
ENV FRIENDICA_UPGRADE=true
ENV PHP_MEMORY_LIMIT 2G
# Use the default production configuration
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
ENV FRIENDICA_PHP_OUT="/var/www/html/php.out"
RUN set -ex; \
touch ${FRIENDICA_PHP_OUT:-"php.out"}; \
chown www-data:www-data ${FRIENDICA_PHP_OUT:-"php.out"};
RUN sed -i 's/access.log = \/proc\/self\/fd\/2/access.log = \/proc\/self\/fd\/1/g' /usr/local/etc/php-fpm.d/docker.conf

View File

@ -0,0 +1,18 @@
FROM nginx:latest
RUN usermod -u 82 www-data
RUN set -ex; \
mkdir -p /var/www/html; \
mkdir -p /etc/nginx/snippets;
COPY ./templates /etc/nginx/conf.d/templates
COPY nginx.conf /etc/nginx/nginx.conf
COPY error-page.html /var/www/html/error-page.html
COPY custom-error-page.conf /etc/nginx/snippets/custom-error-page.conf
COPY *.sh /
RUN chmod +x /*.sh
CMD ["/cmd.sh"]

View File

@ -0,0 +1,8 @@
#!/bin/sh
set -eu
envsubst < /etc/nginx/conf.d/templates/server_name.template > /etc/nginx/conf.d/server_name.active
nginx -qt
until ping app -c1 > /dev/null; do sleep 1; done
exec nginx -g 'daemon off;'

View File

@ -0,0 +1,5 @@
error_page 404 403 500 503 /error-page.html;
location = /error-page.html {
root /var/www/html;
internal;
}

View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html>
<head>
<style type=text/css>
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
padding: 0;
margin: 0;
}
#notfound {
position: relative;
height: 100vh;
}
#notfound .notfound {
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.notfound {
max-width: 520px;
width: 100%;
line-height: 1.4;
text-align: center;
}
.notfound .notfound-error {
position: relative;
height: 200px;
margin: 0px auto 20px;
z-index: -1;
}
.notfound .notfound-error h1 {
font-family: 'Montserrat', sans-serif;
font-size: 200px;
font-weight: 300;
margin: 0px;
color: #211b19;
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
@media only screen and (max-width: 767px) {
.notfound .notfound-error h1 {
font-size: 148px;
}
}
@media only screen and (max-width: 480px) {
.notfound .notfound-error {
height: 148px;
margin: 0 auto 10px;
}
.notfound .notfound-error h1 {
font-size: 120px;
font-weight: 200;
}
.notfound .notfound-error h2 {
font-size: 30px;
}
.notfound a {
padding: 7px 15px;
font-size: 24px;
}
}
</style>
</head>
<body>
<div id="notfound">
<div class="notfound">
<h1>Sorry the page can't be loaded!</h1>
<div class="notfound-error">
<p>Contact the site's administrator or support for assistance.</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,70 @@
##
# Friendica Nginx configuration
# by Olaf Conradi, modified by Philipp Holzer
#
worker_processes 4;
events {
worker_connections 1024;
}
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
http {
map $request_id $formatted_id {
"~*(?<p1>[0-9a-f]{8})(?<p2>[0-9a-f]{4})(?<p3>[0-9a-f]{4})(?<p4>[0-9a-f]{4})(?<p5>.*)$" "${p1}-${p2}-${p3}-${p4}-${p5}";
}
map $http_x_request_id $uuid {
default "${request_id}";
~* "${http_x_request_id}";
}
charset utf-8;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format logger-json escape=json '{"source": "nginx", "time": $msec, "resp_body_size": $body_bytes_sent, "host": "$http_host", "address": "$remote_addr", "request_length": $request_length, "method": "$request_method", "uri": "$request_uri", "status": $status, "user_agent": "$http_user_agent", "resp_time": $request_time, "upstream_addr": "$upstream_addr", "request_id": "$uuid"}';
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log logger-json;
log_not_found off;
# If behind reverse proxy, forwards the correct IP
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from fc00::/7;
real_ip_header X-Real-IP;
server {
listen 80;
include /etc/nginx/conf.d/server_name.active;
include /etc/nginx/snippets/custom-error-page.conf;
#Uncomment the following line to include a standard configuration file
#Note that the most specific rule wins and your standard configuration
#will therefore *add* to this file, but not override it.
#include standard.conf
# allow uploads up to 20MB in size
client_max_body_size 20m;
client_body_buffer_size 128k;
add_header X-Request-ID $uuid;
location /avatar/ {
root /var/www/;
}
include mime.types;
# deny access to all dot files
location ~ /\. {
deny all;
}
}
}

View File

@ -0,0 +1 @@
server_name ${HOSTNAME};

View File

@ -0,0 +1,6 @@
[PHP]
memory_limit = 8G
upload_max_filesize= 10G
post_max_size = 11G
max_execution_time = 3600
max_input_time = 3600

View File

@ -0,0 +1,11 @@
[www]
pm = dynamic
pm.max_children=100
pm.start_servers=10
pm.min_spare_servers = 4
pm.max_spare_servers = 10
;pm.process_idle_timeout = 10s;
;pm.max_requests = 1000
clear_env = no
catch_workers_output = yes

View File

@ -0,0 +1 @@
friendica

View File

@ -0,0 +1 @@
PLEASE_CHANGE_ME

View File

@ -0,0 +1 @@
PLEASE_CHANGE_ME

View File

@ -0,0 +1 @@
friendica-user

View File

@ -0,0 +1,148 @@
version: '3'
services:
redis:
image: redis
restart: always
volumes:
- friendica-redis-vol-1:/data
command:
- --save 60 1
- --loglevel warning
app:
build: ./app
restart: always
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
deploy:
replicas: 3
resources:
limits:
cpus: '5.00'
memory: '10g'
reservations:
cpus: '1.00'
memory: '1.5g'
depends_on:
- redis
volumes:
- friendica-vol-1:/var/www/html
- friendica-avatar-1:/var/www/avatar
- ./config/app/www.overload.conf:/usr/local/etc/php-fpm.d/www.overload.conf:ro
- ./config/app/friendica.ini:/usr/local/etc/php/conf.d/friendica.ini:ro
environment:
- MYSQL_USER_FILE=/run/secrets/mysql_user
- MYSQL_PASSWORD_FILE=/run/secrets/mysql_password
- MYSQL_DATABASE_FILE=/run/secrets/mysql_database
- MYSQL_HOST=${DBHOST}
- MYSQL_PORT=${DBPORT}
- FRIENDICA_ADMIN_MAIL=${MAILNAME}
- FRIENDICA_TZ=${TZ}
- FRIENDICA_LANG=${LANGUAGE}
- FRIENDICA_UPDATE=true
- SITENAME=${SITENAME}
- SMTP=${SMTP}
- SMTP_DOMAIN=${SMTP_DOMAIN}
- SMTP_AUTH_USER=${SMTP_AUTH_USER}
- SMTP_AUTH_PASS=${SMTP_AUTH_PASS}
- SMTP_TLS=${SMTP_TLS}
- SMTP_STARTTLS=${SMTP_STARTTLS}
- REDIS_HOST=redis
- FRIENDICA_DISTRIBUTED_CACHE_DRIVER=redis
- FRIENDICA_LOGGER=syslog
- FRIENDICA_SYSLOG_FLAGS=39
- FRIENDICA_DATA=Filesystem
- FRIENDICA_DEBUGGING=true
secrets:
- mysql_database
- mysql_user
- mysql_password
cron:
build: ./app
restart: always
volumes:
- friendica-vol-1:/var/www/html
- friendica-avatar-1:/var/www/avatar
- ./config/app/www.overloaded.conf:/usr/local/etc/php-fpm.d/www.overloaded.conf:ro
- ./config/app/friendica.ini:/usr/local/etc/php/conf.d/friendica.ini:ro
environment:
- SITENAME=${SITENAME}
- SMTP=${SMTP}
- SMTP_DOMAIN=${SMTP_DOMAIN}
- SMTP_AUTH_USER=${SMTP_AUTH_USER}
- SMTP_AUTH_PASS=${SMTP_AUTH_PASS}
- SMTP_TLS=${SMTP_TLS}
- SMTP_STARTTLS=${SMTP_STARTTLS}
- MYSQL_HOST=${DBHOST}
- MYSQL_PORT=${DBPORT}
- MYSQL_USERNAME=${DBUSER}
- MYSQL_PASSWORD=${DBPASS}
- MYSQL_DATABASE=${DBDATA}
- FRIENDICA_ADMIN_MAIL=${MAILNAME}
- FRIENDICA_DISTRIBUTED_CACHE_DRIVER=redis
- FRIENDICA_DEBUGGING=true
- FRIENDICA_LOGLEVEL=notice
- FRIENDICA_LOGGER=syslog
- FRIENDICA_SYSLOG_FLAGS=39
depends_on:
- app
entrypoint: /cron.sh
avatar:
build: ./avatar
deploy:
replicas: 3
restart: on-failure:3
volumes:
- friendica-avatar-1:/var/www/avatar:ro
environment:
- HOSTNAME=${HOSTNAME}
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.avatar.entrypoints=websecure"
- "traefik.http.routers.domain.rule=(Host(`www.your.domain`) || Host(`your.domain`)) && PathPrefix(`/avatar`)"
- "traefik.http.routers.domain.middlewares=https-chain@file"
- "traefik.http.routers.domain.tls=true"
- "traefik.http.routers.domain.tls.certresolver=default"
web:
build: ./web
restart: always
deploy:
replicas: 3
volumes:
- friendica-vol-1:/var/www/html:ro
environment:
- HOSTNAME=${HOSTNAME}
depends_on:
- app
networks:
- web
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.yourdomain.entrypoints=websecure"
- "traefik.http.routers.yourdomain.rule=Host(`www.your.domain`) || Host(`your.domain`)"
- "traefik.http.routers.yourdomain.middlewares=https-chain@file"
- "traefik.http.routers.yourdomain.tls=true"
- "traefik.http.routers.yourdomain.tls.certresolver=default"
secrets:
mysql_database:
file: ./config/secrets/mysql_database.txt
mysql_user:
file: ./config/secrets/mysql_user.txt
mysql_password:
file: ./config/secrets/mysql_password.txt
volumes:
friendica-avatar-1:
friendica-vol-1:
friendica-redis-vol-1:
networks:
web:
external: true

View File

@ -0,0 +1,26 @@
# ------------------------------
# friendica configuration
# ------------------------------
# example.org is _not_ a valid hostname, use a fqdn here.
HOSTNAME=example.org
# ------------------------------
# SQL database configuration
# ------------------------------
DBHOST=db
DBPORT=3306
SITENAME="My SiteName"
# Your timezone
TZ=Europe/Berlin
MAILNAME=admin@philipp.info
SMTP=mail
SMTP_DOMAIN=my.domain
SMTP_AUTH_USER=smtp_user
SMTP_AUTH_PASS=smpt_pass
SMTP_TLS=true
SMTP_STARTTLS=true
LANGUAGE=de

View File

@ -0,0 +1,11 @@
FROM nginx:latest
RUN usermod -u 82 www-data
COPY ./templates /etc/nginx/conf.d/templates
COPY nginx.conf /etc/nginx/nginx.conf
COPY *.sh /
RUN chmod +x /*.sh
CMD ["/cmd.sh"]

View File

@ -0,0 +1,8 @@
#!/bin/sh
set -eu
envsubst < /etc/nginx/conf.d/templates/server_name.template > /etc/nginx/conf.d/server_name.active
nginx -qt
until ping app -c1 > /dev/null; do sleep 1; done
exec nginx -g 'daemon off;'

View File

@ -0,0 +1,123 @@
##
# Friendica Nginx configuration
# by Olaf Conradi, modified by Philipp Holzer
#
#worker_processes 4;
events {
worker_connections 1024;
}
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
http {
map $request_id $formatted_id {
"~*(?<p1>[0-9a-f]{8})(?<p2>[0-9a-f]{4})(?<p3>[0-9a-f]{4})(?<p4>[0-9a-f]{4})(?<p5>.*)$" "${p1}-${p2}-${p3}-${p4}-${p5}";
}
map $http_x_request_id $uuid {
default "${request_id}";
~* "${http_x_request_id}";
}
charset utf-8;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format logger-json escape=json '{"source": "nginx", "time": $msec, "resp_body_size": $body_bytes_sent, "host": "$http_host", "address": "$remote_addr", "request_length": $request_length, "method": "$request_method", "uri": "$request_uri", "status": $status, "user_agent": "$http_user_agent", "resp_time": $request_time, "upstream_addr": "$upstream_addr", "request_id": "$uuid"}';
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log logger-json;
# If behind reverse proxy, forwards the correct IP
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from fc00::/7;
real_ip_header X-Real-IP;
upstream php-handler {
server app:9000;
}
server {
listen 80;
include /etc/nginx/conf.d/server_name.active;
index index.php;
root /var/www/html;
#Uncomment the following line to include a standard configuration file
#Note that the most specific rule wins and your standard configuration
#will therefore *add* to this file, but not override it.
#include standard.conf
# allow uploads up to 20MB in size
client_max_body_size 20m;
client_body_buffer_size 128k;
proxy_set_header X-Request-ID $uuid;
add_header X-Request-ID $uuid;
# rewrite to front controller as default rule
location / {
try_files $uri /index.php?pagename=$uri&$args;
}
# make sure webfinger and other well known services aren't blocked
# by denying dot files and rewrite request to the front controller
location ^~ /.well-known/ {
allow all;
try_files $uri /index.php?pagename=$uri&$args;
}
# statically serve these file types when possible
# otherwise fall back to front controller
# allow browser to cache them
# added .htm for advanced source code editor library
#location ~* \.(jpg|jpeg|gif|png|ico|css|js|htm|html|ttf|woff|svg)$ {
# expires 30d;
# try_files $uri /index.php?pagename=$uri&$args;
#}
include mime.types;
# block these file types
location ~* \.(tpl|md|tgz|log|out)$ {
deny all;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
# or a unix socket
location ~* \.php$ {
# Zero-day exploit defense.
# http://forum.nginx.org/read.php?2,88845,page=3
# Won't work properly (404 error) if the file is not stored on this
# server, which is entirely possible with php-fpm/php-fcgi.
# Comment the 'try_files' line out if you set up php-fpm/php-fcgi on
# another machine. And then cross your fingers that you won't get hacked.
try_files $uri =404;
# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-handler;
fastcgi_read_timeout 300;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_X_REQUEST_ID $uuid;
}
# deny access to all dot files
location ~ /\. {
deny all;
}
}
}

View File

@ -0,0 +1 @@
server_name ${HOSTNAME};