|
| 1 | +# Creating OpenNebula Appliances - Automatic Method |
| 2 | + |
| 3 | +**Quick appliance creation using the generator script (5 minutes)** |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## 📖 Introduction |
| 8 | + |
| 9 | +This guide shows you how to quickly create OpenNebula appliances from Docker containers using an automated generator script. The generator creates all necessary files following the proven Phoenix RTOS/Node-RED structure. |
| 10 | + |
| 11 | +**What you'll create:** |
| 12 | +- A VM image (QCOW2 format) with Ubuntu 22.04 + Docker |
| 13 | +- Automatic Docker container startup on VM boot |
| 14 | +- SSH access with password and key authentication |
| 15 | +- Console and serial console auto-login |
| 16 | +- OpenNebula context integration for runtime configuration |
| 17 | + |
| 18 | +**Time required:** ~5 minutes for generation + 15-20 minutes for building |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## ✅ Prerequisites |
| 23 | + |
| 24 | +- Linux system (Ubuntu 22.04+ recommended) |
| 25 | +- Git |
| 26 | +- Packer (for building the image) |
| 27 | +- QEMU/KVM (for building the image) |
| 28 | + |
| 29 | +```bash |
| 30 | +sudo apt update |
| 31 | +sudo apt install -y git qemu-kvm qemu-utils |
| 32 | +``` |
| 33 | + |
| 34 | +--- |
| 35 | + |
| 36 | +## 🚀 Quick Start |
| 37 | + |
| 38 | +### Step 1: Clone Repository |
| 39 | + |
| 40 | +```bash |
| 41 | +git clone https://github.com/OpenNebula/marketplace-community.git |
| 42 | +cd marketplace-community |
| 43 | +``` |
| 44 | + |
| 45 | +### Step 2: Create Configuration File |
| 46 | + |
| 47 | +Create a `.env` file with your Docker container details: |
| 48 | + |
| 49 | +```bash |
| 50 | +cd docs/automatic-appliance-tutorial |
| 51 | + |
| 52 | +cat > myapp.env << 'ENVEOF' |
| 53 | +# Required variables |
| 54 | +DOCKER_IMAGE="your-docker-image:tag" |
| 55 | +APPLIANCE_NAME="myapp" |
| 56 | +APP_NAME="MyApp" |
| 57 | +PUBLISHER_NAME="Your Name" |
| 58 | +PUBLISHER_EMAIL="your.email@example.com" |
| 59 | +
|
| 60 | +# Optional variables |
| 61 | +APP_DESCRIPTION="MyApp description" |
| 62 | +APP_FEATURES="Feature 1,Feature 2,Feature 3" |
| 63 | +DEFAULT_CONTAINER_NAME="myapp-container" |
| 64 | +DEFAULT_PORTS="8080:8080" |
| 65 | +DEFAULT_ENV_VARS="" |
| 66 | +DEFAULT_VOLUMES="/data:/data" |
| 67 | +APP_PORT="8080" |
| 68 | +WEB_INTERFACE="true" |
| 69 | +ENVEOF |
| 70 | +``` |
| 71 | + |
| 72 | +### Step 3: Run Generator |
| 73 | + |
| 74 | +```bash |
| 75 | +./generate-docker-appliance.sh myapp.env |
| 76 | +``` |
| 77 | + |
| 78 | +The generator will: |
| 79 | +1. Create all appliance files |
| 80 | +2. Generate Packer configuration |
| 81 | +3. Prompt you to build the image immediately |
| 82 | + |
| 83 | +**Output:** |
| 84 | +``` |
| 85 | +🚀 Loading configuration from myapp.env |
| 86 | +🎯 Generating complete appliance: myapp (MyApp) |
| 87 | +📁 Creating directory structure... |
| 88 | +✅ Directory structure created |
| 89 | +📝 Generating metadata.yaml... |
| 90 | +✅ Metadata files generated |
| 91 | +📝 Generating README.md... |
| 92 | +✅ README.md generated |
| 93 | +📝 Generating appliance.sh installation script... |
| 94 | +✅ appliance.sh generated |
| 95 | +📝 Generating Packer configuration files... |
| 96 | +✅ Packer configuration files generated |
| 97 | +🎉 Appliance 'myapp' generated successfully! |
| 98 | +
|
| 99 | +Do you want to build the image now? (y/n): |
| 100 | +``` |
| 101 | + |
| 102 | +### Step 4: Build the Image |
| 103 | + |
| 104 | +If you answered 'y' to the prompt, the build starts automatically. Otherwise: |
| 105 | + |
| 106 | +```bash |
| 107 | +cd ../../apps-code/community-apps |
| 108 | +make myapp |
| 109 | +``` |
| 110 | + |
| 111 | +**Build time:** 15-20 minutes (downloads Ubuntu, installs Docker, pulls your container image) |
| 112 | + |
| 113 | +**Output file:** `export/myapp.qcow2` |
| 114 | + |
| 115 | +--- |
| 116 | + |
| 117 | +## 📋 Configuration Variables |
| 118 | + |
| 119 | +| Variable | Required | Description | Example | |
| 120 | +|----------|----------|-------------|---------| |
| 121 | +| `DOCKER_IMAGE` | Yes | Docker image name | `nginx:alpine` | |
| 122 | +| `APPLIANCE_NAME` | Yes | Lowercase name (no spaces) | `nginx` | |
| 123 | +| `APP_NAME` | Yes | Display name | `NGINX Web Server` | |
| 124 | +| `PUBLISHER_NAME` | Yes | Your name | `John Doe` | |
| 125 | +| `PUBLISHER_EMAIL` | Yes | Your email | `john@example.com` | |
| 126 | +| `APP_DESCRIPTION` | No | Full description | `NGINX is a web server...` | |
| 127 | +| `APP_FEATURES` | No | Comma-separated features | `Web Server,Reverse Proxy` | |
| 128 | +| `DEFAULT_CONTAINER_NAME` | No | Container name | `nginx-container` | |
| 129 | +| `DEFAULT_PORTS` | No | Port mappings | `80:80,443:443` | |
| 130 | +| `DEFAULT_ENV_VARS` | No | Environment variables | `KEY=value,KEY2=value2` | |
| 131 | +| `DEFAULT_VOLUMES` | No | Volume mappings | `/data:/data,/config:/config` | |
| 132 | +| `APP_PORT` | No | Main application port | `80` | |
| 133 | +| `WEB_INTERFACE` | No | Has web UI? | `true` or `false` | |
| 134 | + |
| 135 | +--- |
| 136 | + |
| 137 | +## 📁 Generated Files |
| 138 | + |
| 139 | +The generator creates: |
| 140 | + |
| 141 | +``` |
| 142 | +marketplace-community/ |
| 143 | +├── appliances/myapp/ |
| 144 | +│ ├── appliance.sh # Installation script |
| 145 | +│ ├── metadata.yaml # Build configuration |
| 146 | +│ ├── <uuid>.yaml # Marketplace metadata |
| 147 | +│ ├── README.md # Documentation |
| 148 | +│ ├── CHANGELOG.md # Version history |
| 149 | +│ └── tests/ |
| 150 | +│ └── tests.yaml # Test configuration |
| 151 | +└── apps-code/community-apps/packer/myapp/ |
| 152 | + ├── myapp.pkr.hcl # Packer build file |
| 153 | + └── myapp.auto.pkrvars.hcl # Packer variables |
| 154 | +``` |
| 155 | + |
| 156 | +--- |
| 157 | + |
| 158 | +## 🔧 Customization |
| 159 | + |
| 160 | +After generation, you can customize the files: |
| 161 | + |
| 162 | +### Modify Container Configuration |
| 163 | + |
| 164 | +Edit `appliances/myapp/appliance.sh`: |
| 165 | + |
| 166 | +```bash |
| 167 | +# Change default values |
| 168 | +DEFAULT_CONTAINER_NAME="custom-name" |
| 169 | +DEFAULT_PORTS="8080:8080,8443:8443" |
| 170 | +DEFAULT_ENV_VARS="DEBUG=true,LOG_LEVEL=info" |
| 171 | +``` |
| 172 | + |
| 173 | +### Add Custom Installation Steps |
| 174 | + |
| 175 | +Add to the `service_install()` function in `appliance.sh`: |
| 176 | + |
| 177 | +```bash |
| 178 | +service_install() |
| 179 | +{ |
| 180 | + # ... existing Docker installation ... |
| 181 | + |
| 182 | + # Add your custom steps here |
| 183 | + apt-get install -y additional-package |
| 184 | + |
| 185 | + # Custom configuration |
| 186 | + echo "custom config" > /etc/myapp.conf |
| 187 | +} |
| 188 | +``` |
| 189 | + |
| 190 | +### Rebuild After Changes |
| 191 | + |
| 192 | +```bash |
| 193 | +cd apps-code/community-apps |
| 194 | +make clean |
| 195 | +make myapp |
| 196 | +``` |
| 197 | + |
| 198 | +--- |
| 199 | + |
| 200 | +## 📦 Examples |
| 201 | + |
| 202 | +See `docs/automatic-appliance-tutorial/examples/` for complete working examples: |
| 203 | + |
| 204 | +### NGINX Web Server |
| 205 | + |
| 206 | +```bash |
| 207 | +cat > nginx.env << 'EOF' |
| 208 | +DOCKER_IMAGE="nginx:alpine" |
| 209 | +APPLIANCE_NAME="nginx" |
| 210 | +APP_NAME="NGINX" |
| 211 | +PUBLISHER_NAME="Your Name" |
| 212 | +PUBLISHER_EMAIL="your@email.com" |
| 213 | +DEFAULT_PORTS="80:80,443:443" |
| 214 | +APP_PORT="80" |
| 215 | +WEB_INTERFACE="true" |
| 216 | +EOF |
| 217 | + |
| 218 | +./generate-docker-appliance.sh nginx.env |
| 219 | +``` |
| 220 | + |
| 221 | +### Node-RED |
| 222 | + |
| 223 | +```bash |
| 224 | +cat > nodered.env << 'EOF' |
| 225 | +DOCKER_IMAGE="nodered/node-red:latest" |
| 226 | +APPLIANCE_NAME="nodered" |
| 227 | +APP_NAME="Node-RED" |
| 228 | +PUBLISHER_NAME="Your Name" |
| 229 | +PUBLISHER_EMAIL="your@email.com" |
| 230 | +DEFAULT_PORTS="1880:1880" |
| 231 | +DEFAULT_VOLUMES="/data:/data" |
| 232 | +APP_PORT="1880" |
| 233 | +WEB_INTERFACE="true" |
| 234 | +EOF |
| 235 | + |
| 236 | +./generate-docker-appliance.sh nodered.env |
| 237 | +``` |
| 238 | + |
| 239 | +### PostgreSQL Database |
| 240 | + |
| 241 | +```bash |
| 242 | +cat > postgres.env << 'EOF' |
| 243 | +DOCKER_IMAGE="postgres:16-alpine" |
| 244 | +APPLIANCE_NAME="postgres" |
| 245 | +APP_NAME="PostgreSQL" |
| 246 | +PUBLISHER_NAME="Your Name" |
| 247 | +PUBLISHER_EMAIL="your@email.com" |
| 248 | +DEFAULT_PORTS="5432:5432" |
| 249 | +DEFAULT_ENV_VARS="POSTGRES_PASSWORD=changeme" |
| 250 | +DEFAULT_VOLUMES="/var/lib/postgresql/data:/var/lib/postgresql/data" |
| 251 | +APP_PORT="5432" |
| 252 | +WEB_INTERFACE="false" |
| 253 | +EOF |
| 254 | + |
| 255 | +./generate-docker-appliance.sh postgres.env |
| 256 | +``` |
| 257 | + |
| 258 | +--- |
| 259 | + |
| 260 | +## 🧪 Testing Your Appliance |
| 261 | + |
| 262 | +### 1. Test Locally with QEMU |
| 263 | + |
| 264 | +```bash |
| 265 | +cd apps-code/community-apps/export |
| 266 | + |
| 267 | +# Start VM with QEMU |
| 268 | +qemu-system-x86_64 \ |
| 269 | + -enable-kvm \ |
| 270 | + -m 2048 \ |
| 271 | + -smp 2 \ |
| 272 | + -drive file=myapp.qcow2,format=qcow2 \ |
| 273 | + -net nic -net user,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:8080 \ |
| 274 | + -vnc :0 |
| 275 | +``` |
| 276 | + |
| 277 | +Connect via VNC to `localhost:5900` and verify: |
| 278 | +- Console auto-login works |
| 279 | +- Docker container is running: `docker ps` |
| 280 | +- Application is accessible |
| 281 | + |
| 282 | +### 2. Test on OpenNebula |
| 283 | + |
| 284 | +See the [Manual Appliance Guide](MANUAL_APPLIANCE_GUIDE.md#deploying-to-opennebula) for detailed deployment instructions. |
| 285 | + |
| 286 | +--- |
| 287 | + |
| 288 | +## 🐛 Troubleshooting |
| 289 | + |
| 290 | +### Generator Fails |
| 291 | + |
| 292 | +**Problem:** Missing required variables |
| 293 | +**Solution:** Check all required variables are set in .env file |
| 294 | + |
| 295 | +```bash |
| 296 | +# Verify your .env file has all required fields |
| 297 | +grep -E "DOCKER_IMAGE|APPLIANCE_NAME|APP_NAME|PUBLISHER" myapp.env |
| 298 | +``` |
| 299 | + |
| 300 | +### Build Fails |
| 301 | + |
| 302 | +**Problem:** Packer build fails |
| 303 | +**Solution:** Check Packer logs |
| 304 | + |
| 305 | +```bash |
| 306 | +cd apps-code/community-apps |
| 307 | +make myapp 2>&1 | tee build.log |
| 308 | +``` |
| 309 | + |
| 310 | +Common issues: |
| 311 | +- Network connectivity (can't download Ubuntu ISO) |
| 312 | +- Insufficient disk space |
| 313 | +- Docker image doesn't exist or is private |
| 314 | + |
| 315 | +### Container Doesn't Start |
| 316 | + |
| 317 | +**Problem:** Container fails to start on VM boot |
| 318 | +**Solution:** Check the generated `appliance.sh` for correct Docker image name |
| 319 | + |
| 320 | +```bash |
| 321 | +# Verify Docker image name in appliance.sh |
| 322 | +grep "DOCKER_IMAGE=" appliances/myapp/appliance.sh |
| 323 | +``` |
| 324 | + |
| 325 | +### Permission Issues |
| 326 | + |
| 327 | +**Problem:** Container can't write to volumes |
| 328 | +**Solution:** The generator automatically sets ownership to `1000:1000`. If your container uses a different UID, edit `appliance.sh`: |
| 329 | + |
| 330 | +```bash |
| 331 | +# In service_install() function |
| 332 | +mkdir -p /data |
| 333 | +chown 1001:1001 /data # Change to your container's UID:GID |
| 334 | +``` |
| 335 | + |
| 336 | +--- |
| 337 | + |
| 338 | +## 📤 Next Steps |
| 339 | + |
| 340 | +After successfully building and testing your appliance: |
| 341 | + |
| 342 | +1. **Add a logo** - Create a 256x256 PNG logo at `logos/myapp.png` |
| 343 | +2. **Test thoroughly** - Deploy on OpenNebula and verify all functionality |
| 344 | +3. **Submit to marketplace** - See [Manual Appliance Guide](MANUAL_APPLIANCE_GUIDE.md#submitting-to-marketplace) for PR instructions |
| 345 | + |
| 346 | +--- |
| 347 | + |
| 348 | +## 💡 Tips |
| 349 | + |
| 350 | +- **Start simple** - Begin with minimal configuration, add features incrementally |
| 351 | +- **Use official images** - Prefer official Docker images from Docker Hub |
| 352 | +- **Test the Docker image first** - Run `docker run` locally before generating appliance |
| 353 | +- **Check examples** - Study the example .env files for reference |
| 354 | +- **Volume permissions** - If container runs as non-root, ensure volume directories have correct ownership |
| 355 | +- **Environment variables** - Use DEFAULT_ENV_VARS for container configuration |
| 356 | +- **Port conflicts** - Ensure ports don't conflict with system services |
| 357 | + |
| 358 | +--- |
| 359 | + |
| 360 | +## 📖 Additional Resources |
| 361 | + |
| 362 | +- [Manual Appliance Guide](MANUAL_APPLIANCE_GUIDE.md) - For advanced customization |
| 363 | +- [OpenNebula Documentation](https://docs.opennebula.io/) |
| 364 | +- [Docker Hub](https://hub.docker.com/) |
| 365 | +- [Packer Documentation](https://www.packer.io/docs) |
| 366 | +- [OpenNebula Marketplace](https://marketplace.opennebula.io/) |
| 367 | + |
0 commit comments