AlmaLinux 使用ATS搭建CDN

在我写这篇文章前,查阅了近百篇ATS教程,几乎没有一篇可直接用于生产环境。

从逐一尝试Varnish、Nginx、HAProxy缓存功能,到最终确定使用ATS,期间一次又一次采坑,耗费了大量时间。仅从零学习ATS官方文档及生产环境调试这一项,时间成本就在1~2周左右。

最终将这片文章写成教学,对于一个技术人员而言,能够直接将ATS的入门时间降低至几小时,没有做过多排版见谅。


一、程序部署

安装所需的程序库

AlmaLinux 8

sudo dnf -y upgrade
sudo dnf -y install epel-release
sudo dnf -y install gcc gcc-c++ wget bzip2 pkgconfig autoconf automake libtool iptables-services libmaxminddb-devel libwebp-devel ImageMagick-c++-devel tcl-devel expat-devel pcre-devel perl-ExtUtils-MakeMaker perl-CPAN libaio-devel libcap libcap-devel libunwind libunwind-devel hiredis hiredis-devel curl curl-devel ncurses  ncurses-devel luajit luajit-devel brotli-devel zlib-devel openssl-devel numactl hwloc http://mirror.centos.org/centos/8-stream/PowerTools/x86_64/os/Packages/hwloc-devel-2.2.0-3.el8.x86_64.rpm

AlmaLinux 9

sudo dnf -y upgrade
sudo dnf -y install epel-release
dnf -y install gcc gcc-c++ wget bzip2 pkgconfig autoconf automake libtool iptables-services libwebp-devel ImageMagick-c++-devel tcl-devel expat-devel pcre-devel perl-ExtUtils-MakeMaker perl-CPAN libaio-devel libcap libcap-devel libunwind libunwind-devel hiredis hiredis-devel curl curl-devel ncurses  ncurses-devel luajit luajit-devel brotli-devel zlib-devel openssl-devel numactl hwloc https://repo.almalinux.org/almalinux/9/AppStream/x86_64/os/Packages/libmaxminddb-1.5.2-3.el9.x86_64.rpm https://repo.almalinux.org/almalinux/9/AppStream/x86_64/os/Packages/hwloc-devel-2.4.1-5.el9.x86_64.rpm

下载源码包

mkdir -p /ats/src
cd /ats/src
wget https://www.openssl.org/source/openssl-1.1.1q.tar.gz
wget https://dlcdn.apache.org/trafficserver/trafficserver-9.1.3.tar.bz2
wget https://github.com/jemalloc/jemalloc/releases/download/3.6.0/jemalloc-3.6.0.tar.bz2

编译Jemalloc

tar -jxvf jemalloc-3.6.0.tar.bz2
cd jemalloc-3.6.0/
./configure
make
make install
ln -s /usr/local/lib/libjemalloc* /lib64/
cd ../
rm -rf jemalloc-3.6.0

编译openssl

tar -zxvf openssl-1.1.1q.tar.gz
cd openssl-1.1.1q/
./config shared enable-mdc2 enable-md2 --prefix=/ats/openssl
make
make install
cd ../
rm -rf openssl-1.1.1q

创建用户和组

/usr/sbin/groupadd www
/usr/sbin/useradd -g www www

编译安装ATS

tar -jxvf trafficserver-9.1.3.tar.bz2
cd trafficserver-9.1.3/
./configure --prefix=/ats --enable-experimental-plugins --with-user=www --with-group=www --with-pcre --with-zlib --with-brotli --with-hiredis --with-luajit --with-jemalloc --with-openssl=/ats/openssl
make
make install
cd ../

考虑到生产环境的服务器在线编译成本较高,程序库与编译参数附带了部分默认未开启的功能,避免使用时再次编译。

编译过程耗时较长,可在编译时进行参数配置的步骤,以节约部署时间。

加入服务

cat > /etc/systemd/system/ats.service <<EOF
[Unit]
Description=Apache Traffic Server
After=syslog.target network.target

[Service]
Type=forking
ExecStart=/ats/bin/trafficserver start
ExecReload=/ats/bin/traffic_ctl config reload
ExecStop=/ats/bin/trafficserver stop
Restart=on-abort

[Install]
WantedBy=multi-user.target
EOF

开启服务

systemctl start ats
systemctl enable ats

二、参数配置

在生产环境中,至少需要修改10个配置文件,才能保证ATS的基础运行,这会让初学者感到崩溃。而当使用优化过的配置文件模板时,实际只需要修改几处配置,即可快速在生产环境中部署。

配置文件教学部分,更多给出的是如何完成基础配置的思路,能够让初学者在最短时间从零到一掌握ATS的使用,之后再去查看官方文档,将大幅度降低学习与理解的成本,这也是写这篇教学文章的初衷。

为了方便理解,这里将多个配置文件进行了分类:

配置文件目录 /ats/etc/trafficserver/
基础配置 records.config
插件配置 plugin.config
header_rewrite.config
缓存配置 storage.config
volume.config
hosting.config
cache.config
域名配置 remap.config
parent.config
ssl_multicert.config

