본문으로 건너뛰기

"server" 태그로 연결된 4개 게시물개의 게시물이 있습니다.

모든 태그 보기

12 팩터 앱

· 약 4분

개요

헤로쿠 사에서 클라우드-네이티브 아키텍처를 구축하면서 나온 패턴들이 12-팩터 앱 가이드로 정착. 단일 유닛으로 이뤄진 어플리케이션 아키텍처를 declarative configuration, statelessness, deployment independence에 초점을 두고 설계하는 안내서

MSA에 적용

12 factor

  • 코드베이스
  • 의존성
  • 설정
  • 백엔드 서비스
  • 빌드, 릴리즈, 실행
  • 프로세스
  • 포트 바인딩
  • 동시성
  • 폐기 가능 (쓰고 버리기)
  • 환경 일치
  • 로그
  • 관리 프로세스

코드베이스

  • codebase
  • 하나의 코드베이스는 VCS 에서 여러 배포버전을 기록하며, 하나의 앱은 하나의 코드베이스를 기반으로 소스 코드를 형상관리
  • 코드는 여러 대의 분산처리 운영서버나 여러 개발자의 컴퓨터로 배포, 실행될 수 있다

의존성

  • dependency
  • 의존성은 명시적으로 선언하되 격리한다
  • 인터넷이 차단된 개발 환경의 의존성을 어떻게 패키징할지도 고민 대상
  • 시스템 유틸리티에 의존하지 않는다

설정

  • configuration
  • 여러 서버의 접속 정보 등을 설정을 코드에 저장하지 않는다
  • 가변적인 것들은 환경변수를 이용한다

백엔드 서비스

  • backing service
  • 기반 서비스
  • 부가 리소스로 취급하며 URL 를 통해 서비스에 접속한다

빌드, 릴리즈, 실행

  • 빌드와 실행단계를 엄격히 구분한다
  • CI / CD 와 연관된다

프로세스

  • 단일/다수의 무상태 프로세스로 앱을 실행한다
  • 프로세스 사이에 아무 것도 공유하지 않고, 내부 상태를 갖고 있지 않는다
  • 모든 데이터는 백엔드 서비스로 다뤄야한다

포트 바인딩

  • 서비스는 포트를 바인딩하여 내보낸다
  • 한 앱이 다른 앱의 서비스가 될 수 있다

동시성

  • concurrency
  • 프로세스 모델 중심으로 scale out
  • 개별 프로세스는 멀티스레딩하면 된다
  • 같은 일하는 프로세스는 수직적, 다른일 하는 프로세스는 수평적

폐기 가능

  • disposability
  • 프로세스는 쓰고 바로 버릴 수 있어야한다
  • 프로세스가 갑자기 죽어도 서비스 장애가 발생해서는 안 된다

같은 환경

  • environment parity
  • 개발, 스테이징, 운영 환경을 최대한 비슷하게 유지한다

로그

  • 표준 콘솔창 (stdout)에 로그를 출력한다

관리 프로세스

  • admin process
  • 관리자가 수행하는 작업(DB migration, batch, at 등)은 일회성 프로세스로 실행한다
  • 데이터베이스를 직접 수정하지 않는다

참고

AWS CodeCommit 사용하기

· 약 5분

AWS 에서 제공하는 Cloud Git Repository 인 CodeCommit 으로 소스코드를 관리해보자.

설정

공식 문서가 아주 잘 되어있다. 문서를 보고 시작해도 된다.

IAM 유저 생성

IAM 에서 유저를 만든 뒤에 AWSCodeCommitFullAccess 권한을 추가한다.

키 파일 업로드

ssh 키를 생성하고 Public Key 파일(id_rsa.pub)을 해당 유저의 Security credentials 메뉴에서 업로드한다. 키가 등록되면 SSH key ID가 보이는데 메모해 놓자.

config

사용할 유저의 ssh config 를 설정해줘야한다. User 에 들어가는 값은 위에서 적어놓은 SSH key ID 값이다.

$ vi ~/.ssh/config

## 아래 내용을 맨 위에 넣어주자.
Host git-codecommit.*.amazonaws.com
User EXAMPLEEXAMPLEEXAMPLE
IdentityFile ~/.ssh/id_rsa

## 저장한 뒤 권한을 바꿔준다.
chmod 600 ~/.ssh/config

테스트

설정이 완료된 후에 서울리젼으로 ssh 연결을 시도해보자.

$ ssh git-codecommit.ap-northeast-2.amazonaws.com

## 다음과 같은 메세지가 리턴되면 성공이다.
You have successfully authenticated over SSH. You can use Git to interact with AWS CodeCommit. Interactive shells are not supported.Connection to git-codecommit.
ap-northeast-2.amazonaws.com closed by remote host.
Connection to git-codecommit.ap-northeast-2.amazonaws.com closed.

연동

이제 AWS CodeCommit 과 내 소스를 연결시켜보자.

## 이미 존재하는 프로젝트의 경우
$ git remote remove origin
$ git remote add origin ssh://git-codecommit.ap-northeast-2.amazonaws.com/v1/repos/레파지토리명

