這一陣子在處理許多 DevOps 的問題,遇到了要讓 EKS Cluster 存取不同 VPC 服務之間的問題,在 AWS 架構下可以使用 VPC 的 Peer Connection 來讓兩個不同 VPC 的網路對聯,交換 Route Table 地址,這樣彼此就可以發現對方。
原理
除了讓 A, B 對連線外,還會讓 A 服務的 Network Route Table 知道 B 的 Network CIDR Table,這樣 A, B 就可以存取(發現)到不同網段下的電腦,像是 BGP Routing Table 這樣。
但是不可以存取跟自己相同 VPC CIDR 的服務,那樣 Route Table 會亂掉,見下方注意事項。
注意
當實施 VPC Peer Connection 架構下,需要特別注意如果還想要存取更多不同 VPC 資源,就一定要注意 VPC CIDR 分配不可以衝突,因此一定要做好分配。
服務少的話,使用 172.16.0.0/12 架構來分配 AWS 上的資源應該是沒問題的,比方說 EKS (CIDR: 172.16.1.0/24) Peer Connection CIDR 172.16.20.0/24 下的網路,以此類推 10.0.0.0, 192.168...,其網段數量計算可以使用 subnet mask calculator 來看地址 cover 多少電腦。 (私人網段的 spec 可以參考 RFC 1918。)
也許一般來說用 Terraform 因為每個服務都當成 module 複製來複製去,鮮少需要改 VPC CIDR,但如果會發生這樣的狀況,那就一定要注意要手動去修改不同 VPC 的 CIDR,不要讓他們碰撞。
Terraform Module
這個功能會直接寫成模組,這個模組最主要的目的是讓兩個 VPC 可以做對連,兩個模組的 VPC ID 要使用最主要的那個 VPC。
由於本章節要解決的是讓 EKS 的 Terraform 模組可以連線到 RDS 服務上,以此作為範例,EKS 我使用的模組是 cloudposse 的,他在設定上會有點礙手礙腳,但總之要選用 eks 的 main VPC
對連的 Terraform 設定會碰到一個需要注意的問題,我將這個問題放到下一個小節提及。
在這個模組中最主要就是建立一個 aws_vpc_peering_connection、兩個 (對連雙方)aws_route 及 aws_security_group (用於對連連線安全組控制)。
建立後讓 aws_route 去套用同一個 aws_vpc_peering_connection。
而 security_group 則是要讓 eks 或 rds 各自的流量可以互通,我是開 all,但幾於資安考量的話,可以再鎖定到比較細的 security group 規則。
檔案結構:
- main.tf
- var.tf
main.tf:
resource "aws_vpc_peering_connection" "PEER_A2B" { peer_vpc_id = var.peer_vpc_id vpc_id = var.this_vpc_id # acceptor is us auto_accept = true accepter { allow_remote_vpc_dns_resolution = true } requester { allow_remote_vpc_dns_resolution = true } } # bind to existsed rotuer table # aws router (e.g rds to eks) resource "aws_route" "A2B" { count = length(var.this_vpc_route_table_ids) # this route_table_id = var.this_vpc_route_table_ids[count.index] # to b destination_cidr_block = var.peer_vpc_cidr vpc_peering_connection_id = aws_vpc_peering_connection.PEER_A2B.id } # aws router (e.g eks to rds) resource "aws_route" "B2A" { count = length(var.peer_vpc_route_table_ids) # peer route_table_id = var.peer_vpc_route_table_ids[count.index] # to a destination_cidr_block = var.this_vpc_cidr vpc_peering_connection_id = aws_vpc_peering_connection.PEER_A2B.id } # These may cause aws_security_group_rule everytime modify needs to apply two time... (WARNING) resource "aws_security_group_rule" "A" { description = "Peering Config A (terraform-managed)" # count = length([var.peer_vpc_cidr]) could be more but only do once now type = "ingress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = [var.peer_vpc_cidr] security_group_id = var.this_vpc_security_group_id } // InvalidPermission.Duplicate: because everyone apply the same rule resource "aws_security_group_rule" "B" { description = "Peering Config B (terraform-managed)" # count = length(var.this_vpc_cidr) could be more but only do once now type = "ingress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = [var.this_vpc_cidr] security_group_id = var.peer_vpc_security_group_id }
var.tf:
variable "namespace" { type = string } variable "this_vpc_id" { type = string } variable "this_vpc_security_group_id" { type = string } variable "this_vpc_route_table_ids" { type = list(string) } variable "this_vpc_cidr" { type = string } variable "peer_vpc_id" { type = string } variable "peer_vpc_security_group_id" { type = string } variable "peer_vpc_route_table_ids" { type = list(string) } variable "peer_vpc_cidr" { type = string }
叫用 Module:
module "peering_eks_to_rds" { source = "./modules/peering" namespace = "eks2rds_connector" this_vpc_id = module.my_db_instance.vpc_id this_vpc_route_table_ids = [module.my_db_instance.vpc_route_table_id] this_vpc_cidr = module.my_db_instance.vpc_cidr this_vpc_security_group_id = module.my_db_instance.db_security_group_id peer_vpc_id = module.my_eks.eks_vpc_id peer_vpc_route_table_ids = module.my_eks.vpc_all_route_table_ids peer_vpc_cidr = module.my_eks.vpc_cidr peer_vpc_security_group_id = module.my_eks.eks_cluster_security_group_id depends_on = [ module.my_db_instance, module.my_eks ] }
以上解釋一下關於 this_vpc_route_table_ids 會是陣列的原因,是因為有可能套用連線的 vpc 有好幾個 route table,就可以一併加上去,但如果只有一個,那就只填一個進陣列。
模組設計與要面臨的問題
我強烈建議先看完這個問題再開始進行 IaaC 調整,導入 VPC Peer Connection 進去你的架構時會碰到好幾個問題:
1. 你的 A, B 服務的 VPC 都必須存在,才可以建立 VPC Peer Connection,如果你的 A, B 是寫成 terraform module 時,不要把這個程式放到任一個 A, B 模組,要獨立成 C 模組額外套用,像上述用法一樣。 使用這個 VPC Peer Connection 時也要加上 depends_on 等待
2. 你的 A, B 服務的 VPC 是否有 CIDR 衝突,或甚至你其他服務有衝突 VPC,當你會開始考慮 VPC Peer Connection 時就表示未來你急有可能其他服務也都要做 Peer Connection ,最好現在就調整所有的 module CIDR。
以下提供我的 CIDR 設定:
- eks 服務的 VPC 使用: 172.16.0.0/16
- (以下都是 cloudposse 給定的)
- public-apse1a: 172.16.96.0/19
- public-apse1b: 172.16.128.0/19
- public-apse1c: 172.16.160.0/19
- private-apse1a: 172.16.0.0/19
- private-apse1b: 172.16.32.0/19
- private-apse1c: 172.16.64.0/19
- rds 的 VPC 使用 10.0.0.0/16
- public subnet: 10.0.1.0/24
- private subnet: 10.0.0.0/24
- mq 的 VPC 使用 10.11.0.0/16
- public subnet: 10.11.1.0/24
- private subnet: 10.11.0.0/24
我故意區分 eks 用 172.16 網段,但基本上整個 Class B 已經都被這個服務佔走了,其他服務就要改 Class A 級或 192.168 類型的私人網段,所以我隨便列了兩個服務的網段,特別填了不一樣的 Class A,要注意大多數 Terraform Module 都是複製即用,沒有做好 CIDR 管理就會發生太多的 VPC 都用了相同的 CIDR。
至少我在 rds, mq 或其他服務上使用了 10.0.0.0/8 級的分配,只要 10.1, 10.2, 10.3.... 這樣分下去,我最多可以有 254 個服務可以配置。
3. 你的 Terraform VPC 模組要是一開始就預先填上 route 進去,一定會導致 VPC Peer 模組套用時出現第一次都被刪掉,第二次才成功,第三次套用又被刪掉這種窘境 (Terraform double apply VPC route conflict) ,所以要注意,要抽離那個寫法,以下是範例:
resource "aws_route_table" "_" { vpc_id = aws_vpc._.id tags = { Name = "${var.namespace}-route-table" } // 不可以寫在這裡,這樣每次套用就會有機率衝突規則,導致時好時壞。 //shouldn't write in here, because this will conflict with vpc_peer_connection /*dynamic "route" { for_each = local.route content { cidr_block = route.value.cidr_block gateway_id = route.value.gateway_id instance_id = route.value.instance_id nat_gateway_id = route.value.nat_gateway_id } }*/ } // 應該多一個這個資源,套用到上面的 aws_route_table._ 這個 resource 來套用子資源,才不會發生衝突 resource "aws_route" "_" { count = length(local.route) route_table_id = aws_route_table._.id destination_cidr_block = local.route[count.index].cidr_block gateway_id = local.route[count.index].gateway_id instance_id = local.route[count.index].instance_id nat_gateway_id = local.route[count.index].nat_gateway_id }
補充,這裡連 security_group 的 ingress, engress 都要分開來寫,不可以寫在一起 (指寫在 aws_security_group 裡面),要另外寫在 aws_security_group_rule 裡面去套用,否則每次 apply 都會把 8 竿子打不著的服務重建 security group rule,而且還會打架,影響到 VPC Peer Connection 模組的功能。 記得,這個是指其他模組,不是只有在 VPC Peer Connection 的,建議是分開來寫。
你硬要寫在一起,被 VPC PeerConnection 模組引用的套件,每次 apply 功能都一定會這樣...
4. 套用完成後,記得去 VPC Peer Connection 服務上檢查上面的 Route Table 全部都出現了 A, B 服務,舉例假設我配 EKS + RDS,我應該要看到有 eks 的 route table,也有 rds 的 route table,而且兩邊的 security group 都有打通互相存取的權限 CIDR。
5. eks 要測有沒有連上 rds,要開一個 pods 然後 exec 連進去做測試,可以在裡面安裝個 pg_isready 去測試行不行
References:
https://dev.to/bensooraj/accessing-amazon-rds-from-aws-eks-2pc3
https://stackoverflow.com/questions/54596521/terraform-deletes-route-tables-then-adds-them-on-second-run-no-changes-bug-or
沒有留言:
張貼留言