본문 바로가기
개발/k8s

[EKS] 3티어 아키텍처 (RDS, Redis, 인그레스, tls, 도메인연결)

by parkkingcar 2024. 1. 8.

 

AWS EKS를 이용하여 쿠버네티스 환경의 3티어 아키텍처를 구성합니다. 구성환경은 VScode를 사용합니다. 

 

 

먼저, 로컬에서 kubectl 명령을 사용하기 위해 eksctl을 설치해야합니다. 아래 블로그를 참고하여 진행합니다.

 

04_AWS CLI와 PowerShell을 이용한 EKS 관리

오늘은 AWS의 Command Line Interface와 윈도우의 PowerShell을 이용한 EKS 관리 시간입니다. 당연히 AWS CLI v2 설치와 함께 kubectl, iam_authenticator 설치 및 kubeconfig 파일에 리전과 클러스터를 업데이트하는 작업

virtualtech.tistory.com

 

 

이제 쿠버네티스 클러스터를 생성합니다. 아래명령을 입력하여 노드 3개를 가지고 psy라는 키페어를 이용해 ssh 접속이 가능한 클러스터를 서울 리전에 생성합니다.

eksctl.exe create cluster --name 클러스터이름 --region ap-northeast-2 --with-oidc --ssh-public-key psy --nodes 3 --node-type t3.medium --node-volume-size=20 --managed

 

 

 

AWS EKS에서 인그레스를 사용하기 위해 인그레스 컨트롤러를 직접 생성해야 합니다. 쿠버네티스 클러스터가 생성되면 아래 순서대로 인그레스 컨트롤러를 생성합니다.

 

Amazon EKS web application workshop

Building simple web application using Amazon EKS. This workshop covers from creating eks cluster to application's life cycle.

catalog.us-east-1.prod.workshops.aws

 

먼저 클러스터에 대한 IAM OIDC(OpenID Connect) identity Provider를 서울리전에 생성합니다.

eksctl utils associate-iam-oidc-provider --region ap-northeast-2 --cluster 클러스터이름 --approve

 

AWS Load Balancer Controller에 부여할 IAM Policy를 생성하는 작업을 수행합니다.

curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/docs/install/iam_policy.json

aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json

 

AWS Load Balancer Controller를 위한 ServiceAccount를 생성합니다. 클러스터 이름과  arn부분에 유저 id를 입력합니다.

eksctl create iamserviceaccount --cluster=클러스터이름 --namespace=kube-system --name=aws-load-balancer-controller --attach-policy-arn=arn:aws:iam::111111111111:policy/AWSLoadBalancerControllerIAMPolicy --override-existing-serviceaccounts --approve

 

클러스터에 컨트롤러를 추가합니다. Ingress Controller를 설치하기 전에 먼저 Cert-manager를 설치합니다.

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml

 

Load balancer controller yaml 파일을 다운로드 합니다. 

curl -o v2_5_4_full.yaml https://github.com/kubernetes-sigs/aws-load-balancer-controller/releases/download/v2.5.4/v2_5_4_full.yaml

 

v2_5_4_full.yaml  파일에 있는 ServiceAccount 섹션을 제거합니다. 아래 명령이 실행되지 않으면 직접 yaml에서 다음 부분을 제거합니다. 

sed -i.bak -e '596,604d' ./v2_5_4_full.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/name: aws-load-balancer-controller
  name: aws-load-balancer-controller
  namespace: kube-system
---

 

v2_5_4_full.yaml 파일의 Deployment 섹션의 my-cluster를 현재 eks 실습에 사용되는 클러스터 이름으로 변경합니다.

아래 명령이 실행되지 않으면 직접 yaml에서 클러스터이름 부분을 입력합니다.

sed -i.bak -e 's|your-cluster-name|클러스터이름|' ./v2_5_4_full.yaml

 

AWS Load Balancer controller 파일을 배포합니다.

kubectl apply -f v2_5_4_full.yaml

 

IngressClass 및 IngressClassParams 매니페스트를 다운로드합니다. 그리고 클러스터에 매니페스트를 적용합니다.

curl -o v2_5_4_ingclass.yaml https://github.com/kubernetes-sigs/aws-load-balancer-controller/releases/download/v2.5.4/v2_5_4_ingclass.yaml

