init
This commit is contained in:
commit
136cfbb862
7
.env
Normal file
7
.env
Normal file
@ -0,0 +1,7 @@
|
||||
MYSQL_ROOT_PASSWORD=p8CQ8KSypTWFIfWfvsnFYpUq
|
||||
MYSQL_PASSWORD=5niR56SNrcoO7YEy/zz1qPdm
|
||||
MYSQL_USER=fileserver
|
||||
MYSQL_DATABASE=fileserver
|
||||
MYSQL_CHARSET=utf8mb4
|
||||
MYSQL_COLLATION=utf8mb4_unicode_ci
|
||||
NETWORK=app-network
|
||||
7
.env-example
Normal file
7
.env-example
Normal file
@ -0,0 +1,7 @@
|
||||
MYSQL_ROOT_PASSWORD=CHANGEME
|
||||
MYSQL_PASSWORD=CHANGEME
|
||||
MYSQL_USER=CHANGEME
|
||||
MYSQL_DATABASE=CHANGEME
|
||||
MYSQL_CHARSET=utf8mb4
|
||||
MYSQL_COLLATION=utf8mb4_unicode_ci
|
||||
NETWORK=CHANGEME
|
||||
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
35
Dockerfile
Normal file
35
Dockerfile
Normal file
@ -0,0 +1,35 @@
|
||||
FROM mysql:8.0.41-bookworm
|
||||
|
||||
# Install cron and other required tools
|
||||
RUN apt-get update && apt-get install -y \
|
||||
mysql-client \
|
||||
cron \
|
||||
openssl \
|
||||
zip \
|
||||
rsync \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /opt/backups /opt/backups/tmp /var/log/backup /etc/cron.d
|
||||
|
||||
# Copy scripts
|
||||
COPY backup.sh /usr/local/bin/backup.sh
|
||||
COPY create_admin.sh /usr/local/bin/create_admin.sh
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Make scripts executable
|
||||
RUN chmod +x /usr/local/bin/backup.sh \
|
||||
&& chmod +x /usr/local/bin/create_admin.sh \
|
||||
&& chmod +x /entrypoint.sh
|
||||
|
||||
# Setup cron jobs
|
||||
RUN echo "0 * * * * root /usr/local/bin/backup.sh --hourly >> /var/log/backup/hourly.log 2>&1" > /etc/cron.d/backup-cron && \
|
||||
echo "0 0 * * * root /usr/local/bin/backup.sh --daily >> /var/log/backup/daily.log 2>&1" >> /etc/cron.d/backup-cron && \
|
||||
echo "0 0 * * 0 root /usr/local/bin/backup.sh --weekly >> /var/log/backup/weekly.log 2>&1" >> /etc/cron.d/backup-cron && \
|
||||
chmod 0644 /etc/cron.d/backup-cron
|
||||
|
||||
EXPOSE 3306
|
||||
|
||||
# Use our custom entrypoint
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Custom MySQL Docker
|
||||
|
||||
This is a custom docker container that is used in my home lab and in my production servers. Feel free to use it.
|
||||
|
||||
## Prerequisites
|
||||
1. docker
|
||||
2. docker compose
|
||||
3. zip unzip
|
||||
4. (OPTIONAL) rsync and remote server for storing backups offsite
|
||||
|
||||
### 1. Rename ENV
|
||||
Rename `.env-example` to `.env`
|
||||
### 2. EDIT ENV
|
||||
Edit and change all the `CHANGEME` to values you are using
|
||||
### 3. Create Docker Network
|
||||
Run `docker network create your_network_name` to create network and be sure to change `your_network_name` to someting you will use
|
||||
### 4. Modify `backup.sh` if needed
|
||||
`backup.sh` can be modified to store locally, mounted directories, s3, etc. Its currently configured to use SSH
|
||||
### 5. Install
|
||||
Run `docker compose up -d --build`
|
||||
|
||||
> A separate admin user is created with full permissions and grant options.
|
||||
> run docker logs mysql to see password
|
||||
|
||||
[LICENSE](/LICENSE)
|
||||
217
backup.sh
Normal file
217
backup.sh
Normal file
@ -0,0 +1,217 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Load environment variables from .env file
|
||||
if [ -f .env ]; then
|
||||
# Read each line from .env and export variables
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# Skip comments and empty lines
|
||||
[[ $line =~ ^#.*$ ]] && continue
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
# Export the variable
|
||||
export "$line"
|
||||
done < .env
|
||||
else
|
||||
echo "Error: .env file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Variables (now using environment variables with defaults)
|
||||
MYSQL_USER="admin"
|
||||
MYSQL_PASSWORD=${MYSQL_ADMIN_PASSWORD}
|
||||
BACKUP_DIR="$(pwd)/backups"
|
||||
REMOTE_HOST="${BACKUP_SERVER_HOST}" # Remote server hostname/IP
|
||||
REMOTE_USER="${BACKUP_SERVER_USER}" # Remote server username
|
||||
REMOTE_BACKUP_DIR="${BACKUP_SERVER_PATH}" # Remote server backup path
|
||||
TEMP_DIR="$BACKUP_DIR/tmp"
|
||||
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
|
||||
MODE=""
|
||||
HOURLY_RETENTION=4
|
||||
DAILY_RETENTION=3
|
||||
WEEKLY_RETENTION=1
|
||||
MIN_SPACE=20 # Minimum space in GB
|
||||
DB_CONTAINER="mailserver_db"
|
||||
|
||||
# Function to display usage
|
||||
function usage() {
|
||||
echo "Usage: $0 --hourly | --daily | --weekly"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Function to check available space and cleanup if necessary
|
||||
function check_and_cleanup_space() {
|
||||
local available_space=$(df -BG "$REMOTE_BACKUP_DIR" | awk 'NR==2 {gsub("G","",$4); print $4}')
|
||||
|
||||
if [ "$available_space" -le "$MIN_SPACE" ]; then
|
||||
echo "Available space ($available_space GB) is less than minimum required ($MIN_SPACE GB)"
|
||||
echo "Starting cleanup..."
|
||||
|
||||
while [ "$available_space" -le "$MIN_SPACE" ]; do
|
||||
# Find oldest backup file
|
||||
oldest_file=$(find "$REMOTE_BACKUP_DIR" -type f -name "*.zip" -printf '%T+ %p\n' | sort | head -n 1 | awk '{print $2}')
|
||||
|
||||
if [ -z "$oldest_file" ]; then
|
||||
echo "No more files to delete!"
|
||||
break
|
||||
fi
|
||||
|
||||
# Get file size before deletion for logging
|
||||
file_size=$(du -h "$oldest_file" | cut -f1)
|
||||
|
||||
# Delete the file
|
||||
rm -f "$oldest_file"
|
||||
echo "Deleted old backup: $oldest_file (Size: $file_size)"
|
||||
|
||||
# Recalculate available space
|
||||
available_space=$(df -BG "$REMOTE_BACKUP_DIR" | awk 'NR==2 {gsub("G","",$4); print $4}')
|
||||
done
|
||||
|
||||
echo "Cleanup complete. Available space: $available_space GB"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "##########################"
|
||||
echo "Starting backup..."
|
||||
echo "##########################"
|
||||
echo ""
|
||||
|
||||
# Check if the correct parameter is passed
|
||||
if [[ "$1" == "--hourly" ]]; then
|
||||
MODE="hourly"
|
||||
elif [[ "$1" == "--daily" ]]; then
|
||||
MODE="daily"
|
||||
elif [[ "$1" == "--weekly" ]]; then
|
||||
MODE="weekly"
|
||||
else
|
||||
usage
|
||||
fi
|
||||
echo "##########################"
|
||||
echo "Mode: $MODE"
|
||||
echo "##########################"
|
||||
echo ""
|
||||
|
||||
echo "##########################"
|
||||
echo "Creating backup directories..."
|
||||
echo "##########################"
|
||||
echo ""
|
||||
|
||||
# Create backup and temp directories if they don't exist
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
mkdir -p "$TEMP_DIR"
|
||||
mkdir -p "/var/log/backup"
|
||||
|
||||
# Backup all MySQL databases
|
||||
echo "##########################"
|
||||
echo "Backing up MySQL databases..."
|
||||
echo "##########################"
|
||||
echo ""
|
||||
|
||||
# Test database connection first
|
||||
if ! docker exec -i $DB_CONTAINER mariadb -u $MYSQL_USER -p${MYSQL_PASSWORD} -e "SELECT 1;" >/dev/null 2>&1; then
|
||||
echo "Error: Cannot connect to MySQL database. Please check credentials."
|
||||
echo "Container: $DB_CONTAINER"
|
||||
echo "User: $MYSQL_USER"
|
||||
echo "Password being used: ${MYSQL_PASSWORD}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get databases list without using -it flag (which requires terminal)
|
||||
databases=$(docker exec -i $DB_CONTAINER mariadb -u $MYSQL_USER -p${MYSQL_PASSWORD} -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|mysql|sys)")
|
||||
|
||||
for db in $databases; do
|
||||
echo "Backing up database: $db"
|
||||
docker exec -i $DB_CONTAINER mariadb-dump -u $MYSQL_USER -p${MYSQL_PASSWORD} --databases "$db" > "$TEMP_DIR/${db}.sql"
|
||||
if [ $? -eq 0 ]; then
|
||||
zip -j "$TEMP_DIR/${db}.sql.zip" "$TEMP_DIR/${db}.sql"
|
||||
rm "$TEMP_DIR/${db}.sql"
|
||||
else
|
||||
echo "Error backing up database: $db"
|
||||
fi
|
||||
done
|
||||
|
||||
DIRECTORIES_TO_BACKUP=(
|
||||
"/root/isekai"
|
||||
)
|
||||
|
||||
for dir in "${DIRECTORIES_TO_BACKUP[@]}"; do
|
||||
DIR_NAME=$(basename "$dir")
|
||||
zip -r "$TEMP_DIR/${DIR_NAME}_${MODE}_$TIMESTAMP.zip" "$dir" -x "*/node_modules/*" "*/backups/*"
|
||||
done
|
||||
|
||||
# Compress all SQL and directories into a single zip file
|
||||
echo "##########################"
|
||||
echo "Compressing backup files..."
|
||||
echo "##########################"
|
||||
echo ""
|
||||
|
||||
FINAL_BACKUP_FILE="$BACKUP_DIR/${MODE}_backup_$TIMESTAMP.zip"
|
||||
find "$TEMP_DIR" -name "*.zip" | while read file; do
|
||||
zip -ur "$FINAL_BACKUP_FILE" "$file"
|
||||
done
|
||||
|
||||
# Clean up temporary files
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
echo "##########################"
|
||||
echo "Backup complete: $FINAL_BACKUP_FILE"
|
||||
echo "##########################"
|
||||
echo ""
|
||||
|
||||
function apply_retention() {
|
||||
backup_type=$1
|
||||
retention_count=$2
|
||||
|
||||
# Find all backup files of the specified type, sort them by modification time, and keep the newest
|
||||
backups=($(ls -t $BACKUP_DIR/${backup_type}_backup_*.zip))
|
||||
|
||||
# If the number of backups exceeds the retention limit, delete the older ones
|
||||
if [ ${#backups[@]} -gt $retention_count ]; then
|
||||
delete_count=$((${#backups[@]} - $retention_count))
|
||||
for (( i=$retention_count; i<${#backups[@]}; i++ )); do
|
||||
rm -f "${backups[$i]}"
|
||||
echo "Deleted old $backup_type backup: ${backups[$i]}"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
echo "##########################"
|
||||
echo "Applying retention policy..."
|
||||
echo "##########################"
|
||||
echo ""
|
||||
|
||||
# Apply retention policy
|
||||
if [[ "$MODE" == "hourly" ]]; then
|
||||
apply_retention "hourly" $HOURLY_RETENTION
|
||||
elif [[ "$MODE" == "daily" ]]; then
|
||||
apply_retention "daily" $DAILY_RETENTION
|
||||
elif [[ "$MODE" == "weekly" ]]; then
|
||||
apply_retention "weekly" $WEEKLY_RETENTION
|
||||
fi
|
||||
|
||||
echo "Retention policy applied: $MODE backups cleaned."
|
||||
echo "##########################"
|
||||
echo "Backup cleanup complete."
|
||||
echo "##########################"
|
||||
echo ""
|
||||
|
||||
echo "##########################"
|
||||
echo "Checking remote backup space..."
|
||||
echo "##########################"
|
||||
echo ""
|
||||
|
||||
# Check and cleanup space before copying new backup
|
||||
check_and_cleanup_space
|
||||
|
||||
# Use rsync to copy the backup
|
||||
if ! ssh -p "$BACKUP_SERVER_PORT" "${REMOTE_USER}@${REMOTE_HOST}" exit 2>/dev/null; then
|
||||
echo "Error: Cannot connect to remote backup server"
|
||||
echo "Backup file is saved locally at: $FINAL_BACKUP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rsync -av --progress -e "ssh -p $BACKUP_SERVER_PORT" "$FINAL_BACKUP_FILE" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_BACKUP_DIR}/"
|
||||
|
||||
echo "##########################"
|
||||
echo "Remote backup complete"
|
||||
echo "##########################"
|
||||
echo ""
|
||||
36
create_admin.sh
Normal file
36
create_admin.sh
Normal file
@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Variables - replace these with your desired values or use environment variables
|
||||
ADMIN_USER="admin"
|
||||
ADMIN_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
echo "Creating MySQL admin user with root-like privileges..."
|
||||
|
||||
# Execute MySQL commands inside the container
|
||||
mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "
|
||||
DROP USER IF EXISTS '$ADMIN_USER'@'%';
|
||||
CREATE USER '$ADMIN_USER'@'%' IDENTIFIED BY '$ADMIN_PASSWORD';
|
||||
GRANT ALL PRIVILEGES ON *.* TO '$ADMIN_USER'@'%' WITH GRANT OPTION;
|
||||
FLUSH PRIVILEGES;
|
||||
SELECT User, Host FROM mysql.user WHERE User = '$ADMIN_USER';
|
||||
" 2>/dev/null
|
||||
|
||||
# Check if the command was successful
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ MySQL admin user '$ADMIN_USER' created successfully!"
|
||||
echo "Connection details:"
|
||||
echo " - Username: $ADMIN_USER"
|
||||
echo " - Password: $ADMIN_PASSWORD"
|
||||
echo " - Host: localhost"
|
||||
echo " - Port: 3806 (mapped port)"
|
||||
|
||||
# Optionally save credentials to a file
|
||||
echo "Saving credentials to /opt/admin_credentials.txt"
|
||||
echo "Username: $ADMIN_USER" > /opt/admin_credentials.txt
|
||||
echo "Password: $ADMIN_PASSWORD" >> /opt/admin_credentials.txt
|
||||
chmod 600 /opt/admin_credentials.txt
|
||||
else
|
||||
echo "❌ Failed to create MySQL admin user."
|
||||
echo "Please check your container status and credentials."
|
||||
fi
|
||||
44
docker-compose.yml
Normal file
44
docker-compose.yml
Normal file
@ -0,0 +1,44 @@
|
||||
services:
|
||||
mysql:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: mysql
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ADMIN_PASSWORD}
|
||||
MYSQL_USER: ${MYSQL_USER}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE}
|
||||
MYSQL_CHARSET: utf8mb4
|
||||
MYSQL_COLLATION: utf8mb4_unicode_ci
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
- ./config/my.cnf:/etc/mysql/my.cnf
|
||||
command: >
|
||||
--character-set-server=utf8mb4
|
||||
--collation-server=utf8mb4_unicode_ci
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
networks:
|
||||
- ${NETWORK}
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
|
||||
networks:
|
||||
${NETWORK}:
|
||||
name: ${NETWORK}
|
||||
external: true
|
||||
74
entrypoint.sh
Normal file
74
entrypoint.sh
Normal file
@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create required MySQL directories
|
||||
echo "Creating required MySQL directories..."
|
||||
mkdir -p /var/lib/mysql /var/lib/mysql-files /var/run/mysqld
|
||||
chown -R mysql:mysql /var/lib/mysql /var/lib/mysql-files /var/run/mysqld
|
||||
chmod 750 /var/lib/mysql /var/lib/mysql-files
|
||||
chmod 755 /var/run/mysqld
|
||||
|
||||
# Check if MySQL has been initialized
|
||||
if [ ! "$(ls -A /var/lib/mysql/mysql)" ]; then
|
||||
echo "Initializing MySQL data directory..."
|
||||
# Initialize without root password
|
||||
mysqld --initialize-insecure --user=mysql
|
||||
|
||||
# Start MySQL temporarily with --skip-networking to set root password
|
||||
echo "Starting MySQL temporarily to set root password..."
|
||||
mysqld --user=mysql --skip-networking &
|
||||
pid="$!"
|
||||
|
||||
# Wait for MySQL to start up
|
||||
echo "Waiting for temporary MySQL instance to start..."
|
||||
for i in {30..0}; do
|
||||
if mysqladmin ping --socket=/var/run/mysqld/mysqld.sock &> /dev/null; then
|
||||
break
|
||||
fi
|
||||
echo "MySQL not ready yet... waiting"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ "$i" = 0 ]; then
|
||||
echo >&2 "MySQL initialization process failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set root password
|
||||
echo "Setting root password..."
|
||||
mysql --socket=/var/run/mysqld/mysqld.sock -u root <<-EOSQL
|
||||
ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}';
|
||||
FLUSH PRIVILEGES;
|
||||
EOSQL
|
||||
|
||||
# Stop temporary MySQL server
|
||||
echo "Stopping temporary MySQL instance..."
|
||||
if ! mysqladmin shutdown --socket=/var/run/mysqld/mysqld.sock; then
|
||||
kill "$pid"
|
||||
wait "$pid"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start MySQL in the background
|
||||
echo "Starting MySQL server..."
|
||||
mysqld --user=mysql &
|
||||
|
||||
# Wait for MySQL to be ready
|
||||
echo "Waiting for MySQL to be ready..."
|
||||
until mysqladmin ping -h"localhost" -u"root" -p"${MYSQL_ROOT_PASSWORD}" --silent; do
|
||||
echo "MySQL not ready yet... waiting"
|
||||
sleep 2
|
||||
done
|
||||
echo "MySQL is ready!"
|
||||
|
||||
# Run the admin user creation script
|
||||
echo "Creating admin user..."
|
||||
/usr/local/bin/create_admin.sh
|
||||
|
||||
# Start cron service
|
||||
echo "Starting cron service..."
|
||||
service cron start
|
||||
|
||||
# Keep the container running by waiting on the MySQL process
|
||||
echo "MySQL server is running. Container will stay alive until MySQL exits."
|
||||
wait %1
|
||||
Loading…
x
Reference in New Issue
Block a user