Terraform con Proveedor Docker
The Terraform Docker provider enables managing Docker containers, images, networks, and volumes using Terraform's declarative infrastructure-as-code approach. This integration allows you to define containerized infrastructure in code, enabling version control, repeatable deployments, and multi-environment consistency. This guide covers Docker provider configuration, container management, image handling, networking, volumes, Docker Compose comparison, and practical examples.
Descripción General del Proveedor Docker
The Terraform Docker provider enables infrastructure-as-code management of Docker resources. Instead of manually running docker commands, you define desired container infrastructure in Terraform configuration, enabling version control, collaboration, and automated deployment.
Key advantages:
- Infrastructure as Code: Manage containers like other infrastructure
- Version Control: Track container configuration changes in git
- Repeatability: Spin up identical environments reliably
- Multi-Environment: Use same configuration across dev/staging/prod
- Integration: Combine with other Terraform resources
- Documentation: Terraform becomes infrastructure documentation
Docker provider architecture:
┌──────────────────────┐
│ Terraform Config │
│ (docker resources) │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Docker Provider │
│ (Terraform Plugin) │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Docker API │
│ (Local/Remote) │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Docker Engine │
│ (Create/Manage) │
└──────────────────────┘
Configuración del Proveedor
Configure Terraform to use the Docker provider.
Basic provider configuration:
# versions.tf
terraform {
required_version = ">= 1.0"
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
}
}
provider "docker" {
host = "unix:///var/run/docker.sock"
}
Remote Docker configuration:
provider "docker" {
# Connect to remote Docker daemon
host = "tcp://docker-host.example.com:2375"
# Enable TLS for secure connection
# host = "tcp://docker-host.example.com:2376"
# ca_data = file("${path.module}/ca.pem")
# cert_data = file("${path.module}/cert.pem")
# key_data = file("${path.module}/key.pem")
}
Docker registry configuration:
provider "docker" {
host = "unix:///var/run/docker.sock"
# Configure registries
registry_auth {
address = "ghcr.io"
username = var.ghcr_username
password = var.ghcr_password
}
registry_auth {
address = "docker.io"
username = var.dockerhub_username
password = var.dockerhub_password
}
}
Variables for credentials:
# variables.tf
variable "docker_host" {
type = string
description = "Docker daemon socket/host"
default = "unix:///var/run/docker.sock"
}
variable "ghcr_username" {
type = string
description = "GitHub Container Registry username"
sensitive = true
}
variable "ghcr_password" {
type = string
description = "GitHub Container Registry password"
sensitive = true
}
Gestión de Imágenes Docker
Build and manage Docker images with Terraform.
Pull existing image:
# main.tf
# Pull image from Docker Hub
resource "docker_image" "ubuntu" {
name = "ubuntu:22.04"
keep_locally = true # Keep image after destroy
}
# Pull from private registry
resource "docker_image" "private" {
name = "ghcr.io/myorg/myapp:latest"
keep_locally = true
pull_triggers = [
var.image_tag # Repull on tag change
]
}
Build image from Dockerfile:
# Build image
resource "docker_image" "myapp" {
name = "myapp:1.0"
build {
context = "${path.module}/app"
dockerfile = "Dockerfile"
force_remove = true
build_arg = {
ENVIRONMENT = var.environment
VERSION = var.app_version
}
label = {
Name = "myapp"
Environment = var.environment
}
}
}
# Use built image
resource "docker_container" "app" {
image = docker_image.myapp.image_id
name = "myapp-container"
}
Build with registry push:
# Build and push to registry
resource "docker_image" "registry" {
name = "ghcr.io/myorg/myapp:${var.version}"
build {
context = "${path.module}/app"
dockerfile = "Dockerfile"
}
# Push after build
force_remove = true
# Push to registry
registry_digest = docker_registry_image.registry_image.digest
}
resource "docker_registry_image" "registry_image" {
name = docker_image.registry.name
}
Image outputs:
output "image_id" {
value = docker_image.myapp.image_id
description = "Image ID"
}
output "image_sha" {
value = docker_image.myapp.sha256_digest
description = "Image SHA256 digest"
}
output "image_repo_digest" {
value = docker_image.myapp.repo_digest
description = "Repository digest"
}
Gestión de Contenedores
Create and manage Docker containers.
Basic container:
resource "docker_container" "web" {
image = "nginx:latest"
name = "web-server"
ports {
internal = 80
external = 8080
}
env = [
"NGINX_HOST=example.com",
"NGINX_PORT=80"
]
restart_policy = "on-failure"
}
Advanced container configuration:
resource "docker_container" "app" {
image = docker_image.myapp.image_id
name = "myapp-${var.environment}"
# Port mapping
ports {
internal = 3000
external = 3000
}
ports {
internal = 3001
external = 3001
protocol = "tcp"
}
# Port ranges
ports {
internal = 5000
external = 5001
ip = "127.0.0.1"
}
# Environment variables
env = [
"APP_ENV=${var.environment}",
"APP_VERSION=${var.app_version}",
"DATABASE_URL=${var.database_url}",
]
# Mounts
mounts {
type = "bind"
source = "/host/data"
target = "/container/data"
read_only = false
}
mounts {
type = "volume"
source = docker_volume.app_data.name
target = "/app/data"
}
# Volume management
volumes {
container_path = "/data"
host_path = "/host/data"
read_only = false
}
# Resource limits
memory = 512 # MB
memory_swap = -1 # Unlimited swap
cpus = 1.0
# Logging
log_driver = "json-file"
log_opts = {
"max-size" = "10m"
"max-file" = "3"
}
# Restart policy
restart_policy = "on-failure"
max_retries = 5
# Privileged and capabilities
privileged = false
capabilities {
add = ["NET_ADMIN"]
drop = ["ALL"]
}
# Health check
healthchecks {
test = ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval = "30s"
timeout = "5s"
start_period = "5s"
retries = 3
}
# Depends on other containers
depends_on = [
docker_container.database
]
# DNS and hosts
dns = ["8.8.8.8", "8.8.4.4"]
dns_search = ["example.com"]
hostname = "app-server"
# User and groups
user = "appuser"
# Working directory
working_dir = "/app"
# Command and entrypoint
command = ["npm", "start"]
entrypoint = ["/entrypoint.sh"]
# Labels
labels = {
"com.example.app" = "myapp"
"environment" = var.environment
"managed_by" = "terraform"
}
}
Redes Docker
Create and manage Docker networks for container communication.
Create network:
resource "docker_network" "private" {
name = "app-network"
driver = "bridge"
check_duplicate = true
ipam_config {
subnet = "172.20.0.0/16"
}
labels = {
"network_type" = "private"
}
}
# Connect containers to network
resource "docker_container" "web" {
image = "nginx:latest"
name = "web"
networks_advanced {
name = docker_network.private.name
ipv4_address = "172.20.0.2"
}
}
resource "docker_container" "app" {
image = "myapp:latest"
name = "app"
networks_advanced {
name = docker_network.private.name
ipv4_address = "172.20.0.3"
}
}
# Container communication using hostnames
# web can reach app at: http://app:3000
Multi-network setup:
# Frontend network
resource "docker_network" "frontend" {
name = "frontend-network"
driver = "bridge"
}
# Backend network (isolated)
resource "docker_network" "backend" {
name = "backend-network"
driver = "bridge"
}
# Web tier on frontend
resource "docker_container" "nginx" {
image = "nginx:latest"
name = "nginx"
networks_advanced {
name = docker_network.frontend.name
}
}
# App tier bridges networks
resource "docker_container" "app" {
image = "myapp:latest"
name = "app"
networks_advanced {
name = docker_network.frontend.name
}
networks_advanced {
name = docker_network.backend.name
}
}
# Database tier on backend only
resource "docker_container" "db" {
image = "postgres:14"
name = "postgres"
networks_advanced {
name = docker_network.backend.name
}
}
Volúmenes Docker
Manage persistent storage for containers.
Create volume:
# Named volume
resource "docker_volume" "app_data" {
name = "app-data"
driver = "local"
labels = {
backup = "daily"
}
}
# Use volume in container
resource "docker_container" "app" {
image = "myapp:latest"
name = "app"
volumes {
volume_name = docker_volume.app_data.name
container_path = "/app/data"
read_only = false
}
}
# Volume outputs
output "volume_name" {
value = docker_volume.app_data.name
}
output "volume_mountpoint" {
value = docker_volume.app_data.mountpoint
}
Bind mount for development:
resource "docker_container" "dev" {
image = "myapp:latest"
name = "app-dev"
# Bind mount for live code
volumes {
host_path = "${path.module}/app"
container_path = "/app/src"
read_only = false
}
# Named volume for dependencies
volumes {
volume_name = docker_volume.node_modules.name
container_path = "/app/node_modules"
read_only = false
}
}
resource "docker_volume" "node_modules" {
name = "app-node-modules"
}
Comparación con Docker Compose
Terraform vs Docker Compose for container orchestration.
Terraform strengths:
# Define other infrastructure alongside containers
resource "aws_security_group" "app" {
name = "app-sg"
# Security group rules
}
resource "docker_container" "app" {
image = "myapp:latest"
name = "app"
# Container with security group reference
# (demonstrates IaC integration)
}
# Version control and state management
# Easy multi-environment configuration
# Team collaboration with terraform workflows
Docker Compose strengths:
# docker-compose.yml - Simpler for local development
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "8080:80"
depends_on:
- app
app:
build: .
environment:
- DATABASE_URL=postgres://db:5432/myapp
depends_on:
- db
db:
image: postgres:14
environment:
- POSTGRES_PASSWORD=secret
Using both together:
# Terraform manages images and infrastructure
resource "docker_image" "myapp" {
build {
context = "${path.module}/app"
dockerfile = "Dockerfile"
}
}
# Docker Compose runs on developer machines
# Terraform manages production infrastructure
Ejemplos Prácticos
Web application stack:
# Network
resource "docker_network" "app" {
name = "app-network"
}
# Database volume
resource "docker_volume" "db_data" {
name = "postgres-data"
}
# PostgreSQL database
resource "docker_container" "db" {
image = "postgres:14"
name = "postgres"
env = [
"POSTGRES_PASSWORD=${var.db_password}",
"POSTGRES_USER=appuser",
"POSTGRES_DB=myapp"
]
networks_advanced {
name = docker_network.app.name
}
volumes {
volume_name = docker_volume.db_data.name
container_path = "/var/lib/postgresql/data"
}
}
# Application
resource "docker_image" "app" {
name = "myapp:${var.version}"
build {
context = "${path.module}/app"
}
}
resource "docker_container" "app" {
image = docker_image.app.image_id
name = "app"
env = [
"DATABASE_URL=postgres://appuser:${var.db_password}@postgres:5432/myapp",
"ENVIRONMENT=${var.environment}"
]
networks_advanced {
name = docker_network.app.name
}
depends_on = [docker_container.db]
}
# Nginx reverse proxy
resource "docker_container" "nginx" {
image = "nginx:latest"
name = "nginx"
ports {
internal = 80
external = 80
}
volumes {
host_path = "${path.module}/nginx.conf"
container_path = "/etc/nginx/nginx.conf"
}
networks_advanced {
name = docker_network.app.name
}
depends_on = [docker_container.app]
}
Mejores Prácticas
Image management:
# Use specific versions
resource "docker_image" "app" {
name = "myapp:1.0.0" # Specific version
# NOT "myapp:latest"
}
# Tag images consistently
resource "docker_image" "app" {
name = "ghcr.io/myorg/myapp:v${var.app_version}"
}
# Remove dangling images
resource "docker_image" "app" {
force_remove = true
}
Container management:
# Always set restart policy
resource "docker_container" "app" {
restart_policy = "on-failure"
max_retries = 5
}
# Health checks
resource "docker_container" "app" {
healthchecks {
test = ["CMD", "curl", "-f", "http://localhost:3000"]
interval = "30s"
timeout = "5s"
retries = 3
}
}
# Resource limits
resource "docker_container" "app" {
memory = 1024 # 1 GB
cpus = 1.0
}
Conclusión
The Terraform Docker provider brings infrastructure-as-code principles to containerized applications, enabling version-controlled, repeatable Docker deployments. By managing containers, networks, and volumes with Terraform, you gain consistency across environments, better collaboration through git-based workflows, and seamless integration with other infrastructure resources. Combine Terraform's Docker provider with your container strategy to build scalable, maintainable containerized infrastructure.


