MariaDB Galera 10.6에서 11.4로 무중단 롤링 업그레이드

지원이 끝난 MariaDB 10.6 Galera 3노드를 10.6.28 → 10.11.19 → 11.4.13, 세 번의 롤링으로 올렸다. 서비스는 멈추지 않았고, 쓰기 연결이 차수마다 두 번 잠깐 끊긴 것 말고는 DB 오류가 한 건도 없었다.

운영 중인 MariaDB Galera 클러스터 세 대를 10.6 에서 11.4 로 올렸다. 2026-10-01 에 시작해 10-06 에 끝났고, 세 번 모두 업무 시간에 서비스를 켜 둔 채로 했다. 이 글은 그 과정과, 미리 리허설을 돌려 보지 않았다면 운영에서 처음 만났을 함정들을 정리한 것이다. 같은 일을 앞둔 사람이 순서대로 따라 할 수 있게 썼다.

1. 왜 올렸나

  • 10.6 은 커뮤니티 지원이 2026-07-06 에 끝났다. 마지막 판 10.6.28(2026-08-13) 의 릴리스 노트에는 “This is the final release in the MariaDB 10.6 series.” 라고 적혀 있다
  • 그런데 우분투 22.04 패키지는 10.6.23 에 머물러 있었다. jammy-updates 의 최신이 10.6.23 이고, 10.6.28 에 들어간 보안 수정(Galera 쪽 MDEV-40027·MDEV-40056 — SUPER 권한 사용자가 셸 명령을 실행할 수 있던 문제 둘 포함)을 받을 길이 없었다
  • 보안 인증 심사의 패치 관리 항목 에서 지적받을 수 있는 상태였다
  • 새 하드웨어는 없었다. 임시 노드를 붙여 새 클러스터로 넘기는 안은 접고, 지금 세 노드를 한 대씩 올리기로 했다
  • 목표를 11.4 로 잡은 이유: 10.11 의 지원은 2028-02-16 에, 11.4 는 2029-05-29 에 끝난다. 11.4 는 우분투 22.04(jammy)·24.04(noble) 둘 다 공식 패키지가 있어 다음 OS 전환도 수월하다

2. 환경

클러스터 구성 — 앞단 로드밸런서 두 대와 Galera 세 노드
클러스터 구성 — 앞단 로드밸런서 두 대와 Galera 세 노드

이 구성은 예전 글 HAProxy + Galera Cluster (MariaDB) 1/2, 2/2 에서 다뤘다. 그 뒤로 바뀐 것까지 합치면 지금은 이렇다.

  • 노드: 우분투 22.04 VM 세 대 (db1·db2·db3). 노드마다 다른 물리 호스트에 있다. 데이터 디렉터리 /db 는 OS 와 다른 디스크이고 데이터는 약 150GB 다
  • 앞단: HAProxy 두 대(lb1·lb2) 와 keepalived VIP. 모든 연결을 db1 하나로 보내고 db2·db3 는 backup 으로 둔다. 쓰기 노드가 하나라 Galera 의 인증 충돌을 걱정할 일이 없다
  • 상태 검사: 외부 스크립트로 HAProxy 노드를 검사한다. 노드가 Synced(wsrep_local_state=4) 일 때만 UP 이다 — 업그레이드 중인 노드는 저절로 빠진다
  • 암호화: 저장 암호화(TDE, file_key_management) 로 우리 서비스 테이블을 암호화했고, 3306 · Galera 복제(4567) · IST(4568) · SST(4444, rsync + stunnel) 를 모두 TLS 로 감쌌다
  • 쓰기량: 시간당 0.5GB 안팎, gcache 는 2GB 다
  • 함께 쓰는 곳: 우리 서비스 말고도 사내 다른 서비스 여러 개가 같은 클러스터를 쓴다. 그래서 끊김을 줄이는 것이 가장 중요했다

3. 한 번에 못 간다 — 경로

업그레이드 경로 — 10.6.23 에서 11.4.13 까지 세 번
업그레이드 경로 — 10.6.23 에서 11.4.13 까지 세 번
차수 판 Galera 패키지 이름
1차 10.6.23(우분투) → 10.6.28 26.4.9 → 26.4.27 같다 (mariadb-server-10.6 …)
2차 10.6.28 → 10.11.19 그대로 바뀐다 — -10.6 이 붙은 이름이 지워지고 판 없는 이름이 들어온다
3차 10.11.19 → 11.4.13 그대로 같다 + -compat 패키지 둘이 더 들어온다
  • 10.6 에서 11.4 로 바로 가는 롤링은 없다. Galera 는 이웃한 큰 판끼리만 섞어 돌 수 있다
  • 10.6 → 10.11 은 IST 로만 롤링된다. 공식 문서의 표현은 “rolling upgrade with IST is supported, but rolling upgrade with SST does not work for this jump” 다
  • 그래서 10.6 의 최신(10.6.28) 을 먼저 거친다. 문서: “If you are upgrading from the most recent MariaDB 10.6 release to MariaDB 10.11, then the versions will be compatible.”
  • 1차에서 Galera 가 26.4.9 → 26.4.27 로 크게 오른다. 사이에 GCS 프로토콜이 여러 번 바뀌었다. 섞여 있는 동안은 옛 프로토콜로 돌다가 세 노드가 다 오르면 새 프로토콜로 넘어가고, 그때부터는 한 노드만 되돌릴 수 없다 (“prevents downgrades of individual nodes in the cluster as soon as all nodes have been updated”)
  • 패키지 출처가 바뀐다. 우분투 저장소에서 MariaDB 공식 저장소로 옮긴다. 판은 끝자리까지 고정한다 — 리허설한 판과 운영에 들어가는 판이 같아야 한다

4. IST 와 SST, 그리고 gcache 여유

업그레이드한 노드가 다시 붙을 때 — IST 와 SST
업그레이드한 노드가 다시 붙을 때 — IST 와 SST

