iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
DevOps

關於我幫新公司建立整套部屬流程那檔事系列 第 6

EP06 - 從零開始,在 AWS 上建置 Jenkins 使用 Terraform

前幾天我們使用 terraform 配置好 gitlab 環境,
會單純使用 gitlab 來管理程式碼,
而另外建 Jenkins 來處理 CI,
是因為大家使用的 Source Code Management (SCM) 可能不同,
早期可能使用 SVN,
後來可能是自建 Git Server、Github 或是 Gitlab,
會各別拆開,
只是表示 CI/CD 有各種可能,
如果 SCM 不是大家目前使用的,
只需單純將 gitlab 替換成自己使用的即可,
再加上 gitlab 免費版跑 CI/CD 有限制(以月計算),
因此如果不願意付錢購買 License,
但是又無法預估 CI/CD 執行時間,
建立 Jenkins 仍是個理想的選擇。

配置 Jenkins

重構程式碼

昨天我們配置 inbound/outbound 的時候
是使用白名單的方式
配置完我們發現自己的 IP 已經在多處重複使用到
此時會建議重構建立 gitlab 配置的這段程式碼

variables.tf

variable "personal_cidr" {
    description = "the default vpc id when inital the aws"
    default = "你的IP/32"
}

main.tf
將 inbound/outbound 的 CIDR blocks 替換成 var.personal_cidr

resource "aws_security_group_rule" "gitlab_igress_22" {
    type              = "ingress"
    from_port         = 22
    to_port           = 22
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.gitlab.id
}

resource "aws_security_group_rule" "gitlab_egress_22" {
    type              = "egress"
    from_port         = 22
    to_port           = 22
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.gitlab.id
}

resource "aws_security_group_rule" "gitlab_igress_80" {
    type              = "ingress"
    from_port         = 80
    to_port           = 80
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.gitlab.id
}

resource "aws_security_group_rule" "gitlab_egress_80" {
    type              = "egress"
    from_port         = 80
    to_port           = 80
    cidr_blocks       = ["0.0.0.0/0",]
    protocol          = "tcp"
    security_group_id = aws_security_group.gitlab.id
}

resource "aws_security_group_rule" "gitlab_igress_443" {
    type              = "ingress"
    from_port         = 443
    to_port           = 443
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.gitlab.id
}

resource "aws_security_group_rule" "gitlab_egress_443" {
    type              = "egress"
    from_port         = 443
    to_port           = 443
    cidr_blocks       = ["0.0.0.0/0",]
    protocol          = "tcp"
    security_group_id = aws_security_group.gitlab.id
}

執行配置

回到 console 中輸入

terraform apply

理論上不會有什麼變動
或是 pem key 重新產生而已
https://ithelp.ithome.com.tw/upload/images/20210918/20141518LUcme43xty.png

terraform 撰寫

有了昨天 gitlab 配置的訓練
我們知道我們會建立一台 EC2
會有個 Security Group 以及 Security Group Rule
以及綁定 SSH Key 到 EC2 上
這邊我們就一次全部撰寫
不再分段介紹
與 gitlab 不同的是
Jenkins 因為是 Java 的服務
所以有多設置 8080 port
沒用到的 80 port 目前暫不移除

使用的經驗上 EC2 的規格可先配置 t3.medium
如果未來不敷使用
再往上升即可

resource "aws_security_group" "jenkins" {
    name        = "jenkins-server"
    description = "It used for Jenkins server."
    vpc_id      = data.aws_vpc.default.id
    tags        = { Name = "Jenkins-Server" }
    revoke_rules_on_delete = null
}

resource "aws_security_group_rule" "jenkins_igress_22" {
    type              = "ingress"
    from_port         = 22
    to_port           = 22
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.jenkins.id
}

resource "aws_security_group_rule" "jenkins_egress_22" {
    type              = "egress"
    from_port         = 22
    to_port           = 22
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.jenkins.id
}

resource "aws_security_group_rule" "jenkins_igress_80" {
    type              = "ingress"
    from_port         = 80
    to_port           = 80
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.jenkins.id
}

resource "aws_security_group_rule" "jenkins_egress_80" {
    type              = "egress"
    from_port         = 80
    to_port           = 80
    cidr_blocks       = ["0.0.0.0/0",]
    protocol          = "tcp"
    security_group_id = aws_security_group.jenkins.id
}

