728x90

Cluster lever shard allocation

샤드 할당은 initial recovery, replica 할당(증설), 재할당 그리고 노드가 추가/삭제 될때 발생 하는 것에 대한 설정들은 다음과 같습니다. 

  • All
  • Primaries
  • New_primaries
  • None

이 Setting은 노드 재시작시 local Primary shard 복수 시에는 해당하지 않습니다.

Local primaries 란 remote cluster의 primaries가 아닌 것을 말합니다. ( remote cluster)

 

예시) Primary 샤드를 갖고 있는 노드가 재시작하면 재시작된 노드의 primary shard allocation id와 active인 allocation id를 비교해서 같다면 바로 primary로 즉시 복구 합니다. 

cluster.routing.allocation.node_concurrent_incoming_recoveries
cluster.routing.allocation.node_concurrent_outcoming_recoveries

 

동시에 샤드 복구를 허용하는 개수, 두 설정 모두 default가 2

incoming 은 샤드 rebalance 가 아니라면 대부분 replica 샤드이고 outgoing은 샤드 rebalance가 아니라면 대부분 primary 샤드 입니다. 

Cluster.routing.rebalance.enable – 재분배가 가능한 샤드의 종류 (all, primaries, replicas, none) Cluster.routing.allocation.allow_rebalance – 샤드 재분배를 허용하는 경우,
Always
Indices_primaries_active: 모든 primary 샤드가 클러스터에 할당된 경우
Indices_all_active(default): 모든 샤드가 할당된 경우
Cluster.routing.allocation.cluster_concurrent_rebalance : 동시에 재분배할 샤드 갯수

 

 


Shard balancing heuristics

아래 설정 값은 각 샤드를 할당할 노드를 선택할 때 위에 소개한 설정과 함께 사용됩니다. 

3개의  rebalancing setting값에서 (rebalance 문자가 있는 설정값) 허용된

 rebalance 작업이 더 이상 balance.threshold 값보다 높일 수 없다고 판단되면 해당 클러스터는 균형을 이루었다고 판단하고

재분배가 안 일어날 수도 있습니다.

Cluster.routing.allocation.balance.shard

전체 샤드 개수 중 해당 노드에 할당된 샤드 개수의 weight factor

Default는0.45f 값을 높일수록 노드당 갖고 있는 샤드 개수가 비슷해집니다.

Cluster.routing.allocation.balance.index

특정 노드에 할당된 인덱스 당 샤드 개수의 weight factor

Default는0.55f 값을 높일수록 노드당 갖고 있는 인덱스 개수가 비슷해집니다.

Cluster.routing.allocation.balance.threshold

재분배 일어날 최솟값

 

Default1.0f 값을 높일수록 재분배에 덜 민감하게 동작합니다.

노드마다 가중치 계산하고 threshold

 

Balancing algorithm에 의해 재분배를 하려고 해도 forced awareness, allocation filtering에 의해 재분배가 일어나지

않을 수도.

 

 

문제점 : 

 

 

인덱스의 데이터값이 들어올때 primary shard의 할당이 한 노드에 쏠리는 현상을 어떤식으로 방지하거나 혹은 할당된 프라이머리샤드를 재분배를 할 수 있는지.

 

Disk-based shard allocation 

 

노드에 사용 가능한 디스크를 고려해서 샤드를 할당할 수도 있습니다.

Cluster.routing.allocation.disk.watermark.low

샤드 할당 시 고려할 disk low watermark, default 85%

 

) 85%로 설정하면 디스크 사용율이 85%노드에는 샤드 할당을 하지 않습니다.

이 설정은 새로 생기는 인덱스의 primary 샤드엔 적용되지 않습니다.

값을 비율로 할 수도 있고 (ex 85%) 절댓값으로도 (500mb) 할 수 있습니다.

비율로 할 때는 사용량을 의미하고, 절대값으로 할 때는 남는 공간을 의미합니다.

Cluster.routing.allocation.disk.watermark.flood_statge

넘치는 단계로 판단하는 watermark, default 95%

 A  노드의 디스크 사용률이 95%가 넘으면  A노드에 할당된 샤드의 인덱스들은 (한 개의 샤드여도) read-only index block 이 됩니다.

 

인덱싱이 가능한 디스크 공간이 생기면 (수동으로) 수동으로 index block 설정을 수동으로 해제해야 합니다.

