[Logstash on K8S] Azure SQL to ElasticSearch

Azure SQL(이하 sql)의 data를 ElasticSearch(이하 es)로 검색을 하기 위해서 sql 데이터를 es의 index로 옮겨야 한다. 데이터를 옮기는 방법은 여러가지 방법이 있지만 일반적으로 많이 쓰이는 데이터 처리 오픈소스인 Logstash를 사용하여 옮기는 방법을 알아보자.

 Logstash를 운영하는 방식도 다양하지만 간단하게 사용하려면 역시 K8S만 한 것이 없기 때문에 K8S에 구성해보자.

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: logstash-configmap-start
  namespace: search
  labels:
    task: sql-search-init    
data:
  start.sh: |
    #!/bin/bash
    curl -L -O https://go.microsoft.com/fwlink/?linkid=2203102
    tar -xvf ./sqljdbc_11.2.0.0_kor.tar.gz
    mv ./sqljdbc_11.2/enu/mssql-jdbc-11.2.0.jre11.jar ./lib/mssql-jdbc-11.2.0.jre11.jar
  • start.sh 에서는 jdbc 드라이버를 셋팅하는 작업을 수행한다. 이 링크에서 항상 최신 버전의 jdbc 드라이버를 확인하는 것을 권장한다.
apiVersion: v1
kind: ConfigMap
metadata:
  name: logstash-configmap
  namespace: search
  labels:
    task: sql-search    
data:
  logstash.yml: |
    http.host: "127.0.0.0"
    path.config: /usr/share/logstash/pipeline
  logstash.conf: 
    input {
      jdbc {
        jdbc_driver_library => "/usr/share/logstash/lib/mssql-jdbc-11.2.0.jre11.jar"
        jdbc_driver_class => "com.microsoft.sqlserver.jdbc.SQLServerDriver"
        jdbc_connection_string => "jdbc:sqlserver://{database server}:1433;database={Database name};encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;"
        jdbc_user => "{user}"
        jdbc_password => "{password}"
        statement => "{SQL Query statement};"        
        schedule => "{CRON}"
        tracking_column => "{tracking column}"
        
      }
    }    
    output {
      elasticsearch {
        hosts => "{elastic host}"
        index => "{index name}"
        document_id => "%{index id name}"
        user => elastic
        password => "{elastic password}"
      }
      stdout {
        codec => rubydebug
      }
    }
  • input
    • jdbc_connection_string 에서는 접속할 Azure SQL의 connection string을 넣어 주면된다.
    • jdbc_user SQL 접속 계정 정보 값을 넣어준다. logstash 전용 계정을 만들어 사용하는 것을 권장한다.
    • jdbc_password 계정 비밀번호 값을 넣어준다.
    • statement index에 담을 table 혹은 작성한 query를 넣어 준다.
    • schedule 얼마 만큼 반복 작업을 할 것인지 스케줄을 정한다. 표기는 CRON을 사용한다.
    • tracking_column 데이터 변경에 대한 기준이 되는 column을 넣어 준다 (ex. timestemp)
  • output
    • hosts elastic host 정보를 넣어준다.
    • index SQL정보를 저장할 index 이름
    • document_id 인덱스에 저장할 문서를 식별할 키 값
    • password elstic에 접속할 비밀번호

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sql-logstash
  namespace: search
spec:
  replicas: 1
  selector:
    matchLabels:
      task: sql-search      
  template:
    metadata:
      labels:
        task: sql-search        
    spec:
      containers:
      - name: logstash
        image: docker.elastic.co/logstash/logstash:8.4.2
        ports:
          - containerPort: 5044
        imagePullPolicy: Always
        volumeMounts:
        - mountPath: /usr/share/logstash/config
          name: config-volume
        - mountPath: /usr/share/logstash/pipeline
          name: logstash-pipeline-volume
        - mountPath: /start
          name: start
        lifecycle:
          postStart:
            exec:
              command:
               - /start/start.sh
      volumes:
      - name: config-volume
        configMap:
          name: logstash-configmap
          items:
          - key: logstash.yml
            path: logstash.yml
      - name: logstash-pipeline-volume
        configMap:
          name: logstash-configmap
          items:
          - key: logstash.conf
            path: logstash.conf
      - name: start
        configMap:
          name: logstash-configmap-start
          defaultMode: 0777
      securityContext:
        fsGroup: 101

Service

apiVersion: v1
kind: Service
metadata:
  labels:
    task: sql-search
    kubernetes.io/name: logstash
  name: sql-logstash
  namespace: search
spec:
  ports:
  - port: 5000
    targetPort: 5000
  selector:
    k8s-app: logstash

Configmap 부터 Service 까지 순서대로 모두 K8S에 적용해 주면 된다.
(여러 개의 파일로 보는 것이 귀찮다면 하나의 yaml에 모두 넣어서 한번에 실행해도 된다.)

Calculating DTU in AWS RDS for MSSQL

On-Prem이나 VM 형태로 사용하던 MSSQL Server를 Azure SQL로 Migration을 한다고 가정해보자. 기존에 사용하던 처리 성능에 맞춰 Cloud 상에 알맞은 수준의 리소스를 준비하고 옮겨야 할 것이다.

