
배경 상황
EKS 클러스터를 테스트용으로 사용할 때나 아주 작은 규모에서 운영할 경우를 제외하고 대부분의 서비스 운영에서는 여러 개의 클러스터를 이용하는 경우가 대부분이다. 이 때 ArgoCD를 각 클러스터에 배포할수도 있지만, 관리의 편의성을 위해 ArgoCD에 여러 개의 클러스터를 연결하기도 하게 된다. 특히 지금 회사에서는 환경별로 별도의 ArgoCD 서버를 운영한다고 하더라도 서로 다른 계정에 배포된 EKS 클러스터를 ArgoCD에 등록할 필요가 있어 아래 구성을 진행하게 되었다.
Cross Account EKS Cluster 연동 방법
연동 구성을 간단하게 도식화 하면 아래와 같다.

ArgoCD 공식문서를 확인해보면 클러스터의 선언적 구성 방법이 나와있는데, 요약해보면
- ArgoCD가 운영되는 계정에서 IAM Role을 생성하고(아래 그림 상 argocd-management-role), argocd-server, argocd-applicationset-controller, argocd-application-controller 세 개 SA에 연결
- 연결하고자 하는 EKS 클러스터가 배포된 계정에서 IAM Role을 생성(service-cluster-role)하고 Access Entry를 이용해 EKS 클러스터에 대한 접근 권한을 부여
- argocd-management-role에서 service-cluster-role로 assume 할 수 있도록 신뢰 관계를 구성
- argocd management 클러스터에서 Secret을 생성해 ArgoCD에 등록할 EKS 클러스터 정보를 작성
그럼 단계별로 연동 작업을 진행해보도록 하자(아래 예제에서 AWS 리소스는 Terraform으로 생성했다).
1. [ArgoCD Management 계정] IRSA를 구성하기위해서 테라폼 모듈로 IAM Role을 생성한다. 먼저 IAM Role에 연결할 IAM Policy를 구성해 argo-management-role이 Service 계정의 service-cluster-role을 assume할 수 있도록 한다.
module "iam_policy_argo_management" {
source = "terraform-aws-modules/iam/aws//modules/iam-policy"
version = "5.55.0"
name = format("%s-argo-mgmt-policy", local.namespace)
description = format("%s-argo-mgmt-policy", local.namespace)
policy = data.aws_iam_policy_document.argo_mgmt_iam_policy.json
}
data "aws_iam_policy_document" "argo_mgmt_iam_policy" {
statement {
sid = "stsAssumeRole"
effect = "Allow"
actions = [
"sts:AssumeRole",
]
resources = [
"arn:aws:iam::<SERVICE_ACCOUNT_ID>:role/service-cluster-role",
]
}
}
2. [ArgoCD Management 계정] IRSA 구성을 위해 Trust policy document를 작성해준다. 이 정책을 적용하면 argocd 네임스페이스의 세 개 serviceaccount에서 argo-management-role을 assume할 수 있게 된다. 참고로 OIDC 정보를 연결할 때에는 OIDC Issuer URL과 ARN 정보가 둘 다 필요하기 때문에 output에서 미리 정의해놓고 가져다 사용한다.
# Trust Policy에서 OIDC 정보를 불러오기 위한 설정
output "cluster_oidc_issuer_url" {
description = "OIDC issuer URL"
value = module.eks.cluster_oidc_issuer_url
}
output "cluster_oidc_provider_arn" {
description = "OIDC provider ARN"
value = module.eks.oidc_provider_arn
}
# OIDC Issuer URL의 https:// prefix를 제거
locals {
cluster_oidc_issuer_url_no_prefix = replace(module.eks.cluster_oidc_issuer_url, "https://", "")
}
# Trust Policy 정책 정의
data "aws_iam_policy_document" "argo_mgmt_trust_policy" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
effect = "Allow"
principals {
type = "Federated"
identifiers = [module.eks.oidc_provider_arn]
}
condition {
test = "StringEquals"
variable = "${local.cluster_oidc_issuer_url_no_prefix}:sub"
values = [
"system:serviceaccount:argocd:argocd-application-controller",
"system:serviceaccount:argocd:argocd-applicationset-controller",
"system:serviceaccount:argocd:argocd-server"
]
}
condition {
test = "StringEquals"
variable = "${local.cluster_oidc_issuer_url_no_prefix}:aud"
variable = "${module.eks.oidc_provider_arn}:sub"
values = [
"system:serviceaccount:argocd:argocd-application-controller",
"system:serviceaccount:argocd:argocd-applicationset-controller",
"system:serviceaccount:argocd:argocd-server"
]
}
}
statement {
sid = "ExplicitSelfRoleAssumption"
effect = "Allow"
principals {
type = "AWS"
identifiers = ["*"]
}
actions = [
"sts:AssumeRole",
]
condition {
test = "ArnLike"
variable = "aws:PrincipalArn"
values = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/argo-management-role"]
}
}
}
3. [ArgoCD Management 계정] 위에서 생성한 권한들을 가진 argo-management-role이라는 이름의 IAM role을 생성한다.
module "argo-management-role" {
source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role"
version = "5.55.0"
create_role = true
role_name = "argo-management-role"
create_custom_role_trust_policy = true
custom_role_trust_policy = data.aws_iam_policy_document.argo_mgmt_trust_policy.json
custom_role_policy_arns = [
module.iam_policy_argo_admin.arn
]
tags = merge(local.common_tags, {
"Name" = "argo-management-role"
})
}
그리고 service 계정에서 생성할 IAM Role에 신뢰관계를 구성하기 위해 output으로 생성한 argo-management-role에 대한 정보를 뽑아준다.
output "argo_management_role_arn" {
description = "ARN of ArgoCD management role"
value = module.argo-management-role.iam_role_arn
}
4. [Service Cluster 계정] service-eks 클러스터의 어드민 권한을 가진 IAM Role(service-cluster-role)을 생성해야 한다(IRSA 아님). 먼저 위에서 생성한 argo-management-role이 service-cluster-role을 assume할 수 있도록 iam policy document를 생성한다.
data "aws_iam_policy_document" "cluster_role_trust_policy" {
statement {
sid = "ArgoCDClusterRoleAssumption"
effect = "Allow"
principals {
type = "AWS"
identifiers = [local.devops_eks_outputs.argo_management_role_arn]
}
actions = [
"sts:AssumeRole",
]
}
}
5. [Service Cluster 계정] 위에서 작성한 policy document를 기반으로 IAM Role을 생성해준다.
module "eks-cluster-role" {
source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role"
version = "5.55.0"
create_role = true
role_name = "service-cluster-role"
create_custom_role_trust_policy = true
custom_role_trust_policy = data.aws_iam_policy_document.cluster_role_trust_policy.json
tags = merge(local.common_tags, {
"Name" = "service-cluster-role"
})
}
6. [Service Cluster 계정] ArgoCD가 서비스 클러스터의 엔드포인트를 호출해서 매니페스트 파일을 배포하고 업데이트할 수 있게 해야하기 때문에 service-eks 클러스터에 Access Entry를 생성해서 클러스터 접근권한을 부여해준다(IAM Policy 아님).
- 이전 버전의 EKS 클러스터에서는 aws-auth ConfigMap을 수정해야했지만 지금은 Access Entry라는 AWS API를 통해서 EKS 클러스터 권한을 부여/삭제할 수 있게 되었기 때문에 테라폼으로도 권한 부여를 할 수 있다. 참고로 Access Entry는 EKS 클러스터에서 authenticationMode이 API(혹은 API & Config Map)로 설정되어있어야 사용 가능하다. Access Entry에 연결할 Policy는 arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy로 설정한다.
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "20.36.0"
cluster_name = "service-eks"
# 기타 설정 생략
access_entries = {
cluster-role = {
principal_arn = module.eks-cluster-role.iam_role_arn
policy_associations = {
cluster-admin-policy = {
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
access_scope = {
type = "cluster"
}
}
}
}
}
}
7. [ArgoCD Management 계정] 마지막으로 argocd.argoproj.io/secret-type: cluster 레이블이 붙어있는 Secret을 생성해주면 되는데, 연결할 service-eks 클러스터의 CA(Certificate Authority) 데이터 등이 들어가기 때문에 External Secrets으로 생성해준다. 나는 이번에도 Parameter Store에 데이터를 저장 후 불러오는 구성으로 진행했다(External Secrets 설치 방법에 대해서는 링크를 참조). ClusterSecretStore는 이미 생성했다고 가정하고 아래와 같이 설정을 진행하면 클러스터가 ArgoCD 클러스터에 등록된다.
[Kubernetes]External Secrets란? 설치 및 구성방법 (GitOps 도입 시 크레덴셜 정보 안전하게 저장하기)
배경 상황쿠버네티스 환경에서 깃옵스(GitOps)를 하려고 하면 k8s 매니페스트 파일을 깃헙이나 깃랩같은 코드 저장소에 저장하게 된다. 이렇게 ArgoCD를 이용해서 애플리케이션 부트스트래핑 구성
canaryrelease.tistory.com
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: eso-service-cluster-secret
namespace: argocd
spec:
secretStoreRef:
name: aws-parameter-store
kind: ClusterSecretStore
target:
name: eso-service-cluster-secret
creationPolicy: Owner
template:
metadata:
labels:
argocd.argoproj.io/secret-type: cluster
data:
# 정적 값
name: "service-eks"
# 동적 값
server: "{{ .clusterEndpoint }}"
config: |
{
"awsAuthConfig": {
"clusterName": "lbox-service-stg-eks",
"roleARN": "arn:aws:iam::<SERVICE ACCOUNT ID>:role/service-cluster-role"
},
"tlsClientConfig": {
"insecure": false,
"caData": "{{ .clusterCa }}"
}
}
data:
- secretKey: clusterEndpoint
remoteRef:
key: /parameter-store-경로/cluster-endpoint
- secretKey: clusterCa
remoteRef:
key: /parameter-store-경로/cluster-ca
사실 한 번 만들고 나면 별 거 아닌데 번거롭고 손으로 작업하면 실수할 구간들이 꽤 있어서 테라폼으로 만들어놓고 사용하니까 확실히 편하고 실수가 덜해서 좋다(라고 써놓고 테라폼 작성할 때 실수해서 삽질한 건 비밀).