feat: externalize appsettings to K8s Secret — config persists independently of image rebuilds
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m57s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m57s
- Add cms-config.yaml (K8s Secret) for staging and production
- Mount appsettings.{Environment}.json from Secret into /app/
- Update CI/CD pipelines to apply Secret before Deployment
- Remove redundant env vars (Kestrel, FileStorage) — now in config file
This commit is contained in:
@@ -88,13 +88,15 @@ jobs:
|
|||||||
export SSHPASS="${{ secrets.SERVER_PASSWORD }}"
|
export SSHPASS="${{ secrets.SERVER_PASSWORD }}"
|
||||||
|
|
||||||
# Copy K8s manifests to server
|
# Copy K8s manifests to server
|
||||||
|
sshpass -e scp -o StrictHostKeyChecking=no k8s/staging/cms-config.yaml root@${{ env.K8S_SERVER }}:/tmp/cms-config.yaml
|
||||||
sshpass -e scp -o StrictHostKeyChecking=no k8s/staging/cms-deployment.yaml root@${{ env.K8S_SERVER }}:/tmp/cms-deployment.yaml
|
sshpass -e scp -o StrictHostKeyChecking=no k8s/staging/cms-deployment.yaml root@${{ env.K8S_SERVER }}:/tmp/cms-deployment.yaml
|
||||||
|
|
||||||
# Apply manifests and restart
|
# Apply config (Secret) first, then deployment
|
||||||
sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} "
|
sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} "
|
||||||
|
kubectl apply -f /tmp/cms-config.yaml &&
|
||||||
kubectl apply -f /tmp/cms-deployment.yaml &&
|
kubectl apply -f /tmp/cms-deployment.yaml &&
|
||||||
kubectl rollout restart deployment/cms &&
|
kubectl rollout restart deployment/cms &&
|
||||||
kubectl rollout status deployment/cms --timeout=180s &&
|
kubectl rollout status deployment/cms --timeout=180s &&
|
||||||
rm -f /tmp/cms-deployment.yaml
|
rm -f /tmp/cms-config.yaml /tmp/cms-deployment.yaml
|
||||||
"
|
"
|
||||||
echo "✅ Deployed!"
|
echo "✅ Deployed!"
|
||||||
|
|||||||
@@ -88,13 +88,15 @@ jobs:
|
|||||||
export SSHPASS="${{ secrets.SERVER_PASSWORD }}"
|
export SSHPASS="${{ secrets.SERVER_PASSWORD }}"
|
||||||
|
|
||||||
# Copy K8s manifests to server
|
# Copy K8s manifests to server
|
||||||
|
sshpass -e scp -o StrictHostKeyChecking=no k8s/production/cms-config.yaml root@${{ env.K8S_SERVER }}:/tmp/cms-config.yaml
|
||||||
sshpass -e scp -o StrictHostKeyChecking=no k8s/production/cms-deployment.yaml root@${{ env.K8S_SERVER }}:/tmp/cms-deployment.yaml
|
sshpass -e scp -o StrictHostKeyChecking=no k8s/production/cms-deployment.yaml root@${{ env.K8S_SERVER }}:/tmp/cms-deployment.yaml
|
||||||
|
|
||||||
# Apply manifests and update image
|
# Apply config (Secret) first, then deployment and update image
|
||||||
sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} "
|
sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} "
|
||||||
|
kubectl apply -f /tmp/cms-config.yaml &&
|
||||||
kubectl apply -f /tmp/cms-deployment.yaml &&
|
kubectl apply -f /tmp/cms-deployment.yaml &&
|
||||||
kubectl set image deployment/cms cms=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} &&
|
kubectl set image deployment/cms cms=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} &&
|
||||||
kubectl rollout status deployment/cms --timeout=300s &&
|
kubectl rollout status deployment/cms --timeout=300s &&
|
||||||
rm -f /tmp/cms-deployment.yaml
|
rm -f /tmp/cms-config.yaml /tmp/cms-deployment.yaml
|
||||||
"
|
"
|
||||||
echo "✅ Deployed to Production!"
|
echo "✅ Deployed to Production!"
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
# CMS Application Configuration Secret (Production)
|
||||||
|
# This Secret stores appsettings.Production.json externally in K8s
|
||||||
|
# so configuration persists independently of Docker image rebuilds.
|
||||||
|
#
|
||||||
|
# To edit config without redeploying:
|
||||||
|
# kubectl edit secret cms-appsettings
|
||||||
|
# kubectl rollout restart deployment/cms
|
||||||
|
#
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: cms-appsettings
|
||||||
|
namespace: default
|
||||||
|
labels:
|
||||||
|
app: cms
|
||||||
|
environment: production
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
appsettings.Production.json: |
|
||||||
|
{
|
||||||
|
"ZarinPal": {
|
||||||
|
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
|
||||||
|
"UseSandbox": false
|
||||||
|
},
|
||||||
|
"CmsBaseUrl": "https://cms.kbs1.ir",
|
||||||
|
"FrontOfficeBaseUrl": "https://kbs1.ir",
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "Server=mssql-svc;Database=KBS;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True;"
|
||||||
|
},
|
||||||
|
"SeedWorkers": {
|
||||||
|
"MagicWalletCycleSeed": {
|
||||||
|
"Enabled": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"FileStorage": {
|
||||||
|
"UploadPath": "/app/Uploads"
|
||||||
|
},
|
||||||
|
"Kestrel": {
|
||||||
|
"EndpointDefaults": {
|
||||||
|
"Protocols": "Http2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Seq": {
|
||||||
|
"ServerUrl": "http://seq-svc:5341",
|
||||||
|
"ApiKey": "oxpvpUzU1pZxMS4s3Fqq"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Warning",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,13 +45,13 @@ spec:
|
|||||||
value: "Production"
|
value: "Production"
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
- name: Kestrel__EndpointDefaults__Protocols
|
|
||||||
value: "Http1AndHttp2"
|
|
||||||
- name: FileStorage__UploadPath
|
|
||||||
value: "/app/Uploads"
|
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: cms-uploads
|
- name: cms-uploads
|
||||||
mountPath: /app/Uploads
|
mountPath: /app/Uploads
|
||||||
|
- name: cms-config
|
||||||
|
mountPath: /app/appsettings.Production.json
|
||||||
|
subPath: appsettings.Production.json
|
||||||
|
readOnly: true
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "512Mi"
|
memory: "512Mi"
|
||||||
@@ -79,6 +79,9 @@ spec:
|
|||||||
- name: cms-uploads
|
- name: cms-uploads
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: cms-uploads-pvc
|
claimName: cms-uploads-pvc
|
||||||
|
- name: cms-config
|
||||||
|
secret:
|
||||||
|
secretName: cms-appsettings
|
||||||
|
|
||||||
---
|
---
|
||||||
# CMS Service (Production)
|
# CMS Service (Production)
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
---
|
||||||
|
# CMS Application Configuration Secret (Staging)
|
||||||
|
# This Secret stores appsettings.Staging.json externally in K8s
|
||||||
|
# so configuration persists independently of Docker image rebuilds.
|
||||||
|
#
|
||||||
|
# To edit config without redeploying:
|
||||||
|
# kubectl edit secret cms-appsettings
|
||||||
|
# kubectl rollout restart deployment/cms
|
||||||
|
#
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: cms-appsettings
|
||||||
|
namespace: default
|
||||||
|
labels:
|
||||||
|
app: cms
|
||||||
|
environment: staging
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
appsettings.Staging.json: |
|
||||||
|
{
|
||||||
|
"PaymentProvider": "zarinpal",
|
||||||
|
"ZarinPal": {
|
||||||
|
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
|
||||||
|
"UseSandbox": true
|
||||||
|
},
|
||||||
|
"FMS": {
|
||||||
|
"Address": "https://dl.afrino.co"
|
||||||
|
},
|
||||||
|
"JwtSecurityKey": "TvlZVx5TJaHs8e9HgUdGzhGP2CIidoI444nAj+8+g7c=",
|
||||||
|
"JwtIssuer": "https://localhost",
|
||||||
|
"JwtAudience": "https://localhost",
|
||||||
|
"JwtExpiryInDays": 5,
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "Data Source=194.5.195.53,31433; Initial Catalog=Foursat;User ID=sa;Password=87zH26nbqT;Connection Timeout=300000;MultipleActiveResultSets=True;Encrypt=False",
|
||||||
|
"providerName": "System.Data.SqlClient"
|
||||||
|
},
|
||||||
|
"Otp": {
|
||||||
|
"Secret": "K2w8k1h1mH2Qz1kqWk0c8kQ2Pq8q9H1eE2nqN1qQ8x7M="
|
||||||
|
},
|
||||||
|
"Monitoring": {
|
||||||
|
"SentryEnabled": false,
|
||||||
|
"SentryDsn": "",
|
||||||
|
"SlackEnabled": false,
|
||||||
|
"SlackWebhookUrl": "",
|
||||||
|
"EmailAlertsEnabled": false,
|
||||||
|
"AdminEmails": ["admin@example.com"],
|
||||||
|
"SmsNotificationsEnabled": false,
|
||||||
|
"SmsApiKey": "",
|
||||||
|
"SmsGatewayUrl": ""
|
||||||
|
},
|
||||||
|
"Email": {
|
||||||
|
"Enabled": true,
|
||||||
|
"SmtpHost": "smtp.gmail.com",
|
||||||
|
"SmtpPort": 587,
|
||||||
|
"SmtpUsername": "your-email@gmail.com",
|
||||||
|
"SmtpPassword": "your-app-password",
|
||||||
|
"FromEmail": "noreply@foursat.com",
|
||||||
|
"FromName": "FourSat CMS",
|
||||||
|
"EnableSsl": true
|
||||||
|
},
|
||||||
|
"Sms": {
|
||||||
|
"Enabled": true,
|
||||||
|
"Provider": "Kavenegar",
|
||||||
|
"KavenegarApiKey": "497263626F32626A48685A6137524C4F78575A766E4C74694A556B79317648424964655030682B554545413D",
|
||||||
|
"Sender": "1000001110100"
|
||||||
|
},
|
||||||
|
"DayaPayment": {
|
||||||
|
"BaseUrl": "https://api.daya.ir",
|
||||||
|
"ApiKey": "YOUR_DAYA_API_KEY"
|
||||||
|
},
|
||||||
|
"DayaApi": {
|
||||||
|
"UseMock": false,
|
||||||
|
"BaseAddress": "https://Dayadiamond.ir",
|
||||||
|
"MerchantPermissionKey": "56146364$04sXjethI5WxhItR1Q9xnmFdJzl2BB8Bclsq8dAy7YVSZp3vtt-wP7ivrcCvmKLq",
|
||||||
|
"CacheDurationMinutes": 20
|
||||||
|
},
|
||||||
|
"Chatika": {
|
||||||
|
"Enabled": true,
|
||||||
|
"BaseUrl": "https://api.chatika.ir",
|
||||||
|
"ApiKey": "tIukvL8dnV4cB3yVWcCD9Xyfbj8rBxm5wPt2mLyJCgTsBBoMTWjt6mFEqQwpw-er"
|
||||||
|
},
|
||||||
|
"BackgroundJobs": {
|
||||||
|
"WeeklyCommissionCalculation": {
|
||||||
|
"Enabled": true,
|
||||||
|
"CronExpression": "5 0 * * 0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SeedWorkers": {
|
||||||
|
"MagicWalletCycleSeed": {
|
||||||
|
"Enabled": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"FileStorage": {
|
||||||
|
"UploadPath": "/app/Uploads"
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"Kestrel": {
|
||||||
|
"EndpointDefaults": {
|
||||||
|
"Protocols": "Http1AndHttp2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Authentication": {
|
||||||
|
"Authority": "https://ids.domain.com/",
|
||||||
|
"Audience": "domain_api"
|
||||||
|
},
|
||||||
|
"Seq": {
|
||||||
|
"ServerUrl": "https://seq.afrino.co",
|
||||||
|
"ApiKey": "oxpvpUzU1pZxMS4s3Fqq"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,13 +45,13 @@ spec:
|
|||||||
value: "Staging"
|
value: "Staging"
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
- name: Kestrel__EndpointDefaults__Protocols
|
|
||||||
value: "Http1AndHttp2"
|
|
||||||
- name: FileStorage__UploadPath
|
|
||||||
value: "/app/Uploads"
|
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: cms-uploads
|
- name: cms-uploads
|
||||||
mountPath: /app/Uploads
|
mountPath: /app/Uploads
|
||||||
|
- name: cms-config
|
||||||
|
mountPath: /app/appsettings.Staging.json
|
||||||
|
subPath: appsettings.Staging.json
|
||||||
|
readOnly: true
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "512Mi"
|
memory: "512Mi"
|
||||||
@@ -79,6 +79,9 @@ spec:
|
|||||||
- name: cms-uploads
|
- name: cms-uploads
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: cms-uploads-pvc
|
claimName: cms-uploads-pvc
|
||||||
|
- name: cms-config
|
||||||
|
secret:
|
||||||
|
secretName: cms-appsettings
|
||||||
|
|
||||||
---
|
---
|
||||||
# CMS Service
|
# CMS Service
|
||||||
|
|||||||
Reference in New Issue
Block a user