CDK for Terraform (CDKTF) với Python: Triển Khai Tài Nguyên Cloud Mà Không Cần Viết HCL

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

Tại Sao Bạn Muốn Thay HCL bằng Python

Nếu bạn đã từng làm việc với Terraform, bạn biết HCL (HashiCorp Configuration Language) hoàn toàn ổn — cho đến khi bạn cần một vòng lặp làm gì đó hơi khác thường, hoặc muốn tái sử dụng logic giữa các stack mà không phải copy-paste block khắp nơi. HCL được thiết kế theo kiểu khai báo, rất tốt cho hạ tầng đơn giản, nhưng khi độ phức tạp tăng lên, bạn sẽ ước mình có thể viết một function.

Đây chính xác là khoảng trống mà CDK for Terraform lấp đầy. CDKTF cho phép bạn viết code hạ tầng bằng Python (hoặc TypeScript, Go, Java, C#) và tự động sinh ra cấu hình Terraform JSON bên dưới. State, provider và backend Terraform thực tế của bạn vẫn giữ nguyên — bạn chỉ đang dùng một ngôn ngữ lập trình thực sự để định nghĩa chúng.

Tôi đã áp dụng cách tiếp cận này trong môi trường production trên nhiều AWS environment, và kết quả luôn ổn định. Terraform JSON được tạo ra là hợp lệ, dự đoán được, và tương thích tốt với các CI/CD pipeline hiện có đang chạy terraform planterraform apply.

Khi Nào CDKTF Phù Hợp

  • Bạn cần logic có điều kiện hoặc vòng lặp mà viết trong HCL cảm thấy vụng về
  • Team của bạn đã quen Python nhưng thấy cú pháp HCL lạ lẫm
  • Bạn muốn chia sẻ các pattern hạ tầng dưới dạng Python package
  • Bạn đang xây dựng nền tảng nội bộ cho developer mà hạ tầng được sinh ra động

Nếu hạ tầng của bạn nhỏ và đơn giản, Terraform thuần là đủ. Nhưng khi bạn quản lý hàng chục environment với cấu hình dùng chung, CDKTF bắt đầu phát huy giá trị.

Cài Đặt

CDKTF có hai yêu cầu runtime: Node.js (cho CLI) và Python (cho code stack thực tế). Bạn cần cả hai dù đang viết Python — vì CLI là công cụ Node.js.

Bước 1: Cài Đặt Node.js

CLI CDKTF yêu cầu Node.js 18 trở lên. Nếu bạn đang dùng Ubuntu/Debian:

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node --version  # phải là v20.x hoặc mới hơn

Trên macOS với Homebrew:

brew install node

Bước 2: Cài Đặt CDKTF CLI

npm install -g cdktf-cli
cdktf --version  # xác nhận cài đặt thành công

Bước 3: Cài Đặt Terraform

CDKTF sinh ra cấu hình Terraform, vì vậy bạn vẫn cần Terraform để thực hiện provisioning thực tế:

# Trên Ubuntu/Debian
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
terraform --version

Bước 4: Khởi Tạo Dự Án CDKTF Mới

Tạo thư mục mới và khởi tạo dự án Python:

mkdir my-cdktf-project && cd my-cdktf-project
cdktf init --template=python --local

Flag --local lưu Terraform state cục bộ (file terraform.tfstate). Cho môi trường production, bạn sẽ trỏ nó vào S3 hoặc Terraform Cloud, nhưng state cục bộ là đủ để học.

Sau khi khởi tạo, cấu trúc thư mục sẽ trông như thế này:

my-cdktf-project/
├── main.py          ← định nghĩa stack của bạn ở đây
├── cdktf.json       ← cấu hình CDKTF (providers, thư mục output)
├── requirements.txt
└── .gen/            ← provider bindings tự sinh (không chỉnh sửa)

Bước 5: Thiết Lập Python Virtual Environment

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Cấu Hình

Đây là phần thú vị — viết hạ tầng thực tế bằng Python. Ví dụ này triển khai một AWS S3 bucket với tính năng versioning được bật. Ví dụ nhỏ, nhưng minh họa đầy đủ pattern bạn sẽ dùng cho mọi tài nguyên.

Thêm AWS Provider

Chỉnh sửa cdktf.json để thêm AWS provider:

{
  "language": "python",
  "app": "pipenv run python main.py",
  "terraformProviders": ["aws@~> 5.0"],
  "terraformModules": [],
  "output": "cdktf.out"
}

Sau đó sinh provider bindings. Lệnh này tải schema AWS provider và sinh ra các class Python có kiểu dữ liệu cho mọi tài nguyên AWS:

cdktf get

Quá trình này mất một đến hai phút. Khi hoàn tất, bạn sẽ thấy hàng nghìn class trong .gen/providers/aws/ — một class cho mỗi loại tài nguyên AWS.

Viết Stack bằng Python

Mở main.py và thay thế nội dung mặc định:

from constructs import Construct
from cdktf import App, TerraformStack, TerraformOutput
from cdktf_cdktf_provider_aws.provider import AwsProvider
from cdktf_cdktf_provider_aws.s3_bucket import S3Bucket
from cdktf_cdktf_provider_aws.s3_bucket_versioning import (
    S3BucketVersioning,
    S3BucketVersioningVersioningConfiguration,
)


class MyInfraStack(TerraformStack):
    def __init__(self, scope: Construct, id: str, env: str):
        super().__init__(scope, id)

        # Cấu hình AWS provider
        AwsProvider(self, "AWS", region="ap-northeast-1")

        # Tạo S3 bucket
        bucket = S3Bucket(
            self,
            "app-bucket",
            bucket=f"my-app-assets-{env}",
            tags={"Environment": env, "ManagedBy": "cdktf"},
        )

        # Bật versioning cho bucket đó
        S3BucketVersioning(
            self,
            "bucket-versioning",
            bucket=bucket.id,
            versioning_configuration=S3BucketVersioningVersioningConfiguration(
                status="Enabled"
            ),
        )

        # Xuất tên bucket để có thể tham chiếu sau này
        TerraformOutput(
            self,
            "bucket_name",
            value=bucket.bucket,
            description="Tên của S3 bucket vừa tạo",
        )


app = App()
MyInfraStack(app, "staging", env="staging")
MyInfraStack(app, "production", env="production")
app.synth()

Lưu ý điều đang xảy ra ở đây: tham số env cho phép bạn khởi tạo cùng một stack hai lần — một lần cho staging, một lần cho production — mà không cần trùng lặp bất cứ thứ gì. Đây chính là điểm mạnh của Python so với HCL; stack được tham số hóa chỉ là các instance của class.

Tổng Hợp Cấu Hình Terraform

CDKTF không triển khai trực tiếp — nó trước tiên sinh ra Terraform JSON, rồi bạn mới apply. Chạy:

cdktf synth

Lệnh này tạo ra cdktf.out/stacks/staging/cdktf.out/stacks/production/, mỗi thư mục chứa một file cdk.tf.json. Bạn có thể kiểm tra file đó để xác nhận những gì CDKTF đã sinh ra — đây là Terraform JSON chuẩn và bạn có thể đọc như bất kỳ cấu hình Terraform nào.

Triển Khai lên AWS

Đảm bảo thông tin xác thực AWS của bạn đã được cấu hình (biến môi trường, ~/.aws/credentials, hoặc IAM role), sau đó:

# Xem trước các thay đổi trước khi apply
cdktf plan staging

# Apply lên staging
cdktf deploy staging

# Apply lên cả hai stack cùng một lúc
cdktf deploy --all

Bạn sẽ thấy output quen thuộc của Terraform plan — CDKTF chỉ đơn giản chuyển tiếp sang lệnh terraform apply bên dưới cho mỗi stack.

Kiểm Tra và Giám Sát

Khi deployment hoàn tất, hãy xác nhận mọi thứ đã đúng và chuẩn bị để phát hiện sớm các thay đổi ngoài mong muốn.

Kiểm Tra Output của Stack

Sau khi cdktf deploy hoàn tất, các output được in trực tiếp trong terminal. Để truy vấn lại sau này:

cdktf output staging

Bạn sẽ thấy output bucket_name mà chúng ta đã định nghĩa — hữu ích để truyền giá trị giữa các stack hoặc vào cấu hình ứng dụng.

Xác Nhận Tài Nguyên trên AWS

# Liệt kê bucket và xác nhận bucket mới đã tồn tại
aws s3 ls | grep my-app-assets

# Kiểm tra versioning thực sự đã được bật
aws s3api get-bucket-versioning --bucket my-app-assets-staging

Output mong đợi khi kiểm tra versioning:

{
    "Status": "Enabled"
}

Phát Hiện Configuration Drift

Nếu ai đó thay đổi thủ công một tài nguyên trên AWS console, CDKTF (qua Terraform) sẽ phát hiện sự thay đổi đó trong lần plan tiếp theo:

cdktf diff staging

Lệnh này chạy terraform plan so với state hiện tại và hiển thị những gì đã thay đổi ngoài code của bạn. Chạy lệnh này trong CI theo lịch định kỳ là cách rẻ để phát hiện các thay đổi trái phép trước khi chúng gây ra vấn đề.

Xóa Tài Nguyên Khi Xong

Cho các môi trường test bạn muốn dọn sạch:

cdktf destroy staging

Luôn kiểm tra kỹ lệnh này trước khi chạy trên bất cứ thứ gì gần production — nó không thể hoàn tác đối với các tài nguyên có trạng thái như database và S3 bucket chứa dữ liệu.

Tích Hợp với CI/CD

Một workflow GitHub Actions tối giản để plan khi có pull request và apply khi merge:

name: CDKTF Deploy
on:
  push:
    branches: [main]
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: npm install -g cdktf-cli
      - run: pip install -r requirements.txt
      - run: cdktf get
      - name: Plan (chỉ khi PR)
        if: github.event_name == 'pull_request'
        run: cdktf plan --all
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      - name: Deploy (chỉ khi trên nhánh main)
        if: github.ref == 'refs/heads/main'
        run: cdktf deploy --all --auto-approve
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Pattern ở đây — plan khi có PR, apply khi merge — là cùng workflow mà các team dùng với Terraform thuần. CDKTF tích hợp vào mà không làm gián đoạn quy trình hiện có.

Một điều cần lưu ý: bước cdktf get tái sinh provider bindings. Ghim phiên bản provider trong cdktf.json để tránh bất ngờ khi HashiCorp phát hành phiên bản provider mới giữa chừng trong pipeline.

Share: