Chuyển đến nội dung chính

Terraform Docker: Hướng dẫn Toàn diện

Làm chủ hạ tầng dưới dạng mã bằng cách xây dựng các triển khai container có thể tái tạo với Terraform trên Docker. Tìm hiểu quy trình từ thiết lập provider đến CI/CD tự động.
· 13 phút đọc

Khám phá cùng AI

Tôi đã dành vô số giờ để quản lý thủ công các container Docker trên môi trường phát triển và sản xuất. Cấu hình lệch nhau, trạng thái trôi dạt, và cảm giác không chắc chắn liệu thiết lập cục bộ có khớp với sản xuất hay không luôn ám ảnh mỗi lần triển khai. Khi tôi khám phá ra Docker provider của Terraform, mọi thứ đã thay đổi.

Trong hướng dẫn này, tôi sẽ dẫn bạn tích hợp Terraform với Docker để tạo hạ tầng có thể tái tạo và được kiểm soát phiên bản. Bạn sẽ học cách cấp phát container, quản lý mạng và volume, áp dụng giới hạn tài nguyên, và duy trì trạng thái trong toàn nhóm.

Dù bạn đang quản lý máy chủ build hay điều phối microservice, hãy mở terminal và cùng thay đổi cách bạn vận hành hạ tầng Docker.

Nếu bạn mới làm quen với Docker, tôi khuyến nghị bắt đầu với khóa học Giới thiệu về Docker của chúng tôi.

Tích hợp Terraform Docker là gì?

Terraform là công cụ hạ tầng dưới dạng mã quản lý vòng đời tài nguyên thông qua quy trình khai báo. Thay vì chạy các lệnh Docker riêng lẻ, bạn định nghĩa trạng thái hạ tầng mong muốn trong các tệp cấu hình. Terraform sau đó thực thi vòng đời plan-apply-destroy: tính toán thay đổi cần thiết, hiển thị bản xem trước, và chỉ áp dụng sau khi được phê duyệt.

Terraform Docker provider đóng vai trò lớp chuyển dịch giữa mã HashiCorp Configuration Language (HCL) và Docker API. Khi viết resource “docker_container" trong cấu hình, provider sẽ chuyển tuyên bố đó thành các lời gọi Docker API tương ứng. Sự trừu tượng này giúp quản lý tài nguyên Docker bằng cùng quy trình dùng cho hạ tầng đám mây.

Sau đây là điểm Terraform khác với Docker Compose.

Compose quản lý container theo dự án dựa trên tệp YAML, nhưng không duy trì tệp trạng thái theo dõi cái gì thực sự đang tồn tại. 

Ngược lại, Terraform ghi lại mọi tài nguyên nó tạo trong terraform.tfstate, cho phép phát hiện trôi dạt giữa cấu hình và thực tế. Nếu ai đó chỉnh sửa thủ công một container, Terraform sẽ nhận ra và có thể khôi phục về trạng thái đã định nghĩa.

Docker Compose vs Terraform Docker

Cách tiếp cận này tỏa sáng trong nhiều kịch bản:

  • Với máy chủ build có thể tái tạo, bạn có thể hủy và tạo lại toàn bộ môi trường bằng một lệnh, đảm bảo tính nhất quán. 
  • Trong môi trường kiểm thử, bạn có thể khởi tạo hạ tầng biệt lập cho mỗi lần chạy test. 
  • Với triển khai bất biến, bạn thay thế hoàn toàn container thay vì chỉnh sửa tại chỗ, giúp giảm rủi ro triển khai.

Yêu cầu tiên quyết và Cài đặt

Trước khi đi sâu vào cấu hình Terraform, hãy xác minh rằng các công cụ cần thiết đã được cài và cấu hình đúng.

Đáp ứng yêu cầu hệ thống

Đầu tiên, xác minh Docker Engine đang chạy trên máy của bạn. 

  • Linux: Kiểm tra bằng docker ps

  • macOS hoặc Windows: Đảm bảo Docker Desktop đã được cài và đang chạy

