Add practical examples to multiple files
- LF9-03 Virtualisierung: Docker Compose + Volume examples - LF6-02 Frontend: To-Do list practical example - LF8-04 ETL: Complete ETL pipeline example - LF6-04 Sicherheit: Express.js security headers - LF2-04 Nutzwertanalyse: Cloud provider selection example - LF9-04 Monitoring: Prometheus alerts + Python logging
This commit is contained in:
@@ -80,6 +80,73 @@ Laptop B:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Praktisches Beispiel: Cloud-Anbieter Auswahl
|
||||||
|
|
||||||
|
### Szenario
|
||||||
|
Ein Unternehmen muss sich zwischen AWS, Azure und Google Cloud entscheiden.
|
||||||
|
|
||||||
|
### 1. Kriterien definieren
|
||||||
|
|
||||||
|
| Kriterium | Bedeutung |
|
||||||
|
|-----------|-----------|
|
||||||
|
| Preis | Kosten pro Monat |
|
||||||
|
| Leistung | Rechenleistung, Geschwindigkeit |
|
||||||
|
| Sicherheit | Zertifizierungen, Features |
|
||||||
|
| Support | Deutsche Ansprechpartner |
|
||||||
|
| Skalierbarkeit | Einfache Erweiterung |
|
||||||
|
|
||||||
|
### 2. Gewichtung
|
||||||
|
|
||||||
|
| Kriterium | Gewichtung |
|
||||||
|
|-----------|------------|
|
||||||
|
| Preis | 25% |
|
||||||
|
| Leistung | 30% |
|
||||||
|
| Sicherheit | 25% |
|
||||||
|
| Support | 10% |
|
||||||
|
| Skalierbarkeit | 10% |
|
||||||
|
| **Summe** | **100%** |
|
||||||
|
|
||||||
|
### 3. Bewertung (1-10)
|
||||||
|
|
||||||
|
| Kriterium | Gewichtung | AWS | Azure | Google Cloud |
|
||||||
|
|-----------|------------|-----|-------|--------------|
|
||||||
|
| Preis | 25% | 7 | 8 | 9 |
|
||||||
|
| Leistung | 30% | 9 | 8 | 9 |
|
||||||
|
| Sicherheit | 25% | 9 | 8 | 8 |
|
||||||
|
| Support | 10% | 7 | 8 | 6 |
|
||||||
|
| Skalierbarkeit | 10% | 9 | 8 | 9 |
|
||||||
|
|
||||||
|
### 4. Berechnung
|
||||||
|
|
||||||
|
```
|
||||||
|
AWS:
|
||||||
|
= 0,25×7 + 0,30×9 + 0,25×9 + 0,10×7 + 0,10×9
|
||||||
|
= 1,75 + 2,70 + 2,25 + 0,70 + 0,90
|
||||||
|
= 8,30
|
||||||
|
|
||||||
|
Azure:
|
||||||
|
= 0,25×8 + 0,30×8 + 0,25×8 + 0,10×8 + 0,10×8
|
||||||
|
= 2,00 + 2,40 + 2,00 + 0,80 + 0,80
|
||||||
|
= 8,00
|
||||||
|
|
||||||
|
Google Cloud:
|
||||||
|
= 0,25×9 + 0,30×9 + 0,25×8 + 0,10×6 + 0,10×9
|
||||||
|
= 2,25 + 2,70 + 2,00 + 0,60 + 0,90
|
||||||
|
= 8,45
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Ergebnis
|
||||||
|
|
||||||
|
| Anbieter | Nutzwert | Rang |
|
||||||
|
|----------|----------|------|
|
||||||
|
| Google Cloud | 8,45 | 1 |
|
||||||
|
| AWS | 8,30 | 2 |
|
||||||
|
| Azure | 8,00 | 3 |
|
||||||
|
|
||||||
|
**Empfehlung:** Google Cloud (aufgrund des besten Preis-Leistungs-Verhältnisses)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Vorlage
|
## Vorlage
|
||||||
|
|
||||||
### Nutzwertanalyse - Vorlage
|
### Nutzwertanalyse - Vorlage
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ Dieses Lernfeld behandelt die Entwicklung und Verwaltung von Datenbanken.
|
|||||||
|
|
||||||
## Themen
|
## Themen
|
||||||
|
|
||||||
| Nr. | Thema | Beschreibung |
|
| Nr. | Thema | Beschreibung | |
|
||||||
|-----|-------|-------------|
|
| --- | ---------------------------- | --------------------- | ---------------------------------------- |
|
||||||
| 3.1 | [[LF3-01-Datenbankgrundlagen | Datenbankgrundlagen]] | Grundbegriffe, DBMS, Datenmodelle |
|
| 3.1 | [[LF3-01-Datenbankgrundlagen | Datenbankgrundlagen]] | Grundbegriffe, DBMS, Datenmodelle |
|
||||||
| 3.2 | [[LF3-02-Datenmodellierung | Datenmodellierung]] | ER-Modell, Normalisierung |
|
| 3.2 | [[LF3-02-Datenmodellierung | Datenmodellierung]] | ER-Modell, Normalisierung |
|
||||||
| 3.3 | [[LF3-03-SQL-Grundlagen | SQL-Grundlagen]] | SELECT, INSERT, UPDATE, DELETE |
|
| 3.3 | [[LF3-03-SQL-Grundlagen | SQL-Grundlagen]] | SELECT, INSERT, UPDATE, DELETE |
|
||||||
|
|||||||
@@ -99,6 +99,45 @@ element.setAttribute("class", "neu");
|
|||||||
element.getAttribute("href");
|
element.getAttribute("href");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Praktisches Beispiel: To-Do-Liste
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- HTML -->
|
||||||
|
<input type="text" id="todo-input" placeholder="Neue Aufgabe">
|
||||||
|
<button id="add-btn">Hinzufügen</button>
|
||||||
|
<ul id="todo-list"></ul>
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// JavaScript
|
||||||
|
const input = document.getElementById('todo-input');
|
||||||
|
const button = document.getElementById('add-btn');
|
||||||
|
const list = document.getElementById('todo-list');
|
||||||
|
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
const text = input.value.trim();
|
||||||
|
if (text) {
|
||||||
|
// Neues Element erstellen
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = text;
|
||||||
|
|
||||||
|
// Löschen-Button
|
||||||
|
const deleteBtn = document.createElement('button');
|
||||||
|
deleteBtn.textContent = 'X';
|
||||||
|
deleteBtn.onclick = () => li.remove();
|
||||||
|
|
||||||
|
li.appendChild(deleteBtn);
|
||||||
|
list.appendChild(li);
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enter-Taste Support
|
||||||
|
input.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') button.click();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|||||||
@@ -80,6 +80,29 @@ function escapeHtml(text) {
|
|||||||
Content-Security-Policy: default-src 'self'; script-src 'self'
|
Content-Security-Policy: default-src 'self'; script-src 'self'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Praktisches Beispiel: Express.js Sicherheits-Header
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const helmet = require('helmet');
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
app.use(helmet());
|
||||||
|
|
||||||
|
// CORS konfigurieren
|
||||||
|
app.use(cors({
|
||||||
|
origin: 'https://meine-app.de',
|
||||||
|
credentials: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Rate Limiting
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
|
app.use('/api/', rateLimit({
|
||||||
|
windowMs: 15 * 60 * 1000, // 15 Minuten
|
||||||
|
max: 100, // Max 100 Anfragen
|
||||||
|
message: 'Zu viele Anfragen, bitte später versuchen'
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CSRF (Cross-Site Request Forgery)
|
## CSRF (Cross-Site Request Forgery)
|
||||||
|
|||||||
@@ -207,6 +207,81 @@ ETL - Fehlerstrategien
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Praktisches Beispiel: Vollständiger ETL-Pipeline
|
||||||
|
|
||||||
|
```python
|
||||||
|
import pandas as pd
|
||||||
|
import requests
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Logging konfigurieren
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def etl_pipeline():
|
||||||
|
"""
|
||||||
|
Vollständiger ETL-Pipeline für Verkaufsdaten
|
||||||
|
"""
|
||||||
|
|
||||||
|
# === EXTRACT ===
|
||||||
|
logger.info("Starte Extraktion...")
|
||||||
|
|
||||||
|
# Aus CSV
|
||||||
|
kunden_df = pd.read_csv('daten/kunden.csv')
|
||||||
|
bestellungen_df = pd.read_csv('daten/bestellungen.csv')
|
||||||
|
|
||||||
|
# Aus API
|
||||||
|
try:
|
||||||
|
response = requests.get('https://api.shop.de/produkte', timeout=30)
|
||||||
|
produkte_df = pd.DataFrame(response.json())
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"API Fehler: {e}")
|
||||||
|
produkte_df = pd.DataFrame()
|
||||||
|
|
||||||
|
logger.info(f"Extrahiert: {len(kunden_df)} Kunden, {len(bestellungen_df)} Bestellungen")
|
||||||
|
|
||||||
|
# === TRANSFORM ===
|
||||||
|
logger.info("Starte Transformation...")
|
||||||
|
|
||||||
|
# Daten bereinigen
|
||||||
|
kunden_df = kunden_df.drop_duplicates()
|
||||||
|
kunden_df['email'] = kunden_df['email'].str.lower().str.strip()
|
||||||
|
kunden_df['erstellt_am'] = pd.to_datetime(kunden_df['erstellt_am'])
|
||||||
|
|
||||||
|
# Berechnungen
|
||||||
|
bestellungen_df['umsatz_mit_mwst'] = bestellungen_df['umsatz_netto'] * 1.19
|
||||||
|
|
||||||
|
# JOIN: Bestellungen mit Kunden verbinden
|
||||||
|
merged_df = bestellungen_df.merge(
|
||||||
|
kunden_df[['kunden_id', 'name', 'stadt']],
|
||||||
|
on='kunden_id',
|
||||||
|
how='left'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Aggregation: Umsatz pro Stadt
|
||||||
|
umsatz_pro_stadt = merged_df.groupby('stadt')['umsatz_netto'].sum().reset_index()
|
||||||
|
|
||||||
|
logger.info(f"Transformation abgeschlossen: {len(merged_df)} Datensätze")
|
||||||
|
|
||||||
|
# === LOAD ===
|
||||||
|
logger.info("Starte Laden...")
|
||||||
|
|
||||||
|
# Datenbank-Verbindung
|
||||||
|
engine = create_engine('postgresql://user:pass@localhost:5432/warehouse')
|
||||||
|
|
||||||
|
# In Datenbank laden
|
||||||
|
merged_df.to_sql('fact_bestellungen', engine, if_exists='replace', index=False)
|
||||||
|
umsatz_pro_stadt.to_sql('dim_umsatz_stadt', engine, if_exists='replace', index=False)
|
||||||
|
|
||||||
|
logger.info("ETL Pipeline erfolgreich abgeschlossen!")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
etl_pipeline()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Querverweise
|
## Querverweise
|
||||||
|
|
||||||
- [[LF8-03-Datenformate|Zurück: Datenformate]]
|
- [[LF8-03-Datenformate|Zurück: Datenformate]]
|
||||||
|
|||||||
@@ -108,6 +108,59 @@ EXPOSE 3000
|
|||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Praktisches Beispiel: Docker Compose
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Webanwendung
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=postgres://db:5432/webapp
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
- redis
|
||||||
|
|
||||||
|
# Datenbank
|
||||||
|
db:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_PASSWORD=geheim
|
||||||
|
- POSTGRES_DB=webapp
|
||||||
|
|
||||||
|
# Cache
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
### Praktisches Beispiel: Docker Volume
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Volume erstellen
|
||||||
|
docker volume create mydata
|
||||||
|
|
||||||
|
# Volume einhängen
|
||||||
|
docker run -v mydata:/data ubuntu
|
||||||
|
|
||||||
|
# Volumes auflisten
|
||||||
|
docker volume ls
|
||||||
|
|
||||||
|
# Unbenutzte Volumes löschen
|
||||||
|
docker volume prune
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Kubernetes
|
## Kubernetes
|
||||||
|
|||||||
@@ -61,6 +61,73 @@ scrape_configs:
|
|||||||
- targets: ['localhost:9100']
|
- targets: ['localhost:9100']
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Praktisches Beispiel: Alert-Regeln
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# alerts.yml
|
||||||
|
groups:
|
||||||
|
- name: server_alerts
|
||||||
|
rules:
|
||||||
|
# Hohe CPU-Auslastung
|
||||||
|
- alert: HighCPU
|
||||||
|
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
|
||||||
|
for: 5m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
summary: "Hohe CPU-Auslastung auf {{ $labels.instance }}"
|
||||||
|
description: "CPU Auslastung ist seit 5 Minuten über 80%"
|
||||||
|
|
||||||
|
# Wenig Speicherplatz
|
||||||
|
- alert: DiskSpaceLow
|
||||||
|
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 10
|
||||||
|
for: 2m
|
||||||
|
labels:
|
||||||
|
severity: critical
|
||||||
|
annotations:
|
||||||
|
summary: "Wenig Speicherplatz auf {{ $labels.instance }}"
|
||||||
|
|
||||||
|
# Service ausgefallen
|
||||||
|
- alert: ServiceDown
|
||||||
|
expr: up == 0
|
||||||
|
for: 1m
|
||||||
|
labels:
|
||||||
|
severity: critical
|
||||||
|
annotations:
|
||||||
|
summary: "{{ $labels.job }} Service ausgefallen"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Praktisches Beispiel: Python Logging
|
||||||
|
|
||||||
|
```python
|
||||||
|
import logging
|
||||||
|
import logging.handlers
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Logger konfigurieren
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
|
handlers=[
|
||||||
|
logging.StreamHandler(sys.stdout),
|
||||||
|
logging.handlers.RotatingFileHandler(
|
||||||
|
'app.log',
|
||||||
|
maxBytes=10_000_000, # 10 MB
|
||||||
|
backupCount=5
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Log-Ebenen nutzen
|
||||||
|
logger.debug("Detaillierte Debug-Info")
|
||||||
|
logger.info("Anwendung gestartet")
|
||||||
|
logger.warning("Warnung: Konfiguration fehlt")
|
||||||
|
logger.error("Fehler: Datenbank nicht erreichbar")
|
||||||
|
logger.critical("Kritisch: System muss heruntergefahren werden")
|
||||||
|
```
|
||||||
|
|
||||||
### Grafana Dashboard
|
### Grafana Dashboard
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user