read_only, read_only_allow_delete block 설정된 인덱스마다 들어가서 수동으로 해제

3 개의 설정값은 비율과 절댓값을 섞어 쓸 수 없습니다.

(low watermark – 85% / high watermark 500mb를 같이 사용 X)

내부적으로 low < high, high < flood_stage

 

Cluster.info.update.interval 노드의 디스크 사용량 정보 업데이트 간격, default 30

Cluster.routing.allocation.disk.include_relocations
노드의 디스크 사용량을 계산할 때 노드의 재배치 중인 샤드 고려 여부,

default true 재배치 중인 샤드의 크기를 고려한다는 의미는 한 샤드의 재배치가 90% 정도 진행 중일 때
디스크 사용량을 조회하면
이미 재배치가 완료된 것처럼 디스크 사용량이 계산됩니다.

 

https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-cluster.html#shard-allocation-awareness

 

Cluster-level shard allocation and routing settings | Elasticsearch Guide [8.17] | Elastic

You can’t mix the usage of percentage/ratio values and byte values across the cluster.routing.allocation.disk.watermark.low, cluster.routing.allocation.disk.watermark.high, and cluster.routing.allocation.disk.watermark.flood_stage settings. Either all va

www.elastic.co

Shard allocation awareness

 

물리적인 거리를 고려해서 shard 할당할 수 있습니다. ‘rack_id‘zone’ attribute를 이용할 수 있습니다.

https://github.com/elastic/elasticsearch/blob/master/server/src/main/java/org/elasticsearch/cluster/routing/alloc

Attribute 값의 개수만큼 샤드를 복사합니다.

Force awareness

Shard allocation awareness 기능을 이용하여 근거리에 샤드를구성할 수 있습니다.

전 세계에 서비스가 되는데 동일한 데이터 조회가 필요한 경우에 쓰임이 많을것으로 생각됨

Elasticsearch cluster node 들에 primary shard와와 replica shard 할당하는 방법 결정

-물리적 구성 관점에서 장애 요소 최소화해서 할당

Cluster-level shard allocation filtering

특정 노드를 shutdown 시키려고 할 때 해당 기능을 이용하면 좋습니다.

Filtering 할수 있는 attribute는node name, host IP, publish IP , IP(host or public IP), hostname, node id가 있습니다.

 

클러스터 내 특정 노드를 제외 시키기 위한 설정
특정 노드에 문제가 발생해서 신규로 생성되는 인덱스의 샤드가 할당되지 못하도록 함.

 

예시) Node 42_1번의번의 disk 90% 이상 사용 중인 경우

신규 인덱스의 샤드를배치시키게 되면 disk full로 이어질 수 있음.

 

이런 경우 당장 shard rebalancing을 수행하기 어렵고

다른 노드들에 disk usage가 여유가 있을 경우 임시로 제외시키는 설정 가능

 

-> 장애로 이어지기 전에 조치를 취하고 disk 용량을 확보 후 다시 투입

728x90
728x90

작년 2024년 8월30일에 엘라스틱서치 블로그 글을 기반으로 작성을 하였습니다. 

그동안 엘라스틱서치는 7.10 버전 이전까지만 오픈소스화 하였고 그 윗 버전은 아니였습니다. 

하지만 Elasticsearch를 다시 오픈 소스라고 부르게 되었습니다. 

 

아마도 AWS의 OpenSearch와의 문제때문에 오픈소스로 다시 돌아왔지만, 개발자들에겐 더할 나위없는 좋은 선택인거같습니다. 



좋은 소식 : 3년이 지난 지금, Amazon은 자체 포크에 대한 완전한 헌신을 보여주었으며, 초기의 시장 혼란도 대부분 해소되었습니다. 또한, AWS와의 파트너십은 더욱 견고해졌으며, 그 결과 올해의 AWS 파트너로 선정되는 영예를 안았습니다. 저는 시간이 지나면 결국 우리가 다시 오픈 소스 프로젝트로 돌아갈 수 있을 것이라 믿어왔으며, 이제 마침내 그 시점이 도래했습니다.


위에 내용을 더 깊게 말씀드리면, 왜 Elastic 이 3년전에 오픈소스를 더이상 제공하지 않았는지에 대한 이유이빈다. 

 

