参考:terraform官网

参考:terraform docker tutorial

一、什么是terraform?

Terraform是一个开源的基础架构即代码的工具。通过这个工具,可以把我们的基础架构通过代码的形式进行维护。

二、什么是基础架构?

应用运行所需要的的底层设备,可以是物理设备或者虚拟设备。现在基本上都使用的虚拟设备,虚拟设备基本上都是在云上。

比如说,一个公司需要部署一个网站,我们选择了AWS,那么我们需要在aws上创建我们的infrastructure,也就是虚拟机,有了这个infrastructure,才可以去创建我们的网站。

那么这个infrastructure怎么去创建呢?当然,我们可以通过去aws页面上通过鼠标点的方式去创建一台VM,也可以用同样的方式创建其他的资源,比如网络地址,网络策略,数据库,DNS等等。这些都可以通过手动方式去操作,但是手动的操作就不具备可维护性,比如说如果有一天我们需要把这些操作重新搞一遍,就不可能重新去手动重复做这些操作。如果我们能将这些手动的过程代码化,那么我们就可以很快地把我们的infrastructure创建好。

有同学会问,现在很多云厂商,比如AWS,阿里云等,不是提供了自己的API吗?我们可以通过API去创建这些资源。

但是每家云厂商的API是不一样的,如果我们需要管理多套云下面的资源,就需要维护多套代码。实际上Terraform就是帮我们解决这个问题的。Terraform在这个API之上又做了一层,把具体调用各个云厂商的API给屏蔽掉了,它去做具体的调用。我们可以认为Terraform是一种编程语言也好,配置的描述工具也好,我们只需要学习Terraform,就可以很方便地管理云上的API

三、安装Terraform

参考:https://developer.hashicorp.com/terraform/intro

windows电脑安装

下载windows对应的二进制文件,放到C盘/windows/system32目录下面

#下载:
https://releases.hashicorp.com/terraform/1.4.2/terraform_1.4.2_windows_amd64.zip
#放到C盘/windows/system32目录下面
#用cmd或者powershell测试
PS C:\Users\xxxx> terraform --help
Usage: terraform [global options] <subcommand> [args]

The available commands for execution are listed below.
The primary workflow commands are given first, followed by
less common or more advanced commands.

Main commands:
  init          Prepare your working directory for other commands
  validate      Check whether the configuration is valid
  plan          Show changes required by the current configuration
  apply         Create or update infrastructure
  destroy       Destroy previously-created infrastructure
......

vscode安装HashCorp Terraform插件

该插件可以让terraform代码高亮显示,如下图所示

Linux安装(ubuntu)

#安装必要的软件包
 ~]# sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
#导入gpg密钥
~]# curl -fsSL  https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
#添加terraform软件源
~]# echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee 
#安装terraform
~]# apt update
~]# apt install terraform
#验证安装成功
~]# terraform --help
Usage: terraform [global options] <subcommand> [args]

The available commands for execution are listed below.
The primary workflow commands are given first, followed by
less common or more advanced commands.

Main commands:
  init          Prepare your working directory for other commands
  validate      Check whether the configuration is valid
  plan          Show changes required by the current configuration
  apply         Create or update infrastructure
  destroy       Destroy previously-created infrastructure
......
#开启命令行自动补全
~]# terraform -install-autocomplete

四、编写一个最简单的terraform代码

操作AWS,阿里云等云厂商的资源可能需要花钱,这里就选用免费的docker来演示下。

该代码是使用nginx镜像部署一个nginx容器,需要docker环境,需要提前准备好。

#创建一个目录存放项目代码
~]# mkdir learn-terraform-docker-container
~]# cd learn-terraform-docker-container
learn-terraform-docker-container]# cat main.tf
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0.1"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name         = "nginx"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = "demo"

  ports {
    internal = 80
    external = 8000
  }
}

