Tutorial Lengkap Terraform untuk ML: Infrastructure as Code untuk Machine Learning

# Tutorial Lengkap Terraform untuk ML: Infrastructure as Code untuk Machine Learning Terraform adalah tool Infrastructure as Code (IaC) yang memungkinkan Anda melakukan provisioning dan mengelola clo...

By Ruby Abdullah · · tutorial
TerraformInfrastructure as CodeAWSMLOpsCloudMachine Learning

Tutorial Lengkap Terraform untuk ML: Infrastructure as Code untuk Machine Learning

Terraform adalah tool Infrastructure as Code (IaC) yang memungkinkan Anda melakukan provisioning dan mengelola cloud infrastructure secara deklaratif. Untuk tim ML, Terraform membantu mengotomatisasi deployment training clusters, inference endpoints, dan data pipelines.

Mengapa Terraform untuk ML?

Keunggulan Terraform:
  • Reproducibility: Infrastructure sama setiap waktu
  • Version control: Track perubahan infrastructure
  • Multi-cloud: Support AWS, GCP, Azure
  • Modularity: Reusable infrastructure components
  • State management: Track resource state

Use Cases:
  • ML training infrastructure
  • Inference endpoint deployment
  • Data pipeline provisioning
  • Development environments
  • Multi-region deployments

Instalasi

# macOS

brew tap hashicorp/tap

brew install hashicorp/tap/terraform