基础配置

records.config 是ATS的主配置文件,分享一个生产环境的模板

#
# *NOTE*: All options covered in this file should be documented in the docs:
#
#    https://docs.trafficserver.apache.org/records.config
#

# Thread configurations
CONFIG proxy.config.exec_thread.autoconfig INT 0
CONFIG proxy.config.exec_thread.autoconfig.scale FLOAT 1.0
CONFIG proxy.config.exec_thread.limit INT 16
CONFIG proxy.config.accept_threads INT 0
CONFIG proxy.config.task_threads INT 32
CONFIG proxy.config.cache.threads_per_disk INT 16
CONFIG proxy.config.exec_thread.listen INT 1
CONFIG proxy.config.exec_thread.affinity INT 0

# Specify server addresses and ports to bind for HTTP and HTTPS
CONFIG proxy.config.http.server_ports STRING 80 443:proto=http2;http:ssl

# Via / Server headers
CONFIG proxy.config.http.insert_request_via_str INT 1
CONFIG proxy.config.http.insert_response_via_str INT 2
CONFIG proxy.config.http.response_via_str STRING ATS
CONFIG proxy.config.http.response_server_str STRING Cache Server

# Parent proxy configuration, in addition to these settings also see parent.config
CONFIG proxy.config.http.parent_proxy.retry_time INT 300
CONFIG proxy.config.http.parent_proxy.connect_attempts_timeout INT 30
CONFIG proxy.config.http.forward.proxy_auth_to_parent INT 0
CONFIG proxy.config.http.uncacheable_requests_bypass_parent INT 1

# HTTP connection timeouts (secs)
CONFIG proxy.config.http.keep_alive_no_activity_timeout_in INT 60
CONFIG proxy.config.http.keep_alive_no_activity_timeout_out INT 60
CONFIG proxy.config.http.transaction_no_activity_timeout_in INT 30
CONFIG proxy.config.http.transaction_no_activity_timeout_out INT 30
CONFIG proxy.config.http.transaction_active_timeout_in INT 900
CONFIG proxy.config.http.transaction_active_timeout_out INT 0
CONFIG proxy.config.http.accept_no_activity_timeout INT 60
CONFIG proxy.config.net.default_inactivity_timeout INT 86400

# Origin server connect attempts
CONFIG proxy.config.http.connect_attempts_max_retries INT 3
CONFIG proxy.config.http.connect_attempts_max_retries_dead_server INT 1
CONFIG proxy.config.http.connect_attempts_rr_retries INT 3
CONFIG proxy.config.http.connect_attempts_timeout INT 30
CONFIG proxy.config.http.post_connect_attempts_timeout INT 1800
CONFIG proxy.config.http.down_server.cache_time INT 300
CONFIG proxy.config.http.background_fill_active_timeout INT 0
CONFIG proxy.config.http.background_fill_completed_threshold FLOAT 0.000000

# Negative response caching, for redirects and errors
CONFIG proxy.config.http.negative_caching_enabled INT 1
CONFIG proxy.config.http.negative_caching_lifetime INT 10
CONFIG proxy.config.http.negative_caching_list STRING 305 403 404 414 444 500 501 502 503 504

# Proxy users variables
CONFIG proxy.config.http.insert_client_ip INT 1
CONFIG proxy.config.http.insert_squid_x_forwarded_for INT 1

# Security
CONFIG proxy.config.http.push_method_enabled INT 0

# Enable / disable HTTP caching
CONFIG proxy.config.http.cache.http INT 1

# Cache control
CONFIG proxy.config.http.cache.ignore_client_cc_max_age INT 1
CONFIG proxy.config.http.normalize_ae INT 0
CONFIG proxy.config.http.cache.cache_responses_to_cookies INT 1
CONFIG proxy.config.http.cache.cache_urls_that_look_dynamic INT 1
CONFIG proxy.config.http.cache.when_to_revalidate INT 0
CONFIG proxy.config.http.cache.required_headers INT 2
CONFIG proxy.config.http.cache.ignore_client_no_cache INT 1

# Heuristic cache expiration
CONFIG proxy.config.http.cache.heuristic_min_lifetime INT 3600
CONFIG proxy.config.http.cache.heuristic_max_lifetime INT 259200
CONFIG proxy.config.http.cache.heuristic_lm_factor FLOAT 0.10

# Network
CONFIG proxy.config.net.connections_throttle INT 65535
CONFIG proxy.config.net.max_connections_in INT 65535
CONFIG proxy.config.net.max_requests_in INT 0

