$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state DOWN mode DEFAULT group default qlen 1000
    link/ether f4:a3:91:c6:73:c0 brd ff:ff:ff:ff:ff:ff
3: eno2: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state DOWN mode DEFAULT group default qlen 1000
    link/ether f4:a3:91:c6:73:c0 brd ff:ff:ff:ff:ff:ff
5: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
    link/ether f4:a3:91:c6:73:c0 brd ff:ff:ff:ff:ff:ff

작업의 시작은 운용한지 3년이 다되가는 서버들을 점검하는 차원에서 Reboot 겸 update를 하려했다.

 

Dell 서버는 iDRAC(Integrated Dell Remote Access Controller)이란 웹 콘솔을 제공해주어서 직접 Data Center에 가지 않아도 이런작업은 웹 브라우저에서 실행할 수 있다. 웹 브라우저로 접속은 할 수 있지만 물리서버의 Management 전용 IP로 붙어야 한다. 예를 들면 https://172.21.239.47 과 같은 형태의 주소로 접속하고 ID/PW를 입력하여 로그인 하면 서버의 상태 확인 및 remote로 직접 제어할 수 있다.

 

서버의 전원을 켤 때는 높은 전압과 과부하가 생기기때문에 3년이 다되어가는 서버를 껐다가 다시 부팅을 시도하는 경우

다양한 원인으로 서버가 켜지지 않을 수 있다. 그래서 Warm Boot 방식으로 진행했다.

 

사용한지 3년이 다 되어가는(실제로는 2년 10개월) 서버들중 1대에서 Reboot을 완료하고

서버 상태를 확인하는데 네트워크 인터페이스가 Down 상태로 내려가 있어서 통신이 되지 않았다.

서버가 이상한건지 이 앞단에 연결된 스위치가 문제인지 알 수 없었다.

$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state DOWN mode DEFAULT group default qlen 1000
    link/ether f4:a3:91:c6:73:c0 brd ff:ff:ff:ff:ff:ff
3: eno2: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state DOWN mode DEFAULT group default qlen 1000
    link/ether f4:a3:91:c6:73:c0 brd ff:ff:ff:ff:ff:ff
5: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
    link/ether f4:a3:91:c6:73:c0 brd ff:ff:ff:ff:ff:ff

 

네트워크 엔지니어의 확인 결과 LACP(Link Aggregation Control Protocol) PDU(Protocol Data Units)를 받지 못하고 있는 상황이었다.

확인 시에는 스위치장비에 ssh로 접근해서 show interface 를 이용하여 확인한 결과중에 suspended due to no lacp pdus 메시지를 보고 판단하였다.

 

리눅스 서버에서 확인해보면 세팅은 정상이었다.

$ cat /proc/net/bonding/bond0 
Ethernet Channel Bonding Driver: vX.X.X

Bonding Mode: IEEE 802.3ad Dynamic link aggregation
Transmit Hash Policy: layer2 (0)

 

LACP가 문제인가 싶어서 network bonding의 모드를 변경해보았다.

(변경한 후에 실제 반영하려면 `systemctl restart network` 로 서비스를 재시작해주어야 한다.)

$ cat /etc/sysconfig/network-scripts/ifcfg-bond0

NAME=bond0
DEVICE=bond0
ONBOOT=yes
BOOTPROTO=none
IPADDR=172.21.239.209
PREFIX=24
GATEWAY=172.21.239.1
TYPE=Bond
BONDING_OPTS="miimon=100 mode=1"
# 주석처리 BONDING_OPTS="miimon=100 mode=802.3ad"

모드는

 

  • mode=0 혹은 balance-rr
  • mode=1 혹은 active-backup
  • mode=2 혹은 balance-xor
  • mode=3 혹은 broadcast
  • mode=4 혹은 802.3ad

 

자세한 설명은 linux 서버에서 /usr/share/doc/iputils-xxxx/README.bonding 에서 확인할 수 있다.

 

일단 확인해보니 bonding 이슈는 아니었고 다시 설정을 802.3ad로 원복 시켰다.

결국 시스템엔지니어는 데이터센터에 직접 찾아가서 서버를 shutdown 시키고 power cycle 작업을 수행하였다.

약간의 전기가 iDRAC, NIC케이블에 남아있어도 초기화가 안될 수도 있기떄문에 Cold boot를 진행하였다.

이를 위해서 shutdown한 후, 전원케이블을 서버에서 분리하고 네트워크 케이블들도 분리하였다.

 

이러한 작업을 하는김에 NIC의 펌웨어를 업데이트하는 작업을 하였다.

이 작업에서 Dell 서버는 2가지 방식으로 작업이 가능한데

  • DSU (Dell System Update)
  • OME (OpenManage Enterprise)

DSU를 이용하였다.

### 거의 3년만이라 dsu 한번 업데이트 해주고
$ yum update dell-system-update -y

### 업데이트 할때 파일들의 signature verification 을 무시하도록 옵션 준다.
$ dsu --ignore-signature

 

Firmware upgrade하고 통신이 정상적으로 되는것을 확인하였다.

또한 서버 상태도 확인하였다.

  • 네트워크 인터페이스가 UP 된것
  • 게이트웨이로 ping
$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP mode DEFAULT group default qlen 1000
    link/ether f4:a3:91:c6:73:c0 brd ff:ff:ff:ff:ff:ff
3: eno2: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP mode DEFAULT group default qlen 1000
    link/ether f4:a3:91:c6:73:c0 brd ff:ff:ff:ff:ff:ff
5: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether f4:a3:91:c6:73:c0 brd ff:ff:ff:ff:ff:ff
    
