All Posts By

manvscloud

IT/Linux/Kubernetes

YAML language 와 Kubernetes에서 요구되는 필드

안녕하세요. ManVSCloud 김수현입니다.

이번 주 일요일부터 Docker Kubernetes Online Study에서 Kubernetes 소개와 설치가 시작됩니다. 그러므로 오늘은 Yaml 파일에 대한 이해를 돕기 위해 노션에 정리해두었던 Yaml파일에 대한 내용을 정리하여 포스팅해보았습니다.


What is Yaml?

Yaml 또는 Yml 이란 사람이 쉽게 읽을 수 있는 형태로 표현된 데이터 직렬화 양식입니다.

데이터 직렬화는 메모리의 객체를 디스크에 저장하거나 통신하는 대상에게 올바른 형식으로 맞추어 전달하기 위해 바이트 스트림 형태로 만드는 것!
쉽게 말하자면 사람이 읽고 쓰기 쉽도록 만들어진 하나의 약속이라고 볼 수 있습니다.

쿠버네티스 매니페스트는 YAML과 JSON을 사용하게 되는데 주로 YAML을 이용하여 구성 파일을 작성하므로 YAML에 대해 알고 있으면 큰 도움이 됩니다.

  • 역직렬화 : 디스크에 저장된 데이터를 읽거나 네트워크를 통해 전송된 데이터를 받아 메모리에 재구축
  • 바이트 스트림 : 1 byte를 입출력 할 수 있는 스트림, JAVA에서 입출력 스트림을 통해 흘러가는 데이터의 기본 단위 (InputSteam과 OutputStream이 존재)
  • 스트림 : 데이터 처리연산을 지원하도록 소스에서 추출된 연속된 요소이며 데이터를 읽고 쓸 수 있도록 도와준다.
  • 매니페스트 : 컴퓨팅에서 집합의 일부 또는 논리정연한 단위인 파일들의 그룹을 위한 메타데이터를 포함하는 파일 (애플리케이션에 대한 필수적인 정보가 담겨있음)

Grammar

📢 Yaml 문법 포인트

  • Key: Value
  • mappings(해시 or 딕셔너리), sequences(배열 or 리스트), scalars(스트링 or 넘버 등)
  • 각 블록 부모-자식 관계는 들여쓰기로 구분
  • 를 이용하여 시퀀스 사용
  • # 는 주석
  • 는 문서의 시작 (스트림 시작)
  • 는 문서의 끝 (스트림 끝)
  • & 를 이용하여 anchor 설정 (정의)
  • * 를 이용하여 alias 설정 (참조)
  • 띄어쓰기

Scalars
integer: 100
string: “100” 또는 ‘100’
(“”는 이중 문자열, ”는 단일 문자열)
float: 100.0
boolean: yes

(yes/no, true/false, on/off)

→ Example
count: “1”
→ int가 아닌 string

size: ‘3.14’
→ float가 아닌 string

question: ture
→ boolean

answer: “true”
→ string

→ 추가 내용
◎ 값이 비어있으면 null (nil이 아님)
port: “80” 이라고 선언할 수 있으며 템플릿 엔진과 YAML Parser를 정상적으로 통과합니다. 하지만 쿠버네티스가 포트를 integer로 받아야할 경우 실패할 수 있습니다.
이때 YAML 노드 태그를 사용하여 특정 유형 추론을 강제화 할 수 있습니다.
port: !!int “80”
위와 같이 선언할 경우 port가 “”로 감싸져 있어도 int로 취급됩니다.
ex) age: !!str 21 (int가 아닌 string으로 강제)
◎ 추가적인 심화 내용은 아래
TIP의 YAML 기법 링크 참고


Sequences

→ Level 1

- one
- two
- three

Level 2

-
  - Lion
  - Tiger
  - Bear
-
  - Orange
  - Lemon
  - Pear

Level 3

-
  -
    - Golf
    - Soccer
    - Basketball

Mappings

Level 1

Language: English

Level 2

Language: English
  - A
  - B
  - C

Anchor & Alias

Level 1

Linux:
  - linux1: &linux1 centos
  - linux2: &linux2 ubuntu
  - linux3: &linux3 rocky
  - linux4: &linux4 suse

web_linux: *linux1
db_linux: *linux3

Level 2
(<< 를 이용하여 Override(자식 클래스에서 부모 클래스 재정의))

base: &default
  web: apache
  db: mysql
env:
  dev:
    <<: *default
    db: oracle

  prod:
    <<: *default
    web: nginx

⚠️ YAML 앵커 및 별칭은 ‘ [ ‘, ‘ ] ‘, ‘ { ‘, ‘ } ‘및 ‘ , ‘문자를 포함 할 수 없습니다.


Example

Kubernetes에서 퍼시스턴트볼륨을 생성하는 YAML 파일의 예시입니다.
(용량은 25GiB이며 ReadWriteOnce Mode로 생성)

kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv0001
  labels:
    type: local
spec:
  capacity:
    storage: 25Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/data/pv01"

In-depth course

CKAD 시험까지 생각하고 있다면 k8s의 yaml 파일 작성법까지 깊게 알아보는 것도 좋지않을까 생각합니다. 생성하고자 하는 쿠버네티스 오브젝트에 대한 YAML 파일 내에 필드 값들을 설정해주게 되는데 그 필드 값에 대한 내용을 알아보도록 하겠습니다.

  • apiVersion
    : 이 오브젝트를 생성하기 위해 사용하고 있는 쿠버네티스 API 버전이 어떤 것인지

종류
v1
: 쿠버네티스에서 발행한 첫 stable release API
(대부분의 api가 포함되어 있음)
apps/v1
: 쿠버네티스의 common API 모음, Deployment, RollingUpdate, ReplicaSet을 포함
batch/v1
: 배치 프로세스, job-like task를 위한 배포 api
autoscaling/v1
: pod의 autoscale 기능을 포함하는 API, 현재는 CPU metric을 사용한 scaling만 가능
(추후에 alpha, beta version에서 memory, custom metric으로 scaling 기능 추가예정)
batch/v1beta1
: batch/v1에서 cronJob으로 job을 돌리는 api가 추가
certivicates.k8s.io/v1 beta
: 클러스터의 secure network function들이 추가된 API
(TLS 등의 기능 추가)
extensions/v1beta
: Deployments, DaemonSets, ReplicatSets, Ingress 등 상당수 feature들이 새롭게 정의된 API 그러나 상당수의 api들이 apps/v1과 같은 그룹으로 이동되어서, 쿠버네티스 1.6버젼 이후부터는 deprecated 됨
policy/v1beta1
: pod에 대한 security rule이 정의된 API
rbac.authorization.k8s.io/v1
: 쿠버네티스의 role-based access control이 가능한 function이 정의됨

  • kind
    : 어떤 종류의 오브젝트를 생성하고자 하는지 (원하는 kind에 따라 apiVersion 종류가 달라짐)

종류
Pod
: pod을 정의하여 쿠버네티스 선언(아직 배포되지 않은 상태)
ReplicationController(or ReplicaSet)
: 쿠버네티스에 선언된 pod을 배포하기 위한 선언(replicas 갯수, readiness probe 등 선언)
Service
: 동적으로 생성된 pod(각각 다른 ip)을 라벨(label)과 라벨 셀렉터(label selector)을 사용하여 하나의 서비스로 묶어주는 역할
(ex. pod에 라벨이 “wonyoung”이 선언되어 있으면, service는 “wonyoung” label이 붙은 서비스만 골라내서 그 pod간에만 로드벨런싱을 동해 서비스를 외부에 제공)

  • metadata
    : 이름, 문자열, UID, 그리고 선택적인 네임스페이스를 포함하여 오브젝트를 유일하게 구분지어줄 데이터
  • spec
    : 오브젝트에 대해 어떤 상태를 의도하는지

🔑 생성 가능한 오브젝트에 대한 모든 spec 확인

🔑 Pod에 대한 spec 포맷

🔑 Deployment에 대한 spec 포맷


TIP

📌 YAML 기법
📌 YAML 유효성 검사
📌 YAML to JSON
📌 JSON to YAML

Personal Comments

YAML을 전혀 모르던 때에 띄어쓰기 조차 문법인지 몰라서 당황했던 기억이 있습니다.
이 포스팅을 통해 많은 분들이 YAML 문법이 무엇인지 그리고 Kubernetes를 시작하기 전 많은 도움이 되길 바랍니다.

긴 글 읽어주셔서 감사합니다.

IT/Linux/Kubernetes

Docker Kubernetes Online Study 스터디 시작! (Docker의 장점과 Network)

안녕하세요. ManVSCloud 김수현입니다.

CloudNet@에서 진행하는 DOCKER KUBERNETES ONLINE STUDY가 시작되었습니다.
1주차 Docker의 기초를 시작으로 Kubernetes를 제대로 배우게 되어 흥미롭습니다.

지금까지 Docker는 Docker-compose를 이용하여 임의적으로만 사용하거나 kubernetes도 wordpress 사이트를 올려본 것이 전부라 아직 미숙했는데 이번 기회에 깊이있게 공부하고 다양한 테스트를 시작할 수 있게 되었습니다.

