반응형

리액트를 공부하지 않고 감으로 시작하시는 분들은 이런 에러를 봤을 수 있고

왜 일어나는지에 대해서 의문을 품을 수 있다. 

 

내가 겪은 에러였고 왜 그런지에 대해서 알고 싶었다. 

https://ko.reactjs.org/docs/hooks-overview.html#building-your-own-hooks

 

Class에서 구현하느냐 Function에서 구현하느냐 이 차이였다.

 

내가 맞이한 에러는 아래와 같다 

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

 

 

export class SampleClass extends ... {

  const {loading, error, data} = useQuery(SampleGQL);  render(){
    return (<>      ...
    </>)
  }
}
export const SampleClass = () => {
  const {loading, error, data} = useQuery(SampleGQL);
      return <>
        ...
      </>
}

위 두 코드중에서 에러를 발생하는 코드는 어떤 것일까?

위의 문서를 잘봤다면 Class로 구현한 스타일에 문제가 있다는 것을 알 것이다. 

 

 

오류가 나지않으려면 아래 코드와 같은 Function으로 구현하여 렌더링을 해야할 것이다.

 

function으로 구현할때는 아래와 같은것들을 고려해보자.

컴포넌트가 LifeCycle API도 사용하지 않고, state도 사용하지 않고, props만 전달해주는 뷰를 렌더링한다면, 함수형 컴포넌트를 사용할 수 있다.

 

반응형

Naver Map을 설치해보자

https://github.com/react-native-seoul/react-native-kakao-login

위내용을 보며 따라간다.

  • npm은 오류납니다
    npm install react-native-nmap@0.0.66 --save
    
    전체적으로 버전을 하향해야한다.
    ...
    "dependencies: {
    	
    },
    ...
    
    위의 내용으로 설치를 진행하면 npm에서 충돌로 인해서 더이상 진행이 불가하다.

 

 

yarn install react-native-nmap@0.0.66 --save

위 명령어로 진행해야 해당 패키지가 정상적으로 설치 된다.

설치후에 ios package 를 설치해야 한다.

설치 후 에러가 발생할 수 있다.

Installing NMapsMap (3.16.0)

[!] Error installing NMapsMap
[!] /usr/bin/git clone <https://github.com/navermaps/NMapsMap.git> /var/folders/nd/lsjr321s7sxbss72ggwkq5sr0000gn/T/d20221118-42365-1i2mgxz --template= --single-branch --depth 1 --branch release/3.16.0

Cloning into '/var/folders/nd/lsjr321s7sxbss72ggwkq5sr0000gn/T/d20221118-42365-1i2mgxz'...
Note: switching to 'd34a86bee0756adf7e40636f12712315f8de975c'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c 

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

git-lfs filter-process: git-lfs: command not found
fatal: the remote end hung up unexpectedly
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry with 'git restore --source=HEAD :/'

Couldn't install Pods. Updating the Pods project and trying again...
> pod install --repo-update
Auto-linking React Native modules for target `BSpotReactNative`: RNCAsyncStorage, RNScreens, RNVectorIcons, kakao-login, react-native-nmap, react-native-safe-area-context, and react-native-splash-screen
[Codegen] Generating ./build/generated/ios/React-Codegen.podspec.json
Updating local specs repositories

CocoaPods 1.11.3 is available.
To update use: `gem install cocoapods`

For more information, see <https://blog.cocoapods.org> and the CHANGELOG for this version at <https://github.com/CocoaPods/CocoaPods/releases/tag/1.11.3>

Analyzing dependencies
[Codegen] Found FBReactNativeSpec
Downloading dependencies
Installing Alamofire (5.6.2)
Installing KakaoSDKAuth (2.11.1)
Installing KakaoSDKCommon (2.11.1)
Installing KakaoSDKUser (2.11.1)
Installing NMapsMap (3.16.0)

[!] Error installing NMapsMap
[!] /usr/bin/git clone <https://github.com/navermaps/NMapsMap.git> /var/folders/nd/lsjr321s7sxbss72ggwkq5sr0000gn/T/d20221118-42481-1bq6ix8 --template= --single-branch --depth 1 --branch release/3.16.0

Cloning into '/var/folders/nd/lsjr321s7sxbss72ggwkq5sr0000gn/T/d20221118-42481-1bq6ix8'...
Note: switching to 'd34a86bee0756adf7e40636f12712315f8de975c'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c 

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

git-lfs filter-process: git-lfs: command not found
fatal: the remote end hung up unexpectedly
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry with 'git restore --source=HEAD :/'

Couldn't install Pods. Updating the Pods project and trying again...
Command `pod install` failed.
└─ Cause: Error installing NMapsMap
[!] /usr/bin/git clone <https://github.com/navermaps/NMapsMap.git> /var/folders/nd/lsjr321s7sxbss72ggwkq5sr0000gn/T/d20221118-42481-1bq6ix8 --template= --single-branch --depth 1 --branch release/3.16.0

Cloning into '/var/folders/nd/lsjr321s7sxbss72ggwkq5sr0000gn/T/d20221118-42481-1bq6ix8'...
Note: switching to 'd34a86bee0756adf7e40636f12712315f8de975c'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c 

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

git-lfs filter-process: git-lfs: command not found
fatal: the remote end hung up unexpectedly
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry with 'git restore --source=HEAD :/'

위와 같은 에러가 발생한다면 아래 사이트를 방문하여

해당하는 명령어를 위에서부터 아래로 순차적으로 실행한다.

항상 명령어는 나한테 해당하는건지 아닌지 확인해가며 실행한다.

