Terraform guide 3 - CI/CD environment
Terraform: CI/CD environment
Building a custom Jenkins image
Setup environment
1
2mkdir -p jenkins
nano vi DockerfileEdit dockerfile
1
2
3
4
5
6
7
8
9FROM jenkins/jenkins:lts
USER root
RUN apt-get update -y && apt-get -y install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
RUN curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey
RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable"
RUN apt-get update -y
RUN apt-get install -y docker-ce docker-ce-cli containerd.io
RUN curl -O https://releases.hashicorp.com/terraform/0.11.13/terraform_0.11.13_linux_amd64.zip && unzip terraform_0.11.13_linux_amd64.zip -d /usr/local/bin/
USER ${user}Build the image
1
2
3docker build -t jenkins:terraform .
# check images
docker image ls
Setting up Jenkins
Edit
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# Jenkins Volume
resource "docker_volume" "jenkins_volume" {
name = "jenkins_data"
}
# Start the Jenkins Container
resource "docker_container" "jenkins_container" {
name = "jenkins"
image = "jenkins:terraform"
ports {
internal = "8080"
external = "8080"
}
volumes {
volume_name = "${docker_volume.jenkins_volume.name}"
container_path = "/var/jenkins_home"
}
volumes {
host_path = "/var/run/docker.sock"
container_path = "/var/run/docker.sock"
}
}Deploy via Terraform
1
2
3
4
5
6terraform init
terraform plan -out=tfplan
# deploy Jenkins
terraform apply tfplan
# get the admin password
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
Creating a Jenkins simple job
- Job will deploy a Docker container using Terraform, list the container, and then destroy it
- example
- In the Jenkins dashboard: ‘New Item’
- Select ‘Freestyle Project’, item name = “DeployGhost”
- Source Code Management -> Git. Repository URL =
https://github.com/linuxacademy/content-terraform-docker.git
- Build section -> Add build step -> Execute shell
1
2
3
4
5terraform init
terraform plan -out=tfplan
terraform apply tfplan
docker container ls
terraform destroy -auto-approve - ‘Build Now’ (left-hand menu) -> arrow next to
#1
-> Console Output, wait for result
Building a Jenkins pipeline
Deploy out a Ghost blog
- Jenkins dashboard -> ‘New Item’ -> item name = “PipelinePart1”, Pipeline, Ok
- Check the box for ‘This project is parameterized’
- ‘Add Parameter’ -> Choice Parameter
- Name = “action”
- Choices = “Deploy”, “Destroy” (make sure they are on separate lines)
- Description “The action that will be executed”
- ‘Add Parameter’ -> Choice Parameter
- Name = “image_name”
- Choices = “ghost:latest”, “ghost:alpine” (mak3 sure they are on separate lines)
- Description = “Enter The image Ghost Blog will deploy”
- ‘Add Parameter’ -> String Parameter
- Name = “ext_port”
- Default Value = “80”
- DEscription = “The Public Port”
- ‘Add Parameter’ -> Choice Parameter
- In the Pipeline section, ‘Definition of Pipeline script’, and add
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
35node {
git 'https://github.com/linuxacademy/content-terraform-docker.git'
if(action == 'Deploy') {
stage('init') {
sh """
terraform init
"""
}
stage('plan') {
sh label: 'terraform plan', script: "terraform plan -out=tfplan -input=false -var image_name=${image_name} -var ext_port=${ext_port}"
script {
timeout(time: 10, unit: 'MINUTES') {
input(id: "Deploy Gate", message: "Deploy environment?", ok: 'Deploy')
}
}
}
stage('apply') {
sh label: 'terraform apply', script: "terraform apply -lock=false -input=false tfplan"
}
}
if(action == 'Destroy') {
stage('plan_destroy') {
sh label: 'terraform plan destroy', script: "terraform plan -destroy -out=tfdestroyplan -input=false -var image_name=${image_name} -var ext_port=${ext_port}"
}
stage('destroy') {
script {
timeout(time: 10, unit: 'MINUTES') {
input(id: "Destroy Gate", message: "Destroy environment?", ok: 'Destroy')
}
}
sh label: 'Destroy environment', script: "terraform apply -lock=false -input=false tfdestroyplan"
}
}
}
Deploy out a Swarm service
Setup of a Docker Swarm
- On the manager node get the join token
1
docker swarm join-token worker
- On the worker node run the join command (pasting the join token)
1
docker swarm join --token [JOIN_TOKEN] [IP]:2377
- On the manager node get the join token
Get Jenkins Running
- Get the Jenkins password
1
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
- Browse to
http://< Swarm_Manager_Public_IP >:8080/
, use the password - Install the suggested plugins
- Create a user. Done
- If issues with updates or dependencies: Dashboard: ‘Manage Jenkins’, fix things there
- Set up a pipelin
- Jenkins dashboard -> ‘New Item’. Item name =”PipelinePart2”, Pipeline, Ok
- Check box for “This project is parameterized”
- ‘Add Parameter’ -> Choice Parameter
- Name = “action”
- Choices = “Deploy” and “Destroy” (make sure they are on separate lines)
- Description = “The action that will be executed”
- ‘Add Parameter’ -> Choice Parameter
- Name = “image_name”
- Choices = “ghost:latest” and “ghost:alpine” (make sure they are on separate lines)
- Description = “The image Ghost Blog will deploy”
- ‘Add Parameter’ -> String Parameter
- Name = “ghost_ext_port”
- Default Value = “80”
- Description = “The Public Port”
- ‘Add Parameter’ -> Choice Parameter
- In the Pipeline section -> ‘Definition of Pipeline script’, add:
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
36node {
git 'https://github.com/linuxacademy/content-terraform-docker-service.git'
if(action == 'Deploy') {
stage('init') {
sh label: 'terraform init', script: "terraform init"
}
stage('plan') {
sh label: 'terraform plan', script: "terraform plan -out=tfplan -input=false -var image_name=${image_name} -var ghost_ext_port=${ghost_ext_port}"
script {
timeout(time: 10, unit: 'MINUTES') {
input(id: "Deploy Gate", message: "Deploy environment?", ok: 'Deploy')
}
}
}
stage('apply') {
sh label: 'terraform apply', script: "terraform apply -lock=false -input=false tfplan"
}
}
if(action == 'Destroy') {
stage('plan_destroy') {
sh label: 'terraform plan', script: "terraform plan -destroy -out=tfdestroyplan -input=false -var image_name=${image_name} -var ghost_ext_port=${ghost_ext_port}"
}
stage('destroy') {
script {
timeout(time: 10, unit: 'MINUTES') {
input(id: "Destroy Gate", message: "Destroy environment?", ok: 'Destroy')
}
}
sh label: 'terraform apply', script: "terraform apply -lock=false -input=false tfdestroyplan"
}
stage('cleanup') {
sh label: 'cleanup', script: "rm -rf terraform.tfstat"
}
}
}
Create a MySQL Swarm service that uses Docker Secrets
- Jenkins dashboard -> ‘New Item’. Item name = “PipelinePart3”, Pipeline, Ok
- Check the box for ‘This project is parameterized’
- ‘Add Parameter’ -> Choice Parameter.
- Name = “action”
- Choices = “Deploy” and “Destroy”, (make sure they are on separate lines)
- Description = “The action that will be executed”
- ‘Add Parameter’ -> String Parameter
- Name = “mysql_root_password”
- Default Value = “P4ssW0rd0!”
- Description = “MySQL root password”
- ‘Add Parameter’ -> String Parameter
- Name = “mysql_user_password”
- Defaut value = “paSsw0rd0!”
- Description = “MySQL user password”
- ‘Add Parameter’ -> Choice Parameter.
- In the Pipeline section, ‘Definition of Pipeline script’, and add:
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
40node {
git 'https://github.com/linuxacademy/content-terraform-docker-secrets.git'
if(action == 'Deploy') {
stage('init') {
sh label: 'terraform init', script: "terraform init"
}
stage('plan') {
def ROOT_PASSWORD = sh (returnStdout: true, script: """echo ${mysql_root_password} | base64""").trim()
def USER_PASSWORD = sh (returnStdout: true, script: """echo ${mysql_user_password} | base64""").trim()
sh label: 'terraform plan', script: "terraform plan -out=tfplan -input=false -var mysql_root_password=${ROOT_PASSWORD} -var mysql_db_password=${USER_PASSWORD}"
script {
timeout(time: 10, unit: 'MINUTES') {
input(id: "Deploy Gate", message: "Deploy ${params.project_name}?", ok: 'Deploy')
}
}
}
stage('apply') {
sh label: 'terraform apply', script: "terraform apply -lock=false -input=false tfplan"
}
}
if(action == 'Destroy') {
stage('plan_destroy') {
def ROOT_PASSWORD = sh (returnStdout: true, script: """echo ${mysql_root_password} | base64""").trim()
def USER_PASSWORD = sh (returnStdout: true, script: """echo ${mysql_user_password} | base64""").trim()
sh label: 'terraform plan', script: "terraform plan -destroy -out=tfdestroyplan -input=false -var mysql_root_password=${ROOT_PASSWORD} -var mysql_db_password=${USER_PASSWORD}"
}
stage('destroy') {
script {
timeout(time: 10, unit: 'MINUTES') {
input(id: "Destroy Gate", message: "Destroy ${params.project_name}?", ok: 'Destroy')
}
}
sh label: 'terraform apply', script: "terraform apply -lock=false -input=false tfdestroyplan"
}
stage('cleanup') {
sh label: 'cleanup', script: "rm -rf terraform.tfstat"
}
}
}