반응형

일반적으로 Pod에서 에러가나서 오류가 나는 상황을 다루진 않는다.

 

Pod는 정상인 상태인데 HPA설정을 하고 간헐적인 50x에러에 대해서 다루려고한다.

 

해당에러는 GCP에서 진행을 하였고 

Monitoring Alarm을 구성하여 간헐적인 현상을 확인하였다.

 

 

 

참고한 블로그는 사람인 기술 블로그이다. 

자세한 내용이 적혀있으니 해당내용으로 확인해보도록 하자.

https://saramin.github.io/2022-05-17-kubernetes-autoscaling/

 

Kubernetes에서 HPA를 활용한 오토스케일링(Auto Scaling)

소개 안녕하세요, 사람인HR IT연구소 채용시스템개발팀의 김용태입니다. 채용시스템개발팀은 채용 홈페이지 제작부터 채용 설계, 지원서 접수, 지원자 관리, 데이터 관리, 합격 과정에 이르기까

saramin.github.io

 

 

내가 생각했던 과정을 이러하다.

많은 양의 트래픽이 유입이 될때 Alarm로그가 발생하였고 이후 몇분 이내에 정상 상태 Alarm이 돌아왔다.

트래픽이 몰리는것과, 얼마 안되서 다시 정상으로 돌아오는 것이 Auto Scailing과정에서 문제가 있다는 생각을 했다.

 

Scale Out

확장 과정에서의 문제 발생을 확인해봐야 했다.

기본적으로 2가지 설정은 해둔상태이다.

livenessProbe:
  exec:
    command:
      - cat
      - /workspace/app/app.py
  initialDelaySeconds: 10
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /
    port: 5001
  initialDelaySeconds: 20
  periodSeconds: 30

 

livenessProbe -  서버가 살아있는 상태 확인
readinessProbe - 트래픽을 받지 않는 상태에서 쿠버네티스 내부적으로 Health체크를 진행 (트래픽 준비 상태를 체크하는 듯)

 

트래픽을 유입시키는 상태값을 설정하는데 보아하니 이 두 부분의 허용을 늦추는 상태값으로 트래픽을 늦추는 설정이 필요했다.

startupProbe - 해당 체크가 Success가 되면 livenessProbe와 readinessProbe를 체크를 허용한다.

 

spec:
    containers:
    - name: server
    	...
    	livenessProbe:
          exec:
            command:
              - cat
              - /workspace/app/app.py
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 5001
          initialDelaySeconds: 20
          periodSeconds: 30
        startupProbe:
          httpGet:
            path: /
            port: 5001
          initialDelaySeconds: 20
          periodSeconds: 3

 

 

Scale In

다음으로 축소 과정에서 문제발생을 확인해봐야 했다.

축소될때 무슨 이유로 트래픽이 유실되는지 생각해봐야했는데 Alarm상 생각한 이유는 한가지였다.

위 내용을 보면 matched_url_path_rule 에 UNMATCHED 라는 값이 보였다. 

트래픽이 유입되었는데 가야할 곳을 잃은거라고 생각이 들었다. 

1. Pod는 정상 종료 되었다.

2. Ingress, Service에서 트래픽이 정상적으로 처리되지 않았다.

 

이런 흐름이라면 미리 트래픽을 끊고 Pod를 유예기간을 두어서 종료시키켜야 했다.

 

terminationGracePeriodSeconds - Pod 종료 유예시간 설정

lifecycle.preStop.exec.command - 미리 멈추는 기전에 할 명령어 설정

 

spec:
	terminationGracePeriodSeconds: 60
    containers:
    - name: server
    	...
        설정들..
        ...
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sleep", "30"]

 

 

이렇게 설정하고 ab tool를 가지고 강제로 트래픽을 유입시키고 확인해보았다.

pod 숫자 증량에따라 해당 에러가 관찰 되지 않는 모습을 확인하였다. 

 

ab -n 10000 -c 100 "url"

 

반응형

목표

Lambda Function에 python을 통해

영상편집(영상 + 이미지 + 텍스트 이미지화)을 하는 Function을 만드는게 목표.


환경

  1. python 3.7
  2. moviepy (ffmpeg + imageMagick)
  3. Lambda + Api Gateway + CloudWatch + S3

프로세스

  1. API Gateway를 통한 동영상 합성 요청
  2. Lambda 에서 Trigger를 통한 동영상 합성 진행
  3. S3로 바로 결과 파일 생성

2. Lambda 에서 Trigger를 통한 동영상 합성 진행항목을 중점적으로 다뤄본다.

