Tối ưu workflow DevOps nhỏ với Portainer, GitHub Actions và Docker Registry
DevOps không nhất thiết phải bắt đầu bằng Kubernetes, service mesh, Helm chart phức tạp, hay một đội platform riêng. Với team nhỏ, startup, side project, SaaS giai đoạn đầu, mục tiêu thường rất thực tế: code push → build image → deploy ổn định → rollback nhanh → ít thao tác tay.
Bộ ba Portainer + GitHub Actions + Docker Registry đáp ứng tốt bài toán này. Chi phí thấp. Dễ hiểu. Dễ vận hành. Đủ mạnh cho nhiều hệ thống nhỏ đến trung bình.
Workflow lý tưởng:
Developer push code → GitHub Actions build Docker image → push vào Registry → Portainer deploy/update container trên serverKết quả: giảm SSH thủ công, giảm lỗi copy file, chuẩn hóa môi trường, tăng tốc release.
Tổng quan kiến trúc
Một workflow DevOps nhỏ thường gồm:
– GitHub: lưu source code. – GitHub Actions: CI/CD pipeline. – Docker Registry: nơi lưu Docker image. – Portainer: UI quản trị Docker/Compose/Stack. – VPS/server: chạy container production/staging.
Luồng cơ bản:
1. Dev push code lên nhánh main.
2. GitHub Actions chạy test/build.
3. Build Docker image.
4. Tag image theo commit SHA/version.
5. Push image lên Docker Registry.
6. Portainer pull image mới.
7. Container được recreate/update.
8. App chạy version mới.
Điểm hay: mỗi phần làm đúng một việc.
– GitHub Actions → automation. – Registry → artifact storage. – Portainer → runtime management. – Docker → packaging/runtime.
Vì sao team nhỏ nên dùng Portainer?
Portainer là giao diện quản trị Docker trực quan. Thay vì phải SSH vào server rồi chạy docker ps, docker logs, docker compose up -d, bạn có UI để:
– Xem container đang chạy. – Kiểm tra logs. – Restart service. – Quản lý volume/network. – Deploy bằng Compose stack. – Rollback bằng đổi image tag. – Quản lý nhiều environment/server.
Với team nhỏ, Portainer giúp giảm phụ thuộc vào một người “biết server”. Người mới có thể xem trạng thái hệ thống nhanh hơn. Việc debug production cũng trực quan hơn.
Tuy vậy, Portainer không thay thế CI/CD. Nó nên là lớp quản lý runtime, không phải nơi build code.
Docker Registry: trái tim của quy trình release
Registry là nơi lưu Docker image sau khi build. Một số lựa chọn phổ biến:
– GitHub Container Registry: ghcr.io
– Docker Hub
– GitLab Container Registry
– Harbor self-hosted
– AWS ECR / GCP Artifact Registry / Azure ACR
Với GitHub Actions, lựa chọn tiện nhất thường là GitHub Container Registry vì tích hợp sẵn với repo.
Ví dụ image:
ghcr.io/org/my-app:latest
ghcr.io/org/my-app:main
ghcr.io/org/my-app:sha-abc123
ghcr.io/org/my-app:v1.2.0Không nên chỉ dùng latest cho production. Vì latest khó truy vết. Nên tag theo:
– Commit SHA. – SemVer. – Branch. – Build number.
Khuyến nghị:
ghcr.io/org/my-app:sha-<commit>
ghcr.io/org/my-app:v1.4.2Production nên deploy tag cố định. Rollback dễ: đổi lại tag cũ.
GitHub Actions: build, test, push image
GitHub Actions giúp tự động hóa pipeline. File thường đặt tại:
.github/workflows/deploy.ymlPipeline cơ bản:
– Checkout code. – Login registry. – Build Docker image. – Push image. – Gọi webhook hoặc trigger redeploy.
Ví dụ dùng GHCR:
name: Build and Push
on:
push:
branches:
- main
env:
IMAGE_NAME: ghcr.io/your-org/your-app
jobs:
docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
run: |
echo "sha_short=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
- name: Build image
run: |
docker build
-t $IMAGE_NAME:sha-${{ steps.meta.outputs.sha_short }}
-t $IMAGE_NAME:main
.
- name: Push image
run: |
docker push $IMAGE_NAME:sha-${{ steps.meta.outputs.sha_short }}
docker push $IMAGE_NAME:main
Đây mới là build/push. Chưa deploy. Có 2 hướng deploy phổ biến với Portainer.
Cách deploy với Portainer
Cách 1: Portainer Stack + image tag cố định
Bạn tạo Stack trong Portainer bằng Docker Compose:
services:
app:
image: ghcr.io/your-org/your-app:main
restart: always
ports:
- "3000:3000"
environment:
NODE_ENV: production
env_file:
- stack.envSau đó, khi image main mới được push, bạn cần update stack. Có thể thao tác tay trong UI: Pull latest image and redeploy.
Ưu:
– Dễ hiểu. – Không cần automation phức tạp. – Phù hợp giai đoạn đầu.
Nhược:
– Vẫn cần click tay. – Dễ quên deploy. – Không hoàn toàn CI/CD.
Cách 2: Portainer webhook
Portainer hỗ trợ webhook cho service/stack. Khi GitHub Actions build/push xong, gọi webhook để Portainer redeploy.
Luồng:
Push code → build image → push registry → curl Portainer webhook → redeployVí dụ thêm bước cuối:
- name: Trigger Portainer webhook
run: |
curl -X POST "${{ secrets.PORTAINER_WEBHOOK_URL }}"Webhook URL nên lưu trong GitHub Secrets:
PORTAINER_WEBHOOK_URL=https://portainer.example.com/api/webhooks/xxxxƯu:
– Tự động deploy. – Setup đơn giản. – Không cần SSH key.
Nhược:
– Cần bảo vệ webhook. – Khó kiểm soát nếu trigger nhầm. – Phụ thuộc image tag strategy.
Thiết kế tag strategy hợp lý
Tag ảnh hưởng trực tiếp tới rollback, audit, debug.
Môi trường staging
Có thể dùng:
your-app:develop
your-app:stagingMỗi lần merge vào develop → auto deploy staging.
Môi trường production
Nên dùng:
your-app:v1.5.0
your-app:sha-a1b2c3dProduction nên release qua Git tag:
git tag v1.5.0
git push origin v1.5.0GitHub Actions trigger khi có tag:
on:
push:
tags:
- "v*"Build image:
ghcr.io/your-org/your-app:v1.5.0Portainer stack trỏ tới đúng version:
image: ghcr.io/your-org/your-app:v1.5.0Rollback:
image: ghcr.io/your-org/your-app:v1.4.9Redeploy. Xong.
Quản lý secrets và cấu hình
Sai lầm phổ biến: bake secret vào image.
Không nên:
ENV DATABASE_PASSWORD=secretNên:
– App config qua environment variables.
– Secrets lưu trong Portainer env/secret manager.
– GitHub Secrets chỉ dùng cho CI/CD.
– Không commit .env.
Phân tách:
– Build-time config: dùng khi build image. – Runtime config: DB URL, API key, Redis URL, JWT secret.
Runtime config nên nằm ở Portainer Stack env:
environment:
DATABASE_URL: ${DATABASE_URL}
REDIS_URL: ${REDIS_URL}Hoặc env_file.
Với team nhỏ, chỉ cần kỷ luật:
– Không hardcode secret. – Không in secret ra log. – Rotate token định kỳ. – Giới hạn quyền registry token. – Bật 2FA cho GitHub.
Dockerfile tối ưu cho CI/CD nhỏ
Dockerfile tốt → build nhanh, image nhỏ, ít lỗi production.
Ví dụ Node.js:
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/main.js"]
Nguyên tắc:
– Multi-stage build.
– Copy dependency file trước để tận dụng cache.
– Không copy .git, node_modules, file local.
– Có .dockerignore.
Ví dụ .dockerignore:
.git
node_modules
.env
dist
coverage
Dockerfile
docker-compose.ymlHealthcheck, logs, restart policy
Deploy thành công không có nghĩa app khỏe.
Compose nên có:
restart: always
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3App nên có endpoint:
GET /healthLogs nên xuất ra stdout/stderr. Không ghi log file trong container nếu không cần. Portainer đọc logs dễ hơn. Nếu hệ thống lớn dần, thêm Loki, Grafana, Promtail sau.
Restart policy:
– restart: always cho service chính.
– restart: unless-stopped nếu muốn container không tự bật lại sau khi stop thủ công.
Quy trình deploy thực tế cho team nhỏ
Một workflow gọn:
Staging
– Push vào develop.
– GitHub Actions build image staging.
– Push GHCR.
– Trigger Portainer staging webhook.
– QA kiểm tra.
Production
– Merge vào main.
– Tạo Git tag vX.Y.Z.
– GitHub Actions build image version.
– Push GHCR.
– Portainer production redeploy.
– Theo dõi logs/healthcheck.
– Nếu lỗi → rollback tag cũ.
Checklist trước production:
– Migration DB đã kiểm tra? – Env đầy đủ? – Image đã scan basic? – Healthcheck pass? – Backup DB gần nhất? – Có tag rollback?
Bảo mật tối thiểu nhưng cần có
Portainer thường nằm trên internet. Cần bảo vệ nghiêm túc.
Khuyến nghị:
– Đặt Portainer sau HTTPS. – Dùng mật khẩu mạnh. – Bật 2FA nếu có. – Giới hạn IP truy cập bằng firewall/VPN nếu được. – Không public Docker socket. – Không chia sẻ webhook URL. – Registry package nên private nếu app private. – Token GitHub chỉ cấp quyền tối thiểu.
Nếu dùng GHCR private, server cần login registry:
docker login ghcr.ioPortainer cũng cần registry credential để pull image private.
Khi nào nên nâng cấp lên giải pháp lớn hơn?
Bộ ba này phù hợp khi:
– 1–5 server. – Team nhỏ. – Deployment đơn giản. – Ít service. – Không cần autoscale phức tạp.
Nên cân nhắc Kubernetes/Nomad/ECS khi:
– Nhiều service phụ thuộc nhau. – Cần autoscaling. – Cần zero-downtime rollout chuẩn. – Multi-region. – Traffic lớn. – Cần policy/networking phức tạp.
Đừng tối ưu quá sớm. Docker Compose + Portainer có thể chạy tốt rất lâu nếu thiết kế hợp lý.
Kết luận thực tế
Workflow DevOps nhỏ tốt không cần hào nhoáng. Cần ổn định, lặp lại được, dễ rollback, ít thao tác tay.
Portainer + GitHub Actions + Docker Registry tạo nền tảng rất cân bằng:– GitHub Actions → tự động build/push. – Registry → lưu artifact rõ version. – Portainer → quản lý deploy trực quan. – Docker Compose → cấu hình dễ đọc, dễ sửa.
Công thức khuyến nghị:
– Dùng GHCR làm registry. – Build image bằng GitHub Actions. – Tag bằng commit SHA/version. – Deploy qua Portainer Stack. – Trigger bằng webhook. – Config runtime bằng env. – Luôn có rollback tag. – Theo dõi logs/healthcheck sau deploy.
Bắt đầu đơn giản. Chuẩn hóa dần. Khi hệ thống lớn hơn, bạn vẫn giữ được nền tảng quan trọng nhất của DevOps: mỗi thay đổi đều có thể build, deploy, kiểm tra, rollback một cách có kiểm soát.
Bình luận (0)
Chưa có bình luận. Hãy là người đầu tiên!