Deploy a load-balanced application using VMware vSphere, Docker, Gogs, Ansible, Vault, Packer, Terraform, Jenkins and Nginx.
- Deploy self hosted vSphere datacenter and compute cluster with 2 ESXi hosts.
- Enable vSAN, DRS and SDRS.
- Deploy vCenter server appliance on ESXi host.
- Provision a Build machine on next ESXi host with Ubuntu 20.04 server.
- Git bash or linux terminal on local machine.
- Assume IPv4 address of Build machine VM is
192.168.149.8
and it's hostname isbuild-machine
- Check this VMware datacenter lab guide
Two Pipelines will be implemented:
- Manual Pipeline
- Automated Pipeline
This workflow involves the following steps:
- Docker Installation
- Gogs(source control) Installation and Configuration
- Vault Installation and Configuration
- Packer Installation and Configuration
- Terraform Installation and VM Deployment
- Ansible Installation and Machine Configuration
-
DOCKER INSTALLATION
To install Docker Engine for the first time on build-machine, we'll set up the Docker repository. Afterward, we can install and update Docker from the repository.
Add Docker's official GPG key:
sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc
Add the repository to Apt sources:
echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update
To install the latest version:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin.
Verify that the Docker Engine installation is successful by running the
hello-world
image.sudo docker run hello-world
This command downloads a test image and runs it in a container. When the container runs, it prints a confirmation message and exits.
You have now successfully installed and started Docker Engine.
-
GOGS INSTALLATION and CONFIGURATION
Build a simple, stable and extensible self-hosted Git service.
Pull image from Docker Hub.
docker pull gogs/gogs
Create local directory for volume.
mkdir -p /opt/gogs
Use
docker run
for the first time.docker run --name gogs --restart always -p 10022:22 -p 3880:3000 -v /opt/gogs:/data gogs/gogs
It is important to map the SSH service from the container to the host and set the appropriate SSH Port and URI settings when setting up Gogs for the first time.
To access and clone Git repositories with the above configuration you would use:
git clone ssh://git@192.168.149.8:10022/odennav/on-prem-devops-vsphere.git
Files will be store in local path of build-machine instance, /opt/gogs in my case.
For first-time run installation, install gogs with mysqllite3
Initialize local repository and create README
git init touch README.md echo "Hello Gogs!" > README.md
git config --global user.email "odennav@odennav.com" git config --global user.name "odennav" git config --global credentials.helper store
Add all changes to staging area and commit
git add . git commit -m "first commit"
Connect local repo with remote repository
git remote add origin https://192.168.149.8:3880/odennav/on-prem-devops-vsphere.git
Push commits to remote repository
git push -u origin master
Set tracking information for this branch
git branch --set-upstream-to=origin/master master
-
VAULT INSTALLATION and CONFIGURATION
Install jq to format the JSON output for vault
sudo apt-get install jq -y
Secure, store, and tightly control access to tokens, passwords, certificates, and encryption keys in modern computing.
Create the vault to generate unseal and root tokens. Unseal the vault.
- 5 Unseal keys are created upon a vault initialization.
- 3 Unseal keys are required to unseal the vault.
Install the HashiCorp GPG key, verify the key's fingerprint, and install Vault.
Update the package manager and install GPG and wget.
sudo apt update && sudo apt install gpg wget
Download the keyring
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
Verify the keyring
gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint
Add the HashiCorp repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \ sudo tee /etc/apt/sources.list.d/hashicorp.list
Install Vault
sudo apt update && sudo apt install vault
Verify the installation
vault --version
Configure Vault
Create directory path for vault
mkdir -p /opt/vault/data
Edit configuration file
sudo nano /etc/vault.d/vault.hcl
ui = true api_addr = "http://0.0.0.0:8200" log_level = "INFO" storage "file" { path = "/opt/vault/data" } listener "tcp" { address = "0.0.0.0:8200" tls_disable = true }
TLS disabled since we're just communicating with
build-machine
. Use TLS encryption for production environment.Enable vault service
sudo systemctl enable vault sudo systemctl start vault
Confirm vault service is running
sudo systemctl status vault
Set environment variable for vault address This will configure the Vault client to talk to the dev server.
export VAULT_ADDR="http://192.168.149.8:8200"
Generate Unseal keys and Root token
vault operator init
Verify the server is running
vault status
Start Unseal process with Unseal Key 1
vault operator unseal
Implement this three times with other Unseal keys to increment Unseal progress by 1 until
Sealed
state isfalse
.Vault policy requirements
it is recommended that root tokens are only used for just enough initial setup or in emergencies.
As a best practice, use tokens with appropriate set of policies based on your role in the organization.
Write a Vault Policy
As an admin user, we must be able to:
- Read system health check
- Create and manage ACL policies broadly across Vault
- Enable and manage authentication methods broadly across Vault
- Manage the Key-Value secrets engine enabled at secret/ path
Define the admin policy in the file named
admin-policy.hcl
tee admin-policy.hcl <<EOF # Read system health check path "sys/health" { capabilities = ["read", "sudo"] } # Create and manage ACL policies broadly across Vault # List existing policies path "sys/policies/acl" { capabilities = ["list"] } # Create and manage ACL policies path "sys/policies/acl/*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } # Enable and manage authentication methods broadly across Vault # Manage auth methods broadly across Vault path "auth/*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } # Create, update, and delete auth methods path "sys/auth/*" { capabilities = ["create", "update", "delete", "sudo"] } # List auth methods path "sys/auth" { capabilities = ["read"] } # Enable and manage the key/value secrets engine at `secrets/` path # List, create, update, delete and patch key/value secrets path "secrets/*" { capabilities = ["create", "read", "update", "delete","patch", "list", "sudo"] } # Manage secrets engines path "sys/mounts/*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } # List existing secrets engines. path "sys/mounts" { capabilities = ["read"] } EOF
Create admin policy
Create a policy named admin with the policy defined in admin-policy.hcl
vault policy write admin admin-policy.hcl
Display New Policy
List all policies
vault policy list
Read the
admin
policy. Displays the paths and capabilities defined for this policy.vault policy read admin
Create new token
Create a token with the admin policy attached and store the token in the variable
ADMIN_TOKEN
ADMIN_TOKEN=$(vault token create -format=json -policy="admin" | jq -r ".auth.client_token")
Display the
ADMIN_TOKEN
echo $ADMIN_TOKEN
The admin policy defines capabilities for the paths. Retrieve the capabilities of this token for the
secrets/
path.vault token capabilities $ADMIN_TOKEN secrets/*
Set the
VAULT_TOKEN
environment variable to interact with Vault. Setting this environment variable is a way to provide the token to Vault via CLIexport VAULT_TOKEN="<ADMIN_TOKEN>"
Append environment variables to
.profile
Ensure they're automatically set up and available in every new shell session.
Fill in your ADMIN_TOKEN
cat << EOF | sudo tee -a ~/.profile export VAULT_ADDR="http://192.168.149.8:8200" export VAULT_TOKEN="<ADMIN_TOKEN>" EOF
Create KV secrets engine
Enable the key/value secrets engine v1 at secrets/.
vault secrets enable -path="secrets" -description="Secret engine for Vsphere Connection" kv
List enabled secrets engines
vault secrets list
Save multiple key-value pairs
Create a file named vsphere.json that defines
cluster
,datacenter
,esx_datastore
,esx_host
,server
,username
andpassword
fields.tee vsphere.json <<EOF { "username": "administrator@vsphere.local", "password": "**********" "server": "vcenter-II", "datacenter": "odennav-labs", "cluster": "odennav-labs-cluster", "esx_host": "ESXi-2", "esx_datastore": "datastore-2" } EOF
Create new secrets
Create a secret at path secrets/vmware with keys and values defined in
vsphere.json
.vault kv put secrets/vmware @vsphere.json
Disable Vault command history
The option above ensures that the contents of the secret do not appear in the shell history. The secret path would still be accessible through the shell history.
We can configure our shell to avoid logging any
vault
commands to your history.In Bash profile, set the history to ignore all commands that start with
vault
.cat << EOF | sudo tee -a ~/.profile export HISTIGNORE="&:vault*" EOF
Web UI option to create Secrets engine
Access Vault Web UI at http://192.168.149.8:8200/ui
Use Token method and input Root token value
to login.
- Enable secrets
new engine
, click theKV
radio button and specify mountPath
- Click
Enable Engine
- Click
Create secret
and setPath
for this secret. - Enter key and value as
Secret Data
. You can add multiple key/value pairs. - Click
Save
Please ensure vault remains unsealed until build project is completed
-
PACKER INSTALLATION and CONFIGURATION
Packer is a modular tool built by Hashicorp to create raw VM images and templates. It's much more scalable than using a specific hypervisor tool.
We'll be using Packer to create our VM images.
Packer Installation
Add the HashiCorp GPG key.
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
Add the official HashiCorp Linux repository
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
Update and install
sudo apt-get update && sudo apt-get install packer
Verify installation
packer --version
Image Build with Packer Template
With Packer installed, it is time to build our first image.
A Packer template is a configuration file that defines the image you want to build and how to build it.
View the HCL block in
ubuntu20.pkr.hcl
template inpacker/ubuntu20
directory.local "vcenter_username" { exprssion = vault(*/secrets/data/vmware", "username") sensitive = true } local "vcenter_password" { exprssion = vault(*/secrets/data/vmware", "password") sensitive = true } local "vcenter_server" { exprssion = vault(*/secrets/data/vmware", "server") sensitive = true } local "vcenter_datacenter" { exprssion = vault(*/secrets/data/vmware", "datacenter") sensitive = true } local "vcenter_cluster" { exprssion = vault(*/secrets/data/vmware", "vcenter_cluster") sensitive = true } local "esx_host" { exprssion = vault(*/secrets/data/vmware", "esx_host") sensitive = true } local "esx_datastore" { exprssion = vault(*/secrets/data/vmware", "esx_datastore") sensitive = true } locals { buildtime = formatdate("YYYY-MM-DD hh:mm ZZZ", timestamp()) } source "vsphere-iso" "ubuntu20" { username = local.vcenter_username password = local.vcenter_password vcenter_server = local.vcenter_server datacenter = local.vcenter_datacenter cluster = local.vcenter_cluster host = local.esx_host folder = "Templates" datastore = local.esx_datastore insecure_connection = "true" remove_cdrom = true convert_to_template = true guest_os_type = "ubuntu64Guest" notes = "Built by Packer on ${local.buildtime}" vm_name = "packer_ubuntu20" CPUs = "1" RAM = "8192" disk_controller_type = ["pvscsi"] firmware = "bios" storage { disk_size = "40960" disk_thin_provisioned = true } network_adapters { network = "VM Network" network_card = "vmxnet3" } iso_paths = [ “[$(local.esx_datastore)] ubuntu-20.04.6-live-server-amd64” ] iso_checksum = “none” boot_order = "disk,cdrom" boot_wait = “5s” boot_command = [ "<esc><esc><esc>", "<enter><wait>", "/casper/vmlinuz ", "root=/dev/sr0 ", "initrd=/casper/initdrd ", "autoinstall ", "ds=nocloud-net;s=http://192.168.149.8:8600/", "<enter> ] ip_wait_timeout = "20m" ssh_password = "ubuntu" ssh_username = "ubuntu" ssh_timeout = "20m" ssh_handshake_attempts = "100" communicator = "ssh" shutdown_command = "sudo -S -E shutdown -P now" shutdown_timeout = "15m" http_port_min = 8600 http_port_max = 8600 http_directory = "./artifacts" } build { sources = ["source.vsphere-iso.ubuntu20"] provisioner "shell" { inline [ "echo Running updates", "sudo apt-get update", "sudo apt-get -y install open-vm-tools", "sudo touch /etc/cloud/cloud-init.disabled", # Fixing issues with preboot DHCP "sudo apt-get -y purge cloud-init", "sudo sed -i \"s/D /tmp 1777/#D /tmp 1777/\" /usr/lib/tmpfiles.d/tmp.conf", "sudo sed -i \"s/After=/After=dbus.service /\" /lib/systemd/system/open-vm-tools.service", "sudo rm -rf /etc/machine-id", # next four lines fix same ip address being assigned in vmware "sudo rm -rf /var/lib/dbus/machine-id", "sudo touch /etc/machine-id", "sudo ln -s /etc/machine-id /var/lib/dbus/machine-id" ] } }
Note the cloud config boot file,
packer/artifacts/user-data
and the autoinstall configuration in it.Initialize your Packer configuration
cd ~/on-prem-devops-vsphere/packer/ubuntu20/ packer init ubuntu20.pkr.hcl
Ensure template has consistent format
packer fmt ubuntu20.pkr.hcl
Ensure your configuration is syntactically valid and internally consistent
packer validate ubuntu20.pkr.hcl
Build image
packer build ubuntu20.pkr.hcl
View
packer_ubuntu20
VM template created in vcenter vsphere web client.
-
TERRAFORM INSTALLATION and VM DEPLOYMENT
Install Terraform
Ensure that your system is up to date and you have installed the gnupg, software-properties-common, and curl packages installed.
We'll use these packages to verify HashiCorp's GPG signature and install HashiCorp's Debian package repository.
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
Install the HashiCorp GPG key.
wget -O- https://apt.releases.hashicorp.com/gpg | \ gpg --dearmor | \ sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null
Verify the key's fingerprint. The
gpg
command will report the key fingerprint.gpg --no-default-keyring \ --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \ --fingerprint
Add the official HashiCorp repository to your system.
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \ https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \ sudo tee /etc/apt/sources.list.d/hashicorp.list
Download the package information from HashiCorp.
sudo apt update
Install Terraform from the new repository.
sudo apt-get install terraform
Verify that the installation
terraform version
Provision VM Instances on VMware vSphere
Initialize the configuration directory
on-prem-devops-vsphere/terraform/
and install thevSphere
providers defined in the configuration.cd on-prem-devops-vsphere/terraform/ terraform init
Format your configuration
terraform fmt
Validate your configuration
terraform validate
Create an execution plan that describes the changes terraform will make to the infrastructure
terraform plan
Apply the configuration and provision the VMs
terraform apply
Note, the ansible inventory is built dynamically by terraform using the resource
"local_file" "ansible_inventory"
in themyapp.tf
file.Here is the
.tpl
terraform template code for the logical groupings.[lb] %{ for ip in lb_ip ~} ${ip} domain=odennav.com %{ endfor ~} [ws] %{ for ip in ws_ip ~} ${ip} domain=odennav.com %{ endfor ~}
-
ANSIBLE INSTALLATION and MACHINE CONFIGURATION
Install Ansible
Configure the PPA on your system and install Ansible
sudo apt update sudo apt install software-properties-common sudo add-apt-repository --yes --update ppa:ansible/ansible sudo apt install ansible
Run ansible playbooks
Bootstrap each web server Default user
ubuntu
used asremote_user
inon-prem-devops-vsphere/ansible/ansible.cfg
is not created yetansible-playbook -u ubuntu add_user.yaml
Remove Ubuntu default user from each web server
ansible-playbook remove_ubuntu.yaml
Load-balanced application deployment with ansible
Deploy and configure a load-balancer and multiple servers on vSphere VMs in datacenter cluster.
Install Nginx on vSphere Virtual Machines and configure as web servers
ansible-playbook install_nginx_ws.yaml
Install Nginx on vSphere Virtual Machine and configure as load balancer
ansible-playbook install_nginx_lb.yaml
Verify web server and load balancer installation
Make
http
requests to each webserver and the load balancer usingcurl
-
Jenkins Setup
Jenkins is a self-contained, open source automation server which can be used to automate all sorts of tasks related to building, testing, and delivering or deploying software.
Our Jenkins Workflow:
- Define when to run job
- Download latest source code updates
- Run specified set of commands
- Create output artifacts(VM templates)
- Save console output for future debugging & troubleshooting
Generate ssh key-pair
ssh-keygen -t rsa -b 4096
Create local jenkins directories for volume mapping
sudo mkdir -p /opt/jenkins/bin sudo mkdir -p /opt/jenkins/jenkins-docker-certs
Locate executable files of packer command
which packer
Locate executable files of terraform command
which terraform
Copy binaries to jenkins directory volume
sudo cp -rf /usr/bin/packer /opt/jenkins/bin sudo cp -rf /usr/bin/terraform /opt/jenkins/bin
Confirm jenkins user permissions
sudo chown -R odennav:odennav /opt/jenkins
Run jenkins container
sudo docker run \ -d --name jenkins \ --restart always \ -v /opt/jenkins/jenkins-docker-certs:/certs/client \ -v /opt/jenkins:/var/jenkins_home \ -p 8080:8080 \ -p 5000:5000 \ -p 8600:8600 \ jenkins/jenkins:lts-jdk17
-
Unlock Jenkins
When you first access a new Jenkins instance, you are asked to unlock it using an automatically-generated password.
Browse to
http://192.168.149.8:8080
and wait until the Unlock Jenkins page appears.Discover
Administrator password
to unlock jenkins Copy and paste password into setup wizardsudo cat /opt/jenkins/secrets/initialAdminPassword
After unlocking jenkins, click one of the options:
Install suggested plugins
to install the recommended set of plugins based on most common use cases. -
Create the First Administrator User
Finally, after customizing Jenkins with plugins, Jenkins asks you to create your first administrator user. Specify details for your administrator user, then save and continue setup.
When the
Jenkins is ready
page appears, clickStart using Jenkins
Notes:
- This page may indicate Jenkins is almost ready! instead and if so, click Restart.
- If the page does not automatically refresh after a minute, use your web browser to refresh the page manually.
-
Add Gogs Plugin to Jenkins
We'll need to extend jenkins functionality with Gogs plugin.
- At Jenkins Dashboard appears, Go to Manage Jenkins > Manage Plugins
- Select Available tab and search for
gogs
- Select box button of available gogs plugin
- Click on
Download now and install after restart
and select box buttonRestart jenkins when installation is complete and no jobs are running
- Wait while jenkins restarts.
-
Configure Credentials
Next step is to create vault credentials in Jenkins.
Login to Jenkins UI Go to Manage Jenkins > Manage Credentials
Select
Jenkins
store Under System tab, selectGlobal credentials(unrestricted)
Check left tab and click on
Add Credentials
, then choose the following:- Kind: secret text
- Secret:
- ID: vault_token
Get your ADMIN_TOKEN to fill in
Secret
fieldecho $ADMIN_TOKEN
-
Add Private Key as Gogs Credential to Jenkins
Go to Manage Jenkins > Manage Credentials
Select
Jenkins
store Under System tab, selectGlobal credentials(unrestricted)
Check left tab and click on
Add Credentials
, then choose the following:- Kind: SSH Username with private key
- ID: id_rsa
- Username: odennav
Under
Private Key
, selectEnter directly
radio button Copy your private key from here:cat ~/on-prem-devops-vsphere/keys/id_rsa
Then paste in
Key
field and clickOK
-
Add Public SSH Key to Gogs Server
We'll add our public key to Gogs to ensure ssh authentication with Jenkins.
Go to gogs settings page at
http://192.168.149.8:3880/user/settings
Select
SSH Keys
tab and click onAdd Key
onManage SSH Keys
tabEnter
Key Name
asid_rsa
cat ~/on-prem-devops-vsphere/keys/id_rsa.pub | tr -d '\n'
Copy your public key, paste into
Content
field and clickAdd Key
-
Automate CI/CD Pipeline
Go to Jenkins Dashboard and select
New Item
on left tab.Enter an item name, select
Freestyle project
and clickOK
When
General
setup page appears, scroll down toSource Code Management
underGogs Webhook
tab and select radio button forGit
Fill the
Repository URL
field with:ssh://git@192.168.149.8:2222/odennav/on-prem-devops-vsphere.git
Select
odennav
credentials for SSH keysScroll down to
Build Environment
section under theBuild Triggers
tabSelect box button
Use secret text(s) or file(s)
Add Binding for
Secret text
, enter variable name asvault_token
and select credentials previously createdvault_token
Sroll down to
Build Environment
section under theBuild Environment
tabClick on
Add build step
drop down and selectExecute shell
Fill in the following into
Command
field:export VAULT_ADDR=http://192.168.149.8:8200" export VAULT_TOKEN=$vault_token export PACKER_LOG=1 cd "./packer/" /var/jenkins_home/bin/packer init ubuntu20.pkr.hcl /var/jenkins_home/bin/packer build -force ubuntu20.pkr.hcl export TF_LOG=INFO cd "./packer/terraform" /var/jenkins_home/bin/terraform init /var/jenkins_home/bin/terraform validate /var/jenkins_home/bin/terraform plan /var/jenkins_home/bin/terraform apply -auto-approve cd "./packer/ansible" /var/jenkins_home/bin/ansible ansible-playbook -u ubuntu add_user.yaml /var/jenkins_home/bin/ansible ansible-playbook remove_ubuntu.yaml /var/jenkins_home/bin/ansible ansible-playbook install_nginx_ws.yaml /var/jenkins_home/bin/ansible ansible-playbook install_nginx_lb.yaml
Click
Save
The Jenkins dashboard will show your project build just created.
Select
Build Now
at left tab and the build job will show up underBuild History
sectionClick on this job, select
Console Output
at left tab and view the build job in real time.When build job is completed, check the vSphere datacenter and confirm the following are deployed:
- VM template
- web01
- web02
- web03
- lb01
Browse website deployed on web servers through load balancer
View the Level website on your browser.
Enjoy!