# Jenkins 배포하기

## Jenkins 배포 핵심

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

{% content-ref url="/pages/-MP7LuXFsqXGBdB9AGFi" %}
[3. 어플리케이션 서비스 만들기](/2..md)
{% endcontent-ref %}

### Elastic File System 생성

* Terraform으로 Elastic file system을 생성합니다.
* 생성된 EFS의 Domain name은 Instance의 Userdata에서 참조됩니다.

{% code title="vim terraform/platform/jenkins/\_module/jenkins/efs.tf" %}

```bash
# 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,
  ]
}
```

{% endcode %}

### User data 정의

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

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

{% code title="vim terraform/platform/jenkins/\_module/jenkins/scripts/userdata.sh.tpl" %}

```bash
#!/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

```

{% endcode %}

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

{% code title="vim terraform/platform/jenkins/\_module/jenkins/template.tf" %}

```bash
data "template_file" "init" {
  template = "${file("${path.module}/scripts/userdata.sh.tpl")}"
  vars = {
    efs_dns_name = aws_efs_file_system.file_system.dns_name
  }
}
```

{% endcode %}

### Module 사용 부분

* Jenkins 전체 세트는 Module을 통해서 정의하고, 실제 변수들은 환경에 따라 다르게 세팅합니다.&#x20;
* `terraform/platform/jenkins/<환경>` directory를 통째로 복사해서 새로운 환경 변수를 정의하면, 동일한 jenkins 세트를 생성하실 수 있습니다.
* 배포하는 리전이 서울(ap-northeast-2)가 아니면, `terraform.tfvars` 파일에서 `jenkins_master_ami` 를 해당 리전의 AMI ID로 변경해주시기 바랍니다.(Amazon Linux 2)
* External LB는 보안을 위해서 `반드시 내부망만 오픈`하시기 바랍니다!!!&#x20;
  * Github Webhook을 사용하는 경우에는 퍼블릭으로 오픈하지 마시고, <https://api.github.com/meta> 사이트에서 hook 서버 IP들만 오픈하시는 것을 추천드립니다.
  * 또 webhook을 보내더라도 바로 jenkins로 보내지 마시고, API Gateway + Lambda 등을 중간에 두고 인증절차를 거치시기 바랍니다.
    * <https://github.com/serverless/examples/tree/master/aws-node-github-webhook-listener>

{% code title="vim terraform/platform/jenkins/dayonep\_apnortheast2/services.tf" %}

```bash
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"
  ]
}
```

{% endcode %}

## 배포 후 세팅

* Jenkins를 배포하고 나면 Route53 URL을 통해서 Jenkins에 접속하실 수 있습니다.&#x20;
* 홈페이지에 접속하시면 아래와 같이 init password를 입력하라고 나옵니다.
  * 배포된 인스턴스에 접속하셔서 웹페이지에 적힌 path 파일에서 패스워드를 얻으신 후에 붙여넣으시면 됩니다.
  * Instance 접속은 bastion host -> instance로 하셔도 되고, aws sessions manager를 통해서 접속하실 수도 있습니다.
  * SSM 접속을 위한 기본적인 Permission은 이미 IAM Role에 들어 있습니다.

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

![](/files/-MSsCO1LxXMu0V-aZiO0)

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

![](/files/-MSsCQZvrCy6JW9IYpub)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://terraform201.devart.tv/5.-jenkins/jenkins-2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