해당 내용을 진행하기 전에 Lambda에 대해서 알아두어야할 점이 있다.

  1. OS( Amazone Linux2 )

  2. 서버의 경로가 존재한다.

  3. Lambda는 소스파일을 50M 업로드 제한이 있다.

    1. Lambda 기본 Storage를 활용한다. ( /tmp ) 20GB제한

      • tmp는 영상 임시 저장 경로로 활용하였다.
        추천하지 않는 방법 내 생각엔 tmp에 소스를 옮기는 작업은 비효율적이다.

        기본적으로 부착되는 Storage이기 때문에 영상을 받고 S3로 업로드하기 위한 임시 경로

    2. Lambda Layer를 활용한다( /opt )

      • 해당 내용은 소스코드를 참조하여 진행하면된다.

        https://github.com/serverlesspub/imagemagick-aws-lambda-2
        해당 코드는 Layer에 ImageMagick이라는 Utilitiy를 배포할수있는 스크립트이다.


        내 버전에서는 freetype 을 지원하여하는데 해당버전을 지원하게 하려면 수정이 필요하였다.

        https://github.com/serverlesspub/imagemagick-aws-lambda-2/blob/master/Makefile_ImageMagick
        위 파일의 내용을 수정해야하는데

        https://github.com/serverlesspub/imagemagick-aws-lambda-2/issues/13
        위 이슈를 참고하여 수정하였다.

          LIBPNG_VERSION ?= 1.6.37
          LIBJPG_VERSION ?= 9c
          OPENJP2_VERSION ?= 2.3.1
          LIBTIFF_VERSION ?= 4.0.9
          BZIP2_VERSION ?= 1.0.6
          LIBWEBP_VERSION ?= 0.6.1
          IMAGEMAGICK_VERSION ?= 7.0.8-45
          **FREETYPE_VERSION ?= 2.8.1**
        
          TARGET_DIR ?= /opt/
          PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
          CACHE_DIR=$(PROJECT_ROOT)build/cache
        
          .ONESHELL:
        
          CONFIGURE = PKG_CONFIG_PATH=$(CACHE_DIR)/lib/pkgconfig \
              ./configure \
                  CPPFLAGS=-I$(CACHE_DIR)/include \
                  LDFLAGS=-L$(CACHE_DIR)/lib \
                  --disable-dependency-tracking \
                  --disable-shared \
                  --enable-static \
                  --prefix=$(CACHE_DIR)
        
          **##freetype
        
          FREETYPE_SOURCE=freetype-$(FREETYPE_VERSION).tar.gz
        
          $(FREETYPE_SOURCE):
              curl -LO http://www.imagemagick.org/download/delegates//$(FREETYPE_SOURCE)
        
          $(CACHE_DIR)/lib/freetype.a: $(FREETYPE_SOURCE)
              tar xf $<
              cd freetype-*
              $(CONFIGURE)
              make clean
              make install**
        
          ## libjpg
        
          LIBJPG_SOURCE=jpegsrc.v$(LIBJPG_VERSION).tar.gz
        
          $(LIBJPG_SOURCE):
              curl -LO http://ijg.org/files/$(LIBJPG_SOURCE)
        
          $(CACHE_DIR)/lib/libjpeg.a: $(LIBJPG_SOURCE)
              tar xf $<
              cd jpeg*
              $(CONFIGURE)     
              make
              make install
        
          ## libpng
        
          LIBPNG_SOURCE=libpng-$(LIBPNG_VERSION).tar.xz
        
          $(LIBPNG_SOURCE):
              curl -LO http://prdownloads.sourceforge.net/libpng/$(LIBPNG_SOURCE)
        
          $(CACHE_DIR)/lib/libpng.a: $(LIBPNG_SOURCE)
              tar xf $<
              cd libpng*
              $(CONFIGURE)     
              make
              make install
        
          # libbz2
        
          BZIP2_SOURCE=bzip2-$(BZIP2_VERSION).tar.gz
        
          $(BZIP2_SOURCE):
              curl -LO http://prdownloads.sourceforge.net/bzip2/bzip2-$(BZIP2_VERSION).tar.gz
        
          $(CACHE_DIR)/lib/libbz2.a: $(BZIP2_SOURCE)
              tar xf $<
              cd bzip2-*
              make libbz2.a
              make install PREFIX=$(CACHE_DIR)
        
          # libtiff
        
          LIBTIFF_SOURCE=tiff-$(LIBTIFF_VERSION).tar.gz
        
          $(LIBTIFF_SOURCE):
              curl -LO http://download.osgeo.org/libtiff/$(LIBTIFF_SOURCE)
        
          $(CACHE_DIR)/lib/libtiff.a: $(LIBTIFF_SOURCE) $(CACHE_DIR)/lib/libjpeg.a
              tar xf $<
              cd tiff-*
              $(CONFIGURE)     
              make
              make install
        
          # libwebp
        
          LIBWEBP_SOURCE=libwebp-$(LIBWEBP_VERSION).tar.gz
        
          $(LIBWEBP_SOURCE):
              curl -L https://github.com/webmproject/libwebp/archive/v$(LIBWEBP_VERSION).tar.gz -o $(LIBWEBP_SOURCE)
        
          $(CACHE_DIR)/lib/libwebp.a: $(LIBWEBP_SOURCE)
              tar xf $<
              cd libwebp-*
              sh autogen.sh
              $(CONFIGURE)     
              make
              make install
        
          ## libopenjp2
        
          OPENJP2_SOURCE=openjp2-$(OPENJP2_VERSION).tar.gz
        
          $(OPENJP2_SOURCE):
              curl -L https://github.com/uclouvain/openjpeg/archive/v$(OPENJP2_VERSION).tar.gz -o $(OPENJP2_SOURCE)
        
          $(CACHE_DIR)/lib/libopenjp2.a: $(OPENJP2_SOURCE) $(CACHE_DIR)/lib/libpng.a $(CACHE_DIR)/lib/libtiff.a
              tar xf $<
              cd openjpeg-*
              mkdir -p build
              cd build 
              PKG_CONFIG_PATH=$(CACHE_DIR)/lib/pkgconfig cmake .. \
                  -DCMAKE_BUILD_TYPE=Release \
                  -DCMAKE_INSTALL_PREFIX=$(CACHE_DIR) \
                  -DBUILD_SHARED_LIBS:bool=off \
                  -DBUILD_CODEC:bool=off
              make clean
              make install
        
          ## ImageMagick
        
          IMAGE_MAGICK_SOURCE=ImageMagick-$(IMAGEMAGICK_VERSION).tar.gz
        
          $(IMAGE_MAGICK_SOURCE):
              curl -L https://github.com/ImageMagick/ImageMagick/archive/$(IMAGEMAGICK_VERSION).tar.gz -o $(IMAGE_MAGICK_SOURCE)
        
          LIBS:=$(CACHE_DIR)/lib/libjpeg.a \
              $(CACHE_DIR)/lib/libpng.a \
              $(CACHE_DIR)/lib/libopenjp2.a \
              $(CACHE_DIR)/lib/libtiff.a \
              $(CACHE_DIR)/lib/libbz2.a \
              **$(CACHE_DIR)/lib/freetype.a \**
              $(CACHE_DIR)/lib/libwebp.a
        
          $(TARGET_DIR)/bin/identify: $(IMAGE_MAGICK_SOURCE) $(LIBS)
              tar xf $<
              cd ImageMa*
              PKG_CONFIG_PATH=$(CACHE_DIR)/lib/pkgconfig \
                  ./configure \
                  CPPFLAGS=-I$(CACHE_DIR)/include \
                  LDFLAGS=-L$(CACHE_DIR)/lib \
                  --disable-dependency-tracking \
                  --disable-shared \
                  --enable-static \
                  --prefix=$(TARGET_DIR) \
                  --enable-delegate-build \
                  --without-modules \
                  --disable-docs \
                  --without-magick-plus-plus \
                  --without-perl \
                  --without-x \
                  --disable-openmp
              make clean
              make all
              make install
        
          libs: $(LIBS)
        
          all: $(TARGET_DIR)/bin/identify

        수정을 하고 깃허브 README.md파일을 참고로 배포를 진행 하면 Layer로 배포가 된다.


