Terraform 通过 Provisioner 配置服务器

Provisioner 介绍

当虚拟服务器创建完成后,通常需要执行一些初始化的操作。例如:安装软件,配置系统,服务等。

在前面的案例中使用云商的 user_data 用户数据来执行 shell 脚本来安装 nginx 服务器。

Terraform 也提供了 Provisioner 来完成这种场景。通过 Provisioner 可以在基础设施资源创建或销毁时,执行定制化的操作。 Provisioner 可以于 ansible,puppet,saltstack 配置管理工具结合使用,利用现有的配置管理工具管理资源。

Provisioner 使用案例

使用 Provisioner 购买基础设施并搭建一个服务。使用3个文件

mian.tf

复制代码
/*
  创建 AWS EC2 实例
  运行 Docker 容器
*/

# 供应商
provider "aws" {          // 云供应商
  region = var.region     // 地域
}

// 数据源
data "aws_security_groups" "default" {  // 数据源为"aws_security_groups",数据源名称"default"
  filter {
    name = "group-name"                 // 过滤 group_name = default 的安全组
    values = ["default"]
  }
}

// 创建 EC2 实例,运行 docker 容器
resource "aws_instance" "ss" {  
    ami = lookup(var.amis, var.region)    # 配置参数,ami的id。
    instance_type = var.instance_type     # 配置参数,启动的ec2的类型,t2.micro是免费的
    key_name = aws_key_pair.ssh.key_name  # 引用了 aws_key_pair ssh 中的 key_name。于EC2实例绑定,实现可以ssh的目的。
    tags = {    # 将EC2 实例命名为 "ss-server"
      Name = "ss-server"
    }

    # 连接远程服务器
    connection {
      type = "ssh"
      user = "ubuntu"
      private_key = file("id_rsa")
      host = aws_instance.ss.public_ip
    }

    # 安装 docker 并运行 ss 容器
    provisioner "remote-exec" {
      inline = [
      "sudo apt update",
      "sudo apt install -y docker.io",
      "sudo docker run -e PASSWORD=${var.ss_password} -p 8388:8388 -p 8388:8388/udp -d shadowsocks/shadowsocks-libev",
      ]
    }
}

# 添加SSH登陆密钥
resource "aws_key_pair" "ssh" {
  key_name = "admin"
  public_key = file(var.public_key)
}

// 开放22端口,允许SSH登陆
resource "aws_security_group_rule" "ssh" {
  type = "ingress"
  from_port = 22
  to_port = 22
  protocol = "tcp"
  cidr_blocks = ["0.0.0.0/0"]
  security_group_id = data.aws_security_groups.default.ids[0]   // 调用数据源的信息,列表的第一个值
}

// 开放80端口,允许WEB访问   
resource "aws_security_group_rule" "web" {
  type = "ingress"
  from_port = 8388
  to_port = 8388
  protocol = "all"
  cidr_blocks = ["0.0.0.0/0"]
  security_group_id = data.aws_security_groups.default.ids[0]
}

variables.tf

复制代码
# variables.tf

variable "region" {             //  变量名 region,不可用重复。花括号里面是参数
  type = string                 //  输入变量的类型
  default = "us-west-2"         //  变量的默认值
  description = "AWS region"    //  变量的描述
}

variable "amis" {
  type = map
  default = {
    us-west-2 = "ami-03f65b8614a860c29"  // ubuntu ami
  }
  description = "AWS ID"
}

variable "instance_type" {
  type = string
  default = "t2.micro"
  description = "EC2 instance type"
}

variable "public_key" {
  type = string
  default = "id_rsa.pub"
  description = "SSH public key"
}

variable "ss_password" {
  type = string
  description = "ss password"
}

outputs.tf

复制代码
output "IP" {
  value = aws_instance.ss.public_ip
  description = "AWS EC2 public IP"
} 

connection 连接

connection 块告诉 Terraform 用什么方式与远端机器进行通讯。

复制代码
# 连接远程服务器
connection {
  type = "ssh"                                            # 连接方式
  user = "ec2-user"                                    # 远程服务器的服务名
  private_key = file("id_rsa")            # 配置使用ssh密钥登陆。也可以使用password登陆
  host = aws_instanc.ss.public_ip        # 远端服务器的IP地址
}

provisioner remote-exec 执行命令

登陆到服务器上之后,要关注 Terraform 执行的操作。本案例是在服务器上安装 docker,在docker上运行一个镜像服务。

inline参数支持接受一个命令列表。EC2实例创建完成后,按顺序执行命令列表中的命令。

安装 docker 并运行 ss 容器

复制代码
provisioner "remote-exec" {
  inline = [
  "sudo apt update",
  "sudo apt install -y docker.io",
  "sudo docker run -e PASSWORD=${var.ss_password} -p 8388:8388 -p 8388:8388/udp -d shadowsocks/shadowsocks-libev",
  ]
}