노드 하나를 멈췄다가 새 판으로 다시 켜면, 그 노드는 빠져 있던 동안의 쓰기를 따라잡아야 한다.

  • IST: 빠진 쓰기가 남은 노드의 gcache 에 아직 있으면 그것만 받는다. 우리 운영에서는 2~3천 건, 30초 남짓이었다
  • SST: gcache 를 넘었으면 데이터 전체(약 150GB) 를 받는다. rsync SST 는 보내는 노드(donor) 를 전송 내내 읽기 잠금으로 묶는다
  • 판이 섞인 동안의 SST 는 위험하다. 새 판 joiner 는 옛 판 donor 가 보내는 SST 를 받지 못한다 (MDEV-36202, InnoDB: Upgrade after a crash is not supported). 리허설에서 일부러 gcache 를 넘겨 보니 그 노드만 죽고 남은 두 노드는 멀쩡했다. 그 노드를 옛 판으로 내려 켜면 전체 SST 로 다시 붙었다

그래서 노드를 멈추기 전에 IST 여유를 잰다. 노드마다 60초 간격으로 두 번 읽는다.

SELECT VARIABLE_NAME, VARIABLE_VALUE
  FROM information_schema.GLOBAL_STATUS
 WHERE VARIABLE_NAME IN ('WSREP_LAST_COMMITTED', 'WSREP_LOCAL_CACHED_DOWNTO');
여유(분) = (last_committed − cached_downto) ÷ 1분 동안 늘어난 last_committed

우리 값은 세 번 모두 203~244분이었고, 노드가 빠져 있는 시간은 1분 안팎이었다. 여유가 1시간 미만이면 미루기로 정해 두었다.

donor 도 정해 둔다. 60-galera.cnf 에 wsrep_sst_donor 한 줄을 넣는다.

  • 첫 노드 db3 는 쓰기 노드가 아닌 db2 에서 받는다 — "node2,". rsync SST 가 donor 를 잠그기 때문이다
  • db2·db1 은 이미 올라간 db3 에서 받는다 — "node3,". IST 가 안 돼 SST 로 넘어가도 같은 판끼리라 받을 수 있다
  • 끝의 쉼표는 “지정한 노드가 못 주면 다른 노드도 된다” 는 뜻이다

단, 큰 판이 섞여 있는 동안(특히 10.6 → 10.11)에는 끝 쉼표가 위험하다. 지정한 노드가 못 주면 옛 판 노드(쓰기 노드일 수도 있다)에서 SST 를 받으려 하는데, 새 판 joiner 는 그것을 받지 못하고(MDEV-36202) rsync SST 는 그동안 donor 를 잠근다. 섞인 동안에는 쉼표를 빼 같은 판 donor 로 못 박는 편이 안전하다. 우리는 쉼표를 둔 채로 했고 세 차수 모두 IST 로 붙어 이 길을 타지 않았다.

하나 더 — MDEV-28233 부터는 SST 암호화를 설정해 두고 실제로 할 수 없으면 평문으로 보내지 않고 SST 를 멈춘다. MariaDB 패키지는 stunnel 을 의존성으로 끌어오지 않으니 미리 깔려 있는지 본다.

5. 도커 리허설에서 미리 본 함정

운영에서는 읽기만 하고, 같은 우분투 22.04 · 같은 패키지 · 같은 설정(TDE·TLS 포함) 으로 도커에 세 노드를 세워 세 차수를 끝까지 돌렸다. 무결성 검사는 10번 모두 합격했고, 끊김은 차수마다 정확히 두 번이었다. 다음은 리허설이 아니었으면 운영에서 처음 만났을 것들이다.

10.11 설치는 서버를 켜지 않는다

1차(10.6.28) 와 3차(11.4.13) 는 설치 스크립트가 MariaDB 를 켠다. 2차(10.11.19) 는 켜지 않는다. mariadb-server-10.6 이 지워질 때 그 postrm 이 유닛을 mask 하고, 새 패키지의 postinst 는 멈춰 둔 서버를 그대로 둔다. 그래서 설치 뒤에 꺼져 있으면 켠다.

systemctl is-active --quiet mariadb || { systemctl daemon-reload; systemctl start mariadb; }

mariadb-upgrade 는 노드마다 돌린다

mariadb-upgrade 는 세션에 SET SQL_LOG_BIN=0, WSREP_ON=OFF 를 건다. 다른 노드로 복제되지 않으니 노드마다 한 번씩 돌려야 한다. 리허설에서 쓰기를 멈추고 여섯 번 돌려 보니 세 노드의 wsrep_last_committed 가 그대로였다 — 옛 판 노드가 섞여 있어도 해가 없다.

/db 안에 백업 사본을 두면 안 된다

데이터 디렉터리가 그 디스크의 맨 위라, 거기 둔 디렉터리는 이름이 무엇이든 MariaDB 가 DB 로 본다. 리허설에서 /db/.db-bak 이 #mysql50#.db-bak 으로 보였다.

  • mariadb-upgrade 가 Incorrect database name '#mysql50#.db-bak' 으로 실패한다
  • rsync SST 가 그 사본까지 함께 보낸다 — SST 가 두 배가 되고 받는 쪽 디스크가 찬다

OS 디스크에는 150GB 를 둘 자리가 없었다. 그래서 백업은 하이퍼바이저의 VM 스냅샷 으로 했다 (8절).

CREATE_OPTIONS 의 표기가 바뀐다

10.6.23 은 `ENCRYPTED`=YES, 10.6.28 부터는 `ENCRYPTED`='YES' 다. “아직 암호화 안 된 테이블” 을 고르는 우리 스크립트가 NOT LIKE 로 옛 표기를 찾고 있어서, 10.6.28 에서는 이미 암호화된 테이블을 전부 “안 된 것” 으로 골랐다. 그대로 실행했다면 큰 테이블을 다시 ALTER 했을 것이다. 두 표기를 다 받게 고쳤다.

