最終更新日: 2022年9月6日

Proxyをバランシングする

1. 想定環境

HTTP ProxyとしてSquidの利用を想定します。 構成は以下のような構成とします。

2.前準備

HAProxyおよびSquidをRedhat/CentOS系で構築する場合、SELinuxとFirewalldの設定を予め済ませておくと良いでしょう。
  1. SELinuxの設定
SELinuxはデフォルトで有効になっているのでこれを無効化します。(Squid、HAProxy)
# setenforce 0
また、OSの再起動時にSELinuxが設定されないように /etc/sysconfig/selinux の以下の部分を修正します。
# SELINUX=enforcing
SELINUX=disabled
# SELINUXTYPE= can take one of three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted


  1. Firewalldの設定
ファイアーウォール機能(デフォルトは有効になっている)を使っている場合には、Squid、HAProxyのそれぞれのサーバで待ち受けポートへのアクセスを許可するようにルールを設定します。
(1) Squidの場合
Squid用のファイアーウォールルールは、/usr/lib/firewalld/services/squid.xml としてデフォルトで用意されていますが、このルールではSquidのデフォルトの待ち受けポートである 3128/TCP のアクセスを許可するようになっているので、これを 8080/TCP に変更します。

<?xml version="1.0" encoding="utf-8"?>
<service>
    <short>squid</short>
    <description>Squid HTTP proxy server</description>
    <port protocol="tcp" port="8080"/>
</service>

変更後、このルールを適用します。
# firewall-cmd --add-service=squid --zone=public --permanent
# systemctl reload firewalld
(2) HAProxyの場合
HAProxyでは、Firewall用のルールファイルがデフォルトで存在しないので、Ssquid用のルールファイルをコピーしてHAProxy用に編集します。
# cp /usr/lib/firewalld/services/squid.xml /etc/firewalld/services/haproxy.xml
コピーした /etc/firewalld/services/haproxy.xml を次のように書き換えます。
<?xml version="1.0" encoding="utf-8"?>
<service>
    <short>haproxy</short>
    <description>haproxy Load Balancer</description>
    <port protocol="tcp" port="8080"/>
    <port protocol="tcp" port="8888"/>
</service>
このルールは、8080/TCP と 8888/TCP のアクセスを許可するようになっています。 作成したルールを適用します。
# firewall-cmd --add-service=haproxy --zone=public --permanent
# systemctl reload firewalld

3.Squidの設定

Squidでは、syslogを使いSquidのログをrSyslogサーバに転送するように/etc/squid/squid.confを設定を行います。
■ 設定項目
   :
# http_port 3128
http_port 8080 # ---proxyの待ち受けポート番号
  :
#Default:
# cache_mem 8 MB
cache_mem 32 MB # ---実メモリを考えて設定する
  :
# ACL list
# acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
# acl localnet src 172.16.0.0/12 # RFC1918 possible internal network
# acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
acl localnet src 192.168.10.0/24 # My Localnetwork ----- Proxyへのアクセスを利用許可するサブネット
acl localnet src fc00::/7 # RFC 4193 local private network range
acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
acl SSL_ports port 443
  :
#
acl MyServers dstdomain example.jp # internal Network cache ignore --- 自分のサーバのキャッシュをしないためのACL
acl has-xff req_header X-Forwarded-For ^(([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)|(\[([0-9a-f]+)?:([0-9a-f:]+)?:([0-9a-f]+|0-9\.]+)?\])) # --- X-Forwarded-For 情報の有無判定用ACL
  :
# no cache
cache deny MyServers # --- 自分のサーバはキャッシュしない
  :
# ローカルネットワークからのアクセスにはX-Forwarded-forヘッダを受け付ける
# follow_x_forwarded_for allow localhost
follow_x_forwarded_for allow localnet # ---
  :

# Only allow cachemgr access from localhost
http_access allow manager localhost
http_access deny manager
http_access allow localhost # --- localhostからのアクセスを許可
http_access allow localnet # ---ローカルネットワークからのアクセスを許可/行の順番に意味があるので必ずこの行へ記述すること

# And finally deny all other access to this proxy
http_access deny all

  :
#Default:
# forwarded_for on
forwarded_for off # ---接続相手にローカルIPを渡さない為の設定
  :
#cache_dir ufs /var/spool/squid 100 16 256
cache_dir ufs /var/spool/squid 1000 16 256 # --- キャッシュ用ディスクサイズを指定
  :