$ ping 172.21.239.1
PING 172.21.239.1 (172.21.239.1) 56(84) bytes of data.
64 bytes from 172.21.239.1: icmp_seq=1 ttl=64 time=0.177ms
64 bytes from 172.21.239.1: icmp_seq=2 ttl=64 time=0.118ms
..

 

 

개념이 잘 이해가 안갔던 것들은 검색해서 모아두었다.

(요약한 내용은 본인의 생각을 적은것이므로 원작자의 의도와 다를 수 있다.)

 

 

ARP(Address Resoultion Protocol)는 IP주소를 가지고 MAC주소를 가져오기 위한 프로토콜

RARP(Reverse)는 반대로 MAC주소를 가지고 IP주소를 가져옴

https://peemangit.tistory.com/207

 

[Network] ARP, RARP,GARP 개념

1. ARP (Address Resolution Protocol) IP 주소를 이용해 상대방의 MAC 주소를 알아오는 프로토콜이다. ARP 요청·응답 완료되면 ARP table에 각 노드의 ARP 정보를 저장한다. ARP 요청: 특정 IP주소에 대해 MAC..

peemangit.tistory.com

LACP는 여러개의 이더넷포트를 하나로 묶어서 사용할 수 있어서

대역폭을 넓힐 수 있고, 하나의 포트에 장애가 발생해도 다른 포트에서 처리할 수 있어서 링크의 단절을 피할 수 있다.

https://devopsnet.tistory.com/39

 

LACP(802.1ax) 이야기

우리가 알고있는 Network에서 사용되는 LACP(Link Aggregation Control Protocol)는 여러 물리링크를 하나의 논리적인 링크로 생성함으로써 대역폭을 증가시키고 장애 발생시 정상적인 성능 저하(fault-tolerant)

devopsnet.tistory.com

SDU: 상향/하향 통신 레이어 간에 전달되는 실제 정보

PDU: 동일 레이어 내에서 데이터 단위

https://velog.io/@hidaehyunlee/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%84%B8%EA%B7%B8%EB%A8%BC%ED%8A%B8-%ED%8C%A8%ED%82%B7-%ED%97%B7%EA%B0%88%EB%A6%B4-%EB%95%90-PDU%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90

 

데이터? 세그먼트? 패킷? 헷갈릴 땐 PDU를 알아보자

모든 계층에서, 우리가 전송하는 데이터를 `데이터`라고 부를까? PDU를 알고난 후 부터는 그렇지 않을 것이다.

velog.io

 

 

 

'Infra' 카테고리의 다른 글

Python으로 Opendistro Account 생성하기  (0) 2021.03.17
Cloudfront와 S3를 이용한 웹 서비스 구성  (0) 2021.03.10
Hashicorp Terraform (테라폼)  (0) 2020.10.28
AWS Subnet 생성하기  (0) 2020.07.23
AWS VPC 생성하기  (0) 2020.07.23

기존에 Cognito를 이용해서 Kibana의 접근을 제한해왔었다.

기존에는 단순히 kibana 접근만 제한하고 있어서 audit 통과가 목적이었지만

운영하다보니 Role을 부여해서 세세하게 접근을 제한하고 싶어졌다.

Elasticsearch의 index단위로 접근을 제한하고 read/write 권한 조절하기 위하여

Opendistro를 이용하기로 했다.

 

Elasticsearch 설치형에서는 이미 사용중이었고

AWS의 ElasticSearch에 지원을 한지 좀 되었지만.. 이제 반영하려한다.

 

Launching Open Distro for Elasticsearch security features on Amazon Elasticsearch Service | Amazon Web Services

We are excited to announce that we are making new Open Distro for Elasticsearch security features available on Amazon Elasticsearch Service. Amazon Elasticsearch Service is frequently used for sensitive enterprise workloads, and today’s launch adds multi

aws.amazon.com

 

Open Distro의 API가 있겠거니하고 문서를 찾아본다.

 

API

Documentation for Open Distro for Elasticsearch, the community-driven, 100% open source distribution of Elasticsearch with advanced security, alerting, deep performance analysis, and more.

opendistro.github.io


Account Create 요청이다.

#### REQUEST

PUT _opendistro/_security/api/internalusers/<username>
{
  "password": "kirkpass",
  "backend_roles": ["captains", "starfleet"],
  "attributes": {
    "attribute1": "value1",
    "attribute2": "value2"
  }
}

 

backend role 대신에 일반 role을 맵팽하는 방식을 사용할 것이고,

attributes 정보는 아직은 안쓸것이라서 

password만 사용할 예정이다. (이것도 default pw로 처음 로그인할때 알아서 바꾸게 해줄 방법은 없으려나..)

 

1차로 구현했던 코드

아래의 내용은 본인 환경에 맞게 전부 수정해서 사용해야 한다.

 

참고로 opendistro_admin, opendistro_admin_pw는 실제 admin 계정의 것을 사용하거나, 계정관련 security menu 접근 권한이 있는 계정의 정보를 이용한다.

import json
import requests

def opendistro_create_user():
    user_name = 'testuser'
    base_url = 'https://vpc-sfixer-applog-jdqfbp4ttozpwfxxxxxxxxxxxx.ap-eastsouth-1.es.amazonaws.com/'
    path = '_opendistro/_security/api/internalusers/%s' % (user_name)
    url = base_url + path
    payload = {'password': 'INITpassword1234!@#$', 'backend_roles': ['read_write_all_index']}
    rs = requests.put(url, auth=('opendistro_admin', 'opendistro_admin_pw'), data=payload)
    print(rs.content)

매뉴얼에 나와있는대로 PUT을 수행했다.

결과는 에러

b'{"error":"Content-Type header is missing","status":406}'

해더를 빼먹었으니 넣어준다.

 