기존에 Elastc 의 라이센스 변경은 기업들이 ElasticSearch 와 Kibana 제품을 가져다가 협업하지 않고 서비스로 직접 제공하는 것을 방지하기 위한것이였다고 합니다. 

 

AWS와의 분쟁에서 법원을 거치는 방법을 시도했지만 결국 라이센스 변경을 결정했다고 합니다. 

아마존 CTO 가 트위터를 통해서 Elastic과 협업하여 출시했다고 했는데 사실이 아니였고 아마존과 같은 큰 회사가 apache 오픈소스로 Amazon ElasticSearch Service를 만들어 제공한 것에 대한 문제점들 때문에 오픈소스를 더이상 제공하지 않았던거같습니다.

 

최근에, 우리는 윤리적으로 문제가 되는 행동이라고 여겨지는 더 많은 예를 발견했습니다.

우리는 독점 기능으로 차별화해왔고, 이제 이러한 기능 설계가 Amazon에게 "영감을 주는" 역할을 하는 것을 봅니다.

그들의 행동은 계속되고 있으며 더 뻔뻔한 것입니다. 좋지 않습니다.

 

Elastic은 Microsoft, Google, Alibaba, Tencent, Clever Cloud 등을 포함한 클라우드 서비스 제공자와 협업합니다. 우리는 이렇게 협업하는 방법을 찾을 수 있다는 것을 보여주었습니다. 우리는 심지어 Amazon의 다른 부분들과도 함께 일합니다. 우리는 항상 협업을 하는 데 열려 있습니다. 단, 좋은 방식이어야 합니다.

 

저는 투명성, 협업, 개방성이라는 오픈 소스 커뮤니티의 핵심 가치를 믿습니다. 전 세계 사용자가 누릴 수 있도록 훌륭한 제품을 빌드하는 것입니다. Elasticsearch와 Kibana를 사용하여 놀라운 것들이 빌드되어왔고 앞으로도 계속 빌드될 것입니다.

 

그리고 분명한 것은, 이러한 변경이 우리 사용자들에게는 아무런 영향도 주지 않는다는 것입니다. 클라우드나 온프레미스에서 우리와 협력하는 고객에게는 아무런 영향도 미치지 않습니다.

 

우리는 Elasticsearch를 만들었으며, 다른 누구보다도 Elasticsearch를 소중히 여깁니다. 그것은 우리 필생의 작업입니다. Elastic은 날마다 최선을 다해 기술을 발전시키고 여러분을 대신하여 혁신하기 위해 더 많은 노력을 기울일 것입니다.

 


엘라스틱서치 왈 : 

더보기
  • "라이선스를 변경한 것은 실수였고, Elastic은 이제 이를 철회하려 한다." 우리는 3년 전 라이선스를 변경하면서 시장의 혼란을 많이 해소했습니다. 그 결과로 많은 것이 변화했고 지금은 완전히 다른 환경이 되었습니다. 우리는 과거에 머물지 않고 사용자들에게 더 나은 미래를 제공하고자 합니다. 우리가 그때 행동을 취했기 때문에 지금도 행동을 취할 수 있는 위치에 있는 것입니다.
  • "진정한 오픈소스는 AGPL가 아니라 라이선스 X이다." AGPL은 OSI가 승인한 라이선스이며 널리 채택된 라이선스입니다. 예를 들어 MongoDB는 AGPL이고 Grafana는 AGPL입니다. 이는 AGPL이 사용량이나 인기도에 영향을 미치지 않는다는 것을 보여줍니다. 우리가 AGPL을 선택한 이유는 OSI와 함께 전 세계에 더 많은 오픈소스를 위한 길을 열 수 있는 가장 좋은 방법이라고 믿기 때문입니다.
  • "Elastic 실적이 좋지 않아서 라이선스를 변경한다" - 저는 엘라스틱의 미래가 여전히 어느 때보다 기대된다는 말로 시작하겠습니다. 우리 제품과 우리 팀의 실행력이 정말 자랑스럽습니다. 우리는 Stateless Elasticsearch, ES|QL, 그리고 GenAI 사용 사례를 위한 다양한 벡터 데이터베이스/하이브리드 검색 개선 사항을 출시했습니다. 또한 로깅과 관측 가능성 분야에서는 OTel 집중하고 있습니다. 보안 분야의 SIEM 제품은 놀라운 기능을 계속 추가하고 있으며 시장에서 가장 빠르게 성장하는 제품 하나입니다. 우리는 사용자들의 반응은 겸허하게 받아들이고 있습니다. 주식 시장의 기복이 있을 있지만 장담할 있는 것은 우리가 항상 장기적인 관점에서 생각하고 있으며 이번 변화도 일환이라는 것입니다.
