4ef4bfbeef
- payment-gateway.md: سکشن ۸-۱۳ (ZarinPal callback, تخفیف ۱۰۰٪, VAT, ExpirePendingOrders, DeliveryStatus mapping, Production deploy) - CICD-PIPELINE-GUIDE.md: باگ cross-deploy, قالب workflow Production, جدول مقایسه دو محیط - INFRASTRUCTURE-GUIDE.md: سرور Production (45.149.79.127), DB KBS, Proto v0.0.179 - DISCOUNT-STORE-STATUS.md: وضعیت Production Deploy, فلوی پرداخت جدید - SERVER-MIRRORS-CONFIG.md: registries.yaml سرور Production - INDEX.md: تاریخ, توضیحات بروز, لینکهای سریع جدید
616 lines
22 KiB
Markdown
616 lines
22 KiB
Markdown
# 🔧 راهنمای CI/CD Pipeline — Gitea Actions + K3s
|
||
|
||
> آخرین بروزرسانی: February 17, 2026
|
||
|
||
---
|
||
|
||
## 📐 معماری کلی
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ K3s Cluster (194.5.195.53) │
|
||
│ │
|
||
│ ┌─────────────────────────────────────────────────┐ │
|
||
│ │ gitea-runner Pod (2 containers) │ │
|
||
│ │ │ │
|
||
│ │ ┌──────────────────┐ ┌──────────────────────┐ │ │
|
||
│ │ │ docker (DinD) │ │ runner (act_runner) │ │ │
|
||
│ │ │ docker:dind │ │ gitea/act_runner │ │ │
|
||
│ │ │ privileged: true │ │ DOCKER_HOST= │ │ │
|
||
│ │ │ port: 2375 │ │ tcp://localhost:2375│ │ │
|
||
│ │ └──────────────────┘ └──────────────────────┘ │ │
|
||
│ │ ▲ shared volumes: docker-storage │ │
|
||
│ └─────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌─────────────┐ ┌───────────┐ ┌────────────────┐ │
|
||
│ │ Gitea │ │ Nexus │ │ Docker Reg. │ │
|
||
│ │ :3000 │ │ :32081 │ │ :32082 (pull) │ │
|
||
│ │ │ │ (NuGet) │ │ :30080 (push) │ │
|
||
│ └─────────────┘ └───────────┘ └────────────────┘ │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### سه لایه Docker-in-Docker:
|
||
```
|
||
K3s containerd (لایه ۱)
|
||
└── gitea-runner Pod → docker container (DinD daemon) (لایه ۲)
|
||
└── workflow: docker run / docker build (لایه ۳)
|
||
```
|
||
|
||
---
|
||
|
||
## 📁 فایلهای Workflow
|
||
|
||
### 🐳 K8s Pipelines (Docker + K3s) — آفلاین
|
||
|
||
| سرویس | فایل | Branch | Image | Deploy |
|
||
|--------|------|--------|-------|--------|
|
||
| CMS | `kub-deploy.yml` | `kub-stage` | `admin/cms` | SSH → kubectl |
|
||
| BackOffice | `kub-deploy.yml` | `kub-stage` | `admin/backoffice` | SSH → kubectl |
|
||
| FrontOffice | `kub-deploy.yml` | `kub-stage` | `admin/frontoffice` | SSH → kubectl |
|
||
| CMS | `prod-deploy.yml` | `production` | `admin/cms:prod` | SSH → kubectl |
|
||
| BackOffice | `prod-deploy.yml` | `production` | `admin/backoffice:prod` | SSH → kubectl |
|
||
| FrontOffice | `prod-deploy.yml` | `production` | `admin/frontoffice:prod` | SSH → kubectl |
|
||
|
||
### 🪟 Windows/IIS Pipelines (Legacy) — آنلاین
|
||
|
||
| سرویس | فایل | Branch | Target |
|
||
|--------|------|--------|--------|
|
||
| CMS | `cms-stage.yml` | `stage_new` | IIS → `cms.kbs1.ir` |
|
||
| BackOffice | `bo-stage.yml` | `stage-new` | IIS → `admin.kbs1.ir` |
|
||
| FrontOffice | `fo-stage.yml` | `stage-new` | IIS → `kbs1.ir` |
|
||
|
||
> ⚠️ Stage pipeline ها از Windows runner + IIS استفاده میکنن و Docker ندارن.
|
||
|
||
### ساختار مشترک Pipeline:
|
||
```
|
||
1. Start Docker daemon (DinD)
|
||
2. Checkout code (git clone)
|
||
3. Login to Docker registries (32082 + 30080)
|
||
4. [CMS only] Publish Protobuf packages
|
||
5. Build Docker Image
|
||
6. Push to Registry
|
||
7. Deploy to Kubernetes (SSH → kubectl rollout restart)
|
||
```
|
||
|
||
---
|
||
|
||
## 🐛 مشکلات حلشده و راهحلها
|
||
|
||
### مشکل ۱: `iptables failed: Permission denied`
|
||
|
||
**خطا:**
|
||
```
|
||
iptables v1.8.10 (nf_tables): Could not fetch rule set generation id: Permission denied
|
||
```
|
||
|
||
**علت:** K3s containerd به Docker daemon اجازه تغییر iptables نمیده.
|
||
|
||
**راهحل:** غیرفعال کردن networking در dockerd:
|
||
```bash
|
||
dockerd --iptables=false --ip6tables=false --bridge=none --storage-driver=vfs &
|
||
```
|
||
|
||
> ⚠️ با `--bridge=none` نیاز به شبکهسازی Docker نیست چون فقط build و push انجام میشه.
|
||
|
||
---
|
||
|
||
### مشکل ۲: `failed to unmount overlayfs: operation not permitted`
|
||
|
||
**خطا:**
|
||
```
|
||
failed to register layer: unshare: operation not permitted
|
||
```
|
||
|
||
**علت:** `overlay2` storage driver نیاز به mount namespace داره که داخل K3s مجاز نیست.
|
||
|
||
**راهحل:** استفاده از `vfs` storage driver:
|
||
```bash
|
||
dockerd --storage-driver=vfs &
|
||
```
|
||
|
||
> ⚠️ `vfs` کندتره ولی هیچ mount syscall خاصی نیاز نداره. برای CI/CD کافیه.
|
||
|
||
---
|
||
|
||
### مشکل ۳: `no basic auth credentials` هنگام pull ایمیج
|
||
|
||
**خطا:**
|
||
```
|
||
Error response from daemon: Head "https://194.5.195.53:32082/v2/dotnet/sdk/manifests/9.0":
|
||
no basic auth credentials
|
||
```
|
||
|
||
**علت:** `docker login` فقط قبل از push انجام میشد، ولی `docker build` (یا `docker run`) هم از `32082` ایمیج pull میکنه.
|
||
|
||
**راهحل:** اضافه کردن step "Login to Docker registries" بلافاصله بعد از Checkout:
|
||
```yaml
|
||
- name: Login to Docker registries
|
||
run: |
|
||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login 194.5.195.53:32082 -u admin --password-stdin
|
||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u admin --password-stdin
|
||
```
|
||
|
||
---
|
||
|
||
### مشکل ۴: `unshare: operation not permitted` هنگام extract لایهها
|
||
|
||
**خطا:**
|
||
```
|
||
docker: failed to register layer: unshare: operation not permitted
|
||
```
|
||
|
||
**علت اصلی (دو بخش):**
|
||
|
||
**بخش ۱:** Gitea act_runner دیفالت `container.privileged: false` داره. یعنی job container ها بدون privileged ساخته میشن — حتی اگه workflow بنویسه `options: --privileged`.
|
||
|
||
**بخش ۲:** env var `CONFIG_FILE` در runner container ست نبود → `run.sh` فلگ `--config` رو به `act_runner daemon` پاس نمیداد → config.yaml اصلاً لود نمیشد!
|
||
|
||
**راهحل (سمت سرور):**
|
||
|
||
۱. ساخت ConfigMap:
|
||
```yaml
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: runner-config
|
||
namespace: default
|
||
data:
|
||
config.yaml: |
|
||
log:
|
||
level: info
|
||
runner:
|
||
file: .runner
|
||
capacity: 1
|
||
timeout: 3h
|
||
container:
|
||
privileged: true
|
||
options: "--security-opt seccomp=unconfined --security-opt apparmor=unconfined"
|
||
valid_volumes:
|
||
- "**"
|
||
```
|
||
|
||
۲. Mount کردن در Deployment + env var:
|
||
```bash
|
||
kubectl patch deployment gitea-runner --type=json -p='[
|
||
{"op":"add","path":"/spec/template/spec/containers/1/env/-",
|
||
"value":{"name":"CONFIG_FILE","value":"/data/config.yaml"}},
|
||
{"op":"add","path":"/spec/template/spec/containers/1/volumeMounts/-",
|
||
"value":{"name":"runner-config","mountPath":"/data/config.yaml","subPath":"config.yaml"}},
|
||
{"op":"add","path":"/spec/template/spec/volumes/-",
|
||
"value":{"name":"runner-config","configMap":{"name":"runner-config"}}}
|
||
]'
|
||
```
|
||
|
||
> ⚠️ **نکته مهم:** بدون `CONFIG_FILE=/data/config.yaml` env var، فایل `run.sh` داخل act_runner image فلگ `--config` رو پاس نمیده!
|
||
|
||
---
|
||
|
||
### مشکل ۵: Protobuf restore از nuget.org بجای Nexus
|
||
|
||
**علت:** `dotnet restore` بدون `--configfile` از دیفالت NuGet sources استفاده میکنه.
|
||
|
||
**راهحل:**
|
||
```bash
|
||
dotnet restore "$proj" --configfile src/NuGet.config
|
||
```
|
||
|
||
---
|
||
|
||
### مشکل ۶: عدم دسترسی شبکه با `--bridge=none`
|
||
|
||
**علت:** `dockerd --bridge=none` شبکه Docker bridge رو غیرفعال میکنه. در نتیجه container هایی که با `docker run` یا `docker build` ساخته میشن، دسترسی شبکه ندارن (مثلاً `dotnet restore` نمیتونه به Nexus وصل بشه).
|
||
|
||
**راهحل:** استفاده از `--network host` در `docker run` و `docker build`:
|
||
```bash
|
||
# Protobuf step
|
||
docker run --rm --network host -v $(pwd):/src -w /src ...
|
||
|
||
# Build step
|
||
DOCKER_BUILDKIT=0 docker build --network host -t ... .
|
||
```
|
||
|
||
---
|
||
|
||
### مشکل ۷: `failed to prepare ... as ...: invalid argument` (BuildKit)
|
||
|
||
**خطا:**
|
||
```
|
||
ERROR: failed to build: failed to solve: failed to prepare xxx as yyy: invalid argument
|
||
```
|
||
|
||
**علت:** BuildKit (بیلدر پیشفرض Docker ≥23) از snapshotter overlay استفاده میکنه که با `--storage-driver=vfs` سازگاری نداره.
|
||
|
||
**راهحل:** غیرفعال کردن BuildKit:
|
||
```bash
|
||
DOCKER_BUILDKIT=0 docker build --network host -t ... .
|
||
```
|
||
|
||
> ⚠️ Legacy builder از vfs بدون مشکل استفاده میکنه.
|
||
|
||
---
|
||
|
||
### مشکل ۸: `COPY libs/` fails in Docker build (BackOffice)
|
||
|
||
**خطا:**
|
||
```
|
||
COPY failed: file not found in build context: stat libs/: file does not exist
|
||
```
|
||
|
||
**علت:** Dockerfile خط `COPY ["libs/", "libs/"]` داشت ولی `libs/` خارج از Docker build context (`src/`) بود. قبلاً BFF DLLها استفاده میشدن، ولی حالا از NuGet package مستقیم استفاده میشه.
|
||
|
||
**راهحل:**
|
||
1. حذف `COPY ["libs/", "libs/"]` از Dockerfile
|
||
2. تغییر `ProjectReference` به `PackageReference` در csproj:
|
||
```xml
|
||
<!-- قبل -->
|
||
<ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />
|
||
<!-- بعد -->
|
||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.178" />
|
||
```
|
||
|
||
---
|
||
|
||
### مشکل ۹: ProjectReference خارج از Docker context (FrontOffice/BackOffice)
|
||
|
||
**خطا:**
|
||
```
|
||
error CS0246: The type or namespace name 'CustomerAddressModel' could not be found
|
||
```
|
||
|
||
**علت:** csproj از `ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/..."` استفاده میکرد. در Docker build context فقط `src/` موجوده → CMS قابل دسترسی نیست.
|
||
|
||
**راهحل:**
|
||
1. بامپ نسخه پروتوباف (`0.0.177` → `0.0.178`)
|
||
2. `dotnet pack -c Release` و push به Nexus
|
||
3. تغییر هر دو پروژه (FrontOffice + BackOffice) به `PackageReference`
|
||
|
||
```bash
|
||
# Pack & Push
|
||
cd CMS/src/CMSMicroservice.Protobuf
|
||
dotnet pack -c Release
|
||
dotnet nuget push bin/Release/Foursat.CMSMicroservice.Protobuf.0.0.178.nupkg \
|
||
--source http://194.5.195.53:32081/repository/foursat-nuget-hosted/index.json \
|
||
--api-key admin:87zH26nbqT --skip-duplicate
|
||
```
|
||
|
||
---
|
||
|
||
### مشکل ۱۰: `nginx:alpine` TLS handshake timeout
|
||
|
||
**خطا:**
|
||
```
|
||
Get "https://registry-1.docker.io/v2/": net/http: TLS handshake timeout
|
||
```
|
||
|
||
**علت:** Dockerfile خط `FROM nginx:alpine` مستقیم از Docker Hub پول میکرد ولی سرور به Docker Hub دسترسی نداره.
|
||
|
||
**راهحل:** تغییر به رجیستری لوکال:
|
||
```dockerfile
|
||
# قبل
|
||
FROM nginx:alpine AS final
|
||
# بعد
|
||
FROM 194.5.195.53:32082/nginx:alpine AS final
|
||
```
|
||
|
||
---
|
||
|
||
### مشکل ۱۱: SERVER_PASSWORD secret missing → Permission denied
|
||
|
||
**خطا:**
|
||
```
|
||
Permission denied, please try again.
|
||
```
|
||
|
||
**علت:** سکرت `SERVER_PASSWORD` در ریپو Gitea تنظیم نشده بود. Pipeline از `sshpass -e` با `${{ secrets.SERVER_PASSWORD }}` برای SSH استفاده میکنه.
|
||
|
||
**راهحل:** اضافه کردن سکرت از طریق Gitea API:
|
||
```bash
|
||
curl -sk -u "admin:87zH26nbqT" -X PUT \
|
||
"https://git.se.kbs1.ir/api/v1/repos/admin/BackOffice/actions/secrets/SERVER_PASSWORD" \
|
||
-H "Content-Type: application/json" -d '{"data":"87zH26nbqT"}'
|
||
```
|
||
|
||
---
|
||
|
||
### مشکل ۱۲: CMS ingress 502 — backend-protocol: GRPC
|
||
|
||
**خطا:** `https://cms.se.kbs1.ir/` → 502 Bad Gateway
|
||
|
||
**علت:** CMS ingress annotation `backend-protocol: GRPC` داشت + Kestrel فقط `Http2`. مرورگر HTTP/1.1 میفرسته → nginx نمیتونه به gRPC backend فوروارد کنه.
|
||
|
||
**راهحل (دو تغییر):**
|
||
1. Kestrel protocol → `Http1AndHttp2` (هم gRPC هم REST):
|
||
```bash
|
||
kubectl set env deployment/cms Kestrel__EndpointDefaults__Protocols=Http1AndHttp2
|
||
```
|
||
2. حذف GRPC annotations از ingress:
|
||
```bash
|
||
kubectl annotate ingress cms-ingress nginx.ingress.kubernetes.io/backend-protocol-
|
||
kubectl annotate ingress cms-ingress nginx.ingress.kubernetes.io/grpc-backend-
|
||
```
|
||
|
||
> ⚠️ FrontOffice از gRPC-Web استفاده میکنه که روی HTTP/1.1 هم کار میکنه.
|
||
|
||
---
|
||
|
||
## 🔄 تغییرات prod-deploy (قدیم → جدید)
|
||
|
||
| مورد | قدیم (prod-deploy) | جدید |
|
||
|------|-------------------|------|
|
||
| Container image | `docker:latest` | `docker-sshpass:latest` (شامل sshpass + git) |
|
||
| Proxy | `HTTP_PROXY` + `HTTPS_PROXY` | حذف شد (آفلاین) |
|
||
| Registry | `gitea-svc:3000` + external | فقط `194.5.195.53:30080` |
|
||
| kubectl | `apk add` + `curl` از اینترنت | SSH → `kubectl` مستقیم روی سرور |
|
||
| Auth | hardcoded password | `secrets.REGISTRY_PASSWORD` + `secrets.SERVER_PASSWORD` |
|
||
| BuildKit | فعال (دیفالت) | `DOCKER_BUILDKIT=0` |
|
||
| Network | Docker bridge (دیفالت) | `--network host` |
|
||
| dockerd | دیفالت | `--iptables=false --ip6tables=false --bridge=none --storage-driver=vfs` |
|
||
| Deploy | `KUBECONFIG_PROD` (base64) | SSH + sshpass (مثل kub-stage) |
|
||
|
||
> ✅ حالا همه ۶ K8s pipeline (۳ stage + ۳ prod) از **یک الگوی مشترک آفلاین** استفاده میکنن.
|
||
|
||
---
|
||
|
||
containers:
|
||
- name: docker # DinD sidecar
|
||
image: 194.5.195.53:32082/docker:dind
|
||
securityContext:
|
||
privileged: true
|
||
env:
|
||
- DOCKER_TLS_CERTDIR: ""
|
||
volumeMounts:
|
||
- /var/lib/docker → docker-storage
|
||
- /etc/docker/daemon.json → docker-config (ConfigMap)
|
||
|
||
- name: runner # Gitea act_runner
|
||
image: 194.5.195.53:32082/gitea/act_runner:latest
|
||
env:
|
||
- GITEA_INSTANCE_URL: http://gitea-svc:3000
|
||
- DOCKER_HOST: tcp://localhost:2375
|
||
- CONFIG_FILE: /data/config.yaml # ← حیاتی! بدون این runner config لود نمیشه
|
||
volumeMounts:
|
||
- /data → runner-data
|
||
- /data/config.yaml → runner-config (ConfigMap)
|
||
```
|
||
|
||
### ConfigMaps:
|
||
| نام | محتوا | Mount Path |
|
||
|-----|-------|------------|
|
||
| `docker-daemon-config` | `daemon.json` با insecure-registries | `/etc/docker/daemon.json` |
|
||
| `runner-config` | `config.yaml` با privileged + seccomp | `/data/config.yaml` |
|
||
|
||
### Labels (ثبتشده در Gitea):
|
||
```
|
||
ubuntu-latest → docker://docker.gitea.com/runner-images:ubuntu-latest
|
||
ubuntu-24.04 → docker://docker.gitea.com/runner-images:ubuntu-24.04
|
||
ubuntu-22.04 → docker://docker.gitea.com/runner-images:ubuntu-22.04
|
||
```
|
||
|
||
---
|
||
|
||
## 🔑 Secrets مورد نیاز (Gitea → Settings → Secrets)
|
||
|
||
| Secret | استفاده |
|
||
|--------|---------|
|
||
| `REGISTRY_PASSWORD` | پسورد Docker registry (admin) |
|
||
| `SERVER_PASSWORD` | پسورد SSH سرور (root) — ⚠️ باید در هر ۳ ریپو ست بشه |
|
||
|
||
> **نکته:** اگر `SERVER_PASSWORD` ست نباشه، مرحله Deploy با `Permission denied` فیل میشه.
|
||
> با API اضافه کنید:
|
||
> ```bash
|
||
> curl -sk -u "admin:PASSWORD" -X PUT \
|
||
> "https://git.se.kbs1.ir/api/v1/repos/admin/REPO/actions/secrets/SERVER_PASSWORD" \
|
||
> -H "Content-Type: application/json" -d '{"data":"PASSWORD"}'
|
||
> ```
|
||
|
||
---
|
||
|
||
## 🔧 dockerd فلگهای نهایی
|
||
|
||
```bash
|
||
dockerd --iptables=false --ip6tables=false --bridge=none --storage-driver=vfs &
|
||
```
|
||
|
||
| Flag | دلیل |
|
||
|------|-------|
|
||
| `--iptables=false` | K3s اجازه تغییر iptables نمیده |
|
||
| `--ip6tables=false` | مشابه بالا برای IPv6 |
|
||
| `--bridge=none` | نیازی به Docker bridge network نیست |
|
||
| `--storage-driver=vfs` | overlay2 نمیتونه mount کنه داخل K3s |
|
||
|
||
---
|
||
|
||
## 🔍 عیبیابی Pipeline
|
||
|
||
### ۱. چک وضعیت Runner:
|
||
```bash
|
||
# SSH به سرور
|
||
ssh root@194.5.195.53
|
||
|
||
# آیا runner pod بالاست؟
|
||
kubectl get pods -l app=gitea-runner
|
||
|
||
# لاگ runner
|
||
kubectl logs <pod-name> -c runner --tail=30
|
||
|
||
# لاگ DinD
|
||
kubectl logs <pod-name> -c docker --tail=30
|
||
```
|
||
|
||
### ۲. تست Docker داخل Runner:
|
||
```bash
|
||
# exec به DinD container
|
||
kubectl exec <pod-name> -c docker -- docker info
|
||
|
||
# آیا registry قابل دسترسیه؟
|
||
kubectl exec <pod-name> -c docker -- docker pull 194.5.195.53:32082/dotnet/sdk:9.0
|
||
```
|
||
|
||
### ۳. چک config runner:
|
||
```bash
|
||
# آیا config.yaml mount شده؟
|
||
kubectl exec <pod-name> -c runner -- cat /data/config.yaml
|
||
|
||
# آیا privileged فعاله؟
|
||
kubectl exec <pod-name> -c docker -- docker inspect <job-container> \
|
||
--format '{{.HostConfig.Privileged}} {{.HostConfig.SecurityOpt}}'
|
||
```
|
||
|
||
### ۴. ریاستارت runner:
|
||
```bash
|
||
kubectl rollout restart deployment/gitea-runner
|
||
kubectl rollout status deployment/gitea-runner --timeout=120s
|
||
```
|
||
|
||
---
|
||
|
||
## 📋 Workflow Template (کامل)
|
||
|
||
```yaml
|
||
name: Build and Deploy to Kubernetes
|
||
|
||
on:
|
||
push:
|
||
branches:
|
||
- kub-stage
|
||
|
||
env:
|
||
REGISTRY: 194.5.195.53:30080
|
||
IMAGE_NAME: admin/<service-name>
|
||
K8S_SERVER: 194.5.195.53
|
||
|
||
jobs:
|
||
build-and-deploy:
|
||
runs-on: ubuntu-latest
|
||
container:
|
||
image: 194.5.195.53:32082/docker-sshpass:latest
|
||
options: --privileged
|
||
steps:
|
||
- name: Start Docker daemon
|
||
run: |
|
||
mkdir -p /etc/docker
|
||
cat > /etc/docker/daemon.json << 'DAEMON'
|
||
{
|
||
"insecure-registries": ["194.5.195.53:30080", "194.5.195.53:32500", "194.5.195.53:32082"]
|
||
}
|
||
DAEMON
|
||
dockerd --iptables=false --ip6tables=false --bridge=none --storage-driver=vfs &
|
||
for i in $(seq 1 90); do
|
||
if docker info >/dev/null 2>&1; then
|
||
echo "✅ Docker ready"; break
|
||
fi
|
||
sleep 2
|
||
done
|
||
|
||
- name: Checkout code
|
||
run: |
|
||
git clone --depth 1 --branch kub-stage http://gitea-svc:3000/admin/<repo>.git .
|
||
|
||
- name: Login to Docker registries
|
||
run: |
|
||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login 194.5.195.53:32082 -u admin --password-stdin
|
||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u admin --password-stdin
|
||
|
||
- name: Build Docker Image
|
||
run: |
|
||
DOCKER_BUILDKIT=0 docker build --network host -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest .
|
||
|
||
- name: Push to Registry
|
||
run: |
|
||
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||
|
||
- name: Deploy to Kubernetes
|
||
run: |
|
||
export SSHPASS="${{ secrets.SERVER_PASSWORD }}"
|
||
sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} "
|
||
kubectl rollout restart deployment/<service>
|
||
kubectl rollout status deployment/<service> --timeout=180s
|
||
"
|
||
```
|
||
---
|
||
|
||
## 🐛 باگ بحرانی: Cross-Deployment — Push به Production ریدیپلوی Staging (اسفند ۱۴۰۴)
|
||
|
||
### علائم:
|
||
- Push به برنچ `production` → هم production و هم staging ریدیپلوی شدند
|
||
- CMS staging pod بعد از push ریستارت شد
|
||
- Runner log: **۲ تسک CMS** بجای ۱ تسک اجرا شد
|
||
|
||
### علت ریشهای:
|
||
Gitea Act Runner **تمام فایلهای workflow** داخل `.gitea/workflows/` برنچ push شده رو اجرا میکنه — حتی اگه `on.push.branches` برنچ دیگهای باشه. وقتی production push شد، `kub-deploy.yml` (trigger: `kub-stage`) هم اجرا شد و ایمیج `admin/cms:latest` رو با کد production ساخت → staging از `latest` pull کرد → **staging با DB production بالا اومد!**
|
||
|
||
### راهحل:
|
||
حذف workflowهای staging از برنچ production (هر ۳ ریپو):
|
||
```bash
|
||
git rm .gitea/workflows/kub-deploy.yml .gitea/workflows/cms-stage.yml # CMS
|
||
git rm .gitea/workflows/fo-stage.yml .gitea/workflows/kub-deploy.yml # FrontOffice
|
||
git rm .gitea/workflows/bo-stage.yml .gitea/workflows/kub-deploy.yml # BackOffice
|
||
```
|
||
|
||
> ⚠️ **قانون طلایی:** هر برنچ فقط workflow مربوط به خودش رو داشته باشه.
|
||
|
||
---
|
||
|
||
## 🐛 مشکل ۱۳: Production deploy ایمیج pull نمیشد
|
||
|
||
**علت:** Production K8s از `git.foursat.afrino.co/admin/cms:prod` pull میکرد، ولی CI ایمیج رو به `194.5.195.53:30080` push میکرد.
|
||
|
||
**راهحل:**
|
||
1. اضافه کردن `194.5.195.53:30080` به `/etc/rancher/k3s/registries.yaml` پروداکشن + ریاستارت K3s
|
||
2. آپدیت deployment image: `kubectl set image deployment/cms cms=194.5.195.53:30080/admin/cms:prod`
|
||
3. فیکس `prod-deploy.yml`: `K8S_SSH_PASSWORD` → `SERVER_PASSWORD`, `rollout restart` → `set image :sha`
|
||
|
||
---
|
||
|
||
## 🔄 Production Workflow Template (فعلی)
|
||
|
||
```yaml
|
||
name: Build and Deploy to Production
|
||
on:
|
||
push:
|
||
branches: [production]
|
||
env:
|
||
REGISTRY: 194.5.195.53:30080
|
||
IMAGE_NAME: admin/<service>
|
||
K8S_SERVER: 45.149.79.127
|
||
jobs:
|
||
build-and-deploy:
|
||
runs-on: ubuntu-latest
|
||
container:
|
||
image: 194.5.195.53:32082/docker-sshpass:latest
|
||
options: --privileged
|
||
steps:
|
||
# ... (Start Docker, Checkout, Login — مشابه staging)
|
||
- name: Build Docker Image
|
||
run: |
|
||
DOCKER_BUILDKIT=0 docker build --network host \
|
||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
|
||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod .
|
||
- name: Push to Registry
|
||
run: |
|
||
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
|
||
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod
|
||
- name: Deploy to Production
|
||
run: |
|
||
export SSHPASS="${{ secrets.SERVER_PASSWORD }}"
|
||
sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} "
|
||
kubectl set image deployment/<svc> <svc>=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
|
||
kubectl rollout status deployment/<svc> --timeout=300s
|
||
"
|
||
```
|
||
|
||
> تفاوت staging vs production: staging = tag `latest` + `rollout restart` | production = tag `sha` + `set image`
|
||
|
||
---
|
||
|
||
## 🏗️ مشخصات دو محیط
|
||
|
||
| | Staging | Production |
|
||
|--|---------|------------|
|
||
| **سرور** | `194.5.195.53` | `45.149.79.127` |
|
||
| **DB** | `mssql-svc@Foursat` | `45.149.79.127,31433@KBS` |
|
||
| **Registry** | `194.5.195.53:30080` (local) | همان staging registry |
|
||
| **Image Tags** | `:latest` | `:prod` + `:sha` |
|
||
| **Branch** | `kub-stage` | `production` |
|
||
| **Domains** | `*.se.kbs1.ir` | `*.kbs1.ir` | |