Terraform provider cần truy cập Docker daemon, nên bạn phải xác nhận người dùng của bạn có quyền truy cập socket Unix tại /var/run/docker.sock trên Linux hoặc named pipe trên Windows.

Tiếp theo, cài Terraform CLI từ trang chính thức của HashiCorp. Cài đặt Terraform khác nhau tùy hệ điều hành:

  • Linux: Tải gói phù hợp hoặc dùng trình quản lý gói của bản phân phối. 

  • macOS: Dùng Homebrew. Trước tiên, thêm tap của HashiCorp bằng brew tap hashicorp/tap, sau đó cài Terraform với lệnh brew install hashicorp/tap/terraform. 

  • Windows: Tải binary từ hashicorp.com và thêm vào PATH, hoặc dùng Chocolatey: choco install terraform. 

Sau khi cài xong, xác minh bằng cách chạy terraform version. 

Khi đã xác minh Docker và Terraform, bước tiếp theo là tổ chức tệp dự án để dễ bảo trì.

Cấu hình cấu trúc dự án

Các dự án Terraform hưởng lợi từ cách tiếp cận rõ ràng và có cấu trúc. Thông thường, tôi tạo một thư mục riêng cho dự án Terraform và khởi tạo kiểm soát phiên bản:

mkdir terraform-docker-project
cd terraform-docker-project
git init

Kiểm soát phiên bản là tối quan trọng với hạ tầng dưới dạng mã. Mọi thay đổi đối với hạ tầng Docker cần được theo dõi, rà soát và có thể hoàn nguyên. Thông thường tôi thêm thư mục làm việc và tệp trạng thái của Terraform vào tệp .gitignore để loại trừ chúng khỏi kiểm soát phiên bản:

.terraform/
*.tfstate
*.tfstate.backup
.terraform.lock.hcl

Cuối cùng, tôi tạo một tệp main.tf trống để định nghĩa hạ tầng. Tệp này sẽ chứa cấu hình provider và các khai báo tài nguyên.

Terraform Docker Provider và Tài nguyên Cốt lõi

Khi đã có cấu trúc dự án, đã đến lúc cấu hình Docker provider và bắt đầu định nghĩa tài nguyên.

Khởi tạo Docker provider

Đầu tiên, thêm cấu hình provider vào main.tf:

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0"
    }
  }
}

provider "docker" {
  host = "unix:///var/run/docker.sock"
}

Khối required_providers chỉ định nơi Terraform sẽ tải plugin provider. Sử dụng ràng buộc phiên bản (~> 3.0) cho phép cập nhật phiên bản nhỏ đồng thời tránh thay đổi phá vỡ. Với Docker host từ xa, bạn có thể đổi tham số host thành dạng như tcp://192.168.1.100:2376.

Bây giờ hãy chạy terraform init tại thư mục gốc dự án. Lệnh này tải plugin Docker provider và lưu trong .terraform/providers/. Lệnh cũng in ra phiên bản cài đặt cụ thể, và Terraform tạo tệp khóa để đảm bảo phiên bản provider nhất quán trong nhóm.

Terraform Init

Quản lý image container

Sau khi khởi tạo provider, bước tiếp theo là định nghĩa các image container. Trước khi tạo container, bạn cần quản lý các image sẽ sử dụng, vì vậy hãy định nghĩa một tài nguyên image:

resource "docker_image" "nginx" {
  name         = "nginx:1.29"
  keep_locally = true
}

Thêm tài nguyên này ngay sau khối provider. Tham số keep_locally quan trọng vì nó ngăn Terraform xóa image khi tôi chạy terraform destroy. Điều này hữu ích khi nhiều container dùng chung một image hoặc khi tôi muốn giữ cache image giữa các lần triển khai.

Trong triển khai sản xuất, hãy ghim image bằng digest SHA256 để đảm bảo tính bất biến:

resource "docker_image" "nginx" {
 name = "nginx@sha256:b60f25eb5acdd79172de1a289891d9cfe4fc3669daddeeb68619240cbe839d9f"
}

Digest có thể lấy từ registry của container. Với image trên Docker Hub như nginx, bạn có thể tìm trong trang chính thức của Docker mục Tags. Dùng digest bảo đảm triển khai đúng cùng một binary image mỗi lần, ngay cả khi ai đó đẩy một image mới với cùng thẻ.