# RAM and disk cache configurations
CONFIG proxy.config.cache.ram_cache.size INT 1024M
CONFIG proxy.config.cache.ram_cache_cutoff INT 4194304
CONFIG proxy.config.cache.target_fragment_size INT 524288
CONFIG proxy.config.cache.ram_cache.compress INT 1
CONFIG proxy.config.cache.ram_cache.algorithm INT 0
CONFIG proxy.config.cache.ram_cache.use_seen_filter INT 0
CONFIG proxy.config.cache.limits.http.max_alts INT 5
CONFIG proxy.config.cache.max_doc_size INT 0
CONFIG proxy.config.cache.min_average_object_size INT 16384
CONFIG proxy.config.cache.enable_read_while_writer INT 2
CONFIG proxy.config.cache.read_while_writer.max_retries INT 10
CONFIG proxy.config.cache.read_while_writer_retry.delay INT 50
CONFIG proxy.config.http.cache.max_open_read_retries INT 5
CONFIG proxy.config.http.cache.open_read_retry_time INT 10
CONFIG proxy.config.http.cache.open_write_fail_action INT 5

# Logging Config
CONFIG proxy.config.log.logging_enabled INT 3
CONFIG proxy.config.log.max_space_mb_for_logs INT 15000
CONFIG proxy.config.log.max_space_mb_headroom INT 1000
CONFIG proxy.config.log.rolling_enabled INT 1
CONFIG proxy.config.log.rolling_interval_sec INT 86400
CONFIG proxy.config.log.rolling_size_mb INT 10
CONFIG proxy.config.log.auto_delete_rolled_files INT 1
CONFIG proxy.config.log.periodic_tasks_interval INT 6

# These settings control remapping, and if the proxy allows (open) forward proxy or not
CONFIG proxy.config.url_remap.remap_required INT 1
CONFIG proxy.config.url_remap.pristine_host_hdr INT 1
CONFIG proxy.config.reverse_proxy.enabled INT 1

# SSL configurations
CONFIG proxy.config.ssl.client.verify.server.policy STRING PERMISSIVE
CONFIG proxy.config.ssl.client.verify.server.properties STRING ALL
CONFIG proxy.config.ssl.client.CA.cert.filename STRING NULL
CONFIG proxy.config.ssl.server.cert.path STRING /ats/etc/ssl/
CONFIG proxy.config.ssl.server.private_key.path STRING /ats/etc/ssl/
CONFIG proxy.config.ssl.server.ticket_key.filename STRING ticket.key
CONFIG proxy.config.ssl.server.cipher_suite STRING 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
CONFIG proxy.config.ssl.server.TLSv1_3.cipher_suites STRING TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
CONFIG proxy.config.ssl.server.groups_list STRING X25519:P-256:P-384
CONFIG proxy.config.ssl.ocsp.enabled INT 1
CONFIG proxy.config.ssl.ocsp.response.path STRING /ats/etc/ssl/
CONFIG proxy.config.ssl.TLSv1 INT 0
CONFIG proxy.config.ssl.TLSv1_1 INT 0
CONFIG proxy.config.ssl.TLSv1_2 INT 1
CONFIG proxy.config.ssl.TLSv1_3 INT 1
CONFIG proxy.config.ssl.session_cache INT 2
CONFIG proxy.config.ssl.session_cache.size INT 102400
CONFIG proxy.config.ssl.session_cache.num_buckets INT 256
CONFIG proxy.config.ssl.server.session_ticket.enable INT 1
CONFIG proxy.config.ssl.server.session_ticket.number INT 1
CONFIG proxy.config.ssl.server.max_early_data INT 0
CONFIG proxy.config.ssl.server.allow_early_data_params INT 0

# Debugging
CONFIG proxy.config.diags.debug.enabled INT 0
CONFIG proxy.config.diags.debug.tags STRING http|dns

# ToDo Undocumented
CONFIG proxy.config.dump_mem_info_frequency INT 0
CONFIG proxy.config.http.slow.log.threshold INT 0

# Http UI Endpoints
CONFIG proxy.config.http_ui_enabled INT 1

需要根据生产环境的实际情况,调整模板中的参数

proxy.config.exec_thread.limit
每个CPU核心启动的线程数,一些知名云服务厂商提供的数据表明,设置为16性能最佳,如果服务器的CPU单核性能较弱,可以适当减少这个数值
proxy.config.task_threads

处理任务的总线程数,建议设置为CPU的核心数x每个CPU核心分配的线程数。

例如:一台双核服务器,当proxy.config.exec_thread.limit设置为16时,应将该参数设置为32
proxy.config.cache.threads_per_disk

用于处理硬盘I/O的线程数,由于已开启内存缓存会大幅度降低硬盘I/0,一般设置为单核心的线程数即16
proxy.config.exec_thread.affinity

该参数用于将线程与处理单元进行绑定,默认数值1是将线程分配给NUMA节点,其目的是防止ATS进程跨芯片以提高性能,当服务器并非多路物理机时,建议设置为0与机器绑定。

设置为1时,应使用numactl命令控制ATS使用的CPU和内存,例如:
numactl --cpunodebind=0,1 -m 0,1 trafficserver start
proxy.config.http.response_server_str

自定义header标头中server内容,安全考虑对访问者隐藏ATS信息
proxy.config.http.insert_request_via_str
proxy.config.http.insert_response_via_str

这两个参数用于配置header标头中的via信息,查看缓存的命中情况。