kubectl apply -f v2_5_4_ingclass.yaml

 

컨트롤러 설치를 확인합니다.

kubectl get deployment -n kube-system aws-load-balancer-controller

 

클러스터 노드그룹에 EC2FullAccess 권한을 부여합니다. 노드그룹의 역할에 접속하여 권한을 추가합니다.

 

 

 

인그레스 컨트롤러를 설치하면 이제 RDS를 생성합니다. AWS 콘솔에서 RDS 서비스에서 데이터베이스 생성을 클릭합니다. mysql을 선택하고 엔진버전은 5.7.44 를 선택합니다.

 

식별자에 RDS의 이름을 입력하고 마스터 암호를 임의로 설정합니다.

 

연결정보에서  퍼블릭 엑세스를 허용하고 새로운 보안그룹을 생성합니다. 나머지 값을 기본으로 유지하고 데이터베이스를 생성합니다.

 

 

이제 사용하려는 데이터베이스, 유저 ,비밀번호, 테이블을 생성하고 특정 유저에게 작업을 위한 데이터베이스에 권한을 부여합니다. 데이터베이스에 로컬에서 접근하기 위해 보안그룹에서 3306포트의 인바운드 규칙을 허용해야 합니다.

 

로컬 터미널에서 mysql -h 데이터베이스앤드포인트 -u admin -p 명령을 이용하여 RDS에 접근합니다.

 

아래 명령을 입력하여  boot_board  데이터베이스를 생성합니다.

CREATE DATABASE boot_board;

 

아래 명령을 입력하여 유저를 생성하고 boot_board에 대한 모든 권한을 부여합니다.

CREATE USER 'suyong'@'%' IDENTIFIED BY '비밀번호';
GRANT ALL PRIVILEGES ON boot_board.* TO 'username'@'%';
FLUSH PRIVILEGES;

 

이후 아래 파일을 import하여  boot_board 데이터베이스에 필요한 테이블을 생성합니다. 

Dump.sql
0.00MB

 

 

이제 생성한 RDS에 연결되는 서비스를 생성합니다. db-svc.yaml 파일에서 externalName에 생성한 RDS의 엔드포인트를 입력합니다.  이후 apply 명령을 실행합니다.

apiVersion: v1
kind: Service
metadata:
  name: mysql-svc
spec:
  ports:
  - port: 3306
  type: ExternalName
  externalName: 데이터베이스 엔드포인트
kubectl apply -f ./db-svc.yaml

 

아래 명령을 통해 생성한 서비스에 외부IP 주소가 잘 연결되었는지 확인합니다.

kubectl get svc

 

이제 redis 파드를 생성합니다. 해당 conf파일을 이용하여 도커 이미지로 빌드하고 도커허브 저장소에 push합니다.

# Dockerfile

FROM redis

RUN mkdir /usr/local/etc/redis

COPY ./redis.conf /usr/local/etc/redis/redis.conf

CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]

redis.conf
0.09MB

 

 

해당 이미지를 사용하여 redis 디플로이먼트와 서비스, 컨피그맵을 생성합니다. 

