Terraform 部署 Docker

Terraform 部署 Docker

文件目录结构

./
├── network                             # network 目录,创建 network
│   ├── main.tf
│   ├── outputs.tf
│   ├── terraform.tfstate
│   ├── terraform.tfstate.backup
│   └── versions.tf
└── service                             # service 目录,创建 service
    ├── nginx.tf
    ├── main.tf
    ├── terraform.tfstate
    ├── terraform.tfstate.backup
    └── versions.tf

3 directories, 10 files

创建 network

查看当前 docker network

evescn@evescndeMacBook-Pro network % docker network ls        
NETWORK ID     NAME      DRIVER    SCOPE
a97ca4e7cded   bridge    bridge    local
43c409a0c3e3   host      host      local
02706eb1b5ba   none      null      local

编写 Terraform 代码

  • 查看 docker provider

https://registry.terraform.io/browse/providers

  • 文件结构
./
└── network                             # network 目录,创建 network
    ├── main.tf                         # 定义 network 的创建信息
    ├── outputs.tf                      # 定义模块输出信息,后续其他模块/服务使用
    └── versions.tf                     # 定义 provider 版本 

文档地址:https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/network

  • 定义 provider 版本
## versions.tf

# 定义使用的 docker provider 版本
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "3.0.2"
    }
  }
}
  • 定义 network
## main.tf

# 定义 docker 服务信息,当前使用本机测试,如果远程连接,需要开启 docker 的远程连接服务
provider "docker" {
  host = "unix:///var/run/docker.sock"
}

# 定义环境变量,后续引用
locals {
  network_settings = [
    {
      # network 名称
      name   = "devops1"
      # network 网络模式
      driver = "bridge"
      # network 子网范围
      subnet = "10.10.10.0/24"
    }
  ]
}

# docker network 定义,详细参数,查看官方文档
resource "docker_network" "network" {
  count  = length(local.network_settings)
  name   = local.network_settings[count.index]["name"]
  driver = local.network_settings[count.index]["driver"]
  ipam_config {
    subnet = local.network_settings[count.index]["subnet"]
  }
}
  • 定义 output 后续 docker 容器创建需要使用此次创建的 network
## output.tf

# 定义输出信息
output "network" {
  value = docker_network.network
  # value = [for net in docker_network.network : tomap({ "name" : net.name, "subnet" : tolist(net.ipam_config)[0].subnet })]
}
  • terraform plan 查看服务信息
evescn@evescndeMacBook-Pro network % terraform fmt    
outputs.tf

evescn@evescndeMacBook-Pro network % terraform validate
Success! The configuration is valid.