-- 암호화된 테이블
WHERE CREATE_OPTIONS RLIKE '`ENCRYPTED`=.?YES'

11.4 클라이언트는 서버 인증서를 기본으로 검증한다

3차 도중에는 먼저 올라간 노드의 11.4 클라이언트로 아직 10.11 인 노드에 붙을 일이 생긴다. 3차 앞에 세 노드에 클라이언트용 CA 설정을 한 줄 둔다.

# /etc/mysql/mariadb.conf.d/63-client-tls.cnf
[client-mariadb]
ssl-ca = <DB 전용 CA 파일>

패키지 파일 50-client.cnf 는 고치지 않는다. 고치면 다음 업그레이드에서 conffile 질문이 생긴다.

자동 업데이트가 MariaDB 를 고르지 않게

저장소를 MariaDB 공식 저장소로 바꾸면 unattended-upgrades 가 새 판을 끌어올 수 있다. Package-Blacklist 에 mariadb-·libmariadb·galera 를 넣고, needrestart 가 MariaDB 를 저절로 재시작하지 않게 막은 뒤 확인한다.

unattended-upgrade --dry-run -d 2>&1 | grep -iE 'blacklist|mariadb|galera'

그 밖에

  • 없어진 옵션은 기동을 막지 않는다. 10.11·11.4 모두 “was removed. It does nothing now” 경고만 낸다. 그래도 1차에서 네 줄(innodb_buffer_pool_instances·innodb_log_files_in_group·innodb_thread_concurrency·innodb-defragment) 을 지웠다
  • /db/lost+found 가 있으면 mariadb-upgrade 가 lost@002bfound 라는 빈 DB 를 만든다. 생겼으면 업그레이드 전에 치운다

6. 노드 한 대 절차

노드 한 대를 올리는 순서
노드 한 대를 올리는 순서

순서는 차수마다 db3 → db2 → db1 이다. 쓰기 노드 db1 을 맨 마지막에 둔다.

준비 (재시작 없음) — 차수 전날까지 세 노드에 해 둔다.

# /etc/mysql 을 통째로 백업 (키·인증서·설정이 다 여기 있다. 경로는 예시)
tar czf /var/backups/etc-mysql-$(date +%F).tgz -C / etc/mysql

# MariaDB 저장소 키링 — sha256 을 공식 값과 대조한다
install -d -m 0755 /etc/apt/keyrings
curl -fsSL -o /etc/apt/keyrings/mariadb-keyring.gpg https://supplychain.mariadb.com/mariadb-keyring-2025.gpg
sha256sum /etc/apt/keyrings/mariadb-keyring.gpg

# 이번 차수 판으로 저장소 파일을 직접 놓는다 — 판은 끝자리까지
cat > /etc/apt/sources.list.d/mariadb.sources <<'EOS'
Types: deb
URIs: https://dlm.mariadb.com/repo/mariadb-server/10.11.19/repo/ubuntu
Suites: jammy
Components: main
Architectures: amd64
Signed-By: /etc/apt/keyrings/mariadb-keyring.gpg
EOS
# 이 저장소가 우분투 패키지보다 앞서게 핀을 건다
printf 'Package: *\nPin: origin dlm.mariadb.com\nPin-Priority: 1000\n' > /etc/apt/preferences.d/mariadb-enterprise.pref
apt-get update
apt-get -d -y install mariadb-server mariadb-client mariadb-backup galera-4 libmariadb3 mysql-common   # 받아만 둔다

mariadb_repo_setup 스크립트는 수시로 바뀐다. 운영 날 받은 스크립트가 리허설 때와 다른 파일을 만들 수 있어서, 스크립트가 만드는 파일을 직접 놓았다. 키링 파일은 sha256 을 공식 값과 대조한다.

아래 명령은 노드·로드밸런서에서 root 로 친다.

노드 한 대

  1. 시작 전 확인 · 이 노드 차례의 wsrep_sst_donor 지정 (4절) — 세 노드 Synced, wsrep_cluster_size=3, Primary. 로드밸런서 두 대에서 db1~3 이 모두 UP
  2. (db1 차례만) 쓰기를 db2 로 — 7절
  3. IST 여유를 잰다 — 1시간 미만이면 미룬다
  4. 멈춘다 — systemctl stop mariadb. 남은 두 노드가 Primary·Synced 인지 본다
  5. (db3 차례만) VM 스냅샷 — 8절
  6. 설치

DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=l apt-get install -y \
-o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold \
mariadb-server mariadb-client mariadb-backup galera-4 libmariadb3 mysql-common

  1. 꺼져 있으면 켠다 — 2차만 해당 (5절)

systemctl is-active --quiet mariadb || { systemctl daemon-reload; systemctl start mariadb; }

  1. Synced 를 확인한 뒤 mariadb-upgrade

cd /tmp && sudo -u mysql mariadb-upgrade --user=mysql --skip-write-binlog

우분투 기본 설치는 root 가 unix_socket 이라 sudo mariadb-upgrade --skip-write-binlog 로 된다. 우리 노드는 root 로그인이 막혀 있어 설치 때 만들어지는 mysql@localhost(unix_socket) 로 돌렸다. cd /tmp 는 mysql 사용자가 지금 디렉터리(/root)를 못 읽어 나는 오류를 피하려는 것이다

  1. (db1 차례만) 쓰기를 db1 로 되돌린다
  2. 몇 분 지켜보고 다음 노드. 차수의 첫 노드 db3 는 30분 지켜봤다

