iT邦幫忙

2021 iThome 鐵人賽

DAY 14
0
DevOps

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

EP14 - 調整架構 EC2 與負載平衡器

昨天我們很有膽試的將 VPC 砍掉重建,
為的就是將網段重新安排,
但是還沒處理好的部分,
則是 Gitlab 和 Jenkins 目前還連不上,
比較理想的方式是我們建一個 VPC Endpoint 直連 AWS,
但是嘗試了很久在我本機環境建不起來,
所以這個坑只能晚點填,
建議有點規模的公司,
可以考慮建立一個 Site-to-Site VPN,
當作內網的延伸,
這樣 172.31 某個網段以後的都是 aws,
以前的是地端主機,
這樣會更好分辨與管理。

我們沒有要建 VPN,
可是我們還是有連到 Jenkins 和 Gitlab 的需求,
這時候我們可以考慮之前做的 ALB(Application Load Balance),
前幾天我們幫 Portal 建立的 ALB,
其 ALB 和 EC2 套用的 Security Group 是一樣的,
都是收 22、80、443 port,
但是 ALB 只能監聽 80 和 443,
所以即使開了 22 port,
還是只能透過我們綁定的 Elastic IP 去管理主機,
而 ALB 又可以掛載 Web ACLs,
所以整體的安全有保護到,
今天我們 Gitlab 和 Jenkins 也打算使用差不多的方式,
我們建立新的 Security Group,
但是只綁定 Stage VPC 的 CIDR 和我們私人的 IP,
這樣在設定完 DNS 後,
在 Load Balance 就可以根據 IP 白名單來過去,
不在 CIDR 中的就不允許進入。


建立 ALB

建立的部分雖然都在 stage/main.tf
但是我們分四個區塊

  1. 建立 Security Group
  2. ALB For Jenkins
  3. ALB For Gitlab
  4. ALB 綁定 WebACLs

這裡我們不使用 NLB
係因為 NLB 無法設定 Security Group
也無法綁定 Web ACLs
無法綁定 Web ACLs 問題不大
但是無法鎖定特定 IP 才能存取
就會變成 CI/CD Tools 在網路上裸奔
這不會是我們熱見的
大部分我們的操作都會是連進去點按
比較少會 ssh 進去上 patch
因此這部分我是覺得可以暫時使用 ALB
如果真的需要其實可以偷雞一點
將 pem key scp 到 portal 或是跳板用的主機
等操作完再把 pem key 刪除

建立 Security Group

一開始我們設定的
stage/main.tf

resource "aws_security_group" "elb_internal" {
    name        = "elb-internal"
    description = "It used for load balance internal."
    vpc_id      = aws_vpc.stage.id
    tags        = { Name = "elb-internal" }
    revoke_rules_on_delete = null
}

resource "aws_security_group_rule" "elb_internal_igress_80" {
    type              = "ingress"
    from_port         = 80
    to_port           = 80
    cidr_blocks       = [aws_vpc.stage.cidr_block, var.personal_cidr]
    protocol          = "tcp"
    security_group_id = aws_security_group.elb_internal.id
}

resource "aws_security_group_rule" "elb_internal_igress_443" {
    type              = "ingress"
    from_port         = 443
    to_port           = 443
    cidr_blocks       = [aws_vpc.stage.cidr_block, var.personal_cidr]
    protocol          = "tcp"
    security_group_id = aws_security_group.elb_internal.id
}

resource "aws_security_group_rule" "elb_internal_egress" {
    type              = "egress"
    from_port         = 0
    to_port           = 0
    cidr_blocks       = ["0.0.0.0/0",]
    protocol          = "-1"
    security_group_id = aws_security_group.elb_internal.id
}

Jenkins

portal 的部分,
target group 是使用 IP 的寫法,
這次我們換換味道,
換成 instance 的寫法。

stage/main.tf

resource "aws_lb" "jenkins" {
    name               = "jenkins"
    internal           = false
    load_balancer_type = "application"
    security_groups    = [ aws_security_group.elb_internal.id ]
    subnets            = data.aws_subnet_ids.public_subnet_ids.ids
    
    enable_deletion_protection = false
    
    tags = {
        Creator = "Terraform"
    }
}