Layer에 적용한 모습

 

 

위 사진처럼 Layer에 내가 만든파일을 적용하게되면

/opt 경로를 접근할수 있게된다.

위의 상태로 소스코드만 작업해주면 된다.

/tmp의 경로에는 image, video가 떨어질 경로

import json
import os
import stat
import shutil
import boto3
import datetime

lambda_tmp_dir = '/tmp'

image_path = "{0}/{1}".format(lambda_tmp_dir, "images")
video_path = "{0}/{1}".format(lambda_tmp_dir, "video")
video_name = "video.mp4"

video_bucket = "heesun-video"

s3 = boto3.client ('s3')
s3.download_file('heesun','font/malgun.ttf','/tmp/malgun.ttf')

from moviepy.editor import *
from moviepy.config import *
change_settings({"IMAGEMAGICK_BINARY": "/opt/bin/magick"})

print('Loading function')

def prepare_path(target):
  if os.path.exists(target):
    shutil.rmtree(target)

  os.mkdir(target)

def move_video(video_file, bucket, dest_key):
    video = open(video_file,"r")

    s3.upload_file(
        video_file,bucket,dest_key,
        ExtraArgs={'ACL':'public-read'}
    )
# functions
def funcHeight(startSec, t):
    return 50+((startSec+t)*30)

def makeVideoFileClip(path, hasMask):
    return (VideoFileClip(path, has_mask=hasMask))

