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

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

외부스크립트를 이용해서 HAProxy node 체크하기

HAProxy + MariaDB Galera Cluster 구성을 하고 테스트를 진행하니 문제가 발생해서 글을 남겨본다.

HAProxy

haproxy.cfg 설정파일에서 아래 옵션을 사용

option mysql-check user haproxy

이 옵션은 TCP 포트로의 접속가능 여부만 판단해서 health check를 한다.
Galera Cluster의 경우 노드가 복구될 때 쿼리를 하면 아래 메시지가 출력된다.

WSREP has not yet prepared node for application use

따라서 이 경우는 HAProxy에서 노드를 살려줘서는 안된다.

MariaDB Galera Cluster

MariaDB [(none)] > show global status where variable_name='wsrep_local_state';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| wsrep_local_state | 4     |
+-------------------+-------+

동기화가 완료되서 사용이 가능한 상태값이다.
다른 상태값은 아래 링크에서 확인한다.
https://mariadb.com/docs/ent/ref/mdb/status-variables/wsrep_local_state/

HAProxy external-check

기존에 mysql-check를 외부 스크립트를 이용하도록 변경한다.
외부 스크립트는 wsrep_local_state 값을 읽어서 원하는 값이면 exit 0 을 리턴하고 다른값이면 exit 255를 리턴시켜주면 된다.

/etc/haproxy/haproxy.cfg

global
        log /dev/log    local0
        log /dev/log    local1 notice	
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon
        external-check
        insecure-setuid-wanted

frontend mysql-front
        bind *:3306
        mode tcp
        default_backend mysql-back

backend mysql-back
        mode tcp        
	#option mysql-check user haproxy
	option external-check
        external-check path "/usr/bin:/bin"
        external-check command /etc/haproxy/script/check_node.sh
	balance roundrobin
        server db1 172.16.0.20:3306 check
        server db2 172.16.0.21:3306 check backup weight 2
        server db3 172.16.0.22:3306 check backup weight 1
  1. global 항목에 external-check 값이 추가되었다.
  2. external-check를 사용하기위해서 insecure-setuid-wanted 값을 추가해야한다
    https://www.haproxy.com/blog/announcing-haproxy-2-2/#security-hardening
  3. backend 항목에 기존 mysql-check 항목을 external-check로 변경
  4. external-check path 추가
  5. external-check command 추가

/etc/haproxy/script/check_node.sh

#!/bin/bash

MYSQL_HOST="$3"
MYSQL_PORT="$4"
MYSQL_USERNAME="haproxy"
MYSQL_PASSWORD=""
MYSQL_BIN="/bin/mysql"
MYSQL_OPTS="--no-defaults --connect-timeout=10 -snNE"
CMDLINE="$MYSQL_BIN $MYSQL_OPTS --host=$MYSQL_HOST --port=$MYSQL_PORT --user=$MYSQL_USERNAME -e"
WSREP_LOCAL_STATE_CHK=`$CMDLINE "SHOW GLOBAL STATUS WHERE VARIABLE_NAME='wsrep_local_state'" | tail -1`

if [ "$WSREP_LOCAL_STATE_CHK" != "4" ]; then
    exit 255
fi

exit 0

파라메터 정보

$1 = Virtual Service IP (VIP)
$2 = Virtual Service Port (VPT)
$3 = Real Server IP (RIP)
$4 = Real Server Port (RPT) 
$5 = Check Source IP 

$3, $4를 이용해서 노드의 상태를 읽는다.

HAProxy + Galera Cluster (MariaDB) 2/2

HAProxy

우분투 20.04 에서 진행한다. 패키지관리자로 설치가 가능하다. SSL을 지원하는 2.x 버전이 설치된다.

apt install -y haproxy
root@haproxy1:~# haproxy --version
HA-Proxy version 2.0.29-0ubuntu1 2022/08/26 - https://haproxy.org/
Usage : haproxy [-f <cfgfile|cfgdir>]* [ -vdVD ] [ -n <maxconn> ] [ -N <maxpconn> ]
        [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]
        -v displays version ; -vv shows known build options.
        -d enters debug mode ; -db only disables background mode.
        -dM[<byte>] poisons memory with <byte> (defaults to 0x50)
        -V enters verbose mode (disables quiet mode)
        -D goes daemon ; -C changes to <dir> before loading files.
        -W master-worker mode.
        -Ws master-worker mode with systemd notify support.
        -q quiet mode : don't display messages
        -c check mode : only check config files and exit
        -n sets the maximum total # of connections (uses ulimit -n)
        -m limits the usable amount of memory (in MB)
        -N sets the default, per-proxy maximum # of connections (0)
        -L set local peer name (default to hostname)
        -p writes pids of all children to this file
        -de disables epoll() usage even when available
        -dp disables poll() usage even when available
        -dS disables splice usage (broken on old kernels)
        -dG disables getaddrinfo() usage
        -dR disables SO_REUSEPORT usage
        -dr ignores server address resolution failures
        -dV disables SSL verify on servers side
        -sf/-st [pid ]* finishes/terminates old pids.
        -x <unix_socket> get listening sockets from a unix socket
        -S <bind>[,<bind options>...] new master CLI

