Azure Devops와 AKS배포 관리 2편

- Docker 이미지의 AKS 배포

Docker 이미지의 AKS 배포

빌드된 어플리케이션을 배포하는 방법은 여러가지 방법이 있다. 통합된 Devops 툴을 사용하기 전에는 Jenkins와 같은 빌드서버에서 Ansible과 같은 배포툴로 서버에 배포했다. 그러기 위해서는 어플리케이션에 따른 빌드환경과 배포환경을 직접 셋팅이 필요했다. 통합된 Devops와 Cloud Service는 이러한 작업을 상당히 줄여준다.

아래는 전체 아키텍쳐이다. 상당히 통합되어 있어보인다.

여러 개의 어플리케이션 구현을 위해 Mysql을 직접 배포해보았다.

2편 구현 목표

1. Artifact 배포

2. Manifest 파일을 통해 Kubernetes에 Application을 배포.

3. 모니터링 Kubernetes Prometheus, Grafana 배포.

4. ARM template으로 IaC 구현.

Kubernetes 배포

1편에서 어플리케이션의 도커 이미지 생성까지 구현했다. 그리고 Kubernetes는 컨테이너 기반 어플리케이션을 관리한다고 했다. 따라서, 빌드된 도커 이미지를 기준으로 Manifest 작성으로 Kubernetes에 어플리케이션을 배포할 수 있다. 작성된 Manifest를 배포 파이프라인에서 사용하도록 Artifact에 저장하고, Kubernetes에 배포해보자.

Artifact? 
소프트웨어 개발을 진행하면서 생성되는 다양한 산출물을 의미하며, 각종 설계문서, 소스코드, 라이브러리 등 이 있다.

Kubernetes 리소스 생성

Azure Shell에서 AKS를 생성하자.

 
ACR_NAME=CMACR            # ACR 이름 변수 지정
RESOURCE_GROUP=CM_Devops  # 리소스 그룹 지정
REGION_NAME=koreacentral  # AKS 리전 지정
AKS_NAME = aksclusterdevops
 
# Kubernetes 최신버전(preview 제외)
VERSION=$(az aks get-versions \
    –location $REGION_NAME \
    –query ‘orchestrators[?!isPreview] | [-1].orchestratorVersion’ \
    –output tsv)
 
# 예제환경에서는 1개의 노드와 가장 작은 사이즈의 VM을 사용
# attack-acr을 통해 1편에서 만든 ACR 권한 부여
az aks create \
–resource-group $RESOURCE_GROUP \
–name $AKS_NAME \
–location $REGION_NAME \
–kubernetes-version $VERSION \
–generate-ssh-keys \
–node-vm-size Standard_B2s \
–node-count 1 \
–attach-acr $ACR_NAME
 

Manifest 생성

Yaml이나 Json형식으로 Kubernetes가 유지할 객체의 원하는 상태를 지정한다. pod, replica set, configmap, secret, deployment 등과 같은 리소스를 Manifest를 통해 생성해보자. 예제 Manifests는 > Manifest 위치 를 참고하자.

기존 Docker image를 Kubernetes 환경에 올리기 위해서는 Deployment, Service, Config 등을 고려해야한다. Config 부분은 예를들면 Database 접속정보, 어플리케이션의 Config, Docker pull을 위한 인증정보 등이 있다. 이러한 정보는 Secret이나 configmap을 통해 배포할 수 있다. 이 부분까지 Manifest에 포함 시킬 예정이다.

   Set Deployment > 위치

# MYSQL + PV  Deployment

apiVersion: “apps/v1” # for versions before 1.9.0 use apps/v1beta2

kind: Deployment

metadata:

name: simpleapp-mysql

labels:

    app: simpleapp

spec:

selector:

    matchLabels:

    app: simpleapp

    tier: mysql

strategy:

    type: Recreate

