20 KiB
راهنمای دیپلوی آفلاین FourSat
تاریخ: 2026-01-29 هدف: دیپلوی بدون نیاز به اینترنت خارجی
📋 خلاصه اجرایی
این راهنما شامل تنظیمات لازم برای دیپلوی کامل آفلاین پروژه FourSat است. با استفاده از Nexus به عنوان registry مرکزی و mirror های ایرانی به عنوان fallback، نیازی به اینترنت خارجی نیست.
🖥️ سرورها
| سرور | IP | نقش | رمز عبور |
|---|---|---|---|
| Stage | 194.5.195.53 |
Nexus, Gitea, Runner | 87zH26nbqT |
| Production | 45.149.79.127 |
K8S Production | 87zH26nbqT |
🐳 Nexus Registry
پورتها
| سرویس | پورت | پروتکل |
|---|---|---|
| Nexus UI | 32081 |
HTTP |
| Docker Registry | 32082 |
HTTP (insecure) |
| NuGet | 32081/repository/nuget-group/index.json |
HTTP |
Credentials
Username: admin
Password: 87zH26nbqT
ریپوزیتوریهای Docker
| نام | نوع | توضیح |
|---|---|---|
docker-hosted |
hosted | ایمیجهای پروژه |
docker-hub-proxy |
proxy | پروکسی Docker Hub |
docker-arvancloud-proxy |
proxy | پروکسی ArvanCloud |
docker-all |
group | گروه همه ریپوها |
ریپوزیتوریهای NuGet
| نام | نوع | توضیح |
|---|---|---|
foursat-nuget-hosted |
hosted | پکیجهای پروتوباف |
nuget.org-proxy |
proxy | پروکسی NuGet.org |
nuget-runflare-proxy |
proxy | پروکسی Runflare |
nuget-group |
group | گروه همه ریپوها |
🪞 Mirror های ایرانی (Fallback)
Docker
https://docker.arvancloud.ir
APT/Ubuntu
http://mirror.arvancloud.ir/ubuntu
NuGet
https://mirror-nuget.runflare.com/v3/index.json
PyPI
https://mirror-pypi.runflare.com/simple
NPM
https://mirror-npm.runflare.com
📦 ایمیجهای ذخیره شده در Nexus
| ایمیج | تگ | سایز تقریبی |
|---|---|---|
gitea/gitea |
1.25.3 |
~78MB |
mcr.microsoft.com/mssql/server |
2022-CU16-ubuntu-22.04 |
~1.6GB |
gitea/act_runner |
0.2.11, latest |
~50MB |
registry.k8s.io/ingress-nginx/controller |
v1.14.1 |
~280MB |
dotnet/sdk |
9.0 |
~900MB |
dotnet/aspnet |
9.0 |
~220MB |
library/nginx |
alpine |
~40MB |
docker |
dind |
~400MB |
docker-sshpass |
latest |
~500MB |
⚙️ تنظیمات K3s
فایل: /etc/rancher/k3s/registries.yaml
# Registry Mirrors Configuration
# Primary: Nexus (194.5.195.53:32082)
# Fallback: ArvanCloud (docker.arvancloud.ir)
mirrors:
"docker.io":
endpoint:
- "http://194.5.195.53:32082"
- "https://docker.arvancloud.ir"
- "https://registry-1.docker.io"
"194.5.195.53:32082":
endpoint:
- "http://194.5.195.53:32082"
"ghcr.io":
endpoint:
- "http://194.5.195.53:32082"
- "https://docker.arvancloud.ir"
"gcr.io":
endpoint:
- "http://194.5.195.53:32082"
- "https://docker.arvancloud.ir"
"registry.k8s.io":
endpoint:
- "http://194.5.195.53:32082"
- "https://docker.arvancloud.ir"
"quay.io":
endpoint:
- "http://194.5.195.53:32082"
- "https://docker.arvancloud.ir"
"mcr.microsoft.com":
endpoint:
- "http://194.5.195.53:32082"
- "https://docker.arvancloud.ir"
configs:
"194.5.195.53:32082":
auth:
username: admin
password: 87zH26nbqT
اعمال تغییرات
sudo systemctl restart k3s
📝 تنظیمات APT
فایل: /etc/apt/sources.list.d/ubuntu.sources
Types: deb
URIs: http://mirror.arvancloud.ir/ubuntu http://archive.ubuntu.com/ubuntu
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Types: deb
URIs: http://mirror.arvancloud.ir/ubuntu http://security.ubuntu.com/ubuntu
Suites: noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
🐍 تنظیمات PIP
فایل: /root/.config/pip/pip.conf
[global]
index-url = https://pypi.org/simple
extra-index-url = https://mirror-pypi.runflare.com/simple
trusted-host = mirror-pypi.runflare.com
timeout = 60
📦 تنظیمات NPM
فایل: /root/.npmrc
registry=https://registry.npmjs.org/
# Fallback (uncomment if needed):
# registry=https://mirror-npm.runflare.com
🔧 تنظیمات Gitea Runner
مشکل: Runner نمیتونه از Nexus (HTTP) pull کنه
علت: Docker daemon داخل Runner سعی میکنه با HTTPS وصل بشه.
راه حل: ConfigMap برای daemon.json
ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: docker-daemon-config
data:
daemon.json: |
{
"insecure-registries": ["194.5.195.53:32082", "194.5.195.53:30080"]
}
Deployment Patch
kubectl patch deployment gitea-runner --type=json -p='[
{
"op": "add",
"path": "/spec/template/spec/volumes/-",
"value": {
"name": "docker-config",
"configMap": {
"name": "docker-daemon-config"
}
}
},
{
"op": "add",
"path": "/spec/template/spec/containers/0/volumeMounts/-",
"value": {
"name": "docker-config",
"mountPath": "/etc/docker/daemon.json",
"subPath": "daemon.json"
}
}
]'
بررسی
kubectl exec $(kubectl get pods -l app=gitea-runner -o jsonpath='{.items[0].metadata.name}') \
-c docker -- docker info | grep -A 5 'Insecure Registries'
📁 ساختار Dockerfile ها
الگوی استاندارد (با Nexus)
FROM 194.5.195.53:32082/dotnet/sdk:9.0 AS build
WORKDIR /src
# Copy NuGet config
COPY src/NuGet.config ./
# Restore and build
RUN dotnet restore "Project.csproj" --configfile NuGet.config
RUN dotnet publish "Project.csproj" -c Release -o /app/publish --no-restore
FROM 194.5.195.53:32082/dotnet/aspnet:9.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "Project.dll"]
NuGet.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nexus" value="http://194.5.195.53:32081/repository/nuget-group/index.json" />
</packageSources>
</configuration>
🔄 ساختار Workflow (CI/CD)
الگوی استاندارد kub-deploy.yml
name: Build and Deploy
on:
push:
branches:
- kub-stage # یا production
env:
REGISTRY: 194.5.195.53:30080
IMAGE_NAME: admin/project-name
K8S_SERVER: 194.5.195.53 # یا 45.149.79.127 برای Production
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:32082"]
}
DAEMON
dockerd &
for i in $(seq 1 90); do
docker info >/dev/null 2>&1 && break || sleep 2
done
- name: Checkout code
run: |
git clone --depth 1 --branch $BRANCH http://gitea-svc:3000/admin/PROJECT.git .
- name: Build Docker Image
run: |
docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest .
- name: Push to Registry
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u admin --password-stdin
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- name: Deploy
run: |
sshpass -p "${{ secrets.K8S_SSH_PASSWORD }}" ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} \
"kubectl rollout restart deployment/PROJECT"
🔐 Secrets مورد نیاز در Gitea
| Secret | مقدار | توضیح |
|---|---|---|
REGISTRY_PASSWORD |
87zH26nbqT |
رمز Gitea Registry |
K8S_SSH_PASSWORD |
87zH26nbqT |
رمز SSH سرور |
💾 بکاپ روزانه MSSQL
CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: mssql-backup
spec:
schedule: "0 2 * * *" # هر روز ساعت 2 صبح
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: 194.5.195.53:32082/mcr.microsoft.com/mssql-tools:latest
command:
- /bin/bash
- -c
- |
DATE=$(date +%Y%m%d)
for DB in gitea Foursat; do
/opt/mssql-tools/bin/sqlcmd -S mssql-svc -U sa -P '87zH26nbqT' \
-Q "BACKUP DATABASE [$DB] TO DISK='/backups/${DB}_${DATE}.bak'"
done
volumeMounts:
- name: backup-volume
mountPath: /backups
volumes:
- name: backup-volume
hostPath:
path: /mnt/mssql-backups
restartPolicy: OnFailure
📊 خلاصه پروژهها
| پروژه | Dockerfile | Workflow Stage | Workflow Prod |
|---|---|---|---|
| BackOffice | ✅ Nexus | ✅ | ✅ |
| BackOffice.BFF | ✅ Nexus | ✅ | ✅ |
| CMS | ✅ Nexus | ✅ | ✅ |
| FrontOffice | ✅ Nexus | ✅ | ✅ |
| FrontOffice.BFF | ✅ Nexus | ✅ | ✅ |
🚨 Troubleshooting
مشکل: Image pull failed - HTTPS error
Error: http: server gave HTTP response to HTTPS client
راه حل: اضافه کردن registry به insecure-registries
مشکل: NuGet restore failed
راه حل: بررسی NuGet.config و اتصال به Nexus
مشکل: Runner CrashLoopBackOff
راه حل: بررسی لاگها با kubectl logs
مشکل: K3s نمیتونه pull کنه
راه حل: بررسی /etc/rancher/k3s/registries.yaml و restart K3s
📞 دستورات مفید
بررسی وضعیت Runner
kubectl get pods -l app=gitea-runner
kubectl logs -l app=gitea-runner -c runner --tail=50
تست pull از Nexus
crictl pull 194.5.195.53:32082/dotnet/sdk:9.0
بررسی ایمیجها در Nexus
curl -u admin:87zH26nbqT http://194.5.195.53:32082/v2/_catalog
Restart K3s
sudo systemctl restart k3s
📅 تاریخچه تغییرات
| تاریخ | تغییر |
|---|---|
| 2026-01-29 | راهاندازی اولیه، تنظیم Nexus، Runner، و Mirror ها |
| 2026-01-29 | تنظیم Production server برای استفاده از Stage Nexus |
| 2026-01-29 | آپدیت Dockerfile ها و Workflow های production |
| 2026-01-29 | فیکس insecure registry برای Gitea Runner |
📝 این داکیومنت توسط Copilot تهیه شده و باید با تغییرات پروژه بروزرسانی شود.
تنظیمات Nexus (جزئیات کامل)
✅ Nexus Repository Manager - Complete Setup
📦 Deployed Services
Nexus Repository Manager
- Version: 3.38.0 (Compatible with x86-64-v1 CPU)
- Web UI: https://nexus.se.kbs1.ir
- NodePort: http://194.5.195.53:32081
- Credentials: admin / 87zH26nbqT
Kubernetes Resources
# Pod
kubectl get pod | grep nexus
# nexus-6575454f69-fv29t 1/1 Running
# Service (NodePort)
kubectl get svc nexus
# Ports: 8081:32081 (Web UI)
# 8082:32082 (Docker Hosted)
# 8083:32083 (Docker Proxy)
# 8084:32084 (Docker Group)
# Ingress
kubectl get ingress nexus-ingress
# Host: nexus.se.kbs1.ir
# TLS: Self-signed certificate (via cert-manager)
📦 Repositories Created
NuGet Repositories
-
nuget-org-proxy (Proxy)
- Proxies: https://api.nuget.org/v3/index.json
- Caches packages from nuget.org
- URL: https://nexus.se.kbs1.ir/repository/nuget-org-proxy/index.json
-
foursat-nuget-hosted (Hosted)
- For private FourSat packages
- URL: https://nexus.se.kbs1.ir/repository/foursat-nuget-hosted/index.json
-
nuget-all (Group)
- Combines: nuget-org-proxy + foursat-nuget-hosted
- Use this URL in projects
- URL: https://nexus.se.kbs1.ir/repository/nuget-all/index.json
Docker Repositories
-
docker-hosted (Hosted)
- For private Docker images
- Port: 32082
- URL: 194.5.195.53:32082
-
docker-hub-proxy (Proxy)
- Proxies: https://registry-1.docker.io (Docker Hub)
- Caches images from Docker Hub
- Port: 32083
- URL: 194.5.195.53:32083
-
docker-all (Group)
- Combines: docker-hosted + docker-hub-proxy
- Port: 32084
- Use this for Kubernetes
- URL: 194.5.195.53:32084
🔧 Project Configuration
NuGet.config (Already Updated)
All projects now use Nexus as primary source:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<!-- Nexus as primary source (proxies nuget.org + caches packages) -->
<add key="Nexus" value="https://nexus.se.kbs1.ir/repository/nuget-all/index.json" />
<!-- Backup: Direct Gitea registries -->
<add key="FourSat" value="https://git.afrino.co/api/packages/FourSat/nuget/index.json" />
<add key="Afrino" value="https://git.afrino.co/api/packages/Afrino/nuget/index.json" />
</packageSources>
<packageSourceCredentials>
<Nexus>
<add key="Username" value="admin" />
<add key="ClearTextPassword" value="87zH26nbqT" />
</Nexus>
<FourSat>
<add key="Username" value="masoud" />
<add key="ClearTextPassword" value="87zH26nbqT" />
</FourSat>
<Afrino>
<add key="Username" value="systemuser" />
<add key="ClearTextPassword" value="sZSA7PTiv3pUSQZ" />
</Afrino>
</packageSourceCredentials>
</configuration>
Updated files:
- ✅
/BackOffice/src/BackOffice/NuGet.config - ✅
/BackOffice.BFF/src/BackOffice.BFF.WebApi/NuGet.config - ✅
/FrontOffice/src/FrontOffice.Main/NuGet.config - ✅
/FrontOffice.BFF/src/FrontOffice.BFF.WebApi/NuGet.config
🐳 Docker Registry Configuration
For Kubernetes Deployments
Update /etc/containerd/config.toml on all nodes:
[plugins."io.containerd.grpc.v1.cri".registry]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."194.5.195.53:32084"]
endpoint = ["http://194.5.195.53:32084"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["http://194.5.195.53:32084"]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.configs."194.5.195.53:32084".auth]
username = "admin"
password = "87zH26nbqT"
Then restart containerd:
systemctl restart containerd
For Docker
Add to /etc/docker/daemon.json:
{
"insecure-registries": [
"194.5.195.53:32082",
"194.5.195.53:32083",
"194.5.195.53:32084"
],
"registry-mirrors": [
"http://194.5.195.53:32084"
]
}
Then restart Docker:
systemctl restart docker
Docker Login
docker login 194.5.195.53:32084 -u admin -p 87zH26nbqT
docker login 194.5.195.53:32082 -u admin -p 87zH26nbqT
docker login 194.5.195.53:32083 -u admin -p 87zH26nbqT
🚀 Usage Examples
Pull Docker Images via Nexus Proxy
# Instead of: docker pull nginx:alpine
docker pull 194.5.195.53:32084/nginx:alpine
# Instead of: docker pull mcr.microsoft.com/dotnet/aspnet:9.0
docker pull 194.5.195.53:32084/mcr.microsoft.com/dotnet/aspnet:9.0
First pull: Downloads from Docker Hub and caches in Nexus
Subsequent pulls: Served from Nexus cache (no internet needed)
Push Private Docker Images
# Tag image
docker tag myapp:latest 194.5.195.53:32082/myapp:latest
# Push to hosted repository
docker push 194.5.195.53:32082/myapp:latest
NuGet Package Restore
cd /path/to/project
dotnet restore
First restore: Downloads from nuget.org via Nexus proxy
Subsequent restores: Served from Nexus cache (no internet needed)
Publish Private NuGet Packages
# Pack project
dotnet pack MyProject.csproj -c Release
# Push to Nexus hosted repository
dotnet nuget push MyProject.1.0.0.nupkg \
--source https://nexus.se.kbs1.ir/repository/foursat-nuget-hosted/ \
--api-key admin:87zH26nbqT
🔍 Verification
Check NuGet Sources
dotnet nuget list source
Expected output:
Registered Sources:
1. Nexus [Enabled]
https://nexus.se.kbs1.ir/repository/nuget-all/index.json
2. FourSat [Enabled]
https://git.afrino.co/api/packages/FourSat/nuget/index.json
3. Afrino [Enabled]
https://git.afrino.co/api/packages/Afrino/nuget/index.json
Test Package Download
# This should use Nexus as primary source
dotnet add package Newtonsoft.Json
# Check Nexus logs
kubectl logs nexus-6575454f69-fv29t | tail -20
Check Cached Packages in Nexus
# SSH to server
ssh root@194.5.195.53
# Check blob storage
du -sh /var/lib/nexus/blobs/default/content/*
📊 Benefits
NuGet Caching
- ✅ Packages download once, cached forever
- ✅ No repeated downloads from nuget.org
- ✅ Faster CI/CD builds
- ✅ Works offline after first download
Docker Caching
- ✅ Base images cached locally (aspnet, sdk, nginx, etc.)
- ✅ No repeated downloads from Docker Hub
- ✅ Faster Kubernetes deployments
- ✅ Works offline after first pull
Private Package Hosting
- ✅ Host private NuGet packages
- ✅ Host private Docker images
- ✅ Version control for artifacts
- ✅ Access control via credentials
🛠️ Maintenance
Check Repository Storage
Via UI:
- Login to https://nexus.se.kbs1.ir
- Go to: ⚙️ Settings → System → Blob Stores
- View: Storage usage per blob store
Via API:
curl -u admin:87zH26nbqT \
http://194.5.195.53:32081/service/rest/v1/blobstores
Clear Cache (if needed)
Via UI:
- Go to: ⚙️ Settings → Repository → Repositories
- Select repository (e.g.,
nuget-org-proxy) - Click: Delete cache
Backup Nexus Data
# Stop Nexus
kubectl scale deployment nexus --replicas=0
# Backup data
tar -czf nexus-backup-$(date +%Y%m%d).tar.gz /var/lib/nexus/
# Start Nexus
kubectl scale deployment nexus --replicas=1
📝 Files Created
- ✅
/deployment/nexus-k8s.yaml- Kubernetes deployment - ✅
/deployment/nexus-ingress.yaml- Ingress with TLS - ✅
/deployment/create-nexus-repos.sh- Repository creation script - ✅
/deployment/NEXUS-COMPLETE-SETUP.md- This document
🎯 Next Steps
-
Test NuGet Caching:
cd BackOffice/src dotnet clean rm -rf ~/.nuget/packages dotnet restore # Check Nexus UI → Browse → nuget-org-proxy -
Configure Kubernetes to use Docker proxy:
# Update containerd config (see Docker Registry Configuration above) systemctl restart containerd # Pull image via Nexus crictl pull 194.5.195.53:32084/nginx:alpine -
Update Dockerfiles to use local images:
# Instead of: FROM mcr.microsoft.com/dotnet/aspnet:9.0 FROM 194.5.195.53:32084/mcr.microsoft.com/dotnet/aspnet:9.0 -
Update CI/CD workflows:
- Already using local registry:
194.5.195.53:32500 - Can migrate to Nexus Docker registry:
194.5.195.53:32084
- Already using local registry:
✅ Summary
Deployed: Nexus Repository Manager 3.38.0
Accessible: https://nexus.se.kbs1.ir (with TLS)
Repositories: NuGet (proxy, hosted, group) + Docker (proxy, hosted, group)
Projects Updated: All 4 NuGet.config files now use Nexus as primary source
Status: Ready for production use
Result: Complete offline deployment capability for both NuGet packages and Docker images! 🎉