Complete Node.js container implementation with multi-version support
Some checks failed
Cloud Node Container / Build-and-Push (18) (push) Failing after 10s
Cloud Node Container / Build-and-Push (20) (push) Failing after 8s
Cloud Node Container / Build-and-Push (22) (push) Failing after 8s

- Add Dockerfile with AlmaLinux 9 base, Nginx reverse proxy, and PM2
- Support Node.js versions 18, 20, 22 with automated installation
- Implement memory-optimized configuration (256MB minimum, 512MB recommended)
- Add Memcached session storage for development environments
- Create comprehensive documentation (README, USER-GUIDE, MEMORY-GUIDE, CLAUDE.md)
- Include example applications (simple website and REST API)
- Add Gitea CI/CD pipeline for automated multi-version builds
- Provide local development script with helper utilities
- Implement health monitoring, log rotation, and backup systems

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-21 16:00:46 -07:00
parent 36285fa102
commit 2989cd590a
28 changed files with 1777 additions and 1 deletions

37
.dockerignore Normal file
View File

@@ -0,0 +1,37 @@
# Git files
.git
.gitignore
# Documentation
README.md
CLAUDE.md
# Local development files
user/
instance_*
local-dev.sh
# Logs
*.log
logs/
# Temporary files
tmp/
temp/
.tmp
# IDE files
.vscode/
.idea/
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db
# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -0,0 +1,40 @@
name: Cloud Node Container
run-name: ${{ gitea.actor }} pushed a change to main
on:
push:
branches:
- main
jobs:
Build-and-Push:
runs-on: ubuntu-latest
strategy:
matrix:
nodever: [18, 20, 22]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea
uses: docker/login-action@v3
with:
registry: repo.anhonesthost.net
username: ${{ secrets.CI_USER }}
password: ${{ secrets.CI_TOKEN }}
- name: Build and Push Image
uses: docker/build-push-action@v6
with:
platforms: linux/amd64
push: true
build-args: |
NODEVER=${{ matrix.nodever }}
tags: |
repo.anhonesthost.net/cloud-hosting-platform/cnc:node${{ matrix.nodever }}
${{ matrix.nodever == '20' && 'repo.anhonesthost.net/cloud-hosting-platform/cnc:latest' || '' }}

96
CLAUDE.md Normal file
View File

@@ -0,0 +1,96 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Cloud Node Container (CNC) is a Docker-based Node.js hosting environment that supports multiple Node.js versions (18, 20, 22) with Nginx reverse proxy, designed for both local development and production deployment.
## Common Development Commands
### Local Development Setup
```bash
# Quick start with automated setup (creates helper scripts and default app)
./local-dev.sh -n local-dev
# With specific Node.js version
./local-dev.sh -n myproject -a 22 # Node.js 22
# Helper scripts created by local-dev.sh:
./instance_start # Start container
./instance_stop # Stop container
./instance_logs # View container logs
./instance_shell # Access container shell
```
### Building and Testing
```bash
# Build container locally
docker build -t cloud-node-container:latest .
# Build with specific Node.js version
docker build --build-arg NODEVER=22 -t cnc:node22 .
# Run container manually
docker run -d -p 80:80 -p 443:443 \
-e NODEVER=20 -e environment=DEV \
-e uid=$(id -u) -e user=$(whoami) -e domain=localhost \
-v"$(pwd)/user":/home/$(whoami) \
--name test-container cloud-node-container:latest
```
### Server Deployment
- Production git directory: `/root/whp`
- After `git pull`, sync web files: `rsync -av web-files/ /docker/whp/web/`
## Architecture and Key Components
### Directory Structure
- `/scripts/` - Container setup scripts (entrypoint, Node.js installers, nginx config)
- `/configs/` - Nginx, PM2, and default application configurations
- User applications go in `/home/$user/app/`
### Container Behavior
1. **Entrypoint Flow** (`scripts/entrypoint.sh`):
- Creates user with specified UID
- Sets up directory structure (/home/$user/app/, logs/)
- Configures Nginx reverse proxy based on environment variables
- In DEV mode: starts Memcached for session storage
- Starts Nginx and PM2-managed Node.js application
2. **Environment Modes**:
- **DEV** (`environment=DEV`): Local Memcached, automatic backups, verbose logging
- **PROD** (default): Expects external services, optimized for production
3. **Node.js Version Management**:
- Controlled via `NODEVER` environment variable (18, 20, 22)
- Each version has dedicated install script in `/scripts/`
- PM2 ecosystem configuration adapts to user paths
### Key Environment Variables
- `uid` (required): User ID for file permissions
- `user` (required): Username for container user
- `domain` (required): Primary domain for Nginx configuration
- `serveralias`: Additional domains (comma-separated)
- `NODEVER`: Node.js version to use (default: 20)
- `environment`: DEV or PROD mode
## Important Technical Details
1. **Health Check**: Available at `/ping` endpoint (Node.js app must implement)
2. **Logs Location**: `/home/$user/logs/nginx/` and `/home/$user/logs/nodejs/`
3. **Application Backups** (DEV mode): Every 30 minutes to `/home/$user/_backups/`
4. **Log Rotation**: Compress after 3 days, delete after 7 days
5. **SSL**: Self-signed certificate auto-generated, Nginx handles SSL termination
6. **Static Files**: Nginx serves from `/home/$user/app/public/` at `/static/` path
7. **Process Management**: PM2 handles Node.js application lifecycle
## Application Requirements
User applications in `/home/$user/app/` should include:
- `package.json` with dependencies and scripts
- Main application file (default: `index.js`)
- Optional: `ecosystem.config.js` for PM2 configuration
- Optional: `public/` directory for static assets
The container provides a default Express.js application with health endpoints and Memcached session support if no user application is found.