def makeTextClip(msg, fpath, fcolor, fsize):
    return (TextClip(msg, font=fpath, color=fcolor, fontsize=fsize))

# 텍스트가 위에서 아래로 내려오는 애니메이션
def applyTextAnimation(tclip, startSec, endSec):
    txt_col = tclip.on_color(size=(200,30), color=(0,0,0), pos=(6,'center'), col_opacity=0.6)
    txt_mov = (txt_col.set_pos(lambda t: ('center', funcHeight(startSec, t))))
    return (txt_mov.set_start(startSec).set_end(endSec))

def makeImageClip(path, startSec, endSec):
    return (ImageClip(path).set_start(startSec).set_end(endSec))

def createVideo(tTxt, tColor, tSize, iPath, startSec, endSec):    
    # Create Image
    iclip = (makeImageClip(iPath,startSec,endSec))

    # Create Text
    txt = (makeTextClip(tTxt, "/tmp/malgun.ttf", tColor, tSize))
    tclip = (applyTextAnimation(txt,startSec,endSec))

    # txt + image
    return (CompositeVideoClip([iclip, tclip]))

def lambda_handler(event, context):

    tdatetime = datetime.datetime.now()
    prefix = tdatetime.strftime('%Y/%m/%d/')
    prepare_path(video_path)
    video_file = "{0}/{1}".format(video_path, video_name)

    arr = [("등장!!", "http://heesun.s3-ap-northeast-1.amazonaws.com/first.jpg", 2, 3), ("또또또!! 등장!!", "http://heesun.s3-ap-northeast-1.amazonaws.com/two.jpg", 6, 7)]

    clip = (makeVideoFileClip('https://heesun.s3-ap-northeast-1.amazonaws.com/background.mp4', True))

    arr_final = [clip]

    for (txt, img, startsec, endsec) in arr:
        arr_final.append(createVideo(txt, 'white', 24, img, startsec, endsec))

    final = CompositeVideoClip(arr_final)
    final.subclip(0,8).write_videofile(video_file,fps=24,codec='libx264',temp_audiofile="/tmp/test.mp4")
    final.close()

    ymd = prefix.split('/')
    video_key = "{0}/{1}/{2}.mp4".format(ymd[0], ymd[1],"".join(ymd))
    move_video(video_file, video_bucket, video_key)        

    os.system ('cd /tmp && ls -al')

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

소스는 미흡하지만 원하는대로 동작이 되었다.

위 코드 내용에서 필요한것들은

  1. S3 버킷
  2. 이미지 2가지 ( 위 버킷에 넣을 두가지 )

두가지 이미지에 텍스트가 합성되어 추출된다.

해결된 사항

  1. audio=True 할 시에 writing 되는 path가 /tmp 를 바라보지 않고 있어서 writing이 되지 않음. → 설정 필요

    해당 내용은 임시로 Audio file이 Writing할때 경로를 지정해주어야 하는데 해당 부분이 없어서 시스템경로에 Writing되면서 발생된다. temp_audiofile 라는 parameter를 추가해주면 된다.

     final.write_videofile(video_file,fps=24,codec='libx264',temp_audiofile="/tmp/test.mp4")

    코어(라이브러리 파일)쪽에 손을 대는 방법이다. 현재 둘다 적용이 되어있다.

     # moveipy.video.VideoClip.py 
     # 306 line
     audiofile = (tempDirPath + "/" + name + Clip._TEMP_FILES_PREFIX +
                                  "wvf_snd.%s" % audio_ext)

해결되지 않은 사항

  1. Lambda Function Call이 반복될 때 반복적인 다운로드, 프로세스를 제거해야 함.

  2. CloudWatch를 통해서 logging을 추가 해야 함.

  3. tmp 디렉토리 안에 동영상파일이 계속 남아있는지 로그 체크 해봐야함.

    ⇒ 남아있진 않음. 그러나 여러차례 호출하는 api로 작업을 진행 및 테스트 해봐야 함.

    ⇒ 남아있다면 만들고 업로드 후 지워주는 프로세스 추가


Lambda 리소스 성능

램은 어느 일정 수준 이후로는 더 이상 초가 증가 되지 않으나 넉넉한 용량일 때 생성속도가 빨라진다.

이미지, 동영상 용량등이 생성 속도에 영향을 준걸로 확인 어느정도 크기 이후에는 큰 영향이 없음


이전에 기록했던 영상 합성 Lambda 함수를 다시 정리해 보았다.

영상 관련된 부분을 작업하려면 ffmpeg가 필요하였다.

되게 고생하면서 작업했던 기억이 있는데 글을 정리하려고보니 두서없이 정리된거 같다.

반응형

업무에서 slack pipline을 사용하여 slack으로 알람을 제공한다. 보통 에러 알람을 확인하기 위해서 작업한다.