这段代码是要做什么?

  • required_providers:需要操作的provider,比如docker,或者aws,或者google cloud等。你可以认为它是一个API或者软件包等(是不是跟python导入模块有点像?)

    required_providers {
      docker = {
        source  = "kreuzwerker/docker"
        version = "~> 3.0.1"
      }
    }
    
  • resource:资源。指具体要操作的对象。这里定义了两个资源

    • docker_image资源:需要一个nginx镜像

      resource "docker_image" "nginx" {
        name         = "nginx"
        keep_locally = false
      }
      
    • docker_container资源:container的名字,使用的镜像,映射的端口等

      resource "docker_container" "nginx" {
        image = docker_image.nginx.image_id
        name  = "demo"
      
        ports {
          internal = 80
          external = 8000
        }
      }
      

五、如何使用上面这段代码

格式化和校验

#如果代码缩进不统一,可以让terraform帮我们调整代码缩进等
#如果代码缩进没有问题,那么执行该命令不会有输出
learn-terraform-docker-container]# terraform fmt 
main.tf
#校验配置文件是否有效
learn-terraform-docker-container]# terraform validate 
Success! The configuration is valid.

初始化项目

terraform init :初始化项目。它会根据配置文件下载我们需要的provider plugins,让terraform与docker进行交互。这一步会连接github下载,需要保证网络顺通。

learn-terraform-docker-container]# HTTPS_PROXY="192.168.66.1:10811" terraform init

Initializing the backend...

Initializing provider plugins...
- Finding kreuzwerker/docker versions matching "~> 3.0.1"...
- Installing kreuzwerker/docker v3.0.2...
- Installed kreuzwerker/docker v3.0.2 (self-signed, key ID BD080C4571C6104C)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

展示当前配置的计划

有点像k8s集群创建资源的--dry-run,它会检测tf配置文件,告诉我们哪些资源做了变更,但是不做创建或更新资源的动作

  • docker_container.nginx将会被创建,docker_image.nginx将会被创建
learn-terraform-docker-container]# 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_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                                       = (known after apply)
      + init                                        = (known after apply)
      + ipc_mode                                    = (known after apply)
      + log_driver                                  = (known after apply)
      + logs                                        = false
      + must_run                                    = true
      + name                                        = "tutorial"
      + 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

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

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

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

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply"
now.

创建基础设施

terraform apply:根据tf配置文件创建基础设施,并生成一个terraform.tfstate文件,用于保存基础设施的状态。

  • 中间会有一个交互式操作,需要我们输入yes确认,才会创建相关的资源
  • 执行apply后会先把trraform plan的内容打印一遍,然后让我们输出yes确认,然后才开始创建资源。
  • 先是创建了docker_image.nginx资源,然后创建了docker_container.nginx资源
learn-terraform-docker-container]# terraform apply

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                                       = (known after apply)
      + init                                        = (known after apply)
      + ipc_mode                                    = (known after apply)
      + log_driver                                  = (known after apply)
      + logs                                        = false
      + must_run                                    = true
      + name                                        = "tutorial"
      + 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

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

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

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

docker_image.nginx: Creating...
docker_image.nginx: Still creating... [10s elapsed]
docker_image.nginx: Creation complete after 16s [id=sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8nginx]
docker_container.nginx: Creating...
docker_container.nginx: Creation complete after 1s [id=981b7c660d041ed1c296a82fcb7db0927b244cb0ce00024863ad2be420a11436]

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

查看镜像和创建的容器

learn-terraform-docker-container]# docker image ls nginx
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        latest    904b8cb13b93   2 weeks ago   142MB
learn-terraform-docker-container]# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                  NAMES
981b7c660d04   904b8cb13b93   "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   0.0.0.0:8000->80/tcp   tutorial

terraform目录结构

看下面第六节

检查管理基础设施状态

  • terraform show:检查当前infrastructure的状态
  • terraform state list:列出项目状态下的资源
