Skip to content
> ๐Ÿ’ป ๐Ÿง  ืงื•ื“ 1001 > ๐Ÿ› ๏ธ DevOps > ๐Ÿš€ CI/CD: ืžื™ืกื•ื“ื•ืช ืœื™ื™ืฆื•ืจ ื‘-GCP ืขื GitHub Actions โ€“ ืžื“ืจื™ืš ืžืœื ืขื ื“ื•ื’ืžืื•ืช ๐Ÿš€

๐Ÿš€ CI/CD: ืžื™ืกื•ื“ื•ืช ืœื™ื™ืฆื•ืจ ื‘-GCP ืขื GitHub Actions โ€“ ืžื“ืจื™ืš ืžืœื ืขื ื“ื•ื’ืžืื•ืช ๐Ÿš€

ืฉืœื•ื, ืžืคืชื—ื™ื! ื‘ืžืืžืจ ื–ื” ืื“ื‘ืจ ืขืœ CI/CD โ€“ ื”ืงื•ื ืกืคื˜.

In Questo Articolo

ืžื”ื• CI/CD pipeline ื‘ื”ืงืฉืจ ืฉืœ ืชื›ื ื•ืช?

CI/CD pipeline (Continuous Integration / Continuous Delivery ืื• Continuous Deployment) ื”ื•ื ืชื”ืœื™ืš ืื•ื˜ื•ืžื˜ื™, ื”ืžืืคืฉืจ ืœืžืคืชื—ื™ื ืœืกืคืง ืฉื™ื ื•ื™ื™ ืงื•ื“ ื‘ืžื”ื™ืจื•ืช ื•ื‘ืืžื™ื ื•ืช ืœืกื‘ื™ื‘ืช ืขื‘ื•ื“ื” (ืคืจื•ื“ืงืฉืŸ).

ื‘ื•ืื• ื ืคืจืง ืืช ืžื•ืฉื’ื™ ื”ืžืคืชื—:

๐Ÿ”ง CI โ€” Continuous Integration (ืื™ื ื˜ื’ืจืฆื™ื” ืžืชืžืฉื›ืช)
ื–ื•ื”ื™ ืคืจืงื˜ื™ืงื” ืฉื‘ื” ืžืคืชื—ื™ื ืžื‘ืฆืขื™ื ืฉื™ื ื•ื™ื™ื ืชื›ื•ืคื™ื ื‘ื‘ืกื™ืก ืงื•ื“ ืžืฉื•ืชืฃ. ื›ืœ ืฉื™ื ื•ื™ ื›ื–ื” ื‘ืื•ืคืŸ ืื•ื˜ื•ืžื˜ื™:

  • ื ื‘ื ื” (build)
  • ื ื‘ื“ืง (ื‘ื“ื™ืงื•ืช ื™ื—ื™ื“ื”, ื‘ื“ื™ืงื•ืช ืื™ื ื˜ื’ืจืฆื™ื”)
  • ื ื‘ื“ืง ืœื”ืชืืžื” ืœืชืงื ื™ื (linting, ื ื™ืชื•ื— ืกื˜ื˜ื™)

๐Ÿ‘‰ ืžื˜ืจืช CI: ืœื–ื”ื•ืช ืฉื’ื™ืื•ืช ื‘ืฉืœื‘ ืžื•ืงื“ื ื›ื›ืœ ื”ืืคืฉืจ, ืœืคื ื™ ืฉื”ืŸ ืฉื•ื‘ืจื•ืช ืžืฉื”ื• ื—ืฉื•ื‘ ืื• ืžื’ื™ืขื•ืช ืœื’ืจืกื”.

๐Ÿš€ CD โ€” Continuous Delivery (ืืกืคืงื” ืžืชืžืฉื›ืช) ืื• Continuous Deployment (ืคืจื™ืกื” ืžืชืžืฉื›ืช)
ื›ืืŸ ื™ืฉ ืฉืชื™ ืืคืฉืจื•ื™ื•ืช:

โœ… Continuous Delivery (ืืกืคืงื” ืžืชืžืฉื›ืช)
ืœืื—ืจ ื”ืฉืœืžื” ืžื•ืฆืœื—ืช ืฉืœ ืฉืœื‘ ื”-CI, ื”ืฉื™ื ื•ื™ื™ื ื‘ืื•ืคืŸ ืื•ื˜ื•ืžื˜ื™:

  • ืขื•ื‘ืจื™ื ื‘ื“ื™ืงื•ืช ื ื•ืกืคื•ืช (ืœื“ื•ื’ืžื”, E2E โ€“ ื‘ื“ื™ืงื•ืช ืžืงืฆื” ืœืงืฆื”)
  • ืžื’ื™ืขื™ื ืœืฉืจืช staging (ื‘ื“ื™ืงื”)
    ๐Ÿ‘‰ ืืš ืคืจื™ืกื” ืœืคืจื•ื“ืงืฉืŸ ืขื“ื™ื™ืŸ ื“ื•ืจืฉืช ืื™ืฉื•ืจ ื™ื“ื ื™. ื–ื” ื ื•ืชืŸ ืœืฆื•ื•ืช ืฉืœื™ื˜ื” ืขืœ ืžืชื™ ื‘ื“ื™ื•ืง ื”ืžืฉืชืžืฉื™ื ื™ืจืื• ืืช ื”ืฉื™ื ื•ื™ื™ื.

๐Ÿค– Continuous Deployment (ืคืจื™ืกื” ืžืชืžืฉื›ืช)
ื–ื”ื• ื”ืฉืœื‘ ื”ื‘ื ืœืื—ืจ Continuous Delivery. ื›ืืŸ ื”ืคืจื™ืกื” ืœืคืจื•ื“ืงืฉืŸ ืžืชืจื—ืฉืช ื‘ืื•ืคืŸ ืื•ื˜ื•ืžื˜ื™ ืœื—ืœื•ื˜ื™ืŸ, ืื ื›ืœ ืฉืœื‘ื™ ื”-pipeline ื”ืงื•ื“ืžื™ื (ื‘ื ื™ื™ื”, ื›ืœ ื”ื‘ื“ื™ืงื•ืช) ืขื‘ืจื• ื‘ื”ืฆืœื—ื”. ื–ื•ื”ื™ ื”ืจืžื” ื”ืžืชืงื“ืžืช ื‘ื™ื•ืชืจ ืฉืœ ืื•ื˜ื•ืžืฆื™ื”.

๐Ÿ”„ ืžืžื” ืžื•ืจื›ื‘ ื‘ื“ืจืš ื›ืœืœ CI/CD pipeline?

Pipeline ื˜ื™ืคื•ืกื™ ื›ื•ืœืœ ืืช ื”ืฉืœื‘ื™ื ื”ื‘ืื™ื:

  1. Checkout โ€” ืฉื™ื‘ื•ื˜ ื”ื’ืจืกื” ื”ืื—ืจื•ื ื” ืฉืœ ื”ืงื•ื“ ืžื”ืžืื’ืจ.
  2. Build โ€” ื‘ื ื™ื™ืช ื”ืคืจื•ื™ืงื˜ (ืงื•ืžืคื™ืœืฆื™ื”, ื”ืจื›ื‘ืช ืืจื˜ื™ืคืงื˜ื™ื, ืชืžื•ื ื•ืช Docker).
  3. Test โ€” ื”ืคืขืœืช ืกื•ื’ื™ื ืฉื•ื ื™ื ืฉืœ ื‘ื“ื™ืงื•ืช (ื™ื—ื™ื“ื”, ืื™ื ื˜ื’ืจืฆื™ื”, E2E).
  4. Lint/Code Quality โ€” ื‘ื“ื™ืงืช ืงื•ื“ ืœื”ืชืืžื” ืœืกื’ื ื•ืŸ ื•ืฉื’ื™ืื•ืช ืคื•ื˜ื ืฆื™ืืœื™ื•ืช ื‘ืืžืฆืขื•ืช ืžื ืชื—ื™ื ืกื˜ื˜ื™ื™ื.
  5. Deploy โ€” ืคืจื™ืกืช ื”ื™ื™ืฉื•ื (ืœืฉืจืช staging ืื• production).
  6. Notify โ€” ืฉืœื™ื—ืช ื”ืชืจืื•ืช ืขืœ ืกื˜ื˜ื•ืก ื”-pipeline ืœืฆื•ื•ืช (ืœื“ื•ื’ืžื”, ื‘-Slack, Email).