41
Dockerfile Normal file
View File

@@ -0,0 +1,41 @@
FROM almalinux/9-base
ARG NODEVER=20
# Install repos, update, install only needed packages, clean up in one layer
RUN dnf install -y \
https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm && \
dnf update -y && \
dnf install -y wget procps cronie iproute nginx openssl curl && \
dnf clean all && \
rm -rf /var/cache/dnf /usr/share/doc /usr/share/man /usr/share/locale/* \
/var/cache/yum /tmp/* /var/tmp/*
# Copy scripts into the image and set permissions
COPY ./scripts/ /scripts/
RUN chmod +x /scripts/*
# Generate self-signed cert, create needed dirs, install Node.js, clean up
RUN openssl req -newkey rsa:2048 -nodes -keyout /etc/pki/tls/private/localhost.key -x509 -days 3650 -subj "/CN=localhost" -out /etc/pki/tls/certs/localhost.crt && \
mkdir -p /var/log/nodejs && \
/scripts/install-node$NODEVER.sh && \
rm -rf /tmp/*
# Install PM2 globally for process management with minimal footprint
RUN npm install -g pm2@latest --production && \
npm cache clean --force && \
rm -rf /tmp/*
# Copy configs and web files
COPY ./configs/nginx.conf /etc/nginx/nginx.conf
COPY ./configs/default.conf /etc/nginx/conf.d/default.conf
COPY ./configs/index.js /var/www/html/
COPY ./configs/package.json /var/www/html/
COPY ./configs/ecosystem.config.js /var/www/html/
# Set up cron job for log rotation
RUN echo "15 */12 * * * root /scripts/log-rotate.sh" >> /etc/crontab
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
CMD curl -f http://localhost:3000/ping || exit 1
ENTRYPOINT [ "/scripts/entrypoint.sh" ]

229
MEMORY-GUIDE.md Normal file
View File

@@ -0,0 +1,229 @@
# Memory Usage Guide - Cloud Node Container
## Memory Requirements & Recommendations
### Minimum RAM Requirements
| Configuration | Minimum RAM | Recommended RAM | Use Case |
|---------------|-------------|-----------------|----------|
| **Production (Basic)** | 256MB | 512MB | Simple websites, APIs with <100 concurrent users |
| **Production (Standard)** | 512MB | 1GB | Standard web applications, APIs with moderate traffic |
| **Development** | 512MB | 1GB | Local development with all services (Memcached, logging) |
| **High Traffic** | 1GB | 2GB+ | Applications with >500 concurrent users or heavy processing |
### Memory Breakdown (Optimized Configuration)
| Component | Memory Usage | Notes |
|-----------|--------------|-------|
| **Base AlmaLinux 9** | ~80-120MB | Minimal base system |
| **Node.js Runtime** | ~30-50MB | V8 JavaScript engine |
| **PM2 Process Manager** | ~15-25MB | Process monitoring and management |
| **Nginx (Optimized)** | ~8-15MB | Single worker, limited buffers |
| **User Application** | ~50-200MB | Depends on application complexity |
| **Memcached (DEV mode)** | ~10-15MB | 32MB memory limit, actual usage varies |
| **System Overhead** | ~30-50MB | Logging, cron, system processes |
**Total Typical Usage:** 220-480MB
## Memory Optimizations Applied
### Container-Level Optimizations
1. **Node.js Heap Limit**: Set to 200MB via `--max-old-space-size=200`
2. **PM2 Memory Restart**: Applications restart at 256MB usage
3. **Memcached Limit**: 32MB maximum cache size
4. **Nginx Workers**: Single worker process for memory efficiency
5. **Buffer Limits**: Reduced client buffer sizes
6. **Log Buffering**: 2-minute flush intervals to reduce I/O
### Application-Level Recommendations
```javascript
// Memory-efficient application practices
const express = require('express');
const app = express();
// Use compression to reduce memory for responses
app.use(require('compression')());
// Limit request size
app.use(express.json({ limit: '1mb' }));
app.use(express.urlencoded({ limit: '1mb', extended: true }));
// Stream large files instead of loading into memory
const fs = require('fs');
app.get('/large-file', (req, res) => {
const stream = fs.createReadStream('large-file.pdf');
stream.pipe(res);
});
```
## Memory Monitoring
### Built-in Monitoring Scripts
```bash
# Check current memory usage
docker exec container-name /scripts/memory-info.sh
# Monitor PM2 processes
docker exec container-name pm2 monit
# View memory usage over time
docker stats container-name
```
### Memory Alerts & Automatic Restarts
The container includes automatic memory management:
- **PM2 Restart**: Apps restart if they exceed 256MB
- **Health Checks**: Container monitors `/ping` endpoint
- **Process Recovery**: Failed processes automatically restart
- **Memory Leak Protection**: Regular restarts prevent memory buildup
## Scaling Guidelines
### When to Scale UP (More RAM):
- PM2 shows frequent memory restarts
- Response times increase significantly
- Multiple applications in one container
- Heavy data processing or file handling
- High concurrent user count (>200 simultaneous)
### When to Scale OUT (More Containers):
- CPU usage consistently >80%
- Memory usage consistently >80% of allocation
- Need for geographic distribution
- Fault tolerance requirements
### Memory-Efficient Application Patterns
**Good Practices:**
```javascript
// Stream processing instead of loading entire files
const stream = require('stream');
// Use connection pooling for databases
const mysql = require('mysql2');
const pool = mysql.createPool({
host: 'localhost',
user: 'user',
password: 'password',
database: 'db',
connectionLimit: 5, // Limit connections
acquireTimeout: 60000,
timeout: 60000
});
// Clean up intervals and timeouts
const cleanup = () => {
clearInterval(myInterval);
clearTimeout(myTimeout);
};
process.on('SIGTERM', cleanup);
process.on('SIGINT', cleanup);
```
**Avoid These Memory Patterns:**
```javascript
// DON'T: Store large amounts in memory
let globalCache = {}; // Can grow unbounded
// DON'T: Create memory leaks with closures
setInterval(() => {
const largeData = new Array(1000000).fill('data');
// largeData never gets garbage collected
}, 1000);
// DON'T: Load entire files into memory
const fs = require('fs');
const hugeFile = fs.readFileSync('huge-file.txt'); // Use streams instead
```
## Container Resource Limits
### Setting Memory Limits
```bash
# Limit container memory usage
docker run --memory=512m --memory-swap=512m your-container
# Or in docker-compose.yml
services:
node-app:
mem_limit: 512m
memswap_limit: 512m
```
### Monitoring Memory Usage
```bash
# Real-time memory stats
docker exec container-name cat /proc/meminfo
# Process-specific memory
docker exec container-name ps aux --sort=-%mem
# Application memory usage
docker exec container-name pm2 show app-name
```
## Troubleshooting Memory Issues
### Common Memory Problems
1. **Container OOM (Out of Memory)**
- **Symptom**: Container suddenly stops
- **Solution**: Increase container memory or optimize application
2. **Application Memory Leaks**
- **Symptom**: Memory usage steadily increases
- **Solution**: Use PM2 memory restart feature, fix code leaks
3. **Slow Performance**
- **Symptom**: High response times, high memory usage
- **Solution**: Enable compression, optimize database queries
### Memory Debugging Tools
```bash
# Inside container - check memory usage
free -h
cat /proc/meminfo
ps aux --sort=-%mem
# PM2 memory monitoring
pm2 monit
pm2 show app-name
# Node.js heap snapshots (development)
# Add to your app:
const v8 = require('v8');
const fs = require('fs');
const heapSnapshot = v8.writeHeapSnapshot();
console.log('Heap snapshot written to', heapSnapshot);
```
## Production Memory Recommendations
### Small Applications (< 50 concurrent users)
- **Container RAM**: 512MB
- **Node.js Heap**: 200MB (default)
- **Expected Usage**: 250-350MB
### Medium Applications (50-200 concurrent users)
- **Container RAM**: 1GB
- **Node.js Heap**: 400MB (`--max-old-space-size=400`)
- **Expected Usage**: 500-700MB
### Large Applications (200+ concurrent users)
- **Container RAM**: 2GB+
- **Node.js Heap**: 800MB+ (`--max-old-space-size=800`)
- **Expected Usage**: 1-1.5GB
- **Consider**: Multiple container instances with load balancing
The container is optimized for efficient memory usage while maintaining good performance and reliability.