learn-terraform-docker-container]# terraform show
# docker_container.nginx:
resource "docker_container" "nginx" {
    attach                                      = false
    command                                     = [
        "nginx",
        "-g",
        "daemon off;",
    ]
    container_read_refresh_timeout_milliseconds = 15000
    cpu_shares                                  = 0
    entrypoint                                  = [
        "/docker-entrypoint.sh",
    ]
    env                                         = []
    hostname                                    = "c6e5cbe2abef"
    id                                          = "c6e5cbe2abef2285b4a658ec21cdeebf35c4b57ddb3b6542bd9ba137841ff0d2"
    image                                       = "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8"
    init                                        = false
    ipc_mode                                    = "private"
    log_driver                                  = "json-file"
    logs                                        = false
    max_retry_count                             = 0
    memory                                      = 0
    memory_swap                                 = 0
    must_run                                    = true
    name                                        = "tutorial"
    network_data                                = [
        {
            gateway                   = "172.17.0.1"
            global_ipv6_address       = ""
            global_ipv6_prefix_length = 0
            ip_address                = "172.17.0.2"
            ip_prefix_length          = 16
            ipv6_gateway              = ""
            mac_address               = "02:42:ac:11:00:02"
            network_name              = "bridge"
        },
    ]
    network_mode                                = "default"
    privileged                                  = false
    publish_all_ports                           = false
    read_only                                   = false
    remove_volumes                              = true
    restart                                     = "no"
    rm                                          = false
    runtime                                     = "runc"
    security_opts                               = []
    shm_size                                    = 64
    start                                       = true
    stdin_open                                  = false
    stop_signal                                 = "SIGQUIT"
    stop_timeout                                = 0
    tty                                         = false
    wait                                        = false
    wait_timeout                                = 60

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

# docker_image.nginx:
resource "docker_image" "nginx" {
    id           = "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8nginx"
    image_id     = "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8"
    keep_locally = false
    name         = "nginx"
    repo_digest  = "nginx@sha256:aa0afebbb3cfa473099a62c4b32e9b3fb73ed23f2a75a65ce1d4b4f55a5c2ef2"
}
learn-terraform-docker-container]# terraform state list
docker_container.nginx
docker_image.nginx

修改terraform文件

也就是修改infrastructure配置。我这里将容器的名字由tutorial改为demo,然后再次执行terraform apply

可以看到terraform执行的详细过程。首先是把terraform plan的内容打印了一下(需要add一个资源,destroy一个资源),然后输入yes确认;然后是真正的操作过程(先把docker_container.nginx资源destroy,然后create一个新的docker_container.nginx资源)

learn-terraform-docker-container]# terraform apply
docker_image.nginx: Refreshing state... [id=sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8nginx]
docker_container.nginx: Refreshing state... [id=c6e5cbe2abef2285b4a658ec21cdeebf35c4b57ddb3b6542bd9ba137841ff0d2]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # docker_container.nginx must be replaced
-/+ resource "docker_container" "nginx" {
      + bridge                                      = (known after apply)
      ~ command                                     = [
          - "nginx",
          - "-g",
          - "daemon off;",
        ] -> (known after apply)
      + container_logs                              = (known after apply)
      - cpu_shares                                  = 0 -> null
      - dns                                         = [] -> null
      - dns_opts                                    = [] -> null
      - dns_search                                  = [] -> null
      ~ entrypoint                                  = [
          - "/docker-entrypoint.sh",
        ] -> (known after apply)
      ~ env                                         = [] -> (known after apply)
      + exit_code                                   = (known after apply)
      - group_add                                   = [] -> null
      ~ hostname                                    = "c6e5cbe2abef" -> (known after apply)
      ~ id                                          = "c6e5cbe2abef2285b4a658ec21cdeebf35c4b57ddb3b6542bd9ba137841ff0d2" -> (known after apply)
      ~ init                                        = false -> (known after apply)
      ~ ipc_mode                                    = "private" -> (known after apply)
      ~ log_driver                                  = "json-file" -> (known after apply)
      - log_opts                                    = {} -> null
      - max_retry_count                             = 0 -> null
      - memory                                      = 0 -> null
      - memory_swap                                 = 0 -> null
      ~ name                                        = "tutorial" -> "demo" # forces replacement
      ~ network_data                                = [
          - {
              - gateway                   = "172.17.0.1"
              - global_ipv6_address       = ""
              - global_ipv6_prefix_length = 0
              - ip_address                = "172.17.0.2"
              - ip_prefix_length          = 16
              - ipv6_gateway              = ""
              - mac_address               = "02:42:ac:11:00:02"
              - network_name              = "bridge"
            },
        ] -> (known after apply)
      - network_mode                                = "default" -> null
      - privileged                                  = false -> null
      - publish_all_ports                           = false -> null
      ~ runtime                                     = "runc" -> (known after apply)
      ~ security_opts                               = [] -> (known after apply)
      ~ shm_size                                    = 64 -> (known after apply)
      ~ stop_signal                                 = "SIGQUIT" -> (known after apply)
      ~ stop_timeout                                = 0 -> (known after apply)
      - storage_opts                                = {} -> null
      - sysctls                                     = {} -> null
      - tmpfs                                       = {} -> null
        # (14 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

docker_container.nginx: Destroying... [id=c6e5cbe2abef2285b4a658ec21cdeebf35c4b57ddb3b6542bd9ba137841ff0d2]
docker_container.nginx: Destruction complete after 0s
docker_container.nginx: Creating...
docker_container.nginx: Creation complete after 0s [id=de2f6494366e68a392c5ffb99386c05677d553799cedd6d03114088ff871102c]

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

销毁操作

terraform destroy:销毁tf配置文件里面定义的infrastructure

在main.tf文件里面docker_image资源里面有一个keep_locally = false的内容,表示销毁该资源的时候不保留本地镜像

learn-terraform-docker-container]# terraform destroy 
docker_image.nginx: Refreshing state... [id=sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8nginx]
docker_container.nginx: Refreshing state... [id=de2f6494366e68a392c5ffb99386c05677d553799cedd6d03114088ff871102c]

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