붙지 못하면 곧바로 멈춘다. systemctl stop mariadb; systemctl reset-failed mariadb. 리허설에서 붙지 못한 노드를 그냥 두었더니 systemd 가 5분 동안 31번 다시 띄웠고, 붙으려 할 때마다 남은 두 노드가 그 노드를 넣었다 뺐다 하며 커밋을 세웠다 — 1초 넘게 걸린 쓰기가 41건, 10초 시간 초과가 5건이었다.

7. 쓰기 노드 옮기기

db1 차례에 쓰기 노드를 옮기는 순서
db1 차례에 쓰기 노드를 옮기는 순서

db3·db2 는 연결이 없는 대기 노드라 멈춰도 끊기는 연결이 없다. db1 차례만 HAProxy 런타임 API 로 쓰기를 옮긴다. 두 로드밸런서에 같은 명령을, 대기 쪽 lb2 먼저 보낸다.

# 앞 — db1 을 빼고 남은 세션을 끊는다
echo "set server <백엔드>/db1 state maint"   | nc -U /run/haproxy/admin.sock
# 1분쯤 기다리며 db1 의 Threads_connected 가 0 에 가까운지 본다
echo "shutdown sessions server <백엔드>/db1" | nc -U /run/haproxy/admin.sock     # 끊김 1

# 뒤 — db1 이 Synced 로 돌아오고 mariadb-upgrade 까지 끝나면
echo "set server <백엔드>/db1 state ready"   | nc -U /run/haproxy/admin.sock
echo "shutdown sessions server <백엔드>/db2" | nc -U /run/haproxy/admin.sock     # 끊김 2
  • 끊김은 차수마다 이 두 번뿐이다. 앱이 다시 붙으면 끝난다
  • maint 로 돌려도 풀에 오래 붙어 있는 연결은 남는다. 3차에서는 1분 반이 지나도 10개가 남아 있었다. 기다리지 말고 shutdown sessions 로 끊는 것이 낫다
  • 로드밸런서를 거치지 않는 연결도 있었다. 1차 때 db1 에 바로 붙어 있던 연결 두 개가 db1 을 멈출 때 끊겼다. 미리 세어 보면 누가 바로 붙는지 알 수 있다
  • 읽기를 여러 노드에 나누는 구성이면 노드가 Synced 가 되는 순간 상태 검사를 통과해 읽기가 들어온다. mariadb-upgrade 가 끝날 때까지 그 노드를 로드밸런서에서 maint 로 두는 편이 낫다

8. 스냅샷은 차수 중간에만 쓸모 있다

  • 차수마다 db3 차례에 MariaDB 를 멈춘 뒤, 설치 전에 하이퍼바이저에서 db3 VM 스냅샷을 찍었다. 메모리 없이 디스크 둘(OS·/db) 만. MariaDB 가 멈춰 있어 디스크가 일관된다
  • 그 차수 도중에 문제가 생기면 db3 를 스냅샷으로 되돌려 켠다. 다른 두 노드가 모든 쓰기를 갖고 있으니 IST(또는 같은 판끼리 SST) 로 따라잡는다 — 잃는 것이 없다
  • 차수가 끝나면 그 스냅샷은 쓸 데가 없다. 한 노드만 옛 판으로 되돌릴 수 없고(리허설: Group requested gcs_proto_ver: 6, max supported by this node: 2), 그 스냅샷으로 클러스터를 다시 세우면 스냅샷 뒤의 쓰기를 모두 잃는다. 남겨 두면 씬 풀만 차고 키가 든 디스크 사본이 남는다. 그래서 차수가 끝나면 바로 지웠다
  • 패키지만 옛 판으로 내리는 되돌리기는 안 된다. 10.8 에서 redo 로그 형식이 바뀌었고 11.0 에서 change buffer 가 없어졌다 — 새 판이 한 번 연 /db 는 옛 판이 열지 못한다

9. 결과

차수 날짜 걸린 시간 판 노드가 빠진 시간 (db3 / db2 / db1) IST 여유 끊김
1차 2026-10-01 약 12분 10.6.23 → 10.6.28 48초 / 48초 / 31초 240분 2번
2차 2026-10-02 약 42분 (첫 노드 뒤 30분 지켜본 것 포함) → 10.11.19 48초 / 33초 / 35초 244분 2번
3차 2026-10-06 약 38분 (같음) → 11.4.13 29초 / 36초 / 34초 203분 2번
  • 세 차수 모두 백엔드 DB 오류 0건, 종단 시험(e2e) 네 가지가 앞뒤로 실패 0건
  • 3차 IST 는 노드마다 2,167~2,553건이었고, mariadb-upgrade 는 41·58·91초 걸렸다 (그동안 노드는 이미 클러스터에서 돈다)
  • 3차 뒤 세 노드의 우리 서비스 테이블 수십 개의 체크섬이 같았고, 저장 암호화(file_key_management ACTIVE) 와 TLS 가 그대로였다
  • 지금은 세 노드 모두 MariaDB 11.4.13 · Galera 26.4.27 이다

10. 배운 것

  • 리허설은 운영과 같은 패키지 · 같은 설정이어야 한다. 5절의 함정은 대부분 “판이 바뀌면서 조용히 달라진 것” 이었다. 도커로 세 차수를 끝까지 돌려 본 덕에 운영에서는 놀랄 일이 없었다
  • root 로그인이 막혀 있으면 mariadb-upgrade 에 --user=mysql 을 붙인다. 빼면 root 로 붙어 1045 로 실패한다. 우리 노드는 OS 의 mysql 사용자가 unix_socket 으로 붙는 관리자 계정을 갖고 있었다
  • 기록 파일의 소유자를 본다. 1차 때 /db/mysql_upgrade_info 가 예전에 root 로 만들어져 있어 errno: 13 으로 실패했다. chown mysql:mysql 로 고쳤다. 11.4 는 mariadb_upgrade_info 를 새로 만드는데 mysql:mysql 로 생겨 다시 나지 않았다
  • 재시작 때 debian-start 의 root 1045 는 무해하다. 기동도 apt 도 실패시키지 않는다
  • 마지막 차수 전에 개발 DB 를 먼저 올렸다. 컨테이너 이미지만 10.6.22 → 11.4.13 으로 바꾸고(볼륨은 그대로) 개발 쪽 종단 시험을 돌렸다. 되돌릴 수 있게 볼륨은 깨끗이 멈춘 뒤 따로 떠 두었다
  • 업무 시간에 해도 됐다. 끊김이 차수마다 두 번뿐이라 한가한 시간을 기다리지 않았다