246
README.md
View File

@@ -1,2 +1,246 @@
# cloud-node-container
# Cloud Node Container
This is a base container for running Node.js applications, supporting multiple Node.js versions (18, 20, 22). The default is Node.js 20. The container is based on AlmaLinux 9 and uses Nginx as a reverse proxy with SSL. It is designed for both development and production use.
**You must have Docker or compatible containerization software running.**
---
## What's Included?
- **Multiple Node.js Versions:** 18, 20, 22 (set with `NODEVER` or `-a` flag)
- **PM2 Process Manager:** For production-grade Node.js application management
- **Nginx Reverse Proxy:** SSL-enabled reverse proxy with automatic HTTP to HTTPS redirect
- **Development Features:** Memcached for sessions, automatic backups, log rotation
- **Health Monitoring:** Built-in `/ping` endpoint for health checks
---
## Quick Start: Local Development with `local-dev.sh`
The easiest way to start a local development environment is with the provided `local-dev.sh` script. This script automates container setup, volume creation, log directories, and creates a sample Node.js application.
### Usage Example
```bash
./local-dev.sh -n local-dev
```
**Flags:**
- `-n` Name of the container (required)
- `-p` HTTP port (default: 80)
- `-s` HTTPS port (default: 443)
- `-r` Root path for files (default: current directory)
- `-a` Node.js version (default: 20; options: 18, 20, 22)
- `-v` Enable verbose mode
- `-h` Show help
The script will:
- Create a user directory and log folders
- Create a default Node.js Express application if none exists
- Start the container with the correct environment variables
- Generate helper scripts in your root path:
- `instance_start` Start the container
- `instance_stop` Stop the container
- `instance_logs` View container logs
- `instance_shell` Access container shell
---
## Manual Docker Usage
You can also run the container manually:
```bash
mkdir -p local-development/domain.tld
cd local-development/domain.tld
mkdir user
mkdir -p user/{app,logs/{nginx,nodejs}}
docker run -d -p 80:80 -p 443:443 -e NODEVER=20 -e environment=DEV --mount type=bind,source="$(pwd)"/user,target=/home/$(whoami) -e uid=$(id -u) -e user=$(whoami) -e domain=localhost --name local-dev cloud-node-container:latest
```
---
## Accessing the Container
```bash
docker exec -it local-dev /bin/bash
```
---
## For Users: How to Deploy Your Application
### Step 1: Prepare Your Node.js Application
Your Node.js application needs just two files to get started:
**Required Files:**
- `package.json` - Describes your app and its dependencies
- A main JavaScript file (usually `server.js` or `index.js`)
**Optional Files:**
- `public/` folder for static files (HTML, CSS, images)
- `ecosystem.config.js` for advanced PM2 configuration
### Step 2: What Users Need to Do
**If you're not familiar with Node.js, here's what you need to know:**
1. **Create a `package.json` file** - This tells Node.js about your app:
```json
{
"name": "my-website",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
```
2. **Create your main server file** - This runs your application:
```javascript
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.use(express.static('public')); // Serve static files
app.get('/', (req, res) => {
res.send('<h1>My Website is Running!</h1>');
});
app.get('/ping', (req, res) => {
res.json({ status: 'ok' }); // Health check for the container
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
```
3. **Put your files in the right place**:
- Copy your `package.json` and `server.js` to `user/app/`
- Put HTML, CSS, images in `user/app/public/`
4. **Start the container**:
```bash
./local-dev.sh -n my-app
```
**That's it!** The container will:
- Install your dependencies automatically
- Start your application with PM2
- Handle SSL and reverse proxy
- Provide health monitoring
### Step 3: Example Applications
See the `examples/` directory for complete working examples:
**Simple Website** (`examples/simple-website/`):
- Static HTML pages with navigation
- CSS styling
- Express.js server for routing
**API Server** (`examples/api-server/`):
- REST API with CRUD operations
- JSON responses
- In-memory data storage
### What Happens Automatically
When you place your app in `user/app/`, the container automatically:
1. Runs `npm install` to install dependencies
2. Starts your app with PM2 process manager
3. Sets up Nginx reverse proxy with SSL
4. Configures logging and health checks
5. Starts Memcached for session storage (in DEV mode)
### Troubleshooting for New Users
- **App won't start?** Check that your `package.json` has a valid "start" script
- **Can't access your site?** Make sure your app listens on port 3000 and has a `/ping` endpoint
- **Dependencies missing?** List all required packages in your `package.json` dependencies section
- **Static files not loading?** Put them in a `public/` folder and use `app.use(express.static('public'))`
---
## Features
- **Multiple Node.js Versions:** 18, 20, 22 (set with `NODEVER` environment variable)
- **Process Management:** PM2 for production-grade Node.js application management
- **Reverse Proxy:** Nginx handles SSL termination and proxies requests to Node.js
- **Automatic Backups:** Application files backed up every 30 minutes in DEV mode
- **Log Management:** Log rotation compresses logs older than 3 days, deletes after 7 days
- **Session Storage:** Memcached available in DEV mode for session management
- **SSL:** Self-signed certificate enabled by default with automatic HTTP→HTTPS redirect
- **Health Checks:** `/ping`, `/info` endpoints for monitoring
- **Static Files:** Nginx serves static files from `/home/$user/app/public/`
---
## Environment Variables
**Required:**
- `uid` User ID for file permissions
- `user` Username for file permissions
- `domain` Primary domain for configuration
**Optional:**
- `environment` Set to `DEV` to start Redis and enable development features
- `serveralias` Comma-separated list of alternative hostnames
- `NODEVER` Node.js version (18, 20, 22)
---
## Default Application Endpoints
The default Express.js application provides:
- `/` Basic application info and status
- `/ping` Health check endpoint (JSON response)
- `/info` Detailed system information
- `/session` Session demo endpoint (shows Memcached sessions working)
---
## Helpful Notes
- To restart the instance: `./instance_start` or `docker start {container-name}`
- To stop: `./instance_stop` or `docker stop {container-name}`
- To view logs: `./instance_logs` or `docker logs -f {container-name}`
- To access shell: `./instance_shell` or `docker exec -it {container-name} /bin/bash`
- To delete a container: `docker rm {container-name}` (does not delete user files)
- Application logs are in `/home/$user/logs/nodejs/`
- Nginx logs are in `/home/$user/logs/nginx/`
---
## System Requirements
### Memory Requirements
- **Minimum RAM**: 256MB (basic applications)
- **Recommended RAM**: 512MB (standard applications)
- **Development**: 512MB-1GB (with all services enabled)
The container is optimized for memory efficiency with automatic memory management and process restarts. See `MEMORY-GUIDE.md` for detailed memory optimization information.
### Performance Features
- Automatic application restart at 256MB memory usage
- V8 heap limited to 200MB by default
- Nginx optimized for single-worker, low-memory operation
- Memcached limited to 32MB cache size
## Troubleshooting
- The first run may take a few minutes as Node.js and dependencies are installed
- If you need to change Node.js version, stop and remove the container, then recreate with the desired version
- For custom applications, ensure your `package.json` has a valid start script
- Check `/home/$user/logs/nodejs/error.log` for application errors
- The health check endpoint `/ping` should return a 200 status when the application is running properly
- **Memory issues**: Run `/scripts/memory-info.sh` inside container to check memory usage
- **Process monitoring**: Use `pm2 monit` to watch application performance

