Merge pull request #105 from cloud-neutral/codex/create-neurapress-deployment-configuration
Add neurapress docker deployment role
This commit is contained in:
commit
31f4c2ba19
11
playbooks/deploy_neurapress_docker.yaml
Normal file
11
playbooks/deploy_neurapress_docker.yaml
Normal 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/
|
||||
7
playbooks/roles/docker/neurapress/defaults/main.yml
Normal file
7
playbooks/roles/docker/neurapress/defaults/main.yml
Normal 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
|
||||
51
playbooks/roles/docker/neurapress/files/nginx/nginx.conf
Normal file
51
playbooks/roles/docker/neurapress/files/nginx/nginx.conf
Normal 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;
|
||||
}
|
||||
6
playbooks/roles/docker/neurapress/files/run.sh
Normal file
6
playbooks/roles/docker/neurapress/files/run.sh
Normal 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
|
||||
69
playbooks/roles/docker/neurapress/tasks/main.yml
Normal file
69
playbooks/roles/docker/neurapress/tasks/main.yml
Normal 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 }}"
|
||||
@ -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:
|
||||
@ -0,0 +1,11 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ neurapress_domain }};
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 不 redirect!不要 https!
|
||||
# certbot 需要纯 http 验证
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user