docs: add comprehensive application deployment guide
Add APP_DEPLOYMENT.md with step-by-step guide for deploying applications to the Talos Kubernetes cluster. Covers: - Directory structure and GitOps organization - Creating namespaces and deployments - Configuring services and ingress - Storage with PersistentVolumeClaims - Using Kustomize for manifest management - Examples for common application types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ea415ba584
commit
2ed1e82953
445
APP_DEPLOYMENT.md
Normal file
445
APP_DEPLOYMENT.md
Normal file
@ -0,0 +1,445 @@
|
||||
# Application Deployment Guide
|
||||
|
||||
This guide explains how to deploy applications to your Talos Kubernetes cluster following the GitOps structure used in this repository.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
Applications are organized in the `testing1/first-cluster/apps/` directory:
|
||||
|
||||
```
|
||||
testing1/first-cluster/
|
||||
├── cluster/
|
||||
│ └── base/ # Cluster-level resources (namespaces, RBAC, etc.)
|
||||
└── apps/
|
||||
├── demo/ # Example nginx app
|
||||
│ ├── nginx-deployment.yaml
|
||||
│ └── nginx-service.yaml
|
||||
└── gitlab/ # GitLab with Container Registry
|
||||
├── namespace.yaml
|
||||
├── pvc.yaml
|
||||
├── configmap.yaml
|
||||
├── deployment.yaml
|
||||
├── service.yaml
|
||||
├── runner-secret.yaml
|
||||
├── runner-configmap.yaml
|
||||
├── runner-deployment.yaml
|
||||
└── kustomization.yaml
|
||||
```
|
||||
|
||||
## Deploying Applications
|
||||
|
||||
### Method 1: Direct kubectl apply
|
||||
|
||||
Apply individual app manifests:
|
||||
|
||||
```bash
|
||||
# Deploy a specific app
|
||||
kubectl apply -f testing1/first-cluster/apps/gitlab/
|
||||
|
||||
# Or use kustomize
|
||||
kubectl apply -k testing1/first-cluster/apps/gitlab/
|
||||
```
|
||||
|
||||
### Method 2: Using kustomize (Recommended)
|
||||
|
||||
Each app directory can contain a `kustomization.yaml` file that lists all resources:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
```
|
||||
|
||||
Deploy with:
|
||||
```bash
|
||||
kubectl apply -k testing1/first-cluster/apps/<app-name>/
|
||||
```
|
||||
|
||||
## Adding a New Application
|
||||
|
||||
Follow these steps to add a new application to your cluster:
|
||||
|
||||
### 1. Create App Directory
|
||||
|
||||
```bash
|
||||
mkdir -p testing1/first-cluster/apps/<app-name>
|
||||
cd testing1/first-cluster/apps/<app-name>
|
||||
```
|
||||
|
||||
### 2. Create Namespace (Optional but Recommended)
|
||||
|
||||
Create `namespace.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: <app-name>
|
||||
```
|
||||
|
||||
### 3. Create Application Resources
|
||||
|
||||
Create the necessary Kubernetes resources. Common resources include:
|
||||
|
||||
#### Deployment
|
||||
|
||||
Create `deployment.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: <app-name>
|
||||
namespace: <app-name>
|
||||
labels:
|
||||
app: <app-name>
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: <app-name>
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: <app-name>
|
||||
spec:
|
||||
containers:
|
||||
- name: <container-name>
|
||||
image: <image:tag>
|
||||
ports:
|
||||
- containerPort: <port>
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
```
|
||||
|
||||
#### Service
|
||||
|
||||
Create `service.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: <app-name>
|
||||
namespace: <app-name>
|
||||
spec:
|
||||
type: NodePort # or ClusterIP, LoadBalancer
|
||||
selector:
|
||||
app: <app-name>
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: <container-port>
|
||||
nodePort: 30XXX # if using NodePort (30000-32767)
|
||||
```
|
||||
|
||||
#### PersistentVolumeClaim (if needed)
|
||||
|
||||
Create `pvc.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: <app-name>-data
|
||||
namespace: <app-name>
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
```
|
||||
|
||||
#### ConfigMap (if needed)
|
||||
|
||||
Create `configmap.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: <app-name>-config
|
||||
namespace: <app-name>
|
||||
data:
|
||||
config.yml: |
|
||||
# Your configuration here
|
||||
```
|
||||
|
||||
#### Secret (if needed)
|
||||
|
||||
Create `secret.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: <app-name>-secret
|
||||
namespace: <app-name>
|
||||
type: Opaque
|
||||
stringData:
|
||||
password: "change-me"
|
||||
api-key: "your-api-key"
|
||||
```
|
||||
|
||||
### 4. Create Kustomization File
|
||||
|
||||
Create `kustomization.yaml` to organize all resources:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- pvc.yaml
|
||||
- configmap.yaml
|
||||
- secret.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
```
|
||||
|
||||
### 5. Deploy the Application
|
||||
|
||||
```bash
|
||||
# From the repository root
|
||||
kubectl apply -k testing1/first-cluster/apps/<app-name>/
|
||||
|
||||
# Verify deployment
|
||||
kubectl get all -n <app-name>
|
||||
```
|
||||
|
||||
## GitLab Deployment Example
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Ensure your cluster is running and healthy:
|
||||
```bash
|
||||
kubectl get nodes
|
||||
talosctl health
|
||||
```
|
||||
|
||||
2. **IMPORTANT**: Install a storage provisioner first:
|
||||
```bash
|
||||
# Check if storage class exists
|
||||
kubectl get storageclass
|
||||
|
||||
# If no storage class found, install local-path-provisioner
|
||||
./install-local-path-storage.sh
|
||||
```
|
||||
|
||||
Without a storage provisioner, GitLab's PersistentVolumeClaims will remain in Pending state and pods won't start.
|
||||
|
||||
### Deploy GitLab
|
||||
|
||||
1. **Update the runner registration token** in `testing1/first-cluster/apps/gitlab/runner-secret.yaml`:
|
||||
|
||||
After GitLab is running, get the registration token from:
|
||||
- GitLab UI: `Admin Area > CI/CD > Runners > Register an instance runner`
|
||||
- Or for project runners: `Settings > CI/CD > Runners > New project runner`
|
||||
|
||||
2. **Deploy GitLab and Runner**:
|
||||
```bash
|
||||
kubectl apply -k testing1/first-cluster/apps/gitlab/
|
||||
```
|
||||
|
||||
3. **Wait for GitLab to be ready** (this can take 5-10 minutes):
|
||||
```bash
|
||||
kubectl get pods -n gitlab -w
|
||||
```
|
||||
|
||||
4. **Access GitLab**:
|
||||
- GitLab UI: `http://<any-node-ip>:30080`
|
||||
- SSH: `<any-node-ip>:30022`
|
||||
- Container Registry: `http://<any-node-ip>:30500`
|
||||
|
||||
5. **Get initial root password**:
|
||||
```bash
|
||||
kubectl exec -n gitlab deployment/gitlab -- grep 'Password:' /etc/gitlab/initial_root_password
|
||||
```
|
||||
|
||||
6. **Configure GitLab Runner**:
|
||||
- Login to GitLab
|
||||
- Get the runner registration token
|
||||
- Update `runner-secret.yaml` with the token
|
||||
- Re-apply the secret:
|
||||
```bash
|
||||
kubectl apply -f testing1/first-cluster/apps/gitlab/runner-secret.yaml
|
||||
```
|
||||
- Restart the runner:
|
||||
```bash
|
||||
kubectl rollout restart deployment/gitlab-runner -n gitlab
|
||||
```
|
||||
|
||||
### Using the Container Registry
|
||||
|
||||
1. **Login to the registry**:
|
||||
```bash
|
||||
docker login <node-ip>:30500
|
||||
```
|
||||
|
||||
2. **Tag and push images**:
|
||||
```bash
|
||||
docker tag myapp:latest <node-ip>:30500/mygroup/myapp:latest
|
||||
docker push <node-ip>:30500/mygroup/myapp:latest
|
||||
```
|
||||
|
||||
3. **Example `.gitlab-ci.yml` for building Docker images**:
|
||||
```yaml
|
||||
stages:
|
||||
- build
|
||||
- push
|
||||
|
||||
variables:
|
||||
DOCKER_DRIVER: overlay2
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
|
||||
|
||||
build:
|
||||
stage: build
|
||||
image: docker:24-dind
|
||||
services:
|
||||
- docker:24-dind
|
||||
tags:
|
||||
- docker
|
||||
script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
- docker build -t $IMAGE_TAG .
|
||||
- docker push $IMAGE_TAG
|
||||
```
|
||||
|
||||
## Resource Sizing Guidelines
|
||||
|
||||
When adding applications, consider these resource guidelines:
|
||||
|
||||
### Small Applications (web frontends, APIs)
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
```
|
||||
|
||||
### Medium Applications (databases, caching)
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
cpu: "500m"
|
||||
memory: "1Gi"
|
||||
limits:
|
||||
cpu: "2000m"
|
||||
memory: "4Gi"
|
||||
```
|
||||
|
||||
### Large Applications (GitLab, monitoring stacks)
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
cpu: "1000m"
|
||||
memory: "4Gi"
|
||||
limits:
|
||||
cpu: "4000m"
|
||||
memory: "8Gi"
|
||||
```
|
||||
|
||||
## Service Types
|
||||
|
||||
### ClusterIP (default)
|
||||
- Only accessible within the cluster
|
||||
- Use for internal services
|
||||
|
||||
### NodePort
|
||||
- Accessible on every node's IP at a static port (30000-32767)
|
||||
- Use for services you need to access from outside the cluster
|
||||
- Example: GitLab on port 30080
|
||||
|
||||
### LoadBalancer
|
||||
- Creates an external load balancer (if cloud provider supports it)
|
||||
- On bare metal, requires MetalLB or similar
|
||||
|
||||
## Storage Considerations
|
||||
|
||||
### Access Modes
|
||||
- `ReadWriteOnce` (RWO): Single node read/write (most common)
|
||||
- `ReadOnlyMany` (ROX): Multiple nodes read-only
|
||||
- `ReadWriteMany` (RWX): Multiple nodes read/write (requires special storage)
|
||||
|
||||
### Storage Sizing
|
||||
- Logs: 1-5 GB
|
||||
- Application data: 10-50 GB
|
||||
- Databases: 50-100+ GB
|
||||
- Container registries: 100+ GB
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Check Pod Status
|
||||
```bash
|
||||
kubectl get pods -n <namespace>
|
||||
kubectl describe pod <pod-name> -n <namespace>
|
||||
kubectl logs <pod-name> -n <namespace>
|
||||
```
|
||||
|
||||
### Check Events
|
||||
```bash
|
||||
kubectl get events -n <namespace> --sort-by='.lastTimestamp'
|
||||
```
|
||||
|
||||
### Check Resource Usage
|
||||
```bash
|
||||
kubectl top nodes
|
||||
kubectl top pods -n <namespace>
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **ImagePullBackOff**: Container image cannot be pulled
|
||||
- Check image name and tag
|
||||
- Verify registry credentials if using private registry
|
||||
|
||||
2. **CrashLoopBackOff**: Container keeps crashing
|
||||
- Check logs: `kubectl logs <pod> -n <namespace>`
|
||||
- Check resource limits
|
||||
- Verify configuration
|
||||
|
||||
3. **Pending Pods**: Pod cannot be scheduled
|
||||
- Check node resources: `kubectl describe node`
|
||||
- Check PVC status if using storage
|
||||
- Verify node selectors/taints
|
||||
|
||||
4. **PVC Stuck in Pending**: Storage cannot be provisioned
|
||||
- **Most common issue on Talos**: No storage provisioner installed
|
||||
- Check if storage class exists: `kubectl get sc`
|
||||
- If no storage class, install one:
|
||||
```bash
|
||||
./install-local-path-storage.sh
|
||||
```
|
||||
- Check PVC events: `kubectl describe pvc <pvc-name> -n <namespace>`
|
||||
- For GitLab specifically, use the redeploy script:
|
||||
```bash
|
||||
./redeploy-gitlab.sh
|
||||
```
|
||||
- Verify storage is available on nodes
|
||||
|
||||
5. **Storage Provisioner Issues**
|
||||
- Run diagnostics: `./diagnose-storage.sh`
|
||||
- Check provisioner pods: `kubectl get pods -n local-path-storage`
|
||||
- View provisioner logs: `kubectl logs -n local-path-storage deployment/local-path-provisioner`
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Set up FluxCD for GitOps automation
|
||||
- Configure ingress controller for HTTP/HTTPS routing
|
||||
- Set up monitoring with Prometheus and Grafana
|
||||
- Implement backup solutions for persistent data
|
||||
- Configure network policies for security
|
||||
Loading…
Reference in New Issue
Block a user