[MySQL] MySQL Percona XtraDB Cluster 소개 및 설정 변수 알아보기

 

l  Version : MySQL Percona 8.X

 

 

Percona XtraDB Cluster MySQL을 위한 완전한 오픈 소스 고가용성 솔루션으로 MySQL Percona Server Percona XtraBackup Galera 라이브러리와 통합하여 동기식 다중 소스 복제를 가능하게 한다. Galera 라이브러리에 대한 내용은 아래 링크를 참고한다.

l  MySQL/MariaDB 환경에서 다중 마스터 복제를 지원하는 Galera Cluster 알아보기 : https://sungwookkang.com/entry/MySQLMariaDB-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%8B%A4%EC%A4%91-%EB%A7%88%EC%8A%A4%ED%84%B0-%EB%B3%B5%EC%A0%9C%EB%A5%BC-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94-Galera-Cluster-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0

l  MySQL Galera Cluster + ProxySQL에서 Galera Cluster 특성을 고려한 R/W 호스트 그룹 설정 하기 : https://sungwookkang.com/entry/MySQL-Galera-Cluster-ProxySQL%EC%97%90%EC%84%9C-Galera-Cluster-%ED%8A%B9%EC%84%B1%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%9C-RW-%ED%98%B8%EC%8A%A4%ED%8A%B8-%EA%B7%B8%EB%A3%B9-%EC%84%A4%EC%A0%95-%ED%95%98%EA%B8%B0

 

 

클러스터는 노드로 구성되며, 각 노드에는 노드 전체에 걸쳐 동기화된 동일한 데이터 세트가 포함되어 있다. 권장되는 구성은 3개 이상의 노드를 보유하는 것이지만 2개의 노드를 보유할 수도 있다. 각 노드는 일반 MySQL Server 인스턴스(: Percona Server)이다. 기존 MySQL Server 인스턴스를 노드로 변환하고 이 노드를 기본으로 사용하여 클러스터를 실행할 수 있다. 클러스터에서 노드를 분리하여 일반 MySQL Server 인스턴스로 사용할 수도 있다.

 

Percona XtraDB Cluster 장점은 쿼리를 실행하면 노드에서 로컬로 실행된다. 모든 데이터는 로컬에서 사용할 수 있으므로 원격 액세스가 필요하지 않다. 중앙 관리 시스템이 아니므로 언제든지 노드를 유연하게 구성할 수 있으며 클러스터는 데이터 손실 없이 계속 작동한다. 읽기 작업 부하를 확장하는 데 적합한 솔루션이며 모든 노드에 읽기 쿼리를 호출할 수 있다.

단점으로는 노드 프로비저닝의 오버헤드가 있다. 새 노드를 추가하면 기존 노드 중 하나에서 전체 데이터 세트를 복사해야 한다. 예를 들어 노드가 100GB이면 100GB를 복사하는 방식이다. 이러한 특징 때문에 쓰기 확장 솔루션으로는 사용할 수 없다. 전체 트래픽을 1개 노드에 실행하는 경우에 비해 2개 노드에 쓰기 트래픽을 실행하는 경우 쓰기 처리량이 약간 향상될 수 있지만 많은 것을 기대할 수는 없다. 모든 쓰기는 여전히 모든 노드에서 진행되기 때문이다.

 

Percona XtraDB Cluster 구성요소는 XtraDB 스토리지 엔진으로 실행되는 MySQL Percona 서버를 기반으로 하며, Codership Oy에서 개발한 쓰기 세트 복제(wsrep) API의 구현인 Galera 라이브러리를 사용한다. 기본 및 권장 데이터 전송 방법은 Percona XtraBackup을 사용한다.

 

[XtraDB Cluster제약 사항]

l   복제는 InnoDB 스토리지 엔진에서만 작동한다.

l   다른 유형의 테이블에 대한 쓰기는 복제되지 않는다.

l   LOCK TABLES UNLOCK TABLES는 다중 소스 설정에서 지원되지 않는다.

n  GET_LOCK(), RELEASE_LOCK() 등과 같은 잠금 함수

l   쿼리 로그를 테이블로 보낼 수 없다. 쿼리 로깅을 활성화하는 경우 로그를 파일로 전달해야 한다. 쿼리 로깅 및 로그 파일 이름을 선택하려면 General_log General_log_file을 사용해야 한다.

log_output = FILE

 

l   허용되는 최대 트랜잭션 크기는 wsrep_max_ws_rows wsrep_max_ws_size 변수에 의해 정의된다.

l   LOAD DATA INFILE 처리는 10,000개 행마다 커밋된다. 따라서 LOAD DATA로 인한 대규모 트랜잭션은 일련의 작은 트랜잭션으로 분할된다.

l   COMMIT를 발행하는 트랜잭션은 해당 단계에서 여전히 중단될 수 있다.

l   클러스터 수준의 낙관적 동시성 제어로 인해 동일한 행에 쓰고 별도의 Percona XtraDB 클러스터 노드에서 커밋하는 두 개의 트랜잭션이 있을 수 있으며 그 중 하나만 성공적으로 커밋할 수 있다. 실패한 항목은 중단되며, 클러스터 수준 중단의 경우 Percona XtraDB Cluster는 교착 상태 오류 코드를 반환한다.

(Error: 1213 SQLSTATE: 40001  (ER_LOCK_DEADLOCK)).

 

l   XA 트랜잭션은 지원되지 않는다. 커밋시 롤백이 가능하기 때문이다.

l   전체 클러스터의 쓰기 처리량은 가장 약한 노드에 의해 제한된다. 한 노드가 느려지면 전체 클러스터도 느려진다.

l   최소 권장 클러스터 크기는 노드 3개이다. 세 번째 노드는 중재자가 될 수 있다.

l   enforce_storage_engine=InnoDB wsrep_replicate_myisam=OFF와 호환되지 않는다. wsrep_replicate_myisam은 기본적으로 OFF로 설정된다.

l   클러스터 모드에서 Percona XtraDB Cluster를 실행할 때 ALTER TABLE ... IMPORT/EXPORT 워크로드를 가급적 사용하지 않도록 한다. 모든 노드에서 동기화되어 실행되지 않으면 노드 불일치가 발생할 수 있다.

l   모든 테이블에는 기본 키가 있어야 합니다. 이렇게 하면 동일한 행이 다른 노드에서 동일한 순서로 표시된다. 기본 키가 없는 테이블에서는 DELETE 문이 지원되지 않는다.

l   임시 테이블에 영구 테이블 이름을 재사용하지 않도록 한다. MySQL은 영구 테이블과 동일한 이름의 임시 테이블을 허용하지만 이 접근 방식은 권장되지 않는다. Galera Cluster는 이름이 임시 테이블의 이름과 일치하는 영구 테이블의 복제를 차단한다.

l   wsrep_debug 1로 설정하면 오류 로그에 다음 메시지가 포함될 수 있다.

... [Note] WSREP: TO BEGIN: -1, 0 : create table t (i int) engine=innodb
... [Note] WSREP: TO isolation skipped for: 1, sql: create table t (i int) engine=innodb.Only temporary tables affected.

 

l   버전 8.0.21부터 INPLACE ALTER TABLE 쿼리는 쿼리 실행 중에 테이블에 대한 내부 공유 잠금을 사용한다. 이 변경으로 인해 모든 INPLACE ALTER TABLE 쿼리에 대해 LOCK=NONE 절이 더 이상 허용되지 않는다. 이 변경 사항은 다음 시나리오에서 클러스터 노드가 중단될 수 있는 교착 상태를 해결한다.

n  한 세션에 있거나 TOI(Total Order Isolation)로 적용되는 INPLACE ALTER TABLE 쿼리

n  다른 세션의 동일한 테이블에 있는 DML

n  log_bin, log_bin_index 값을 정의할 때 하나 이상의 점 문자(.)를 사용하지 않는다.

n  MySQL XtraBackup은 서로 다른 방식으로 값을 처리하며 이러한 차이로 인해 예측할 수 없는 동작이 발생한다.

 

 

[Percona 버전 번호이해]

버전 번호는 제품 릴리스를 식별한다. 제품에는 해당 릴리스 당시의 최신 GA(일반 출시) 기능이 포함되어 있다.

 

Percona는 기본 버전, 마이너 빌드 및 선택적 사용자 정의 빌드 패턴을 따르는 의미 체계 버전 번호 지정을 사용한다. Percona는 각 마이너 빌드 릴리스에 대해 음수가 아닌 고유한 정수를 오름차순으로 할당한다. 버전 번호는 필요한 경우 기본 Percona Server for MySQL 버전 번호, 마이너 빌드 버전 및 사용자 정의 빌드 버전을 결합한다.

l   Base Version : 기본으로 사용되는 Percona Server for MySQL 버전을 나타내는 가장 왼쪽 숫자 집합으로, 베이스 버전이 이 증가하면 마이너 빌드 버전과 사용자 정의 빌드 버전이 0으로 재설정된다.

l   Minor Build : Percona XtraDB 클러스터가 릴리스 될 때마다 증가하는 내부 번호이며 사용자 정의 빌드 번호는 0으로 재설정된다.

l   Customer Build : 버그 수정에 사용되는 사용자 정의 빌드에 할당된 선택적 번호이다. 수정 사항에 해당 기능이 포함되지 않는 한 기능은 변경되지 않는다. 예를 들어 Percona XtraDB Cluster 8.0.20-11.1, 8.0.20-11.2 8.0.20-11.3은 동일한 Percona Server for MySQL 버전 및 마이너 빌드 버전을 기반으로 하지만 사용자 정의 빌드 버전이다.

 

[wsrep status variables]

wsrep status variables Comments
wsrep_apply_oooe 병렬화 효율성, 쓰기가 순서 없이 적용되는 빈도.
wsrep_apply_oool 더 높은 시퀀스 번호를 가진 쓰기 세트가 더 낮은 시퀀스 번호를 가진 쓰기 세트보다 먼저 적용되는 빈도.
wsrep_apply_window 동시에 적용된 최고 및 최저 시퀀스 번호 사이의 평균 거리.
wsrep_causal_reads wsrep_causal_reads 변수가 ON으로 설정된 동안 처리된 쓰기 세트 수.
wsrep_cert_bucket_count 인증 색인 해시 테이블의 셀 수.
wsrep_cert_deps_distance 병렬로 적용될 수 있는 가장 높은 시퀀스 번호와 가장 낮은 시퀀스 번호 사이의 평균 거리.
wsrep_cert_index_size 인증 색인의 항목 수.
wsrep_cert_interval 트랜잭션이 복제되는 동안 수신된 평균 쓰기 세트 수.
wsrep_cluster_conf_id 발생한 클러스터 멤버십 변경 수.
wsrep_cluster_size 클러스터의 현재 노드 수.
wsrep_cluster_state_uuid 클러스터의 UUID 상태로, 이 값이 wsrep_local_state_uuid의 값과 동일하면 노드가 클러스터와 동기화상태임.
wsrep_cluster_status 클러스터 구성 요소의 상태로
Primary, Non-Primary, Disconnected가 있음.
wsrep_commit_oooe 트랜잭션이 순서 없이 커밋된 빈도.
wsrep_commit_oool 이 변수는 현재 사용하지 않음.
wsrep_commit_window 동시에 커밋된 최고 시퀀스 번호와 최저 시퀀스 번호 사이의 평균 거리.
wsrep_connected 노드가 클러스터에 연결되어 있는지 여부를 표시. 값이 OFF이면 노드가 아직 클러스터 구성 요소에 연결되지 않은 상태로, 이는 잘못된 구성으로 인한 것일 수 있음.
wsrep_evs_delayed 지연된 것으로 간주되는 쉼표로 구분된 노드 목록. 노드 형식은 <uuid>:<address>:<count>, <count>는 해당 노드에 대한 지연 목록의 항목 수.
wsrep_evs_evict_list 제거된 노드의 UUID 목록.
wsrep_evs_repl_latency 그룹 통신 복제 대기 시간에 관한 정보를 제공. 이 대기 시간은 메시지가 전송된 시점부터 메시지가 수신될 때까지 초 단위로 측정.
출력 형식:
<min>/<avg>/<max>/<std_dev>/<sample_size>
wsrep_evs_state 내부 EVS 프로토콜 상태.
wsrep_flow_control_interval Galera 흐름 제어의 하한 및 상한을 표시. 상한은 대기열에 허용되는 최대 요청 수이며, 대기열이 상한에 도달하면 새 요청이 거부된다. 기존 요청이 처리되면 대기열이 줄어들고, 하한에 도달하면 새 요청이 다시 허용된다.
wsrep_flow_control_interval_high 트리거할 흐름 제어의 상한을 표시.
wsrep_flow_control_interval_low 흐름 제어를 정지하는 하한값을 표시.
wsrep_flow_control_paused 흐름 제어로 인해 일시 중지된 마지막 상태 쿼리 이후의 시간.
wsrep_flow_control_paused_ns 일시 중지된 상태에서 소요된 총 시간은 나노초 단위로 측정.
wsrep_flow_control_recv 마지막 상태 쿼리 이후 수신된 FC_PAUSE 이벤트 수. 대부분의 상태 변수와 달리 이 카운터는 쿼리를 실행할 때마다 재설정되지 않으며, 서버가 다시 시작되면 재설정된다.
wsrep_flow_control_requested 노드가 복제 일시 중지를 요청했는지 여부를 반환.
wsrep_flow_control_sent 마지막 상태 쿼리 이후 전송된 FC_PAUSE 이벤트 수. 대부분의 상태 변수와 달리 이 카운터는 쿼리를 실행할 때마다 재설정되지 않으며, 서버가 다시 시작되면 재설정된다.
wsrep_flow_control_status 노드에 일반 트래픽에 대해 흐름 제어가 활성화되어 있는지 여부를 표시. SST 중에는 흐름 제어 상태를 나타내지 않는다.
wsrep_gcache_pool_size GCache에 할당된 페이지 풀 및 동적 메모리의 크기(바이트)를 표시.
wsrep_gcomm_uuid Galera 보기 ID(따라서 클러스터 상태 UUID와 관련 없음) gvwstate.dat UUID를 노출. UUID는 각 노드마다 고유하며, 수동 제거 기능을 사용할 때 이 값을 알아야 한다.
wsrep_incoming_addresses 클러스터에 있는 수신 노드 주소의 쉼표로 구분된 목록을 표시.
wsrep_ist_receive_status JOIN 노드에 대한 IST 진행 상황을 표시. IST가 실행 중이 아닌 경우 값은 비어 있으며, IST가 실행 중인 경우 값은 완료된 전송 비율.
wsrep_ist_receive_seqno_end IST의 마지막 트랜잭션의 시퀀스 번호.
wsrep_ist_receive_seqno_current IST에서 현재 트랜잭션의 시퀀스 번호.
wsrep_ist_receive_seqno_start IST의 첫 번째 트랜잭션의 시퀀스 번호.
wsrep_last_applied 마지막으로 적용된 트랜잭션의 시퀀스 번호.
wsrep_last_committed 마지막으로 커밋된 트랜잭션의 시퀀스 번호.
wsrep_local_bf_aborts 실행되는 동안 복제본 트랜잭션에 의해 중단된 로컬 트랜잭션 수.
wsrep_local_cached_downto GCache에서 가장 낮은 시퀀스 번호. 이 정보는 IST SST를 결정하는 데 도움이 될 수 있다. 값이 0이면 GCache에 쓰기 세트가 없음을 의미 (일반적으로 단일 노드의 경우).
wsrep_local_cert_failures 인증 테스트에 실패한 쓰기 세트 수.
wsrep_local_commits 노드에 커밋된 쓰기 세트 수.
wsrep_local_index 클러스터에 있는 노드의 인덱스.
wsrep_local_recv_queue 수신 대기열의 현재 길이(, 적용을 기다리는 쓰기 세트 수).
wsrep_local_recv_queue_avg 마지막 상태 쿼리 이후 수신 대기열의 평균 길이. 이 숫자가 0보다 크면 노드가 쓰기 세트를 수신하는 속도만큼 빠르게 적용할 수 없음을 의미. 이는 노드가 과부하되어 복제 제한이 발생할 수 있다는 신호일 수 있다.
wsrep_local_replays 비대칭 잠금 세분성으로 인한 트랜잭션 재생 횟수.
wsrep_local_send_queue 전송 대기열의 현재 길이(, 전송 대기 중인 쓰기 세트 수).
wsrep_local_send_queue_avg 마지막 상태 쿼리 이후 전송 대기열의 평균 길이. 클러스터에서 네트워크 처리량 문제나 복제 제한이 발생하는 경우 이 값은 0보다 훨씬 커진다.
wsrep_local_state 내부 Galera 클러스터 FSM 상태 번호.
wsrep_local_state_comment 노드 상태에 대한 내부 번호 및 사람이 읽을 수 있는 해당 설명.
1 : Joining - Node is joining the cluster
2 : Donor/Desynced - Node is the donor to the node joining the cluster
3 : Joined - Node has joined the cluster
4 : Synced-Node is synced with the cluster
wsrep_local_state_uuid 노드에 저장된 상태의 UUID.
wsrep_monitor_status 로컬 모니터(로컬 및 복제 작업), 적용 모니터(쓰기 세트의 적용 작업) 및 커밋 모니터(쓰기 세트의 커밋 작업)의 상태. 이 변수의 값에서 각 모니터(L: Local, A: Apply, C: Commit) last_entered last_left 쌍으로 표시.
 
wsrep_monitor_status (L/A/C)    [ ( 7, 5), (2, 2), ( 2, 2) ]
wsrep_protocol_version 사용된 wsrep 프로토콜의 버전.
wsrep_provider_name wsrep 공급자의 이름(일반적으로 Galera).
wsrep_provider_vendor wsrep 공급자 공급업체의 이름(일반적으로 Codership Oy).
wsrep_provider_version wsrep 공급자의 현재 버전.
wsrep_ready 노드가 쿼리를 수락할 준비가 되었는지 표시. 상태가 OFF이면 거의 모든 쿼리가 ERROR 1047(08S01) 알 수 없는 명령 오류로 인해 실패한다. (wsrep_on 변수가 0으로 설정되지 않은 경우).
wsrep_received 다른 노드에서 수신된 총 쓰기 세트 수.
wsrep_received_bytes 다른 노드에서 수신된 쓰기 세트의 총 크기(바이트).
wsrep_repl_data_bytes 복제된 데이터의 총 크기(바이트).
wsrep_repl_keys 복제된 총 키 수.
wsrep_repl_keys_bytes 복제된 키의 총 크기(바이트).
wsrep_repl_other_bytes 복제된 다른 비트의 총 크기.
wsrep_replicated 다른 노드로 전송된 총 쓰기 세트 수.
wsrep_replicated_bytes 복제된 쓰기 세트의 총 크기. 네트워크를 통해 클러스터 피어로 전송된 실제 바이트 크기를 계산하려면 이 변수의 값에 지정된 네트워크 세그먼트의 클러스터 피어 수를 곱한다.

 

 

[wsrep system variables]

wsrep system variables Comments
pxc_encrypt_cluster_traffic SSL 암호화의 자동 구성을 활성화 유무.
l   ON, 1, true : Enabled(default).
l   OFF, 0, false : Disabled.
pxc_maint_mode ProxySQL의 설정을 조정하지 않고 노드를 종료하기 위한 유지 관리 모드를 지정.
l   DISABLED : ProxySQL에게 평소와 같이 트래픽을 노드로 라우팅하도록 지시하는 기본 상태.
l   SHUTDOWN: 노드 종료를 시작할 때 자동으로 설정.
l   MAINTENANCE: 노드를 종료하지 않고 유지 관리를 수행해야 하는 경우 이 상태를 수동으로 변경할 수 있다.
pxc_maint_transition_period pxc_maint_mode SHUTDOWN 또는 MAINTENANCE로 변경할 때 전환 기간을 정의. 기본적으로 기간은 10초로 설정. 장기 실행 트랜잭션을 수용하도록 값을 늘릴 수 있다.
pxc_strict_mode Percona XtraDB Cluster에서 실험적 기능과 지원되지 않는 기능의 사용을 피하기 위해 검증을 실행하는 PXC Strict 모드를 제어. 선택한 실제 모드에 따라 유효성 검사에 실패하면 서버에서 오류가 발생하거나(시작 중지 또는 작업 거부) 경고를 기록하고 정상적으로 계속 실행.
wsrep_applier_FK_checks Percona XtraDB Cluster 8.0.26-16부터 wsrep_slave_FK_checks 변수는 이 변수를 위해 더 이상 사용되지 않음.
wsrep_applier_threads Percona XtraDB Cluster 8.0.26-16부터 wsrep_slave_threads 변수는 더 이상 사용되지 않으며, wsrep_applier_threads 변수를 사용한다.
wsrep_applier_UK_checks Percona XtraDB Cluster 8.0.26-16부터 wsrep_slave_UK_checks 변수는 더 이상 사용되지 않으며, wsrep_applier_UK_checks 변수를 사용한다.
wsrep_auto_increment_control 클러스터 크기에 따라 자동 증가 시스템 변수의 자동 조정을 활성화.
l   auto_increment_increment : 연속적인 AUTO_INCREMENT 열 값 사이의 간격을 제어.
l   auto_increment_offse : AUTO_INCREMENT 열 값의 시작점을 결정.
wsrep_causal_reads 경우에 따라 소스가 복제본보다 더 빠르게 이벤트를 적용할 수 있으며, 이로 인해 소스와 복제본이 잠시 동안 동기화되지 않을 수 있다. 이 변수가 ON으로 설정되면 복제본은 다른 쿼리를 수행하기 전에 해당 이벤트가 적용될 때까지 기다린다. 이 변수를 활성화하면 대기 시간이 길어진다.
wsrep_certification_rules 클러스터에서 인증이 수행되는 방식을 제어하며, 특히 외래 키가 처리되는 방식에 영향을 미친다.
wsrep_certify_nonPK 기본 키가 없는 행에 대해 기본 키 자동 생성을 활성화.
wsrep_cluster_address 클러스터에 연결할 때 노드가 사용하는 백엔드 스키마, IP 주소, 포트 및 옵션을 정의. 이 변수는 활성 상태이고 클러스터의 구성원인 다른 노드의 주소를 하나 이상 지정해야 한다. 실제로는 가능한 모든 클러스터 노드의 전체 목록을 제공하는 것이 가장 좋다(필수는 아님).
wsrep_cluster_name 클러스터의 이름을 지정하며 모든 노드에서 동일해야 한다. 노드는 클러스터에 연결을 시도할 때 값을 확인한다. 이름이 일치하면 노드가 연결됩니다.
wsrep_data_home_dir  wsrep 공급자가 해당 파일(: grastate.dat)을 저장하는 디렉터리의 경로를 지정.
wsrep_dbug_option wsrep 공급자에게 전달할 DBUG 옵션을 정의.
wsrep_debug 데이터베이스 서버 및 wsrep-lib에 대한 디버그 수준 로깅을 활성화(기본적으로 --wsrep-debug 변수는 비활성화되어 있음). 이 변수는 문제를 진단하거나 버그를 제출할 때 사용할 수 있다.
wsrep_desync 노드가 흐름 제어에 참여해야 하는지 여부를 정의 (비활성화 기본값). 수신 대기열이 너무 커지면 노드가 흐름 제어에 참여한다. 이 변수를 활성화하면 노드에 대한 흐름 제어가 비활성화된다. 적용할 수 없는 쓰기 세트를 계속 수신하고 수신 대기열은 계속 증가하며 노드는 계속해서 클러스터 뒤처지게 된다.
 