resource "aws_lb_target_group" "jenkins_tcp_8080" {
    name        = "jenkins"
    port        = 80
    protocol    = "HTTP"
    target_type = "instance"
    vpc_id      = aws_vpc.stage.id
}

resource "aws_lb_target_group_attachment" "jenkins_tcp_8080" {
    target_group_arn = aws_lb_target_group.jenkins_tcp_8080.arn
    target_id        = module.ec2_jenkins.id
    port             = 8080
}

resource "aws_lb_listener" "jenkins_tcp_80" {
    load_balancer_arn = aws_lb.jenkins.arn
    port = "80"
    protocol = "HTTP"

    default_action {
        type             = "redirect"
        target_group_arn = aws_lb_target_group.jenkins_tcp_8080.arn
        
        redirect {
            port        = "443"
            protocol    = "HTTPS"
            status_code = "HTTP_301"
        }
    }
}

resource "aws_lb_listener" "jenkins_tcp_443" {
    load_balancer_arn = aws_lb.jenkins.arn
    port              = "443"
    protocol          = "HTTPS"
    ssl_policy        = "ELBSecurityPolicy-2016-08"
    certificate_arn   = aws_acm_certificate.cert.arn
    
    default_action {
        type             = "forward"
        target_group_arn = aws_lb_target_group.jenkins_tcp_8080.arn
    }
}

Gitlab

portal 的部分,
target group 是使用 IP 的寫法,
這次我們和 Gitlab 一樣換換味道,
換成 instance 的寫法。

resource "aws_lb_target_group" "gitlab" {
    name        = "gitlab"
    port        = 80
    protocol    = "HTTP"
    target_type = "instance"
    vpc_id      = aws_vpc.stage.id
}

resource "aws_lb_target_group_attachment" "gitlab" {
    target_group_arn = aws_lb_target_group.gitlab.arn
    target_id        = module.ec2_gitlab.id
    port             = 80
}

resource "aws_lb_listener" "gitlab_80" {
    load_balancer_arn = aws_lb.gitlab.arn
    port = "80"
    protocol = "HTTP"

    default_action {
        type             = "redirect"
        target_group_arn = aws_lb_target_group.gitlab.arn
        
        redirect {
            port        = "443"
            protocol    = "HTTPS"
            status_code = "HTTP_301"
        }
    }
}

resource "aws_lb_listener" "gitlab_443" {
    load_balancer_arn = aws_lb.gitlab.arn
    port              = "443"
    protocol          = "HTTPS"
    ssl_policy        = "ELBSecurityPolicy-2016-08"
    certificate_arn   = aws_acm_certificate.cert.arn
    
    default_action {
        type             = "forward"
        target_group_arn = aws_lb_target_group.gitlab.arn
    }
}

WebACLs

stage/main.tf

resource "aws_wafv2_web_acl_association" "jenkins" {
    resource_arn = aws_lb.jenkins.arn
    web_acl_arn  = aws_wafv2_web_acl.fundamental_acl.arn
}

執行配置

terraform apply

DNS 指向

檢視 ALB 的 DNS

執行完配置以後
我們登入 AWS Cloud Console
可在 EC2 的負載平衡器看到我們剛剛建立的 ALB

https://ithelp.ithome.com.tw/upload/images/20210926/20141518oHMpRL4pVb.png

設定 DNS 指向

接下來就是要去自己的 DNS Server 設定 CNAME Record

https://ithelp.ithome.com.tw/upload/images/20210926/20141518uzdkoTt0oO.png

https://ithelp.ithome.com.tw/upload/images/20210926/20141518ncTz7IGGdR.png

雖然剛剛我有試著遮 Load Balance 的 DNS Host
不過實際上懂技術的人
還是可以知道 DNS Host 和 IP 是什麼
不過即使知道
也會因為 Security Group 擋住而連不上

調整 CI/CD 設定

調整 Jenkins 設定

管理 Jenkins

登入後點按左側選的「管理 Jenkins」
https://ithelp.ithome.com.tw/upload/images/20210926/20141518F2F56TSX9J.png

設定 Jnekins

https://ithelp.ithome.com.tw/upload/images/20210926/20141518blfG21iJZv.png

修改 Jenkins 位置

