1) dynamic to add ENTRYPOINTS, 2) fixed the bug by manage cmd to remove sub-domain / domain
This commit is contained in:
parent
d825a46ded
commit
20c55e0c4b
File diff suppressed because it is too large
Load Diff
@ -241,6 +241,116 @@ Select configurations to remove (enter numbers separated by space, or 'q' to qui
|
||||
- Domain removal does not require Traefik restart (dynamic config removal)
|
||||
- All changes are permanent and cannot be undone - make sure you want to remove before confirming
|
||||
|
||||
### Step 6: Configure Extra Traefik Entrypoints (QUIC, Custom Ports, etc.)
|
||||
|
||||
Beyond the default entrypoints (`web` on :80, `websecure` on :443), you can add extra entrypoints such as HTTP/3 QUIC (UDP 443) or other custom protocols. Entrypoints are defined via command-line arguments to the Traefik container, so the container must be recreated to apply them.
|
||||
|
||||
**Configuration Variable:**
|
||||
- `EXTRA_ENTRYPOINTS` in `.env.rvs-prxy` (global proxy config) or per-domain `.env-<domain>` files
|
||||
- Format: `name=:port/protocol,name2=:port` (comma-separated)
|
||||
- Protocol defaults to `tcp` if omitted; use `/udp` for UDP entrypoints
|
||||
- Example: `quic=:443/udp` creates entrypoint `quic` listening on UDP port 443
|
||||
- Example: `quic=:443/udp, mqtt=:1883` adds both QUIC and a custom TCP entrypoint on port 1883
|
||||
|
||||
---
|
||||
|
||||
#### Scenario A: First-Time Setup (Traefik Not Yet Created)
|
||||
|
||||
The wizard asks for extra entrypoints during the Traefik configuration step. You can also pre-configure them:
|
||||
|
||||
1. Create `.env.rvs-prxy` with the entrypoints setting:
|
||||
```bash
|
||||
EXTRA_ENTRYPOINTS="quic=:443/udp"
|
||||
```
|
||||
2. Run the script as usual:
|
||||
```bash
|
||||
curl -s "https://www.ivantito.com/app-pub/demos/test/deploy-rvs-prxy.sh" | bash
|
||||
```
|
||||
3. **What happens:**
|
||||
- The docker-compose.yml generated by the script includes the extra entrypoints in the `command:` section and their port mappings in the `ports:` section
|
||||
- The Traefik container starts with `--entrypoints.quic.address=:443/udp` and `-p 443:443/udp`
|
||||
- Both TCP and UDP on port 443 are now mapped (QUIC shares port 443 with HTTPS)
|
||||
|
||||
---
|
||||
|
||||
#### Scenario B: Running Traefik — Add New Extra Entrypoints
|
||||
|
||||
To add a new entrypoint (e.g., QUIC) to a Traefik container that is already running:
|
||||
|
||||
1. Set `EXTRA_ENTRYPOINTS` in `.env.rvs-prxy` (proxy config) or in the per-domain `.env-<domain>` file:
|
||||
```bash
|
||||
EXTRA_ENTRYPOINTS="quic=:443/udp"
|
||||
```
|
||||
If the variable already has values, add the new one with a comma:
|
||||
```bash
|
||||
EXTRA_ENTRYPOINTS="quic=:443/udp, mqtt=:1883"
|
||||
```
|
||||
|
||||
2. Run the script:
|
||||
```bash
|
||||
curl -s "https://www.ivantito.com/app-pub/demos/test/deploy-rvs-prxy.sh" | bash
|
||||
```
|
||||
The script detects the running Traefik container and checks if `EXTRA_ENTRYPOINTS` has changed.
|
||||
|
||||
3. **What happens:**
|
||||
- The script calls `parse_extra_entrypoints()` which parses the variable
|
||||
- It inspects the current Traefik container's command-line args
|
||||
- If any extra entrypoints are missing from the running container, the script:
|
||||
1. Stops and removes the current Traefik container (data in `traefik_data` volume is preserved)
|
||||
2. Recreates the container with all standard entrypoints plus the new extra entrypoints and their port mappings
|
||||
- Brief downtime (~5 seconds) while the container is recreated
|
||||
- Existing domain routing and port exposure configurations remain intact (they are stored in the volume)
|
||||
|
||||
4. **Expected output** (proxy setup mode — `deploy-rvs-prxy.sh`):
|
||||
```
|
||||
[INFO] Extra entrypoints changed, recreating Traefik container to apply...
|
||||
[INFO] Updating Traefik container...
|
||||
[INFO] Traefik container recreated with new entry points
|
||||
```
|
||||
**Expected output** (domain setup mode — `.env-p2prly1-ivantito-com`):
|
||||
```
|
||||
[INFO] Loading domain config from .env-p2prly1-ivantito-com
|
||||
[INFO] Detected running Traefik reverse proxy
|
||||
[INFO] Extra entrypoints changed, recreating Traefik container to apply...
|
||||
[INFO] Checking if Application Service is running locally...
|
||||
...
|
||||
[INFO] Traefik configured to route traffic to Application Service
|
||||
```
|
||||
|
||||
5. **Verify the new entrypoint is active:**
|
||||
```bash
|
||||
# Check container args for entrypoint definitions
|
||||
docker inspect traefik --format '{{range .Config.Cmd}}{{println .}}{{end}}' | grep entrypoints
|
||||
# Should show: --entrypoints.quic.address=:443/udp
|
||||
|
||||
# Check port mappings (TCP and UDP on 443)
|
||||
docker port traefik
|
||||
# Should show: 443/tcp → 0.0.0.0:443 and 443/udp → 0.0.0.0:443
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Scenario C: Running Traefik — Remove an Extra Entrypoint
|
||||
|
||||
1. Edit `.env.rvs-prxy` and remove the entrypoint from `EXTRA_ENTRYPOINTS`
|
||||
2. Run the script to trigger the recreation with the updated entrypoints (same as Scenario B steps 2-4)
|
||||
|
||||
---
|
||||
|
||||
#### Scenario D: Running Traefik — Add/Remove During Port Exposure Setup
|
||||
|
||||
When you use port exposure (`--manage` or `.env-port-<port>`), the `add_traefik_entry_point()` and `manage_configurations()` functions automatically preserve all `EXTRA_ENTRYPOINTS`. Extra entrypoints are re-injected whenever the Traefik container is recreated for port operations, so they are never lost.
|
||||
|
||||
**No additional steps needed** — extra entrypoints configured in `.env.rvs-prxy` are automatically preserved across all port management operations.
|
||||
|
||||
---
|
||||
|
||||
**Important Notes:**
|
||||
- Recreating the Traefik container causes ~5 seconds of downtime
|
||||
- The `traefik_data` volume (config files, SSL certificates, ACME data) is preserved across recreations
|
||||
- `EXTRA_ENTRYPOINTS` adds entrypoints to the Traefik container's command-line args. When a domain config also has a UDP port in `LOCAL_SERVICE_PORT` (e.g., `80,4443/udp`), the script automatically creates a Traefik UDP router in the domain config, so no manual routing setup is needed for that UDP port
|
||||
- For port-mapped entrypoints (like port-exposure), existing `--manage` flows already handle adding/removing
|
||||
|
||||
## Run this System
|
||||
|
||||
### Using with curl (Remote Execution)
|
||||
@ -400,6 +510,8 @@ When the FRP reverse proxy is already running, you can add new domains:
|
||||
- **Configuration Files:**
|
||||
- `.env.rvs-prxy` - Reverse proxy instance configuration (contains `INSTANCE_NAME`, `FRP_SERVER_ADDR`, etc.)
|
||||
- `.env-<domain>` - Domain-specific configuration (contains `DOMAIN_NAME`, `LOCAL_SERVICE_NAME`, `LOCAL_SERVICE_PORT`)
|
||||
- `LOCAL_SERVICE_PORT` supports comma-separated ports with protocol: `80` or `80,4443/udp`
|
||||
- First TCP port is used for HTTPS routing; additional UDP ports get automatic Traefik UDP router config
|
||||
|
||||
- **What Happens:**
|
||||
- A new TCP proxy is added to frpc.toml for the domain's service
|
||||
@ -428,6 +540,9 @@ If environment variables are not set, the script will prompt for:
|
||||
|
||||
#### Traefik Container (Traefik Mode)
|
||||
|
||||
**Entrypoints:**
|
||||
Default entrypoints are `web` (:80) and `websecure` (:443). See **[Step 6: Configure Extra Traefik Entrypoints](#step-6-configure-extra-traefik-entrypoints-quic-custom-ports-etc)** for a complete guide on adding/removing extra entrypoints (QUIC, custom ports, etc.) during both first-time setup and on a running Traefik container.
|
||||
|
||||
```bash
|
||||
# Start/run container
|
||||
docker run -d --name traefik --restart=unless-stopped \
|
||||
@ -453,8 +568,11 @@ docker inspect traefik
|
||||
# Remove container
|
||||
docker rm -f traefik
|
||||
|
||||
# Access Traefik Dashboard
|
||||
http://localhost:8080
|
||||
# Access Traefik Dashboard (port from DASHBOARD_PORT, default 18080)
|
||||
http://localhost:${DASHBOARD_PORT:-18080}/dashboard/
|
||||
|
||||
# Check docker-compose.yml used to create the container (preserved after each run)
|
||||
cat /tmp/docker-compose.yml
|
||||
|
||||
# Complete removal (including data)
|
||||
docker rm -f traefik
|
||||
|
||||
@ -109,13 +109,15 @@
|
||||
**Required Variables (only 3):**
|
||||
- `DOMAIN_NAME` - Domain name for reverse proxy routing (e.g., `app.example.com`)
|
||||
- `LOCAL_SERVICE_NAME` - Name of the local Docker application container
|
||||
- `LOCAL_SERVICE_PORT` - Port number of the local application service
|
||||
- `LOCAL_SERVICE_PORT` - Port(s) of the local application service, comma-separated with optional protocol suffix (e.g., `80` or `80,4443/udp`)
|
||||
|
||||
**Example `.env-app-example-com`:**
|
||||
```bash
|
||||
DOMAIN_NAME="app.example.com"
|
||||
LOCAL_SERVICE_NAME="my-app-container"
|
||||
LOCAL_SERVICE_PORT="3000"
|
||||
LOCAL_SERVICE_PORT="3000" # Single port (TCP)
|
||||
# LOCAL_SERVICE_PORT="80,4443/udp" # Multiple ports (TCP + UDP)
|
||||
|
||||
```
|
||||
|
||||
**Reverse Proxy Instance Configuration (`.env.rvs-prxy`):**
|
||||
@ -127,6 +129,7 @@ LOCAL_SERVICE_PORT="3000"
|
||||
- `INSTANCE_NAME` - Container name for Traefik instance (default: `traefik`)
|
||||
- `DOCKER_NETWORK` - Docker network name for container connectivity (default: `traefik-public`)
|
||||
- `DASHBOARD_PORT` - Traefik dashboard port exposed on localhost (default: `18080`)
|
||||
- `EXTRA_ENTRYPOINTS` - Additional Traefik entrypoints beyond defaults (web/websecure), format: `name=:port/protocol,name2=:port`, e.g., `quic=:443/udp` for HTTP/3 QUIC support (default: empty). Can be set in `.env.rvs-prxy` or per-domain `.env-<domain>` files
|
||||
- `TRAEFIK_VER` - Traefik version (default: `v3.6.9`)
|
||||
- `LETSENCRYPT_EMAIL` - Email for Let's Encrypt certificates
|
||||
- `RESTART_TRAEFIK` - Whether to restart Traefik after config update (`true`/`false`, default: `false`)
|
||||
@ -146,7 +149,7 @@ LOCAL_SERVICE_PORT="3000"
|
||||
**Required Variables:**
|
||||
- `DOMAIN_NAME` - Domain name for reverse proxy routing (e.g., `app.example.com`, `sub.domain.org.hk`)
|
||||
- `LOCAL_SERVICE_NAME` - Name of the local Docker application container
|
||||
- `LOCAL_SERVICE_PORT` - Port number of the local application service
|
||||
- `LOCAL_SERVICE_PORT` - Port(s) of the local application service, comma-separated with optional protocol suffix (e.g., `80` or `80,4443/udp`)
|
||||
|
||||
**Port-Based Routing Configuration (`.env-port-<port>`):**
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
# Before Starting Any Work
|
||||
- Please read the following documents carefully **before taking any actions** on this project: `README.md`, `Design_Doc/Spec.md`, `Design_Doc/Operations.md` and `Design_Doc/Tech_Desc.md`. You may skip the `Design_Doc/Tech_Desc.md` -> "AI Prompt" section and its sub-sections when reading. These files provide essential background information and project understanding. Only after reviewing them should you proceed with the following tasks.
|
||||
|
||||
- Please read the following documents carefully **before taking any actions** on this project: `README.md`, `Design_Doc/Spec.md`, `Design_Doc/Operations.md` and `Design_Doc/Tech_Desc.md`. These files provide essential background information and project understanding. Only after reviewing them should you proceed with the following tasks.
|
||||
|
||||
# Your Current Tasks
|
||||
- Listing the specific tasks for this session.
|
||||
@ -10,53 +11,20 @@ Whenever you finish making code changes, please take a few minutes to update the
|
||||
This keeps everything clear and up-to-date for everyone on the team.
|
||||
|
||||
## Which files might need an update?
|
||||
|
||||
Only touch the files that your changes actually affect. The most common ones are:
|
||||
|
||||
- `README.md`
|
||||
- `Design_Doc/Spec.md`
|
||||
- `Design_Doc/Operations.md`
|
||||
- `Design_Doc/Operations.md`
|
||||
- `Design_Doc/Tech_Desc.md`
|
||||
|
||||
## How should you update them?
|
||||
Just follow the style and purpose each file already has.
|
||||
|
||||
Just follow the style and purpose each file already has.
|
||||
(You can check the "Documentation Overview" part in `README.md` if you're not sure.)
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ CRITICAL RULE for `Design_Doc/Tech_Desc.md` - The "AI Prompt" Section
|
||||
|
||||
**The "AI Prompt" section at the end of `Design_Doc/Tech_Desc.md` must NEVER be modified, deleted, or rewritten by you.**
|
||||
|
||||
### What is the "AI Prompt" section?
|
||||
- It is the section that starts with `# AI Prompt` (level-1 heading)
|
||||
- It is always located at the **very end** of `Design_Doc/Tech_Desc.md`
|
||||
- It contains sub-sections like `## AI Prompt habit as a reminder`, `## Script Code`, `## Doc.`
|
||||
- It contains **task history and instructions from previous sessions** written by the user
|
||||
|
||||
### Why must you NOT touch it?
|
||||
- This section is **written and maintained by the user only**
|
||||
- It contains **accumulated task history** that the user wants to preserve
|
||||
- It is **not documentation** about the project - it is a **working log of tasks**
|
||||
- Other team members (including the user) will update this section manually when needed
|
||||
|
||||
### What you MUST do:
|
||||
✅ **Leave the "AI Prompt" section completely unchanged**
|
||||
✅ **Keep the exact content** that is currently there (do not revert, do not update, do not clean up)
|
||||
✅ **Only edit the technical documentation sections** that come BEFORE the "AI Prompt" section
|
||||
✅ **Ensure "AI Prompt" stays at the end** of the file (do not move it)
|
||||
|
||||
### What you MUST NOT do:
|
||||
❌ **Do NOT delete** the "AI Prompt" section
|
||||
❌ **Do NOT modify** any words, sentences, or formatting inside "AI Prompt"
|
||||
❌ **Do NOT revert** it to an older version
|
||||
❌ **Do NOT clean up** or reorganize its content
|
||||
❌ **Do NOT add** new task results to it (the user does this manually)
|
||||
❌ **Do NOT remove** task history from it
|
||||
|
||||
### Simple rule to remember:
|
||||
> **Everything before `# AI Prompt` = Your responsibility to update**
|
||||
> **Everything from `# AI Prompt` to the end = User's responsibility, DO NOT TOUCH**
|
||||
|
||||
---
|
||||
|
||||
Thank you so much for helping keep the documents accurate and the task history preserved — it makes a big difference to the whole team!
|
||||
BACKUP before you touch the above mentioned documents:
|
||||
- you make a doc. backup folder `Design_Doc/bck/` if not exist, then before any your touch of documents, such as: `Design_Doc/Tech_Desc.md`, you copy that current version into eg. `Design_Doc/bck/Design_Doc/Tech_Desc_YYYYMMDDHHMMSS.md`. The other document files as same approach by keeping the path and file name with Date and Time at file name into backup folder.
|
||||
@ -34,6 +34,12 @@ INSTANCE_NAME="traefik" # Default container name, can be overridden in .env.rvs
|
||||
DOCKER_NETWORK="${DOCKER_NETWORK}" # Default Docker network name, can be overridden in .env.rvs-prxy
|
||||
DASHBOARD_PORT="18080" # Default dashboard port, can be overridden in .env.rvs-prxy
|
||||
|
||||
# Extra entrypoints beyond default web (:80/tcp) and websecure (:443/tcp)
|
||||
# Format: comma-separated list of name=address
|
||||
# Example: "quic=:443/udp, custom-tcp=:8888"
|
||||
# Address format: :port or :port/protocol (default protocol: tcp)
|
||||
EXTRA_ENTRYPOINTS=""
|
||||
|
||||
# Target server variables (for Traefik mode - now running locally)
|
||||
LOCAL_SERVICE_NAME=""
|
||||
LOCAL_SERVICE_PORT=""
|
||||
@ -411,14 +417,16 @@ interactive_setup_wizard() {
|
||||
fi
|
||||
done
|
||||
|
||||
# Get service port
|
||||
echo -e "Enter your application's internal port:" >&2
|
||||
# Get service port(s)
|
||||
echo -e "Enter your application's internal port(s):" >&2
|
||||
echo -e " Single port: 80" >&2
|
||||
echo -e " Multiple: 80,4443/udp (comma-separated, protocol defaults to tcp)" >&2
|
||||
while [ -z "$LOCAL_SERVICE_PORT" ]; do
|
||||
LOCAL_SERVICE_PORT=$(read_terminal "Service port (default: 3000): " "3000")
|
||||
if [ -z "$LOCAL_SERVICE_PORT" ]; then
|
||||
LOCAL_SERVICE_PORT="3000"
|
||||
elif ! [[ "$LOCAL_SERVICE_PORT" =~ ^[0-9]+$ ]] || [ "$LOCAL_SERVICE_PORT" -lt 1 ] || [ "$LOCAL_SERVICE_PORT" -gt 65535 ]; then
|
||||
echo -e "${RED}Invalid port. Please enter a number between 1 and 65535.${NC}" >&2
|
||||
elif ! [[ "$LOCAL_SERVICE_PORT" =~ ^[0-9]+(/tcp|/udp)?(,[0-9]+(/tcp|/udp)?)*$ ]]; then
|
||||
echo -e "${RED}Invalid format. Use comma-separated port/protocol, e.g. 80 or 80,4443/udp${NC}" >&2
|
||||
LOCAL_SERVICE_PORT=""
|
||||
fi
|
||||
done
|
||||
@ -577,6 +585,16 @@ interactive_setup_wizard() {
|
||||
done
|
||||
save_to_env "LETSENCRYPT_EMAIL" "$LETSENCRYPT_EMAIL" ".env.rvs-prxy"
|
||||
echo "" >&2
|
||||
|
||||
# Extra entrypoints (e.g., QUIC)
|
||||
echo -e "Extra Traefik entrypoints (optional):" >&2
|
||||
echo -e " Format: name=:port/protocol (comma-separated)" >&2
|
||||
echo -e " Example: quic=:443/udp enables HTTP/3 QUIC on port 443/udp" >&2
|
||||
EXTRA_ENTRYPOINTS=$(read_terminal "Extra entrypoints (leave empty for none): " "")
|
||||
if [ -n "$EXTRA_ENTRYPOINTS" ]; then
|
||||
save_to_env "EXTRA_ENTRYPOINTS" "$EXTRA_ENTRYPOINTS" ".env.rvs-prxy"
|
||||
fi
|
||||
echo "" >&2
|
||||
|
||||
# Optional: Debug mode
|
||||
DEBUG_MODE=$(read_terminal "Enable debug mode? (y/N): " "n")
|
||||
@ -1433,6 +1451,63 @@ local_volume_copy() {
|
||||
docker run --rm -v "$volume:$dest" -v "$(dirname "$source"):/tmp/source:ro" alpine:latest cp -r "/tmp/source/$(basename "$source")" "$dest/"
|
||||
}
|
||||
|
||||
# Function to parse EXTRA_ENTRYPOINTS variable and produce entrypoint args and port mappings
|
||||
# Globals read: EXTRA_ENTRYPOINTS
|
||||
# Globals set: EXTRA_ENTRYPOINT_ARGS (array of --entrypoints.NAME.address=ADDRESS)
|
||||
# EXTRA_PORT_MAPPINGS (array of port mappings for docker-compose/docker-run)
|
||||
# EXTRA_COMPOSE_ENTRYPOINTS (string of YAML command entries for compose)
|
||||
# EXTRA_COMPOSE_PORTS (string of YAML port entries for compose)
|
||||
parse_extra_entrypoints() {
|
||||
EXTRA_ENTRYPOINT_ARGS=()
|
||||
EXTRA_PORT_MAPPINGS=()
|
||||
EXTRA_COMPOSE_ENTRYPOINTS=""
|
||||
EXTRA_COMPOSE_PORTS=""
|
||||
|
||||
if [ -z "$EXTRA_ENTRYPOINTS" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local saved_ifs="$IFS"
|
||||
IFS=','
|
||||
for entry in $EXTRA_ENTRYPOINTS; do
|
||||
IFS="$saved_ifs"
|
||||
entry=$(echo "$entry" | xargs)
|
||||
local name="${entry%%=*}"
|
||||
local address="${entry#*=}"
|
||||
address=$(echo "$address" | xargs)
|
||||
|
||||
# Determine port and protocol from address
|
||||
local port="${address#:}"
|
||||
local protocol="tcp"
|
||||
if [[ "$port" == *"/"* ]]; then
|
||||
protocol="${port#*/}"
|
||||
port="${port%%/*}"
|
||||
fi
|
||||
|
||||
# Build entrypoint arg
|
||||
EXTRA_ENTRYPOINT_ARGS+=("--entrypoints.${name}.address=${address}")
|
||||
|
||||
# Build port mapping
|
||||
if [ "$protocol" = "udp" ]; then
|
||||
EXTRA_PORT_MAPPINGS+=("${port}:${port}/udp")
|
||||
else
|
||||
EXTRA_PORT_MAPPINGS+=("${port}:${port}")
|
||||
fi
|
||||
|
||||
# Build compose YAML strings (with real newlines for heredoc expansion)
|
||||
EXTRA_COMPOSE_ENTRYPOINTS="${EXTRA_COMPOSE_ENTRYPOINTS} - '--entrypoints.${name}.address=${address}'
|
||||
"
|
||||
if [ "$protocol" = "udp" ]; then
|
||||
EXTRA_COMPOSE_PORTS="${EXTRA_COMPOSE_PORTS} - '${port}:${port}/udp'
|
||||
"
|
||||
else
|
||||
EXTRA_COMPOSE_PORTS="${EXTRA_COMPOSE_PORTS} - '${port}:${port}'
|
||||
"
|
||||
fi
|
||||
done
|
||||
IFS="$saved_ifs"
|
||||
}
|
||||
|
||||
# Function to check if Traefik container exists locally (Traefik Mode)
|
||||
check_traefik_container() {
|
||||
log_info "Checking if Traefik container exists locally..."
|
||||
@ -1464,8 +1539,33 @@ create_traefik_container_local() {
|
||||
|
||||
if [ -n "$container_exists" ] && [ "$container_exists" = "$TRAEFIK_CONTAINER_NAME" ]; then
|
||||
log_info "Traefik container '$TRAEFIK_CONTAINER_NAME' already exists locally"
|
||||
# Only update the config files in the volume, don't recreate the container
|
||||
log_info "Updating configuration in existing Traefik container..."
|
||||
|
||||
# Parse extra entrypoints and check if container needs recreation
|
||||
parse_extra_entrypoints
|
||||
if [ ${#EXTRA_ENTRYPOINT_ARGS[@]} -gt 0 ]; then
|
||||
# Check if entrypoints are already in the container
|
||||
local current_cmd
|
||||
current_cmd=$(docker inspect traefik --format='{{json .Config.Cmd}}' 2>/dev/null || echo "[]")
|
||||
local needs_recreate=false
|
||||
for ep_arg in "${EXTRA_ENTRYPOINT_ARGS[@]}"; do
|
||||
if ! echo "$current_cmd" | grep -qF -- "${ep_arg}"; then
|
||||
needs_recreate=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$needs_recreate" = true ]; then
|
||||
log_info "Extra entrypoints changed, recreating Traefik container to apply..."
|
||||
# Re-enter the creation path by removing the container reference
|
||||
docker stop traefik 2>/dev/null || true
|
||||
sleep 2
|
||||
docker rm traefik 2>/dev/null || true
|
||||
container_exists=""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$container_exists" ] && [ "$container_exists" = "$TRAEFIK_CONTAINER_NAME" ]; then
|
||||
log_info "Traefik container '$TRAEFIK_CONTAINER_NAME' exists, updating configuration..."
|
||||
|
||||
# Use specified Traefik version or default to v3.6.9
|
||||
local TRAEFIK_VER="${TRAEFIK_VER:-v3.6.9}"
|
||||
@ -1575,6 +1675,9 @@ EOF_HEREDOC
|
||||
docker run --rm -v "$TRAEFIK_VOLUME_NAME:/etc/traefik" -v "/tmp:/tmp" alpine:latest sh -c 'mkdir -p /etc/traefik/config /etc/traefik/acme && cp /tmp/traefik.yml /etc/traefik/config/traefik.yml && touch /etc/traefik/acme/acme.json && chmod 600 /etc/traefik/acme/acme.json'
|
||||
rm -f "$temp_config"
|
||||
|
||||
# Parse extra entrypoints for dynamic entrypoint/port configuration
|
||||
parse_extra_entrypoints
|
||||
|
||||
# Determine Docker version to decide if we need the version field
|
||||
local compose_version_field
|
||||
docker_version=$(docker --version | sed 's/Docker version //' | sed 's/,.*//')
|
||||
@ -1607,7 +1710,7 @@ services:
|
||||
- '--providers.docker.exposedbydefault=false'
|
||||
- '--entrypoints.web.address=:80'
|
||||
- '--entrypoints.websecure.address=:443'
|
||||
- '--certificatesresolvers.letsencrypt.acme.httpchallenge=true'
|
||||
${EXTRA_COMPOSE_ENTRYPOINTS} - '--certificatesresolvers.letsencrypt.acme.httpchallenge=true'
|
||||
- '--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web'
|
||||
- '--certificatesresolvers.letsencrypt.acme.email=$LETSENCRYPT_EMAIL'
|
||||
- '--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json'
|
||||
@ -1616,7 +1719,7 @@ services:
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
- "127.0.0.1:${DASHBOARD_PORT}:8080" # Bind dashboard to localhost only for security
|
||||
${EXTRA_COMPOSE_PORTS} - "127.0.0.1:${DASHBOARD_PORT}:8080" # Bind dashboard to localhost only for security
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- $TRAEFIK_VOLUME_NAME:/etc/traefik
|
||||
@ -1650,7 +1753,7 @@ services:
|
||||
- '--providers.docker.exposedbydefault=false'
|
||||
- '--entrypoints.web.address=:80'
|
||||
- '--entrypoints.websecure.address=:443'
|
||||
- '--certificatesresolvers.letsencrypt.acme.httpchallenge=true'
|
||||
${EXTRA_COMPOSE_ENTRYPOINTS} - '--certificatesresolvers.letsencrypt.acme.httpchallenge=true'
|
||||
- '--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web'
|
||||
- '--certificatesresolvers.letsencrypt.acme.email=$LETSENCRYPT_EMAIL'
|
||||
- '--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json'
|
||||
@ -1659,7 +1762,7 @@ services:
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
- "127.0.0.1:${DASHBOARD_PORT}:8080" # Bind dashboard to localhost only for security
|
||||
${EXTRA_COMPOSE_PORTS} - "127.0.0.1:${DASHBOARD_PORT}:8080" # Bind dashboard to localhost only for security
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- $TRAEFIK_VOLUME_NAME:/etc/traefik
|
||||
@ -1693,9 +1796,12 @@ EOF
|
||||
|
||||
# Start Traefik container using appropriate command
|
||||
$compose_cmd -f "$temp_compose" up -d
|
||||
cp "$temp_compose" /tmp/docker-compose.yml
|
||||
rm -f "$temp_compose"
|
||||
|
||||
log_info "Traefik container '$TRAEFIK_CONTAINER_NAME' is running locally using image: $TRAEFIK_IMAGE with volume: $TRAEFIK_VOLUME_NAME"
|
||||
log_info "Docker compose file saved to /tmp/docker-compose.yml for reference"
|
||||
log_info "Traefik dashboard: http://localhost:${DASHBOARD_PORT}/dashboard/"
|
||||
|
||||
# Wait a bit for the container to start
|
||||
sleep 3
|
||||
@ -1707,6 +1813,38 @@ EOF
|
||||
configure_traefik_local() {
|
||||
log_info "Configuring Traefik locally to connect to Application Service..."
|
||||
|
||||
# Parse LOCAL_SERVICE_PORT for multi-port support
|
||||
# Format: comma-separated port/protocol entries (protocol defaults to tcp)
|
||||
# Example: "80,4443/udp" → two ports: 80/tcp and 4443/udp
|
||||
local service_ports=()
|
||||
local service_protocols=()
|
||||
local first_tcp_port=""
|
||||
local saved_ifs="$IFS"
|
||||
local entry port protocol
|
||||
IFS=','
|
||||
for entry in $LOCAL_SERVICE_PORT; do
|
||||
IFS="$saved_ifs"
|
||||
entry=$(echo "$entry" | xargs)
|
||||
port="$entry"
|
||||
protocol="tcp"
|
||||
if [[ "$port" == *"/"* ]]; then
|
||||
protocol="${port#*/}"
|
||||
port="${port%%/*}"
|
||||
fi
|
||||
service_ports+=("$port")
|
||||
service_protocols+=("$protocol")
|
||||
if [ -z "$first_tcp_port" ] && [ "$protocol" = "tcp" ]; then
|
||||
first_tcp_port="$port"
|
||||
fi
|
||||
done
|
||||
IFS="$saved_ifs"
|
||||
|
||||
if [ -z "$first_tcp_port" ]; then
|
||||
log_error "No TCP port found in LOCAL_SERVICE_PORT ($LOCAL_SERVICE_PORT)"
|
||||
log_error "At least one TCP port is required for HTTP routing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if the target service is running
|
||||
local service_running
|
||||
service_running=$(docker ps --filter name="$LOCAL_SERVICE_NAME" --format '{{.Names}}' 2>/dev/null || true)
|
||||
@ -1785,7 +1923,7 @@ http:
|
||||
${LOCAL_SERVICE_NAME}-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: 'http://${LOCAL_SERVICE_NAME}:${LOCAL_SERVICE_PORT}'
|
||||
- url: 'http://${LOCAL_SERVICE_NAME}:${first_tcp_port}'
|
||||
passHostHeader: true
|
||||
EOF
|
||||
else
|
||||
@ -1811,11 +1949,71 @@ http:
|
||||
${LOCAL_SERVICE_NAME}-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: 'http://${LOCAL_SERVICE_NAME}:${LOCAL_SERVICE_PORT}'
|
||||
- url: 'http://${LOCAL_SERVICE_NAME}:${first_tcp_port}'
|
||||
passHostHeader: true
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Generate UDP/TCP router config for non-HTTP service ports (e.g., UDP relay ports)
|
||||
local udp_routers_added=false
|
||||
if [ ${#service_ports[@]} -gt 1 ]; then
|
||||
parse_extra_entrypoints
|
||||
|
||||
local udp_ep_ports=()
|
||||
local udp_ep_names=()
|
||||
for ep_arg in "${EXTRA_ENTRYPOINT_ARGS[@]}"; do
|
||||
local ep_name="${ep_arg#--entrypoints.}"
|
||||
ep_name="${ep_name%%.address=*}"
|
||||
local ep_addr="${ep_arg#*=}"
|
||||
if [[ "$ep_addr" == *"/udp" ]]; then
|
||||
local ep_port="${ep_addr#:}"
|
||||
ep_port="${ep_port%/udp}"
|
||||
udp_ep_ports+=("$ep_port")
|
||||
udp_ep_names+=("$ep_name")
|
||||
fi
|
||||
done
|
||||
|
||||
for i in "${!service_ports[@]}"; do
|
||||
if [ "${service_protocols[$i]}" = "udp" ]; then
|
||||
local container_port="${service_ports[$i]}"
|
||||
local matching_ep=""
|
||||
|
||||
for j in "${!udp_ep_ports[@]}"; do
|
||||
if [ "${udp_ep_ports[$j]}" = "$container_port" ]; then
|
||||
matching_ep="${udp_ep_names[$j]}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$matching_ep" ] && [ ${#udp_ep_names[@]} -gt 0 ]; then
|
||||
matching_ep="${udp_ep_names[0]}"
|
||||
fi
|
||||
|
||||
if [ -n "$matching_ep" ]; then
|
||||
cat >> "$temp_config" << EOF
|
||||
|
||||
udp:
|
||||
routers:
|
||||
${LOCAL_SERVICE_NAME}-${matching_ep}:
|
||||
entryPoints:
|
||||
- ${matching_ep}
|
||||
service: ${LOCAL_SERVICE_NAME}-udp
|
||||
services:
|
||||
${LOCAL_SERVICE_NAME}-udp:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- address: "${LOCAL_SERVICE_NAME}:${container_port}"
|
||||
EOF
|
||||
udp_routers_added=true
|
||||
log_info "Added UDP routing: entrypoint '${matching_ep}' → ${LOCAL_SERVICE_NAME}:${container_port}"
|
||||
else
|
||||
log_warn "UDP port ${container_port} in LOCAL_SERVICE_PORT has no matching EXTRA_ENTRYPOINTS entry"
|
||||
log_warn "Add e.g. EXTRA_ENTRYPOINTS=\"myudp=:${container_port}/udp\" to your .env file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Generate a unique configuration file name based on the domain and service name to avoid conflicts
|
||||
# Use the domain (without path) and service name to create a unique identifier
|
||||
local domain_base=$(echo "$DOMAIN_NAME" | cut -d'/' -f1)
|
||||
@ -2517,6 +2715,19 @@ add_traefik_entry_point() {
|
||||
log_info "Updating Traefik container with new entry point '${entry_point_name}'..."
|
||||
log_info "Using Let's Encrypt email: ${letsencrypt_email}"
|
||||
|
||||
# Parse extra entrypoints from config to include them in the recreation
|
||||
parse_extra_entrypoints
|
||||
|
||||
# Build extra entrypoint args and port mappings
|
||||
local extra_ep_args=""
|
||||
local extra_port_maps=""
|
||||
for ep_arg in "${EXTRA_ENTRYPOINT_ARGS[@]}"; do
|
||||
extra_ep_args="${extra_ep_args} ${ep_arg}"
|
||||
done
|
||||
for pm in "${EXTRA_PORT_MAPPINGS[@]}"; do
|
||||
extra_port_maps="${extra_port_maps} -p ${pm}"
|
||||
done
|
||||
|
||||
# Stop current container
|
||||
log_info "Stopping Traefik container..."
|
||||
docker stop traefik 2>/dev/null || true
|
||||
@ -2525,7 +2736,7 @@ add_traefik_entry_point() {
|
||||
# Remove old container (keep volume)
|
||||
docker rm traefik 2>/dev/null || true
|
||||
|
||||
# Recreate container with all standard entry points plus the new one
|
||||
# Recreate container with all standard entry points plus the new one and extras
|
||||
docker run -d \
|
||||
--name ${INSTANCE_NAME} \
|
||||
--restart=unless-stopped \
|
||||
@ -2534,6 +2745,7 @@ add_traefik_entry_point() {
|
||||
-p 443:443 \
|
||||
-p "127.0.0.1:${DASHBOARD_PORT}:8080" \
|
||||
-p "${port}:${port}" \
|
||||
${extra_port_maps} \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
-v traefik_data:/etc/traefik \
|
||||
"${current_image}" \
|
||||
@ -2543,6 +2755,7 @@ add_traefik_entry_point() {
|
||||
--entrypoints.web.address=:80 \
|
||||
--entrypoints.websecure.address=:443 \
|
||||
"--entrypoints.${entry_point_name}.address=:${port}" \
|
||||
${extra_ep_args} \
|
||||
--certificatesresolvers.letsencrypt.acme.httpchallenge=true \
|
||||
--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web \
|
||||
"--certificatesresolvers.letsencrypt.acme.email=${letsencrypt_email}" \
|
||||
@ -2575,6 +2788,29 @@ setup_new_domain() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if extra entrypoints need to be applied to the Traefik container
|
||||
if [ -n "$EXTRA_ENTRYPOINTS" ]; then
|
||||
parse_extra_entrypoints
|
||||
if [ ${#EXTRA_ENTRYPOINT_ARGS[@]} -gt 0 ]; then
|
||||
local current_cmd
|
||||
current_cmd=$(docker inspect traefik --format='{{json .Config.Cmd}}' 2>/dev/null || echo "[]")
|
||||
local needs_recreate=false
|
||||
for ep_arg in "${EXTRA_ENTRYPOINT_ARGS[@]}"; do
|
||||
if ! echo "$current_cmd" | grep -qF -- "${ep_arg}"; then
|
||||
needs_recreate=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$needs_recreate" = true ]; then
|
||||
log_info "Extra entrypoints changed, recreating Traefik container to apply..."
|
||||
docker stop traefik 2>/dev/null || true
|
||||
sleep 2
|
||||
docker rm traefik 2>/dev/null || true
|
||||
create_traefik_container_local
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if Application Service exists locally
|
||||
if ! check_app_service_local; then
|
||||
log_error "Application Service not found locally. Please ensure the service '$LOCAL_SERVICE_NAME' is running."
|
||||
@ -2847,7 +3083,7 @@ Environment Variables (in .env.rvs-prxy file):
|
||||
|
||||
Traefik Mode Variables (FRP=false):
|
||||
LOCAL_SERVICE_NAME Name of the local Docker application container
|
||||
LOCAL_SERVICE_PORT Port number of the local application service
|
||||
LOCAL_SERVICE_PORT Port(s) of the local app service, e.g. 80 or 80,4443/udp
|
||||
DOMAIN_NAME Domain name for reverse proxy routing
|
||||
TRAEFIK_VER Traefik version (default: v3.6.9)
|
||||
LETSENCRYPT_EMAIL Email for Let's Encrypt certificates
|
||||
@ -2983,10 +3219,10 @@ manage_configurations() {
|
||||
if [ -n "$domain_name" ] && [ -n "$service_name" ]; then
|
||||
log_info "Removing domain config from Traefik: ${domain_name}"
|
||||
# Remove the dynamic config file from Traefik volume
|
||||
# Use specific pattern: app-service-{domain}-{service}.yml
|
||||
# This avoids accidentally removing port config files that share the same service name
|
||||
# Must use same domain_base logic as configure_traefik_local():
|
||||
# cut -d'/' -f1 (not dots-to-dashes) to match actual filename
|
||||
local domain_base
|
||||
domain_base=$(echo "$domain_name" | sed 's/\./-/g')
|
||||
domain_base=$(echo "$domain_name" | cut -d'/' -f1)
|
||||
docker run --rm -v "traefik_data:/etc/traefik" alpine:latest sh -c "rm -f /etc/traefik/config/app-service-${domain_base}-${service_name}.yml 2>/dev/null" || true
|
||||
fi
|
||||
|
||||
@ -3040,17 +3276,39 @@ manage_configurations() {
|
||||
local existing_entrypoints
|
||||
existing_entrypoints=$(echo "$current_cmd" | grep -o 'entrypoints\.[a-zA-Z0-9_-]*\.address=[^"]*' | tr -d '"' || echo "")
|
||||
|
||||
# Build new entry point list excluding removed ports
|
||||
local new_entrypoint_args="--entrypoints.web.address=:80 --entrypoints.websecure.address=:443"
|
||||
local new_port_mappings="-p 80:80 -p 443:443 -p 127.0.0.1:${DASHBOARD_PORT}:8080"
|
||||
# Also include extra entrypoints from EXTRA_ENTRYPOINTS config
|
||||
parse_extra_entrypoints
|
||||
local extra_ep_args=""
|
||||
local extra_port_maps=""
|
||||
for ep_arg in "${EXTRA_ENTRYPOINT_ARGS[@]}"; do
|
||||
extra_ep_args="${extra_ep_args} ${ep_arg}"
|
||||
done
|
||||
for pm in "${EXTRA_PORT_MAPPINGS[@]}"; do
|
||||
extra_port_maps="${extra_port_maps} -p ${pm}"
|
||||
done
|
||||
|
||||
# Parse existing entry points
|
||||
# Build new entry point list excluding removed ports
|
||||
local new_entrypoint_args="--entrypoints.web.address=:80 --entrypoints.websecure.address=:443 ${extra_ep_args}"
|
||||
local new_port_mappings="-p 80:80 -p 443:443 -p 127.0.0.1:${DASHBOARD_PORT}:8080 ${extra_port_maps}"
|
||||
|
||||
# Parse existing entry points (skip web/websecure and extra entries already covered)
|
||||
while IFS= read -r ep; do
|
||||
if [ -n "$ep" ]; then
|
||||
local ep_name
|
||||
ep_name=$(echo "$ep" | sed 's/entrypoints\.\(.*\)\.address=.*/\1/')
|
||||
local ep_address
|
||||
ep_address=$(echo "$ep" | sed 's/entrypoints\.[a-zA-Z0-9_-]*\.address=//')
|
||||
local ep_port
|
||||
ep_port=$(echo "$ep" | grep -o ':[0-9]*' | sed 's/://')
|
||||
ep_port=$(echo "$ep_address" | sed 's/^://' | sed 's/\/.*//')
|
||||
|
||||
# Check if this is already covered by extra entrypoints
|
||||
local already_covered=false
|
||||
for ep_arg in "${EXTRA_ENTRYPOINT_ARGS[@]}"; do
|
||||
if echo "$ep_arg" | grep -q "entrypoints.${ep_name}.address"; then
|
||||
already_covered=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Check if this port is being removed
|
||||
local should_skip=false
|
||||
@ -3061,9 +3319,14 @@ manage_configurations() {
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$should_skip" = false ] && [ "$ep_name" != "web" ] && [ "$ep_name" != "websecure" ]; then
|
||||
new_entrypoint_args="${new_entrypoint_args} --entrypoints.${ep_name}.address=:${ep_port}"
|
||||
new_port_mappings="${new_port_mappings} -p ${ep_port}:${ep_port}"
|
||||
if [ "$should_skip" = false ] && [ "$ep_name" != "web" ] && [ "$ep_name" != "websecure" ] && [ "$already_covered" = false ]; then
|
||||
new_entrypoint_args="${new_entrypoint_args} --entrypoints.${ep_name}.address=${ep_address}"
|
||||
# Handle protocol suffix in port mapping
|
||||
if [[ "$ep_address" == *"/udp" ]]; then
|
||||
new_port_mappings="${new_port_mappings} -p ${ep_port}:${ep_port}/udp"
|
||||
else
|
||||
new_port_mappings="${new_port_mappings} -p ${ep_port}:${ep_port}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done <<< "$existing_entrypoints"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user