https://github.com/navermaps/ios-map-sdk?ref=morioh.com&utm_source=morioh.com#대용량-파일을-받기-위해-git-lfs-설치가-필요합니다

보통은 cocopods는 설치되어있으니 나는 아래와 같이 명령어를 실행했다.

 

brew install git-lfs
git-lfs install
pod install --repo-update

 

네이버 NMap 설치 

'프론트엔드 > ReactNative' 카테고리의 다른 글

[ReactNative] Class Component Error  (0) 2022.11.22
반응형

분명 잘 구현한거같은데 해당 토큰을 넣고 테스트를 진행하면

자꾸 인증이 되지 않는다는 메세지를 보게되었을때 점검해봐야하는 부분이다.

 

다른 여러가지 부분을 확인해봐야하겠지만

내 구현엔 문제가 없는데... 이런생각이라면

import한 부분을 체크해보자.

 

import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-local'; # 여기가 문제
import { ExtractJwt } from 'passport-jwt';

export class GraphqlStrategy extends PassportStrategy(Strategy, 'jwt') {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_SECRET,
      usernameField: 'email',
      passwordField: 'password',
    });
  }

  async validate(token: string) {
    return token;
  }
}

 

위에서 문제는 passport-jwt와 passport-local을 호환해서 사용했기 때문이다.

 

 

import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';

export class GraphqlStrategy extends PassportStrategy(Strategy, 'jwt') {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_SECRET,
      usernameField: 'email',
      passwordField: 'password',
    });
  }

  async validate(token: string) {
    return token;
  }
}

 

반응형

일반적으로 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"

 

반응형

schedule_interval

Airflow에서 Dag들이 실행하는데 정시에 실행이 되지않고 약간의 초가 지난뒤에 실행되는걸 볼수있다.

Run: ... 이 부분을 확인할 것

이유는

with models.DAG(
        MAIN_DAG_ID,
        default_args=default_args,
        schedule_interval='*/30 * * * * *',
        start_date=datetime(2021, 10, 12, 0, 0, 0),
        catchup=False,
        tags=["hyper-dmp", "export", "trait", "ga", "dynamic_segment"],
) as dag:
    ...

property 중 schedule_interval 값을 6자리로 유지하면 안된다.

5자리 cron expression식으로 쓸것.

그러면 원했던 시간에 실행되는 것을 볼 수 있다.

다음으로

start_date

이 속성은 현재 시간보다 앞서나가면 실행이 안된다.

예약의 조건으로 사용하거나
catchup=true속성으로 지난 시간들의 모든 schedule을 가져올때 사용하던지 해야한다.

위 코드에 없는

execution_time

해당 부분을 dag이 실행될때 부여되는 속성이다.
이상하게도 schdule_interval을 6자리 할때도 execution_time은 정확하게 부여되었다.

중요한건 해당 execution_time이 실행될때 바로 전 타임 execution_time을 확인할 수 있다는 것이다.

예를들어

현재시간 : 13:00
with models.DAG(
        MAIN_DAG_ID,
        default_args=default_args,
        schedule_interval='*/30 * * * * *',
        start_date=datetime(2021, 10, 12, 0, 0, 0),
        catchup=False,
        tags=["hyper-dmp", "export", "trait", "ga", "dynamic_segment"],
) as dag:

위 코드로 현재시간에 실행된다면 execution_time값은 무엇일까 ?

답은

12:30

이다.

누군가의 설명에 따르면 schedule_interval 간격마다 바로 전 execution_time을 실행한다

로 이해하라고 되어있었다.

반응형

gunicorn에 대해서 처음 작업해보았는데 성능을 개선해보아야했다.

worker라던지, gevent라던지 이런 부분에 대해서 알고있어야하는데

관련해서 추천할 블로그이다.

http://blog.hwahae.co.kr/all/tech/tech-tech/5567/

오늘의 집 테크 블로그인데 gunicorn에 대해서 공부하기 좋다.

나름 글이 잘읽히고 정보의 질이 좋다.

위 내용이 어느정도 읽히고 테스트를 해보았다면

오늘 에러관련내용해서 쓰는글을 이해하기 편할것이다.

문제가 되는 부분의 코드이다.

import logging.config
from gevent.monkey import patch_all
patch_all()
# 이 부분이 해결하기 위한 코드 
# import grpc.experimental.gevent as grpc_gevent
# grpc_gevent.init_gevent()

from flask import Flask
from config.default import CONFIG
from views.pixel_view import bp
from config.logger import LOGGING_CONFIG
from views.scheduler import scheduler, get_destination_template

def create_app():
    logging.config.dictConfig(LOGGING_CONFIG)
    if CONFIG['APP_ENV'] == 'PRODUCTION':
        logging.disable(logging.DEBUG)
    else:
        logging.disable(logging.NOTSET)

    ap = Flask(__name__)

    ap.url_map.strict_slashes = False
    ap.register_blueprint(bp)

    ap.config['SCHEDULER_API_ENABLED'] = True

    if not scheduler.running:
        scheduler.init_app(ap)
        scheduler.start()
        # break point
    **get_destination_template()**

    return ap

app = create_app()
logger = logging.getLogger('default')

if __name__ == '__main__':
    logger.info('APP START AND POLL START')
    app.run(port=5001)




위 코드를 보면 python cronjob도 돌리고있고 api서버의 기능도 있다.

스케줄링관련 프로세스에는 google 제품을 사용하는 코드들이 있다. bigtable요청관련된 부분이다.

그렇지만 위에 break point를 걸어보면 아래와 같은 코드에서 freezing이 걸린다.