오늘 스터디 이전에 정리해두었던 내용 중 몇가지를 포스팅하려 합니다.
Docker의 장점과 네트워크 관련 내용입니다.


Advantages of Docker

한국어 자막 지원합니다.

위 영상은 컨테이너가 무엇인지 알려주는 영상입니다.
한국어 자막도 지원하고 설명이 잘 되어있어 1분 30초만 투자하면 컨테이너가 무엇인지 알 수 있습니다.

도커의 장점 TOP 5
  • 신속한 애플리케이션 배포 – 컨테이너에는 애플리케이션 의 최소 런타임 요구 사항이 포함되어 크기를 줄이고 신속하게 배포 할 수 있습니다.
  • 머신 간 이식성 – 애플리케이션 및 모든 종속 항목을 Linux 커널, 플랫폼 배포 또는 배포 모델의 호스트 버전과 독립적인 단일 컨테이너로 번들링 할 수 있습니다. 또한 이 컨테이너는 Docker를 실행하는 다른 머신으로 전송할 수 있으며 호환성 문제없이 실행할 수 있습니다.
  • 버전 제어 및 구성 요소 재사용 – 컨테이너의 연속 버전을 추적하고 차이점을 검사하거나 이전 버전으로 롤백 할 수 있습니다. 또 컨테이너는 이전 레이어의 구성 요소를 재사용하므로 매우 가볍습니다.
  • 공유 – 원격 저장소를 사용하여 다른 사람과 컨테이너를 공유 할 수 있습니다. 
  • 가벼운 설치 공간 및 최소한의 오버 헤드 – Docker 이미지는 일반적으로 매우 작기 때문에 신속한 제공이 가능하고 새로운 애플리케이션 컨테이너를 배포하는 시간이 단축됩니다.
  • 단순화 된 유지 관리 – Docker는 애플리케이션 종속성으로 인한 노력과 문제의 위험을 줄여줍니다.

Network

root@dkos-master:~# ip route show
default via 192.168.0.1 dev enp0s3 proto static
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.0.0/24 dev enp0s3 proto kernel scope link src 192.168.0.30

제가 docker를 처음 사용했을 때 생겼던 궁금증이었는데 이제는 많은 블로그에 포스팅된 내용이기도 합니다.
docker 설치 및 실행 후 ip route show 또는 ip a 명령어로 확인 시 docker 0의 bridge 네트워크 IP 대역이 default 설정으로 172.17.0.0/16 대역으로 설정되어 있을 것입니다.
이 ip대역 변경이 가능합니다.

/etc/docker 디렉토리 아래 daemon.json라는 .json 파일이 없는데 이걸 만들어주면 됩니다.

root@dkos-master:~# vi /etc/docker/daemon.json

예를 들어 아래와 같은 대역대로 설정해주고 저장합니다.

{
"bip": "10.3.0.1/16"
}
root@dkos-master:~# systemctl restart docker

root@dkos-master:~# ip route show
default via 192.168.0.1 dev enp0s3 proto static
10.3.0.0/16 dev docker0 proto kernel scope link src 10.3.0.1 linkdown
192.168.0.0/24 dev enp0s3 proto kernel scope link src 192.168.0.30

root@dkos-master:~# iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 10.3.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN

docker를 재시작해주면 설정해줬던 10.3.0.0/16 대역으로 변경된 것을 알 수 있습니다.

3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:2a:78:b0:a3 brd ff:ff:ff:ff:ff:ff
    inet 10.3.0.1/16 brd 10.3.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:2aff:fe78:b0a3/64 scope link
       valid_lft forever preferred_lft forever

브리지 네트워크 구성 방법에 대해 자세하게 알고싶으신 경우 아래 링크를 참고하시기 바랍니다.

또한 Docker에서는 다양한 네트워크 작동 방식이 존재합니다.
bridge, host, overlay, macvlan, none 등이 있는데 기본 네트워크 드라이브는 bridge로 설정되어 있으며 각 네트워크 작동 방식에 대해 설명하기에는 내용이 너무 방대하여 간단한 요약만 작성하겠습니다. 더 자세히 알고싶으신 경우 링크를 남겨두었으니 확인해보시기 바랍니다.

  • bridge : 동일한 Docker 호스트에서 통신하기 위해 여러 컨테이너가 필요할 때 사용
  • host : 네트워크 스택이 Docker 호스트에서 격리되어서는 안되지만 컨테이너의 다른 측면을 격리하려는 경우 사용
  • overlay : 통신을 위해 서로 다른 Docker 호스트에서 실행되는 컨테이너가 필요하거나 여러 애플리케이션이 스웜(swarm) 서비스를 사용하여 함께 작동 할 때 사용
  • macvlan : VM 설정에서 마이그레이션하거나 컨테이너가 각각 고유 한 MAC 주소를 가진 네트워크의 물리적 호스트처럼 보이도록해야 할 때 사용
  • ipvlan : ipvlan은 macvlan과 유사하지만 엔드포인트의 MAC 주소가 동일하며 L2 및 L3 모드를 지원합니다.
  • none : 모든 네트워킹을 비활성화, 스웜(swarm) 서비스 사용 불가

아래 링크는 Macvlan과 IPvlan의 차이점을 알 수 있습니다.


TIP

Docker는 단일 컨테이너 관리에 적합하도록 만들어져 있습니다.
컨테이너가 세분화되어 다수의 컨테이너화 된 앱을 사용하게 되면 관리와 오케스트레이션이 어려워집니다. 그렇다면 여기서 오케스트레이션은 무엇입니까?
오케스트라(orchestra)라는 말은 많이 들어보셨으리라 생각합니다.

오케스트레이션은 컴퓨팅, 네트워킹, 리소스 배포, 관리, 배치, 정렬을 자동화합니다.
위 컨테이너를 지휘하고 있는 이미지와 함께 보면 어떤 느낌인지 이해가 가시나요?
다수의 컨테이너가 네트워킹, 보안, 텔레메트리와 같은 서비스를 쉽게 제공하려면 이 컨테이너들을 그룹화해야 할 필요가 있습니다. 그렇기 때문에 Kubernetes, Docker Swarm, Apache Mesos, Nomad 와 같은 툴을 사용합니다.
Kubernetes는 무엇입니까? 컨테이너 오케스트레이션 툴입니다.


Personal Comments

이번 주 일요일부터는 Kubernetes에 대한 내용이 시작됩니다.
아직 화요일인데 벌써 일요일이 기다려지네요.
DKOS 스터디 진행 및 운영 해주시는 모든 운영진 분들에게 감사의 인사 올립니다.

마지막으로 컨테이너는 chroot와 namespace, cgroups과 같은 커널 기반 기술이 사용되는데 이에 대해 잘 설명이 되어있는 사이트가 있어 아래 링크로 공유합니다.

긴 글 읽어주셔서 감사합니다.

IT/Linux/Kubernetes

xcp-ng 8.2 install (xen server)

안녕하세요. ManVSCloud 김수현입니다.

오늘은 XenServer의 무료 버전인 XCP-ng 오픈소스 하이퍼바이저를 설치하도록 하겠습니다.
비유를 하지면 Xen이 Redhat, XCP-ng가 CentOS라고 보면 이해하기 쉬울 것입니다.

XCP-ng를 설치하는 이유는 곧 CloudNet@에서 진행하는 DKOS(Docker Kubernetes Online Study)를 실제 서버에 올려서 운영 테스트까지 하는 것이 목적이기 때문입니다.

서버 여러대 사용하는 것도 조금 아닌 것같고 그렇다고 약 8주간 스터디 동안 클라우드에 테스트용 서버를 계속 올렸다가 스냅샷찍고 내렸다가 반복하는 것도 아닌 것같아서 Xcp-ng를 설치하게 되었습니다.

알고계신가요? 네이버 클라우드도 XenServer으로 만들어졌다는 사실?
Xen Server와 가상화에 대한 개념이 궁금하신 분을 위해 아래 링크를 추가해두었습니다.


XCP-ng Install

XCP-ng는 위 링크에서 .iso파일을 다운로드할 수 있습니다.

