IaC Automation with EKS
일반적으로 EKS는 Kubernetes를 통해 컨테이너화된 어플리케이션을 관리, 배포하는 용도로 많이 사용된다.
하지만 어플리케이션을 서비스하는데 필요한 AWS 자원(Route53, ELB 등)까지 점점 영역을 넓혀가면서 요즘들어 인프라 관리까지도 EKS와 같은 Kubernetes를 통해서 관리하는 도구 및 방법들이 활성화 되고 있다.
이런 도구들 중 Crossplane이라는 도구에 대해서 알아보고 간단한 테스트를 해보았다.
Crossplane이란?
2018년에 Upbound라는 회사에서 만들었고 2021년 9월에 CNCF Incubating 프로젝트로 승인되었다.
공식 문서에서는 CrossPlane을 오픈소스 멀티 클라우드 컨트롤 플레인이라고 소개한다.
Crossplane을 만들게 된 계기도 클라우드 공급자들의 독점적이고 폐쇄된 클라우드 컴퓨팅 환경에서 Kubernetes의 광범위한 사용에 주목했다고 한다. 멀티클라우드를 사용하는데 발생하는 다양한 제약과 복잡성, 벤더 종속성을 해결할 수 있는 클라우드 컴퓨팅 범용 API를 Kubernetes를 통해 제공하는게 목적이라고 한다.
We believe there is a need for a new portability layer on top of existing managed services and cloud offerings. We need a control plane that exposes a universal declarative-style API for cloud computing and offers full lifecycle management, orchestration, scheduling, and policy enforcement. A control plane that can span cloud providers, regions and offerings. A control plane that is driven by the open source community.
공식 Github에 가보면 현재 Crossplane을 채택한 곳을 나열해놨는데, 어떤 용도로 사용하는지 표기되어있어 이렇게 사용할 수도 있다는 좋은 가이드가 되었다.
https://github.com/crossplane/crossplane/blob/master/ADOPTERS.md
Crossplane 사용해보기
https://docs.crossplane.io/v1.12/getting-started/provider-aws/ 에서 제공하는 가이드를 따라서 테스트를 진행하였다.
Crossplane 설치
설치 사전 조건으로는 아래의 준비가 필요하다.
- Helm version v3.2.0 or later
- AWS access keys (an AWS account with permissions to create an S3 storage bucket)
- permissions to create pods and secrets in the Kubernetes cluster
- a Kubernetes cluster with at least 6 GB of RAM
- 왜 6GB의 사양이 필요한지 찾아보니 관리하는 자원이 많아질 수록 자원 사용률에 대한 이슈가 있는 것으로 보인다.
- https://github.com/crossplane/crossplane/issues/1846
- Resource를 추가로 생성할 수록 필요한 자원이 늘어날 것으로 보인다.
- Scaling Kubernetes to Thousands of CRDs : https://blog.upbound.io/scaling-kubernetes-to-thousands-of-crds
내 경우엔 EKS Cluster를 사전에 설치해서 사용했다.
가이드 문서에서는 Kind를 사용해 로컬에서 Kubernetes Cluster를 구성해서 사용할 수도 있다고 한다.
(https://kind.sigs.k8s.io/)
# Crossplane Helm Chart 추가 및 최신화
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
# Crossplane 설치 (23.06.10 기준 1.12.2 버전으로 설치됨)
helm install crossplane crossplane-stable/crossplane --namespace crossplane-system --create-namespace
# Crossplane 설치 확인
kubectl get pods -n crossplane-system
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl get pods -n crossplane-system
NAME READY STATUS RESTARTS AGE
crossplane-5c84d45d5b-glk69 1/1 Running 0 24s
crossplane-rbac-manager-56bf656ddd-p4xq4 1/1 Running 0 24s
# Crossplane Kubernetes API 확인
kubectl api-resources | grep crossplane
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl api-resources | grep crossplane
compositeresourcedefinitions xrd,xrds apiextensions.crossplane.io/v1 false CompositeResourceDefinition
compositionrevisions apiextensions.crossplane.io/v1 false CompositionRevision
compositions apiextensions.crossplane.io/v1 false Composition
environmentconfigs apiextensions.crossplane.io/v1alpha1 false EnvironmentConfig
configurationrevisions pkg.crossplane.io/v1 false ConfigurationRevision
configurations pkg.crossplane.io/v1 false Configuration
controllerconfigs pkg.crossplane.io/v1alpha1 false ControllerConfig
locks pkg.crossplane.io/v1beta1 false Lock
providerrevisions pkg.crossplane.io/v1 false ProviderRevision
providers pkg.crossplane.io/v1 false Provider
storeconfigs secrets.crossplane.io/v1alpha1 false StoreConfig
Provider 설치
Crossplane에서 컨트롤할 자원의 Provider를 설치한다.
Terraform에서 init을 수행할 때 Provider를 가져와 설치하는 것과 유사하다.
Crossplane에서 제공하는 Provider의 종류는 아래의 링크에서 확인 가능하며, 테스트에서는 AWS로 설치하였다.
# Crossplane의 AWS Provider 설치
cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: upbound-provider-aws
spec:
package: xpkg.upbound.io/upbound/provider-aws:v0.40.0
EOF
# 설치된 Provider 확인
# HEALTHY의 경우
kubectl get providers
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl get providers
NAME INSTALLED HEALTHY PACKAGE AGE
upbound-provider-aws True True xpkg.upbound.io/upbound/provider-aws:v0.40.0 95
Kubernetes secret 생성
AWS 자원을 컨트롤할 AWS 계정의 Access Key 와 Secret Key의 정보를 Kubernetes Secret으로 생성한다.
# AWS Credentias 파일 생성
export AWS_ACCESS_KEY_ID=<value>;
export AWS_SECRET_ACCESS_KEY=<value>;
cat <<EOF > aws-credentials.txt
[default]
aws_access_key_id = $AWS_ACCESS_KEY_ID
aws_secret_access_key = $AWS_SECRET_ACCESS_KEY
EOF
# Kubernetes secret 생성
kubectl create secret \
generic aws-secret \
-n crossplane-system \
--from-file=creds=./aws-credentials.txt
# Kubernetes secret 확인
kubectl describe secret -n crossplane-system
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl describe secret -n crossplane-system
Name: aws-secret
Namespace: crossplane-system
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
creds: 116 bytes
...
ProviderConfig 생성
Terraform의 경우 aws configure나 환경변수로 자격증명을 수행하였지만, Crossplane의 경우 Cluster에서 해당 역할을 수행해야하므로 별도의 ProviderConfig Kind를 생성한다.
cat <<EOF | kubectl apply -f -
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: aws-secret
key: creds
EOF
Crossplane을 통해 관리할 S3 생성하기
# S3 bucket명은 고유해야하므로 임의의 랜덤 값을 추가한 bucket명 생성 명령어
bucket=$(echo "crossplane-bucket-"$(head -n 4096 /dev/urandom | openssl sha1 | tail -c 10))
# Crossplane으로 S3 생성
cat <<EOF | kubectl apply -f -
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
name: $bucket
spec:
forProvider:
region: ap-northeast-2
providerConfigRef:
name: default
EOF
# S3 생성 확인
kubectl get buckets
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl get buckets
NAME READY SYNCED EXTERNAL-NAME AGE
crossplane-bucket-4b0d5a3fe True True crossplane-bucket-4b0d5a3fe 35s
S3 Tag 추가 및 수동 삭제 테스트
S3 Tag 추가
아래의 yaml 수정으로 S3에 Tag 추가도 가능하다.
# 기존에 생성한 S3 Bucket에 Tag 추가
cat <<EOF | kubectl apply -f -
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
name: $bucket
spec:
forProvider:
region: ap-northeast-2
tags:
s3_tag1 : test1
S3_tag2 : test2
providerConfigRef:
name: default
EOF
S3 Tag를 AWS Console에서 수동 삭제
Tag를 삭제하고 자원의 상태를 확인해보면 아래와 같이 변경을 감지한다.
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl describe buckets
...
...
Status: True
Type: LastAsyncOperation
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CreatedExternalResource 58m managed/s3.aws.upbound.io/v1beta1, kind=bucket Successfully requested creation of external resource
Warning CannotInitializeManagedResource 58m managed/s3.aws.upbound.io/v1beta1, kind=bucket Operation cannot be fulfilled on buckets.s3.aws.upbound.io "crossplane-bucket-4b0d5a3fe": the object has been modified; please apply your changes to the latest version and try again
Normal UpdatedExternalResource 36m managed/s3.aws.upbound.io/v1beta1, kind=bucket Successfully requested update of external resource
그리고 몇분이 지나면 본인이 가지고 있던 가장 최신의 정보를 토대로 복구한다. (복구에 체감상 4~5분은 걸리는 것 같다.)
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl describe buckets
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal UpdatedExternalResource 102s (x2 over 40m) managed/s3.aws.upbound.io/v1beta1, kind=bucket Successfully requested update of external resource
생성한 S3 Bucket에 대한 정의 확인
생성한 자원에 대해서 yaml로 확인해보면 status항목이 보이는데 이를 토대로도
Crossplane에서 생성한 자원의 상태를 지속적으로 모니터링 하는 것을 알 수 있다.
# 생성한 bucket에 대해 yaml 확인
kubectl get bucket -o yaml | cat
apiVersion: v1
items:
- apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
annotations:
crossplane.io/external-create-pending: "2023-06-09T16:25:54Z"
crossplane.io/external-create-succeeded: "2023-06-09T16:25:54Z"
crossplane.io/external-name: crossplane-bucket-4b0d5a3fe
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"s3.aws.upbound.io/v1beta1","kind":"Bucket","metadata":{"annotations":{},"name":"crossplane-bucket-4b0d5a3fe"},"spec":{"forProvider":{"region":"ap-northeast-2","tags":{"S3_tag2":"test2","s3_tag1":"test1"}},"providerConfigRef":{"name":"default"}}}
upjet.crossplane.io/provider-meta: "null"
creationTimestamp: "2023-06-09T16:25:50Z"
finalizers:
- finalizer.managedresource.crossplane.io
generation: 4
name: crossplane-bucket-4b0d5a3fe
resourceVersion: "24217"
uid: 02712cfb-df35-487a-8c03-7b165b301c7f
spec:
deletionPolicy: Delete
forProvider:
hostedZoneId: Z3W03O7B5YMIYP
region: ap-northeast-2
tags:
S3_tag2: test2
crossplane-kind: bucket.s3.aws.upbound.io
crossplane-name: crossplane-bucket-4b0d5a3fe
crossplane-providerconfig: default
s3_tag1: test1
providerConfigRef:
name: default
status:
atProvider:
accelerationStatus: ""
arn: arn:aws:s3:::crossplane-bucket-4b0d5a3fe
bucketDomainName: crossplane-bucket-4b0d5a3fe.s3.amazonaws.com
bucketRegionalDomainName: crossplane-bucket-4b0d5a3fe.s3.ap-northeast-2.amazonaws.com
grant:
- id: 6923bc668cf04b658bb5da60337e4d3960e8845d98c4deabe9f6411d141f30d5
permissions:
- FULL_CONTROL
type: CanonicalUser
uri: ""
id: crossplane-bucket-4b0d5a3fe
policy: ""
requestPayer: BucketOwner
serverSideEncryptionConfiguration:
- rule:
- applyServerSideEncryptionByDefault:
- kmsMasterKeyId: ""
sseAlgorithm: AES256
bucketKeyEnabled: false
tagsAll:
crossplane-kind: bucket.s3.aws.upbound.io
crossplane-name: crossplane-bucket-4b0d5a3fe
crossplane-providerconfig: default
versioning:
- enabled: false
mfaDelete: false
conditions:
- lastTransitionTime: "2023-06-09T16:26:05Z"
reason: Available
status: "True"
type: Ready
- lastTransitionTime: "2023-06-09T16:25:54Z"
reason: ReconcileSuccess
status: "True"
type: Synced
- lastTransitionTime: "2023-06-09T16:25:59Z"
reason: Finished
status: "True"
type: AsyncOperation
- lastTransitionTime: "2023-06-09T16:25:59Z"
reason: Success
status: "True"
type: LastAsyncOperation
kind: List
metadata:
resourceVersion: ""
https://doc.crds.dev/github.com/crossplane/provider-aws/s3.aws.crossplane.io/Bucket/v1beta1@v0.40.0 링크에서 각 항목에 대한 상세 의미를 알 수 있는데, Crossplane을 사용할 때 특히 주요하게 볼만한 부분은 아래와 같다.
- forProvider : 필수 항목으로 실제 자원을 구성하는데 사용하는 설정이다. S3의 경우 관련된 설정이 많이 없지만, RDS와 같은 경우 더 다양한 설정이 제공된다.
- deletionPolicy : Crossplane에서 관리하는 자원을 Kubernetes API에서 삭제할 경우 실제 자원까지 삭제할지 결정하는 설정이다. 기본값은 Delete이며 Orphan으로 설정할 경우 Crossplane에서 삭제해도 실제 자원은 삭제되지 않는다.
테스트 환경 삭제 및 후기
# Crossplane에서 관리하는 자원 확인 및 삭제
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl get managed
NAME READY SYNCED EXTERNAL-NAME AGE
bucket.s3.aws.upbound.io/crossplane-bucket-4b0d5a3fe True True crossplane-bucket-4b0d5a3fe 77m
kubectl delete bucket crossplane-bucket-4b0d5a3fe
# Crossplane Provider 확인 및 삭제
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl get providers
NAME INSTALLED HEALTHY PACKAGE AGE
upbound-provider-aws True True xpkg.upbound.io/upbound/provider-aws:v0.40.0 118m
kubectl delete provider upbound-provider-aws
# Crossplane Help Deployment 삭제
helm uninstall crossplane --namespace crossplane-system
(test_admin@myeks:N/A) [root@myeks-bastion ~]# kubectl get pods -n crossplane-system
No resources found in crossplane-system namespace.
# Namespace 삭제
kubectl delete namespace crossplane-system
Kubernetes와 YAML에 익숙하다면 상당히 쓰기 편한 느낌이었다.
AWS SDK가 개발자가 사용하는 JAVA, PHP 등을 사용해 AWS 자원을 컨트롤 하는 것처럼, 클라우드 엔지니어에게 익숙한 YAML을 가지고 다양한 클라우드 자원을 한군데에서 컨트롤할 수 있다는 점이 매력적이었는데, 이 부분은 테스트를 못해본 점이 약간 아쉬움으로 남는다.
정말 간단한 테스트로 S3만 생성해봤는데 추후에 테스트용 AWS 환경을 Crossplane으로 구성하고 관리해서 추후에 코드를 재사용하면 유용할 것 같다.
참고자료
- Crossplane Github : https://github.com/crossplane/crossplane
- Crossplane Learn More : https://docs.crossplane.io/knowledge-base/guides/learn-more/
- Crossplane is now a CNCF Incubating project : https://blog.crossplane.io/crossplane-cncf-incubation/
- : https://docs.crossplane.io/v1.12/getting-started/provider-aws/
- Crossplane #1 - 소개 : https://blog.outsider.ne.kr/1561
- Introducing Crossplane : https://docs.google.com/document/d/1whncqdUeU2cATGEJhHvzXWC9xdK29Er45NJeoemxebo