def _run_channel_spin_thread(state):

    def channel_spin():
        while True:
            #freezing point 
            cygrpc.block_if_fork_in_progress(state)
            event = state.channel.next_call_event()
            if event.completion_type == cygrpc.CompletionType.queue_timeout:
                continue
            call_completed = event.tag(event)
            if call_completed:
                with state.lock:
                    state.managed_calls -= 1
                    if state.managed_calls == 0:
                        return

    channel_spin_thread = cygrpc.ForkManagedThread(target=channel_spin)
    channel_spin_thread.setDaemon(True)
    channel_spin_thread.start()




이 파일의 경로는 아래와 같다





grcp에서 프로세스를 fork하는 부분에서 freezing이 걸리는걸 확인하는 순간이다.

충돌이 일어난다고 볼수 있다.

이 부분을 해결하기 위해선 첫 코드에서 주석을 해제하는 코드를 작성해야한다.

import logging.config
from gevent.monkey import patch_all
patch_all()

# 이 부분이 해결하기 위한 코드 
import grpc.experimental.gevent as grpc_gevent
grpc_gevent.init_gevent()

from flask import Flask
from config.default import CONFIG
from views.pixel_view import bp
from config.logger import LOGGING_CONFIG
from views.scheduler import scheduler, get_destination_template

def create_app():
    logging.config.dictConfig(LOGGING_CONFIG)
    if CONFIG['APP_ENV'] == 'PRODUCTION':
        logging.disable(logging.DEBUG)
    else:
        logging.disable(logging.NOTSET)

    ap = Flask(__name__)

    ap.url_map.strict_slashes = False
    ap.register_blueprint(bp)

    ap.config['SCHEDULER_API_ENABLED'] = True

    if not scheduler.running:
        scheduler.init_app(ap)
        scheduler.start()
        # break point
    **get_destination_template()**

    return ap

app = create_app()
logger = logging.getLogger('default')

if __name__ == '__main__':
    logger.info('APP START AND POLL START')
    app.run(port=5001)




사용한 패키지 버전이다.

gevent = "~=21.8.0"
gunicorn = "~=20.1.0"

google제품과 사용 시 freezing현상이 있다면 이 부분을 확인해보면 도움이 될 거 같다.

'백엔드 > Python' 카테고리의 다른 글

[Python]ModuleNotFoundError: No module named '_ctypes'  (0) 2021.01.04
반응형

실패상황1

1Airflow에서 mysql_default를 사용중에 localhost:3306을 설정하여 커넥션을 시도하였으나
실패하였다.

실패상황2

docker inspect containerId 를 통해서 내부 ip를 파악해여 커넥션을 시도하였으나
실패하였다.

이유가 무엇일까 생각해보니 네트워크가 서로 분리되어있었다.

mysql은 기본 네트워크 (bridge)에 존재하였고

airflow는 생성할때 입력한 네트워크에 container들과 함께 생성되었다.

# 네트워크 리스트 명령어
docker network ls

# 해당 네트워크에 어떤 컨테이너가 있는지, 또는 정보를 확인하는 명령어
docker network inspect networkName

# docker network inspect bridge 
# docker network inspect "airflow network name"

서로 다른 네트워크에 존하는것을 확인하였다. 그렇다면 어떻게 해야할까 ?



내가 작업한 방법은 airflow network에 mysql container를 붙여서 내부 아이피를 사용하는 방법을 사용하였다.

먼저 local-mysql container를 bridge 네트워크에서 제거하는 명령어

docker network disconnect bridge local-mysql




이후 airflow network에 local-mysql을 붙이는 명령어

docker network connect "airflow network name" local-mysql




이후 잘되었는지 확인

docker network inspect "airflow network name"
# docker network inspect hyper-dmp-v2-workflow_default 결과

[
    {
        "Name": "hyper-dmp-v2-workflow_default",
        "Id": "97e809b8c591b205ab25ac0ac0c30efcbea1628cf0dbafb977a5ff00af551906",
        "Created": "2021-08-17T06:18:29.488748Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "290d92d8050eabee9e160f3e0439df8fb5d4d4d622a8f14317f68966c68d9947": {
                "Name": "workflow_airflow-worker_1",
                "EndpointID": "f28016d500d349b9dcc123db5f4312bca8226b6ebb3d4552f571b732b7e6be65",
                "MacAddress": "02:42:ac:12:00:05",
                "IPv4Address": "172.18.0.5/16",
                "IPv6Address": ""
            },
            "3ea2b515e45b2bd3d17b4e5b34f8ddb3ad5eb5d773c55a71724d7dfa7eb9a8c8": {
                "Name": "workflow_airflow-webserver_1",
                "EndpointID": "3e38cb185a20ba33157d363574b2fd6844c3834ebe633713321dbcd7ece26a4d",
                "MacAddress": "02:42:ac:12:00:07",
                "IPv4Address": "172.18.0.7/16",
                "IPv6Address": ""
            },
            "5a2cebcd308971a2db78a321ba99f304004111fcdffab2648f2264d8e156a359": {
                "Name": "workflow_airflow-scheduler_1",
                "EndpointID": "2cd982cea25db9651d98a503d9009e1ed20d68c09c513ca9c5894708d06b58ee",
                "MacAddress": "02:42:ac:12:00:04",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
            },
            **"8e68ca23d180eca523b5c1bc35d44c11c5ffab95b7734724279582313df5d0c9": {
                "Name": "local-mysql",
                "EndpointID": "e9dd16cdf8fdd1856cea04ee2ba74948928bd796b681f8182c81ac6ed1711f6e",
                "MacAddress": "02:42:ac:12:00:06",
                "IPv4Address": "172.18.0.6/16",
                "IPv6Address": ""
            },# 이 부분이 추가 된 부분**
            "b3788433fb68182e847b8d782e38cd07caffdf4f7de41bba59a8705d1cd00cc9": {
                "Name": "workflow_redis_1",
                "EndpointID": "6ed71a24bf27c327ed996fbaae0ade72bee75c4e371b80d11aefff8167aadef7",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "c3a4444096dd17a2d43449fc5c0273c786ba9c14cd328ef3818bc6724cd985d0": {
                "Name": "workflow_flower_1",
                "EndpointID": "27d9564194beebf24cced1e5802e5e49b2d55e2bc89cea6f4ef95d26481f2d6f",
                "MacAddress": "02:42:ac:12:00:08",
                "IPv4Address": "172.18.0.8/16",
                "IPv6Address": ""
            },
            "ce08580c8b40b50798a37ab39072e22f3bc9c37cae1fc9adb328ec5cc0b1ac53": {
                "Name": "hworkflow_postgres_1",
                "EndpointID": "d7640e67ec833bb595c4af43ce374d80eeaf049a70af8e0c9afc68c098e6a40f",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "default",
            "com.docker.compose.project": "workflow",
            "com.docker.compose.version": "1.29.1"
        }
    }
]