728x90
728x90

Udemy - 'Docker & Kubernetest : 실전 가이드' 를 베이스로 한 내용입니다.

앞으로 총 4주간 위의 강의를 듣고 정리하며 도커와 쿠버네티스에 관한 내용을 공유하도록하겠습니다.


도커란?

도커는 컨테이너라는 기술을 사용해 소프트웨어를 쉽게 만들고 관리할 수 있게 도와주는 도구입니다. 

여기서 말하는 컨테이너는 간단히 말해 소프트웨어 패키지 입니다. 

이 패키지 안에는 애플리케이션의 코드뿐만 아니라, 그 코드를 실행하는 데 필요한 모든 도구나 설정도 들어 있습니다.

예를 들어, NodeJs 애플리케이션을 도커로 만든다면, NodeJs 코드와 함께 NodeJs 실행 환경도 한 번에 묶여 들어가게 되는거죠.

이렇게 한 번 컨테이너에 담아두면, 어디서나 동일한 환경에서 그 애플리케이션을 실행 할 수 있습니다.

 

예시) 음악 앨법을 생각해보자!

- 내가 좋아하는 가수의 새로운 앨범을 발표했을때, 앨범에는 곡이 있고, 그 곡들을 들려줄 수 있는 플레이어가 필요합니다. 

만약 앨범을 특별한 패키지로 만들어 판매 한다면, 그 패키지 안에는 앨범을 들을 수 있는 플레이어와 함께, 앨범의 모든 곡이 들어가 있게 된다.

이런식으로 포장된 앨범을 다른 사람에게 주면, 그 사람은 별다른 준비 없이도 바로 앨범에 수록된 곡들을 들을 수 있습니다.

 

이게 바로 도커 컨테이너와 비슷한 개념이라고합니다. 

즉, 도커는 애플리케이션을 만들어서 그 안에 필요한 코드와 도구(예를 들어 db, runtime envir) 를 다 넣고, 그걸 쉽게 배포할 수 있게 도와줍니다. 이렇게 묶여진 app은 어디서나 동일하게 실행될 수 있습니다. 

또한 하나의 패키지로 포함되기 때문에 설치나 환경 설정에 신경 쓸 필요 없이 언제든지 바로 실행할 수 있는 장점이 있습니다.

 


왜 컨테이너가 필요할까?

영상에서 설명했지만, 도커와 컨테이너의 중요성을 좀 더 쉽게 설명 해보도록 하겠습니다.

 

예를 들어 잘 만들어진 애플리케이션이 있습니다. 이 애플리케이션은 NodeJs에서 잘 작동 되지만, 만약 이 코드를 서버에 배포할 때 서버에 구 버전 NodeJS가 설치 되어 있다면?? 

서버에서 애플리케이션이 제대로 작동하지 않을 수 있습니다. 왜냐하면 구버전의 NodeJs에서는 최신 기능이 지원하지 않기 때문입니다. 

이러한 문제를 해결하려면, 애플리케이션을 실행하느 서버의 환경이 개발 환경과 똑같아야합니다.

 

그렇다면, 여기서 도커가 도움이 됩니다. 

도커는 애플리케이션과 함께 필요한 환경을 컨테이너에 담아 움직입니다. 

즉, 어디서나 동일하게 실행됩니다. 

 

회사안, 팀원들과 프로젝트 진행중, 서로 다른 버전을 사용하므로 코드가 제대로 작동하지 않을 수 있습니다.

이럴 때 도커를 사용하면, 팀원들이 각자 같은 환경을 사용할 수 있기 때문에 호환성 문제를 줄일 수 있습니다.

또한, 각 프로젝트에서 필요한 다른 버전의 소프트웨어를 설치 해야 할 수도 있습니다.

예를 들어 자바 8 다른 프로젝트에서는 자바 11이 필요하다면, 매번 버전을 설치하고 제거하는 것이 불편합니다.