很多教程都是建议调试结束后关闭via信息,而实际观察一些知名的CDN标头信息,大多是没有隐藏的,所以建议调试结束后,通过重写header的方式隐藏ATS信息,这篇教学文章采用的就是这种方法,具体可以参考文章后面的重写header部分。

将proxy.config.http.insert_response_via_str设置为2时,刷新页面查看header中的via信头,一般会看到三个不同的值:

[cHs f ] 命中缓存,无需回源
[cMsSfW] 未命中缓存,请求回源,如内容在缓存规则中则请求内容写入缓存
[cSsSfU] 命中过期缓存,请求回源,并更新ATS服务器上的缓存

也可以通过traffic_via命令查看这些数值表示的含义,例如:
/ats/etc/bin/traffic_via '[cHs f ]'
proxy.config.http.normalize_ae

这是一个几乎会让所有用户踩坑的参数,而且还是两次。

[坑一]
proxy.config.http.normalize_ae_gzip 是该参数的前身,ATS9.0之后的版本已不再使用,在写这篇教学文章前,没找到任何一篇教学文章修改了此处,这将导致新参数以默认为1的数值运行,结果就是如果源服务器开启了br压缩,将造成ATS缓存服务器的br与gzip全部失效

[坑二]
官方文档与很多教程都建议开启该参数,此处严重误导用户,这个参数开启后的实际效果是,只会缓存gzip或br压缩的文件,其它文件将不再被缓存。正确的配置方式:应将该参数设置为0并在源服务器开启br与gzip压缩
proxy.config.cache.ram_cache.size

设置内存缓存大小,默认是硬盘缓存大小的1%

需要注意的是,部分系统服务与TCP连接也会消耗内存,所以设置内存缓存大小时,应计算这些情况后,再去使用更多的内存缓存
proxy.config.cache.ram_cache_cutoff

内存缓存的最大文件尺寸限制,默认4MB,可以根据实际情况进行调整,修改参数时应注意大小单位,例如1MB应使用数值1048576表示(1x1024x1024)
proxy.config.cache.max_doc_size

全部缓存的最大文件尺寸限制,默认不限制
proxy.config.log.max_space_mb_for_logs

日志文件大小限制,默认25000即25000MB,建议根据实际情况调整
proxy.config.ssl.server.cert.path
proxy.config.ssl.server.private_key.path
proxy.config.ssl.ocsp.response.path

这三个参数用于指向证书目录,可根据实际情况调整目录
proxy.config.ssl.server.max_early_data
proxy.config.ssl.server.allow_early_data_params

这两个参数用于开启0-RTT,考虑到重放攻击的安全问题,配置模板中没有开启

开启0-RTT参数示例:
CONFIG proxy.config.ssl.server.max_early_data INT 16384
CONFIG proxy.config.ssl.server.allow_early_data_params INT 1

其余参数使用优化模板中的默认配置即可,如需深度调试,请参考官方文档。


插件配置

plugin.config 是ATS的插件配置文件

建议开启压缩、ESI、重写header这些个比较常用的插件

compress.so
esi.so --private-response --first-byte-flush
header_rewrite.so /ats/etc/trafficserver/header_rewrite.config

需要注意 plugin.config 是全局开启插件功能,如果仅需要单域名开启某个插件,应在 remap.config 中配置,更多插件的调用请参考官方文档。


header_rewrite.config 是重写header插件的配置文件,需自行创建

#
# header_rewrite.config
#
# Documentation:
#    https://docs.trafficserver.apache.org/admin-guide/plugins/header_rewrite.en.html
#
cond %{READ_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:Strict-Transport-Security} ="" [AND]
cond %{INBOUND:TLS} /./
set-header Strict-Transport-Security "max-age=31536000; preload"

cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:via} ="http/1.1 traffic_server (ATS [cRs f ])"
set-header via "x-cache Hit"
cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:via} ="http/1.1 traffic_server (ATS [cMsSfW])"
set-header via "x-cache Miss"
cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:via} ="http/1.1 traffic_server (ATS [cSsSfU])"
set-header via "x-cache Miss"
cond %{SEND_RESPONSE_HDR_HOOK}
set-header via "x-cache Get"

以上rewrite规则中,加入了两个功能:

HSTS

cond %{READ_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:Strict-Transport-Security} ="" [AND]
cond %{INBOUND:TLS} /./
set-header Strict-Transport-Security "max-age=31536000; preload"

[坑]
在records.config对HSTS也是有配置选项的,但没有preload功能,所以应使用重写header的方式开启
Via信头重写

cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:via} ="http/1.1 traffic_server (ATS [cRs f ])"
set-header via "x-cache Hit"

cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:via} ="http/1.1 traffic_server (ATS [cMsSfW])"
set-header via "x-cache Miss"

cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:via} ="http/1.1 traffic_server (ATS [cSsSfU])"
set-header via "x-cache Miss"

cond %{SEND_RESPONSE_HDR_HOOK}
set-header via "x-cache Get"

主要是用于隐藏ATS信息,也方便查看缓存命中情况