설치 진행 시 첫 화면
키보드 레이아웃을 설정하는 곳입니다.
us로 선택하면 됩니다.
OS 설치가 진행되면 디스크에 기존 데이터들이 전부 삭제되니 백업을 하라는 내용입니다.
백업이 필요할 경우 Reboot 그냥 진행하려면 OK를 누르면 됩니다.
라이센스 동의하는지 묻는 내용입니다.
[Accept EULA]를 바로 선택해줍시다.
일반 서버에 설치하면 나오지않습니다.
일반 서버에 설치한 것은 화질이 좋지않아 제가 VirtualBox로 한번 더 이미지 뽑느라 나온 것!
VirtualBox에서 설치하면 가상화 지원에 이상이 있다고 알려주는 내용이니 무시합시다.
스토리지를 선택하는 곳입니다.
참고로 설치 시 최소 용량이 약 46G? 이상이 필요한 것으로 압니다.
일반 리눅스 설치할 때처럼 적게 용량을 주면 설치가 안될 것입니다.
설치한 .iso 파일을 마운트하여 설치하는 것이니 [Local media]를 선택하여 설치합니다.
위에서 선택한 media 설치 소스를 검증하기 위해 테스트할지 스킵할지 물어보는 것입니다.
Skip 해줍시다.
설치 후 XCP-ng Center의 초기 패스워드입니다.
계정은 root입니다.
네트워크 설정을 해줍시다.
DHCP를 체크 해제하고 Static configuration에서 고정 IP 설정이 가능합니다.
Host와 DNS 설정을 해주는 곳입니다.
위 이미지와 같이 원하는 host명과 DNS를 설정합니다.
Time Zone 설정하는 곳입니다.
[Asia]로 설정
[Seoul]을 선택합니다.
현지 시간 결정을 어떻게할지 물어봅니다.
NTP Server를 지정해줄 수 있습니다.
time.bora.net, time.nuri.net 등 공용 NTP 서버는 많으니 원하시는 NTP Server를 등록해주면 됩니다.
설정이 끝났습니다 Install 해줍시다.
이건 추가 패키지 설치할래? 물어보는 건데
추가 패키지 설치할 거 없으면 [No]를 선택하면 됩니다.
설치가 완료되면 위와 같은 화면을 볼 수 있습니다.
이 화면이 메인 화면인 Ctrl+Alt+F1 화면입니다.
Ctrl+Alt+F2는 시스템 메시지 화면이고 Ctrl+Alt+F3부터 아래와 같은 터미널 구경이 가능합니다.
Ctrl+Alt+F3
이건 제가 서버에 XCP-ng 설치 후 XCP-ng Center로 접속한 화면입니다. (XCP-ng-Center-20.04.00.32.msi)

XCP-ng Center는 아래 링크에서 다운로드 및 내용 확인이 가능합니다.


마무리

금일 저녁부터 DKOS가 시작됩니다.
스터디 이후에도 정기적으로 쿠버네티스 학습이 필요할 것으로 보입니다.
XCP-ng를 이용하여 쿠버네티스 Master,Node를 생성하고 각종 테스트 실습을 거칠 예정입니다.

집에 사용하지 않는 PC가 있다면 Xcp-ng를 설치하여 사용하면 생각보다 다양하게 사용할 수 있어 좋습니다.

긴 글 읽어주셔서 감사합니다.

AWS

[AWS] image resizing failure, trouble shooting

안녕하세요. ManVSCloud 김수현입니다.

오늘은 이전에 실패했던 image resizing 실패 원인을 확인 후
정상적으로 resizing 성공 후기를 남겨보려고 합니다.


First Cause

첫번째 원인을 찾았습니다.
CloudFront 트리거 구성 시 Origin Response가 되어야하는데 Request로 설정을 해두었던 것!

Lambda에서 변경 시 위에서 오리진 응답으로 변경하면 됩니다.

만약 Cloudfront에서 변경을 하실 경우 연결된 cloudfront에서 [Behaviors]-[경로 선택 및 Edit] 후 아래 CloudFront Event에서 Origin Response로 변경해줄 수 있습니다.

이 부분은 리눅서님의 도움을 받았습니다.
해당 부분이 Response가 되어야하는 이유는 인지하고 있었는데 다시 한번 포인트를 잡아주시고 놓치고 지나친 부분 찾아주신 리눅서님에게 감사의 인사 올립니다.


Find hints

위 첫번째 원인을 해결하고도 정상적으로 이미지 리사이징이 되지 않았습니다.
그래서 현재 이미지 리사이징이 어떻게 동작하고있고 특정 오류가 발생하는가 확인해보기로 했습니다.


우선 CloudFront의 Cache statistics와 Monitoring을 체크해보았고
각 오류 코드 및 Header값을 확인해보았습니다.

3xx의 정체는 301, 2xx는 200, 4xx는 403으로 보입니다.

http://주소/경로/tom.png?w=110&h=60&f=webp&q=90 [Viewer Request]
Status Code 로301 Move Permanently를 받으며 리다이렉트 되었습니다.
X-Cache : Redirect from cloudfront

https로 리다이렉트 되고 Status Code로 200을 받았습니다.
x-cache : Miss from cloudfront 엣지 로케이션 캐시에 tom.png?w=110&h=60&f=webp&q=90가 없어 Origin에 요청합니다. 여기서 Origin은 tom.png 이미지가 있는 S3 버킷!
이미지도 잘 데려왔습니다.

그런데 이미지 리사이징이 되지않은 상태로 넘어왔습니다.
User가 Cloudfront로 “Viewer request” 했고 (1. User → Cloudfront)
Miss from cloudfront라 Cloudfront가 Origin에게 “Origin request” 하고 (2. Cloudfront → Origin)
Origin으로부터 “Origin reponse” 후 (3. Origin → Cloudfront)
Cloudfront가 다시 User에게 “Viewer response” (4. Cloudfront → User) 되어
이미지를 받았다라고 했을 때 [1], [2], [4] 과정은 문제가 없었을 것이라 생각됩니다.

[1],[4] 요청과 응답에 대한 통신은 정상적으로 되었으며 Miss from cloudfront 응답에
[2] CloudFront는 Origin에게 요청을 했을 것입니다.

그렇다면 [3] 과정에서 정상적으로 동작하지 않는 것이 있다는 것인데?

Cloudfront 설정은 몇번을 반복하여 보아도 더 이상 잘못 설정한 것이 없었습니다.
그래서 Lambda 설정을 조금 더 검토해보기로 했습니다.

Second Cause

원인을 전혀 못찾다가 사내 상사의 도움으로 “Node.js 버전에 맞는 소스 코드를 사용했는가? 소스 코드가 버전에 맞지않아서 그럴 수 있다”라는 힌트를 얻게 되었습니다.

그런데 아무리 생각해봐도 다른 설정은 잘 되어있고 정말 소스 코드쪽이 크게 의심되어 바로 버전과 소스 코드 변경을 진행하였습니다.

Node.js 버전은 12.x 버전으로 변경하였고 소스 코드는 아래와 같이 수정하였습니다.

'use strict';

const querystring = require('querystring'); // Don't install.
const AWS = require('aws-sdk'); // Don't install.
const Sharp = require('sharp');

const S3 = new AWS.S3({
  region: 'ap-northeast-2'
});
const BUCKET = 'mybucket';

exports.handler = async (event, context, callback) => {
  const { request, response } = event.Records[0].cf;
  // Parameters are w, h, f, q and indicate width, height, format and quality.
  const params = querystring.parse(request.querystring);

  // Required width or height value.
  if (!params.w && !params.h) {
    return callback(null, response);
  }

  // Extract name and format.
  const { uri } = request;
  const [, imageName, extension] = uri.match(/\/?(.*)\.(.*)/);

  // Init variables
  let width;
  let height;
  let format;
  let quality; // Sharp는 이미지 포맷에 따라서 품질(quality)의 기본값이 다릅니다.
  let s3Object;
  let resizedImage;

  // Init sizes.
  width = parseInt(params.w, 10) ? parseInt(params.w, 10) : null;
  height = parseInt(params.h, 10) ? parseInt(params.h, 10) : null;

  // Init quality.
  if (parseInt(params.q, 10)) {
    quality = parseInt(params.q, 10);
  }

  // Init format.
  format = params.f ? params.f : extension;
  format = format === 'jpg' ? 'jpeg' : format;

  // For AWS CloudWatch.
  console.log(`parmas: ${JSON.stringify(params)}`); // Cannot convert object to primitive value.
  console.log(`name: ${imageName}.${extension}`); // Favicon error, if name is `favicon.ico`.

  try {
    s3Object = await S3.getObject({
      Bucket: BUCKET,
      Key: decodeURI(imageName + '.' + extension)
    }).promise();
  } catch (error) {
    console.log('S3.getObject: ', error);
    return callback(error);
  }

  try {
    resizedImage = await Sharp(s3Object.Body)
      .resize(width, height)
      .toFormat(format, {
        quality
      })
      .toBuffer();
  } catch (error) {
    console.log('Sharp: ', error);
    return callback(error);
  }

  const resizedImageByteLength = Buffer.byteLength(resizedImage, 'base64');
  console.log('byteLength: ', resizedImageByteLength);

  // `response.body`가 변경된 경우 1MB까지만 허용됩니다.
  if (resizedImageByteLength >= 1 * 1024 * 1024) {
    return callback(null, response);
  }

  response.status = 200;
  response.body = resizedImage.toString('base64');
  response.bodyEncoding = 'base64';
  response.headers['content-type'] = [
    {
      key: 'Content-Type',
      value: `image/${format}`
    }
  ];
  return callback(null, response);
};

개발쪽은 아직 미숙하여 소스 코드는 아래 링크를 참고하였습니다.

소스 코드 변환 후 lambda 재배포하여 다시 한 번 리사이징 테스트를 해보았습니다.


Success