template:

    metadata:

    labels:

        app: simpleapp

        tier: mysql

    spec:

    containers:

    – image: mysql:5.7

        name: mysql

        args:

        – “–ignore-db-dir=lost+found”

        env:

        – name: MYSQL_DATABASE

        value: simpleapp

        – name: MYSQL_USER

        value: user

        – name: MYSQL_PASSWORD      # Secret 값 사용

        valueFrom:

            secretKeyRef:

            name: mysql-secret

            key: password

        – name: MYSQL_PORT

        value: “3306”

        – name: MYSQL_ROOT_PASSWORD     # Secret 값 사용

        valueFrom:

            secretKeyRef:

            name: mysql-secret

            key: password

        ports:

        – containerPort: 3306

        name: mysql

        volumeMounts:

        – name: mysql-persistent-storage

        mountPath: /var/lib/mysql

    volumes:

    – name: mysql-persistent-storage

        persistentVolumeClaim:

        claimName: mysql-pv-claim

  —

  # 예제 어플리케이션 Deployment.

  apiVersion: “apps/v1” # for versions before 1.9.0 use apps/v1beta2

  kind: Deployment

  metadata:

  name: simpleapp

  labels:

      app: simpleapp

  spec:

  selector:

      matchLabels:

      app: simpleapp

      tier: frontend

  strategy:

      type: Recreate

  template:

      metadata:

      labels:

          app: simpleapp

          tier: frontend

      spec:

      containers:

      # 예제 어플리케이션 – Django

      – image: acrdevopse.azurecr.io/simple-app:latest

          name: simpleapp

          env:

          – name: DJANGO_SETTINGS_MODULE

          value: simpleapp.settings.production

          – name: MYSQL_DATABASE

          value: simpleapp

          – name: MYSQL_USER

          value: user

          – name: MYSQL_PASSWORD      # Secret 값 사용

          valueFrom:

              secretKeyRef:

              name: mysql-secret

              key: password

          – name: MYSQL_PORT

          value: “3306”

          – name: MYSQL_HOST

          value: simpleapp-mysql.simple-app

          – name: MYSQL_ROOT_PASSWORD     # Secret 값 사용

          valueFrom:

              secretKeyRef:

              name: mysql-secret

              key: password

          ports:

          – containerPort: 8000

          name: simpleapp

          volumeMounts: # django의 staticfiles들을 nginx와 공유

          – name: staticfiles

          mountPath: /staticfiles

      # nginx

      – image: nginx

          name: simpleapp-nginx

          ports:

          – containerPort: 80

          volumeMounts: # django의 staticfiles들을 nginx와 공유

          – name: staticfiles

          mountPath: /staticfiles

          – mountPath: /etc/nginx/conf.d

          name: nginx-conf

          readOnly: true

      volumes: # django의 staticfiles들을 nginx와 공유

          – name: staticfiles

          emptyDir: {}

          – name: nginx-conf

          configMap:

              name: nginx-conf

 

   ㆍSet Service > 위치

 

# 예제 어플리케이션 Service

# MYSQL

apiVersion: v1

kind: Service

metadata:

  name: simpleapp-mysql

  labels:

    app: simpleapp

spec:

  ports:

    – port: 3306

  selector:

    app: simpleapp

    tier: mysql

  clusterIP: None

— # MYSQL PVC

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: mysql-pv-claim

  labels:

    app: simpleapp

spec:

  accessModes:

    – ReadWriteOnce

  resources:

    requests:

      storage: 20Gi

— # Django 어플리케이션 Service

apiVersion: v1

kind: Service

metadata:

  name: simpleapp

  labels:

    app: simpleapp

spec:

  ports:

    – port: 80

      targetPort: 80

  selector:

    app: simpleapp

    tier: frontend

  type: LoadBalancer

 

   ㆍSet Configmap – nginx config (default.conf)

 

upstream website {

  server 127.0.0.1:8000;

}

server {

  location /static/ {

    autoindex on;

    alias /staticfiles/;

  }

  location / {

    proxy_pass http://website/;

    proxy_read_timeout 60;

    proxy_connect_timeout 60;

    proxy_send_timeout 60;

  }

  listen 80;

  server_name localhost;

}

 

Artifact 배포

작성된 Manifest를 Release 파이프라인에서 사용하기위해 Artifact에 저장해야한다. Build Pipeline에서 Git에 저장된 Manifest를 Artifact로 보내는 작업을 추가한다. 준비한 Manifest를 프로젝트의 특정 위치에 업로드 후 아래와 같이 Artifact로 drop 한다.

Release 파이프라인

배포한 AKS, Artifact를 통해 어플리케이션을 Kubernetes환경에 올릴 차례이다. Release 파이프라인에 아래와 같이 설정을 배포하기만 하면된다.

apply secret

어플리케이션 배포시 Keyvault에서 관리되는 환경변수들을 Kubernetes Secret으로 배포할 수 있다.

apply imagepullsecret

ACR에서 Pull 하기위한 인증을 위해 Imagepullsecret을 설정하자.