이를 다시 OFF로 전환하려면 비동기화 기간에 따라 IST 또는 SST가 필요하다. 이는 RSU TOI 중에 발생하는 클러스터 비동기화와 유사하다. 이 때문에 wsrep_desync를 오랜 기간 동안 활성화하거나 여러 노드에 대해 동시에 활성화하는 것은 좋은 생각이 아니다.
wsrep_dirty_reads 노드가 비작동 상태, 즉 기본 구성요소에 대한 연결이 끊어졌을 때 읽기 쿼리를 수락할지 여부를 정의. 기본적으로 이 변수는 비활성화되어 있으며 데이터가 올바른지 알 수 있는 방법이 없기 때문에 노드는 모든 쿼리를 거부한다. 이 변수를 활성화하면 노드는 읽기 쿼리(USE, SELECT, LOCK TABLE UNLOCK TABLES)를 허용하지만 작동하지 않는 노드에서 데이터베이스를 수정하거나 업데이트하는 모든 명령은 계속 거부된다(DDL DML 문 포함). INSERT, DELETE, UPDATE ).
교착 상태 오류를 방지하려면 wsrep_dirty_reads를 활성화한 경우 wsrep_sync_wait 변수를 0으로 설정한다.
Percona XtraDB Cluster 8.0.26-16부터 set_var 힌트를 사용하여 변수를 업데이트할 수 있다.
wsrep_drupal_282555_workaround Drupal에 영향을 미치는 MySQL InnoDB 버그(Drupal 버그 #282555 MySQL 버그 #41984)에 대한 해결 방법을 활성화한다. 경우에 따라 AUTO_INCREMENT 열에 DEFAULT 값을 삽입할 때 중복 키 오류가 발생한다.
wsrep_forced_binlog_format 클라이언트 세션 binlog_format 변수 값에 관계없이 항상 유효한 바이너리 로그 형식을 정의한다.
l   ROW: Force row-based logging format
l   STATEMENT: Force statement-based logging format
l   MIXED: Force mixed logging format
l   NONE: Do not force the binary log format and use whatever is set by the binlog_format variable (default)
wsrep_ignore_apply_errors 오류에 대한 wsrep 적용자 동작 규칙을 정의.
wsrep_min_log_verbosity wsrep/Galera의 최소 로깅 상세 수준을 정의하고 log_error_verbosity 변수와 함께 작동. wsrep_min_log_verbosity log_error_verbosity와 동일한 값을 갖는다. log_error_verbosity wsrep_min_log_verbosity보다 큰 경우 wsrep/Galera의 실제 로그 상세 수준은 wsrep_min_log_verbosity 값보다 클 수 있다.
wsrep_load_data_splitting 노드가 대규모 LOAD DATA 트랜잭션을 분할해야 하는지 여부를 정의. 이 변수는 기본적으로 활성화됩니다. , LOAD DATA 명령은 10,000행 이하의 트랜잭션으로 분할된다. 이 변수를 비활성화하면 막대한 데이터 로드로 인해 충돌 발생 시 노드가 작업을 완전히 롤백하지 못할 수 있으며 커밋된 내용은 커밋된 상태로 유지된다.
wsrep_log_conflicts 노드가 충돌에 대한 추가 정보를 기록해야 하는지 여부를 정의. 기본적으로 이 변수는 비활성화되어 있으며 Percona XtraDB Cluster MySQL의 표준 로깅 기능을 사용. 이 변수를 활성화하면 충돌이 발생한 테이블과 스키마뿐만 아니라 충돌을 일으킨 키의 실제 값도 기록된다.
wsrep_max_ws_rows 각 쓰기 세트에 포함될 수 있는 최대 행 수를 정의. 기본적으로 쓰기 세트의 최대 행 수에는 제한이 없다. 허용되는 최대 값은 1048576이다.
wsrep_max_ws_size 최대 쓰기 세트 크기(바이트)를 정의. 지정된 값보다 큰 값은 거부된다. 1024와 기본값 2147483647 사이의 값으로 설정할 수 있다.
wsrep_mode Percona XtraDB Cluster 8.0.31에서 구현되었으며, 지정된 값에 따라 노드 동작을 정의. 값은 기본적으로 비어 있거나 비활성화되어 있음.
l   비어 있음 - 노드 동작을 변경하지 않음.
l   IGNORE_NATIVE_REPLICATION_FILTER_RULES - 기본 복제 필터 규칙을 무시하도록 wsrep 동작을 변경.
wsrep_node_address 노드의 네트워크 주소를 지정. 기본적으로 이 변수는 첫 번째 네트워크 인터페이스(일반적으로 eth0 또는 enp2s0) IP 주소와 기본 포트(4567)로 설정.
wsrep_node_incoming_address 노드가 클라이언트 연결을 예상하는 네트워크 주소를 지정. 기본적으로 wsrep_node_address IP 주소와 포트 번호 3306을 사용.
wsrep_node_name 노드의 고유 이름을 정의. 기본값은 호스트 이름. 이 변수의 값을 클러스터에서 지정된 노드를 식별하는 수단으로 사용할 수 있다.
wsrep_notify_cmd 클러스터 멤버십이나 로컬 노드 상태가 변경될 때마다 노드가 실행해야 하는 알림 명령을 지정. 이는 경고를 보내거나 로드 밸런서를 재구성하는 데 사용될 수 있다.
wsrep_on 노드의 현재 세션 트랜잭션 변경 사항이 클러스터에 복제되는지 정의. 세션을 OFF로 설정하면 해당 세션에 트랜잭션 변경 사항이 복제되지 않는다. 설정으로 인해 노드가 클러스터를 떠나지 않으며 노드가 다른 노드와 통신한다.
wsrep_OSU_method 노드가 DDL 문을 복제하는 데 사용하는 온라인 스키마 업그레이드 방법을 정의.
wsrep_provider Galera 라이브러리의 경로를 지정. 경로를 지정하지 않거나 값이 유효하지 않은 경우 노드는 MySQL의 독립 실행형 인스턴스로 작동.
wsrep_provider_options wsrep_provider옵션 인덱스에 설명된 복제 공급자에 대한 선택적 설정을 지정. 이러한 옵션은 복제 중에 다양한 상황이 처리되는 방식에 영향을 준다.
wsrep_recover 로그에서 GTID를 구문 분석하여 충돌 후 데이터베이스 상태를 복구. GTID가 발견되면 서버의 초기 위치로 지정된다.
wsrep_reject_queries 노드가 클라이언트의 쿼리를 거부해야 하는지 여부를 정의. 쿼리 거부는 업그레이드 중에 쿼리를 수락하지 않고 노드를 유지하고 쓰기 세트를 적용하려는 경우 유용할 수 있다.
wsrep_replicate_myisam MyISAM 테이블에 대한 DML 문을 복제해야 하는지 여부를 정의. 기본적으로 비활성화되어 있다.
전역 수준에서 wsrep_replicate_myisam은 시작 중에만 설정할 수 있다. 세션 수준에서는 런타임 중에도 변경할 수 있다.
wsrep_restart_replica Percona XtraDB Cluster 8.0.26-16부터 더 이상 사용되지 않음.
wsrep_restart_slave Percona XtraDB Cluster 8.0.26-16부터 더 이상 사용되지 않음.
wsrep_retry_autocommit 인증 오류가 발생할 경우 클러스터에서 자동 커밋 트랜잭션을 다시 시도할 횟수를 지정. 충돌이 있는 경우 클러스터 노드는 클라이언트에 오류를 반환하지 않고 명령문을 다시 시도하여 다음에 통과할 수 있기를 바라면 안전하다. 이는 복제 충돌로 인해 발생할 수 있는 교착 상태 오류를 방지하기 위해 자동 커밋을 사용하는 애플리케이션을 돕는 데 유용할 수 있다. 이 변수가 0으로 설정되면 자동 커밋 트랜잭션이 다시 시도되지 않는다.
wsrep_RSU_commit_timeout RSU를 시작하기 전에 활성 연결이 COMMIT 작업을 완료할 수 있도록 허용하는 시간 제한(마이크로초)을 지정. RSU를 실행하는 동안 사용자가 노드를 격리했고 노드에서 실행 중인 활성 트래픽이 없을 것으로 예상된다. RSU는 이를 확인하고 RSU를 시작하기 전에 COMMIT 상태의 활성 연결을 기다린다. 기본적으로 이 검사의 시간 제한은 5밀리초이지만 어떤 경우에는 COMMIT가 더 오래 걸린다. 이 변수는 제한 시간을 설정하며 (5밀리초, 365) 범위의 값을 허용한다. 값은 마이크로초 단위로 설정된다. 변수의 단위는 마이크로초 단위이므로 그에 맞춰 설정한다.
wsrep_slave_FK_checks Percona XtraDB Cluster 8.0.26-16부터 이 변수는 더 이상 사용되지 않음.
wsrep_slave_threads  Percona XtraDB Cluster 8.0.26-16부터 이 변수는 더 이상 사용되지 않음.
wsrep_slave_UK_checks  Percona XtraDB Cluster 8.0.26-16부터 이 변수는 더 이상 사용되지 않음.
wsrep_SR_store 스트리밍 복제 조각을 위한 스토리지를 정의. 사용 가능한 값은 기본값인 table과 변수를 비활성화하는 none이다.
wsrep_sst_allowed_methods wsrep_sst_method 변수에 대해 서버에서 허용하는 SST 방법을 제한. 기본값은 xtrabackup-v2이다.
wsrep_sst_donor 현재 노드가 SST IST에 대한 기증자로 선호해야 하는 노드 목록(wsrep_node_name 값 사용)을 지정.
wsrep_sst_method SST(상태 스냅샷 전송)에 대한 방법 또는 스크립트를 정의.
wsrep_sst_receive_address 기증자 노드가 상태 전송을 보내야 하는 네트워크 주소를 지정. 기본적으로 이 변수는 AUTO로 설정되며 wsrep_node_address IP 주소가 사용됩니다.
wsrep_start_position 노드의 시작 위치를 UUID:seqno로 지정. 이 변수에 대해 모든 노드가 동일한 값을 갖도록 설정하면 상태 전송 없이 클러스터를 설정할 수 있다.
wsrep_sync_wait 특정 명령문에 대한 클러스터 전체의 인과성 검사를 제어한다. 클러스터와 완전히 동기화된 노드에서 명령문이 실행되는지 확인한다.
Percona XtraDB Cluster 8.0.26-16부터 set_var 힌트를 사용하여 변수를 업데이트할 수 있다.
wsrep_trx_fragment_size 스트리밍 복제 조각 크기를 정의한다. 이 변수는 wsrep_trx_fragment_unit에 의해 정의된 값으로 측정된다. 최소값은 0이고 최대값은 2147483647이다.
Percona XtraDB Cluster for MySQL 8.0.26-16부터 set_var 힌트를 사용하여 변수를 업데이트할 수 있다.
wsrep_trx_fragment_unit wsrep_trx_fragment_size에 대한 측정 유형을 정의. 가능한 값은 바이트, , 명령문이다. Percona XtraDB Cluster for MySQL 8.0.26-16부터 set_var 힌트를 사용하여 변수를 업데이트할 수 있다.

 

 

[참고자료]

l   About Percona XtraDB Cluster : https://docs.percona.com/percona-xtradb-cluster/8.0/intro.html

 

 

 

2023-09-27 / Sungwook Kang / http://sungwookkang.com

 

MySQL Percona, XtraDB, XtraBackup, XtraDB Cluster, 퍼코나 클러스터, 퍼코나 데이터베이스

[Linux] 리눅스 디스크 용량 확인

 

l  Linux

 

리눅스 시스템을 운영하다 보면 디스크 공간 사용률이 100% 되어 서비스에 장애가 발생하는 경우가 있다. 일반적으로 실수하는 부분이 로그 파일을 방치하여 어느 순간 디스크 공간을 모두 소진하였거나, 데이터베이스 서버의 경우 데이터를 백업할 순간적으로 디스크를 모두 소진하여 발생할 때가 있다. 주기적인 시스템 모니터링으로 디스크 공간에 대한 사용률을 확인하여 사전에 이러한 부분을 방지할 있도록 하는 것이 중요하다. 그렇다면 어느 디렉터리에서 많은 공간을 사용하는지를 확인하는 방법은 무엇일까? 이번 포스트에서는 리눅스에서 디스크 사용량을 확인하는 방법에 대해서 알아본다.

 

리눅스에서 디스크 공간을 확인하는 명령어는 df (Disk Free)이다. 마운트 되어있는 모든 디스크의 사용량을 보여준다. 1K-blocks 항목이 디스크에 할당된 용량이며, Used 실제 사용량, Available 사용 가능한 공간, Use% 디스크 공간에서 사용률을 퍼센트로 나타낸다.

df

 

 

디스크 용량 사용량이 킬로바이트로 표시되기 때문에 용량이 경우 자릿수로 인해 쉽게 사용자가 확인하기 어렵다. 이때 -h 옵션을 사용하면 사용자 친화적으로 확인할 있다.

df -h

 

 

 

디스크가 아닌 디렉터리별 사용량 확인은 du명령을 사용한다. du disk usage 약자로 하위 디렉토리를 포함한 디스크 사용량을 보여준다.

du -h /var/lib/

 

 

 

하위 디렉터리가 많을 경우, 어느 디렉토리에서 사용량이 많은지 한번에 확인하기 힘들다. 따라서 사용량 기준으로 정렬한 결과를 표시할 때에는 sort -n 옵션을 사용한다.

du -h /var/log | sort -n

 

 

 

그런데 나타난 결과를 보면 정렬 상태가 이상하다. 단위가 바뀌면서 88K 4.1G 보다 것처럼 정렬되었다. 따라서 정렬을 때에는 -h 옵션을 사용하지 않도록 한다.

du  /var/log | sort -n

 

 

 

 

 

 

2023-09-26 / Sungwook Kang / https://sungwookkang.com

 

 

리눅스, Linux, 디스크 사용량, 리눅스 디스크, 디스크 usage, disk usage

'Linux' 카테고리의 다른 글

[Linux] 쉘(shell) 스크립트 기초  (0) 2023.09.05
[Linux] Alpine Linux (알파인 리눅스) 는 무엇일까?  (0) 2023.09.04
[Linux] OOM(Out of Memory)란?  (0) 2023.08.30
CnetOS 방화벽 설정  (0) 2015.07.22
CentOS 파일 압축/풀기  (0) 2015.07.22

[MySQL] MySQL 5.7에서 8.0으로 업그레이드시 변경되는 설정 옵션 정리

 

l  Version : MySQL 8.X

 

MySQL 5.7 버전과 MySQL 8.0 변화가 있기 때문에, 업그레이드하기 전에 많은 주의가 필요하다. 현재 운영중인 MySQL 5.7X 버전에서 MySQL 8.0.X 업그레이드 프로젝트를 진행하게 되어, 업그레이드시 주의해야 사항 변경되는 사항을 정리해본다.

 

[Data Dictionary]

MySQL Server 8.0에는 트랜잭션 테이블의 데이터베이스 개체에 대한 정보가 포함된 전역 Data Dictionary 통합되어 있다. 이전 MySQL 시리즈에서는 Data Dictionary 메타데이터 파일과 InnoDB 시스템 테이블에 저장되었다. 이전에는 innodb_read_only 시스템 변수를 활성화하면 InnoDB 스토리지 엔진에 대해서만 테이블을 생성하고 삭제할 없었다. MySQL 8.0부터 innodb_read_only 활성화하면 모든 스토리지 엔진에 대해 이러한 작업이 방지된다. 모든 스토리지 엔진에 대한 테이블 생성 삭제 작업은 mysql 시스템 데이터베이스의 데이터 Dictionary 테이블을 수정하지만 해당 테이블은 InnoDB 스토리지 엔진을 사용하며 innodb_read_only 활성화되면 수정할 없다. Data Dictionary 테이블을 수정해야 하는 다른 테이블 작업에도 동일한 원칙이 적용된다.

 

이전에는 STATISTICS 테이블 통계에 대한 INFORMATION_SCHEMA 쿼리가 스토리지 엔진에서 직접 통계를 검색했다면, MySQL 8.0부터는 캐시된 테이블 통계가 기본적으로 사용된다. information_schema_stats_expiry 시스템 변수는 캐시된 테이블 통계가 만료되는 기간을 정의한다. 기본값은 86400(24시간)이며, 테이블에 대해 수동으로 캐시된 값을 업데이트하려면 ANALYZE TABLE 사용할 있다. 캐시된 통계가 없거나 통계가 만료된 경우에는 테이블 통계 열을 쿼리할 스토리지 엔진에서 통계가 검색된다. 항상 스토리지 엔진에서 직접 최신 통계를 검색하려면 information_schema_stats_expiry 0으로 설정한다.

 

INFORMATION_SCHEMA 테이블은 Data Dictionary 테이블에 대한 뷰로, 이를 통해 최적화 프로그램은 해당 기본 테이블에 대한 인덱스를 사용할 있다. 결과적으로 최적화 프로그램 선택에 따라 INFORMATION_SCHEMA 쿼리 결과의 순서가 이전 결과와 다를 있다. 쿼리 결과에 특정 순서 특성이 있어야 하는 경우 ORDER BY 절을 포함한다.

 

INFORMATION_SCHEMA 테이블에 대한 쿼리는 이전 MySQL 시리즈와는 다른 문자로 이름을 반환할 있다. 애플리케이션은 결과 세트에 대해 이름이 대소문자를 구분하지 않도록 테스트해야 한다. 이러한 부분을 방지하려면 필수 문자 대소문자로 이름을 반환하도록 별칭을 사용한다.

 

mysqldump mysqlpump 명령줄에 명시적으로 이름이 지정된 경우에도 이상 INFORMATION_SCHEMA 데이터베이스를 덤프하지 않는다. Data Dictionary 대한 자세한 내용은 아래 링크를 참고한다.

l  Data Dictionary Usage Differences : https://dev.mysql.com/doc/refman/8.0/en/data-dictionary-usage-differences.html

 

 

[caching_sha2_password 인증 플러그인 변경]

caching_sha2_password sha256_password 인증 플러그인은 mysql_native_password 플러그인보다 안전한 비밀번호 암호화를 제공하고, caching_sha2_password sha256_password보다 나은 성능을 제공한다. caching_sha2_password MySQL 8.0부터 기본 인증 플러그인으로 사용된다. 변경 사항은 서버와 libmysqlclient 클라이언트 라이브러리 모두에 영향을 미친다.

서버의 경우 default_authentication_plugin 시스템 변수의 기본값이 mysql_native_password에서 caching_sha2_password 변경된다. 변경 사항은 MySQL 8.0 이상을 설치하거나 업그레이드한 후에 생성된 계정에만 적용된다. 업그레이드된 설치에 이미 존재하는 계정의 경우 해당 인증 플러그인은 변경되지 않은 상태로 유지된다. 기존 사용자를 caching_sha2_password 전환하려면  ALTER USER 문을 사용하여 전환할 있다.

ALTER USER user IDENTIFIED WITH caching_sha2_password BY 'password';

 

MySQL 8.0 이상으로 업그레이드한 호환성 문제가 발생하는 경우 이전 기본 인증 플러그인으로 되돌리도록 서버를 재구성할 있다. 설정을 사용하면 기본의 애플리케이션들이 이전의 패스워드 방식을 사용할 있다. 하지만 방법은 보안에 취약하기 때문에 영구적으로 사용할 없다.

[mysqld]
default_authentication_plugin=mysql_native_password

 

자세한 내용은 아래 링크를 참고한다.

l  Pluggable Authentication : https://dev.mysql.com/doc/refman/8.0/en/pluggable-authentication.html#pluggable-authentication-compatibility

 

 

[caching_sha2_password and the root Administrative Account]

MySQL 8.0으로 업그레이드하는 경우 'root'@'localhost' 관리 계정용 플러그인을 포함하여 기존 인증 플러그인 계정은 변경되지 않은 상태로 유지된다. 새로운 MySQL 8.0 설치의 경우 데이터 디렉터리를 초기화하면 'root'@'localhost' 계정이 생성되고 해당 계정은 기본적으로 caching_sha2_password 사용한다. 따라서 데이터 디렉터리 초기화 서버에 연결하려면 caching_sha2_password 지원하는 클라이언트나 커넥터를 사용해야 한다. 신규 설치 루트 계정이 mysql_native_password 사용하도록 하려면 MySQL 설치하고 평소와 같이 데이터 디렉토리를 초기화한다. 그런 다음 루트로 서버에 연결하고 다음과 같이 ALTER USER 사용하여 계정 인증 플러그인과 비밀번호를 변경한다.

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';

 

 

[caching_sha2_password and Replication]

모든 서버가 MySQL 8.0.4 이상으로 업그레이드된 복제 시나리오에서 원본 서버에 대한 복제본 연결은 caching_sha2_password 인증하는 계정을 사용할 있다. 이러한 연결의 경우 caching_sha2_password 인증하는 계정을 사용하는 다른 클라이언트와 동일한 요구 사항이 적용된다. , 보안 연결 또는 RSA 기반 암호 교환을 사용한다.

 

 

[Configuration Changes]

MySQL 스토리지 엔진은 자체 파티셔닝 핸들러 제공을 담당하며 MySQL 서버는 이상 일반 파티셔닝 지원을 제공하지 않는다. InnoDB NDB MySQL 8.0에서 지원되는 기본 파티셔닝 핸들러를 제공하는 유일한 스토리지 엔진이다. 다른 스토리지 엔진을 사용하는 파티션된 테이블은 서버를 업그레이드하기 전에 InnoDB 또는 NDB 변환하거나 파티셔닝을 제거하기 위해 변경해야 한다. 그렇지 않으면 나중에 사용할 없다.

 

collation_server collation_database 시스템 변수의 기본값이 latin1_swedish_ci에서 utf8mb4_0900_ai_ci 변경되었다. 결과적으로 개체의 기본 문자 집합과 데이터 정렬은 명시적인 문자 집합과 데이터 정렬을 지정하지 않는 이전과 다르다. 여기에는 테이블, , 저장된 프로그램 데이터베이스와 안에 있는 개체가 포함된다. 이전 기본값이 사용되었다고 가정하면 이를 유지하기 위한 방법은 my.cnf 파일에서 아래 명령을 사용하여 서버를 재시작 한다.

[mysqld]
character_set_server=latin1
collation_server=latin1_swedish_ci

 

복제된 설정에서 MySQL 5.7에서 8.0으로 업그레이드할 , 업그레이드하기 전에 기본 문자 집합을 MySQL 5.7에서 사용된 문자 집합으로 다시 변경하는 것이 좋다. 업그레이드가 완료되면 기본 문자 집합을 utf8mb4 변경할 있다. 또한 MySQL 8.0 MySQL 5.7 수행하지 않는 특정 문자 집합의 허용된 문자에 대한 검사를 시행한다는 점을 기억해야 한다. 이는 알려진 문제로 업그레이드를 시도하기 전에 사용 중인 문자 세트에 대해 정의되지 않은 문자가 주석에 포함되어 있는지 확인해야 한다.  아래 가지 방법 하나로 문제를 해결할 있다.

l  문제의 문자를 포함하는 문자 세트로 문자 세트를 변경

l  문제가 되는 문자를 제거

 

MySQL 8.0.11부터 서버 초기화 사용된 설정과 다른 lower_case_table_names 설정으로 서버를 시작할 없다. 다양한 데이터 사전 테이블 필드에서 사용되는 데이터 정렬은 서버가 초기화될 정의된 lower_case_table_names 설정을 기반으로 하고, 다른 설정으로 서버를 다시 시작하면 식별자 정렬 비교 방법과 관련하여 불일치가 발생하므로 제한이 필요하다.

 

 

[Server Changes]

MySQL 8.0.11에서는 사용자 계정의 비권한 특성을 수정하기 위한 GRANT 사용, NO_AUTO_CREATE_USER SQL 모드, PASSWORD() 함수, old_passwords 시스템 변수 계정 관리와 관련된 이상 사용되지 않는 여러 기능이 제거되었다. 이러한 제거된 기능을 참조하는 명령문을 MySQL 5.7에서 8.0으로 복제하면 복제 오류가 발생할 있다. 제거된 기능을 사용하는 애플리케이션은 이를 방지하도록 수정되어야 하며, 가능하면 MySQL 8.0에서 제거된 기능에 설명된 대로 대안을 사용해야 한다.

 

MySQL 8.0에서 시작 실패를 방지하려면 MySQL 옵션 파일의 sql_mode 시스템 변수 설정에서 NO_AUTO_CREATE_USER 인스턴스를 제거한다. 저장된 프로그램 정의에 NO_AUTO_CREATE_USER SQL 모드를 포함하는 덤프 파일을 MySQL 8.0 서버로 로드하면 오류가 발생한다. MySQL 5.7.24 MySQL 8.0.13부터 mysqldump 저장된 프로그램 정의에서 NO_AUTO_CREATE_USER 제거한다. 이전 버전의 mysqldump 생성된 덤프 파일을 수동으로 수정하여 NO_AUTO_CREATE_USER 인스턴스를 제거해야 한다.

 

MySQL 8.0.11에서는 이상 사용되지 않는 호환성 SQL 모드인 DB2, MAXDB, MSSQL, MYSQL323, MYSQL40, ORACLE, POSTGRESQL, NO_FIELD_OPTIONS, NO_KEY_OPTIONS, NO_TABLE_OPTIONS 제거되었다. 이상 sql_mode 시스템 변수에 할당하거나 mysqldump -- Compatible 옵션에 허용되는 값으로 사용할 없다. MAXDB 제거된다는 것은 CREATE TABLE 또는 ALTER TABLE 대한 TIMESTAMP 데이터 유형이 이상 DATETIME으로 처리되지 않음을 의미한다.

 

제거된 SQL 모드를 참조하는 명령문을 MySQL 5.7에서 8.0으로 복제하면 복제 오류가 발생할 있다. 여기에는 현재 sql_mode 값에 제거된 모드가 포함되어 있는 동안 실행되는 저장 프로그램(저장 프로시저 함수, 트리거 이벤트) 대한 CREATE 문의 복제가 포함된다.

 

MySQL 8.0.3부터 공간 데이터 유형은 열에 저장된 값에 대한 공간 참조 시스템(SRS) 명시적으로 나타내기 위해 SRID 속성을 허용한다. 명시적인 SRID 특성이 있는 공간 열은 SRID 제한되며 해당열은 해당 ID 가진 값만 사용하며 열의 SPATIAL 인덱스는 최적화 프로그램에서 사용하게 된다. 최적화 프로그램은 SRID 속성이 없는 공간 열의 SPATIAL 인덱스를 무시한다. 최적화 프로그램이 SRID 제한되지 않은 공간 열의 SPATIAL 인덱스를 고려하도록 하려면 해당 열을 각각 수정해야 한다.

 

정확한 연산을 수행하는 함수에 대해 ST_ 접두사를 구현하거나 최소 경계 사각형을 기반으로 연산을 수행하는 함수에 대해 MBR 접두사를 구현하는 공간 함수 네임스페이스 변경으로 인해 MySQL 8.0.0에서 여러 공간 함수가 제거되었다. 생성된 정의에서 제거된 공간 함수를 사용하면 업그레이드가 실패할 있다. 업그레이드하기 전에 제거된 공간 함수에 대해 mysqlcheck --check-upgrade 실행하고 찾은 모든 것을 ST_ 또는 MBR이라는 이름의 대체 항목으로 변경해야 한다. 제거된 함수목록은 아래 링크를 참고한다.

l   Features Removed in MySQL 8.0 : https://dev.mysql.com/doc/refman/8.0/en/mysql-nutshell.html#mysql-nutshell-removals

 

BACKUP_ADMIN 권한은 MySQL 8.0.3 이상으로 전체 업그레이드를 수행할 RELOAD 권한이 있는 사용자에게 자동으로 부여된다.

MySQL 8.0.13부터는 기반 또는 혼합 복제 모드와 명령문 기반 복제 모드의 임시 테이블 처리 방식 차이로 인해 런타임 바이너리 로깅 형식 전환에 대한 새로운 제한 사항이 있다. 세션에 열려 있는 임시 테이블이 있으면 SET @@SESSION.binlog_format 사용할 없다. 복제 채널에 열려 있는 임시 테이블이 있으면 SET @@global.binlog_format SET @@persist.binlog_format 사용할 없다. PERSIST 달리 PERSIST_ONLY 런타임 전역 시스템 변수 값을 수정하지 않기 때문에 복제 채널에 열려 있는 임시 테이블이 있는 경우 SET @@persist_only.binlog_format 허용된다. 복제 채널 적용자가 실행 중인 경우 SET @@global.binlog_format SET @@persist.binlog_format 사용할 없다. 이는 적용자가 다시 시작될 때만 변경 사항이 복제 채널에 적용되며 이때 복제 채널에 열려 있는 임시 테이블이 있을 있기 때문이다. 동작은 이전보다 제한적이며 복제 채널 적용자가 실행 중인 경우 SET @@persist_only.binlog_format 허용된다.

 

MySQL 8.0.27부터 Internal_tmp_mem_storage_engine 대한 세션 설정을 구성하려면 SESSION_VARIABLES_ADMIN 또는 SYSTEM_VARIABLES_ADMIN 권한이 필요하다.

 

MySQL 8.0.27부터 복제 플러그인은 복제 작업이 진행되는 동안 donor 역할을 하는 MySQL 인스턴스에서 동시 DDL 작업을 허용한다. 이전에는 복제 작업 중에 백업 잠금이 유지되어 donor 대한 동시 DDL 방지되었다. 복제 작업 중에 donor에서 동시 DDL 차단하는 이전 동작으로 되돌리려면 clone_block_ddl 변수를 활성화한다. 자세한 내용은 아래 링크를 참고한다.

l   Cloning and Concurrent DDL : https://dev.mysql.com/doc/refman/8.0/en/clone-plugin-concurrent-ddl.html

 

MySQL 8.0.30부터 시작 log_error_services 값에 나열된 오류 로그 구성 요소는 MySQL Server 시작 시퀀스 초기에 암시적으로 로드된다. 이전에 INSTALL COMPONENT 사용하여 로드 가능한 오류 로그 구성 요소를 설치했고 시작 읽는 log_error_services 설정(: 옵션 파일에서) 해당 구성 요소를 나열한 경우 시작 경고가 발생하지 않도록 구성을 업데이트해야 한다. 자세한 내용은 아래 링크를 참고한다.

l   Error Log Configuration Methods : https://dev.mysql.com/doc/refman/8.0/en/error-log-configuration.html#error-log-configuration-methods

 

 

[InnoDB Changes]

InnoDB 시스템 테이블을 기반으로 하는 INFORMATION_SCHEMA 뷰는 Data Dictionary 테이블의 내부 시스템 뷰로 대체되었다. 그리고 영향을 받은 InnoDB INFORMATION_SCHEMA 뷰의 이름이 변경되었다. MySQL 8.0.3 이상으로 업그레이드한 이전 InnoDB INFORMATION_SCHEMA 이름을 참조하는 스크립트가 있다면 새로운 이름으로 업데이트 해야 한다.

Old Name New Name
INNODB_SYS_COLUMNS INNODB_COLUMNS
INNODB_SYS_DATAFILES INNODB_DATAFILES
INNODB_SYS_FIELDS INNODB_FIELDS
INNODB_SYS_FOREIGN INNODB_FOREIGN
INNODB_SYS_FOREIGN_COLS INNODB_FOREIGN_COLS
INNODB_SYS_INDEXES INNODB_INDEXES
INNODB_SYS_TABLES INNODB_TABLES
INNODB_SYS_TABLESPACES INNODB_TABLESPACES
INNODB_SYS_TABLESTATS INNODB_TABLESTATS
INNODB_SYS_VIRTUAL INNODB_VIRTUAL

 

MySQL 함께 번들로 제공되는 zlib 라이브러리 버전이 버전 1.2.3에서 버전 1.2.11 상향되었다. zlib 1.2.11 zlib 압축Bound() 함수는 zlib 버전 1.2.3에서보다 주어진 바이트 길이를 압축하는 필요한 버퍼 크기의 약간 높은 추정치를 반환한다. CompressBound() 함수는 압축된 InnoDB 테이블을 생성하거나 압축된 InnoDB 테이블에 행을 삽입 업데이트할 허용되는 최대 크기를 결정하는 InnoDB 함수에 의해 호출된다. 결과적으로 이전 릴리스에서 성공했던 최대 크기에 매우 가까운 크기를 사용하는 CREATE TABLE ... ROW_FORMAT=COMPRESSED, INSERT UPDATE 작업이 이제 실패할 있다. 문제를 방지하려면 업그레이드하기 전에 MySQL 8.0 테스트 인스턴스에서 행이 있는 압축된 InnoDB 테이블에 대해 CREATE TABLE 문을 테스트해야 한다.

 

--innodb-directories 기능이 도입되면서 절대 경로 또는 데이터 디렉터리 외부 위치로 생성된 테이블당 파일 일반 테이블스페이스 파일의 위치가 innodb_directories 인수 값에 추가되어야 한다. 그렇지 않으면 InnoDB 복구 중에 이러한 파일을 찾을 없어 오류가 발생한다. 테이블스페이스 파일 위치를 보려면 정보 스키마 FILES 테이블을 쿼리한단.

SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES \G

 

실행 취소 로그는 이상 시스템 테이블스페이스에 상주하지 않는다. MySQL 8.0에서 실행 취소 로그는 기본적으로 개의 실행 취소 테이블스페이스에 있다. 자세한 내용은 아래 링크를 참고한다.

l   Undo Tablespaces : https://dev.mysql.com/doc/refman/8.0/en/innodb-undo-tablespaces.html

 

MySQL 5.7에서 MySQL 8.0으로 업그레이드하면 MySQL 5.7 인스턴스에 존재하는 모든 실행 취소 테이블스페이스가 제거되고 개의 새로운 기본 실행 취소 테이블스페이스로 대체된다. 기본 실행 취소 테이블스페이스는 innodb_undo_directory 변수에 의해 정의된 위치에 생성된다. innodb_undo_directory 변수가 정의되지 않은 경우 데이터 디렉터리에 실행 취소 테이블스페이스가 생성된다. MySQL 5.7에서 MySQL 8.0으로 업그레이드하려면 MySQL 5.7 인스턴스의 실행 취소 테이블스페이스가 비어 있고 안전하게 제거될 있도록 느린 종료가 필요하다. 이전 MySQL 8.0 릴리스에서 MySQL 8.0.14 이상으로 업그레이드하는 경우 innodb_undo_tablespaces 설정이 2보다 결과로 업그레이드 인스턴스에 존재하는 실행 취소 테이블스페이스는 사용자 정의 실행 취소 테이블스페이스로 처리된다. 업그레이드 각각 ALTER UNDO TABLESPACE DROP UNDO TABLESPACE 구문을 사용하여 삭제되었다. MySQL 8.0 릴리스 시리즈 내에서 업그레이드할 항상 느린 종료가 필요한 것은 아니며, 이는 기존 실행 취소 테이블스페이스에 실행 취소 로그가 포함될 있음을 의미한다. 따라서 기존 실행 취소 테이블스페이스는 업그레이드 프로세스에 의해 제거되지 않는다.

 

MySQL 8.0.17부터 CREATE TABLESPACE ... ADD DATAFILE 절은 순환 디렉터리 참조를 허용하지 않는다. 예를 들어, 다음 명령문의 순환 디렉터리 참조(/../) 허용되지 않는다. 제한사항에 대한 예외는 Linux에서 존재하며, 이전 디렉토리가 심볼릭 링크인 경우 순환 디렉토리 참조가 허용된다. 예를 들어, 예의 데이터 파일 경로는 any_directory 심볼릭 링크인 경우 허용된다. (데이터 파일 경로가 '../' 시작하는 것은 여전히 허용된다.) 업그레이드 문제를 방지하려면 MySQL 8.0.17 이상으로 업그레이드하기 전에 테이블스페이스 데이터 파일 경로에서 순환 디렉터리 참조를 제거하고, 테이블스페이스 경로를 검사하려면 정보 스키마 INNODB_DATAFILES 테이블을 쿼리한다.

 

MySQL 8.0.14 도입된 회귀로 인해 분할된 테이블이 있고 lower_case_table_names=1 설정된 인스턴스의 경우 MySQL 5.7 또는 MySQL 8.0.14 이전의 MySQL 8.0 릴리스에서 MySQL 8.0.16으로의 대소문자 불일치 문제로 인해 시스템 전체 업그레이드가 실패한다.

 

MySQL 테이블 파티션의 테이블스페이스 이름과 파일 이름을 구성할 구분 기호 문자열을 사용한다. 다음과 같이 " #p# " 구분 기호 문자열은 파티션 이름 앞에 오고 " #sp# " 구분 기호 문자열은 하위 파티션 이름 앞에 위치한다. MySQL 업그레이드 작업 아래 상황을 확인하고 수정한다.

l   소문자 구분 기호와 파티션 이름을 보장하기 위해 디스크와 Data Dirctionray 파일 이름을 파티션 한다.

l   이전 버그 수정으로 인해 발생한 관련 문제에 대한 데이터 사전의 파티션 메타데이터이다.

l   이전 버그 수정으로 인해 발생한 관련 문제에 대한 InnoDB 통계 데이터이다.

l   테이블스페이스 가져오기 작업 중에 디스크의 파티션 테이블스페이스 파일 이름을 확인하고 필요한 경우 소문자 구분 기호와 파티션 이름을 확인하기 위해 수정한다.

 

MySQL 8.0.21부터는 시작 또는 MySQL 5.7에서 업그레이드할 테이블스페이스 데이터 파일이 없는 디렉터리에 있는 것으로 발견되면 오류 로그에 경고가 기록된다. 알려진 디렉터리는 datadir, innodb_data_home_dir innodb_directories 변수에 의해 정의된 디렉터리이다. 디렉터리를 알리려면 해당 디렉터리를 innodb_directories 설정에 추가한다. 디렉터리를 알리면 복구 중에 데이터 파일을 찾을 있다. 자세한 내용은 아래 링크를 참고한다.

l   Tablespace Discovery During Crash Recovery : https://dev.mysql.com/doc/refman/8.0/en/innodb-recovery.html#innodb-recovery-tablespace-discovery

 

MySQL 8.0.30부터 innodb_redo_log_capacity 변수는 리두 로그 파일이 차지하는 디스크 공간의 양을 제어한다. 변경으로 인해 리두 로그 파일의 기본 수와 해당 위치도 변경되었다. MySQL 8.0.30부터 InnoDB 데이터 디렉토리의 #innodb_redo 디렉토리에 32개의 리두 로그 파일을 유지한다. 이전에 InnoDB 기본적으로 데이터 디렉터리에 개의 리두 로그 파일을 생성했으며, 리두 로그 파일의 수와 크기는 innodb_log_files_in_group innodb_log_file_size 변수에 의해 제어되었다. 변수는 이제 이상 사용되지 않는다.

 

innodb_redo_log_capacity 설정이 정의되면 innodb_log_files_in_group innodb_log_file_size 설정이 무시된다. 그렇지 않으면 해당 설정은 innodb_redo_log_capacity 설정(innodb_log_files_in_group * innodb_log_file_size = innodb_redo_log_capacity) 계산하는 사용된다. 해당 변수가 설정되지 않은 경우 리두 로그 용량은 innodb_redo_log_capacity 기본값인 104857600바이트(100MB) 설정된다.

 

MySQL 5.7.35 이전에는 중복 또는 압축 형식이 있는 테이블의 인덱스에 대한 크기 제한이 없었다. MySQL 5.7.35부터 제한은 767바이트이며, 5.7.35 이전의 MySQL 버전에서 MySQL 8.0으로 업그레이드하면 액세스할 없는 테이블이 생성될 있다. 중복 또는 압축 형식의 테이블에 767바이트보다 인덱스가 있는 경우 MySQL 8.0으로 업그레이드하기 전에 인덱스를 삭제하고 다시 생성해야 한다. 이러한 부분에 문제가 발생하면 아래와 같은 오류 메시지가 나타난다.

mysql> ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.

 

 

[SQL Changes]

MySQL 8.0.13부터 GROUP BY 절에 대해 이상 사용되지 않는 ASC 또는 DESC 한정자가 제거되었다. 이전에 GROUP BY 정렬에 의존했던 쿼리는 이전 MySQL 버전과 다른 결과를 생성할 있다. 지정된 정렬 순서를 생성하려면 ORDER BY 절을 사용해야 한다. GROUP BY 절에 ASC 또는 DESC 한정자를 사용하는 MySQL 8.0.12 이하에서 해당 형식의 쿼리를 사용한다면 수정해야 한다. 그렇지 않으면 MySQL 8.0.13 이상 복제본 서버로의 복제와 마찬가지로 MySQL 8.0.13 이상으로의 업그레이드가 실패할 있다.

 

MySQL 5.7에서는 예약되지 않았던 일부 키워드가 MySQL 8.0에서는 예약어로 사용된다. 이로 인해 이전에 식별자로 사용된 단어가 불법이 있다. 변경된 예약어는 아래 링크를 참고한다.

l   Keywords and Reserved Words : https://dev.mysql.com/doc/refman/8.0/en/keywords.html

 

업그레이드한 후에는 애플리케이션 코드에 지정된 최적화 프로그램 힌트를 테스트하여 원하는 최적화 전략을 달성하는 힌트가 여전히 필요한지 확인하는 것이 좋다. 최적화 기능 향상으로 인해 특정 최적화 기능 힌트가 불필요해질 있다. 어떤 경우에는 불필요한 최적화 힌트가 역효과를 낳을 수도 있다.

 

MySQL 5.7에서 CONSTRAINT 기호 없이 InnoDB 테이블에 대한 FOREIGN KEY 정의를 지정하거나 기호 없이 CONSTRAINT 키워드를 지정하면 InnoDB 생성된 제약 조건 이름을 사용하게 된다. 동작은 생성된 이름 대신 FOREIGN KEY index_name 값을 사용하는 InnoDB 사용하여 MySQL 8.0에서 변경되었다. 제약 조건 이름은 스키마(데이터베이스)별로 고유해야 하기 때문에 변경으로 인해 스키마별로 고유하지 않은 외래 인덱스 이름으로 인해 오류가 발생한다. 이러한 오류를 방지하기 위해 새로운 제약 조건 명명 동작은 MySQL 8.0.16에서 되돌려졌으며 InnoDB 다시 한번 생성된 제약 조건 이름을 사용한다. InnoDB와의 일관성을 위해 MySQL 8.0.16 이상 기반의 NDB 릴리스는 CONSTRAINT 기호 절이 지정되지 않거나 기호 없이 CONSTRAINT 키워드가 지정된 경우 생성된 제약 조건 이름을 사용한다. MySQL 5.7 이전 MySQL 8.0 릴리스를 기반으로 하는 NDB 릴리스는 FOREIGN KEY index_name 값을 사용했다. 이러한 변경 사항으로 인해 이전 외래 제약 조건 명명 동작에 의존하는 응용 프로그램에 대한 비호환성이 발생할 있다.

 

IFNULL() CASE() 같은 MySQL 흐름 제어 함수에 의한 시스템 변수 처리는 MySQL 8.0.22에서 변경되었다. 이제 시스템 변수 값은 상수가 아닌 동일한 문자 데이터 정렬의 값으로 처리된다. 이전에 성공했던 시스템 변수와 함께 이러한 함수를 사용하는 일부 쿼리는 나중에 잘못된 데이터 정렬 혼합으로 인해 거부될 있다. 이러한 경우 시스템 변수를 올바른 문자 집합과 데이터 정렬로 캐스팅해야 한다.

 

MySQL 8.0.28 이전 MySQL 8.0 릴리스에서 CONVERT() 함수가 때때로 BINARY 값을 이진이 아닌 문자 집합으로 잘못 변환하는 것을 허용하는 문제를 수정했다. 동작에 의존할 있는 응용 프로그램을 확인하고 필요한 경우 업그레이드 전에 수정해야 한다. 특히 CONVERT() 인덱스 생성 열에 대한 표현식의 일부로 사용된 경우 함수 동작의 변경으로 인해 MySQL 8.0.28 업그레이드한 인덱스가 손상될 있다. 이러한 문제를 예방하기 위해 아래 목록을 적용해 있다. 사전에 입력 데이터의 유효성을 검사할 없는 경우 MySQL 8.0.28 업그레이드를 수행할 때까지 인덱스를 다시 생성하거나 테이블을 다시 작성해서는 안된다.

l   업그레이드를 수행하기 전에 잘못된 입력 데이터를 수정

l   인덱스를 삭제한 다음 다시 생성

l   ALTER TABLE 테이블 FORCE 사용하여 테이블을 강제로 재구축

l   MySQL 소프트웨어를 업그레이드

 

 

[Changed Server Defaults]

아래표는 변경된 서버의  기본값이 요약되어 있다.

Option/Parameter Old Default New Default
Server changes    
character_set_server latin1 utf8mb4
collation_server latin1_swedish_ci utf8mb4_0900_ai_ci
explicit_defaults_for_timestamp OFF ON
optimizer_trace_max_mem_size 16KB 1MB
validate_password_check_user_name OFF ON
back_log -1 (autosize) changed from : back_log = 50 + (max_connections / 5) -1 (autosize) changed to : back_log = max_connections
max_allowed_packet 4194304 (4MB) 67108864 (64MB)
max_error_count 64 1024
event_scheduler OFF ON
table_open_cache 2000 4000
log_error_verbosity 3 (Notes) 2 (Warning)
local_infile ON (5.7) OFF
InnoDB changes    
innodb_undo_tablespaces 0 2
innodb_undo_log_truncate OFF ON
innodb_flush_method NULL fsync (Unix), unbuffered (Windows)
innodb_autoinc_lock_mode 1 (consecutive) 2 (interleaved)
innodb_flush_neighbors 1 (enable) 0 (disable)
innodb_max_dirty_pages_pct_lwm 0 (%) 10 (%)
innodb_max_dirty_pages_pct 75 (%) 90 (%)
Performance Schema changes    
performance-schema-instrument='wait/lock/metadata/sql/%=ON' OFF ON
performance-schema-instrument='memory/%=COUNTED' OFF COUNTED
performance-schema-consumer-events-transactions-current=ON OFF ON
performance-schema-consumer-events-transactions-history=ON OFF ON
performance-schema-instrument='transaction%=ON' OFF ON
Replication changes    
log_bin OFF ON
server_id 0 1
log-slave-updates OFF ON
expire_logs_days 0 30
master-info-repository FILE TABLE
relay-log-info-repository FILE TABLE
transaction-write-set-extraction OFF XXHASH64
slave_rows_search_algorithms INDEX_SCAN, TABLE_SCAN INDEX_SCAN, HASH_SCAN
slave_pending_jobs_size_max 16M 128M
gtid_executed_compression_period 1000 0
Group Replication changes    
group_replication_autorejoin_tries 0 3
group_replication_exit_state_action ABORT_SERVER READ_ONLY
group_replication_member_expel_timeout 0 5

 

 

[Server Defaults]

Character_set_server 시스템 변수 명령줄 옵션 --character-set-server 기본값이 latin1에서 utf8mb4 변경되었다. MySQL 5.7에서 MySQL 8.0으로 업그레이드해도 기존 데이터베이스 개체의 문자 집합은 변경되지 않는다. 하지만 Character_set_server 명시적으로 설정하지 않는 (이전 또는 값으로) 스키마는 기본적으로 utf8mb4 사용한다. 가능하면 utf8mb4 전환하는 것이 좋다.

 

collation_server 시스템 변수 명령줄 인수 --collation-server 기본값이 latin1_swedish_ci에서 utf8mb4_0900_ai_ci 변경되었다. 이는 서버의 기본 데이터 정렬, 문자 집합의 문자 순서이다. 문자 집합에는 가능한 데이터 정렬 목록이 제공되므로 데이터 정렬과 문자 집합 사이에는 링크가 있다. 5.7에서 8.0으로 업그레이드해도 기존 데이터베이스 개체의 데이터 정렬은 변경되지 않지만 개체에는 적용된다.

 

licit_defaults_for_timestamp 시스템 변수의 기본값이 OFF(MySQL 레거시 동작)에서 ON(SQL 표준 동작)으로 변경되었다. 옵션은 원래 5.6 도입되었으며 5.6 5.7에서는 OFF 되었다가 8.0에서 다시 ON으로 변경되었다.

 

Optimizer_trace_max_mem_size 시스템 변수의 기본값이 16KB에서 1MB 변경되었다. 이전 기본값으로 인해 모든 중요 쿼리에 대해 최적화 프로그램 추적이 잘렸다. 이러한 변경으로 인해 대부분의 쿼리에 유용한 최적화 프로그램 추적이 보장된다.

 

verify_password_check_user_name 시스템 변수의 기본값이 OFF에서 ON으로 변경되었다. , verify_password 플러그인이 활성화되면 기본적으로 현재 세션 사용자 이름과 일치하는 비밀번호를 거부한다.

 

back_log 시스템 변수의 자동 크기 조정 알고리즘이 변경되었다. 자동 크기(-1) 값은 이제 max_connections 값으로 설정됩니다. 이는 50 + (max_connections / 5) 계산된 값보다 크다. back_log 서버가 들어오는 요청을 따라잡을 없는 상황에서 들어오는 IP 연결 요청을 대기열에 추가한다. 최악의 경우, 동시에 재연결을 시도하는 클라이언트 수가 max_connections 경우 모두 버퍼링될 있으며 거부-재시도 루프가 방지된다.

 

max_allowed_packet 시스템 변수의 기본값이 4194304(4M)에서 67108864(64M) 변경되었다. 기본값의 주요 이점은 max_allowed_packet보다 삽입 또는 쿼리에 대한 오류를 수신할 가능성이 적다는 것이다. 이전 동작으로 되돌리려면 max_allowed_packet=4194304 설정한다.

 

max_error_count 시스템 변수의 기본값이 64에서 1024 변경되었다. 이는 MySQL 1000개의 행에 닿는 UPDATE 문과 같은 많은 수의 경고를 처리하고 많은 경고가 변환 경고를 제공하도록 보장한다. 변경 사항은 정적 할당이 없으므로 많은 경고를 생성하는 문의 메모리 소비에만 영향을 미친다.

 

event_scheduler 시스템 변수의 기본값이 OFF에서 ON으로 변경되었다. , 이벤트 스케줄러는 기본적으로 활성화되어 있다. 이는 "유휴 트랜잭션 종료" 같은 SYS 새로운 기능을 활성화하는 것이다.

 

table_open_cache 시스템 변수의 기본값이 2000에서 4000으로 변경되었다. 이는 테이블 액세스 세션 동시성을 높이는 것으로 사소한 변경 사항이다.

 

log_error_verbosity 시스템 변수의 기본값이 3(참고)에서 2(경고) 변경되었다. 목적은 기본적으로 MySQL 8.0 오류 로그를 장황하게 만드는 것이다.

 

 

[InnoDB Defaults]

innodb_undo_tablespaces 시스템 변수의 기본값이 0에서 2 변경되었다. InnoDB에서 사용하는 실행 취소 테이블스페이스 수를 구성한다. MySQL 8.0에서 innodb_undo_tablespaces 최소값은 2이며 이상 시스템 테이블스페이스에 롤백 세그먼트를 생성할 없다. 따라서 이는 5.7 동작으로 다시 되돌릴 없는 경우로, 변경의 목적은 실행 취소 로그(다음 항목 참조) 자동으로 자르고 mysqldump 같은 (가끔) 트랜잭션에 사용되는 디스크 공간을 회수할 있도록 하는 것이다.

 

innodb_undo_log_truncate 시스템 변수의 기본값이 OFF에서 ON으로 변경되었다. 활성화되면 innodb_max_undo_log_size 정의된 임계값을 초과하는 실행 취소 테이블스페이스가 잘림으로 표시된다. 실행 취소 테이블스페이스만 잘라낼 있다. 시스템 테이블스페이스에 있는 실행 취소 로그 자르기는 지원되지 않는다. 5.7에서 8.0으로 업그레이드하면 실행 취소 테이블스페이스를 사용하도록 시스템이 자동으로 변환된다. 시스템 테이블스페이스 사용은 8.0에서 옵션이 아니다.

 

innodb_flush_method 시스템 변수의 기본값은 Unix 계열 시스템에서는 NULL에서 fsync, Windows 시스템에서는 NULL에서 unbuffered 변경되었다. 이는 실질적인 영향을 주지 않는것으로 용어 옵션 정리에 가깝다. Unix 경우 이는 5.7에서도 기본값이 fsync였기 때문에 문서 변경일 뿐이다(기본 NULL fsync 의미함). 마찬가지로 Windows에서 innodb_flush_method 기본값 NULL 5.7에서 async_unbuffered 의미했으며 8.0에서는 기본 unbuffered 대체되었다. 이는 기존 기본 innodb_use_native_aio=ON 결합하여 동일한 효과를 갖는다.

 

호환되지 않는 변경 innodb_autoinc_lock_mode 시스템 변수의 기본값이 1(연속)에서 2(인터리브) 변경되었다. 기본 설정이 인터리브 잠금 모드로 변경된 것은 MySQL 5.7에서 발생한 기본 복제 유형이 명령문 기반에서 기반 복제로 변경된 것을 반영한다. 명령문 기반 복제에는 주어진 SQL 시퀀스에 대해 자동 증가 값이 예측 가능하고 반복 가능한 순서로 할당되도록 연속 자동 증가 잠금 모드가 필요한 반면, 기반 복제는 SQL 문의 실행 순서에 민감하지 않는다. 따라서 변경 사항은 명령문 기반 복제와 호환되지 않는 것으로 알려져 있으며 순차 자동 증분에 의존하는 일부 응용 프로그램이나 사용자 생성 테스트 모음을 손상시킬 있다. innodb_autoinc_lock_mode=1 설정하여 이전 기본값을 복원할 있다.

 

innodb_flush_neighbors 시스템 변수의 기본값은 1(활성화)에서 0(비활성화)으로 변경된다. 이는 빠른 IO(SSD) 이제 배포의 기본값이기 때문에 수행된다. 대부분의 사용자에게는 이로 인해 성능이 약간 향상될 것으로 예상된다. 느린 하드 드라이브를 사용하는 사용자는 성능 손실을 경험할 있으므로 innodb_flush_neighbors=1 설정하여 이전 기본값으로 되돌리는 것이 좋다.

 

innodb_max_dirty_pages_pct_lwm 시스템 변수의 기본값이 0(%)에서 10(%)으로 변경되었다. innodb_max_dirty_pages_pct_lwm=10 사용하면 InnoDB 버퍼 풀의 10% 이상이 수정된('Dirty') 페이지를 포함할 플러시 활동을 증가시킨다. 변경의 목적은 보다 일관된 성능을 제공하는 대신 최대 처리량을 약간 낮추는 것이다.

 

innodb_max_dirty_pages_pct 시스템 변수의 기본값이 75(%)에서 90(%)으로 변경되었다. 변경 사항은 innodb_max_dirty_pages_pct_lwm 대한 변경 사항과 결합되어 원활한 InnoDB 플러시 동작을 보장하고 플러시 버스트를 방지한다. 이전 동작으로 되돌리려면 innodb_max_dirty_pages_pct=75 innodb_max_dirty_pages_pct_lwm=0으로 설정한다.

 

 

[Performance Schema Defaults]

성능 스키마 MDL(메타 데이터 잠금) 계측은 기본적으로 켜져 있다. Performance-schema-instrument='wait/lock/metadata/sql/%=ON' 대한 컴파일된 기본값이 OFF에서 ON으로 변경되었다. 이는 SYS MDL 지향 뷰를 추가하기 위한 활성화 도구이다.

 

성능 스키마 메모리 계측은 기본적으로 켜져 있다. Performance-schema-instrument='memory/%=COUNTED' 대한 컴파일된 기본값이 OFF에서 COUNTED 변경되었다. 서버 시작 계측이 활성화된 경우 집계 결과가 올바르지 않고 할당이 누락되었다가 여유 공간이 확보되면 마이너스 결과 값이 발생할 있으므로 이는 중요하다.

 

성능 스키마 트랜잭션 계측은 기본적으로 켜져 있습니다. Performance-schema-consumer-events-transactions-current=ON, Performance-schema-consumer-events-transactions-history=ON Performance-schema-instrument='transaction%=ON' 대한 컴파일된 기본값이 OFF에서 ON으로 변경되었다.

 

 

[Replication Defaults]

log_bin 시스템 변수의 기본값이 OFF에서 ON으로 변경되었다. , 바이너리 로깅이 기본적으로 활성화되어 있다. 거의 모든 프로덕션 설치에는 복제 특정 시점 복구에 사용되는 바이너리 로그가 활성화되어 있다. 따라서 기본적으로 바이너리 로그를 활성화하면 하나의 구성 단계가 제거되고 나중에 활성화하려면 mysqld 다시 시작해야 한다. 기본적으로 이를 활성화하면 나은 테스트 적용 범위가 제공되고 성능 회귀를 쉽게 발견할 있다. server_id 설정해야 한다. 8.0 기본 동작은 ./mysqld --log-bin --server-id=1 실행한 것과 같다. 8.0 사용하고 있고 5.7 동작을 원하는 경우 ./mysqld --skip-log-bin --server-id=0 실행할 있다.

 

server_id 시스템 변수의 기본값이 0에서 1 변경되었다(log_bin=ON으로의 변경과 결합). 기본 ID 서버를 시작할 있지만 실제로 중복된 서버 ID 방지하려면 배포 중인 복제 인프라에 따라 서버 ID 설정해야 한다.

 

log-slave-updates 시스템 변수의 기본값이 OFF에서 ON으로 변경되었다. 이로 인해 복제본은 복제된 이벤트를 자체 바이너리 로그에 기록한다. 옵션은 그룹 복제에 필요하며 오늘날 표준이 다양한 복제 체인 설정에서 올바른 동작을 보장한다.

 

expire_logs_days 시스템 변수의 기본값이 0에서 30으로 변경되었다. 새로운 기본값 30 mysqld 30일보다 오래된 사용되지 않은 바이너리 로그를 주기적으로 제거하도록 한다. 변경은 복제 또는 복구 목적에 이상 필요하지 않은 바이너리 로그에 과도한 양의 디스크 공간이 낭비되는 것을 방지하는 도움이 된다. 이전 0 자동 바이너리 로그 제거를 비활성화한다.

 

master_info_repository Relay_log_info_repository 시스템 변수의 기본값이 FILE에서 TABLE 변경된다. 따라서 8.0에서는 복제 메타데이터가 기본적으로 InnoDB 저장된다. 이렇게 하면 기본적으로 충돌 방지 복제를 시도하고 달성하기 위한 안정성이 향상된다.

 

transaction-write-set-extraction 시스템 변수의 기본값이 OFF에서 XXHASH64 변경되었다. 변경으로 인해 기본적으로 트랜잭션 쓰기 세트가 활성화된다. 트랜잭션 쓰기 세트를 사용하면 소스가 쓰기 세트를 생성하기 위해 약간 많은 작업을 수행해야 하지만 결과는 충돌 감지에 도움이 된다. 이는 그룹 복제에 대한 요구 사항이며 새로운 기본값을 사용하면 소스에서 바이너리 로그 쓰기 집합 병렬화를 쉽게 활성화하여 복제 속도를 높일 있다.

 

slave_rows_search_algorithms 시스템 변수의 기본값이 INDEX_SCAN, TABLE_SCAN에서 INDEX_SCAN, HASH_SCAN으로 변경되었다. 변경 사항은 기본 없이 테이블에 변경 사항을 적용하기 위해 복제본 적용자가 수행해야 하는 테이블 검색 횟수를 줄여 기반 복제 속도를 높인다.

 

slave_pending_jobs_size_max 시스템 변수의 기본값이 16M에서 128M으로 변경되었다. 변경으로 인해 다중 스레드 복제본에 사용할 있는 메모리 양이 늘어난다.

 

gtid_executed_compression_기간 시스템 변수의 기본값이 1000에서 0으로 변경되었다. 변경으로 인해 mysql.gtid_executed 테이블의 압축은 필요할 때만 암시적으로 발생한다.

 

 

[Group Replication Defaults]

group_replication_autorejoin_tries 기본값이 0에서 3으로 변경되었다. , 자동 재참여가 기본적으로 활성화되어 있음을 의미한다. 시스템 변수는 그룹이 추방되거나 group_replication_unreachable_majority_timeout 설정에 도달하기 전에 그룹의 대다수에 연결할 없는 경우 구성원이 그룹에 자동으로 다시 참여하기 위해 시도하는 횟수를 지정한다.

 

group_replication_exit_state_action 기본값이 ABORT_SERVER에서 READ_ONLY 변경되었다. 예를 들어 네트워크 오류가 발생한 구성원이 그룹을 종료하면 인스턴스가 종료되지 않고 읽기 전용이 된다.

 

group_replication_member_expel_timeout 기본값이 0에서 5 변경되었다. 그룹과의 연락이 두절된 것으로 의심되는 구성원은 5 감지 기간 이후 5 후에 제명 책임을 지게 된다.

 

innodb_dedicated_server라는 옵션의 경우 개발 서버나 개인 랩탑의 경우 ON OFF 설정한다. 이유는 사용할 있는 모든 메모리를 사용하기 때문에 랩탑 같은 일반적인 공유 환경에서 메모리 부족 문제가 발생할 있기 때문이다. 하지만 프로덕션 환경의 경우 innodb_dedicated_server ON으로 설정하는 것이 좋다. ON으로 설정하면 다음 InnoDB 변수(명시적으로 지정하지 않은 경우) 사용 가능한 메모리 innodb_buffer_pool_size, innodb_log_file_size innodb_flush_method 기반으로 자동 크기 조정된다. 작세한 내용은 아래 링크를 참고한다.

l   Enabling Automatic Configuration for a Dedicated MySQL Server : https://dev.mysql.com/doc/refman/8.0/en/innodb-dedicated-server.html

 

 

 

Conclusion

대부분의 기본값은 개발 프로덕션 환경 모두에 적합하지만, 항상 예외는 발생할 있기 때문에 각자의 환경에 맞게 기본값을 적절히 수정해서 사용할 있도록 한다. MySQL 8.0에는 시스템 변수에 대해 가장 최근에 설정된 소스와 해당 범위를 보여주는 성능 스키마 Variable_info 테이블이 있다. 이는 구성 변수와 값에 대해 알아야 모든 것에 대한 SQL 액세스를 제공한다.

 

 

 

[참고자료]

l   Changes in MySQL 8.0 :  https://dev.mysql.com/doc/refman/8.0/en/upgrading-from-previous-series.html

 

 

 

 

 

2023-09-13 / Sungwook Kang / http://sungwookkang.com

 

MySQL, 업그레이드 고려사항, MySQL8 변경사항

[Kubernetes] 쿠버네티스에서 POD(파드)? 그리고 POD 생성하기

 

l  Kubernetes

 

파드(Pod) 쿠버네티스에서 생성하고 관리할 있는 배포 가능한 가장 작은 컴퓨팅 단위이다. 도커가 가장 일반적으로 알려진 컨테이너 런타임이지만, 쿠버네티스는 도커 외에도 다양한 컨테이너 런타임을 지원한다.

 

파드는 하나 이상의 컨테이너의 그룹이며 스토리지 네트워크를 공유하고, 해당 컨테이너를 구동하는 방식에 대한 명세를 갖는다. 파드의 컨텐츠는 항상 함께 배치되고, 스케줄되며, 공유 컨텍스트에서 실행된다. , 파드는 공유 네임스페이스와 공유 파일시스템 볼륨이 있는 컨테이너들의 집합과 비슷하다.

 

파드는 애플리케이션의 "논리 호스트" 모델링한다. 여기에는 상대적으로 밀접하게 결합된 하나 이상의 애플리케이션 컨테이너가 포함된다. 클라우드가 아닌 컨텍스트에서, 동일한 물리 또는 가상 머신에서 실행되는 애플리케이션은 동일한 논리 호스트에서 실행되는 클라우드 애플리케이션과 비슷하다.

 

애플리케이션 컨테이너와 마찬가지로, 파드에는 파드 시작 중에 실행되는 초기화 컨테이너가 포함될 있다. 클러스터가 제공하는 경우, 디버깅을 위해 임시 컨테이너를 삽입할 수도 있다. 쿠버네티스에서는 이러한 파드를 생성하기 위한 방법으로 kubectl run, kubectl create, kubectl apply 3가지가 있다. 각의 특징에 대해서 알아본다.

 

[kubectl run]

kubectl run kubernetes 클러스터에서 파드나 작업을 생성하는 명령어이다. 가장 간단하게 pod 만드는 방식이며, 실행 명령어 뒤에 각종 옵션 정보를 입력해서 생성한다. 가장 간편한 실행 방법인 반면 입력 정보가 남지 않기 때문에 재실행 문제가 발생했을 원인을 찾거나 해결 방법을 공유하는 등의 어려움이 있다. 일반적인 개발 단계에서 간편하게 pod 생성할 유용하지만 많은 옵션을 함께 사용해야 경우 모든 명령어를 입력해야하기 때문에, yaml 작성하여 사용하는 것이 일반적이다. 기본 구조는 아래와 같다.

Kubectl run [파드이름] –image=[이미지 이름]

 

아래 예시는 파드 생성시 실행할 이미지를 지정한다.

kubectl run nginx --image=nginx

 

아래 예시는 실행할 파드의 개수를 지정한다. Nginx 이미지를 사용하여 3개의 파드를 생성한다. 파드가 중간에 삭제되면 지정한 개수만큼 파드가 유지되도록 파드가 자동 생성된다.

kubectl run nginx --image=nginx --replicas=3

 

아래 예시는 파드를 생성할 포트 번호를 노출시킨다.

kubectl run nginx --image=nginx --port=80

 

아래 예시는 파드를 생성할 다양한 환경변수를 대입하여 파드를 실행한다.

kubectl run my-proxysql --image=my-proxysql --port=6033 --image-pull-policy=Never --restart=Never --env PROXY_ADMIN_PWD=pwd --env PROXY_ADMIN_ID=id

 

 

[kubectl create]

Kubectl create 명령은 kubernetes에서 새로운 리소스를 생성할 사용한다. 명령을 사용하면 yaml파일을 생성하지 않고도 명령어 자체에서 리소스를 생성할 있다. 기본구조는 아래와 같다.

kubectl create [리소스 종류] [리소스 이름]

 

아래 예시는 Nginx 이미지를 사용하는 my-nginx라는 pod 리소스를 생성한다.

kubectl create pod my-nginx --image=nginx

 

아래 예시는 Nginx 이미지를 사용하는 my-deployment 이름을 가진 Deployment 리소스를 생성한다.

Kubectl create deployment my-deployment --image=nginx

 

아래 예시는 80포트를 8080으로 매핑하는 my-service라는 이름의 ClusterIP 서비스 리소스를 생성한다.

Kubectl create service clusterip my-service --tcp=80:8080

 

아래 예시는 key1=value1, key2=value2 데이터를 가진 my-config 이름을 가진 ConfigMap 리소스를 생성한다.

kubectl create configmap my-config --from-literal=key1=value1 --from-literal=key2=value2

 

아래 예시는 password=1234데이터를 가진 my-secret 이름의 secret 리소스를 생성한다.

Kubectl create secret generic my-secret --from-literal=password=1234

 

아래 예시는 리소스를 생성하지 않고 생성 결과만 미리 확인한다.

kubectl create deployment my-deployment --image=nginx --dry-run

 

아래 예시는 파일(yaml 또는 json) 사용하여 리소스를 생성한다.

kubectl create -f test.yaml

 

아래 예시는 리소스가 생성될 네임스페이스를 지정한다.

Kubectl create deployment my-deployment --image=nginx -n my-namespace

 

아래 예시는 컨테이너의 재시작 정책을 지정한다. 상태는 Always, OnFailure, Never 하나를 사용한다.

Kubectl create pod my-pod --image=nginx --restart-policy=OnFailure

 

 

[kubectl apply]

kubectl apply kubectl create 동일하지만 가장 차이점은 기존에 동일한 pod 없다면 새로운 pod 생성하고, 이미 동일한 pod 존재하는 경우 기존 config 비교하여 수정된 부분만 업데이트 한다.

 

 

[kubectl delete]

파드나 리소스를 삭제하기 위해서는 kubectl delete 명령을 사용한다. 기본 구조는 아래와 같다.

Kubectl delete [리소스 유형] [리소스 이름]

 

아래 예시는 nginx라는 파드를 삭제한다.

kubectl delete pod nginx

 

삭제할 다양한 옵션을 함께 사용할 있다.

l  --all : 모든 리소스를 삭제

l  --force : 강제로 삭제

l  ---grace-period=<second> : 대기시간() 이후 삭제 설정

l  --timeout=<second> : 명령이 실행될 대기할 최대 시간() 설정

 

 

 

2023-09-08 / Sungwook Kang / https://sungwookkang.com

 

 

Kubernetes, 쿠버네티스, pod, 파드, kubectl, create, delete, apply, 파드생성, 리소스 생성

[kubernetes] minikube에서 로컬 docker image 사용하기

 

l  Kubernetes, Docker

 

일반적으로 로컬 컴퓨터에서 쿠버네티스(Kubernetes) 테스트할 minikube 환경을 많이 사용한다. 도커 데스크탑(docker desktop) 설치된 환경에서도 쿠버네티스를 활성화하면 minikube 활성화되어 동작한다. 이러한 환경에서 kubectl run 또는 yaml 파드를 생성할 , 로컬의 이미지를 사용할 경우 이미지를 가져올 없다는 오류와 함께 파드가 생성된 종료된다. 이번 포스트에서는 minikube 환경에서 로컬의 docker image 사용하여 파드를 생성하는 방법을 알아본다.

 

로컬의 docker images에서 커스텀 빌드된 이미지 proxysql 있다.

 

로컬의 도커 이미지를 사용하여 파드를 생성하면 오류가 발생한다.

kubectl run my-proxysql --image=proxysql

 

 

오류를 살펴보면 ErrImagePull이라는 상태 메시지를 있는데, 상태를 자세히 확인하기 위해 kubectl describe 명령을 사용하여 상세 메시지를 확인한다.

kubectl describe pods my-proxysql

 

 

마지막에 오류 메시지를 보면, 이미지를 가져올 없어 발생한 오류라는 것을 확인할 있다.

Failed to pull image "proxysql": rpc error: code = Unknown desc = Error response from daemon: pull access denied for proxysql, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

 

일반적으로 생각할 , 쿠버네티스를 사용하여 파드를 생성하면, 로컬의 이미지를 사용할 알았는데 그렇지 않다. 그래서 minikube에서 로컬의 이미지를 사용할 있도록 minikube 도커를 연결해야해는데, 다행히 쿠버네티스에서는 공식적으로 이러한 부분을 지원하고 있다.

 

아래 명령어를 사용하면, 현재 minikube에서 도커 host 연결할 있는 방법을 알려준다. 결과 정보를 바탕으로 일일이 등록해도 되지만, 마지막줄에서 도커 호스트와 한번에 연결할 있는 명령어를 제공하기 때문에 그대로 복사해서 사용할 있다.

minikube docker-env

 

 

마지막 줄의 명령어를 실행하여 minikube에서 docker host 연결한다.

eval $(minikube -p minikube docker-env)

 

명령어실행 도커에서 컨테이너를 조회해 보면 쿠버네티스의 컨테이너가 조회되는 것을 확인할 있다.

 

지금부터는 docker 이미지가 minikube에서 동작하는 driver(docker) 연결되었기 때문에 이제부터 생성되는 docker image minikube 위에 생성하게 된다.

 

다시 한번 이미지를 빌드 한다. 이번에는 이전과 차별을 두기 위해 my-proxysql 이라는 이름으로 이미지를 생성하였다.

docker build -t my-proxysql .

 

아래 명령을 사용하여 minikube 이미지 목록을 확인한다. 방금 빌드한 이미지 목록을 확인할 있다.

minikube image ls --format table

 

 

아래 명령을 사용하여 파드를 실행한다. 이때 로컬의 이미지를 사용하도록 --image-pull-policy=Never 옵션을 함께 사용한다. 정상적으로 파드가 실행중인 것을 확인할 있다.

kubectl run my-proxysql1 --image=my-proxysql --image-pull-policy=Never --restart=Never

 

 

 

[참고자료]

l  https://minikube.sigs.k8s.io/docs/handbook/pushing/#1-pushing-directly-to-the-in-cluster-docker-daemon-docker-env

l  https://www.baeldung.com/docker-local-images-minikube

l  https://egkatzioura.com/2020/03/08/use-local-docker-image-on-minikube/

 

 

 

2023-09-07 / Sungwook Kang / https://sungwookkang.com

 

 

도커파일, dockerfile, minikube, Kubernetes, 로컬이미지 사용, 파드생성, pod create, 도커이미지

[Docker] 컨테이너실행시 sh 스크립트 호출과 매개변수 사용

 

l  Docerk, Shell script

 

Dockerfil 제작하면서, 컨테이너 실행시 특정 스크립트를 실행할 , 스크립트 실행에 필요한 파라메터 값을 받아오지 못하는 문제가 있었다. (도커의 문제가 아닌 나의 코드 오류 문제) 단독으로 스크립트를 실행하면 정상적으로 실행되는데, 유독 컨테이너 실행시에만 값을 받지 못하였다. 해결 방법은 매우 간단하게 Dockerfile에서 사용한 ENV 환경변수를 그대로 스크립트에서 사용하면 되는거였다. (너무 어렵게 생각해서 문제를 해결하지 못한 것이었다.)

 

[잘못된 예시]

abc.sh에서 인풋 변수로 값을 사용할 있도록 read 명령어를 사용하였으며, 해당 변수로 받은 값을 출력하려고 작성.

abc.sh
#!/usr/bin/env bash
 
read var1
read var2
 
echo $var1
echo $var2

 

컨테이너 실행시 ENTRYPOINT에서 abc.sh 실행하면서 파라메터를 전달하려고 작성.

Dockerfile
FROM ~~~~
 
ENV sh_var1 var1_value
ENV sh_var2 var2_value
 
ENTRYPOINT [“abc.sh”, “$sh_var1”, “$sh_var2”]

 

 

[정상 예시]

abc.sh
#!/usr/bin/env bash
 
#read var1 <- 인풋 변수 삭제
#read var2 <- 인풋 변수 삭제
 
echo $sh_var1 #<- dockerfile ENV 변수를 그대로 가져다 사용
echo $sh_var2 #<- dockerfile ENV 변수를 그대로 가져다 사용

 

Dockerfile
FROM ~~~~
 
ENV sh_var1 var1_value
ENV sh_var2 var2_value
 
ENTRYPOINT [“abc.sh”] #<- ENTRYPOINT호출시 sh 파일만 실행

 

 

물론 방법 외에도 다양한 사용법이 있을텐데, 우선 나의 코드에서는 각각의 변수를 호출해서 사용하려고 해서 발생한 문제로, 도커파일과 스크립트간의 매개변수 전달에 대한 이해가 부족해서 발생한 문제였다.

 

 

 

 

2023-09-07 / Sungwook Kang / https://sungwookkang.com

 

 

도커파일, dockerfile, 쉘스크립트, shell script, 매개변수, 도커환경변수, docker env, shell read

[Linux] (shell) 스크립트 기초

 

l  Linux

 

스크립트(shell script) 셸이나 명령줄 인터프리터(Interpreter)에서 실행되도록 작성되었거나 운영 체제를 위해 쓰인 스크립트이다. 스크립트가 수행하는 일반 기능으로는 파일 이용, 프로그램 실행, 문자열 출력 등이 있다. 스크립트에서는 .sh라는 파일 확장자를 가진 파일이 특정 종류의 스크립트를 가리키는 것이 보통이지만, 대부분의 스크립트는 파일 확장자를 지니지 않는다. 스크립트는 인터프리터 방식이므로 라인별로 읽어 실행함으로 속도가 다소 느리다는 단점이 있다.

스크립트라는 말은 유닉스 쉘을 위해 쓰인 스크립트를 말하는 반면, command.com(도스) cmd.exe (윈도우) 명령 스크립트는 보통 배치 파일이라고 불린다.  최근엔 마이크로소프트가 개발한 CLI쉘인 파워쉘이 있다.

 

이번 포스트에서는 가장 널리 쓰이는 bash 쉘을 사용하는 스크립트를 설명한다.

 

문장의 시작 (#!/bin/bash)

스크립트의 번째줄에는 항상 #!/bin/bash 기입되어 있어야 한다. 내용은 쉘이 실행될 어떤 쉘로 스크립트를 실행할지 정의하는 곳이다. 쉘에는 다양한 버전이 있다. 아래 예시는 hello.sh 라는 스크립트를 생성하고 실행하는 방법을 설명한다. 아래 스크립트를 hello.sh라는 파일을 생성하고 입력한다.

#!/bin/bash
 
echo "Hello, Shell Script"

 

아래 명령을 사용하여 스크립트를 실행하면 Hello, Shell Script 출력한다. 스크립트 명령어에 대한 부분은 아래에서 자세히 설명하도록 한다. 만약 실행이 되지 않는다면 실행 권한이 없어 발생한것으로 chmod 777 hello.sh 실행하여 권한 변경 실행한다.

$sh hello.sh

 

 

 

주석 처리 (#)

스크립트에서 단일 주석은 (#) 사용한다.

#!/bin/bash
 
#여기는 주석입니다.
#주석은 실행되지 않습니다.
 
echo "Hello, Shell Script"

 

블록 주석은 : <<”END” ~ ~ END 사용한다.

#!/bin/bash
 
: << “END” #주석 시작
여기는 주석입니다.
주석은 실행되지 않습니다.
END #주석
 
echo "Hello, Shell Script"
 

 

 

입력(read), 출력(echo)

echo 출력, read 입력을 있다. 위에서 이미 echo 사용하였으므로 read 사용하여 입력한 값을 출력하도록 스크립트를 작성해 본다. 입력 받은 값을 변수로 사용하려면 $기호를 사용한다.

#!/bin/bash
read input_value
echo "Hello, $input_value!"

 

스크립트를 실행하면 입력값이 발생할 때까지 실행되지 않고 대기한다. hahaha라고 입력하면 입력 받은 값이 변수로 사용되어 결과가 출력된다.

$sh hello.sh
hahaha

 

 

 

변수 특징

l  변수의 타입에는 로컬변수와 전역변수, 환경변수, 예약변수, 매개변수 다양하다.

l  변수는 , 소문자를 구별한다.

l  변수명에는 영문자, 숫자, 언더바( _ ) 사용된다.

l  변수의 이름은 숫자를 포함할 있지만, 숫자로 시작할 없다.

l  변수에는 모든 값을 문자열로 저장된다.

l  변수에는 자료형을 기입하지 않기 때문에 어떠한 값도 입력할 있다. (변수에 타입을 지정할 수도 있다.)

l  값을 사용할 때는 변수명 앞에 특수문자 "$" 사용한다. (Ex. echo ${data})

l  값을 대입(삽입) 때는 특수문자 "$" 사용하지 않는다. (Ex. data=mac)

l  변수를 생성할 때는 "=" 대입문자 앞뒤로 공백이 없어야 한다. (Ex. data="abcd") 문자열인 경우 “ “ 감싼다.

l  하나의 변수에 개의 값만 보존된다.

l  변수 값이 덮어 쓰기되는 것을 방지하기 위해서는 readonly 사용한다.

l  변수를 unset으로 삭제할 있다. (readonly 선언된 것은 삭제가 불가능하다.)

 

스크립트에서 시스템이 사용하는 예약어가 있다. 가지 대표적인 예약어이다.

변수 설명
HOME 사용자 디렉토리 의미
PATH 실행 파일의 경로
LANG 프로그램 실행 지원되는 언어
UID 사용자의 UID
SHELL 사용자가 로그인시 실행되는
USER 사용자 계정 이름
FUNCNAME 현재 실행되고 있는 함수 이름
TERM 로그인 터미널
Set 변수를 출력하는 명령어
Env 환경 변수를 출력하는 명령어
Export 특정 변수의 범위를 환경 변수의 데이터 공간으로 전송하여 자식 프로세스에서도 특정 변수를 사용 가능하게 한다.
unset 선언된 변수를 제거

 

 

전역변수, 지역변수

쉘에서 선언된 변수는 기본적으로 전역변수(global variable)이며, 함수안에서 지역변수(local variable) 사용할 있는데, 이때에는 local 붙여서 사용한다. 함수안에서 local 사용하지 않으면 전역 변수의 값을 덮어쓰게 된다.

#!/bin/bash
 
# 전역 변수 지정
string="hello shell, global variable"
 
function string_test() {
    # local 붙여야 지역변수로 인식. 만약 local 빼면 전역변수에 덮어쓰기로 적용
    local string="hello shell, local variable"
    echo ${string}
}
 
# 함수 호출
string_test # > hello shell, local variable
echo ${string} # > hello shell, global variabl
 
# 변수 초기화
unset string

 

 

 

변수 타입 지정

기본적으로 Bash 문자열만 저장하지만 데이터 타입을 지정할 수도 있다.

변수타입 설명
declare -r 읽기 전용 타입
declare -i 정수형 타입
declare -a 배열 타입
declare -A 연관배열(MAP) 타입
declare -f 함수 타입
declare -x 환경변수(export) 지정으로 외부 환경에서도 변수를 사용할 있다.

 

 

읽기전용 변수 (readonly)

변수에 값을 덮어 쓸수 있지만 readonly 지정한 경우 변수를 덮어쓸수 없다. 아래 예시는 readonly에서 변수 덮어쓰기를 시도할 발생한 오류를 확인할 있다.

#!/bin/bash
 
var="변수1"
VaR_2="변수2"
echo "Var_2=$VaR_2"
 
VaR_2="VaR_2 변경합니다."
echo ${VaR_2}
 
readonly var
var="readonly var 변수를 변경해 봅니다."

 

 

 

변수 값의 치환

변수의 값을 치환할 사용하는 문법을 표로 정리하였다.

문법 설명
${var} 변수 값을 바꿔 넣는다.
${var:-word} 변수가 아직 세팅되지 않거나 공백 문자열의 경우 word 반환한다. var에는 저장되지 않는다.
${var:=word} 변수가 아직 세팅되지 않거나 공백 문자열의 word 반환한다. var 저장된다.
${var:?word} 변수가 아직 세팅되지 않거나 공백 문자열의 경우 치환에 실패하고, 스탠다드 에러에 에러가 표시된다.
${var:+word} 변수가 세팅되지 않은 경우 word 반환된다. var에는 저장되지 않는다.

 

#!/bin/sh
 
echo "1 - ${var:-wordSetInEcho1}"
echo "2 - var = ${var}"
echo "3 - ${var:=wordSetInEcho3}"
echo "4 - var = ${var}"
unset var
echo "5 - ${var:+wordSetInEcho5}"
echo "6 - var = $var"
var="newVarValue"
echo "7 - ${var:+wordSetInEcho7}"
echo "8 - var = $var"
echo "9 - ${var:?StandardErrorMessage}"
echo "10 - var = ${var}"

 

 

문자열 패턴 비교는 아래 조건을 사용할 있다.

문법 설명
${var%pattern} 끝에서 부터 word 패턴이 일치하는 var 최소 부분(첫번째 일치) 제거하고 나머지를 반환
${var%%pattern} 끝에서 부터 word 패턴이 일치하는 var 최대 부분(마지막 일치) 제거하고 나머지를 반환
${var#pattern} 처음 부터 word 패턴이 일치하는 var 최소 부분( 번째 일치) 제거하고 나머지 부분을 반환
${var##pattern} 처음 부터 word 패턴이 일치하는 var 최대 부분(마지막 일치) 제거하고 나머지 부분을 반환

 

 

환경 변수 (export)

스크립트에서 변수명 앞에 export 붙여 환경변수(environment variable) 사용할 있다. 다만 환경변수 사용시 시스템에 미리 정의된 예약변수와 겹치지 않게 주의한다. 실습을 위해 개의 파일을 준비한다.

shell1.sh
#!/bin/bash
 
echo ${export_variable}

 

shell2.sh
#!/bin/bash
 
#환경변수 선언
export export_variable="export test, hello shell"
 
#변수값을 받아서 사용할 스크립트 호출
/파일 경로/shell1.sh

 

shell2.sh 실행한다. Export 변수의 값은 shell1.sh 호출할 사용된다.

 

 

매개변수

프로그램에서 실행할 인자를 사용하듯 스크립트에서도 매개변수를 사용할 있다. 규칙이 있는데, 실행한 스크립트 이름은 ${0}, 이후 인자 값들은 ${1}. ${2}…으로 사용한다.

변수 기능
$0 스크립트명
$1 ~ $9 인수 1 번째부터 인수 9번째까지 사용
$# 스크립트에 전달될 인수의
$* 모든 인수를 모아 하나로 처리
$@ 모든 인수를 각각 처리
$? 직전에 실행한 커멘드의 종료 (0 성공, 1 실패)
$$ 스크립트의 프로세스ID
$! 마지막으로 실행한 백그라운드 프로세스 ID

 

아래 스크립트는 매개변수를 입력 받아 어떻게 사용되는지 알아보는 예시이다.

#!/bin/sh
 
echo "\$0script name: $0"
echo "\$11st param: $1"
echo "\$22nd param: $2"
echo "\$#number of param: $#"
echo "\"\$*\": \"$*\""
echo "\"\$@\": \"$@\""
VAR="exit code is  0 "
echo $?

 

실행 결과는 아래와 같이 나타난다.

 

 

배열 (Array)

스크립트에서 배열은 1차원 배열만 지원하며 중괄호를 사용해야 한다. 배열 원소는 소괄호안에 공백으로 구분하며, 배열 원소는 문자열이든 숫자형이던 상관없이 있다.

#!/bin/bash
 
arr=("hello" "shell" 1 2 3 4 5)
 
echo "배열 전체 : ${arr[@]}"
echo "배열 원소의 갯수 : ${#arr[@]}"
echo "배열 첫번째 : ${arr}, 혹은 ${arr[0]}"
echo "4 index 갖는 배열의 원소 : ${arr[4]}"
 
arr[5]="four"
 
echo "4번째 index 갖는 배열의 원소 : ${arr[4]}"
 
# 4번째 해제
unset arr[4]
echo "4 배열 삭제 완료"
echo "4 index 갖는 배열의 원소 : ${arr[4]}"
echo "5 index 갖는 배열의 원소 : ${arr[5]}"

 

 

 

함수(function)

스크립트에서도 함수를 사용할 있다. 함수의 정의 형식은 아래와 같다. 주의할 점은 함수가 호출되기 전에 함수가 정의되어 있어야 하며 호출할 때는 괄호를 사용하지 않고 호출한다.

#!/bin/bash
 
function func(){
        echo "func()"
}
 
#함수 호출
func

 

 

비교문(if..fi)

쉘에서는 비교문이 약간 특이하다. bash if문의 특이한 점은 fi 대괄호([ ]) 이다. 다른 언어와 달리 중괄호를 사용하지 않기 때문에 fi if문의 끝을 알려주어야 한다. 주의해야 점은 if 뒤에 나오는 대괄호 [ ] 조건식 사이에는 반드시 공백이 존재해야 한다.

 

비교문에는 다양한 연산자를 사용할 있다.

연산자 설명
1 -eq 2 # 값이 같음(equal)
1 -ne 2 # 값이 같지 않음(not equal)
1 -lt 2 # 1 2보다 작음(less than)
1 -le 2 # 1 2보다 작거나 같음(less or equal)
1 -gt 2 # 1 2보다 (greater than)
1 -ge 2 # 1 2보다 크거나 같음(greater or equal)

 

문자열 비교

연산자 설명
문자1 = 문자2 # 문자1 문자2 일치 (sql같이 = 하나만 써도 일치로 인식)
문자1 == 문자2 # 문자1 문자2 일치
문자1 != 문자2 # 문자1 문자2 일치하지 않음
-z 문자 # 문자가 null 이면
-n 문자 # 문자가 null 아니면
문자 == 패턴 # 문자열이 패턴과 일치
문자 != 패턴 # 문자열이 패턴과 일치하지 않음

 

논리 연산자

연산자 설명
조건1 -a 조건2 # AND
조건1 -o 조건2 # OR
조건1 && 조건2 # 양쪽 성립
조건1 || 조건2 # 한쪽 또는 양쪽다 성립
!조건 # 조건이 성립하지 않음
true # 조건이 언제나 성립
false # 조건이 언제나 성립하지 않음

 

산술연산자 (쉘에서는 expr 숫자 연산자 숫자형식으로 사용한다.)

연산자 의미 예시
+ 덧셈 echo `expr 10 + 20` => 30
- 뺄셈 echo `expr 20 - 10` => 10
\* 제곱 echo `expr 11 \* 11` => 121
/ 나눗셈 echo `expr 10 / 2` => 5
% 나머지 echo `expr 10 % 4` => 2
= 저장 a=$b b 값은 a 저장
== 동일 [ "$a" == "$b" ] $a $b 동일하는 경우 TRUE 반환
!= 다름 [ "$a" != "$b" ] $a $b 동일하지 않는 경우 TRUE 반환

 

 

예제로 살펴본다.

#!/bin/bash
 
function func(){
        a=10
        b=5
 
        if [ ${a} -eq ${b} ]; then
                echo "a b 같다."
        fi
 
        if [ ${a} -ne ${b} ]; then
                echo "a b 같지 않다."
        fi
 
        if [ ${a} -gt ${b} ]; then
                echo "a b보다 크다."
        fi
 
        if [ ${a} -ge ${b} ]; then
                echo "a b보다 크거나 같다."
        fi
 
        if [ ${a} -lt ${b} ]; then
                echo "a b보다 작다."
        fi
 
        if [ ${a} -le ${b} ]; then
                echo "a b보다 작거나 같다."
        fi
 
}
 
#함수 호출
func

 

 

 

여러 조건을 사용할때, 다른 언어에서는 else if역할을 하는 것을 스크립트에서는 elif라는 것을 사용하여 else if 동일한 역할을 있다. elif[ 조건 ]; then 형식으로 사용한다. 만약 반복문에서 사용하여 조건이 참일때 반복문을 멈추고 싶을때 break라는 키워드를 사용하여 반복문을 멈출 있다.

#!/bin/bash
 
case ${1} in
        "linux") echo "리눅스" ;;
        "unix") echo "유닉스" ;;
        "windows") echo "윈도우즈" ;;
        "MacOS") echo "OS" ;;
        *) echo "해당없음" ;;
esac

 

 

파일 관련 조건문

스크립트는 명령어를 다루는 스크립트이기 때문에 파일이 존재하는지, 디렉토리가 존재하는지 파일과 관련된 조건 여부도 조건문을 통해 있다. 아래 표는 조건을 정리한 것이다. , 거짓을 판단할 , 느낌표(!) 사용하면 반대로 결과를 출력한다.

조건 설명
if [ -d ${변수} ]; then ${변수} 디렉토리가 존재하면 true.
if [ ! -d ${변수} ]; then ${변수} 디렉토리가 존재하지 않으면 true.
if [ -e ${변수} ]; then ${변수}라는 파일이 존재하면 true.
if [ ! -e ${변수} ]; then ${변수}라는 파일이 존재하지 않으면 true.
if [ -L ${변수} ]; then 파일이 symbolic link이면 true.
if [ -s ${변수} ]; then 파일의 크기가 0보다 크면 true.
if [ -S ${변수} ]; then 파일 타입이 소켓이면 true.
if [ -r ${변수} ]; then 파일을 읽을 있으면 true.
if [ -w ${변수} ]; then 파일을 있으면 true.
if [ -x ${변수} ]; then 파일을 실행할 있으면 true.
if [ -f ${변수} ]; then 파일이 정규 파일이면 true.
if [ -c ${변수} ]; then 파일이 문자 장치이면 true.
if [ ${변수1} -nt ${변수2}]; then 변수1 파일이 변수2 파일보다 최신 파일이면 true.
if [ ${변수1} -ot ${변수2}]; then 변수1 파일이 변수2 파일보다 최신이 아니면 true.
if [ ${변수1} -ef ${변수2}]; then 변수1 파일과 변수2 파일이 동일하면 true.

 

 

CASE

일반적인 CASE 사용법과 비슷하지만 CASE 끝에 세미콜론을 2개씩 사용하는 특징이 있다. 그리고 CASE 끝은 ESAC 닫아준다.

#!/bin/bash
 
DRINK="coffee"
case "$DRINK" in
    "beer") echo "맥주입니다"
    ;;
    "juice") echo "주스입니다"
    ;;
    "coffee") echo "개발자에겐 카페인 필수!"
    ;;