이러한 문제는 도커가 각 프로젝트를 별도의 컨테이너로 관리하게 해주므로, 프로젝트 간에 쉽게 전환 할 수 있습니다.

 

각 프로젝트가 필요한 환경을 그대로 담고 있기 때문에, 버전 충돌이나 설치 문제를 걱정할 필요가 없습니다.

 

결론 : 

 

  • 개발 환경과 운영 환경을 일치시켜서 문제를 예방한다.
  • 팀원들이 동일한 환경에서 작업하게 해준다.
  • 프로젝트 간 환경을 쉽게 전환하게 도와준다.

 

 


버추얼 머신 vs 컨테이너

버추얼 머신과 컨테이너를 비교했을 때 장점을 알아보도록 하겠습니다. 

 

버추얼 머신 (VM)은 호스트 운영 체제 위에 가상 운영 체제를 올려서 새로운 시스템을 만드는 방식

각 VM 은 별도의 운영 체제를 포함하고 있어 시스템 자원을 많이 차지하고 성능이 저하될 수 있습니다. 

또한 여러 VM을 설정하고 구성하는 것이 복잡하고, 공유나 재구축이 어려운 점이 있습니다. 

 

컨테이너 장점 : 

컨테이너는 가상 운영 체제를 전체적으로 실행하지 않고, 필요한 환경만 가볍게 캡슐화 합니다.

이를 통해 자원 소비가 적고, 빠르며, 효율적입니다. 

또한 이미지 라는 구성 파일을 사용해 동일한 환경을 다른 사람과 쉽게 공유하거나 재구축할 수 있어서 협업에 좋습니다. 

 

결론 : 

도커는 컨테이너를 효율적으로 만들고 관리하는 도구로, 버추얼 머신보다 훨씬 가볍고 빠르며 효율적인 환경을 제공합니다.

이 덕분에 여러 개발 환경을 쉽게 관리하고, 애플리케이션과 관련된 모든 것을 한 곳에 담아 움직일 수 있습니다. 

 

[참조]

도커 설치  : https://zinirun.github.io/2020/08/15/how-to-use-docker/

728x90
728x90

GitHub에 있는 프로젝트를 IntelliJ IDEA로 불러오는 방법은 다음과 같습니다. 아래 단계를 따라 진행하시면 됩니다.

1. **GitHub에서 프로젝트 Clone하기**

IntelliJ IDEA에서 직접 GitHub 프로젝트를 Clone할 수 있습니다.
 

(1) IntelliJ IDEA 실행
- IntelliJ IDEA를 실행합니다.

 (2) **Get from VCS** 선택
- 시작 화면에서 **Get from VCS**를 클릭합니다.
  - 이미 프로젝트가 열려 있다면, 상단 메뉴에서 **File > New > Project from Version Control**을 선택합니다.

(3) GitHub URL 입력
- **Repository URL** 필드에 GitHub 프로젝트의 URL을 입력합니다.
  - 예: `https://github.com/사용자명/레포지토리명.git`
- **Directory** 필드에는 프로젝트를 저장할 로컬 경로를 지정합니다.
- **Clone** 버튼을 클릭합니다.

(4) 인증 정보 입력
- GitHub 계정으로 로그인하라는 팝업이 나타납니다.
  - **Token** 또는 **GitHub 계정**으로 로그인합니다.
  - Token을 사용하려면 GitHub에서 Personal Access Token을 생성해야 합니다.

(5) 프로젝트 열기
- Clone이 완료되면 IntelliJ IDEA가 자동으로 프로젝트를 엽니다.



 2. **로컬에 Clone된 프로젝트 열기**


이미 로컬에 프로젝트를 Clone했다면, IntelliJ IDEA에서 해당 프로젝트를 열 수 있습니다.

(1) IntelliJ IDEA 실행
- IntelliJ IDEA를 실행합니다.

(2) **Open** 선택
- 시작 화면에서 **Open**을 클릭합니다.
  - 이미 프로젝트가 열려 있다면, 상단 메뉴에서 **File > Open**을 선택합니다.

(3) 프로젝트 폴더 선택
- 로컬에 Clone된 프로젝트 폴더를 선택합니다.
- **OK** 버튼을 클릭합니다.