11. 다음

  • OS 24.04 전환 — MariaDB 는 OS 표준 지원이 끝나면 빌드를 멈춘다. jammy 용 11.4 는 2027-06 무렵까지다. noble 에는 10.6 이 없지만 11.4 는 있으니, 클러스터를 먼저 11.4 로 올린 것이 다음 단계의 준비이기도 하다
  • mariabackup SST 검토 — rsync SST 는 donor 를 전송 내내 묶는다. mariabackup 은 끝에 잠깐만 묶는다. 리허설에서는 11.4 에서 TLS·암호화된 테이블까지 넘어갔다
  • 하이퍼바이저 판 올리기와 오래된 노드를 새 서버로 바꾼 이야기는 다음 글에서 다룬다

Proxmox / PR_END_OF_FILE_ERROR

Proxmox 로그인시 아래 메시지가 출력되고 접속이 안되는 상황이 발생

보안 연결 실패

hostname:8006에 연결하는 동안 오류가 발생했습니다. PR_END_OF_FILE_ERROR

오류 코드: PR_END_OF_FILE_ERROR

    받은 데이터의 신뢰성을 확인할 수 없으므로 보시려는 페이지를 표시할 수 없습니다.
    웹 사이트 관리자에게 연락하여 이 문제를 알려주실 수 있습니다.

ssh 접속이 가능하다면 아래 명령어 실행

cd /etc/pve/local ; rm pve*ssl.* ; pvecm updatecerts --force ; service pveproxy restart

OpenWrt 컴파일 for MX4300 (Linksys)

Linksys LN1301 (MX4300)에 OpenWrt를 설치하면서 남기는 글이다.

1. 소스코드

MX4300은 OpenWrt에 정식버전이 없기 때문에 코드를 받아서 컴파일을 해야한다.
아래에서 코드를 다운로드 한다.
https://github.com/qosmio/openwrt-ipq/tree/qualcommax-6.x-nss-mx4300-6.9

2. 환경구축

가상환경에 우분투 24.04.1 LTS를 설치하고 아래 패키지를 설치해준다.

sudo apt update
sudo apt upgrade -y
sudo apt install build-essential clang flex bison g++ gawk gcc-multilib g++-multilib gettext git libncurses-dev libssl-dev python3-distutils python3-setuptools rsync swig unzip zlib1g-dev file wget

3. git clone

git에서 코드를 복제해준다.
아래부터는 root 권한으로 진행하면 컴파일할 때 오류가 발생한다.

cd /home
git clone https://github.com/qosmio/openwrt-ipq -b main-nss-mx4300
cd openwrt-ipq
./scripts/feeds update
./scripts/feeds install -a
cp nss-setup/config-nss.seed .config

추가할 패키지가 있으면 아래명령사용

./scripts/feeds install luci-app-samba4
./scripts/feeds install samba4-server
./scripts/feeds install samba4-utils

.config 파일을 열어서 MX4300 부분의 “is not set”을 수정해준다.

#CONFIG_TARGET_qualcommax_ipq807x_DEVICE_linksys_mx4300 is not set
CONFIG_TARGET_qualcommax_ipq807x_DEVICE_linksys_mx4300=y
make defconfig V=s
make download -j$(nproc) V=s
make -j$(nproc) V=s

4. bin 파일

yell@dev-openwrt:~/openwrt-ipq$ cd bin/targets/qualcommax/ipq807x/
yell@dev-openwrt:~/openwrt-ipq/bin/targets/qualcommax/ipq807x$ ls -al
total 100032
drwxr-xr-x 3 yell yell     4096 Dec 23 08:56 .
drwxr-xr-x 3 yell yell     4096 Dec 23 06:37 ..
-rw-r--r-- 1 yell yell     5873 Dec 23 08:18 config.buildinfo
-rw-r--r-- 1 yell yell      703 Dec 23 08:18 feeds.buildinfo
-rw-r--r-- 1 yell yell 35916962 Dec 23 08:56 kernel-debug.tar.zst
-rw-r--r-- 1 yell yell 21581628 Dec 23 08:56 openwrt-qualcommax-ipq807x-linksys_mx4300-initramfs-uImage.itb
-rw-r--r-- 1 yell yell     8489 Dec 23 08:56 openwrt-qualcommax-ipq807x-linksys_mx4300.manifest
-rw-r--r-- 1 yell yell 24383488 Dec 23 08:56 openwrt-qualcommax-ipq807x-linksys_mx4300-squashfs-factory.bin
-rw-r--r-- 1 yell yell 20490511 Dec 23 08:56 openwrt-qualcommax-ipq807x-linksys_mx4300-squashfs-sysupgrade.bin
drwxr-xr-x 3 yell yell    12288 Dec 23 08:56 packages
-rw-r--r-- 1 yell yell     2026 Dec 23 08:56 profiles.json
-rw-r--r-- 1 yell yell      923 Dec 23 08:56 sha256sums
-rw-r--r-- 1 yell yell       18 Dec 23 08:18 version.buildinfo

googleusercontent.coom / 이미지 표시 문제

사내메일서버에서 지메일로 메일발송할 경우 이미지가 표시안되는 문제가 발생했다.
해당 문제는 본문에 이미지를 삽입했을 때 발생했고 첨부파일로 넣었을 경우는 발생하지 않았다.

문제가 발생한 메일 본문