apply configmap

configmap은 데이터를 저장하는데 사용하는 API오브젝트이다. 파일을 통한 저장이 가능하다.

apply deployment

Artifact에 저장한 yaml을 경로로 deployment를 설정하고, Deploy를 해준다. ImagePullSecret은 위에서 설정한 값 그대로 써준다.

apply deployment

위처럼 Service도 설정해준다.

성공

배포가 성공되면 아래와 같이 Azure shell에서 명령어를 통해 목록을 볼 수있다. 실패시 파이프라인의 로그를 보고 슈팅을 하도록하자.

모니터링

Helm Chart

Helm이란, 쿠버네티스를 패키지로 해서 관리해주는 것으로 Python의 pip, Node.js의 npm의 역할로 보면된다. Helm Chart란 패키지 포멧으로 쿠버네티스를 설명하는 파일의 집합이라고 볼 수 있다.

모니터링 도구로써 Prometheus와 대시보드 Grafana를 함께 많이 사용하고 있다. Helm Chart의 실습을 통해 모니터링 구성을 해보았다. Azure Shell을 통해 우선 배포 테스트를 해보았으며, 추후 배포 파이프라인에 추가해볼 예정이다.

ㆍPrometheus 배포

 
kubectl create namespace monitoring
 
helm search repo prometheus
 
helm install monitoring stable/prometheus
 
 
– Grafana 배포
 
helm install prometheus stable/prometheus -n monitoring
 
helm install grafana stable/grafana –set persistence.enabled=true –set service.type=Loadbalancer –set persistence.size=8Gi –namespace monitoring
 
“`
 
위 명령어를 통해 기본 설정으로 배포해주었다. 그 다음 kubectl get service -n monitoring 명령어를 통해 Grafana IP로 접속해서, 대시보드를 Import 해보자.

ㆍ위 스크린샷처럼 접속 후 왼쪽에 있는 + 메뉴에서 Import로 들어간다.

ㆍ위 칸에6417 를 적으면 아래 스크린샷처럼 나온다.  
    
    Grafana Labs 참조

ㆍImport를 하고 생성한 대시보드를 띄우면 아래와 같이 나온다.

이렇게 쉬운 셋팅으로 쿠버네티스의 인프라에 대해 모니터링을 할 수있게 되었다. 필요한 Metric을 설정하고 alert 설정도 가능하다.
자세한 사용방법은 넘어가겠다.

IaC (Infrastructure as Code) – Azure ARM Template

사실 이 포스팅의 의도는, ARM Template으로 AKS, ACR을 자동 배포 후 어플리케이션을 올려보기 위함이었다. 하지만 구현 중 많은 어려움이 존재해 ARM template를 통한 적절한 IaC방법을 찾지 못했다.

ARM Template?
Azure resource를 코드로써 배포하기위한, 인프라 및 구성을 정의하는 JSON 파일이다. 이러한 선언적 배포를 통해 일관적인 리소스를 오케스트레이션 할 수 있다.

ARM template 배포시, Azure Devops에서 Connection을 가지고 오기가 힘들다.
Azure Devops에서는 Service간 connection을 한번의 설정만으로 관리해준다.

ACR과의 Pull권한 연결

Github Issue 를 보면 이 방법은 전에 이미 논의했던 흔적을 볼 수있고, 불가능하다라고 적혀있다.

그래서 빌드파이프라인에서 ARM template이 아닌, Azure Cli의 명령어를 통해 배포해보았다. (Attach가능) 하지만, 위에서 말한 Service connection문제가 있어 어려웠다.

정리

Azure 서비스를 통해 인프라를 구성하고 Azure Devops를 통해 어플리케이션을 도커화하고 Manifest로 Azure Kubernetes Service에 서비스를 배포해 보았고 동시에 모니터링 서비스까지 구축해보았다. Azure Devops에서 인프라 구성 자동화까지는 어려운점이 있었지만, 간단한 명령어만으로 Kubernetes Cluster 생성이 가능했다.

이렇게 만들어진 Kubernetes Cluster위에 컨테이너화된 마이크로서비스들은 Manifest만으로 배포하면서 개발주기가 엄청나게 향상될 것을 기대할수있다. 초기 도입은 힘들겠지만 엄청나게 큰 서비스를 개발하기에는 Kubernetes는 정말 매력적인 것 같다.

#AKS #Azure DevOps #DevOps #Monitoring
Buy now