# Cache Memory Area size
cache_mem 2048 MB # ---メモリキャッシュサイズ指定(自分の持っている物理メモリの 50%としました。)
  :
# maximum Cache size on memory.
maximum_object_size_in_memory 2048 KB # ---メモリキャッシュできるオブジェクトの最大サイズを指定(2MB以上のオブジェクトはキャッシュしない)
  :
# maximum file object size
maximum_object_size 20 MB # ファイルキャッシュ対象はは20MB以内のオブジェクトとする
  :
# Log format (default formats available)
#logformat squid %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %[un %Sh/%<a %mt
#logformat common %>a %[ui %[un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st %Ss:%Sh
#logformat combined %>a %[ui %[un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh
#logformat referrer %ts.%03tu %>a %{Referer}>h %ru
#logformat useragent %>a [%tl] "%{User-Agent}>h"
#logformat squid-xff %ts.%03tu %6tr %{X-Forwarded-For}>h %Ss/%03>Hs %<st %rm %ru %[un %Sh/%<a %mt
#
# SquidへのアクセスはHAProxy経由で行なわれるので、Squidのアクセスログはアクセス元のIPアドレスとしてHAProxyのアドレスを記録してしまい、
# クライアントPCのIPアドレスが記録されなくなる。 これの対策として、ログへの記録としてX-Forwarded-forヘッダ情報を記録させるようにする。# LOGフォーマットとして、"combined"を基に通常のソースIPの代わりにX-Forwarded-forヘッダ情報出力する新たな"combined-xff"というフォーマットを定義する
logformat combined-xff %{X-Forwarded-For}>h %[ui %[un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh

# ローカルネットワークからのアクセスの場合には“combined-xff”形式、それ以外には”combine”形式でSyslogへ出力する
# access log output place.
# access_log udp://172.16.6.104:514 squid
access_log syslog:local5.info combined !has-xff
access_log syslog:local5.info combined-xff has-xff


# X-Forwarded-Forヘッダー情報を上位のサーバには渡さない(アクセス元のローカルIPを隠ぺい)する。
forwarded_for off

# Squidエラー画面を日本語にする
error_default_language ja

# ホスト名だけのURLでのアクセスの場合に、ドメイン名を付加する。
dns_defnames on
append_domain .example.jp


# Squidのホスト名を明示的に宣言(古いバージョンのSquidでは必要だったが、現在は設定しなくても良い)
# node-1 or node-2 or node-3 をそれぞれ設定する。
visible_hostname node-1.example.jp

4.rSyslogサーバの設定

squidのAccessログはNode-1~3までのそれぞれのサーバで発生するします。そこで後で集計する場合などにぞれぞれのファイルをマージする処理を避けるために、Accessログをrsyslogを実行しているサーバに集約させるようにSquidを上記で設定しています。
rsyslogサーバでは/etc/rsyslog.confで次の設定を行います。
   :
$ModLoad imudp
$UDPServerRun 514
# --- 514/udp ポートでsyslogを待ち受ける(なお、従来必要だったコマンドでの"-r"オプションは現在のrsyslogでは不要)
   :
# *.info;mail.none;authpriv.none;cron.none /var/log/messages
*.info;mail.none;authpriv.none;cron.none;local5.none /var/log/messages # --- local5.noneのメッセージを/var/log/messagesに記録させないように対象除外とする。
   :
# ログのフォーマットのテンプレートとして、メッセージの先頭1文字を除外した "t-squid"という形式を設定
# これはsyslogで送られてきたSquidのAccessログの先頭に1スペース勝手に補完されてしまう事への対策
# Save proxy log messages also to squid access.log
$template t-squid,"%msg:2:$%\n"

local5.* /var/log/squid/r_access.log;t-squid # --- local5.* レベルのsyslogを /var/log/squid/r_access.log ファイルに出力。その際の出力フォーマットは t-squid とする。
  

5.HAProxyの設定

HAProxyでは、フロントエンドであるHAProxyがクライアントPCからの待ち受けをし、バックエンドにある3台のSquidサーバへロードバランシングを行うようにする。 ロードバランスの方式として単純なラウンドロビンで行ってしまうと、SSLセッションの維持ができなくなる可能性が高いので、バランシング方式はソースIPアドレスを使ったハッシュバランス方式とする。
/etc/haproxy/haproxy.cfg を以下のように設定する。
#---------------------------------------------------------------------
# Global settings
# グローバル設定では、システムに影響する全体的な設定を行います。
#---------------------------------------------------------------------
global
     log 127.0.0.1 local2

     chroot /var/lib/haproxy
     pidfile /var/run/haproxy.pid
     maxconn 30000 # --- 最大接続セッション数を指定
     user haproxy
     group haproxy
     daemon

     # turn on stats unix socket
     stats socket /var/lib/haproxy/stats

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
# デフォルト設定は、この後に設定する各'listen'や'backend'といったセクションの
# デフォルト値を設定します。
#---------------------------------------------------------------------
defaults
     mode http # --- モードはhttp(パケット)モードとします。
     log global
     option httplog
     option dontlognull
     option http-server-close
     option forwardfor except 127.0.0.0/8
     option redispatch
     retries 3
     timeout http-request 10s
     timeout queue 1m
     timeout connect 10s
     timeout client 1m
     timeout server 1m
     timeout http-keep-alive 10s
     timeout check 10s
     maxconn 10000 # --- 同時接続セッション数です。グローバルで指定した数を超えないようにします。

#---------------------------------------------------------------------
# main frontend which proxys to the backends
# フロントエンド(待ち受け)に関するセクションです。
#---------------------------------------------------------------------
frontend haproxy # --- 'haproxy'という名前でフロントエンドを定義します。
     bind 0.0.0.0:8080 # --- 待ち受けポートは、8080とします。
     bind :::8080 # --- IPv6の待ち受けポートは、8080とします。

     # acl url_static path_beg -i /static /images /javascript /stylesheets
     # acl url_static path_end -i .jpg .gif .png .css .js

     # use_backend static if url_static
     option http-keep-alive #
     option httpclose
     maxconn 8000
     option httpclose
     option forwardfor # --- X-Forwarded-for ヘッダを処理します。
     default_backend back_proxy # --- 'back_proxy'として定義したバックエンドにパケットを渡します。
     timeout client 30s #

     # これ以外の設定は、デフォルト設定に従います。

#---------------------------------------------------------------------
# static backend for serving up images, stylesheets and such
#---------------------------------------------------------------------
# backend static
#     balance roundrobin
#     server static 127.0.0.1:4331 check

#---------------------------------------------------------------------
# round robin balancing between the various backends
# バックエンドとして、フロントエンドで受け取ったパケットを転送する相手の情報を
# 設定します。
#---------------------------------------------------------------------
backend back_proxy # --- 'back_proxy'という名前でバックエンドを定義します。
     fullconn 10000 # --- バックエンドで処理する最大接続数
     mode http
     # balance roundrobin
     balance source # --- ソースIPを基にしたバランシングを行います。
     #
     # stickiness
     stick-table type ip size 100k expire 30m
     stick on src
     # timer
     timeout connect 30s
     timeout server 30s
     http-reuse safe # HTTP/2 connection
     #
     option forwardfor

     # 以下は、転送先の相手の情報です。 バックエンドは死活監視します。
     # これ以外の設定は、デフォルト設定に従います。
     server proxy1 192.168.10.21:8080 check inter 1000 maxconn 3000 # --- maxconnは各サーバで処理する接続数。指定しない場合には fullconn の範囲で自動調整される
     server proxy2 192.168.10.22:8080 check inter 1000 maxconn 3000
     server proxy3 192.168.10.23:8080 check inter 1000 maxconn 3000

#---------------------------------------------------------------------
# ステータス情報表示用Webページを表示させるための設定
# アクセスは
#     http://HAProxyのIPアドレス:8888
#     ログインID: admin
#     パスワード: P@ssword
#---------------------------------------------------------------------
listen stats
     bind :8888
     bind :::8888 # ----- IPv6の受信ポート
     mode http
     maxconn 10
     stats enable
     # stats hide-versionsystemc
     stats refresh 10s
     stats show-node
     stats auth admin:P@ssword
     stats uri /


5.HOSTSの設定

HAproxyおよび各Squid Proxyサーバでは、DNSの名前解決ができなくても相手が見つかるようにそれぞれの /etc/hosts を以下のように設定しておきます。
#
127.0.0.1 localhost localhost.localdomain
::1 localhost localhost.localdomain

#
192.168.10.81 haproxy haproxy.example.jp

# Proxys
192.168.10.21 node1
192.168.10.22 node2
192.168.10.23 node3

参考:

https://gist.github.com/alvarow/fa409edc70aeb4cf89bbc83c1c78f392