## 처음 시작하는 경우
$ git clone ssh://git-codecommit.ap-northeast-2.amazonaws.com/v1/repos/레파지토리명

## 권한이 없거나 ssh 계정을 물어볼 때
## origin의 path 앞에 SSH key ID를 추가하자
$ git remote add origin ssh://EXAMPLEEXAMPLEEXAMPLE@git-codecommit.ap-northeast-2.amazonaws.com/v1/repos/레파지토리명

git pull origin master시에 정상적으로 가져오는 걸 확인할 수 있다.

ssh 연동이 잘 되지 않을시

메뉴얼대로 따라해도 연동이 안될 때는 당황하지 말고 IAM > Users > Security credentials에서 HTTPS Git credentials for AWS CodeCommit를 만들어주고 CodeCommit Repository 연결을 http로 하면 된다.

Webhook

Github, Bitbucket 와 달리 Webhook 설정하는 법이 조금은 복잡하다. (GCP 의 Pub/Sub 와 비슷한 느낌)

CodeDeploy 서비스를 사용하면 쉬워질 것 같은데, 그럼 CodePipeline 도 쓰고 싶을 것 같고 CI 세팅을 해야되고 다음 기회에

Lambda

람다는 웹(URL)으로 호출할 수 있는 Javascript function 이다.

Lambda 설정

먼저 Lambda > 함수 > 함수생성 > 새로 작성 메뉴에서 webhook 이란 이름의 함수를 생성한다. 트리거 구성 메뉴에서 CodeCommit 을 선택하고 입력 폼을 잘 채워주자.

image from hexo

기존 브랜치로 푸시, master 브랜치를 선택했다. 사용자 지정데이터에는 webhook 을 걸 URL 경로를 넣어준다. (예: https://yourdomain.com/webhook)

소스

이미 세팅된 함수를 사용하자.

"use strict";

const url = require("url");
const https = require("https");

exports.handler = (event, context, callback) => {
const webhook_url = event.Records[0].customData;

if (!webhook_url) {
const error = new Error("Web-hook URL not provided as custom data.");
callback(error);
} else {
console.log("POST web-hook to " + webhook_url);
const options = url.parse(webhook_url);
options["method"] = "POST";

const req = https.request(options, (res) => {
let body = "";
console.log("Status:", res.statusCode);
console.log("Headers:", JSON.stringify(res.headers));
res.setEncoding("utf8");
res.on("data", (chunk) => (body += chunk));
res.on("end", () => {
console.log("Successfully triggered web-hook.");
// If we know it's JSON, parse it.
if (res.headers["content-type"] === "application/json") {
body = JSON.parse(body);
}
callback(null, body);
});
});

req.on("error", callback);
req.end();
}
};

https 커넥션이 아닌 경우 http 모듈을 사용해서 request 를 보내면 될 것 같다.

등록 후엔 CodeCommit > 트리거 > webhook > 트리거 테스트를 진행하면 정상적으로 호출이 된다.

로그

호출 로그는 CloudWatch > 로그에서 생성한 Lambda 명으로 확인할 수 있다.

로그 스트림이 설정되지 않았을 경우엔 Lambda > 구성 > 기존 역할에 표시된 역할(Rule)이 해당 Lambda 함수를 잡고 있는지 확인해야한다. IAM > Rules > 해당 룰 > Permissions에서 Show policy를 누르면 Resource 속성에서 확인할 수 있고 다르다면 제대로 연결시켜주면 된다.

여담

캐시메모리가 꽉차서 메모리 용량이 부족한 경우

· 약 2분

파일에 접근이 빈번한 시스템에서 메모리 용량을 확인시

## 메모리 용량 확인
$ free -m

캐시메모리의 점유율이 꽉찰 정도로 높게 나타나 메모리 용량이 부족한 걸 확인할 수 있다.

원인

리눅스는 물리적인 저장/통신 장치와 데이터를 주고 받을 때 메모리에 먼저 적재한 후에 데이터를 주고 받는다. 이는 동일한 데이터에 대한 접근을 할 경우 메모리에서 바로 가져오도록 하여 I/O 성능을 높이기 위함이다.

해결

vfs_cache_pressure

시스템 설정 파일을 열어 디렉토리와 inode 오브젝트에 대한 캐시로 사용된 메모리를 반환하는 정도를 지정하는 vfs_cache_pressure 파라미터를 수정해준다.

$ vi /etc/sysctl.conf
## 기본값은 100
vm.vfs_cache_pressure = 10000

cache memory drop

캐시메모리를 주기적으로 비워주자

$ crontab -e
## 매일 새벽 4시에 캐시메모리 강제비우기
0 4 * * * sync && echo 3 > /proc/sys/vm/drop_caches

sync 명령어로 캐시메모리에 담긴 데이터를 실제 저장장치에 반영해주고, drop_caches를 실행한다. 커맨드로 실행시에 메모리를 반환하면서 서버가 잠시 멈출 수 있으니 새벽에 실행을 걸어놓자

  • echo 1 : page cache 해제
  • echo 2 : inode, dentry cache 해제
  • echo 3 : 모두 해제