A네트워크에 Container를 붙여서 내부아이피를 사용하게 만드는 법이다.

보통 로컬 컴퓨터에 DB를 깔고 docker로 다른 제품을 사용하다보면 이렇게 네트워크까지 가지고있는 docker compoer파일들이 있는데
이런것들을 사용하던 로컬 DB와 혼용할때 유용하다.

반응형

젠킨스를 사용하다보면 상단의 업데이트가 표기된것을 볼 수 있다.

빨간 숫자 2

해당 표기를 눌러보면 Plugins, Jenkins 업데이트하라는 내용이 대부분이다.

젠킨스를 업데이트하여 최신의 기능을 빠르게 적용시켜보자.

젠킨스 경로

# 젠킨스 경로 이동
cd /usr/lib/jenkins

# jenkins.war 파일 확인
ls -al

# 최종 경로 
# /usr/lib/jenkins/jenkins.war

기존 jenkins file 백업

#기존의 jenkins.war는 백업
cp jenkins.war jenkins_20200529.war

{

젠킨스 업데이트 파일 다운로드

wget http://mirrors.jenkins-ci.org/war/latest/jenkins.war

또는

자신이 운영하는 젠킨스 사이트에서 업데이트 하라는 링크를 직접 입력한다. (버전이 명시적임)

wget $LINK

}

젠킨스 재시작

service jenkins restart
  • 젠킨스 파일 찾기
find / -name 'jenkins.war'
반응형

가끔 프로젝트를 생성하다보면 내가 원하지 않은 파일이 올라가있을때가 있다.

올라간것을 지우려면 직접 Github repository에 가서 변경하거나 귀찮게 작업을 해왔었다.

로컬에서 작업을 진행 후 push를 해서 반영가능하게 하는 작업을 아래와 같이 설명 한다.

아래는 bin폴더가 잘못 올라간 상황을 가정한다.

  1. git ignore 파일에 아래와 같이 추가, 작성한다.
bin/
  1. 아래와 같은 명령어를 실행한다.
git rm -r --cached bin

3. push 한다. 

git push 리모트-저장소이름 로컬-브렌치명


4. 리모트 저장소를 확인해보면 없어진것을 확인할 수 있다.

'Utils > Github' 카테고리의 다른 글

[Github] Projects 메뉴 활용  (0) 2021.01.10
[GitHub] 프로젝트 삭제 방법 - 웹  (0) 2016.09.15
반응형

서비스 운영 중 다음과 같은 에러가 발생되었다.

AWS CloudWatch Error Log:

2020-04-16 12:38:00.373 ERROR 1 --- [pool-1-thread-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task
org.springframework.dao.DeadlockLoserDataAccessException: PreparedStatementCallback; SQL [DELETE FROM SPRING_SESSION WHERE EXPIRY_TIME < ?]; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:267) ~[spring-jdbc-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72) ~[spring-jdbc-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443) ~[spring-jdbc-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) ~[spring-jdbc-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:862) ~[spring-jdbc-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:917) ~[spring-jdbc-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:927) ~[spring-jdbc-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.session.jdbc.JdbcIndexedSessionRepository.lambda$cleanUpExpiredSessions$8(JdbcIndexedSessionRepository.java:553) ~[spring-session-jdbc-2.2.0.RELEASE.jar!/:2.2.0.RELEASE]
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.session.jdbc.JdbcIndexedSessionRepository.cleanUpExpiredSessions(JdbcIndexedSessionRepository.java:553) ~[spring-session-jdbc-2.2.0.RELEASE.jar!/:2.2.0.RELEASE]
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93) [spring-context-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_151]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_151]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_151]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_151]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_151]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_151]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_151]
Caused by: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:123) ~[mysql-connector-java-8.0.19.jar!/:8.0.19]
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.19.jar!/:8.0.19]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.19.jar!/:8.0.19]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953) ~[mysql-connector-java-8.0.19.jar!/:8.0.19]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1092) ~[mysql-connector-java-8.0.19.jar!/:8.0.19]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1040) ~[mysql-connector-java-8.0.19.jar!/:8.0.19]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1347) ~[mysql-connector-java-8.0.19.jar!/:8.0.19]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1025) ~[mysql-connector-java-8.0.19.jar!/:8.0.19]
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61) ~[HikariCP-3.4.2.jar!/:na]
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java) ~[HikariCP-3.4.2.jar!/:na]
at org.springframework.jdbc.core.JdbcTemplate.lambda$update$0(JdbcTemplate.java:867) ~[spring-jdbc-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:617) ~[spring-jdbc-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]