esac

 

 

CASE에서 OR 연산도 가능하다.

COUNTRY=korea
 
case $COUNTRY in
  "korea"|"japan"|"china") # or 연산도 가능하다
    echo "$COUNTRY is Asia"
    ;;
  "USA"|"Canada"|"Mexico")
    echo "$COUNTRY is Ameria"
    ;;
  * )
    echo "I don't know where is $COUNTRY"
    ;;
esac

 

 

반복문(for)

반복문을 빠져나갈때에는 break 사용하고, 현재 반복분이나 조건을 건너뛸때에는 continue 사용한다.

#!/bin/bash
 
# 초기값; 조건값; 증가값을 사용한 정통적인 for
for ((i=1; i<=4; i++)); do
    echo $i
done

 

 

 

반복문(for in)

#!/bin/bash
 
function func(){
        echo "사용예1"
        for i in 1 2 3 4 5
        do
                echo "${i}"
        done
 
        echo "사용예2"
        list="1 2 3 4 5"
        for i in ${list}
        do
                echo "${i}"
        done
 
        echo "사용예3"
        for i in {1..5}
        do
                echo "${i}"
        done
 
        echo "사용예4: 크기를 2만큼 증가시키면서 출력"
        for i in {1..5..2}
        do
                echo "${i}"
        done
 
        echo "사용예5: 배열을 이용"
        arr=(1 2 3 4 5)
        for i in "${arr[@]}"
        do
                echo "${i}"
        done
 
 
        echo "사용예6: C 유사한 형식의 for"
        for ((i=0; i<5; i++)); do
                echo "${i}"
        done
}
 
