Add initial Kubernetes deployment setup with GitHub Actions workflow, Dockerfile, and necessary Kubernetes manifests

This commit is contained in:
2025-10-01 13:51:33 +08:00
parent 24154f42d4
commit 00c02e8393
8 changed files with 471 additions and 0 deletions

48
.dockerignore Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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}}