[坑]
需要注意,最后一行加入了一个Get状态,原因是重写header的操作会发生在缓存处理之前,简单来说,假设我们访问一个没有被缓存的文件:

第一次访问为[cMsSfW]或[cSsSfU]的状态,通过重写标记Miss
第二次访问为[cRs f ]状态,通过重写标记为Hit

但实际会发现,第二次访问时,重写并未生效,依旧显示为[cRs f ],所以需要加入一个Get状态,来处理这种特殊情况,最终效果:

第一次访问为[cMsSfW]或[cSsSfU]的状态,通过重写标记Miss
第二次访问为[cRs f ]状态,重写发生在缓存处理前,通过重写标记为Get
第三次访问为[cRs f ]状态,通过重写标记Hit

更多的重写header用法请参考官方文档。


缓存配置

storage.config 是硬盘缓存的配置文件

一般情况下仅需调整缓存占用硬盘的空间即可,例如配置10GB硬盘缓存空间

var/trafficserver 10240M

其中 var/trafficserver 为相对地址,可更改为绝对地址

/ats/var/trafficserver 10240M

也可以使用整块硬盘作为缓存空间,具体方法可以参考官方文档


volume.config 用于创建不同大小的缓存卷

如果以内存缓存为主,这个配置文件可以留空不做任何修改;

如果更依赖硬盘缓存,需要减少single-lock单锁压力,或是限制每个域名的硬盘缓存空间使用,在只有一块硬盘的情况下,应配置该文件来增加卷。

例如配置单硬盘增加3个卷:

volume=1 scheme=http size=20%
volume=2 scheme=http size=40%
volume=3 scheme=http size=40%

分配卷时,建议每个卷的空间高于内存缓存的大小,多硬盘的配置请参考官方文档。


hosting.config 用于为每个域名分配缓存卷

如果 volume.config 配置留空,则该文件也留空即可。

如果 volume.config 创建了多个缓存卷,例如已创建3个缓存卷,则应修改配置文件

hostname=*  volume=1,2,3

这里使用通配符,让全部域名平均分布在全部缓存卷中,如果想为域名指定缓存卷,可修改为:

hostname=aaa.com volume=1
hostname=bbb.com volume=2,3
hostname=* volume=1,2,3

在配置文件中,有hostname与domain两种参数,建议使用hostname以更灵活的配置域名,如使用domain参数则所有子域名也会生效。

[坑]
即使为域名指向了存储卷,也一定要加入一条hostname=*参数且volume必须覆盖全部缓存卷,否则会引起部分缓存控制功能失效。

cache.config 是缓存规则的配置文件

分享一个通用的配置

url_regex=.* suffix=ashx action=never-cache
url_regex=.* suffix=asp action=never-cache
url_regex=.* suffix=aspx action=never-cache
url_regex=.* suffix=do action=never-cache
url_regex=.* suffix=jsp action=never-cache
url_regex=.* suffix=php action=never-cache
url_regex=.* suffix=shtml action=never-cache

url_regex=.* suffix=bmp ttl-in-cache=7d
url_regex=.* suffix=csv ttl-in-cache=7d
url_regex=.* suffix=doc ttl-in-cache=7d
url_regex=.* suffix=docx ttl-in-cache=7d
url_regex=.* suffix=eot ttl-in-cache=7d
url_regex=.* suffix=ico ttl-in-cache=7d
url_regex=.* suffix=less ttl-in-cache=7d
url_regex=.* suffix=m3u8 ttl-in-cache=7d
url_regex=.* suffix=odt ttl-in-cache=7d
url_regex=.* suffix=otf ttl-in-cache=7d
url_regex=.* suffix=pdf ttl-in-cache=7d
url_regex=.* suffix=ppt ttl-in-cache=7d
url_regex=.* suffix=pptx ttl-in-cache=7d
url_regex=.* suffix=rtf ttl-in-cache=7d
url_regex=.* suffix=svg ttl-in-cache=7d
url_regex=.* suffix=svgz ttl-in-cache=7d
url_regex=.* suffix=swf ttl-in-cache=7d
url_regex=.* suffix=ts ttl-in-cache=7d
url_regex=.* suffix=ttf ttl-in-cache=7d
url_regex=.* suffix=txt ttl-in-cache=7d
url_regex=.* suffix=woff ttl-in-cache=7d
url_regex=.* suffix=woff2 ttl-in-cache=7d
url_regex=.* suffix=xls ttl-in-cache=7d
url_regex=.* suffix=xlsx ttl-in-cache=7d
url_regex=.* suffix=xml ttl-in-cache=7d

url_regex=.* suffix=gif ttl-in-cache=30d
url_regex=.* suffix=jpg ttl-in-cache=30d
url_regex=.* suffix=jpeg ttl-in-cache=30d
url_regex=.* suffix=png ttl-in-cache=30d
url_regex=.* suffix=webp ttl-in-cache=30d

