Daily Docker Container Usage Summary Script
Docker Container Daily Summary Script
This script monitors the CPU and memory usage of Docker containers and performs the following:
- Logs resource usage statistics every 5 minutes.
- Sends alert emails if any container exceeds predefined CPU or memory thresholds.
- Generates a daily summary report with the average and maximum usage statistics for all containers and sends it via email.
Features
- Monitor Docker container CPU and memory usage.
- Log container statistics into a CSV file.
- Send alert emails for threshold breaches.
- Generate and email a daily summary report.
Table of Contents
- Features
- Prerequisites
- Configuration
- Script
- Key Functions
- Main Execution
- Example Output
- Daily Summary Email
- Setting Up Cron Jobs
- Troubleshooting
- Potential Enhancements
-
License
Prerequisites
- Python 3.12
- Docker and Docker-Py library
- SMTP server credentials (e.g., Gmail app password)
- Cron configured for periodic execution
Install the required Python package:
pip3 install docker
Configuration
- Recipient Emails: Add recipient names and emails in the
recipient_emaillist.recipient_email = [ {'name': 'abc', 'email': 'abc@gmail.com'} ] - Sender Email Credentials:
- Use your email and app password for Gmail:
sender_email = "abc@gmail.com" password = "your_app_password" - Generate the app password here.
- Use your email and app password for Gmail:
- Thresholds: Adjust CPU and memory thresholds as needed:
CPU_THRESHOLD = 70.0 # in percent MEMORY_THRESHOLD = 70.0 # in percent - Stats File Path: Update the file path to store daily container statistics:
STATS_FILE = "/home/ubuntu/python/daily_container_stats.csv" - SMTP Settings: Update SMTP host and port if using a service other than Gmail:
host = "smtp.gmail.com" port = 587
Script
import docker
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import logging
import json
import os
import csv
from datetime import datetime
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
recipient_email = [
{'name': 'abc', 'email': 'abc@gmail.com'}
]
sender_email = "abc@gmail.com"
password = "" # https://myaccount.google.com/u/1/apppasswords
host = "smtp.gmail.com"
port = 587
# File to store daily container stats
STATS_FILE = "/home/ubuntu/python/daily_container_stats.csv"
# Adjust the THRESHOLD as required.
CPU_THRESHOLD = 70.0 # in percent
MEMORY_THRESHOLD = 70.0 # in percent
# Initialize Docker client
docker_client = docker.from_env()
# Function to initialize the CSV file for stats
def initialize_stats_file():
try:
if not os.path.exists(STATS_FILE):
with open(file=STATS_FILE, mode="w", newline="") as file:
writer = csv.writer(file)
writer.writerow(["timestamp", "container_name", "cpu_usage", "memory_usage"])
logging.info(f"Stats file created: {STATS_FILE}")
except Exception as e:
logging.error(f"Failed to initialize stats file: {e}")
# Function to send an alert email
def send_alert_email(container_name, resource_type, usage):
"""Send an alert email for resource threshold breaches."""
try:
logging.info(f"Sending alert email for {container_name} - {resource_type} usage: {usage:.2f}%")
with smtplib.SMTP(host=host, port=port) as smtp_server:
smtp_server.starttls()
smtp_server.login(user=sender_email, password=password)
for recipient in recipient_email:
message = MIMEMultipart('alternative')
message['Subject'] = f"Alert: {container_name} {resource_type} Usage Exceeded"
message['To'] = recipient['email']
message['From'] = sender_email
body = f"The container '{container_name}' is using {usage:.2f}% {resource_type}. Please investigate."
mail_template = MIMEText(body, "plain")
message.attach(mail_template)
smtp_server.sendmail(to_addrs=recipient['email'], from_addr=sender_email, msg=message.as_string())
logging.info(f"Alert email sent to {recipient['email']}")
except Exception as e:
logging.error(f"Failed to send alert email: {e}")
# Function to monitor containers and log their stats
def monitor_containers():
"""Log the stats of all running containers and send alerts for threshold breaches."""
try:
for container in docker_client.containers.list():
try:
# Fetch container stats
stats = container.stats(stream=False)
# CPU usage calculation
cpu_delta = stats["cpu_stats"]["cpu_usage"]["total_usage"] - stats["precpu_stats"]["cpu_usage"]["total_usage"]
system_delta = stats["cpu_stats"]["system_cpu_usage"] - stats["precpu_stats"]["system_cpu_usage"]
cpu_usage = (cpu_delta / system_delta) * 100 if system_delta > 0 else 0
# Memory usage calculation
memory_usage = stats["memory_stats"]["usage"] / stats["memory_stats"]["limit"] * 100
logging.info(f"Container: {container.name}, CPU: {cpu_usage:.2f}%, Memory: {memory_usage:.2f}%")
# Log stats to the file
with open(file=STATS_FILE, mode="a", newline="") as file:
writer = csv.writer(file)
writer.writerow([datetime.now().isoformat(), container.name, cpu_usage, memory_usage])
# Check thresholds and send alerts
if cpu_usage > CPU_THRESHOLD:
send_alert_email(container.name, "CPU", cpu_usage)
if memory_usage > MEMORY_THRESHOLD:
send_alert_email(container.name, "Memory", memory_usage)
except KeyError as e:
logging.warning(f"Container: {container.name} - Missing key in stats: {e}")
except Exception as e:
logging.error(f"Error monitoring container '{container.name}': {e}")
except Exception as e:
logging.error(f"Failed to monitor containers: {e}")
# Function to generate the daily summary report
def generate_daily_summary():
"""Generate a summary of daily stats from the CSV file."""
summary = {}
try:
with open(file=STATS_FILE, mode="r") as file:
reader = csv.DictReader(file)
for row in reader:
container_name = row["container_name"]
cpu_usage = float(row["cpu_usage"])
memory_usage = float(row["memory_usage"])
if container_name not in summary:
summary[container_name] = {
"cpu_usages": [],
"memory_usages": []
}
summary[container_name]["cpu_usages"].append(cpu_usage)
summary[container_name]["memory_usages"].append(memory_usage)
# Calculate averages and max values for each container
report = "Daily Summary of Container Stats:\n\n"
for container_name, stats in summary.items():
avg_cpu = sum(stats["cpu_usages"]) / len(stats["cpu_usages"])
max_cpu = max(stats["cpu_usages"])
avg_memory = sum(stats["memory_usages"]) / len(stats["memory_usages"])
max_memory = max(stats["memory_usages"])
report += (
f"Container: {container_name}\n"
f" Average CPU Usage: {avg_cpu:.2f}%\n"
f" Max CPU Usage: {max_cpu:.2f}%\n"
f" Average Memory Usage: {avg_memory:.2f}%\n"
f" Max Memory Usage: {max_memory:.2f}%\n\n"
)
return report
except Exception as e:
logging.error(f"Failed to generate daily summary: {e}")
return None
# Function to send the daily summary email
def send_summary_email(report):
"""Send the daily summary email."""
try:
logging.info("Sending daily summary email...")
with smtplib.SMTP(host=host, port=port) as smtp_server:
smtp_server.starttls()
smtp_server.login(user=sender_email, password=password)
for recipient in recipient_email:
message = MIMEMultipart('alternative')
message['Subject'] = "Daily Summary of Docker Container Usage"
message['To'] = recipient['email']
message['From'] = sender_email
email_template = f"""\
Greetings {recipient['name']},
{report}
Regards,
Monitoring System
"""
mail_template = MIMEText(email_template, "plain")
message.attach(mail_template)
smtp_server.sendmail(to_addrs=recipient['email'], from_addr=sender_email, msg=message.as_string())
logging.info(f"Daily summary email sent to {recipient['email']}")
# Clear the stats file after sending the summary
open(STATS_FILE, "w").close()
logging.info("Cleared the stats file after sending the summary.")
except Exception as e:
logging.error(f"Failed to send daily summary email: {e}")
# Main execution
if __name__ == "__main__":
initialize_stats_file()
# Decide the mode of operation based on CLI arguments
import sys
if len(sys.argv) > 1 and sys.argv[1] == "--summary":
report = generate_daily_summary()
if report:
send_summary_email(report)
else:
monitor_containers()
Key Functions
initialize_stats_file(): Creates a CSV file to store container stats if it doesn’t already exist.monitor_containers(): Logs the CPU and memory usage of all running containers and sends alert emails for threshold breaches.generate_daily_summary(): Reads the CSV file and generates a summary of container stats for the day.send_alert_email(): Sends an alert email if a container exceeds CPU or memory thresholds.send_summary_email(): Sends a daily summary email with average and maximum usage statistics.
Main Execution
The script operates in two modes:
-
Monitoring Mode (default): Logs container stats and sends alerts for threshold breaches.
python3 docker_monitor.py -
Daily Summary Mode: Generates and sends the daily summary report.
python3 docker_monitor.py --summary
Example Output
Terminal Output (Monitoring Mode)
python3 docker_monitor.py
2024-12-30 14:23:45 - INFO - Starting Docker container monitoring...
2024-12-30 14:23:46 - INFO - Container: nginx, CPU: 15.32%, Memory: 7.45%
2024-12-30 14:23:46 - INFO - Email sent to abc@gmail.com
Daily Summary Email
Subject: Daily Summary of Docker Container Usage
Greetings abc,
Daily Summary of Container Stats:
Container: nginx
Average CPU Usage: 10.25%
Max CPU Usage: 30.50%
Average Memory Usage: 12.10%
Max Memory Usage: 40.75%
Regards,
Monitoring System
Setting Up Cron Jobs
- Monitor Containers Every 5 Minutes:
Collect container stats every 5 minutes store it into /home/ubuntu/python/daily_container_stats.csv file. If any container exceed the cpu, mem threshold, it will send the email as well.
*/5 * * * * python3 /home/ubuntu/python/docker_monitor.py - Send Daily Summary Email at 11:59 PM:
59 23 * * * python3 /home/ubuntu/python/docker_monitor.py --summary
Troubleshooting
- Email Delivery Issues:
- Ensure SMTP credentials are correct.
- Verify the app password setup for Gmail.
- Docker Access:
- Ensure the script runs with a user that has permissions to access Docker.
- File Permissions:
- Ensure the CSV file path is writable by the script.
Potential Enhancements
- Log stats to a database for long-term analysis.
- Add support for additional container metrics like network I/O and disk usage.
- Integrate with Slack or webhook-based notification systems.
License
This project is licensed under the MIT License. You are free to use, modify, and distribute it for personal or commercial purposes.
How would you rate this article?
Was this article helpful?