Skip to content

Commit

Permalink
Working deployment of spring boot app with Terraform
Browse files Browse the repository at this point in the history
  • Loading branch information
thewaterwalker committed Nov 7, 2020
1 parent 180fa88 commit f34c18b
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 173 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ jspm_packages/
# Optional npm cache directory
.npm
node/
.DS_Store/
.DS_Store
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ GraphQL Batching and DataLoaders : https://github.com/graphql-java/java-dataload

Start the Spring Boot development server with hotreload as follows

```
```shell script
$ mvn clean spring-boot:run -P hotreload
```

Now start the React development server (in the same folder as the package.json) and browse to `http://localhost:3000` for hot-reload of the ReactJS app while you are developing
```
```shell script
$ cd src/main/frontend
$ npm install
$ npm start
Expand All @@ -40,20 +40,20 @@ You can now make code changes in ReactJS and Java and the code will be hot-reloa
### Releasing

Once development is complete, simply run the following from the directory that contains `pom.xml` to create a release version of the application
```
```shell script
$ mvn clean install -P react
```

If you don't want to use Maven to build the release version of the ReactJS app run the following in the `frontend` directory and then run Maven separately
```
```shell script
$ cd src/main/frontend
$ npm run build
$ cd -
$ mvn clean install
```

You can now run the app using Java 11 or above as follows
```
```shell script
$ ${JAVA_HOME}/bin/java -jar target/spring-boot-react-0.0.1-SNAPSHOT.jar
```

Expand Down
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

Expand Down Expand Up @@ -85,6 +85,7 @@
</dependencies>

<build>
<finalName>spring-boot-ldap</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ spring.datasource.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
# Useful to show the efficiency of the GraphQL DataLoaders which remove the N+1 loads
spring.jpa.show-sql=true
server.port=9090

Expand Down
169 changes: 3 additions & 166 deletions terraform/1_terraform_simple/README.md
Original file line number Diff line number Diff line change
@@ -1,168 +1,5 @@
# Terraform Simple Deployment
This terraform plan will create a simple configuration that includes a single
VPC and a single subnet will all resources in that subnet. This subnet is public facing so that clients can access the
spring boot server.

## Terraform Basics

### Variables
Variables are declared in `variables.tf` and can be defined on the command line to in a file called `terraform.tfvars` in the same directory. Alternatively pass them on the command line using the `-var` parameter to terraform.

### Locals
Locals are variables that are defined within a `.tf` file. We do not use them apart from to set the `Name` tag for AWS resources.

```hcl
locals {
appname = "Spring Boot LDAP Demo"
}
# Use with ${local.appname}
```

### Outputs
Outputs allow developers to print specific pieces of information to the command line after the terraform utility is run. This is useful, for example, to get the ips that have been assigned to your EC2 instances. This can be a string or an attribute of any resource. Set `sensitive=true` if you want the output saved to the state file but not to the console.

```hcl
output "sample_string" {
value = "testone"
}
output "instance_ip" {
value = aws_instance.spring_boot.public_ip
}
```

### Data Sources
Data sources allow developers to grab data from existing resources that are available in the cloud provider. This may be something like the VPC id of the default VPC that exists in your Amazon account

```hcl
provider "aws" {
version = "~> 2.65"
region = "eu-west-2
}
data "aws_vpc" "myvpc" {
# you can, for example, pick the default VPC
default = true
# OR use a filter
filter {
name = "tag:Name"
value = ["Spring Boot LDAP Demo VPC"]
}
}
output "myvpc" {
value = data.aws_vpc.myvpc
}
```

You can also fetch an existing AMI for example
```hcl
data "aws_ami" "my_ami" {
owners = ["self"]
most_recent = true
}
```

### Provisioners
Similar to Amazon user data they allow you to make final configuration changes to your resources. They are a last-resort option and are not normally needed. The file provisioner can be used to write files to the newly created instance. Note that we will use Packer to create a custom AMI in the advanced

```hcl
resource "aws_instance" "spring-ldap" {
ami = "some ami id from amazon"
instance_type = "t2.micro"
tags {
Name = local.appname
}
connection {
type = "ssh"
host = self.public_ip
user = "ec2-user"
private_key = file("/home/user/key.pem")
}
provisioner "file" {
source = "../target/spring-boot-react.jar"
destination = "/spring-boot-react.jar"
}
}
```

Remote exec allows us to run a command on the remote machine.

```hcl
resource "aws_instance" "spring-ldap" {
ami = "some ami id from amazon"
instance_type = "t2.micro"
tags {
Name = local.appname
}
connection {
type = "ssh"
host = self.public_ip
user = "ec2-user"
private_key = file("/home/user/key.pem")
}
provisioner "remote-exec" {
inline = [
"touch /var/tmp/test.txt"
]
}
}
```

You can use `when = "destroy"` so that the provisioner is only run on teardown of the resource

### Terraform Workspaces
Workspace allow you to store state for a single Terraform configuration file in separate areas. For example, you may want separate state for dev and prod such that you can test any changes in a dev environment.

Show workspaces:
```hcl
$ terraform workspace list
* default
```
```hcl
$ terraform workspace new prod
default
* prod
```
```hcl
$ terraform workspace select dev
```

We can refer to the workspace name in Terraform by say assigning a local and using that in the configuration
```hcl
locals {
instance_name = "${terraform.workspace}-instance"
}
resource "aws_instance" "webserver" {
tags = {
Name = local.instance_name
}
}
```

Variables can be declared in a `variables.tf` file and defined in environment specific tf files such as `dev.tfvars` and `prod.tfvars` the use the file as follows:
```hcl
$ terraform apply -var-file dev.tfvars
```

# Terraform for Spring Boot LDAP Demo

## VPC
We define a VPC to and split that across two AWS Availability Zones but splitting that VPC into two subnets.
```tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr_block
}
resource "aws_subnet" "main" {
vpc_id = aws_vpc.main.id
cidr_block = var.subnet_cidr_block
map_public_ip_on_launch = true
tags = {
Name = "spring-boot-subnet"
}
}
```
This terraform plan will create a simple configuration that used the default VPC and a single subnet
with all resources in that subnet. This subnet is public facing so that clients can access the
spring boot server.
88 changes: 88 additions & 0 deletions terraform/1_terraform_simple/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# define provider with region set in variables
provider "aws" {
version = "~> 2.65"
region = var.region
}

# define local variables
locals {
appname = "Spring Boot LDAP Demo"
}

data "aws_subnet" "spring_boot_ldap_subnet" {
availability_zone = "eu-west-2a"
}

data "aws_vpc" "default_vpc" {
default = true
}

# use data to get the latest amazon linux 2 AMI
data "aws_ami" "amazon_linux_2" {
owners = ["amazon"]
most_recent = true

filter {
name = "owner-alias"
values = ["amazon"]
}
filter {
name = "architecture"
values = ["x86_64"]
}
filter {
name = "description"
values = ["Amazon Linux 2 AMI 2.0*"]
}
}

resource "aws_security_group" "allow_http" {
name = "allow_http"
vpc_id = data.aws_vpc.default_vpc.id

ingress {
from_port = 9090
to_port = 9090
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

# this is needed for the provisioner step below
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

# create the EC2 instance
resource "aws_instance" "spring_boot" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = var.instance_type
vpc_security_group_ids = [aws_security_group.allow_http.id]
subnet_id = data.aws_subnet.spring_boot_ldap_subnet.id
key_name = "spring_ldap_key_pair"
user_data = file("userdata.sh")
tags = {
Name = local.appname
}
connection {
type = "ssh"
host = self.public_ip
user = "ec2-user"
private_key = file("/xxx/spring_ldap_key_pair.pem")
}
# upload artifact using the provisioner
provisioner "file" {
source = "../../target/spring-boot-ldap.jar"
destination = "/home/ec2-user/spring-boot-ldap.jar"
}
}
7 changes: 7 additions & 0 deletions terraform/1_terraform_simple/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "the_ami_to_be_used" {
value = data.aws_ami.amazon_linux_2
}

output "instance_ip" {
value = aws_instance.spring_boot.public_ip
}
6 changes: 6 additions & 0 deletions terraform/1_terraform_simple/terraform.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Set up
region = "eu-west-2"
profile = "default"

# EC2 variables
instance_type = "t2.micro"
2 changes: 2 additions & 0 deletions terraform/1_terraform_simple/userdata.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
sudo amazon-linux-extras install java-openjdk11
14 changes: 14 additions & 0 deletions terraform/1_terraform_simple/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
variable "region" {
type = string
description = "AWS Region (Europe (London))"
}

# variables require
variable "profile" {
type = string
description = "AWS credentials profile you want to use (default)"
}

variable "instance_type" {
type = string
}

0 comments on commit f34c18b

Please sign in to comment.