이미지 리사이징이 정상적으로 완료되었습니다!
Node.js 버전과 소스 코드 변경 후 정상적으로 리사이징이 되네요

이번 이미지 리사이징을 계기로 개발쪽 공부도 해야겠다는 생각이 드는데 바쁜 일정으로 올해는 무리입니다…

그래도 이미지 리사이징 성공할 수 있어서 뿌듯합니다.

긴 글 읽어주셔서 감사합니다.

NCP

2-2. [NCP] 네이버 클라우드에서의 보안 – Server (내부 방화벽을 이용한 GEOIP)

안녕하세요. ManVSCloud 김수현입니다.

오늘 포스팅은 이전 포스팅 주제였던 ACG(Access Control Group)에 이어 서버 내부 방화벽,
그 중에서도 GEOIP에 대해서 작성해보려합니다.

글로벌 사이버테러는 지속적으로 늘어나고 있고 공격 유형도 다양해지고 있습니다.

ACG는 Inbound Rule이 기본 차단이고 추가하는 IP/ACG에 대해서 허용하는 방식입니다.
또한 NACL을 이용한다고 하여도 한 국가가 사용하는 IP 대역을 전부 허용/차단 룰을 추가해주는 것도 힘들 것입니다.

주로 웹 서비스가 대표적인 케이스인데 해외 서비스를 하지 않는 경우 또는 특정 국가에서 서비스를 해야하는데 지속적인 공격으로 지역 기반 차단이 필요한 경우가 있습니다.

물론 Naver Cloud Platform의 Security Monitoring 서비스를 사용한다면 IDS/IPS, WAF, Anti-DDOS 등을 사용할 수 있으나 아직 보안 장비에 투자할 비용이 부족한 사용자에게는 부담스러울 수가 있어 비록 100% 완벽하지 않으나 어느정도 피해를 최소화하기 위해 GeoIP 모듈을 이용하여 내부 방화벽으로 서버를 지역 기반 차단하는 방법을 작성해보겠습니다.


Linux

iptables

Linux에서 지역 기반 차단을 진행해봅시다.
방화벽은 모두가 아는 iptables를 사용할 것입니다.

[Environment]
Naver Cloud Platform : centos-7.8-64

※ 기본 설정 및 설치 😎

[root@manvscloud-dev-pub-kr1 ~]# sestatus 
SELinux status:                 disabled

[root@manvscloud-dev-pub-kr1 ~]# yum install -y iptables*
yum install lrzsz libtool wget patch pcre-devel lua-devel libxml2-devel ncurses-devel zlib zlib-devel libtermcap-devel libc-client-devel bison gcc g++ cpp gcc-c++ curl curl-devel make automake unzip zip xz -y

[root@manvscloud-dev-pub-kr1 ~]# yum install -y "kernel-devel-uname-r == $(uname -r)"

[root@manvscloud-dev-pub-kr1 ~]# yum install -y yum install perl-Text-CSV_XS perl-NetAddr-IP perl-CPAN.noarch

※ Kernel-devel 확인 🕶

kernel-devel이 정상적으로 설치되지 않으면 이후 설치할 xtables 컴파일이 정상적으로 되지않습니다. yum으로 현재 커널 버전에 맞는 kernel-devel이 설치되지 않을 경우 repo를 찾아 커널 버전에 devel을 설치해주어야합니다.

※ xtables 설치 😎

[root@manvscloud-dev-pub-kr1 ~]# cd /usr/local/src
[root@manvscloud-dev-pub-kr1 src]# wget mirror.koreaidc.com/iptables/xtables-addons-2.10.tar.gz

// https://sourceforge.net/projects/xtables-addons/files/Xtables-addons/ ← 접속 시 각 버전별 xtables가 있습니다.

[root@manvscloud-dev-pub-kr1 src]# tar xvfz xtables-addons-2.10.tar.gz
[root@manvscloud-dev-pub-kr1 src]# cd xtables-addons-2.10/
[root@manvscloud-dev-pub-kr1 xtables-addons-2.10]# cat -n mconfig | grep TARPIT
    12	build_TARPIT=m

// build_TRIPIT=m은 redhat에서 지원하지않아 주석처리 해주었습니다.
[root@manvscloud-dev-pub-kr1 xtables-addons-2.10]# sed -i '12s/build_TARPIT=m/#&/' mconfig

(참고로 CentOS6 버전 이용 시 커널 버전이 낮아 xtables 버전 역시 낮춰서 설치하셔야합니다.
1.37버전 설치 권장드리며 mconfig에서 build_RAWNAT=m, build_SYSRQ=m, build_TARPIT=m, build_length2=m 총 4가지를 주석처리 해줘야합니다.)

[root@manvscloud-dev-pub-kr1 xtables-addons-2.10]# ./configure
[root@manvscloud-dev-pub-kr1 xtables-addons-2.10]# make
[root@manvscloud-dev-pub-kr1 xtables-addons-2.10]# make install
[root@manvscloud-dev-pub-kr1 xtables-addons-2.10]# cd geoip

[root@manvscloud-dev-pub-kr1 geoip]# ./00_download_geolite2
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (6) Could not resolve host: geolite.maxmind.com; Unknown error

00_download_geolite2를 실행해도 되지않기때문에 따로 GeoLite2-Country-CSV를 받아야합니다.
https://maxmind.com/ 에 가입 후 라이센스 발급 및 GeoLite2-Country-CSV.zip 설치가 가능합니다.

// 아래 MaxMind 링크를 추가해두었습니다.
[root@manvscloud-dev-pub-kr1 geoip]# ll | grep GeoLite2
-rw-r--r-- 1 root root 1934768 May 29 08:25 GeoLite2-Country-CSV_20210525.zip

[root@manvscloud-dev-pub-kr1 geoip]# ./10_download_countryinfo
[root@manvscloud-dev-pub-kr1 geoip]# unzip GeoLite2-Country-CSV_20210525.zip
[root@manvscloud-dev-pub-kr1 geoip]# perl -MCPAN -e shell
cpan[1]> install NetAddr::IP
cpan[2]> install Getopt::Long
cpan[3]> quit

[root@manvscloud-dev-pub-kr1 geoip]# cat GeoLite2-Country-CSV_20210525/GeoLite2-Country-Blocks-IPv{4,6}.csv | ./20_convert_geolite2 /tmp/CountryInfo.txt > GeoIP-legacy.csv
[root@manvscloud-dev-pub-kr1 geoip]# ./xt_geoip_build GeoIP-legacy.csv
[root@manvscloud-dev-pub-kr1 geoip]# mkdir -p /usr/share/xt_geoip
[root@manvscloud-dev-pub-kr1 geoip]# cp -a {BE,LE} /usr/share/xt_geoip/
[root@manvscloud-dev-pub-kr1 geoip]# cp -a /etc/sysconfig/iptables /etc/sysconfig/iptables_org

[root@manvscloud-dev-pub-kr1 ~]# systemctl enable iptables
[root@manvscloud-dev-pub-kr1 ~]# systemctl start iptables

/etc/sysconfig/iptables 수정하여 룰셋 적용해서 사용하시면 됩니다.


TIP & Personal Comments and Wrap-up

windows firewall

Windows에서 역시 국가 기반 차단이 가능합니다.

Powershell을 이용한 방화벽 컨트롤 및 IPSec 설정의 방법이 존재하는데 이번 편에서는 따로 다루지 않고 IPsec 편에서 추가적으로 설명하도록 하겠습니다.

또한 Apache와 Nginx 등 소프트웨어단에서 국가 기반 차단을 할 수도 있습니다.
이후 이러한 소프트웨어단에서 국가 기반 차단을 하는 방법도 포스팅할 것입니다.

위 링크는 WHOIS입니다.
서버내에서 공격성 접근 확인 시 해당 IP를 위 사이트에서 검색할 경우 어느 나라의 IP인지 확인이 가능합니다.

국가 기반 차단 시 필요한 국가 코드 및 WHOIS 조회 시 국가코드가 어느 나라인지 확인할 수 있는 국가 코드 조회 사이트도 참고하시면 좋을듯합니다.

긴 글 읽어주셔서 감사합니다.


IT/Linux/Kubernetes

Docker Kubernetes Online Study – Ubuntu 20.04 Install

안녕하세요. ManVSCloud 김수현입니다.

CloudNet@에서 진행했던 ANOS 2기 스터디에 이어 DKOS(DOCKER KUBERNETES ONLINE STUDY)의 기회를 얻게되었습니다.

핵심만 콕! 쿠버네티스

위 책을 기반으로 공부를 하게 될 것으로 보이며 스터디가 6월 6일부터 시작되어 Ubuntu 설치와 사전 권장 학습을 진행해야합니다.

오늘은 간단하게 Ubuntu 20.04 설치에 대해서 포스팅했습니다.


Ubuntu 20.04 Install

VirutalBox

VirutalBox는 위 링크에서 다운로드 받을 수 있으며 Ubuntu는 20.04 Server 버전으로 VirtualBox를 이용하여 설치해줄 것입니다.

