一般的に、これは誰かに興味があるように思えました。 したがって、この決定を強調することにしました。 さらに、最後に、有名な競合他社にはないHAProxyを使用する柔軟性を示します。
簡単に機能する仕組み
HAProxyはRDPを識別し、プロキシ化し、rdp_cookieを解析して必要な情報をフェッチし、ルーティングメカニズムで使用できます。
クライアントはプロキシに接続し、rdp_cookieからログインを引き出し、そのサーバーを選択し、「login-server」値をスティックテーブルに書き込み、ユーザーをサーバーに接続します。
したがって、次回同じクライアントが(このログインで)接続されると、プロキシはテーブル内のエントリを見つけ、ユーザーが開いているセッションを持っているサーバーに接続します。 華麗でシンプル!
スティックテーブルは、プロセスメモリに保存されているテーブルで、各レコードについて寿命を判断できます。 8時間に設定すると、クライアントは1日中同じサーバーにアクセスできます。
以下は標準設定です:
#/usr/local/etc/haproxy.conf global daemon stats socket /var/run/haproxy.sock mode 600 level admin stats timeout 2m defaults log global mode tcp option tcplog option dontlognull frontend fr_rdp mode tcp bind *:3389 name rdp log global option tcplog tcp-request inspect-delay 2s tcp-request content accept if RDP_COOKIE default_backend BK_RDP backend BK_RDP mode tcp balance leastconn timeout server 5s timeout connect 4s log global option tcplog stick-table type string len 32 size 10k expire 8h stick on rdp_cookie(mstshash),bytes(0,6) stick on rdp_cookie(mstshash) option tcp-check tcp-check connect port 3389 default-server inter 3s rise 2 fall 3 server TS01 172.16.50.11:3389 weight 10 check server TS02 172.16.50.12:3389 weight 20 check server TS03 172.16.50.13:3389 weight 10 check server TS04 172.16.50.14:3389 weight 20 check server TS05 172.16.50.15:3389 weight 10 check server TS06 172.16.50.16:3389 weight 10 check server TS07 172.16.50.17:3389 weight 20 check server TS08 172.16.50.18:3389 weight 20 check listen stats bind *:9000 mode http stats enable #stats hide-version stats show-node stats realm Haproxy\ Statistics stats uri /
難しさ
スティックテーブルはメモリ内にあるため、プロセスを再起動すると、クライアントとサーバーのペアに関するすべての情報が失われます。これは、この場合の重要な情報です。 この状況から抜け出すために、プロセスを再起動するために使用するスクリプトを書きました。 プロセスを停止する前のスクリプトは、スティックテーブルをファイルにスローし、プロセスの開始後にデータを書き戻します(現在のセッションは中断しません)。
#!/usr/bin/env python import sys import socket import re import subprocess haproxyConf = '/usr/local/etc/haproxy.conf' def restart(): backends = [] with open(haproxyConf) as f: for line in f: lines = line.split(' ') if lines[0] == 'backend': backends.append(lines[1].strip('\n')) for backend in backends: getDataTables(backend) rebootHa() for backend in backends: insertDataTables(backend) # Writes data from stik-tables to external files def getDataTables(table): print table tmp_f = open('/tmp/tmp.' + table,'w') tableVal = {} c = socket.socket( socket.AF_UNIX ) c.connect("/var/run/haproxy.sock") c.send("prompt\r\n") c.send("show table " + table + "\r\n") d = c.recv(10240) for line in d.split('\n'): if re.search('^[a-zA-Z_0-9]',line): line = line.split(' ') del line[0] for item in line: key = item.split('=')[0] val = item.split('=')[1] tableVal[key] = val print tableVal['key'] print tableVal['server_id'] tmp_f.write(tableVal['key'] + ',' + tableVal['server_id'] + '\n') tmp_f.close() def rebootHa(): subprocess.call("/usr/local/etc/rc.d/haproxy reload", shell=True) # Writes data from files to stik-tables def insertDataTables(table): tmp_f = open('/tmp/tmp.' + table,'r') c = socket.socket( socket.AF_UNIX ) c.connect("/var/run/haproxy.sock") c.send("prompt\r\n") for line in tmp_f: line = line.split(',') print "set table " + table + " key " + line[0] + " data.server_id " + line[1] c.send("set table " + table + " key " + line[0] + " data.server_id " + line[1] +"\r\n") c.recv(10240) c.close() restart()
他に何?
特定のクライアントをプロキシするサーバーを柔軟に制御することもできます。 これは、ログイン、IPアドレス、ネットワーク、時刻などに基づいて実行できます。
ADのグループに基づいて、ファームサーバーを部門に分割する方法の例を示します。例:
アカウンティング用の2つのサーバー
マーケティング用の2つのサーバー
セールスマン用の2つのサーバー
みんなのために2つ。
サーバーの各グループには、インストール済みのソフトウェアと特定の設定など、異なる容量が存在する可能性があることは明らかなので、それらを分離します。
特定のポリシーに基づいて、HAProxyを使用すると、ユーザーを接続するサーバーを柔軟に決定でき、すべてのRDP接続に1つのエントリポイントを使用できます(間違いなく便利です)。
これを行うには、HAProxy構成と再起動スクリプトをわずかに変更する必要があります。
#/usr/local/etc/haproxy.conf global daemon stats socket /var/run/haproxy.sock mode 600 level admin stats timeout 2m defaults log global mode tcp option tcplog option dontlognull frontend fr_rdp mode tcp bind *:3389 name rdp #timeout client 1h log global option tcplog tcp-request inspect-delay 2s tcp-request content accept if RDP_COOKIE acl Accounting_ACL rdp_cookie(mstshash),bytes(0,6) -m str -i -f /usr/local/etc/haproxy/Accounting acl Marketing_ACL rdp_cookie(mstshash),bytes(0,6) -m str -i -f /usr/local/etc/haproxy/Marketing acl Sales_ACL rdp_cookie(mstshash),bytes(0,6) -m str -i -f /usr/local/etc/haproxy/Sales use_backend Accounting_BK if Accounting_ACL use_backend Marketing_BK if Marketing_ACL use_backend Sales_BK if Sales_ACL default_backend DF_RDP backend DF_RDP mode tcp balance leastconn log global option tcplog stick-table type string len 32 size 10k expire 8h stick on rdp_cookie(mstshash),bytes(0,6) option tcp-check tcp-check connect port 3389 default-server inter 3s rise 2 fall 3 server TS01 172.16.50.11:3389 weight 10 check server TS02 172.16.50.12:3389 weight 10 check backend Accounting_BK mode tcp balance leastconn log global stick-table type string len 32 size 10k expire 8h stick on rdp_cookie(mstshash),bytes(0,6) option tcplog tcp-check connect port 3389 default-server inter 3s rise 2 fall 3 server TS03 172.16.50.13:3389 weight 10 check server TS04 172.16.50.14:3389 weight 10 check backend Marketing_BK mode tcp balance leastconn log global stick-table type string len 32 size 10k expire 8h stick on rdp_cookie(mstshash),bytes(0,6) option tcplog tcp-check connect port 3389 default-server inter 3s rise 2 fall 3 server TS05 172.16.50.15:3389 weight 10 check server TS06 172.16.50.16:3389 weight 10 check backend Sales_BK mode tcp balance leastconn log global stick-table type string len 32 size 10k expire 8h stick on rdp_cookie(mstshash),bytes(0,6) option tcplog tcp-check connect port 3389 default-server inter 3s rise 2 fall 3 server TS07 172.16.50.17:3389 weight 10 check server TS08 172.16.50.18:3389 weight 10 check listen stats bind *:9000 mode http stats enable #stats hide-version stats show-node stats realm Haproxy\ Statistics stats uri /
変更された再起動スクリプト:
#!/usr/bin/env python import sys import ldap import socket import re import subprocess ldapDomain = '' ldapUser = '' ldapPass = '' ldapDN = '' # OU=GROUPS,DC=domain,DC=tld' haproxyConf = '/usr/local/etc/haproxy.conf' action = sys.argv[1] # Get users from Active Directory Groups and store it to files def getADGroups(): l = ldap.open(ldapDomain) l.simple_bind_s(ldapUser,ldapPass) f = open('/usr/local/etc/haproxy/' + groupName,'w') results = l.search_s("cn=%s, %s" % (groupName, ldapDN), ldap.SCOPE_BASE) for result in results: result_dn = result[0] result_attrs = result[1] if "member" in result_attrs: for member in result_attrs["member"]: f.write(member.split(',')[0].split('=')[1] + '\n') f.close() restart() # Searching stik-tables to save it and to restore after reload def restart(): backends = [] with open(haproxyConf) as f: for line in f: lines = line.split(' ') if lines[0] == 'backend': backends.append(lines[1].strip('\n')) for backend in backends: getDataTables(backend) rebootHa() for backend in backends: insertDataTables(backend) # Writes data from stik-tables to external files def getDataTables(table): print table tmp_f = open('/tmp/tmp.' + table,'w') tableVal = {} c = socket.socket( socket.AF_UNIX ) c.connect("/var/run/haproxy.sock") c.send("prompt\r\n") c.send("show table " + table + "\r\n") d = c.recv(10240) for line in d.split('\n'): if re.search('^[a-zA-Z_0-9]',line): line = line.split(' ') del line[0] for item in line: key = item.split('=')[0] val = item.split('=')[1] tableVal[key] = val print tableVal['key'] print tableVal['server_id'] tmp_f.write(tableVal['key'] + ',' + tableVal['server_id'] + '\n') tmp_f.close() def rebootHa(): #pass subprocess.call("/usr/local/etc/rc.d/haproxy reload", shell=True) # Writes data from files to stik-tables def insertDataTables(table): #pass tmp_f = open('/tmp/tmp.' + table,'r') #tableVal = {} c = socket.socket( socket.AF_UNIX ) c.connect("/var/run/haproxy.sock") c.send("prompt\r\n") for line in tmp_f: line = line.split(',') print "set table " + table + " key " + line[0] + " data.server_id " + line[1] c.send("set table " + table + " key " + line[0] + " data.server_id " + line[1] +"\r\n") c.recv(10240) c.close() if action == 'restart': restart() if action == 'group': groupName = sys.argv[2] getADGroups()
仕組み:
グループはADで作成されます(おそらく既にそのようなグループがあります)。アカウント、マーケティング、販売、従業員はこれらのグループに配置されます。 スクリプトはADに接続し、選択したグループの従業員のリストを受け取ります。 従業員のリストは、グループの名前とともにファイルに保存されます。
HAProxy構成では、ACLはこれらのグループファイルのソースで構成されます。 新しい従業員がグループに追加された場合、スクリプトを実行してグループファイルを更新する必要があります。
プロキシは、指定されたファイルにログインがあるかどうかを確認します。 存在する場合、このグループに固有のバックエンドに送信します。 すべてが非常に簡単です!
スクリプト起動オプション:
haproxy.py group group_name-グループの再起動、現在のセッションは中断しません。
haproxy.py restart-プロセスを再起動(構成を再読み込み)しますが、現在のセッションは中断しません。
耐障害性
彼女はそこにいません
この例では、ソリューションに耐障害性はありません。
まず、haproxyは予約されていません。
第二に、クライアントサーバーの値をスティックテーブルに書き込むソリューションでは、haproxyがユーザーをレコードが既にテーブルにあるライブサーバーに接続することを許可せず、接続されたサーバーは現在利用できません。 彼はオンラインではないにもかかわらず、テーブルからサーバーにそれらを送信しようとします。
まず、haproxyバックアップはさまざまな方法で実行できます。
おかげでvasilevkirill 、彼はコメントで共有した組み込みソリューションがあります
habrahabr.ru/post/335872/#comment_10369854
二番目はもっと難しいです。 サーバーで何が起こっているかを正確に判断するメカニズムが必要です。 サーバーは、何らかの正当な理由であまり正当な理由ではないが、たとえば1分間など、ネットワーク上で利用できない場合があります。 ただし、同時にすべてのRDPセッションを開いてください。 サーバーが使用できなくなったと判断し、すべてのユーザーを他のサーバーに切り替える必要がある場合、未保存のデータを取得したり、クライアントが大量の作業を失ったりする可能性があります。
技術的には、スティックテーブルクリーンアップの実装は簡単です。 サーバーの状態を監視するには、さまざまな監視システムを使用できます。 同じZabbixでは、イベントでローカルスクリプトを呼び出すことができますが、この場合、スティックテーブルクリーニングスクリプトを呼び出すことができます。
結論として、上で示した欠点を考慮に入れて、HAProxyは非常に安定して確実に動作します。