Jenkins 생성하기

Terraform으로 jenkins 생성하기

Jenkins 배포 핵심

Jenkins 배포에서 중요한 것은 EFS 활용과 Userdata 부분입니다. ALB + Autoscaling 을 생성하는 부분은 앞선 실습에서 했기 때문에 이번 실습에서는 생략하겠습니다.

pageElastic Load Balancer 실습

Elastic File System 생성

  • Terraform으로 Elastic file system을 생성합니다.

  • 생성된 EFS의 Domain name은 Instance의 Userdata에서 참조됩니다.

vim terraform/platform/jenkins/_module/jenkins/efs.tf
# Security Group for EFS
## Should allow Jenkins Instance ONLY!!!
resource "aws_security_group" "efs" {
  name        = "${var.service_name}-efs-${var.vpc_name}"
  description = "${var.service_name} efs sg for ${var.vpc_name}"
  vpc_id      = var.target_vpc

  ingress {
    from_port = 2049 # for NFS

    to_port  = 2049
    protocol = "tcp"
    security_groups = [
      # EC2 Instance Security Group
      aws_security_group.ec2.id,
    ]
  }

  tags = {
    Name = "${var.service_name}-efs-${var.vpc_name}"
  }
}

resource "aws_efs_file_system" "file_system" {
  tags = {
    Name = "${var.service_name}-efs-${var.vpc_name}"
  }
  
  #You can control this value through variable
  provisioned_throughput_in_mibps = var.efs_provisioned_throughput_in_mibps
  
  #You can control this mode through variable
  throughput_mode = var.efs_throughput_mode

}

resource "aws_efs_mount_target" "mount_target" {
  # If you do not have NAT gateway  or NAT instance in private subnets,
  # You should deploy jenkins to public subnet!
  count          = length(var.private_subnets)
  #count          = length(var.public_subnets)
  file_system_id = aws_efs_file_system.file_system.id

  subnet_id      = element(var.private_subnets, count.index)
  #subnet_id      = element(var.public_subnets, count.index)
  security_groups = [
    aws_security_group.efs.id,
  ]
}

User data 정의

  • Userdata에 efs 도메인 이름을 참조하기 위해서 테라폼의 template_file을 활용합니다.

먼저 userdata.sh.tpl 을 살펴보겠습니다. Userdata에서 배포에 필요한 기본 패키지를 설치하고, 생성된 EFS를 /var/lib/jenkins 에mount합니다.

vim terraform/platform/jenkins/_module/jenkins/scripts/userdata.sh.tpl
#!/bin/bash -ex

function waitForJenkins() {
    echo "Waiting jenkins to launch on 8080..."

    while ! nc -z localhost 8080; do
      sleep 0.1 # wait for 1/10 of the second before check again
    done

    echo "Jenkins launched"
}

function waitForPasswordFile() {
    echo "Waiting jenkins to generate password..."

    while [ ! -f /var/lib/jenkins/secrets/initialAdminPassword ]; do
      sleep 2 # wait for 1/10 of the second before check again
    done

    echo "Password created"
}

amazon-linux-extras install corretto8

yum update -y
yum install -y jq git awscli nmap-ncat nfs-common

export JENKINS_HOME=/var/lib/jenkins
mkdir -p $JENKINS_HOME

mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport ${efs_dns_name}:/ $JENKINS_HOME

wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat-stable/jenkins.repo
rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key

yum install -y jenkins

sed -i 's/Djava.awt.headless=true/Djava.awt.headless=true -Xmx2G -Xms2G -Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia\/Seoul/g' /etc/sysconfig/jenkins

service jenkins start

위에서 정의한 user_data 파일은 template_file 형태로 참조합니다. 변수로는 EFS dns_name을 넘겨줍니다.

vim terraform/platform/jenkins/_module/jenkins/template.tf
data "template_file" "init" {
  template = "${file("${path.module}/scripts/userdata.sh.tpl")}"
  vars = {
    efs_dns_name = aws_efs_file_system.file_system.dns_name
  }
}