Cấp phát container Docker

Khi đã định nghĩa image, bước tiếp theo là tạo container tham chiếu đến chúng:

resource "docker_container" "web" {
  name  = "nginx-web"
  image = docker_image.nginx.image_id

  ports {
    internal = 80
    external = 8080
  }

  env = [
    "NGINX_HOST=example.com",
    "NGINX_PORT=80"
  ]
}

Image được tham chiếu bằng docker_image.nginx.image_id. Điều này tạo phụ thuộc ngầm: Terraform sẽ không tạo container cho đến khi image tồn tại. Khối ports ánh xạ cổng nội bộ 80 của container sang cổng 8080 trên host, tương đương docker run -p 8080:80.

Các biến môi trường được đưa vào danh sách env dưới dạng chuỗi. Terraform sẽ tạo lại container nếu bất kỳ tham số nào thay đổi, đảm bảo hạ tầng đang chạy luôn khớp cấu hình.

Tại thời điểm này, chạy terraform plan để xem Terraform sẽ tạo gì, sau đó terraform apply để cấp phát hạ tầng. Terraform sẽ tạo image và container. Bạn có thể xác minh bằng Docker Desktop hoặc chạy docker ps trong terminal.

Mạng, Lưu trữ và Giới hạn Tài nguyên trong Terraform

Giờ bạn đã có một container cơ bản và hiểu quy trình cốt lõi, hãy mở rộng hạ tầng với các tính năng sẵn sàng cho sản xuất. Các phần tiếp theo xây dựng trên nền tảng này bằng cách thêm mạng, lưu trữ bền vững và ràng buộc tài nguyên — đều thiết yếu cho triển khai thực tế

Quản lý mạng

Để cho phép các container giao tiếp với nhau đồng thời vẫn cô lập, việc tạo mạng riêng là cần thiết. Đây là cách tạo mạng cách ly để kiểm soát kết nối giữa container. Thêm tài nguyên sau vào tệp main.tf:

resource "docker_network" "app_network" {
  name   = "application_net"
  driver = "bridge"
}

Bây giờ thêm container thứ hai sử dụng mạng này. Thêm phần này sau container web:

resource "docker_container" "api" {
  name  = "api-server"
  image = docker_image.nginx.image_id

  networks_advanced {
    name = docker_network.app_network.name
  }
}

Điều này tạo một mạng bridge chuyên dụng và gắn container api vào đó. Các container trên cùng mạng có thể giao tiếp bằng tên container làm hostname, rất phù hợp cho kiến trúc microservice. 

Xác minh cô lập mạng bằng cách chạy docker network inspect application_net và xác nhận chỉ các container mong muốn được kết nối. Hãy đảm bảo bạn chạy lệnh này sau khi tạo tài nguyên bằng terraform plan và terraform apply.

Xác minh Tạo Mạng

Khi đã cấu hình mạng cho giao tiếp giữa các container, khía cạnh quan trọng tiếp theo là tính bền vững của dữ liệu.

Cấu hình volume bền vững

Với dữ liệu cần tồn tại sau khi container khởi động lại, hãy thêm tài nguyên volume. Chèn những phần sau trước các định nghĩa container:

resource "docker_volume" "db_data" {
  name = "postgres_data"
}

Giờ hãy thêm container cơ sở dữ liệu sử dụng volume này:

resource "docker_container" "database" {
  name  = "postgres"
  image = "postgres:15"

 env = [
    "POSTGRES_PASSWORD=mysecretpassword"
  ]

  volumes {
    volume_name    = docker_volume.db_data.name
    container_path = "/var/lib/postgresql/data"
  }
}

Các volume được quản lý theo cách này do Docker tạo và kiểm soát, lưu trong thư mục dữ liệu của Docker. Để chia sẻ tệp cấu hình từ host, dùng bind mount bằng cách chỉ định host_path thay cho volume_name.

volumes {
  host_path      = "/opt/app/config"
  container_path = "/etc/app/config"
  read_only      = true
}

