docker
This commit is contained in:
161
DOCKER.md
Normal file
161
DOCKER.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Running with Docker
|
||||
|
||||
This app is packaged as a small Docker stack and is designed to run **behind
|
||||
the host's existing Nginx**, connecting to the **host's existing MySQL** (where
|
||||
both `sppm_taklimat` and the external `sppm` database live).
|
||||
|
||||
```
|
||||
Internet
|
||||
│
|
||||
▼
|
||||
host nginx (taklimatspr.apps.mbip.my, port 80) ← reverse proxy
|
||||
│ proxy_pass 127.0.0.1:8080
|
||||
▼
|
||||
web container (nginx) ──fastcgi──► app container (php-fpm 8.4)
|
||||
│
|
||||
▼ host.docker.internal:3306
|
||||
host MySQL (sppm_taklimat, sppm)
|
||||
```
|
||||
|
||||
Containers:
|
||||
|
||||
| Service | Image | Role |
|
||||
|-------------|--------------------|-------------------------------------------------|
|
||||
| `web` | `taklimatspr-web` | nginx, serves static assets, proxies PHP. Binds `127.0.0.1:8080`. |
|
||||
| `app` | `taklimatspr-app` | PHP 8.4-FPM. Runs migrations on boot. |
|
||||
| `queue` | `taklimatspr-app` | `queue:work` (database queue). |
|
||||
| `scheduler` | `taklimatspr-app` | `schedule:work` (Laravel scheduler). |
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites on the host (Ubuntu 24.04)
|
||||
|
||||
- Docker Engine + Compose plugin:
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker.io docker-compose-v2
|
||||
sudo systemctl enable --now docker
|
||||
```
|
||||
- The existing Nginx and MySQL services already installed on the host.
|
||||
|
||||
---
|
||||
|
||||
## 1. Configure host MySQL to accept connections from containers
|
||||
|
||||
On Linux, `host.docker.internal` resolves to the Docker bridge gateway (e.g.
|
||||
`172.17.0.1`) via the `host-gateway` mapping already set in
|
||||
`docker-compose.yml`. The host MySQL must accept connections on that interface.
|
||||
|
||||
**a) Listen on all interfaces** — edit
|
||||
`/etc/mysql/mysql.conf.d/mysqld.cnf`:
|
||||
|
||||
```ini
|
||||
bind-address = 0.0.0.0
|
||||
```
|
||||
Then restart: `sudo systemctl restart mysql`.
|
||||
|
||||
**b) Allow the app user from the Docker subnet.** Prefer a dedicated user over
|
||||
`root` (and then set the credentials in `.env.docker`):
|
||||
|
||||
```sql
|
||||
CREATE USER 'taklimat'@'172.%' IDENTIFIED BY 'CHANGE_ME_STRONG';
|
||||
GRANT ALL PRIVILEGES ON sppm_taklimat.* TO 'taklimat'@'172.%';
|
||||
GRANT SELECT ON sppm.* TO 'taklimat'@'172.%';
|
||||
FLUSH PRIVILEGES;
|
||||
```
|
||||
|
||||
Make sure the `sppm_taklimat` database exists:
|
||||
```sql
|
||||
CREATE DATABASE IF NOT EXISTS sppm_taklimat
|
||||
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
```
|
||||
|
||||
**c) Firewall** — if `ufw` is active, allow MySQL from the Docker subnet only:
|
||||
```bash
|
||||
sudo ufw allow from 172.16.0.0/12 to any port 3306 proto tcp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Set the container environment
|
||||
|
||||
`.env.docker` holds the runtime config (it is git-ignored because it contains
|
||||
credentials). Review it and update at least:
|
||||
|
||||
- `DB_USERNAME` / `DB_PASSWORD` and `SPPM_DB_*` to match step 1b.
|
||||
- `APP_KEY` is pre-filled; to rotate it run
|
||||
`docker compose run --rm app php artisan key:generate --show` and paste the
|
||||
result.
|
||||
|
||||
`DB_HOST` / `SPPM_DB_HOST` are already set to `host.docker.internal` — leave them.
|
||||
|
||||
---
|
||||
|
||||
## 3. Build and start
|
||||
|
||||
```bash
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
On boot the `app` container waits for MySQL, caches config/routes/views, runs
|
||||
`php artisan migrate --force`, and starts php-fpm. Check it:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs -f app
|
||||
curl -I http://127.0.0.1:8080 # should return HTTP 200/302
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Point host Nginx at the stack
|
||||
|
||||
```bash
|
||||
sudo cp docker/host-nginx/taklimatspr.apps.mbip.my.conf \
|
||||
/etc/nginx/sites-available/taklimatspr.apps.mbip.my
|
||||
sudo ln -s /etc/nginx/sites-available/taklimatspr.apps.mbip.my \
|
||||
/etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
The app should now be reachable at <http://taklimatspr.apps.mbip.my>.
|
||||
|
||||
---
|
||||
|
||||
## Common operations
|
||||
|
||||
```bash
|
||||
# Tail logs
|
||||
docker compose logs -f app web queue
|
||||
|
||||
# Artisan / composer inside the app container
|
||||
docker compose exec app php artisan about
|
||||
docker compose exec app php artisan tinker
|
||||
|
||||
# Re-cache config after editing .env.docker
|
||||
docker compose exec app php artisan config:cache
|
||||
|
||||
# Rebuild & redeploy after pulling new code
|
||||
docker compose build && docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes & limitations
|
||||
|
||||
- **Static uploads:** `web` (nginx) serves a baked copy of `public/`. Files
|
||||
written at runtime to `storage/app/public` (the `public` disk + `storage:link`)
|
||||
live only in the `app` container and are **not** served by nginx. The current
|
||||
app renders QR codes as inline SVG, so this isn't needed today — but if you
|
||||
later serve uploaded files via `/storage`, switch `web` to share the app's
|
||||
public/storage via a volume.
|
||||
- **Persistent data:** the named volume `app_storage` keeps logs, compiled
|
||||
views, and `storage/app` across restarts. Application/session data lives in
|
||||
MySQL.
|
||||
- **TLS:** terminate HTTPS at the host Nginx (e.g. with certbot) if/when the
|
||||
domain moves to `https://`. Then add `SESSION_SECURE_COOKIE=true` and
|
||||
`APP_URL=https://...` in `.env.docker`.
|
||||
- **HEALTHCHECK / scheduler:** there are currently no scheduled commands, but
|
||||
the `scheduler` service is included so future `app/Console` schedules work
|
||||
without changing the stack. Remove it if you prefer.
|
||||
Reference in New Issue
Block a user