위를 배포하려면 배포 순서가 있다.

  1. 버킷을 만드는 cloud formation을 배포한다.
  2. 생성할 람다함수를 만들어서 cloudformation이 생성한 버킷안에 넣어두고
  3. 중간에 파일을 받아서 S3에 수동으로 업로드해야 한다
  4. 람다를 생성한다.
  5. cloudwatch logs에 원하는 log stream을 할당하고 알람을 받는다.

https://www.dwolla.com/updates/aws-lambda-function-serverless/ 링크를 참고하여 진행하여도 된다. 

 

CloudFormation에 아래 스크립트를 순서대로 넣어서 진행해도 된다.

 

순서는
1 스크립트를 넣고 Create, 
2 스크립트를 1번에서 Update하여 생성

 

1. 스크립트

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "The AWS CloudFormation template for this Serverless application",
    "Resources": {
        "ServerlessDeploymentBucket": {
            "Type": "AWS::S3::Bucket",
            "Properties": {
                "BucketEncryption": {
                    "ServerSideEncryptionConfiguration": [
                        {
                            "ServerSideEncryptionByDefault": {
                                "SSEAlgorithm": "AES256"
                            }
                        }
                    ]
                }
            }
        },
        "SlackLogGroup": {
            "Type": "AWS::Logs::LogGroup",
            "Properties": {
                "LogGroupName": "/aws/lambda/aws-cloudwatch-logs-pipline-prod-slack"
            }
        },
        "IamRoleLambdaExecution": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [
                                    "lambda.amazonaws.com"
                                ]
                            },
                            "Action": [
                                "sts:AssumeRole"
                            ]
                        }
                    ]
                },
                "Policies": [
                    {
                        "PolicyName": {
                            "Fn::Join": [
                                "-",
                                [
                                    "prod",
                                    "aws-cloudwatch-logs-pipline",
                                    "lambda"
                                ]
                            ]
                        },
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "logs:CreateLogStream"
                                    ],
                                    "Resource": [
                                        {
                                            "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-cloudwatch-logs-pipline-prod-slack:*"
                                        }
                                    ]
                                },
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "logs:PutLogEvents"
                                    ],
                                    "Resource": [
                                        {
                                            "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-cloudwatch-logs-pipline-prod-slack:*:*"
                                        }
                                    ]
                                }
                            ]
                        }
                    }
                ],
                "Path": "/",
                "RoleName": {
                    "Fn::Join": [
                        "-",
                        [
                            "aws-cloudwatch-logs-pipline",
                            "prod",
                            "ap-northeast-2",
                            "lambdaRole"
                        ]
                    ]
                }
            }
        }
    }
}

2. 스크립트

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "The AWS CloudFormation template for this Serverless application",
    "Resources": {
        "ServerlessDeploymentBucket": {
            "Type": "AWS::S3::Bucket",
            "Properties": {
                "BucketEncryption": {
                    "ServerSideEncryptionConfiguration": [
                        {
                            "ServerSideEncryptionByDefault": {
                                "SSEAlgorithm": "AES256"
                            }
                        }
                    ]
                }
            }
        },
        "SlackLogGroup": {
            "Type": "AWS::Logs::LogGroup",
            "Properties": {
                "LogGroupName": "/aws/lambda/aws-cloudwatch-logs-pipline-prod-slack"
            }
        },
        "IamRoleLambdaExecution": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [
                                    "lambda.amazonaws.com"
                                ]
                            },
                            "Action": [
                                "sts:AssumeRole"
                            ]
                        }
                    ]
                },
                "Policies": [
                    {
                        "PolicyName": {
                            "Fn::Join": [
                                "-",
                                [
                                    "prod",
                                    "aws-cloudwatch-logs-pipline",
                                    "lambda"
                                ]
                            ]
                        },
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "logs:CreateLogStream"
                                    ],
                                    "Resource": [
                                        {
                                            "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-cloudwatch-logs-pipline-prod-slack:*"
                                        }
                                    ]
                                },
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "logs:PutLogEvents"
                                    ],
                                    "Resource": [
                                        {
                                            "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-cloudwatch-logs-pipline-prod-slack:*:*"
                                        }
                                    ]
                                }
                            ]
                        }
                    }
                ],
                "Path": "/",
                "RoleName": {
                    "Fn::Join": [
                        "-",
                        [
                            "aws-cloudwatch-logs-pipline",
                            "prod",
                            "ap-northeast-2",
                            "lambdaRole"
                        ]
                    ]
                }
            }
        },
        "SlackLambdaFunction": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Code": {
                    "S3Bucket": {
                        "Ref": "ServerlessDeploymentBucket"
                    },
                    "S3Key": "aws-cloudwatch-logs-pipline.zip"
                },
                "FunctionName": "aws-cloudwatch-logs-pipline-prod-slack",
                "Handler": "handler.slack",
                "MemorySize": 1024,
                "Role": {
                    "Fn::GetAtt": [
                        "IamRoleLambdaExecution",
                        "Arn"
                    ]
                },
                "Runtime": "nodejs10.x",
                "Timeout": 300
            },
            "DependsOn": [
                "SlackLogGroup",
                "IamRoleLambdaExecution"
            ]
        },
        "SlackLambdaVersionEoC4WbwxFBAlMbb8V5JeomKbnUYx1nxynGLGsoDBvqs": {
            "Type": "AWS::Lambda::Version",
            "DeletionPolicy": "Retain",
            "Properties": {
                "FunctionName": {
                    "Ref": "SlackLambdaFunction"
                },
                "CodeSha256": "T1d8cKfq8lCyaPzZCKTg5xF2m+JaSzPfJ4D63PdqDtc="
            }
        }
    },
    "Outputs": {
        "ServerlessDeploymentBucketName": {
            "Value": {
                "Ref": "ServerlessDeploymentBucket"
            }
        },
        "SlackLambdaFunctionQualifiedArn": {
            "Description": "Current Lambda function version",
            "Value": {
                "Ref": "SlackLambdaVersionEoC4WbwxFBAlMbb8V5JeomKbnUYx1nxynGLGsoDBvqs"
            }
        }
    }
}

 

 