2차로 구현한 코드

import json
import requests

def opendistro_create_user():
    user_name = 'testuser'
    base_url = 'https://vpc-sfixer-applog-jdqfbp4ttozpwfxxxxxxxxxxxx.ap-eastsouth-1.es.amazonaws.com/'
    path = '_opendistro/_security/api/internalusers/%s' % (user_name)
    url = base_url + path
    headers = {'content-type': 'application/json'}
    payload = {'password': 'INITpassword1234!@#$', 'backend_roles': ['read_write_all_index']}
    rs = requests.put(url, auth=('opendistro_admin', 'opendistro_admin_pw'), data=payload, headers=headers)
    print(rs.content)

결과는 다시 에러

b'{"status":"error","reason":"Could not parse content of request."}'

data type이 문제로 보인다.

 

requests.put 할때 파라메터를 수정해주는데, 해결방안이 2개가 있다. 

  1. data=json.dumps(payload)
  2. json=payload

2안을 택했다.

3차로 구현한 코드 [성공]

import json
import requests

def opendistro_create_user():
    user_name = 'testuser'
    base_url = 'https://vpc-sfixer-applog-jdqfbp4ttozpwfxxxxxxxxxxxx.ap-eastsouth-1.es.amazonaws.com/'
    path = '_opendistro/_security/api/internalusers/%s' % (user_name)
    url = base_url + path
    headers = {'content-type': 'application/json'}
    payload = {'password': 'INITpassword1234!@#$', 'backend_roles': ['read_write_all_index']}
    rs = requests.put(url, auth=('opendistro_admin', 'opendistro_admin_pw'), json=payload, headers=headers)
    print(rs.content)

결과는 성공이다.

b'{"status":"CREATED","message":"\'testuser\' created."}'

 

Role Mapping 관련해서는 

mapping 해주거나 backend_role을 사용하도록 해야한다.

다른 동료들이 나중에 Kibana에서 보고 처리하기 쉬울것 같아서 mapping 해주는 방식을 선택하였다.

#### REQUEST

PUT _opendistro/_security/api/rolesmapping/<role>
{
  "backend_roles" : [ "starfleet", "captains", "defectors", "cn=ldaprole,ou=groups,dc=example,dc=com" ],
  "hosts" : [ "*.starfleetintranet.com" ],
  "users" : [ "worf" ]
}

여기에서 나는 role만 매핑할거라서 사용하게될 정보는 users만 사용한다.

그리고 미리 role을 kibana에서 생성해두고 그것을 사용한다.

 

1차 시도한 코드

import json
import requests

   
def opendistro_mapping_role(user_name, role_name='read_write_limit'):
    base_url = 'https://vpc-sfixer-applog-jdqfbp4ttozpwfxxxxxxxxxxxx.ap-eastsouth-1.es.amazonaws.com/'
    path = '_opendistro/_security/api/rolesmapping/%s' % (role_name)
    url = base_url + path
    headers = {'content-type': 'application/json'}
    payload = {'users': [user_name]}
    rs = requests.put(url, auth=('opendistro_admin', 'opendistro_admin_pw'), headers=headers, json=payload)
    print(rs.content)

돌려보니 되는줄 알았다.

문제는 users라고 되어있는것을 제대로 파악 못한것이 문제.

이런식으로 루프 돌려가면서 호출했더니 가장 마지막에 호출한 user 정보 1개만 남았다.

for user in user_list:

    opendistro_mapping_role(user)

 

 2차 시도한 코드는 원하는대로 동작 [성공]

import json
import requests

   
def opendistro_mapping_role(user_list, role_name='read_write_limit'):
    base_url = 'https://vpc-sfixer-applog-jdqfbp4ttozpwfxxxxxxxxxxxx.ap-eastsouth-1.es.amazonaws.com/'
    path = '_opendistro/_security/api/rolesmapping/%s' % (role_name)
    url = base_url + path
    headers = {'content-type': 'application/json'}
    payload = {'users': user_list}
    rs = requests.put(url, auth=('opendistro_admin', 'opendistro_admin_pw'), headers=headers, json=payload)
    print(rs.content)

1차에서 개별 사용자를 처리하려고 loop 돌렸던 것을 없애고 

그 사용자 list를 인자값으로 넘겼더니 제대로 mapping 된것을 kibana에서 확인하였다.

사용자 list라함은 이런 형태로 값이 저장되어있다.

  • user_list = ['user1', 'user2', 'user3', ... , ... , 'user100']

 

 

'Infra' 카테고리의 다른 글

인프라 개념 LACP  (0) 2022.06.10
Cloudfront와 S3를 이용한 웹 서비스 구성  (0) 2021.03.10
Hashicorp Terraform (테라폼)  (0) 2020.10.28
AWS Subnet 생성하기  (0) 2020.07.23
AWS VPC 생성하기  (0) 2020.07.23

 

Web service를 시작하기로 하였다.

CDN을 둬서 static contents들을 서빙하려하고 cloudfront를 사용하기로 결정하였다.

Data의 보관은 S3 bucket에 static contents를 두고 cloudfront에서 접근해서 가져가는 방식을 사용하기로 결정하였다.

 

기본 세팅

S3, Cloudfront, Route53 관련 세팅하는 것은 하나씩 읽어가면서 누르면 된다. 검색하면 포스팅한 글이 이미 많다.

개인적으로는 각 설정 항목에 대한 설정은 한번 자세히 보고 넘어야가야 한다는 생각이다.

혹은 cloudformation 템플릿을 이용해서 구성한다. 

CloudFront와 S3를 이용해서 서빙을 한다면 처음 세팅할때 이 부분을 반드시 "Yes"로 해주고 Identity는 Access S3 bucket content only through CloudFront를 선택한다.

