postgres service for external use
This commit is contained in:
commit
7d1acd8469
|
|
@ -0,0 +1,13 @@
|
|||
# PostgreSQL Configuration
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=postgres_secure_password
|
||||
POSTGRES_DB=postgres
|
||||
|
||||
# Backup Configuration
|
||||
BACKUP_DIR=./backups
|
||||
BACKUP_RETENTION_DAYS=7
|
||||
|
||||
# Resource Limits (for container configuration)
|
||||
# Recommended: 2 CPU cores, 4GB RAM minimum
|
||||
POSTGRES_CPU_LIMIT=2
|
||||
POSTGRES_MEMORY_LIMIT=4G
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
# Makefile for PostgreSQL Service
|
||||
|
||||
.PHONY: setup start stop restart status logs backup restore monitor clean help
|
||||
|
||||
# Default environment
|
||||
ENV ?= dev
|
||||
|
||||
help:
|
||||
@echo "PostgreSQL Service Management"
|
||||
@echo "============================"
|
||||
@echo "Available commands:"
|
||||
@echo " make setup - Setup the PostgreSQL service (make scripts executable)"
|
||||
@echo " make start - Start the PostgreSQL service"
|
||||
@echo " make stop - Stop the PostgreSQL service"
|
||||
@echo " make restart - Restart the PostgreSQL service"
|
||||
@echo " make status - Check the status of the PostgreSQL service"
|
||||
@echo " make logs - View the logs of the PostgreSQL service"
|
||||
@echo " make backup - Create a backup of the PostgreSQL database"
|
||||
@echo " make restore - Restore a backup of the PostgreSQL database"
|
||||
@echo " make monitor - Monitor the PostgreSQL service"
|
||||
@echo " make clean - Remove all containers, volumes, and networks"
|
||||
@echo ""
|
||||
@echo "Environment options:"
|
||||
@echo " make start ENV=dev - Start with development configuration (default)"
|
||||
@echo " make start ENV=staging - Start with staging configuration"
|
||||
@echo " make start ENV=production - Start with production configuration"
|
||||
|
||||
setup:
|
||||
@echo "Setting up PostgreSQL service..."
|
||||
@cd scripts && ./setup.sh
|
||||
|
||||
start:
|
||||
@echo "Starting PostgreSQL service with $(ENV) environment..."
|
||||
ifeq ($(ENV), dev)
|
||||
@docker-compose up -d
|
||||
else
|
||||
@docker-compose -f docker-compose.yaml -f environments/$(ENV).yaml up -d
|
||||
endif
|
||||
@echo "PostgreSQL service started."
|
||||
|
||||
stop:
|
||||
@echo "Stopping PostgreSQL service..."
|
||||
ifeq ($(ENV), dev)
|
||||
@docker-compose down
|
||||
else
|
||||
@docker-compose -f docker-compose.yaml -f environments/$(ENV).yaml down
|
||||
endif
|
||||
@echo "PostgreSQL service stopped."
|
||||
|
||||
restart: stop start
|
||||
|
||||
status:
|
||||
@echo "PostgreSQL service status:"
|
||||
@docker-compose ps
|
||||
|
||||
logs:
|
||||
@echo "PostgreSQL service logs:"
|
||||
@docker-compose logs -f postgres
|
||||
|
||||
backup:
|
||||
@echo "Creating PostgreSQL backup..."
|
||||
@cd scripts && ./backup.sh
|
||||
|
||||
restore:
|
||||
@echo "Restoring PostgreSQL backup..."
|
||||
@cd scripts && ./restore.sh $(BACKUP)
|
||||
|
||||
monitor:
|
||||
@echo "Monitoring PostgreSQL service..."
|
||||
@cd scripts && ./monitor.sh
|
||||
|
||||
clean:
|
||||
@echo "Cleaning up PostgreSQL service..."
|
||||
@docker-compose down -v --remove-orphans
|
||||
@echo "PostgreSQL service cleaned up."
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
# PostgreSQL Production Setup for Proxmox LXC Container
|
||||
|
||||
This repository contains a production-ready PostgreSQL setup using Docker Compose, designed to run on a Proxmox LXC container.
|
||||
|
||||
## Overview
|
||||
|
||||
The configuration includes:
|
||||
|
||||
- PostgreSQL 15 with optimized configuration
|
||||
- Persistent data storage
|
||||
- Security features
|
||||
- Health checks
|
||||
- Resource limits
|
||||
- Backup and restore capabilities
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Proxmox VE with LXC container support
|
||||
- Docker and Docker Compose installed on the LXC container
|
||||
- Proper network configuration in Proxmox
|
||||
|
||||
## Configuration Details
|
||||
|
||||
### docker-compose.yml Explained
|
||||
|
||||
```yaml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15 # Using PostgreSQL 15
|
||||
container_name: postgres
|
||||
restart: always # Ensures PostgreSQL restarts automatically
|
||||
environment:
|
||||
# Environment variables for authentication
|
||||
- POSTGRES_USER=${POSTGRES_USER:-postgres} # Default: postgres
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} # Default: password
|
||||
- POSTGRES_DB=${POSTGRES_DB:-postgres} # Default: postgres
|
||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
# Persistent data storage
|
||||
- postgres_data:/var/lib/postgresql/data # Database files
|
||||
- ./config/postgres.conf:/etc/postgresql/postgresql.conf # Configuration file
|
||||
- ./init:/docker-entrypoint-initdb.d # Initialization scripts
|
||||
ports:
|
||||
- "5432:5432" # Expose PostgreSQL port
|
||||
command: postgres -c config_file=/etc/postgresql/postgresql.conf
|
||||
healthcheck:
|
||||
# Regular health checks
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-postgres}",
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
networks:
|
||||
- postgres_network
|
||||
ulimits:
|
||||
# Increase file descriptor limits for production
|
||||
nofile:
|
||||
soft: 64000
|
||||
hard: 64000
|
||||
logging:
|
||||
# Log rotation to prevent disk space issues
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "200m"
|
||||
max-file: "10"
|
||||
|
||||
volumes:
|
||||
postgres_data: # Persistent volume for database files
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
postgres_network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Authentication**: PostgreSQL is configured with authentication enabled by default
|
||||
2. **Environment Variables**: Sensitive information is passed via environment variables
|
||||
3. **Network Isolation**: Services run on a dedicated bridge network
|
||||
4. **Configuration**: Optimized PostgreSQL configuration for security and performance
|
||||
|
||||
## Initialization Script
|
||||
|
||||
The initialization scripts in the `init/` directory:
|
||||
|
||||
- Create default roles and permissions
|
||||
- Set up sample schemas and tables
|
||||
- Configure database parameters for optimal performance
|
||||
|
||||
## Usage
|
||||
|
||||
1. Create a `.env` file with your custom credentials:
|
||||
|
||||
```
|
||||
POSTGRES_USER=your_postgres_username
|
||||
POSTGRES_PASSWORD=your_secure_password
|
||||
POSTGRES_DB=your_database_name
|
||||
```
|
||||
|
||||
2. Start the services:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
3. Connect to PostgreSQL:
|
||||
|
||||
```
|
||||
psql -h your-server-ip -p 5432 -U your_postgres_username -d your_database_name
|
||||
```
|
||||
|
||||
## Backup and Restore
|
||||
|
||||
### Creating a Backup
|
||||
|
||||
```bash
|
||||
./scripts/backup.sh
|
||||
```
|
||||
|
||||
### Restoring from Backup
|
||||
|
||||
```bash
|
||||
./scripts/restore.sh <backup-file>
|
||||
```
|
||||
|
||||
## Proxmox LXC Container Configuration
|
||||
|
||||
For optimal performance in a Proxmox LXC container:
|
||||
|
||||
1. Ensure the container has sufficient resources:
|
||||
|
||||
- At least 2 CPU cores
|
||||
- Minimum 4GB RAM
|
||||
- At least 20GB storage
|
||||
|
||||
2. Enable necessary features in the LXC container:
|
||||
|
||||
```
|
||||
pct set <container-id> -features nesting=1
|
||||
```
|
||||
|
||||
3. Configure container for Docker:
|
||||
```
|
||||
echo 'kernel.unprivileged_userns_clone=1' > /etc/sysctl.d/unprivileged-userns-clone.conf
|
||||
sysctl -p /etc/sysctl.d/unprivileged-userns-clone.conf
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
- **Backups**: PostgreSQL data is stored in named volumes. Use Docker's volume backup mechanisms:
|
||||
|
||||
```bash
|
||||
docker run --rm -v postgres_data:/data -v $(pwd):/backup alpine tar -czf /backup/postgres-data-backup.tar.gz /data
|
||||
```
|
||||
|
||||
- **Monitoring**: The service is configured with health checks and can be integrated with monitoring tools like Prometheus and Grafana
|
||||
|
||||
- **Updating**: To update PostgreSQL version, change the image tag in docker-compose.yml and restart:
|
||||
```bash
|
||||
docker-compose down
|
||||
# Edit docker-compose.yml to update image version
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Connection Issues**: Ensure ports are not blocked by firewall
|
||||
- **Performance Issues**: Check PostgreSQL logs with `docker-compose logs postgres`
|
||||
- **Resource Problems**: Monitor container resource usage and adjust limits if needed
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
# PostgreSQL Development Configuration File
|
||||
|
||||
# CONNECTIONS AND AUTHENTICATION
|
||||
listen_addresses = '*'
|
||||
max_connections = 50
|
||||
password_encryption = scram-sha-256
|
||||
ssl = off
|
||||
|
||||
# RESOURCE USAGE
|
||||
shared_buffers = 64MB
|
||||
work_mem = 4MB
|
||||
maintenance_work_mem = 32MB
|
||||
effective_cache_size = 1GB
|
||||
max_worker_processes = 4
|
||||
max_parallel_workers_per_gather = 1
|
||||
max_parallel_workers = 4
|
||||
|
||||
# WRITE-AHEAD LOG
|
||||
wal_level = minimal
|
||||
max_wal_size = 512MB
|
||||
min_wal_size = 40MB
|
||||
checkpoint_timeout = 5min
|
||||
checkpoint_completion_target = 0.9
|
||||
|
||||
# QUERY TUNING
|
||||
random_page_cost = 4.0
|
||||
effective_io_concurrency = 1
|
||||
default_statistics_target = 100
|
||||
|
||||
# LOGGING
|
||||
log_destination = 'stderr'
|
||||
logging_collector = on
|
||||
log_directory = 'pg_log'
|
||||
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
log_truncate_on_rotation = off
|
||||
log_rotation_age = 1d
|
||||
log_rotation_size = 10MB
|
||||
log_min_duration_statement = 250
|
||||
log_checkpoints = on
|
||||
log_connections = on
|
||||
log_disconnections = on
|
||||
log_duration = on
|
||||
log_error_verbosity = verbose
|
||||
log_line_prefix = '%m [%p] %q%u@%d '
|
||||
log_statement = 'all'
|
||||
|
||||
# AUTOVACUUM
|
||||
autovacuum = on
|
||||
log_autovacuum_min_duration = 250
|
||||
autovacuum_max_workers = 2
|
||||
autovacuum_naptime = 1min
|
||||
autovacuum_vacuum_threshold = 50
|
||||
autovacuum_analyze_threshold = 50
|
||||
|
||||
# CLIENT CONNECTION DEFAULTS
|
||||
datestyle = 'iso, mdy'
|
||||
timezone = 'UTC'
|
||||
lc_messages = 'en_US.UTF-8'
|
||||
lc_monetary = 'en_US.UTF-8'
|
||||
lc_numeric = 'en_US.UTF-8'
|
||||
lc_time = 'en_US.UTF-8'
|
||||
default_text_search_config = 'pg_catalog.english'
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
# PostgreSQL Production Configuration File
|
||||
|
||||
# CONNECTIONS AND AUTHENTICATION
|
||||
listen_addresses = '*'
|
||||
max_connections = 200
|
||||
password_encryption = scram-sha-256
|
||||
ssl = on
|
||||
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
|
||||
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
|
||||
|
||||
# RESOURCE USAGE
|
||||
shared_buffers = 2GB
|
||||
work_mem = 8MB
|
||||
maintenance_work_mem = 256MB
|
||||
effective_cache_size = 8GB
|
||||
max_worker_processes = 12
|
||||
max_parallel_workers_per_gather = 4
|
||||
max_parallel_workers = 12
|
||||
|
||||
# WRITE-AHEAD LOG
|
||||
wal_level = replica
|
||||
max_wal_size = 2GB
|
||||
min_wal_size = 1GB
|
||||
checkpoint_timeout = 15min
|
||||
checkpoint_completion_target = 0.9
|
||||
archive_mode = on
|
||||
archive_command = 'test ! -f /var/lib/postgresql/archive/%f && cp %p /var/lib/postgresql/archive/%f'
|
||||
|
||||
# REPLICATION
|
||||
max_wal_senders = 10
|
||||
wal_keep_size = 1GB
|
||||
hot_standby = on
|
||||
hot_standby_feedback = on
|
||||
|
||||
# QUERY TUNING
|
||||
random_page_cost = 1.1
|
||||
effective_io_concurrency = 200
|
||||
default_statistics_target = 500
|
||||
jit = on
|
||||
|
||||
# LOGGING
|
||||
log_destination = 'stderr'
|
||||
logging_collector = on
|
||||
log_directory = 'pg_log'
|
||||
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
log_truncate_on_rotation = off
|
||||
log_rotation_age = 1d
|
||||
log_rotation_size = 100MB
|
||||
log_min_duration_statement = 1000
|
||||
log_checkpoints = on
|
||||
log_connections = on
|
||||
log_disconnections = on
|
||||
log_duration = off
|
||||
log_error_verbosity = default
|
||||
log_line_prefix = '%m [%p] %q%u@%d '
|
||||
log_statement = 'none'
|
||||
|
||||
# AUTOVACUUM
|
||||
autovacuum = on
|
||||
log_autovacuum_min_duration = 1000
|
||||
autovacuum_max_workers = 6
|
||||
autovacuum_naptime = 1min
|
||||
autovacuum_vacuum_threshold = 50
|
||||
autovacuum_analyze_threshold = 50
|
||||
autovacuum_vacuum_scale_factor = 0.05
|
||||
autovacuum_analyze_scale_factor = 0.025
|
||||
|
||||
# CLIENT CONNECTION DEFAULTS
|
||||
datestyle = 'iso, mdy'
|
||||
timezone = 'UTC'
|
||||
lc_messages = 'en_US.UTF-8'
|
||||
lc_monetary = 'en_US.UTF-8'
|
||||
lc_numeric = 'en_US.UTF-8'
|
||||
lc_time = 'en_US.UTF-8'
|
||||
default_text_search_config = 'pg_catalog.english'
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
# PostgreSQL Staging Configuration File
|
||||
|
||||
# CONNECTIONS AND AUTHENTICATION
|
||||
listen_addresses = '*'
|
||||
max_connections = 100
|
||||
password_encryption = scram-sha-256
|
||||
ssl = on
|
||||
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
|
||||
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
|
||||
|
||||
# RESOURCE USAGE
|
||||
shared_buffers = 256MB
|
||||
work_mem = 6MB
|
||||
maintenance_work_mem = 64MB
|
||||
effective_cache_size = 2GB
|
||||
max_worker_processes = 6
|
||||
max_parallel_workers_per_gather = 2
|
||||
max_parallel_workers = 6
|
||||
|
||||
# WRITE-AHEAD LOG
|
||||
wal_level = replica
|
||||
max_wal_size = 1GB
|
||||
min_wal_size = 80MB
|
||||
checkpoint_timeout = 5min
|
||||
checkpoint_completion_target = 0.9
|
||||
|
||||
# QUERY TUNING
|
||||
random_page_cost = 4.0
|
||||
effective_io_concurrency = 2
|
||||
default_statistics_target = 100
|
||||
|
||||
# LOGGING
|
||||
log_destination = 'stderr'
|
||||
logging_collector = on
|
||||
log_directory = 'pg_log'
|
||||
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
log_truncate_on_rotation = off
|
||||
log_rotation_age = 1d
|
||||
log_rotation_size = 10MB
|
||||
log_min_duration_statement = 500
|
||||
log_checkpoints = on
|
||||
log_connections = on
|
||||
log_disconnections = on
|
||||
log_duration = off
|
||||
log_error_verbosity = default
|
||||
log_line_prefix = '%m [%p] %q%u@%d '
|
||||
log_statement = 'mod'
|
||||
|
||||
# AUTOVACUUM
|
||||
autovacuum = on
|
||||
log_autovacuum_min_duration = 500
|
||||
autovacuum_max_workers = 3
|
||||
autovacuum_naptime = 1min
|
||||
autovacuum_vacuum_threshold = 50
|
||||
autovacuum_analyze_threshold = 50
|
||||
|
||||
# CLIENT CONNECTION DEFAULTS
|
||||
datestyle = 'iso, mdy'
|
||||
timezone = 'UTC'
|
||||
lc_messages = 'en_US.UTF-8'
|
||||
lc_monetary = 'en_US.UTF-8'
|
||||
lc_numeric = 'en_US.UTF-8'
|
||||
lc_time = 'en_US.UTF-8'
|
||||
default_text_search_config = 'pg_catalog.english'
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
# PostgreSQL Configuration File
|
||||
# This is the main configuration file that will be used by default
|
||||
|
||||
# CONNECTIONS AND AUTHENTICATION
|
||||
listen_addresses = '*'
|
||||
max_connections = 100
|
||||
password_encryption = scram-sha-256
|
||||
ssl = on
|
||||
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
|
||||
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
|
||||
|
||||
# RESOURCE USAGE
|
||||
shared_buffers = 128MB
|
||||
work_mem = 4MB
|
||||
maintenance_work_mem = 64MB
|
||||
effective_cache_size = 4GB
|
||||
max_worker_processes = 8
|
||||
max_parallel_workers_per_gather = 2
|
||||
max_parallel_workers = 8
|
||||
|
||||
# WRITE-AHEAD LOG
|
||||
wal_level = replica
|
||||
max_wal_size = 1GB
|
||||
min_wal_size = 80MB
|
||||
checkpoint_timeout = 5min
|
||||
checkpoint_completion_target = 0.9
|
||||
|
||||
# QUERY TUNING
|
||||
random_page_cost = 4.0
|
||||
effective_io_concurrency = 2
|
||||
default_statistics_target = 100
|
||||
|
||||
# LOGGING
|
||||
log_destination = 'stderr'
|
||||
logging_collector = on
|
||||
log_directory = 'pg_log'
|
||||
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
log_truncate_on_rotation = off
|
||||
log_rotation_age = 1d
|
||||
log_rotation_size = 10MB
|
||||
log_min_duration_statement = 1000
|
||||
log_checkpoints = on
|
||||
log_connections = on
|
||||
log_disconnections = on
|
||||
log_duration = off
|
||||
log_error_verbosity = default
|
||||
log_line_prefix = '%m [%p] %q%u@%d '
|
||||
log_statement = 'none'
|
||||
|
||||
# AUTOVACUUM
|
||||
autovacuum = on
|
||||
log_autovacuum_min_duration = 1000
|
||||
autovacuum_max_workers = 3
|
||||
autovacuum_naptime = 1min
|
||||
autovacuum_vacuum_threshold = 50
|
||||
autovacuum_analyze_threshold = 50
|
||||
|
||||
# CLIENT CONNECTION DEFAULTS
|
||||
datestyle = 'iso, mdy'
|
||||
timezone = 'UTC'
|
||||
lc_messages = 'en_US.UTF-8'
|
||||
lc_monetary = 'en_US.UTF-8'
|
||||
lc_numeric = 'en_US.UTF-8'
|
||||
lc_time = 'en_US.UTF-8'
|
||||
default_text_search_config = 'pg_catalog.english'
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# This override file is for local development and is automatically loaded when running docker-compose up
|
||||
# without specifying a different file
|
||||
|
||||
services:
|
||||
postgres:
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- ./config/postgres-dev.conf:/etc/postgresql/postgresql.conf
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres_dev_password
|
||||
command: postgres -c config_file=/etc/postgresql/postgresql.conf
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
pgadmin:
|
||||
ports:
|
||||
- "5050:80"
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: dev@example.com
|
||||
PGADMIN_DEFAULT_PASSWORD: pgadmin_dev_password
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
services:
|
||||
postgres:
|
||||
image: postgres:15 # Using PostgreSQL 15
|
||||
container_name: postgres
|
||||
restart: always # Ensures PostgreSQL restarts automatically
|
||||
environment:
|
||||
# Environment variables for authentication
|
||||
- POSTGRES_USER=${POSTGRES_USER:-postgres} # Default: postgres
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} # Default: password
|
||||
- POSTGRES_DB=${POSTGRES_DB:-postgres} # Default: postgres
|
||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
# Persistent data storage
|
||||
- postgres_data:/var/lib/postgresql/data # Database files
|
||||
- ./config/postgres.conf:/etc/postgresql/postgresql.conf # Configuration file
|
||||
- ./init:/docker-entrypoint-initdb.d # Initialization scripts
|
||||
ports:
|
||||
- "5432:5432" # Expose PostgreSQL port
|
||||
command: postgres -c config_file=/etc/postgresql/postgresql.conf
|
||||
healthcheck:
|
||||
# Regular health checks
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-postgres}",
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
networks:
|
||||
- postgres_network
|
||||
ulimits:
|
||||
# Increase file descriptor limits for production
|
||||
nofile:
|
||||
soft: 64000
|
||||
hard: 64000
|
||||
logging:
|
||||
# Log rotation to prevent disk space issues
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "200m"
|
||||
max-file: "10"
|
||||
|
||||
volumes:
|
||||
postgres_data: # Persistent volume for database files
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
postgres_network:
|
||||
driver: bridge
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
environment:
|
||||
POSTGRES_USER: postgres_dev
|
||||
POSTGRES_PASSWORD: postgres_dev_password
|
||||
POSTGRES_DB: postgres_dev
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data_dev:/var/lib/postgresql/data
|
||||
- ./config/postgres-dev.conf:/etc/postgresql/postgresql.conf
|
||||
command: postgres -c config_file=/etc/postgresql/postgresql.conf
|
||||
|
||||
pgadmin:
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: dev@example.com
|
||||
PGADMIN_DEFAULT_PASSWORD: pgadmin_dev_password
|
||||
ports:
|
||||
- "5050:80"
|
||||
volumes:
|
||||
- pgadmin_data_dev:/var/lib/pgadmin
|
||||
|
||||
volumes:
|
||||
postgres_data_dev:
|
||||
name: postgres_data_dev
|
||||
pgadmin_data_dev:
|
||||
name: pgadmin_data_dev
|
||||
|
||||
networks:
|
||||
postgres_network:
|
||||
name: postgres_network_dev
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data_prod:/var/lib/postgresql/data
|
||||
- ./config/postgres-production.conf:/etc/postgresql/postgresql.conf
|
||||
command: postgres -c config_file=/etc/postgresql/postgresql.conf
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "200m"
|
||||
max-file: "10"
|
||||
|
||||
pgadmin:
|
||||
image: dpage/pgadmin4:latest
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
|
||||
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
||||
PGADMIN_CONFIG_SERVER_MODE: 'True'
|
||||
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'True'
|
||||
ports:
|
||||
- "5050:80"
|
||||
volumes:
|
||||
- pgadmin_data_prod:/var/lib/pgadmin
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "5"
|
||||
|
||||
volumes:
|
||||
postgres_data_prod:
|
||||
name: postgres_data_prod
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: 'none'
|
||||
o: 'bind'
|
||||
device: '/data/postgres'
|
||||
pgadmin_data_prod:
|
||||
name: pgadmin_data_prod
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: 'none'
|
||||
o: 'bind'
|
||||
device: '/data/pgadmin'
|
||||
|
||||
networks:
|
||||
postgres_network:
|
||||
name: postgres_network_prod
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 172.28.0.0/16
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
environment:
|
||||
POSTGRES_USER: postgres_staging
|
||||
POSTGRES_PASSWORD: postgres_staging_password
|
||||
POSTGRES_DB: postgres_staging
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- postgres_data_staging:/var/lib/postgresql/data
|
||||
- ./config/postgres-staging.conf:/etc/postgresql/postgresql.conf
|
||||
command: postgres -c config_file=/etc/postgresql/postgresql.conf
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
|
||||
pgadmin:
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: staging@example.com
|
||||
PGADMIN_DEFAULT_PASSWORD: pgadmin_staging_password
|
||||
ports:
|
||||
- "5051:80"
|
||||
volumes:
|
||||
- pgadmin_data_staging:/var/lib/pgadmin
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
|
||||
volumes:
|
||||
postgres_data_staging:
|
||||
name: postgres_data_staging
|
||||
pgadmin_data_staging:
|
||||
name: pgadmin_data_staging
|
||||
|
||||
networks:
|
||||
postgres_network:
|
||||
name: postgres_network_staging
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
-- PostgreSQL Initialization Script
|
||||
-- This script will be executed when the PostgreSQL container is first created
|
||||
|
||||
-- Create extensions
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "pg_stat_statements";
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- Create roles
|
||||
CREATE ROLE readonly;
|
||||
GRANT CONNECT ON DATABASE postgres TO readonly;
|
||||
GRANT USAGE ON SCHEMA public TO readonly;
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;
|
||||
|
||||
CREATE ROLE readwrite;
|
||||
GRANT CONNECT ON DATABASE postgres TO readwrite;
|
||||
GRANT USAGE, CREATE ON SCHEMA public TO readwrite;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO readwrite;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO readwrite;
|
||||
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO readwrite;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE ON SEQUENCES TO readwrite;
|
||||
|
||||
-- Create sample users
|
||||
CREATE USER sample_readonly WITH PASSWORD 'readonly_password';
|
||||
GRANT readonly TO sample_readonly;
|
||||
|
||||
CREATE USER sample_readwrite WITH PASSWORD 'readwrite_password';
|
||||
GRANT readwrite TO sample_readwrite;
|
||||
|
||||
-- Create sample schema and tables
|
||||
CREATE SCHEMA IF NOT EXISTS sample;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sample.users (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
email VARCHAR(100) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(100) NOT NULL,
|
||||
first_name VARCHAR(50),
|
||||
last_name VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sample.posts (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID NOT NULL REFERENCES sample.users(id) ON DELETE CASCADE,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
content TEXT,
|
||||
is_published BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_users_username ON sample.users(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON sample.users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_posts_user_id ON sample.posts(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_posts_created_at ON sample.posts(created_at);
|
||||
|
||||
-- Insert sample data
|
||||
INSERT INTO sample.users (username, email, password_hash, first_name, last_name)
|
||||
VALUES
|
||||
('johndoe', 'john.doe@example.com', crypt('password123', gen_salt('bf')), 'John', 'Doe'),
|
||||
('janedoe', 'jane.doe@example.com', crypt('password123', gen_salt('bf')), 'Jane', 'Doe')
|
||||
ON CONFLICT (username) DO NOTHING;
|
||||
|
||||
-- Get user IDs for sample posts
|
||||
DO $$
|
||||
DECLARE
|
||||
john_id UUID;
|
||||
jane_id UUID;
|
||||
BEGIN
|
||||
SELECT id INTO john_id FROM sample.users WHERE username = 'johndoe';
|
||||
SELECT id INTO jane_id FROM sample.users WHERE username = 'janedoe';
|
||||
|
||||
INSERT INTO sample.posts (user_id, title, content, is_published)
|
||||
VALUES
|
||||
(john_id, 'First Post', 'This is my first post content.', TRUE),
|
||||
(john_id, 'Second Post', 'This is my second post content.', FALSE),
|
||||
(jane_id, 'Hello World', 'Hello world post content.', TRUE)
|
||||
ON CONFLICT DO NOTHING;
|
||||
END $$;
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/bash
|
||||
# PostgreSQL Backup Script for Proxmox LXC Container
|
||||
|
||||
# Load environment variables
|
||||
if [ -f ../.env ]; then
|
||||
source ../.env
|
||||
else
|
||||
echo "Error: .env file not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set default values if not provided in .env
|
||||
BACKUP_DIR=${BACKUP_DIR:-"../backups"}
|
||||
POSTGRES_USER=${POSTGRES_USER:-"postgres"}
|
||||
POSTGRES_DB=${POSTGRES_DB:-"postgres"}
|
||||
BACKUP_RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-7}
|
||||
|
||||
# Create backup directory if it doesn't exist
|
||||
mkdir -p ${BACKUP_DIR}
|
||||
|
||||
# Generate timestamp for backup file
|
||||
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||
BACKUP_FILE="${BACKUP_DIR}/postgres_backup_${TIMESTAMP}.sql.gz"
|
||||
|
||||
echo "Starting PostgreSQL backup..."
|
||||
echo "Database: ${POSTGRES_DB}"
|
||||
echo "Backup file: ${BACKUP_FILE}"
|
||||
|
||||
# Perform the backup
|
||||
docker exec -t postgres pg_dump -U ${POSTGRES_USER} -d ${POSTGRES_DB} | gzip > ${BACKUP_FILE}
|
||||
|
||||
# Check if backup was successful
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Backup completed successfully: ${BACKUP_FILE}"
|
||||
|
||||
# Remove backups older than retention period
|
||||
find ${BACKUP_DIR} -name "postgres_backup_*.sql.gz" -type f -mtime +${BACKUP_RETENTION_DAYS} -delete
|
||||
echo "Cleaned up old backups (older than ${BACKUP_RETENTION_DAYS} days)"
|
||||
else
|
||||
echo "Error: Backup failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Print backup information
|
||||
echo "Backup size: $(du -h ${BACKUP_FILE} | cut -f1)"
|
||||
echo "Available backups:"
|
||||
ls -lh ${BACKUP_DIR}
|
||||
|
||||
# Create a full volume backup as well (for disaster recovery)
|
||||
echo "Creating volume backup..."
|
||||
VOLUME_BACKUP_FILE="${BACKUP_DIR}/postgres_volume_backup_${TIMESTAMP}.tar.gz"
|
||||
docker run --rm -v postgres_data:/data -v $(pwd)/${BACKUP_DIR}:/backup alpine tar -czf /backup/$(basename ${VOLUME_BACKUP_FILE}) /data
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Volume backup completed successfully: ${VOLUME_BACKUP_FILE}"
|
||||
else
|
||||
echo "Warning: Volume backup failed!"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#!/bin/bash
|
||||
# PostgreSQL Monitoring Script for Proxmox LXC Container
|
||||
|
||||
# Load environment variables
|
||||
if [ -f ../.env ]; then
|
||||
source ../.env
|
||||
else
|
||||
echo "Error: .env file not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set default values if not provided in .env
|
||||
POSTGRES_USER=${POSTGRES_USER:-"postgres"}
|
||||
POSTGRES_DB=${POSTGRES_DB:-"postgres"}
|
||||
|
||||
echo "PostgreSQL Service Monitoring for Proxmox LXC"
|
||||
echo "=========================================="
|
||||
echo
|
||||
|
||||
# Check container status
|
||||
echo "Container Status:"
|
||||
docker ps --filter "name=postgres" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
echo
|
||||
|
||||
# Check PostgreSQL service health
|
||||
echo "PostgreSQL Health Check:"
|
||||
docker exec postgres pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "PostgreSQL is accepting connections"
|
||||
else
|
||||
echo "PostgreSQL is not accepting connections"
|
||||
fi
|
||||
echo
|
||||
|
||||
# Database size information
|
||||
echo "Database Size Information:"
|
||||
docker exec postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) as size FROM pg_database ORDER BY pg_database_size(datname) DESC;"
|
||||
echo
|
||||
|
||||
# Connection information
|
||||
echo "Current Connections:"
|
||||
docker exec postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c "SELECT datname, count(*) as connections FROM pg_stat_activity GROUP BY datname;"
|
||||
echo
|
||||
|
||||
# Active queries
|
||||
echo "Active Queries (running for more than 5 seconds):"
|
||||
docker exec postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c "SELECT pid, datname, usename, application_name, client_addr, state, now() - query_start as duration, query FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '5 seconds';"
|
||||
echo
|
||||
|
||||
# Resource usage
|
||||
echo "Container Resource Usage:"
|
||||
docker stats postgres --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
|
||||
echo
|
||||
|
||||
# Disk usage
|
||||
echo "Disk Usage:"
|
||||
df -h $(docker volume inspect -f '{{ .Mountpoint }}' postgres_data)
|
||||
echo
|
||||
|
||||
# System resource usage
|
||||
echo "System Resource Usage:"
|
||||
echo "CPU:"
|
||||
top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1"%"}'
|
||||
echo "Memory:"
|
||||
free -m | awk 'NR==2{printf "Used: %s MB (%.2f%%)", $3, $3*100/$2 }'
|
||||
echo
|
||||
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
#!/bin/bash
|
||||
# PostgreSQL Restore Script for Proxmox LXC Container
|
||||
|
||||
# Check if backup file is provided
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 <backup-file>"
|
||||
echo "Example: $0 ../backups/postgres_backup_20250419_120000.sql.gz"
|
||||
echo " $0 ../backups/postgres_volume_backup_20250419_120000.tar.gz"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKUP_FILE=$1
|
||||
|
||||
# Check if backup file exists
|
||||
if [ ! -f "${BACKUP_FILE}" ]; then
|
||||
echo "Error: Backup file '${BACKUP_FILE}' not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Load environment variables
|
||||
if [ -f ../.env ]; then
|
||||
source ../.env
|
||||
else
|
||||
echo "Error: .env file not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set default values if not provided in .env
|
||||
POSTGRES_USER=${POSTGRES_USER:-"postgres"}
|
||||
POSTGRES_DB=${POSTGRES_DB:-"postgres"}
|
||||
|
||||
echo "Starting PostgreSQL restore..."
|
||||
echo "Database: ${POSTGRES_DB}"
|
||||
echo "Backup file: ${BACKUP_FILE}"
|
||||
|
||||
# Determine backup type (SQL dump or volume backup)
|
||||
if [[ "${BACKUP_FILE}" == *"postgres_backup_"* ]]; then
|
||||
# SQL dump restore
|
||||
# Confirm restore operation
|
||||
read -p "Warning: This will overwrite the existing database. Continue? (y/n): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Restore operation cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Perform the restore
|
||||
echo "Restoring database from SQL dump..."
|
||||
gunzip -c ${BACKUP_FILE} | docker exec -i postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB}
|
||||
|
||||
# Check if restore was successful
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Restore completed successfully from: ${BACKUP_FILE}"
|
||||
else
|
||||
echo "Error: Restore failed!"
|
||||
exit 1
|
||||
fi
|
||||
elif [[ "${BACKUP_FILE}" == *"postgres_volume_backup_"* ]]; then
|
||||
# Volume backup restore
|
||||
# Confirm restore operation
|
||||
read -p "WARNING: This will completely replace the PostgreSQL data volume. All current data will be lost. Continue? (y/n): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Restore operation cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Stopping PostgreSQL container..."
|
||||
docker-compose down
|
||||
|
||||
echo "Restoring volume data from backup..."
|
||||
# Create a temporary container to restore the volume
|
||||
docker run --rm -v postgres_data:/data -v $(pwd)/$(dirname ${BACKUP_FILE}):/backup alpine sh -c "rm -rf /data/* && tar -xzf /backup/$(basename ${BACKUP_FILE}) -C / --strip-components=1"
|
||||
|
||||
# Check if restore was successful
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Volume restore completed successfully from: ${BACKUP_FILE}"
|
||||
echo "Starting PostgreSQL container..."
|
||||
docker-compose up -d
|
||||
echo "Waiting for PostgreSQL to start..."
|
||||
sleep 10
|
||||
echo "PostgreSQL service restored and running."
|
||||
else
|
||||
echo "Error: Volume restore failed!"
|
||||
echo "Starting PostgreSQL container..."
|
||||
docker-compose up -d
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Error: Unknown backup file format. Expected postgres_backup_*.sql.gz or postgres_volume_backup_*.tar.gz"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
# Setup script for PostgreSQL service
|
||||
|
||||
# Make scripts executable
|
||||
chmod +x ./backup.sh
|
||||
chmod +x ./restore.sh
|
||||
chmod +x ./monitor.sh
|
||||
|
||||
# Create necessary directories
|
||||
mkdir -p ../backups
|
||||
mkdir -p ../logs
|
||||
|
||||
# Copy environment file if it doesn't exist
|
||||
if [ ! -f ../.env ]; then
|
||||
cp ../.env.example ../.env
|
||||
echo "Created .env file from .env.example"
|
||||
echo "Please update the .env file with your configuration"
|
||||
fi
|
||||
|
||||
echo "Setup completed successfully!"
|
||||
echo "You can now start the PostgreSQL service with: docker-compose up -d"
|
||||
Loading…
Reference in New Issue