# Redis_conf.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-conf
data:
  redis.conf: |
    bind 0.0.0.0
    protected-mode yes
    port 6379
    tcp-backlog 511
    timeout 0
    tcp-keepalive 300
    daemonize no
    pidfile /var/run/redis_6379.pid
    loglevel notice
    logfile ""
    databases 16
    always-show-logo no
    set-proc-title yes
    proc-title-template "{title} {listen-addr} {server-mode}"
    stop-writes-on-bgsave-error yes
    rdbcompression yes
    rdbchecksum yes
    dbfilename dump.rdb
    rdb-del-sync-files no
    dir ./
    replica-serve-stale-data yes
    replica-read-only yes
    repl-diskless-sync no
    repl-diskless-sync-delay 5
    repl-diskless-load disabled
    repl-disable-tcp-nodelay no
    replica-priority 100
    acllog-max-len 128
    requirepass suyong # 여기에 redis에서 사용할 비밀번호를 입력
    lazyfree-lazy-eviction no
    lazyfree-lazy-expire no
    lazyfree-lazy-server-del no
    replica-lazy-flush no
    lazyfree-lazy-user-del no
    lazyfree-lazy-user-flush no
    oom-score-adj no
    oom-score-adj-values 0 200 800
    disable-thp yes
    appendonly no
    appendfilename "appendonly.aof"
    appendfsync everysec
    no-appendfsync-on-rewrite no
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    aof-load-truncated yes
    aof-use-rdb-preamble yes
    lua-time-limit 5000
    slowlog-log-slower-than 10000
    slowlog-max-len 128
    latency-monitor-threshold 0
    notify-keyspace-events ""
    hash-max-ziplist-entries 512
    hash-max-ziplist-value 64
    list-max-ziplist-size -2
    list-compress-depth 0
    set-max-intset-entries 512
    zset-max-ziplist-entries 128
    zset-max-ziplist-value 64
    hll-sparse-max-bytes 3000
    stream-node-max-bytes 4096
    stream-node-max-entries 100
    activerehashing yes
    client-output-buffer-limit normal 0 0 0
    client-output-buffer-limit replica 256mb 64mb 60
    client-output-buffer-limit pubsub 32mb 8mb 60
    hz 10
    dynamic-hz yes
    aof-rewrite-incremental-fsync yes
    rdb-save-incremental-fsync yes
    jemalloc-bg-thread yes
# redis.yaml

apiVersion: v1
kind: Service
metadata:
  name: redis-svc
spec:
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: 도커이미지
        volumeMounts:
          - mountPath: /usr/local/etc/redis/
            name: conf
        imagePullPolicy: Always
        ports:
        - containerPort: 6379
      volumes:
      - name: conf
        configMap:
          name: redis-conf

 

이후 apply 명령을 실행합니다.

kubectl apply -f ./Redis_conf.yaml
kubectl apply -f ./redis.yaml

 

 

이제 스프링을 이용하여 was를 구축합니다. gradle을 통해 빌드를 하고  nginx 컨테이너로 이미지를 빌드하고 푸쉬하여 사용합니다. 아래는 속성파일 예제입니다.

# application.properties

server.port=8080

spring.main.allow-bean-definition-overriding=true

spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mysql://mysql-svc:3306/boot_board?serverTimezone=UTC
#validationQuery="select 1"
spring.datasource.hikari.username=suyong
spring.datasource.hikari.password=suyong
spring.datasource.hikari.connection-test-query=SELECT 1
#root#root
mybatis.configuration.map-underscore-to-camel-case=true

# spring.session.store-type=none
spring.session.store-type=redis
spring.redis.host=redis-svc
spring.redis.password=suyong
spring.redis.port=6379
spring.session.redis.flush-mode=on_save
spring.session.redis.namespace=spring:session

spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

 

아래는 도커 이미지 빌드를 위한 도커파일입니다.

FROM tomcat:9.0.52-jdk11-openjdk-buster

COPY my-spring-board-0.0.1-SNAPSHOT.war /usr/local/tomcat/webapps/ROOT.war

 

was 서비스와 디플로이먼트를 실행하기위한 yaml 파일입니다.

# was.yaml

apiVersion: v1
kind: Service
metadata:
  name: mywas-svc
spec:
  selector:
    app: mywas
  ports:
  - port: 8080
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mywas
spec:
  selector:
    matchLabels:
      app: mywas
  template:
    metadata:
      labels:
        app: mywas
    spec:
      containers:
      - name: mywas
        image: 도커이미지
        imagePullPolicy: Always
        ports:
        - containerPort: 8080

 

이후 apply 명령을 실행합니다.

kubectl apply -f ./was.yaml

 

 

이제 web 파드를 실행합니다.  아래는 web 파드로 접속할 때 was 파드로 리다이렉트 시키도록 nginx.conf를 구성하는 컨피그맵입니다.