일단 DKOS-Ubuntu-Master라는 이름으로 생성해주었습니다.
이후 Node를 OS부터 하나씩 설치하지않고 Master를 특정 부분까지 설치 및 설정 해준 뒤 스냅샷으로 Node를 생성할 것입니다.

English 선택
키보드 레이아웃 고르는 것입니다.
저는 Korean으로 선택해주었습니다.
네트워크 설정해주는 곳인데 저는 이곳에서 설정을 해줬습니다.
이후 서버 내에서 설정하는 것도 알려드리겠습니다.
DHCP로 하지않고 고정으로 설정 해보겠습니다.
저는 VirtualBox에서 네트워크 연결 방식을 “어댑터에 브릿지” 으로 설정해두었으므로
위와 같이 설정해주었습니다.
192.168.0.30을 Master IP로 사용할 것입니다.
이전에 CentOS 환경으로 Kubernetes를 설치 및 테스트한 경험이 있습니다.
이전에 테스트 당시 만들어둔 표인데 대충 참고만하면 좋을듯합니다.
Disk 파티션을 설정하는 곳입니다.
Custom storage layout을 선택하면 사용자 설정을 할 수 있지만 저는 그냥 통으로 설치했습니다.
사용자 계정과 패스워드 등을 설정해줍시다.
위 Install OpenSSH Server를 체크해줍시다.
이후 따로 설치해줄 필요없습니다.
설치가 진행되고 이후 update&reboot이 나오면 reboot을 진행해주고 설치를 마무리합니다.
설치가 완료되면 설치 시 생성한 사용자 계정을 통해 접속이 가능합니다.
설치가 완료되었다면 VirtualBox에서 스냅샷을 찍어줍시다.★
스냅샷을 찍어두는 습관은 이후에도 많은 도움이 됩니다.
putty를 이용하여 생성한 OS에 접근
Ubuntu 20.04 설치 완료

Today’s Tips

Ubuntu는 CentOS와 다른 것들이 많았습니다.
저도 Redhat 기반의 경험이 많아 데비안 환경은 조금 불편할 때가 많았습니다.

  • sudo
    (sudo su – 를 하여 root 계정으로 사용할 수 있지만 사용자 계정 환경에 익숙해져보려고 합니다.)
  • apt-get
    (CentOS에서는 패키지관리자가 yum으로 사용되었지만 Ubuntu에서는 apt-get으로 사용해줍니다. +rpm과 dpkg의 차이)

또한 Ubuntu18 이후 버전부터는 네트워크 설정 파일이 yaml 파일로 설정되어있습니다.
/etc/sysconfig/network-scripts/가 아닌 /etc/netplan/ 아래 yaml 파일을 이용하여 네트워크 설정이 가능합니다.

//편의를 위한 root 권한으로 변경
manvscloud@dkos-master:~$ sudo su -
[sudo] password for manvscloud:

//작업 전 백업
root@dkos-master:~# sudo cp -avp /etc/netplan/00-installer-config.yaml /etc/netplan/00-installer-config.yaml_org
'/etc/netplan/00-installer-config.yaml' -> '/etc/netplan/00-installer-config.yaml_org'
root@dkos-master:~# cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    enp0s3:
      addresses:
      - 192.168.0.30/24
      gateway4: 192.168.0.1
      nameservers:
        addresses:
        - 168.126.63.1
        search:
        - 169.126.63.2
  version: 2

위 yaml 파일을 보면 설치 시 설정했던 IP와 nameserver 등이 들어가있는 것을 볼 수 있습니다.
위 파일 수정 후 적용은 아래 명령어로 적용할 수 있습니다.

root@dkos-master:~# netplan apply

이후 스냅샷으로 생성한 Node들의 IP 설정을 해줄 때 사용하게 될 것입니다.
추가로 Hostname 변경 방법도 추가해두겠습니다.

//현재 hostname 확인
root@dkos-master:~# hostnamectl status

//hostname 변경
root@dkos-master:~# hostnamectl set-hostname dkos-node1

//hosts 파일 변경
root@dkos-master:~# vi /etc/hosts

network 및 hostname 설정간에 큰 어려움 없으시길 바랍니다.


Personal Comments

Kubernetes 시험 등록 후 다른 여러 일정으로 아직까지 시험을 못보고 있었습니다.
CloudNet@에서 진행하는 DKOS로 쿠버네티스 학습에 조금 더 집중을 할 수 있게 될 것같습니다. 2021-08-17 전에 시험 합격을 목표로 하고 있어 “네이버 클라우드에서의 보안” 포스팅을 제외하고는 쿠버네티스에 몰두할 예정입니다.

기회를 주신 CloudNet@ 팀에 다시 한 번 감사의 인사 올리며 포스팅을 마무리 하겠습니다.
긴 글 읽어주셔서 감사합니다.

NCP

2-1. [NCP] 네이버 클라우드에서의 보안 – Server (ACG)

안녕하세요. ManVSCloud 김수현입니다.

“1-1~1-2. [NCP] 네이버 클라우드에서의 보안 – Account 편” 포스팅이 끝나고
“2. [NCP] 네이버 클라우드에서의 보안 – Server 편” 포스팅을 작성하게 되었습니다.

오늘은 네이버 클라우드에서 Server 보안의 첫 번째 ACG(Access Control Group)를 알아봅시다.
ACG는 IP/Port 기반 네트워크 접근 제어 및 관리를 할 수 있는 무료 방화벽 서비스입니다.
Console에서 쉽게 허용된 트래픽만 접근할 수 있도록 설정하여 외부의 침입으로부터 차단해주는데 간단하고 단순하지만 잘 사용한다면 견고한 보안을 보여줄 것입니다.


ACG (Access Control Group)

https://www.ncloud.com/product/networking/vpc

네이버 클라우드에서 ACG는 Classic 환경과 VPC 환경에 조금 차이가 있습니다.
간단하게 표로 만들어보았으니 그림을 통해 차이를 확인해보시기 바랍니다.

Classic – ACG
VPC – ACG

ACG에서는 TCP, UDP, ICMP 프로토콜에 대해 규칙 설정이 가능하며 192.168.0.1(단일), 10.0.1.0/24(대역), 0.0.0.0/0(전체), manvscloud-acg(ACG 이름) 으로 접근소스 지정이 가능합니다. (manvscloud.com과 같이 도메인은 불가능)

VPC 환경에서는 Inbound / Outbound 규칙을 설정할 수 있으며 기본적으로 Inbound는 차단, Outbound는 허용입니다. (Classic은 X)

  • Inbound 규칙 : 서버로 들어오는 트래픽(외부→내부)에 대한 규칙
  • Outbound 규칙 : 서버에서 나가는 트래픽(내부→외부)에 대한 규칙

How to use ACG well

ACG를 어떻게 잘 사용할까요?

VPC 환경을 기준으로 아래와 같이 서버를 생성하여 보여드리겠습니다.

예시로 bastion host 1대, web server 1대, db server 1대를 생성하였습니다.
Bastion host에 대한 ACG는 정말 접속이 필요한 사용자만 접근할 수 있도록 허용하는 방법을 사용하고 추가적인 접근 통제는 VPN 또는 서버 내부에서 사용자 계정을 생성하여 계정 관리 및 탐지를 합니다.

Web Server는 ACG를 어떻게 주면 좋을까요?
Web Server로 SSH 접근하는 IP를 추가해주어야 할 것이고 HTTP, HTTPS 포트도 열어주어야할 것입니다.
저는 ACG 하나에 몰아서 넣기보다 분리해서 사용하며 ACG 생성 시 네이밍도 이후 잘 구분할 수 있도록 만드는 편입니다. (프로젝트명-용도-acg)

manvscloud-admin-acg에는 관리자 접속용 acg입니다.
manvscloud 프로젝트 전체에 접속이 필요한 관리자들에 대한 정책을 추가하여 사용합니다.
동일한 프로젝트의 다른 서버 생성 시에도 해당 acg만 추가해주기만 하면 되어 관리하기도 좋습니다.
또한 manvscloud-web-acg는 manvscloud 프로젝트의 web서버에 대한 acg입니다.
web서버마다 동일하게 필요한 정책을 추가하여 관리합니다. 이후 추가되는 web 서버마다 acg를 추가 생성하지 않고 해당 acg를 추가하여 공통으로 사용합니다.
마지막 하나의 acg는 위 사진에는 추가되어 있지않지만 필요 시에 해당 서버에만 특정적으로 필요로하는 서비스 해당 서버에서만 open되어야하는 정책에대한 acg를 추가하여 관리합니다. (예를 들면 개발자가 특정 웹 서버 FTP 접근이 필요할 경우)

정리하면 제가 ACG를 관리하는 방식은 아래와 같습니다.

  1. admin-acg (프로젝트 공통 관리자 관리 정책 ACG)
  2. web-acg, mysql-acg, was-acg 등 (공통 서비스 관리 정책 ACG)
  3. *-acg (특정 서버에만 적용되어야하는 정책 ACG)

더 좋은 ACG 사용 방법이 있다면 댓글로 의견 부탁드립니다.

