이번 포스팅은 '클라우드 네이티브를 위한 쿠버네티스 실전 프로젝트'의 교재 내용을 바탕으로
Amazon EKS를 활용하여 쿠버네티스 클러스터 환경을 구축하고,
예제 애플리케이션을 배포하는 과정을 설명한다.
VPC 생성하기
먼저 로컬에서 git 레포지토리를 다운로드 받는다.
[CloudFormation]에서 /k8s-aws-book/eks-env/01_base_resources_cnf.yaml 파일을 업로드하여
스택을 생성한다.
여기서 주의할 점은 공유 계정으로 실습 시 아래 [ClusterBaseName]이 같으면 VPC 생성 시 오류가 발생하므로
이 부분은 각자 다르게 생성한다.
ClusterBaseName이 동일하면 아래와 같이 CREATE_FAILED가 뜬다.
검토까지 변경할 사항은 없으며 그대로 스택을 생성해주면 된다.
[VPC] 서비스로 접속하여 생성된 VPC를 확인한다.
EKS 클러스터 구축
클러스터를 구축하기에 앞서,
[CloudFormation] 서비스에서 해당 스택을 클릭한 후 [출력] 부분에서 WorkerSubnets의 값을 따로 복사해둔다.
이번 실습은 Cloud9 서비스를 통해 IDE 환경을 생성한 후 실습을 진행한다.
[Cloud9] 서비스에 접속한 후 [Create Environment]를 클릭한다.
[Name]을 지정하고 [Next step]을 클릭한다.
아래 [Network settings]에서 VPC를 조금 전 생성했던 VPC로 선택하고 WorkerSubnet1을 클릭한 후
[Next step] -> [Create Environment]를 클릭한다.
생성이 완료되면 아래와 같이 IDE 환경이 하나 생성된다.
이제 이 곳에서 나머지 실습을 진행한다.
먼저 eksctl 명령어를 설치해보자.
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv -v /tmp/eksctl /usr/local/bin
혹은 Windows 사용자라면, 아래 링크를 통해 amd64.zip 파일을 다운받고 압축해제 후 환경변수를 등록해준다.
https://github.com/weaveworks/eksctl/releases
설치가 완료되면 eksctl version을 통해 명령어를 확인한다.
이제 EKS 클러스터를 구축하는 과정이다.
eksctl create cluster \
> --vpc-public-subnets [아까 복사해둔 WorkerSubnets 값] \
> --name eks-work-cluster \
> --region ap-northeast-2 \
> --version 1.19 \
> --nodegroup-name eks-work-nodegroup \
> --node-type t3.small \
> --nodes 2 \
> --nodes-min 2 \
> --nodes-max 5
클러스트가 구축되는 과정은 CloudFormation 스택에서 확인할 수 있다.
마찬가지로 Worker 노드를 생성하는 스택도 확인한다.
클러스터를 구축하는 과정에서 아래와 같이 오류가 발생하고 워커 노드 구축이 되지 않는 경우가 있다.
이럴 땐 access key를 다운 받고, aws configure 명령어를 통해 aws 계정 설정을 한 후에 스택을 삭제하고
다시 구축해보길 권한다.
kubectl를 설치해준다.
curl -LO https://dl.k8s.io/release/v1.22.0/bin/windows/amd64/kubectl.exe
sudo curl --silent --location -o /usr/local/bin/kubectl "https://amazon-eks.s3.us-west-2.amazonaws.com/1.19.6/2021-01-05/bin/linux/amd64/kubectl"
sudo chmod +x /usr/local/bin/kubectl
이제 EKS 클러스터가 잘 구축됐는지 접속 테스트를 해보자.
kubectl get nodes 명령어를 통해 Workder Node를 확인한다.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-0-238.ap-northeast-2.compute.internal Ready <none> 6m1s v1.19.6-eks-49a6c0
ip-192-168-2-12.ap-northeast-2.compute.internal Ready <none> 5m57s v1.19.6-eks-49a6c0
이상으로 EKS 클러스터 구축이 완료됐다.
이제 Pod를 생성해보자.
레포지토리로 이동하여 eks-env/02_nginx_k8s.yaml 파일을 이용하여 파드를 생성한다.
$ kubectl apply -f 02_nginx_k8s.yaml
pod/nginx-pod created
파드가 생성된 것을 확인한다.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-pod 1/1 Running 0 92s
아래와 같이 포트포워딩을 해준다.
이렇게 설정해놓으면 local에서 8080번 포트로 접속할 때 nginx-pod의 80포트로 접속이 가능하다.
$ kubectl port-forward nginx-pod 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
localhost:8080을 접속하면 nginx의 Welcome를 확인할 수 있다.
생성했던 파드는 다시 지워주도록 한다.
$ kubectl delete -f 02_nginx_k8s.yaml
pod "nginx-pod" deleted
데이터베이스 환경 구축
이제 예제 애플리케이션에서 사용할 데이터베이스 환경을 구축해보자.
/eks-env 경로의 10_rds_ope_cfn.yaml 파일을 이용하여 CloudFormation에서 새로운 스택을 생성한다.
[스택 이름]과 [EksWorkVPC]를 선택하고 OpeServerRouteTable은 처음 생성했던 eks-work-base의 RouteTable 값을 입력한다.
검토 후 [스택 생성]을 통해 새로운 스택을 생성한다.
생성이 완료된 후 RDS 서비스에서 데이터베이스가 생성된 것을 확인한다.
RDS 인스턴스가 생성됐으니 이제 배스천 호스트를 접속해보자.
[AWS Systems Manager]에서 [세션 관리자]를 클릭한 후 인스턴스를 선택한 후 [세션 시작]을 클릭한다.
아래는 배스천 호스트의 콘솔 화면이다.
배스천 호스트에 Git과 PostgreSQL 클라이언트를 설치해준다.
sh-4.2$ sudo yum install -y git
sh-4.2$ sudo amazon-linux-extras install -y postgresql11
깃허브에서 레포지토리를 클론한다.
sh-4.2$ pwd
/home/ssm-user
sh-4.2$ git clone https://github.com/dybooksIT/k8s-aws-book.git
sh-4.2$ ls
k8s-aws-book
CloudFormation 스택에서 RDS의 엔드포인트 주소를 확인한다.
[Secrets Manager] 서비스에서 RDS의 사용자 관리자 비밀번호를 확인한다.
[보안 암호 값 검색]을 클릭한 후 "password" 항목을 확인하면 된다.
이제 세션으로 돌아와서 데이터베이스 사용자를 생성한다.
여기서 Enter password for new role: 에는 RdsUserSecret의 패스워드를,
마지막 Password는 RdsMasterSecret의 패스워드를 입력한다.
sh-4.2$ createuser -d -U eksdbadmin -P -h eks-work-db1.cem4tfbk3l9s.ap-northeast-2.rds.amazonaws.com mywork
Enter password for new role:
Enter it again:
Password:
sh-4.2$
데이터베이스를 생성한다.
Password는 mywork의 사용자 비밀번호를 입력한다.
sh-4.2$ createdb -U mywork -h eks-work-db1.cem4tfbk3l9s.ap-northeast-2.rds.amazonaws.com -E UTF8 myworkdb
Password:
아래와 같이 데이터베이스에 접속한다.
sh-4.2$ psql -U mywork -h eks-work-db1.cem4tfbk3l9s.ap-northeast-2.rds.amazonaws.com myworkdb
Password for user mywork:
psql (11.12)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
myworkdb=>
미리 준비한 SQL 스크립트 파일을 이용하여 예제 데이터를 불러와보자.
\i는 postgreSQL에서 외부 스크립트를 불러오는 명령어이다.
myworkdb=> \i k8s-aws-book/backend-app/scripts/10_ddl.sql
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
myworkdb=> \i k8s-aws-book/backend-app/scripts/20_insert_sample_data.sql
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
위와 같이 정상적으로 데이터 조회가 가능한 것을 확인한 후 \q를 통해 데이터베이스에서 로그아웃 한다.
API 애플리케이션 빌드와 배포
이제 API 애플리케이션을 빌드하고 배포할 차례이다.
아래 과정은 그래들을 이용하여 미리 정의한 소스 코드를 빌드하는 과정이다.
sh-4.2$ cd k8s-aws-book/backend-app
sh-4.2$ sudo chmod 755 ./gradlew
sh-4.2$ ./gradle
gradle/ gradlew
sh-4.2$ ./gradlew clean build
Downloading https://services.gradle.org/distributions/gradle-5.2.1-bin.zip
...................................................................................
Welcome to Gradle 5.2.1!
Here are the highlights of this release:
- Define sets of dependencies that work together with Java Platform plugin
- New C++ plugins with dependency management built-in
- New C++ project types for gradle init
- Service injection into plugins and project extensions
For more details see https://docs.gradle.org/5.2.1/release-notes.html
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :test
2021-08-16 15:22:30.873 [SpringContextShutdownHook] INFO o.s.s.c.ThreadPoolTaskExecutor - Shutting down ExecutorService 'applicationTaskExecutor'
2021-08-16 15:22:30.875 [SpringContextShutdownHook] INFO o.s.o.j.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
2021-08-16 15:22:30.880 [SpringContextShutdownHook] INFO o.s.s.c.ThreadPoolTaskExecutor - Shutting down ExecutorService 'applicationTaskExecutor'
2021-08-16 15:22:30.886 [SpringContextShutdownHook] INFO o.s.o.j.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
BUILD SUCCESSFUL in 3m 25s
14 actionable tasks: 12 executed, 2 up-to-date
위 과정을 수행하면 다음 작업이 수행된다.
1. 의존성 라이브러리 다운로드
2. 프로그램 컴파일
3. 테스트 프로그램 컴파일
4. 테스트 실행
5. 프로그램 실행용 JAR 파일 생성
이제 컨테이너 이미지를 생성해준다.
docker Desktop을 실행한 후 아래 명령어를 통해 컨테이너 이미지를 생성해준다.
docker build -t k8sbook/backend-app:1.0.0 --build-arg JAR_FILE=build/libs/backend-app-1.0.0.jar .
이미지 리스트를 확인해보면 방금 다운받은 k8sbook/backend-app 이미지가 존재한다.
현재 이 컨테이너 이미지는 로컬 환경에 존재한다.
이 이미지를 EKS로 배포하기 위해 먼저 이 컨테이너 이미지를 저장할 컨테이너 레지스트리가 필요하다.
AWS ECR 서비스를 사용해보자.
이제 컨테이너 이미지를 푸시할 차례이다.
푸시하기 전 docker login 명령으로 ECR에 인증을 수행해야 한다.
명령어는 아래와 같으며 계정 ID 의 경우 AWS 관리 콘솔에서 내 계정 옆에 있는 12자리 숫자이다.
$ aws ecr get-login-password --region ap-northeast-2 | \
> docker login --username AWS --password-stdin \
> [계정 id].dkr.ecr.ap-northeast-2.amazonaws.com
Login Succeeded
푸시는 다음 순서로 진행된다.
1. 이미지에 docker tag 명령어를 통한 태그 설정
2. 해당 태그에 대해 docker push 명령 실행
먼저 tag를 설정한다.
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker101tutorial latest 5b593be173e1 16 minutes ago 28.2MB
k8sbook/backend-app 1.0.0 5b593be173e1 16 minutes ago 28.2MB
alpine/git latest b8f176fa3f0d 2 months ago 25.1MB
$ docker tag k8sbook/backend-app:1.0.0 \
> [계정 id].dkr.ecr.ap-northeast-2.amazonaws.com/ljj-k8sbook/backend-app:1.0.0
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker101tutorial latest 5b593be173e1 17 minutes ago 28.2MB
[계정 id].dkr.ecr.ap-northeast-2.amazonaws.com/ljj-k8sbook/backend-app 1.0.0 5b593be173e1 17 minutes ago 28.2MB
k8sbook/backend-app 1.0.0 5b593be173e1 17 minutes ago 28.2MB
alpine/git latest b8f176fa3f0d 2 months ago 25.1MB
docker push 명령어를 통해 ECR에 컨테이너 이미지를 푸시한다.
$ docker push [계정 ID].dkr.ecr.ap-northeast-2.amazonaws.com/ljj-k8sbook/backend-app:1.0.0
ECR 레지스터를 확인해보면 PUSH한 컨테이너 이미지가 업로드돼있다.
이제 EKS 클러스터에 API 애플리케이션을 배포해보자.
이를 위해 다음 단계를 진행한다.
1. 네임스페이스 생성
2. kubeconfig에 네임스페이스 반영
3. DB 접속용 시크릿 등록
4. API 애플리케이션 배포
5. API 애플리케이션 외부 공개
쿠버네티스는 Cluster 하나를 네임스페이스라는 논리적인 단위로 구분하여 관리한다.
먼저 네임스페이스를 생성한다.
$ kubectl apply -f 20_create_namespace_k8s.yaml
namespace/eks-work created
이제 kubeconfig 파일에 네임스페이스를 반영해보자.
$ kubectl config set-context eks-work --cluster jj-eks-work-cluster.ap-northeast-2.eksctl.io --user mzmz01@jj-eks-work-cluster.ap-northeast-2.eksctl.io --namespace eks-work
$ kubectl config use-context eks-work
Switched to context "eks-work".
$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* eks-work jj-eks-work-cluster.ap-northeast-2.eksctl.io mzmz01@jj-eks-work-cluster.ap-northeast-2.eksctl.io eks-work
mzmz01@jj-eks-work-cluster.ap-northeast-2.eksctl.io jj-eks-work-cluster.ap-northeast-2.eksctl.io mzmz01@jj-eks-work-cluster.ap-northeast-2.eksctl.io
DB 접속용 시크릿을 등록한다.
시크릿은 클러스터 안에 비밀번호 등을 보관하기 위한 구성 요소이다.
여기서 필요한 정보는 RDS의 엔드포인트 주소와 RdsUserSecret의 password이다.
$ DB_URL=jdbc:postgresql://[RDS 엔드포인트 주소]/myworkdb \
> DB_PASSWORD='사용자 password' \
> envsubst < 21_db_config_k8s.yaml.template | \
> kubectl apply -f -
secret/db-config created
이제 API 애플리케이션을 배포할 차례이다.
$ ECR_HOST=[계정 id].dkr.ecr.ap-northeast-2.amazonaws.com \
> envsubst < 22_deployment_backend-app_k8s.yaml.template | \
> kubectl apply -f -
deployment.apps/backend-app created
위 명령어를 통해 배포가 실행되었다.
kubectl apply 명령으로 적용한 파일은 Deployment라는 오브젝트를 생성했다.
디플로이먼트는 컨테이너를 배포하는 것이고, 디플로이먼트를 생성하면 -> ReplicaSet -> Pod가 생성된다.
디플로이먼트 이외에 Replicaset과 Pod가 생성된 것을 확인한다.
API 애플리케이션 배포는 끝났지만 아직 클러스터 외부에서는 API 호출이 불가능하다.
쿠버네티스의 Pod를 외부에 공개하기 위해 Service가 준비되어 있다.
이번 실습에선 LoadBalancer라는 서비스 타입을 이용한다.
- 23_service_backend-app_k8s.yaml
apiVersion: v1
kind: Service
metadata:
name: backend-app-service
spec:
type: LoadBalancer
selector:
app: backend-app
ports:
- protocol: TCP
port: 8080
targetPort: 8080
클러스터 외부에서 접속하기 위한 EXTERNAL-IP가 할당된 로드밸런서 서비스가 동작 중인 것을 확인한다.
로드밸런서와 연결된 인스턴스 상태를 확인하면 "InService"로 돼있는 것을 확인할 수 있다.
curl 명령어로 API에 접속해보자.
아래와 같이 "status":"OK"가 출력되면 정상적으로 API에 접속이 가능한 것이다.
$ curl -s http://a62ff8af8e6ec4b64a53b032addd0af2-524911502.us-west-2.elb.amazonaws.com:8080/health
{"status":"OK"}
이제 Frontend 애플리케이션을 빌드하고 배포해보자.
mzmz01:~/k8s-aws-book (master) $ cd frontend-app/
mzmz01:~/k8s-aws-book/frontend-app (master) $ npm install
애플리케이션 빌드를 실행한다.
mzmz01:~/k8s-aws-book/frontend-app (master) $ BASE_URL=http://a62ff8af8e6ec4b64a53b032addd0af2-524911502.us-west-2.elb.amazonaws.com:8080 npm run build
> frontend-app@1.0.0 build /home/ec2-user/k8s-aws-book/frontend-app
> nuxt build
예제 애플리케이션의 프론트엔드는 S3와 Cloudfront를 웹 서버로 사용한다.
S3 버킷과 CloudFront 배포를 생성한다.
aws sync 명령어를 통해 콘텐츠를 업로드한다.
아래 명령어를 통해 CloudFront의 캐시를 무효화한다.
CloudFront는 콘텐츠를 캐시하여 S3에 접속하지 않고 사용자 요청을 처리한다.
빠른 콘텐츠 전송이 가능하다는 장점이 있지만, S3에 새로운 콘텐츠가 업로드돼도 CloudFront에는
오래된 콘텐츠가 캐시돼있기 때문에 사용자는 최신 콘텐츠를 수신받을 수 없다.
기존 배포에 콘텐츠를 업로드하는 경우 바로 콘텐츠를 적용하려면 아래 명령어를 통해 캐시 무효화를 해야 한다.
이제 웹 페이지를 접속해보자.
아래 URL을 클릭한다.
404 Not Fount가 표시되면 Home page를 클릭한다.
이제 배치 애플리케이션을 빌드하고 배포해보자.
mzmz01:~/k8s-aws-book/batch-app (master) $ ./gradlew clean build
BUILD SUCCESSFUL in 1m 8s
14 actionable tasks: 13 executed, 1 up-to-date
docker 빌드 시 아래와 같이 에러가 나는 경우는 EBS 볼륨 용량이 부족하기 때문이다.
디스크 용량을 확인해보자.
/dev/xvda1 파티션이 100%로 꽉 차있기 때문에 위 에러가 발생한다.
mzmz01:~/k8s-aws-book/batch-app (master) $ df -k
Filesystem 1K-blocks Used Available Use% Mounted on
devtmpfs 485128 0 485128 0% /dev
tmpfs 503444 0 503444 0% /dev/shm
tmpfs 503444 576 502868 1% /run
tmpfs 503444 0 503444 0% /sys/fs/cgroup
/dev/xvda1 10473452 10383732 89720 100% /
[볼륨] 탭에서 볼륨을 확장한 후 아래 과정을 통해 파티션을 확장하자.
mzmz01:/var/spool $ sudo growpart /dev/xvda 1
CHANGED: partition=1 start=4096 old: size=20967391 end=20971487 new: size=41938911 end=41943007
mzmz01:/var/spool $ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 20G 0 disk
└─xvda1 202:1 0 20G 0 part /
mzmz01:~/k8s-aws-book/batch-app (master) $ sudo xfs_growfs /dev/xvda1
meta-data=/dev/xvda1 isize=512 agcount=6, agsize=524159 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1 spinodes=0
data = bsize=4096 blocks=2620923, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0 ftype=1
log =internal bsize=4096 blocks=2560, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
data blocks changed from 2620923 to 5242363
mzmz01:~/k8s-aws-book/batch-app (master) $ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 474M 0 474M 0% /dev
tmpfs 492M 0 492M 0% /dev/shm
tmpfs 492M 544K 492M 1% /run
tmpfs 492M 0 492M 0% /sys/fs/cgroup
/dev/xvda1 20G 10G 11G 50% /
다시 빌드하면 정상적으로 빌드되는 것을 확인할 수 있다.
mzmz01:~/k8s-aws-book/batch-app (master) $ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
k8sbook/batch-app 1.0.0 2520aafd2596 4 minutes ago 753MB
image의 태그를 설정한다.
이미지를 푸시한다.
배치 애플리케이션이 사용할 S3를 만들기 위해 새로운 CloudFormation 스택을 생성해준다.
이제 배치 애플레케이션 설정값을 저장하는 ConfigMap을 생성한다.
여기엔 S3 버킷 이름, S3 디렉터리 이름 등을 보관한다.
$ BUCKET_SUFFIX=ljj \
> envsubst < 41_config_map_batch_k8s.yaml.template | kubectl apply -f -
configmap/batch-app-config created
배치 애플리케이션에서는 S3에서 파일을 가져와 이용한다.
이 S3 버킷은 특정 사용자만 접속할 수 있도록 설정하므로 시크릿을 생성하여 관리한다.
[Systems Manager]에서 [파라미터 스토어]를 클릭한다.
아래 명령어를 통해 시크릿을 생성해준다.
$ AWS_ACCESSKEY=[액세스 키] \
> AWS_SECRETKEY=[비밀 액세스 키] \
> envsubst < 42_batch_secrets_k8s.yaml.template | kubectl apply -f -
secret/batch-secret-config created
입력 파일을 업로드한다.
$ aws s3 sync batch-app/sample_data/normal/ s3://eks-work-batch-ljj/locationData --delete --include "*" --acl public-read
배치 애플리케이션을 배포하기 전 DB와 S3 버킷을 확인해준다.
[세션 관리자]에서 [세션 시작]을 클릭한다.
DB에 접속 후 테이블 내용을 확인한다.
S3 버킷의 work-batch- 로 이동하여 csv 파일이 업로드 돼있는 것을 확인한다.
이제 Cronjob이라는 쿠버네티스 리소스를 이용하여 배치 애플리케이션을 배포한다.
$ ECR_HOST=371156277055.dkr.ecr.us-west-2.amazonaws.com \
> envsubst < eks-env/43_cronjob_k8s.yaml.template | \
> kubectl apply -f -
cronjob.batch/batch-app created
$ kubectl get cronjob
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
batch-app */5 * * * * False 0 <none> 76s
시간이 지나면 새로운 pod가 생성된다.
이러한 파드는 5의 배수 분만큼 시간이 지날 때마다 하나씩 생성되고 완료된다.
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/backend-app-5d6886d769-mhr82 1/1 Running 0 5h48m
pod/backend-app-5d6886d769-nrrvp 1/1 Running 0 5h48m
pod/batch-app-1629278400-7zqb4 0/1 Completed 0 2m1s
배치 애플리케이션을 싱행한 후에 DB와 S3 버킷을 확인한다.
location 테이블을 확인하면 데이터가 추가로 생성된 것을 확인할 수 있다.
S3 버킷을 확인하면 CSV 파일이 사라져있는데,
이 파일은 배치 애플리케이션에 사용된 후 삭제돼있는 상태이다.
'Kubernetes' 카테고리의 다른 글
Kubernetes 오브젝트를 이용한 Apache 웹 서버 구성하기 (0) | 2021.09.07 |
---|---|
Kubeadm을 이용한 쿠버네티스 클러스터 배포하기 (0) | 2021.09.07 |
Kubernetes 인강 정리 (0) | 2021.08.20 |
The connection to the server ~ 에러 발생 해결 (0) | 2021.07.30 |
Why Kubernetes? (0) | 2021.07.27 |