기존에 사용하던 SQL Server는 CPU와 Memory, Disk I/O등 Hardware적인 요소로 처리 성능을 나타낸다. 하지만 Azure SQL에서는 DTU(Database Throughput Unit)라는 단위를 사용하여 처리 성능을 나타내기 때문에 성능 비교가 쉽지가 않다.

DTU는 Hardware 성능 요소인 CPU, Disk I/O와 Database Log flush 발생양을 이용하여서 계산해낸 단위이다. 때문에 MSSQL Server에서 각 Metric들을 추출해 낼 수 있다면, DTU를 계산해 낼 수 있다. DTU 계산은 이미 Azure DTU Calculator라는 계산기가 제공되고 있기 때문에 추출해낸 Metric 값들을 설명대로 잘 넣어 주기만 하면 쉽게 확인해 볼 수 있다.

Metric을 추출하는 방법은 대상이 되는 SQL Server에서 DTU Calculator에서 제공하는 PowerShell Script를 관리자 권한으로 실행시켜 주기만 하면 된다. Script는 CPU, Disk I/O, Database Log flush에 대한 Metric들을 DTU Calculator에서 요구하는 형식으로 추출해준다. 때문에 일반적으로 On-Prem이나 VM 형태로 MSSQL Server를 사용하고 있다면, 계산해 내는데 별다른 어려움이 없을 것이다.

하지만 AWS RDS for MSSQL 같은 경우 MSSQL Server를 AWS에서 Cloud Service로 제공하기 위해 Wrapping한 서비스다보니 SQL Server에 대한 관리자 권한을 얻을 수가 없다. 이러한 이유 때문에 DTU Calculator에서 제공하는 PowerShell Script를 사용할 수 가 없다.

이런 경우, DTU 계산에 필요한 Metric들을 AWS에서 별도로 추출해야 한다. AWS에는 Cloudwatch라는 서비스를 제공하고 있는데, AWS 서비스들에 대한 Performance Metric을 기록하고 제공하는 역할을 한다.

Cloudwatch에서 metric을 추출하기 위해서는 AWS CLI를 사용하는 것이 편리하다. AWS Console의 우측 상단에 있는 CLI 실행 아이콘을 눌러 AWS CLI를 실행해 보자.

AWS CLI가 실행 되었다면, 아래 조회 명령어(list-metrics)를 실행시켜 제공되는 metric들에 대한 정보를 확인해 보자.

aws cloudwatch list-metrics --namespace AWS/RDS --dimensions Name=DBInstanceIdentifier,Value=[RDS for MSSQL Name]

잘 실행되었다면, cloudwatch에서 제공하는 Metric 리스트들을 확인 할 수 있다. 리스트 중 DTU계산에 필요한 CPU Processor Time과 Disk Reads/sec, Writes/sec에 값에 해당 하는 Metric 다음과 같다. (참고로, Log Bytes Flushed/sec에 해당하는 metric은 제공되지 않는다.)

{
    "Namespace": "AWS/RDS",
    "MetricName": "ReadIOPS",
    "Dimensions": [
        {
            "Name": "DBInstanceIdentifier",
            "Value": "[RDS for MSSQL Name]"
        }
    ]
},
{
    "Namespace": "AWS/RDS",
    "MetricName": "WriteIOPS",
    "Dimensions": [
        {
            "Name": "DBInstanceIdentifier",
            "Value": "[RDS for MSSQL Name]"
        }
    ]
},
{
    "Namespace": "AWS/RDS",
    "MetricName": "CPUUtilization",
    "Dimensions": [
        {
            "Name": "DBInstanceIdentifier",
            "Value": "[RDS for MSSQL Name]"
        }
    ]
}

이제, 필요한 metric 정보에 대해서 확인 하였으니 수집해보자. 아래 metric 수집 명령어(get-metric-statistics)를 사용하면 수집된 metric정보가 csv파일로 저장된다.

aws cloudwatch get-metric-statistics --namespace AWS/RDS --dimensions Name=DBInstanceIdentifier,Value=[RDS for MSSQL Name] --metric-name=CPUUtilization --period 3600 --statistics Average --start-time 2021-01-01T00:00:00.000Z --end-time 2021-02-01T00:00:00.000Z | jq -r '.Datapoints[] | [.Timestamp, .Average, .Unit] | @csv' | cat > cpu.csv

aws cloudwatch get-metric-statistics --namespace AWS/RDS --dimensions Name=DBInstanceIdentifier,Value=[RDS for MSSQL Name] --metric-name=ReadIOPS --period 3600 --statistics Average --start-time 2021-01-01T00:00:00.000Z --end-time 2021-02-01T00:00:00.000Z | jq -r '.Datapoints[] | [.Timestamp, .Average, .Unit] | @csv' | cat > read.csv

aws cloudwatch get-metric-statistics --namespace AWS/RDS --dimensions Name=DBInstanceIdentifier,Value=[RDS for MSSQL Name] --metric-name=WriteIOPS --period 3600 --statistics Average --start-time 2021-01-01T00:00:00.000Z --end-time 2021-02-01T00:00:00.000Z | jq -r '.Datapoints[] | [.Timestamp, .Average, .Unit] | @csv' | cat > write.csv

생성한 csv 파일은 AWS CLI Storage에 저장 되어있다. 우측 상단의 Actions > Download File 메뉴를 사용하면 로컬 환경으로 다운 받을 수 있다.

