استقرار AWS ECS Fargate خود را با Terraform و GitLab CI/CD با استفاده از برنامه پایتون ساده کنید.

تمام فایل های این پروژه در مخزن GitHub من ذخیره می شود: https://github.com/sahibgasimov/ecs-gitlab-terraform
در این آموزش، نحوه استقرار یک برنامه پایتون در AWS ECS Fargate را به همراه اتوماسیون زیرساخت Terraform و GitLab CI/CD برای استقرار مداوم به طور خودکار توضیح خواهم داد. بیایید شروع کنیم!
آنچه را پوشش خواهیم داد:
- مروری بر معماری
- پیش نیازها
- راه اندازی زیرساخت با Terraform
- ایجاد ماژول Terraform Backend
- راه اندازی زیرساخت شبکه
- ایجاد زیرساخت ECS
- پیکربندی GitLab CI/CD
- استقرار در توسعه و تولید
در پایان این راهنما، شما یک خط لوله کاملاً خودکار آماده برای استقرار برنامههای کانتینری در محیطهای توسعهدهنده و پرود خواهید داشت.
معماری پروژه:
- AWS ECS Fargate: برای میزبانی خدمات کانتینری.
- Terraform: تامین زیرساخت.
- GitLab CI/CD: خطوط لوله ساخت، آزمایش و استقرار را مدیریت می کند.
- شبکه: VPC ایزوله با زیرشبکه های عمومی/خصوصی.
- Load Balancer: برنامه Load Balancer (ALB) برای مسیریابی ترافیک.
- پشتیبانی TLS: راه اندازی HTTPS با ACM و Route 53.
پیش نیازها
قبل از وارد شدن به تنظیمات، مطمئن شوید که:
- AWS CLI نصب و پیکربندی شد.
- Terraform (نسخه 1.6.0 یا بالاتر).
- حساب GitLab با مجوزهای کافی.
- داکر برای ساخت های محلی نصب شده است.
نمودار بالا هدف پروژه را نشان می دهد. اساسا ما کد منبع برنامه خود را در GitLab و همچنین دو شاخه DEV و PROD خواهیم داشت. هر بار که ما به شعبه مربوطه فشار می دهیم، اتوماسیون CI/CD جای خود را می گیرد.
بیا برویم
راه اندازی زیرساخت
ما میخواهیم یک ماژول پشتیبان terraform جداگانه ایجاد کنیم که در نهایت فایل حالت terraform کل پروژه در آن ذخیره میشود.
مرحله 1: Repository را کلون کنید
git clone https://github.com/sahibgasimov/ecs-gitlab-terraform.git
cd ecs-gitlab-terraform
درخت ساختار پوشه های Github:
├── backend/ # Terraform backend module
├── networking/ # VPC and networking module
├── ecs/ # ECS cluster and services module
├── gitlab_cicd/ # Gitlab pipeline file
├── images/ # Architecture and CI/CD images
├── app/ # Python application code
├── .gitlab-ci.yml # GitLab CI/CD pipeline
└── README.md # Project documentation
مرحله 2: Terraform Backend را راه اندازی کنید
این پروژه از یک سطل S3 برای مدیریت حالت Terraform و DynamoDB برای قفل استفاده می کند.
بلوک زیر را در main.tf کامنت کنید و terraform application را برای ایجاد ماژول backend اجرا کنید. پس از ایجاد به روز رسانی با سطل s3 موجود خود برای انتقال فایل حالت به سطل s3.
terraform {
backend "s3" {
bucket = "my-project-terraform-state-prod" # Replace with your S3 bucket name
key = "backend/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "my-project-terraform-lock"
encrypt = true
}
}
دستورات Terraform را برای مقداردهی اولیه و اعمال پیکربندی اجرا کنید:
terraform init
terraform apply
اکنون میتوانید نظر را لغو کنید و فایل حالت را به باطن S3 منتقل کنید:
terraform init -migrate-state
Terraform Backend اکنون آماده است!
شبکه سازی
ماژول VPC برای ECS Cluster
این ماژول یک AWS Virtual Private Cloud (VPC) را برای خوشه ECS، شامل زیرشبکه های خصوصی و عمومی، مسیرهای دروازه NAT برای زیرشبکه خصوصی و دروازه اینترنت برای زیرشبکه عمومی پیکربندی می کند.
VPC با بلوک CIDR 10.0.0.0/16، شامل زیرشبکه های عمومی و خصوصی در مناطق در دسترس. Internet Gateway و NAT Gateway به ترتیب برای دسترسی به اینترنت خروجی از زیر شبکه های عمومی و خصوصی فعال هستند.
پیش نیازها شامل پیکربندی یک سطل S3 و جدول DynamoDB برای وضعیت Terraform و تنظیم اعتبار AWS برای منطقه مشخص شده است.
cd networking/
terraform {
backend "s3" {
bucket = "my-project-terraform-state-prod" # Replace with your actual bucket name
key = "networking/terraform.tfstate" # Unique state file key for networking module
region = "us-east-1" # Replace with your AWS region
dynamodb_table = "my-project-terraform-lock" # Replace with your actual DynamoDB table name
encrypt = true
}
}
terraform init
terraform apply
اکنون که VPC و منابع شبکه مرتبط ایجاد شدهاند، میخواهیم قسمت ECS را ادامه دهیم.
زیرساخت ECS
برای شروع فرآیند استقرار برنامه، ما البته نیاز داریم که خوشه ECS Fargate ایجاد شود، اما حتی قبل از آن باید تصاویر داکر خود را به ECR فشار دهیم، بنابراین با ایجاد مخزن تصویر AWS ECR به صورت دستی شروع می کنیم، سپس تصویر را می سازیم و فشار می دهیم. و سپس می توانیم آن را به terraform وارد کنیم. به سادگی به کنسول AWS ECR بروید و یک مخزن خصوصی ECR ایجاد کنید. برای این آموزش نام آن را “web-app-repository” می گذاریم.
سپس در پوشه ECS سی دی قرار دهید و تصویر داکر جدید بسازید:
cd ecs/docker_dev (then docker_prod folder)
Dockerfile را بسازید تا تصویر داکر را برای محیط های DEV و PROD بسازید و سپس تصاویر را به مخزن ECR خود فشار دهید. از دستورالعمل ها در کنسول AWS ECR استفاده کنید:
این دستورات را برای PROD اجرا کنید:
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 1234567.dkr.ecr.us-east-1.amazonaws.com
docker build -t web-app .
docker tag web-app:latest 1234567.dkr.ecr.us-east-1.amazonaws.com/web-app-repository:main-latest
docker push 124567.dkr.ecr.us-east-1.amazonaws.com/web-app-repository:main-latest
همین کار را برای DEV انجام دهید:
cd ../docker_dev/
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 1234567.dkr.ecr.us-east-1.amazonaws.com
docker build -t web-app .
docker tag web-app:latest 1234567.dkr.ecr.us-east-1.amazonaws.com/web-app-repository:dev-latest
docker push 1234567.dkr.ecr.us-east-1.amazonaws.com/web-app-repository:dev-latest
به پوشه ecs برگردید، provider.tf خود را با سطل backend s3 برای ecs خود بهروزرسانی کنید و از دادههای شبکه state backend استفاده کنید تا به صورت پویا از اجزای شبکه مانند vpc، subnets و غیره استفاده کنید.
provider.tf
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.73.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.6.0"
}
}
backend "s3" {
bucket = "my-project-terraform-state-prod" # Replace with your S3 bucket name
key = "ecs/terraform.tfstate" # Unique key for ECS state file
region = "us-east-1" # Replace with your AWS region
dynamodb_table = "my-project-terraform-lock" # Replace with your DynamoDB table name
encrypt = true
}
}
provider "aws" {
region = var.region
# Optional: Default tags that will be applied to all resources
}
data "terraform_remote_state" "vpc" {
backend = "s3"
config = {
bucket = "my-project-terraform-state-prod" # Replace with your S3 bucket name
key = "networking/terraform.tfstate" # Path to the networking state file
region = "us-east-1" # Replace with your AWS region
dynamodb_table = "my-project-terraform-lock" # Replace with your DynamoDB table name
encrypt = true
}
}
پس از پیکربندی باطن، مقادیر حساب خود را در terraform.tfvars به روز کنید
# Environment and Region
env_name_prod = "prod"
env_name_dev = "dev" # Change to "prod" for production
region = "us-east-1" # AWS region
# Application Details
app_name = "web-app" # Your application name
# AWS Account Details
account_id = "12345678" # Your AWS account ID
# ALB Configuration
alb_hostname_dev = "web-app-dev.12345678.realhandsonlabs.net"
alb_hostname_prod = "web-app-prod.12345678.realhandsonlabs.net"
# Subnet and VPC IDs
route53_zone_id = "Z07843433K5DR356RSU8Z"
برای ایجاد تمامی زیرساخت ها، terraform application را اجرا کنید.
راه اندازی GitLab CI/CD
برای جزئیات بیشتر ساختار پوشه gitlab_cicd را بررسی کنید:
درخت ساختار پوشه Gitlab شما در نهایت باید به این شکل باشد
ما یک خط لوله GitLab CI/CD را از ابتدا ایجاد خواهیم کرد تا استقرار خودکار در AWS ECS Fargate را با استفاده از کانتینرهای Docker، AWS ECR برای ذخیرهسازی تصویر و GitLab YAML برای پیکربندی بسازیم.
ساختار خط لوله: دو شاخه (dev و prod) به صورت پویا سرویس/وظیفه ECS هدف را بر اساس نام محیط تعریف می کنند و برنامه را مستقر می کنند. مراحل: اعتبارسنجی، ساخت، فشار و استقرار را از طریق مراحل تعریف شده CI/CD به صورت خودکار انجام دهید.
کاربر GitLab IAM
- یک کاربر IAM ایجاد کنید 'gitlab-cicd' مجوزهای زیر را بدهید. این کاربر تنها به دسترسی به کنسول برنامهریزی شده نیاز ندارد. کلیدهای دسترسی و مخفی را برای این کاربر ایجاد کنید.
- به حساب Gitlab خود وارد شوید و یک پروژه جدید ایجاد کنید
- وارد ترمینال لینوکس خود شوید و git initialize new repository
- شما باید دو شاخه 'dev' و 'main' ایجاد کنید:
از هر دو شاخه خود در برابر حذف و همچنین فشار بدون ادغام محافظت کنید
3 متغیر را برای خط لوله خود تنظیم کنید، متغیرهای زیر را در تنظیمات GitLab CI/CD اضافه کنید:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION
راه اندازی خطوط لوله
می توانید تمام فایل های CI/CD را از مخزن gitlab من https://gitlab.com/sahib.gasimov2/gitlabcicd-ecs کپی کنید.
فایل gitlab-ci.yml کل خط لوله را برای استقرار یک برنامه وب در AWS ECS خودکار می کند. این محیط را تأیید میکند، تصاویر Docker را میسازد و به AWS ECR میفرستد، و در سرویسهای ECS مستقر میشود (شاخه توسعه خودکار مستقر میشود، شعبه اصلی برای تولید به تأیید دستی نیاز دارد). از متغیرهای محیطی برای انعطافپذیری استفاده میکند و بهروزرسانیهای ECS را با استقرارهای جدید اجباری برای محیطهای توسعهدهنده و پرود ادغام میکند.
AWS_ACCOUNT_NUMBER و REGION را با مقادیر خود بهروزرسانی کنید.
stages:
- validate_environment
- build_and_publish
- deploy_to_dev
- deploy_to_production
- finalize_pipeline
workflow: # Trigger pipeline only for specific branches
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "dev"'
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
variables:
AWS_ACCOUNT_NUMBER : "1245678" #update with your account number
REGION : "us-east-1" #update with your region
IMAGE_REPOSITORY : "web-app-repository"
CLUSTER_NAME : "web-app-cluster"
DEV_SERVICE_NAME : "web-app-dev"
DEV_TASK_NAME : "web-app-dev"
PROD_SERVICE_NAME : "web-app-prod"
PROD_TASK_NAME : "web-app-prod"
test_environment:
stage: validate_environment
script:
- echo "Validating environment setup..."
- aws --version
- docker --version
- jq --version
- aws sts get-caller-identity
build_and_push:
stage: build_and_publish
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2375
before_script:
- aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_NUMBER.dkr.ecr.$REGION.amazonaws.com
script:
- echo "Building the Docker image..."
- docker build -t $IMAGE_REPOSITORY .
- echo "Tagging the image..."
- docker tag $IMAGE_REPOSITORY:latest $AWS_ACCOUNT_NUMBER.dkr.ecr.$REGION.amazonaws.com/$IMAGE_REPOSITORY:$CI_COMMIT_BRANCH-latest
- docker tag $IMAGE_REPOSITORY:latest $AWS_ACCOUNT_NUMBER.dkr.ecr.$REGION.amazonaws.com/$IMAGE_REPOSITORY:$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
- echo "Pushing the image to ECR..."
- docker push $AWS_ACCOUNT_NUMBER.dkr.ecr.$REGION.amazonaws.com/$IMAGE_REPOSITORY:$CI_COMMIT_BRANCH-latest
- docker push $AWS_ACCOUNT_NUMBER.dkr.ecr.$REGION.amazonaws.com/$IMAGE_REPOSITORY:$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
deploy_to_dev:
stage: deploy_to_dev
rules:
- if: '$CI_COMMIT_BRANCH == "dev"'
script:
- echo "Deploying to development environment..."
- |
aws ecs update-service \
--cluster $CLUSTER_NAME \
--service $DEV_SERVICE_NAME \
--task-definition $DEV_TASK_NAME \
--force-new-deployment
deploy_to_production:
stage: deploy_to_production
when: manual # Require manual confirmation for production
manual_confirmation: 'Proceed with production deployment?'
allow_failure: false # Must succeed to continue
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
script:
- echo "Deploying to production environment..."
- |
aws ecs update-service \
--cluster $CLUSTER_NAME \
--service $PROD_SERVICE_NAME \
--task-definition $PROD_TASK_NAME \
--force-new-deployment
finalize_pipeline:
stage: finalize_pipeline
script:
- echo "CI/CD Pipeline completed successfully!"
پس از ایجاد یک شاخه و ادغام تغییر به توسعه دهنده یا شاخه اصلی، شاخه خط لوله را راه اندازی می کند و برنامه را اجرا و به روز می کند.
برنامه پایتون و داکرفایل
من از یک برنامه ساده پایتون برای این نسخه آزمایشی استفاده خواهم کرد که به سادگی تصویر را به PDF تبدیل می کند.
from flask import Flask, request, send_file, jsonify
from PIL import Image
import os
app = Flask(__name__)
UPLOAD_FOLDER = 'uploads'
OUTPUT_FOLDER = 'output'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
@app.route('/')
def home():
return '''
Image to PDF Converter
'''
@app.route('/convert', methods=['POST'])
def convert_to_pdf():
if 'image' not in request.files:
return "No file uploaded", 400
file = request.files['image']
if file.filename == '':
return "No selected file", 400
try:
# Save the uploaded file
image_path = os.path.join(UPLOAD_FOLDER, file.filename)
file.save(image_path)
# Convert to PDF
image = Image.open(image_path)
if image.mode != 'RGB':
image = image.convert('RGB')
pdf_path = os.path.join(OUTPUT_FOLDER, f"{os.path.splitext(file.filename)[0]}.pdf")
image.save(pdf_path, "PDF")
# Send the PDF file as a response
return send_file(pdf_path, as_attachment=True)
except Exception as e:
return f"An error occurred: {e}", 500
# Health check endpoint for ALB
@app.route('/api/health', methods=['GET'])
def health_check():
return jsonify({"status": "healthy"}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080, debug=True)
Dockerfile
# Use an official Python runtime as the base image
FROM python:3.9-slim
# Set the working directory in the container
WORKDIR /app
# Copy the application code and requirements into the container
COPY . /app
# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Expose port 8080 for the Flask application
EXPOSE 8080
# Run the Flask application
CMD ["python", "app.py"]
از پروژه خود لذت ببرید!