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