만약 No로 한 경우에는 나중에 Origin 설정에서 바꾸려고 해도 바꿀수는 없고, Origin을 삭제하고 다시 만드는 방법이 있을 수 있겠다.

HTTPS로만 접근을 허용하겠지만
그렇다고 HTTP 요청을 다 버리고 싶진 않으니 Redirect 하도록 Behavior에서 설정해주었다.

 

TTL

TTL 설정을 통한 cache 전략에서 고민이 시작되었다.

평소에는 static 파일들을 추가만 할테니 TTL을 aws에서 default로 제공하는 것을 그냥 사용해도 아무런 문제가 없다.

문제는 hotfix. 

배포된 이미지 파일의 구문이 잘 못되서 급히 변경해야 되는 상황 같은 경우.

물론 Invalidations 기능이 있어서 긴급할때는 이 기능을 이용해서 cache를 다 날려버릴 수 있다.

AWS console에서 눌러가면서 하거나

 

아니면 awscli로 처리한다.

# sample

aws cloudfront create-invalidation --distribution-id $distribution_ID --paths "/*"

 

다시 TTL 설정으로 돌아와서 3개의 TTL 의미 파악을 못했다.

물론 HTTP Header에 Cache-Control: max-age=xx, Cache-Control: s-maxage=xx 같은 값이 없으면 default TTL인 것은 알겠으나, 
minimum TTL, maximum TTL 관련해서는 이 메뉴얼을 읽어보았지만 이해를 못했었다.

  • default TTL (Cache-Control, Expires header가 없는 경우)
  • minimum TTL
  • maximum TTL

 

그러던중에 이를 이해할 수 있게 정리한 포스팅을 찾았다.

  • 만약 Cache-Control 헤더의 max-age 값이 사용자가 정의한 minimum TTL과 maximum TTL 사이에 있는 경우, CloudFront는 maximum TTL에 지정된 시간에 대해 개체를 캐시합니다.
  • 만약 max-age 값이 사용자가 정의한 minimum TTL보다 작으면 CloudFront는 minimum TTL값에 대해 개체를 캐시합니다.
  • 만약 max-age 값이 사용자가 정의한 maximum TTL보다 큰 경우 CloudFront는 maximum TTL 값에 대해 개체를 캐시합니다.

대충 이해가 간다.

Cache-Contorl, Expires 해더를 이용해서 세팅을 해주면 TTL을 내가 원하는대로 맞춰서 사용할 수 있다는 것은 알겠다.

 

그럼 다시 Cloudfront로 돌아와서 Cache policies 중에 "Managed-CachingOptimized"의 TTL 세팅을 다시 본다.

  • Minimum TTL: 1 sec
  • Maximum TTL: 31536000 sec (1 year)
  • Default TTL: 86400 sec (1 day)

어떤 설정인지는 알겠는데 좀 더 확실하게 알아야겠다고 생각하고 메뉴얼을 다시 읽어 보았다.

AWS는 뉴얼을 잘 봐야하는 걸 알면서도 제대로 안보고 지나쳤던것이 문제였다.

이제서야 메뉴얼이 눈에 들어오기 시작한다.

메뉴얼을 자세히 보면 이해가 되겠지만 다시한번 적어보면서 이해를 해본다.

  • minimum TTL은 Min TTL, maximum TTL은 Max TTL로 표현한다.

표에 나오는 HTTP header

  • Cache-Control: max-age=<seconds>  (e.g. Cache-Control: max-age=1200)
  • Cache-Control: s-maxage=<seconds> (e.g. Cache-Control: s-maxage=1200)
  • Expires: <http-date> (e.g. Expires: Wed, 10 Mar 2021 07:28:00 GMT)
Origin 해더 Min TTL = 0 sec Min TTL > 0 sec (1초 보다 큼)
Origin object에
Cache-Control: max-age
헤더 추가
[CloudFront 캐싱]
Cloudfront는
Cache-Control max-age와 Max TTL의 값 중
더 작은 값 동안 캐싱


[Browser 캐싱]
Browser는
Cache-Control max-age 동안 캐싱

[CloudFront 캐싱]
Cloudfront는
Min TTL < max-age < Max TTL

- Cache-Control max-age 동안 캐싱
max-age < Min TTL
- Min TTL 동안 캐싱
max-age > Max TTL
- Max TTL 동안 캐싱

[Browser 캐싱]
Browser는
Cache-Control max-age 동안 캐싱

Origin object에
Cache-Control: max-age
헤더 추가 안함
[CloudFront 캐싱]
Cloudfront는 
default TTL
 동안 캐싱

[Browser 캐싱]
Browser 마다 다름
[CloudFront 캐싱]
Cloudfront는
Min TTL과 default TTL 둘중 더 값이 큰 값 동안 캐싱

[Browser 캐싱]
Browser 마다 다름
Origin object에
Cache-Control: max-age와
Cache-Control: s-maxage
헤더 추가 
[CloudFront 캐싱]
Cloudfront는 
Cache-Control s-maxage와 Max
 TTL의 값 중
더 작은 값 동안 캐싱


[Browser 캐싱]
Cache-Control max-age 동안 캐싱
[CloudFront 캐싱]
Cloudfront는
Min TTL < s-maxage < Max TTL

- Cache-Control s-maxage 동안 캐싱
s-maxage < Min TTL
- Min TTL 동안 캐싱
s-maxage > Max TTL
- Max TTL 동안 캐싱

[Browser 캐싱]
Browser는
Cache-Control max-age 동안 캐싱
Origin object에
Expires 헤더 추가
[CloudFront 캐싱]
Cloudfront는 
Expires의 날짜와 Max
 TTL의 값 중