275
USER-GUIDE.md Normal file
View File

@@ -0,0 +1,275 @@
# User Guide: Deploying Applications with Cloud Node Container
## Quick Start (For Non-Node.js Users)
If you've never used Node.js before, don't worry! This container makes it simple.
### What You Need to Know
**Node.js** is like PHP, but for JavaScript. Instead of Apache serving PHP files, your JavaScript code runs as a server application.
**Express.js** is like a web framework (similar to Laravel for PHP) that makes it easy to handle web requests.
### Basic Workflow Comparison
| PHP/Apache Workflow | Node.js Container Workflow |
|---------------------|---------------------------|
| Put `.php` files in web directory | Put `.js` files in `user/app/` |
| Apache serves files directly | Express.js handles requests |
| Database connection in each file | Server runs continuously |
| `<?php echo "Hello"; ?>` | `res.send("Hello")` |
## Step-by-Step: Deploy Your First App
### 1. Create Your Application Files
Create these two files in your `user/app/` directory:
**File: `package.json`**
```json
{
"name": "my-first-app",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
```
**File: `server.js`**
```javascript
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
// Serve static files (HTML, CSS, images) from 'public' folder
app.use(express.static('public'));
// Route for home page
app.get('/', (req, res) => {
res.send(`
<h1>Welcome to My Website!</h1>
<p>This is running on Node.js</p>
<a href="/about">About Page</a>
`);
});
// Route for about page
app.get('/about', (req, res) => {
res.send(`
<h1>About Us</h1>
<p>This website is powered by Node.js and Express</p>
<a href="/">Back to Home</a>
`);
});
// Health check (required by container)
app.get('/ping', (req, res) => {
res.json({ status: 'ok', timestamp: new Date() });
});
// Start the server
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
```
### 2. Start Your Container
```bash
./local-dev.sh -n my-first-app
```
### 3. Access Your Site
Visit `https://localhost` (accept the SSL warning) and you'll see your website!
## Common Use Cases
### Static Website (Like HTML/CSS sites)
Put your HTML files in `user/app/public/` and use this simple server:
```javascript
const express = require('express');
const app = express();
app.use(express.static('public'));
app.get('/ping', (req, res) => res.json({ status: 'ok' }));
app.listen(3000, () => console.log('Static site running'));
```
### Form Processing (Like PHP forms)
```javascript
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true })); // Process form data
app.use(express.static('public'));
// Show contact form
app.get('/contact', (req, res) => {
res.send(`
<form method="POST" action="/contact">
<input name="name" placeholder="Your Name" required>
<input name="email" type="email" placeholder="Email" required>
<textarea name="message" placeholder="Message" required></textarea>
<button type="submit">Send</button>
</form>
`);
});
// Process form submission
app.post('/contact', (req, res) => {
const { name, email, message } = req.body;
// Here you would save to database, send email, etc.
console.log('Contact form:', { name, email, message });
res.send(`<h1>Thanks ${name}!</h1><p>We received your message.</p>`);
});
app.get('/ping', (req, res) => res.json({ status: 'ok' }));
app.listen(3000);
```
### Database Connection (Like MySQL in PHP)
```javascript
const express = require('express');
const app = express();
// In package.json dependencies, add: "mysql2": "^3.6.0"
const mysql = require('mysql2');
const db = mysql.createConnection({
host: 'localhost',
user: 'your_user',
password: 'your_password',
database: 'your_database'
});
app.get('/users', (req, res) => {
db.query('SELECT * FROM users', (err, results) => {
if (err) {
res.status(500).json({ error: err.message });
} else {
res.json(results);
}
});
});
app.get('/ping', (req, res) => res.json({ status: 'ok' }));
app.listen(3000);
```
## File Structure Examples
### Simple Website
```
user/app/
├── package.json
├── server.js
└── public/
├── index.html
├── about.html
├── style.css
└── images/
└── logo.png
```
### API Application
```
user/app/
├── package.json
├── server.js
├── routes/
│ ├── users.js
│ └── products.js
└── public/
└── api-docs.html
```
## Key Differences from PHP
| Concept | PHP | Node.js |
|---------|-----|---------|
| **File serving** | Apache serves .php files directly | Express routes handle requests |
| **Variables** | `$variable` | `let variable` |
| **Arrays** | `$arr = array()` | `let arr = []` |
| **Echo/Print** | `echo "Hello"` | `res.send("Hello")` |
| **Include files** | `include 'file.php'` | `require('./file.js')` |
| **Database** | Connect in each file | Connect once, reuse connection |
| **Sessions** | `$_SESSION` | `req.session` (with middleware) |
| **Forms** | `$_POST` | `req.body` (with middleware) |
## Troubleshooting
### Common Issues for PHP Users
**Issue:** "Cannot GET /" error
**Solution:** Make sure you have an `app.get('/', ...)` route defined
**Issue:** Static files (CSS/images) not loading
**Solution:** Put them in `public/` folder and add `app.use(express.static('public'))`
**Issue:** Forms not working
**Solution:** Add `app.use(express.urlencoded({ extended: true }))` before your routes
**Issue:** App crashes on restart
**Solution:** PM2 will automatically restart it, but check logs in `user/logs/nodejs/`
### Getting Help
- Check container logs: `./instance_logs`
- Check your app specifically: `docker exec -it your-container-name pm2 logs`
- Access container shell: `./instance_shell`
- Health check: Visit `/ping` to see if your app is responding
## Advanced Features
### Using Sessions (like PHP $_SESSION)
```javascript
// Add to package.json dependencies: "express-session": "^1.17.3"
const session = require('express-session');
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: false
}));
app.get('/login', (req, res) => {
req.session.user = 'john_doe';
res.send('Logged in!');
});
app.get('/profile', (req, res) => {
if (req.session.user) {
res.send(`Hello ${req.session.user}!`);
} else {
res.send('Please log in');
}
});
```
### File Uploads
```javascript
// Add to package.json: "multer": "^1.4.5-lts.1"
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('file'), (req, res) => {
res.send(`File uploaded: ${req.file.originalname}`);
});
```
The container handles all the complex server setup, so you can focus on building your application!