3개의 파일 모두 다운 받아서 DTU 계산에 필요한 형식으로 맞춰준다.

  • % Processor Time = CPUUtilization
  • Disk Reads/sec = ReadIOPS
  • Disk Writes/sec = WriteIOPS
  • Log Bytes Flushed/sec에 해당하는 데이터가 없기때문에 0으로 넣어준다.

그림과 같은 형태로 구성될 것이다.

RDS for MSSQL metrics for DTU Calculator

이제, 한땀 한땀 준비한 데이터를 Azure DTU Calculator에 넣어 주기만 하면 되는데 한가지 주의할 점이 있다. 우리가 얻은 데이터는 RDS for MSSQL Server에 대한 정보이다. 때문에 여러 DB Instance에 대한 계산을 하는 Elastic Database 메뉴를 선택하여 계산 하도록 해야한다.

계산이 끝나면 아래와 같이 나오는데 이 결과를 통해서 AWS RDS에서 사용하던 성능 수준을 Azure SQL에서 그대로 사용하기 위해서 구성 해야하는 Service Tire/Performance Level을 확인 할 수 있다.

DTU Result

Azure DevOps 개요

DevOps 란?

데브옵스(DevOps)는 소프트웨어의 개발(Development)과 운영(Operations)의 합성어로서, 소프트웨어 개발자와 정보기술 전문가 간의 소통, 협업 및 통합을 강조하는 개발 환경이나 문화를 말한다.

주로 아래 그림과 같이 개발과 운영간의 연속적인 사이클로 설명할 수 있다.

이를 통해서 얻는 장점은 다음과 같다.

  1. 신속한 제공
    • 릴리스의 빈도와 속도를 개선하여 제품을 더 빠르게 혁신하고 향상할 수 있다. 새로운 기능의 릴리스와 버그 수정 속도가 빨라질수록 고객의 요구에 더 빠르게 대응할 수 있다.
  2. 안정성
    • 최종 사용자에게 지속적으로 긍정적인 경험을 제공하는 한편 더욱 빠르게 안정적으로 제공할 수 있도록, 애플리케이션 업데이트와 인프라 변경의 품질을 보장할 수 있다.
  3. 협업 강화
    • 개발자와 운영 팀은 긴밀하게 협력하고, 많은 책임을 공유할 수 있도록, 워크플로를 결합한다. 이를 통해 비효율성을 줄이고 시간을 절약할 할 수 있다.
  4. 보안
    • 제어를 유지하고 규정을 준수하면서 신속하게 진행할 수 있다. 자동화된 규정 준수 정책, 세분화된 제어 및 구성 관리 기술을 사용할 수 있다.

그럼, DevOps를 실현하기 위해서는 어떻게 해야 하는가?

DevOps를 실현하기 위해서는 CI(Continuous Integration)/CD(Continuous Deployment(Delivery))라는 2가지 작업을 해야 한다.

CI(Continuous Integration)은 Development에 속하는 작업으로 지속적으로 프로젝트의 요구사항을 추적하며, 개발된 코드를 테스트 및 빌드를 수행한다.

  1. 프로젝트 기획 + 요구사항 추적
    • 프로젝트 시작
    • 기획(프로젝트 방법론 채택)
    • 작업관리(Backlog 관리)
    • 진행상황 추적
  2. 개발 + 테스트
    • 코드작성
    • 단위 테스트
    • 소스제어
    • 빌드
    • 빌드 확인

CD(Continuous Deployment(Delivery))는 Operations에 속하는 작업으로 CI가 완료되어 빌드된 소스를 통합 테스트(개발, QA, Staging)를 거쳐 배포를 하며, 배포된 사항들을 지속적으로 모니터링하고 프로젝트 요구사항에 피드백하는 작업을 수행한다.

  1. 빌드 + 배포
    • 자동화된 기능 테스트
    • 통합 테스트 환경(Dev)
    • 사전 제작 환경
      (QA, Load testing)
    • 스테이징 환경(Staging)
  2. 모니터링 + 피드백
    • 모니터링
    • 피드백

이제, DevOps를 하기위한 구체적인 작업을 알았으니, 실제 구성을 하도록 하는 제품들에 대해서 알아보자.

Azure DevOps vs Other Software

DevOps를 하기 위한 솔루션들은 이미 시중에 엄청나게 나와 있으며, 대게 오픈소스 형태로 많이 제공되고 있어서 바로 가져다 사용할 수 있다.

위 그림에서 볼 수 있듯이 DevOps의 각 단계에 맞추어서 원하는 (특화된)제품을 선택하여 사용하면 된다.

모든 단계를 빠짐 없이 구현한다고 가정하여, 예를 들면 다음과 같이 DevOps가 구현 될 수 있다.

  1. Slack으로 요구사항 관리를 하고
  2. Git으로 소스코드 관리를 하고
  3. Maven으로 빌드를 하고
  4. JUnit으로 테스트하고
  5. Jenkins로 Docker에 배포하고
  6. Kubernetes로 운영하며
  7. Splunk로 모니터링 한다.

그런데 이런 경우, 벌써 필요한 제품에 8개나 된다. 프로젝트에 참여하는 모든인원이 이 제품들에 대해서 이해하고 사용하기 어려우며, 각 제품에대한 담당자들있어야 제대로 운영 될 수 있을 것이다. 게다가, 각기 다른 제품이라 다음 단계로 넘어가기 위한 추가적인 관리를 해야할 것이다.