畫面中間的「Jenkins URL」改成自己的網址
https://ithelp.ithome.com.tw/upload/images/20210926/20141518PL2y7wOF6Z.png

修改 Jenkinsfile

之前的教學中
我們的 Jenkinsfile 還沒進版控
這時候我們可以從 Pipeline 進去修改

點按組態

在 Pipeline 的向下箭頭點按後選擇「組態」
https://ithelp.ithome.com.tw/upload/images/20210926/201415186JSa3zAKy9.png

編輯 Gitlab URL

將 Git Repository 的內網 IP 貼上,並儲存
https://ithelp.ithome.com.tw/upload/images/20210926/20141518cKWjuFC215.png

修改 Gitlab 設定

將 gitlab 的 pem key 上傳到 portal

sudo scp -i "/vagrant_data/project/terraform/stage/portal.pem" "/vagrant_data/project/terraform/stage/gitlab.pem" ubuntu@35.73.42.74:/home/ubuntu

調整 portal port 22 inbound/outbound

當時我們對 portal 只有設定本機電腦
為了要使用 portal 當跳板到 Gitlab 改設定
因此我們需要將 port 22 的 inbound/outbound 做修改
改成 vpc 內的 ip 都可以連

resource "aws_security_group_rule" "ithome_ironman_igress_22" {
    type              = "ingress"
    from_port         = 22
    to_port           = 22
    cidr_blocks       = [var.personal_cidr, aws_vpc.stage.cidr_block]
    protocol          = "tcp"
    security_group_id = aws_security_group.ithome_ironman_portal.id
}

resource "aws_security_group_rule" "ithome_ironman_egress_22" {
    type              = "egress"
    from_port         = 22
    to_port           = 22
    cidr_blocks       = [var.personal_cidr, aws_vpc.stage.cidr_block]
    protocol          = "tcp"
    security_group_id = aws_security_group.ithome_ironman_portal.id
}

ssh 到 portal

ssh -i "/vagrant_data/project/terraform/stage/portal.pem" ubuntu@portal的EIP

修改 pem key 權限

sudo chmod 400 gitlab.pem

編輯 gitlab 組態

sudo vi /etc/gitlab/gitlab.rb 

將 external url 改成 gitlab 網址

https://ithelp.ithome.com.tw/upload/images/20210926/20141518mPNVTDApEG.png

重新組態

sudo gitlab-ctl reconfigure

在 gitlab-ctl reconfigure 過程中有出錯是正常的
因為我們 Load Balance 只有鎖內網和個人 IP
因此 let's encryption 在產生憑證的時候就會無法驗證而出錯
在意這問題的人
其實可以先把 security 打開,驗證好之後再關閉
這樣做我們就會得到奇怪的架構
一個是 aws 的自發憑證
另一個則是 load balance 到 EC2 的 Let's Encryption 憑證

重新啟動 Gitlab

sudo gitlab-ctl restart

離開 Gitlab

exit

刪除 portal 上的 pem key

sudo rm -rf gitlab.pem

離開 portal

exit

將 gitlab_80 和 gitlab 443 註解

因為我們要修改 target group
因此要先將正在監聽中的 80 和 443 port 註解(刪除)

# resource "aws_lb_listener" "gitlab_80" {
#     load_balancer_arn = aws_lb.gitlab.arn
#     port = "80"
#     protocol = "HTTP"

#     default_action {
#         type             = "redirect"
#         target_group_arn = aws_lb_target_group.gitlab.arn
        
#         redirect {
#             port        = "443"
#             protocol    = "HTTPS"
#             status_code = "HTTP_301"
#         }
#     }
# }

# resource "aws_lb_listener" "gitlab_443" {
#     load_balancer_arn = aws_lb.gitlab.arn
#     port              = "443"
#     protocol          = "HTTPS"
#     ssl_policy        = "ELBSecurityPolicy-2016-08"
#     certificate_arn   = aws_acm_certificate.cert.arn
    
#     default_action {
#         type             = "forward"
#         target_group_arn = aws_lb_target_group.gitlab.arn
#     }
# }

修改 target group

原本是 80 port 改成 443 port

resource "aws_lb_target_group" "gitlab" {
    name        = "gitlab"
    port        = 443
    protocol    = "HTTPS"
    target_type = "instance"
    vpc_id      = aws_vpc.stage.id
}