이미지가 나오지 않고 404 코드를 반환한다

<img src="https://ci3.googleusercontent.com/meips/ADKq_NaOOgOCqGIvHrYcYIpTuAvRgcg0AN7kloSXWd4lQFTMeY82IxJGj3p4mMDpxhIyNup7MGJ46SI1UVM30Ldx8JcqQ96BYIIFs4Ppn4B8DbIocydPPcshB-xDUSSmteQnsk9VhU9uHsO6R8i4euJOri1veXp3fyEf1NQ=s0-d-e1-ft#https://mail.domain.com/data/mailing/202404/29/f5abecd29fb2a516346a6cebd02591f39c98a0e8.jpg" class="CToWUd" data-bit="iit" jslog="138226; u014N:xr6bB; 53:WzAsMl0.">

구글메일은 이미지 삽입시 클라이언트에서 읽지 않고 구글 이미지 서버를 거쳐서 캐싱을 한다.

구글링으로 찾아본 해결책

https://stackoverflow.com/questions/40570117/http403-forbidden-error-when-trying-to-load-img-src-with-google-profile-pic

이미지 태그에 referrerPolicy=’no-referrer’ 속성을 추가하는 방법을 안내해준다

결론부터 말하자면 해당 태그를 넣어도 구글에서 해당태그를 아래처럼 없애버린다.

googleusercontent.com

구글 이미지 프록시에서 사내메일서버의 아이피를 블랙리스트로 등록했을
가능성을 생각해보고 우회하는 방법을 생각해봤다.

사내메일서버 아이피를 변경할 수 없는 상황이지만
여러 대역의 사용가능한 아이피가 있는 상황이기에 리버스 프록시를 사용해본다.

Reserse proxy / Nginx

구글 이미지 프록시가 메일서버에 직접 접근하는 상황을 아래처럼 변경한다

리버스 프록시 서버는 IDC내 가상서버로 구축하고
4개의 서로 다른 아이피 대역을 할당해줬다.

DNS 설정

img01-mail  IN  A x.1.x.x
img02-mail IN A x.x.2.x
img03-mail IN A 3.x.x.x
img04-mail IN A x.x.x.4

2차 도메인을 DNS서버에 설정한다. 기존 메일서버와는 다른 대역을 사용했다.

메일발송

메일 발송시 본문에서 이미지 태그를 확인하고 도메인 부분을 치환하는 코드를 작성한다.
치환시 img01 ~ img04 중 랜덤하게 사용되도록 해준다.

Nginx 설정

nginx 설정은 따로 특별한게 없다. 도메인만 여러개 추가해준다.

server {
listen 443 ssl http2;
server_name img01-mail.domain.com img02-mail.domain.com img03-mail.domain.com img04-mail.domain.com;
server_tokens off;

location / {
proxy_redirect off;
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://mail.domain.com/;
proxy_redirect off;
proxy_http_version 1.1;
}

ssl_certificate /home/ssl/fullchain.pem;
ssl_certificate_key /home/ssl/_wildcard_domain.com_SHA256WITHRSA.key;
ssl_protocols TLSv1.1 TLSv1.2;
}

테스트

발송할 때마다 2차 도메인은 계속 변경되었고, 이미지도 잘 표시된다.

<img src="https://ci3.googleusercontent.com/meips/ADKq_NaOODCx4XS5A5i2FvMs7IHhVH638Rt18wraT9Lsqo6YmS0oVpeCdEerGQHHdgqODi5uY4s9BZWFfpcGS49R7JJTCOmMG4NH9HLSdVThm7g5tYzRf5go5-gacdoLbsxo3I4r-fOwswrlhRKb-4XjudQPFOC2qznJeSM=s0-d-e1-ft#https://img02-mail.domain.com/data/mailing/202404/29/1b2b7bb8190e4c444ac418b600272d3dbd713e62.jpg" class="CToWUd a6T" data-bit="iit" tabindex="0">

Proxmox VE – Resizing guest disk

Proxmox에서 생성한 VM의 디스크 용량이 부족한 상황이 발생했다.
디스크를 추가하기 보다는 사용중인 디스크의 용량을 증가시키는 방법을 선택하기로 하고 다른 VM에 테스트후 적용했다.

Proxmox WebUI

웹UI로 접속해서 해당 VM의 디스크를 선택

[Disk Action] 에서 [Resize]를 선택하고

증가시킬 용량을 입력한다

VM에 SSH로 접속

root@db-gtv:~# df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 794M 1.3M 793M 1% /run
/dev/sda2 79G 73G 2.2G 98% /
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 794M 4.0K 794M 1% /run/user/0

디스크 용량을 확인해 보면 기존 용량으로 보인다.
파티션 조정이 필요해 보인다.

Disk /dev/sda: 120 GiB, 128849018880 bytes, 251658240 sectors
Disk model: QEMU HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 853010EE-DB5B-4D1D-BF27-A8485BA881EE

Device Start End Sectors Size Type
/dev/sda1 2048 4095 2048 1M BIOS boot
/dev/sda2 4096 167770111 167766016 80G Linux filesystem

디스크의 파티션을 확인한다. Proxmox WebUI에서 조정한만큼 용량이 증가한 것을 확인할 수 있다.

root@db-gtv:~# growpart /dev/sda 2
CHANGED: partition=2 start=4096 old: size=167766016 end=167770112 new: size=251654111 end=251658207

growpart 명령을 이용해서 파티션을 조정한다.

root@db-gtv:~# resize2fs /dev/sda2
resize2fs 1.46.5 (30-Dec-2021)
Filesystem at /dev/sda2 is mounted on /; on-line resizing required
old_desc_blocks = 10, new_desc_blocks = 15
The filesystem on /dev/sda2 is now 31456763 (4k) blocks long.

resize2fs 명령을 이용해서 파일시스템도 조절해준다.