Sau khi đảm bảo dữ liệu tồn tại qua các lần khởi động lại, điều quan trọng không kém là ngăn một container đơn lẻ độc chiếm tài nguyên hệ thống.

Thiết lập ràng buộc tài nguyên

Ngoài lưu trữ, việc kiểm soát lượng CPU và bộ nhớ mỗi container có thể tiêu thụ là rất quan trọng cho sự ổn định hệ thống. Để ngăn container dùng quá mức tài nguyên, hãy áp dụng giới hạn tài nguyên.

Mặc dù Docker cho phép cập nhật một số giới hạn tài nguyên tại chỗ, mô hình khai báo của Terraform thường yêu cầu thay thế container để đảm bảo trạng thái khớp cấu hình. Để tránh downtime trong quá trình thay thế, chúng ta dùng quy tắc vòng đời create_before_destroy. 

Quan trọng: Chúng ta phải bỏ tham số name cố định để tránh xung đột tên trong quá trình thay thế. Một lựa chọn là dùng tham số name_prefix thay thế.

resource "docker_container" "api" {
  name_prefix       = "api-server-"
  image      = docker_image.nginx.image_id
  memory     = 512
  cpu_shares = 1024

  networks_advanced {
    name = docker_network.app_network.name
  }

 lifecycle {
    create_before_destroy = true
  }
}

Tham số memory giới hạn RAM theo megabyte. Tham số cpu_shares hoạt động khác: đó là trọng số tương đối khi có tranh chấp CPU. Container có 1024 shares sẽ nhận gấp đôi thời gian CPU so với container 512 shares, nhưng chỉ khi host bị tải. 

Để đặt giới hạn CPU cứng, bạn cũng có thể dùng cpu_set để ghim container vào các lõi CPU cụ thể:

cpu_set = "0,1"
}

Xác thực các ràng buộc này bằng cách chạy docker inspect background-worker --format='{{.HostConfig.Memory}} {{.HostConfig.CpuShares}} và kiểm tra giá trị Memory và NanoCpus khớp cấu hình. 

Một lần nữa, như đã nêu ở trên, bạn cần chạy terraform plan và terraform apply trước để tạo tài nguyên.

Đến lúc này, tệp main.tf đã chứa cấu hình provider, một image, một mạng, một volume, và ba container với các cấu hình khác nhau. 

Biến, Module và Bí mật trong Terraform

Khi hạ tầng phát triển, các giá trị hardcode trở nên thiếu linh hoạt. Phần này cho thấy cách làm cấu hình có thể tái sử dụng bằng biến, module và xử lý bí mật đúng cách.

Sử dụng biến đầu vào

Bước đầu để làm cấu hình linh hoạt là tách các giá trị hardcode thành biến có thể dễ dàng thay đổi mà không sửa mã chính. Làm điều này bằng cách tạo tệp mới tên variables.tf trong cùng thư mục:

variable "web_port" {
  description = "External port for web container"
  type        = number
  default     = 8080

  validation {
    condition     = var.web_port > 1024 && var.web_port < 65535
    error_message = "Port must be between 1024 and 65535."
  }
}

Bây giờ cập nhật container web trong main.tf để dùng biến này:

resource "docker_container" "web" {
  name  = "nginx-web"
  image = docker_image.nginx.image_id

  ports {
    internal = 80
    external = var.web_port
  }

  env = [
    "NGINX_HOST=example.com",
    "NGINX_PORT=80"
  ]
}

Khối validation ngăn dùng các cổng đặc quyền. Để ghi đè giá trị mặc định, tạo tệp terraform.tfvars:

web_port = 9000

Khi chạy terraform apply, Terraform sẽ tự động đọc giá trị từ tệp này. Hoặc truyền giá trị qua dòng lệnh: terraform apply -var="web_port=9000".

Tạo module có thể tái sử dụng

Biến giải quyết vấn đề giá trị hardcode, nhưng nếu bạn cần triển khai cùng một mẫu toàn bộ nhiều lần, kèm theo tất cả tài nguyên thì sao? 

