AWS Terraform Section4 - Project

创建Git仓库用于存放Terraform代码

创建.gitignore

1
2
3
4
# ignore local .terraform dir
.terraform/*


目标:创建一个EC2实例,并且在EC2里运行一个nginx docker容器

image

需要创建的AWS资源

*最好不要使用默认的AWS资源

  1. 创建VPC
  2. 创建Subnet
  3. 创建Route Table & Internet Gateway
  4. 创建EC2
  5. 部署nginx docker容器
  6. 创建Security Group(Firewall),并允许ssh

image

创建VPC & Subnet

terraform.tfvars

1
2
3
4
vpc_cidr_block = "10.0.0.0/16"
subnet_cidr_block = "10.0.0.0/24"
avail_zone = "eu-west-3b"
env_prefix = "dev"

main.tf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
provider "aws" {
region = "eu-west-3"
}

variable vpc_cidr_block {}
variable subnet_cidr_block {}
variable avail_zone {}
variable env_prefix {}

# 创建VPC
resource "aws_vpc" "myapp-vpc" {
cidr_block = var.vpc_cidr_block

# vpc名称带有"vpc"前缀
tags = {
Name: "${var.env_prefix}-vpc"
}
}

# 创建Subnet
resource "aws_subnet" "myapp-subnet-1" {
vpc_id = aws_vpc.myapp-vpc.id
cidr_block = var.vpc_cidr_block
availability_zone = var.avail_zone
tags = {
Name: "${var.env_prefix}-subnet-1"
}
}

在AWS上查看结果

image

Route Table

Route table决定了VPC转发的目的地

image

在Terraform code不必根据执行的先后来编写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 创建Route table
resource "aws_route_table" "myapp-route-table" {
vpc_id = aws_vpc.myapp-vpc.id

route = {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.myapp-igw.id
}

tags = {
Name: "${var.env_prefix}-rtb"
}
}

# Route table gateway
resource "aws_internet_gateway" "myapp-igw" {
vpc_id = aws_vpc.myapp-vpc.id

tags = {
Name: "${var.env_prefix}-igw"
}
}

执行结果

image

同时还要为Route table和Subnet创建关联

image

Terraform code

1
2
3
4
5
# Route table & Subnect
resource "aws_route_table_association" "a-rtb-subnet" {
subnet_id = aws_subnet.myapp-subnet-1.id
route_table_id = aws_route_table.myapp-route-table.id
}

Security Group & Firewall

创建安全组

image

Terraform code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Security Group
resource "aws_security_group" "myapp-sg" {
name = "myapp-sg"
vpc_id = aws_vpc.myapp-vpc.id

# Incoming traffic
ingress = {
from_port = 22
to_port = 22
protocal = "tcp"

# IPs allow list
cidr_block = ["your_allow_ip/32"]
}

# Outgoing traffic
egress = {
# Allowing all port and ip
from_port = 0
to_port = 0
protocal = "-1"
cidr_block = ["0.0.0.0/0"]
prefix_list_ids = []
}

tags = {
Name: "${var.env_prefix}-sg"
}
}

执行结果

image

EC2 & AMI

Amazon Machine Image(AMI)是aws提供的操作系统镜像,用户可以根据AMI创建自己的操作系统实例。

image

Terraform code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# AWS AMI data definition
data "aws_ami" "latest_aws-linux-image" {
# Always get latest image
most_recent = true

owners = ["amazon"]

# filter image name with regrex
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64_gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}

# Only test for ami id output
# When execute "terraform plan", can see value from console
# output "name" {
# value = data.aws_ami.latest_aws-linux-image
# }

# 创建aws key pair
resource "aws_key_pair" "ssh-key" {
key_name = "server-key"

# 引用文件
public_key = "${file("/User/xxx/.ssh/key_file.pub")}"
}

# 创建操作系统实例 - EC2
resource "aws_instance" "myapp-server" {
# Id of ami
ami = data.aws_ami.latest_aws-linux-image.id
instance_type = "t2.micro"

# 引用已经创建好的VPC & Subnect
subnet_id = aws_subnet.myapp-subnet-1.id
vpc_security_group_ids = [aws_default_security_group.default-sg.id]
availability_zone = var.avail_zone
associate_public_ip_address = true

# Server key pair, permission should be 400
# AWS will reject ssh request if permission not set correctly
# It's generate from AWS console, provide the key file name
key_name = aws_key_pair.ssh-key.key_name

tags = {
Name = "${var.env_prefix}-server"
}
}

等待EC2创建

image

EC2 initialize script

添加 user_data=​,设置EC2初始化时自动运行脚本

下图中的代码块只会在初始化的时候被执行一次

image

如果需要执行定义好的脚本而不是代码块,可以使用如下方式

image

另一种方式:使用Provisioner执行脚本

使用remote-exec​在EC2上执行脚本

image

可以使用local-exec​在本地机器上执行命令

1
2
3
4

provisioner "local-exec" {
command = "echo ${self.public_ip} > output.txt"
}

Terraform文档上说明Provisioners​是没有其他选择时的最后手段

  • 尽量采用user_data​而不是Provisioners
  • 尽量使用”local provider”取代local-exec
  • 尽量采用配置管理工具取代Provisioners​,如Ansible, Jenkins

实际上Provisioners​打破了Terraform的概念。Terraform能通过相同的组件状态总是提供一致的返回结果,但是Provisioners​执行的脚本却是未知的,Terraform无法获取到其中的状态。

image

一种可能的情况是,Provisioner中的脚本执行错误,此时terraform不会返回具体的状态信息。

这种状态下无法得知EC2具体是否被创建

image

0%