resource "aws_security_group_rule" "jenkins_igress_443" {
    type              = "ingress"
    from_port         = 443
    to_port           = 443
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.jenkins.id
}

resource "aws_security_group_rule" "jenkins_egress_443" {
    type              = "egress"
    from_port         = 443
    to_port           = 443
    cidr_blocks       = ["0.0.0.0/0",]
    protocol          = "tcp"
    security_group_id = aws_security_group.jenkins.id
}

resource "aws_security_group_rule" "jenkins_igress_8080" {
    type              = "ingress"
    from_port         = 8080
    to_port           = 8080
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.jenkins.id
}

resource "aws_security_group_rule" "jenkins_egress_8080" {
    type              = "egress"
    from_port         = 8080
    to_port           = 8080
    cidr_blocks       = [var.personal_cidr,]
    protocol          = "tcp"
    security_group_id = aws_security_group.jenkins.id
}

resource "tls_private_key" "jenkins" {
    algorithm = "RSA"
    rsa_bits  = 4096
}

resource "aws_key_pair" "jenkins" {
    key_name   = "jenkins"
    public_key = tls_private_key.jenkins.public_key_openssh
}

resource "local_file" "jenkins" {
    content  = tls_private_key.jenkins.private_key_pem
    filename = format("%s.pem", aws_key_pair.jenkins.key_name)
}

resource "aws_instance" "jenkins" {
    ami                     = data.aws_ami.ubuntu.id
    instance_type           = "t3.medium"
    subnet_id               = sort(data.aws_subnet_ids.subnet_ids.ids)[0]
    key_name                = aws_key_pair.jenkins.key_name
    vpc_security_group_ids  = [ aws_security_group.jenkins.id ]
    disable_api_termination = false
    ebs_optimized           = true
    hibernation             = false
    
    tags = {
        Name  = "Jenkins Server"
        Usage = "CI Tools"
        Creator = "Terraform"
    }

    root_block_device {
        delete_on_termination = true
        encrypted             = false
        throughput            = 0
        volume_size           = 30
        volume_type           = "gp2"
        tags                  = {
            Name     = "Jenkins Server"
            Attached = "Jenkins Server"
        }
    }
}

建立完成後
我們可以透過 aws 的 console 看到之前建立 gitlab server
以及剛剛建立好的 jenkins server
https://ithelp.ithome.com.tw/upload/images/20210918/20141518rHptvSE0lc.png

變更權限

同昨天的教學
要變更讀寫權限

sudo chmod 400 jenkins.pem

連到 Jenkins Server

ssh -i "jenkins.pem" ubuntu@你的主機

更新

sudo apt-get update
sudo apt-get upgrade -y

安裝 Java

因為 Jenkins 是由 Java 編寫的開源持續整合工具
因此在安裝 Jenkins 之前需要安裝 Java
Java 版本的選擇選擇 Java 11
會選擇 Java 11 的原因是因為 LTS
而 Java 17 又太新
不過如果沒有要在 Jenkins Server 做其他的操作
其實版本應該是不需要太在意(?)

sudo apt-get install openjdk-11-jdk

添加 repository key

在安裝時我喜歡安裝最新版本
尤其許多套件可能也會跟著 Jenkins 版本再走
因此下方是安裝最新版本的 Jenkins 而不是穩定版本
如果想要安裝穩定版本的
可以參考 Digital Ocean 的這篇文章

sudo wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins.io/debian binary/ > /etc/apt/sources.list.d/jenkins.list'

添加完後
記得要先 update 更新清單

sudo apt update

更新完後
可以看到列表中出現 jenkins
https://ithelp.ithome.com.tw/upload/images/20210918/20141518DpAxFcxnHz.png

安裝 jenkins

sudo apt install jenkins

https://ithelp.ithome.com.tw/upload/images/20210918/20141518IG2rggXFT2.png

啟動 jenkins

sudo systemctl start jenkins

啟動後可以輸入以下指令
確定 jenkins 啟動狀況

sudo systemctl status jenkins

https://ithelp.ithome.com.tw/upload/images/20210918/20141518p9rabIUHT7.png

設定 Jenkins

目前已經有使用 security group 去管 inbound/outbound
因此就不會開啟 ubunut 內建的 firewall

在瀏覽器中輸入下列網址
http:// your_server_ip_or_domain :8080
就可以開始設定 jenkins 了

有看到畫面
表示 Jenkins 服務有正常啟動
https://ithelp.ithome.com.tw/upload/images/20210918/201415183rRuDJsqSP.png