Đây là nơi module tỏa sáng. Hãy xem module như một bản thiết kế gom tất cả phần rời rạc vào một gói, cho phép bạn “đóng dấu” toàn bộ thiết lập phức tạp đó như một đơn vị duy nhất.

Bắt đầu bằng cách tạo cấu trúc thư mục module:

mkdir -p modules/web-app

Giờ tạo tệp modules/web-app/variables.tf và thêm các nội dung sau:

variable "environment" {
  description = "Environment name"
  type        = string
}

variable "port" {
  description = "External port"
  type        = number
}

Tiếp theo, tạo tệp chính modules/web-app/main.tf với tài nguyên sau:

resource "docker_container" "app" {
  name  = "app-${var.environment}"
  image = var.image_id

  ports {
    internal = 80
    external = var.port
  }
}

Cuối cùng, tạo tệp outputs modules/web-app/outputs.tf, chủ yếu dùng để hiển thị trên terminal giá trị mong muốn sau khi chạy terraform apply:

output "container_ip" {
  value       = docker_container.app.network_data[0].ip_address
  description = "IP address of the application container"
}

Sau khi thêm định nghĩa module, chạy terraform init để cài module, rồi terraform apply để tạo cả hai container từ cùng một mẫu. Bạn sẽ thấy tất cả tài nguyên trong Docker Desktop.

Module TerraformKhi đã thiết lập mẫu hạ tầng tái sử dụng thông qua module, vẫn còn một khía cạnh quan trọng của quản lý cấu hình: xử lý an toàn thông tin nhạy cảm như mật khẩu và khóa API.

Xử lý dữ liệu nhạy cảm

Không được hardcode các giá trị nhạy cảm như mật khẩu cơ sở dữ liệu. Thay vào đó, bạn có thể dùng tệp variables.tf và đặt sensitive = true:

variable "db_password" {
  description = "Database root password"
  type        = string
  sensitive   = true
}

Giờ hãy cập nhật container cơ sở dữ liệu trong main.tf để tham chiếu mật khẩu:

resource "docker_container" "database" {
  name  = "postgres"
  image = "postgres:15"

  env = [
    "POSTGRES_PASSWORD=${var.db_password}"
  ]

  volumes {
    volume_name    = docker_volume.db_data.name
    container_path = "/var/lib/postgresql/data"
  }
}

Thay vì đặt mật khẩu trong terraform.tfvars và hardcode, hãy đặt nó như một biến môi trường:

export TF_VAR_db_password="your-secure-password"
terraform apply

Cờ sensitive = true ngăn Terraform hiển thị giá trị trên console. 

Tuy nhiên, các giá trị này được lưu ở dạng văn bản thuần trong terraform.tfstate, nên để đảm bảo tệp này an toàn, chúng ta có thể bảo mật bằng backend từ xa có mã hóa. Hãy khám phá cách thực hiện.

Quản lý Trạng thái Terraform và Tích hợp CI/CD

Phần này đề cập cơ chế trạng thái và quy trình tự động hóa thiết yếu cho triển khai sản xuất. Nhưng trước khi triển khai tự động hóa, điều quan trọng là hiểu Terraform theo dõi hạ tầng của bạn như thế nào thông qua tệp trạng thái.

Hiểu cơ chế trạng thái

Trạng thái là nền tảng cho khả năng quản lý hạ tầng của Terraform. Tệp terraform.tfstate ánh xạ cấu hình với ID container Docker thực và các thuộc tính tài nguyên. Khi chạy terraform apply, Terraform so sánh trạng thái này với cấu hình và tính toán thay đổi tối thiểu cần thiết.

Xem trạng thái hiện tại bằng terraform show, lệnh này hiển thị mọi tài nguyên đang được quản lý. Với làm việc nhóm, tệp trạng thái cục bộ gây vấn đề, xung đột và có thể mất dữ liệu. Giải pháp là backend từ xa.

Với quy trình tập trung vào Docker mà không có hạ tầng đám mây, tầng miễn phí của Terraform Cloud là lý tưởng. Thêm khối backend vào phần terraform trong main.tf:

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0"
    }
  }
  
  backend "remote" {
    organization = "your-org-name"
    
    workspaces {
      name = "docker-infrastructure"
    }
  }
}