이렇게 되면, 규모가 작은 곳에서는 몇가지 단계를 건너띄고 관리를 하게 되는데 이런 부분에서 예외사항이 생기기 시작하고, 결국 프로젝트 끝에서는 DevOps를 거의하지 못하는 상황이 생길 수 도있다.

반면, Azure DevOps는 하나의 DevOps 관리 솔루션을 제공한다. 때문에 Azure DevOps 하나만 사용해도 모든 절차를 구성 할 수 있다.
그리고 만약, 기존에 사용하던 제품이 있다 하더라도 아래 그림과 같이 3rd-party를 마이그레이션 혹은 연동 설정 할 수 있도록 하기 때문에 Azure DevOps 제품안에서 하나로 통합 관리 할 수 있다.

마지막으로, Azure DevOps를 사용하는 간단한 시나리오에 대해서 알아보자.

Azure DevOps에서는 파이프라인이라는 형태로 CI/CD를 구성하도록 되어 있으며, 각 단계 구성은 아래 그림과 같다.

  1. Project (Agile) Board를 통해서 프로젝트 요구사항 추적 관리를 하고
  2. Repo에서 각 Agile Board Task에 대한 소스코드 관리를 하고
  3. 소스가 커밋이 되면 CI 파이프라인을 통해 빌드 + 테스트를 수행하고
  4. CI가 완료되면 Trigger 형태로 CD 파이프라인을 실행하고
  5. CD 파이프라인을 통해 통합 테스트 + 배포를 하고
  6. (옵션)담당자에게 최종 승인을 받고
  7. 운영 적용 및 모니터링을 한다.

Azure DevOps 이 외의 제품을 사용 했을 때와 단계는 거의 동일 하지만, 여기서 주목 할 점은 Azure DevOps 단일 제품에서 모두 제공 받고 구성 할 수 있다는 것이다.

(**여기서 다 설명 못한 부분이지만 CI/CD 과정 중에 Function(Trigger) 형태로 여러 기능들을 다양하게 엮을 수도 있다. 예를 들어 6번)

각각의 솔루션 전문가들이 있어서 운영한다면 문제가 없겠지만, DevOps를 처음 도입한다던가 규모가 작아 축소 운영을 해야하는 상황이라면 고려해 볼 수 있을 것 같다.

Azure Key Vault 개요

일반적으로, Application Security를 하기위해 Key Vault를 적용하는 사례/방법은 아래와 같이 크게 4가지가 있다.

  1. General Secret Storage
    • 다양한 종류의 비밀들을 저장 할 수 있다.
      (예, 민감한 환경설정, 데이터베이스 크리덴셜, API 키 등)
    • Plan Text file,프로젝트 환경설정 관리, DB에 저장 등, 보통 사용되는 방식 보다 Vault Read나 Vault API를 사용하여 Query하는 것이 훨씬 안전하다.
    • Vault의 Audit Log를 통해서 접근들을 보호 할 수 있다.
  2. Employee Credential Storage
    • General Secret Storage의 확장되는 개념이다.
    • 웹 서비스에 접근하는 직원들의 인증 정보를 저장하기 좋다.
    • Audit Log를 통해서 어떤 직원이 비밀에 접근(및 작업) 했는지 쉽게 알 수 있다.
  3. API Key Generation for Scripts
    • 이상적인 Vault의 기능인 “Dynamic Secrets” 을 통해서 비밀에 접근하는 키를 일정 기간동안 생성하고 만료 시킬 수 있다.
    • Keypair는 스크립트 동작으로만 존재 하며, 키의 생성 작업은 완벽하게 로그로 남는다.
    • IAM(Identity Access Management) 사용 이상의 기술이지만, 때로는 하드코딩으로 접근 제한을 두는 것이더 효율 적일 수 있다.
  4. Data Encryption
    • Vault를 사용하여 비밀을 저장할 수있을뿐만 아니라 다른 곳에 저장된 데이터를 암호화/복호화 할 수 있다.
    • 이 것을 사용하면 응용 프로그램이 기본 데이터 저장소에 데이터를 저장하면서 데이터를 암호화를 할 수 있다.

Azure Key Vault와 같은 서비스는 Azure에만 있는 것은 아니고 대부분의 클라우드 서비스에서 데이터 암화에 대한 KMS(Key Management Service)를 제공하고 있다. 그러면 이러한 서비스이 어떤 차이 점이 있는지 알아보자

AWS KMS(Amazon Web Service Key Management Service)

  • 암호화 표준은 RSA-OAEP와 PKCS#1v1.5(현재 2.2까지 나왔음.)를 사용한다.
  • CMK(Customer Master Key)와 Data Key를 이용하여 데이터를 암호화 작업을 한다.
  1. CMK
    • 256-bit AES 키이며, 이 키는 내보낼 수 없다.
    • 키에 사용되는 대칭 알고리즘은 AES-GCM-256을 사용한다.
    • CMK를 사용하여 최대 4KB의 데이터를 암호화하고 해제 할 수 있다.
  2. Data Key
    • CMK를 사용하여 생성한다.
    • 많은 양의 데이터를 암호화 하는데 사용한다.