#함수 호출
func

 

 

 

반복문 (while)

수행 조건이 true일때 실행되는 반복문이다.

count=0
while [ ${count} -le 5 ];
do
    echo ${count}
    count=$(( ${count}+1 ))
done

 

 

이중 괄호를 사용하면 논리 기호로도 사용 가능하다.

count=0
while (( ${count} <= 5 ));  # 이중괄호 사용하면 논리기호 사용 가능
do
    echo ${count}
    count=$(( ${count}+1 ))
done

 

 

반복문(until)

수행 조건이 false 일때 실행되는 반복문이다. 조건이 While 반대라고 생각하면 된다.

count2=10
 
until [ ${count2} -le 5 ]; do
    echo ${count2}
    count2=$(( ${count2}-1 ))
done

 

 

 

종료 상태 코드

명령어가 실패할 경우 리턴되는 결과에 따라 다른 동작을 하려면 명령어의 종료 코드를 반환 받아 사용하면 된다. 명령어의 종료 코드는 $? 사용하여 가장 최근 실행한 명령어의 종료 코드를 받을 있다. 정상적인 종료는 0 반환되며 0 아닌 값은 오류라고 판단할 있다. 아래 표는 종료 코드에 따른 설명을 정리한 것이다.

종료 코드 설명
0 성공 (Success)
1 일반적 오류 (Catchall for general errors)
2 내장 명령의 틀린 사용 (Misuse of shell builtins)
126 파일이 실행 가능하지 않음 (Command invoked cannot execute)
127 명령어를 찾을 없음 (Command not found)
128 종료할 잘못된 인수 적용 (Invalid argument to exit)
128+n 치명적인 시그널 n 에러 (Fatal error signal “n”)
130 [Ctrl + C] 조합에 의한 종료 (Script terminated by Control-C)

 

 

이스케이프 문자

명령어 설명
\f 문자열만큼 열을 밀어서 이동한다.
\n 새로운 줄로 바꾼다.
\r 문자열의 앞부분 부터 뒷문자열 만큼 대체하고 반환한다.
\t 만큼 띄운다.

 

 

외에도 다양한 명령이 있다. 명령을 활용하면 리눅스 환경에서 시스템 관련 다양한 정보들을 스크립트로 얻을 있다.

 

 

 

[참고자료]

l   https://ko.wikipedia.org/wiki/%EC%85%B8_%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8

l   https://reakwon.tistory.com/136

l   https://inpa.tistory.com/entry/LINUX-%EC%89%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%95%B5%EC%8B%AC-%EB%AC%B8%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC

l   https://engineer-mole.tistory.com/200

 

 

 

2023-09-05 / Sungwook Kang / https://sungwookkang.com

 

 

리눅스, Linux, 쉘스크립트, 쉘사용법, 쉘명령어, shell command, shell script

'Linux' 카테고리의 다른 글

[Linux] 리눅스 디스크 용량 확인  (0) 2023.09.26
[Linux] Alpine Linux (알파인 리눅스) 는 무엇일까?  (0) 2023.09.04
[Linux] OOM(Out of Memory)란?  (0) 2023.08.30
CnetOS 방화벽 설정  (0) 2015.07.22
CentOS 파일 압축/풀기  (0) 2015.07.22

[Linux] Alpine Linux (알파인 리눅스) 무엇일까?

 

l  Linux

 

컨테이너 환경으로 다양한 서비스를 빌드하다보니, DevOps 팀에서 베이스 이미지를 ubuntu centos 대신 alpine(알파인) 리눅스를 사용하는 것이 어떻겠냐는 제안을 받았다. 리눅스는 다양한 커널 기반으로 배포판을 가지고 있다. 이번 포스트에서는 Alpine Linux (이하 알파인 리눅스”) 대해서 알아본다.

 

[개요]

알파인 리눅스는 리눅스 커널 기반으로 리눅스 배포판 하나이며 MIT 라이선스 아래에 배포되었으며 LEAF 리눅스의 포크 버전이다. 알파인 리눅스는 다른 배포판과 다르게 Musl BusyBox 기반으로 한다. 알파인 리눅스의 컨셉은 LEAF 리눅스와 동일하게 작고”, “보안이 뛰어나고”, “간단함 기반으로 한다. 하지만 시간이 지나면서 다양한 사람들에 의해 알파인 리눅스만의 형태로 발전되고 있다.

 

[특징]

장점

l  가볍고, 간단하며 보안성을 목적으로 개발한 배포판

l  용량을 줄이기 위해 시스템의 기본 C runtime glibc 대신 musl libc 사용

l  다양한 명령어는 GNU util대신 busybox 탑재

l  초경량( 80MB) 배포판으로 임베디드나 네트워크 서버 특정 용도에 적합.

l  특히 도커(Docker) 공식 이미지로 채택되어 5MB 크기의 리눅스 이미지로 유명함

l  보안에 염두해두고 설계되어 모든 바이너리는 독립적인 실행 파일(PIE) 컴파일

 

단점

l   리눅스 표준인 GNU 도구가 없어가나 Busybox 대신 들어가 있어, 명령어를 실행할 주의가 필요

l   라이트한 대신 패키지가 설치되어 있지 않아 직접 설치해주어야 하는 것들이 많다.

l   이것저것 설치하다보면 표준 리눅스 배포판과 거의 비슷한 용량이 되기도 한다. (개인적인 생각)

 

 

BusyBox?
BusyBox 일종의 명령어 모음집으로 박스안에 필요한 응용 프로그램들을 담아 놓은 것이다. 리눅스에서 사용되는 명령어들만 모아 놓아도 상당한 크기를 차지할 있는데, BusyBox에서는 각각에 함수들을 최소 사이즈로 다시 구현하였다. 그렇기 때문에 Embbeded(임베디드), 네트웍 서버 특정 용도에 적합하며 도커 컨테이너의 OS 많이 사용한다.

 

 

[자주 사용되는 명령어]

알파인 리눅스에서는 패키지 인스톨러가, 전통적인 yum, apt 대신에 apk 사용한다. 그리고 기본 쉘이 배시가 아니라 애쉬(ash)이다.

 

명령 설명
apk update 패키지 저장소 목록을 업데이트
apk search <package> 패키지 저장소 목록에서 패키지를 검색. v 옵션으로 설명 확인 가능.
apk add <package[=version]> 패키지를 설치. 버전을 명시하여 설치 가능.
apk del <package> 특정 패키지를 제거
apk cache clean 패키지 캐시를 모두 제거
apk cache -v sync 패키지 캐시를 초기화하고 부족한 패키지를 자동으로 다운로드
apk stats 현재 패키지 저장소에서 사용가능한 패키지나 설치된 패키지 등의 정보를 출력
apk fix 복구가 필요한 패키지를 복구하거나 종속성을 제외하고 업그레이드 진행.
apk info 현재 설치된 모든 패키지 정보 나열. -L 옵션을 사용하면 패키지내 파일 정보 출력.

 

 

[ 컨테이너에서 알파인 리눅스를 사용할까?]

지극히 개인적인 생각이다. 컨테이너 이미지 파일을 만들 , 베이스 이미지 위에 필요한 다양한 패키지를 설치하게 된다. 당연히 베이스 이미지 + 기타 필요한 패키지 크기만큼 이미지 사이즈가 되므로 빌드 용량도 커지게 된다. 그리고 이를 보관 배포하기 위해 스토리지 비용 네트워크 비용이 증가할 있다. 사실 스토리지 비용은 컴퓨팅 비용중에 가장 저렴한 부분 하나이다. 그래서 요즘은 용량 고민은 많이 하지 않는 편이다. 특히 온프레미스 환경에서 직접적으로 모든 것을 운용할 때에는 더더욱 그렇다. 하지만 클라우드 환경에선, 모든것이 비용으로 청구되기 때문에 최소한의 사이즈로 운영하면 비용 최적화에 많은 도움이 있을 것이라 생각한다.

 

 

 

[참고자료]

l   Alpine Linux:Overview : https://wiki.alpinelinux.org/wiki/Alpine_Linux:Overview

l   Alpine Linux  : https://namu.wiki/w/Alpine%20Linux

 

 

 

2023-09-04 / Sungwook Kang / https://sungwookkang.com

 

 

리눅스, Linux, 알파인 리눅스, Apline Linux, APK 명령어, 리눅스 배포판

'Linux' 카테고리의 다른 글

[Linux] 리눅스 디스크 용량 확인  (0) 2023.09.26
[Linux] 쉘(shell) 스크립트 기초  (0) 2023.09.05
[Linux] OOM(Out of Memory)란?  (0) 2023.08.30
CnetOS 방화벽 설정  (0) 2015.07.22
CentOS 파일 압축/풀기  (0) 2015.07.22

[MySQL] 성능 모니터링을 위한 Performance_Schema 개념

 

l  Version : MySQL 5.X, 8.X

 

MySQL에서 다양한 성능 관리 지표를 얻기 위해 사용하는 정보는 performance_schema (이하 성능 스키마”) information_schema이다. 이번 포스트에서는 performance_schema 관한 전체적인 개념을 다루며 항목에 대한 자세한 세부 내용은 별도 포스팅 한다.

 

 MySQL performance_schema 로우레벨에 실행되며, 런타임으로 서버 내부를 검사한다. 사용자가 자주 사용하는 것은 디폴트로 활성화되어 있기 때문에 따로 설정하지 않아도 정보를 있지만, 외에는 필요에 따라 설정을 활성화해야 한다. performance_schema 정보를 수집하는 instruments 수집한 정보를 저장하는 consumers 설정하여 이용하는 구조이다.

l  Performance_schema : 성능 데이터 검사에 사용.

l  Information_schema : 메타데이터 검사에 사용

 

  performance_schema information_schema
주요 목적 성능 데이터의 수집 메타 데이터의 수집
애플리케이션 성능 개선 감시 도구이자 관리 도구
도입된 버전 5.5 5
SQL 표준 MySQL 전용 기능 O
설치방법 스토리지 엔진으로 설치 infomation schema API
데이터 수집 방법 mysqld 내부에서 런타임 수집 information schema 테이블 접근 시
통상적인 오버 헤드 있음 없음
출력에 의한 오버 헤드 적음 많음

 