참고

1. https://www.dwolla.com/updates/aws-lambda-function-serverless/

 

 

  1.  
반응형

기본적으로 AWS console을 통해서 리소스를 제어해보는 선수과정이 필요해보인다.

각 리소스의 특성들을 파악하고 이해하고 원하는걸 이해했다면

CloudFormation으로 반복작업들을 가져다 사용하는 흐름을 적용시켜보자.

 

기본적으로 자주 사용하는 네트워크 구성이다.

 


1. 개발용

기본적으로 개발이 가능하도록 인프라 구성을 하였다.

Security Group의 제한을 받는 Public한 DB가 특이점이라 할 수 있다.

 

특징

  1. DB가 public access
  2. Private Subnet의 리소스는 NAT로 트래픽이 빠져나감
  3. InternetGateway로 트래픽이 빠져나가는 Public Subnet
  4. Security Group 흐름이 DB → 중간 리소스 → 웹 (최종) 으로 흘러감.
  5. 맨 오른쪽은 Cluster.

 

2. 운영용

운영을 위한 구성도이다.

특징

  1. Private한 DB
  2. Tunneling용 EC2 인스턴스.
  3. Private Subnet의 리소스는 NAT로 트래픽이 빠져나감
  4. DB사이즈가 개발용보다 크다.
  5. 맨위 리소스가 Cluster.

 

 

 

위 내용을 기반으로 하여 개발용과 운영용의 차이를 이해하여본다.

반응형

각 단어에 대한 상세설명은 1. 간략 개요 글을 읽어주기 바랍니다.

각 단계에서 일어나는 일들을 위 도식도 순서에 맞게 설명을 덧붙여 보겠습니다.

PROVISIONING

내부적으로 준비하는 상태( 간략개요글 참고 )

PENDING

ECR에서 Docker 이미지를 가져와 실행

이때 ECR이미지는 이미 빌드가 완료된 Docker image를 저장하고 있어야 한다.

특별한 설정을 하지 않았다면, 빌드가 잘못되었다고해서 run이 안되진 않는다.

ACTIVATING

TargetGroup에 Task를 등록하는 상태

이때 Task Definition에는 서버 리소스 사용, 환경변수, 여러 상태를 미리

정의시켜놓고 늘 그 상태로 배포될수있는 외적인 요소들이라고 생각하면 된다.

 

이때 제일 많이 나는 오류

  1. ECR의 주소를 못찾아서

RUNNING

이때는 배포한다는 흐름상

Old_Service가 New_Service로 교체되는 상황이 생긴다.

위에서 TargetGroup이 Old_Service와 New_Service의 HealtyCheck를 하여서

서비스 교체의 여부를 결정짓는다.

이때는 여러 오류가 생겨서 Old_Service와 New_Service의 교체가 이루어 지지 않는다.

다음과 같은 오류들이 생긴다.

  1. DB Connection 오류 ( 인프라상 접근이 불가하면 Connection Time out )
  2. 로드밸런서의 상태확인의 실패 ( TargetGroup 에서 healty Check Path 확인 )
  3. 로드밸런서의 상태확인 코드 설정 범위 ( 기본 200 → 200-404 까지 범위 허용 )
  4. 로드밸런서의 상태체크 횟수 확장 ( 기본 2 → 5 )
  5. Service 설정시 설정 값.
    healthCheckGracePeriod :
    정의된 기간동안 ALB의 상태확인은 무시된다.

    => 애플리케이션이 로드되는 시간을 넉넉잡아 해두어야 한다.