더 빨리 만료되는 동안 객체를 캐싱


[Browser 캐싱]
Browser는
Expires의 날짜와 시간 까지 캐싱
[CloudFront 캐싱]
Cloudfront는
Min TTL < Expires < Max TTL

- Expires의 날짜와 시간 까지 캐싱
Expires < Min TTL
- Min TTL 동안 캐싱
Expires > Max TTL
- Max TTL 동안 캐싱

[Browser 캐싱]
Browser는
Expires의 날짜와 시간 까지 캐싱
Origin object에
Cache-Control: no-cache, no-store
혹은 private 
헤더 추가
CloudFront, Broser는 헤더의 설정에 따른다.

CloudFront에서 Cache-Control: no-cache 처리방식은 링크 참고
[CloudFront 캐싱]
Cloudfront는 Min TTL 동안 캐싱

[Browser 캐싱]
Browser 헤더의 설정을 따름

 

 

다음으로는 나의 static file들이 S3에 있으니 S3에 object 세팅을 해줘야 할 것 같다.

S3 object에 Metadata을 이용하면 되겠다.

일단 그럼 테스트를 한번 해본다.

테스트라서 빌드 디렉터리를 통째로 옵션을 줬지만,, 실제로는 파일 확장자 단위로 하거나 좀 더 잘게 나눠서 할 예정이다.

(react app 만들고 빌드할 설정과 aws s3 sync할 설정에 대한 설명은 제외,, 다른 방법으로도 테스트는 가능하므로)

> create-react-app sample-deploy
...
...

> cd sample-deploy
> yarn build
...
...

> aws s3 sync ./build s3://test-upload-bucket --metadata='{"Cache-Control":"max-age=300"}'
...
...

원하는대로 metadata가 세팅이 되었는지 확인해본다.

 

Value는 내가 원하는대로 올라갔는데 Key 값이 다르다.

"x-amz-meta-"로 시작하는 헤더는 User defined(사용자 정의) 메타데이터 HTTP 헤더로 별도로 구분하기 위한 키워드인데 이것은 원래 하려던것이 아니다.

 

다른 옵션을 찾아서 옵션을 바꿔보았다. '--cache-control' 로 해보니 원하는대로 Key, Value가 세팅되었다.

> aws s3 sync ./build s3://test-upload-bucket -cache-control max-age=1200
...
...

 

S3 Object 접근 제한

외부에서 S3 Object에 직접적인 접근은 제한을 걸고 싶다.

그래서 크게 2개의 접근만 허용해주고 나머지는 제한하려고 했다.

  • 업무하는 사무실의 접근
  • CloudFront의 접근

사무실에서의 접근은 가장 편하고 직관적인 IP 허용으로 처리했다.

CloudFront에서의 접근은 Origin Access Identity를 이용해서 허용해주었다. (ColudFront의 Origin 설정에서 확인)

 

S3의 Bucket에서 Permission 탭에 있는 Bucket policy 세팅 결과이다.

주요 정보들은 x, y 같은 것으로 바꿔두었다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::test-upload-bucket/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "x.x.x.x/32",
                        "y.y.y.y/32"
                    ]
                }
            }
        },
        {
            "Sid": "CloudfrontOAI",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E2ZZZ2IRJXXXXX"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::test-upload-bucket/*"
        }
    ]
}

테스트를 해본다.

wifi를 끈 휴대폰을 이용해서 S3 url에 직접 접근해았더니 Access Denied 발생한다.

Cloudfront에 접근해서 보니 잘 보인다.

참고로 Route53에 dns 세팅을 미리 해두었고 그것을 Alternative Domain Names(CNAMEs)에 추가해서 이용하였다. 

이 부분은 마우스 클릭 몇번이면 되므로 패스.

 

나머지는 운용해나가면서 적절히 맞춰가야겠는데

Behaviors에서 Path Pattern 적용해서 Cache policy 적용해주는것 (개발팀과 협의 (modify없이 add만)? 배포할때마다 버저닝?)

Error Pages에서 에러 발생에 대한 응답 페이지/코드 세팅해주는 정도를 추가로 맞추게 되지 않을까 싶다.

거기에 Origin이 늘어나면 거기에 따른 추가 설정들도..

'Infra' 카테고리의 다른 글

인프라 개념 LACP  (0) 2022.06.10
Python으로 Opendistro Account 생성하기  (0) 2021.03.17
Hashicorp Terraform (테라폼)  (0) 2020.10.28
AWS Subnet 생성하기  (0) 2020.07.23
AWS VPC 생성하기  (0) 2020.07.23

회사에서 라이센스를 유지하려면 감사를 받아야하고 그 항목중에는 퇴사자 처리 규칙이 존재한다.

계정관리가 완전히 되면 상관이 없겠지만 혼자서 한국/외국 사무실과 인프라 관련 계정을 관리하다보니

직접 관리하기가 생각보다 어렵다. 각국의 인사팀에서 slack은 확실하게 관리해주고 있어서 slack 기준으로 퇴사자를 비교한다.

대부분 API를 제공해줘서 (github, aws, private cloud, apm monitoring tool) 문서를 보고 알아서 만들어다가 썼는데

 

Jenkins는 예외였다..

관리하려면 jenkins 사용자 목록을 가져와야 하고 그것과 관련한 API가 있긴한데

Script Console에서 Groovy script 문법을 이용해서 가져오는 방법이 있다.

다른 방법은 못 찾았다. 분명 더 편한 방법이 있을텐데...

 

개발, 스테이지 등.. 환경별로 다르게 jenkins를 사용하고 있고 여기에 각각의 계정이 존재한다.