root@db-gtv:~# df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 794M 1.3M 793M 1% /run
/dev/sda2 118G 73G 40G 65% /
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 794M 4.0K 794M 1% /run/user/0

증가된 용량을 확인할 수 있다.

라즈베리파이를 무선공유기로 만들기

https://www.raspberrypi.com/software/operating-systems

라즈베리파이 이미지중 Raspberry Pi OS Lite 를 설치하고 진행한다.

필수 패키지 설치

apt install -y hostapd
apt install -y dnsmasq

설정파일

interface=wlan1
driver=nl80211
ssid=jongwan
hw_mode=g
channel=7
wmm_enabled=0
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=12345678
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP

/etc/hostapd/hostapd.conf

interface=wlan1
dhcp-range=192.168.10.2,192.168.10.20,255.255.255.0,24h
domain=wlan
address=/gw.wlan/192.168.10.1

/etc/dnsmasq.conf

# 아래 내용 추가
interface wlan1
static ip_address=192.168.10.1/24
nohook wpa_supplicant

/etc/dhcpcd.conf

# 아래 주석 해제
#net.ipv4.ip_forward=1

/etc/sysctl.conf

root@raspberrypi:/# sysctl -p
net.ipv4.ip_forward = 1
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

서비스 실행

systemctl unmask hostapd
systemctl enable hostapd
systemctl start hostapd
systemctl enable dnsmasq
systemctl start dnsmasq

제주도 한라산 (성판악~백록담~관음사코스)

5월 첫째주 한라산에 다녀왔다.

날씨 예보로는 아슬아슬했는데
안타깝게도 정상에 올라서부터 비가 내리기 시작했고
하루종일 비가 내린날이었다.

한라산은 날씨에 행운이 따라야 한다나??

전날 저녁비행기를 타고 제주도로 이동해서
제주버스터미널 근처 게스트하우스에서 묵었다.

제주버스터미널에 숙소를 잡은 이유는
여기서 161번 급행 첫차가 06:10에 출발하기 때문이다.

당일 아침 첫 비행기를 타서 제주도로 오는 경우
공항 3번 출구 앞 버스정류장에서 07:20분에 161번 버스를 탈 수 있다.

이게 가능하려면 김포공항에서
06:00~06:10 비행기를 연착없이 타야 하는데
빠듯하게 가느니 전날가서 한숨자는게 나아보인다

아침일찍 일어나서 터미널에 나와보면
바로옆에 CU편의점이 크게 있으니 여기서 물과 간식거리를 구매한다.

161번 버스는 터미널 안쪽이 아니라 밖에서 탄다.

성판악에 07:30 정도에 도착했다.
주차장은 한산한 편이었다.
아마도 비가 예보되어 있어서 그랬지 싶다.

이날 성판악은 시간별로 모두 예약이 완료되었었다.
https://visithalla.jeju.go.kr/main/main.do

진달래밭대피소까지 오르면서 어려웠던 구간은 없었다.

입산제한시간 확인!!

중간에 몇미터를 올라왔는지 저런식으로 세워져 있다.

정상에 다오니 구름속..

기온이 낮은편은 아니었던것 같은데,
바람이 불고 비를 맞아서 체온이 떨어져 버렸다.

이날 수학여행 온 고등학생들이 성판악에 오르다가
저체온증으로 구조대가 출동했다고 한다.
비소식이 있으면 보온에 신경을 쓰도록 하자.

춥고 바람불고 구름속이어서 백록담은 보이지도 않고
사진만 찍고 관음사 방면으로 하산을 시작한다

관음사 탐방로입구 버스정류장에는
475번 버스가 다니고 배차간격이 길어서 내려오는 시간을 잘 맞춰야 한다.

오후 배차시간
11:58, 13:28, 14:48, 15:43, 16:38, 17:28, 18:23, 19:13, 19:58

제주대학교에서 공항가는 버스로 환승이 가능하나
제주도에 온김에 동문시장에서 국수먹고 공항으로 갔다.

오늘 운동기록!!
성판악에서 올라가는건 힘들지 않고
관음사방면으로 내려가는 길이 길어서 조금 힘든정도??
그리고 관음사로 올라가면 난이도가 확~ 올라갈 듯하니 다음엔 관음사로 올라가기로..

최고높이는 GPS 오차 때문에….

HAProxy + Apache2 웹서비스 이중화 구성하기

HAProxy 설치

우분투 22.04 기준 패키지 매니저로 설치한다.

apt install haproxy

haproxy.cfg

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        ssl-default-bind-options no-sslv3
        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
        ssl-default-server-options no-sslv3
        ssl-default-server-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

####################################################
# stats
####################################################
listen stats
        bind *:8080
        stats enable
        stats uri /
        stats realm HAProxy\ Statistics
        stats auth admin:1234
        stats refresh 5s

####################################################
# jongwan.com
####################################################
frontend jongwan_com-front
        mode http
        bind 172.16.0.10:80
        redirect scheme https code 301 if !{ ssl_fc }

frontend jongwan_com-ssl-front
        mode http
        bind 172.16.0.10:443 ssl crt /etc/haproxy/ssl/_wildcard_jongwan_com.pem
        default_backend jongwan_com-backend

backend jongwan_com-backend
        mode http
        option httpchk
        option forwardfor
        http-request set-header X-Forwarded-Port %[dst_port]
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
        server www1 10.0.0.1:80 check weight 1
        server www2 10.0.0.2:80 check weight 1 backup
listen stats
        bind *:8080
        stats enable
        stats uri /
        stats realm HAProxy\ Statistics
        stats auth admin:1234
        stats refresh 5s

stat은 8080포트로 바인딩하고 암호를 설정해준다.

frontend jongwan_com-front
        mode http
        bind 172.16.0.10:80
        redirect scheme https code 301 if !{ ssl_fc }