GCP KMS(Google Cloud Platform Key Management Service)

  • 암호화 표준은 RSA-OAEP와 AES-GCM을 사용한다.
  • 암호화 Key의 정확한 제어 엑세스 관리를 위해 Key ring, Key, Key version이라는 계층 구조를 가지고 있다.
  1. Key ring
    • 조직화 목적을 위해 키를 그룹화 한 것 이다.
    • Key는 Key를 포함하는 Key ring에서 권한을 상속받는다. 연관된 권한을 가진 Key를 Key ring으로 그룹화하면 각 키를 개벽적으로 작업을 수행할 필요 없이 Key ring 수준에서 해당 키를 대상으로 권한을 부여, 취소, 수정 할 수 있다.
  2. Key
    • AES-256키를 GCM(Galois Counter Mode)로 사용한다.
    • 특정한 용도를 위해 사용되는 암호화 키를 나타내는 명명된 객체이다.
    • 시간이 경과 되어 새로운 키 버전이 생성되면 키자료인 암호화에 사용되는 실제 비트가 변경될 수 있음.
  3. Key version
    • 일정한 시점에 하나의 키와 관련된 키 자료를 나타낸다.
    • 임의의 여러 버전이 존재 할 수 있으며 버전은 최소 하나 이상 있어야 한다.

Azure Key Vault

  • 암호화 표준은 RSA-OAEP를 사용한다.
  • 비밀정보를 중앙 위치에 보관하고 보안 액세스, 권한 제어 및 액세스 로깅을 제공한다.
  • 비밀 정보를 접근하고 사용하려면 Key Vault에 인증 해야하며, Key Vault 인증은 AAD(Azure Active Directory)에서 인증 지원하는 ID(App Client ID)를 통해 인증 받을 수 있다.
    • 접근 정책은 ‘작업’을 기반으로 한다.
      – Application 별로 비밀 값 읽기, 비밀 이름 나열, 비밀 만들기 등의 권한을 부여한다.
  • 제공되는 보안 컨트롤
    • Data Protect
      – TDE(투명한 데이터 암호화)와 Azure Key Vault를 통합하면 TDE 보호기라는 고객 관리형 비대칭 키를 사용하여 DEK(데이터베이스 암호화 키)를 암호화할 수 있습니다.
    • Network ACL
      – 가상 네트워크 서비스 End-Point를 사용하면 지정된 가상 네트워크에 대한 액세스를 제한할 수 있습니다.

정리하면, AWS, GCP의 KMS는 암호화 키를 안전하게 저장 하는 것과 키를 암호화하는 작업(암호화와 복호화)에  초점이 맞춰져 있다고 볼 수 있고, 반면, Azure Key Vault는 백엔드에서는 키 암호화 작업을 통해 비밀을 저장하는 등, KMS와 유사하게 기능들을 제공하지만 그 외 추가 적인 관리 기능들을 제공하기 때문에 비밀 관리 솔루션을 제공한다고 볼 수 있다.

Azure에서 이중화로 구성된 VM에 Load Balancer 구성하기

Azure에서 IaaS 서비스인 가상머신을 사용하여 시스템을 구성할 때 가장 먼저 고려해야 하는 사항이 바로, 이중화 구성에 대한 부하 분산 설정이다. 

이전 Azure ASM(Azure Service Manager, 클래식 버전)에서는 Cloud Service라는 PaaS 서비스를 이용하여 부하 분산이 되도록 하였다. Cloud Service의 경우 초기 설정이나 사용에 제한이 있었는데 나에게 가장 어려웠던 2가지가 “NAT가 해시 기반 모드만 지원한다“라는 것과 “유휴시간 설정을 별도로 할 수 없다“라는 것이었다. 이렇게 되면 Local Storage를 많이 이용하는 시스템의 경우 바로 이중화 작업을 하기 힘들어진다.

이후, Resource Group라는 개념이 추가된 Azure ARM(Azure Resource Manager, 리소스 버전)에서는 Load  Balancer를 독립적으로 PaaS 서비스로 제공하게 되면서, 위 문제점 들을 해소할 수 있게 되었다.

이제 위 언급한 문제점들을 어떻게 설정하는지 확인해보자.

우선, Load Balancer에 연결할 VM(2대)을 간단하게 생성해 보자.

위 그림을 살펴보면, 좌측 1번째 VM은 생성할 때 가상 네트워크, 서브넷, 네트워크 보안 그룹(방화벽), 가용성 집합을 새로 만드는 것을 확인할 수 있다. 그리고 우측 2번째 VM은 1번 VM을 생성했을 때 만들어진 것들을 그대로 사용하여 같은 가상 네트워크 서브넷과 가용성 집합 내에 만들어지는 것을 확인할 수 있다. 

VM 2대 모두 생성 완료되었다면 만들어진 리소스 그룹에는  위에 언급한 서비스 리소스들이 구성된 것을 볼 수 있다.

이제, 생성된 각 VM에 원격접속을 하여 IIS 기능을 추가하고 기본 웹 사이트(Default Web Site)가 동작하도록 설정한다.
이때, 각 VM의 사이트에 부하 분산이 될 때 식별될 수 있도록 간단히 index.html 페이지에 표시해 주도록 하면 좋다.
(원격제어 및 IIS 설정, 기본 사이트 구성 등 관련 절차는 생략한다.)    

