Docker buildを高速化するためにsquidで透過型プロキシを立ててみた

スポンサード リンク

先日,docker buildを高速化したくて Dockerfile内部でapt-cacher-ngを自動検出する | 電脳手帳 という記事を書いた。aptのキャッシュを使うためにプロキシを通したいが,プロキシがない環境でもそのままbuildできるDockerfileを書くのが目的だった。

ならば,透過型プロキシをsquidで立てるべきだということで立ててみた。 やってみると意外と簡単だったので,apt-cacher-ngは廃止してsquidを採用することにした。squidならhttp接続のすべてをキャッシュできる,つまりyumやwgetも高速化できるという利点がある。

1. squidコンテナの準備

まず,squidを動作させるために以下の内容のDockerfileを用意する。

FROM ubuntu:xenial
MAINTAINER M. Tsuyuki

RUN apt -q update \
&&  apt -qy --no-install-recommends install squid3 \
&&  apt clean \
&&  rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*

COPY squid.conf /etc/squid/squid.conf
CMD chown -R proxy:proxy /var/cache/squid \
&& squid -Nz \
&& squid \
&& sleep 10s \
&& tail -f /var/log/squid/access.log

次に,squidの設定ファイルをsquid.confとして作成する。 この設定ファイルではキャッシュのサイズ上限を50GBとした。

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 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 Safe_ports port 80          # http
acl Safe_ports port 21          # ftp
acl Safe_ports port 443         # https
acl Safe_ports port 70          # gopher
acl Safe_ports port 210         # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280         # http-mgmt
acl Safe_ports port 488         # gss-http
acl Safe_ports port 591         # filemaker
acl Safe_ports port 777         # multiling http
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localnet
http_access allow localhost
http_access deny all
http_port 3128
coredump_dir /var/spool/squid
refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
refresh_pattern (Release|Packages(.gz)*)$      0       20%     2880
refresh_pattern .               0       20%     4320
visible_hostname unkown
http_port 3129 intercept

httpd_suppress_version_string on
maximum_object_size 8192 MB
cache_dir ufs /var/cache/squid 50000 16 256
cache_mem 2000 MB

maximum_object_size_in_memory 10240 KB
cache_replacement_policy lru
cache_access_log /var/log/squid/access.log
cache_log /var/log/squid/cache.log
cache_store_log /var/log/squid/store.log
pid_filename /var/run/squid.pid

# refresh pattern for debs and udebs
refresh_pattern deb$   129600 100% 129600
refresh_pattern udeb$   129600 100% 129600
refresh_pattern tar.gz$  129600 100% 129600
refresh_pattern gem$  129600 100% 129600

# refresh pattern Packages and Release files
refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 300 100% 300 refresh-ims
refresh_pattern \/Release(|\.gpg)$ 300 100% 300 refresh-ims
refresh_pattern \/InRelease$ 300 100% 300 refresh-ims

# handle meta-release and changelogs.ubuntu.com special
# (fine to have this on debian too)
refresh_pattern changelogs.ubuntu.com/*  0  1% 1

forwarded_for off
request_header_access X-FORWARDED-FOR deny all
request_header_access Via deny all
request_header_access Cache-Control deny all

iptablesでsquidへnatを設定するので,dockerホストの再起動後も iptablesの設定が永続化されるようにパッケージをインストールする。

sudo apt update
sudo apt install -y iptables-persistent

後はbuildしてiptablesでnatを設定してからrunするだけ。

docker build --compress --pull -t squid .

sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to 3129 -w
sudo /etc/init.d/netfilter-persistent save

docker run -d \
    -v /etc/localtime:/etc/localtime:ro \
    -v /var/cache/squid:/var/cache/squid \
    --network host \
    --name=squid \
    --hostname=squid \
    squid

以下のようなMakefileを作ってsquidを簡単に有効化/無効化できるようにすると便利。

CONTAINER=squid
IMAGE=$(CONTAINER)

build:
    docker build --network --compress --pull -t $(IMAGE) .

shell:
    docker exec -it $(CONTAINER) /bin/bash

run: on
    docker run -d \
        -v /etc/localtime:/etc/localtime:ro \
        -v squid-cache:/var/cache/squid \
        --restart always \
        --log-driver json-file \
        --log-opt max-size=1g \
        --network host \
        --name=$(CONTAINER) \
        --hostname=$(CONTAINER) \
        $(IMAGE)

rm: off
    -docker rm -f $(CONTAINER)

on:
    sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to 3129 -w
    sudo /etc/init.d/netfilter-persistent save
off:
    -sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to 3129 -w
    sudo /etc/init.d/netfilter-persistent save

du:
    sudo docker exec -it $(CONTAINER) du -sh /var/cache/squid

intall:
    sudo apt update
    sudo apt install -y iptables-persistent

最後に

以上の作業でDockerコンテナが使うネットワークを含め, この物理ホストでnatされて外にでる80番ポートのアクセスは squidコンテナをproxyとして使うようになった。

なお,物理ホスト自体からのアクセスはnatされないので squidコンテナを経由しないし,そもそも経由してはいけない。 なぜならば,物理ホストのhttp_proxy環境変数でsquidコンテナを指定すると, squidコンテナ自体から発せられるhttp接続もsquidコンテナ自身を 経由するようになり,ループが発生するからだ。

Comments !

social