(4) 프로젝트 설정 확인
- IntelliJ IDEA가 프로젝트를 분석하고 필요한 설정을 자동으로 구성합니다.
- Maven/Gradle 프로젝트라면 의존성(dependencies)을 자동으로 다운로드합니다.




3. **Git 연동 확인**


IntelliJ IDEA는 Git과 연동되어 있습니다. 프로젝트를 열면 Git 기능을 사용할 수 있습니다.

(1) Git 탭 확인
- 하단의 **Version Control** 탭을 클릭하여 Git 로그, 변경 사항 등을 확인할 수 있습니다.
(2) Git 작업 수행
- **Commit**, **Push**, **Pull** 등의 Git 작업을 IntelliJ IDEA에서 직접 수행할 수 있습니다.
  - 상단 메뉴에서 **VCS > Git**을 선택하거나, 오른쪽 상단의 Git 아이콘을 사용합니다.




4. **프로젝트 설정 (필요한 경우)**


프로젝트에 따라 추가 설정이 필요할 수 있습니다.

(1) JDK 설정
- 프로젝트에 JDK가 설정되어 있지 않다면, **File > Project Structure > Project**에서 JDK를 설정합니다.

(2) 빌드 도구 설정
- Maven/Gradle 프로젝트라면, IntelliJ IDEA가 자동으로 빌드 도구를 인식합니다.
- 필요한 경우 **File > Settings > Build, Execution, Deployment > Build Tools**에서 설정을 확인합니다.




5. **프로젝트 실행**


- 프로젝트 설정이 완료되면, IntelliJ IDEA에서 프로젝트를 실행할 수 있습니다.
- **Run** 버튼을 클릭하거나, 상단 메뉴에서 **Run > Run '프로젝트명'**을 선택합니다.




728x90
728x90

* 마스토 후보 노드는 마스터 노드 선출하는 역할을 하며, 또 자신이 마스터 노드로 선출될 수도 있다.

 

Quorum-based decision making 

Quorum : 마스터 후보 노드의 부분집합 수, 즉 투표를 위해 필요한 마스터 노드의 최소 개수 (매우 중요해서 시스템이 주로 관리한다.)

공식 : Minumim number of master nodes(quorum) = number of master-eligible nodes / 2)+1

 * 기본적으로 분산시스템에서도 사용이 되는점 

 

 

 

 

 

 

마스터 후보 노드 구성시 주의사항

1. 3개 이상의 홀 수로 구성

2. 한번에 절반 이상의 노드 제거 금지

3. initial_master_nodes 와의 관계

 

 

3개 이상의 홀 수로 구성

 

 

split brain을 예방하기 위해서 (클러스터링 시스템에서) 사용하는 기법이 quorum 기반의 알고리즘 혹은 네트워크 알고리즘적으로 예방한다. 

 

주의사항 1)

3개 이상의 홀수로 구성 되어 있는데 

마스터 후보 노드가 짝 수인 경우 

- Quorum (n=2) : (n/2) +1 = 2

- Quorum (n=4) : (n/2) +1 = 3

- Quorum (n=6) : (n/2) +1 = 4

 

split brain이 발생하면 모든 클러스터가 중지된다.

 

주의사항 2)

한번에 절반 이상의 노드 제거 금지

Quorum : 투표를 위해 필요한 마스터 노드의 최소 개수

Minumim number of master nodes(quorum) = number of master-eligible nodes / 2)+1

 

(4/2)+ 1 = 3, 3개의 투표 가능한 마스터 후보 노드가 있어야 하는데 2개를 종료하면 2개가 남게된다. 

(5/2)+ 1 = 3, 3개의 투표 가능한 마스터 후보 노드가 있어야 하는데 3개를 종료하면 2개가 남게된다.

 

주의사항 3)

initial_master_nodes 와의 관계

- 최초에 클러스터를 생성할 때 마스터 후보 역할을 갖는 노드는 모두 initial_master_nodes에 포함되어야한다.

그러나 클러스터가 생성된 이후에 추가되는 마스터 후보 노드는 모두 initial_master_nodes 에 포함되지 않아야 한다.(절대)

지정해준 역할에 의해서 마스터 eligerble 로 등록이 되기 때문에 절대로 추가되는 마스터 노드에 intitial master nodes에 더해지면 안됩니다.

728x90

+ Recent posts