haproxy 설정

파일을 생성하거나 수정해야 한다. 각 HAProxy 노드별로 설정파일이 같다.

/etc/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

        # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

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

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

frontend mysql-front
        bind *:3306
        mode tcp
        default_backend mysql-back

backend mysql-back
        mode tcp
        option mysql-check user haproxy
        balance roundrobin
        server db1 172.16.0.20:3306 check
        server db2 172.16.0.21:3306 check backup weight 2
        server db3 172.16.0.22:3306 check backup weight 1

이 설정은 db1 노드를 Active 상태로 두고 나머지는 stand-by한다. db1 노드에 장애가 발생하면 다음번 노드가 활성화된다.

mysql-check 옵션

백엔드에 입력된 노드상태 확인을 위해서 mysql 접근이 가능해야한다. 따라서 각 데이터베이스 노드에 haproxy 유저를 추가해준다.

MariaDB [(none)] > create user 'haproxy'@'172.16.0.%';

Keepalived

HAProxy를 이중화하기 위해서 keepalived가 필요하다. VIP를 가지고 있다가 마스터 노드에 장애가 발생하면 슬래이브 노드에 VIP를 바인딩해준다.

apt install -y keepalived

로컬 호스트주소외 다른 가상아이피를 바인딩할 수 있도록 설정을 변경해야 한다.
아래파일에 추가하거나 수정한다.

/etc/sysctl.conf

net.ipv4.ip_nonlocal_bind = 1
root@haproxy1:/etc/keepalived# sysctl -p
net.ipv4.ip_nonlocal_bind = 1

HAProxy 1번노드

/etc/keepalived/keepalived.conf

global_defs {
        router_id HAProxy1
}

vrrp_script check_haproxy {
        script "killall -0 haproxy"
        interval 2
        weight 2
}

vrrp_instance VIS_TEST {
        interface         eth0
        state             MASTER
        priority          101
        virtual_router_id 51
        advert_int        1

        virtual_ipaddress {
                172.16.0.5
        }

        track_script {
                check_haproxy
        }
}

HAProxy 2번노드

/etc/keepalived/keepalived.conf

global_defs {
        router_id HAProxy2
}

vrrp_script check_haproxy {
        script "killall -0 haproxy"
        interval 2
        weight 2
}

vrrp_instance VIS_TEST {
        interface         eth0
        state             MASTER
        priority          100
        virtual_router_id 51
        advert_int        1

        virtual_ipaddress {
                172.16.0.5
        }

        track_script {
                check_haproxy
        }
}

router_id 값은 각 노드마다 다르다.
priority 값은 마스터가 값이 더 커야 한다.
virtual_ipaddress 값은 VIP로 마스터가 이 VIP를 바인딩하다 장애가 발생하면 다른 노드가 이 VIP를 바인딩해서 이중화가 가능하다.

서비스 재시작

systemctl restart keepalived.service

네트워크 확인

1번 노드에 VIP가 바인딩된 것이 보인다. 1번노드에서 haproxy 서비스를 중지시키면 2번 노드에 VIP가 바인딩되는 것을 확인할 수 있다.

root@haproxy1:/etc/haproxy# ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 172.16.0.10/24 brd 172.16.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 172.16.0.5/32 scope global eth0
       valid_lft forever preferred_lft forever
root@haproxy2:/etc/haproxy# ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 172.16.0.11/24 brd 172.16.0.255 scope global eth0
       valid_lft forever preferred_lft forever

HAProxy + Galera Cluster (MariaDB) 1/2

Galera Cluster

우분투 20.04 에서 진행한다. 패키지관리자에서 제공하는 mariadb-server를 설치하면 galera3이 같이 설치된다.

$ apt update && apt upgrade -y
$ apt install -y mariadb-server

Galera 클러스터 설정하기

  1. wsrep_cluster_address : 전체 노드의 아이피를 쉼표로 구분해서 모두 입력
  2. wsrep_node_address : 현재 노드의 아이피를 입력
  3. wsrep_node_name : 현재 노드를 구분할 이름을 입력

Database1

/etc/mysql/mariadb.conf.d/60-galera.cnf

[galera]
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0