Mysql 에서 자신의 Transaction Isolation Level을 확인하려면 다음과 같은 명령어를 사용한다

show variables like 'tx_isolation';

위의 에러는 REPEATABLE READ 라는 상태일 때 나타나게 된다.

참고 문서에 아래와 같이 나와있다.

REPEATABLE-READ에서는 현재 Select 버전을 보장하기 위해 Snapshot을 이용하는데, 
이 경우 해당 데이터에 관해서 암묵적으로 Lock과 비슷한 효과가 나타납니다.

즉, Select 작업이 종료될 때까지 해당 데이터 변경 작업이 불가합니다.

tx_isolation 변수의 상태는 4가지가 있다.

  • READ UNCOMMITTED
    다른 트랜잭션이 Commit 전 상태를 볼 수 있음Binary Log가 자동으로 Row Based로 기록됨 (Statement설정 불가, Mixed 설정 시 자동 변환)
  • READ-COMMITTED
    Commit된 내역을 읽을 수 있는 상태로, 트랜잭션이 다르더라도 특정 타 트랜잭션이 Commit을 수행하면 해당 데이터를 Read할 수 있음Binary Log가 자동으로 Row Based로 기록됨 (Statement설정 불가, Mixed 설정 시 자동 변환)
  • REPEATABLE READ
    MySQL InnoDB 스토리지 엔진의 Default Isolation LevelSelect 시 현재 데이터 버전의 Snapshot을 만들고, 그 Snapshot으로부터 데이터를 조회동일 트랜잭션 내에서 데이터 일관성을 보장하고 데이터를 다시 읽기 위해서는 트랜잭션을 다시 시작해야 함
  • SERIALIZABLE
    가장 높은 Isolation Level로 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock이 걸림다른 트랜잭션에서는 해당 영역에 관한 데이터 변경 뿐만 아니라 입력도 불가

Oracle 에서는 READ-COMMITTED 상태가 Default 상태여서 해당 에러가 나타나지 않는다.

Mysql 의 tx_isolation변수값을 READ-COMMITTED 값으로 변경해야지 해당 에러가 발생하지 않는다.

참고 :

  1. https://gywn.net/2012/05/mysql-transaction-isolation-level/

'DataBase' 카테고리의 다른 글

[Mysql] Mysql 계정 설정  (0) 2021.01.23
반응형

DB 계정 정책 적용 목록

  1. 최소길이 & 복잡도 점검 (10자리 & 2가지 조합 (소문+숫자+특수문자))
  2. 변경 주기 90일
  3. 계정잠금 5회
  4. 직전 비밀번호 사용금지 최소 2개 이상
  5. 세션 타임아웃
  6. 동시 접속자 차단
  7. 비밀번호 실패 시 계정 잠금 5회 10분후 자동 잠금 해제

최소길이 & 복잡도 점검 (10자리 & 2가지 조합 (소문+숫자+특수문자))

# plugin 설치
INSTALL PLUGIN validate_password SONAME 'validate_password.so';
# plugin 상태 조회
SELECT PLUGIN_NAME, PLUGIN_STATUS
FROM INFORMATION_SCHEMA.PLUGINS
WHERE PLUGIN_NAME LIKE 'validate%';
# 패스워드 정책 조회 
SHOW VARIABLES LIKE 'validate_password%';

참고 :
AWS 공식 문서 : https://aws.amazon.com/ko/about-aws/whats-new/2019/05/amazon-rds-for-mysql-supports-password-validation/

Mysql Plugins 가이드 :

변경 주기 90일

# 최대 비밀번호 만료일 조회 
SELECT password_lifetime FROM mysql.user
WHERE user='test' AND host='%';
# 최대 비밀번호 만료일 조회 
UPDATE mysql.user SET password_lifetime = 90
WHERE user='test' AND host='%';

FLUSH PRIVILEGES;

동시접속자 차단

# 최대 커넥션 개수 조회 
SELECT max_user_connections FROM mysql.user
WHERE user='test' AND host='%';
# 최대 커넥션 개수 업데이트 
UPDATE mysql.user SET max_user_connections = 1
WHERE user='test' AND host='%';

FLUSH PRIVILEGES;

'DataBase' 카테고리의 다른 글

[Mysql] Deadlock found when trying to get lock  (0) 2021.01.24
반응형

.tt_article_useless_p_margin > p > span

ERROR in multi eventsource-polyfill webpack-hot-middleware/client?reload=true&timeout=30000&ansiColors=&overlayStyles=&path=%2F__webpack_hmr%2Fclient&name=client ./.nuxt/client.js
Module not found: Error: Can't resolve 'eslint-loader' in '/Users/xxxxxx/Documents/GitHub/xxxxxxxx/frontend'

패키지 버전을 최신화 하다가 해당 모듈에서 충돌이 생겼다.

 

 

package.json 

...
"devDependencies": { 
   ...
   
  "@nuxtjs/eslint-module": "^1.0.0",

   ...
}
...

 

nuxt.config.js

build: { 
	/* ** You can extend webpack config here */ 
    extend(config, { isDev, isClient }) { 
    	if (isDev && isClient) {
			config.module.rules.push({ 
            	test: /\.(js|vue)$/, 
                loader: 'eslint-loader', 
                exclude: /(node_modules)/, 
                options: { fix: true } 
            }) 
        } 
     }, 
 }

 

 

