Add neurapress docker deployment role

This commit is contained in:
cloudneutral 2025-12-15 21:50:31 +08:00
parent 4dc54f6644
commit 1d7263767d
8 changed files with 272 additions and 0 deletions

View File

@ -0,0 +1,11 @@
- name: setup neurapress
hosts: all
become: true
vars:
neurapress_domain: "{{ domain }}"
neurapress_workspace: /opt/neurapress
neurapress_image: neurapress:prod
neurapress_certbot_email: manbuzhe2009@qq.com
roles:
- vhosts/docker/
- docker/neurapress/

View File

@ -0,0 +1,7 @@
---
# Default deployment directory for Neurapress Docker stack
neurapress_deploy_dir: /opt/neurapress
neurapress_workspace: "{{ neurapress_deploy_dir }}"
neurapress_domain: write.svc.plus
neurapress_image: neurapress:prod
neurapress_certbot_email: manbuzhe2009@qq.com

View File

@ -0,0 +1,51 @@
user nginx;
worker_processes auto;
# Logs container stdout / stderr
error_log /dev/stderr warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Access log stdout
log_format main
'$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /dev/stdout main;
# Core performance (safe defaults)
sendfile on;
tcp_nodelay on;
keepalive_timeout 65;
server_tokens off;
# TLS session cache (in-memory only)
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Gzip (lightweight)
gzip on;
gzip_comp_level 6;
gzip_min_length 256;
gzip_types
text/plain
text/css
application/json
application/javascript
application/xml
image/svg+xml;
# Allow uploads (Markdown / images)
client_max_body_size 50m;
# Load virtual hosts
include /etc/nginx/conf.d/*.conf;
}

View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
# Helper script to start the Neurapress docker compose stack
cd "$(dirname "$0")"
docker compose -f docker-compose.yaml up -d

View File

@ -0,0 +1,69 @@
---
- name: Ensure Neurapress directories exist
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0755"
loop:
- "{{ neurapress_workspace }}"
- "{{ neurapress_workspace }}/certbot"
- "{{ neurapress_workspace }}/certbot/conf"
- "{{ neurapress_workspace }}/certbot/www"
- "{{ neurapress_workspace }}/nginx"
- "{{ neurapress_workspace }}/nginx/conf.d"
- name: Ensure Neurapress workspace ownership
become: true
ansible.builtin.file:
path: "{{ neurapress_workspace }}"
state: directory
recurse: true
owner: "1000"
group: "1000"
mode: "0755"
- name: Template Neurapress configuration files
become: true
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ neurapress_workspace }}/{{ item.dest }}"
mode: "{{ item.mode | default('0644') }}"
loop:
- { src: 'docker-compose.yaml', dest: 'docker-compose.yaml' }
- { src: 'nginx/conf.d/default.conf', dest: 'nginx/conf.d/default.conf' }
- { src: 'nginx/conf.d/bootstrap-nginx.conf', dest: 'nginx/conf.d/bootstrap-nginx.conf' }
- name: Copy Neurapress static files
become: true
ansible.builtin.copy:
src: "{{ item.src }}"
dest: "{{ neurapress_workspace }}/{{ item.dest }}"
mode: "{{ item.mode | default('0644') }}"
loop:
- { src: 'run.sh', dest: 'run.sh', mode: '0755' }
- { src: 'nginx/nginx.conf', dest: 'nginx/nginx.conf' }
- name: Bootstrap NGINX (80-only for ACME)
become: true
command: docker compose --profile bootstrap -f {{ neurapress_workspace }}/docker-compose.yaml up -d bootstrap-nginx
args:
chdir: "{{ neurapress_workspace }}"
- name: Run certbot initial ACME challenge
become: true
command: docker compose --profile bootstrap -f {{ neurapress_workspace }}/docker-compose.yaml run --rm certbot
args:
chdir: "{{ neurapress_workspace }}"
- name: Destroy Bootstrap NGINX (80-only for ACME)
become: true
command: docker compose --profile bootstrap -f {{ neurapress_workspace }}/docker-compose.yaml down bootstrap-nginx
args:
chdir: "{{ neurapress_workspace }}"
- name: Bring up Neurapress stack
become: true
command: docker compose -f {{ neurapress_workspace }}/docker-compose.yaml up -d
args:
chdir: "{{ neurapress_workspace }}"

View File

@ -0,0 +1,68 @@
services:
app:
image: "{{ neurapress_image }}"
command: pnpm start
ports:
- "3000:3000"
environment:
- NODE_ENV=production
networks:
- app
nginx:
image: nginx:mainline-alpine
container_name: neurapress-nginx
depends_on:
- app
ports:
- "80:80"
- "443:443"
volumes:
- "{{ neurapress_workspace }}/nginx/nginx.conf:/etc/nginx/nginx.conf:ro"
- "{{ neurapress_workspace }}/nginx/conf.d:/etc/nginx/conf.d:ro"
- "{{ neurapress_workspace }}/certbot/conf:/etc/letsencrypt"
- "{{ neurapress_workspace }}/certbot/www:/var/www/certbot"
networks:
- app
bootstrap-nginx:
profiles: ["bootstrap"]
image: nginx:mainline-alpine
container_name: bootstrap-nginx
volumes:
- "{{ neurapress_workspace }}/nginx/nginx.conf:/etc/nginx/nginx.conf:ro"
- "{{ neurapress_workspace }}/nginx/conf.d/bootstrap-nginx.conf:/etc/nginx/conf.d/default.conf:ro"
- "{{ neurapress_workspace }}/certbot/conf:/etc/letsencrypt"
- "{{ neurapress_workspace }}/certbot/www:/var/www/certbot"
ports:
- "80:80"
networks:
- app
healthcheck:
test: ["CMD", "wget", "-qO-", "http://{{ neurapress_domain }}"]
interval: 3s
timeout: 2s
retries: 10
start_period: 3s
certbot:
profiles: ["bootstrap"]
image: certbot/certbot
container_name: certbot
command: >
certonly --webroot
--webroot-path=/var/www/certbot
--email {{ neurapress_certbot_email }}
--agree-tos
--no-eff-email
--keep-until-expiring
--non-interactive
-d {{ neurapress_domain }}
volumes:
- "{{ neurapress_workspace }}/certbot/conf:/etc/letsencrypt"
- "{{ neurapress_workspace }}/certbot/www:/var/www/certbot"
networks:
- app
networks:
app:

View File

@ -0,0 +1,11 @@
server {
listen 80;
server_name {{ neurapress_domain }};
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 不 redirect不要 https
# certbot 需要纯 http 验证
}

View File

@ -0,0 +1,49 @@
# ----------------------------------------------------
# 80 - ACME Challenge + Redirect to HTTPS
# ----------------------------------------------------
server {
listen 80;
server_name {{ neurapress_domain }};
# Certbot HTTP-01 challenge
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# All HTTP → HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# ----------------------------------------------------
# 443 - TLS Termination for Neurapress
# ----------------------------------------------------
server {
listen 443 ssl http2;
server_name {{ neurapress_domain }};
ssl_certificate /etc/letsencrypt/live/{{ neurapress_domain }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ neurapress_domain }}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# Next.js / Neurapress
location / {
proxy_pass http://app:3000;
proxy_http_version 1.1;
# WebSocket / HMR support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Standard headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 300;
}
}