IIS 사이트가 잘 구성되었다면, 각 사이트(Default Web Site)가 아래 그림과 같이 나타날 것이다. 

여기까지 가 Load Balancer를 사용하기 전까지 준비 단계이다. ㅡㅡ;;;;

이제 본격 적으로 Load Balancer 서비스와 어렵게 구성한 VM 2대를 가지고 부하 분산이 되도록 구성하여야 하는데…. 그전에 부하 분산의 2가지 형태에 대해서 설명하고 진행 하겠다.

Azure Load balancer의 부하분산모드에는 해시 기반 모드와 소스 IP 선호도 모드 2가지를 제공있는데 각 특성은 아래와 같다.

1. 해시 기반 모드
기본 배포 모드로 5개의 Tuple을 가지고 connection을 hash하여 DIP에 연결해 주는 방식이다. 
5 Tuple의 구성은 Source IP, Source Port, Destination IP, Destination Port, Protocol type으로 구성되 있다. 동일한 세션 내에서는 동일한 DIP 인스턴스로 보내지게 되지만, Source의 세션이 새로 시작되면 connection이 다른 DIP 인스턴스로 보내질 수 있다.

2. 소스 IP 선호도 모드
2개 혹은 3개의 Tuple을 가지고 connection을 hash하여 DIP에 연결해 주는 방식이다.
2 Tuple의 구성은 Source IP, Destination IP로 구성되어 있으며, 3 Tuple은 Source IP, Destination Protocol type으로 구성되 있다. 동일한 Source은 항상 동일한 DIP 인스턴스로 보내지게 된다. 단, 이경우 connection traffic이 불균형하게 분산될 수 있다.

(참조 링크: https://docs.microsoft.com/ko-kr/azure/load-balancer/load-balancer-distribution-mode)

이제 진짜 Load Balancer를 만들어 보자. 
준비 과정이 길어서 어려울 것 같지만 사실상 Load Balancer는 리소스 이름하고 Public IP 구성만 하면  수분 내에 다음 그림과 같이 만들어지는 것을 확인 할 수 있다.

이제 생성이 된 부하 분산 장치의 설정을 메뉴 순서대로 구성만 해주면 된다.
(실제로, 좌측 메뉴의 설정을 위에서 아래 순으로 구성만 해주면 되기 때문에 이미지외 설명은 생략 하도록 한다.)

1. 백엔드 풀

2. 상태 프로브

3. 부하 분산 규칙

4. 인바운드 NAT 규칙
위에서 IIS기능 구성 당시 네트워크 보안 그룹에서 80포트에 대한 인바운드 규칙을 이미 해두었기 때문에 규칙 중복 오류가 발생한다. 이때, 당황하지 말고 침착하게 설정을 안하고 지나가도록 하자. ^^

1~4번까지 설정이 완료되면, Load Balancer의 공용 IP(혹은 DNS) 주소를 가지고 사이트에 접근해보자. 브라우저 세션 강제 삭제 및 새로 고침(F5)을 몇 번 해보면 부하 분산 규칙에 따라 사이트(VM 2대)가 바뀌는 것을 볼 수 있다.

[.NET]XML주석을 활용하여 Swagger UI에 세부 메타 데이터 정보 추가 하기

Swagger를 사용하여 개발하다 보면 API에 대한 좀더 자세한 설명을 제공 하고 싶을 때가 있는데, 스팩화면을 별도로 개발하거나 문서를 작성하지 않고 C#코드에 추가한 XML주석을 활용하여 Swagger UI에 바로 세부 메타 데이터 정보 추가 할 수 있다.

먼저, C# 코드의 각 Class, Method, Properties 등에 XML 주석을 추가 작성해 보자.
(TIP, 정의 코드 위에서 ‘/(Slash)’를 연속 3번 누르면 자동으로 XML 테그를 생성해준다.)

이제 작성된 XML 코드들이 문서(파일)로 저장 될 수 있도록 프로젝트 환경설정에 설정 하도록 한다.
프로젝트 속성에서 빌드 메뉴를 살펴 보면 출력 관련 설정하는 영역을 확인 할 수 있다.

XML 문서 파일 체크 박스를 그림과 같이 체크한다, 설정(체크) 하면 우측에 경로가 기본값으로 셋팅 되며, 해당 경로 값을 복사해 둔다.
(원하는 경로가 있다면 변경해도 되지만, 기본값 쓰는 것을 권장한다.)

다음으로, 저장된 XML 문서 파일을 Swagger에서 열어서 화면에 표시 할 수 있도록 설정 해야 한다. 
SwaggerConfig.cs을 열어 다음과 같이 코드를 추가 한다.  

GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
 c.IncludeXmlComments(string.Format(@"{0}\bin\SwaggerFramework.XML"
, System.AppDomain.CurrentDomain.BaseDirectory));
})

/*.......... 생략 ............*/

(복사한 경로값으로 “\bin\SwaggerFramework.XML”부분을 변경하여 설정 한다.)   

이제, 설정이 완료 되었으니 프로젝트를 실행 시켜 어떻게 나오는지 확인해 보자.

Swagger UI를 확인해 보면 위 XML코드를 작성한 내용이 태그 이름에 따라 화면에 나타는 것을 확인 할 수 있다.