패키지를 설치해보자

npm install 
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! Found: @nuxtjs/eslint-module@3.0.2
npm ERR! node_modules/@nuxtjs/eslint-module
npm ERR!   dev @nuxtjs/eslint-module@"^1.0.0" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! dev @nuxtjs/eslint-module@"^1.0.0" from the root project
npm ERR! 
npm ERR! Conflicting peer dependency: eslint@6.8.0
npm ERR! node_modules/eslint
npm ERR!   peer eslint@"^5.0.0 || ^6.0.0" from @nuxtjs/eslint-module@1.2.0
npm ERR!   node_modules/@nuxtjs/eslint-module
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! See /Users/xxxxx/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/lhs/.npm/_logs/2021-01-13T01_00_35_327Z-debug.log

 

애러 내용을 보면 Found: @nuxtjs/eslint-module@3.0.2 이 내용을 볼 수 있다. 

해당 패키지의 버전을 3.0.2로 수정한다.

 

이제 nuxt 파일을 run 해보자.

npm run dev

 

빌드가 완료되고 페이지를 접속해보면 아래와 같은 에러 내용을 확인 할 수 있다.

ERROR in multi eventsource-polyfill webpack-hot-middleware/client?reload=true&timeout=30000&ansiColors=&overlayStyles=&path=%2F__webpack_hmr%2Fclient&name=client ./.nuxt/client.js
Module not found: Error: Can't resolve 'eslint-loader' in '/Users/lhs/Documents/GitHub/xxxxxxxxxx/frontend'

 

Module not found: Error: Can't resolve 'eslint-loader' in '/Users/xxxxx/Documents/GitHub/xxxxxxxx/frontend'

eslint-loader 모듈을 찾을 수 없다는 내용이다. 

 

pacakage-lock.json 파일을 1.0.0v / 3.0.2v 버전과 비교해보면

eslint-loader 내용이 빠져있는것을 확인할 수 있다.

해당 사이트를 확인해보니 eslint-loader가 depreected되었다는 내용을 찾아 볼 수 있었다.

 

그리하여 대체할 패키지로 설치를 진행하였다.

 

package.json 

...
"devDependencies": { 
   ...
   
  "@nuxtjs/eslint-module": "^1.0.0",
  "eslint-webpack-plugin": "^2.4.1",

   ...
}
...

nuxt.config,js

build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, {
      isDev,
      isClient
    }) {
      if (isDev && isClient) {
        const options = {
          exclude: ['node_modules'],
          extensions: ['js','vue'],
          fix: true
        }
        const EslintPlugin = require('eslint-webpack-plugin')
        config.plugins.push(new EslintPlugin(options))
      }
    },
  },

 

실행을 해보자

npm install 
npm run dev

 

 

 

port를 3333으로 세팅해두어 

localhost:3333으로 접속하여 정상 페이지가 뜨는걸 확인하였다. 

 

 

 

핵심내용은 eslint-loader가 deprecated가 되어 제외되면서 생긴 문제이다.

대체할 플러그인(eslint-webpack-plugin)을 설치하면서 해결하였다. 

 

 

 

 


참고 

1. www.npmjs.com/package/eslint-webpack-plugin 

 

eslint-webpack-plugin

A ESLint plugin for webpack

www.npmjs.com

2. github.com/webpack-contrib/eslint-loader

 

webpack-contrib/eslint-loader

[DEPRECATED] A ESlint loader for webpack. Contribute to webpack-contrib/eslint-loader development by creating an account on GitHub.

github.com

 

반응형

SSH  계정 정책 설정

  • 적용된 운영체제는 Amazon Linux2 버전입니다.

Session Timeout 적용

# 해당파일 수정
vi ~/.bash_profile 
# 적당한 위치에 삽입
export TMOUT=60 (=1분)
export TMOUT=600 (=10분)
# 해당파일 수정
vi /etc/profile
#적당한 위치에 삽입
export TMOUT=60 (=1분)
export TMOUT=600 (=10분)

 

 

  • 아래 코드 사용해야 적용 됨.
# 적용 명령어
source /etc/profile
source ~/.bash_profile

 

 

 

 


 

아래 내용은 수정 시 즉시 반영됨. (곧 즉시 테스트 가능)

 

같은 계정을 한명 만 사용하는 설정

vi /etc/security/limits.conf

# 아래 사진에서 @keyclass1 이 아이디 할당하는 부분을 * 로해서 전체 아이디 에 적용되도록 해야함
# 예
# *    hard   maxlogins    1
vi /etc/pam.d/login

# 해당 내용 추가
# session required pam_limits.so

 

 

이전 사용한 암호 기억 설정 접근

vi /etc/pam.d/system-auth

# 2회까지 기억 함
password    sufficient    pam_unix.so try_first_pass nullok remember=2

 

 

비밀번호 정책 정의

**저장 이후로 생성된 계정들에 대한 정책**

# vi /etc/login.defs
PASS_MAX_DAYS   100
PASS_MIN_DAYS   0
# PASS_MIN_LEN  5
PASS_WARN_AGE   7
**이미 생성된 계정들에 대한 정책**

최대 사용기간
chage -M <days> <username>

최소 사용기간
chage -m <days> <username>

암호 만료 안내
chage -W <days> <username>

확인 
chage -l <username>

 

 

로그인 실패 시 후 잠금 처리

# 계정 제한 파일
vi /etc/pam.d/password-auth
# 아래 코드와 같이 적용되어야 한다
**auth        required      pam_tally2.so  deny=5 unlock_time=600**
...
account     required      pam_unix.so
**account     required      pam_tally2.so**

 

 

