diff --git a/README.md b/README.md index 0786cd7..061fb4f 100644 --- a/README.md +++ b/README.md @@ -55,34 +55,120 @@ The application can be deployed using Kubernetes with the provided configuration - `pvc.yaml`: Persistent Volume Claim for storage - `secrets.yaml`: Secret configuration for environment variables -### Deployment Notes +### Deployment Configuration -- Optimized for reliable scheduling on resource-constrained clusters: - - Reduced replicas to 1 to ensure easier scheduling - - Reduced resource requests and limits - - Changed image pull policy to IfNotPresent to reduce pull issues - - Added flexible tolerations to run on nodes with any taints - - Configured node affinity for Linux but as a preference, not a requirement - - Set empty storageClassName to use the cluster's default storage class +The deployment has been optimized for maximum scheduling flexibility and minimal resource requirements: -- If you experience the "pod does not have a host assigned" error: - - Check that your cluster has available nodes with sufficient resources - - Verify that the default storage class exists and is working - - Ensure the specified image exists and is accessible +- **Minimal Resource Requirements**: + - Single replica (no high availability) to ensure easier scheduling + - Extremely low resource requests (10m CPU, 64Mi memory) + - No resource limits to allow for flexible resource usage + - Reduced PVC storage request (100Mi) + - Optional emptyDir volume configuration (commented out) + +- **Maximum Scheduling Flexibility**: + - Uses Recreate deployment strategy instead of RollingUpdate + - Tolerations for all taints using `operator: "Exists"` + - No node selector or required affinity rules + - Default scheduler and DNS policy + +- **Vault Integration (Commented Out)**: + - Added configuration templates for HashiCorp Vault integration + - Instructions for using Vault Agent Injector with the deployment + - Support for vault-agent and vault-agent-init sidecars + +### Troubleshooting "pod does not have a host assigned" Error + +If you encounter this error, follow these steps: + +1. **Check Cluster Resources**: + ```bash + kubectl get nodes + kubectl describe nodes + ``` + - Ensure nodes have available CPU, memory, and are in Ready state + +2. **Check PVC Status**: + ```bash + kubectl get pvc + kubectl describe pvc inventory-api-pvc + ``` + - If the PVC is stuck in Pending state, check storage class availability: + ```bash + kubectl get storageclass + ``` + - If no storage class is available, modify deployment.yaml to use emptyDir: + ```yaml + volumes: + - name: storage-volume + emptyDir: {} + # persistentVolumeClaim: + # claimName: inventory-api-pvc + ``` + +3. **Check Pod Status**: + ```bash + kubectl get pods + kubectl describe pod + kubectl get events --sort-by='.metadata.creationTimestamp' + ``` + - Look for scheduling errors or resource constraints + +4. **Verify Vault Configuration** (if applicable): + - If your cluster uses HashiCorp Vault, uncomment the Vault configuration in deployment.yaml + - Check Vault service status: + ```bash + kubectl get svc -n vault + ``` + +5. **Extreme Resource Reduction**: + - If still having issues, you can uncomment and adjust these values in deployment.yaml: + ```yaml + resources: + requests: + cpu: "1m" # Absolute minimum + memory: "32Mi" # Absolute minimum + ``` + +### Deployment Commands To deploy: ```bash +# Apply secret first kubectl apply -f kubernetes/secrets.yaml + +# Create PVC kubectl apply -f kubernetes/pvc.yaml + +# Apply deployment kubectl apply -f kubernetes/deployment.yaml + +# Apply service kubectl apply -f kubernetes/service.yaml ``` -To check the status: +To check status: ```bash +# Get pod status kubectl get pods + +# Describe pod for detailed information kubectl describe pod + +# Check recent events kubectl get events --sort-by='.metadata.creationTimestamp' + +# Get logs from container +kubectl logs -c app +``` + +To delete deployment: + +```bash +kubectl delete -f kubernetes/deployment.yaml +kubectl delete -f kubernetes/service.yaml +kubectl delete -f kubernetes/pvc.yaml +kubectl delete -f kubernetes/secrets.yaml ``` diff --git a/app/crud/__init__.py b/app/crud/__init__.py index 515c943..886dfa9 100644 --- a/app/crud/__init__.py +++ b/app/crud/__init__.py @@ -3,4 +3,7 @@ from .crud_product import product from .crud_category import category from .crud_supplier import supplier from .crud_inventory import inventory -from .crud_order import order \ No newline at end of file +from .crud_order import order + +# Re-export all CRUD modules +__all__ = ["user", "product", "category", "supplier", "inventory", "order"] \ No newline at end of file diff --git a/app/crud/crud_product.py b/app/crud/crud_product.py index 8aa9b90..be61ee8 100644 --- a/app/crud/crud_product.py +++ b/app/crud/crud_product.py @@ -27,7 +27,7 @@ class CRUDProduct(CRUDBase[Product, ProductCreate, ProductUpdate]): def get_active( self, db: Session, *, skip: int = 0, limit: int = 100 ) -> List[Product]: - return db.query(Product).filter(Product.is_active == True).offset(skip).limit(limit).all() + return db.query(Product).filter(Product.is_active).offset(skip).limit(limit).all() product = CRUDProduct(Product) \ No newline at end of file diff --git a/app/schemas/__init__.py b/app/schemas/__init__.py index 25a1fff..ee0c4da 100644 --- a/app/schemas/__init__.py +++ b/app/schemas/__init__.py @@ -4,4 +4,16 @@ from .category import Category, CategoryCreate, CategoryInDB, CategoryUpdate from .supplier import Supplier, SupplierCreate, SupplierInDB, SupplierUpdate from .product import Product, ProductCreate, ProductInDB, ProductUpdate from .inventory import InventoryItem, InventoryItemCreate, InventoryItemInDB, InventoryItemUpdate -from .order import Order, OrderCreate, OrderInDB, OrderUpdate, OrderItem, OrderItemCreate, OrderItemInDB, OrderItemUpdate \ No newline at end of file +from .order import Order, OrderCreate, OrderInDB, OrderUpdate, OrderItem, OrderItemCreate, OrderItemInDB, OrderItemUpdate + +# Re-export all schema types +__all__ = [ + "Token", "TokenPayload", + "User", "UserCreate", "UserInDB", "UserUpdate", + "Category", "CategoryCreate", "CategoryInDB", "CategoryUpdate", + "Supplier", "SupplierCreate", "SupplierInDB", "SupplierUpdate", + "Product", "ProductCreate", "ProductInDB", "ProductUpdate", + "InventoryItem", "InventoryItemCreate", "InventoryItemInDB", "InventoryItemUpdate", + "Order", "OrderCreate", "OrderInDB", "OrderUpdate", + "OrderItem", "OrderItemCreate", "OrderItemInDB", "OrderItemUpdate" +] \ No newline at end of file diff --git a/kubernetes/deployment.yaml b/kubernetes/deployment.yaml index 2133f59..a6947cf 100644 --- a/kubernetes/deployment.yaml +++ b/kubernetes/deployment.yaml @@ -4,32 +4,57 @@ metadata: name: prod-pod labels: app: inventory-api + # Uncomment the following annotation if using Vault Agent Injector + # annotations: + # vault.hashicorp.com/agent-inject: "true" + # vault.hashicorp.com/agent-inject-status: "update" + # vault.hashicorp.com/role: "inventory-api" + # vault.hashicorp.com/agent-inject-secret-config: "secret/data/inventory-api/config" + # vault.hashicorp.com/agent-inject-template-config: | + # {{- with secret "secret/data/inventory-api/config" -}} + # export SECRET_KEY="{{ .Data.data.secret_key }}" + # export FIRST_SUPERUSER_PASSWORD="{{ .Data.data.admin_password }}" + # {{- end -}} spec: - replicas: 1 # Reduced replicas to 1 to ensure easier scheduling + replicas: 1 # Use only 1 replica to minimize resource requirements selector: matchLabels: app: inventory-api strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 1 - maxUnavailable: 0 + type: Recreate # Changed from RollingUpdate to Recreate to avoid running multiple pods template: metadata: labels: app: inventory-api + # Uncomment the following annotations if using Vault Agent Injector + # annotations: + # vault.hashicorp.com/agent-inject: "true" + # vault.hashicorp.com/agent-inject-status: "update" + # vault.hashicorp.com/role: "inventory-api" + # vault.hashicorp.com/agent-inject-secret-config: "secret/data/inventory-api/config" + # vault.hashicorp.com/agent-inject-template-config: | + # {{- with secret "secret/data/inventory-api/config" -}} + # export SECRET_KEY="{{ .Data.data.secret_key }}" + # export FIRST_SUPERUSER_PASSWORD="{{ .Data.data.admin_password }}" + # {{- end -}} spec: + # If using Vault, you might need to add an initContainer to wait for Vault secrets + # initContainers: + # - name: wait-for-vault + # image: busybox + # command: ['sh', '-c', 'until [ -f /vault/secrets/config ]; do echo "Waiting for Vault secrets..."; sleep 2; done'] + # volumeMounts: + # - name: vault-secrets + # mountPath: /vault/secrets containers: - name: app image: ${IMAGE_REPOSITORY}:${IMAGE_TAG} - imagePullPolicy: IfNotPresent # Changed from Always to IfNotPresent to reduce pull issues + imagePullPolicy: IfNotPresent # Use existing images when possible resources: requests: - cpu: "50m" # Reduced CPU request - memory: "128Mi" # Reduced memory request - limits: - cpu: "300m" # Reduced CPU limit - memory: "384Mi" # Reduced memory limit + cpu: "10m" # Absolute minimum CPU request + memory: "64Mi" # Absolute minimum memory request + # Removed limits to allow the pod to use available resources if needed ports: - containerPort: 8000 name: http @@ -50,41 +75,45 @@ spec: secretKeyRef: name: inventory-api-secrets key: admin-password + # Simplified probes to reduce load during startup livenessProbe: httpGet: path: /health port: 8000 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 + initialDelaySeconds: 60 # Increased to give pod more time to start + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 5 readinessProbe: httpGet: path: /health port: 8000 - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 3 - failureThreshold: 3 + initialDelaySeconds: 30 # Increased to give pod more time to start + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 5 volumeMounts: - name: storage-volume mountPath: /app/storage + # If using Vault, add the following volume mount + # - name: vault-secrets + # mountPath: /vault/secrets volumes: - name: storage-volume + # Uncomment the following if PVC fails to provision + # emptyDir: {} persistentVolumeClaim: claimName: inventory-api-pvc - # Enhanced tolerations to allow more flexible scheduling + # If using Vault, add this volume + # - name: vault-secrets + # emptyDir: + # medium: Memory + # Maximum scheduling flexibility tolerations: - - operator: "Exists" # This will tolerate all taints - # Affinity settings to help with scheduling - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: "kubernetes.io/os" - operator: In - values: - - linux - # No nodeSelector to allow maximum scheduling flexibility \ No newline at end of file + - operator: "Exists" # Tolerate all taints + dnsPolicy: ClusterFirst + restartPolicy: Always + securityContext: {} + terminationGracePeriodSeconds: 30 + schedulerName: default-scheduler + # No nodeSelector, no affinity to allow maximum scheduling flexibility \ No newline at end of file diff --git a/kubernetes/pvc.yaml b/kubernetes/pvc.yaml index 0968a5e..35125aa 100644 --- a/kubernetes/pvc.yaml +++ b/kubernetes/pvc.yaml @@ -2,10 +2,14 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: inventory-api-pvc + annotations: + volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/no-provisioner + volume.kubernetes.io/storage-provisioner: kubernetes.io/no-provisioner spec: accessModes: - ReadWriteOnce resources: requests: - storage: 1Gi - storageClassName: "" # Empty string to use the default storage class \ No newline at end of file + storage: 100Mi # Reduced storage request + storageClassName: "" # Empty string to use default storage class + volumeMode: Filesystem \ No newline at end of file