이 밖에도 여러 오류가 생긴다.

해당 오류를 디버깅하기 위해서는 두가지만 유의깊게 보면 된다.

  1. Running으로 돌아서지만 계속 STOPPED되는 경우 ⇒ Task Definition에서 CloudWatch 로그 경로를 꼭 설정하고 로그를 확인.
  2. Running이 보이기도 전에 STOPPED되는 경우 ⇒ 이때는 꼭 STOPPED된 Task를 들어가 상세한 정보를 확인하여야한다. 보통 ECR Pull Fail 이런 오류가 많이 날텐데 ECR url을 올바르게 수정해본다.

DEACTIVATING

TargetGroup에서 Task가 등록 취소 된다.

Old_Service와 New_Service의 교체가 이루어지는 과정에서 성공이든 실패든 해당 과정으로 오게되어있다.

재시도를 할 수 있고 정상 처리가 될 수도있다.

해당 상태로 성공적인 배포라 판단하면 안된다.

 

STOPPING

실행중인 컨테이너를 정상적으로 종료하는 프로세싱 단계

이미 중지되었다면 처리되지 않지만 정상적으로 설정되었다면 StopTimeout 설정값 까지 대기하고 컨테이너를 강제 종료. (기본값 30초)

 

DEPROVISIONING

생성된 리소스, 네트워크에서 정리 및 분리

 

STOPPED

최종적으로 정리된 상태


참고 :

https://garbe.io/blog/2020/03/04/deep-dive-ecs-deployment/

'Cloud > AWS-ECS' 카테고리의 다른 글

[AWS] TaskLifeCycle 단어 정의  (0) 2020.12.12
[AWS] Schduled task(EC2/Fargate)  (0) 2020.12.12
반응형

수명주기 상태

다음은 각 작업 수명주기 상태에 대한 설명입니다.

 

PROVISIONING

Amazon ECS는 작업을 시작하기 전에 추가 단계를 수행해야합니다. 
예를 들어 awsvpc네트워크 모드 를 사용하는 작업의 경우 탄력적 네트워크 인터페이스를 프로비저닝해야합니다.

 

PENDING

이는 Amazon ECS가 추가 작업을 수행하기 위해 컨테이너 에이전트를 기다리는 전환 상태입니다.

 

ACTIVATING

Amazon ECS는 작업이 시작된 후 작업이 RUNNING상태로 전환되기 전에 추가 단계를 수행해야합니다 . 
예를 들어 서비스 검색이 구성된 작업의 경우 서비스 검색 리소스를 만들어야합니다. 

여러 Elastic Load Balancing 대상 그룹을 사용하도록 구성된 서비스의 일부인 작업의 경우 대상 그룹 등록이이 상태에서 발생합니다.

 

RUNNING

작업이 성공적으로 실행 중입니다.

 

DEACTIVATING

Amazon ECS는 작업이 중지되기 전에 추가 단계를 수행해야합니다. 

예를 들어 여러 Elastic Load Balancing 대상 그룹을 사용하도록 구성된 서비스의 일부인 작업의 경우
대상 그룹 등록 취소가이 상태에서 발생합니다.

 

STOPPING

이는 Amazon ECS가 추가 작업을 수행하기 위해 컨테이너 에이전트를 기다리는 전환 상태입니다.

 

DEPROVISIONING

Amazon ECS는 작업이 중지 된 후 작업이 STOPPED상태로 전환되기 전에 추가 단계를 수행해야합니다 . 

예를 들어 awsvpc네트워크 모드 를 사용하는 작업의 경우 탄력적 네트워크 인터페이스를 분리하고 삭제해야합니다.

 

STOPPED 작업이 성공적으로 중지되었습니다.

 

 


 

참고 :

https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-lifecycle.html

'Cloud > AWS-ECS' 카테고리의 다른 글

[AWS] TaskLifeCycle 상세 설명  (0) 2020.12.12
[AWS] Schduled task(EC2/Fargate)  (0) 2020.12.12
반응형

Batch작업이 있어 기존에 EC2로 작업해두었던 Task

Fargate형식으로 바꾸는 작업을 진행을 하였다. 

기존의 방식은 스케줄링에 의해서 EC2인스턴스를 일정기간동안 띄워서

해당 스케줄링이 정상 처리되도록 task를 작동시키는 방법이었다.

 

1. Create버튼 클릭

2. 맨아래 Launch Type 에서 EC2로

 

문제는 EC2는 비싸다는것이다.
실무 시스템상 EC2의 고정 리소스는 필요가 없었고 필요할때만 쓰면 되는 것이었다.
람다도 좋지만 람다의 특성이 있기때문에 ECS에서 빌드하여 배포된 이미지로 작업을 해야했다.