Terraform will perform the following actions:

  # docker_container.nginx will be destroyed
  - resource "docker_container" "nginx" {
      - attach                                      = false -> null
      - command                                     = [
          - "nginx",
          - "-g",
          - "daemon off;",
        ] -> null
      - container_read_refresh_timeout_milliseconds = 15000 -> null
      - cpu_shares                                  = 0 -> null
      - dns                                         = [] -> null
      - dns_opts                                    = [] -> null
      - dns_search                                  = [] -> null
      - entrypoint                                  = [
          - "/docker-entrypoint.sh",
        ] -> null
      - env                                         = [] -> null
      - group_add                                   = [] -> null
      - hostname                                    = "de2f6494366e" -> null
      - id                                          = "de2f6494366e68a392c5ffb99386c05677d553799cedd6d03114088ff871102c" -> null
      - image                                       = "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8" -> null
      - init                                        = false -> null
      - ipc_mode                                    = "private" -> null
      - log_driver                                  = "json-file" -> null
      - log_opts                                    = {} -> null
      - logs                                        = false -> null
      - max_retry_count                             = 0 -> null
      - memory                                      = 0 -> null
      - memory_swap                                 = 0 -> null
      - must_run                                    = true -> null
      - name                                        = "demo" -> null
      - network_data                                = [
          - {
              - gateway                   = "172.17.0.1"
              - global_ipv6_address       = ""
              - global_ipv6_prefix_length = 0
              - ip_address                = "172.17.0.2"
              - ip_prefix_length          = 16
              - ipv6_gateway              = ""
              - mac_address               = "02:42:ac:11:00:02"
              - network_name              = "bridge"
            },
        ] -> null
      - network_mode                                = "default" -> null
      - privileged                                  = false -> null
      - publish_all_ports                           = false -> null
      - read_only                                   = false -> null
      - remove_volumes                              = true -> null
      - restart                                     = "no" -> null
      - rm                                          = false -> null
      - runtime                                     = "runc" -> null
      - security_opts                               = [] -> null
      - shm_size                                    = 64 -> null
      - start                                       = true -> null
      - stdin_open                                  = false -> null
      - stop_signal                                 = "SIGQUIT" -> null
      - stop_timeout                                = 0 -> null
      - storage_opts                                = {} -> null
      - sysctls                                     = {} -> null
      - tmpfs                                       = {} -> null
      - tty                                         = false -> null
      - wait                                        = false -> null
      - wait_timeout                                = 60 -> null

      - ports {
          - external = 8000 -> null
          - internal = 80 -> null
          - ip       = "0.0.0.0" -> null
          - protocol = "tcp" -> null
        }
    }

  # docker_image.nginx will be destroyed
  - resource "docker_image" "nginx" {
      - id           = "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8nginx" -> null
      - image_id     = "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8" -> null
      - keep_locally = false -> null
      - name         = "nginx" -> null
      - repo_digest  = "nginx@sha256:aa0afebbb3cfa473099a62c4b32e9b3fb73ed23f2a75a65ce1d4b4f55a5c2ef2" -> null
    }

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

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