# Galera Provider Configuration
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so

# Galera Cluster Configuration
wsrep_cluster_name="galera_cluster"
wsrep_cluster_address="gcomm://172.16.0.20,172.16.0.21,172.16.0.22"

# Galera Synchronization Configuration
wsrep_sst_method=rsync

# Galera Node Configuration
wsrep_node_address="172.16.0.20"
wsrep_node_name="node1"

Database2

/etc/mysql/mariadb.conf.d/60-galera.cnf

[galera]
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0

# Galera Provider Configuration
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so

# Galera Cluster Configuration
wsrep_cluster_name="galera_cluster"
wsrep_cluster_address="gcomm://172.16.0.20,172.16.0.21,172.16.0.22"

# Galera Synchronization Configuration
wsrep_sst_method=rsync

# Galera Node Configuration
wsrep_node_address="172.16.0.21"
wsrep_node_name="node2"

Database3

/etc/mysql/mariadb.conf.d/60-galera.cnf

[galera]
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0

# Galera Provider Configuration
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so

# Galera Cluster Configuration
wsrep_cluster_name="galera_cluster"
wsrep_cluster_address="gcomm://172.16.0.20,172.16.0.21,172.16.0.22"

# Galera Synchronization Configuration
wsrep_sst_method=rsync

# Galera Node Configuration
wsrep_node_address="172.16.0.22"
wsrep_node_name="node3"

클러스터 초기화

첫번째 노드에서 실행한다

$ galera_new_cluster

서비스 재시작

각 노드별로 실행해준다

$ systemctl restart mariadb.service

클러스터 상태 확인

$ mysql -uroot -p
MariaDB [(none)] > show status like 'wsreq%';
+-------------------------------+----------------------------------------------------------+
| Variable_name                 | Value                                                    |
+-------------------------------+----------------------------------------------------------+
| wsrep_applier_thread_count    | 1                                                        |
| wsrep_apply_oooe              | 0.000000                                                 |
| wsrep_apply_oool              | 0.000000                                                 |
| wsrep_apply_window            | 1.000000                                                 |
| wsrep_causal_reads            | 0                                                        |
| wsrep_cert_deps_distance      | 95.038207                                                |
| wsrep_cert_index_size         | 106                                                      |
| wsrep_cert_interval           | 0.000000                                                 |
| wsrep_cluster_conf_id         | 22                                                       |
| wsrep_cluster_size            | 3                                                        |
| wsrep_cluster_state_uuid      | 2f9b1216-670b-11ed-a410-02c6026a7d9b                     |
| wsrep_cluster_status          | Primary                                                  |
| wsrep_cluster_weight          | 3                                                        |
| wsrep_commit_oooe             | 0.000000                                                 |
| wsrep_commit_oool             | 0.000000                                                 |
| wsrep_commit_window           | 1.000000                                                 |
| wsrep_connected               | ON                                                       |
| wsrep_desync_count            | 0                                                        |
| wsrep_evs_delayed             |                                                          |
| wsrep_evs_evict_list          |                                                          |
| wsrep_evs_repl_latency        | 0.00183486/0.00210722/0.00237959/0.000272366/2           |
| wsrep_evs_state               | OPERATIONAL                                              |
| wsrep_flow_control_paused     | 0.000068                                                 |
| wsrep_flow_control_paused_ns  | 395662483                                                |
| wsrep_flow_control_recv       | 15                                                       |
| wsrep_flow_control_sent       | 1                                                        |
| wsrep_gcomm_uuid              | 9ca4e10d-6a23-11ed-9e58-135c9b8d1ba7                     |
| wsrep_incoming_addresses      | 172.16.0.20:3306,172.16.0.21:3306,172.16.0.22:3306 |
| wsrep_last_committed          | 1941702                                                  |
| wsrep_local_bf_aborts         | 0                                                        |
| wsrep_local_cached_downto     | 1885300                                                  |
| wsrep_local_cert_failures     | 0                                                        |
| wsrep_local_commits           | 0                                                        |
| wsrep_local_index             | 1                                                        |
| wsrep_local_recv_queue        | 0                                                        |
| wsrep_local_recv_queue_avg    | 0.401237                                                 |
| wsrep_local_recv_queue_max    | 110                                                      |
| wsrep_local_recv_queue_min    | 0                                                        |
| wsrep_local_replays           | 0                                                        |
| wsrep_local_send_queue        | 0                                                        |
| wsrep_local_send_queue_avg    | 0.000000                                                 |
| wsrep_local_send_queue_max    | 1                                                        |
| wsrep_local_send_queue_min    | 0                                                        |
| wsrep_local_state             | 4                                                        |
| wsrep_local_state_comment     | Synced                                                   |
| wsrep_local_state_uuid        | 2f9b1216-670b-11ed-a410-02c6026a7d9b                     |
| wsrep_open_connections        | 0                                                        |
| wsrep_open_transactions       | 0                                                        |
| wsrep_protocol_version        | 9                                                        |
| wsrep_provider_name           | Galera                                                   |
| wsrep_provider_vendor         | Codership Oy <info@codership.com>                        |
| wsrep_provider_version        | 3.29(ra60e019)                                           |
| wsrep_ready                   | ON                                                       |
| wsrep_received                | 57714                                                    |
| wsrep_received_bytes          | 17158682                                                 |
| wsrep_repl_data_bytes         | 0                                                        |
| wsrep_repl_keys               | 0                                                        |
| wsrep_repl_keys_bytes         | 0                                                        |
| wsrep_repl_other_bytes        | 0                                                        |
| wsrep_replicated              | 0                                                        |
| wsrep_replicated_bytes        | 0                                                        |
| wsrep_rollbacker_thread_count | 1                                                        |
| wsrep_thread_count            | 2                                                        |
+-------------------------------+----------------------------------------------------------+