이와 같이, Swagger UI에서 C#코드의 사용자 정의 XML(주석)을 사용하여 API들에 대한 설명들을 메타 데이터 표현할 수 있도록 함으로써, 추가적인 별다른 노력 없이 좀 더 자세한 API 스팩 문서를 작성 및 제공 할 수 있다.

Azure Cloud Service Too many redirects

Azure의 Cloud Service를 개발 할 때, Local에서 테스트 할 때는 정상 작동했는데, 프로젝트 패키지를 프로덕션(혹은 스테이징)에 배포하고 확인 하니 Too many redirects 라는 오류 페이지가 뜨고 아무것도 확인 할 수 없을 때가 있다.

이런 오류는 보통 Cloud Service에 설정된 Framework과 로컬에서 프로젝트에 구성한 Framework버전이 달라서 생기는 문제다.

우선 WebRole을 비롯한 솔루션에 추가된(종속된) 프로젝트들의 속성에 들어가 그림과 같이 Target Framework(대상 프레임워크) 버전을 확인 한다. (되도록이면, 사용하는 프로젝트들의 Framework 버전중 최상위 버전 1가지로 통일 하는 것을 추천하며 예제에서는 4.6을 사용한다고 가정한다.) 

각 프로젝트의 버전을 확인 한 후 Cloud Service에서 해당 Framework 버전을 지원하는 Azure Guest OS Release 버전을 확인 한다.  
(참조 링크: https://docs.microsoft.com/ko-kr/azure/cloud-services/cloud-services-guestos-update-matrix)

링크에 들어가서 보면 알 수 있겠지만 글 작성일 기준으로 각 Guest OS에 설치된 Framework Version을 정리해 보면 다음 표와 같다.

Guest OS Release Installed .NET Framework Version
Release 5, Windows Server 2016 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2
Release 4, Windows Server 2012 R2 4.0, 4.5, 4.5.1, 4.5.2
Release 3, Windows Server 2012 4.0, 4.5, 4.5.1, 4.5.2
Release 2, Windows Server 2008 R2 SP1 3.5, 4.0, 4.5, 4.5.1, 4.5.2

이제 위에서 확인한 프로젝트들의 target Framework 버전을 지원하는 Guest OS가 설정되어 있는지 Cloud Service 환경설정을 확인 해보아야 한다. Cloud Service의 환경설정을 확인 하기 위해서 아래 그림의 위치의 *.cscfg 파일을 열어 보자.

확인 해 보면 Line:2의 ‘ServiceConfiguration‘태그의 ‘osFamily‘속성을 확인 해 볼 수 있는데, 이곳이 OS Release 버전을 설정하는 곳이다. 프로젝트들의 Target Framework(대상 프레임웍)버전 설정이 Cloud Service에 설정된 osFamily에서 지원하는 것인지 확인하여 지원하는 것으로 고쳐 준다. (예제는 Release 4로 설정되어 있는 osFamily 속성을 4.6버전을 지원하는 Release 5로 변경하였다.)

AS-IS

<ServiceConfiguration serviceName="CloudServiceSample1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="4" osVersion="*" schemaVersion="2015-04.2.6">

TO-BE

<ServiceConfiguration serviceName="CloudServiceSample1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="5" osVersion="*" schemaVersion="2015-04.2.6">

이제 다시 빌드 하고 배포하면 정상적인 프로젝트 동작을 확인 해 볼 수 있다.

[.NET]Swagger에서 Namespace를 이용하여 API 버전 구분(그룹화)하기

API를 개발 하다보면 자연스럽게 생기는 대표적인 고민이 바로 버전 관리 이다.

예를 들어

  1. 버전을 개별 기능(URL) 별로 관리 할 것인가? 아니면 개별 기능을 구룹화하여 구룹별로 할 것인가?
  2. 버전에 대한 URL 설계는 어떻게 할 것인가?
  3. 버전 관리를 한다면 구버전 지원은 어떻게 할 것인가? 

등등…

이러한 고민들을 Swagger의 MultipleApiVersions을 이용하여 멀티 버전을 설정하면 해결 할 수 있다.

API APP 프로젝트를 만들면 다음과 같은 경로에 SwaggerConfig.cs라는 파일을 볼 수 있다. (위차: ~/App_Start/SwaggerConfig.cs)

클릭해서 열어보면 대략 Line:35 정도에 c.SingleApiVersion 이라는 것이 기본으로 설정되어 있는 것을 볼 수 있는데 이것은 API APP프로젝트를 처음 만들면 API 샘플(~/Controller/ValueController.cs) 1개가 등록 되어 있기때문에 멀티 버전 관리를 할 수 있는게 없기 때문이다.

멀티 버전을 관리 설정을 하기 전에 우선 간단히 아래 그림과 같이 2개의 API를 만들어 보도록 하겠다.
(프로젝트 샘플 소스를 가지고 실제 기능은 없고 단순히 호출 할 수 있는 Method만 만들었다. Student – ClassRoom: 학생 API / Teacher – Report :  선생님 API)

위와 같이 구성하면, 각각의 컨트롤러 파일의 Namespace는 
Student : SwaggerFramework.Controllers.Student
Teacher : SwaggerFramework.Controllers.Teacher

로 설정 된 것을 확인 할 수 있다.

이제 위와 같이 설정된 Namespace를 가지고 API 버전을 구분 할 수 있도록 MultipleApiVersions를 설정해 보자.

앞서 언급한 SwaggerConfig.cs라는 파일을 열어 Line:35 정도에 있는 c.SingleApiVersion으로 이동하여, 해당 줄을 삭제(보통 주석 해두는 것이 좋다.)하고 아래와 같이 코드를 추가 하도록 한다.

//c.SingleApiVersion("v1", "SwaggerFramework");
c.MultipleApiVersions((apiDesc, targetApiVersion) =>
{
   var controllerNamespace = apiDesc.ActionDescriptor.ControllerDescriptor.ControllerType.FullName;
	
   if(CultureInfo.InvariantCulture.CompareInfo.IndexOf(controllerNamespace, string.Format(".{0}.", targetApiVersion), CompareOptions.IgnoreCase) >= 0)
   { 
	return true; 
   }
   return false;
},(vc) =>
{
   vc.Version("Student", "Student API v1");
   vc.Version("Teacher", "Teacher API v1");
});

위와 같이 설정하면 각 Namespace(Student, Teacher)이름에 따라서 버전이 구분을 할 수 있다. 

실제로 구분이 되는지 프로젝트를 실행 해보면 다음 그림과 같이 API가 Namespace 별로 구분되어 확인 할 수 있다.

Swagger Explore 창에 Namespace를 이용하여 검색해보면, SwaggerConfig.cs에 설정해 둔 각 API설명과 해당 Namespace를 가지고 있는 API 기능을 구별하여 확인 할 수 있다.
(Explore 검색 예시 – http://{ Domain Path }/swagger/docs/[ Studnet | Teacher ])

이와 같이 Swagger의 환경 설정에서 Namespace를 이용하면 간단히 API를 버전별로 구분(그룹화)하여 제공 할 수 있기 때문에 버전관리를 쉽게 할 수 있다. 

[.NET]Swagger를 이용하여 Azure API APP 문서 만들기

Swagger를 사용하면 개발된 Restful API에대한 스팩을 별도의 문서를 만들지 않고도 자동으로 문서화 하여 웹형태로 쉽게 제공 할 수 있다.

먼저, Swagger에서 제공하는 API 스팩 문서 화면을 살펴 보자.

Rest API의 URL(Route 주소)목록을 제일 먼저 볼 수 있는데, 직관적으로 해당 API APP에 어떤기능들 있는지 한번에 파악 할 수 있게해준다. 특정 URL을 클릭하면 그 API안에 구현된 HTTP Method 리스트를 볼 수 있다.

Restful API의 동작은 동일한 Route 주소에 HTTP Method를 통해서 CRUD(생성 – Post, 조회 – Get, 변경 – Put, 삭제 – Delete)기능을 구분하며 이러한 구분을 Swagger에서는 4가지 색으로 구분하여 표현해준다.

다음으로, Get Method를 클릭하여 Swagger에서 해당 API에 대한 문서를 어떻게 제공하는지 실제 코드랑 비교하 살펴보자.

Swagger Source Code

// GET api/values/5
[SwaggerOperation("GetById")]
[SwaggerResponse(HttpStatusCode.OK)]        
[SwaggerResponse(HttpStatusCode.NotFound)]
public string Get(int id)
{
    return "value";
}

Swagger Document

첫 번째, 최상단의 GET /api/Values/{id}는 Rest API를 호출하기 위한 라우트 주소로 메서드 역할을 정의하는 public string Get(int id) 의 정보이다.

두 번째, 최상단 바로 및의 Parameters영역은 API의 메서드의 변수에 대한 설명을 확인 할 수 있으며, Value 필드에 임의 값을 넣고 테스트 해볼 수 도 있다. 
(값을 넣고 아래 Try it Out! 이라는 버튼을 누르면 개발한 API가 동작한다.)

마지막, Response Messages영역은 API가 HTTP통신을 할때 반환하는 HttpResponseMessage의 HTTP State Code에 대한 정의이다. Swashbuckle의 속성 설정 방법을 통해서 정의 할 수 있다.
(예: [SwaggerResponse(HttpStatusCode.OK)], [SwaggerResponse(HttpStatusCode.NotFound)])

Restful API를 개발하는 사람이라면 시각화가 되어 있지 않은 API의 각종 정보들을 다른 사람에게 전달하기 정말 까다로운 일이라는 것을 알고 있을 것이다. 이런 고충을 Swagger Framework을 통해서 해소 할 수가 있다. 

Azure의 APP API서비스는 Swagger Framework을 기본으로 제공해 주고 있어서 Azure에서 API를 개발 할 때, 몇가지 정의 방법으로 인터페이스 설명 및 기본 설정을 하면, Swagger.UI에서 보기 좋게 문서화 해서 Web으로 잘 보여 준다. 그래서 개발자가 문서 작업때문에 시간들일 필요도, 머리 아플일도 전혀 없다. 그리고 표준 Rest API 정의에 대한 표현을 Swagger에서 정말 잘 다루고 있기 때문에 어느정도 Rest API에 대한 인식이 있다면 API를 제공받은 사람 입장에서도 서비스에 대한 정보를 별다른 노력 없이 쉽게 인식할 수 있다.