(그룹계정을 사용하면 편한데 auditor 지적사항에 걸리면 안되서 사용 불가하다.)

 

외국 현지 부서의 인력이 드나드는 것은 공유가 안되서 알기가 어렵다. 거기에도 담당인력을 뽑아주면 되긴하지만.. 일단 혼자 하게 되었다.

 

 

Groovy script를 이용하는 방법이 있다.

stackoverflow.com/a/56558259

 

'Manage Jenkins' 를 클릭하고 좀 내려보면 'Script Console'이 있는데 그것을 이용한다.

 

 

curl을 이용해서 jenkins에 로그인 해서 Groovy script를 돌릴 수 있다.

참고로 명령어에 id와 key 정보가 있으니 내부망에서 안전하게 수행하는것을 권고한다. 

 

1. API token이 필요하다. 이 것은 내 계정의 Configure에서 만들 수 있고 적절하게 이름을 붙여준다.

curl 명령어

curl --user '계정:API_TOKEN' -XPOST https://jenkins.enqdeq.net/scriptText -d 'script=Hudson.instance.getSecurityRealm().getAllUsers().each%20{%20println%20it%20}'

사용한 옵션

  • --user의 ID:API_TOKEN 정보로 auth 획득
  • -XPOST를 이용해서 groovy script를 run 한다. (참고로 위의 url은 존재하지 않고 본인의 jenkins url을 사용한다)
  • -d 로 전달하려는 데이터 추가 (groovy script를 한줄로 적는데 space를 사용하면 "Illegal character SPACE=' '" 에러가 발생하기 때문에 url 인코딩 시킨 %20으로 대신한다.

명령어 결과는 실제로 jenkins에서 수행한것과 동일하게 보인다.

 

그 결과와 퇴사자 계정 비교해서 처리하도록 해두면 완료

간단하게 적어두긴했으나 웹에서 수동으로 Groovy script 돌리던것을 curl 명령어로 할 생각을 하기까지 1년정도 걸렸다.

 

 

 

'기타' 카테고리의 다른 글

Python3를 이용한 Tistory Authentication Code 방식 인증 (2020.09.01 변경사항 반영)  (1) 2020.09.25
테넌트  (0) 2020.07.23
TCP Status 상태에 대해서  (1) 2020.07.15
TCP 통계수치 변화 (장애 상황)  (0) 2020.07.15
TCP 통계  (0) 2020.07.14

Terraform

[ 사전적 의믜 ]

Terraforming(테라포밍)은 지구가 아닌 다른 행성 및 위성, 기타 천체의 환경을 지구의 대기 및 온도 생태계와 비슷하게 바꾸어 인간이 살 수 있도록 만드는 작업으로 화성탐사 관련해서 종종 등장하는 용어입니다.

하시코프에서 이 제품의 이름을 왜 이렇게 지었을지 생각해보았습니다.

단어 뜻에서 각기 다른 모든 vendor의 인프라 환경을 비슷하게 바꾸어 서비스를 할 수 있도록 만드는 작업을 하는 도구라고 느껴집니다.

 

[ 특징 ]

테라폼은 Chef, Puppet, 과 같이 Infrastructure provisioning 툴로 분류할 수 있습니다.

source: http://blog.shippable.com/the-future-of-devops-is-assembly-lines

The key features of Terraform are:

  • Infrastructure as Code: Infrastructure is described using a high-level configuration syntax. This allows a blueprint of your datacenter to be versioned and treated as you would any other code. Additionally, infrastructure can be shared and re-used.  👉 버전으로 인프라 이력관리, 인프라 공유 및 재사용

  • Execution Plans: Terraform has a "planning" step where it generates an execution plan. The execution plan shows what Terraform will do when you call apply. This lets you avoid any surprises when Terraform manipulates infrastructure. 
    👉 plan 단계를 거치면서 인프라에 어떤 변화가 생길지 볼 수 있음

  • Resource Graph: Terraform builds a graph of all your resources, and parallelizes the creation and modification of any non-dependent resources. Because of this, Terraform builds infrastructure as efficiently as possible, and operators get insight into dependencies in their infrastructure. 👉 모든 리소스를 그래프로 작성하고 종속되지 않은 리소스의 추가와 변경을 병렬로 처리해서 효율적으로 인프라를 구축하고 각 리소스의 의존성을 확인 할 수 있음

source: https://www.terraform.io/docs/commands/graph.html

  • Change Automation: Complex changesets can be applied to your infrastructure with minimal human interaction. With the previously mentioned execution plan and resource graph, you know exactly what Terraform will change and in what order, avoiding many possible human errors. 👉 terraform plan, 그 결과로 나오는 resource graph로 어떤 순서로 변경이 발생할지 미리 확인할 수 있어서 휴먼에러를 많이 줄일 수 있음

 

[ 버전 관리 ]

다른 패키지와 마찬가지로 일정 시간이 지나면 new feature, bug fix등의 작업으로 upgrade 될것이고 그에 따라 맞춰주는 작업이 필요해집니다. 시간이 지나면 버전관리할 필요가 생기게 되는데 이때 크게 2가지 방법을 사용할 수 있습니다.

[ Use cases ]

이중에서 제가 Terraform을 선택하려는 이유는

  • Heroku App setup  : -
  • Multi-Tier Applications : API 서버, 캐싱 서버등 추가되는 레이어를 개별적으로 확장
  • Self-Service Clusters : 서비스 구축과 확장한 방법들을 코드화
  • Software Demos : -
  • Disposable Environments : 서비스에 따라서 Dev, Stage 환경을 빠르게 구성해주었다가 폐기
  • Software Defined Networking : AWS VPC 부터 네트워크 구성을 코드화
  • Resource Schedulers : -
  • Multi-Cloud Deployment : 관리를 단순화 시켜 대규모 멀티 클라우드 인프라 용이

 

 

'Infra' 카테고리의 다른 글

Python으로 Opendistro Account 생성하기  (0) 2021.03.17
Cloudfront와 S3를 이용한 웹 서비스 구성  (0) 2021.03.10
AWS Subnet 생성하기  (0) 2020.07.23
AWS VPC 생성하기  (0) 2020.07.23
AWS EKS Service Role 생성 (IAM)  (0) 2020.07.23

안녕하세요 쿤드입니다. 🍀

 

가끔씩 보고 싶은 정보들을 모아 블로그에서 보고 싶을때 정보를 쫙 모아다가

Tistory의 OpenAPI를 이용해서 올리곤 했었습니다. (가끔이라서 수동으로 함)

 

오늘 오랜만에 좀 보려고 했는데

자꾸 400 Error가 발생하고 이유는 모르겠고.. 뒤져보니 response_type=code 방식을 이용해서 

하라고 하는 것을 보니 뭔가 변경된것 같았습니다.

 

Open API 사용하기 위한 App 등록이 선행되어야 하는데
이는 검색하면 많이 나와서 넘어갑니다.

 

자세히 Autoriztion Code 방식을 읽어보니 기존에 제가 인증받아서 사용하던 code에서 끝나는 것이 아니라

그 code를 가지고 token을 받아서 그 token을 이용해야 하는 것이 었습니다.

 

그래서 방법은 총 3단계 (기존엔 2단계)

  1. Tistory Login
  2. Authentication code 발급
  3. Access Token 발급

 

확인하는 방법은 2가지가 있습니다.

웹브라우저에서 해보거나, 뭐가 되었든 코드로 짜서 자동으로 하거나

우선은 수동으로 먼저 확인해봅니다. python3 코드는 이 페이지 하단에서 볼 수 있습니다.

 

웹브라우저에서 직접 해본다.

1번 과정 - https://www.tistory.com 로그인한다.

2번 과정 - Autentication Code 발급 받는다.

웹 브라우저에 입력할 내용

https://www.tistory.com/oauth/authorize?client_id=app의 ClientID&redirect_uri=리다이렉션 받을 URI&response_type=code  

 

예시(아래 것은 샘플이라 에러페이지 나옴, client_id에 임의의 값을 넣어서 되지 않음, redirection은 내 맘대로함)

https://www.tistory.com/oauth/authorize?client_id=d962867605b93asdfasdfasd6fb96f8cc27568b141c&redirect_uri=sfixer.tistory.com&response_type=code

 

 

 

3. Access Token을 발급 받는다.

웹 브라우저에 입력할 내용

www.tistory.com/oauth/access_token?client_id=나의 CLIENT_IDc&client_secret=나의 CLIENT_SECRET&redirect_uri=설정한 redirect uri &code=2번과정에서 받아온 code 정보&grant_type=authorization_code

 

예시 (샘플이라 아무정보나 들어있음, 본인것에 맞게 바꿔야함)

www.tistory.com/oauth/access_token?client_id=ddszxcvzxcv36fb96f8cc27568b141c&client_secret=d962867zxcvzxcvelkasjdflkjasldk51ca867454aac07d9b98bde63553ba3d8a93&redirect_uri=sfixer.tistory.com&code=a1175b123409821093saldjfklasdjflkasdjflke91c5a3dc8b4514268a4e3a066&grant_type=authorization_code

 

 

코드로 구현한다

 

위에 처럼 해도되지만 간단하게 파이썬 코드 몇줄로 3단계 동작을 수행하겠습니다. 

개인정보들은 환경변수에 등록해서 가져와서 썼기때문에 그 부분만 따로 (하드코딩이든 뭐든) 처리해주시면 동작합니다.

테스트까지는 해보았습니다.

물론 아래의 제가 한 코드보다 훨씬 더 간결하게 구현이 가능할 것입니다. 

이를 공유한 이유는 저 처럼 삽질하는 사람을 좀 돕고자 공유했습니다.

 

Python3.8.5을 사용했으며

사용했던 패키지 버전 정보입니다.

  • requests==2.24.0
  • selenium==3.141.0
#!/usr/bin/env python3
import os
import re
from requests import get
from selenium import webdriver
from selenium.webdriver.chrome.options import Options


# tistory에 로그인을 합니다.
def tistory_login():
    tistory_id = os.environ.get('TISTORY_ID')
    tistory_pw = os.environ.get('TISTORY_PW')
    chromedriver_path = os.environ.get('CHROMEDRIVER_PATH')
    chrome_options = Options()
    chrome_options.add_argument("--headless")  # chrome 띄워서 보려면 주석처리
    driver = webdriver.Chrome(chromedriver_path, options=chrome_options)
    driver.implicitly_wait(3)
    driver.get('https://www.tistory.com/auth/login?redirectUrl=http%3A%2F%2Fwww.tistory.com%2F')
    driver.find_element_by_name('loginId').send_keys(tistory_id)
    driver.find_element_by_name('password').send_keys(tistory_pw)
    driver.find_element_by_xpath('//*[@id="authForm"]/fieldset/button').click()
    return driver


# authentication code 정보를 가져옵니다.
def get_authentication_code(driver, client_id, redirect_url):
    req_url = 'https://www.tistory.com/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code&state=langoo' % (client_id, redirect_url)
    driver.get(req_url)
    driver.find_element_by_xpath('//*[@id="contents"]/div[4]/button[1]').click()
    redirect_url = driver.current_url
    temp = re.split('code=', redirect_url)
    code = re.split('&state=', temp[1])[0]
    return code


# http://www.tistory.com/guide/api/index
# access token 정보를 가져옵니다.
def get_access_token(code, client_id, client_secret, redirect_url):
    url = 'https://www.tistory.com/oauth/access_token?'
    payload = {'client_id': client_id,
               'client_secret': client_secret,
               'redirect_uri': redirect_url,
               'code': code,
               'grant_type': 'authorization_code'}
    res = get(url, params=payload)
    token = res.text.split('=')[1]
    return token


def main():
    client_id = os.environ.get('TISTORY_CLIENT_ID')
    client_secret = os.environ.get('TISTORY_CLIENT_SECRET')
    redirect_url = os.environ.get('TISTORY_REDIRECT')
    driver = tistory_login()
    code = get_authentication_code(driver, client_id, redirect_url)
    token = get_access_token(code, client_id, client_secret, redirect_url)
    print(token)


if __name__ == '__main__':
    main()

'기타' 카테고리의 다른 글

Jenkins 사용자 목록 curl로 가져오기  (0) 2020.12.29
테넌트  (0) 2020.07.23
TCP Status 상태에 대해서  (1) 2020.07.15
TCP 통계수치 변화 (장애 상황)  (0) 2020.07.15
TCP 통계  (0) 2020.07.14
  1. 오냐TV 2021.12.20 23:04

    관리자의 승인을 기다리고 있는 댓글입니다

안녕하세요 쿤드입니다. 🍀

이번엔 Subnet을 생성해보려고 합니다.

 

제가 사용하는 리전에는 Availability Zone이 3개가 있긴한데 

2개의 Zone만 사용해서 private용 2개, public용 2개 총 4개를 생성했습니다.

 

IPv4 CIDR block으로 IP 범위를 지정해주었는데

각 서브넷당 최대 4094개의 host를 할당 해 줄 수 있도록 netmask는 20으로 설정했습니다.

  • 10.3.0.0/20 (10.3.0.1 ~ 10.3.15.254)
  • 10.3.16.0/20 (10.3.16.1 ~ 10.3.31.254)
  • 10.3.32.0/20 (10.3.32.1 ~ 10.3.47.254)
  • 10.3.48.0/20 (10.3.48.1 ~ 10.3.63.254)

Subnet을 생성하고 나면 각 서브넷마다 할당할 수 있는 IP 주소 여분의 개수도 확인할 수 있습니다.

 

서브넷을 세팅하고 나면 Route Table을 설정합니다. 

 

클릭하고 들어간 후에 할당해줄 subnet을 선택하고 서로 Associate시켜 줍니다.

 

 

그리고 나머지 Route Table은 필요한 경우에 Edit routes를 이용해서 정보를 추가해줍니다.

 

 

IPSec VPN을 이용해서 네트웍 설정을 한 경우에는 위의 메뉴에서 직접 설정이 불가능했었습니다.

저 같은 경우에는 자체 설정한 Site-to-Site VPN 설정, 직접 구축한 OpenVPN에 대한 라우팅 설정을 할때 직접 추가 했었습니다.

 

 

나머지 작업은

Public으로 사용하기 위한 subnet에서 사용할 Internet Gateway

Private으로 사용하기 위한 subnet에서 사용할 NAT Gateway

  • 참고로 NAT Gateway 생성할 때는 여기에서 사용할 때는 Associate Elastic IP address를 클릭하면 알아서 EIP를 생성해서 할당해줍니다.
  • NAT Gateway를 삭제 혹은 이미 Associate Elastic IP address를 클릭하고 NAT Gateway 생성을 취소한 경우 EIP가 이미 생성되어 있으므로 release 시켜서 불필요한 비용 발생하지 않도록 합니다.

설정만 해주면 기본적인 VPC 구성은 되었다고 볼 수 있습니다.

'Infra' 카테고리의 다른 글

Cloudfront와 S3를 이용한 웹 서비스 구성  (0) 2021.03.10
Hashicorp Terraform (테라폼)  (0) 2020.10.28
AWS VPC 생성하기  (0) 2020.07.23
AWS EKS Service Role 생성 (IAM)  (0) 2020.07.23
AWS EKS Cluster 세팅  (0) 2020.07.23

안녕하세요 쿤드입니다. 🍀

테넌트의 개념에 대해서 알아보려고 합니다.

 

테넌트(tenant): 클라우드 인프라/서비스 제공 방식

  • 싱글 테넌트: 1개 서버에 대해 1개 기업의 데이터, 어플리케이션만 제공
  • 멀티 테넌트: 다른 사용자들과 서버, 스토리지를 공유

싱글과 멀티의 차이는 하드웨어를 공유하는가 안 하는가의 차이입니다.

 

이해를 돕기위해 이미지를 같이 보면

독립적으로 구성이 될 수록 Single-Tenant에 가깝고

Multi-Tenant에 가까워 질수록 사용자가 모든 자원을 공유해서 사용합니다.

출처: http://publicstaticmain.blogspot.com/2016/05/multitenancy-with-spring-boot.html

 

멀티테넌시의 장점으로는 비용 절감 입니다. (출처: http://www.itworld.co.kr/news/101255)

그리고 관리상의 이점도 있습니다.

하드웨어 리소스를 효율적으로 사용할 수 있고, 

하드웨어에 설치된 software에 보안 patch를 하거나 upgrade 하는 경우에도 1곳만 신경써주면 되기때문입니다.


반대로 단점으로는

내부적으로는 각 사용자별로 isolate 시켜야하고, 한곳의 보안이 뚫리게 되면 모든 사용자들의 정보가 다 유출되기때문에 

이를 관리하기 위한 인력의 비용도 무시할 수는 없습니다.

+ Recent posts