url_regex=.* suffix=css revalidate=1d
url_regex=.* suffix=js revalidate=1d
url_regex=.* suffix=htm revalidate=1d
url_regex=.* suffix=html revalidate=1d

url_regex=.* scheme=http revalidate=1h
url_regex=.* scheme=https revalidate=1h

其中的缓存时间应根据实际情况修改,示例中排除掉了一些大型静态文件,会根据scheme=http/https的规则来缓存。

下面给出一些常见的大型静态文件后缀,方便补充缓存规则:

7z|avi|bz2|flac|flv|gz|mka|mkv|mov|mp3|mp4|mpeg|mpg|ogg|ogm|opus|rar|tar|tgz|tbz|txz|wav|webm|xz|zip

ATS的缓存规则功能非常强大,示例中仅提供了基础的规则,更多高级规则使用请参考官方文档。


域名配置

remap.config 是域名映射配置文件

需要注意的是,即使只有一台回源服务器,也推荐使用均衡负载的回源示例:

map https://aaa.com http://aaa.com
redirect http://aaa.com/ https://aaa.com/

map https://bbb.com https://bbb.com
redirect http://bbb.com/ https://bbb.com/

.definefilter origin_only @action=allow @src_ip=127.0.0.1 @src_ip=192.168.0.1-192.168.0.254
.activatefilter origin_only
regex_map https://.*.([0-9]+) http://{cache}
regex_map http://.*.([0-9]+) http://{cache}

这种写法可能会让新手迷茫,解释一下含义:

map https://aaa.com http://aaa.com

第一个链接是ATS监听的域名与协议,第二个链接是该域名的回源地址与协议,其中回源地址是不会使用DNS解析的,而是根据parent.config的配置指向一个或多个IP地址。

[坑]
网络上几乎所有的教程,都是将第二个链接指向IP,又或是自定义一个虚拟的回源地址。实际上这种方法是有缺陷的,会引起部分缓存控制功能失效,所以回源地址应使用与域名相同的地址,回源协议优先推荐使用http,如果非内网或有劫持再使用https回源,源站建议开启0-RTT减少握手次数。
redirect http://aaa.com/ https://aaa.com/

用于将http重定向至https
.definefilter origin_only @action=allow @src_ip=127.0.0.1 @src_ip=192.168.0.1-192.168.0.254
.activatefilter origin_only
regex_map https://.*.([0-9]+) http://{cache}
regex_map http://.*.([0-9]+) http://{cache}

用于开启UI管理页面,这个界面可以通过正则管理缓存,建议开启。需要限制哪些IP可以访问,可以是单个IP,也可以是IP段,通过一个或多个@src_ip参数定义即可。

必须将这段代码放置最底部,这里使用了.activatefilter指令来实现后面所有规则的访问IP限制,这种写法的好处,当有未绑定的域名指向ATS服务器时也会一起被禁止访问,兼顾了安全性。

[坑]
官方文档示例的写法,是用 map / http://{cache} 指令来映射UI管理页面,这种方式会造成重定向功能失效,原因是map的优先级高于redirect,所以应使用regex_map通过正则限制仅通过IP访问UI管理页面。

更多用法请参考官方文档。


parent.config 用于配置域名的回源地址及均衡负载

常用的配置示例:

dest_host=aaa.com parent="192.168.1.100:80" parent_is_proxy=false round_robin=strict
dest_host=bbb.com parent="192.168.1.101:443|1.0, 192.168.1.102:443|2.0" parent_is_proxy=false round_robin=strict

需要配置的选项

dest_host=aaa.com
配置文件中的示例使用的是dest_domain参数,建议使用dest_host以更灵活的配置域名,如使用dest_domain参数则所有子域名也会指向配置的IP地址。

parent="192.168.1.100:80"
回源的IP地址,必须带有端口号,端口号由 remap.config 中配置的回源协议决定,默认情况下http回源使用80、https回源使用443,如果回源服务器只有一台或只有一个IP地址,参考该示例中的配置即可。

parent="192.168.1.101:443|1.0, 192.168.1.102:443|2.0"
如果有多个回源IP,应配置均衡负载,ATS的均衡负载附带故障转移功能,所以健康检测非必须配置,示例中使用轮训算法回源,建议在回源IP后面加入比例参数,实现更灵活的回源控制。

更多复杂的回源规则配置,请参考官方文档。


ssl_multicert.config 是SSL证书的配置文件

需要先将域名的ssl证书与秘钥放置在records.config配置指向的证书目录中

如果该目录不存在,应使用mkdir命令创建,例如:

mkdir -p /ats/etc/ssl

然后对ssl_multicert.config进行如下配置:

dest_ip=* ssl_hostname=aaa.com ssl_cert_name=aaa.cer ssl_key_name=aaa.key
dest_ip=* ssl_hostname=bbb.com ssl_cert_name=bbb.cer ssl_key_name=bbb.key ssl_ocsp_name=bbb.cer

推荐使用acme.sh脚本管理证书的自动更新:

#下载与安装脚本
curl https://get.acme.sh | sh
alias acme.sh=~/.acme.sh/acme.sh