evescn@evescndeMacBook-Pro network % terraform plan    

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # docker_network.network[0] will be created
  + resource "docker_network" "network" {
      + driver      = "bridge"
      + id          = (known after apply)
      + internal    = (known after apply)
      + ipam_driver = "default"
      + name        = "devops1"
      + options     = (known after apply)
      + scope       = (known after apply)

      + ipam_config {
          + subnet = "10.10.10.0/24"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + network = [
      + {
          + attachable      = null
          + check_duplicate = null
          + driver          = "bridge"
          + id              = (known after apply)
          + ingress         = null
          + internal        = (known after apply)
          + ipam_config     = [
              + {
                  + aux_address = null
                  + gateway     = ""
                  + ip_range    = ""
                  + subnet      = "10.10.10.0/24"
                },
            ]
          + ipam_driver     = "default"
          + ipam_options    = null
          + ipv6            = null
          + labels          = []
          + name            = "devops1"
          + options         = (known after apply)
          + scope           = (known after apply)
        },
    ]
  • Changes to Outputs 输出的信息过多,很多无用信息对输出信息进行过滤下
## output.tf

# 定义输出信息
output "network" {
  # value = docker_network.network
  value = [for net in docker_network.network : tomap({ "name" : net.name, "subnet" : tolist(net.ipam_config)[0].subnet })]
}
evescn@evescndeMacBook-Pro network % terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # docker_network.network[0] will be created
  + resource "docker_network" "network" {
      + driver      = "bridge"
      + id          = (known after apply)
      + internal    = (known after apply)
      + ipam_driver = "default"
      + name        = "devops1"
      + options     = (known after apply)
      + scope       = (known after apply)

      + ipam_config {
          + subnet = "10.10.10.0/24"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + network = [
      + {
          + name   = "devops1"
          + subnet = "10.10.10.0/24"
        },
    ]
  • 创建 network
evescn@evescndeMacBook-Pro network % terraform apply --auto-approve

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # docker_network.network[0] will be created
  + resource "docker_network" "network" {
      + driver      = "bridge"
      + id          = (known after apply)
      + internal    = (known after apply)
      + ipam_driver = "default"
      + name        = "devops1"
      + options     = (known after apply)
      + scope       = (known after apply)

      + ipam_config {
          + subnet = "10.10.10.0/24"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + network = [
      + {
          + name   = "devops1"
          + subnet = "10.10.10.0/24"
        },
    ]
docker_network.network[0]: Creating...
docker_network.network[0]: Creation complete after 2s [id=f037e957ab9932aacea87581e741bdc2568eb2b973a7296a4b0b928a4c8456a1]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

network = [
  tomap({
    "name" = "devops1"
    "subnet" = "10.10.10.0/24"
  }),
]
  • 查看 docker 信息
evescn@evescndeMacBook-Pro network % docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a97ca4e7cded   bridge    bridge    local
f037e957ab99   devops1   bridge    local
43c409a0c3e3   host      host      local
02706eb1b5ba   none      null      local
evescn@evescndeMacBook-Pro network % docker inspect devops1
[
    {
        "Name": "devops1",
        "Id": "f037e957ab9932aacea87581e741bdc2568eb2b973a7296a4b0b928a4c8456a1",
        "Created": "2023-08-01T09:58:29.897985467Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.10.10.0/24"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

创建 service

查看当前运行的 docker 容器

evescn@evescndeMacBook-Pro network % docker ps -a
CONTAINER ID   IMAGE       COMMAND                   CREATED        STATUS                     PORTS                               NAMES
09c8438af98c   mysql:5.7   "docker-entrypoint.s…"   4 months ago   Exited (255) 5 weeks ago   0.0.0.0:3306->3306/tcp, 33060/tcp   mysql

编写 Terraform 代码

  • 查看 docker provider

https://registry.terraform.io/browse/providers

  • 文件结构
./
└── service                             # service 目录,创建 service
    ├── main.tf                         # 定义 docker 连接 output 等信息
    ├── nginx.tf                        # 定义 nginx 的创建信息 
    └── versions.tf                     # 定义 provider 版本 

文档地址:https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/image
文档地址:https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container

  • 定义 provider 版本
## versions.tf

# 定义使用的 docker provider 版本
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "3.0.2"
    }
  }
}
  • 定义 docker 连接信息
## main.tf

# 定义 docker 服务信息,当前使用本机测试,如果远程连接,需要开启 docker 的远程连接服务
provider "docker" {
  host = "unix:///var/run/docker.sock"
}

# 获取上文 network 中 output 输出的 network 信息
data "terraform_remote_state" "network" {
  backend = "local"
  config = {
    path = "../network/terraform.tfstate"
  }
}

# 输出 etwork 中 output 输出的 network 信息,查看确定
output "network_value" {
  value = data.terraform_remote_state.network.outputs
}
  • 定义 nginx 信息
## nginx.tf

# 定义环境变量,后续引用
locals {
  # 定义容器使用镜像
  container_image_name = "nginx:1.25.1"
  # 定义容器名称
  container_name = "nginx"
  # 容器网络,上文 network 中定义
  container_network = data.terraform_remote_state.network.outputs.network[0].name
  # 容器ip
  container_ip = "10.10.10.10"

  # 容器暴露端口
  container_ports = [
    {
      internal = 80
      external = 80
    },
    {
      internal = 443
      external = 443
    },
  ]

  # 容器挂载
  container_volumes = [
    {
      container_path = "/usr/share/nginx/html/"
      host_path      = "/tmp/nginx/"
    },
  ]

}

# 定义容器镜像
resource "docker_image" "nginx" {
  name         = local.container_image_name
  # 容器删除时是否本地保存镜像
  keep_locally = true
}

# Start a container
resource "docker_container" "nginx" {
  name  = local.container_name
  image = docker_image.nginx.name
  networks_advanced {
    name         = local.container_network
    ipv4_address = local.container_ip
  }

  dynamic "ports" {
    for_each = local.container_ports
    content {
      internal = ports.value.internal
      external = ports.value.external
      ip       = "0.0.0.0"
      protocol = "tcp"
    }
  }

  dynamic "volumes" {
    for_each = local.container_volumes
    content {
      container_path = volumes.value.container_path
      host_path      = volumes.value.host_path
    }
  }

  # 定义依赖,依赖容器镜像先执行成功
  depends_on = [
    docker_image.nginx
  ]
}
  • terraform plan 查看服务信息
evescn@evescndeMacBook-Pro service % terraform fmt
nginx.tf

evescn@evescndeMacBook-Pro service % terraform validate
Success! The configuration is valid.

evescn@evescndeMacBook-Pro service % terraform plan     
data.terraform_remote_state.network: Reading...
data.terraform_remote_state.network: Read complete after 0s

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # docker_container.nginx will be created
  + resource "docker_container" "nginx" {
      + attach                                      = false
      + bridge                                      = (known after apply)
      + command                                     = (known after apply)
      + container_logs                              = (known after apply)
      + container_read_refresh_timeout_milliseconds = 15000
      + entrypoint                                  = (known after apply)
      + env                                         = (known after apply)
      + exit_code                                   = (known after apply)
      + hostname                                    = (known after apply)
      + id                                          = (known after apply)
      + image                                       = "nginx:1.25.1"
      + init                                        = (known after apply)
      + ipc_mode                                    = (known after apply)
      + log_driver                                  = (known after apply)
      + logs                                        = false
      + must_run                                    = true
      + name                                        = "nginx"
      + network_data                                = (known after apply)
      + read_only                                   = false
      + remove_volumes                              = true
      + restart                                     = "no"
      + rm                                          = false
      + runtime                                     = (known after apply)
      + security_opts                               = (known after apply)
      + shm_size                                    = (known after apply)
      + start                                       = true
      + stdin_open                                  = false
      + stop_signal                                 = (known after apply)
      + stop_timeout                                = (known after apply)
      + tty                                         = false
      + wait                                        = false
      + wait_timeout                                = 60

      + networks_advanced {
          + aliases      = []
          + ipv4_address = "10.10.10.10"
          + name         = "devops1"
        }

      + ports {
          + external = 80
          + internal = 80
          + ip       = "0.0.0.0"
          + protocol = "tcp"
        }
      + ports {
          + external = 443
          + internal = 443
          + ip       = "0.0.0.0"
          + protocol = "tcp"
        }

      + volumes {
          + container_path = "/usr/share/nginx/html/"
          + host_path      = "/tmp/nginx/"
        }
    }

  # docker_image.nginx will be created
  + resource "docker_image" "nginx" {
      + id           = (known after apply)
      + image_id     = (known after apply)
      + keep_locally = true
      + name         = "nginx:1.25.1"
      + repo_digest  = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + network_value = {
      + network = [
          + {
              + name   = "devops1"
              + subnet = "10.10.10.0/24"
            },
        ]
    }
  • Changes to Outputs 输出中,获取到了上文 network 输出的 docker 网络信息
Changes to Outputs:
  + network_value = {
      + network = [
          + {
              + name   = "devops1"
              + subnet = "10.10.10.0/24"
            },
        ]
    }
  • 创建 service
evescn@evescndeMacBook-Pro service % terraform apply --auto-approve
data.terraform_remote_state.network: Reading...
data.terraform_remote_state.network: Read complete after 0s

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # docker_container.nginx will be created
  + resource "docker_container" "nginx" {
      + attach                                      = false
      + bridge                                      = (known after apply)
      + command                                     = (known after apply)
      + container_logs                              = (known after apply)
      + container_read_refresh_timeout_milliseconds = 15000
      + entrypoint                                  = (known after apply)
      + env                                         = (known after apply)
      + exit_code                                   = (known after apply)
      + hostname                                    = (known after apply)
      + id                                          = (known after apply)
      + image                                       = "nginx:1.25.1"
      + init                                        = (known after apply)
      + ipc_mode                                    = (known after apply)
      + log_driver                                  = (known after apply)
      + logs                                        = false
      + must_run                                    = true
      + name                                        = "nginx"
      + network_data                                = (known after apply)
      + read_only                                   = false
      + remove_volumes                              = true
      + restart                                     = "no"
      + rm                                          = false
      + runtime                                     = (known after apply)
      + security_opts                               = (known after apply)
      + shm_size                                    = (known after apply)
      + start                                       = true
      + stdin_open                                  = false
      + stop_signal                                 = (known after apply)
      + stop_timeout                                = (known after apply)
      + tty                                         = false
      + wait                                        = false
      + wait_timeout                                = 60

      + networks_advanced {
          + aliases      = []
          + ipv4_address = "10.10.10.10"
          + name         = "devops1"
        }

      + ports {
          + external = 80
          + internal = 80
          + ip       = "0.0.0.0"
          + protocol = "tcp"
        }
      + ports {
          + external = 443
          + internal = 443
          + ip       = "0.0.0.0"
          + protocol = "tcp"
        }

      + volumes {
          + container_path = "/usr/share/nginx/html/"
          + host_path      = "/tmp/nginx/"
        }
    }

  # docker_image.nginx will be created
  + resource "docker_image" "nginx" {
      + id           = (known after apply)
      + image_id     = (known after apply)
      + keep_locally = true
      + name         = "nginx:1.25.1"
      + repo_digest  = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + network_value = {
      + network = [
          + {
              + name   = "devops1"
              + subnet = "10.10.10.0/24"
            },
        ]
    }
docker_image.nginx: Creating...
docker_image.nginx: Creation complete after 0s [id=sha256:89da1fb6dcb964dd35c3f41b7b93ffc35eaf20bc61f2e1335fea710a18424287nginx:1.25.1]
docker_container.nginx: Creating...
docker_container.nginx: Creation complete after 1s [id=dc07a9304899020bb8b51c433d7492527901825ee4a1b5d124e89e9e0cdd12ad]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

network_value = {
  "network" = [
    tomap({
      "name" = "devops1"
      "subnet" = "10.10.10.0/24"
    }),
  ]
}
  • 查看容器信息
evescn@evescndeMacBook-Pro service % docker ps -a
CONTAINER ID   IMAGE          COMMAND                   CREATED          STATUS                     PORTS                                      NAMES
dc07a9304899   nginx:1.25.1   "/docker-entrypoint.…"   33 seconds ago   Up 33 seconds              0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   nginx
09c8438af98c   mysql:5.7      "docker-entrypoint.s…"   4 months ago     Exited (255) 5 weeks ago   0.0.0.0:3306->3306/tcp, 33060/tcp          mysql
evescn@evescndeMacBook-Pro service % docker inspect nginx
[
    {
        ......
        # 容器名称
        "Name": "/nginx",
        ......
        "HostConfig": {
            "Binds": [
                "/tmp/nginx/:/usr/share/nginx/html/:rw"
            ],
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {
                "443/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "443"
                    }
                ],
                "80/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "80"
                    }
                ]
            },
            ......
        },
        ......
        # 容器挂载信息
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/host_mnt/private/tmp/nginx",
                "Destination": "/usr/share/nginx/html",
                "Mode": "rw",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
        "Config": {
            ......
            "ExposedPorts": {
                "443/tcp": {},
                "80/tcp": {}
            },
            ......
            "Cmd": [
                "nginx",
                "-g",
                "daemon off;"
            ],
            # 容器镜像信息
            "Image": "nginx:1.25.1",
            "Volumes": {
                "/usr/share/nginx/html/": {}
            },
            "WorkingDir": "",
            "Entrypoint": [
                "/docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": {
                "maintainer": "NGINX Docker Maintainers \u003cdocker-maint@nginx.com\u003e"
            },
            "StopSignal": "SIGQUIT"
        },
        "NetworkSettings": {
            ......
            # 容器端口暴露
            "Ports": {
                "443/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "443"
                    }
                ],
                "80/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "80"
                    }
                ]
            },
            ......
            "Networks": {
                "devops1": {
                    "IPAMConfig": {
                        "IPv4Address": "10.10.10.10" # 容器 IP 地址
                    },
                    "Links": null,
                    "Aliases": [
                        "dc07a9304899"
                    ],
                    "NetworkID": "f037e957ab9932aacea87581e741bdc2568eb2b973a7296a4b0b928a4c8456a1",
                    "EndpointID": "ea872c47b60383f6f4aedf3174194fd02d19b36d58588a6bfc86efd7a52b6a20",
                    "Gateway": "10.10.10.1",
                    "IPAddress": "10.10.10.10",
                    "IPPrefixLen": 24,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:0a:0a:0a:0a",
                    "DriverOpts": null
                }
            }
        }
    }
]
  • 测试 nginx 服务