작업 순서는 위와 동일화고 Launch Type만 Fargate로 진행하면 된다.


1. Create버튼 클릭

2. 맨아래 Launch Type 에서 Fargate

 

 

Cron Expression은 친절하게 입력하면 스케줄 표가 보이니 하나씩 변경해보면

금방 이해가 될 것이다.

'Cloud > AWS-ECS' 카테고리의 다른 글

[AWS] TaskLifeCycle 상세 설명  (0) 2020.12.12
[AWS] TaskLifeCycle 단어 정의  (0) 2020.12.12
반응형

탄력적 IP란 ?


고정아이피를 쓰기 위해서 할당받는 것이다.

AWS메뉴에서 네트워크 및 보안 >  Elastic IP 라는 메뉴에서 확인할 수 있다.



현재 A인스턴스가 있다면 A인스턴스는 유동 아이피로 설정되어있다.

인스턴스를 STOP해버리면 원치 않게 아이피가 변경되버리곤 한다.


이래서 탄력적 IP를 발급받아 A인스턴스에 설정을 해주어야 한다.



아래는 참고 사진이다. 아래 사진과 같은 메뉴에서 IP를 발급 받으면 된다.


'Cloud > AWS-Resources' 카테고리의 다른 글

[AWS] pem키를 잃어버렸을때.  (0) 2017.11.26
반응형

일단 문제사항으로는.

* mac 환경에서 sublime text3 에서 connection timeout 이 계속 발생.

- 해결을 하기위해 Desktop/.ssh  폴더안의 key값을 변경을 시도.

- 서버의  authorize_key(?) 를 변경시도함.

- 그러자 mac Terminal에서는 되던 ssh도 먹통. 


초기 증상에서는 단순히 sublime Text3에서 오류라고 생각 되었지만 해결하고나니까 ssh관련 key의 문제가 꼬여있었던것으로 생각이 된다.

현재는 정상적으로 sublime text3 , terminal ssh connection이 이루어지고 있다.




위의 문제상황에서 가장 문제가 되던 부분은 

변경을 시도하다가 authorize_key를 건들여 버렸다는 것이다. 


public key와 private key가 일치하도록 하는 부분인데 

그곳을 바꿔버렸으니 모든게 먹통이 되버렸다고 한다 푸헤헤헤헤헤..






그래서 해결과정을 정리해보려고한다.

(= 루트 디바이스를 재 부여하는 방법),    (= 백업 디바이스를 루트 디바이스로 설정 하는 방법)



1. 자신의 aws console로 접속을 한다.


2. 일단 인스턴트를 정지한다. 

(     - 임시 데이터가 모두 날라가니 주의 바람. 종료가 아닌 중지를 해야 한다.  

-탄력적 ip를 설정하지 않았다면 ip도 변경될 수 있다.)

위 사진에서 stop 노랑 상태로 둔다.


3. 하나밖에 없다면 자신의 볼륨 유형을 그대로 두도록 한다. (1~3번 내용은 화면을 일단 숙지하고 확인하는 작업이다.)


4. 다시 인스턴스 메뉴로가서 새로운 인스턴스를 생성한다.

4-1. 생성할때 next로 넘기다보면 pem관련된 항목이 존재한다.

4-2. 인스턴스를 생성할때 기존의 pem키를 그대로 쓰려면 기존 키로 그대로 설정하면 된다. 


5. 생성한 뒤에 새로생성한 인스턴스를 중지상태로 설정한다.


6. 기존에 쓰던 볼륨을 연결 해제한다.

아래 사진에서 

위의 볼륨이 새로 생성된 볼륨이고

아래의 볼륨의 기존 볼륨이다.

모두 해제된 상태로 만들어 버리면 된다.

7. 기존 볼륨을 새 인스턴스에 할당을 한다.( 6~7번 문항이 키를 새로 세팅 하는 작업)

오른쪽 버튼을 새로 할당된 볼륨에게 올려두면 작업이 가능하다.


8. 이후에 새로 생성한 인스턴스를 시작합니다.

9. 확인을 하면 제대로 접속이 되는걸 확인 할 수 있습니다.




자세한 설명은 없지만  AWS자체의 인터페이스는 각 메뉴에 제한된 기능을 제공하므로

개발자또는 사용자라면 충분히 어떤메뉴가 있는지 확인해볼 수 있을 것이다.




다음에디터라그런가 사진을 편집하기가 상당히 어렵다. 

미리 꾸미고 올리도록 해야겠다.

'Cloud > AWS-Resources' 카테고리의 다른 글

[AWS]Elastic IP  (0) 2017.11.26

+ Recent posts