머신러닝 관련 자료를 보다보면 Regression 이란 단어가 자주나온다.

Linear Regression, Logistic regression, ...

보다보니 통계학에서도 마찬가지다.

회귀라는 말은 알겠는데 뭔말인지 이해가 되지 않는다.

 

찾다보면 결국 프랜시스 골턴 우생학자의 논문이 기원이었다.

(Francis Galton in his 1886 paper "Regression towards mediocrity in hereditary stature")

평균으로의 회귀(regression to the mean)

조금이해가 되긴하는데 좀 더 따라가볼까.

 

단순히 한글 뜻은 '회귀'.

  • 한 바퀴 돌아서 본디의 자리나 상태로 돌아오는 것. 순화어는 `돌아옴'.

回歸

  • 돌아오다 회
  • 돌아오다 귀

원래대로 되돌아 온다는 말인데 Regression의 의미를 다 표현하지 못하는 느낌이다.

결국 영어 단어 뜻이 가장 와 닿는다. [1]

Regression: "Relapse to a less perfect or developed state."
"덜 완벽하거나 발전된 상태로 되돌아가는 것."

 

머신러닝에서 회귀란 

하나 이상의 입력 변수를 기반으로 연속적인 수치 출력 변수를 예측하는 작업을 말한다.

 

  • 단순 선형 회귀 [2]
    • 선형 함수로 정의
      • Y= β0*X + β1 + ε
      • β0과 β1은 회귀 기울기, ε(엡실론)은 오차
    • 두 변수 간의 관계 모델링
      • 강우량과 작물 수확량
      • 어린이의 나이와 키
      • 온도계에서 금속 수은의 온도와 팽창

https://godongyoung.github.io/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D/2018/01/20/ISL-linear-regression_ch3.html

 

  • 다중 선형 회귀
    • 데이터 세트에는 하나의 종속 변수와 여러 독립 변수가 포함
      • Y= β0*X0 + β1X1 + β2X2+…… βnXn+ ε
      • 예측 변수의 수가 증가하면 β도 그에 따라 증가
    • 여러 변수와 그 변수가 결과에 미치는 영향을 모델링
      • 강우량, 온도 및 비료 사용에 작물 수확량에 미치는 영향
      • 식이요법과 운동이 심장병에 미치는 영향
      • 임금 인상과 인플레이션이 주택 대출 금리에 미치는 영향

https://www.mathworks.com/help/stats/regress_ko_KR.html

 

  • 로지스틱 회귀
    • 데이터 사이언티스트는 로지스틱 회귀 분석을 사용하여 이벤트 발생 확률을 측정
    • 예측은 0과 1 사이의 값
      • 0: 발생할 가능성이 낮은 이벤트
      • 1: 발생할 가능성이 가장 높은 이벤트
      • 로지스틱 방정식은 로그 함수를 사용하여 회귀선을 계산합니다.
    • 예시
      • 스포츠 경기에서 승리 또는 패배 확률
      • 테스트 통과 또는 실패 확률 
      • 이미지가 과일 또는 동물일 확률

http://primo.ai/index.php?title=Logistic_Regression_%28LR%29

 


머신러닝에서 "회귀"라는 용어가 사용되는 이유는 입력 변수를 선형 또는 비선형 함수를 통해

"회귀" 또는 출력 변수로 매핑하는 모델을 찾는 것이 목표인데

 

이 프로세스에는 출력 변수의 예측 값과 실제 값 사이의 오차를 최소화하는 

위의 이미지들 같이 최적의 선 또는 곡선을 찾아서 일반화 할 수 있기 때문이다.

 

 

 

참고:

[1] https://stats.stackexchange.com/questions/11087/why-are-regression-problems-called-regression-problems

[2] https://aws.amazon.com/ko/what-is/linear-regression/#:~:text=%EC%A4%84%20%EC%88%98%20%EC%9E%88%EB%82%98%EC%9A%94%3F-,%EC%84%A0%ED%98%95%20%ED%9A%8C%EA%B7%80%EB%9E%80%20%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C%3F,%EC%9C%BC%EB%A1%9C%20%EC%88%98%ED%95%99%EC%A0%81%EC%9C%BC%EB%A1%9C%20%EB%AA%A8%EB%8D%B8%EB%A7%81%ED%95%A9%EB%8B%88%EB%8B%A4.

 

반응형

https://www.youtube.com/watch?v=gy22liV3oNk 

 

ヨホホホ ヨホホホ

요호호호 요호호호

ビンクスの 酒を 届けにゆくよ

빙크스노 사케오 토도케니유쿠요

[ 빙크스의 술을 전하러 간다네 ]


海風 気まかせ 波まかせ

우미카제 키마카세 나미마카세

[ 해풍에 실려 파도에 실려 ]

潮の向こうで 夕日も騒ぐ

시오노무코우데  유우히모사와구

[ 수평선 멀리 석양도 넘실대네 ]


空にゃ 輪をかく鳥の唄

소라냐 와우카쿠 토리노 우타

[ 하늘엔 빙글빙글 노니는 새의 노래 ]

さよなら港つむぎの里よ

사요나라 미나토 츠무기노 사토요

[ 안녕 항구여, 정들었던 고향이여 ]

 

ドンと一丁唄お船出の唄

돈토잇쵸 우타오 후나데노우타

[ 우렁차게 한바탕 부르자, 출항의 노래 ]

 

金波銀波も しぶきにかえて

킨파긴은파모 시부키니카에테

[ 금물결 은물결도 물보라로 바꾸며 ]

 

おれ達ゃゆくぞ 海の限り

우레타챠 유쿠조 우미노 카기리

[ 우리는 간다네, 저 바다 끝으로 ]

ビンクスの酒を届けにゆくよ

빙크스노 사케오 토도케니유쿠요

[ 빙크스의 술을 전하러 간다네 ]


我ら海賊 海割ってく

와레라 가이조쿠 우미왓테쿠

[ 우리는 해적, 바다를 가르며 간다 ]


波を枕に 寝ぐらは船よ

나미오 마쿠라니 네구라와 후네요

[ 파도를 베개 삼아, 잠자리는 배라오 ]


帆に旗に 蹴立てるはドクロ

호니하타니 케타테루와 도쿠로
[ 돛에 깃발에 휘날리는 그것은 해골 ]

嵐がきたぞ 千里の空に

아라시가 키타조 센리노 소라니

[ 폭풍우가 왔구나, 천리의 하늘에 ]

 

波がおどるよ ドラムならせ

나미가 오도루요 도라무 나라세

[ 파도가 춤춘다 드럼을 울려라 ]

おくびょう風に 吹かれりゃ最後

오쿠뵤우카제니 후카레랴 사이고

[ 두려움의 바람에 휘둘리면 그때는 끝 ]


明日の朝日が ないじゃなし

아스노 아사히가 나이쟈나시
[ 내일 아침해가 없는 것도 아닌걸 ]

ヨホホホ ヨホホホ
요호호호 요호호호

ビンクスの酒を届けにゆくよ

빙크스노 사케오 토도케니유쿠요

[ 빙크스의 술을 전하러 간다네 ]

 

今日か明日かと宵の夢

쿄우가 아스카토 요이노 유메

[ 오늘일까 내일일까, 애타는 초저녁 꿈 ]


手をふる影に もう会えないよ

데오후루 카게니 모우아에나이요

[ 손 흔드는 그림자, 다시는 만나지 못하리 ]


何をくよくよ 明日も月夜

나니오쿠요쿠요 아스모 츠쿠요

[ 끙끙대서 무엇하랴, 내일도 달밤이라오 ]


ビンクスの酒を届けにゆくよ

빙크스노 사케오 토도케니유쿠요
[ 빙크스의 술을 전하러 간다네 ]

 

ドンと一丁唄お 海の唄

돈토잇쵸 우타오 우나바노우타

[ 우렁차게 한바탕 부르자, 바다의 노래 ]


どうせ誰でも いつかはホネよ

도우세다렛데모 이츠카와 호네요

[ 어차피 누구나 언젠가는 백골이라오 ]

 

果てなし あてなし 笑い話

하테나시 아테나시 와리아바나시

[ 끝없이 한없이 즐거운 이야기 ]

ヨホホホ ヨホホホ

요호호호 요호호호


반응형

결론부터말하면

Containerd 1.4.4-1의 rc 1.0.0-rc93의 문제로

이 버전만 피해가면 이슈 없음

 

상황

kubespray 2.16.0을 이용해서 k8s upgrade를 1.18 -> 1.19 수행하여 클러스터 업그레이드 완료.

이후 Pod의 상태를 확인하면서 CrashLoopBackOff, Error인 것들을 재시작 수행.

이 과정에 갑자기 Node가 NotReady가 되면서 그 node의 pod 전체가 다 Terminating 됨.

- 이때부터 pod 상태가 엉망이 됨.

노드의 상태가 NotReady였다가 일부 pod가 종료 처리되고 잠시 Ready 되었었다.

처음에는 pod에 짧은 시간에 많은 노드가 기동해서 그런가 싶었다.

 

문제파악

우선은 k8s의 노드의 kubelet 상태를 확인하였다.

kubelet은 k8s의 모든 노드에서 실행되는 Agent로써 이 서비스가 내려가면 노드가 동작하지 않는다.

간단하게 현재상태의 일부는 systemctl로도 조회 가능하다.

$ systemctl status kubelet

...
kubelet[3694]: I0220 14:23:09.120100    3694 kubelet.go:1775] skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 4h19m47.369998188s ago; threshold is 3m0s]

일단은 뭔가 pod가 갑자기 많이 기동하니까 동기화가 되지 않는 이슈로 생각했다.

좀더 자세히 보기 위해서 journalctl에 follow 옵션 줘서 실시간 발생하는 로그를 확인했다.

$ sudo journalctl -f

문제있는 worker 노드에서 kubelet, containerd, docker를 재시작해서 노드를 Ready로 바꿔주고 다시 확인했다.

처음에는 pod들이 잘 뜨는것 처럼 보이다가

`PLEG is not healthy` 메시지가 보이는 순간

노드는 NotReady로 상태가 변경되면서 모든 pod가 다 Terminating 되었다.

 

PLEG (Pod Lifecycle Event Generator)

  • Kubelet과 통신하여 노드에 떠있는 컨테이너 상태를 Pod 상태와 주기적으로 동기화.

  • Kubelet은 Control Plane에서 만든 spec에 맞춰 상태를 유지시키기 위해 각 pod의 상태 정보를 가지고 있어야 한다.
    런 상태 정보를 유지하기 위해 kubelet이 pod을 polling 하는 작업을 한다. 주기적으로 polling하는 작업은 pod의 수 증가함에 따라 무시할 없는 오버헤드가 발생한다.
    Pod를 주기적으로 polling 하는 작업의 오버헤드를 줄이기 위해 PLEG (Pod Lifecycle Event Generator)를 사용한다
    PLEG를 활용하는 것은 kubelet이 polling해서 정보를 가져오는 작업과 유사하지만 PLEG를 수행하는 싱글 쓰레드를 통해 컨테이너의 상태를 확인하여 kubelet이 polling하는 작업보다 오버헤드를 줄이는 효과를 볼 수 있다
  • PLEG는 컨테이너 이벤트를 검색하기 위해 relist를 한다.
    relist 하는 주기가 길다는 것은 kubelet이 컨테이너 변경 사항을 감지하고 파드 상태를 업데이트하는 데 시간이 더 오래 걸린다는 것을 의미한다. 반면에 주기가 짧으면 재등록(예: 컨테이너 런타임 작업)이 더 자주 발생하여 CPU 사용량이 증가하게 된다. 주기를 1초로 설정하더라도 컨테이너 런타임이 느리게 응답하거나 한 주기에 많은 컨테이너 변경 사항이 있는 경우 relisting 자체가 완료되는 데 1초 이상 걸릴 수 있다는 점에 유의한다.

 

대응

생각해보니 k8s 클러스터에 존재하는 pod 수가 적은 환경에서는 이슈가 없었는데

relist 하는 taint를

kubectl taint nodes worker-1 plegnothealthy=true:NoSchedule

 

 

Reference

반응형

 

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

 

Best practices for writing Dockerfiles

 

docs.docker.com

 

수시로 변경될 수 있는 컨테이너를 생성 (Create ephemeral containers)

컨테이너는 멈추고 파기될 수 있고 언제든 바뀔수 있기에 최소한의 셋업과 설정으로 구성해야 하므로

도커 이미지를 정의하는 Dockerfile은 이를 염두해두고 생성해야한다.

 

build context에 대한 이해 (Understand build context)

`docker build` 명령을 실행하는 현재 디렉터리를 build context라고 하고 이 디렉터리안에 Dockerfile이 있습니다.

이 명령을 실행할 때 옵션으로 -f --file string(Default is 'PATH/Dockerfile')을 사용하면 다른 디렉터리에 있는 Dockerfile을 지정할 수 있다.

Dockerfile이 저장된 디렉터리의 위치는 상관이 없고,

그 디렉터리 안에 있는 모든 파일과 디렉터리들이 build context로서 도커 데몬에 보내지게 된다.

# Create a directory for the build context and cd into it. 
# Write “hello” into a text file named hello and 
# create a Dockerfile that runs cat on it. 
# Build the image from within the build context (.):

 mkdir myproject && cd myproject
 echo "hello" > hello
 echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile
 docker build -t helloapp:v1 .

# Move Dockerfile and hello into separate directories 
# and build a second version of the image 
# (without relying on cache from the last build). 
# Use -f to point to the Dockerfile and specify the directory of the build context:


 mkdir -p dockerfiles context
 mv Dockerfile dockerfiles && mv hello context
 docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context

이미지를 만드는데 필요없는 파일을 포함시키면 build context가 커지게되고

도커 데몬에 전달할 데이터가 커지게 된다.

이로 인해 이미지의 size가 커지게 되면서 build에 소요되는 시간, push와 pull에 소요되는 시간, run time등

모든작업의 실행에 소요되는 시간에 영향을 준다.

그렇기 때문에 Dockerfile을 build할 때 결과 메시지에서 이 크기를 가급적 줄이도록한다.

Sending build context to Docker daemon  187.8MB

 

 

.dockerignore로 제외한다 (Exclude with .dockerignore)

.dockerignoer는 .gitignore와 유사하게 빌드에서 제외할 파일 패턴을 제외할 수 있도록 지원한다. 

멀티 스테이지 빌드를 사용 (Use multi-stage builds)

멀티 스테이지 빌드는 `FROM`이 1개가 아닌 2개 이상이 존재할 수 있고

이로 인해 빌드이미지와 실행이미지를 분리할 수 있어서 간편하게 이미지의 크기를 줄일 수 있다.

 

734.28MB -> 6.91MB로 감소

##############################################
FROM golang:1.19.3 AS builder
# Set Environment Variables
ENV HOME /app
ENV CGO_ENABLED 0
ENV GOOS linux

# go package install
WORKDIR /app
RUN go mod init mux
RUN go mod tidy
RUN go mod download
RUN go get github.com/gorilla/mux

COPY . .
RUN go build -a -o main .

##############################################
FROM scratch

WORKDIR /root/
# 이전stage에서 build했던 바이너리 파일을 복사
COPY --from=builder /app/main .
EXPOSE 8080

CMD [ "./main" ]

불필요한 패키지 설치하지 않는다. (Don’t install unnecessary packages)

복잡도, 의존성, 파일크기, 빌드 시간등을 줄이기 위해서 불필요한 패키지들은 뺀다.

반드시 필요하지 않는 패키지를 의미하는데 예를 들면 Database 이미지에 텍스트 에디터 패키지는 필요가 없다.

 

어플리케이션 디커플링 (Decouple applications)

각각의 컨테이너는 단일한 처리를 목적으로 한다.

어플리케이션을 디커플링해서 쪼개면 수평 확장과, 컨테이너 재사용이 쉬워진다.

예를 들어, 웹 어플리케이션을 스택을 3개의 컨테이너로 구성한다고 했을때

웹, 데이터 베이스, 인메모리 캐시로 분리하여 관리할 수 있다.

 

각 컨테이너를 디커플링하여 하나의 프로세스로 제한하는것은 경험상 좋은 규칙이지만 모두 그렇지는 않습니다.

Celery의 multiple worker process들과 Apache는 request마다 프로세스를 생서할 수도 있습니다.

(Spawned with an init process)

 

컨테이너를 가능하면 모듈화하고 명확하게 관리하면서 최선의 선택을 한다.

컨테이너간에 서로 의존적이라면 Docker container networks로 서로간 통신하게 할 수도 있다.

레이어의 수 최소화 (Minimize the number of layers)

  • RUN, COPY, ADD 로만 레이어를 생성한다. 다른 명령어는 임시로 중간 이미지를 생성하며, 빌드 사이즈에 영향을 안준다.
  • 가능하다면, 멀티 스테이지 빌드를 사용하고 필요한 아티팩트만 마지막 이미지에 복사하는 것이 좋다. 이렇게 하면 최종 이미지의 크기를 늘리지 않고도 중간 빌드 단계에서 각종 도구와 디버그 정보를 포함 시킬 수 있다.

멀티라인 인수 정렬 (Sort multi-line arguments)

가능하다면, 여러 줄의 인수를 알파벳순으로 정렬하는 것이 좋다. 이렇게 하면 패키지 중복을 방지하고 쉽게 업데이트 할 수 있다. 또한 PR을 읽고 검토하기에 용이해진다. '\' 앞에 공백을 추가하는 것도 도움이 된다.

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion \
  && rm -rf /var/lib/apt/lists/*

레버리지 빌드 캐시 (Leverage build cache)

이미지를 빌드할때, 도커는 Dockerfile에 설정한 순서대로 실행한다.

각 명령을 검토할 때 도커는 새로운 (중복) 이미지를 만들지 않고 캐시에서 재사용할 수 있는 이미지를 찾는다.

캐시를 사용하지 않으려면 docker build 할때 --no-cache=true 옵션을 사용할 수 있다.

 

도커가 캐시를 활용할 수 있도록 하기 위해서는, 일치하는 이미지를 찾을 수 있는 경우와 없는 경우에 대해 이해하는 것이 중요하다.

도커가 따르는 기본적인 규칙은 아래와 같다.

  • 이미 캐시에 있는 부모 이미지를 시작으로, 다음 명령어를 해당 기본 이미지에서 파생된 모든 하위 이미지와 비교하여 동일한 명령어를 사용하여 빌드되었는지 확인한다. 그렇지 않으면 캐시가 무효화 된다.
  • 대부분의 경우 Dockerfile의 명령어를 하위 이미지 들과 비교하는 것으로 충분하다. 하지만, 어떤 명령어는 더 많은 검토가 필요하다.
  • ADD COPY의 경우 이미지 파일 내용을 검사하고 각 파일에 대한 체크섬을 추가로 계산한다. 여기에서 파일의 마지막 수정 시간이나 엑세스 시간은 고려하지 않는다. 캐시 조회 중에 체크섬을 기존 이미지의 체크섬과 비교한다. 파일에서 내용이나 메타데이터의 변경이 있으면 캐시가 무효화 된다.
  • ADD COPY 명령어 외에도 캐시 일치 여부를 확인하기 위해 컨테이너의 파일을 확인하지 않는다. 일례로 RUN apt-get -y update를 처리할때 컨테이너에서 업데이트된 파일을 검사하여 캐시와 치하는 경우가 존재하는지 확인하지 않는다. 이 경우는, 명령 문자열 자체만 일치하는지만 검토한다.

일단 캐시가 무효화되면 이후의 모든 Dockerfile 명령은 새로운 이미지를 생성하며, 캐시는 사용되지 않는다.

Pipe를 이용한 Standard Input사용(Pipe Dockerfile through `stdin`)

docker build를 수행할 때 pipe를 이용하여 Dockerfile을 disk에 만들지 않을 수도 있다.

일회성 빌드나 테스트 같이 가볍게 사용할때 유용하다.

# For example, the following commands are equivalent:


echo -e 'FROM busybox\nRUN echo "hello world"' | docker build -

docker build -<<EOF
FROM busybox
RUN echo "hello world"
EOF

# You can substitute the examples with your preferred approach, 
# or the approach that best fits your use-case.

 

build context 전송없이 stdin으로 이미지 생성
(Build an image using a Dockerfile from stdin, without sending build context)

docker build 할때 `-` 하이픈 옵션을 사용하면 Dockerfile을 생성하지 않고 build context 전송하지도 않아서 

build 속도를 향상 시킬 수 있다.

  • .dockerignore 를 이용해서 docker build 할때 포함하지 않도록 제외할 수 있다.
docker build -t myimage:latest -<<EOF
FROM busybox
RUN echo "hello world"
EOF

 

주의할점은 COPY 혹은 ADD를 사용하는 경우라면 실패

# create a directory to work in
mkdir example
cd example

# create an example file
touch somefile.txt

docker build -t myimage:latest -<<EOF
FROM busybox
COPY somefile.txt ./
RUN cat /somefile.txt
EOF

# observe that the build fails
...
Step 2/3 : COPY somefile.txt ./
COPY failed: stat /var/lib/docker/tmp/docker-builder249218248/somefile.txt: no such file or directory

 

반응형

'Infra' 카테고리의 다른 글

인프라 개념 LACP  (1493) 2022.06.10
Python으로 Opendistro Account 생성하기  (615) 2021.03.17
Cloudfront와 S3를 이용한 웹 서비스 구성  (1938) 2021.03.10
Hashicorp Terraform (테라폼)  (757) 2020.10.28
AWS Subnet 생성하기  (2534) 2020.07.23
$ 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' 카테고리의 다른 글

Dockerfile Best Practice (모범 사례)  (456) 2022.11.03
Python으로 Opendistro Account 생성하기  (615) 2021.03.17
Cloudfront와 S3를 이용한 웹 서비스 구성  (1938) 2021.03.10
Hashicorp Terraform (테라폼)  (757) 2020.10.28
AWS Subnet 생성하기  (2534) 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' 카테고리의 다른 글

Dockerfile Best Practice (모범 사례)  (456) 2022.11.03
인프라 개념 LACP  (1493) 2022.06.10
Cloudfront와 S3를 이용한 웹 서비스 구성  (1938) 2021.03.10
Hashicorp Terraform (테라폼)  (757) 2020.10.28
AWS Subnet 생성하기  (2534) 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  (1493) 2022.06.10
Python으로 Opendistro Account 생성하기  (615) 2021.03.17
Hashicorp Terraform (테라폼)  (757) 2020.10.28
AWS Subnet 생성하기  (2534) 2020.07.23
AWS VPC 생성하기  (893) 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년정도 걸렸다.

 

 

 

반응형

+ Recent posts