๐Ÿ›  ื›ืœื™ื ืคื•ืคื•ืœืจื™ื™ื ืœ-CI/CD:

  • GitHub Actions (ื”ืžื•ืงื“ ืฉืœื ื• ื”ื™ื•ื!)
  • GitLab CI/CD
  • Jenkins
  • CircleCI
  • Bitbucket Pipelines
  • Azure DevOps
  • TeamCity

๐Ÿง  ืœืžื” ื‘ื›ืœืœ ืฆืจื™ืš CI/CD?

  • ืžืคื—ื™ืช ื˜ืขื•ื™ื•ืช ืื ื•ืฉ: ืื•ื˜ื•ืžืฆื™ื” ืžื‘ื˜ืœืช ืฉื’ื™ืื•ืช ื”ืงืฉื•ืจื•ืช ืœืคืขื•ืœื•ืช ื™ื“ื ื™ื•ืช.
  • ื–ื™ื”ื•ื™ ืžื”ื™ืจ ืฉืœ ื‘ืื’ื™ื: ืฉื’ื™ืื•ืช ื ืžืฆืื•ืช ืžื•ืงื“ื ื™ื•ืชืจ, ืงืœ ื•ื–ื•ืœ ื™ื•ืชืจ ืœืชืงืŸ ืื•ืชืŸ.
  • ืื•ื˜ื•ืžืฆื™ื” ืฉืœ ืžืฉื™ืžื•ืช ืฉื’ืจืชื™ื•ืช: ืžืคืชื—ื™ื ืžืงื“ื™ืฉื™ื ืคื—ื•ืช ื–ืžืŸ ืœื‘ื ื™ื™ื” ื•ืคืจื™ืกื”, ื•ื™ื•ืชืจ โ€“ ืœืงื•ื“.
  • ืฉื™ืคื•ืจ ืื™ื›ื•ืช ื”ืงื•ื“: ื‘ื“ื™ืงื•ืช ื•ืžื‘ื—ื ื™ื ืžืชืžื™ื“ื™ื ืžืขืœื™ื ืืช ืจืฃ ื”ืื™ื›ื•ืช ื”ื›ืœืœื™.
  • ืืกืคืงื” ืžื”ื™ืจื” ืฉืœ ืคื™ืฆ'ืจื™ื ืœืžืฉืชืžืฉื™ื: ื™ื›ื•ืœื•ืช ื—ื“ืฉื•ืช ืžื’ื™ืขื•ืช ืœืžืฉืชืžืฉ ื”ืงืฆื” ืžื”ืจ ื™ื•ืชืจ ื•ื‘ืชื“ื™ืจื•ืช ื’ื‘ื•ื”ื” ื™ื•ืชืจ.

๐Ÿ“ฆ ื“ื•ื’ืžืื•ืช ืคืฉื•ื˜ื•ืช ืœ-CI/CD ืขื GitHub Actions

ื‘ื•ืื• ื ืกืชื›ืœ ืขืœ pipelines ื‘ืกื™ืกื™ื™ื ืœื˜ื›ื ื•ืœื•ื’ื™ื•ืช ืคื•ืคื•ืœืจื™ื•ืช. ื›ืœ ื”ื“ื•ื’ืžืื•ืช ืžืฉืชืžืฉื•ืช ื‘-GitHub Actions ื•ื ืฉืžืจื•ืช ื‘ืกืคืจื™ื™ืช .github/workflows/code>span> ืฉืœ ื”ืคืจื•ื™ืงื˜ ืฉืœื›ื.</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":4} --> <h4 class="wp-block-heading">๐Ÿ CI/CD ืขื‘ื•ืจ Python (ืขื <span dir="ltr"><code>pytestcode>span> ื•-flake8)</h4> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code> # .github/workflows/python-ci.yml name: Python CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' # ืฆื™ื™ืŸ ืืช ื”ื’ืจืกื” ืฉืœืš - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt # ื•ื“ื ืฉื™ืฉ ืœืš requirements.txt pip install flake8 pytest - name: Lint with flake8 run: | # ื‘ื“ื•ืง ืงื•ื“ ื‘ืชื™ืงื™ื•ืช src ื•-tests (ื”ืชืื ืœืคืจื•ื™ืงื˜ ืฉืœืš) flake8 src tests - name: Run tests run: | pytest

๐ŸŒ CI/CD ืขื‘ื•ืจ Node.js (ืขื npm testcode>span> ื•-eslint) <code># .github/workflows/node-ci.yml name: Node.js CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [18.x] # ืฆื™ื™ืŸ ืืช ื’ืจืกืช ื”-Node.js ืฉืœืš steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm install # ืื• npm ci ืœื”ืชืงื ื” ืฆืคื•ื™ื” ื™ื•ืชืจ - name: Lint with ESLint run: npx eslint . # ื•ื“ื ืฉ-ESLint ืžื•ื’ื“ืจ ื‘ืคืจื•ื™ืงื˜ - name: Run tests run: npm test

๐Ÿณ CI/CD ืขื‘ื•ืจ Docker (ื‘ื ื™ื™ื” ื•-push ืœ-Docker Hub)

ืœื“ื•ื’ืžื” ื–ื•, ืชืฆื˜ืจืš ืกื•ื“ื•ืช `DOCKER_USERNAME` ื•-`DOCKER_PASSWORD` (ืื• ืืกื™ืžื•ืŸ) ื‘ื”ื’ื“ืจื•ืช ืžืื’ืจ ื”-GitHub ืฉืœืš (`Settings -> Secrets and variables -> Actions`).


# .github/workflows/docker-ci.yml
name: Docker CI/CD

on:
  push:
    branches: [ main ] # ื”ืคืขืœ ืจืง ืขื‘ื•ืจ ืขื ืฃ main

jobs:
  docker:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Log in to Docker Hub
        run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin

      - name: Build Docker image
        # ื”ื—ืœืฃ ืืช myapp ื‘ืฉื ื”ื™ื™ืฉื•ื ืฉืœืš
        run: docker build -t ${{ secrets.DOCKER_USERNAME }}/myapp:latest .

      - name: Push Docker image
        run: docker push ${{ secrets.DOCKER_USERNAME }}/myapp:latest

๐Ÿšš ืคืจื™ืกื” ืœืคืœื˜ืคื•ืจืžื•ืช ืคื•ืคื•ืœืจื™ื•ืช

ื›ืขืช, ื›ืฉื™ืฉ ืœื ื• ืืจื˜ื™ืคืงื˜ื™ื ื‘ื ื•ื™ื™ื ื•ื ื‘ื“ืงื™ื (ืœื“ื•ื’ืžื”, ืชืžื•ื ืช Docker), ื‘ื•ืื• ื ืจืื” ื›ื™ืฆื“ ื ื™ืชืŸ ืœืคืจื•ืก ืื•ืชื.

๐ŸŸฃ ืคืจื™ืกื” ืœ-Heroku

**๐Ÿ” ืกื•ื“ื•ืช GitHub:** `HEROKU_API_KEY`, `HEROKU_APP_NAME`.


# .github/workflows/deploy-heroku.yml
name: Deploy to Heroku

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install Heroku CLI
        run: curl https://cli-assets.heroku.com/install.sh | sh
      - name: Login to Heroku
        env:
          HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
        run: heroku auth:token
      - name: Deploy to Heroku
        env:
          HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
        run: |
          heroku git:remote -a ${{ secrets.HEROKU_APP_NAME }}
          git push heroku main -f # ื”ื™ื–ื”ืจ ืขื -f (force push)

ืื ืืชื” ืคื•ืจืก ืชืžื•ื ืช Docker ืœ-Heroku:


# ... (ืฉืœื‘ื™ ื‘ื ื™ื™ื” ื•ื”ืชื—ื‘ืจื•ืช ืœ-Docker Hub/GHCR ืžื“ื•ื’ืžืื•ืช ืงื•ื“ืžื•ืช) ...
# deploy:
#   name: Deploy to Heroku
#   needs: build # ืชืœื•ื™ ื‘ืžืฉื™ืžืช ื‘ื ื™ื™ืช ื”ืชืžื•ื ื”
#   runs-on: ubuntu-latest
#   steps:
#     # ...
#     - name: Login to Heroku container registry
#       run: echo "${{ secrets.HEROKU_API_KEY }}" | docker login --username=_ --password-stdin registry.heroku.com
#     - name: Tag image for Heroku
#       # ื‘ื”ื ื—ื” ืฉื”ืชืžื•ื ื” ื ื‘ื ื™ืช ื›-ghcr.io/username/repo/myapp:latest
#       run: docker tag ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest registry.heroku.com/${{ secrets.HEROKU_APP_NAME }}/web
#     - name: Push image to Heroku
#       run: docker push registry.heroku.com/${{ secrets.HEROKU_APP_NAME }}/web
#     - name: Release Heroku App
#       env:
#         HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
#       run: heroku container:release web --app ${{ secrets.HEROKU_APP_NAME }}

๐ŸŸจ ืคืจื™ืกื” ืœ-AWS (ืœื“ื•ื’ืžื”, ืกื˜ื˜ื™ ืœ-S3)