docker_container.nginx: Destroying... [id=de2f6494366e68a392c5ffb99386c05677d553799cedd6d03114088ff871102c]
docker_container.nginx: Destruction complete after 0s
docker_image.nginx: Destroying... [id=sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8nginx]
docker_image.nginx: Destruction complete after 0s

Destroy complete! Resources: 2 destroyed.

执行销毁操作后,tfstate状态文件也就没有资源的状态了

learn-terraform-docker-container]# cat terraform.tfstate
{
  "version": 4,
  "terraform_version": "1.4.2",
  "serial": 26,
  "lineage": "c07a4276-9059-92fb-f905-095f2c5a9e2d",
  "outputs": {},
  "resources": [],
  "check_results": null
}

六、terraform目录结构

可以看到,我们刚才只创建了一个main.tf文件,现在又多出了好几个文件

  • .terraform目录:存放docker的provider,版本是v3.0.2
  • .terraform.lock.hcl:存放docker provider版本相关的信息,锁定docker provider的版本(是不是有点像nodejs里面的package-lock.json文件)
  • terraform.tfstate:状态文件。 这个文件是非常非常重要的 ,它表示的是当前infrastructure的状态是什么。这个文件就确保了我们在部署infrastructure有个一致性了。当我们再次执行terraform apply的时候,他说不会有任何变化的,因为它会读取tfstate文件,发现已经满足我们的要求了,就不会再执行。
    • 如果不小心把tfstate文件删除了,再次执行terraform apply它就会执行拉取镜像和创建容器的操作。
    • 每次执行terraform apply,它都会对原来的tfstate文件做一个backup,也就是下面的terraform.tfstate.backup文件
learn-terraform-docker-container]# tree . -a
.
├── main.tf
├── .terraform
│   └── providers
│       └── registry.terraform.io
│           └── kreuzwerker
│               └── docker
│                   └── 3.0.2
│                       └── linux_amd64
│                           ├── CHANGELOG.md
│                           ├── LICENSE
│                           ├── README.md
│                           └── terraform-provider-docker_v3.0.2
├── .terraform.lock.hcl
├── terraform.tfstate
└── terraform.tfstate.backup

7 directories, 8 files
learn-terraform-docker-container]# cat .terraform.lock.hcl 
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.

provider "registry.terraform.io/kreuzwerker/docker" {
  version     = "3.0.2"
  constraints = "~> 3.0.1"
  hashes = [
    "h1:cT2ccWOtlfKYBUE60/v2/4Q6Stk1KYTNnhxSck+VPlU=",
    "zh:15b0a2b2b563d8d40f62f83057d91acb02cd0096f207488d8b4298a59203d64f",
    "zh:23d919de139f7cd5ebfd2ff1b94e6d9913f0977fcfc2ca02e1573be53e269f95",
    "zh:38081b3fe317c7e9555b2aaad325ad3fa516a886d2dfa8605ae6a809c1072138",
    "zh:4a9c5065b178082f79ad8160243369c185214d874ff5048556d48d3edd03c4da",
    "zh:5438ef6afe057945f28bce43d76c4401254073de01a774760169ac1058830ac2",
    "zh:60b7fadc287166e5c9873dfe53a7976d98244979e0ab66428ea0dea1ebf33e06",
    "zh:61c5ec1cb94e4c4a4fb1e4a24576d5f39a955f09afb17dab982de62b70a9bdd1",
    "zh:a38fe9016ace5f911ab00c88e64b156ebbbbfb72a51a44da3c13d442cd214710",
    "zh:c2c4d2b1fd9ebb291c57f524b3bf9d0994ff3e815c0cd9c9bcb87166dc687005",
    "zh:d567bb8ce483ab2cf0602e07eae57027a1a53994aba470fa76095912a505533d",
    "zh:e83bf05ab6a19dd8c43547ce9a8a511f8c331a124d11ac64687c764ab9d5a792",
    "zh:e90c934b5cd65516fbcc454c89a150bfa726e7cf1fe749790c7480bbeb19d387",
    "zh:f05f167d2eaf913045d8e7b88c13757e3cf595dd5cd333057fdafc7c4b7fed62",
    "zh:fcc9c1cea5ce85e8bcb593862e699a881bd36dffd29e2e367f82d15368659c3d",
  ]
}