프런트앤드를 추가한다.

  • bind : 외부에서 접근하는 아이피 (공인아이피)
  • redirect : https 포트로 리다이렉트한다.
frontend jongwan_com-ssl-front
        mode http
        bind 172.16.0.10:443 ssl crt /etc/haproxy/ssl/_wildcard_jongwan_com.pem
        default_backend jongwan_com-backend

HTTPS 프런트앤드를 설정한다.

  • bind : 외부에서 접근하는 아이피를 입력한다. PEM 형식의 인증서를 추가해야 하고 아래쪽에 만드는 방법이 있다.
  • default_backend : 기본 백앤드
backend jongwan_com-backend
        mode http
        option httpchk
        option forwardfor
        http-request set-header X-Forwarded-Port %[dst_port]
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
        server www1 10.0.0.1:80 check weight 1
        server www2 10.0.0.2:80 check weight 1 backup
  • mode : http 방식으로 health 체크를 한다. ssl 확인을 위해 header를 사용하기 때문에 tcp로 하면 동작하지 않는다.
  • server : 실제 웹서버 정보를 입력한다. 사용자와 통신은 443 (SSL) 통신을 하지만 haproxy와 webserver간에는 80으로 통신한다.

아파치 웹서버 설정

<VirtualHost *:80>
        ServerAdmin aiseki@gmail.com
        DocumentRoot /home/jongwan/public_html
        ServerName jongwan.com
        ServerAlias www.jongwan.com

        <Directory /home/jongwan/public_html/>
                Options FollowSymLinks
                AllowOverride FileInfo
                Require all granted
        </Directory>

        RemoteIPHeader X-Forwarded-For
        RemoteIPInternalProxy 172.16.0.10/24

        #LogLevel info ssl:warn
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>
  • RemoteIPHeader X-Forwarded-For : 사용자는 haproxy를 통해서 웹서버에 접근하기 때문에 실제 웹서버 로그에는 haproxy 아이피가 나온다. 이때 haproxy는 X-Forwarded-For 헤더에 클라이언트의 실제아이피를 담아준다.
    mod_remoteip 모듈을 활성화하고 해당 옵션을 이용하면 아파치가 실제 클라이언트 아이피를 가져올 수 있다.
    PHP의 경우 REMOTE_ADDR 값에 영향을 준다.
  • RemoteIPInternalProxy : haproxy 아이피 대역을 입력한다.

mod_remoteip 활성화하기

a2enmod remoteip

PEM 인증서 생성하기

key, crt 파일을 이용해서 pem 형식의 인증서를 만드는 방법이다.
합친파일에서 ^M 문자열이 있을 경우 오류가 나기 때문에 tr 명령을 이용해서 삭제해준다.

cat _wildcard_jongwan_com_SHA256WITHRSA.key _wildcard_jongwan_com.crt ChainCA/rsa-dv.chain-bundle.pem | tr -d '\015' > _wildcard_jongwan_com.pem

금학산 (금학공원~매바위~정상~마애불상~원점회귀)

철원여자고등학교로 올라오면 지도에 표시한 위치에 주차장이 있습니다.
큰 주차장에 무료로 사용이 가능하고,
에어건도 있어서 편리합니다.

금학산은 전체적으로 힘들지는 않았고,
쉬엄쉬엄 올라가면 금방 정상에 도착합니다.
정상에서 마애불상쪽은 하산시 이정표를 잘 보면서 내려가야 하는데
네이버나 다음지도의 등산로와 살짝 안맞는듯 합니다.

내려오다가 계곡에서 뱀 봤습니다!!!

여기로 쭉 올라가면 금학체육공원이 나옵니다.

이곳으로 올라가서 시계 반대방향으로 등산할 예정입니다.
금학체육공원 ~ 매바위 ~ 정상 ~ 마애불상 ~ 거북이약수터 ~ 금학체육공원 ~ 주차장

매바위!!!

가끔 나오는 가파른 계단

정승바위!!!

정상옆 헬기장입니다.

947미터 금학산 정상입니다.

고대산이 보이네요.
여기서 고대산 연계산행이 가능하니 여유가 된다면 가보는 것도 좋겠습니다.

네이버나 다음지도의 등산로를 보고 내려오면
길이 약간 어긋납니다.
거북이 약수터로 내려와서 C코스로 이동한 뒤 주차장으로 갑니다.

C코스가 은근 오르막길

삼악산 등선입구~정상~상원사~의암매표소 코스

2월이 끝나갈 무렵 삼악산에 올랐다.

오전일찍 청량리에서 청춘ITX를 이용해서 강촌역까지 이동한다. 등선폭포로 가려면 쉬엄쉬엄 걸어가도 되고, 버스를 타면 금방이다.
1번출구에서 5번 또는 7번 버스를 타면 등선입구에서 내릴 수 있다.

등선입구에서 내리면 횡단보도가 없고 지하도가 있으니 입구가 보이는 곳까지 걸어가보자. 처음에 무단횡단을 해야하나 고민을 많이 했다.

지하도 입구
등선폭포 정보
입장료

입장료가 있는데 [춘천사랑상품권]으로 돌려준다. 산에서 내려와서 춘천시내에서 밥을 먹기로 했는데 그 때 사용이 가능했다.

계곡이라 아직 얼음이 있다
수많은 돌탑들
레고랜드가 보인다

암벽구간인데 기어서 올라가는 사람이 보일 정도로 가파르다. 거기다가 미끄럽기도 해서 많이 위험했다.

삼악산을 오른다면 의암매표소(상원사) ~ 정상 ~ 등선입구로 가는게 비교적 안전할 것 같다.

심지어 사진에는 없는데 상원사 위쪽 구간이 모두 얼음이라 아이젠 없이 가다 넘어지는 사람을 몇명 봤다.

춘천에 왔으니깐 닭갈비는 필수코스

기차시간이 남아서 택시타고 온 구봉산 카페거리