**๐Ÿ” ืกื•ื“ื•ืช GitHub:** `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `S3_BUCKET_NAME`.


# .github/workflows/deploy-aws-s3.yml
name: Deploy Static Site to AWS S3

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}
      - name: Sync files to S3
        # ื”ื—ืœืฃ ืืช ./public ื‘ื ืชื™ื‘ ืœืงื‘ืฆื™ื ื”ืกื˜ื˜ื™ื™ื ืฉืœืš
        run: aws s3 sync ./public s3://${{ secrets.S3_BUCKET_NAME }} --delete

ืœืคืจื™ืกื” ืœ-**AWS** **Elastic** **Beanstalk** ื‘ื“ืจืš ื›ืœืœ ืžืฉืชืžืฉื™ื ื‘-EB CLI, ื”-pipeline ื™ื”ื™ื” ื“ื•ืžื”, ืืš ืขื ืคืงื•ื“ื•ืช eb deploycode>span>.</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":4} --> <h4 class="wp-block-heading">๐Ÿ”ต ืคืจื™ืกื” ืœ-Google Cloud Platform (GCP App Engine)</h4> <!-- /wp:heading --> <!-- wp:paragraph --> <p>**๐Ÿ” ืกื•ื“ื•ืช GitHub:** `GCP_CREDENTIALS` (ืžืคืชื— JSON ืฉืœ ื—ืฉื‘ื•ืŸ ืฉื™ืจื•ืช), `GCP_PROJECT_ID`.</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> # .github/workflows/deploy-gcp-app-engine.yml name: Deploy to GCP App Engine on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Cloud SDK uses: google-github-actions/setup-gcloud@v2 with: project_id: ${{ secrets.GCP_PROJECT_ID }} service_account_key: ${{ secrets.GCP_CREDENTIALS }} export_default_credentials: true - name: Deploy to App Engine # ื•ื“ื ืฉื™ืฉ ืœืš app.yaml ื‘ืฉื•ืจืฉ ื”ืคืจื•ื™ืงื˜ run: gcloud app deploy --quiet

๐ŸŸช ืคืจื™ืกื” ืœ-Render.com

Render ืœืขื™ืชื™ื ืงืจื•ื‘ื•ืช ืคื•ืจืก ืื•ื˜ื•ืžื˜ื™ืช ื‘-push ืœ-GitHub, ืื ื”ืžืื’ืจ ืžื—ื•ื‘ืจ. ืื‘ืœ ืขื‘ื•ืจ ื˜ืจื™ื’ืจ ื™ื“ื ื™ (ืื• ื›ื—ืœืง ืž-pipeline ืžื•ืจื›ื‘ ื™ื•ืชืจ) ื ื™ืชืŸ ืœื”ืฉืชืžืฉ ื‘-Deploy Hook.

**๐Ÿ” ืกื•ื“ื•ืช GitHub:** `RENDER_DEPLOY_HOOK` (URL, ืฉื”ืชืงื‘ืœ ืžื”ื’ื“ืจื•ืช ื”ืฉื™ืจื•ืช ื‘-Render).


# .github/workflows/deploy-render.yml
name: Trigger Render Deploy

on:
  workflow_dispatch: # ื”ืคืขืœื” ื™ื“ื ื™ืช ืž-UI GitHub

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger Render Deploy Hook
        run: curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}

๐ŸŒŸ CI/CD ืžืชืงื“ื: ื‘ื ื™ื™ืช Docker โ† Push ืœ-GHCR โ† Staging/Production ื‘-GCP Cloud Run

ื•ืขื›ืฉื™ื• ื”ื“ื•ื‘ื“ื‘ืŸ ืฉื‘ืงืฆืคืช! ื ื‘ื ื” pipeline ืžืชืงื“ื: ื‘ื ื™ื™ืช ืชืžื•ื ืช Docker.
ืคืจืกื•ื ื”ืชืžื•ื ื” ื‘-GitHub Container Registry (ghcr.io).
ืคืจื™ืกื” ืื•ื˜ื•ืžื˜ื™ืช ืœืกื‘ื™ื‘ืช staging ื‘-GCP Cloud Run.
ืคืจื™ืกื” ืœืกื‘ื™ื‘ืช production ื‘-GCP Cloud Run ืœืื—ืจ ืื™ืฉื•ืจ ื™ื“ื ื™.

ืœืฉื ื›ืš ื ืฆื˜ืจืš ืžืกืคืจ ืงื‘ืฆื™ workflow.

**ืกื•ื“ื•ืช GitHub ื ื“ืจืฉื™ื:**GCP_PROJECT_ID: ืžื–ื”ื” ื”ืคืจื•ื™ืงื˜ ืฉืœืš ื‘-GCP.
GCP_CREDENTIALS: ืžืคืชื— JSON ืฉืœ ื—ืฉื‘ื•ืŸ ืฉื™ืจื•ืช GCP ืขื ื”ืจืฉืื•ืช ืคืจื™ืกื” ืœ-Cloud Run ื•ื’ื™ืฉื” ืœ-GHCR (ืื ื ื“ืจืฉ). ื‘ื“ืจืš ื›ืœืœ GITHUB_TOKEN ืžืกืคื™ืง ืœื’ื™ืฉื” ืœ-GHCR ืž-Actions.
GCP_REGION: ืื–ื•ืจ ืขื‘ื•ืจ Cloud Run (ืœื“ื•ื’ืžื”, europe-west1).

1. ื‘ื ื™ื™ื” ื•ืคืจืกื•ื ืชืžื•ื ืช Docker ื‘-GHCR


# .github/workflows/build.yml
name: Build & Push to GHCR

on:
  push:
    branches: [main]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read      # ืขื‘ื•ืจ checkout
      packages: write     # ืขื‘ื•ืจ push ืœ-GHCR

    steps:
      - uses: actions/checkout@v3

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build Docker image
        # ื”ื—ืœืฃ ืืช myapp ื‘ืฉื ื”ื™ื™ืฉื•ื ืฉืœืš
        run: docker build -t ${{ secrets.DOCKER_USERNAME }}/myapp:latest .

      - name: Push Docker image
        run: docker push ${{ secrets.DOCKER_USERNAME }}/myapp:latest

github.repository_owner: ื‘ืขืœ ื”ืžืื’ืจ (ืฉื ื”ืžืฉืชืžืฉ ืื• ื”ืืจื’ื•ืŸ ืฉืœืš).
github.event.repository.name: ืฉื ื”ืžืื’ืจ.
myapp: ืฉื ื”ื™ื™ืฉื•ื/ื”ืชืžื•ื ื” ืฉืœืš.

2. ืคืจื™ืกื” ืื•ื˜ื•ืžื˜ื™ืช ืœ-Staging (GCP Cloud Run)

ื”-workflow ื”ื–ื” ื™ื•ืคืขืœ ืื•ื˜ื•ืžื˜ื™ืช ืœืื—ืจ ื”ืฉืœืžื” ืžื•ืฆืœื—ืช ืฉืœ `build.yml`.


# .github/workflows/deploy-staging.yml
name: Deploy to GCP Cloud Run (Staging)

on:
  workflow_run:
    workflows: ["Build & Push to GHCR"] # ืฉื ื”-workflow ืฉืœ ื”ื‘ื ื™ื™ื”
    types:
      - completed

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    # ื”ืคืขืœ ืจืง ืื ื”-workflow ืฉืœ ื”ื‘ื ื™ื™ื” ื”ืกืชื™ื™ื ื‘ื”ืฆืœื—ื”
    if: ${{ github.event.workflow_run.conclusion == 'success' }}

    # ื”ืฉืชืžืฉ ื‘ืกื‘ื™ื‘ื•ืช GitHub ืขื‘ื•ืจ staging (ืื•ืคืฆื™ื•ื ืœื™, ืืš ืคืจืงื˜ื™ืงื” ื˜ื•ื‘ื”)
    environment:
      name: staging
      url: ${{ steps.deploy.outputs.url }} # ื”-URL ื™ื”ื™ื” ื–ืžื™ืŸ ืœืื—ืจ ื”ืคืจื™ืกื”

    steps:
      - uses: actions/checkout@v3

      - id: 'auth'
        uses: 'google-github-actions/auth@v2'
        with:
          credentials_json: '${{ secrets.GCP_CREDENTIALS }}'

      - name: 'Deploy to Cloud Run (Staging)'
        id: deploy
        uses: 'google-github-actions/deploy-cloudrun@v2'
        with:
          service: 'myapp-staging' # ืฉื ืฉื™ืจื•ืช ื”-staging ืฉืœืš ื‘-Cloud Run
          region: '${{ secrets.GCP_REGION }}'
          # ื”ืฉืชืžืฉ ื‘ืชืžื•ื ื” ืฉื ืฉืœื—ื” ื‘-build.yml
          image: 'ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest'
          project_id: '${{ secrets.GCP_PROJECT_ID }}'
          flags: '--allow-unauthenticated --platform=managed' # ืืคืฉืจ ื’ื™ืฉื” ืœื ืžืื•ืžืชืช ืœื“ื•ื’ืžื”

3. ืคืจื™ืกื” ืœ-Production ืขื ืื™ืฉื•ืจ ื™ื“ื ื™ (GCP Cloud Run)

ื”-workflow ื”ื–ื” ืžื•ืคืขืœ ื™ื“ื ื™ืช ื“ืจืš UI ื”-GitHub Actions.


# .github/workflows/deploy-prod.yml
name: Deploy to GCP Cloud Run (Production)

on:
  workflow_dispatch: # ืžืืคืฉืจ ื”ืคืขืœื” ื™ื“ื ื™ืช

jobs:
  deploy-production:
    runs-on: ubuntu-latest

    environment:
      name: production
      url: ${{ steps.deploy.outputs.url }}

    steps:
      - uses: actions/checkout@v3

      - id: 'auth'
        uses: 'google-github-actions/auth@v2'
        with:
          credentials_json: '${{ secrets.GCP_CREDENTIALS }}'

      - name: 'Deploy to Cloud Run (Production)'
        id: deploy
        uses: 'google-github-actions/deploy-cloudrun@v2'
        with:
          service: 'myapp-production' # ืฉื ืฉื™ืจื•ืช ื”-production ืฉืœืš
          region: '${{ secrets.GCP_REGION }}'
          image: 'ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest' # ื”ืฉืชืžืฉ ื‘ืื•ืชื” ืชืžื•ื ืช 'latest'
          project_id: '${{ secrets.GCP_PROJECT_ID }}'
          flags: '--allow-unauthenticated --platform=managed'
          # ืขื‘ื•ืจ production ื ื™ืชืŸ ืœื”ื•ืกื™ืฃ --no-traffic ื•ืœืื—ืจ ืžื›ืŸ ืœื”ื—ืœื™ืฃ ืชื ื•ืขื” ื‘ื”ื“ืจื’ื”
          # traffic:
          #   latest: true
          #   percent: 100

**ื ืงื•ื“ื•ืช ื—ืฉื•ื‘ื•ืช ื‘-pipeline ืžืชืงื“ื ื–ื”:**GitHub Container Registry (ghcr.io): ืื ื• ืžืฉืชืžืฉื™ื ื‘ื• ืœืื—ืกื•ืŸ ืชืžื•ื ื•ืช Docker. ื–ื” ื ื•ื— ืžื›ื™ื•ื•ืŸ ืฉื”ื•ื ืžืฉื•ืœื‘ ื”ื™ื˜ื‘ ืขื GitHub Actions.
workflow_run: ืžืืคืฉืจ ืœื”ืคืขื™ืœ workflow ืื—ื“ (ืคืจื™ืกืช staging) ืขื ื”ืฉืœืžืช workflow ืื—ืจ (ื‘ื ื™ื™ื”).
workflow_dispatch: ืžืืคืฉืจ ื”ืคืขืœื” ื™ื“ื ื™ืช ืฉืœ workflow (ืคืจื™ืกืช production), ื”ืžืกืคืงืช ืฉืœื™ื˜ื”.
GitHub Environments: ืžืืคืฉืจื™ื ืœืš ืœื”ื’ื“ื™ืจ ื›ืœืœื™ ื”ื’ื ื” ืขื‘ื•ืจ production (ืœื“ื•ื’ืžื”, ื“ืจื™ืฉืช ืื™ืฉื•ืจ ืžืžื‘ืงืจื™ื ืกืคืฆื™ืคื™ื™ื) ื•ืœืื—ืกืŸ ืกื•ื“ื•ืช ืกืคืฆื™ืคื™ื™ื ืœืกื‘ื™ื‘ื”.
GCP Cloud Run: ืืคืฉืจื•ืช serverless ืžืฆื•ื™ื ืช ืœื”ืคืขืœืช ื™ื™ืฉื•ืžื™ื ืžื‘ื•ืกืกื™ ืงื•ื ื˜ื™ื™ื ืจื™ื.

๐Ÿ” ืื‘ื˜ื—ื” โ€“ ื–ื” ื—ืฉื•ื‘!

ื”ืฉืชืžืฉ ื‘ืกื•ื“ื•ืช GitHub: ืœืขื•ืœื ืืœ ืชืฉืžื•ืจ ืืกื™ืžื•ื ื™ื, ืกื™ืกืžืื•ืช, ืžืคืชื—ื•ืช API ื™ืฉื™ืจื•ืช ื‘ืงื‘ืฆื™ YAML. ื”ืฉืชืžืฉ ื‘-Settings -> Secrets and variables -> Actions ื‘ืžืื’ืจ ืฉืœืš.
ื”ืจืฉืื•ืช ืžื™ื ื™ืžืœื™ื•ืช: ืขื‘ื•ืจ ื—ืฉื‘ื•ื ื•ืช ืฉื™ืจื•ืช (ืœื“ื•ื’ืžื”, GCP), ื”ืขื ืง ืจืง ืืช ื”ื”ืจืฉืื•ืช ื”ื ื—ื•ืฆื•ืช ื‘ืืžืช ืœื‘ื™ืฆื•ืข ืžืฉื™ืžื•ืช CI/CD.
ื‘ื™ื“ื•ื“ ืกื‘ื™ื‘ื•ืช: Staging ื•-Production ืฆืจื™ื›ื™ื ืœื”ื™ื•ืช ืžื‘ื•ื“ื“ื™ื ื›ื›ืœ ื”ืืคืฉืจ. ืคืจื•ื™ืงื˜ื™ื/ื—ืฉื‘ื•ื ื•ืช ืฉื•ื ื™ื ื‘ืกืคืงื™ ืขื ืŸ โ€“ ื–ื• ืคืจืงื˜ื™ืงื” ื˜ื•ื‘ื”.
ื”ื’ื ืช ืขื ืคื™ื: ื”ื’ื“ืจ ื”ื’ื ื” ืขื‘ื•ืจ ืขื ืฃ main (ืื• master) ื›ืš ืฉ-push ืืœื™ื• ื™ืชืืคืฉืจ ืจืง ื‘ืืžืฆืขื•ืช Pull Request ืขื ื‘ื“ื™ืงื•ืช CI ื—ื•ื‘ื”.

๐Ÿš€ CI/CD: ืžื™ืกื•ื“ื•ืช ืœื™ื™ืฆื•ืจ ื‘-GCP ืขื GitHub Actions โ€“ ืžื“ืจื™ืš ืžืœื ืขื ื“ื•ื’ืžืื•ืช ๐Ÿš€

ืฉืœื•ื, ืžืคืชื—ื™ื! ื‘ืžืืžืจ ื–ื” ืื“ื‘ืจ ืขืœ CI/CD โ€“ ื”ืงื•ื ืกืคื˜.

ืžื”ื• CI/CD pipeline ื‘ื”ืงืฉืจ ืฉืœ ืชื›ื ื•ืช?

CI/CD pipeline (Continuous Integration / Continuous Delivery ืื• Continuous Deployment) ื”ื•ื ืชื”ืœื™ืš ืื•ื˜ื•ืžื˜ื™, ื”ืžืืคืฉืจ ืœืžืคืชื—ื™ื ืœืกืคืง ืฉื™ื ื•ื™ื™ ืงื•ื“ ื‘ืžื”ื™ืจื•ืช ื•ื‘ืืžื™ื ื•ืช ืœืกื‘ื™ื‘ืช ืขื‘ื•ื“ื” (ืคืจื•ื“ืงืฉืŸ).

ื‘ื•ืื• ื ืคืจืง ืืช ืžื•ืฉื’ื™ ื”ืžืคืชื—:

๐Ÿ”ง CI โ€” Continuous Integration (ืื™ื ื˜ื’ืจืฆื™ื” ืžืชืžืฉื›ืช)
ื–ื•ื”ื™ ืคืจืงื˜ื™ืงื” ืฉื‘ื” ืžืคืชื—ื™ื ืžื‘ืฆืขื™ื ืฉื™ื ื•ื™ื™ื ืชื›ื•ืคื™ื ื‘ื‘ืกื™ืก ืงื•ื“ ืžืฉื•ืชืฃ. ื›ืœ ืฉื™ื ื•ื™ ื›ื–ื” ื‘ืื•ืคืŸ ืื•ื˜ื•ืžื˜ื™:

  • ื ื‘ื ื” (build)
  • ื ื‘ื“ืง (ื‘ื“ื™ืงื•ืช ื™ื—ื™ื“ื”, ื‘ื“ื™ืงื•ืช ืื™ื ื˜ื’ืจืฆื™ื”)
  • ื ื‘ื“ืง ืœื”ืชืืžื” ืœืชืงื ื™ื (linting, ื ื™ืชื•ื— ืกื˜ื˜ื™)

๐Ÿ‘‰ ืžื˜ืจืช CI: ืœื–ื”ื•ืช ืฉื’ื™ืื•ืช ื‘ืฉืœื‘ ืžื•ืงื“ื ื›ื›ืœ ื”ืืคืฉืจ, ืœืคื ื™ ืฉื”ืŸ ืฉื•ื‘ืจื•ืช ืžืฉื”ื• ื—ืฉื•ื‘ ืื• ืžื’ื™ืขื•ืช ืœื’ืจืกื”.

๐Ÿš€ CD โ€” Continuous Delivery (ืืกืคืงื” ืžืชืžืฉื›ืช) ืื• Continuous Deployment (ืคืจื™ืกื” ืžืชืžืฉื›ืช)
ื›ืืŸ ื™ืฉ ืฉืชื™ ืืคืฉืจื•ื™ื•ืช:

โœ… Continuous Delivery (ืืกืคืงื” ืžืชืžืฉื›ืช)
ืœืื—ืจ ื”ืฉืœืžื” ืžื•ืฆืœื—ืช ืฉืœ ืฉืœื‘ ื”-CI, ื”ืฉื™ื ื•ื™ื™ื ื‘ืื•ืคืŸ ืื•ื˜ื•ืžื˜ื™:

  • ืขื•ื‘ืจื™ื ื‘ื“ื™ืงื•ืช ื ื•ืกืคื•ืช (ืœื“ื•ื’ืžื”, E2E โ€“ ื‘ื“ื™ืงื•ืช ืžืงืฆื” ืœืงืฆื”)
  • ืžื’ื™ืขื™ื ืœืฉืจืช staging (ื‘ื“ื™ืงื”)
    ๐Ÿ‘‰ ืืš ืคืจื™ืกื” ืœืคืจื•ื“ืงืฉืŸ ืขื“ื™ื™ืŸ ื“ื•ืจืฉืช ืื™ืฉื•ืจ ื™ื“ื ื™. ื–ื” ื ื•ืชืŸ ืœืฆื•ื•ืช ืฉืœื™ื˜ื” ืขืœ ืžืชื™ ื‘ื“ื™ื•ืง ื”ืžืฉืชืžืฉื™ื ื™ืจืื• ืืช ื”ืฉื™ื ื•ื™ื™ื.

๐Ÿค– Continuous Deployment (ืคืจื™ืกื” ืžืชืžืฉื›ืช)
ื–ื”ื• ื”ืฉืœื‘ ื”ื‘ื ืœืื—ืจ Continuous Delivery. ื›ืืŸ ื”ืคืจื™ืกื” ืœืคืจื•ื“ืงืฉืŸ ืžืชืจื—ืฉืช ื‘ืื•ืคืŸ ืื•ื˜ื•ืžื˜ื™ ืœื—ืœื•ื˜ื™ืŸ, ืื ื›ืœ ืฉืœื‘ื™ ื”-pipeline ื”ืงื•ื“ืžื™ื (ื‘ื ื™ื™ื”, ื›ืœ ื”ื‘ื“ื™ืงื•ืช) ืขื‘ืจื• ื‘ื”ืฆืœื—ื”. ื–ื•ื”ื™ ื”ืจืžื” ื”ืžืชืงื“ืžืช ื‘ื™ื•ืชืจ ืฉืœ ืื•ื˜ื•ืžืฆื™ื”.

๐Ÿ”„ ืžืžื” ืžื•ืจื›ื‘ ื‘ื“ืจืš ื›ืœืœ CI/CD pipeline?

Pipeline ื˜ื™ืคื•ืกื™ ื›ื•ืœืœ ืืช ื”ืฉืœื‘ื™ื ื”ื‘ืื™ื:

  1. Checkout โ€” ืฉื™ื‘ื•ื˜ ื”ื’ืจืกื” ื”ืื—ืจื•ื ื” ืฉืœ ื”ืงื•ื“ ืžื”ืžืื’ืจ.
  2. Build โ€” ื‘ื ื™ื™ืช ื”ืคืจื•ื™ืงื˜ (ืงื•ืžืคื™ืœืฆื™ื”, ื”ืจื›ื‘ืช ืืจื˜ื™ืคืงื˜ื™ื, ืชืžื•ื ื•ืช Docker).
  3. Test โ€” ื”ืคืขืœืช ืกื•ื’ื™ื ืฉื•ื ื™ื ืฉืœ ื‘ื“ื™ืงื•ืช (ื™ื—ื™ื“ื”, ืื™ื ื˜ื’ืจืฆื™ื”, E2E).
  4. Lint/Code Quality โ€” ื‘ื“ื™ืงืช ืงื•ื“ ืœื”ืชืืžื” ืœืกื’ื ื•ืŸ ื•ืฉื’ื™ืื•ืช ืคื•ื˜ื ืฆื™ืืœื™ื•ืช ื‘ืืžืฆืขื•ืช ืžื ืชื—ื™ื ืกื˜ื˜ื™ื™ื.
  5. Deploy โ€” ืคืจื™ืกืช ื”ื™ื™ืฉื•ื (ืœืฉืจืช staging ืื• production).
  6. Notify โ€” ืฉืœื™ื—ืช ื”ืชืจืื•ืช ืขืœ ืกื˜ื˜ื•ืก ื”-pipeline ืœืฆื•ื•ืช (ืœื“ื•ื’ืžื”, ื‘-Slack, Email).

๐Ÿ›  ื›ืœื™ื ืคื•ืคื•ืœืจื™ื™ื ืœ-CI/CD:

  • GitHub Actions (ื”ืžื•ืงื“ ืฉืœื ื• ื”ื™ื•ื!)
  • GitLab CI/CD
  • Jenkins
  • CircleCI
  • Bitbucket Pipelines
  • Azure DevOps
  • TeamCity

๐Ÿง  ืœืžื” ื‘ื›ืœืœ ืฆืจื™ืš CI/CD?

  • ืžืคื—ื™ืช ื˜ืขื•ื™ื•ืช ืื ื•ืฉ: ืื•ื˜ื•ืžืฆื™ื” ืžื‘ื˜ืœืช ืฉื’ื™ืื•ืช ื”ืงืฉื•ืจื•ืช ืœืคืขื•ืœื•ืช ื™ื“ื ื™ื•ืช.
  • ื–ื™ื”ื•ื™ ืžื”ื™ืจ ืฉืœ ื‘ืื’ื™ื: ืฉื’ื™ืื•ืช ื ืžืฆืื•ืช ืžื•ืงื“ื ื™ื•ืชืจ, ืงืœ ื•ื–ื•ืœ ื™ื•ืชืจ ืœืชืงืŸ ืื•ืชืŸ.
  • ืื•ื˜ื•ืžืฆื™ื” ืฉืœ ืžืฉื™ืžื•ืช ืฉื’ืจืชื™ื•ืช: ืžืคืชื—ื™ื ืžืงื“ื™ืฉื™ื ืคื—ื•ืช ื–ืžืŸ ืœื‘ื ื™ื™ื” ื•ืคืจื™ืกื”, ื•ื™ื•ืชืจ โ€“ ืœืงื•ื“.
  • ืฉื™ืคื•ืจ ืื™ื›ื•ืช ื”ืงื•ื“: ื‘ื“ื™ืงื•ืช ื•ืžื‘ื—ื ื™ื ืžืชืžื™ื“ื™ื ืžืขืœื™ื ืืช ืจืฃ ื”ืื™ื›ื•ืช ื”ื›ืœืœื™.
  • ืืกืคืงื” ืžื”ื™ืจื” ืฉืœ ืคื™ืฆ'ืจื™ื ืœืžืฉืชืžืฉื™ื: ื™ื›ื•ืœื•ืช ื—ื“ืฉื•ืช ืžื’ื™ืขื•ืช ืœืžืฉืชืžืฉ ื”ืงืฆื” ืžื”ืจ ื™ื•ืชืจ ื•ื‘ืชื“ื™ืจื•ืช ื’ื‘ื•ื”ื” ื™ื•ืชืจ.

๐Ÿ“ฆ ื“ื•ื’ืžืื•ืช ืคืฉื•ื˜ื•ืช ืœ-CI/CD ืขื GitHub Actions

ื‘ื•ืื• ื ืกืชื›ืœ ืขืœ pipelines ื‘ืกื™ืกื™ื™ื ืœื˜ื›ื ื•ืœื•ื’ื™ื•ืช ืคื•ืคื•ืœืจื™ื•ืช. ื›ืœ ื”ื“ื•ื’ืžืื•ืช ืžืฉืชืžืฉื•ืช ื‘-GitHub Actions ื•ื ืฉืžืจื•ืช ื‘ืกืคืจื™ื™ืช .github/workflows/code>span> ืฉืœ ื”ืคืจื•ื™ืงื˜ ืฉืœื›ื.</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":4} --> <h4 class="wp-block-heading">๐Ÿ CI/CD ืขื‘ื•ืจ Python (ืขื <span dir="ltr"><code>pytestcode>span> ื•-flake8)</h4> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code> # .github/workflows/python-ci.yml name: Python CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' # ืฆื™ื™ืŸ ืืช ื”ื’ืจืกื” ืฉืœืš - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt # ื•ื“ื ืฉื™ืฉ ืœืš requirements.txt pip install flake8 pytest - name: Lint with flake8 run: | # ื‘ื“ื•ืง ืงื•ื“ ื‘ืชื™ืงื™ื•ืช src ื•-tests (ื”ืชืื ืœืคืจื•ื™ืงื˜ ืฉืœืš) flake8 src tests - name: Run tests run: | pytest

๐ŸŒ CI/CD ืขื‘ื•ืจ Node.js (ืขื npm testcode>span> ื•-eslint) <code># .github/workflows/node-ci.yml name: Node.js CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [18.x] # ืฆื™ื™ืŸ ืืช ื’ืจืกืช ื”-Node.js ืฉืœืš steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm install # ืื• npm ci ืœื”ืชืงื ื” ืฆืคื•ื™ื” ื™ื•ืชืจ - name: Lint with ESLint run: npx eslint . # ื•ื“ื ืฉ-ESLint ืžื•ื’ื“ืจ ื‘ืคืจื•ื™ืงื˜ - name: Run tests run: npm test

๐Ÿณ CI/CD ืขื‘ื•ืจ Docker (ื‘ื ื™ื™ื” ื•-push ืœ-Docker Hub)

ืœื“ื•ื’ืžื” ื–ื•, ืชืฆื˜ืจืš ืกื•ื“ื•ืช `DOCKER_USERNAME` ื•-`DOCKER_PASSWORD` (ืื• ืืกื™ืžื•ืŸ) ื‘ื”ื’ื“ืจื•ืช ืžืื’ืจ ื”-GitHub ืฉืœืš (`Settings -> Secrets and variables -> Actions`).


# .github/workflows/docker-ci.yml
name: Docker CI/CD

on:
  push:
    branches: [ main ] # ื”ืคืขืœ ืจืง ืขื‘ื•ืจ ืขื ืฃ main

jobs:
  docker:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Log in to Docker Hub
        run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin

      - name: Build Docker image
        # ื”ื—ืœืฃ ืืช myapp ื‘ืฉื ื”ื™ื™ืฉื•ื ืฉืœืš
        run: docker build -t ${{ secrets.DOCKER_USERNAME }}/myapp:latest .

      - name: Push Docker image
        run: docker push ${{ secrets.DOCKER_USERNAME }}/myapp:latest

๐Ÿšš ืคืจื™ืกื” ืœืคืœื˜ืคื•ืจืžื•ืช ืคื•ืคื•ืœืจื™ื•ืช

ื›ืขืช, ื›ืฉื™ืฉ ืœื ื• ืืจื˜ื™ืคืงื˜ื™ื ื‘ื ื•ื™ื™ื ื•ื ื‘ื“ืงื™ื (ืœื“ื•ื’ืžื”, ืชืžื•ื ืช Docker), ื‘ื•ืื• ื ืจืื” ื›ื™ืฆื“ ื ื™ืชืŸ ืœืคืจื•ืก ืื•ืชื.

๐ŸŸฃ ืคืจื™ืกื” ืœ-Heroku

**๐Ÿ” ืกื•ื“ื•ืช GitHub:** `HEROKU_API_KEY`, `HEROKU_APP_NAME`.


# .github/workflows/deploy-heroku.yml
name: Deploy to Heroku

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install Heroku CLI
        run: curl https://cli-assets.heroku.com/install.sh | sh
      - name: Login to Heroku
        env:
          HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
        run: heroku auth:token
      - name: Deploy to Heroku
        env:
          HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
        run: |
          heroku git:remote -a ${{ secrets.HEROKU_APP_NAME }}
          git push heroku main -f # ื”ื™ื–ื”ืจ ืขื -f (force push)

ืื ืืชื” ืคื•ืจืก ืชืžื•ื ืช Docker ืœ-Heroku:


# ... (ืฉืœื‘ื™ ื‘ื ื™ื™ื” ื•ื”ืชื—ื‘ืจื•ืช ืœ-Docker Hub/GHCR ืžื“ื•ื’ืžืื•ืช ืงื•ื“ืžื•ืช) ...
# deploy:
#   name: Deploy to Heroku
#   needs: build # ืชืœื•ื™ ื‘ืžืฉื™ืžืช ื‘ื ื™ื™ืช ื”ืชืžื•ื ื”
#   runs-on: ubuntu-latest
#   steps:
#     # ...
#     - name: Login to Heroku container registry
#       run: echo "${{ secrets.HEROKU_API_KEY }}" | docker login --username=_ --password-stdin registry.heroku.com
#     - name: Tag image for Heroku
#       # ื‘ื”ื ื—ื” ืฉื”ืชืžื•ื ื” ื ื‘ื ื™ืช ื›-ghcr.io/username/repo/myapp:latest
#       run: docker tag ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest registry.heroku.com/${{ secrets.HEROKU_APP_NAME }}/web
#     - name: Push image to Heroku
#       run: docker push registry.heroku.com/${{ secrets.HEROKU_APP_NAME }}/web
#     - name: Release Heroku App
#       env:
#         HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
#       run: heroku container:release web --app ${{ secrets.HEROKU_APP_NAME }}

๐ŸŸจ ืคืจื™ืกื” ืœ-AWS (ืœื“ื•ื’ืžื”, ืกื˜ื˜ื™ ืœ-S3)

**๐Ÿ” ืกื•ื“ื•ืช GitHub:** `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `S3_BUCKET_NAME`.