取得密碼

輸入指令取得密碼後貼上以繼續設定

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

繼續設定

選擇建議的擴充套件後
接下來就是等待安裝的過程

https://ithelp.ithome.com.tw/upload/images/20210918/20141518oB4Deoxu7T.png

https://ithelp.ithome.com.tw/upload/images/20210918/20141518sqWvPjAIWj.png

配置 admin

https://ithelp.ithome.com.tw/upload/images/20210918/20141518RyliXLWTbv.png

設置網址

此步驟可於日後更改
如果網址設定不正確
則會一直跳出警示
https://ithelp.ithome.com.tw/upload/images/20210918/20141518TizuZDr2TU.png

結束安裝

儲存後就結束整個安裝的過程
也會自動跳轉到主頁面

https://ithelp.ithome.com.tw/upload/images/20210918/20141518e2YSzIB1kP.png

https://ithelp.ithome.com.tw/upload/images/20210918/20141518svd6L2RIl5.png

tfstate 放到 S3 託管

這兩天我們用 terraform 建立好兩台 EC2
此時也許會開始有兩個人進行協作
但也如同前幾天說的 tfstate 是重要的檔案
它記錄全部配置的狀態
但是同時可能也包含機密資訊
因此不適合放在 git 做管理
常見的作法則是將這檔案放到 S3 做管理

新增 S3 bucket

創建 S3 bucket 時要注意
bucket 的名稱必須是全世界獨一無二的
因此儘量避免菜市場命名

resource "aws_s3_bucket" "tfstate" {
    bucket = "ithome-ironman-markmew-tfstate"
    acl    = "private"
    
    tags = {
        Name     = "My bucket"
        Creator  = "Terraform"
    }

    versioning {
        enabled = true
    }
}

配置 tfstate backend

最初我們做了一個設置
表示我們 terraform 撰寫時
使用的套件來源和版本

terraform {
    required_providers {
        aws = {
            source  = "hashicorp/aws"
            version = "~>3.0" 
        }
    }
}

程式碼需添加 backend
來告知我們 tfstate 要放在哪個位置

terraform {
    required_providers {
        aws = {
            source  = "hashicorp/aws"
            version = "~>3.0" 
        }
    }

    backend "s3" {
        bucket = "ithome-ironman-markmew-tfstate"
        key    = "ithome-ironman.tfstate"
        region = "ap-northeast-1"
    }
}

配置完後
需要初始化配置

terraform init

https://ithelp.ithome.com.tw/upload/images/20210918/20141518HfTAMP1tYO.png

配置完後
在 aws 的 s3 就可以看到我們的tfstate檔案已經建立好了

https://ithelp.ithome.com.tw/upload/images/20210918/20141518lyq66NYrc2.png

但是還沒結束
我們的 tfstate 移動到 s3
同樣的我們也必須設定 tfstate 要從 s3 抓取
在 main.tf 中增加

data "terraform_remote_state" "tfstate" {
    backend = "s3"
    config  = {
        bucket = "ithome-ironman-markmew-tfstate"
        key    = "ithome-ironman.tfstate"
        region = "ap-northeast-1"
    }
}

新增完後再 apply 一次

terraform apply

正常狀況應該會是沒有任何變更
https://ithelp.ithome.com.tw/upload/images/20210918/20141518W87EHCHezU.png

check-in code

選擇變更的檔案

git add stage/main.tf 
git add stage/variables.tf 

撰寫變更描述(我承認我寫得不好QQ)

git commit -m "1. add jenkins server\n 2. refactor white list settings\n 3. migrate tfstate to s3"

push

git push

回顧一下我們今天做的
我們使用 terraform 建立第二個 EC2
白名單的設定上
用簡單的方式重構程式碼
讓我們不需要重複輸入自己 IP
並且為了能夠多人協作
也將 tfstate 檔案放到 s3 上
最後也別忘了程式碼要記得 push

參考資料:

  1. How To Install Jenkins on Ubuntu 20.04
  2. Migrating State from Local Terraform
  3. Resource: aws_s3_bucket

上一篇
EP05 - 從零開始,在 AWS 建置 Gitlab 使用 Terraform
下一篇
EP07 - Jenkins Pipeline 整合 Gitlab 使用 Webhook
系列文
關於我幫新公司建立整套部屬流程那檔事30

尚未有邦友留言

立即登入留言