잠금상태 확인

# pam_tally2 사용법

#유저 잠금 확인 
pam_tally2 --user=my_user_id

#유저 잠금 해제
pam_tally2 --user=my_user_id -r

#전체 잠금 계정 확인
faillock

#실패 로그 삭제
faillock --user aaronkilik --r

 

 

 

참고 : http://i5on9i.blogspot.com/2018/07/linux-login.html

아래 명령어로 비밀번호 생성 규칙 정의

인스턴스에서 지원하는 형식을 확인 해야함.( 확인 경로 : /etc/pam.d/system-auth)
해당 파일 내에 (pam_cracklib.so / pwquality.conf) 확인하여 지원하는 한 형식으로만 작성해야함.

# vi /etc/pam.d/system-auth (pwquality.conf 형식)

password    requisite     pam_pwquality.so try_first_pass local_users_only retry=5 minlen=8 lcredit=-1 dcredit=-1 ocredit=-1 authtok_type=
# vi /etc/pam.d/system-auth (pam_cracklib.so 형식)

password    requisite     pam_cracklib.so retry=5 **minlen=8 lcredit=-1 dcredit=-1 ocredit=-1**
  • root 계정으로 패스워드를 바꾸면 정책을 무시하고 다 바뀐다.
  • *일반 계정이 자기 자신의 비밀번호를 변경하려 할 때만 복잡도 설정이 적용* 된다

 

 

참고 : https://docs.aws.amazon.com/ko_kr/inspector/latest/userguide/inspector_security-best-practices.html#support-ssh-v2-only

모듈(pam_unix) : https://linux.die.net/man/8/pam_unix

모듈(pam_cracklib) : https://linux.die.net/man/8/pam_cracklib

모듈(pwquality.conf) : https://linux.die.net/man/5/pwquality.conf

 

 

 


 

위 내용 테스트 시 필요한 명령어

계정 추가

useradd 계정명

비밀번호 변경

passwd 계정명

새비밀번호:
새비밀번호 확인:

계정 목록 + 비밀번호 저장 파일

cat /etc/shadow

계정 목록

cat /etc/passwd
반응형

 

프로젝트 관리 및 업무파악을 하기 위해서 해당 메뉴가이드를 작업하다 내용을 가져와보았다.

이전에 JIRA로 전체 업무에 적용하여 작업을 해왔는데 참여도가 굉장히 저조했다. 

 

JIRA 툴 자체는 좋지만 업무특성에 맞게 적용하는것도 중요하다 생각이 들었다.

기획 디자인 개발 프로세스가 흘러가지만 갑에게 늘 휘둘리는 회사라면 

이런 업무툴은 업무시간을 까먹는 불필요한 프로세스라고 생각한다. 

 

추가적으로 툴을 여러개 섞어쓰는걸 별로라고 생각하는데 

그 이유는 로그인 비용과 부수적이 변화에 대한 적용 비용이 든다 생각하기때문이다. 

최소화시킬수있다면 업무동선이 많이 줄거라 생각이 든다.

 

 

깃허브 Projects 메뉴가 있기에 해당 내용을 파악해보았다. 

해당 메뉴는 JIRA와 비슷한점들이 많았다. 

 

글은 

1. 관리자가 구성하는 화면

2. 사용자가 구성하는 화면 

순서로 작성하였다.


 

 

먼저 관리자가 적용할 수 있는 화면을 구성해보았다. 

 

1. 팀프로젝트가 있다면 가장 루트, 팀 레파지토리 화면에서 > Projects 메뉴에 들어간다.

2. New Project 버튼을 클릭한다.

 

 

3. Project를 생성화면으로써 알맞는 값들을 선택한다. 

Template는 일단 None 선택을 한다.

 

 

4. 기존에 Repository를 만들어 두었다면 해당 프로젝트를 검색해서

Project와 Repository를 연결시켜준다.

 

 

5. 연결을 시켰다면 Create Project 버튼을 클릭하고 아래와 같은 화면이 보인다.

업무 프로세스를 추가하는 Column을 추가하는 화면이다.

 

 

6. Issue생성 기준으로 해당 컬럼에 자동으로 배치시키를 룰을 적용시킬수 있다. 
- To Do : 새로운 Issue가 생성될 경우
- In progress: 기존 이슈를 재오픈 할 경우
(강제로 In pregress로 변경 가능)
- Done: Issue를 Done처리 할 경우

 

 

7. 총 3가지 단계로 구성해 보았다. 

할일 > 작업중 > 끝 

이렇게 해두면 사용자에게 내 할일을 생성하여 끝내는 프로세스까지 진행하도록 공유해주어야 한다.

사용자들은 일할 Repository에서 이슈생성해서 Done처리를 하면
팀 레파지토리> Projects에서 전체를 이슈를 모니터링 할 수 있다.

 

 


 

 

+ 추가적으로 연결된 프로젝트를 추가하려면 아래 사진 순서와 같이 작업하면 된다.

1. 팀 레파지토리 > Projects 화면에서 내가 만든 Project에서 아래 사진과 같이 클릭

 

 

2. Linked repositories 메뉴에서 Link a repositroy 클릭

 

 

3. 연결될 레파지토리를 검색해서 입력한다. ( 추가된 레파지토리는 만들어져있는 상태여야 한다. )

 


 

 

사용자 프로젝트 작업 가이드

 

1. 업무가 있는 프로젝트에서 Issues 메뉴로 들어온다.

2. New issue 버튼을 클릭한다.

 