除了inline参数,provisioner还支持 script 和 scripts 参数,后两者支持执行脚本,这三个参数是互斥的只能使用一个参数。

Provisioner 部署
复制代码
$ cp ~/.ssh/id_rsa* .

$ terraform plan

$ terraform apply
var.ss_password
  ss password

  Enter a value: Wsj@123456

...

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
  
...

提交代码

复制代码
git add .
git commit -m "using remote-exec"

Provisioner file 上传文件

使用 Provisioner file 完成服务的定制化配置。

新建安装服务的配置文件模板

其中{server} 和 {password} 是变量,由 terraform apply 的时候渲染。

复制代码
$ cat ss-config.json

{
    "server": "${server}",
    "server_port": 8388,
    "local_port": 1080,
    "password": "${password}",
    "timeout": 600,
    "method": "chacha20-ietf-poly1305",
    "fast_open": true
}
将配置文件上传到服务器上

新增了 provisioner "file" {} 代码块。可以将文件上传到服务器上。分别定义了文件内容和文件保存路径。

修改了 docker run 命令,运行 docker 的时候将本地路径挂载到 docker 容器里面。

复制代码
# main.tf
...

// 创建 EC2 实例,运行 docker 容器
resource "aws_instance" "ss" {  
    ami = lookup(var.amis, var.region)    # 配置参数,ami的id。
    instance_type = var.instance_type     # 配置参数,启动的ec2的类型,t2.micro是免费的
    key_name = aws_key_pair.ssh.key_name  # 引用了 aws_key_pair ssh 中的 key_name。于EC2实例绑定,实现可以ssh的目的。
    tags = {    # 将EC2 实例命名为 "ss-server"
      Name = "ss-server"
    }

    # 连接远程服务器
    connection {
      type = "ssh"
      user = "ubuntu"
      private_key = file("id_rsa")
      host = aws_instance.ss.public_ip
    }

    # 上传文件到服务器
    provisioner "file" {
      content =  templatefile("ss-config.json", { server = aws_instance.ss.public_ip, password = var.ss_password }) // 文件内容
      destination = "/var/tmp/ss-config.json"  // 远端服务器的文件路径
    }

    # 安装 docker 并运行 ss 容器
    provisioner "remote-exec" {
      inline = [
      "sudo apt update",
      "sudo apt install -y docker.io",
      "sudo docker run -v /var/tmp:/var/tmp -e ARGS='-c /var/tmp/ss-config.json' -p 8388:8388 -p 8388:8388/udp -d shadowsocks/shadowsocks-libev",
      ]
    }
}
...
Provisioner 部署
复制代码
% terraform apply
var.ss_password
  ss password

  Enter a value: Wsj@123456
查看docker进程
复制代码
% ssh ubuntu@18.237.89.77 sudo docker ps

CONTAINER ID   IMAGE                           COMMAND                  CREATED          STATUS          PORTS                                                                                  NAMES
ab71ac7234fd   shadowsocks/shadowsocks-libev   "/bin/sh -c 'exec ss..."   36 seconds ago   Up 34 seconds   0.0.0.0:8388->8388/tcp, 0.0.0.0:8388->8388/udp, :::8388->8388/tcp, :::8388->8388/udp   trusting_nobel
提交代码
复制代码
git add .
git commit -m "using file provisioner"
相关推荐
杜子不疼.15 分钟前
仓颉语言构造函数深度实践指南
java·服务器·前端
风一样的美狼子18 分钟前
仓颉语言 LinkedList 链表实现深度解析
java·服务器·前端
wanhengidc34 分钟前
云手机 基于云计算的虚拟手机
运维·服务器·游戏·智能手机·云计算
华普微HOPERF5 小时前
Matter协议,如何塑造更宜居、流畅的智能家居网络?
服务器·网络·智能家居
小志biubiu7 小时前
【Linux】Ext系列文件系统
linux·服务器·c语言·经验分享·笔记·ubuntu·操作系统
DooTask官方号7 小时前
DooTask 1.3.38 版本更新:MCP 服务器与 AI 工具深度融合,开启任务管理新体验
运维·服务器·人工智能·开源软件·dootask
wanhengidc9 小时前
云手机适配用户有哪些?
服务器·arm开发·安全·智能手机·玩游戏
KubeSphere 云原生10 小时前
云原生周刊:在 Kubernetes 上运行机器学习
云原生·容器·kubernetes
不知道累,只知道类11 小时前
记一次诡异的“偶发 404”排查:CDN 回源到 OSS 导致 REST API 失败
java·云原生
clear sky .12 小时前
ETH, Lwip,Tcp之间关系
服务器·网络·tcp/ip