Module 사용 부분

  • Jenkins 전체 세트는 Module을 통해서 정의하고, 실제 변수들은 환경에 따라 다르게 세팅합니다.

  • terraform/platform/jenkins/<환경> directory를 통째로 복사해서 새로운 환경 변수를 정의하면, 동일한 jenkins 세트를 생성하실 수 있습니다.

  • 배포하는 리전이 서울(ap-northeast-2)가 아니면, terraform.tfvars 파일에서 jenkins_master_ami 를 해당 리전의 AMI ID로 변경해주시기 바랍니다.(Amazon Linux 2)

  • External LB는 보안을 위해서 반드시 내부망만 오픈하시기 바랍니다!!!

vim terraform/platform/jenkins/dayonep_apnortheast2/services.tf
module "jenkins" {
  source           = "../_module/jenkins"
  service_name     = "jenkins"
  service_port     = 8080
  healthcheck_port = 8080
  account_id       = var.account_id.prod

  shard_id                 = data.terraform_remote_state.vpc.outputs.shard_id
  public_subnets           = data.terraform_remote_state.vpc.outputs.public_subnets
  private_subnets          = data.terraform_remote_state.vpc.outputs.private_subnets
  aws_region               = data.terraform_remote_state.vpc.outputs.aws_region
  target_vpc               = data.terraform_remote_state.vpc.outputs.vpc_id
  vpc_name                 = data.terraform_remote_state.vpc.outputs.vpc_name
  vpc_cidr_numeral         = data.terraform_remote_state.vpc.outputs.cidr_numeral
  route53_internal_domain  = data.terraform_remote_state.vpc.outputs.route53_internal_domain
  route53_internal_zone_id = data.terraform_remote_state.vpc.outputs.route53_internal_zone_id
  billing_tag              = data.terraform_remote_state.vpc.outputs.billing_tag

  newrelic_monitor        = "false"

  ssh_key_name            = "dayone-prod-master"

  instance_ami            = var.jenkins_master_ami
  tag_first_owner         = var.tag_first_owner
  tag_second_owner        = var.tag_second_owner
  tag_project             = var.tag_project
  efs_provisioned_throughput_in_mibps = 0
  
  #KMS Key for deployment
  deployment_common_arn     = data.terraform_remote_state.kms.outputs.aws_kms_key_prod_apne2_deployment_common_arn

  # Instance Count Variables
  instance_count_max     = 1
  instance_count_min     = 1
  instance_count_desired = 1

  # Route53 variables
  acm_external_ssl_certificate_arn  = var.r53_variables.prod.star_dayonedevops_com_acm_arn_apnortheast2
  route53_external_zone_id          = var.r53_variables.prod.dayonedevops_com_zone_id
  domain_name                       = "jenkins"

  # Resource LoadBalancer variables
  lb_variables                      = var.lb_variables

  # Security Group variables
  sg_variables                      = var.sg_variables  

  # Home Security Group via remote_state
  home_sg                       = data.terraform_remote_state.vpc.outputs.aws_security_group_home_id
  #github_hook_sg                = data.terraform_remote_state.vpc.outputs.aws_security_group_github_hook_id
  github_hook_sg                = ""

  # CIDR for external LB
  # Control allowed IP for external LB 
  ext_lb_ingress_cidrs = [
    "0.0.0.0/0"
  ]
}

배포 후 세팅

  • Jenkins를 배포하고 나면 Route53 URL을 통해서 Jenkins에 접속하실 수 있습니다.

  • 홈페이지에 접속하시면 아래와 같이 init password를 입력하라고 나옵니다.

    • 배포된 인스턴스에 접속하셔서 웹페이지에 적힌 path 파일에서 패스워드를 얻으신 후에 붙여넣으시면 됩니다.

    • Instance 접속은 bastion host -> instance로 하셔도 되고, aws sessions manager를 통해서 접속하실 수도 있습니다.

    • SSM 접속을 위한 기본적인 Permission은 이미 IAM Role에 들어 있습니다.

[root@ip-10-20-19-82 ~]# cat /var/lib/jenkins/secrets/initialAdminPassword
04369ed0856c4f7bad4c64dde084cae9

이후에 설치를 진행합니다. 원하시는 메뉴로 설치를 진행하시기 바랍니다.

Last updated