Tạo tài khoản miễn phí tại trang web Terraform. Sau đó, chạy terraform login để xác thực bằng token cung cấp, rồi terraform init để di chuyển trạng thái. Điều này cung cấp khóa trạng thái trong workspace từ xa và cộng tác nhóm mà không cần hạ tầng đám mây. 

Tuy nhiên, bạn cũng có thể tạo backend từ xa trên các nhà cung cấp Đám mây như AWS hoặc GCP.

Terraform Cloud Remote Backend

Tự động hóa quy trình

Khi đã cấu hình quản lý trạng thái và lưu trữ từ xa, hạ tầng sẵn sàng cho cộng tác nhóm. Mảnh ghép cuối là tự động hóa quy trình triển khai qua pipeline CI/CD, đảm bảo thay đổi nhất quán và có thể rà soát.

Đầu tiên, tạo .github/workflows/terraform.yml:

name: Terraform Docker Deploy

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        
      - name: Terraform Init
        run: terraform init
        
      - name: Terraform Plan
        if: github.event_name == 'pull_request'
        run: terraform plan
        
      - name: Terraform Apply
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        run: terraform apply -auto-approve

Workflow này chạy terraform plan trên mọi pull request, hiển thị cho người rà soát các thay đổi hạ tầng sẽ xảy ra. Khi thay đổi được hợp nhất vào nhánh main, terraform apply chạy tự động, đảm bảo mọi thay đổi hạ tầng đi qua cùng quy trình rà soát như thay đổi mã nguồn.

Khắc phục sự cố Terraform Docker

Ngay cả khi cấu hình cẩn thận, sự cố vẫn có thể phát sinh. Hiểu các vấn đề thường gặp và khi nào nên cân nhắc công cụ thay thế giúp đưa ra quyết định kiến trúc tốt hơn. 

“Permission denied”

Lỗi phổ biến nhất trên Linux là "permission denied" khi kết nối tới socket Docker. Điều này xảy ra khi người dùng thiếu quyền truy cập /var/run/docker.sock. Sửa bằng cách thêm người dùng vào nhóm docker:

sudo usermod -aG docker $USER

Sau đó đăng xuất và đăng nhập lại.

Sự cố kết nối WSL2

Trên Windows với WSL2, sự cố kết nối phát sinh vì đường dẫn socket khác nhau. Đảm bảo bật thiết lập "Expose daemon on tcp://localhost:2375 without TLS" trong Docker Desktop, sau đó cập nhật provider trong main.tf:

provider "docker" {
  host = "tcp://localhost:2375"
}

Lỗi xác thực registry riêng

Với các registry Docker riêng, hãy thêm xác thực vào khối provider, định nghĩa thông tin xác thực như biến và thiết lập qua biến môi trường để tránh vấn đề do hardcode.

hclprovider "docker" {
  host = "unix:///var/run/docker.sock"
  
  registry_auth {
    address  = "registry.mycompany.com"
    username = var.registry_username
    password = var.registry_password
  }
}

Các lựa chọn thay thế Terraform

Ngoài việc khắc phục sự cố, việc hiểu khi nào công cụ khác phù hợp hơn Terraform cho trường hợp sử dụng cụ thể của bạn cũng rất hữu ích.

Terraform và Kubernetes

Kubernetes cung cấp các tính năng điều phối tinh vi hơn: 

  • Tự phục hồi
  • Lập lịch nâng cao
  • Khám phá dịch vụ
  • Cân bằng tải tích hợp

Với các triển khai đa nút phức tạp có yêu cầu sẵn sàng cao, Kubernetes là lựa chọn tốt hơn. Terraform có thể cấp phát hạ tầng Kubernetes, trong khi Helm hoặc các manifest của Kubernetes xử lý triển khai ứng dụng.

Terraform và Ansible

Ansible vượt trội ở quản lý cấu hình bên trong container. Trong khi Terraform cấp phát hạ tầng, Ansible quản lý cấu hình phần mềm và cài đặt gói. Hai công cụ thường phối hợp: Terraform tạo container Docker, sau đó Ansible cấu hình ứng dụng chạy bên trong.

