init
This commit is contained in:
commit
fb6af905cd
30 files changed
+8653
No files matched your search
+378
@@ -0,0 +1,378 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# .dockerignore - Optimize Docker Build Context
|
||||||
|
# ============================================================================
|
||||||
|
# This file excludes unnecessary files from Docker build context to:
|
||||||
|
# - Reduce build time
|
||||||
|
# - Minimize image size
|
||||||
|
# - Improve cache efficiency
|
||||||
|
# - Enhance security (exclude sensitive files)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# VERSION CONTROL
|
||||||
|
# ============================================================================
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.gitattributes
|
||||||
|
.gitmodules
|
||||||
|
.github/
|
||||||
|
.gitlab/
|
||||||
|
.gitlab-ci.yml
|
||||||
|
.gitlab-ci.include.yml
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# NODE.JS & PACKAGE MANAGERS
|
||||||
|
# ============================================================================
|
||||||
|
# Dependencies (will be installed in container)
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.npm/
|
||||||
|
.yarn/
|
||||||
|
.pnpm/
|
||||||
|
.pnpm-store/
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Package manager lock files (keep these for consistent installs)
|
||||||
|
# NOTE: Uncomment if you want to exclude lock files (not recommended)
|
||||||
|
# package-lock.json
|
||||||
|
# yarn.lock
|
||||||
|
# pnpm-lock.yaml
|
||||||
|
|
||||||
|
# Node.js runtime files
|
||||||
|
.node_repl_history
|
||||||
|
.node_tmp/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# FRAMEWORK-SPECIFIC BUILD OUTPUTS
|
||||||
|
# ============================================================================
|
||||||
|
# Next.js
|
||||||
|
.next/
|
||||||
|
out/
|
||||||
|
.vercel/
|
||||||
|
|
||||||
|
# React.js / Vite
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# NestJS
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Angular
|
||||||
|
dist/
|
||||||
|
.angular/
|
||||||
|
|
||||||
|
# Nuxt.js
|
||||||
|
.nuxt/
|
||||||
|
.output/
|
||||||
|
|
||||||
|
# SvelteKit
|
||||||
|
.svelte-kit/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DEVELOPMENT & TESTING
|
||||||
|
# ============================================================================
|
||||||
|
# Test coverage
|
||||||
|
coverage/
|
||||||
|
.coverage/
|
||||||
|
.nyc_output/
|
||||||
|
.jest/
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
*.test.js.snap
|
||||||
|
__tests__/
|
||||||
|
test/
|
||||||
|
tests/
|
||||||
|
spec/
|
||||||
|
specs/
|
||||||
|
|
||||||
|
# Storybook
|
||||||
|
.storybook/
|
||||||
|
storybook-static/
|
||||||
|
|
||||||
|
# Cypress
|
||||||
|
cypress/videos/
|
||||||
|
cypress/screenshots/
|
||||||
|
cypress/downloads/
|
||||||
|
|
||||||
|
# Playwright
|
||||||
|
playwright-report/
|
||||||
|
test-results/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ENVIRONMENT & CONFIGURATION
|
||||||
|
# ============================================================================
|
||||||
|
# Environment files (security risk)
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
!.env.template
|
||||||
|
|
||||||
|
# Local environment
|
||||||
|
.local
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
# Configuration files
|
||||||
|
.eslintcache
|
||||||
|
.stylelintcache
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
*.tsbuildinfo
|
||||||
|
.tscache/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CI/CD & DEPLOYMENT
|
||||||
|
# ============================================================================
|
||||||
|
# CI/CD files
|
||||||
|
ci/
|
||||||
|
.circleci/
|
||||||
|
.travis.yml
|
||||||
|
.appveyor.yml
|
||||||
|
azure-pipelines.yml
|
||||||
|
buildspec.yml
|
||||||
|
cloudbuild.yaml
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
Dockerfile.*
|
||||||
|
!Dockerfile
|
||||||
|
docker-compose*.yml
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Kubernetes
|
||||||
|
k8s/
|
||||||
|
kubernetes/
|
||||||
|
*.yaml
|
||||||
|
*.yml
|
||||||
|
!package.json
|
||||||
|
!package-lock.json
|
||||||
|
!yarn.lock
|
||||||
|
!pnpm-lock.yaml
|
||||||
|
|
||||||
|
# Terraform
|
||||||
|
*.tf
|
||||||
|
*.tfstate
|
||||||
|
*.tfvars
|
||||||
|
.terraform/
|
||||||
|
|
||||||
|
# Ansible
|
||||||
|
inventory/
|
||||||
|
playbooks/
|
||||||
|
roles/
|
||||||
|
ansible.cfg
|
||||||
|
requirements.yml
|
||||||
|
|
||||||
|
# Helm
|
||||||
|
charts/
|
||||||
|
helm/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DEVELOPMENT TOOLS & EDITORS
|
||||||
|
# ============================================================================
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Vim
|
||||||
|
.vim/
|
||||||
|
.vimrc
|
||||||
|
|
||||||
|
# Emacs
|
||||||
|
.emacs.d/
|
||||||
|
.emacs
|
||||||
|
|
||||||
|
# Sublime Text
|
||||||
|
*.sublime-*
|
||||||
|
|
||||||
|
# WebStorm
|
||||||
|
.webstorm/
|
||||||
|
|
||||||
|
# VS Code extensions
|
||||||
|
.vscode-test/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# OPERATING SYSTEM
|
||||||
|
# ============================================================================
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
Icon?
|
||||||
|
._*
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
*.stackdump
|
||||||
|
[Dd]esktop.ini
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
*~
|
||||||
|
.fuse_hidden*
|
||||||
|
.directory
|
||||||
|
.Trash-*
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# LOGS & TEMPORARY FILES
|
||||||
|
# ============================================================================
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids/
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Temporary folders
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
.tmp/
|
||||||
|
.temp/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DOCUMENTATION & ASSETS
|
||||||
|
# ============================================================================
|
||||||
|
# Documentation
|
||||||
|
docs/
|
||||||
|
documentation/
|
||||||
|
README*.md
|
||||||
|
CHANGELOG*.md
|
||||||
|
CONTRIBUTING*.md
|
||||||
|
LICENSE*
|
||||||
|
SECURITY*.md
|
||||||
|
CODE_OF_CONDUCT*.md
|
||||||
|
|
||||||
|
# Design assets (large files)
|
||||||
|
*.psd
|
||||||
|
*.ai
|
||||||
|
*.sketch
|
||||||
|
*.fig
|
||||||
|
*.xd
|
||||||
|
|
||||||
|
# Media files (if not needed in production)
|
||||||
|
# *.png
|
||||||
|
# *.jpg
|
||||||
|
# *.jpeg
|
||||||
|
# *.gif
|
||||||
|
# *.svg
|
||||||
|
# *.ico
|
||||||
|
# *.webp
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# MONITORING & ANALYTICS
|
||||||
|
# ============================================================================
|
||||||
|
# Bundle analyzers
|
||||||
|
.webpack-bundle-analyzer/
|
||||||
|
bundle-analyzer-report.html
|
||||||
|
|
||||||
|
# Performance monitoring
|
||||||
|
.lighthouse/
|
||||||
|
lighthouse-report.html
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# SECURITY & SECRETS
|
||||||
|
# ============================================================================
|
||||||
|
# Security scan results
|
||||||
|
security-audit.json
|
||||||
|
vulnerability-report.json
|
||||||
|
|
||||||
|
# Certificates and keys
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.crt
|
||||||
|
*.cert
|
||||||
|
*.p12
|
||||||
|
*.pfx
|
||||||
|
|
||||||
|
# Secrets
|
||||||
|
secrets/
|
||||||
|
.secrets/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# BACKUP & ARCHIVE
|
||||||
|
# ============================================================================
|
||||||
|
# Backup files
|
||||||
|
*.bak
|
||||||
|
*.backup
|
||||||
|
*.old
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# Archive files
|
||||||
|
*.tar
|
||||||
|
*.tar.gz
|
||||||
|
*.zip
|
||||||
|
*.rar
|
||||||
|
*.7z
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CUSTOM PROJECT EXCLUSIONS
|
||||||
|
# ============================================================================
|
||||||
|
# Add your project-specific exclusions here
|
||||||
|
# Examples:
|
||||||
|
# assets/raw/
|
||||||
|
# scripts/dev/
|
||||||
|
# mock-data/
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
# auto-generated/
|
||||||
|
# generated/
|
||||||
|
|
||||||
|
# Large datasets
|
||||||
|
# data/
|
||||||
|
# datasets/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# KEEP THESE FILES (Explicitly include important files)
|
||||||
|
# ============================================================================
|
||||||
|
# Ensure these files are included even if excluded above
|
||||||
|
!package.json
|
||||||
|
!yarn.lock
|
||||||
|
!package-lock.json
|
||||||
|
!pnpm-lock.yaml
|
||||||
|
!tsconfig.json
|
||||||
|
!next.config.js
|
||||||
|
!nuxt.config.js
|
||||||
|
!vite.config.js
|
||||||
|
!svelte.config.js
|
||||||
|
!tailwind.config.js
|
||||||
|
!postcss.config.js
|
||||||
|
!babel.config.js
|
||||||
|
!webpack.config.js
|
||||||
|
!rollup.config.js
|
||||||
|
!jest.config.js
|
||||||
|
!cypress.config.js
|
||||||
|
!playwright.config.js
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# END OF .dockerignore
|
||||||
|
# ============================================================================
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# This is a reusable Gitea Actions workflow.
|
||||||
|
# It's designed to be called by other repositories to standardize the deployment process.
|
||||||
|
# It performs two main jobs:
|
||||||
|
# 1. Build: Builds a Docker image from the calling repository's Dockerfile and pushes it to a registry.
|
||||||
|
# 2. Deploy: Runs the ansible-gitea playbook to deploy the newly built image to a target server.
|
||||||
|
|
||||||
|
name: Reusable CI/CD
|
||||||
|
|
||||||
|
# This workflow is triggered by a call from another workflow.
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
# ------------------ INPUTS ------------------
|
||||||
|
# These are parameters that the calling workflow must provide.
|
||||||
|
inputs:
|
||||||
|
# The name of the application/service being deployed.
|
||||||
|
# Used to find project-specific variables in ansible.
|
||||||
|
app_name:
|
||||||
|
description: 'The application name'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
# The deployment environment (e.g., staging, production).
|
||||||
|
deploy_env:
|
||||||
|
description: 'The deployment environment'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
# The user on the target server. If not provided, defaults to 'deploy_user'.
|
||||||
|
server_user:
|
||||||
|
description: 'User on the target server'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: 'deploy_user'
|
||||||
|
|
||||||
|
# ------------------ SECRETS ------------------
|
||||||
|
# These are secrets that the calling workflow must pass.
|
||||||
|
# They should be configured in the Gitea organization or repository settings.
|
||||||
|
secrets:
|
||||||
|
# Address of the target host(s) for Ansible.
|
||||||
|
ANSIBLE_HOSTS:
|
||||||
|
description: 'Target hosts for Ansible (e.g., "user@host")'
|
||||||
|
required: true
|
||||||
|
# SSH private key to connect to the Ansible target host.
|
||||||
|
SSH_PRIVATE_KEY:
|
||||||
|
description: 'SSH Private Key for Ansible'
|
||||||
|
required: true
|
||||||
|
# Password for the Ansible vault.
|
||||||
|
VAULT_PASSWORD:
|
||||||
|
description: 'Ansible Vault Password'
|
||||||
|
required: true
|
||||||
|
# Docker registry username.
|
||||||
|
DOCKER_USERNAME:
|
||||||
|
description: 'Docker Registry Username'
|
||||||
|
required: true
|
||||||
|
# Docker registry password or token.
|
||||||
|
DOCKER_PASSWORD:
|
||||||
|
description: 'Docker Registry Password'
|
||||||
|
required: true
|
||||||
|
# The URL of the Docker registry.
|
||||||
|
DOCKER_REGISTRY:
|
||||||
|
description: 'Docker Registry URL'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ============================================================================
|
||||||
|
# JOB 1: BUILD AND PUSH DOCKER IMAGE
|
||||||
|
# ============================================================================
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
image_tag: ${{ steps.meta.outputs.version }}
|
||||||
|
image_name: ${{ secrets.DOCKER_REGISTRY }}/${{ github.repository_owner }}/${{ inputs.app_name }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Docker Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ secrets.DOCKER_REGISTRY }}
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ secrets.DOCKER_REGISTRY }}/${{ github.repository_owner }}/${{ inputs.app_name }}
|
||||||
|
tags: |
|
||||||
|
type=sha,prefix=,format=long
|
||||||
|
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# JOB 2: DEPLOY WITH ANSIBLE
|
||||||
|
# ============================================================================
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build # This job runs only after the 'build' job succeeds.
|
||||||
|
steps:
|
||||||
|
- name: Checkout Application Code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Checkout Ansible Repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
# This should be the path to your ansible-gitea repository
|
||||||
|
repository: ${{ github.repository_owner }}/ansible-gitea
|
||||||
|
path: .ansible
|
||||||
|
|
||||||
|
- name: Set up Ansible and Dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y sshpass python3-pip
|
||||||
|
pip3 install ansible
|
||||||
|
ansible-galaxy collection install community.docker
|
||||||
|
|
||||||
|
- name: Setup SSH
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||||
|
chmod 600 ~/.ssh/id_rsa
|
||||||
|
# Disable strict host key checking for simplicity in CI
|
||||||
|
echo "StrictHostKeyChecking no" >> ~/.ssh/config
|
||||||
|
|
||||||
|
- name: Setup Ansible Vault
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.VAULT_PASSWORD }}" > .ansible/.vault_pass
|
||||||
|
|
||||||
|
- name: Run Ansible Playbook
|
||||||
|
working-directory: .ansible
|
||||||
|
run: |
|
||||||
|
ansible-playbook playbook.yml \
|
||||||
|
-i "${{ secrets.ANSIBLE_HOSTS }}," \
|
||||||
|
--extra-vars "app_name=${{ inputs.app_name }} \
|
||||||
|
deploy_env=${{ inputs.deploy_env }} \
|
||||||
|
server_user=${{ inputs.server_user }} \
|
||||||
|
git_repo=${{ github.server_url }}/${{ github.repository }} \
|
||||||
|
docker_image_tag=${{ needs.build.outputs.image_tag }} \
|
||||||
|
docker_image_name=${{ needs.build.outputs.image_name }} \
|
||||||
|
docker_image_full=${{ needs.build.outputs.image_name }}:${{ needs.build.outputs.image_tag }} \
|
||||||
|
docker_registry=${{ secrets.DOCKER_REGISTRY }}" \
|
||||||
|
--vault-password-file .vault_pass
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# Ansible
|
||||||
|
*.retry
|
||||||
|
.vault_pass
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Local testing
|
||||||
|
vagrant/
|
||||||
|
.vagrant/
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
/tmp/ansible_ssh_key
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,4 @@
|
|||||||
|
include:
|
||||||
|
- project: '$PROJECT_CI_ANSIBLE_PATH'
|
||||||
|
ref: 'main'
|
||||||
|
file: '.gitlab-ci.include.yml'
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[defaults]
|
||||||
|
host_key_checking = False
|
||||||
|
timeout = 30
|
||||||
|
deprecation_warnings = False
|
||||||
|
interpreter_python = auto_silent
|
||||||
|
|
||||||
|
[ssh_connection]
|
||||||
|
pipelining = True
|
||||||
|
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
---
|
||||||
|
port_registry_file: "group_vars/port-registry.yml"
|
||||||
|
|
||||||
|
# Thông tin triển khai
|
||||||
|
deploy_env: production # Môi trường triển khai: development hoặc production
|
||||||
|
app_name: "App" # Tên ứng dụng (sẽ được ghi đè)
|
||||||
|
app_port: 3000 # Port mặc định
|
||||||
|
node_env: "{{ deploy_env }}" # NODE_ENV
|
||||||
|
nodejs_version: "18" # Phiên bản Node.js mặc định
|
||||||
|
framework: expressjs # Framework (sẽ được ghi đè): expressjs, nestjs, nextjs, reactjs
|
||||||
|
|
||||||
|
# Thông tin Git
|
||||||
|
git_repo: "" # Repo URL (bắt buộc ghi đè)
|
||||||
|
git_tag: "latest" # Tag/branch để checkout
|
||||||
|
git_username: "" # Username để clone repo (nếu private)
|
||||||
|
git_token: "" # Token để clone repo (nếu private)
|
||||||
|
|
||||||
|
# Thông tin triển khai
|
||||||
|
deploy_strategy: "docker" # Phương thức triển khai (pm2, systemd, etc)
|
||||||
|
app_base_dir: "/var/www" # Thư mục cơ sở
|
||||||
|
app_dir: "{{ app_base_dir }}/{{ app_name }}-{{ deploy_env }}{{ env_tag }}"
|
||||||
|
deploy_dir: "{{ app_dir }}-{{ git_tag }}"
|
||||||
|
current_dir: "{{ app_dir }}-current"
|
||||||
|
keep_releases: 3 # Số lượng phiên bản cần giữ lại
|
||||||
|
|
||||||
|
# Các lệnh ứng dụng
|
||||||
|
app_install_cmd: "npm ci" # Lệnh cài đặt dependencies
|
||||||
|
app_build_cmd: "npm run build" # Lệnh build ứng dụng
|
||||||
|
app_start_cmd: "" # Lệnh khởi động ứng dụng
|
||||||
|
app_start_cmds: # Lệnh khởi động ứng dụng theo framework
|
||||||
|
expressjs: "./dist/main.js"
|
||||||
|
nestjs: "./dist/main.js"
|
||||||
|
reactjs: "serve"
|
||||||
|
nextjs: "node_modules/next/dist/bin/next"
|
||||||
|
app_start_args: # Tham so cho lệnh khởi động ứng dụng
|
||||||
|
expressjs: ""
|
||||||
|
nestjs: ""
|
||||||
|
reactjs: "-s build"
|
||||||
|
nextjs: "start"
|
||||||
|
#app_start_cmd_override_worker: "./dist/cli.js sync_queue" #override start command for worker
|
||||||
|
|
||||||
|
# Thông tin server
|
||||||
|
server_host: "" # Server IP/hostname (bắt buộc ghi đè)
|
||||||
|
server_port: "22" # Server Port
|
||||||
|
server_user: "" # Username SSH (bắt buộc ghi đè)
|
||||||
|
server_password: "" # Password SSH (để trống nếu dùng SSH key)
|
||||||
|
server_become_password: "" # Password sudo (để trống nếu dùng SSH key)
|
||||||
|
ssh_key_path: "" # Đường dẫn SSH private key file (để trống nếu dùng ssh_key_content)
|
||||||
|
ssh_key_content: "" # Nội dung SSH private key (từ GitLab CI/CD variables)
|
||||||
|
auth_method: "password" # Phương thức xác thực: "password" hoặc "ssh_key"
|
||||||
|
|
||||||
|
# Environment-specific overrides
|
||||||
|
environment_config:
|
||||||
|
development:
|
||||||
|
short_key: "dev"
|
||||||
|
log_level: "debug"
|
||||||
|
enable_swagger: true
|
||||||
|
enable_hot_reload: true
|
||||||
|
database_logging: true
|
||||||
|
pm2_instances: 1
|
||||||
|
pm2_max_memory: "1G"
|
||||||
|
production:
|
||||||
|
short_key: "prod"
|
||||||
|
log_level: "warn"
|
||||||
|
enable_swagger: false
|
||||||
|
enable_hot_reload: false
|
||||||
|
database_logging: false
|
||||||
|
pm2_instances: 1
|
||||||
|
pm2_max_memory: "2G"
|
||||||
|
|
||||||
|
# Path Configurations
|
||||||
|
entities_dir: "src/api/models"
|
||||||
|
migrations_dir: "src/database/migrations"
|
||||||
|
seeding_factories: "src/database/factories/**/*{.ts,.js}"
|
||||||
|
seeding_seeds: "src/database/seeds/**/*{.ts,.js}"
|
||||||
|
controllers_dir: "/api/controllers/**/*Controller{.ts,.js}"
|
||||||
|
cron_dir: "/api/cron-jobs/**/*Job{.ts,.js}"
|
||||||
|
middlewares_dir: "/infrastructure/middlewares/**/*{.ts,.js}"
|
||||||
|
subscribers_dir: "/api/subscribers/**/*Subscriber{.ts,.js}"
|
||||||
|
events_dir: "/api/events/**/*{.ts,.js}"
|
||||||
|
resolvers_dir: "/api/resolvers/**/*Resolver{.ts,.js}"
|
||||||
|
|
||||||
|
# Health Check Configuration
|
||||||
|
health_check:
|
||||||
|
enabled: false
|
||||||
|
endpoint: ""
|
||||||
|
timeout: 5000
|
||||||
|
interval: 30000
|
||||||
|
|
||||||
|
# Security Configuration
|
||||||
|
security:
|
||||||
|
cors_origins: "*"
|
||||||
|
helmet_enabled: true
|
||||||
|
rate_limit_enabled: true
|
||||||
|
request_size_limit: "50mb"
|
||||||
|
trust_proxy: true
|
||||||
|
|
||||||
|
# Custom NPM Scripts
|
||||||
|
custom_scripts:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Deployment Configuration
|
||||||
|
pm2_restart_delay: 5000
|
||||||
|
|
||||||
|
# Auto-scaling configuration
|
||||||
|
replicas: 1
|
||||||
|
max_replicas: 9
|
||||||
|
health_check_timeout: 30
|
||||||
|
deployment_timeout: 300
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# port-registry.yml
|
||||||
|
projects:
|
||||||
|
tarot-fe:
|
||||||
|
development: 3002
|
||||||
|
production: 3002
|
||||||
|
staging: 13002
|
||||||
|
|
||||||
|
tarot-be:
|
||||||
|
development: 3003
|
||||||
|
production: 3003
|
||||||
|
staging: 13003
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
# default resource_limits
|
||||||
|
resource_limits:
|
||||||
|
development:
|
||||||
|
memory: "2g"
|
||||||
|
cpu: "1.0"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
# default resource_limits
|
||||||
|
resource_limits:
|
||||||
|
production:
|
||||||
|
memory: "4g"
|
||||||
|
cpu: "2.0"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
all:
|
||||||
|
hosts:
|
||||||
|
dev_server:
|
||||||
|
ansible_host: "{{ server_host if server_host is defined and server_host != '' else '10.252.231.175' }}"
|
||||||
|
ansible_port: "{{ server_port if server_port is defined and server_port != '' else '22' }}"
|
||||||
|
ansible_user: "{{ server_user }}"
|
||||||
|
# Cấu hình SSH linh động - password hoặc SSH key
|
||||||
|
ansible_password: "{{ server_password if auth_method == 'password' else omit }}"
|
||||||
|
ansible_ssh_private_key_file: "{{ ssh_key_path if (auth_method == 'ssh_key' and ssh_key_path != '') else omit }}"
|
||||||
|
ansible_become_password: "{{ server_become_password if auth_method == 'password' else omit }}"
|
||||||
|
ansible_sudo_pass: "{{ server_become_password if auth_method == 'password' else omit }}"
|
||||||
|
vars:
|
||||||
|
# Load vault variables
|
||||||
|
# vault_file: "defaults/vault/development.yml"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
all:
|
||||||
|
hosts:
|
||||||
|
prod_server:
|
||||||
|
ansible_host: "{{ server_host if server_host is defined and server_host != '' else '10.252.252.10' }}"
|
||||||
|
ansible_port: "{{ server_port if server_port is defined and server_port != '' else '22' }}"
|
||||||
|
ansible_user: "{{ server_user }}"
|
||||||
|
# Cấu hình SSH linh động - password hoặc SSH key
|
||||||
|
ansible_password: "{{ server_password if auth_method == 'password' else omit }}"
|
||||||
|
ansible_ssh_private_key_file: "{{ ssh_key_path if (auth_method == 'ssh_key' and ssh_key_path != '') else omit }}"
|
||||||
|
ansible_become_password: "{{ server_become_password if auth_method == 'password' else omit }}"
|
||||||
|
ansible_sudo_pass: "{{ server_become_password if auth_method == 'password' else omit }}"
|
||||||
|
vars:
|
||||||
|
# Load vault variables
|
||||||
|
# vault_file: "defaults/vault/production.yml"
|
||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy Application with Auto-Scaling
|
||||||
|
hosts: "{{ target_hosts | default('all') }}"
|
||||||
|
become: yes
|
||||||
|
become_method: sudo
|
||||||
|
vars_files:
|
||||||
|
- group_vars/default.yml
|
||||||
|
- "group_vars/vault/{{ deploy_env }}.yml"
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# Deployment configuration
|
||||||
|
deploy_strategy: "{{ deploy_strategy | default('docker') }}"
|
||||||
|
docker_image_tag: "{{ docker_image_tag | default('latest') }}"
|
||||||
|
|
||||||
|
# Environment detection
|
||||||
|
is_production: "{{ deploy_env == 'production' }}"
|
||||||
|
is_staging: "{{ deploy_env == 'staging' }}"
|
||||||
|
is_development: "{{ deploy_env == 'development' }}"
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Validate required variables
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- app_name is defined and app_name != ""
|
||||||
|
- git_repo is defined and git_repo != ""
|
||||||
|
- server_user is defined and server_user != ""
|
||||||
|
- deploy_env is defined and deploy_env != ""
|
||||||
|
fail_msg: "Missing required variables: app_name, git_repo, server_user, and deploy_env must be defined"
|
||||||
|
|
||||||
|
- name: Validate auto-scaling configuration
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- replicas | int >= 1
|
||||||
|
- replicas | int <= max_replicas | int
|
||||||
|
- deployment_strategy in ['rolling', 'blue-green', 'recreate']
|
||||||
|
fail_msg: "Invalid auto-scaling configuration. Replicas must be between 1 and {{ max_replicas }}, and deployment_strategy must be one of: rolling, blue-green, recreate"
|
||||||
|
|
||||||
|
- name: Load project-specific variables
|
||||||
|
include_vars: "projects/{{ app_name }}/vars.yml"
|
||||||
|
when:
|
||||||
|
- lookup('file', 'projects/' + app_name + '/vars.yml', errors='ignore')
|
||||||
|
|
||||||
|
- name: Override deployment strategy from project vars
|
||||||
|
set_fact:
|
||||||
|
deploy_strategy: "{{ deploy_strategy | default('docker') }}"
|
||||||
|
|
||||||
|
- name: Ensure port registry exists
|
||||||
|
file:
|
||||||
|
path: "{{ port_registry_file }}"
|
||||||
|
state: touch
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Initialize port registry if empty
|
||||||
|
copy:
|
||||||
|
content: "projects: {}"
|
||||||
|
dest: "{{ port_registry_file }}"
|
||||||
|
force: no
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# ============================================================================
|
||||||
|
# DOCKER DEPLOYMENT WITH AUTO-SCALING
|
||||||
|
# ============================================================================
|
||||||
|
- name: Include docker role with auto-scaling
|
||||||
|
include_role:
|
||||||
|
name: docker
|
||||||
|
when: deploy_strategy in ['simple-docker', 'docker']
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# POST-DEPLOYMENT VERIFICATION
|
||||||
|
# ============================================================================
|
||||||
|
- name: Wait for deployment to settle
|
||||||
|
pause:
|
||||||
|
seconds: 10
|
||||||
|
when: deploy_strategy in ['simple-docker', 'docker']
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CLEANUP AND MAINTENANCE
|
||||||
|
# ============================================================================
|
||||||
|
- name: Cleanup old Docker images
|
||||||
|
shell: |
|
||||||
|
sudo /home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh clean
|
||||||
|
become_user: "{{ server_user }}"
|
||||||
|
when:
|
||||||
|
- deploy_strategy in ['simple-docker', 'docker']
|
||||||
|
- cleanup_old_images | default(true)
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DEPLOYMENT SUMMARY
|
||||||
|
# ============================================================================
|
||||||
|
- name: Generate deployment summary
|
||||||
|
set_fact:
|
||||||
|
deployment_summary:
|
||||||
|
application: "{{ app_name }}"
|
||||||
|
environment: "{{ deploy_env }}{{ env_tag | default('') }}"
|
||||||
|
version: "{{ docker_image_tag }}"
|
||||||
|
replicas: "{{ replicas }}"
|
||||||
|
strategy: "{{ deployment_strategy }}"
|
||||||
|
deploy_strategy: "{{ deploy_strategy }}"
|
||||||
|
timestamp: "{{ ansible_date_time.iso8601 }}"
|
||||||
|
duration: "{{ ansible_play_duration | default('unknown') }}"
|
||||||
|
|
||||||
|
- name: Display deployment summary
|
||||||
|
debug:
|
||||||
|
msg:
|
||||||
|
- "🎉 DEPLOYMENT SUMMARY"
|
||||||
|
- "===================="
|
||||||
|
- "Application: {{ deployment_summary.application }}"
|
||||||
|
- "Environment: {{ deployment_summary.environment }}"
|
||||||
|
- "Version: {{ deployment_summary.version }}"
|
||||||
|
- "Replicas: {{ deployment_summary.replicas }}"
|
||||||
|
- "Strategy: {{ deployment_summary.strategy }}"
|
||||||
|
- "Timestamp: {{ deployment_summary.timestamp }}"
|
||||||
|
|
||||||
|
- name: Save deployment record
|
||||||
|
copy:
|
||||||
|
content: "{{ deployment_summary | to_nice_yaml }}"
|
||||||
|
dest: "/home/{{ server_user }}/ci/logs/{{ app_name }}/{{ deploy_env }}/deployment-{{ ansible_date_time.epoch }}.yml"
|
||||||
|
when: save_deployment_record | default(true)
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HANDLERS
|
||||||
|
# ============================================================================
|
||||||
|
handlers:
|
||||||
|
- name: reload nginx
|
||||||
|
service:
|
||||||
|
name: nginx
|
||||||
|
state: reloaded
|
||||||
|
when: ansible_service_mgr == "systemd"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# POST TASKS
|
||||||
|
# ============================================================================
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
# Docker configuration
|
||||||
|
service_name: "{{ app_name }}-{{ deploy_env }}{{ env_tag }}"
|
||||||
|
#docker_registry: "{{ ansible_env.CI_REGISTRY | default('gitlab.forlife.one') }}"
|
||||||
|
#docker_project_path: "{{ ansible_env.CI_PROJECT_PATH | default('') }}"
|
||||||
|
#docker_image_tag: "{{ ansible_env.CI_COMMIT_TAG | default('latest') }}"
|
||||||
|
#docker_image_full: "{{ ansible_env.CI_REGISTRY_IMAGE }}:{ ansible_env.CI_COMMIT_TAG | default(docker_image_tag) }"
|
||||||
|
#docker_image_name: "{{ ansible_env.CI_REGISTRY_IMAGE }}"
|
||||||
|
docker_image_rollback: "{{ app_name }}:{{ previous_version.stdout | default('latest') }}"
|
||||||
|
|
||||||
|
# Directory configuration
|
||||||
|
deploy_dir: "{{ '/home/' + server_user + '/ci/deployments/' + app_name + '/' + deploy_env }}"
|
||||||
|
current_dir: "{{ '/home/' + server_user + '/ci/deployments/' + app_name + '/current' }}"
|
||||||
|
|
||||||
|
# GitLab CI integration
|
||||||
|
#gitlab_ci_user: "{{ ansible_env.CI_REGISTRY_USER | default('gitlab-ci-token') }}"
|
||||||
|
|
||||||
|
# Health Check Configuration
|
||||||
|
health_path: ""
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# NGINX HANDLERS FOR AUTO-SCALING DEPLOYMENT
|
||||||
|
# ============================================================================
|
||||||
|
# if nginx install by aaPanel: run command > mkdir -p /etc/nginx/conf.d/
|
||||||
|
# if nginx install by aaPanel: run command > ln -fs /www/server/nginx/sbin/nginx /usr/bin/nginx
|
||||||
|
# if nginx install by aaPanel: run command > echo "map $http_upgrade $connection_upgrade { default upgrade; '' close; }" > /www/server/panel/vhost/nginx/0.websocket.conf
|
||||||
|
# if nginx install by aaPanel: run command > echo "include /etc/nginx/conf.d/*.conf;" > /www/server/panel/vhost/nginx/forlife-upstream.conf
|
||||||
|
|
||||||
|
- name: test nginx configuration
|
||||||
|
command: nginx -t
|
||||||
|
become: yes
|
||||||
|
listen: test nginx configuration
|
||||||
|
|
||||||
|
- name: reload nginx
|
||||||
|
shell: |
|
||||||
|
# Test nginx configuration
|
||||||
|
nginx -t && /etc/init.d/nginx reload
|
||||||
|
|
||||||
|
# Wait for reload
|
||||||
|
sleep 3
|
||||||
|
become: yes
|
||||||
|
when: ansible_service_mgr == "systemd"
|
||||||
|
listen: reload nginx
|
||||||
|
|
||||||
|
- name: webapplication purge cache
|
||||||
|
shell: |
|
||||||
|
filepath="/www/server/scripts/cloudflare-purge-cache.sh"
|
||||||
|
if [ -f "$filepath" ]; then bash "$filepath"; fi
|
||||||
|
become: yes
|
||||||
|
when: ansible_service_mgr == "systemd"
|
||||||
|
listen: webapplication purge cache
|
||||||
|
|
||||||
|
- name: restart nginx
|
||||||
|
shell: |
|
||||||
|
# Test nginx configuration
|
||||||
|
nginx -t && /etc/init.d/nginx restart
|
||||||
|
|
||||||
|
# Wait for reload
|
||||||
|
sleep 3
|
||||||
|
become: yes
|
||||||
|
when: ansible_service_mgr == "systemd"
|
||||||
|
listen: restart nginx
|
||||||
|
|
||||||
|
- name: validate nginx upstream
|
||||||
|
shell: |
|
||||||
|
# Test nginx configuration
|
||||||
|
nginx -t && /etc/init.d/nginx reload
|
||||||
|
|
||||||
|
# Wait for reload
|
||||||
|
sleep 3
|
||||||
|
become: yes
|
||||||
|
ignore_errors: true
|
||||||
|
listen: validate nginx upstream
|
||||||
|
|
||||||
|
# service:
|
||||||
|
# name: nginx
|
||||||
|
# state: reloaded
|
||||||
|
# become: yes
|
||||||
|
# when: ansible_service_mgr == "systemd"
|
||||||
|
# listen: reload nginx
|
||||||
|
|
||||||
|
#- name: restart nginx
|
||||||
|
# service:
|
||||||
|
# name: nginx
|
||||||
|
# state: restarted
|
||||||
|
# become: yes
|
||||||
|
# when: ansible_service_mgr == "systemd"
|
||||||
|
# listen: restart nginx
|
||||||
|
|
||||||
|
#- name: validate nginx upstream
|
||||||
|
# shell: |
|
||||||
|
# # Test nginx configuration
|
||||||
|
# nginx -t && nginx -s reload
|
||||||
|
#
|
||||||
|
# # Wait for reload
|
||||||
|
# sleep 3
|
||||||
|
#
|
||||||
|
# # Test upstream connectivity
|
||||||
|
# if command -v curl >/dev/null 2>&1; then
|
||||||
|
# echo "Testing upstream connectivity..."
|
||||||
|
# curl -f -s --max-time 10 http://localhost/health || echo "⚠️ Upstream health check skipped"
|
||||||
|
# fi
|
||||||
|
# become: yes
|
||||||
|
# ignore_errors: true
|
||||||
|
# listen: validate nginx upstream
|
||||||
@@ -0,0 +1,650 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# ENHANCED DOCKER DEPLOYMENT WITH AUTO-SCALING SUPPORT
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# SYSTEM VALIDATION AND REQUIREMENTS CHECK
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Validate deployment environment
|
||||||
|
debug:
|
||||||
|
msg:
|
||||||
|
- "=== AUTO-SCALING DEPLOYMENT VALIDATION ==="
|
||||||
|
- "Target OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
|
||||||
|
- "Architecture: {{ ansible_architecture }}"
|
||||||
|
- "Available memory: {{ ansible_memtotal_mb }}MB"
|
||||||
|
- "Available disk: {{ ansible_mounts[0].size_available / 1024 / 1024 / 1024 | round(2) }}GB"
|
||||||
|
- "Target replicas: {{ docker_replicas | default(replicas | default(1)) }}"
|
||||||
|
- "Deployment strategy: {{ docker_deployment_strategy | default(deployment_strategy | default('rolling')) }}"
|
||||||
|
|
||||||
|
- name: Check minimum system requirements for auto-scaling
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- ansible_memtotal_mb >= (docker_replicas | default(1) | int * 512)
|
||||||
|
- (ansible_mounts[0].size_available / 1024 / 1024 / 1024) >= 5
|
||||||
|
- docker_replicas | default(1) | int <= docker_max_replicas | default(9) | int
|
||||||
|
fail_msg: |
|
||||||
|
Insufficient system resources for {{ docker_replicas | default(1) }} replicas.
|
||||||
|
Need at least {{ docker_replicas | default(1) | int * 512 }}MB RAM and 5GB disk space.
|
||||||
|
Replicas ({{ docker_replicas | default(1) }}) cannot exceed max_replicas ({{ docker_max_replicas | default(9) }}).
|
||||||
|
success_msg: "✅ System requirements met for auto-scaling deployment."
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# PROJECT-SPECIFIC CONFIGURATION LOADING
|
||||||
|
# ============================================================================
|
||||||
|
- name: Load project-specific vars if exists
|
||||||
|
block:
|
||||||
|
- name: Check if project-specific variables file exists
|
||||||
|
stat:
|
||||||
|
path: "projects/{{ app_name }}/vars.yml"
|
||||||
|
register: project_vars_file
|
||||||
|
|
||||||
|
- name: Load project-specific variables
|
||||||
|
include_vars:
|
||||||
|
file: "projects/{{ app_name }}/vars.yml"
|
||||||
|
when: project_vars_file.stat.exists
|
||||||
|
|
||||||
|
- name: Check if project-specific variables by environment file exists
|
||||||
|
stat:
|
||||||
|
path: "projects/{{ app_name }}/group_vars/{{ deploy_env }}.yml"
|
||||||
|
register: project_vars_env_file
|
||||||
|
|
||||||
|
- name: Load project-specific variables by environment
|
||||||
|
include_vars:
|
||||||
|
file: "projects/{{ app_name }}/group_vars/{{ deploy_env }}.yml"
|
||||||
|
when: project_vars_env_file.stat.exists
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Load port registry
|
||||||
|
include_vars:
|
||||||
|
file: "{{ port_registry_file | default('group_vars/port_registry.yml') }}"
|
||||||
|
name: port_registry
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
when: port_registry_file is defined
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DEPLOYMENT CONFIGURATION SETUP
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Set deployment environment tag
|
||||||
|
set_fact:
|
||||||
|
deploy_env_tag: "{{ deploy_env }}{{ env_tag | default('') }}"
|
||||||
|
|
||||||
|
- name: Set auto-scaling configuration
|
||||||
|
set_fact:
|
||||||
|
# Auto-scaling parameters
|
||||||
|
final_replicas: "{{ docker_replicas | default(replicas | default(1)) }}"
|
||||||
|
final_max_replicas: "{{ docker_max_replicas | default(max_replicas | default(9)) }}"
|
||||||
|
final_deployment_strategy: "{{ docker_deployment_strategy | default(deployment_strategy | default('rolling')) }}"
|
||||||
|
final_health_check_timeout: "{{ docker_health_check_timeout | default(health_check_timeout | default(30)) }}"
|
||||||
|
final_deployment_timeout: "{{ docker_deployment_timeout | default(deployment_timeout | default(300)) }}"
|
||||||
|
|
||||||
|
# Port configuration
|
||||||
|
final_app_port: "{{ port_registry.projects[app_name][deploy_env_tag] | default(3000) }}"
|
||||||
|
final_container_port: "{{ container_port | default(3000) }}"
|
||||||
|
|
||||||
|
# Resource configuration
|
||||||
|
final_memory_limit: "{{ container_memory_limit | default(resource_limits[deploy_env].memory | default('1g')) }}"
|
||||||
|
final_cpu_limit: "{{ container_cpu_limit | default(resource_limits[deploy_env].cpu | default('1.0')) }}"
|
||||||
|
|
||||||
|
# Network configuration
|
||||||
|
final_docker_network: "{{ docker_network_name | default(app_name + '-' + deploy_env + '-network') }}"
|
||||||
|
final_restart_policy: "{{ container_restart_policy | default('unless-stopped') }}"
|
||||||
|
|
||||||
|
- name: Display auto-scaling deployment configuration
|
||||||
|
debug:
|
||||||
|
msg:
|
||||||
|
- "🚀 AUTO-SCALING DEPLOYMENT CONFIGURATION"
|
||||||
|
- "========================================"
|
||||||
|
- "Application: {{ app_name }}"
|
||||||
|
- "Environment: {{ deploy_env_tag }}"
|
||||||
|
- "Framework: {{ framework | default('unknown') }}"
|
||||||
|
- "Docker Image: {{ docker_image_name }}:{{ docker_image_tag }}"
|
||||||
|
- "Target Replicas: {{ final_replicas }}"
|
||||||
|
- "Max Replicas: {{ final_max_replicas }}"
|
||||||
|
- "Deployment Strategy: {{ final_deployment_strategy }}"
|
||||||
|
- "Base Port: {{ final_app_port }} (Pattern: {{ final_app_port }}{{ replica_number | default(1) }})"
|
||||||
|
- "Container Port: {{ final_container_port }}"
|
||||||
|
- "Memory Limit: {{ final_memory_limit }}"
|
||||||
|
- "CPU Limit: {{ final_cpu_limit }}"
|
||||||
|
- "Health Check Timeout: {{ final_health_check_timeout }}s"
|
||||||
|
- "Deployment Timeout: {{ final_deployment_timeout }}s"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# PRE-DEPLOYMENT VALIDATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Validate auto-scaling deployment parameters
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- app_name is defined and app_name != ""
|
||||||
|
- deploy_env in ['development', 'staging', 'production']
|
||||||
|
- docker_image_tag is defined and docker_image_tag != ""
|
||||||
|
- final_replicas | int >= 1
|
||||||
|
- final_replicas | int <= final_max_replicas | int
|
||||||
|
- final_deployment_strategy in ['rolling', 'blue-green', 'recreate']
|
||||||
|
- docker_registry is defined
|
||||||
|
fail_msg: "Missing or invalid auto-scaling deployment parameters"
|
||||||
|
success_msg: "✅ Auto-scaling deployment parameters validated"
|
||||||
|
tags: [validate]
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DIRECTORY AND ENVIRONMENT SETUP
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Create deployment directories
|
||||||
|
file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ server_user }}"
|
||||||
|
group: "{{ server_user }}"
|
||||||
|
mode: '0755'
|
||||||
|
loop:
|
||||||
|
- "/home/{{ server_user }}/scripts"
|
||||||
|
- "/home/{{ server_user }}/ci"
|
||||||
|
- "/home/{{ server_user }}/ci/configs/{{ app_name }}/{{ deploy_env }}"
|
||||||
|
- "/home/{{ server_user }}/ci/data/{{ app_name }}/{{ deploy_env }}"
|
||||||
|
- "/home/{{ server_user }}/ci/logs/{{ app_name }}/{{ deploy_env }}"
|
||||||
|
- "/home/{{ server_user }}/ci/backups/{{ app_name }}/{{ deploy_env }}"
|
||||||
|
- "/home/{{ server_user }}/ci/deployments/{{ app_name }}/{{ deploy_env }}"
|
||||||
|
|
||||||
|
- name: Create environment file from vault if secrets_dict is defined
|
||||||
|
copy:
|
||||||
|
dest: "/home/{{ server_user }}/ci/configs/{{ app_name }}/{{ deploy_env }}/.env"
|
||||||
|
content: |
|
||||||
|
{% for key, value in secrets_dict.items() %}
|
||||||
|
{{ key }}={{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
when: secrets_dict is defined
|
||||||
|
|
||||||
|
- name: Load project-specific env if exists
|
||||||
|
block:
|
||||||
|
- name: Check if project-specific .env template exists by environment
|
||||||
|
stat:
|
||||||
|
path: "projects/{{ app_name }}/templates/.env.{{ deploy_env }}.j2"
|
||||||
|
register: custom_env_template_by_env
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Create environment file from project template by environment
|
||||||
|
template:
|
||||||
|
src: "projects/{{ app_name }}/templates/.env.{{ deploy_env }}.j2"
|
||||||
|
dest: "/home/{{ server_user }}/ci/configs/{{ app_name }}/{{ deploy_env }}/.env"
|
||||||
|
owner: "{{ server_user }}"
|
||||||
|
group: "{{ server_user }}"
|
||||||
|
mode: '0600'
|
||||||
|
backup: yes
|
||||||
|
when: env_file_enabled | default(true) and custom_env_template_by_env.stat.exists
|
||||||
|
|
||||||
|
- name: Check if project-specific .env template exists
|
||||||
|
stat:
|
||||||
|
path: "projects/{{ app_name }}/templates/.env.j2"
|
||||||
|
register: custom_env_template
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Create environment file from project template
|
||||||
|
template:
|
||||||
|
src: "projects/{{ app_name }}/templates/.env.j2"
|
||||||
|
dest: "/home/{{ server_user }}/ci/configs/{{ app_name }}/{{ deploy_env }}/.env"
|
||||||
|
owner: "{{ server_user }}"
|
||||||
|
group: "{{ server_user }}"
|
||||||
|
mode: '0600'
|
||||||
|
backup: yes
|
||||||
|
when: env_file_enabled | default(true) and custom_env_template.stat.exists and not custom_env_template_by_env.stat.exists
|
||||||
|
|
||||||
|
- name: Create environment file with default template
|
||||||
|
template:
|
||||||
|
src: .env.j2
|
||||||
|
dest: "/home/{{ server_user }}/ci/configs/{{ app_name }}/{{ deploy_env }}/.env"
|
||||||
|
owner: "{{ server_user }}"
|
||||||
|
group: "{{ server_user }}"
|
||||||
|
mode: '0600'
|
||||||
|
backup: yes
|
||||||
|
when: not custom_env_template_by_env.stat.exists and not custom_env_template.stat.exists
|
||||||
|
vars:
|
||||||
|
# Pass auto-scaling configuration to template
|
||||||
|
auto_scaling_config:
|
||||||
|
replicas: "{{ final_replicas }}"
|
||||||
|
max_replicas: "{{ final_max_replicas }}"
|
||||||
|
deployment_strategy: "{{ final_deployment_strategy }}"
|
||||||
|
base_port: "{{ final_app_port }}"
|
||||||
|
container_port: "{{ final_container_port }}"
|
||||||
|
tags: [environment]
|
||||||
|
when: secrets_dict is not defined
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DOCKER ENVIRONMENT SETUP
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Setup Docker environment for auto-scaling
|
||||||
|
block:
|
||||||
|
- name: Check if Docker is installed
|
||||||
|
shell: |
|
||||||
|
command -v docker
|
||||||
|
register: docker_check
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Install Docker (Debian/Ubuntu)
|
||||||
|
apt:
|
||||||
|
name: docker.io
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
become: yes
|
||||||
|
when:
|
||||||
|
- docker_check.rc != 0
|
||||||
|
- ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Install Docker (CentOS/RHEL)
|
||||||
|
yum:
|
||||||
|
name: docker
|
||||||
|
state: present
|
||||||
|
become: yes
|
||||||
|
when:
|
||||||
|
- docker_check.rc != 0
|
||||||
|
- ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
# Kiểm tra xem Python đã có Docker SDK chưa
|
||||||
|
- name: Check if Docker SDK for Python is installed
|
||||||
|
command: python3 -c "import docker"
|
||||||
|
register: docker_sdk_check
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
# Cài Docker SDK bằng pip nếu chưa có
|
||||||
|
- name: Install Docker SDK for Python via apt (nếu không muốn dùng pip)
|
||||||
|
apt:
|
||||||
|
name: python3-docker
|
||||||
|
state: present
|
||||||
|
become: yes
|
||||||
|
when:
|
||||||
|
- ansible_os_family == "Debian"
|
||||||
|
- docker_sdk_check.rc != 0
|
||||||
|
|
||||||
|
- name: Ensure Docker service is started and enabled
|
||||||
|
service:
|
||||||
|
name: docker
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# - name: Create Docker network for auto-scaling deployment
|
||||||
|
# docker_network:
|
||||||
|
# name: "{{ final_docker_network }}"
|
||||||
|
# state: present
|
||||||
|
# driver: bridge
|
||||||
|
# ipam_config:
|
||||||
|
# - subnet: "172.{{ (final_app_port | string)[-2:] | int + 20 }}.0.0/16"
|
||||||
|
|
||||||
|
- name: Gather Docker networks on host
|
||||||
|
shell: docker network inspect $(docker network ls -q) 2>/dev/null || echo '[]'
|
||||||
|
register: docker_networks_raw
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Parse Docker networks JSON
|
||||||
|
set_fact:
|
||||||
|
docker_networks_info: "{{ docker_networks_raw.stdout | from_json }}"
|
||||||
|
|
||||||
|
- name: Extract existing subnets (as strings) from docker networks
|
||||||
|
set_fact:
|
||||||
|
existing_subnets: >-
|
||||||
|
{{
|
||||||
|
docker_networks_info | default([])
|
||||||
|
| selectattr('IPAM', 'defined')
|
||||||
|
| selectattr('IPAM.Config', 'defined')
|
||||||
|
| map(attribute='IPAM.Config')
|
||||||
|
| select
|
||||||
|
| map('first')
|
||||||
|
| selectattr('Subnet', 'defined')
|
||||||
|
| map(attribute='Subnet')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Debug existing subnets
|
||||||
|
debug:
|
||||||
|
var: existing_subnets
|
||||||
|
|
||||||
|
- name: Get used second-octets from existing subnets (172.X.0.0/16 only)
|
||||||
|
set_fact:
|
||||||
|
used_seconds: >-
|
||||||
|
{%- set result = [] -%}
|
||||||
|
{%- for subnet in existing_subnets -%}
|
||||||
|
{%- if subnet.startswith('172.') -%}
|
||||||
|
{%- set parts = subnet.split('.') -%}
|
||||||
|
{%- if parts | length >= 2 -%}
|
||||||
|
{%- set _ = result.append(parts[1] | int) -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
{{ result }}
|
||||||
|
|
||||||
|
- name: Debug used second octets
|
||||||
|
debug:
|
||||||
|
var: used_seconds
|
||||||
|
|
||||||
|
- name: Compute start octet from final_app_port (bounded 20..250)
|
||||||
|
set_fact:
|
||||||
|
start_octet: >-
|
||||||
|
{{
|
||||||
|
(((final_app_port | string)[-2:] | int) + 20)
|
||||||
|
if (((final_app_port | string)[-2:] | int) + 20) <= 250
|
||||||
|
else 250
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Build candidate second-octet list (start -> 250 then 20 -> start-1)
|
||||||
|
set_fact:
|
||||||
|
candidate_list: >-
|
||||||
|
{{
|
||||||
|
(range(start_octet | int, 251) | list) + (range(20, start_octet | int) | list)
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Debug candidate list
|
||||||
|
debug:
|
||||||
|
msg: "Candidates: {{ candidate_list }}, Used: {{ used_seconds }}"
|
||||||
|
|
||||||
|
- name: Select first unused second-octet
|
||||||
|
set_fact:
|
||||||
|
chosen_second: >-
|
||||||
|
{{
|
||||||
|
candidate_list
|
||||||
|
| map('int')
|
||||||
|
| reject('in', used_seconds)
|
||||||
|
| list
|
||||||
|
| first
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Fail if couldn't find free subnet octet
|
||||||
|
fail:
|
||||||
|
msg: "No available 172.X.0.0/16 subnet found in candidate space (20..250). Existing used: {{ used_seconds }}"
|
||||||
|
when: chosen_second is not defined
|
||||||
|
|
||||||
|
- name: Build chosen subnet
|
||||||
|
set_fact:
|
||||||
|
chosen_subnet: "172.{{ chosen_second }}.0.0/16"
|
||||||
|
|
||||||
|
- name: Debug chosen subnet
|
||||||
|
debug:
|
||||||
|
msg: "Creating Docker network '{{ final_docker_network }}' with subnet {{ chosen_subnet }}"
|
||||||
|
|
||||||
|
- name: Create Docker network for auto-scaling deployment
|
||||||
|
community.docker.docker_network:
|
||||||
|
name: "{{ final_docker_network }}"
|
||||||
|
state: present
|
||||||
|
driver: bridge
|
||||||
|
ipam_config:
|
||||||
|
- subnet: "{{ chosen_subnet }}"
|
||||||
|
become: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- name: Pull Docker image for auto-scaling deployment
|
||||||
|
docker_image:
|
||||||
|
name: "{{ docker_image_full | default(docker_image_name + ':' + docker_image_tag) }}"
|
||||||
|
source: pull
|
||||||
|
force_source: true
|
||||||
|
register: docker_pull_result
|
||||||
|
|
||||||
|
tags: [docker]
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ENHANCED DOCKER MANAGEMENT SCRIPT GENERATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Generate enhanced Docker management script with auto-scaling
|
||||||
|
template:
|
||||||
|
src: manage-docker.sh.j2
|
||||||
|
dest: "/home/{{ server_user }}/scripts/manage-{{ service_name }}.sh"
|
||||||
|
owner: "{{ server_user }}"
|
||||||
|
group: "{{ server_user }}"
|
||||||
|
mode: '0755'
|
||||||
|
backup: yes
|
||||||
|
vars:
|
||||||
|
# Container configuration
|
||||||
|
server_user: "{{ server_user }}"
|
||||||
|
container_name: "{{ service_name }}"
|
||||||
|
framework: "{{ framework }}"
|
||||||
|
|
||||||
|
# Auto-scaling configuration
|
||||||
|
default_replicas: "{{ final_replicas }}"
|
||||||
|
max_replicas: "{{ final_max_replicas }}"
|
||||||
|
health_check_timeout: "{{ final_health_check_timeout }}"
|
||||||
|
deployment_timeout: "{{ final_deployment_timeout }}"
|
||||||
|
|
||||||
|
# Port configuration
|
||||||
|
app_port: "{{ final_app_port }}"
|
||||||
|
container_port: "{{ final_container_port }}"
|
||||||
|
|
||||||
|
# Resource configuration
|
||||||
|
container_restart_policy: "{{ final_restart_policy }}"
|
||||||
|
container_memory_limit: "{{ final_memory_limit }}"
|
||||||
|
container_cpu_limit: "{{ final_cpu_limit }}"
|
||||||
|
|
||||||
|
# Network configuration
|
||||||
|
docker_network_name: "{{ final_docker_network }}"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# AUTO-SCALING DEPLOYMENT EXECUTION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Check current deployment state
|
||||||
|
shell: |
|
||||||
|
if [ -f "/home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh" ]; then
|
||||||
|
sudo /home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh status 2>/dev/null || echo "no-containers"
|
||||||
|
else
|
||||||
|
echo "no-script"
|
||||||
|
fi
|
||||||
|
become_user: "{{ server_user }}"
|
||||||
|
register: current_deployment_state
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Display current deployment state
|
||||||
|
debug:
|
||||||
|
msg: "{{ current_deployment_state.stdout_lines }}"
|
||||||
|
when: current_deployment_state.stdout_lines is defined
|
||||||
|
|
||||||
|
- name: Ensure Docker is installed and user has permissions
|
||||||
|
block:
|
||||||
|
- name: Add user to docker group
|
||||||
|
user:
|
||||||
|
name: "{{ server_user }}"
|
||||||
|
groups: docker
|
||||||
|
append: yes
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
- name: Reset SSH connection for group changes
|
||||||
|
meta: reset_connection
|
||||||
|
|
||||||
|
- name: Test Docker access
|
||||||
|
command: docker version
|
||||||
|
register: docker_test
|
||||||
|
failed_when: docker_test.rc != 0
|
||||||
|
|
||||||
|
- name: Execute smart deployment with auto-scaling
|
||||||
|
shell: |
|
||||||
|
sudo /home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh deploy {{ replicas | default(1) }} {{ docker_image_tag }} rolling
|
||||||
|
async: 600
|
||||||
|
poll: 10 # Poll every 10 seconds instead of 0
|
||||||
|
register: deployment_result
|
||||||
|
become_user: "{{ server_user }}"
|
||||||
|
|
||||||
|
- name: Check deployment result
|
||||||
|
fail:
|
||||||
|
msg: "Deployment failed: {{ deployment_result.stderr | default('Unknown error') }}"
|
||||||
|
when:
|
||||||
|
- deployment_result.rc is defined
|
||||||
|
- deployment_result.rc != 0
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# POST-DEPLOYMENT VERIFICATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Wait for auto-scaling deployment to settle
|
||||||
|
pause:
|
||||||
|
seconds: 5
|
||||||
|
tags: [health]
|
||||||
|
|
||||||
|
- name: Verify auto-scaling deployment completion
|
||||||
|
shell: |
|
||||||
|
sudo /home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh health
|
||||||
|
become_user: "{{ server_user }}"
|
||||||
|
register: auto_scaling_health_check
|
||||||
|
retries: 5
|
||||||
|
delay: 15
|
||||||
|
until: auto_scaling_health_check.rc == 0
|
||||||
|
tags: [health]
|
||||||
|
|
||||||
|
- name: Get final auto-scaling deployment status
|
||||||
|
shell: |
|
||||||
|
sudo /home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh status
|
||||||
|
become_user: "{{ server_user }}"
|
||||||
|
register: final_auto_scaling_status
|
||||||
|
changed_when: false
|
||||||
|
tags: [health]
|
||||||
|
|
||||||
|
- name: Extract auto-scaling deployment metrics
|
||||||
|
set_fact:
|
||||||
|
auto_scaling_metrics:
|
||||||
|
running_containers: "{{ final_auto_scaling_status.stdout | regex_search('Running Containers: (\\d+)', '\\1') | first | default('0') }}"
|
||||||
|
current_version: "{{ final_auto_scaling_status.stdout | regex_search('Current Version: ([^\\n]+)', '\\1') | first | default('unknown') }}"
|
||||||
|
base_port: "{{ final_auto_scaling_status.stdout | regex_search('Base Port: (\\d+)', '\\1') | first | default('0') }}"
|
||||||
|
|
||||||
|
# - name: Validate auto-scaling deployment metrics
|
||||||
|
# assert:
|
||||||
|
# that:
|
||||||
|
# - auto_scaling_metrics.running_containers | int == final_replicas | int
|
||||||
|
# - auto_scaling_metrics.current_version == docker_image_tag
|
||||||
|
# fail_msg: |
|
||||||
|
# Auto-scaling deployment validation failed:
|
||||||
|
# Expected: {{ final_replicas }} replicas with version {{ docker_image_tag }}
|
||||||
|
# Actual: {{ auto_scaling_metrics.running_containers }} replicas with version {{ auto_scaling_metrics.current_version }}
|
||||||
|
# success_msg: "✅ Auto-scaling deployment validation passed"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# LOAD BALANCER INTEGRATION
|
||||||
|
# ============================================================================
|
||||||
|
- name: Get active ports from script
|
||||||
|
shell: |
|
||||||
|
sudo /home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh deploy_active_ports
|
||||||
|
become_user: "{{ server_user }}"
|
||||||
|
register: active_ports_result
|
||||||
|
when: env_tag | default('') != '-worker'
|
||||||
|
|
||||||
|
- name: Convert ports to clean list
|
||||||
|
set_fact:
|
||||||
|
active_ports_list: "{{ active_ports_result.stdout.split() }}"
|
||||||
|
when: env_tag | default('') != '-worker' and active_ports_result.stdout | length > 0
|
||||||
|
|
||||||
|
- name: Generate nginx upstream configuration
|
||||||
|
template:
|
||||||
|
src: nginx-upstream.conf.j2
|
||||||
|
dest: "/etc/nginx/conf.d/{{ app_name }}-{{ deploy_env }}-upstream.conf"
|
||||||
|
backup: yes
|
||||||
|
vars:
|
||||||
|
upstream_name: "{{ app_name | regex_replace('[^A-Za-z0-9_]', '_') }}_{{ deploy_env }}_backend"
|
||||||
|
base_port: "{{ final_app_port }}"
|
||||||
|
replica_count: "{{ final_replicas }}"
|
||||||
|
load_balancing_method: "{{ nginx_config.load_balancing_method | default('least_conn') }}"
|
||||||
|
max_fails: "{{ nginx_config.max_fails | default(1) }}"
|
||||||
|
fail_timeout: "{{ nginx_config.fail_timeout | default(10) }}"
|
||||||
|
active_ports: "{{ active_ports_list }}"
|
||||||
|
when:
|
||||||
|
- update_load_balancer | default(true)
|
||||||
|
- env_tag | default('') != '-worker'
|
||||||
|
notify:
|
||||||
|
- test nginx configuration
|
||||||
|
- reload nginx
|
||||||
|
- webapplication purge cache
|
||||||
|
tags: [loadbalancer]
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DEPLOYMENT SUCCESS SUMMARY
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Display auto-scaling deployment success
|
||||||
|
debug:
|
||||||
|
msg:
|
||||||
|
- "🎉 AUTO-SCALING DEPLOYMENT SUCCESS"
|
||||||
|
- "=================================="
|
||||||
|
- "✅ Successfully deployed {{ app_name }} ({{ framework | default('unknown') }})"
|
||||||
|
- "🔢 Replicas: {{ auto_scaling_metrics.running_containers }}/{{ final_replicas }}"
|
||||||
|
- "🏷️ Version: {{ auto_scaling_metrics.current_version }}"
|
||||||
|
- "🚀 Strategy: {{ final_deployment_strategy }}"
|
||||||
|
- "🌐 Base Port: {{ auto_scaling_metrics.base_port }}"
|
||||||
|
- "🏥 Health Status: All containers healthy"
|
||||||
|
- "⚖️ Load Balancer: Updated with {{ final_replicas }} targets"
|
||||||
|
- ""
|
||||||
|
- "🔧 Management Commands:"
|
||||||
|
- " Status: ./scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh status"
|
||||||
|
- " Scale: ./scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh scale <replicas>"
|
||||||
|
- " Logs: ./scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh logs"
|
||||||
|
- " Health: ./scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh health"
|
||||||
|
tags: [info]
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CLEANUP AND MAINTENANCE
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Cleanup old Docker images (keep last 5 versions)
|
||||||
|
shell: |
|
||||||
|
# Enhanced cleanup for auto-scaling deployment
|
||||||
|
sudo /home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh clean
|
||||||
|
become_user: "{{ server_user }}"
|
||||||
|
ignore_errors: true
|
||||||
|
when: cleanup_old_images | default(true)
|
||||||
|
tags: [cleanup]
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DEPLOYMENT RECORD AND AUDIT TRAIL
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Create auto-scaling deployment record
|
||||||
|
copy:
|
||||||
|
content: |
|
||||||
|
# Auto-Scaling Deployment Record
|
||||||
|
timestamp: "{{ ansible_date_time.iso8601 }}"
|
||||||
|
application: "{{ app_name }}"
|
||||||
|
environment: "{{ deploy_env }}{{ env_tag | default('') }}"
|
||||||
|
version: "{{ docker_image_tag }}"
|
||||||
|
replicas: {{ final_replicas }}
|
||||||
|
max_replicas: {{ final_max_replicas }}
|
||||||
|
strategy: "{{ final_deployment_strategy }}"
|
||||||
|
base_port: "{{ final_app_port }}"
|
||||||
|
framework: "{{ framework | default('unknown') }}"
|
||||||
|
node_version: "{{ node_version | default('unknown') }}"
|
||||||
|
status: "success"
|
||||||
|
duration: "{{ auto_scaling_deployment_result.delta | default('unknown') }}"
|
||||||
|
operator: "{{ ansible_user_id }}"
|
||||||
|
git_commit: "{{ git_sha | default('unknown') }}"
|
||||||
|
git_tag: "{{ git_tag | default('unknown') }}"
|
||||||
|
memory_limit: "{{ final_memory_limit }}"
|
||||||
|
cpu_limit: "{{ final_cpu_limit }}"
|
||||||
|
docker_network: "{{ final_docker_network }}"
|
||||||
|
health_check_timeout: "{{ final_health_check_timeout }}"
|
||||||
|
deployment_timeout: "{{ final_deployment_timeout }}"
|
||||||
|
load_balancer_updated: {{ update_load_balancer | default(true) }}
|
||||||
|
management_script: "/home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh"
|
||||||
|
dest: "/home/{{ server_user }}/ci/logs/{{ app_name }}/{{ deploy_env }}/deployment-{{ ansible_date_time.epoch }}.yml"
|
||||||
|
owner: "{{ server_user }}"
|
||||||
|
group: "{{ server_user }}"
|
||||||
|
mode: '0644'
|
||||||
|
tags: [logging]
|
||||||
|
|
||||||
|
- name: Register auto-scaling deployment in system log
|
||||||
|
lineinfile:
|
||||||
|
path: "/var/log/microservices-deployments.log"
|
||||||
|
line: "{{ ansible_date_time.iso8601 }} | {{ app_name }} | {{ deploy_env }} | {{ docker_image_tag }} | {{ git_tag }} | AUTO-SCALE-SUCCESS | {{ final_replicas }}@{{ auto_scaling_metrics.base_port }} | {{ framework | default('unknown') }}"
|
||||||
|
create: true
|
||||||
|
mode: '0644'
|
||||||
|
tags: [logging]
|
||||||
|
|
||||||
|
- name: Cleanup old backups file
|
||||||
|
shell: |
|
||||||
|
ls -1t /home/{{ server_user }}/ci/configs/{{ app_name }}/{{ deploy_env }}/.env.*~ 2>/dev/null | tail -n +4 | xargs -r rm --
|
||||||
|
ls -1t /home/{{ server_user }}/scripts/manage-{{ service_name }}.sh.*~ 2>/dev/null | tail -n +4 | xargs -r rm --
|
||||||
|
ls -1t /etc/nginx/conf.d/{{ app_name }}-{{ deploy_env }}-upstream.conf.*~ 2>/dev/null | tail -n +4 | xargs -r rm --
|
||||||
|
args:
|
||||||
|
warn: false
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# AUTO-GENERATED ENVIRONMENT FILE FOR AUTO-SCALING DEPLOYMENT
|
||||||
|
# Application: {{ app_name }}
|
||||||
|
# Environment: {{ deploy_env }}{{ env_tag | default('') }}
|
||||||
|
# Generated: {{ ansible_date_time.iso8601 }}
|
||||||
|
# Ansible User: {{ ansible_user_id }}
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
NODE_ENV={{ deploy_env }}
|
||||||
|
APP_NAME={{ app_name }}
|
||||||
|
APP_VERSION={{ docker_image_tag }}
|
||||||
|
DEPLOY_ENV={{ deploy_env }}
|
||||||
|
INSTANCE_ID={{ app_name }}-{{ deploy_env }}
|
||||||
|
|
||||||
|
# Port Configuration
|
||||||
|
PORT={{ final_container_port | default(container_port) | default(3000) }}
|
||||||
|
HOST=0.0.0.0
|
||||||
|
|
||||||
|
# Auto-Scaling Configuration
|
||||||
|
{% if auto_scaling_config is defined %}
|
||||||
|
REPLICAS={{ auto_scaling_config.replicas }}
|
||||||
|
MAX_REPLICAS={{ auto_scaling_config.max_replicas }}
|
||||||
|
DEPLOYMENT_STRATEGY={{ auto_scaling_config.deployment_strategy }}
|
||||||
|
BASE_PORT={{ auto_scaling_config.base_port }}
|
||||||
|
CONTAINER_PORT={{ auto_scaling_config.container_port }}
|
||||||
|
{% else %}
|
||||||
|
REPLICAS={{ final_replicas | default(2) }}
|
||||||
|
MAX_REPLICAS={{ final_max_replicas | default(5) }}
|
||||||
|
DEPLOYMENT_STRATEGY={{ final_deployment_strategy | default('rolling') }}
|
||||||
|
BASE_PORT={{ final_app_port | default(3000) }}
|
||||||
|
CONTAINER_PORT={{ final_container_port | default(3000) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Framework Configuration
|
||||||
|
{% if framework is defined %}
|
||||||
|
FRAMEWORK={{ framework }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if node_version is defined %}
|
||||||
|
NODE_VERSION={{ node_version }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if package_manager is defined %}
|
||||||
|
PACKAGE_MANAGER={{ package_manager }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Docker Configuration
|
||||||
|
DOCKER_NETWORK={{ final_docker_network | default(app_name + '-' + deploy_env + '-network') }}
|
||||||
|
RESTART_POLICY={{ final_restart_policy | default('unless-stopped') }}
|
||||||
|
MEMORY_LIMIT={{ final_memory_limit | default('1g') }}
|
||||||
|
CPU_LIMIT={{ final_cpu_limit | default('1.0') }}
|
||||||
|
|
||||||
|
# Health Check Configuration
|
||||||
|
{% if health_check is defined %}
|
||||||
|
HEALTH_CHECK_PATH={{ health_check.path | default('/health') }}
|
||||||
|
HEALTH_CHECK_TIMEOUT={{ health_check.timeout | default(10) }}
|
||||||
|
HEALTH_CHECK_INTERVAL={{ health_check.interval | default(30) }}
|
||||||
|
HEALTH_CHECK_RETRIES={{ health_check.retries | default(3) }}
|
||||||
|
{% else %}
|
||||||
|
HEALTH_CHECK_PATH={{ health_path | default('/health') }}
|
||||||
|
HEALTH_CHECK_TIMEOUT=10
|
||||||
|
HEALTH_CHECK_INTERVAL=30
|
||||||
|
HEALTH_CHECK_RETRIES=3
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Git Information
|
||||||
|
{% if git_tag is defined %}
|
||||||
|
GIT_TAG={{ git_tag }}
|
||||||
|
{% endif %}
|
||||||
|
{% if git_sha is defined %}
|
||||||
|
GIT_COMMIT={{ git_sha }}
|
||||||
|
{% endif %}
|
||||||
|
{% if git_branch is defined %}
|
||||||
|
GIT_BRANCH={{ git_branch }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Build Information
|
||||||
|
BUILD_DATE={{ ansible_date_time.iso8601 }}
|
||||||
|
PIPELINE_ID={{ pipeline_id | default('unknown') }}
|
||||||
|
|
||||||
|
# Environment-Specific Variables
|
||||||
|
{% if environment_variables is defined and environment_variables[deploy_env] is defined %}
|
||||||
|
{% for key, value in environment_variables[deploy_env].items() %}
|
||||||
|
{{ key }}={{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Legacy env_vars support (backward compatibility)
|
||||||
|
{% if env_vars is defined %}
|
||||||
|
{% for key, value in env_vars.items() %}
|
||||||
|
{{ key }}={{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Project-Specific Variables
|
||||||
|
{% if project_env_vars is defined and project_env_vars[deploy_env] is defined %}
|
||||||
|
{% for key, value in project_env_vars[deploy_env].items() %}
|
||||||
|
{{ key }}={{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Database Configuration (if applicable)
|
||||||
|
{% if database_config is defined %}
|
||||||
|
{% for key, value in database_config.items() %}
|
||||||
|
{{ key | upper }}={{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Queue Configuration (if applicable)
|
||||||
|
{% if queue_config is defined %}
|
||||||
|
{% for key, value in queue_config.items() %}
|
||||||
|
{{ key | upper }}={{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Monitoring Configuration
|
||||||
|
{% if monitoring is defined %}
|
||||||
|
{% if monitoring.enable_metrics %}
|
||||||
|
ENABLE_METRICS=true
|
||||||
|
METRICS_PORT={{ monitoring.metrics_port | default(9090) }}
|
||||||
|
METRICS_PATH={{ monitoring.metrics_path | default('/metrics') }}
|
||||||
|
{% endif %}
|
||||||
|
{% if monitoring.enable_tracing %}
|
||||||
|
ENABLE_TRACING=true
|
||||||
|
JAEGER_ENDPOINT={{ monitoring.jaeger_endpoint | default('http://jaeger:14268/api/traces') }}
|
||||||
|
{% endif %}
|
||||||
|
{% if monitoring.log_aggregation %}
|
||||||
|
LOG_AGGREGATION=true
|
||||||
|
LOG_FORMAT={{ monitoring.log_format | default('json') }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Load Balancer Information
|
||||||
|
LB_UPSTREAM_NAME={{ app_name }}_{{ deploy_env }}_backend
|
||||||
|
LB_METHOD={{ nginx_config.load_balancing_method | default('least_conn') }}
|
||||||
|
|
||||||
|
# Service Discovery
|
||||||
|
SERVICE_NAME={{ app_name }}
|
||||||
|
SERVICE_VERSION={{ docker_image_tag }}
|
||||||
|
SERVICE_ENVIRONMENT={{ deploy_env }}
|
||||||
|
|
||||||
|
# Auto-scaling Metadata for Application
|
||||||
|
AUTOSCALE_ENABLED=true
|
||||||
|
AUTOSCALE_TARGET_REPLICAS={{ final_replicas | default(2) }}
|
||||||
|
AUTOSCALE_MAX_REPLICAS={{ final_max_replicas | default(5) }}
|
||||||
|
AUTOSCALE_STRATEGY={{ final_deployment_strategy | default('rolling') }}
|
||||||
|
|
||||||
|
# Deployment Metadata
|
||||||
|
DEPLOYMENT_TIMESTAMP={{ ansible_date_time.epoch }}
|
||||||
|
DEPLOYMENT_USER={{ ansible_user_id }}
|
||||||
|
DEPLOYMENT_HOST={{ ansible_host }}
|
||||||
|
|
||||||
|
# Security Configuration
|
||||||
|
{% if security_config is defined %}
|
||||||
|
{% for key, value in security_config.items() %}
|
||||||
|
{{ key | upper }}={{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Feature Flags
|
||||||
|
{% if feature_flags is defined %}
|
||||||
|
{% for key, value in feature_flags.items() %}
|
||||||
|
FEATURE_{{ key | upper }}={{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Next.js Specific (if framework is nextjs)
|
||||||
|
{% if framework == 'nextjs' %}
|
||||||
|
NEXT_TELEMETRY_DISABLED=1
|
||||||
|
{% if environment_variables is defined and environment_variables[deploy_env] is defined %}
|
||||||
|
{% for key, value in environment_variables[deploy_env].items() %}
|
||||||
|
{% if key.startswith('NEXT_PUBLIC_') %}
|
||||||
|
{{ key }}={{ value }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# React Specific (if framework is reactjs)
|
||||||
|
{% if framework == 'reactjs' or framework == 'reactjs-vite' %}
|
||||||
|
{% if environment_variables is defined and environment_variables[deploy_env] is defined %}
|
||||||
|
{% for key, value in environment_variables[deploy_env].items() %}
|
||||||
|
{% if key.startswith('REACT_APP_') %}
|
||||||
|
{{ key }}={{ value }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# NestJS Specific (if framework is nestjs)
|
||||||
|
{% if framework == 'nestjs' %}
|
||||||
|
NEST_DEBUG=false
|
||||||
|
{% if deploy_env == 'development' %}
|
||||||
|
NEST_DEBUG=true
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Express Specific (if framework is expressjs)
|
||||||
|
{% if framework == 'expressjs' %}
|
||||||
|
EXPRESS_ENV={{ deploy_env }}
|
||||||
|
TRUST_PROXY=true
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Worker Specific (if it's a worker deployment)
|
||||||
|
{% if env_tag is defined and 'worker' in env_tag %}
|
||||||
|
WORKER_CONCURRENCY={{ worker_config.concurrency | default(5) }}
|
||||||
|
WORKER_TIMEOUT={{ worker_config.timeout | default(30000) }}
|
||||||
|
QUEUE_NAME={{ worker_config.queue_name | default('default') }}
|
||||||
|
QUEUE_RETRY_ATTEMPTS={{ worker_config.retry_attempts | default(3) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# AUTO-SCALING DEPLOYMENT INFORMATION
|
||||||
|
# ============================================================================
|
||||||
|
# This file was auto-generated for auto-scaling deployment
|
||||||
|
# Management script: /home/{{ server_user }}/scripts/manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh
|
||||||
|
#
|
||||||
|
# Scale command: ./manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh scale <replicas>
|
||||||
|
# Status command: ./manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh status
|
||||||
|
# Health command: ./manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh health
|
||||||
|
# Logs command: ./manage-{{ app_name }}-{{ deploy_env }}{{ env_tag | default('') }}.sh logs
|
||||||
|
#
|
||||||
|
# Port Pattern: {{ final_app_port | default(3000) }}{replica_number}
|
||||||
|
# Current Replicas: {{ final_replicas | default(2) }}
|
||||||
|
# Max Replicas: {{ final_max_replicas | default(5) }}
|
||||||
|
# Strategy: {{ final_deployment_strategy | default('rolling') }}
|
||||||
|
# ============================================================================
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# Express.js Optimized Dockerfile
|
||||||
|
# Auto-generated for {{ app_name }}
|
||||||
|
# Node.js Version: {{ node_version }}
|
||||||
|
# Package Manager: {{ package_manager }}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Build arguments
|
||||||
|
# ============================================================================
|
||||||
|
ARG NODE_VERSION={{ node_version }}
|
||||||
|
ARG PACKAGE_MANAGER={{ package_manager }}
|
||||||
|
ARG BUILD_COMMAND={{ build_command }}
|
||||||
|
ARG APP_PORT={{ framework_settings_port }}
|
||||||
|
ARG NODE_ENV=production
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Base stage
|
||||||
|
# ============================================================================
|
||||||
|
FROM gitlab.forlife.one:5050/share/node:${NODE_VERSION}-alpine AS base
|
||||||
|
|
||||||
|
# Install dependencies for native modules and health checks
|
||||||
|
RUN apk update && apk add imagemagick && apk add graphicsmagick && apk add tmux && apk add ffmpeg
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
libc6-compat \
|
||||||
|
python3 \
|
||||||
|
make \
|
||||||
|
g++ \
|
||||||
|
curl \
|
||||||
|
dumb-init
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Production stage
|
||||||
|
# ============================================================================
|
||||||
|
FROM base AS production
|
||||||
|
|
||||||
|
COPY . ./app
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Build arguments
|
||||||
|
ARG GIT_COMMIT=unknown
|
||||||
|
ARG BUILD_DATE=unknown
|
||||||
|
|
||||||
|
# Set build environment
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
# Create build info endpoint
|
||||||
|
RUN mkdir -p src/common && \
|
||||||
|
echo 'export const BUILD_INFO = {"version":"{{ docker_image_tag }}","commit":"'${GIT_COMMIT}'","buildDate":"'${BUILD_DATE}'","framework":"expressjs","nodeVersion":"{{ node_version }}"}' > src/common/build-info.ts
|
||||||
|
|
||||||
|
# Build app
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN addgroup --system --gid 1001 expressjs && adduser --system --uid 1001 expressjs
|
||||||
|
|
||||||
|
# Ensure dist folder is owned by expressjs user
|
||||||
|
RUN chown -R expressjs:expressjs /app/dist
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=${APP_PORT}
|
||||||
|
ENV APP_PORT=${APP_PORT}
|
||||||
|
ENV NODE_OPTIONS="--max-old-space-size={{ MEMORY_LIMIT_MB }}"
|
||||||
|
|
||||||
|
RUN echo '#!/bin/sh\n\
|
||||||
|
status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${APP_PORT}/health)\n\
|
||||||
|
if [ "$status" -eq 200 ] || [ "$status" -eq 404 ]; then exit 0; fi\n\
|
||||||
|
\n\
|
||||||
|
status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${APP_PORT}/)\n\
|
||||||
|
if [ "$status" -eq 200 ] || [ "$status" -eq 404 ]; then exit 0; fi\n\
|
||||||
|
\n\
|
||||||
|
exit 1' > /health-check.sh && \
|
||||||
|
chmod +x /health-check.sh
|
||||||
|
|
||||||
|
# Create logs directory
|
||||||
|
RUN mkdir -p ./logs && chown -R expressjs:expressjs ./logs
|
||||||
|
RUN ([ -d "./src/public" ] && cp -r ./src/public ./dist && chown -R expressjs:expressjs ./dist/public) || true
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER expressjs
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE ${APP_PORT}
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
#HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 CMD /health-check.sh
|
||||||
|
|
||||||
|
# Start the application with dumb-init for proper signal handling
|
||||||
|
#CMD ["dumb-init", "node", "dist/main"]
|
||||||
|
CMD {{ RUN_CMD }}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# Define a build-time variable for the PHP version, with a default of 8.1
|
||||||
|
ARG PHP_VERSION={{ php_version }}
|
||||||
|
|
||||||
|
# Stage 1: Build Stage (Optional but good practice)
|
||||||
|
# Use the PHP_VERSION argument in the FROM instruction
|
||||||
|
FROM gitlab.forlife.one:5050/share/php:${PHP_VERSION}-fpm-alpine AS base
|
||||||
|
|
||||||
|
# Set working directory inside the container
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
|
# -----------------------------------------------
|
||||||
|
# 1. INSTALL SYSTEM DEPENDENCIES & PHP EXTENSIONS
|
||||||
|
# -----------------------------------------------
|
||||||
|
# Added redis extension installation via PECL.
|
||||||
|
RUN apk update && apk add --no-cache \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
libpng-dev \
|
||||||
|
libjpeg-turbo-dev \
|
||||||
|
libwebp-dev \
|
||||||
|
freetype-dev \
|
||||||
|
zip \
|
||||||
|
unzip \
|
||||||
|
libzip-dev \
|
||||||
|
oniguruma-dev \
|
||||||
|
autoconf \
|
||||||
|
g++ \
|
||||||
|
make \
|
||||||
|
nginx \
|
||||||
|
supervisor \
|
||||||
|
dcron \
|
||||||
|
postgresql-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
&& pecl install redis \
|
||||||
|
&& docker-php-ext-enable redis \
|
||||||
|
# Configure and install other extensions
|
||||||
|
&& docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
|
||||||
|
&& docker-php-ext-install -j$(nproc) gd pdo_mysql pdo_pgsql opcache bcmath exif pcntl sockets zip mbstring dom \
|
||||||
|
# Clean up
|
||||||
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
|
# Setup Laravel scheduler cron job to run every minute
|
||||||
|
# This cron job will be run by the 'www-data' user
|
||||||
|
RUN echo "* * * * * cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1" > /etc/crontabs/www-data
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Production stage
|
||||||
|
# ============================================================================
|
||||||
|
FROM base AS production
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
|
# -----------------------------------------------
|
||||||
|
# 2. APPLICATION SETUP & DEPENDENCIES
|
||||||
|
# -----------------------------------------------
|
||||||
|
|
||||||
|
# Install Composer v2, suitable for modern PHP versions
|
||||||
|
#COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||||
|
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
|
# Copy the Laravel application files
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Install Composer dependencies (Assuming composer.lock is present)
|
||||||
|
RUN composer install --no-dev --optimize-autoloader --no-interaction
|
||||||
|
|
||||||
|
# Optimize Laravel
|
||||||
|
RUN php artisan optimize && \
|
||||||
|
php artisan route:clear && \
|
||||||
|
php artisan route:cache && \
|
||||||
|
php artisan config:clear && \
|
||||||
|
php artisan config:cache && \
|
||||||
|
php artisan view:clear && \
|
||||||
|
php artisan view:cache
|
||||||
|
|
||||||
|
#RUN php artisan storage:link
|
||||||
|
|
||||||
|
# remove this line if you do not want to run migrations on each build
|
||||||
|
#RUN php artisan migrate --force
|
||||||
|
|
||||||
|
# Set appropriate permissions for Laravel (www-data is the default FPM user on Alpine)
|
||||||
|
RUN chown -R www-data:www-data /var/www/html \
|
||||||
|
&& chmod -R 775 storage bootstrap/cache
|
||||||
|
RUN ([ -d "public/cms/backend/uploads" ] && chmod -R 775 public/cms/backend/uploads) || true
|
||||||
|
|
||||||
|
# -----------------------------------------------
|
||||||
|
# 3. CONFIGURATION & ENTRYPOINT (THE FIX)
|
||||||
|
# -----------------------------------------------
|
||||||
|
RUN mkdir -p /run/nginx \
|
||||||
|
&& chown -R www-data:www-data /run/nginx \
|
||||||
|
&& chown -R www-data:www-data /var/lib/nginx
|
||||||
|
|
||||||
|
# Copy Nginx Configuration (Assuming it's in the 'docker' subdirectory)
|
||||||
|
# Nginx listens on port 80 and proxies PHP requests to FPM (port 9000).
|
||||||
|
COPY docker/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY docker/laravel.conf /etc/nginx/http.d/site.conf
|
||||||
|
COPY docker/php.ini /usr/local/etc/php/php.ini
|
||||||
|
|
||||||
|
# Copy Supervisor Configuration (Assuming it's in the 'docker' subdirectory)
|
||||||
|
# Supervisor manages both Nginx and PHP-FPM processes.
|
||||||
|
COPY docker/supervisord.conf /etc/supervisord.conf
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Start Supervisor, which runs Nginx, PHP-FPM, and Cron
|
||||||
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Express.js Optimized Dockerfile
|
||||||
|
# Auto-generated for {{ app_name }}
|
||||||
|
# Node.js Version: {{ node_version }}
|
||||||
|
# Package Manager: {{ package_manager }}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Build arguments
|
||||||
|
# ============================================================================
|
||||||
|
ARG NODE_VERSION={{ node_version }}
|
||||||
|
ARG PACKAGE_MANAGER={{ package_manager }}
|
||||||
|
ARG BUILD_COMMAND={{ build_command }}
|
||||||
|
ARG APP_PORT={{ framework_settings_port }}
|
||||||
|
ARG NODE_ENV=production
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Base stage
|
||||||
|
# ============================================================================
|
||||||
|
FROM gitlab.forlife.one:5050/share/node:${NODE_VERSION}-alpine AS base
|
||||||
|
|
||||||
|
# Install dependencies for native modules and health checks
|
||||||
|
RUN apk update && apk add imagemagick && apk add graphicsmagick && apk add tmux && apk add ffmpeg
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
libc6-compat \
|
||||||
|
python3 \
|
||||||
|
make \
|
||||||
|
g++ \
|
||||||
|
curl \
|
||||||
|
dumb-init
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Production stage
|
||||||
|
# ============================================================================
|
||||||
|
FROM base AS production
|
||||||
|
|
||||||
|
COPY . ./app
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
# Build arguments
|
||||||
|
ARG GIT_COMMIT=unknown
|
||||||
|
ARG BUILD_DATE=unknown
|
||||||
|
|
||||||
|
# Set build environment
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
# Create build info endpoint
|
||||||
|
RUN mkdir -p src/common && \
|
||||||
|
echo 'export const BUILD_INFO = {"version":"{{ docker_image_tag }}","commit":"'${GIT_COMMIT}'","buildDate":"'${BUILD_DATE}'","framework":"nestjs","nodeVersion":"{{ node_version }}"}' > src/common/build-info.ts
|
||||||
|
|
||||||
|
# Build app
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN addgroup --system --gid 1001 nestjs && adduser --system --uid 1001 nestjs
|
||||||
|
|
||||||
|
# Ensure dist folder is owned by nestjs user
|
||||||
|
RUN chown -R nestjs:nestjs /app/dist
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=${APP_PORT}
|
||||||
|
ENV APP_PORT=${APP_PORT}
|
||||||
|
ENV NODE_OPTIONS="--max-old-space-size={{ MEMORY_LIMIT_MB }}"
|
||||||
|
|
||||||
|
# Create health check script
|
||||||
|
RUN echo '#!/bin/sh\n\
|
||||||
|
status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${APP_PORT}/health)\n\
|
||||||
|
if [ "$status" -eq 200 ] || [ "$status" -eq 404 ]; then exit 0; fi\n\
|
||||||
|
\n\
|
||||||
|
status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${APP_PORT}/)\n\
|
||||||
|
if [ "$status" -eq 200 ] || [ "$status" -eq 404 ]; then exit 0; fi\n\
|
||||||
|
\n\
|
||||||
|
exit 1' > /health-check.sh && \
|
||||||
|
chmod +x /health-check.sh
|
||||||
|
|
||||||
|
# Create logs directory
|
||||||
|
RUN mkdir -p ./logs && chown -R nestjs:nestjs ./logs
|
||||||
|
RUN ([ -d "./src/public" ] && cp -r ./src/public ./dist && chown -R nestjs:nestjs ./dist/public) || true
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER nestjs
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE ${APP_PORT}
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
#HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 CMD /health-check.sh
|
||||||
|
|
||||||
|
# Start the application with dumb-init for proper signal handling
|
||||||
|
#CMD ["dumb-init", "node", "dist/main"]
|
||||||
|
CMD {{ RUN_CMD }}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# Next.js Optimized Dockerfile with Dynamic Build Args
|
||||||
|
# Auto-generated for {{ app_name }}
|
||||||
|
# Node.js Version: {{ node_version }}
|
||||||
|
# Package Manager: {{ package_manager }}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Build arguments - Base Configuration
|
||||||
|
# ============================================================================
|
||||||
|
ARG NODE_VERSION={{ node_version }}
|
||||||
|
ARG PACKAGE_MANAGER={{ package_manager }}
|
||||||
|
ARG BUILD_COMMAND={{ build_command }}
|
||||||
|
ARG START_COMMAND={{ start_command }}
|
||||||
|
ARG APP_PORT={{ framework_settings_port }}
|
||||||
|
ARG NODE_ENV=production
|
||||||
|
ARG APP_VERSION={{ docker_image_tag }}
|
||||||
|
ARG FRAMEWORK=nextjs
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Base stage - Alpine for smaller size
|
||||||
|
# ============================================================================
|
||||||
|
FROM gitlab.forlife.one:5050/share/node:${NODE_VERSION}-alpine AS base
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
libc6-compat \
|
||||||
|
python3 \
|
||||||
|
make \
|
||||||
|
g++ curl
|
||||||
|
|
||||||
|
# Install dependencies only when needed
|
||||||
|
FROM base AS deps
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies based on the preferred package manager
|
||||||
|
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
||||||
|
RUN \
|
||||||
|
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||||
|
elif [ -f package-lock.json ]; then npm ci; \
|
||||||
|
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
|
||||||
|
else {{ package_manager }} install; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
#RUN {{ package_manager }} install
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Next.js collects completely anonymous telemetry data about general usage.
|
||||||
|
# Learn more here: https://nextjs.org/telemetry
|
||||||
|
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||||
|
# ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
# If using npm comment out above and use below instead
|
||||||
|
# RUN npm run build
|
||||||
|
|
||||||
|
# Production image, copy all the files and run next
|
||||||
|
FROM base AS production
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
# Uncomment the following line in case you want to disable telemetry during runtime.
|
||||||
|
# ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
ENV NODE_OPTIONS="--max-old-space-size={{ MEMORY_LIMIT_MB }}"
|
||||||
|
|
||||||
|
|
||||||
|
# Create health check script
|
||||||
|
RUN echo '#!/bin/sh\n\
|
||||||
|
status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${APP_PORT}/health)\n\
|
||||||
|
if [ "$status" -eq 200 ] || [ "$status" -eq 404 ]; then exit 0; fi\n\
|
||||||
|
\n\
|
||||||
|
status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${APP_PORT}/)\n\
|
||||||
|
if [ "$status" -eq 200 ] || [ "$status" -eq 404 ]; then exit 0; fi\n\
|
||||||
|
\n\
|
||||||
|
exit 1' > /health-check.sh && \
|
||||||
|
chmod +x /health-check.sh
|
||||||
|
|
||||||
|
# You only need to copy next.config.js if you are NOT using the default configuration
|
||||||
|
# COPY --from=builder /app/next.config.js ./
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
|
||||||
|
# Automatically leverage output traces to reduce image size
|
||||||
|
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||||
|
COPY --from=builder /app/.next/standalone ./
|
||||||
|
COPY --from=builder /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV HOSTNAME=0.0.0.0
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV APP_PORT=3000
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
#HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 CMD /health-check.sh
|
||||||
|
|
||||||
|
#CMD ["node", "server.js"]
|
||||||
|
CMD {{ RUN_CMD }}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
# React.js Optimized Dockerfile (No Nginx)
|
||||||
|
# Auto-generated for {{ app_name }}
|
||||||
|
# Node.js Version: {{ node_version }}
|
||||||
|
# Package Manager: {{ package_manager }}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Build arguments
|
||||||
|
# ============================================================================
|
||||||
|
ARG NODE_VERSION={{ node_version }}
|
||||||
|
ARG PACKAGE_MANAGER={{ package_manager }}
|
||||||
|
ARG BUILD_COMMAND={{ build_command }}
|
||||||
|
ARG APP_PORT={{ framework_settings_port }}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Base stage
|
||||||
|
# ============================================================================
|
||||||
|
FROM gitlab.forlife.one:5050/share/node:${NODE_VERSION}-alpine AS base
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
libc6-compat \
|
||||||
|
python3 \
|
||||||
|
make \
|
||||||
|
g++ curl
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Dependencies stage
|
||||||
|
# ============================================================================
|
||||||
|
FROM base AS deps
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY package*.json yarn.lock* pnpm-lock.yaml* ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
#RUN yarn install
|
||||||
|
RUN {{ package_manager }} install
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Builder stage
|
||||||
|
# ============================================================================
|
||||||
|
FROM base AS builder
|
||||||
|
|
||||||
|
# Copy dependencies
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build arguments for runtime configuration
|
||||||
|
ARG GIT_COMMIT=unknown
|
||||||
|
ARG BUILD_DATE=unknown
|
||||||
|
ARG REACT_APP_VERSION={{ docker_image_tag }}
|
||||||
|
|
||||||
|
# Set build environment
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV GENERATE_SOURCEMAP=false
|
||||||
|
ENV REACT_APP_VERSION=${REACT_APP_VERSION}
|
||||||
|
ENV REACT_APP_BUILD_DATE=${BUILD_DATE}
|
||||||
|
ENV REACT_APP_GIT_COMMIT=${GIT_COMMIT}
|
||||||
|
|
||||||
|
# Create build info file
|
||||||
|
RUN echo '{"version":"{{ docker_image_tag }}","commit":"'${GIT_COMMIT}'","buildDate":"'${BUILD_DATE}'","framework":"reactjs","nodeVersion":"{{ node_version }}"}' > public/build-info.json
|
||||||
|
|
||||||
|
# Require BuildKit enabled
|
||||||
|
# Mount secret nếu tồn tại, copy vào thư mục app
|
||||||
|
RUN --mount=type=secret,id=env,target=/run/secrets/env \
|
||||||
|
cp /run/secrets/env .env || echo ".env not found"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Build the application
|
||||||
|
# ============================================================================
|
||||||
|
RUN \
|
||||||
|
if [ -f yarn.lock ]; then \
|
||||||
|
yarn build; \
|
||||||
|
elif [ -f package-lock.json ]; then \
|
||||||
|
npm run build; \
|
||||||
|
elif [ -f pnpm-lock.yaml ]; then \
|
||||||
|
pnpm build; \
|
||||||
|
else \
|
||||||
|
npm run build; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Production stage with serve
|
||||||
|
# ============================================================================
|
||||||
|
FROM node:${NODE_VERSION}-alpine AS production
|
||||||
|
|
||||||
|
# Install curl for health checks
|
||||||
|
RUN apk add --no-cache curl
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install serve globally for serving static files
|
||||||
|
RUN npm install -g serve
|
||||||
|
|
||||||
|
# Copy built React app
|
||||||
|
COPY --from=builder /app/build ./build
|
||||||
|
|
||||||
|
# Create package.json for serving
|
||||||
|
RUN echo '{ \
|
||||||
|
"name": "{{ app_name }}-server", \
|
||||||
|
"version": "{{ docker_image_tag }}", \
|
||||||
|
"scripts": { \
|
||||||
|
"start": "serve -s build -l 3000" \
|
||||||
|
}, \
|
||||||
|
"dependencies": { \
|
||||||
|
"serve": "latest" \
|
||||||
|
} \
|
||||||
|
}' > package.json
|
||||||
|
|
||||||
|
# Create serve.json configuration
|
||||||
|
RUN echo '{ \
|
||||||
|
"public": "build", \
|
||||||
|
"rewrites": [ \
|
||||||
|
{ "source": "**", "destination": "/index.html" } \
|
||||||
|
], \
|
||||||
|
"headers": [ \
|
||||||
|
{ \
|
||||||
|
"source": "**/*.@(jpg|jpeg|gif|png|svg|ico|css|js)", \
|
||||||
|
"headers": [ \
|
||||||
|
{ \
|
||||||
|
"key": "Cache-Control", \
|
||||||
|
"value": "public, max-age=31536000, immutable" \
|
||||||
|
} \
|
||||||
|
] \
|
||||||
|
}, \
|
||||||
|
{ \
|
||||||
|
"source": "**", \
|
||||||
|
"headers": [ \
|
||||||
|
{ \
|
||||||
|
"key": "X-Content-Type-Options", \
|
||||||
|
"value": "nosniff" \
|
||||||
|
}, \
|
||||||
|
{ \
|
||||||
|
"key": "X-Frame-Options", \
|
||||||
|
"value": "DENY" \
|
||||||
|
}, \
|
||||||
|
{ \
|
||||||
|
"key": "X-XSS-Protection", \
|
||||||
|
"value": "1; mode=block" \
|
||||||
|
} \
|
||||||
|
] \
|
||||||
|
} \
|
||||||
|
] \
|
||||||
|
}' > serve.json
|
||||||
|
|
||||||
|
# Create health check endpoint
|
||||||
|
RUN mkdir -p build && echo 'healthy' > build/health.txt
|
||||||
|
|
||||||
|
# Create health check script
|
||||||
|
RUN echo '#!/bin/sh\n\
|
||||||
|
status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${APP_PORT}/health)\n\
|
||||||
|
if [ "$status" -eq 200 ] || [ "$status" -eq 404 ]; then exit 0; fi\n\
|
||||||
|
\n\
|
||||||
|
status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${APP_PORT}/)\n\
|
||||||
|
if [ "$status" -eq 200 ] || [ "$status" -eq 404 ]; then exit 0; fi\n\
|
||||||
|
\n\
|
||||||
|
exit 1' > /health-check.sh && \
|
||||||
|
chmod +x /health-check.sh
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN addgroup --system --gid 1001 reactapp && \
|
||||||
|
adduser --system --uid 1001 reactapp
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
RUN chown -R reactapp:reactapp /app
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER reactapp
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV APP_PORT=3000
|
||||||
|
ENV NODE_OPTIONS="--max-old-space-size={{ MEMORY_LIMIT_MB }}"
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
#HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 CMD /health-check.sh
|
||||||
|
|
||||||
|
# Start the application
|
||||||
|
#CMD ["npm", "start"]
|
||||||
|
|
||||||
|
#CMD [ "serve", "-s", "build" ]
|
||||||
|
CMD {{ RUN_CMD }}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e # Thoát ngay lập tức nếu có lệnh nào thất bại
|
||||||
|
|
||||||
|
echo "Checking /health endpoint..."
|
||||||
|
health_status=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT}/health")
|
||||||
|
|
||||||
|
if [ "$health_status" -eq 200 ] || [ "$health_status" -eq 404 ]; then
|
||||||
|
echo "/health check passed with status ${health_status}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "/health check failed with status ${health_status}. Checking / endpoint..."
|
||||||
|
root_status=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT}/")
|
||||||
|
|
||||||
|
if [ "$root_status" -eq 200 ] || [ "$root_status" -eq 404 ]; then
|
||||||
|
echo "/ check passed with status ${root_status}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "/ check failed with status ${root_status}. Health check failed overall."
|
||||||
|
exit 1
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,182 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# method 1: crontab with parallel block
|
||||||
|
#* * * * * flock -n /var/lock/monitor-container-script.lock /www/server/scripts/monitor-container-script.sh >>/var/log/monitor-container-script.log 2>&1
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
# method 2: crontab with parallel block
|
||||||
|
# only use method 1 or method 2
|
||||||
|
# Khóa tiến trình (FD 9)
|
||||||
|
#~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~
|
||||||
|
exec 9>/var/lock/monitor-container-script.lock
|
||||||
|
if ! flock -n 9; then
|
||||||
|
echo "[$(date '+%F %T')] ⚠️ Another run is in progress. Exit."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
trap 'flock -u 9' EXIT
|
||||||
|
#~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~
|
||||||
|
|
||||||
|
# === Cấu hình chung ===
|
||||||
|
CONTAINER_PREFIX="webapplication-production-"
|
||||||
|
CONTAINER_PORT=3000 # cổng bên trong container (không cần dùng ở đây)
|
||||||
|
HOST="localhost"
|
||||||
|
TIMEOUT=3
|
||||||
|
SLEEP_TIME=10
|
||||||
|
COOLDOWN=180 # giây
|
||||||
|
|
||||||
|
CPU_LIMIT=80
|
||||||
|
MEM_LIMIT=90
|
||||||
|
MANAGE_SCRIPT="/home/forlifeci/scripts/manage-webapplication-production.sh restart_single"
|
||||||
|
|
||||||
|
CONTAINER_LOCK_FILE="/var/lib/container_maps.lock"
|
||||||
|
LOCK_DIR="/tmp/container_restart_locks" && mkdir -p "$LOCK_DIR/logs"
|
||||||
|
|
||||||
|
remove_timestamp() { [[ $1 =~ ^(.*-[0-9]+)-([0-9]+)$ ]] && echo "${BASH_REMATCH[1]}" || echo "$1"; }
|
||||||
|
|
||||||
|
check_container_health() {
|
||||||
|
local container=$1
|
||||||
|
local cpu mem
|
||||||
|
|
||||||
|
echo "📡 $(date '+%Y-%m-%d %H:%M:%S') - Kiểm tra container... $container"
|
||||||
|
|
||||||
|
#cpu=$(docker stats --no-stream --format "{{.CPUPerc}}" "$container" | tr -d '%')
|
||||||
|
#mem=$(docker stats --no-stream --format "{{.MemPerc}}" "$container" | tr -d '%')
|
||||||
|
|
||||||
|
#echo "Kiểm tra CPU / RAM quá giới hạn -> $container"
|
||||||
|
#if (( $(echo "$cpu > $CPU_LIMIT" | bc -l) )) || (( $(echo "$mem > $MEM_LIMIT" | bc -l) )); then
|
||||||
|
# echo "⚠️ $container quá tải (CPU: $cpu%, RAM: $mem%)"
|
||||||
|
# return 1
|
||||||
|
#fi
|
||||||
|
|
||||||
|
#echo "Kiểm tra port 3000 bên trong container -> $container"
|
||||||
|
if ! docker exec "$container" sh -c "nc -z -w 2 127.0.0.1 3000" >/dev/null 2>&1; then
|
||||||
|
echo "❌ $container không phản hồi trên port 3000"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# === Telegram Config ===
|
||||||
|
TELEGRAM_BOT_TOKEN="7837210802:AAHFlwXOdm3PExgIbADJ_TstNoXwJ_Aq7g4"
|
||||||
|
TELEGRAM_CHAT_ID="-1002468192435"
|
||||||
|
THREAD_ID="10248"
|
||||||
|
|
||||||
|
# === Hàm gửi cảnh báo Telegram ===
|
||||||
|
send_telegram_alert() {
|
||||||
|
local container=$1
|
||||||
|
local port=$2
|
||||||
|
local fileName=${3:-"log"}
|
||||||
|
local now=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
local msg="🚨 *CẢNH BÁO* 🚨
|
||||||
|
|
||||||
|
Container: \`$container\`
|
||||||
|
Port: \`$port\`
|
||||||
|
Thời gian: *$now*
|
||||||
|
Hành động: \`docker restart\`
|
||||||
|
|
||||||
|
➡️ Container không phản hồi, đã được khởi động lại.
|
||||||
|
|
||||||
|
http://42.118.103.30/logs/$container.restart.$fileName.html
|
||||||
|
"
|
||||||
|
|
||||||
|
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \
|
||||||
|
-d chat_id="$TELEGRAM_CHAT_ID" \
|
||||||
|
-d parse_mode="Markdown" \
|
||||||
|
-d message_thread_id="$THREAD_ID" \
|
||||||
|
-d text="$msg" > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# === Hàm kiểm tra port ===
|
||||||
|
#check_port() {
|
||||||
|
# local port=$1
|
||||||
|
# nc -z -w $TIMEOUT $HOST $port
|
||||||
|
#}
|
||||||
|
|
||||||
|
check_port() {
|
||||||
|
local port=$1
|
||||||
|
curl -s --max-time "$TIMEOUT" "http://$HOST:$port" -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
should_skip_check() {
|
||||||
|
local container=$1
|
||||||
|
local lock_file="$LOCK_DIR/$container.lock"
|
||||||
|
|
||||||
|
if [[ -f "$lock_file" ]]; then
|
||||||
|
local last_restart
|
||||||
|
last_restart=$(cat "$lock_file")
|
||||||
|
local now
|
||||||
|
now=$(date +%s)
|
||||||
|
if (( now - last_restart < COOLDOWN )); then
|
||||||
|
return 0 # bỏ qua
|
||||||
|
else
|
||||||
|
rm -f "$lock_file" # hết hạn cooldown
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
mark_restart() {
|
||||||
|
local container=$1
|
||||||
|
date +%s > "$LOCK_DIR/$container.lock"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get_docker_binding_port() {
|
||||||
|
local container=$1
|
||||||
|
echo $(docker port "$container" 3000/tcp | awk -F: '{print $2}' | xargs)
|
||||||
|
}
|
||||||
|
|
||||||
|
containers=$(docker ps --format '{{.Names}}' | grep "^$CONTAINER_PREFIX")
|
||||||
|
# === Vòng lặp kiểm tra ===
|
||||||
|
#while true; do
|
||||||
|
|
||||||
|
for container in $containers; do
|
||||||
|
if [[ -f $CONTAINER_LOCK_FILE ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
container_locked=$(remove_timestamp "$container")
|
||||||
|
|
||||||
|
# 🔹 Bỏ qua nếu container đang trong cooldown
|
||||||
|
if should_skip_check "$container_locked"; then
|
||||||
|
echo "⏩ Bỏ qua $container_locked (đang cooldown sau khi restart)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
host_port=$(get_docker_binding_port "$container")
|
||||||
|
|
||||||
|
if [ -z "$host_port" ]; then
|
||||||
|
echo "⚠ $container không map port 3000/tcp, bỏ qua."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "";
|
||||||
|
echo "👉 Kiểm tra $container tại $HOST:$host_port..."
|
||||||
|
|
||||||
|
if ! check_port $host_port; then
|
||||||
|
echo "⚠️ $container KHÔNG phản hồi. Chờ 5s để xác nhận..."
|
||||||
|
sleep 5
|
||||||
|
if ! check_port $host_port; then
|
||||||
|
echo "❌ $container vẫn không phản hồi! Restart & gửi Telegram..."
|
||||||
|
logName=$(date +%s)
|
||||||
|
mark_restart "$container_locked" # đánh dấu cooldown
|
||||||
|
#docker restart "$container"
|
||||||
|
restart=`bash $MANAGE_SCRIPT $container`
|
||||||
|
logPath="$LOCK_DIR/logs/$container.restart.$logName.html"
|
||||||
|
if [ ! -f "$logPath" ]; then
|
||||||
|
echo "<!DOCTYPE html><html><meta charset=utf-8><style>pre{text-wrap: auto;}</style>" > "$logPath"
|
||||||
|
fi
|
||||||
|
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br>" >> "$logPath"
|
||||||
|
printf "%s" "<pre>$restart</pre>" >> "$logPath"
|
||||||
|
send_telegram_alert "$container" "$host_port" "$logName"
|
||||||
|
else
|
||||||
|
echo "✅ $container đã phản hồi lại sau khi chờ."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✅ $container phản hồi OK."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# sleep $SLEEP_TIME
|
||||||
|
#done
|
||||||
|
#/home/forlifeci/scripts/manage-webapplication-production.sh restart_single $container
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# AUTO-GENERATED NGINX UPSTREAM CONFIGURATION
|
||||||
|
# Application: {{ app_name }}
|
||||||
|
# Environment: {{ deploy_env }}{{ env_tag | default('') }}
|
||||||
|
# Generated: {{ ansible_date_time.iso8601 }}
|
||||||
|
# Replicas: {{ replica_count }}
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
{% if framework is defined and framework == "nextjs" %}
|
||||||
|
map $arg_url ${{app_name | regex_replace('[^A-Za-z0-9_]', '_')}}_clean_url {
|
||||||
|
~^(?<base>.+)%3Fv%3D\d+$ $base; # chỉ có ?v=xxx
|
||||||
|
~^(?<base>.+)%3Fv%3D\d+%26(?<rest>.+)$ $base?$rest; # có ?v=xxx&... → giữ lại phần sau
|
||||||
|
default $arg_url; # mặc định giữ nguyên
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
upstream {{ upstream_name | regex_replace('[^A-Za-z0-9_]', '_') }} {
|
||||||
|
# Load balancing method
|
||||||
|
{% if load_balancing_method is defined %}
|
||||||
|
{{ load_balancing_method }};
|
||||||
|
{% else %}
|
||||||
|
least_conn;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Health check configuration
|
||||||
|
{% if health_check_interval is defined %}
|
||||||
|
health_check interval={{ health_check_interval }} fails={{ health_check_fails | default(1) }} passes={{ health_check_passes | default(2) }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Upstream servers (auto-generated from active ports)
|
||||||
|
{% for port in active_ports %}
|
||||||
|
server localhost:{{ port }} weight={{ server_weight | default(1) }} max_fails={{ max_fails | default(1) }} fail_timeout={{ fail_timeout | default(10) }}s;
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
# Backup server (optional)
|
||||||
|
{% if backup_server is defined %}
|
||||||
|
server {{ backup_server }} backup;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Keep-alive connections
|
||||||
|
keepalive {{ keepalive_connections | default(64) }};
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if framework is defined and framework == "nextjs" %}
|
||||||
|
proxy_cache_path /var/cache/nginx/{{app_name | regex_replace('[^A-Za-z0-9_]', '_')}}_cache levels=1:2 keys_zone={{app_name | regex_replace('[^A-Za-z0-9_]', '_')}}_cache:512m inactive=30d max_size=100g use_temp_path=off;
|
||||||
|
|
||||||
|
#levels=1:2: Tạo cây thư mục theo hash key để phân bổ file.
|
||||||
|
#keys_zone=tkl_web_img_cache:512m: Tăng RAM dùng cho metadata (1MB chứa được ~8000 keys) → 2G = ~16 triệu keys.
|
||||||
|
#inactive=30d: Nếu file không được truy cập trong 30 ngày → bị xóa.
|
||||||
|
#max_size=100g: Giới hạn tối đa 100GB cache disk.
|
||||||
|
#use_temp_path=off: Ghi thẳng vào cache thay vì ghi tạm → hiệu quả hơn.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen {{ base_port }};
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
{% if framework is defined and framework == "nextjs" %}
|
||||||
|
|
||||||
|
location ~* /_next/image {
|
||||||
|
access_log off;
|
||||||
|
proxy_pass http://{{ upstream_name | regex_replace('[^A-Za-z0-9_]', '_') }}; # upstream load balancing
|
||||||
|
|
||||||
|
proxy_cache {{app_name | regex_replace('[^A-Za-z0-9_]', '_')}}_cache;
|
||||||
|
proxy_cache_key "url=${{app_name | regex_replace('[^A-Za-z0-9_]', '_')}}_clean_url&w=$arg_w&q=$arg_q";
|
||||||
|
proxy_cache_valid 200 180d;
|
||||||
|
proxy_cache_use_stale error timeout updating http_502 http_503 http_504;
|
||||||
|
proxy_next_upstream error timeout http_502 http_503 http_504;
|
||||||
|
proxy_next_upstream_tries 3;
|
||||||
|
|
||||||
|
# Chống cache stampede (bottleneck)
|
||||||
|
proxy_cache_lock on;
|
||||||
|
proxy_cache_lock_timeout 10s;
|
||||||
|
proxy_cache_lock_age 5s;
|
||||||
|
|
||||||
|
# Timeout & buffering
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
send_timeout 60s;
|
||||||
|
|
||||||
|
# Ignore upstream cache headers (Next.js gửi max-age + vary)
|
||||||
|
proxy_ignore_headers Cache-Control Vary;
|
||||||
|
proxy_hide_header Set-Cookie;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
# Chuẩn hóa Accept để tránh tạo nhiều cache key khác nhau
|
||||||
|
proxy_set_header Accept "image/webp,image/*,*/*;q=0.8";
|
||||||
|
|
||||||
|
add_header X-Cache-Status $upstream_cache_status;
|
||||||
|
#add_header X-Cache-Key "url=${{app_name | regex_replace('[^A-Za-z0-9_]', '_')}}_clean_url&w=$arg_w&q=$arg_q";
|
||||||
|
|
||||||
|
# Keepalive settings (optional)
|
||||||
|
keepalive_timeout 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
access_log off;
|
||||||
|
|
||||||
|
proxy_pass http://{{ upstream_name | regex_replace('[^A-Za-z0-9_]', '_') }};
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header REMOTE-HOST $remote_addr;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_cache off;
|
||||||
|
|
||||||
|
proxy_next_upstream error timeout http_502 http_503 http_504;
|
||||||
|
proxy_next_upstream_tries 3;
|
||||||
|
|
||||||
|
# Timeout & buffering
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
send_timeout 60s;
|
||||||
|
|
||||||
|
proxy_buffering on;
|
||||||
|
client_max_body_size 256M;
|
||||||
|
|
||||||
|
# Keepalive settings (optional)
|
||||||
|
keepalive_timeout 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CONFIGURATION METADATA (for debugging)
|
||||||
|
# ============================================================================
|
||||||
|
# Configuration Details:
|
||||||
|
# - Base Port: {{ base_port }}
|
||||||
|
# - Load Balancing: {{ load_balancing_method | default('least_conn') }}
|
||||||
|
# - Max Fails: {{ max_fails | default(1) }}
|
||||||
|
# - Fail Timeout: {{ fail_timeout | default(10) }}s
|
||||||
|
# - Health Check: {{ 'enabled' if enable_health_check_endpoint | default(true) else 'disabled' }}
|
||||||
|
# ============================================================================
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
server {
|
||||||
|
listen 3000;
|
||||||
|
server_name localhost;
|
||||||
|
root /var/www/html/public; # Đây là thư mục public của Laravel
|
||||||
|
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN";
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
add_header X-Content-Type-Options "nosniff";
|
||||||
|
|
||||||
|
index index.html index.htm index.php;
|
||||||
|
charset utf-8;
|
||||||
|
|
||||||
|
#access_log /dev/stdout;
|
||||||
|
#error_log /dev/stdout;
|
||||||
|
|
||||||
|
location ~\.(yml|env|example|log|prod|dev|local|production)$ {
|
||||||
|
deny all;
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|mp4|vtt)$ {
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
expires 30d;
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
error_page 404 /index.php;
|
||||||
|
|
||||||
|
location ~ "^(.+\.php)($|/)" {
|
||||||
|
|
||||||
|
index index.php index.html index.htm admin.php;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(.*)$;
|
||||||
|
fastcgi_pass 127.0.0.1:9000;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param HTTPS off;
|
||||||
|
send_timeout 120;
|
||||||
|
fastcgi_read_timeout 120;
|
||||||
|
client_max_body_size 20m;
|
||||||
|
fastcgi_param SERVER_PROTOCOL $server_protocol;
|
||||||
|
fastcgi_param QUERY_STRING $query_string;
|
||||||
|
fastcgi_param REQUEST_METHOD $request_method;
|
||||||
|
fastcgi_param CONTENT_TYPE $content_type;
|
||||||
|
fastcgi_param CONTENT_LENGTH $content_length;
|
||||||
|
fastcgi_param SERVER_ADDR $server_addr;
|
||||||
|
fastcgi_param SERVER_PORT $server_port;
|
||||||
|
fastcgi_param SERVER_NAME $server_name;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.(?!well-known).* {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#user nginx;
|
||||||
|
user www-data;
|
||||||
|
|
||||||
|
# Set number of worker processes automatically based on number of CPU cores.
|
||||||
|
worker_processes auto;
|
||||||
|
|
||||||
|
# Enables the use of JIT for regular expressions to speed-up their processing.
|
||||||
|
pcre_jit on;
|
||||||
|
|
||||||
|
# Configures default error logger.
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
|
||||||
|
# Includes files with directives to load dynamic modules.
|
||||||
|
include /etc/nginx/modules/*.conf;
|
||||||
|
|
||||||
|
# Include files with config snippets into the root context.
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
|
||||||
|
events {
|
||||||
|
# The maximum number of simultaneous connections that can be opened by
|
||||||
|
# a worker process.
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
# Includes mapping of file name extensions to MIME types of responses
|
||||||
|
# and defines the default type.
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
# Name servers used to resolve names of upstream servers into addresses.
|
||||||
|
# It's also needed when using tcpsocket and udpsocket in Lua modules.
|
||||||
|
#resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001];
|
||||||
|
|
||||||
|
# Don't tell nginx version to the clients. Default is 'on'.
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
# Specifies the maximum accepted body size of a client request, as
|
||||||
|
# indicated by the request header Content-Length. If the stated content
|
||||||
|
# length is greater than this size, then the client receives the HTTP
|
||||||
|
# error code 413. Set to 0 to disable. Default is '1m'.
|
||||||
|
client_max_body_size 100m;
|
||||||
|
|
||||||
|
# Sendfile copies data between one FD and other from within the kernel,
|
||||||
|
# which is more efficient than read() + write(). Default is off.
|
||||||
|
sendfile on;
|
||||||
|
|
||||||
|
# Causes nginx to attempt to send its HTTP response head in one packet,
|
||||||
|
# instead of using partial frames. Default is 'off'.
|
||||||
|
tcp_nopush on;
|
||||||
|
|
||||||
|
|
||||||
|
# Enables the specified protocols. Default is TLSv1 TLSv1.1 TLSv1.2.
|
||||||
|
# TIP: If you're not obligated to support ancient clients, remove TLSv1.1.
|
||||||
|
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
|
# Path of the file with Diffie-Hellman parameters for EDH ciphers.
|
||||||
|
# TIP: Generate with: `openssl dhparam -out /etc/ssl/nginx/dh2048.pem 2048`
|
||||||
|
#ssl_dhparam /etc/ssl/nginx/dh2048.pem;
|
||||||
|
|
||||||
|
# Specifies that our cipher suits should be preferred over client ciphers.
|
||||||
|
# Default is 'off'.
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
# Enables a shared SSL cache with size that can hold around 8000 sessions.
|
||||||
|
# Default is 'none'.
|
||||||
|
ssl_session_cache shared:SSL:2m;
|
||||||
|
|
||||||
|
# Specifies a time during which a client may reuse the session parameters.
|
||||||
|
# Default is '5m'.
|
||||||
|
ssl_session_timeout 1h;
|
||||||
|
|
||||||
|
# Disable TLS session tickets (they are insecure). Default is 'on'.
|
||||||
|
ssl_session_tickets off;
|
||||||
|
|
||||||
|
|
||||||
|
# Enable gzipping of responses.
|
||||||
|
gzip on;
|
||||||
|
|
||||||
|
# Set the Vary HTTP header as defined in the RFC 2616. Default is 'off'.
|
||||||
|
#gzip_vary on;
|
||||||
|
|
||||||
|
|
||||||
|
# Helper variable for proxying websockets.
|
||||||
|
map $http_upgrade $connection_upgrade {
|
||||||
|
default upgrade;
|
||||||
|
'' close;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Specifies the main log format.
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||||
|
|
||||||
|
# Sets the path, format, and configuration for a buffered log write.
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
|
||||||
|
real_ip_header X-Forwarded-For;
|
||||||
|
real_ip_recursive on;
|
||||||
|
set_real_ip_from 0.0.0.0/0;
|
||||||
|
|
||||||
|
#access_log /dev/stdout;
|
||||||
|
#error_log /dev/stdout;
|
||||||
|
|
||||||
|
# Includes virtual hosts configs.
|
||||||
|
include /etc/nginx/http.d/*.conf;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,39 @@
|
|||||||
|
[supervisord]
|
||||||
|
nodaemon=true
|
||||||
|
# (Tùy chọn) Ngăn Supervisor tự tạo log file của chính nó
|
||||||
|
logfile=/dev/null
|
||||||
|
logfile_maxbytes=0
|
||||||
|
|
||||||
|
[program:nginx]
|
||||||
|
command=/usr/sbin/nginx -g "daemon off;"
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
priority=10
|
||||||
|
# Chuyển hướng stdout của Nginx ra stdout của Supervisor
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
# Chuyển hướng stderr của Nginx ra stderr của Supervisor
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
|
||||||
|
[program:php-fpm]
|
||||||
|
command=/usr/local/sbin/php-fpm --nodaemonize --fpm-config /usr/local/etc/php-fpm.conf
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
priority=5
|
||||||
|
# Chuyển hướng stdout của PHP-FPM ra stdout của Supervisor
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
# Chuyển hướng stderr của PHP-FPM ra stderr của Supervisor
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
|
||||||
|
[program:queue-work]
|
||||||
|
process_name=%(program_name)s_%(process_num)02d
|
||||||
|
command=php /var/www/html/artisan queue:work
|
||||||
|
user=www-data
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
numprocs=1
|
||||||
|
redirect_stderr=true
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
Reference in new issue
Block a user