با Docker، GitHub Actions و سرور Ubuntu - از صفر تا استقرار کامل
CI (Continuous Integration) یا یکپارچهسازی مداوم، به فرآیندی گفته میشود که در آن تغییرات کد به صورت مداوم با کد اصلی ادغام شده و تستهای خودکار روی آن اجرا میشود.
CD (Continuous Deployment/Delivery) یا استقرار مداوم، به فرآیندی گفته میشود که در آن کد پس از موفقیت در تستها، به صورت خودکار روی سرور مستقر میشود.
ابتدا یک پروژه Web API ساده با .NET ایجاد میکنیم.
# ایجاد پوشه پروژه
mkdir MyDotNetApp
cd MyDotNetApp
# ایجاد پروژه Web API
dotnet new webapi -n MyDotNetApp
# ورود به پوشه پروژه
cd MyDotNetApp
# اجرای تست محلی
dotnet run
MyDotNetApp/
├── MyDotNetApp/
│ ├── Controllers/
│ │ └── WeatherForecastController.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── appsettings.json
│ ├── appsettings.Development.json
│ ├── Program.cs
│ └── MyDotNetApp.csproj
└── (Dockerfile و docker-compose.yml اینجا اضافه میشوند)
Dockerfile فایلی است که دستورات ساخت Docker Image را مشخص میکند. ما از روش Multi-Stage Build استفاده میکنیم که باعث کاهش حجم Image نهایی میشود.
# ===========================================
# مرحله ۱: Build
# ===========================================
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# کپی فایل پروژه و restore وابستگیها
COPY ["MyDotNetApp/MyDotNetApp.csproj", "MyDotNetApp/"]
RUN dotnet restore "MyDotNetApp/MyDotNetApp.csproj"
# کپی بقیه فایلها
COPY . .
WORKDIR "/src/MyDotNetApp"
# Build پروژه
RUN dotnet build "MyDotNetApp.csproj" -c Release -o /app/build
# ===========================================
# مرحله ۲: Publish
# ===========================================
FROM build AS publish
RUN dotnet publish "MyDotNetApp.csproj" -c Release -o /app/publish /p:UseAppHost=false
# ===========================================
# مرحله ۳: Runtime (Image نهایی)
# ===========================================
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
# تنظیم پورت
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080
# کپی فایلهای publish شده
COPY --from=publish /app/publish .
# اجرای برنامه
ENTRYPOINT ["dotnet", "MyDotNetApp.dll"]
| دستور | توضیح |
|---|---|
| FROM | تعیین Image پایه |
| WORKDIR | تعیین پوشه کاری داخل Container |
| COPY | کپی فایلها از سیستم میزبان به Container |
| RUN | اجرای دستورات در زمان Build |
| EXPOSE | مشخص کردن پورت (برای مستندسازی) |
| ENTRYPOINT | دستور اجرا هنگام شروع Container |
این فایل مشخص میکند چه فایلهایی نباید به Docker Context ارسال شوند:
**/.git
**/.vs
**/.vscode
**/bin
**/obj
**/.dockerignore
**/Dockerfile*
**/docker-compose*
**/*.md
**/.gitignore
# در پوشه ریشه پروژه (کنار Dockerfile)
docker build -t mydotnetapp:latest .
# اجرای Container
docker run -d -p 8080:8080 --name myapp mydotnetapp:latest
# تست
curl http://localhost:8080/weatherforecast
Docker Compose ابزاری است برای تعریف و اجرای چند Container به صورت همزمان. حتی اگر فعلاً یک Container دارید، استفاده از آن مدیریت را سادهتر میکند.
version: '3.8'
services:
webapi:
image: ${DOCKER_USERNAME}/mydotnetapp:${TAG:-latest}
container_name: mydotnetapp
ports:
- "8080:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://+:8080
restart: unless-stopped
networks:
- app-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
app-network:
driver: bridge
اگر پروژه شما به دیتابیس نیاز دارد:
version: '3.8'
services:
webapi:
image: ${DOCKER_USERNAME}/mydotnetapp:${TAG:-latest}
container_name: mydotnetapp
ports:
- "8080:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://+:8080
- ConnectionStrings__DefaultConnection=Host=db;Database=mydb;Username=postgres;Password=${DB_PASSWORD}
depends_on:
db:
condition: service_healthy
restart: unless-stopped
networks:
- app-network
db:
image: postgres:16-alpine
container_name: postgres_db
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- app-network
volumes:
postgres_data:
networks:
app-network:
driver: bridge
# در پوشه ریشه پروژه
git init
git add .
git commit -m "Initial commit - .NET project with Docker"
# اتصال به GitHub
git remote add origin https://github.com/YOUR_USERNAME/MyDotNetApp.git
git branch -M main
git push -u origin main
MyDotNetApp/
├── .github/
│ └── workflows/
│ └── deploy.yml # فایل CI/CD
├── MyDotNetApp/
│ ├── Controllers/
│ ├── Program.cs
│ └── MyDotNetApp.csproj
├── .dockerignore
├── .gitignore
├── Dockerfile
├── docker-compose.yml
└── README.md
# اتصال SSH
ssh root@YOUR_SERVER_IP
# یا با کلید
ssh -i ~/.ssh/your_key root@YOUR_SERVER_IP
# آپدیت پکیجها
sudo apt update && sudo apt upgrade -y
# نصب ابزارهای ضروری
sudo apt install -y curl wget git apt-transport-https ca-certificates gnupg lsb-release
# اضافه کردن GPG key رسمی Docker
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# اضافه کردن Repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# نصب Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# اضافه کردن کاربر به گروه docker
sudo usermod -aG docker $USER
# فعالسازی سرویس
sudo systemctl enable docker
sudo systemctl start docker
# تست نصب
docker --version
docker compose version
# ایجاد پوشه
sudo mkdir -p /opt/apps/mydotnetapp
sudo chown -R $USER:$USER /opt/apps/mydotnetapp
# ایجاد فایل docker-compose.yml
nano /opt/apps/mydotnetapp/docker-compose.yml
محتوای فایل docker-compose.yml را از مرحله ۳ کپی کنید.
# فعالسازی UFW
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 8080/tcp
sudo ufw enable
# مشاهده وضعیت
sudo ufw status
# روی سرور، کلید جدید بسازید
ssh-keygen -t ed25519 -C "github-actions" -f ~/.ssh/github_actions -N ""
# کلید عمومی را به authorized_keys اضافه کنید
cat ~/.ssh/github_actions.pub >> ~/.ssh/authorized_keys
# کلید خصوصی را کپی کنید (برای GitHub Secrets)
cat ~/.ssh/github_actions
GitHub Actions سرویس CI/CD رایگان GitHub است. ما یک Workflow میسازیم که با هر Push به branch اصلی:
name: Build and Deploy
# زمان اجرای Workflow
on:
push:
branches:
- main
workflow_dispatch: # اجرای دستی
# متغیرهای محیطی
env:
DOCKER_IMAGE: ${{ secrets.DOCKER_USERNAME }}/mydotnetapp
DEPLOY_PATH: /opt/apps/mydotnetapp
jobs:
# ======================================
# Job 1: Build و Push به Docker Hub
# ======================================
build:
name: Build & Push Docker Image
runs-on: ubuntu-latest
outputs:
image_tag: ${{ steps.meta.outputs.tags }}
steps:
# گام ۱: دریافت کد
- name: Checkout code
uses: actions/checkout@v4
# گام ۲: تنظیم Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# گام ۳: ورود به Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# گام ۴: تولید Tagها
- name: Docker Metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_IMAGE }}
tags: |
type=sha,prefix=
type=raw,value=latest
# گام ۵: Build و Push
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ======================================
# Job 2: استقرار روی سرور
# ======================================
deploy:
name: Deploy to Server
runs-on: ubuntu-latest
needs: build # منتظر اتمام build میماند
steps:
# گام ۱: اتصال SSH و استقرار
- name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_SSH_KEY }}
port: ${{ secrets.SERVER_PORT || 22 }}
script: |
cd ${{ env.DEPLOY_PATH }}
# تنظیم متغیرهای محیطی
export DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}
export TAG=latest
# Pull کردن Image جدید
docker compose pull
# متوقف کردن Container قبلی و اجرای جدید
docker compose up -d --remove-orphans
# پاکسازی Imageهای قدیمی
docker image prune -f
# نمایش وضعیت
docker compose ps
Secrets متغیرهای رمزنگاریشدهای هستند که اطلاعات حساس مانند رمز عبور را ذخیره میکنند.
| نام Secret | توضیح | مثال |
|---|---|---|
| DOCKER_USERNAME | نام کاربری Docker Hub | myusername |
| DOCKER_PASSWORD | Access Token یا رمز Docker Hub | dckr_pat_xxx... |
| SERVER_HOST | آدرس IP سرور | 192.168.1.100 |
| SERVER_USERNAME | نام کاربری SSH | root یا deploy |
| SERVER_SSH_KEY | کلید خصوصی SSH (کل محتوا) | -----BEGIN OPENSSH PRIVATE KEY-----... |
| SERVER_PORT | پورت SSH (اختیاری) | 22 |
git add .
git commit -m "Trigger CI/CD pipeline"
git push origin main
# اتصال به سرور
ssh user@YOUR_SERVER_IP
# مشاهده Containerها
docker ps
# مشاهده لاگها
docker logs mydotnetapp -f
# تست API
curl http://localhost:8080/weatherforecast
# در مرورگر یا ترمینال محلی
curl http://YOUR_SERVER_IP:8080/weatherforecast
# بررسی کنید:
# 1. DOCKER_USERNAME درست باشد
# 2. DOCKER_PASSWORD یک Access Token معتبر باشد
# 3. Repository در Docker Hub وجود داشته باشد یا Public باشد
# بررسی کنید:
# 1. SERVER_HOST آیپی صحیح باشد
# 2. SERVER_SSH_KEY کل کلید خصوصی باشد (همراه با -----BEGIN و -----END)
# 3. پورت 22 باز باشد
# 4. کلید عمومی در authorized_keys سرور باشد
# روی سرور:
docker logs mydotnetapp
# بررسی فضای دیسک
df -h
# بررسی حافظه
free -m
# بررسی firewall
sudo ufw status
# باز کردن پورت
sudo ufw allow 8080/tcp
# مشاهده همه Containerها
docker ps -a
# مشاهده لاگها
docker logs CONTAINER_NAME -f --tail 100
# ورود به Container
docker exec -it CONTAINER_NAME /bin/bash
# پاکسازی کامل
docker system prune -a
# ریاستارت Container
docker compose restart
# توقف و حذف
docker compose down
ایجاد پروژه Web API
Multi-stage build برای بهینهسازی
تعریف سرویسها و شبکه
آپلود کد به Repository
نصب Docker و تنظیمات SSH
Workflow برای CI/CD
تنظیم اطلاعات امن
بررسی عملکرد پایپلاین