View File

@@ -0,0 +1,31 @@
module.exports = {
apps: [{
name: 'node-app',
script: 'index.js',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '256M', // Restart if app uses more than 256MB
kill_timeout: 3000,
wait_ready: true,
listen_timeout: 3000,
env: {
NODE_ENV: 'development',
PORT: 3000,
NODE_OPTIONS: '--max-old-space-size=200' // Limit V8 heap to 200MB
},
env_production: {
NODE_ENV: 'production',
PORT: 3000,
NODE_OPTIONS: '--max-old-space-size=200'
},
log_file: '/home/myuser/logs/nodejs/app.log',
error_file: '/home/myuser/logs/nodejs/error.log',
out_file: '/home/myuser/logs/nodejs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
log_type: 'json',
merge_logs: true,
max_restarts: 5,
min_uptime: '10s'
}]
};

101
configs/index.js Normal file
View File

@@ -0,0 +1,101 @@
const express = require('express');
const session = require('express-session');
const app = express();
const port = process.env.PORT || 3000;
// Middleware
app.use(express.json());
app.use(express.static('public'));
// Session configuration with Memcache (only in DEV mode when memcached is available)
if (process.env.NODE_ENV !== 'production') {
try {
const MemcachedStore = require('connect-memcached')(session);
app.use(session({
store: new MemcachedStore({
hosts: ['localhost:11211']
}),
secret: process.env.SESSION_SECRET || 'your-secret-key-change-in-production',
resave: false,
saveUninitialized: false,
cookie: {
secure: false, // Allow HTTP in development
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}
}));
console.log('Memcached session store initialized');
} catch (err) {
console.log('Memcached not available, using memory store');
app.use(session({
secret: process.env.SESSION_SECRET || 'your-secret-key-change-in-production',
resave: false,
saveUninitialized: false,
cookie: {
secure: false,
maxAge: 24 * 60 * 60 * 1000
}
}));
}
} else {
// Production session configuration (expects external session store)
app.use(session({
secret: process.env.SESSION_SECRET || 'your-secret-key-change-in-production',
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only in production
maxAge: 24 * 60 * 60 * 1000
}
}));
}
// Health check endpoint
app.get('/ping', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
version: process.env.npm_package_version || '1.0.0'
});
});
// Default route
app.get('/', (req, res) => {
res.json({
message: 'Cloud Node Container is running!',
nodeVersion: process.version,
environment: process.env.NODE_ENV || 'development',
timestamp: new Date().toISOString()
});
});
// Info endpoint
app.get('/info', (req, res) => {
res.json({
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
uptime: process.uptime(),
memory: process.memoryUsage(),
env: process.env.NODE_ENV || 'development'
});
});
// Session demo endpoint
app.get('/session', (req, res) => {
if (!req.session.visits) {
req.session.visits = 0;
}
req.session.visits++;
res.json({
sessionId: req.sessionID,
visits: req.session.visits,
message: 'Session is working with ' + (process.env.NODE_ENV !== 'production' ? 'Memcached' : 'default store')
});
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
});