learn-terraform-docker-container]# cat terraform.tfstate
{
  "version": 4,
  "terraform_version": "1.4.2",
  "serial": 13,
  "lineage": "c07a4276-9059-92fb-f905-095f2c5a9e2d",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "docker_container",
      "name": "nginx",
      "provider": "provider[\"registry.terraform.io/kreuzwerker/docker\"]",
      "instances": [
        {
          "schema_version": 2,
          "attributes": {
            "attach": false,
            "bridge": "",
            "capabilities": [],
            "cgroupns_mode": null,
            "command": [
              "nginx",
              "-g",
              "daemon off;"
            ],
            "container_logs": null,
            "container_read_refresh_timeout_milliseconds": 15000,
            "cpu_set": "",
            "cpu_shares": 0,
            "destroy_grace_seconds": null,
            "devices": [],
            "dns": null,
            "dns_opts": null,
            "dns_search": null,
            "domainname": "",
            "entrypoint": [
              "/docker-entrypoint.sh"
            ],
            "env": [],
            "exit_code": null,
            "gpus": null,
            "group_add": null,
            "healthcheck": null,
            "host": [],
            "hostname": "143d04de74e8",
            "id": "143d04de74e840acc6bca210767606ffac4352539e7e7fbc81c12aa4a52057a2",
            "image": "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8",
            "init": false,
            "ipc_mode": "private",
            "labels": [],
            "log_driver": "json-file",
            "log_opts": null,
            "logs": false,
            "max_retry_count": 0,
            "memory": 0,
            "memory_swap": 0,
            "mounts": [],
            "must_run": true,
            "name": "demo",
            "network_data": [
              {
                "gateway": "172.17.0.1",
                "global_ipv6_address": "",
                "global_ipv6_prefix_length": 0,
                "ip_address": "172.17.0.2",
                "ip_prefix_length": 16,
                "ipv6_gateway": "",
                "mac_address": "02:42:ac:11:00:02",
                "network_name": "bridge"
              }
            ],
            "network_mode": "default",
            "networks_advanced": [],
            "pid_mode": "",
            "ports": [
              {
                "external": 8000,
                "internal": 80,
                "ip": "0.0.0.0",
                "protocol": "tcp"
              }
            ],
            "privileged": false,
            "publish_all_ports": false,
            "read_only": false,
            "remove_volumes": true,
            "restart": "no",
            "rm": false,
            "runtime": "runc",
            "security_opts": [],
            "shm_size": 64,
            "start": true,
            "stdin_open": false,
            "stop_signal": "SIGQUIT",
            "stop_timeout": 0,
            "storage_opts": null,
            "sysctls": null,
            "tmpfs": null,
            "tty": false,
            "ulimit": [],
            "upload": [],
            "user": "",
            "userns_mode": "",
            "volumes": [],
            "wait": false,
            "wait_timeout": 60,
            "working_dir": ""
          },
          "sensitive_attributes": [],
          "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjIifQ==",
          "dependencies": [
            "docker_image.nginx"
          ]
        }
      ]
    },
    {
      "mode": "managed",
      "type": "docker_image",
      "name": "nginx",
      "provider": "provider[\"registry.terraform.io/kreuzwerker/docker\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "build": [],
            "force_remove": null,
            "id": "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8nginx",
            "image_id": "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8",
            "keep_locally": false,
            "name": "nginx",
            "platform": null,
            "pull_triggers": null,
            "repo_digest": "nginx@sha256:aa0afebbb3cfa473099a62c4b32e9b3fb73ed23f2a75a65ce1d4b4f55a5c2ef2",
            "triggers": null
          },
          "sensitive_attributes": [],
          "private": "bnVsbA=="
        }
      ]
    }
  ],
  "check_results": null
}