evescn@evescndeMacBook-Pro service % ls /tmp/ 
KSOutOfProcessFetcher.EGu0wlZOI1        MozillaUpdateLock-2656FF1E876E9973      com.apple.launchd.jsirs0xUVs            nginx                                   ovpnconnect-local-ipc.sock              powerlog
evescn@evescndeMacBook-Pro service % ls /tmp/nginx 
evescn@evescndeMacBook-Pro service % echo "<h1>Evescn Test Page</h1>" > /tmp/nginx/index.html
evescn@evescndeMacBook-Pro service % curl localhost                                          
<h1>Evescn Test Page</h1>

Docker 开启远程 API

编辑 /etc/systemd/system/docker.service, 添加 -H tcp://0.0.0.0:2375

ExecStart=/usr/bin/dockerd --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock  -H fd://

重启服务

systemctl daemon-reload  && systemctl restart docker

测试API : curl http://127.0.0.1:2375/version

{"Platform":{"Name":"Docker Desktop 4.19.0 (106363)"},"Components":[{"Name":"Engine","Version":"23.0.5","Details":{"ApiVersion":"1.42","Arch":"amd64","BuildTime":"2023-04-26T16:17:45.000000000+00:00","Experimental":"false","GitCommit":"94d3ad6","GoVersion":"go1.19.8","KernelVersion":"5.15.49-linuxkit","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"containerd","Version":"1.6.20","Details":{"GitCommit":"2806fc1057397dbaeefbea0e4e17bddfbd388f38"}},{"Name":"runc","Version":"1.1.5","Details":{"GitCommit":"v1.1.5-0-gf19387a"}},{"Name":"docker-init","Version":"0.19.0","Details":{"GitCommit":"de40ad0"}}],"Version":"23.0.5","ApiVersion":"1.42","MinAPIVersion":"1.12","GitCommit":"94d3ad6","GoVersion":"go1.19.8","Os":"linux","Arch":"amd64","KernelVersion":"5.15.49-linuxkit","BuildTime":"2023-04-26T16:17:45.000000000+00:00"}

修改上文中 main.tf 配置文件

## main.tf

# 定义 docker 服务信息,当前使用本机测试,如果远程连接,需要开启 docker 的远程连接服务
provider "docker" {
  # host = "unix:///var/run/docker.sock"
  #docker连接方式  
  host = "tcp://IP:2375"
}
posted @ 2023-08-02 18:47  evescn  阅读(178)  评论(0编辑  收藏  举报