3. New issue화면에서 제목, 내용 및 할당될사람, issue lables, 어떤 Projects에 붙일 것인지 등 내용을 채워 나간다.

Lables는 기본적으로 9가지가 있다. 해당내용은 커스텀이 가능하다. 관리자와 내용을 조율하여 관리한다.

milestone은 일정을 생성하여 적용할 수 있는 기능이다. 
조금 번거로워 보인다.

3-1. 내용을 선택하는 화면이다. (참고)

 

 

4. 생성하고나면 이렇게 완료된 화면이 나온다.

+ 이렇게 추가된 프로젝트는 팀 레파지토리에서 관리되는 칸반보드에서 모두 확인이 가능하다.

 

 

+ Done처리 방법

1. 생성된 Issue에 들어가서 Close issue 버튼을 클릭한다.

 

2. 칸반보드에서 내용 확인

 

 

+ In progress 처리 방법

해당 issue화면에서 프로세스 상태값을 변경한다. 
자동으로 할당도되는데 
관리자와 해당 룰을 설명 받은뒤 사용자들에게 공유하면 될 것으로 보인다. 

 

'Utils > Github' 카테고리의 다른 글

[Github] Git ignore 등록 후 Remote 동기화하기  (0) 2021.01.26
[GitHub] 프로젝트 삭제 방법 - 웹  (0) 2016.09.15
반응형

여러가지 적용해보았는데 제일 괜찮았던 테마이다.

 

https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh/related

 

Dark Reader

모든 웹사이트에 다크 모드를 적용합니다. 밤이나 일상적인 웹 브라우징을 할 때 어두운 테마를 사용하여 눈을 보호하십시오.

chrome.google.com

 

별점 높은것도 깔아서 사용해보았는데 이게 제일 나았던거 같다. 

물론.. 개발자들은 내가 어떤색상을 적용시켰는지 확인하려면

테마를 걷어내야겠지만 ...!

반응형

목표

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가 필요하였다.

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

반응형

pyenv나 기타 방법으로 파이썬 3.7.x를 설치하려다 보면 아래와 같은 에러가 발생하는 경우가 있다.

ModuleNotFoundError: No module named '_ctypes'


해결방법

# libffi-dev 패키지를 설치해준다.
# 우분투
sudo apt install libffi-dev 

# 레드헷이나 페도라 리눅스는 아래처럼 설치.
sudo apt install libffi-devel

#centos
sudo yum install libffi-devel

 

설치 시 libffi-devel 해당 모듈이 설치가 되지 않아 생기는 오류이다.

해당 os에 맞게 설치 한다.

'백엔드 > Python' 카테고리의 다른 글

[Python] gunicorn 과 grcp 충돌  (0) 2021.09.24
반응형

해당 에러는 개발 시 메모리를 많이 사용하는 부분이 생성되어

코드 중 어느 부분인가 잘못되면 메모리가 사용 되게 되었다.

오늘은 잘못 짠 내 코드를 보면서 개선해보겠다.

// file : File 변수
// s3Client :s3 uploader

byte[] bytes = IOUtils.toByteArray(file.getInputStream());
    	
ObjectMetadata objMeta = new ObjectMetadata();
objMeta.setContentLength(bytes.length);
ByteArrayInputStream byteArrayIs = new ByteArrayInputStream(bytes);
s3Client.putObject(new PutObjectRequest(filePath, fileName, byteArrayIs, objMeta)
        .withCannedAcl(CannedAccessControlList.PublicRead));
s3Client.getUrl(filePath, fileName).toString();

 

아직도 스트림에 대해서 제대로 이해를 하지 못하는 수준의 코드이다.

 

 

대략적으로 설명을 하자면

S3 Uploader에서 Metadata 에 length를 계산하기 위해 IOUtils.toByteArray 으로 바이트로 변환하여

byte[] 을 .length로 출력한다.

이후 ByteArrayInputStream 으로 다시 변환하여 S3에 업로드 변수에 넣는다.

이렇게 설명할 수 있다.

 

보다 보니 반복되는 부분이 보이는것 같다. InpustStream이 두번 생성되는 부분이 있다.

코드 첫라인인 1번째 라인과 5번 라인에서 반복되는 구문이 보인다. 이렇게 사용하게되면 물론 반복에도 문제가 있지만

해당 서비스는 용량이 큰 파일을 upload하기때문에 메모리에 큰 부하가 오게된다.

개선하기 위해 아레와 같이 반복되는 부분을 제거하였다.

// file : File 변수
// s3Client :s3 uploader
    	
ObjectMetadata objMeta = new ObjectMetadata();
objMeta.setContentLength(bytes.length);
s3Client.putObject(new PutObjectRequest(filePath, fileName, file.getInputStream(), objMeta)
        .withCannedAcl(CannedAccessControlList.PublicRead));
s3Client.getUrl(filePath, fileName).toString();

 

 

반복 구문

 

byte[] bytes = IOUtils.toByteArray(file.getInputStream());

ByteArrayInputStream byteArrayIs = new ByteArrayInputStream(bytes);

OutputStream으로 만들고, InputStream으로 만드는 두 부분을 제거하였다.

반복되는 부분을 제거하고 메모리 모니터링을 진행한 결과

현저히 메모리 사용량이 줄어들었다. 엉뚱한곳을 바라보고 메모리 수정을 하였으나

Java heap memory 에러에서는 몇번째 라인에서 에러가 나는지 보여주고있었다.

꼭 에러로그에 기반한 오류수정을 우선적으로 하길.

반응형

업무에서 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.

 

 

 

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

+ Recent posts