Setting up a Jenkins build server
Introduction
Using Python and the CDK to setup a Jenkins build server.
2 stacks were created for this example.
These stacks help define the following AWS components:
1. Network Stack
- VPC
- Public Subnets
- Routing table
- Internet gateway
2. EC2 Stack
- Auto Scaling Group
- Security Groups
Ansible was used to install Jenkins on the EC2 instance.
app.py
#!/usr/bin/env python3
from aws_cdk import core
from EC2Stack import EC2Stack
from NetworkStack import NetworkStack
props = {
'namespace':'JenkinsBuildServer',
'vpc_name':'devops',
'ec2_instance_name':'jenkins-build-server',
'wan_ip':'your_ip_address',
'ec2_instance_type' : 't2.micro',
'key_pair':'your_key_pair',
}
env = core.Environment(account="your-aws-account-id",region="your-region")
app = core.App()
network_stack = NetworkStack(app,f"{props['namespace']}-network",props,env=env)
ec2_stack = EC2Stack(app, f"{props['namespace']}-ec2",network_stack.output_props,env=env)
ec2_stack.add_dependency(network_stack)
app.synth()
NetworkStack.py
from aws_cdk import (
core,
aws_ec2 as ec2
)
class NetworkStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, props, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# The code that defines your stack goes here
# Create VPC
# Create VPC with a single Public Subnet
vpc = ec2.CfnVPC(
self,
"VPC",
enable_dns_hostnames=True,
enable_dns_support=True,
cidr_block="10.0.0.0/16"
)
vpc.tags.set_tag(key="Name",value=props['vpc_name'])
# Create Routing table for public subnet
route_table_public = ec2.CfnRouteTable(
self,
"RtbPublic",
vpc_id=vpc.ref
)
route_table_public.tags.set_tag(key="Name",value="EC2 Public Routing Table")
# Create public subnet
public_subnet = ec2.CfnSubnet(
self,
"PublicSubnet",
cidr_block="10.0.0.0/24",
vpc_id=vpc.ref,
map_public_ip_on_launch=True,
)
public_subnet.tags.set_tag(key="Name",value="devops-public-subnet")
# Create internet gateway
inet_gateway = ec2.CfnInternetGateway(
self,
"DevOpsIgw",
tags=[core.CfnTag(key="Name",value="devops-igw")]
)
ec2.CfnVPCGatewayAttachment(
self,
"IgwAttachment",
vpc_id=vpc.ref,
internet_gateway_id=inet_gateway.ref
)
# Add gw to route to routing table
ec2.CfnRoute(
self,
"RouteInetGateway",
route_table_id=route_table_public.ref,
destination_cidr_block="0.0.0.0/0",
gateway_id=inet_gateway.ref
)
# Routing table association with public subnet
ec2.CfnSubnetRouteTableAssociation(
self,
"RtbAssocPublic",
route_table_id=route_table_public.ref,
subnet_id=public_subnet.ref
)
self.output_props = props.copy()
self.output_props['vpc_id'] = vpc.ref
@property
def outputs(self):
return self.output_props
EC2Stack.py
from aws_cdk import (
core,
aws_ec2 as ec2,
aws_autoscaling as autoscaling
)
class EC2Stack(core.Stack):
def __init__(self, scope: core.Construct, id: str, props, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# The code that defines your stack goes here
# EC2 security group
sg_ec2 = ec2.CfnSecurityGroup(
self,
"ec2-sec-group",
group_description="EC2 Security Group",
vpc_id=props['vpc_id']
)
sg_ec2.tags.set_tag(key="Name",value="sg-devops-ec2")
# add rules to sg
ec2.CfnSecurityGroupIngress(
self,
"sg-http-ingress",
ip_protocol="tcp",
from_port=80,
to_port=80,
cidr_ip="0.0.0.0/0",
group_id=sg_ec2.ref
)
ec2.CfnSecurityGroupIngress(
self,
"sg-https-ingress",
ip_protocol="tcp",
from_port=443,
to_port=443,
cidr_ip="0.0.0.0/0",
group_id=sg_ec2.ref
)
ec2.CfnSecurityGroupIngress(
self,
"sg-ssh-ingress",
ip_protocol="tcp",
cidr_ip=props['wan_ip']+"/32",
from_port=22,
to_port=22,
group_id=sg_ec2.ref
)
# define machine image ami
amzn_linux_ami = ec2.MachineImage.latest_amazon_linux(
edition=ec2.AmazonLinuxEdition.STANDARD,
generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
virtualization=ec2.AmazonLinuxVirt.HVM,
storage=ec2.AmazonLinuxStorage.GENERAL_PURPOSE
)
# create autoscaling group for ec2 instance
asg = autoscaling.AutoScalingGroup(
self,
"AutoScalingGroup",
instance_type=ec2.InstanceType(props['ec2_instance_type']),
machine_image=amzn_linux_ami,
associate_public_ip_address=True,
update_type=autoscaling.UpdateType.REPLACING_UPDATE,
desired_capacity=1,
vpc=ec2.Vpc.from_lookup(self,"VPCLookup",vpc_name=props['vpc_name']),
vpc_subnets={'subnet_type':ec2.SubnetType.PUBLIC},
security_group=ec2.SecurityGroup.from_security_group_id(self,"LookupSG",security_group_id=sg_ec2.ref),
key_name=props['key_pair']
)
Ansible
A playbook was created to setup Jenkins on the EC2 instance.
---
# tasks file for jenkins-build-server
- name: Run yum upgrade
yum:
name: "*"
state: latest
- name: Install openjdk 8
yum:
name: java-1.8.0-openjdk-devel.x86_64
state: present
- name: Add the Jenkins repo
get_url:
url: http://pkg.jenkins-ci.org/redhat/jenkins.repo
dest: /etc/yum.repos.d/jenkins.repo
- name: Import a key file from Jenkins-CI to enable installation from the package
rpm_key:
state: present
key: https://pkg.jenkins.io/redhat/jenkins.io.key
- name: Install Jenkins
yum:
name: jenkins
state: latest
- name: Start Jenkins as a service
service:
name: jenkins
state: started
Inventory
[jenkins]
EC2_INSTANCE_IP_ADDRESS ansible_ssh_user=ec2-user ansible_ssh_private_key_file=PATH_TO_PEM_FILE
Playbook
hosts: jenkins
become: yes
become_user: root
roles:
- jenkins-build-server
Running the playbook
ansible-playbook -i inventory playbook.yml