Linux

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 $(lsbrelease -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

sudo apt update && sudo apt install terraform

Verify installation

terraform --version

Quick Start

1. Project Structure

ml-infrastructure/

├── main.tf # Main configuration

├── variables.tf # Input variables

├── outputs.tf # Output values

├── providers.tf # Provider configuration

├── terraform.tfvars # Variable values

└── modules/

├── training/

├── inference/

└── storage/

2. Basic Configuration

# providers.tf

terraform {

requiredproviders {

aws = {

source = "hashicorp/aws"

version = "~> 5.0"

}

}

requiredversion = ">= 1.0"

}

provider "aws" {

region = var.awsregion

}

# variables.tf

variable "awsregion" {

description = "AWS region"

type = string

default = "us-west-2"

}

variable "environment" {

description = "Environment name"

type = string

default = "dev"

}

variable "projectname" {

description = "Project name"

type = string

default = "ml-platform"

}

3. Basic Commands

# Initialize Terraform

terraform init

Format configuration

terraform fmt

Validate configuration

terraform validate

Plan changes

terraform plan

Apply changes

terraform apply

Destroy infrastructure

terraform destroy

S3 untuk ML Data

1. Data Lake Setup

# modules/storage/main.tf

resource "awss3bucket" "mldata" {

bucket = "${var.projectname}-${var.environment}-ml-data"

tags = {

Environment = var.environment

Project = var.projectname

Purpose = "ML Data Lake"

}

}

resource "awss3bucketversioning" "mldata" {

bucket = awss3bucket.mldata.id

versioningconfiguration {

status = "Enabled"

}

}

resource "awss3bucketlifecycleconfiguration" "mldata" {

bucket = awss3bucket.mldata.id

rule {

id = "archive-old-data"

status = "Enabled"

transition {

days = 90

storageclass = "STANDARDIA"

}

transition {

days = 180

storageclass = "GLACIER"

}

}

}

Folder structure

resource "awss3object" "rawdata" {

bucket = awss3bucket.mldata.id

key = "raw/"

contenttype = "application/x-directory"

}

resource "awss3object" "processeddata" {

bucket = awss3bucket.mldata.id

key = "processed/"

contenttype = "application/x-directory"

}

resource "awss3object" "models" {

bucket = awss3bucket.mldata.id

key = "models/"

contenttype = "application/x-directory"

}

2. Model Registry Bucket

resource "awss3bucket" "modelregistry" {

bucket = "${var.projectname}-${var.environment}-models"

tags = {

Purpose = "Model Registry"

}

}

resource "awss3bucketpublicaccessblock" "modelregistry" {

bucket = awss3bucket.modelregistry.id

blockpublicacls = true

blockpublicpolicy = true

ignorepublicacls = true

restrictpublicbuckets = true

}

EC2 Training Instances

1. GPU Training Instance

# modules/training/main.tf

data "awsami" "deeplearning" {

mostrecent = true

owners = ["amazon"]

filter {

name = "name"

values = ["Deep Learning AMI (Ubuntu 22.04) "]

}

}

resource "awsinstance" "training" {

ami = data.awsami.deeplearning.id

instancetype = var.instancetype # "p3.2xlarge" untuk GPU

vpcsecuritygroupids = [awssecuritygroup.training.id]

subnetid = var.subnetid

iaminstanceprofile = awsiaminstanceprofile.training.name

rootblockdevice {

volumesize = 200

volumetype = "gp3"

}

userdata = <<-EOF

#!/bin/bash

# Install additional packages

pip install mlflow wandb

# Download training data

aws s3 sync s3://${var.databucket}/processed/ /data/

# Start training (contoh)

python /opt/ml/train.py

EOF

tags = {

Name = "${var.projectname}-training"

Environment = var.environment

}

}

resource "awssecuritygroup" "training" {

name = "${var.projectname}-training-sg"

description = "Security group untuk training instances"

vpcid = var.vpcid

egress {

fromport = 0

toport = 0

protocol = "-1"

cidrblocks = ["0.0.0.0/0"]

}

tags = {

Name = "${var.projectname}-training-sg"

}

}

2. Spot Instance untuk Cost Savings

resource "awsspotinstancerequest" "trainingspot" {

ami = data.awsami.deeplearning.id

instancetype = "p3.2xlarge"

spotprice = "0.90" # Max price per jam

spottype = "one-time"

waitforfulfillment = true

vpcsecuritygroupids = [awssecuritygroup.training.id]

subnetid = var.subnetid

iaminstanceprofile = awsiaminstanceprofile.training.name

rootblockdevice {

volumesize = 200

volumetype = "gp3"

}

tags = {

Name = "${var.projectname}-training-spot"

}

}

SageMaker Resources

1. SageMaker Domain

# modules/sagemaker/main.tf

resource "awssagemakerdomain" "mldomain" {

domainname = "${var.projectname}-${var.environment}"

authmode = "IAM"

vpcid = var.vpcid

subnetids = var.subnetids

defaultusersettings {

executionrole = awsiamrole.sagemakerexecution.arn

jupyterserverappsettings {

defaultresourcespec {

instancetype = "system"

}

}

kernelgatewayappsettings {

defaultresourcespec {

instancetype = "ml.t3.medium"

}

}

}

tags = {

Environment = var.environment

Project = var.projectname

}

}

resource "awssagemakeruserprofile" "datascientist" {

domainid = awssagemakerdomain.mldomain.id

userprofilename = "data-scientist"

usersettings {

executionrole = awsiamrole.sagemakerexecution.arn

}

}

2. SageMaker Training Job

resource "awssagemakertrainingjob" "modeltraining" {

trainingjobname = "${var.projectname}-${var.modelversion}"

rolearn = awsiamrole.sagemakerexecution.arn

algorithmspecification {

trainingimage = var.trainingimage

traininginputmode = "File"

}

resourceconfig {

instancecount = 1

instancetype = "ml.p3.2xlarge"

volumesizeingb = 50

}

inputdataconfig {

channelname = "training"

datasource {

s3datasource {

s3uri = "s3://${var.databucket}/processed/train/"

s3datatype = "S3Prefix"

}

}

}

outputdataconfig {

s3outputpath = "s3://${var.modelbucket}/output/"

}

stoppingcondition {

maxruntimeinseconds = 86400 # 24 jam

}

hyperparameters = {

epochs = "100"

learningrate = "0.001"

batchsize = "32"

}

}

3. SageMaker Endpoint

resource "awssagemakermodel" "inferencemodel" {

name = "${var.projectname}-model-${var.modelversion}"

executionrolearn = awsiamrole.sagemakerexecution.arn

primarycontainer {

image = var.inferenceimage

modeldataurl = "s3://${var.modelbucket}/models/${var.modelversion}/model.tar.gz"

environment = {

SAGEMAKERPROGRAM = "inference.py"

}

}

tags = {

ModelVersion = var.modelversion

}

}

resource "awssagemakerendpointconfiguration" "inference" {

name = "${var.projectname}-endpoint-config"

productionvariants {

variantname = "primary"

modelname = awssagemakermodel.inferencemodel.name

initialinstancecount = var.endpointinstancecount

instancetype = var.endpointinstancetype

initialvariantweight = 1

}

# Enable data capture

datacaptureconfig {

enablecapture = true

initialsamplingpercentage = 100

destinations3uri = "s3://${var.databucket}/endpoint-data-capture/"

captureoptions {

capturemode = "Input"

}

captureoptions {

capturemode = "Output"

}

}

}

resource "awssagemakerendpoint" "inference" {

name = "${var.projectname}-endpoint"

endpointconfigname = awssagemakerendpointconfiguration.inference.name

tags = {

Environment = var.environment

}

}

EKS untuk ML Workloads

1. EKS Cluster

# modules/eks/main.tf

module "eks" {

source = "terraform-aws-modules/eks/aws"

version = "~> 19.0"

clustername = "${var.projectname}-${var.environment}"

clusterversion = "1.28"

vpcid = var.vpcid

subnetids = var.privatesubnetids

clusterendpointpublicaccess = true

eksmanagednodegroups = {

# CPU nodes untuk general workloads

cpunodes = {

name = "cpu-nodes"

instancetypes = ["m5.xlarge"]

minsize = 1

maxsize = 10

desiredsize = 2

labels = {

workload = "general"

}

}

# GPU nodes untuk training

gpunodes = {

name = "gpu-nodes"

instancetypes = ["p3.2xlarge"]

minsize = 0

maxsize = 5

desiredsize = 0

amitype = "AL2x8664GPU"

labels = {

workload = "gpu"

"nvidia.com/gpu" = "true"

}

taints = [{

key = "nvidia.com/gpu"

value = "true"

effect = "NOSCHEDULE"

}]

}

}

tags = {

Environment = var.environment

Project = var.projectname

}

}

Install NVIDIA device plugin

resource "kubernetesdaemonset" "nvidiadeviceplugin" {

dependson = [module.eks]

metadata {

name = "nvidia-device-plugin-daemonset"

namespace = "kube-system"

}

spec {

selector {

matchlabels = {

name = "nvidia-device-plugin-ds"

}

}

template {

metadata {

labels = {

name = "nvidia-device-plugin-ds"

}

}

spec {

toleration {

key = "nvidia.com/gpu"

operator = "Exists"

effect = "NoSchedule"

}

container {

name = "nvidia-device-plugin-ctr"

image = "nvcr.io/nvidia/k8s-device-plugin:v0.14.0"

securitycontext {

allowprivilegeescalation = false

capabilities {

drop = ["ALL"]

}

}

volumemount {

name = "device-plugin"

mountpath = "/var/lib/kubelet/device-plugins"

}

}

volume {

name = "device-plugin"

hostpath {

path = "/var/lib/kubelet/device-plugins"

}

}

}

}

}

}

2. ML Workload di Kubernetes

# Training Job

resource "kubernetesjob" "trainingjob" {

metadata {

name = "model-training-${var.modelversion}"

namespace = "ml-workloads"

}

spec {

template {

metadata {

labels = {

app = "model-training"

}

}

spec {

nodeselector = {

"nvidia.com/gpu" = "true"

}

toleration {

key = "nvidia.com/gpu"

operator = "Exists"

effect = "NoSchedule"

}

container {

name = "training"

image = var.trainingimage

resources {

limits = {

"nvidia.com/gpu" = "1"

memory = "32Gi"

cpu = "8"

}

}

env {

name = "S3DATAPATH"

value = "s3://${var.databucket}/processed/"

}

env {

name = "S3MODELPATH"

value = "s3://${var.modelbucket}/models/"

}

}

restartpolicy = "Never"

}

}

backofflimit = 2

}

}

IAM Roles untuk ML

1. SageMaker Execution Role

# modules/iam/main.tf

resource "awsiamrole" "sagemakerexecution" {

name = "${var.projectname}-sagemaker-execution"

assumerolepolicy = jsonencode({

Version = "2012-10-17"

Statement = [{

Action = "sts:AssumeRole"

Effect = "Allow"

Principal = {

Service = "sagemaker.amazonaws.com"

}

}]

})

}

resource "awsiamrolepolicy" "sagemakers3" {

name = "sagemaker-s3-access"

role = awsiamrole.sagemakerexecution.id

policy = jsonencode({

Version = "2012-10-17"

Statement = [

{

Effect = "Allow"

Action = [

"s3:GetObject",

"s3:PutObject",

"s3:DeleteObject",

"s3:ListBucket"

]

Resource = [

"arn:aws:s3:::${var.databucket}",

"arn:aws:s3:::${var.databucket}/",

"arn:aws:s3:::${var.modelbucket}",

"arn:aws:s3:::${var.modelbucket}/"

]

},

{

Effect = "Allow"

Action = [

"logs:CreateLogGroup",

"logs:CreateLogStream",

"logs:PutLogEvents"

]

Resource = "arn:aws:logs:::"

},

{

Effect = "Allow"

Action = [

"ecr:GetDownloadUrlForLayer",

"ecr:BatchGetImage",

"ecr:BatchCheckLayerAvailability"

]

Resource = "*"

}

]

})

}

2. Training Instance Role

resource "awsiamrole" "traininginstance" {

name = "${var.projectname}-training-instance"

assumerolepolicy = jsonencode({

Version = "2012-10-17"

Statement = [{

Action = "sts:AssumeRole"

Effect = "Allow"

Principal = {

Service = "ec2.amazonaws.com"

}

}]

})

}

resource "awsiaminstanceprofile" "training" {

name = "${var.projectname}-training-profile"

role = awsiamrole.traininginstance.name

}

resource "awsiamrolepolicyattachment" "trainings3" {

role = awsiamrole.traininginstance.name

policyarn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"

}

Complete ML Platform Module

# main.tf

module "storage" {

source = "./modules/storage"

projectname = var.projectname

environment = var.environment

}

module "networking" {

source = "./modules/networking"

projectname = var.projectname

environment = var.environment

vpccidr = var.vpccidr

}

module "sagemaker" {

source = "./modules/sagemaker"

projectname = var.projectname

environment = var.environment

vpcid = module.networking.vpcid

subnetids = module.networking.privatesubnetids

databucket = module.storage.databucketid

modelbucket = module.storage.modelbucketid

}

module "eks" {

source = "./modules/eks"

projectname = var.projectname

environment = var.environment

vpcid = module.networking.vpcid

privatesubnetids = module.networking.privatesubnetids

}

outputs.tf

output "sagemakerendpoint" {

value = module.sagemaker.endpointurl

}

output "eksclusterendpoint" {

value = module.eks.clusterendpoint

}

output "databucket" {

value = module.storage.databucketid

}

Integrasi CI/CD

1. GitHub Actions

# .github/workflows/terraform.yml

name: Terraform

on:

push:

branches: [main]

pullrequest:

branches: [main]

jobs:

terraform:

runs-on: ubuntu-latest

steps:

  • uses: actions/checkout@v3
  • name: Setup Terraform
uses: hashicorp/setup-terraform@v2

  • name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2

with:

aws-access-key-id: ${{ secrets.AWSACCESSKEYID }}

aws-secret-access-key: ${{ secrets.AWSSECRETACCESSKEY }}

aws-region: us-west-2

  • name: Terraform Init
run: terraform init

  • name: Terraform Format
run: terraform fmt -check

  • name: Terraform Plan
run: terraform plan -out=tfplan

  • name: Terraform Apply
if: github.ref == 'refs/heads/main'

run: terraform apply -auto-approve tfplan

2. Remote State

# backend.tf

terraform {

backend "s3" {

bucket = "my-terraform-state-bucket"

key = "ml-platform/terraform.tfstate"

region = "us-west-2"

dynamodbtable = "terraform-locks"

encrypt = true

}

}

Buat state resources

resource "awss3bucket" "terraformstate" {

bucket = "my-terraform-state-bucket"

lifecycle {

preventdestroy = true

}

}

resource "awsdynamodbtable" "terraformlocks" {

name = "terraform-locks"

billingmode = "PAYPERREQUEST"

hashkey = "LockID"

attribute {

name = "LockID"

type = "S"

}

}

Best Practices

1. Gunakan Workspaces

# Buat workspaces

terraform workspace new dev

terraform workspace new staging

terraform workspace new prod

Switch workspace

terraform workspace select prod

Gunakan di configuration

locals {

environment = terraform.workspace

}

2. Module Versioning

module "mlplatform" {

source = "git::https://github.com/org/terraform-ml-platform.git?ref=v1.2.0"

projectname = var.projectname

environment = var.environment

}

3. Cost Tags

locals {

commontags = {

Project = var.projectname

Environment = var.environment

ManagedBy = "terraform"

CostCenter = var.costcenter

Owner = var.owneremail

}

}

resource "awsinstance" "example" {

# ... configuration ...

tags = merge(local.commontags, {

Name = "example-instance"

})

}

Kesimpulan

Terraform adalah essential untuk ML infrastructure dengan:

  • Reproducibility: Consistent environments
  • Version control: Track perubahan infrastructure
  • Modularity: Reusable components
  • Multi-cloud: Support AWS, GCP, Azure
  • CI/CD integration: Automated deployments
  • Key takeaways:

    • Gunakan modules untuk reusability
    • Implement remote state dengan locking
    • Tag resources untuk cost tracking
    • Gunakan workspaces untuk environments
    • Integrate dengan CI/CD pipelines

    Artikel Terkait

    Tutorial Lengkap AWS SageMaker: Machine Learning di Cloud

    Tutorial Lengkap AWS SageMaker: End-to-End ML Pipeline Amazon SageMaker adalah layanan machine learning terkelola penuh ...

    ZenML: Bikin Pipeline ML dan LLM yang Portable dan Siap Produksi

    ZenML: Bikin Pipeline ML dan LLM yang Portable dan Siap Produksi Halo temen-temen, ketemu lagi sama aku, Ruby Abdullah. ...

    Tutorial Lengkap Replicate: Menjalankan dan Deploy Model ML via API

    Tutorial Lengkap Replicate: Menjalankan dan Deploy Model ML via API Replicate adalah platform cloud yang memungkinkan An...

    Tutorial Lengkap Comet ML: Platform MLOps untuk Experiment Tracking dan Model Management

    Tutorial Lengkap Comet ML: Platform MLOps untuk Experiment Tracking dan Model Management Dalam dunia machine learning mo...