# .github/workflows/deploy-aws-s3.yml
name: Deploy Static Site to AWS S3

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}
      - name: Sync files to S3
        # ื”ื—ืœืฃ ืืช ./public ื‘ื ืชื™ื‘ ืœืงื‘ืฆื™ื ื”ืกื˜ื˜ื™ื™ื ืฉืœืš
        run: aws s3 sync ./public s3://${{ secrets.S3_BUCKET_NAME }} --delete

ืœืคืจื™ืกื” ืœ-**AWS** **Elastic** **Beanstalk** ื‘ื“ืจืš ื›ืœืœ ืžืฉืชืžืฉื™ื ื‘-EB CLI, ื”-pipeline ื™ื”ื™ื” ื“ื•ืžื”, ืืš ืขื ืคืงื•ื“ื•ืช eb deploycode>span>.</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":4} --> <h4 class="wp-block-heading">๐Ÿ”ต ืคืจื™ืกื” ืœ-Google Cloud Platform (GCP App Engine)</h4> <!-- /wp:heading --> <!-- wp:paragraph --> <p>**๐Ÿ” ืกื•ื“ื•ืช GitHub:** `GCP_CREDENTIALS` (ืžืคืชื— JSON ืฉืœ ื—ืฉื‘ื•ืŸ ืฉื™ืจื•ืช), `GCP_PROJECT_ID`.</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> # .github/workflows/deploy-gcp-app-engine.yml name: Deploy to GCP App Engine on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Cloud SDK uses: google-github-actions/setup-gcloud@v2 with: project_id: ${{ secrets.GCP_PROJECT_ID }} service_account_key: ${{ secrets.GCP_CREDENTIALS }} export_default_credentials: true - name: Deploy to App Engine # ื•ื“ื ืฉื™ืฉ ืœืš app.yaml ื‘ืฉื•ืจืฉ ื”ืคืจื•ื™ืงื˜ run: gcloud app deploy --quiet

๐ŸŸช ืคืจื™ืกื” ืœ-Render.com

Render ืœืขื™ืชื™ื ืงืจื•ื‘ื•ืช ืคื•ืจืก ืื•ื˜ื•ืžื˜ื™ืช ื‘-push ืœ-GitHub, ืื ื”ืžืื’ืจ ืžื—ื•ื‘ืจ. ืื‘ืœ ืขื‘ื•ืจ ื˜ืจื™ื’ืจ ื™ื“ื ื™ (ืื• ื›ื—ืœืง ืž-pipeline ืžื•ืจื›ื‘ ื™ื•ืชืจ) ื ื™ืชืŸ ืœื”ืฉืชืžืฉ ื‘-Deploy Hook.

**๐Ÿ” ืกื•ื“ื•ืช GitHub:** `RENDER_DEPLOY_HOOK` (URL, ืฉื”ืชืงื‘ืœ ืžื”ื’ื“ืจื•ืช ื”ืฉื™ืจื•ืช ื‘-Render).


# .github/workflows/deploy-render.yml
name: Trigger Render Deploy

on:
  workflow_dispatch: # ื”ืคืขืœื” ื™ื“ื ื™ืช ืž-UI GitHub

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger Render Deploy Hook
        run: curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}

๐ŸŒŸ CI/CD ืžืชืงื“ื: ื‘ื ื™ื™ืช Docker โ† Push ืœ-GHCR โ† Staging/Production ื‘-GCP Cloud Run