Information_schema 경우 DROP DATABASE 명령으로 삭제할 없다. 이유는 SYSTEM VIEW 이기 때문이다. 따라서 일반 사용자에게 all privileges on *.* 권한을 주더라도 삭제할 없다. 스키마의 table_type 아래와 같다.

l   Information_schema : SYSTEM VIEW

l   mysql : BASE TABLE

l   performance_schema : BASE TABLE

l   sys : VIEW or BASE TABLE

 

performance_schema 이벤트는 서버의 바이너리 로그(데이터 수정을 설명) 기록된 이벤트 이벤트 스케줄러 이벤트(저장 프로그램 유형) 다르다. 성능 스키마 이벤트는 MySQL 서버의 특정 인스턴스에만 적용된다. 성능 스키마 테이블은 서버에 로컬로 간주되며 해당 테이블에 대한 변경 사항은 바이너리 로그에 복제되거나 기록되지 않는다.

 

[Performance_Schema 스토리지 엔진]

performance_schema 스토리지 엔진은 서버 소스 코드의 "계측 지점(Instrumentation Point)" 사용하여 이벤트 데이터를 수집한다. 수집된 이벤트는 Performance_schema 데이터베이스의 테이블에 저장된다. 이러한 테이블은 다른 테이블과 마찬가지로 SELECT 문을 사용하여 쿼리할 있다. 그리고 성능 스키마의 테이블은 지속적인 온디스크 스토리지를 사용하지 않는 인메모리 테이블이기 때문에, 수집된 내용은 서버 시작 다시 채워지고 서버 종료 삭제된다.

 

아래 쿼리를 사용하여 performance_schema 어떤 스토리지 엔진을 사용하는지 확인할 있다.

SELECT * FROM INFORMATION_SCHEMA.ENGINES WHERE ENGINE = 'PERFORMANCE_SCHEMA';

 

 

결과를 살펴보면 전용 PERFORMANCE_SCHEMA 스토리지 엔진을 사용하는 것을 확인할 있으며, Transactions 항목을 보면 NO 되어 있으므로 SQL 사용할 수는 있지만 InnoDB 와는 다르게 Transaction 지원하지 않는다는 것을 있다.

 

성능 스키마 구성은 SQL 문을 통해performance_schema 데이터베이스의 테이블을 업데이트하여 동적으로 수정할 있다. 구성 변경은 데이터 수집에 즉시 영향을 미친다. 데이터 수집은 계측을 추가하기 위해 서버 소스 코드를 수정하는 방식으로 구현된다. 복제나 이벤트 스케줄러와 같은 다른 기능과 달리 성능 스키마와 관련된 별도의 스레드가 없다

 

 

[Performance_Schema 동작 방식]

Performance_schema 동작은 가지로 나눌 있다.

l   Instrument(인스트루먼트) : 정보를 얻고자 하는 MySQL 코드를 나타냄

l   Consumer(컨슈머) : 어떤 코드를 수행하였는지에 대한 정보를 저장

 

Instrument(인스트루먼트)

performance_schema setup_instruments 테이블에는 지원되는 모든 인스트루먼트 목록이 포함되어 있으며, 모든 인스트루먼트 명은 슬래시로 구분해서 구성되어 있다. 가지 예시를 살펴보자.

l   statement/sql/select

l   wait/synch/mutex/innodb/autoinc_mutex

인스트루먼트 이름의 가장 왼쪽이 인스트루먼트의 종류를 의미한다. 따라서 위의 예시에서 statement 인스트루먼트가 sql 문장임을 나태내고 있으며, wait/ 대기 관련된 내용을 나타내고 있다. 인스트루먼트에서 수집할 있는 성능 정보 항목은 performance_schema.setup_instruments 에서 확인 있다. 그룹별로 개의 인스투르먼트가 있는지 확인한다.

select substring_index(name, '/', 1) as category, count(*) from setup_instruments group by substring_index(name, '/', 1);

 

 

인스트루먼트의 상세 목록은 아래 쿼리를 사용한다. MySQL 5.7 버전에서는 1032개의 인스트루먼트가 있다.

select * from performance_schema.setup_instruments;

 

 

 

Consumer(컨슈머)

컨슈머는 인스트루먼트가 정보를 보내는 대상을 의미한다. Performance_schema 인스트루먼트 결과를 다양한 테이블에 저장하게 된다. performance_schema 종류는 아래 쿼리로 확인할 있으며, 테이블의 내용은 SELECT 구문으로 조회할 있다. 이번 포스트에서는 테이블의 기능 역할을 설명하지는 않는다. MySQL 5.7 기준으로 89개의 테이블이 있다. MySQL 8.0 경우에는 110 테이블이 존재한다. 버전별로 특성이 있기 때문에 구분해서 사용할 있도록 한다.

use performance_schema;
show tables;

 

 

 

[Performance_Schema 활성화]

인스트루먼트를 활성화하거나 비활성화 하는 방법은 performance_schema에서 수집하는 성능 지표 항목에 따라 활성 비활성을 있다. 아래 쿼리로 목록을 조회하고, 결과를 살펴보면 ENABLED 컬럼의 결과로 활성화 여부를 확인할 있다.

select * from performance_schema.setup_instruments;

 

 

 setup_instruments 테이블에서는 ENABLED TIMED 칼럼을 업데이트하여 기능을 활성화할 있습니다. UPDATE 문으로 이들 값을 'YES' 변경하면 활성화되고, 'NO' 변경하면 비활성화된다. setup_instruments TIMED 칼럼값을 'YES' 변경하여 활성화하면 instruments 타이밍 정보를 수집할 있다. memory 카테고리의 instruments 계측하는 구조가 없으므로 TIMED 칼럼을 'YES' UPDATE해도 쿼리는 성공하지만 카테고리 레코드 값은 'NO' 남아 있어 활성화할 없다.

update performance_schema.setup_instruments set ENABLED='YES', TIMED='YES' where name='statement/sql/select';

 

인스트루먼트의 활성/비활성화를 쿼리로 업데이트한 경우, 영구적이지 않기 때문에 MySQL 서버 재시작시 다시 적용해주어야 한다. 그렇기 때문에 영구 적용을 위해서는 my.cnf 시스템 변수로 설정한다.

mysql.cnf

performance-schema-instrument='wait/synch/mutex/sql/LOCK_status=ON'
performance-schema-instrument='wait/synch/mutex/innodb/autoinc_mutex=ON'

 

컨슈머를 활성화하는 방법도 인스트루먼트와 유사하게 performance_schema.setup_consumers 테이블을 update하거나 my.cnf 시스템 변수에 등록한다.

 

 

 

[참고자료]

l   Chapter 1 MySQL Performance Schema : https://dev.mysql.com/doc/mysql-perfschema-excerpt/8.0/en/performance-schema.html

l   MySQL Performance Schema - 성능 스키마 : https://hoing.io/archives/3811

l   MySQL performance-schema-instruments 사용에 따른 성능 영향 실험 : https://engineering.linecorp.com/ko/blog/mysql-research-performance-schema-instruments#:~:text=performance_schema%EB%8A%94%20%EC%A0%95%EB%B3%B4%EB%A5%BC%20%EC%88%98%EC%A7%91,%EC%84%A4%EC%A0%95%ED%95%98%EC%97%AC%20%EC%9D%B4%EC%9A%A9%ED%95%98%EB%8A%94%20%EA%B5%AC%EC%A1%B0%EC%9E%85%EB%8B%88%EB%8B%A4.

 

 

 

 

2023-09-02 / Sungwook Kang / http://sungwookkang.com

 

MySQL, 성능 모니터, performance_schema, 성능스키마, MySQL모니터링, MySQL 시스템 정보 수집

[MySQL] “innodb_table_stats” not found 오류와 예상하지 못한 사이드 이펙트

 

l  Version : MySQL 5.7

 

MySQL 5.7에서 서비스 재시작 아래와 같은 오류가 로그에 잔뜩 쌓여있다. MySQL 에러 로그 위치는 일반적으로 아래와 같다.

sudo cat /var/log/mysql/error.log

 

2023-08-31T05:15:56.437819Z 2598 [ERROR] InnoDB: Table `mysql`.`innodb_table_stats` not found.
2023-08-31T05:15:56.437825Z 2598 [ERROR] InnoDB: Fetch of persistent statistics requested for table `XXXX`.`wp_46549_XXXXX ` but the required system tables mysql.innodb_table_stats and mysql.innodb_index_stats are not present or have unexpected structure. Using transient stats instead.

 

서비스를 재시작 했을 뿐인데 이와 같은 로그가 발생한 것일까? 이유를 살펴보니 MySQL 5.6부터는 mysql database 통계정보나 slave 관련 정보를 저장하기 위해 새롭게 InnoDB 테이블이 추가되었다.

l  innodb_index_stats

l  innodb_table_stats

l  slave_master_info

l  slave_relay_log_info

l  slave_worker_info

MySQL 5.5 데이터를 MySQL 5.6이나 MySQL 5.7 업그레이드 했을때, mysql_upgrade 작업을 추가로 해주지 않으면 mysql database 구조는 5.5 되돌아간다. 그렇기 때문에 반드시 mysql_upgrade 진행해 주어야 한다.

 

발생하는 오류를 해결하기 위해서는 아래 방법을 실행한다.

1.       관련 테이블 삭제

USE mysql;
DROP TABLE innodb_index_stats;
DROP TABLE innodb_table_stats;
DROP TABLE slave_master_info;
DROP TABLE slave_relay_log_info;
DROP TABLE slave_worker_info;

 

2.       MySQL 서비스 중지 mysql database 존재하는 ibd 파일 삭제 (frm 파일은 DROP TABLE 삭제되었음)

cd /var/lib/mysql/mysql/
 
rm -rf innodb_index_stats.ibd
rm -rf innodb_table_stats.ibd
rm -rf slave_master_info.ibd
rm -rf slave_relay_log_info.ibd
rm -rf slave_worker_info.ibd

 

3.       MySQL 서비스 시작

4.       mysql 강제 업그레이드를 진행한다. 작업을 통해서 존재하지 않는 5 테이블이 새로 생성된다.

mysql_upgrade –force -uroot –p

 

MySQL 업그레이드가 진행되면 아래와 같은 메시지들이 출력되며 업그레이드를 진행한다. 전체 스키마가 진행되는 모든 로그가 출력되기 때문에 테이블이 많은 데이터베이스라면 시간이 오래 걸린다. (나의 경우 테이블 갯수가 십만개 존재하는 데이터베이스였다.)

root@XXXXX:/var/lib/mysql/mysql# mysql_upgrade --force -uroot -p
Enter password:
Checking server version.
Running queries to upgrade MySQL server.
mysql_upgrade: [ERROR] 1146: Table 'mysql.plugin' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.servers' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_topic' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_category' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_relation' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_keyword' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone_name' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone_transition' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone_transition_type' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone_leap_second' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.gtid_executed' doesn't exist
mysql_upgrade: [ERROR] 1243: Unknown prepared statement handler (stmt) given to EXECUTE
mysql_upgrade: [ERROR] 1243: Unknown prepared statement handler (stmt) given to DEALLOCATE PREPARE
mysql_upgrade: [ERROR] 1146: Table 'mysql.server_cost' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.server_cost' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.engine_cost' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.engine_cost' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.plugin' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.servers' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.gtid_executed' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_category' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_topic' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_topic' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_category' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_relation' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.help_keyword' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.plugin' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.servers' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone_leap_second' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone_name' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone_transition' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.time_zone_transition_type' doesn't exist
mysql_upgrade: [ERROR] 1146: Table 'mysql.servers' doesn't exist
Checking system database.
mysql.columns_priv                                 OK
mysql.db                                           OK
mysql.engine_cost
Error    : Table 'mysql.engine_cost' doesn't exist
status   : Operation failed
mysql.event                                        OK
mysql.func                                         OK
mysql.general_log                                  OK
mysql.gtid_executed
Error    : Table 'mysql.gtid_executed' doesn't exist
status   : Operation failed
mysql.help_category
Error    : Table 'mysql.help_category' doesn't exist
status   : Operation failed
mysql.help_keyword
Error    : Table 'mysql.help_keyword' doesn't exist
status   : Operation failed
mysql.help_relation
Error    : Table 'mysql.help_relation' doesn't exist
status   : Operation failed
mysql.help_topic
Error    : Table 'mysql.help_topic' doesn't exist
status   : Operation failed
mysql.innodb_index_stats                           OK
mysql.innodb_table_stats                           OK
mysql.ndb_binlog_index                             OK
mysql.plugin
Error    : Table 'mysql.plugin' doesn't exist
status   : Operation failed
mysql.proc                                         OK
mysql.procs_priv                                   OK
mysql.proxies_priv                                 OK
mysql.server_cost
Error    : Table 'mysql.server_cost' doesn't exist
status   : Operation failed
mysql.servers
Error    : Table 'mysql.servers' doesn't exist
status   : Operation failed
mysql.slave_master_info                            OK
mysql.slave_relay_log_info                         OK
mysql.slave_worker_info                            OK
mysql.slow_log                                     OK
mysql.tables_priv                                  OK
mysql.time_zone
Error    : Table 'mysql.time_zone' doesn't exist
status   : Operation failed
mysql.time_zone_leap_second
Error    : Table 'mysql.time_zone_leap_second' doesn't exist
status   : Operation failed
mysql.time_zone_name
Error    : Table 'mysql.time_zone_name' doesn't exist
status   : Operation failed
mysql.time_zone_transition
Error    : Table 'mysql.time_zone_transition' doesn't exist
status   : Operation failed
mysql.time_zone_transition_type
Error    : Table 'mysql.time_zone_transition_type' doesn't exist
status   : Operation failed
mysql.user                                         OK
 
Repairing tables
mysql.engine_cost
Error    : Table 'mysql.engine_cost' doesn't exist
status   : Operation failed
mysql.gtid_executed
Error    : Table 'mysql.gtid_executed' doesn't exist
status   : Operation failed
mysql.help_category
Error    : Table 'mysql.help_category' doesn't exist
status   : Operation failed
mysql.help_keyword
Error    : Table 'mysql.help_keyword' doesn't exist
status   : Operation failed
mysql.help_relation
Error    : Table 'mysql.help_relation' doesn't exist
status   : Operation failed
mysql.help_topic
Error    : Table 'mysql.help_topic' doesn't exist
status   : Operation failed
mysql.plugin
Error    : Table 'mysql.plugin' doesn't exist
status   : Operation failed
mysql.server_cost
Error    : Table 'mysql.server_cost' doesn't exist
status   : Operation failed
mysql.servers
Error    : Table 'mysql.servers' doesn't exist
status   : Operation failed
mysql.time_zone
Error    : Table 'mysql.time_zone' doesn't exist
status   : Operation failed
mysql.time_zone_leap_second
Error    : Table 'mysql.time_zone_leap_second' doesn't exist
status   : Operation failed
mysql.time_zone_name
Error    : Table 'mysql.time_zone_name' doesn't exist
status   : Operation failed
mysql.time_zone_transition
Error    : Table 'mysql.time_zone_transition' doesn't exist
status   : Operation failed
mysql.time_zone_transition_type
Error    : Table 'mysql.time_zone_transition_type' doesn't exist
status   : Operation failed
The sys schema is already up to date (version 1.5.2).
Checking databases.

 

5.       MySQL 서비스를 재시작 오류 로그를 확인하여 정상적으로 동작하는지 확인한다.

 

 

[Side Effect]

CASE 1.

mysql_upgrade 작업을 완료 MySQL 서비스를 재시작 하였더니 이번엔 새로운 로그가 기록되고 있었다. 다행이 오류 수준이 [ERROR] 아닌 [WARNING]이었지만 뭔가 이상하다. 발생한 로그는 아래와 같다.

2023-08-31T06:17:51.827559Z 25 [Warning] InnoDB: Cannot open table XXXX/XXXX_XXXX_indexable from the internal data dictionary of InnoDB though the .frm file for the table exists. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue.
2023-08-31T06:17:51.827751Z 25 [Warning] InnoDB: Cannot open table XXXX/XXXX_XXXX _indexable_hierarchy from the internal data dictionary of InnoDB though the .frm file for the table exists. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue.
2023-08-31T06:17:51.828046Z 25 [Warning] InnoDB: Cannot open table XXXX/XXXX_XXXX _yoast_migrations from the internal data dictionary of InnoDB though the .frm file for the table exists. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue.
2023-08-31T06:17:51.828290Z 25 [Warning] InnoDB: Cannot open table XXXX/XXXX_XXXX _primary_term from the internal data dictionary of InnoDB though the .frm file for the table exists. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue.
2023-08-31T06:17:51.829080Z 25 [Warning] InnoDB: Cannot open table XXXX/XXXX_XXXX from the internal data dictionary of InnoDB though the .frm file for the table exists. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue.
2023-08-31T06:17:51.829516Z 25 [Warning] InnoDB: Cannot open table XXXX/XXXX_XXXX from the internal data dictionary of InnoDB though the .frm file for the table exists. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue.

 

로그는 데이터베이스 복구 과정에서 발생한 것이다. 문제는 위와 같은 내용을 에러로그에 기록하면서 CPU 메모리를 엄청 많이 소비했다는 것이다. 현재 개발 환경이므로 CPU 2Core, Memory 32GB 환경에 MySQL buffer cache 3GB 세팅 되어 있다. 그런데 모니터링 그래프를 보면 CPU 100% 사용에 물리 메모리를 모두 소진한 것을 확인할 있다.

 

 

그런데 실제 버퍼캐시 메모리 사용은 거의 없었다. 예상하기는 mysql 시스템 작업을 위해 별도 스레드에서 사용한거 같은데, 중요한건 작업이 완료된 후에도 메모리가 반환되지 않는 상태로 mysqld 프로세스가 계속 사용중으로 유지되고 있었다.

 

작업이 완료되고, CPU 사용량이 정상화된 , MySQL 서비스 재시작으로 메모리를 회수할 있었으며, 이후 정상적인 메모리 상태를 유지하고 있다.

 

CASE 2.

당시 상황을 스크린샷으로 남기지 못했지만, 상황에서 평소 1GB 미만의 메모리 사용량을 사용하는 pmm-agent에서 10GB 이상의 메모리 사용량을 보이면서 이상현상을 보이고 있었다. pmm-agent 서비스를 재시작 해보았지만 동일 증상이 계속되었다. 그래서 pmm client 완전히 제거 재설치 하여 해당 문제를 해결하였다. 모니터링 솔루션이기 때문에 해당 문제의 원인을 찾아야 안정적인 라이브 서비스를 있을 듯한데, 원인을 아직 못찾았다.  예상으로는 perfschema 모니터링 하면서 변경된 사항을 추적하느라 발생하는 것으로 예상만 하고 있다. (다음에 동일한 상황이 재현되면 상세히 확인해 예정)

 

개발 서버에서 발생한 문제이다 보니 다양한 상태를 모니터링 하기 위한 설정이 미흡하여 많은 정보를 수집하지 못해 근본적인 원인을 찾지 못하는 아쉬움이 남는 트러블슈팅이었다. 모니터링은 과하다 싶을 정도로 많이 해서 문제가 발생했을 빠르고 정확하게 해결할 있는 정보로 활용할 있도록 하는 것이 매우 중요하다.

 

2023-09-01 / Sungwook Kang / http://sungwookkang.com

 

MySQL, innodb_index_stats, MySQL업그레이드, MySQL 트러블슈팅, MySQL Error

[Linux] OOM(Out of Memory)?

 

l  Linux

 

몇일전부터 개발 환경에서 겪던 문제로 멀쩡하던 MySQL 서비스가 갑자기 연결이 되지 않는다. 처음엔 단순히 현상인줄 알고, MySQL 서비스를 재시작 하였다. 그런데 동일한 상황이 하루에도 여러 발생하고 있어서 원인을 살펴보기로 했다. 원인을 살펴보니, 아래와 같은 로그가 있었다.

Aug 30 01:35:08 xxxxx kernel: [7189052.423131] [18540]   111 18540  1930392  1535479 12935168        0             0 mysqld
Aug 30 01:35:08 xxxxx kernel: [7189052.423132] Out of memory: Kill process 18540 (mysqld) score 512 or sacrifice child
Aug 30 01:35:08 xxxxx kernel: [7189052.424065] Killed process 18540 (mysqld) total-vm:7721568kB, anon-rss:6141916kB, file-rss:0kB, shmem-rss:0kB
Aug 30 01:35:08 xxxxx kernel: [7189052.749286] oom_reaper: reaped process 18540 (mysqld), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

 

OOM으로 인한 OOM Killer(강제 서비스 종료)였다. OOM 무엇이고 발생하는 것일까? 그리고 로그는 어떻게 확인할 있을까?

 

OOM (Out of Memory)

Linux swap 메모리와 물리 메모리를 모두 사용 , 프로세스에서는 필요에 의해 추가적인 물리 메모리 할당을 요청하였는데, 이상 할당할 물리 메모리 공간이 없어 할당이 불가능한 상태를 의미한다. 메모리 Over Commit 상태일 발생하는 현상으로 물리적으로 메모리가 부족하다는 뜻이다. 이러한 문제는 물리 메모리만 증설하면 사실 간단히 해결된다. 하지만 이러한 현상이 발생하는지 원인을 찾지 못한 상태에서 무한정 메모리를 증설할 수도 없다.

 

OOM Killer

서버 메모리 부족으로 OOM 발생하였을 , 리눅스에서는 메모리를 확보하기 위해 OOM Score 기준으로 점수가 높은 프로세스를 강제로 Kill 하여 메모리를 확보하는 Kernel 기능이다. 이렇게 OOM Killer 프로세스가 종료되면 내부 시스템 로그에 기록되어 추후 확인할 있다.

 

OOM Killer Log 확인

OOM Killer 프로세스가 종료되었을 때에는 각종 시스템 로그에서 확인할 있다. 시스템 로그에는 OOM Killer외에도 다양한 로그가 있기 때문에 로그 양이 많을 경우 필터를 해서 검색을 해야 수도 있다. 일반적으로 특별히 로그 경로를 수정하지 않았다면 아래 경로에서 다양한 시스템 로그를 확인할 있다.

 /var/log/

 

Kern.log에서는 아래와 같은 OOM 로그를 확인할 있었다.

cat kern.log

 

Aug 30 01:35:08 XXXXX kernel: [7189052.423131] [18540]   111 18540  1930392  1535479 12935168        0             0 mysqld
Aug 30 01:35:08 XXXXX kernel: [7189052.423132] Out of memory: Kill process 18540 (mysqld) score 512 or sacrifice child
Aug 30 01:35:08 XXXXX kernel: [7189052.424065] Killed process 18540 (mysqld) total-vm:7721568kB, anon-rss:6141916kB, file-rss:0kB, shmem-rss:0kB
Aug 30 01:35:08 xxxxx kernel: [7189052.749286] oom_reaper: reaped process 18540 (mysqld), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

 

syslog에서는 다양한 로그가 많이 기록되어 OOM이라는 필터를 사용하여 확인하였다.

cat syslog | grep oom

 

