docs: add podman deployment guide
New docs/DEPLOY.md covering container image, compose config, volume mounts, host networking, operations, and troubleshooting. Updated README, INSTALL, CHEATSHEET, and DEBUG to reference it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
14
README.md
14
README.md
@@ -52,12 +52,26 @@ IRC Client(s) --> [bouncer:6667] --> Router --> [SOCKS5:1080] --> IRC Server(s)
|
||||
4. Joins configured channels
|
||||
5. Clients connect to bouncer, receive backlog replay and channel state
|
||||
|
||||
## Podman Deployment
|
||||
|
||||
```bash
|
||||
cp config/bouncer.example.toml config/bouncer.toml
|
||||
$EDITOR config/bouncer.toml
|
||||
|
||||
make build
|
||||
make up
|
||||
make logs
|
||||
```
|
||||
|
||||
See [docs/DEPLOY.md](docs/DEPLOY.md) for full container documentation.
|
||||
|
||||
## Documentation
|
||||
|
||||
| Document | Description |
|
||||
|----------|-------------|
|
||||
| [docs/INSTALL.md](docs/INSTALL.md) | Prerequisites and setup |
|
||||
| [docs/USAGE.md](docs/USAGE.md) | Comprehensive guide |
|
||||
| [docs/DEPLOY.md](docs/DEPLOY.md) | Podman container deployment |
|
||||
| [docs/CHEATSHEET.md](docs/CHEATSHEET.md) | Quick reference |
|
||||
| [docs/DEBUG.md](docs/DEBUG.md) | Troubleshooting |
|
||||
|
||||
|
||||
@@ -9,6 +9,21 @@ bouncer --version # version
|
||||
bouncer --help # help
|
||||
```
|
||||
|
||||
## Podman
|
||||
|
||||
```bash
|
||||
make build # build container image
|
||||
make up # podman-compose up -d
|
||||
make down # podman-compose down
|
||||
make logs # podman logs -f bouncer
|
||||
```
|
||||
|
||||
```bash
|
||||
podman logs bouncer 2>&1 | grep -E 'INFO|WARN' # filtered logs
|
||||
podman exec -it bouncer bash # shell into container
|
||||
podman ps --filter name=bouncer # status
|
||||
```
|
||||
|
||||
## Develop
|
||||
|
||||
```bash
|
||||
|
||||
@@ -123,3 +123,41 @@ bouncer -c config/bouncer.toml -v 2>&1 | grep -v aiosqlite
|
||||
```
|
||||
|
||||
The `grep -v aiosqlite` filters out noisy SQLite debug lines.
|
||||
|
||||
## Container Debugging
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
podman logs -f bouncer # follow
|
||||
podman logs --tail 50 bouncer # last 50 lines
|
||||
podman logs bouncer 2>&1 | grep -E 'INFO|WARN' # important only
|
||||
podman logs bouncer 2>&1 | grep -v aiosqlite # filter sqlite noise
|
||||
```
|
||||
|
||||
### No log output from container
|
||||
|
||||
Two things must be set:
|
||||
|
||||
1. `PYTHONUNBUFFERED=1` in the Containerfile
|
||||
2. `logging: driver: k8s-file` in compose.yaml
|
||||
|
||||
Verify log driver:
|
||||
|
||||
```bash
|
||||
podman inspect bouncer --format '{{.HostConfig.LogConfig.Type}}'
|
||||
```
|
||||
|
||||
### Container exits immediately
|
||||
|
||||
```bash
|
||||
podman logs bouncer
|
||||
```
|
||||
|
||||
Likely causes: missing config file, SOCKS5 proxy not running, TOML syntax error.
|
||||
|
||||
### Shell into running container
|
||||
|
||||
```bash
|
||||
podman exec -it bouncer bash
|
||||
```
|
||||
|
||||
234
docs/DEPLOY.md
Normal file
234
docs/DEPLOY.md
Normal file
@@ -0,0 +1,234 @@
|
||||
# Podman Deployment
|
||||
|
||||
## Overview
|
||||
|
||||
The bouncer runs in a rootless podman container with host networking. Source code
|
||||
and configuration are mounted as volumes -- the container image only contains
|
||||
Python and pip dependencies.
|
||||
|
||||
```
|
||||
Container (bouncer)
|
||||
|- python:3.12-slim + deps
|
||||
|- /app/src <- ./src (read-only volume)
|
||||
|- /data <- ./config (read-write volume)
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `podman` and `podman-compose`
|
||||
- SOCKS5 proxy reachable at `127.0.0.1:1080` (host network)
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
podman --version
|
||||
podman-compose --version
|
||||
ss -tlnp | grep 1080
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
cd ~/git/bouncer
|
||||
|
||||
# create config from template
|
||||
cp config/bouncer.example.toml config/bouncer.toml
|
||||
$EDITOR config/bouncer.toml
|
||||
|
||||
# build and start
|
||||
make build
|
||||
make up
|
||||
|
||||
# check logs
|
||||
make logs
|
||||
```
|
||||
|
||||
## Container Image
|
||||
|
||||
The image installs only runtime dependencies. Source code is not baked in.
|
||||
|
||||
```bash
|
||||
# build
|
||||
podman build -t bouncer -f Containerfile .
|
||||
|
||||
# rebuild after dependency changes
|
||||
podman build --no-cache -t bouncer -f Containerfile .
|
||||
```
|
||||
|
||||
### Containerfile
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.12-slim
|
||||
WORKDIR /app
|
||||
RUN pip install --no-cache-dir \
|
||||
"python-socks[asyncio]>=2.4" \
|
||||
"aiosqlite>=0.19"
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONPATH=/app/src
|
||||
VOLUME /app/src
|
||||
VOLUME /data
|
||||
ENTRYPOINT ["python", "-m", "bouncer"]
|
||||
CMD ["-c", "/data/bouncer.toml"]
|
||||
```
|
||||
|
||||
## Compose
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bouncer:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Containerfile
|
||||
container_name: bouncer
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
logging:
|
||||
driver: k8s-file
|
||||
volumes:
|
||||
- ./src:/app/src:Z,ro
|
||||
- ./config:/data:Z
|
||||
command: ["-c", "/data/bouncer.toml", "-v"]
|
||||
```
|
||||
|
||||
### Volume Mounts
|
||||
|
||||
| Host Path | Container Path | Mode | Purpose |
|
||||
|-----------|---------------|------|---------|
|
||||
| `./src` | `/app/src` | read-only | Python source code |
|
||||
| `./config` | `/data` | read-write | Config + SQLite backlog |
|
||||
|
||||
The `:Z` suffix applies the correct SELinux context for rootless podman.
|
||||
|
||||
### Host Networking
|
||||
|
||||
`network_mode: host` is required because the bouncer needs access to:
|
||||
|
||||
- `127.0.0.1:1080` -- SOCKS5 proxy (outbound IRC connections)
|
||||
- `127.0.0.1:6667` -- client listen port (inbound IRC clients)
|
||||
|
||||
### Log Driver
|
||||
|
||||
`k8s-file` is used instead of the default `journald` driver to ensure
|
||||
`podman logs` works reliably in rootless mode.
|
||||
|
||||
## Operations
|
||||
|
||||
### Start / Stop / Restart
|
||||
|
||||
```bash
|
||||
podman-compose up -d # start (detached)
|
||||
podman-compose down # stop and remove
|
||||
podman-compose restart # restart
|
||||
```
|
||||
|
||||
Or with make targets:
|
||||
|
||||
```bash
|
||||
make up # podman-compose up -d
|
||||
make down # podman-compose down
|
||||
```
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
podman logs -f bouncer # follow all logs
|
||||
podman logs --tail 50 bouncer # last 50 lines
|
||||
podman logs bouncer 2>&1 | grep -v aiosqlite # filter sqlite noise
|
||||
podman logs bouncer 2>&1 | grep -E 'INFO|WARN' # important events only
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```bash
|
||||
make logs # podman logs -f bouncer
|
||||
```
|
||||
|
||||
### Status
|
||||
|
||||
```bash
|
||||
podman ps --filter name=bouncer
|
||||
podman inspect bouncer --format '{{.State.Status}}'
|
||||
```
|
||||
|
||||
### Shell Access
|
||||
|
||||
```bash
|
||||
podman exec -it bouncer bash
|
||||
podman exec bouncer python -c "import bouncer; print(bouncer.__version__)"
|
||||
```
|
||||
|
||||
### Inspect Backlog
|
||||
|
||||
The SQLite database is stored in the config volume:
|
||||
|
||||
```bash
|
||||
sqlite3 config/bouncer.db "SELECT network, COUNT(*) FROM messages GROUP BY network;"
|
||||
```
|
||||
|
||||
## Updating
|
||||
|
||||
After pulling new code:
|
||||
|
||||
```bash
|
||||
git pull
|
||||
podman-compose down
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
The image only needs rebuilding if dependencies change:
|
||||
|
||||
```bash
|
||||
podman-compose down
|
||||
make build
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container exits immediately
|
||||
|
||||
Check logs for config or proxy errors:
|
||||
|
||||
```bash
|
||||
podman logs bouncer
|
||||
```
|
||||
|
||||
Common causes:
|
||||
|
||||
- Missing `config/bouncer.toml` (not mounted)
|
||||
- SOCKS5 proxy not running on host
|
||||
- Invalid TOML syntax
|
||||
|
||||
### No log output
|
||||
|
||||
Verify `PYTHONUNBUFFERED=1` is set in the Containerfile and the log driver
|
||||
is `k8s-file`:
|
||||
|
||||
```bash
|
||||
podman inspect bouncer --format '{{.HostConfig.LogConfig.Type}}'
|
||||
```
|
||||
|
||||
### Container can't reach SOCKS5 proxy
|
||||
|
||||
`network_mode: host` must be set. Verify the proxy is listening:
|
||||
|
||||
```bash
|
||||
ss -tlnp | grep 1080
|
||||
```
|
||||
|
||||
### Permission denied on volumes
|
||||
|
||||
The `:Z` suffix handles SELinux relabeling. If issues persist:
|
||||
|
||||
```bash
|
||||
podman unshare ls -la config/
|
||||
```
|
||||
|
||||
### Rebuild from scratch
|
||||
|
||||
```bash
|
||||
podman-compose down
|
||||
podman rmi bouncer
|
||||
podman build --no-cache -t bouncer -f Containerfile .
|
||||
podman-compose up -d
|
||||
```
|
||||
@@ -5,7 +5,17 @@
|
||||
- Python 3.10+
|
||||
- SOCKS5 proxy running on `127.0.0.1:1080`
|
||||
|
||||
## Setup
|
||||
## Container Deployment (Recommended)
|
||||
|
||||
See [DEPLOY.md](DEPLOY.md) for full podman/container instructions.
|
||||
|
||||
```bash
|
||||
cp config/bouncer.example.toml config/bouncer.toml
|
||||
$EDITOR config/bouncer.toml
|
||||
make build && make up
|
||||
```
|
||||
|
||||
## Local Setup
|
||||
|
||||
```bash
|
||||
cd ~/git/bouncer
|
||||
|
||||
Reference in New Issue
Block a user