ื•ืขื›ืฉื™ื• ื”ื“ื•ื‘ื“ื‘ืŸ ืฉื‘ืงืฆืคืช! ื ื‘ื ื” pipeline ืžืชืงื“ื: ื‘ื ื™ื™ืช ืชืžื•ื ืช Docker.
ืคืจืกื•ื ื”ืชืžื•ื ื” ื‘-GitHub Container Registry (ghcr.io).
ืคืจื™ืกื” ืื•ื˜ื•ืžื˜ื™ืช ืœืกื‘ื™ื‘ืช staging ื‘-GCP Cloud Run.
ืคืจื™ืกื” ืœืกื‘ื™ื‘ืช production ื‘-GCP Cloud Run ืœืื—ืจ ืื™ืฉื•ืจ ื™ื“ื ื™.

ืœืฉื ื›ืš ื ืฆื˜ืจืš ืžืกืคืจ ืงื‘ืฆื™ workflow.

**ืกื•ื“ื•ืช GitHub ื ื“ืจืฉื™ื:**GCP_PROJECT_ID: ืžื–ื”ื” ื”ืคืจื•ื™ืงื˜ ืฉืœืš ื‘-GCP.
GCP_CREDENTIALS: ืžืคืชื— JSON ืฉืœ ื—ืฉื‘ื•ืŸ ืฉื™ืจื•ืช GCP ืขื ื”ืจืฉืื•ืช ืคืจื™ืกื” ืœ-Cloud Run ื•ื’ื™ืฉื” ืœ-GHCR (ืื ื ื“ืจืฉ). ื‘ื“ืจืš ื›ืœืœ GITHUB_TOKEN ืžืกืคื™ืง ืœื’ื™ืฉื” ืœ-GHCR ืž-Actions.
GCP_REGION: ืื–ื•ืจ ืขื‘ื•ืจ Cloud Run (ืœื“ื•ื’ืžื”, europe-west1).

1. ื‘ื ื™ื™ื” ื•ืคืจืกื•ื ืชืžื•ื ืช Docker ื‘-GHCR


# .github/workflows/build.yml
name: Build & Push to GHCR

on:
  push:
    branches: [main]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read      # ืขื‘ื•ืจ checkout
      packages: write     # ืขื‘ื•ืจ push ืœ-GHCR

    steps:
      - uses: actions/checkout@v3

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build Docker image
        # ื”ื—ืœืฃ ืืช myapp ื‘ืฉื ื”ื™ื™ืฉื•ื ืฉืœืš
        run: docker build -t ${{ secrets.DOCKER_USERNAME }}/myapp:latest .

      - name: Push Docker image
        run: docker push ${{ secrets.DOCKER_USERNAME }}/myapp:latest

github.repository_owner: ื‘ืขืœ ื”ืžืื’ืจ (ืฉื ื”ืžืฉืชืžืฉ ืื• ื”ืืจื’ื•ืŸ ืฉืœืš).
github.event.repository.name: ืฉื ื”ืžืื’ืจ.
myapp: ืฉื ื”ื™ื™ืฉื•ื/ื”ืชืžื•ื ื” ืฉืœืš.

2. ืคืจื™ืกื” ืื•ื˜ื•ืžื˜ื™ืช ืœ-Staging (GCP Cloud Run)

ื”-workflow ื”ื–ื” ื™ื•ืคืขืœ ืื•ื˜ื•ืžื˜ื™ืช ืœืื—ืจ ื”ืฉืœืžื” ืžื•ืฆืœื—ืช ืฉืœ `build.yml`.


# .github/workflows/deploy-staging.yml
name: Deploy to GCP Cloud Run (Staging)