wsrep_cluster_size : 클러스터에 가입된 있는 노드수
wsrep_incoming_addresses : 클러스터에 가입된 아이피
wsrep_local_state_comment : 현재 노드 상태 (다른 노드들과 데이터가 동기화되어 있는 상태면 Synced)

Apache James 메일서버 – 운영

0. 서비스 시작

cd /usr/local/james
./bin/james start

1. 서비스 상태확인

./bin/james status
root@mail:/usr/local/james# ./bin/james status
Apache James :: Server :: Spring :: App is running (22938).

2. 중지하기, 재시작

./bin/james stop
./bin/james restart

3. 로그파일

tail -f log/wrapper.log

자세한 로그를 얻기위해서는 conf/log4j2.xml 파일을 수정하고 재시작하면 된다. status 속성을 변경한다.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" monitorInterval="30">

4. 도메인 추가

conf/domainlist.xml 파일에서 도메인을 추가하지 않고 james-cli 명령을 이용해서 추가가 가능하다.

./bin/james-cli AddDomains domain.com

5. 사용자 추가

도메인을 추가하면 해당 도메인내에서 사용이 가능한 사용자를 추가할 수 있다. 사용자명은 도메인을 포함한 이메일주소여야 한다.
james-cli Adduser <username> <password>

./bin/james-cli AddUser me@jongwan.com 1234

비밀번호 변경하기

./bin/james-cli SetPassword me@jongwan.com abcd1234

사용자 삭제하기

./bin/james-cli RemoveUser me@jongwan.com

6. 사용가능한 CLI 명령어

root@mail:/usr/local/james# ./bin/james-cli.sh
You must specify a command.
usage: java org.apache.james.cli.ServerCmd --host <arg> <command>
 -h,--host <arg>   node hostname or ip address
 -p,--port <arg>   remote jmx agent port number
AddUser <username> <password>
RemoveUser <username>
ListUsers
AddDomain <domainName>
RemoveDomain <domainName>
ContainsDomain <domainName>
ListDomains
ListMappings
ListUserDomainMappings <user> <domain>
AddAddressMapping <fromUser> <fromDomain> <toAddress>
RemoveAddressMapping <fromUser> <fromDomain> <toAddress>
AddRegexMapping <user> <domain> <regex>
RemoveRegexMapping <user> <domain> <regex>
SetPassword <username> <password>
CopyMailbox <srcBean> <dstBean>
DeleteUserMailboxes <user>
CreateMailbox <namespace> <user> <name>
ListUserMailboxes <user>
DeleteMailbox <namespace> <user> <name>
ImportEml <namespace> <user> <name> <path>
GetStorageQuota <quotaroot>
GetMessageCountQuota <quotaroot>
GetQuotaroot <namespace> <user> <name>
GetMaxStorageQuota <quotaroot>
GetMaxMessageCountQuota <quotaroot>
SetMaxStorageQuota <quotaroot> <maxMessageCount>
SetMaxMessageCountQuota <quotaroot> <maxStorage>
SetGlobalMaxStorageQuota <maxStorage>
SetGlobalMaxMessageCountQuota <maxMessageCount>
GetGlobalMaxStorageQuota
GetGlobalMaxMessageCountQuota
ReindexMailbox <namespace> <user> <name>
ReindexAll
GetSieveQuota
SetSieveQuota <quota>
RemoveSieveQuota
GetSieveUserQuota <username>
SetSieveUserQuota <username> <quota>
RemoveSieveUserQuota <username>
AddActiveSieveScript <username> <scriptname> <path>