#添加DNS的API秘钥
#DPI_Id、DPI_Key是DNSPod国际版的参数示例
#其它DNS厂商的参数查阅https://github.com/acmesh-official/acme.sh/wiki/dnsapi
export DPI_Id="your id"
export DPI_Key="your key"
export ACCOUNT_EMAIL='your_email'

#申请证书
#这里以DNSPod国际版申请aaa.com及其泛域名证书示例
acme.sh --issue --dns dns_dpi -d aaa.com -d *.aaa.com

#安装证书
#这里使用 traffic_ctl config reload 命令实现SSL证书的热更新
acme.sh --installcert -d aaa.com -d *.aaa.com \
--key-file /ats/etc/ssl/aaa.com.key \
--fullchain-file /ats/etc/ssl/aaa.com.cer \
--reloadcmd "/ats/bin/traffic_ctl config reload"

#配置自动更新脚本
acme.sh --upgrade --auto-upgrade

如需配置 OCSP Stapling 应注意:

判断证书的OCSP服务会不会返回Certificate

如不会返回,那么只需要配置ssl_cert_name与ssl_key_name即可。

一般情况下,ssl_ocsp_name与ssl_cert_name指向同一文件即可。

如果不清楚自己的证书是否需要配置ssl_ocsp_name,这里提供一种比较简单的方法,先配置ssl_cert_name与ssl_key_name,启动ATS后使用openssl指令测试:

openssl s_client -connect 0.0.0.0:443 -servername aaa.com -status -tlsextdebug < /dev/null 2>&1 | grep -i "OCSP response"

如返回
OCSP response: 
OCSP Response Data:
OCSP Response Status: successful (0x0)
Response Type: Basic OCSP Response
说明OCSP开启成功

如返回
OCSP response: no response sent
应配置ssl_ocsp_name,重启ATS后再次使用相同指令测试。
[坑]
ssl_hostname是一个隐藏参数,官方文档并未提及,官方文档的示例是使用dest_ip参数绑定回源IP地址,但这会造成一个问题,当源服务器上有多个域名时将无法处理,所以建议使用dest_ip=* ssl_hostname=aaa.com的写法。

这样配置适用于绝大多数情况,如果想了解更多SSL的配置,请参考官方文档。

当全部配置文件调试结束后:

首次应执行 systemctl restart ats 重启ATS服务;
之后修改个别参数,执行 systemctl reload ats 重新加载即可。


三、会话复用

运行ATS前应使用openssl指令创建一个会话票证,实现会话复用:

head -c48 /dev/urandom | openssl enc -base64 | head -c48 > /ats/etc/ssl/ticket.key

其中ticket.key的命名与位置,是在record.config中配置的:

CONFIG proxy.config.ssl.server.cert.path STRING /ats/etc/ssl/
CONFIG proxy.config.ssl.server.ticket_key.filename STRING ticket.key

当需要部署多台ATS服务器时,应考虑Session Ticket轮换后的同步问题。

为什么没有提及Seesion ID集群?

因为这是tls1.2时期产生的问题,随着tls1.3普及,大多数web服务使用tls1.2访问的占比几乎不超过1%,并且会越来越低,因此没必要再去部署Seesion ID集群。

Session Ticket的轮换与同步

考虑到安全问题,Session Ticket是需要定期轮换的,当多台服务器上的Ticket不同步时,同一用户访问至不同的ATS服务器时,将无法实现会话复用以减少TLS的握手次数。

ATS有提供会话复用集群插件,但不推荐使用插件实现。因为当无需考虑Seesion ID同步的问题后,只需要解决Session Ticket轮换后的同步问题即可。

为什么ATS可以原生实现会话复用?

以Nginx为例,tls1.3依旧需要使用lua+memcached集群的根本原因,是当更新了Ticket后,Nginx是无法通过reload方式来热更新Ticket的,所以需要使用LUA来解决这一问题。

而ATS却可以通过traffic_ctl config reload方式热更新Ticket,从而简化相关的部署。

这里给出一种相对简单的轮换与同步思路:

在一台源服务器上,创建一个用于生成ticket的脚本

vi /etc/newticket

head -c48 /dev/urandom | openssl enc -base64 | head -c48 > /data/htdocs/wwwroot/ticket.key

应保证 /data/htdocs/wwwroot 目录能够被ATS服务器通过http或https访问到,建议设置为仅允许ATS服务器的IP访问。

使用 crontab -e 命令创建定时任务,每天生成一个新的ticket.key

0 0 * * * /etc/newticket

之后在每台ATS服务器上使用 crontab -e 命令创建定时任务,每天在访问人数较少的时间更新ticket.key

0 2 * * * curl https://ticketdomain.com/ticket.key > /tmp/temp_ticket.key && cat /tmp/temp_ticket.key > /ats/etc/ssl/ticket.key && /ats/bin/traffic_ctl config reload

SSL证书也可以采用相同的思路实现同步更新。


四、缓存控制

ATS可以通过执行traffic_server -Cclear命令清空全部缓存,这种方式需要停止ATS服务后才能使用