on:
  workflow_run:
    workflows: ["Build & Push to GHCR"] # ืฉื ื”-workflow ืฉืœ ื”ื‘ื ื™ื™ื”
    types:
      - completed

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    # ื”ืคืขืœ ืจืง ืื ื”-workflow ืฉืœ ื”ื‘ื ื™ื™ื” ื”ืกืชื™ื™ื ื‘ื”ืฆืœื—ื”
    if: ${{ github.event.workflow_run.conclusion == 'success' }}

    # ื”ืฉืชืžืฉ ื‘ืกื‘ื™ื‘ื•ืช GitHub ืขื‘ื•ืจ staging (ืื•ืคืฆื™ื•ื ืœื™, ืืš ืคืจืงื˜ื™ืงื” ื˜ื•ื‘ื”)
    environment:
      name: staging
      url: ${{ steps.deploy.outputs.url }} # ื”-URL ื™ื”ื™ื” ื–ืžื™ืŸ ืœืื—ืจ ื”ืคืจื™ืกื”

    steps:
      - uses: actions/checkout@v3

      - id: 'auth'
        uses: 'google-github-actions/auth@v2'
        with:
          credentials_json: '${{ secrets.GCP_CREDENTIALS }}'

      - name: 'Deploy to Cloud Run (Staging)'
        id: deploy
        uses: 'google-github-actions/deploy-cloudrun@v2'
        with:
          service: 'myapp-staging' # ืฉื ืฉื™ืจื•ืช ื”-staging ืฉืœืš ื‘-Cloud Run
          region: '${{ secrets.GCP_REGION }}'
          # ื”ืฉืชืžืฉ ื‘ืชืžื•ื ื” ืฉื ืฉืœื—ื” ื‘-build.yml
          image: 'ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest'
          project_id: '${{ secrets.GCP_PROJECT_ID }}'
          flags: '--allow-unauthenticated --platform=managed' # ืืคืฉืจ ื’ื™ืฉื” ืœื ืžืื•ืžืชืช ืœื“ื•ื’ืžื”

3. ืคืจื™ืกื” ืœ-Production ืขื ืื™ืฉื•ืจ ื™ื“ื ื™ (GCP Cloud Run)

ื”-workflow ื”ื–ื” ืžื•ืคืขืœ ื™ื“ื ื™ืช ื“ืจืš UI ื”-GitHub Actions.


# .github/workflows/deploy-prod.yml
name: Deploy to GCP Cloud Run (Production)

on:
  workflow_dispatch: # ืžืืคืฉืจ ื”ืคืขืœื” ื™ื“ื ื™ืช

jobs:
  deploy-production:
    runs-on: ubuntu-latest

    environment:
      name: production
      url: ${{ steps.deploy.outputs.url }}

    steps:
      - uses: actions/checkout@v3

      - id: 'auth'
        uses: 'google-github-actions/auth@v2'
        with:
          credentials_json: '${{ secrets.GCP_CREDENTIALS }}'

      - name: 'Deploy to Cloud Run (Production)'
        id: deploy
        uses: 'google-github-actions/deploy-cloudrun@v2'
        with:
          service: 'myapp-production' # ืฉื ืฉื™ืจื•ืช ื”-production ืฉืœืš
          region: '${{ secrets.GCP_REGION }}'
          image: 'ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest' # ื”ืฉืชืžืฉ ื‘ืื•ืชื” ืชืžื•ื ืช 'latest'
          project_id: '${{ secrets.GCP_PROJECT_ID }}'
          flags: '--allow-unauthenticated --platform=managed'
          # ืขื‘ื•ืจ production ื ื™ืชืŸ ืœื”ื•ืกื™ืฃ --no-traffic ื•ืœืื—ืจ ืžื›ืŸ ืœื”ื—ืœื™ืฃ ืชื ื•ืขื” ื‘ื”ื“ืจื’ื”
          # traffic:
          #   latest: true
          #   percent: 100

**ื ืงื•ื“ื•ืช ื—ืฉื•ื‘ื•ืช ื‘-pipeline ืžืชืงื“ื ื–ื”:**GitHub Container Registry (ghcr.io): ืื ื• ืžืฉืชืžืฉื™ื ื‘ื• ืœืื—ืกื•ืŸ ืชืžื•ื ื•ืช Docker. ื–ื” ื ื•ื— ืžื›ื™ื•ื•ืŸ ืฉื”ื•ื ืžืฉื•ืœื‘ ื”ื™ื˜ื‘ ืขื GitHub Actions.
workflow_run: ืžืืคืฉืจ ืœื”ืคืขื™ืœ workflow ืื—ื“ (ืคืจื™ืกืช staging) ืขื ื”ืฉืœืžืช workflow ืื—ืจ (ื‘ื ื™ื™ื”).
workflow_dispatch: ืžืืคืฉืจ ื”ืคืขืœื” ื™ื“ื ื™ืช ืฉืœ workflow (ืคืจื™ืกืช production), ื”ืžืกืคืงืช ืฉืœื™ื˜ื”.
GitHub Environments: ืžืืคืฉืจื™ื ืœืš ืœื”ื’ื“ื™ืจ ื›ืœืœื™ ื”ื’ื ื” ืขื‘ื•ืจ production (ืœื“ื•ื’ืžื”, ื“ืจื™ืฉืช ืื™ืฉื•ืจ ืžืžื‘ืงืจื™ื ืกืคืฆื™ืคื™ื™ื) ื•ืœืื—ืกืŸ ืกื•ื“ื•ืช ืกืคืฆื™ืคื™ื™ื ืœืกื‘ื™ื‘ื”.
GCP Cloud Run: ืืคืฉืจื•ืช serverless ืžืฆื•ื™ื ืช ืœื”ืคืขืœืช ื™ื™ืฉื•ืžื™ื ืžื‘ื•ืกืกื™ ืงื•ื ื˜ื™ื™ื ืจื™ื.

๐Ÿ” ืื‘ื˜ื—ื” โ€“ ื–ื” ื—ืฉื•ื‘!

ื”ืฉืชืžืฉ ื‘ืกื•ื“ื•ืช GitHub: ืœืขื•ืœื ืืœ ืชืฉืžื•ืจ ืืกื™ืžื•ื ื™ื, ืกื™ืกืžืื•ืช, ืžืคืชื—ื•ืช API ื™ืฉื™ืจื•ืช ื‘ืงื‘ืฆื™ YAML. ื”ืฉืชืžืฉ ื‘-Settings -> Secrets and variables -> Actions ื‘ืžืื’ืจ ืฉืœืš.
ื”ืจืฉืื•ืช ืžื™ื ื™ืžืœื™ื•ืช: ืขื‘ื•ืจ ื—ืฉื‘ื•ื ื•ืช ืฉื™ืจื•ืช (ืœื“ื•ื’ืžื”, GCP), ื”ืขื ืง ืจืง ืืช ื”ื”ืจืฉืื•ืช ื”ื ื—ื•ืฆื•ืช ื‘ืืžืช ืœื‘ื™ืฆื•ืข ืžืฉื™ืžื•ืช CI/CD.
ื‘ื™ื“ื•ื“ ืกื‘ื™ื‘ื•ืช: Staging ื•-Production ืฆืจื™ื›ื™ื ืœื”ื™ื•ืช ืžื‘ื•ื“ื“ื™ื ื›ื›ืœ ื”ืืคืฉืจ. ืคืจื•ื™ืงื˜ื™ื/ื—ืฉื‘ื•ื ื•ืช ืฉื•ื ื™ื ื‘ืกืคืงื™ ืขื ืŸ โ€“ ื–ื• ืคืจืงื˜ื™ืงื” ื˜ื•ื‘ื”.
ื”ื’ื ืช ืขื ืคื™ื: ื”ื’ื“ืจ ื”ื’ื ื” ืขื‘ื•ืจ ืขื ืฃ main (ืื• master) ื›ืš ืฉ-push ืืœื™ื• ื™ืชืืคืฉืจ ืจืง ื‘ืืžืฆืขื•ืช Pull Request ืขื ื‘ื“ื™ืงื•ืช CI ื—ื•ื‘ื”.

ื›ืชื™ื‘ืช ืชื’ื•ื‘ื”

ื”ืื™ืžื™ื™ืœ ืœื ื™ื•ืฆื’ ื‘ืืชืจ. ืฉื“ื•ืช ื”ื—ื•ื‘ื” ืžืกื•ืžื ื™ื *