Terraform và Docker Compose

Với môi trường phát triển cục bộ đơn giản, Docker Compose vẫn là lựa chọn thực dụng nhất. Terraform tỏa sáng khi cần triển khai có thể tái tạo trên nhiều môi trường và khi cần tích hợp với các thành phần hạ tầng khác.

Để giúp bạn chọn lựa, tôi đã tóm tắt các khác biệt quan trọng nhất trong bảng sau:

Công cụ

Thế mạnh cốt lõi

Trường hợp sử dụng tốt nhất

So sánh với Terraform

Kubernetes

Điều phối tinh vi (tự phục hồi, lập lịch nâng cao, cân bằng tải).

Triển khai đa nút phức tạp yêu cầu Tính sẵn sàng cao.

Bổ trợ: Terraform cấp phát hạ tầng (cụm), còn Kubernetes quản lý triển khai ứng dụng.

Ansible

Quản lý cấu hình, cài đặt gói và thiết lập phần mềm.

Quản lý phần mềm và cấu hình bên trong container.

Bổ trợ: Terraform tạo container Docker, Ansible cấu hình ứng dụng chạy bên trong.

Docker Compose

Quản lý theo dự án, đơn giản và thực dụng.

Môi trường phát triển cục bộ đơn giản.

Thay thế: Compose dễ dùng cho phát triển cục bộ, còn Terraform tốt hơn cho triển khai có thể tái tạo trên nhiều môi trường.

Nếu bạn đang tìm một công cụ để thay thế Docker, hãy xem hướng dẫn của chúng tôi về các lựa chọn thay thế Docker hàng đầu.

Kết luận

Đưa Docker vào vòng đời quản lý của Terraform sẽ biến các lần triển khai container từ thao tác thủ công thành hạ tầng có thể tái tạo và kiểm soát phiên bản. Trong hướng dẫn này, tôi đã trình bày cách xây dựng hạ tầng hoàn chỉnh theo từng bước, bắt đầu với provider và một container, rồi lần lượt bổ sung mạng, lưu trữ, biến, module và tự động hóa.

Điểm khác biệt then chốt là Terraform xử lý việc cấp phát, tạo, cập nhật và hủy tài nguyên dựa trên cấu hình khai báo. Điều phối đầy đủ với tự động mở rộng và tự phục hồi cần các công cụ như Kubernetes hoặc Docker Swarm. Terraform có thể cấp phát các bộ điều phối này, nhưng không thay thế chúng.

Cho bước tiếp theo, hãy khám phá tài liệu Terraform Registry chính thức cho Docker provider để tìm các tùy chọn tài nguyên nâng cao như dịch vụ Docker cho Swarm, quản lý bí mật và cấu hình plugin.

Nếu bạn muốn trải nghiệm thực hành với container, tôi khuyến nghị đăng ký lộ trình kỹ năng tương tác Containerization and Virtualization with Docker and Kubernetes của chúng tôi.

Câu hỏi thường gặp về Terraform Docker

Làm sao ngăn Terraform xóa Docker image khi chạy terraform destroy?

Dùng tham số keep_locally = true trong tài nguyên docker_image của bạn. Điều này yêu cầu Terraform giữ image được cache cục bộ ngay cả sau khi hủy hạ tầng, hữu ích khi nhiều container dùng chung image hoặc khi bạn muốn giữ cache image giữa các lần triển khai.

Vì sao Terraform Cloud báo lỗi "Cannot connect to Docker daemon"?

Theo mặc định, Terraform Cloud chạy ở chế độ thực thi từ xa, nghĩa là các lệnh chạy trên máy chủ của Terraform Cloud nơi không thể truy cập Docker. Bạn phải cấu hình workspace để dùng chế độ thực thi cục bộ: vào Settings > General > Execution Mode > Local. Cách này lưu trạng thái từ xa trong khi chạy lệnh Terraform trên máy cục bộ của bạn, nơi có thể truy cập Docker.