45
configs/nginx.conf Normal file
View File

@@ -0,0 +1,45 @@
user nginx;
worker_processes 1; # Single worker for memory efficiency
worker_rlimit_nofile 1024;
error_log /var/log/nginx/error.log warn; # Less verbose logging
pid /run/nginx.pid;
events {
worker_connections 512; # Reduced for memory efficiency
use epoll;
multi_accept on;
}
http {
# Memory-optimized settings
client_body_buffer_size 16k;
client_header_buffer_size 1k;
client_max_body_size 8m;
large_client_header_buffers 2 1k;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main buffer=16k flush=2m;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30; # Reduced from 65
keepalive_requests 100;
types_hash_max_size 1024; # Reduced from 2048
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Optimized gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 2; # Lower compression for less CPU/memory usage
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
include /etc/nginx/conf.d/*.conf;
}

27
configs/package.json Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "cloud-node-container",
"version": "1.0.0",
"description": "Default Node.js application for Cloud Node Container",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"express": "^4.18.2",
"express-session": "^1.17.3",
"connect-memcached": "^1.0.0"
},
"devDependencies": {
"nodemon": "^3.0.1"
},
"keywords": [
"nodejs",
"express",
"container",
"docker"
],
"author": "",
"license": "MIT"
}

View File

@@ -0,0 +1,12 @@
{
"name": "simple-api",
"version": "1.0.0",
"description": "A simple REST API server",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}

View File

@@ -0,0 +1,83 @@
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
// Middleware
app.use(express.json());
// In-memory data store (in production, you'd use a database)
let users = [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];
let nextId = 3;
// Health check endpoint
app.get('/ping', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// API Routes
app.get('/api/users', (req, res) => {
res.json(users);
});
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: 'Name and email are required' });
}
const user = { id: nextId++, name, email };
users.push(user);
res.status(201).json(user);
});
app.put('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const { name, email } = req.body;
if (name) user.name = name;
if (email) user.email = email;
res.json(user);
});
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ error: 'User not found' });
}
users.splice(index, 1);
res.status(204).send();
});
// Basic documentation endpoint
app.get('/', (req, res) => {
res.json({
message: 'Simple API Server',
endpoints: {
'GET /api/users': 'Get all users',
'GET /api/users/:id': 'Get user by ID',
'POST /api/users': 'Create new user',
'PUT /api/users/:id': 'Update user by ID',
'DELETE /api/users/:id': 'Delete user by ID'
}
});
});
app.listen(port, () => {
console.log(`API server running on port ${port}`);
});

View File

@@ -0,0 +1,12 @@
{
"name": "simple-website",
"version": "1.0.0",
"description": "A simple website with static pages",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About - My Simple Website</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<main>
<h1>About Us</h1>
<p>This website is powered by Node.js and Express, running in a containerized environment.</p>
<p>It demonstrates how easy it is to deploy web applications with the Cloud Node Container.</p>
</main>
</body>
</html>

View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact - My Simple Website</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<main>
<h1>Contact Us</h1>
<p>Get in touch with us!</p>
<form>
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<label for="message">Message:</label>
<textarea id="message" name="message" required></textarea>
<button type="submit">Send Message</button>
</form>
</main>
</body>
</html>

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Simple Website</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<main>
<h1>Welcome to My Website</h1>
<p>This is a simple website running on Node.js in a Docker container!</p>
<p>Current time: <span id="time"></span></p>
</main>
<script>
document.getElementById('time').textContent = new Date().toLocaleString();
</script>
</body>
</html>

View File

@@ -0,0 +1,65 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
nav {
background-color: #333;
padding: 1rem;
}
nav a {
color: white;
text-decoration: none;
margin-right: 1rem;
padding: 0.5rem;
}
nav a:hover {
background-color: #555;
border-radius: 4px;
}
main {
max-width: 800px;
margin: 2rem auto;
padding: 2rem;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
color: #333;
}
form {
display: flex;
flex-direction: column;
gap: 1rem;
}
label {
font-weight: bold;
}
input, textarea {
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 0.75rem;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}

View File

@@ -0,0 +1,29 @@
const express = require('express');
const path = require('path');
const app = express();
const port = process.env.PORT || 3000;
// Serve static files from public directory
app.use(express.static('public'));
// Basic routes
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.get('/about', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'about.html'));
});
app.get('/contact', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'contact.html'));
});
// Health check for the container
app.get('/ping', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
app.listen(port, () => {
console.log(`Simple website running on port ${port}`);
});

95
local-dev.sh Executable file
View File

@@ -0,0 +1,95 @@
#!/usr/bin/env bash
name=''
http_port='80'
https_port='443'
root_path="$(pwd)"
verbose='false'
while getopts 'n:p:s:r:a:vh' flag; do
case "${flag}" in
n) name="${OPTARG}" ;;
p) http_port="${OPTARG}" ;;
s) https_port="${OPTARG}" ;;
r) root_path="${OPTARG}" ;;
a) nodever="${OPTARG}" ;;
v) verbose='true' ;;
h) echo "Variables"
echo "-n = Name of Container, Required"
echo "-p = Non-https Port Override, default 80"
echo "-s = Https Port Override, default 443"
echo "-r = Root Path for files, defaults to current working path"
echo "-a = Node.js Version, Default to 20 (options: 18, 20, 22)"
echo "-v = Enable Verbose Mode"
exit 1 ;;
esac
done
# Check if Docker is installed
echo "Checking if Docker is Installed..."
check_docker=$(command -v docker)
if [ $? != 0 ]; then
echo "Docker must be installed to run this application"
exit 1
fi
echo "Docker looks to be installed"
if [ -z "$name" ]; then
echo "Name not set, please set it with -n"
exit 1
fi
if [ -z "$nodever" ]; then
nodever=20;
fi
echo "Building Docker Image..."
user=$(whoami)
uid=$(id -u)
if [ ! -d "$root_path/user" ]; then
mkdir -p "$root_path/user";
mkdir -p "$root_path/user/logs/{nginx,nodejs}";
mkdir -p "$root_path/user/app";
fi
# Create default Node.js app if it doesn't exist
if [ ! -f "$root_path/user/app/package.json" ]; then
echo "Creating default Node.js application..."
cp configs/package.json "$root_path/user/app/"
cp configs/index.js "$root_path/user/app/"
cp configs/ecosystem.config.js "$root_path/user/app/"
# Update ecosystem config with correct user path
sed -i "s/\/home\/myuser/\/home\/$user/g" "$root_path/user/app/ecosystem.config.js"
fi
$check_docker run --pull=always -d -p "$http_port":80 -p "$https_port":443 -e NODEVER=$nodever -e environment=DEV --mount type=bind,source="$root_path"/user,target=/home/"$user" --mount type=bind,source="$(pwd)"/user/logs/nginx,target=/var/log/nginx --mount type=bind,source="$(pwd)"/user/logs/nodejs,target=/var/log/nodejs -e uid="$uid" -e user="$user" -e domain="$name-local.dev" --name "$name" cloud-node-container:latest
echo "Creating management scripts in root directory..."
echo "#!/usr/bin/env bash" > "$root_path/instance_start"
echo "docker start $name" >> "$root_path/instance_start"
echo "#!/usr/bin/env bash" > "$root_path/instance_stop"
echo "docker stop $name" >> "$root_path/instance_stop"
echo "#!/usr/bin/env bash" > "$root_path/instance_logs"
echo "docker logs -f $name" >> "$root_path/instance_logs"
echo "#!/usr/bin/env bash" > "$root_path/instance_shell"
echo "docker exec -it $name /bin/bash" >> "$root_path/instance_shell"
chmod +x $root_path/instance_*
echo "Waiting 30 seconds for setup to finish..."
sleep 30
echo "Local Development Instance Created!"
echo "- Container name: $name"
echo "- HTTP port: $http_port"
echo "- HTTPS port: $https_port"
echo "- Node.js version: $nodever"
echo ""
echo "Management scripts created:"
echo " ./instance_start - Start the container"
echo " ./instance_stop - Stop the container"
echo " ./instance_logs - View container logs"
echo " ./instance_shell - Access container shell"
echo ""
echo "Your application files are in: $root_path/user/app/"
echo "Visit https://localhost:$https_port (accept SSL warning) to see your app"
exit 0

17
scripts/backup.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
USER=$1
BACKUP_DIR="/home/$USER/_backups"
DATE=$(date +%Y%m%d_%H%M%S)
# Create backup directory if it doesn't exist
mkdir -p $BACKUP_DIR
# Backup application files
tar -czf $BACKUP_DIR/app_backup_$DATE.tar.gz -C /home/$USER app/
# Keep only last 10 backups
cd $BACKUP_DIR
ls -t app_backup_*.tar.gz | tail -n +11 | xargs -r rm
echo "Backup completed: app_backup_$DATE.tar.gz"

54
scripts/create-nginx-config.sh Executable file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# Create nginx configuration for reverse proxy to Node.js app
cat > /etc/nginx/conf.d/default.conf << EOF
upstream nodejs_backend {
server 127.0.0.1:3000;
}
server {
listen 80;
server_name $domain $serveralias;
# Redirect HTTP to HTTPS
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name $domain $serveralias;
ssl_certificate /etc/pki/tls/certs/localhost.crt;
ssl_certificate_key /etc/pki/tls/private/localhost.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
access_log /home/$user/logs/nginx/access.log;
error_log /home/$user/logs/nginx/error.log;
location / {
proxy_pass http://nodejs_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_bypass \$http_upgrade;
}
location /ping {
proxy_pass http://nodejs_backend/ping;
access_log off;
}
# Static files
location /static/ {
alias /home/$user/app/public/;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
EOF

68
scripts/entrypoint.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
if [ -z "$NODEVER" ]; then
NODEVER="20";
fi
if [ -z "$environment" ]; then
environment="PROD"
fi
adduser -u $uid $user
mkdir -p /home/$user/app
mkdir -p /home/$user/logs/{nginx,nodejs}
# Link log directories
rm -rf /var/log/nginx
ln -s /home/$user/logs/nginx /var/log/nginx
ln -s /home/$user/logs/nodejs /var/log/nodejs
# Configure nginx for reverse proxy
/scripts/create-nginx-config.sh
# Set ownership and permissions
chown -R $user:$user /home/$user
chmod -R 755 /home/$user
# Start nginx
nginx
if [[ $environment == 'DEV' ]]; then
echo "Starting Dev Deployment"
mkdir -p /home/$user/_backups
# Ensure microdnf is available for installing additional packages in DEV mode
if ! command -v microdnf &> /dev/null; then
echo "microdnf not found, installing with dnf..."
dnf install -y microdnf && dnf clean all
fi
# Install Memcached for session storage in DEV mode with memory limit
microdnf install -y memcached
# Start memcached with 32MB memory limit
nohup memcached -d -u $user -p 11211 -m 32
# Set up automatic backups
echo "*/30 * * * * root /scripts/backup.sh $user" >> /etc/crontab
fi
# Start cron for log rotation and backups
/usr/sbin/crond
# If there's an app in the user directory, start it with PM2
if [ -f /home/$user/app/package.json ]; then
cd /home/$user/app
su -c "npm install" $user
su -c "pm2 start ecosystem.config.js" $user
else
# Start default app
cd /var/www/html
npm install
su -c "pm2 start ecosystem.config.js" $user
fi
# Follow logs
tail -f /home/$user/logs/nginx/* /home/$user/logs/nodejs/*
exit 0

6
scripts/install-node18.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
curl -fsSL https://rpm.nodesource.com/setup_18.x | bash -
dnf install -y nodejs
npm install -g npm@latest
node --version
npm --version

6
scripts/install-node20.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
curl -fsSL https://rpm.nodesource.com/setup_20.x | bash -
dnf install -y nodejs
npm install -g npm@latest
node --version
npm --version

6
scripts/install-node22.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
curl -fsSL https://rpm.nodesource.com/setup_22.x | bash -
dnf install -y nodejs
npm install -g npm@latest
node --version
npm --version

18
scripts/log-rotate.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Log rotation script for nginx and Node.js logs
LOG_DIR="/home/*/logs"
# Compress logs older than 3 days
find $LOG_DIR -name "*.log" -type f -mtime +3 -exec gzip {} \;
# Delete compressed logs older than 7 days
find $LOG_DIR -name "*.gz" -type f -mtime +7 -delete
# Rotate nginx logs
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
fi
# Rotate PM2 logs
pm2 flush

58
scripts/memory-info.sh Executable file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env bash
echo "=== Cloud Node Container Memory Usage ==="
echo "Date: $(date)"
echo ""
echo "=== System Memory ==="
free -h
echo ""
echo "=== Process Memory Usage ==="
echo "Process PID VSZ RSS %MEM"
echo "----------------------------------------"
# Nginx
nginx_pid=$(pgrep nginx | head -1)
if [ ! -z "$nginx_pid" ]; then
ps -o pid,vsz,rss,%mem,comm -p $nginx_pid | tail -n +2 | while read pid vsz rss mem comm; do
printf "%-25s %5s %6s %6s %5s\n" "$comm" "$pid" "${vsz}K" "${rss}K" "$mem%"
done
fi
# Node.js/PM2
pm2_pids=$(pgrep -f "PM2\|node")
if [ ! -z "$pm2_pids" ]; then
echo "$pm2_pids" | while read pid; do
ps -o pid,vsz,rss,%mem,comm -p $pid 2>/dev/null | tail -n +2 | while read pid vsz rss mem comm; do
printf "%-25s %5s %6s %6s %5s\n" "$comm" "$pid" "${vsz}K" "${rss}K" "$mem%"
done
done
fi
# Memcached
memcached_pid=$(pgrep memcached)
if [ ! -z "$memcached_pid" ]; then
ps -o pid,vsz,rss,%mem,comm -p $memcached_pid | tail -n +2 | while read pid vsz rss mem comm; do
printf "%-25s %5s %6s %6s %5s\n" "$comm" "$pid" "${vsz}K" "${rss}K" "$mem%"
done
fi
echo ""
echo "=== Container Resource Limits ==="
if [ -f /sys/fs/cgroup/memory/memory.limit_in_bytes ]; then
limit=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)
if [ $limit -lt 9223372036854775807 ]; then
echo "Memory Limit: $((limit / 1024 / 1024))MB"
else
echo "Memory Limit: No limit set"
fi
fi
echo ""
echo "=== PM2 Process Info ==="
if command -v pm2 &> /dev/null; then
pm2 list 2>/dev/null | grep -E "(App name|online|stopped|errored)"
echo ""
pm2 show 0 2>/dev/null | grep -E "(memory|cpu|uptime)" || echo "No PM2 processes running"
fi