위 정책은 웹 서버에 대한 web-acg 입니다.
제가 80포트를 0.0.0.0/0이 아니라 왜 10.0.13.0/24와 10.0.33.0/24 으로 열어두었을까요?

이유는 바로 제가 LB와 연결을 해놓았기때문입니다.
많은 고객분들이 웹 서버와 LB를 함께 사용하십니다.

[root@manvscloud-web-pub-kr2 ~]# tail -f /var/log/httpd/access_log 
10.0.13.11 - - [25/May/2021:07:15:15 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
10.0.13.11 - - [25/May/2021:07:15:15 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
10.0.33.11 - - [25/May/2021:07:15:45 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
10.0.33.11 - - [25/May/2021:07:15:45 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
10.0.33.10 - - [25/May/2021:07:15:45 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
10.0.33.10 - - [25/May/2021:07:15:45 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
10.0.13.10 - - [25/May/2021:07:15:45 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
10.0.13.10 - - [25/May/2021:07:15:45 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
10.0.13.11 - - [25/May/2021:07:15:45 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
10.0.13.11 - - [25/May/2021:07:15:45 +0900] "HEAD /index.html HTTP/1.1" 200 - "-" "-"
Load Balancer

만약 0.0.0.0/0으로 열어두었다면 도메인-LB-웹서버로 통신되는 것 외에도 웹서버 IP를 통해 웹서버로 접근 역시 가능합니다.
물론 웹서버 내에서 IP주소로 접근이 불가능하도록 설정이 가능하지만 방화벽 룰셋은 굳이 접근이 필요하지 않은 부분까지 허용하는 것은 권장하지 않으며 좋은 습관이 아닙니다.

위 ACG와 같이 설정하게되면 LB로는 접속 가능하지만 웹서버 IP로는 다이렉트로 접속이 불가능하게 됩니다.

이제 예시 중 DB Server에 대한 ACG 관리를 보겠습니다.

DB 서버는 주로 bastion host와 DB 연동이 필요한 서버에 대한 허용 정책을 설정하게되는데
아래와같이 ACG 이름을 이용하여 설정할 수 있습니다.

bastion-host Server와 연결된 manvscloud-bastion-acg를 DB Server ACG에 접근 포트 허용을 해주게된다면 manvscloud-bastion-acg와 연결된 서버들은 DB Server로 접근을 할 수 있게되는 것입니다.

추가 예시로 Web Server 전체가 DB와 연결되어야할 경우 ACG를 넣어줄 수 있으며
“나는 모든 웹 서버와 모든 DB 서버가 연결되지 않아 보안상 따로 따로 지정을 해줘야한다”라고 한다면 특정 서버에만 적용되어야하는 정책 ACG를 추가하여 연결되어야할 서버 IP만 허용해주는 방법이 있습니다.


Personal Comments and Wrap-up

지금까지 쉽고 간단한 ACG(Access Control Group)에 대해 알아보았습니다.
많은 사용자들이 귀차니즘으로 인해 ACG 관리가 잘 되지않고 있습니다.
원격 접속 포트, 쉘 접근 포트 등 중요한 포트를 0.0.0.0/0(전체)로 열어두는 사용자들을 생각보다 많이보게되고 또 보안상 위험을 알려도 아무 일 없을 거라며 넘어가버리는 분들 역시 많았습니다.
(귀찮아서 내버려뒀을 때 편한 감정과 공격받아 데이터 손실 후 감정은 많이 다릅니다…)

제발 막아주세요😥

다음 포스팅은 “2-1. [NCP] 네이버 클라우드에서의 보안 – SERVER”에 이어
“2-2. [NCP] 네이버 클라우드에서의 보안 – ACCOUNT” 내용은 IPSec VPN과 SSL VPN에 대해서 작성하려고 했었는데 ACG에 대한 내용에 이어 서버 내부 방화벽 활용 방법을 먼저 포스팅하려고 합니다.

긴 글 읽어주셔서 감사합니다.


IT/Linux/Kubernetes

[Linux] Cannot allocate memory, 캐시 메모리 제거 및 swap 생성

안녕하세요. ManVSCloud 김수현입니다.

오늘은 최근 제 블로그가 간헐적 끊김이 있어 확인 후
Memory 부족 원인을 파악하고 해결까지에 대한 내용을 작성해보았습니다.


Symptom & Identify the cause

어제 블로그에 포스팅 후 블로그 페이지가 정상적으로 출력되지 않는 현상이 발생했습니다.
서버 접속 시 Cannot allocate memory 에러가 발생하는 중이었으며 명령어 입출력이 정상적이지않은 상태라 우선 인스턴스 재부팅을 진행하게 되었습니다.

-bash: fork: Cannot allocate memory

대시보드에서 자원 사용률을 살펴보았는데 다른 자원 사용률은 정상인데 유독 Memory만 90% 이상 사용 중!! Memory 문제가 있는 것을 알게되었습니다.

VSZ, RSS 확인 시 Apache, PHP에서만 대량 사용 중이라 웹 로그를 먼저 확인해보았습니다.
1주일 단위로 logrotate가 걸려있어 최근 로그만 파악할 수 있는 상태였는데 우선 공격성으로 대량 접근하는 IP가 존재하는지 확인하였고 의심되는 IP는 WAF에서 추가 검토하였는데 따로 공격성 접근은 아닌 것으로 파악되었습니다.

[root@ip-10-0-1-68 httpd]# cat /var/log/httpd/access_log | awk '{print $1}' | sort -n | uniq -c | grep -Ev "10.0|172.16|192.168|-" | sort -rn | head -n 10
    399 88.150.-.-
    319 121.125.-.-
    200 125.141.-.-
    186 63.141.-.-
     83 66.249.-.-
     75 66.249.-.-
     49 117.111.-.-
     46 52.79.-.-
     37 66.249.-.-
     36 45.146.-.-

sar를 이용하여 추가 검토를 진행하였습니다.
인스턴스 재시작 후에도 메모리 사용량이 80% 이상 계속 유지 중이었습니다.

[root@ip-10-0-1-68 ~]# sar -r
Linux 4.14.193-149.317.amzn2.x86_64 (ip-10-0-1-68.ap-northeast-2.compute.internal) 	05/22/2021 	_x86_64_	(1 CPU)

12:00:02 AM kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
12:10:01 AM     65860    941080     93.46         0    103936   4407680    437.73    785580     28120       120
12:20:01 AM     58924    948016     94.15         0    106820   4414144    438.37    789028     31556       176
12:30:01 AM     66768    940172     93.37         0    103328   4410048    437.97    784968     28244       208
12:40:01 AM     64996    941944     93.55         0    102724   4410256    437.99    787600     26904       232
12:50:01 AM     61056    945884     93.94         0    103356   4414996    438.46    792132     26448        40
01:00:01 AM     68052    938888     93.24         0     96388   4412948    438.25    787572     24756       112
01:10:01 AM     63336    943604     93.71         0    100240   4414108    438.37    789356     27116       112
01:20:02 AM     65780    941160     93.47         0    102172   4408852    437.85    785084     29272       112
01:30:01 AM     61096    945844     93.93         0    108252   4408644    437.83    
.
. [생략]
.
08:26:04 AM       LINUX RESTART
08:36:33 AM       LINUX RESTART
08:40:02 AM kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
08:50:01 AM    192852    814088     80.85      2088    365732   3775100    374.91    401104    280868       296
09:00:01 AM    203284    803656     79.81      2088    378252   3162244    314.04    376764    289192       328
09:10:01 AM    195364    811576     80.60      2088    378620   3171048    314.92    384684    289380       332
09:20:01 AM    194748    812192     80.66      2088    379568   3173428    315.16    383840    290344       328
09:30:01 AM    181256    825684     82.00      2088    379744   3187564    316.56    407336    280344       328
Average:       193501    813439     80.78      2088    376383   3293877    327.12    390746    286026       322

마지막으로 free 명령어로 확인해보니 buff/cache가 비워지지 않고 그대로 남아있는 상태였습니다.

[root@ip-10-0-1-68 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:            983         448         125          60         408         338
Swap:             0           0           0

우선 아래 명령어를 이용하여 캐시 메모리를 지우고 메모리 확보를 진행 하였습니다.

[root@ip-10-0-1-68 ~]# sync && echo 3 > /proc/sys/vm/drop_caches
[root@ip-10-0-1-68 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:            983         435         365          60         182         358
Swap:             0           0           0

(sync && echo 3 > /proc/sys/vm/drop_caches 명령어에 대해서는 아래에서 추가적인 설명을 하도록하겠습니다.)

이후 메모리는 정상화 되었으나 추가적인 조치를 하지 않으면 동일한 장애가 다시 발생할 수 있어 우선 swap을 생성하기로 했습니다.


Create Swap

약 2~3G 정도 크기의 볼륨을 생성해줍니다.
swap 볼륨을 추가할 인스턴스와 연결을 시켜줍니다.
[root@ip-10-0-1-68 ~]# fdisk -l
Disk /dev/xvda: 15 GiB, 16106127360 bytes, 31457280 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 66F46060-ED00-4D14-9841-F5CB6310A14A

Device       Start      End  Sectors Size Type
/dev/xvda1    4096 31457246 31453151  15G Linux filesystem
/dev/xvda128  2048     4095     2048   1M BIOS boot

Partition table entries are not in disk order.


Disk /dev/xvdb: 3 GiB, 3221225472 bytes, 6291456 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

연결이 잘됐는지 확인 후 이제 파티션을 아래와 같이 생성해주었습니다.

[root@ip-10-0-1-68 ~]# fdisk /dev/xvdb
Welcome to fdisk (util-linux 2.30.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xb9e2e385.

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-6291455, default 2048): 
Last sector, +sectors or +size{K,M,G,T,P} (2048-6291455, default 6291455): 

Created a new partition 1 of type 'Linux' and of size 3 GiB.

Command (m for help): t
Selected partition 1
Hex code (type L to list all codes): 82
Changed type of partition 'Linux' to 'Linux swap / Solaris'.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

[root@ip-10-0-1-68 ~]# fdisk -l | tail -n 9
Disk /dev/xvdb: 3 GiB, 3221225472 bytes, 6291456 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xb9e2e385

Device     Boot Start     End Sectors Size Id Type
/dev/xvdb1       2048 6291455 6289408   3G 82 Linux swap / Solaris

swap 파티션이 잘 생성되었다면 mkswap으로 swap을 만들어주고 blkid에 나온 UUID를 참고하여 /etc/fstab에 추가해줍니다.
이 부분을 잘못 추가하게될 경우 정상 부팅이 되지않아 번거로운 추가 작업이 생길 수 있으니 잘 추가해줍시다.

[root@ip-10-0-1-68 ~]# mkswap /dev/xvdb1
[root@ip-10-0-1-68 ~]# blkid /dev/xvdb1
/dev/xvdb1: UUID="dc082f63-27d8-41b1-ad74-8b019162f766" TYPE="swap" PARTUUID="b9e2e385-01"
[root@ip-10-0-1-68 ~]# vi /etc/fstab

UUID=dc082f63-27d8-41b1-ad74-8b019162f766       swap    swap    defaults        0 0

[root@ip-10-0-1-68 ~]# swapon -a
[root@ip-10-0-1-68 ~]# swapon -s
Filename				Type		Size	Used	Priority
/dev/xvdb1                             	partition	3144700	0	-2

swapon 명령어에 -a 옵션을 주면 /etc/fstab에 있는 swap 파티션을 모두 enable 합니다.
-s 옵션은 사용중인 스왑 장치를 요약하여 볼 수 있습니다.

저는 볼륨을 2개 추가하여 하여 swap 볼륨 하나가 장애가 발생하더라도 나머지 하나로 작동할 수 있도록 해두었습니다.

[root@ip-10-0-1-68 ~]# swapon -s
Filename				Type		Size	Used	Priority
/dev/xvdb1                             	partition	3144700	0	-2
/dev/xvdc1                             	partition	3144700	0	-3

[root@ip-10-0-1-68 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:            983         461         332          60         188         328
Swap:          6141           0        6141

Swapfile

swap 볼륨을 따로 추가할 수 없을 수도 있습니다.
그때는 swapfile을 만들어서 사용할 수 있는데 간단하게 swapfile을 만드는 방법만 설명하겠습니다.

// dd 명령어를 이용해서 용량 지정 및 swap파일을 만들어줍니다. 
(mkdir로 디렉토리 만들어두는 것 아닙니다.)
[root@manvscloud-test-pub-kr2 ~]# dd if=/dev/zero of=/swapfile bs=1MiB count=4096
4096+0 records in
4096+0 records out
4294967296 bytes (4.3 GB) copied, 12.3267 s, 348 MB/s
//권한 변경
[root@manvscloud-test-pub-kr2 ~]# chmod 600 /swapfile
//스왑 생성
[root@manvscloud-test-pub-kr2 ~]# mkswap /swapfile
Setting up swapspace version 1, size = 4194300 KiB
no label, UUID=4e0d57c0-dd8f-4527-af2d-659ce04921b7

이후 /etc/fstab에 등록해주고 swapon -a 해주는 것은 동일합니다.

/swap01                          swap                swap    defaults        0 0

sync && echo 3 > /proc/sys/vm/drop_caches

Linux에서 아래 명령어를 이용하여 캐시를 비울 수 있습니다.

  • PageCache만 비우기
    sync && echo 1 > /proc/sys/vm/drop_caches
    (= sync; echo 1 > /proc/sys/vm/drop_caches)
  • dentries와 inodes 비우기
    sync && echo 2 > /proc/sys/vm/drop_caches
    (= sync; echo 2 > /proc/sys/vm/drop_caches)
  • PageCache, dentries, inodes 모두 비우기
    sync && echo 3 > /proc/sys/vm/drop_caches
    (= sync; echo 3 > /proc/sys/vm/drop_caches)

sync를 통해 파일 시스템 버퍼를 플러시합니다.
(개발쪽은 잘 모르지만 JAVA나 Python에서 flush() 함수가 버퍼 비우는 용도로 사용되는걸 본 적이 있습니다)

단 echo 3을 사용한다면 모두 비우기가 가능하지만 디스크 I/O가 활발한 시스템에서 사용하게 된다면 CPU 자원 사용량이 일시적으로 상승합니다.

  • pagecache : 리눅스 커널에 의해 사용되는 기본 디스크 캐시.
    대부분의 경우 커널은 디스크에서 읽거나 디스크에 쓸 때 페이지 캐시를 참조합니다.
    I/O 속도와 퍼포먼스를 높이기 위해 시스템이 할당한 임시 메모리 저장소로 임시 메모리에 저장된 파일을 다시 읽을 때 해당 메모리에서 바로 불러오기에 디스크의 읽기,쓰기 속도가 빨라집니다. (Windows에서는 동일한 역할로 페이지 파일이 있습니다.)
  • dentries : dcache, 즉 디렉토리 항목 캐시입니다. 경로/파일명을 특정 dentry로 변환하여 빠른 조회를 제공하며 디스크가 아닌 RAM에 저장됩니다. (inode를 검색하여 수행됩니다.)
  • inodes : 파일과 디렉토리에 관한 정보를 담고 있는 자료 구조
    (물리적 위치, 크기, 생성 일시, 권한)
[root@ip-10-0-1-68 ~]# vmstat -m | head -n 1; vmstat -m | grep proc_inode_cache
Cache                       Num  Total   Size  Pages
proc_inode_cache          13570  13570    688     23

⬆ (위 명령어로 inode 캐시값 확인이 가능합니다.)

※ TIP-1 (Slab) 😎

서비스 프로세스 할당량을 계산해보아도 전체 메모리에 비해 유휴 메모리가 없을 때가 있을 것입니다. 이는 커널에서 메모리를 사용하고 있기때문인데 커널에서 사용하고 있는 메모리는 slab 메모리 영역을 확인하면 얼마나 사용하고 있는지 알 수 있습니다.

[root@ip-10-0-1-68 ~]# cat /proc/meminfo | grep Slab
Slab:              48832 kB

Slab에 대한 내용이 깊이있게 설명되어 있는 곳은 링크로 남겨두었으니 참고바랍니다.

  • Slab : 메모리 영역 중 커널이 직접 사용하는 영역을 Slab영역이며 dentries와 inodes 캐시가 이 영역에 해당됩니다.
    (slabtop 명령어를 이용하여 현재 시스템의 slab 자료 구조 상태를 확인할 수 있습니다.)
[root@ip-10-0-1-68 ~]# slabtop
 Active / Total Objects (% used)    : 232162 / 241799 (96.0%)
 Active / Total Slabs (% used)      : 7662 / 7662 (100.0%)
 Active / Total Caches (% used)     : 74 / 101 (73.3%)
 Active / Total Size (% used)       : 55501.21K / 58750.71K (94.5%)
 Minimum / Average / Maximum Object : 0.01K / 0.24K / 9.50K

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
 39580  39403  99%    0.20K   1979	 20	 7916K vm_area_struct
 38080  38080 100%    0.06K    595	 64	 2380K anon_vma_chain
 30072  30072 100%    0.19K   1432	 21	 5728K dentry
 19136  19089  99%    0.09K    416	 46	 1664K anon_vma
 15504  14550  93%    0.04K    152	102	  608K Acpi-Namespace
 15210  15210 100%    0.13K    507	 30	 2028K kernfs_node_cache
 13570  13570 100%    0.67K    590	 23	 9440K proc_inode_cache
 10868  10476  96%    0.60K    418	 26	 6688K inode_cache
  7808   6525  83%    0.03K     61	128	  244K kmalloc-32
  5746   4799  83%    0.94K    169	 34	 5408K xfs_inode
  5248   3838  73%    0.06K     82	 64	  328K kmalloc-64
  5180   3660  70%    0.57K    185	 28	 2960K radix_tree_node
  3822   2783  72%    0.09K     91	 42	  364K kmalloc-96
  3264   2517  77%    0.25K    102	 32	  816K kmalloc-256
  3248   3248 100%    0.07K     58	 56	  232K Acpi-Operand
  3072   3072 100%    0.02K     12	256        48K kmalloc-16
  3060   3060 100%    0.05K     36	 85	  144K ftrace_event_field
  2560   2560 100%    0.01K	 5	512        20K kmalloc-8
  2037   1762  86%    0.19K     97	 21	  388K kmalloc-192
  1610   1610 100%    0.09K     35	 46	  140K trace_event_file
  1536   1258  81%    0.50K     48	 32	  768K kmalloc-512
  1344   1046  77%    0.12K     42	 32	  168K kmalloc-128

※ TIP-2 (vfs_cache_pressure) 😎

vfs_cache_pressure는 리눅스 커널의 vm구조와 관련된 파라미터입니다.
vfs_cache_pressure를 이용해서 커널에게 디렉토리와 inode Object에 대한 캐시로 사용된 메모리를 반환하도록하는데 이 값이 0일 경우 커널이 오브젝트에 대한 캐시를 반환하지 않아 Out Of Memory가 발생합니다.
보통 이 값이 100으로 설정되어 있으나 자주 캐시를 반환하기 위해서는 이 값을 변경해주어야하며 값이 높아질수록 메모리에서 캐시를 보관하지 않게되어 dentries와 inodes 캐시를 줄일 수 있습니다.

[root@ip-10-0-1-68 ~]# cat /proc/sys/vm/vfs_cache_pressure
100

기본값은 100으로 되어있습니다.
/proc/sys/vm/vfs_cache_pressure 에 값을 지정해주는 것은 일회성이니 sysctl.conf에 설정해줍니다.

[root@ip-10-0-1-68 ~]# vi /etc/sysctl.conf 이후 아래값을 추가 및 저장, load 해줍니다
vm.vfs_cache_pressure = 10000
[root@ip-10-0-1-68 ~]# sysctl -p

Personal Comments and Wrap-up

아직 캐시 메모리를 비우고 swap 생성만 해두었습니다.
이걸로 완벽하게 해결됐다고 보기는 어렵습니다. 이후 메모리 사용량 경보를 추가하여 알림 설정을 하고 현재 오토스케일링 추가를 생각하고 있습니다.
(우선 오토스케일링 추가 전 서버 내 자원 최적화 작업을 먼저 진행해야겠습니다.)

그리고 이번 메모리 최적화에 도움주신 리눅서님에게 감사의 인사올립니다.
(아… 난 왜 swap을 잊고있었지? 매번 감사합니다…🙇🏻‍♂️ 덕분에 이번 기회로 메모리에 대해 많이 공부한 것같습니다.)

IT/Linux/Kubernetes

[IT] Windows registry DWORD와 QWORD

안녕하세요. ManVSCloud 김수현입니다.

오늘은 Windows에서 레지스트리 변경 시 볼 수 있는 DWORD(32비트)와 QWROD(64비트)에 대해 알아보는 시간을 가져볼까합니다.


DWORD & QWORD

Registry에서…

Windows 레지스트리 편집을 하다가보면 DWORD와 QWORD를 마주치게 됩니다.
DWORD와 QWORD는 눈에 보이는 그대로 32비트와 64비트의 차이인가?

그렇다면 사용하는 Server의 OS가 32bit라면 DWORD를 사용해야하고
64bit라면 QWORD를 사용해야할까?

우선 두 차이를 알기 전에 데이터 타입에 대해 알아보았습니다.
데이터 타입에는 다음과 같은 타입들이 존재합니다.


Data Type

인텔의 16비트 프로세서에서는 기본 처리 단위를 WORD로 재정의 했었습니다.
프로세서의 단위가 현재 8비트 16비트 32비트 64비트 등으로 발전하였고 16비트를 기반으로 한 기본처리 단위인 WORD가 변경이 되어야 했습니다.

과거에 DOS 같은 경우 16bit였지만 최근에는 대부분 64bit 운영체제를 사용하고 있으며 소수의 32bit 사용자들이 남아있습니다.

→ 운영체제에 16bit, 32bit, 64bit 무엇을 의미하는가?
: 이는 한 번에 전송이나 수신할 수있는 데이터의 크기를 각 16, 32, 64비트로 나누어놓은 것입니다. 32bit의 OS는 한 번에 32bit의 데이터를 처리할 수있다는 말입니다.

32bit의 환경에서는 DWORD, QWORD가 모두 4byte, 즉 32bit로 표현되며, 64bit의 환경에서는 DWORD는 32bit, QWORD는 64bit로 표현됩니다.

-. 32bit OS
DWORD : 32bit
QWORD : 32bit

-. 64bit OS
DWORD : 32bit
QWORD : 64bit


Then why not use QWORD on 64bit OS?

정확하게 말해서는 과거에 OS가 x86-32비트를 사용하였기에 대부분 시스템이 32bit에 호환되도록 만들어졌습니다.

우리가 흔히 아는 x64는 Only 64bit가 아닙니다. OS x64는 32/64비트 입니다.
32비트와 64비트 모두 호환이 가능합니다. 64bit 전용으로 IA64가 있습니다.
x64는 32비트 프로그램을 직접 실행할 수 있지만 IA64의 경우 32비트 에뮬레이션의 도움이 필요합니다.

Registry는 기본적으로 32bit 영역과 64bit 영역을 가집니다. 32bit 영역은 32bit 프로세스가 사용하며, 64bit 영역은 64bit 프로세스가 사용하게 됩니다.

DWORD
16진수 / 10진수의 값을 저장할 수 있으며, 참과 거짓을 나타낼 때는 1과 0으로 나타냅니다. WORD는 0 에서 65535 까지의 숫자를 나타낼 수 있는 16비트 수입니다.
즉 DOUBLE WORD인 DWORD는 16 X 2인 32 비트가 됩니다.
DWORD 값은 2의 32승으로 40억 가지 이상의 데이터를 표현할 수 있습니다.

QWORD
16진수 / 10진수의 값을 저장할 수 있습니다.
또한 64비트를 지원합니다, 하지만 그렇다고 하여 64비트 OS에서만 사용할 수 있을 것같지만
실제로는 32비트 윈도우에서도 사용할 수 있습니다.
64비트는 그저 데이터 길이가 64비트라는 것을 의미할 뿐입니다.

사실 이론만 들어서는 저도 대학생 때 들어본 거같긴한데 이게 무슨 소린가…합니다.

“개념 없는 직관은 맹목이요, 경험 없는 사고는 공허하다”라는 말이 있습니다.
이론도 중요하지만 직접 테스트를 해보기로 하였습니다.

TEST 1️⃣. TLS 1.2 문제로 테스트 할 때 레지스트리 중 SSL 2.0을 삭제 후 재부팅을 하면 정상적으로 올라오지 않아 강제 재부팅을 하여 올렸던 기억이 있어 SSL 2.0의 Client에 적용된 DWORD 값을 QWORD로 바꾼 뒤에 재부팅을 해보았습니다.

결과 : 정상 부팅 완료.

TEST 2️⃣. Notepad 레지스트리를 아래 그림에 있는 DWORD를 삭제하고 QWORD로 동일한 값을 준 뒤 서버 재시작 후 Notepad가 정상적으로 실행되는지 확인해보았습니다.

결과 : 정상 부팅 후 notepad 실행 시 메모장 정상 작동

TEST 3️⃣. ‘서버관리자’의 레지스트리 값을 위와 같은 방식으로 변경 후 재부팅 해보았습니다.

결과 : 드디어 원하는 결론을 얻어낼 수 있었습니다. 재부팅 후 서버가 올라온 뒤 레지스트리의 종류가 QWORD에서 DWORD로 자동 변환된 것이 존재하는 것을 발견하였습니다.

해당 결과를 보고 의심할 수 있는 점 : 소프트웨어 서버관리자는 32bit를 요구하고 있다는 점! QWORD(64bit)의 데이터 길이로 불렀는데 Kernel → User 등을 넘어오면서 64비트를 32비트로 변환한 것으로 보입니다. 위에서 설명한 “32bit의 환경에서 DWORD, QWORD가 모두 4byte, 즉 32bit로 표현”된다는 것은 OS인 windows, linux 소프트웨어뿐만이 아니라 다른 소프트웨어에서도 동일하게 적용되는 듯합니다.


Conclusion

64비트 환경에서 32비트를 실행하면 물론 사용이 가능은 하나 굳이 그럴 이유는 없습니다.
64비트에서 32비트 프로그램을 실행할 때, 또는 32비트에서 64비트 프로그램을 실행할 때 kernel32.dll, *64.dll, Ntdll.dll 등 각 비트에서 필요한 dll파일이나 코드를 불러오게 됩니다.

하지만 dll 파일이 없거나 코드에 문제가 있다면?

DWORD와 QWORD 역시 QWORD를 사용할 수는 있으나
아직 하드웨어나 소프트웨어적인 부분에서 32bit를 필요로 하는 것들이 존재하기때문에
설정을 할 때 정확히 확인 후 설정이 필요할 것으로 보입니다.

긴 글 읽어주셔서 감사합니다.