Add initial Kubernetes deployment setup with GitHub Actions workflow, Dockerfile, and necessary Kubernetes manifests
This commit is contained in:
48
.dockerignore
Normal file
48
.dockerignore
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids/
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# Temporary folders
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
127
.github/workflows/deploy.yml
vendored
Normal file
127
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
name: Deploy to Kubernetes
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
environment:
|
||||||
|
description: 'Deployment environment'
|
||||||
|
required: true
|
||||||
|
default: 'production'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- production
|
||||||
|
- staging
|
||||||
|
image_tag:
|
||||||
|
description: 'Docker image tag (optional, defaults to commit SHA)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
|
||||||
|
env:
|
||||||
|
APP_NAME: www-cialloo-com
|
||||||
|
REGISTRY: ${{ secrets.DOCKER_REGISTRY }}
|
||||||
|
IMAGE_NAME: ${{ secrets.DOCKER_REGISTRY }}/${{ secrets.DOCKER_REPOSITORY }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: ${{ github.event.inputs.environment || 'production' }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 'latest'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run linting
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
- name: Build application
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Set image tag
|
||||||
|
id: image-tag
|
||||||
|
run: |
|
||||||
|
if [ -n "${{ github.event.inputs.image_tag }}" ]; then
|
||||||
|
echo "tag=${{ github.event.inputs.image_tag }}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "tag=${{ github.sha }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Log in to Docker Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ secrets.DOCKER_REGISTRY }}
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.IMAGE_NAME }}:${{ steps.image-tag.outputs.tag }}
|
||||||
|
${{ env.IMAGE_NAME }}:latest
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
- name: Set up kubectl
|
||||||
|
uses: azure/setup-kubectl@v3
|
||||||
|
with:
|
||||||
|
version: 'v1.28.0'
|
||||||
|
|
||||||
|
- name: Configure kubectl
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.kube
|
||||||
|
echo "${{ secrets.KUBECONFIG }}" | base64 -d > $HOME/.kube/config
|
||||||
|
chmod 600 $HOME/.kube/config
|
||||||
|
|
||||||
|
- name: Verify cluster connection
|
||||||
|
run: kubectl cluster-info
|
||||||
|
|
||||||
|
- name: Deploy to Kubernetes
|
||||||
|
run: |
|
||||||
|
# Replace placeholders in k8s manifests
|
||||||
|
sed -i "s|{{IMAGE_TAG}}|${{ steps.image-tag.outputs.tag }}|g" k8s/deployment.yaml
|
||||||
|
sed -i "s|{{IMAGE_NAME}}|${{ env.IMAGE_NAME }}|g" k8s/deployment.yaml
|
||||||
|
sed -i "s|{{ENVIRONMENT}}|${{ github.event.inputs.environment || 'production' }}|g" k8s/deployment.yaml
|
||||||
|
sed -i "s|{{ENVIRONMENT}}|${{ github.event.inputs.environment || 'production' }}|g" k8s/service.yaml
|
||||||
|
sed -i "s|{{ENVIRONMENT}}|${{ github.event.inputs.environment || 'production' }}|g" k8s/namespace.yaml
|
||||||
|
sed -i "s|{{ENVIRONMENT}}|${{ github.event.inputs.environment || 'production' }}|g" k8s/ingress.yaml
|
||||||
|
|
||||||
|
# Apply manifests
|
||||||
|
kubectl apply -f k8s/namespace.yaml
|
||||||
|
kubectl apply -f k8s/deployment.yaml
|
||||||
|
kubectl apply -f k8s/service.yaml
|
||||||
|
kubectl apply -f k8s/ingress.yaml
|
||||||
|
|
||||||
|
- name: Wait for deployment to be ready
|
||||||
|
run: |
|
||||||
|
kubectl rollout status deployment/${{ env.APP_NAME }} -n ${{ env.APP_NAME }}-${{ github.event.inputs.environment || 'production' }} --timeout=300s
|
||||||
|
|
||||||
|
- name: Get deployment status
|
||||||
|
run: |
|
||||||
|
echo "### Deployment Status" >> $GITHUB_STEP_SUMMARY
|
||||||
|
kubectl get pods -n ${{ env.APP_NAME }}-${{ github.event.inputs.environment || 'production' }} -o wide >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
kubectl get services -n ${{ env.APP_NAME }}-${{ github.event.inputs.environment || 'production' }} >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
- name: Notify deployment success
|
||||||
|
if: success()
|
||||||
|
run: |
|
||||||
|
echo "✅ Deployment successful!"
|
||||||
|
echo "Image: ${{ env.IMAGE_NAME }}:${{ steps.image-tag.outputs.tag }}"
|
||||||
|
echo "Environment: ${{ github.event.inputs.environment || 'production' }}"
|
||||||
|
|
||||||
|
- name: Notify deployment failure
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
echo "❌ Deployment failed!"
|
||||||
|
kubectl describe pods -n ${{ env.APP_NAME }}-${{ github.event.inputs.environment || 'production' }} || true
|
||||||
172
DEPLOYMENT.md
Normal file
172
DEPLOYMENT.md
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
# Kubernetes Deployment Setup
|
||||||
|
|
||||||
|
This document explains how to set up the GitHub Actions workflow for deploying your React application to a Kubernetes cluster.
|
||||||
|
|
||||||
|
## Required GitHub Secrets
|
||||||
|
|
||||||
|
You need to configure the following secrets in your GitHub repository settings (`Settings` → `Secrets and variables` → `Actions`):
|
||||||
|
|
||||||
|
### Docker Registry Secrets
|
||||||
|
- **`DOCKER_REGISTRY`**: Your Docker registry URL (e.g., `docker.io`, `ghcr.io`, `your-registry.com`)
|
||||||
|
- **`DOCKER_REPOSITORY`**: Your Docker repository name (e.g., `username/www-cialloo-com`)
|
||||||
|
- **`DOCKER_USERNAME`**: Username for Docker registry authentication
|
||||||
|
- **`DOCKER_PASSWORD`**: Password or access token for Docker registry authentication
|
||||||
|
|
||||||
|
### Kubernetes Secrets
|
||||||
|
- **`KUBECONFIG`**: Base64-encoded kubeconfig file for your Kubernetes cluster
|
||||||
|
|
||||||
|
## Setting up GitHub Secrets
|
||||||
|
|
||||||
|
### 1. Docker Registry Configuration
|
||||||
|
|
||||||
|
#### For Docker Hub:
|
||||||
|
```bash
|
||||||
|
DOCKER_REGISTRY=docker.io
|
||||||
|
DOCKER_REPOSITORY=yourusername/www-cialloo-com
|
||||||
|
DOCKER_USERNAME=yourusername
|
||||||
|
DOCKER_PASSWORD=your-docker-hub-token
|
||||||
|
```
|
||||||
|
|
||||||
|
#### For GitHub Container Registry:
|
||||||
|
```bash
|
||||||
|
DOCKER_REGISTRY=ghcr.io
|
||||||
|
DOCKER_REPOSITORY=yourusername/www-cialloo-com
|
||||||
|
DOCKER_USERNAME=yourusername
|
||||||
|
DOCKER_PASSWORD=your-github-token
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Kubernetes Configuration
|
||||||
|
|
||||||
|
To get your base64-encoded kubeconfig:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Encode your kubeconfig file
|
||||||
|
cat ~/.kube/config | base64 -w 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the output and paste it as the value for the `KUBECONFIG` secret.
|
||||||
|
|
||||||
|
## GitHub Environments (Optional but Recommended)
|
||||||
|
|
||||||
|
You can set up GitHub environments for better security and approval workflows:
|
||||||
|
|
||||||
|
1. Go to `Settings` → `Environments`
|
||||||
|
2. Create environments: `production`, `staging`
|
||||||
|
3. Configure protection rules (e.g., required reviewers)
|
||||||
|
4. Add environment-specific secrets if needed
|
||||||
|
|
||||||
|
## Kubernetes Cluster Requirements
|
||||||
|
|
||||||
|
Your Kubernetes cluster should have the following components:
|
||||||
|
|
||||||
|
### 1. Traefik Ingress Controller
|
||||||
|
```bash
|
||||||
|
# Install Traefik using Helm
|
||||||
|
helm repo add traefik https://traefik.github.io/charts
|
||||||
|
helm repo update
|
||||||
|
helm install traefik traefik/traefik
|
||||||
|
|
||||||
|
# Or using kubectl with CRDs
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.0/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.0/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Cert-Manager (for SSL certificates)
|
||||||
|
```bash
|
||||||
|
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Docker Registry Secret
|
||||||
|
Create a secret for pulling images from your private registry:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create secret docker-registry docker-registry-secret \
|
||||||
|
--docker-server=your-registry.com \
|
||||||
|
--docker-username=your-username \
|
||||||
|
--docker-password=your-password \
|
||||||
|
--docker-email=your-email@example.com \
|
||||||
|
-n www-cialloo-com-production
|
||||||
|
```
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
### Update Domain Name
|
||||||
|
In `k8s/ingress.yaml`, replace `www.cialloo.com` with your actual domain:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spec:
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- your-domain.com # Change this
|
||||||
|
secretName: www-cialloo-com-tls
|
||||||
|
rules:
|
||||||
|
- host: your-domain.com # Change this
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adjust Resource Limits
|
||||||
|
In `k8s/deployment.yaml`, modify resource requests and limits based on your needs:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "64Mi"
|
||||||
|
cpu: "50m"
|
||||||
|
limits:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scaling
|
||||||
|
Adjust the number of replicas in `k8s/deployment.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spec:
|
||||||
|
replicas: 2 # Change this number
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual Deployment
|
||||||
|
|
||||||
|
To manually trigger the deployment:
|
||||||
|
|
||||||
|
1. Go to your GitHub repository
|
||||||
|
2. Click on "Actions" tab
|
||||||
|
3. Select "Deploy to Kubernetes" workflow
|
||||||
|
4. Click "Run workflow"
|
||||||
|
5. Choose environment and optionally specify an image tag
|
||||||
|
6. Click "Run workflow"
|
||||||
|
|
||||||
|
## Monitoring and Troubleshooting
|
||||||
|
|
||||||
|
### Check deployment status:
|
||||||
|
```bash
|
||||||
|
kubectl get pods -n www-cialloo-com-production
|
||||||
|
kubectl get services -n www-cialloo-com-production
|
||||||
|
kubectl get ingress -n www-cialloo-com-production
|
||||||
|
```
|
||||||
|
|
||||||
|
### View logs:
|
||||||
|
```bash
|
||||||
|
kubectl logs -l app=www-cialloo-com -n www-cialloo-com-production
|
||||||
|
```
|
||||||
|
|
||||||
|
### Describe problematic pods:
|
||||||
|
```bash
|
||||||
|
kubectl describe pod <pod-name> -n www-cialloo-com-production
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **Least Privilege**: Ensure your kubeconfig has minimal required permissions
|
||||||
|
2. **Secret Rotation**: Regularly rotate Docker registry credentials and kubeconfig
|
||||||
|
3. **Environment Separation**: Use different namespaces/clusters for production and staging
|
||||||
|
4. **Network Policies**: Consider implementing Kubernetes network policies
|
||||||
|
5. **RBAC**: Configure proper Role-Based Access Control in your cluster
|
||||||
|
|
||||||
|
## Workflow Features
|
||||||
|
|
||||||
|
- **Manual Trigger Only**: Workflow only runs when manually dispatched
|
||||||
|
- **Environment Selection**: Choose between production and staging
|
||||||
|
- **Custom Image Tags**: Optionally specify custom Docker image tags
|
||||||
|
- **Health Checks**: Includes liveness and readiness probes
|
||||||
|
- **Rolling Updates**: Zero-downtime deployments
|
||||||
|
- **Status Reporting**: Detailed deployment status in GitHub Actions summary
|
||||||
31
Dockerfile
Normal file
31
Dockerfile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Build stage
|
||||||
|
FROM node:18-alpine as build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm ci --only=production
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# Copy built assets from build stage
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Copy custom nginx configuration
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
# Expose port 80
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Start nginx
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
46
k8s/deployment.yaml
Normal file
46
k8s/deployment.yaml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: www-cialloo-com
|
||||||
|
namespace: www-cialloo-com-{{ENVIRONMENT}}
|
||||||
|
labels:
|
||||||
|
app: www-cialloo-com
|
||||||
|
environment: {{ENVIRONMENT}}
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: www-cialloo-com
|
||||||
|
environment: {{ENVIRONMENT}}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: www-cialloo-com
|
||||||
|
environment: {{ENVIRONMENT}}
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: www-cialloo-com
|
||||||
|
image: {{IMAGE_NAME}}:{{IMAGE_TAG}}
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "64Mi"
|
||||||
|
cpu: "50m"
|
||||||
|
limits:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 80
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 80
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: docker-registry-secret
|
||||||
23
k8s/ingress.yaml
Normal file
23
k8s/ingress.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: www-cialloo-com-ingress
|
||||||
|
namespace: www-cialloo-com-{{ENVIRONMENT}}
|
||||||
|
labels:
|
||||||
|
app: www-cialloo-com
|
||||||
|
environment: {{ENVIRONMENT}}
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
spec:
|
||||||
|
ingressClassName: traefik
|
||||||
|
rules:
|
||||||
|
- host: www.cialloo.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: www-cialloo-com-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
7
k8s/namespace.yaml
Normal file
7
k8s/namespace.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: www-cialloo-com-{{ENVIRONMENT}}
|
||||||
|
labels:
|
||||||
|
app: www-cialloo-com
|
||||||
|
environment: {{ENVIRONMENT}}
|
||||||
17
k8s/service.yaml
Normal file
17
k8s/service.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: www-cialloo-com-service
|
||||||
|
namespace: www-cialloo-com-{{ENVIRONMENT}}
|
||||||
|
labels:
|
||||||
|
app: www-cialloo-com
|
||||||
|
environment: {{ENVIRONMENT}}
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
selector:
|
||||||
|
app: www-cialloo-com
|
||||||
|
environment: {{ENVIRONMENT}}
|
||||||
Reference in New Issue
Block a user