Squidにc-icapを複数設定しFailoverや負荷分散をやってみる

## はじめに

個人的にSquidとc-icapを連携させる機会があり、その際に手間取ったので作業メモとして記事を書きます。
設定内容としては、Squidに2台のc-icapを設定しFailoverと負荷分散(Load Distribution)を設定します。

##  概要

SquidはOSSのプロキシサーバーであり、c-icapはOSSのICAPサーバになります。
プロキシサーバとはHTTPリクエストの中継サーバとして使用され、ICAPサーバはプロキシサーバの機能拡張として使用されます。
例えば、プロキシサーバとICAPサーバを連携させることで、プロキシサーバ経由時にウイルススキャンを実施する等の仕組み([Server world : Squid + SquidClamav](https://www.server-world.info/query?os=CentOS_7&p=squid&f=5))が実現可能です。


本記事では、Squidに2台のc-icapを設定しFailoverと負荷分散(Load Distribution)を実現する設定方法を記載します。

以下、システム構成。
（本記事の最後のほうに使用したDockerfileを置いておきます。ご参考までに・・・）


![004.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967883/63e60659-b59a-2427-71e6-06430d5ed8f5.jpeg)


## Squidとc-icapを1対1で連携する

最初にSquidとc-icapを1対1で連携させてみます。
また、連携時の疎通確認方法も併せて記載いたします。

### 設定方法

[Squidとc-icapの連携](https://www.secioss.co.jp/squid%E3%81%A8c-icap%E3%81%AE%E9%80%A3%E6%90%BA/)を参考に作成しました。
c-icapをインストールし起動するとicap側で`/echo`が使用できます。
そのため、squid.confに以下設定を入れるだけで間単にsquidとc-icapの動作が確認できました。

```
icap_enable on
icap_service service_req1 reqmod_precache bypass=0 icap://icap1:1344/echo
adaptation_access service_req1 allow all
```

### 疎通確認方法

疎通確認方法としては以下の３種類かと思います。

1. squid側でc-icapへのアクセスを確認する方法
1. c-icap側でsquidからのアクセスを確認する方法
1. c-icapの起動を状態を確認する方法

#### squid側でc-icapへの通信ログを確認する方法

squid側でc-icapとの連携を確認するためには、squid.confに[icap_log](http://www.squid-cache.org/Versions/v3/3.5/cfgman/icap_log.html)を設定しログを出力させる必要があります。
以下設定例。

```
logformat icap_squid %ts.%03tu %6icap::tr %>a %icap::to/%03icap::Hs %icap::rm %icap::ru %un %icap::<A
icap_log /usr/local/squid/var/logs/icap_log icap_squid
```

#### c-icap側でsquidからのアクセスログを確認する方法

c-icap側に通信が来ていることはアクセスログを参照すれば可能です。
アクセスログの出力先がデフォルトであれ以下でコマンドで確認可能です。

```bash
[root@89c0fa076328 /]#tail -f /usr/local/c-icap/var/log/access.log
```

#### c-icapの起動を状態を確認する方法

icapが起動していることは以下コマンドで確認可能です。

```bash
[root@89c0fa076328 /]# /usr/local/c-icap/bin/c-icap-client
ICAP server:localhost, ip:127.0.0.1, port:1344

OPTIONS:
        Allow 204: Yes
        Preview: 1024
        Keep alive: Yes

ICAP HEADERS:
        ICAP/1.0 200 OK
        Methods: RESPMOD, REQMOD
        Service: C-ICAP/0.5.6 server - Echo demo service
        ISTag: CI0001-XXXXXXXXX
        Transfer-Preview: *
        Options-TTL: 3600
        Date: Sat, 06 Feb 2021 16:02:59 GMT
        Preview: 1024
        Allow: 204
        X-Include: X-Authenticated-User, X-Authenticated-Groups
        Encapsulated: null-body=0

[root@89c0fa076328 /]#
```



## Squidとc-icapを1対2で連携する

1対2で連携する際の通信方法としてFailover方式や負荷分散方式を設定したいケースがあるかと思います。
それぞれの設定方法を記載します。

### Failover方式を設定

squidは基本的にc-icap1のみと通信を行い、c-icap1が死んだ場合のみc-icap2と通信を行う設定になります。
squid.confに[adaptation_service_set](http://www.squid-cache.org/Versions/v3/3.5/cfgman/adaptation_service_set.html)を設定すればFailoverとなります。

以下設定例

```
icap_service service_req1 reqmod_precache bypass=0 icap://c-icap1:1344/echo
icap_service service_req2 reqmod_precache bypass=0 icap://c-icap2:1344/echo

adaptation_service_set icapset1 service_req1 service_req2
adaptation_access icapset1 allow random2
```

### 負荷分散方式を設定

squidがc-icap1とc-icap2に通信を分散させる方式になります。
squid.confに[random ACL](http://www.squid-cache.org/Versions/v3/3.5/cfgman/acl.html)を設定すれば負荷分散方式となります。

以下設定例

```
acl random1 random 1/2
acl random2 random 1/1

icap_service service_req1 reqmod_precache bypass=0 icap://c-icap1:1344/echo
adaptation_access service_req1 allow random1
icap_service service_req2 reqmod_precache bypass=0 icap://c-icap2:1344/echo
adaptation_access service_req2 allow random2
```

random ACLの設定は[Squid random outgoing IP address on each request](https://serverfault.com/questions/1001637/squid-random-outgoing-ip-address-on-each-request)を参考にしました。
ただ、上記の設定の場合、c-icapが片方でもダウンした際にはクライアントにInternal Server Errorが返されます。

個人的には片系がダウンした際には、もう片方に通信を降ってほしいので以下のような設定としました。
（オリジナルなので不安ですが、たぶんうまく動作するはず・・・）

```
acl random1 random 1/2
acl random2 random 1/1

icap_service service_req1 reqmod_precache bypass=0 icap://c-icap1:1344/echo
icap_service service_req2 reqmod_precache bypass=0 icap://c-icap2:1344/echo

adaptation_service_set icapset1 service_req1 service_req2
adaptation_service_set icapset2 service_req2 service_req1

adaptation_access icapset1 allow random1
adaptation_access icapset2 allow random2
```

## squidとc-icap連携に関するsquid.confのパラメタ

squidのFailoverと負荷分散(Load Distribution)に関係しそうなsquid側のパラメタをまとめてみました。
内容に誤り等あれば指摘いただけれると嬉しいです。。。


| 項目 | デフォルト | ざっくり概要 |
| --- | --- | --- |
| [icap_connect_timeout](http://www.squid-cache.org/Versions/v3/3.5/cfgman/icap_connect_timeout.html) | none | SquidからICAP Serverへのリクエストを出してTCPコネクションが完了するまでタイムアウト。 c-icapが死んでてもこの時間は待つ。 |
| [icap_io_timeout](http://www.squid-cache.org/Versions/v3/3.5/cfgman/icap_io_timeout.html) | Use read_timeout | SquidとICAP Server間がEstablishedとなっている状態で発生したI/Oのタイムアウト。|
| [icap_service_failure_limit](http://www.squid-cache.org/Versions/v3/3.5/cfgman/icap_service_failure_limit.html) | 10 | ICAPが死んでると判断するまでの回数。|
| [icap_service_revival_delay](http://www.squid-cache.org/Versions/v3/3.5/cfgman/icap_service_revival_delay.html) | 180 | OPTIONSリクエストが失敗した際の再トライ間隔(?)。最小値は30秒。|

設定例は以下のような感じ。

```
icap_service_failure_limit 1
icap_service_revival_delay 30
icap_connect_timeout 2 seconds
icap_io_timeout 1500 seconds
```

## まとめ

この記事ではsquidとc-icapを連携(Failover/負荷分散)させる方法を記載いたしました。
また、その設定に関連しそうなsquid.confのパラメタも併せて記載いたしました。



ただ、分からなかったこともありました。
具体的には、squidとc-icap間で不定期に実行されるOPTIONS通信です。
たぶんc-icapが生きているかどうかを監視している通信(ヘルスチェック？）と考えているのですが、実行間隔やトリガー、仕組み等がよく分かりませんでした。

参考文献等ご存じの方がいれば教えていただければ幸いです。

## 付録：Dockerfile

squidとc-icapのDockerfileを置いておきます。
どちらもソースからインストールする方法で作成しております。
squidに関してはyumでインストールした方が100倍速いです。

### squid

```Dockerfile
FROM centos:7

# system update
RUN yum -y update && yum clean all

# set locale
RUN yum reinstall -y glibc-common && yum clean all
RUN unlink /etc/localtime && ln -s /usr/share/zoneinfo/Japan /etc/localtime
RUN yum -y install make gcc perl openssl-devel gcc-c++

RUN yum install -y wget && wget -P /opt http://www.squid-cache.org/Versions/v3/3.5/squid-3.5.27.tar.gz && wget -P /opt https://www.openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz
RUN tar zxvf /opt/squid* -C /opt && tar zxvf /opt/openssl* -C /opt
RUN cd /opt/openssl-1.0.2u && ./config --openssldir=/usr/local/ssl -fPIC shared && make && make install && make clean
RUN sed -i "1i\/usr\/local\/ssl\/lib" /etc/ld.so.conf ; ldconfig

RUN useradd squid
RUN cd /opt/squid-3.5.27 && ./configure --prefix=/usr/local/squid  --with-openssl=/usr/local/ssl --with-default-user=squid && make && make install && make clean
RUN chown -R root:squid /usr/local/squid && chmod -R 775 /usr/local/squid

#COPY ./etc/squid.conf /usr/local/squid/etc/
RUN mkdir -p /oss/squid/log && chown -R squid:squid /oss/squid

EXPOSE 3128

CMD ["/usr/local/squid/sbin/squid","-N"]
```


### c-icap

```Dockerfile
FROM centos:7

# system update
RUN yum -y update && yum clean all

RUN yum reinstall -y glibc-common && yum clean all
RUN unlink /etc/localtime && ln -s /usr/share/zoneinfo/Japan /etc/localtime
RUN yum -y install make gcc perl openssl-devel gcc-c++

RUN yum install -y wget && wget --content-disposition -P /opt https://sourceforge.net/projects/c-icap/files/latest/download
RUN tar zxvf /opt/c_icap-* -C /opt && cd /opt/c_icap-0.5.6/ && ./configure --prefix=/usr/local/c-icap && make && make install && make clean

EXPOSE 1344

CMD ["/usr/local/c-icap/bin/c-icap","-D","-N"]
```

## 参考文献
- [Squidとc-icapの連携](https://www.secioss.co.jp/squid%E3%81%A8c-icap%E3%81%AE%E9%80%A3%E6%90%BA/)
- [squid-cache.org](http://www.squid-cache.org/)
- [c-icap project](http://c-icap.sourceforge.net/)
- [Server world : Squid + SquidClamav](https://www.server-world.info/query?os=CentOS_7&p=squid&f=5)
- [This is the documentation for the Squid configuration file](http://www.squid-cache.org/Versions/v3/3.5/cfgman/)
- [Squid random outgoing IP address on each request](https://serverfault.com/questions/1001637/squid-random-outgoing-ip-address-on-each-request)