systemctl stop ats && /ats/bin/traffic_server -Cclear && systemctl restart ats

ATS官方文档中提供的一种基础的缓存控制方式

curl -vX PURGE --resolve aaa.com:443:127.0.0.1 https://aaa.com/demo.css

上述是删除aaa.com域名根目录下demo.css文件缓存的示例,缺点很明显,无法实现批量删除,如何实现批量删除缓存,ATS官方文档并未提供说明。

这里可以利用ATS自带的Http UI Endpoints功能实现,在remap.config配置成功后,即可实现通过正则批量删除缓存,同时也支持删除单个文件的缓存。

curl http://1.2.3.4/delete_url?url=http://aaa.com/demo.css
curl http://1.2.3.4/delete_regex?url=http://aaa.com/.*.css

示例中,1.2.3.4为ATS服务器的IP地址,http://aaa.com为回源地址并非访问地址,需注意回源协议是http还是https,示例第一行表示删除aaa.com域名根目录下demo.css文件的缓存,第二行表示删除aaa.com域名根目录下全部.css文件的缓存。

 

ATS运行过程中,可以通过工具查看缓存状态:

traffic_logstats -o aaa.com

/ats/bin/traffic_top


五、系统优化

执行命令 vi /etc/security/limits.conf 在最下方添加

*   soft     nofile      65535
*   hard     nofile      65535

 

修改ulimit连接数最大值

echo -e '\n\nulimit -SHn 65535\n' >> /etc/profile && source /etc/profile

 

执行命令 vi /etc/sysconfig/selinux 修改选项关闭SELINUX

SELINUX=disabled

保存后执行命令 /usr/sbin/sestatus -v 查看SELINUX状态

 

执行命令 vi /etc/sysctl.conf 优化内核参数

fs.file-max = 65535
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_keepalive_time = 65
net.ipv4.tcp_fin_timeout = 1
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.ip_local_port_range = 1024 65500
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 262144
net.core.netdev_max_backlog = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.ipv4.tcp_timestamps = 0
#net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_fastopen = 2

保存后执行 sysctl -p 使配置立即生效


六、攻击防御

配置iptables防火墙

如果安装了FirewallD,需要关闭FirewallD服务

systemctl stop firewalld && systemctl disable firewalld

执行命令 vi /etc/sysconfig/iptables 配置防火墙

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

开启iptables防火墙服务

systemctl enable iptables
systemctl restart iptables

 

基础防御DDOS与CC攻击,推荐使用ddos-deflate脚本

wget https://github.com/jgmdev/ddos-deflate/archive/master.zip -O ddos-deflate.zip
unzip ddos-deflate.zip
cd ddos-deflate-master
./install.sh
cd ../
rm -rf ddos-deflate-master

执行 vi /etc/ddos/ddos.conf 修改以下参数后保存

#修改为iptables
FIREWALL="iptables"
#修改为自己的邮箱
EMAIL_TO="[email protected]"

重启 DDoS Deflate 服务

systemctl restart ddos

此时防御已经生效,会每5秒钟检测一次连接数量,默认当某个IP超过150个连接后会被禁止访问,可以通过执行netstat命令查看当前每个IP的连接数量

netstat -ntu | awk '{print $5}' | cut -d: -f1 | uniq -c | sort -n

建议执行 ddos -h 查看帮助,方便以后运维使用。

ddos-deflate脚本没有IP黑名单功能,可以使用ATS的IP限制功能补充

需要配置ip_allow.yaml文件,文件默认采用完整格式写法,建议修改为精简格式写法:

ip_allow: [
  { apply: in, ip_addrs: 127.0.0.1, action: allow },
  { apply: in, ip_addrs: "::1", action: allow },
  { apply: in, ip_addrs: 0/0, action: deny, methods: [ PURGE, PUSH, DELETE ] },
  { apply: in, ip_addrs: "::/0", action: deny, methods: [ PURGE, PUSH, DELETE ] }
  ]

当需要限制某个IP时,应在allow规则的下一行加入deny规则,请勿将deny规则加在最后一行,这样不会生效,示例如下:

ip_allow: [
  { apply: in, ip_addrs: 127.0.0.1, action: allow },
  { apply: in, ip_addrs: "::1", action: allow },
  { apply: in, ip_addrs: 1.2.3.1, action: deny },
  { apply: in, ip_addrs: 1.2.2.0/24, action: deny },
  { apply: in, ip_addrs: 0/0, action: deny, methods: [ PURGE, PUSH, DELETE ] },
  { apply: in, ip_addrs: "::/0", action: deny, methods: [ PURGE, PUSH, DELETE ] }
  ]
{ apply: in, ip_addrs: 1.2.3.1, action: deny }, 
禁止IP 1.2.3.1 访问ATS

{ apply: in, ip_addrs: 1.2.2.0/24, action: deny },
禁止IP 1.2.2.x 整个网段访问ATS

之后执行 systemctl reload ats 使规则生效。

至此,ATS的配置全部完成。