Tôi có thể quản lý các giá trị nhạy cảm như mật khẩu cơ sở dữ liệu trong Terraform như thế nào?

Đánh dấu biến là nhạy cảm bằng sensitive = true trong định nghĩa biến. Với Terraform Cloud, đặt biến nhạy cảm trong giao diện workspace và đánh dấu là "Sensitive". Với thực thi cục bộ, dùng biến môi trường với tiền tố TF_VAR_ (ví dụ, export TF_VAR_db_password="password"). Lưu ý rằng các giá trị vẫn được lưu ở dạng văn bản thuần trong terraform.tfstate, vì vậy hãy luôn bảo mật tệp trạng thái của bạn.

Sự khác biệt giữa Terraform và Docker Compose trong quản lý container là gì?

Docker Compose quản lý container thông qua cách tiếp cận YAML dựa trên dự án mà không duy trì tệp trạng thái. Terraform ghi lại mọi tài nguyên trong terraform.tfstate, cho phép phát hiện trôi dạt giữa cấu hình và thực tế. Terraform vượt trội trong triển khai có thể tái tạo trên nhiều môi trường và tích hợp với hạ tầng khác, trong khi Docker Compose đơn giản hơn cho phát triển cục bộ. Terraform cung cấp quản lý trạng thái rõ ràng và kiểm soát vòng đời (plan/apply/destroy).

Làm sao cập nhật giới hạn tài nguyên container Docker mà không gặp lỗi?

Trong khi Docker CLI hỗ trợ cập nhật tại chỗ, Docker provider của Terraform thường buộc thay thế tài nguyên để đảm bảo nhất quán trạng thái. Để tránh downtime trong quá trình thay thế, hãy thêm khối lifecycle { create_before_destroy = true }. Điều mấu chốt là bạn cũng phải bỏ tham số name cố định (hoặc dùng name_prefix) khỏi định nghĩa tài nguyên. Nếu giữ tên cố định như name = "web-server", container mới sẽ không khởi động được với lỗi "name already in use" trước khi container cũ bị hủy.

Benito Martin

Tác giả

Benito Martin

Với vai trò Nhà sáng lập Martin Data Solutions và Nhà khoa học dữ liệu/ Kỹ sư ML & AI tự do, tôi sở hữu danh mục năng lực đa dạng về Hồi quy, Phân loại, Xử lý ngôn ngữ tự nhiên (NLP), LLM, RAG, Mạng nơ-ron, Phương pháp tổ hợp (Ensemble) và Thị giác máy tính.

  • Phát triển thành công nhiều dự án ML end-to-end, bao gồm làm sạch dữ liệu, phân tích, xây dựng mô hình và triển khai trên AWS và GCP, mang lại các giải pháp có tác động và khả năng mở rộng.
  • Xây dựng các ứng dụng web tương tác, có khả năng mở rộng bằng Streamlit và Gradio cho nhiều bài toán trong các ngành khác nhau.
  • Giảng dạy và cố vấn cho học viên về khoa học dữ liệu và phân tích, thúc đẩy sự phát triển nghề nghiệp của họ thông qua phương pháp học tập cá nhân hóa.
  • Thiết kế nội dung khóa học cho các ứng dụng retrieval-augmented generation (RAG) được điều chỉnh theo yêu cầu doanh nghiệp.
  • Viết các blog kỹ thuật AI & ML có sức ảnh hưởng, bao gồm các chủ đề như MLOps, cơ sở dữ liệu vector và LLM, đạt được mức độ tương tác đáng kể.

Trong mỗi dự án tôi đảm nhận, tôi luôn áp dụng các thực hành cập nhật trong kỹ thuật phần mềm và DevOps như CI/CD, linting mã, định dạng, giám sát mô hình, theo dõi thí nghiệm và xử lý lỗi mạnh mẽ. Tôi cam kết cung cấp các giải pháp hoàn chỉnh, chuyển hóa những hiểu biết từ dữ liệu thành chiến lược thực tiễn giúp doanh nghiệp phát triển và khai thác tối đa giá trị của khoa học dữ liệu, học máy và AI.

Chủ đề

Liên quan