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
- 文件结构
./
└── 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
- 文件结构
./
└── 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"
}