resource "aws_lb_target_group_attachment" "gitlab" {
    target_group_arn = aws_lb_target_group.gitlab.arn
    target_id        = module.ec2_gitlab.id
    port             = 443
}

執行配置

terraform apply

將 gitlab_80 和 gitlab 443 取消註解

這邊需要先註解掉再取消註解
是因為沒有這樣做
會造成 Target Group 有綁定監聽
因此一直無法刪除重建

resource "aws_lb_listener" "gitlab_80" {
    load_balancer_arn = aws_lb.gitlab.arn
    port = "80"
    protocol = "HTTP"

    default_action {
        type             = "redirect"
        target_group_arn = aws_lb_target_group.gitlab.arn
        
        redirect {
            port        = "443"
            protocol    = "HTTPS"
            status_code = "HTTP_301"
        }
    }
}

resource "aws_lb_listener" "gitlab_443" {
    load_balancer_arn = aws_lb.gitlab.arn
    port              = "443"
    protocol          = "HTTPS"
    ssl_policy        = "ELBSecurityPolicy-2016-08"
    certificate_arn   = aws_acm_certificate.cert.arn
    
    default_action {
        type             = "forward"
        target_group_arn = aws_lb_target_group.gitlab.arn
    }
}

進入 admin 介面修改設定

因為我們之後要改走內網
所以需要開啟 local network 的設定
Admin -> Settings -> Network -> Outbound Requests -> Allow requests to the local network from hooks and services
勾選 Allow requests to the local network from hooks and services
https://ithelp.ithome.com.tw/upload/images/20210926/20141518lBaOo7Jj6y.png

修改專案的 Webhook 設定

將 Webhook 的 Jenkins URL 改成內網 IP 的 URL
https://ithelp.ithome.com.tw/upload/images/20210926/20141518FWHe1LtSok.png

修改本機 Repository

因為我們使用 alb 以後
只剩下 80 和 443 port
因此只能走 https 來 clone repository

畢竟帳號密碼也算是機密資訊
Gitlab 有提供一個不錯的功能
讓我們來建立 Token
使用 Token 的方式來存取

有些環境下會限制 ssh 的存取
這時候走 https 的話
使用 token 的方式來存取是相對安全的做法

建立 Token

在專案中的 Settings,可選取 Access_Token 建立 Token
https://ithelp.ithome.com.tw/upload/images/20210926/20141518jDqpRgsqWZ.png

更改 repositroy

git remote set-url origin https://使用者名稱:TOKEN@gitlab.markmewmew.com/ithome-ironman-2021/portal.git

嘗試抓取遠端

git fetch origin

CodeDeploy 如果莫名失敗的,請參考這篇,需要建立一個新的 IAM Role,然後原本 EC2 綁定的解綁,綁定新的沒權限的,再解綁然後綁定回原來的。


大方向來說,
架構調整會先在這裡告一段落,
這裡先說聲抱歉,
能力上的不足,
本來想把 VPN 環境建置起來,
這樣就可以順利連到 VPC 直接順取內網資源。

或是在 Route53 中建立自訂託管區域,
這樣會因為 DHCP 的設定,
而解析到 host,
讓我們服務在內部溝通時,
可以不用綁 EC2 或是 IP 綁很死,
但是無奈遇到一些技術上的問題就暫時作罷。

明天我會中場休息做個閒聊,
接下來我們的步驟會有些不同,
繼續進行改造,

將我們的服務打包成 Docker,
並使用 AWS 的 Kubernetes 服務(EKS)管理,
接著搭配新的部署工具 Octopus Deploy
部署到 EKS 上。

  1. CentOS7下GitLab修改域名host
  2. gitlab Import url is blocked: "Requests to the local network are not allowed"
  3. InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller: Missing credentials
  4. How to Set Up Time Synchronization with NTP on Ubuntu 18.04
  5. Load Balancer for multi-node GitLab

上一篇
EP13 - 災難演練,重建你的 VPC
下一篇
EP15 - 中場閒聊,所以我說大家的 DevOps 和 CI/CD 呢?
系列文
關於我幫新公司建立整套部屬流程那檔事30

尚未有邦友留言

立即登入留言