Aug 29 23:45:33 xxxxx kernel: [7182478.018455]  oom_kill_process+0x21f/0x420
Aug 29 23:45:33 xxxxx kernel: [7182478.018529] [ pid ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
Aug 29 23:45:34 xxxxx kernel: [7182478.495925] oom_reaper: reaped process 22648 (mysqld), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
Aug 30 01:35:08 xxxxx kernel: [7189052.422998] icinga2 invoked oom-killer: gfp_mask=0x14200ca(GFP_HIGHUSER_MOVABLE), nodemask=(null), order=0, oom_score_adj=0
Aug 30 01:35:08 xxxxx kernel: [7189052.423021]  oom_kill_process+0x21f/0x420
Aug 30 01:35:08 xxxxx kernel: [7189052.423096] [ pid ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
Aug 30 01:35:08 xxxxx kernel: [7189052.749286] oom_reaper: reaped process 18540 (mysqld), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

 

message 로그 파일에서도 동일한 오류 내역을 확인할 있다. Message 로그 파일은 다양한 로그 파일의 미러 역할을 하기 때문에 정말 다양한 로그가 기록되어 있다.

 

 

OOM killer 우선순위

로그를 살펴보면 OOM 의해 mysqld kill 되었음을 확인할 있다. 그렇다면 프로세스를 종료시키는 우선순위는 어떻게 정해질까? 가지 방법이 있다.

1.          oom_badness() 메소드에서 프로세스별 점수를 계산하여 종료시킬 프로세스를 선택한다.

2.          사용자가 특별히 지정하여 순위를 변동시킬 있다.

 

oom_badness() 사용하는 우선순위 선정 방법은 아래와 같다.

1.          특정 프로세스를 함으로써 최소한의 프로세스만 잃을 있어야 한다.

2.          많은 메모리를 회수할 있는 프로세스 (메모리 사용량이 작으면 되지 않는다.)

3.          프로세스중 Leak 발생하지 않는 프로세스 (릭이 발생하면 프로세스 Kill 의미가 없다)

4.          사용자가 특별히 지정한 프로세스

 

외에도 nice score 기반으로 우선순위를 선정할 있다.

 

 

OOM Killer score 확인

OOM score 확인 방법은 /proc/${pid}/oom_score 파일을 통해 조회할 있다. PID 확인하는 방법은 ps 명령을 사용한다.

l  ps : shell에서 실행중인 프로세스만 출력

l  ps -e : 실행 중인 모든 프로세스 출력

l  ps -f : 실행중인 모든 프로세스의 상태 정보 출력

l  ps -ef | grep mysql : grep 사용하여 특정 프로세스(mysql) PID 확인 한다.

 

ps -e | grep mywql

 

 

cat /proc/15368/oom_score

 

 

 

OOM Killer 순위 설정 방법

1.          특정 프로세스의 PID 조회

2.          /proc/PID/oom_adj 파일에 -17 입력. oom_adj -17 ~ 15 값을 가지며, 낮은 일수록 후순위.

3.          /proc_PID/oom_scroe_adj 파일에 -1000 입력. oom_score_adj -1000 ~ 1000 값을 가지며 낮은 값일수록 후순위.

 

아래 예시는 oom_adj 파일에 -17 변경하는 것으로 PID 변경하여 사용할 있다.

sudo echo -17 > /proc/${pid}/oom_ajd

 

아래 예시는 oom_score_adj파일에 -1000 변경하는 것으로 PID 변경하여 사용할 있다.

sudo echo -1000 > /proc/${pid}/oom_score_adj

 

 

메모리 누수로 인한 System hang 현상을 방지하기 위해 리눅서 커널에서는 OOM killer 자체는 비활성화 없기 때문에 순위 설정을 참고하여 중요한 프로세스는 Kill 되지 않도록 한다.

 

 

 

 

2023-08-30 / Sungwook Kang / https://sungwookkang.com

 

 

리눅스, Linux, OOM, OutOfMemory, 메모리부족현상, 강제킬, 프로세스확인, 메모리압박, 아웃오브메모리, OOMKiller, oom_score

'Linux' 카테고리의 다른 글

[Linux] 쉘(shell) 스크립트 기초  (0) 2023.09.05
[Linux] Alpine Linux (알파인 리눅스) 는 무엇일까?  (0) 2023.09.04
CnetOS 방화벽 설정  (0) 2015.07.22
CentOS 파일 압축/풀기  (0) 2015.07.22
CentOS 심볼 링크 설정  (0) 2015.07.22

MySQL PMM(Percona Monitoring and Management) 소개 설치

 

l  Version : MySQL, MariaDB, PMM

 

PMM Percona Monitoring and Management 약자로 PostgreSQL MySQL, MongoDB 위한 오픈소스 데이터베이스 모니터링 관리 솔루션이다. 이를 통해 데이터베이스 시스템의 상태를 관찰하고, 동작의 새로운 패턴을 탐색하고, 문제를 해결하고, 온프레미스나 클라우드 어디에 있든 데이터베이스 관리 작업을 수행할 있다. PMM 데이터베이스와 해당 호스트로부터 수천 개의 기본 성능 지표를 수집한다. 그리고 UI 대시보드를 제공하여 데이터를 시각화 한다. 추가 기능에는 데이터베이스 상태 평가를 위한 조언을 제공하기도 한다.

 

 

 

PMM 서버, 클라이언트 구조로 노드들에 PMM Client 설치하여 중앙의 PMM 서버로 데이터를 전송하는 방식이다. 화면을 보면 그라파나(Grafana) 대시보드를 사용하는 것을 있다. 그리고 데이터를 수집하는 에이전트는 프로메테우스(Prometheus) 사용한다.

 

PMM Client

l  pmm-admin : 각종 exporter들을 구동 제어한다. pmm-admin 노드 관리를 효과적으로 하기 위해, PMM서버에 포함된 Consul 서버에 exporter 에이전트 접속 정보(아이피와 포트) 등록한다. Prometheus 수집주기마다, Consul 등록된 에이전트 리스트를 가져와서 모니터링 메트릭을 수집한다.

l  node_expoter, mysql_expoter : 모니터링 대상 노드(서버) 서버에 데몬 형태로 구동되는 에이전트로, Prometheus에서 에이전트 포트로 메트릭 요청을 하면 현재 서버의 상태를 전송하는 역할을 한다.

 

PMM Server

l   QNAP : DB서버를 모니터링 , 시스템의 메트릭 뿐만 아닌, 슬로우 쿼리나, 각종 이벤트 정보들을 pmm-server 데이터를 전달해야 하는데, Prometeus 이러한 데이터를 받아들이지 않는다. 이러한 데이터를 받기 위해 QAN API 사용한다. expoter에서 pmm-server 80포트를 사용하여 /qan-api 데이터를 전송하면, 내부의 nginx 서비스가 전달한 데이터를 받아서, 127.0.0.1:9001 리다이렉션하여 쿼리를 저장한다.

l   Prometheus : 타임시리즈 기반의 저장소로, 데이터 수집을 비롯해서 저장 그리고 쿼리 질의, Alert 에이전트의 포트에 접근해서, 모니터링 시스템의 메트릭을 Pull 방식으로 끌어오는 역할을 수행한다.

l   Consul : pmm-admin pmm consul 모니터링이 대상들의 아이피와 포트 번호를 넣어주면, Prometheus 정보를 바탕으로 등록된 scrape_interval마다 DB서버 에이전트에 접근하여 모니터링 지표를 수집한다.

l   Grafana : 수집된 데이터를 Web UI 제공한다.

 

 

PMM설치의 경우, 서버는 컨테이너로 제공되며, 클라이언트는 컨테이너 또는 전용 패키지 설치 파일로 제공된다. 이번 포스트에서는 Docker에서 PMM 서버를 실행하고, Client 패키지로 설치한 다음, 모니터링 서버를 등록하는 과정을 살펴본다.

 

[PMM 서버 설치]

도커 이지지를 다운로드 한다.

docker pull percona/pmm-server:2

 

데이터를 저장할 호스트 볼륨을 생성한다.

docker volume create pmm-data

 

이미지를 실행하여 PMM 서비스를 실행한다. 이때, 관리를 위한 443 포트와 UI 제공하기 위한 80포트를 사용하는데, 포트가 충돌나지 않도록 적절히 포트를 포워딩하여사용한다. ( 포스트에서는 도커 사용법을 다루지는 않는다.)

docker run --detach --restart always \
--publish 443:443 --publish 80:80 \
-v pmm-data:/srv \
--name pmm-server \
percona/pmm-server:2

 

서버가 정상적으로 실행되었으면, 웹브라우저에서 PMM 서버가 설치된 IP 입력하여 UI 대시보드에 접속한다. 필자의 경우 컨테이너를 생성할 , 8080포트를 사용하도록 매핑하였다. 기본 ID 패스워드는 admin / admin 이다. 정상적으로 접속이 되면 우선 서버 설치는 정상적으로 완료되었다고 판단하면 된다.

http://172.30.1.49:8080

 

 

 

 

[PMM Client 설치]

모니터링할 대상 서버에는 PMM Client 설치한다. 운영체제에 따라 전용 패키지 또는 설치 파일을 다운로드하여 설치할 있다. 실습 환경은 우분투이며 편의상 패키지로 설치하였다. 환경에 따른 설치 방법은 아래 링크를 참고한다.

l   Set up PMM Client  : https://docs.percona.com/percona-monitoring-and-management/setting-up/client/index.html

 

apt update
apt-get install -y pmm2-client

 

 

Client 설치가 설치가 완료되었으면 PMM 서버에 수집할 클라이언트 목록을 등록해야 한다. 아래 명령어로 등록할 있다.

pmm-admin config --server-insecure-tls --server-url=https://admin:admin@X.X.X.X:443

l   X.X.X.X : PMM 서버 IP 입력한다.

l   443 : 기본 포트 번호. 컨테이너 실행시 포트 번호를 다르게 매핑하였다면 수정해야한다.

l   Admin/admin : PMM 서버의 기본 접속 정보이다. 만약 이전 UI 통해서 수정하였다면 부분도 수정한다.

 

여기까지 진행되었으면 PMM 대시보드에서 모니터링의 대상 서버가 추가된 것을 확인할 있다. 대시보드에서는 Node Names에서 확인할 있다. 정보가 업데이트 되기까지 약간의 시간이 걸릴수도 있다.

 

이번 실습에서는 MySQL 모니터링을 하기 때문에 MySQL 서비스에 대한 모니터링을 있도록 MySQL 등록한다. 사용자 환경에 따라 필요한 서비스를 등록한다.  지원되는 서비스 목록은 아래와 같다.

l   MySQL (and variants Percona Server for MySQL, Percona XtraDB Cluster, MariaDB)

l   MongoDB

l   PostgreSQL

l   ProxySQL

l   Amazon RDS

l   Microsoft Azure

l   Google Cloud Platform (MySQL and PostgreSQL)

l   Linux

l   External services

l   HAProxy

l   Remote instances

 

MySQL 서비스에 대한 정보를 수집할 있도록 PMM Client 사용할 계정 다양한 시스템 환경을 설정한다. 설정 방법은 공식 문서를 참고할 있도록 한다.

l   https://docs.percona.com/percona-monitoring-and-management/setting-up/client/mysql.html

 

이번 포스트에서는 슬로우 쿼리 로그를 모니터링 있도록 서비스를 등록하였다.

pmm-admin add mysql --query-source=slowlog --username=pmm --password=pass

 

대시보드를 살펴보면 MySQL 서비스가 등록되었으며, 관련 지표를 확인할 있다.

 

 

PMM 내부 적으로 알림 시스템(alert-manager) 가지고 있어, 특정 임계치를 설정하였을 , 임계치를 넘으면 알림을 받을 있다. 이번 포스트에서는 다루지 않지만, 이러한 알림 시스템까지 설정하여 활용하면 시스템에 대한 상태를 매번 지켜보지 않아도 문제가 발생하였을 , 메신저 등으로 알림을 받을 있어 빠르게 문제 인지 원인을 파악할 있다.

 

 

[참고자료]

l   https://prometheus.io/docs/introduction/overview/

l   https://docs.percona.com/percona-monitoring-and-management/setting-up/server/docker.html

l   https://docs.percona.com/percona-monitoring-and-management/setting-up/client/mysql.html

l   https://prometheus.io/docs/introduction/overview/

 

 

 

 

 

2023-08-22 / Sungwook Kang / http://sungwookkang.com

 

MySQL, MariaDB, Percona, DB모니터링, PMM, MySQL모니터링, 퍼코나 모니터링, Grafana, Promethus, 슬로우쿼리모니터링

MySQL Galera Cluster + ProxySQL에서 Galera Cluster 특성을 고려한 R/W 호스트 그룹 설정 하기

 

l  Version : MySQL, MariaDB, Galera Cluster, ProxySQL 2.X

 

Galera Cluster (이하 갈레라 클러스터”) 다중 마스터 동기 복제를 제공하는 솔루션으로 DB 간의 직접적인 데이터 공유가 없는 형태의 오픈소스 고가용성 솔루션이다.  갈레라 클러스터는 동기 방식의 복제구조를 사용하는 멀티마스터 RDB 클러스터로 제공된다.

ProxySQL 여러 데이터베이스에 대해 동일한 커넥션을 재사용할 있도록 멀티 플렉싱 기능과 쿼리를 분석하여 Write/Read 분산하는 다양한 기능을 제공하는 솔루션이다.

l  ProxySQL 이란 무엇인가 : https://sungwookkang.com/1528

l  MySQL/MariaDB 환경에서 다중 마스터 복제를 지원하는 Galera Cluster 알아보기 : https://sungwookkang.com/1536

 

ProxySQL 2.x 부터는 Galera Cluster 좀더 유연하게 지원한다. 이번 포트스에서는 MySQL Galera cluster + ProxySQL 구성된 환경에서 Galera Cluster 특성을 고려한 ProxySQL Write, Read 호스트 그룹 전략에 대해서 알아본다.

 

[mysql_galera_hostgroups]

ProxySQL에서 호스트 그룹에 따라 Write, Read 역할을 정의한다. Galera Cluster 환경에서는 mysql_galera_hostgroups에서 해당 노드의 역할을 정의할 있다. 아래 스크립트를 실행하면 mysql_galera_hostgroups 테이블의 정보를 확인할 있다.

select * from mysql_galera_hostgroups\G

 

Admin> select * from mysql_galera_hostgroupsG
*************************** 1. row ***************************
writer_hostgroup: 10
backup_writer_hostgroup: 20
reader_hostgroup: 30
offline_hostgroup: 9999
active: 1
max_writers: 1
writer_is_also_reader: 2
max_transactions_behind: 20
comment:

 

 

l   writer_hostgroup : 쓰기 가능한 (read_only=0) 모든 구성원을 포함할 호스트 그룹의 ID

l   backup_writer_hostgroup : 쓰기 백업 노드의 그룹 ID. 클러스터가 다중 쓰기 모드(read_only=0 여러 노드가 있는 경우)에서 실행 중이고 max_writers 전체 쓰기 노드 수보다 작은 수로 설정된 경우 추가 노드는 backup_writer_hostgroup 으로 이동된다.

l   reader_hostgroup : 읽기 가능한 (read_only=1 노드) 모든 구성원을 포함할 호스트 그룹의 ID

l   offline_hostgroup : 클러스터에서 참여하지 않는 노드들에 대한 그룹ID. ProxySQL 모니터링에서 호스트가 OFFLINE 것으로 확인되면 호스트가 offline_hostgroup으로 이동된다.

l   Active : 호스트 그룹을 활성화하는 부울 (0 또는 1)

l   max_writers : writer 호스트 그룹에서 허용되는 최대 노드 수를 제어한다. Writer_hostgroup 노드 수가 max_writers 수보다 경우 추가 노드는 backup_writer_hostgroup으로 이동된다.

l   writer_is_also_reader : 1이면 writer_hostgroup 노드가 reader_hostgroup에도 배치되어 읽기에 사용된다. 2 설정하면 backup_writer_hostgroup 노드가 writer_hostgroup 노드 대신 reader_hostgroup 배치된다.

l   max_transactions_behind : 오래된 읽기를 방지하기 위해 노드가 SHUNNED되기 전에 클러스터의 노드가 대기할 있는 최대 쓰기 세트 수를 결정한다. (wsrep_local_recv_queue Galera 변수를 쿼리하여 결정됨).

l   Comment : 사용자가 정의한 목적에 맞게 사용할 있는 텍스트 필드

 

 

ProxySQL MySQL 상태 변수를 모니터링하여 Galera 상태 확인을 수행한다.

l   read_only : 설정값이 ON 경우, writer_is_also_reader 1 아니면 ProxySQL 정의된 호스트를 reader_hostgroup으로 그룹화한다.

l   wsrep_desync : 설정값이 ON 경우, ProxySQL 노드를 사용할 없는 것으로 표시하여 offline_hostgroup으로 이동한다.

l   wsrep_reject_queries : 설정값이 ON이면 ProxySQL 노드를 사용할 없는 것으로 표시하고 이를 offline_hostgroup으로 이동한다. (특정 유지 관리 상황에서 유용함).

l   wsrep_sst_donor_rejects_queries : 설정값이 ON이면 ProxySQL Galera 노드가 SST 기증자 역할을 하는 동안 해당 노드를 사용 불가로 표시하여 offline_hostgroup으로 이동한다.

l   wsrep_local_state : 해당 값이 동기화를 의미하는 4 이외의 값을 반환하면 ProxySQL 노드를 사용할 없는 것으로 표시하고 offline_hostgroup으로 이동한다.

l   wsrep_local_recv_queue : 해당 값이  max_transactions_behind보다 높으면 노드가 회피된다.

l   wsrep_cluster_status : 상태가 기본이 아닌 다른 상태로 반환되면 ProxySQL 노드를 사용할 없는 것으로 표시하고 offline_hostgroup으로 이동한다.

 

ProxySQL 2.x mysql_galera_hostgroups 매개변수와 mysql_query_rules 정책을 함께 결합함으로써 훨씬 다양한 Galera Cluster 구성에 대한 유연성을 갖게 되었다. 예를 들어 쿼리 규칙의 대상 호스트 그룹으로 정의된 단일 쓰기, 다중 쓰기 다중 읽기 호스트 그룹을 가질 있으며, 쓰기 수를 제한하고 오래된 읽기 동작을 보다 세밀하게 제어할 있다.

 

ProxySQL에서 max_writers writer_is_also_reader 변수는 ProxySQL 백엔드 MySQL 서버를 동적으로 그룹화하는 방법을 결정할 있으며 커넥션 분산 쿼리 라우팅에 직접적인 영향을 끼친다. 예를 들어 아래와 같이 MySQL 백엔드(노드) 서버가 등록되어 있다고 가정한다. 등록된 노드는 아래 스크립트로 확인 가능하다.

 

Admin> select hostgroup_id, hostname, status, weight from mysql_servers;

 

Admin> select hostgroup_id, hostname, status, weight from mysql_servers;
+--------------+--------------+--------+--------+
| hostgroup_id | hostname     | status | weight |
+--------------+--------------+--------+--------+
| 10           | DB1          | ONLINE | 1      |
| 10           | DB2          | ONLINE | 1      |
| 10           | DB3          | ONLINE | 1      |
+--------------+--------------+--------+--------+

 

그리고 현재 구성되어 있는 mysql_galera_hostgroup 설정도 살펴본다.

Admin> select * from mysql_galera_hostgroups\G

 

 

Admin> select * from mysql_galera_hostgroups\G
*************************** 1. row ***************************
writer_hostgroup: 10
backup_writer_hostgroup: 20
 reader_hostgroup: 30
offline_hostgroup: 9999
active: 1
max_writers: 1
writer_is_also_reader: 2
max_transactions_behind: 20
comment:

 

현재 노드 설정에서 모든 호스트가 가동되어 실행 중임을 고려하면 ProxySQL 호스트를 아래와 같이 다양한 시나리오로 그룹화할 있다.

 

그림에 따른 3가지 케이스의 특징을 한번 살펴본다.

Configuration Description
writer_is_also_reader=0 l  호스트를 2개의 호스트 그룹(writer backup_writer)으로 그룹화한다.
l  Writer backup_writer 일부이다.
l  Write Reader 아니므로 read_only=1 설정된 호스트가 없기 때문에 호스트 그룹 30(Reader)에는 아무것도 없다. 읽기 전용 플래그를 활성화하는 것은 Galera에서 일반적인 관행이 아니다.
writer_is_also_reader=1 l  호스트를 3개의 호스트 그룹(writer, backup_writer reader)으로 그룹화한다.
l  Galera 변수 read_only=0 영향을 미치지 않으므로 writer 호스트 그룹 30(reader)에도 있다.
l  writer backup_writer 일부가 아니다.
writer_is_also_reader=2 l  writer_is_also_reader=1 유사하지만 writer backup_writer 일부이다.

 

그룹 구성에 따른 특성을 활용하면 사용자는 특정 워크로드에 맞는 호스트 그룹 대상을 다양하게 선택할 있다. "핫스팟" 쓰기는 다중 마스터 충돌을 줄이기 위해 하나의 서버로만 이동하도록 구성할 있다. 충돌하지 않는 쓰기는 다른 마스터에 균등하게 분배될 있다. 쓰기는 최신 서버로 전달될 있으며 분석 읽기는 슬레이브 복제본으로 전달될 있다.

 

[Galera 클러스터용 ProxySQL 배포]

아래 그림과 같이 ClusterControl 의해 배포된 3노드 Galera Cluster 구성되어 있다고 가정하고, 시나리오를 만족하기 위해 클러스터를 세팅하는 과정을 다루어 본다.

 

시나리오는 아래와 같다.

WordPress 애플리케이션은 Docker에서 실행되고 WordPress 데이터베이스는 베어메탈 서버에서 실행되는 Galera Cluster에서 호스팅 된다. 우리는 WordPress 데이터베이스 쿼리 라우팅을 제어하고 데이터베이스 클러스터 인프라를 완전히 활용하기 위해 WordPress 컨테이너와 함께 ProxySQL 컨테이너를 실행하기로 결정했다. 읽기-쓰기 비율이 80%-20%이므로 ProxySQL 다음과 같이 구성하려고 한다.
l   모든 쓰기를 하나의 Galera 노드로 전달(충돌 감소, 쓰기에 집중)
l   다른 Galera 노드에 대한 모든 읽기의 균형을 분배 (대부분의 워크로드에 대해 나은 분배).

 

시나리오를 만족할 있도록 ProxySQL 구성한다. 먼저 컨테이너에 매핑할 있도록 Docker 호스트 내부에 ProxySQL 구성 파일을 만든다.

$ mkdir /root/proxysql-docker
$ vim /root/proxysql-docker/proxysql.cnf

 

아래와 같이 설정값을 입력한다. (코드에 대한 자세한 설명은 아래부분에서 하나씩 설명한다.)

datadir="/var/lib/proxysql"
 
admin_variables=
{
    admin_credentials="admin:admin"
    mysql_ifaces="0.0.0.0:6032"
    refresh_interval=2000
    web_enabled=true
    web_port=6080
    stats_credentials="stats:admin"
}
 
mysql_variables=
{
    threads=4
    max_connections=2048
    default_query_delay=0
    default_query_timeout=36000000
    have_compress=true
    poll_timeout=2000
    interfaces="0.0.0.0:6033;/tmp/proxysql.sock"
    default_schema="information_schema"
    stacksize=1048576
    server_version="5.1.30"
    connect_timeout_server=10000
    monitor_history=60000
    monitor_connect_interval=200000
    monitor_ping_interval=200000
    ping_interval_server_msec=10000
    ping_timeout_server=200
    commands_stats=true
    sessions_sort=true
    monitor_username="proxysql"
    monitor_password="proxysqlpassword"
    monitor_galera_healthcheck_interval=2000
    monitor_galera_healthcheck_timeout=800
}
 
mysql_galera_hostgroups =
(
    {
        writer_hostgroup=10
        backup_writer_hostgroup=20
        reader_hostgroup=30
        offline_hostgroup=9999
        max_writers=1
        writer_is_also_reader=1
        max_transactions_behind=30
        active=1
    }
)
 
mysql_servers =
(
    { address="db1.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
    { address="db2.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
    { address="db3.cluster.local" , port=3306 , hostgroup=10, max_connections=100 }
)
 
mysql_query_rules =
(
    {
        rule_id=100
        active=1
        match_pattern="^SELECT .* FOR UPDATE"
        destination_hostgroup=10
        apply=1
    },
    {
        rule_id=200
        active=1
        match_pattern="^SELECT .*"
        destination_hostgroup=30
        apply=1
    },
    {
        rule_id=300
        active=1
        match_pattern=".*"
        destination_hostgroup=10
        apply=1
    }
)
 
mysql_users =
(
    { username = "wordpress", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 },
    { username = "sbtest", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 }
)

 

코드에서 살펴보면 mysql_galera_hostgroups 구성은 아래와 같이 정의되어 있다.

mysql_galera_hostgroups =
(
    {
        writer_hostgroup=10
        backup_writer_hostgroup=20
        reader_hostgroup=30
        offline_hostgroup=9999
        max_writers=1
        writer_is_also_reader=1
        max_transactions_behind=30
        active=1
    }
)

 

writer 그룹은 10, backup 그룹은 20, reader 그룹은 30으로 정의 되어있다. max_writers 1 설정하여 모든 쓰기가 전송되어야 하는 호스트 그룹 10 대해 단일 쓰기로 작동하도록 하였다. 그런 다음 writer_is_also_reader 1 정의하여 모든 Galera 노드를 읽기 노드에 참여할 있도록 하였다. 이렇게 하면 읽기 요청은 모든 노드에 균등하게 배포할 있다. Offline 그룹은 9999이며 ProxySQL 작동하지 않는 Galera 노드를 감지한 경우 offline_hostgroup으로 이동시킨다.

 

mysql_servers 클러스터에 참여하는 노드 정보를 정의한다. 시나리오에서는 모든 노드를 쓰기 가능한 호스트 그룹인 10으로 MySQL 서버를 구성하였다.

mysql_servers =
(
    { address="db1.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
    { address="db2.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
    { address="db3.cluster.local" , port=3306 , hostgroup=10, max_connections=100 }
)

 

위의 구성을 그림으로 표현하면 ProxySQL 아래와 같이 호스트 그룹을 구성하고 인식한다.

 

 

mysql_query_rules 쿼리 라우팅을 정의한다. 요구 사항에 따라 모든 읽기는 작성자(호스트 그룹 20) 제외한 모든 Galera 노드로 전송되어야 하며 다른 모든 것은 단일 작성자의 호스트 그룹 10으로 전달된다.

mysql_query_rules =
(
    {
        rule_id=100
        active=1
        match_pattern="^SELECT .* FOR UPDATE"
        destination_hostgroup=10
        apply=1
    },
    {
        rule_id=200
        active=1
        match_pattern="^SELECT .*"
        destination_hostgroup=20
        apply=1
    },
    {
        rule_id=300
        active=1
        match_pattern=".*"
        destination_hostgroup=10
        apply=1
    }
)

 

마지막으로 ProxySQL 통해 전달될 MySQL 사용자를 정의한다.

mysql_users =
(
    { username = "wordpress", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 },
    { username = "sbtest", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 }
)

 

우리는 transaction_persistent 0으로 설정하여 사용자로부터 오는 모든 연결이 읽기 쓰기 라우팅에 대한 쿼리 규칙을 준수하도록 한다. 그렇지 않으면 연결이 하나의 호스트 그룹에 도달하게 되어 로드 밸런싱의 목적이 상실하기 때문이다. 모든 MySQL 서버에서 해당 사용자를 먼저 생성하는 것을 잊지 않도록 한다. ClusterControl 활용해서 사용자를 추가하는 경우 [관리] -> [스키마 사용자 기능] 사용하여 해당 사용자를 생성할 있다.

 

설정 파일 생성이 완료되었으면 이제 컨테이너를 실행한다. ProxySQL 컨테이너를 시작할 ProxySQL 구성 파일을 바인드 마운트로 매핑하기 때문에 아래와 같은 명령어로 실행한다.

$ docker run -d
--name proxysql2
--hostname proxysql2
--publish 6033:6033
--publish 6032:6032
--publish 6080:6080
--restart=unless-stopped
-v /root/proxysql/proxysql.cnf:/etc/proxysql.cnf
severalnines/proxysql:2.0

 

마지막으로 ProxySQL 컨테이너 포트 6033 가리키도록 WordPress에서 데이터베이스 연결 포트를 변경한다.

$ docker run -d
--name wordpress
--publish 80:80
--restart=unless-stopped
-e WORDPRESS_DB_HOST=proxysql2:6033
-e WORDPRESS_DB_USER=wordpress
-e WORDPRESS_DB_HOST=passw0rd
wordpress

 

지금까지의 서비스 구성을 그림으로 표현하면 아래 아키텍처와 같다.

 

 

ProxySQL 컨테이너를 영구적으로 유지하려면 /var/lib/proxysql/ Docker 볼륨에 매핑하거나 바인드를 마운트 해야한다. 아래 스크립트는 컨테이너 실행시 설정 파일을 영구 저장소로 바인드하는 예제이다.

$ docker run -d
--name proxysql2
--hostname proxysql2
--publish 6033:6033
--publish 6032:6032
--publish 6080:6080
--restart=unless-stopped
-v /root/proxysql/proxysql.cnf:/etc/proxysql.cnf
-v proxysql-volume:/var/lib/proxysql
severalnines/proxysql:2.0

 

위와 같이 영구 저장소로 실행하면 컨테이너를 다시 시작 /root/proxysql/proxysql.cnf 사용하지 않는 다는 것에 주의한다. 이는 /var/lib/proxysql/proxysql.db 존재하는 경우 ProxySQL 구성 파일에서 로드 옵션을 건너뛰고 대신 SQLite 데이터베이스에 있는 항목을 로드하는 ProxySQL 다중 계층 구성 때문이다. (initial 플래그로 proxysql 서비스를 시작하면 설정 파일의 값으로 시작한다.)  ProxySQL 구성 관리는 구성 파일을 사용하는 대신 포트 6032 ProxySQL 관리 콘솔을 통해 수행해야 한다.

 

ProxySQL 서비스 시작 사용하는 구성파일에 대한 자세한 정보는 아래 글을 참고한다.

l   ProxySQL 서비스에 필요한 설정값을 어디에 저장하고 재사용할까? : https://sungwookkang.com/1535

 

 

[Monitoring]

ProxySQL 프로세스 로그는 기본적으로 syslog 기록되며 표준 docker 명령을 사용하여 있다.

$ docker ps
$ docker logs proxysql2

 

현재 ProxySQL에서 운영중인 호스트 그룹을 확인하려면 runtime_mysql_servers 테이블을 쿼리한다. 아래 스크립트는 호스트 환경에서 컨테이너의 ProxySQL 관리 콘솔에 접속한다.

$ docker exec -it proxysql2 mysql -uadmin -padmin -h127.0.0.1 -P6032 --prompt='Admin> '

 

$ docker exec -it proxysql2 mysql -uadmin -padmin -h127.0.0.1 -P6032 --prompt='Admin> '
Admin> select hostgroup_id,hostname,status from runtime_mysql_servers;
+--------------+--------------+--------+
| hostgroup_id | hostname     | status |
+--------------+--------------+--------+
| 10           | 192.168.0.21 | ONLINE |
| 30           | 192.168.0.21 | ONLINE |
| 30           | 192.168.0.22 | ONLINE |
| 30           | 192.168.0.23 | ONLINE |
| 20           | 192.168.0.22 | ONLINE |
| 20           | 192.168.0.23 | ONLINE |
+--------------+--------------+--------+

 

만약 특정 쓰기 노드가 다운되면 해당 노드는 offline_hostgroup (HID 9999)으로 이동된다. 예제에서는 192.168.0.21 노드가 다운되어 9999 그룹으로 이동되었다.

Admin> select hostgroup_id,hostname,status from runtime_mysql_servers;
+--------------+--------------+--------+
| hostgroup_id | hostname     | status |
+--------------+--------------+--------+
| 10           | 192.168.0.22 | ONLINE |
| 9999         | 192.168.0.21 | ONLINE |
| 30           | 192.168.0.22 | ONLINE |
| 30           | 192.168.0.23 | ONLINE |
| 20           | 192.168.0.23 | ONLINE |
+--------------+--------------+--------+

 

위의 토폴로지 변경 사항은 아래 그림과 같다.

 

 

ProxySQL에서 admin-web_enabled=true 설정할 경우 통계 UI 사용할 있다. UI 액세스하려면 포트 웹브라우저로 Docker 호스트6080 포트로 접속한다. (, http://192.168.0.200:8060) 사용자 이름을 묻는 메시지가 표시되면 admin-stats_credentials 정의된 자격 증명을 입력한다. 로그인하면 아래와 같은 통계 화면을 있다.

 

 

MySQL 연결 테이블을 모니터링하면 모든 호스트 그룹에 대한 연결 분포 개요를 확인할 있다.

Admin> select hostgroup, srv_host, status, ConnUsed, MaxConnUsed, Queries from stats.stats_mysql_connection_pool order by srv_host;

 

Admin> select hostgroup, srv_host, status, ConnUsed, MaxConnUsed, Queries from stats.stats_mysql_connection_pool order by srv_host;
+-----------+--------------+--------+----------+-------------+---------+
| hostgroup | srv_host     | status | ConnUsed | MaxConnUsed | Queries |
+-----------+--------------+--------+----------+-------------+---------+
| 20        | 192.168.0.23 | ONLINE | 5        | 24          | 11458   |
| 30        | 192.168.0.23 | ONLINE | 0        | 0           | 0       |
| 20        | 192.168.0.22 | ONLINE | 2        | 24          | 11485   |
| 30        | 192.168.0.22 | ONLINE | 0        | 0           | 0       |
| 10        | 192.168.0.21 | ONLINE | 32       | 32          | 9746    |
| 30        | 192.168.0.21 | ONLINE | 0        | 0           | 0       |
+-----------+--------------+--------+----------+-------------+---------+

 

위의 출력 값을 살펴보면 쿼리 규칙에 의해 호스트 그룹 30 아무 것도 처리하지 않음을 있다.

 

Galera 노드와 관련된 통계는 mysql_server_galera_log 테이블에서 있다.

>  select * from mysql_server_galera_log order by time_start_us desc limit 3 \G

 

Admin>  select * from mysql_server_galera_log order by time_start_us desc limit 3 \G
*************************** 1. row ***************************
                       hostname: 192.168.0.23
                           port: 3306
                  time_start_us: 1552992553332489
                success_time_us: 2045
              primary_partition: YES
                      read_only: NO
         wsrep_local_recv_queue: 0
              wsrep_local_state: 4
                   wsrep_desync: NO
           wsrep_reject_queries: NO
wsrep_sst_donor_rejects_queries: NO
                          error: NULL
*************************** 2. row ***************************
                       hostname: 192.168.0.22
                           port: 3306
                  time_start_us: 1552992553329653
                success_time_us: 2799
              primary_partition: YES
                      read_only: NO
         wsrep_local_recv_queue: 0
              wsrep_local_state: 4
                   wsrep_desync: NO
           wsrep_reject_queries: NO
wsrep_sst_donor_rejects_queries: NO
                          error: NULL
*************************** 3. row ***************************
                       hostname: 192.168.0.21
                           port: 3306
                  time_start_us: 1552992553329013
                success_time_us: 2715
              primary_partition: YES
                      read_only: NO
         wsrep_local_recv_queue: 0
              wsrep_local_state: 4
                   wsrep_desync: NO
           wsrep_reject_queries: NO
wsrep_sst_donor_rejects_queries: NO
                          error: NULL

 

쿼리 결과 내용은 특정 타임스탬프에 대한 모든 Galera 노드의 관련 MySQL 변수/상태 상태를 반환한다. 구성에서는 Galera 상태 확인이 2초마다 실행되도록 구성되어 있다. (monitor_galera_healthcheck_interval=2000). 따라서 클러스터에 토폴로지 변경이 발생하는 경우 최대 장애 조치 시간은 2초이다.

 

지금까지 MySQL Galera 클러스터 환경에서 ProxySQL 조합하여 사용할 , 쓰기, 읽기 그룹에 대한 정의와 ProxySQL 컨테이너로 구성하는 과정을 다루었다. 실제 운영환경에서는 각자의 노드 구성과 SLA 모두 다르기 때문에 내용을 이해하고 활용할 있도록 한다.

 

[참고자료]

l   How to Run and Configure ProxySQL 2.0 for MySQL Galera Cluster on Docker : https://severalnines.com/blog/how-run-and-configure-proxysql-20-mysql-galera-cluster-docker/

l  ProxySQL 이란 무엇인가 : https://sungwookkang.com/1528

l  MySQL/MariaDB 환경에서 다중 마스터 복제를 지원하는 Galera Cluster 알아보기 : https://sungwookkang.com/1536

l  ProxySQL 서비스에 필요한 설정값을 어디에 저장하고 재사용할까? : https://sungwookkang.com/1535

 

 

 

2023-08-10 / Sungwook Kang / http://sungwookkang.com

 

MySQL, MariaDB, Galera Cluster, 마리아디비, 마이에스큐엘, 갈레라 클러스터, MariaDB 복제, MariaDB Cluster, MySQL Cluster, MariaDB Replication, MySQL, ProxySQL, ProxySQL설정, ProxySQL 구성관리, ProxySQL 시작, Proxy Container

[Kubernetes] 쿠버네티스에서 파드 생성시 프라이빗 레지스트리 이미지 사용하기

 

l  Kubernetes

 

Kubernetes 환경에서 pod 생성할 Private Registry에서 이미지를 다운받아 실행하는 방법에 대해서 알아본다.

 

현재 구성되어 있는 실습 환경은 MAC OS + PODMAN + MINIKUBE이다. 일반적으로 많이 사용하는 docker 환경은 아니지만 사용법이 거의 유사하기 때문에 따라하는데 크게 문제가 없으리라 생각한다. Minikube 설치 Podman 설치는 공식 문서에 쉽게 설명되어 있기 때문에 여기에서는 다루지 않는다.

 

l  Podman Installation Instructions : https://podman.io/docs/installation

l  minikube start : https://minikube.sigs.k8s.io/docs/start/

 

실습 시나리오는 아래와 같다.

1.       Dockerfile 작성

2.       컨테이너 이미지 빌드

3.       Docker Hub 개인 저장소에 이미지 푸시

4.       쿠버네티스에서 프라이빗 시크릿키 생성

5.       YAML 파일 작성

6.       Pod 생성

 

사용자 커스텀 이미지를 만들기 위해 Dockerfile 작성한다. Dockerfile 작성에 관한 방법은 다른 문서를 참고한다. 도커 허브의 공식 이미지 저장소에서는 Dockerfile 대한 샘플코드를 제공한다. 아래는 proxysql 공식 저장소이다.

l   https://hub.docker.com/r/proxysql/proxysql

 

 

 

 Dockerfile 작성이 완료되었으면, 이미지를 빌드 한다. Podman 명령어는 도커 명령어와 거의 유사하기 때문에 어려움 없이 사용할 있다. 아래 스크립트는 Dockerfile 빌드하여 컨테이너 이미지를 생성하고, 현재 생성되어 있는 이미지를 확인한다.

podman build -t proxysql-sw:1.0.0 .
 
podman images

 

 

 

podman에서는 도커 레지스트리에 push하기 위해 로그인 과정이 필요하다. 도커 허브의 로그인은 아래와 같은 명령어를 사용한다. 로그인은 ID, 패스워드로 인증한다.

podman login docker.io

 

 

 

이미지를 푸시 하기 위해서는 이미지 ID 필요하다. Podman images명령을 사용하여 확인한 이미지ID 사용하여 도커 허브의 프라이빗 레지스트리로 푸시 한다.

podman push 561f3e4999fddocker.io/<개인저장소 경로>

 

 

쿠버네티스에서 파드 생성시 사용할 프라이빗 레지스트리에 대한 시크릿 키를 생성한다. 시크릿 키에 대한 생성은 여러 방법이 있지만 여기에서는 커맨드 라인에서 자격증명을 통한 시크릿 키를 생성한다. 아래 예제에서는 시크릿 이름은 regcred 생성한다.

kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>

 

l   <your-registry-server> : 프라이빗 도커 저장소의 FQDN 주소로, 도커허브(DockerHub) https://index.docker.io/v1/ 사용

l   <your-name> : 도커 사용자의 계정

l   <your-pword> : 도커 사용자의 비밀번호

l   <your-email> : 도커 사용자의 이메일 주소

 

키가 정상적으로 생성되었는지는 아래 명령으로 확인 가능하다. 명령을 실행하면 키에 대한 자세한 정보를 확인할 있다.

kubectl get secret regcred --output=yaml

 

 

쿠버네티스에서 파드 생성시 사용할 YAML 파일을 작성한다. YAML에서 시크릿 키를 사용할 있도록 imagePullSecrets 항목에서 시크릿키 이름을 입력한다.

apiVersion: v1
kind: Pod
metadata:
  name: private-reg
spec:
  containers:
  - name: private-reg-container
    image: <your-private-image>
  imagePullSecrets:
  - name: regcred

 

 

YAML파일을 사용하여 쿠버네티스 파드를 생성한다.

kubectl apply -f my-private-reg-pod.yaml

 

 

도커허브의 개인 저장소 이미지를 사용하여 파드가 정상적으로 생성된 것을 확인할 있다.

kubectl get pod 

 

 

 

지금까지 도커 허브의 프라이빗 레지스트리에서 이미지를 받아 파드를 생성하는 방법에 대해서 알아보았다. 원래 나의 목적은 로컬( 컴퓨터)에서 이미지를 빌드한 다음 로컬 저장소의 이미지를 사용하여 쿠버네티스 파드를 생성하려고 하였는데, podman에서 생성한 이미지를 minikube에서 찾지 못하여 (정확히는 내가 모르는 ) 도커 허브를 사용하는 방향으로 실습하였다. 이후 작업으로 로컬 저장소에서 파드를 생성하는 방법을 추가적으로 알아볼 예정이다.

 

 

[참고자료]

l  Podman Installation Instructions : https://podman.io/docs/installation

l  minikube start : https://minikube.sigs.k8s.io/docs/start/

l  docker hub : https://hub.docker.com/

l  프라이빗 레지스트리에서 이미지 받아오기 : https://kubernetes.io/ko/docs/tasks/configure-pod-container/pull-image-private-registry/

 

 

 

 

2023-08-09 / Sungwook Kang / https://sungwookkang.com

 

 

쿠버네티스, Kubernetes, 미니쿠베, minikube, 도커, 도커허브, 컨테이너, 도커파일, dockerfile, yaml, 도커 레지스트리, 프라이빗 레지스트리, podman, 파드맨, 도커빌드. 이미지빌드

MySQL/MariaDB 환경에서 다중 마스터 복제를 지원하는 Galera Cluster 알아보기

 

l  Version : MySQL, MariaDB, Galera Cluster

 

Galera Cluster (이하 갈레라 클러스터”) 다중 마스터 동기 복제를 제공하는 솔루션으로 DB 간의 직접적인 데이터 공유가 없는 형태의 오픈소스 고가용성 솔루션이다.  현재 갈레라 클러스터는 MySQL Percona Xtradb 클러스터와 MariaDB 가지 버전을 지원한다. 특히 MariaDB에서는 Galera Cluster 공식적으로 지원한다. 이번 포스트에서는 갈레라 클러스터의 개념과 장단점에 대해서 알아본다.

 

갈레라 클러스터는 동기 방식의 복제구조를 사용하는 멀티마스터 RDB 클러스터로 제공된다. 갈레라 클러스터는 인증 기반 복제를 제공하며 노드간 통신을 위해 wsrep API 사용한다. 데이터 복제는 논리적으로는 완전 동기이지만 실제 write tablespace commit하는 과정은 별개이고 노드간에는 비동기로 동작한다. 그래서 갈레라 클러스터에서는 이러한 방식을 virtually synchronous replication이라 부른다.

 

 

[Galera Cluster 특징]

l  다중 쓰기 : Active-Active 방식의 다중 Master 구조이기 때문에 모든 노드에서 Read/Write 가능하다.

l  동기 복제 : 슬레이브 지연이 없고 노드 충돌시 데이터 손실이 없다.

l  일관적 데이터 유지 : 모든 노드는 항상 같은 데이터 상태를 유지 한다.

l  장애조치 : 노드 장애가 발생하였을 빠르게 모니터링 되어 전환되므로 서비스 중단이 최소화 된다. 멀티 포인트 쓰기를 지원하므로 전환이 쉽다. 특정 노드 장애가 전체 서비스에 영향을 미치지 않는다. 이론적으로 모든 노드가 쓰기 가능하기 때문에 모든 노드가 다운되기 전까지는 서비스 유지가 가능하다.

l  자동 노드 복제 : 노드를 추가하거나 관리를 위해 종료 또는 노드에서 제거 해야 경우 증분 데이터 또는 기본 데이터를 수동으로 백업할 필요가 없다. 갈레라 클러스터는 노드를 추가할 경우 자동으로 온라인 노드의 데이터를 가져오고 일관성을 유지한다.

 

[Galera Cluster 단점]

l   동기 복제 특성상 클러스터의 성능은 가장 성능이 낮은 노드에 의해 결정된다. 그래서 최대한 동일한 성능의 서버로 구성하는 것이 좋다.

l   동기 복제이기 때문에 쓰기가 많이 발생할 경우 다른 아키텍처(비동기 복제 )보다 상대적으로 성능이 저하되며 스케일 아웃에 한계가 있다.

l   신규 노드가 조인되거나 대규모 노드 조인시 지연이 발생하면 데이터 전체를 복사(SST, 상태 스냅샷 전동) 하는 문제가 발생한다. 이때 데이터를 제공하는 서버를 Donor라고 하는데 Donor 노드는 복제 데이터를 전송하는 동안 읽기 쓰기 서비스가 중단된다.

l   인증 기반 복제를 사용하기 때문에 인증이 실패할 경우 데드락이 발생할 가능성이 있다.

 

[Galera Cluster 제약사항]

l   MyISAM 일부와 InnoDB 스토리지 엔진 테이블만 지원한다.

l   노드 간의 쿼리 실행 순서를 피하기 위해 모든 테이블에서 기본키를 필수적으로 사용한다.

n  기본 키가 없는 테이블에서는 DELETE 작업이 지원되지 않는다.

n  기본 키가 없는 테이블의 행은 다른 노드에서 다른 순서로 나타날 있다.

l   Query Cache 비활성화한다

l   XA 트랜잭션 (전역 트랜잭션) 지원하지 않는다.

l   일반 쿼리 로그와 슬로우 쿼리 로그는 테이블로 보낼 없다. 이러한 로그를 활성화하는 경우 log_output=FILE 설정하여 로그를 파일로 전달해야 한다.

 

 

Galera Cluster wsrep(Write-Set Replication) API 통해 노드와 데이터를 동기화 한다. 여기서 wsrep API DBMS 복제를 위한 인터페이스이며 실제 동기화 구현은 Galera Replication Plugin에서 이루어 진다.

 

 

노드에서 쓰기나 업데이트가 발생할 경우 노드간에 데이터를 복제하고 업데이트 내용을 GCache 영역에 저장한다. GCache 복제된 트랜잭션을 위한 임시 저장소 역할을 한다.

 

 

Galera Cluster 인증기반 복제를 사용한다. 인증 기반 복제의 주요 컨셉은 트랜잭션이 충돌이 없다고 가정하고 커밋 지점에 도달할 때까지 관례적으로 실행된다는 것이다. 이를 낙관적 실행이라고 한다.

Galera Cluster에서 인증 기반 복제 구현은 트랜잭션의 전역 순서에 따라 다르다. Galera Cluster 복제 중에 트랜잭션에 전역 시퀀스 번호 또는 seqno 할당한다. 트랜잭션이 커밋 지점에 도달하면 노드는 마지막으로 성공한 트랜잭션의 시퀀스 번호와 시퀀스 번호를 확인한다. 사이의 간격을 통해 모든 트랜잭션은 해당 트랜잭션과 기본 충돌이 있는지 확인한다. 충돌이 감지되면 인증 테스트에 실패한다.

  그림에서 데이터가 변경되고 커밋되기 까지의 순서를 나타내면 아래와 같다.

1.          클라이언트가 데이터를 수정하고 커밋 요청을 서버에 요청한다.

2.          서버는 커밋 요청을 받으면 실제 커밋을 실행하기 전에 트랜잭션 변경된 행의 기본 키에 의해 데이터베이스에 대한 모든 변경 사항이 쓰기 세트로 수집된다.

3.          데이터베이스는 쓰기 세트를 다른 모든 노드로 전송한다.

4.          쓰기 세트는 기본 키를 사용하여 결정적 인증 테스트를 거친다. 작업은 쓰기 세트를 생성한 노드를 포함하여 클러스터의 노드에서 수행된다.

5.          노드가 쓰기 세트를 적용할 있는지 여부를 결정한다.

6.          인증 테스트에 실패하면 노드는 쓰기 세트를 삭제하고 클러스터는 원래 트랜잭션을 롤백한다. 그러나 테스트가 성공하면 트랜잭션이 커밋되고 쓰기 세트가 클러스터의 나머지 부분에 적용된다.

 

 

Galera Cluster Replication Write 정책은 First Committer Win이다. 트랜잭션이 커밋되는 시점에 다른 노드에 유효한 트랜잭션인지 여부를 체크하는 방식으로 동작한다. 클러스터 내에서 트랜잭션은 모든 서버에 동시에 반영되거나 전부 반영되지 않는 경우 하나로 동작한다. 이러한 이유로 Write 트랜잭션은 하나의 서버로 사용하는 것을 권장한다.

트랜잭션을 시작하는 시점(BEGIN)에는 자신의 노드에서는 Pessimistic Locking으로 동작하나, 노드 사이에서는 Optimistic Locking Model 동작한다. 먼저 트랜잭션을 자신의 노드에 수행을 하고, 커밋을 시점에 다른 노드로부터 해당 트랜잭션에 대한 유효성을 확인한다. 일반적으로 InnoDB 같이 트랜잭션을 지원하는 시스템인 경우 SQL 시작되는 시점에서 Lock 확인할 있으나, 갈레라 클러스터에서는 커밋되는 시점에 노드 트랜잭션 유효성 체크한다.

 

 

트랜잭션 커밋 또는 롤백 결정은 네트워크를 통한 다른 노드와 통신에서 결정된다. 결정 요소에는 아래 항목들의 상태가 포함된다.

l   네트워크 왕복 시간

l   노드에서 유효성 체크 시간

l   노드에서 데이터 반영 시간

 

Galera Cluster 필요한 최소 노드 수는 2개이다. 그러나 최소 3개의 노드가 권장된다. 최대 노드 제한은 없다. 그러나 10 이상의 노드가 있는 단일 클러스터는 네트워크 또는 인터넷에서 너무 많은 노드를 동기화하는 지연이 발생할 있다. 최대 노드 구성은 네트워크 구성에 따라 달라지므로 각자의 환경에 맞게 구성하도록 한다.

 

Galera Cluster 트랜잭션 크기를 명시적으로 제한하지 않지만 쓰기 집합은 단일 메모리 상주 버퍼로 처리되므로 결과적으로 매우 트랜잭션(: LOAD DATA) 노드 성능에 부정적인 영향을 미칠 있다. 이를 방지하기 위해 wsrep_max_ws_rows wsrep_max_ws_size 시스템 변수는 기본적으로 트랜잭션 행을 128K, 트랜잭션 크기를 2Gb 제한한다. 필요한 경우 사용자는 이러한 제한을 늘릴 있다. (향후 버전에서는 트랜잭션 조각화에 대한 지원을 추가할 예정이라고 한다.)

 

 

MySQL MariaDB에서 고가용성을 위한 솔루션으로 Galera Cluster 대해서 살펴보았다. 다양한 솔루션이 있지만 Galera Cluster 특징은 모든 노드가 마스터 역할로 클러스터에 참여하는 구조로 운영되며, 데이터베이스 사이의 연결이 아닌 외부 wsrep API 통한 데이터 복제가 이루어진다는 점에서 흥미로웠다. 이후 클러스터 구성 방법과 장애조치시 마스터 선정, 옵션에 대한 설정 과정을 다른 포스트에서 다뤄볼 예정이다.

 

[참고자료]

l   https://galeracluster.com/library/documentation/

l   http://galeracluster.com/documentation-webpages/galera-documentation.pdf

l   http://galeracluster.com/documentation-webpages/index.html

l   https://www.percona.com/doc/percona-xtradb-cluster/LATEST/index.html

l   https://mariadb.com/kb/en/library/getting-started-with-mariadb-galera-cluster/

l   https://severalnines.com/resources/whitepapers/galera-cluster-mysql-tutorial/

l   https://www.slideshare.net/marcotusa/galera-explained-3

l   MariaDB Galera Cluster - Known Limitations : https://mariadb.com/kb/en/mariadb-galera-cluster-known-limitations/

l   https://galeracluster.com/library/faq.html

l   https://www.slideshare.net/AbdulManaf19/mariadb-galera-cluster-63088921

 

 

 

2023-08-07 / Sungwook Kang / http://sungwookkang.com

 

MySQL, MariaDB, Galera Cluster, 마리아디비, 마이에스큐엘, 갈레라 클러스터, MariaDB 복제, MariaDB Cluster, MySQL Cluster, MariaDB Replication

ProxySQL 서비스에 필요한 설정값을 어디에 저장하고 재사용할까?

 

l  Version : MySQL, ProxySQL

 

ProxySQL에서 서버를 추가하거나, 사용자를 등록하는 설정을 변경하고 적용하려면 관리자 모드 접속을 통해서 필요한 내용을 수정하고, 수정한 내용을 서비스 중단 없이 runtime 적용할 있다. 그리고 서비스 재시작시 기존의 설정을 유지하기 위해서는 현재 설정 값을 데이터베이스 또는 디스크로 저장하여 재사용 있다.

 

아래 그림을 참고하여 설명하면, 일반적으로 관리자 모드에 접속하여 수정하는 부분은 MEMORY영역이다. 그렇기 때문에 관리자에서 수정하였을 경우에는 아직 메모리에서만 적용된 것으로 서비스에 적용되지 않은 상태이다. (아마도 실수가 발생하였을 서비스 보호를 위해서 변경 사항을 자동으로 적용시키지 않는 모델로 디자인한 같다.) 그리고 LOAD 명령어를 사용하여 RUNTIME영역에 적용을 있다. RUNTIME 적용되면 서비스에 반영되어 운영된다. 그리고 메모리 값을 수정하였더라도, 런타임 기준으로 메모리 롤백이 필요할 런타임의 값을 메모리로 SAVE하여 RUNTIME설정을 메모리로 저장할 있다. 이러한 과정을 영역별로 LOAD SAVE 있다.

 

 

ProxySQL 서비스를 시작할 config 파일을 읽어들여 datadir 위치를 확인한다. 다음에 datadir 경로에 위치한 데이터베이스 파일 또는 디스크 파일이 있는지 여부를 확인하여 해당 파일을 읽어 설정 값으로 사용한다. 일반적인 설치를 진행하였다면 config 파일은 아래 경로에 존재한다.

/etc/proxysql.cnf

 

 

datadir 경로에서 데이터베이스 파일이 발견되면 ProxySQL 지속형 온디스크 데이터베이스(proxysql.db) 읽어 메모리 구성을 초기화한다. 따라서 디스크는 메모리에 로드된 다음 런타임 구성으로 전파된다. 데이터베이스 파일이 없는 경우에는 config 파일의 설정 값을 읽어 메모리 데이터베이스에 적용한 다음 온디스크 데이터베이스에 저장되고 런타임에 적용한다.

 

설정 값을 사용할 데이터베이스 파일이 발견되면 config 파일의 설정 값은 사용되지 않는다는 점에 유의해야 한다. , 정상적인 시작 중에 ProxySQL 지속형 온디스크 데이터베이스에서만 메모리 구성을 초기화 한다.

 

ProxySQL 시작할 config 파일의 설정값을 메모리 런타임에 사용하기 위해서는 아래와 같은 순서를 적용할 있다.

1.       최신의 Config 파일을 배포한다. 일반적인 설치를 진행하였다면 아래 경로에 설정 파일이 위치한다.

/etc/proxysql.cnf

 

2.       배포한 Config 파일에 적절한 권한 부여한다. 실습에서는 775 권한을 부여하였다.

sudo chmod 775 proxysql.cnf

 

3.       실행중인 ProxySQL 서비스를 중지한다. 서비스 중지전에 유입되는 트래픽은 다른 곳으로 라우팅 되도록 하여 서비스 장애가 발생하지 않도록 주의한다.

sudo service proxysql stop

 

4.       기존의 데이터베이스 설정이 사용되지 않도록 데이터베이스 파일을 삭제한다.

sudo rm -rf /var/lib/proxysql/proxy*

 

5.       ProxySQL 서비스를 시작한다. 이때 initial 옵션을 함께 사용한다.

sudo service proxysql start initial

 

 

실제 현업에서 ProxySQL 운영한다고 하면 필요에 따라 다수의 ProxySQL 운영하는 환경이 발생한다. 이러한 환경에서는 서버의 설정 파일을 수정하고 이력을 관리하는 것이 매우 중요하다. 그렇기 때문에 직접 서버에 접속하여 설정 파일을 수정하는 것은 이력이 남지 않아 매우 불편하면서도 위험한 상황이 발생할 있다. 그래서 정책적으로 config파일을 기준으로 수정하고, 이력 관리는 Git 등을 사용하면 안전한 이력 관리를 있다. 그리고 서비스 시작 데이터베이스 정보가 아닌 config 값을 사용하면 혼란없이 다수의 서버 환경에서 안전한 관리가 가능하다.

 

 

 

[참고자료]

l  Configuration system : https://github.com/sysown/proxysql/blob/master/doc/configuration_system.md

 

 

 

 

2023-08-04 / Sungwook Kang / http://sungwookkang.com

 

MySQL, ProxySQL, ProxySQL설정, ProxySQL 구성관리, ProxySQL 시작

MySQL HA + ProxySQL 환경에서 서비스 장애조치 구성

 

l  Version : MySQL, ProxySQL

 

MySQL HA + ProxySQL 환경에서 장애조치가 어떻게 동작하는지 알아본다. 실습 환경은 아래 포스트를 참고하여 구성할 있도록 한다.

 

l  ProxySQL 설치 (MySQL 설치부터, 복제 구성, ProxySQL 설정까지 한번에) : https://sungwookkang.com/1529

l  MySQL HA 환경에서 Orchestrator 활용한 클러스터 리팩토링 자동 장애조치 구성 : https://sungwookkang.com/1533

 

실습에 사용되는 서버 구성이다.

Server Name IP OS Service Version
proxy-sql 172.30.1.49 Ubuntu 22.04.2 LTS ProxySQL version 2.4.2-0-g70336d4, codename Truls
mysql-master 172.30.1.97 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33
mysql-slave1 172.30.1.10 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33
mysql-slave2 172.30.1.13 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33
proxy-orchestrator 172.30.1.24 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33, container.d

 

MySQL HA 기본 구성으로 1대의 마스터 서버와, 2대의 슬레이브 서버가 GTID 기반으로 복제 구성이 되어 있다. 그리고 서버 장애시 역할 변경을 위해 오케스트레이터로 자동 failover 구성해 놓은 상태이다.

 

ProxySQL에서는 노드의 서비스 상태를 헬스 체크하여 서비스에 사용할 서버를 판단한다. 아래는 노드의 서비스 상태를 체크하는 모듈이다.

l  connect : 모든 노드(백엔드) 연결하고 성공 또는 실패를 mysql_server_connect_log  테이블에 기록한다.

l  ping : 모든 노드 (백엔드) ping 보내고 성공 또는 실패를 mysql_server_ping_log 테이블에 기록한다. mysql-monitor_ping_max_failures 하트비트가 없으면 MySQL_Hostgroups_Manager 신호를 보내 모든 연결을 끊는다.

l  replication lag : max_replication_lag 0보다 값으로 구성된 모든 노드(백엔드) 대해 Seconds_Behind_Master 확인하고 mysql_server_replication_lag_log 테이블에 확인을 기록한다. Seconds_Behind_Master > max_replication_lag 경우 Seconds_Behind_Master < max_replication_lag까지 서버를 회피한다.

l  read only : mysql_replication_hostgroup 테이블의 호스트 그룹에 있는 모든 호스트에 대해 읽기 전용을 확인하고, 상태값은 mysql_server_read_only_log 기록된다. read_only=1이면 호스트가 reader_hostgroup으로 복사/이동되고, read_only=0이면 호스트가 writer_hostgroup으로 복사/이동된다.

 

이번 포스트에서 다루는 ProxySQL 장애조치 시나리오는 가지이다.

l   시나리오 1 : slave 서버에 장애가 발생하여 장애가 발생한 서버로는 read 트래픽을 요청하지 않는다.

l   시나리오2 : master 서버에 장애가 발생하여 slave 서버중 하나가 master 승격되고, ProxySQL 호스트 그룹에서 Write 가능한 호스트 그룹을 변경한다.

 

시나리오에 따른 실습을 하기 전에 현재 구성되어 있는 ProxySQL 접속하여 현재의 상태를 살펴본다. proxy-sql 서버에서 아래와 같은 스크립트를 실행하여, ProxySQL 관리 모드로 접속한다.

mysql -uadmin -padmin -h 127.0.0.1 -P6032 --prompt='admin> '

 

 

현재 호스트 그룹에 등록된 서버들의 목록을 확인할 있다. hostgroup_id = 1 write 가능한 그룹이며, hostgroup_id = 2 읽기가 가능한 그룹이다.

select * from mysql_servers;

 

 

 

아래 스크립트는 등록된 서버들의 커넥션 로그를 확인한다. 만약 여기서 커넥션 오류로그가 있다면 해당 서버와의 통신에 문제가 발생한 것이다.

select * FROM monitor.mysql_server_connect_log order by time_start_us desc limit 10;

 

 

 

위의 커넥션 로그가 실제 쿼리가 호출될 확인된다면, 아래는 서비스 헬스 체크를 위한 로그이다.

select * from monitor.mysql_server_ping_log order by time_start_us desc limit 10;

 

 

 

ProxySQL 클라이언트용 포트로 접속하여 현재 상태에서 정상적으로 모든 서버가 응답하는지 테스트한다.

mysql -uubuntu -p123456 -h127.0.0.1 -P6033 -e "SELECT @@hostname";

 

 

 

아래 스크립트에서는 현재 연결된 커넥션 풀에 대한 상태를 확인할 있다. 실제 연결에 문제가 발생할 경우 stats_mysql_connection_pool status 컬럼에 상태가 변경되는 것을 확인할 있다.

select * from stats_mysql_connection_pool;

 

 

 

시나리오1.  slave 서비스 장애

slave 서버중 하나에 장애가 발생한 상황으로, 장애가 발생한 서버로는 읽기 요청을 하지 않도록 하여 서비스 장애를 예방한다.

 

mysql-slave2 서버에 접속하여, MySQL 서비스를 중지한다.

sudo systemctl stop mysql
 
sudo systemctl status mysql

 

 

서비스가 종료된 상태에서 ProxySQL 관리자의 mysql_servers 테이블 상태를 살펴보면 여전히 모든 서버가 ONLINE 상태로 보이는 것을 확인할 있다. 사실 테이블은 서버의 목록을 관리하는 것이지, 서버의 상태를 나타내지는 않는다. 다만 수동으로 status 값을 OFFLINE으로 변경하면, 서비스가 활성화 상태여도 해당 서버로 트래픽을 보내지 않는다.

select * from mysql_servers;

 

 

 

현재 실시간의 노드의 상태를 확인하려면 runtime_mysql_servers  테이블의 정보를 확인한다. 아래 스크립트를 사용하여 중지된 서버의 상태가 SHUNNED 표시된 것을 확인할 있다.

select * from runtime_mysql_servers;

 

 

 

커넥션 상태에서도 동일하게 SHUNNED으로 나타나는 것을 확인할 있다.

select * from stats_mysql_connection_pool;

 

 

 

커넥션 로그를 확인해보면 중지된 서버(mysql-slave2) 커넥션 실패 로그를 확인할 있다.

select * FROM monitor.mysql_server_connect_log order by time_start_us desc limit 10;

 

 

 

로그에서도 오류를 확인할 있다.

select * from monitor.mysql_server_ping_log order by time_start_us desc limit 10;

 

 

 

mysql-slave2 서버의 MySQL 서비스가 중지된 상태로, ProxySQL에서도 해당 서버의 문제점을 인지한 상태이다. 이제 클라이언트 연결을 통해서 실제 운영중인 서버로만 트래픽이 정상적으로 라우팅 되는지 확인한다. 테스트를 위해 proxy-sql 서버 콘솔에서 아래 스크립트를 여러 실행한다.

mysql -uubuntu -p123456 -h127.0.0.1 -P6033 -e "SELECT @@hostname";

 

 

문제의 서버를 제외하고 정상적인 서버로만 트래픽을 라우팅하는 것을 확인할 있다. ProxySQL에서는 이렇게 문제의 서버를 감지하여 트래픽을 보내지 않음으로써 서비스 장애를 예방할 있다.

 

다른 시나리오 테스트를 진행하기 위해 중지된 서버를 시작하여 클러스터를 원상 복구한다.

 

시나리오2. master 서버 장애

이번 시나리오는 master서버에 장애가 발생하여, 서비스가 다운되고, slave 서버 하나가 master 승격되어 운영되는 환경이다. 이때 장애를 감지하고 자동으로 서버를 승격시키는 역할은 MySQL 오케스트레이터가 한다.

 

mysql-master 서버에 접속하여 MySQL 서비스를 중지한다. 오케스트레이터에서는 master 서버의 장애를 감지하고, 중지된 master 서버를 클러스터에서 격리한 slave 서버중 하나를 master 승격여 클러스터 토폴로지를 재구성한다.

 

새로 구성된 클러스터를 살펴보면 이전 slave역할이었던 mysql-slave2(172.30.1.13)서버가 master 승격된 것을 확인할 있다. 그리고 나머지 slave 서버가 새로운 master 복제하고 있다.

 

 

ProxySQL에서도 커넥션 로그를 확인해보면 기존의 master역할을 하던 mysql-master(172.30.1.97) 문제가 있음을 인지한 것을 확인할 있다.

select * from stats_mysql_connection_pool;

 

 

 

실행중인 서버의 상태를 확인해봐도 문제를 인지한 것을 확인할 있다.

select * from runtime_mysql_servers;

 

 

 

proxy-sql에서 문제가 발생한 노드가 제외되고 서비스가 정상적으로 되는지 확인한다. 호스트 이름을 검색하는 쿼리를 실행해보면 문제없이 작동한다

mysql -uubuntu -p123456 -h127.0.0.1 -P6033 -e "SELECT @@hostname";

 

 

하지만 현재 상태는 정상적인 서비스를 없는 문제가 있다. 현재 클러스터 상태는 기존의 mysql-master (마스터 역할) 서버가 중지되고 mysql-slave2서버가 master 역할로 승격된 상태인데, mysql_servers 테이블의 정보에는 여전히 이전의 mysql-master 서버가 hostgroup_id=1 등록되어 있다. 실질적으로 ProxySQL에서는 Write 가능한 서버를 잘못 인식하고 있는 상태이다. 정보를 업데이트하지 않으면 write 대해서는 계속해서 실패가 발생하게 된다.

이러한 서버 장애시 hostgroup_id 자동으로 변경하기 위해 복제 호스트 그룹으로 관리할 있다. , 이렇게 하기 위해서는 역할에 따른 MySQL 서버의 Read only 속성값이 중요하다. master 역할 서버는 read_only =false, slave 역할의 서버는 read_only=true 되어 있어야 한다. 그렇지 않으면 ProxySQL에서는 read_only=false 서버를 write 가능한 서버로 인식하기 때문에 master-master구조로 인식되어 의도하지 않는 write 오류가 발생할 있다.

 

아래 스크립트는 write 가능한 호스트들은 1 그룹으로, 읽기가 가능한 호스트들은 2 그룹으로 할당한다. 서버의 정보를 변경하였기에 해당 변경 사항이 런타임으로 적용될 있도록 LOAD 명령어도 함께 실행한다.

INSERT INTO mysql_replication_hostgroups VALUES (1,2,'read_only','1=write, 2=read');
 
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;

 

 

 

기존의 클러스터 구성으로 원상 복구하고, mysql-master (마스터 역할) 서버를 중지하였을 , 오케스트레이터에서 자동 장애 조치가 활성화되어 서비스가 중지된 master 서버는 클러스터에서 분리하고, 정상적인 slave 서버중 하나를 master 승격한다. 그리고 ProxySQL에서는 slave에서 master 승격된 서버를 houstgroup_id=1 자동 할당되는 것을 확인할 있다.

 

 

런타임 서버 상태 테이블에서도 장애가 발생한 서버는 SHUNNED 변경되어 서비스에서 제외된 것을 확인할 있으며, 새로운 master 서버가 hostgroup_id=1 변경된 것을 확인할 있다.

 

 

 

이번 실습을 통해서 mysql_servers 테이블은 호스트의 상태 값을 관리하는 테이블이 아닌 호스트 그룹을 관리하기 위한 서버 목록 용도로 사용되는 것을 있었으며, 실제 서비스들의 상태나 운영가능한 호스트들이 목록은 runtime_mysql_servers 테이블에서 확인 가능한 것을 있었다.

 

MySQL 오케스트레이터와 ProxySQL 조합하여 사용할 경우, 오케스트레이터에서는 자동으로 장애를 감지하고 클러스터 토폴로지 재구성으로 빠르게 역할을 승격하여 안정적인 서비스를 유지할 있고, ProxySQL에서는 오케스트레이션에서 승격시킨 서버의 상태를 확인하여 호스트 그룹의 정보를 자동으로 변경함으로써, 만약의 상태에도 안정적인 서비스가 가능한 구성을 만들 있다는 것을 확인할 있다.

 

[참고자료]

l   Orchestrator : https://github.com/openark/orchestrator

l   Failure detection : https://github.com/openark/orchestrator/blob/master/docs/failure-detection.md

l   MySQL Orchestrator - HA(High Availability) - 2 - 리팩토링 Failover Automated Recovery : https://hoing.io/archives/91

l   Configuration: failure detection : https://github.com/openark/orchestrator/blob/master/docs/configuration-failure-detection.md

l   https://code.openark.org/blog/mysql/what-makes-a-mysql-server-failurerecovery-case : https://code.openark.org/blog/mysql/what-makes-a-mysql-server-failurerecovery-case

l   Orchestrator: MySQL Replication Topology Manager : https://www.percona.com/blog/orchestrator-mysql-replication-topology-manager/

l   Orchestrator and ProxySQL : https://www.percona.com/blog/orchestrator-and-proxysql/

 

 

 

 

 

2023-08-02 / Sungwook Kang / http://sungwookkang.com

 

MySQL, ProxySQL, MySQL Replication, MySQL HA, MySQL복제 오케스트레이션, MySQL복제설치, MySQL MHA, MySQL Orchestrator , MySQL장애조치, MySQL고가용성

MySQL HA 환경에서 Orchestrator 활용한 클러스터 리팩토링 자동 장애조치 구성

 

l  Version : MySQL, Orchestrator

 

MySQL HA 환경에서 Orchestrator(이하 오케스트레이터’) 활용하여 클러스터 리팩토링 장애조치 방법에 대해서 알아본다. 이번 실습을 진행하기 위해서는 MySQL HA Orchestrator 구성이 완료되어 있어야 한다. 아래 링크를 참고하여 HA구성 Orchestrator 구성할 있도록 한다.

l  ProxySQL 설치 (MySQL 설치부터, 복제 구성, ProxySQL 설정까지 한번에) : https://sungwookkang.com/1529

l  MySQL 고가용성 운영을 위한 Orchestrator 설치 : https://sungwookkang.com/1532

 

실습에 사용되는 서버 구성이다.

Server Name IP OS Service Version
proxy-sql 172.30.1.49 Ubuntu 22.04.2 LTS ProxySQL version 2.4.2-0-g70336d4, codename Truls
mysql-master 172.30.1.97 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33
mysql-slave1 172.30.1.10 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33
mysql-slave2 172.30.1.13 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33
proxy-orchestrator 172.30.1.24 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33, container.d

 

MySQL HA 기본 구성으로 1대의 마스터 서버와, 2대의 슬레이브 서버가 GTID 기반으로 복제 구성이 되어 있다.

이번 포스트에서 4개의 시나리오를 구성해 보았으며, 시나리오별로 실습 과정을 다루었다.

시나리오1. Slave Refactoring

Slave간에 복제 토폴로지 리팩토링으로, 슬레이브1, 슬레이브2 마스터에서 싱크하던 환경에서, 슬레이브2 슬레이브1에서 복제를 싱크할 있도록 토폴로지를 변경한다.

 

 

시나리오2. Master Refactoring (Promote)

슬레이브 서버를 마스터로 승격시키고, 기존의 마스터를 슬레이브로 역할을 전환한다.

 

 

시나리오3. Master – Master Refactoring

슬레이브 서버중 하나를 마스터로 승격시켜 대의 마스터로 구성한다.

 

 

시나리오4. Master Failover

기존의 운영중이던 마스터 서버를 강제로 중지하여 장애 상황을 구현한 다음, 현재 슬레이브로 운영되던 서버중 하나가 새로운 마스터가 되어 장애조치 된다. 그리고 나머지 슬레이브 서버는 새로운 마스터 서버와 복제 싱크를 한다.

 

 

실습환경 살펴보기

오케스트레이터 대시보드에 접속하면 현재 구성되어 있는 클러스터 목록을 있다. 여기에서 숫자 3 1 보이는데, 3 현재 구성된 클러스터의 전체 서버 개수이며, 2 슬레이브 서버 개수이다.

 

 

클러스터를 클릭해보면 현구 구성되어 있는 클러스터 토폴로지를 확인할 있다. 시나리오에 따라 서버의 역할이 변경되기 때문에, 현재 구성되어 있는 서버이름과 역할을 혼돈하지 않도록 주의한다.

 

 

시나리오1. Slave Refactoring

Slave간에 복제 토폴로지 리팩토링으로, 슬레이브1, 슬레이브2 마스터에서 싱크하던 환경에서, 슬레이브2 슬레이브1에서 복제를 싱크 있도록 토폴로지를 변경한다.

 

mysql-slave2(172.30.1.13)서버를 드래그하여 mysql-slave1(172.30.1.10) 서버에 드롭한다. 이때 드롭 위치가 중요하다. 서버의 중간쯤으로 이동하면 드롭하는 서버 정보에 relocate라고 표시가 되는데 이때 드롭한다.

 

 

Relocate 대한 변경 사항에 대해서 경고 팝업이 나타나고 OK 클릭하면 변경 사항이 적용된다.

 

 

클러스터 토폴로지 구조가 변경된 것을 확인할 있다.

 

 

mysql-slave2(172.30.1.13) 서버에 접속하여 호스트 이름 복제 상태를 조회해 보면, 현재 mysql-slave1(172.30.1.10) 서버를 마스터 호스트로 연결되어 있는 것을 확인할 있다.

select @@host;
show slave status\G;

 

 

 

시나리오를 처음 상태로 원상복구하려면 mysql-slave2(172.30.1.13) 드래그하여 mysql-master(172.30.1.97) 드롭 한다.

 

 

Relocate 대한 변경 사항에 대해서 경고 팝업이 나타나고 OK 클릭하면 변경 사항이 적용된다.

 

 

클러스터 토폴로지 구조가 변경된 것을 확인할 있다.

 

 

mysql-slave2(172.30.1.13) 서버에 접속하여 호스트 이름 복제 상태를 조회해 보면, 현재 mysql-master(172.30.1.97) 서버가 마스터 호스트로 연결되어 있는 것을 확인할 있다.

 

 

시나리오2. Master Refactoring (Promote)

슬레이브 서버를 마스터로 승격시키고, 기존의 마스터를 슬레이브로 역할을 전환한다.

 

 

mysql-slave1(172.30.1.10) 드래그 하여 mysql-master(172.30.1.97) 드롭 한다. 이때 드롭하는 위치를 현재 마스터보다 조금 앞으로 위치하여 PROMOTE AS MASTER 표시될 드롭 한다. (마스터 보다 뒤에 경우 relocate 표시된다.)

 

 

Takeover 대한 경고 팝업이 나타나고 OK 클릭하면 변경 사항이 적용된다.

 

 

변경사항을 적용하였을 , 대시보드 화면이 바로 갱신이 안되는 경우가 있다. 이때 새로고침을 하면 변경사항이 정상적으로 나타난다. 현재 구성되어 있는 토폴로지 상태를 보면, mysql-slave1(172.30.1.10) 서버가 master 역할을 하며, mysql-master(172.30.1.97)서버가 slave 역할을 하는 것으로 역할 체인지가 것을 확인할 있다. 하지만 mysql-master(172.30.1.97) 서버를 보면 역할 변경 새로운 마스터에 대한 복제 구성이 되지 않아서 오류로 표시되고 있다.

 

 

복제 시작을 위해 slave역할의 mysql-master(172.30.1.97) 서버에서 메뉴 아이콘을 클릭하면 설정 팝업이 나타나는데, [Start replication] 클릭한다.

 

 

복제가 정상적으로 구성되고 모든 클러스터가 정상으로 표시되는 것을 확인할 있다.

 

 

다음 실습을 준비하기 위해 원래 클러스터 토폴로지 구조로 원상복구를 한다. 원상복구 방법은 앞에서 했던 방법과 동일하게 진행한다.

 

 

시나리오3. Master – Master Refactoring

슬레이브 서버중 하나를 마스터로 승격시켜, 대의 마스터로 구성한다.

 

 

mysql-master(172.30.1.97) 서버를 드래그 하여 mysql-slave1(172.30.1.10) 드롭 한다. 이때 MAKE CO MASTER 표시되면 드롭 한다.

 

 

make-co-master 대한 경고 팝업이 나타나고 OK 클릭하면 변경 사항이 적용된다.

 

 

실행이 완료되면 클러스터 토폴로지 구성이 master – master 변경된 것을 확인할 있다.

 

정확히는 master-master 구조만 만들어진 상황으로, 완벽한 master-master 작동을 하는 것은 아니다. 환경은 구성되었지만 mysql-slave(172.30.1.10) 서버는 현재 Read Only 상태이다. [Set writeable] 클릭하여 쓰기 상태로 변경해야 사용가능한 master-master 환경이 된다.

 

 

master-master 구조에서 master-salve 원복하기 위해서는 mysql-master(172.30.1.97) 서버에서 설정 아이콘을 클릭한다. 팝업이 나타나면 Master 항목에서 [Reset replica] 클릭한다.

 

 

이전과 같은 상태로 클러스터 토폴로지가 구성된 것을 확인할 있다.

 

 

시나리오4. Master Failover

기존의 운영중이던 마스터 서버를 강제로 중지하여 장애 상황을 구현한 다음, 현재 슬레이브로 운영되던 서버중 하나가 새로운 마스터가 되어 장애조치 된다. 그리고 나머지 슬레이브 서버는 새로운 마스터 서버와 복제 싱크를 한다.

 

 

현재 master역할을 하고 있는 mysql-master(172.30.1.97) 서버에 접속하여 MySQL 서비스를 강제로 중지한다.

hostname
 
sudo systemctl stop mysql

 

 

 

MySQL 서비스가 중지되고 오케스트레이터가 장애 상황을 인지하면 아래와 같이 대시보드 상태가 변경된다. 파란색 숫자3 현재 구성되어 있는 클러스터 서버 개수이며, 빨간색 숫자2 슬레이브 개수이며 복제 불가능을 표시하고 있다. 검은색 1 응답 없는 서버 개수를 나타낸다.

 

 

해당 클러스터를 클릭해서 상세 대시보드를 살펴보면, 현재 mysql-master(172.30.1.97) 서버가 응답 없음을 나타내고 있으며, 슬레이브는 복제가 되지 않는 것을 확인할 있다. 우측의 느낌표를 클릭하면 노드의 에러 메시지를 상세하게 확인할 있다.

 

오류 메시지는 오케스트레이터의 로그 파일에서도 확인할 있다. 특별히 경로를 변경하지 않았으면 아래 경로에서 확인 가능하다. 만약 경로 확인이 필요하면 설정 파일에서 확인할 있다.

/tmp/recovery.log

 

 

[수동 Failover]

오케스트레이터의 장애조치 모드 기본값은 수동이다. 수동으로 장애조치는 관리자가 문제를 인지하고 직접 장애 조치를 진행해야 한다. 현재 응답이 없는 mysql-master(172.30.1.97) 서버에서 [Recover] 클릭한다. Failover 가능한 서버 목록에서, mysql-salve1(172.30.1.10)서버로 장애조치를 진행한다.

 

 

장애조치를 실행하게 되면, 응답 없는 서버를 기존의 클러스터에 분리된다. 기존 서비스가 중지된 mysql-master 노드는 기존 클러스터에서 분리된 것을 확인할 있다.

 

 

클러스터를 클릭하여 상세 내용을 보면 mysql-slave1(172.30.1.10) 서버가 master역할을 하고, mysql-slave2(172.30.1.13) 서버가 slave역할을 하는 것을 확인할 있다.

 

 

mysql-master(172.30.1.97) 서버를 복구했다는 가정하에 mysql-master(172.30.1.97) 서비스를 다시 시작한다.

 

 

MySQL 서버가 정상적으로 실행되었으며 현재는 독립적으로 실행되고 있기 때문에, 기존의 복제 클러스터에 조인하기 위해서는 현재 master 역할로 운영중인 mysql-slave1(172.30.1.10) 서버로 복제를 연결한다.

 

 

복제가 성공적으로 연결되고, 복제 싱크가 완료되면 오케스트레이터 대시보드에서도 클러스터가 정상적으로 연결되어 운영되는 것을 확인할 있다.

 

 

[자동 failover]

기본 모드가 수종이기 때문에, 자동으로 장애조치를 하기 위해서는 파라메터 수정이 필요하다. 아래 경로의 설정파일에서 수정할 있다. 설정 파라메터의 종류가 많기 때문에 주의해서 살펴본다.

vi /usr/local/orchestrator/orchestrator.conf.json

 

 

변경전 변경후
"RecoveryPeriodBlockSeconds": 3600,
  "RecoveryIgnoreHostnameFilters": [],
  "RecoverMasterClusterFilters": [
    "_master_pattern_"
  ],
  "RecoverIntermediateMasterClusterFilters": [
    "_intermediate_master_pattern_"
  ],
"RecoveryPeriodBlockSeconds": 10,
  "RecoveryIgnoreHostnameFilters": [],
  "RecoverMasterClusterFilters": [
    "*"
  ],
  "RecoverIntermediateMasterClusterFilters": [
    "*"
  ],

 

 

설정 변경을 완료하였으면, 오케스트레이터는 설정 파일에 대한 변경사항을 런타임중에 적용할 있다. 대시보드 상단 메뉴에서 [Home] - [Status] 클릭한다. Status페이지에서 [Reload congifuration] 클릭하여 설정을 로드 한다.

 

 

자동 장애조치 테스트를 위해 현재 master 운영중인 서버의 MySQL 강제로 중지한다.

 

 

Master서버가 중지되고 오케스트레이터에서 장애 상황을 인지한 다음 자동으로 장애조치가 완료되면 대시보드에서 아래와 같이 개의 클러스터를 있다. 장애가 발생한 서버는 기존 클러스터에서 분리가 되었다.

 

 

클러스터를 클릭해서 상태를 살펴보면, 기존의 slave 서버중에 하나가 master 승격되고, 나머지 슬레이브가 새로운 마스터에 조인되어 싱크되는 것을 확인할 있다.

 

분리된 장애 서버에서는 Recover 클릭해도 클러스터에서 분리된 상태이기 때문에 failover 있는 서버 목록을 찾을 없다고 표시된다.

 

 

지금까지 MySQL HA환경에서 오케스터레이터를 활용하여 토포로지 리팩토링 장애조치 하는 방법에 대해서 살펴보았다. 장애조치를 진행할 운영중인 서버중 하나가 랜덤하게(정확히는 씽크가 가장 빠른 서버) 마스터로 승격되는 것을 확인하였는데, 또한 파라메터를 수정하여 다양한 조건을 부여하여 우선순위조건을 조정할 있다. 앞애서도 살펴보았듯이 파라메터의 종류가 매우 많다. 상세한 내용은 다른 포스트에서 하나씩 살펴볼 예정이다.

 

 

 

[참고자료]

l   Orchestrator : https://github.com/openark/orchestrator

l   Failure detection : https://github.com/openark/orchestrator/blob/master/docs/failure-detection.md

l   MySQL Orchestrator - HA(High Availability) - 2 - 리팩토링 Failover Automated Recovery : https://hoing.io/archives/91

l   Configuration: failure detection : https://github.com/openark/orchestrator/blob/master/docs/configuration-failure-detection.md

l   https://code.openark.org/blog/mysql/what-makes-a-mysql-server-failurerecovery-case : https://code.openark.org/blog/mysql/what-makes-a-mysql-server-failurerecovery-case

l   Orchestrator: MySQL Replication Topology Manager : https://www.percona.com/blog/orchestrator-mysql-replication-topology-manager/

l   Orchestrator and ProxySQL : https://www.percona.com/blog/orchestrator-and-proxysql/

 

 

 

2023-07-27 / Sungwook Kang / http://sungwookkang.com

 

MySQL, ProxySQL, MySQL Replication, MySQL HA, MySQL복제 오케스트레이션, MySQL복제설치, MySQL MHA, MySQL Orchestrator, MySQL장애조치, MySQL고가용성

 

MySQL 고가용성 운영을 위한 Orchestrator 설치

 

l  Version : MySQL, Orchestrator

 

데이터베이스를 운영할 단일 장애 포인트(SPOF, Single Point Of Failure) 예방하기 위해서 고가용성(HA, High-Available) 매우 중요한 부분이다. 그렇기 때문에 클러스터링, 복제 고가용성으로 구성된 다수의 데이터베이스를 효율적으로 관리하기 위해 다양한 솔루션들이 존재한다.  

이번 포스트는 MySQL MariaDB 고가용성을 효율적으로 운영하기 위한 솔루션인 Orchestrator(이하 오케스트레이터’) 대해서 알아본다. 오케스트레이터는 MySQL 고가용성 복제 관리 도구로, 서비스로 실행되며 명령줄 액세스, HTTP API 인터페이스를 제공한다.

 

l  Orchestrator : https://github.com/openark/orchestrator

 

오케스트레이터의 주요 기능으로는 아래와 같다.

l  Discovery : 토폴로지를 통해 지속적으로 클러스터의 복제 상태 구성과 같은 기본 MySQL 정보를 읽어 오케스트레이션에 매핑한다. 장애가 발생하였을 복제 문제를 포함하여 토폴로지의 자세한 정보를 시각화로 제공한다.

l  Refactoring : 복제 서버의 Binlog file:position, GTID, Pseudo GTID 다양한 정보를 수집한다. 복제 토폴로지에서 리팩토링은 GUI에서 복제본을 다른 마스터 아래로 끌어서 놓는 방식으로 안전하게 진행할 있다. 오케스트레이터는 비정상적인 리팩토링 시도를 거부하며 다양한 명령줄 옵션을 통해 세밀한 제어가 가능하다.

l  Recovery : 전체적인 접근 방식을 사용하여 마스터 중간 마스터 오류를 감지한다. 토폴로지 자체에서 얻은 정보를 기반으로 다양한 장애 시나리오를 인식한다. 장애가 인식되면 자동 또는 수동 복구를 수행하도록 구성할 있다. 복구 프로세스는 오케스트레이터의 토폴로지 이해와 리팩토링 수행 능력을 활용하며, 구성이 아닌 상태를 기반으로 한다. 오케스트레이터는 자체 복구시 토폴로지를 조사/평가하여 최상의 복구 방법을 선택한다.

 

오케스트레이터는 다양한 인터페이스를 지원하는데 명령줄 인터페이스(CLI, Command Line Interface) 활용하여 디버그 메시지 등을 수집하고 자동화된 스크립팅을 제어할 있다. 외에도 다양한 Web API (HTTP Get access) 인터페이스를 제공한다.

l  Highly available

l  Controlled master takeovers

l  Manual failovers

l  Failover auditing

l  Audited operations

l  Pseudo-GTID

l  Datacenter/physical location awareness

l  MySQL-Pool association

l  HTTP security/authentication methods

 

 

 

Orchestrator 사용하려면 우선 MySQL복제 환경이 구성되어 있어야 한다. 오케스트레이터 구성에서 GTID 사용을 권장하고 있으나, Binlog position 사용시 Pseudo GTID 사용하면 된다. MySQL 복제 구성은 아래 링크를 참고한다.

l  ProxySQL 설치 (MySQL 설치부터, 복제 구성, ProxySQL 설정까지 한번에) : https://sungwookkang.com/1529

 

오케스트레이터를 구성할 서버 정보들이다. 링크에서 구성한 환경을 기반으로 진행한다.

Server Name IP OS Service Version
proxy-sql 172.30.1.49 Ubuntu 22.04.2 LTS ProxySQL version 2.4.2-0-g70336d4, codename Truls
mysql-master 172.30.1.97 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33
mysql-slave1 172.30.1.10 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33
mysql-slave2 172.30.1.13 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33
proxy-orchestrator 172.30.1.24 Ubuntu 22.04.2 LTS mysql  Ver 8.0.33, container.d

 

proxy-orcherator서버에는 오케스트레이터가 설치되어 운영된다. 오케스트레이터에서 필요한 정보를 저장하기 위해 오케스트레이터용 리포지토리 MySQL 설치한다.

sudo apt-get install mysql-server

 

MySQL설치가 완료되면 오케스트레이터가 사용할 계정 데이터 베이스를 생성한다. 테스트 용도이기 때문에 편의상 전체 권한을 부여한다. 실제 업무에서 사용할 때에는 보안 정책에 따라 오케스트레이터에 필요한 권한만 할당한다.

--데이터베이스 생성
CREATE DATABASE IF NOT EXISTS orchestrator;
 
--사용자 생성
CREATE USER 'orchestrator'@'%' IDENTIFIED BY 'orchestrator';
--GRANT SUPER, PROCESS, REPLICATION SLAVE, RELOAD ON *.* TO 'orchestrator'@'%';
--GRANT SELECT ON mysql.slave_master_info TO 'orchestrator'@'%';
 
GRANT ALL PRIVILEGES ON *.* TO 'orchestrator'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

 

 

이번 포스트에서는 컨테이너로 빌드된 오케스트레이터를 설치하기 때문에, 컨테이너를 실행할 있는 도커 패키지를 설치한다.

--시스템 패키지 업데이트
sudo apt-get update
 
--필요한 패키지 설치
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
 
--Docker 공식 GPG키를 추가
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
 
--Docker 공식 apt 저장소를 추가
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
 
--시스템 패키지 업데이트
sudo apt-get update
 
--Docker 설치
sudo apt-get install docker-ce docker-ce-cli containerd.io
 
--Docker 서비스 실행 확인
sudo systemctl status docker

 

 

 

도커 설치가 완료되었으면, 도커 허브에서 오케스트레이터 컨테이너를 다운로드 한다.

ü  https://hub.docker.com/r/openarkcode/orchestrator

 

 

아래 스크립트를 실행하면 도커 이미지가 다운로드 되며, 현재 호스트에 저장되어 있는 도커 이미지를 확인할 있다.

sudo docker pull openarkcode/orchestrator
sudo docker images

 

 

 

아래 스크립트를 사용하여 도커를 실행한다.

sudo docker run -d --name orchestrator -p 3000:3000 openarkcode/orchestrator

 

 

 

오케스트레이터 서비스가 정상적으로 실행되었으면, 브라우저를 사용하여 오케스트레이터 대시보드로 접속한다.

http://172.30.1.24:3000/

 

 

상단의 [Cluster] – [Discover]에서 MySQL 클러스터를 등록할 있다. 기본값으로 호스트 이름으로 등록하게 되어 있다. 호스트 이름으로 MySQL 서버들을 검색하려면 proxy-orchstrator (오케스트레이터의 호스트 서버) 서버의 /etc/hosts 파일에 호스트 정보를 등록한다.

 

 

서버 이름이 아닌 서버IP 클러스터 서버를 찾기 위해서는 아래와 같이 설정 파일을 수정하여 사용할 있다. 기본적으로 설정 파일은 아래 경로에 있다.

/usr/local/orchestrator/

 

컨테이너 환경에서는 컨테이너 내부에서 파일 수정을 하는 것은 좋은 방법은 아니지만 실습 편의상 컨테이너 내부 쉘로 접근해서 수정하였다. 실제 서비스 환경에서는 컨테이너 특성상 데이터가 휘발되기 때문에 반드시 설정파일 따로 보관할 있도록 한다. 아래 스크립트는 실행 중인 컨테이너의 ID 확인하고, 해당 컨테이너 쉘로 접근한다.

sudo docker ps -a
sudo docker exec -it 93c5bfafa537 /bin/bash

 

 

 

설정파일이 위치한 경로로 이동하여 설정 파일을 수정한다.

cd /usr/local/orchestrator/
sudo cp orchestrator-sample.conf.json orchestrator.conf.json
vi orchestrator.conf.json

 

 

오케스트레이터 서버가 MySQL 클러스터 서버들에게 접속할 있는 계정과 비밀번호를 입력한다. 사용자 환경에 맞게 수정하여 사용한다.

"MySQLTopologyUser": "orc_client_user",
"MySQLTopologyPassword": "orc_client_password",

 

오케스트레이터 서버가 orchester 데이터베이스 접속할 있는 계정과 비밀번호를 입력한다. 오케스트레이터용 리포지토리 MySQL 설치 단계에서 생성한 계정을 입력하도록 한다.

"MySQLOrchestratorHost": "127.0.0.1",
"MySQLOrchestratorPort": 3306,
"MySQLOrchestratorDatabase": "orchestrator",
"MySQLOrchestratorUser": " orchestrator ",
"MySQLOrchestratorPassword": " orchestrator ",

 

 

호스트 이름 대신 IP 검색할 있도록 옵션을 수정한다.

변경 "HostnameResolveMethod": "default",
"MySQLHostnameResolveMethod": "@@hostname",
변경 "HostnameResolveMethod": "none",
"MySQLHostnameResolveMethod": "",

 

GTID 사용하지 않고 binlog position 사용할 경우 failover 위해서 PeseudoGTID 활성화 하기위해 아래 파리메터를 입력한다. 해당 파라메터는 설정 파일에 없으므로 직접 추가해야 한다.

"AutoPseudoGTID": true,

 

 

각종 로그파일은 아래 경로에서 확인할 있다. 필요시 사용자 경로에 맞게 수정하여 사용할 있다.

"OnFailureDetectionProcesses": [
"echo 'Detected {failureType} on {failureCluster}. Affected replicas: {countSlaves}' >> /tmp/recovery.log"
  ],
"PreGracefulTakeoverProcesses": [
"echo 'Planned takeover about to take place on {failureCluster}. Master will switch to read_only' >> /tmp/recovery.log"
  ],
"PreFailoverProcesses": [
"echo 'Will recover from {failureType} on {failureCluster}' >> /tmp/recovery.log"
  ],
"PostFailoverProcesses": [
"echo '(for all types) Recovered from {failureType} on {failureCluster}. Failed: {failedHost}:{failedPort}; Successor: {successorHost}:{successorPort}' >> /tmp/recovery.log"
  ],
"PostUnsuccessfulFailoverProcesses": [],
"PostMasterFailoverProcesses": [
"echo 'Recovered from {failureType} on {failureCluster}. Failed: {failedHost}:{failedPort}; Promoted: {successorHost}:{successorPort}' >> /tmp/recovery.log"
  ],
"PostIntermediateMasterFailoverProcesses": [
"echo 'Recovered from {failureType} on {failureCluster}. Failed: {failedHost}:{failedPort}; Successor: {successorHost}:{successorPort}' >> /tmp/recovery.log"
  ],
"PostGracefulTakeoverProcesses": [
"echo 'Planned takeover complete' >> /tmp/recovery.log"
  ],

 

설정값 변경이 완료되었으면 해당 사항을 저장하고 오케스트레이터 서비스를 재시작한다. 오케스트레이터 대시보드에 접속하여 IP Discover 등록할 있다. 서버는 master 역할 서버만 입력한다.

 

 

정상적으로 등록이 완료되면 아래와 같이 등록된 서버가 나타난다.

 

 

해당 서버를 클릭하면 현재 구성된 토폴로지 상태를 나타내는 대시보드를 있다,

 

 

대시보드에 나타난 서버들을 클릭해보면 현재 상태의 상세 정보를 확인할 있으며, 일부 제어를 있는 메뉴가 나타난다.

 

 

 

 

[참고자료]

l  Orchestrator : https://github.com/openark/orchestrator

l  MySQL Orchestrator - HA(High Availability) - 1 - 기능 설명 설치 : https://hoing.io/archives/72

l  https://code.openark.org/blog/mysql/what-makes-a-mysql-server-failurerecovery-case : https://code.openark.org/blog/mysql/what-makes-a-mysql-server-failurerecovery-case

l  Orchestrator: MySQL Replication Topology Manager : https://www.percona.com/blog/orchestrator-mysql-replication-topology-manager/

l  Orchestrator and ProxySQL : https://www.percona.com/blog/orchestrator-and-proxysql/

 

 

 

 

2023-07-25 / Sungwook Kang / http://sungwookkang.com

 

MySQL, ProxySQL, MySQL Replication, MySQL HA, MySQL복제 오케스트레이션, MySQL복제설치, MySQL MHA, MySQL Orchestrator

ProxySQL Internals 시스템 구성 둘러보기

 

l  Version : ProxySQL

 

이번 포스트에서는 ProxySQL 인터널 구조와 설정 파일의 구조에 대해서 살펴본다. 상세한 인터널 구조를 모두 설명하지는 않으며 전체적인 구조 특징만 소개하도록 한다. 세부적인 내용은 다른 포스트에서 하나씩 다뤄볼 예정이다.

 

ProxySQL 시작되면 즉시 프로세스를 생성한다. 상위 프로세스는 엔젤 프로세스(와치독과 비슷한 의미로 해석됨) 작동하고 ProxySQL 크래시 되었을 밀리초 내에 서비스를 다시 시작한다. MySQL 비유하면 mysqld_safe 매우 유사하다.

ProxySQL에는 기본적으로 개의 포트가 필요하다. 트래픽을 수신하는 6033 포트와 ProxySQL 관리하기 위한 게이트웨이 역할을 하는 6032포트이다. 명령줄 관리 인터페이스(CLI) MySQL 클라이언트에서 액세스할 있다. 대부분의 구성은 SQL 사용하여 수행된다. ProxySQL 구성을 저장하는 사용하는 기본 데이터베이스는 SQLite이지만 가능한 MySQL 가까운 환경을 만들기 위해 노력한 흔적을 있다. 예로 일부 MySQL 구문을 사용하여 ProxySQL 구성을 확인하거나(SHOW GLOBAL VARIABLES) 구성을 설정 (SET GLOBAL variable_name = value) 있다. 백엔드 구성은 테이블에 저장된다. 모든 변경 사항은 SQL(INSERT, UPDATE, DELETE) 통해 이루어진다.

 

 

l  Query processor/rules engine : ProxySQL MySQL 프로토콜(쿼리) 해석한다. 규칙 엔진은 들어오는 트래픽을 일치시키고 쿼리를 캐시할지 또는 호스트 그룹 대상에 쿼리를 차단, 경로 재지정, 쿼리 재작성 또는 미러링 할지 여부를 정의한다.

l  User authentication : 기본 데이터베이스에 대한 사용자 자격 증명이 해시되어 프록시에 저장된다.

l  Hostgroup manager : 트래픽을 보낼 서버 그룹을 관리하고 해당 상태를 추적한다.

l  Connection pool : 백엔드 데이터베이스에 대한 연결을 관리한다. 연결 풀은 백엔드를 향해 설정되며 모든 애플리케이션에서 공유/재사용된다.

l  Monitoring : 백엔드를 모니터링하고 지표를 수집한다. 모니터링을 하면서 응답하지 않는 호스트 또는 복제 지연에 대해 필요에 따라 트래픽을 제어한다.

 

Query Processor SQL 트래픽을 분석하고 이해할 있으므로 광범위한 라우팅을 수행할 있다. 스키마, 사용자, 호스트, 포트 또는 정규 표현식과 같은 다양한 속성으로 쿼리를 일치시킬 있다. 이렇게 하면 변경하려는 쿼리와 관련하여 상당한 유연성을 얻을 있다. 예를들어 쿼리캐시는 쿼리 대기 시간을 줄일 있도록 정의된 시간(TTL) 동안 캐시할 있다. 이러한 기능은 일반적으로 응용 프로그램에 추가 복잡성을 도입하는 외부 캐싱 계층의 필요성을 제거할 수도 있다. 또한 MySQL 쿼리 캐시(결국 8.0에서 제거됨)보다 훨씬 나은 솔루션일 수도 있다. 그리고 사용자 정의에 따라 쿼리를 제한할 수도 있다. 예를들어 성능에 영향을 미치는 무거운 쿼리들을 초당 또는 분당 번만 실행되도록 커스텀하게 설정할 있다. 쿼리 미러링기능도 있지만 아직은 모든 트래픽이 미러되는 것은 아니기 때문에 기대를 정도는 아니다. 그러나 특정 상황에서 매우 유용하게 활용하 있다. 주어진 쿼리에 대해 프로덕션 호스트뿐만 아니라 별도의 위치로 보내서 슬로우 로그에서 캡처하거나 일부 디버깅을 수행할 있다. 쿼리 재작성을 통해 쿼리를 즉석에서 재작성할 있다. 간단한 FORCE INDEX 문제를 해결할 있지만 수정, 테스트, 코드 등의 시간 소모적인 프로세스를 거치지 않고는 문제를 추가할 없는 상황에서, ProxySQL에서 쿼리를 다시 작성하면서 인덱스 힌트 등을 추가할 있다.

 

ProxySQL 매우 복잡한 구조를 가진것 처럼 보이지만, 시스템을 구성하고 사용하는데 있어서는 4개의 계층(Runtime, Memory, Disk, Configuration file) 제공하여 쉽게 사용할 있다. 특징으로는 설정에 대해서 즉시 업데이트가 되고, 데몬의 재시작 없이 적용할 있다. 또한 잘못 구성되었을 경우에도 쉽게 롤백 있다.

 

RUNTIME

Runtime ProxySQL 기동 운영에 사용된다. 요청을 처리하는 스레드에서 사용하는 ProxySQL 메모리 데이터 구조를 나타낸다. 여기에는 사용된 전역 변수의 , 호스트 그룹으로 그룹화된 백엔드 서버 목록 또는 프록시에 연결할 있는 MySQL 사용자 목록이 포함된다. 운영자는 RUNTIME 구성 섹션의 내용을 직접 수정할 없다. 하위 레이어를 수정하고 내용을 적용시킬 있다.

 

MEMORY

MEMORY(메인이라고도 ) MySQL 호환 인터페이스를 통해 외부에 노출되는 메모리 SQLite3 데이터베이스를 나타낸다. 사용자는 인터페이스에 MySQL 클라이언트를 연결하고 다른 테이블과 데이터베이스를 쿼리할 있다. 인터페이스를 통해 사용할 있는 구성 테이블은 다음과 같다.

l   mysql_servers : ProxySQL에서 중계할 서버들의 목록을 관리한다. 중계 대상 서버를 백엔드 서버라고 부른다.

l   mysql_users : ProxySQL 연결할 있는 사용자 자격 증명 목록으로, ProxySQL 이러한 자격 증명을 사용하여 백엔드 서버에도 연결한다.

l   mysql_query_rules : 다른 백엔드 서버로 트래픽을 라우팅하기 위한 규칙 목록으로 이러한 규칙으로 인해 쿼리를 다시 작성하거나 결과를 캐싱할 수도 있다.

l   global_variables : 런타임에 조정할 있는 프록시 전체에서 사용되는 전역 변수 목록이다. 아래 그림은 글로벌 전역 변수 예시이다.

 

 

l   mysql_collations : 프록시가 작업할 있는 MySQL 데이터 정렬 목록으로 클라이언트 라이브러리에서 직접 추출된다.

l   debug_levels (디버그 빌드에서만 사용 가능) : ProxySQL 세부 수준과 함께 내보내는 디버그 문의 유형 목록이다. 이를 통해 다양한 문제를 디버깅하기 위해 로그에 어떤 종류의 명령문이 있는지 런타임에 쉽게 구성할