# configMap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-conf
data:
  nginx.conf: |
    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log;
    pid /run/nginx.pid;
    
    # Load dynamic modules. See /usr/share/nginx/README.dynamic.
    include /usr/share/nginx/modules/*.conf;

    events {
        worker_connections 1024;
    }

    http {
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
        
        upstream backend {
        server mywas-svc:8080;
        }

        access_log  /var/log/nginx/access.log  main;

        sendfile            on;
        tcp_nopush          on;
        tcp_nodelay         on;
        keepalive_timeout   65;
        types_hash_max_size 2048;

        default_type        application/octet-stream;

        # Load modular configuration files from the /etc/nginx/conf.d directory.
        # See http://nginx.org/en/docs/ngx_core_module.html#include
        # for more information.
        include /etc/nginx/conf.d/*.conf;

        server {
            listen       80 default_server;
            listen       [::]:80 default_server;
            server_name  _;
            root         /usr/share/nginx/html/;

            # Load configuration files for the default server block.
            include /etc/nginx/default.d/*.conf;

            location / {
            proxy_pass http://backend;
            }

            error_page 404 /404.html;
                location = /40x.html {
            }

            error_page 500 502 503 504 /50x.html;
                location = /50x.html {
            }
        }


    }

 

아래는 web 파드를 실행하기 위한 디플로이먼트와 서비스입니다.

# web.yaml

apiVersion: v1
kind: Service
metadata:
  name: mysvc-web
spec:
  selector:
    app: myweb
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80 
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myweb
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myweb
  template:
    metadata:
      labels:
        app: myweb
    spec:
      containers:
      - name: myweb
        image: nginx
        volumeMounts:
        - mountPath: /etc/nginx/
          name: conf
        ports:
        - containerPort: 80
      volumes:
      - name: conf
        configMap:
          name: nginx-conf

 

 

이후 apply 명령을 실행합니다.

kubectl apply -f ./configMap.yaml
kubectl apply -f ./web.yaml

 

 

마지막으로 Ingress를 생성합니다. 인그레스를 통해 로드밸런서가 web 파드의 서비스에 접근하도록 합니다. 인그레스를 생성하기 전에 먼저 도메인을 구매해야 합니다.

 

tls 설정을 위한 인증서를 생성해야 합니다.  아래 링크를 통해 도메인을 구매합니다.

 

가비아 도메인 구입 방법(Feat. 티스토리 설정)

가비아 도메인 (2차 도메인) 구입 이유 나는 수익형 블로그를 효율적으로 키워보고 싶었기에 추가로 2차 도메인을 구입하게 되었고 그 이유는 2가지다. 우선 도메인의 기초적인 구성개념부터 알

king-father.com

 

가비아에서 구매한 도메인에 대해 AWS Route53의 DNS 서버를 사용하여 도메인을 찾도록 설정합니다. AWS 콘솔에서 Route53를 클릭하고 호스팅 영역을 생성합니다. 구매한 도메인을 입력하고 호스팅 영역을 생성합니다.

 

해당 호스팅 영역에서 아래 사진에서의 대상을 가비아에 등록해야 합니다. My 가비아에 접속합니다. 아래 네임서버 설정을 클릭하고 해당 라우팅 대상을 입력합니다.

 

다시 AWS 콘솔로 돌아와서 AWS Certificate Manager(ACM)에 접속합니다. 인증서 요청을 클릭하고 인그레스 yaml 파일의 호스트 부분의 도메인 이름을 아래 입력한 후 요청합니다.

 

아래 Route 53에서 레코드 생성을 클릭하고 생성하여 인증서 검증이 완료될때 까지 기다립니다.

 

아래는 아래는 인그래스를 생성하는 파일입니다. certificate-arn 부분에 인증서 arn을 입력합니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/scheme: internet-facing 
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:111111111111:certificate/
    alb.ingress.kubernetes.io/ssl-redirect: '443'
spec:
  tls:
    - hosts:
        - www.suyong.store
      secretName: tls-crt 
  rules:
  - host: www.suyong.store
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: mysvc-web
            port: 
              number: 80

 

이후 apply 명령을 실행합니다.

kubectl apply -f ./ingress.yaml

 

그러면 아래 명령을 통해  ingress의 ADDRESS에 로드밸런서 주소가 생성된 것을 확인할 수 있습니다.

 

위 주소를 Route 53에서 레코드 생성에서 www.suyong.store 의 값으로 입력하고 생성합니다.

 

이제  http://www.suyong.store/ 로 접속하면 https로 리다이렉트 되어 아래와 같이 화면이 표시됩니다. 

 

 

 

댓글