分类目录归档:linux

Nginx的location匹配规则解析

情况1:

proxy_pass  http://IPaddr/    #删除匹配段,一般配置接口使用

访问 http://域名/abc/test.map 分下面两种情况
1)location /abc/
会转发到 http://IPaddr/test.map
2) location /abc
会转发成 http://IPaddr//test.map —————-linux //路径=/路径
所以也等价于 http://IPaddr/test.map 的访问

情况2:

proxy_pass  http://IPaddr    #匹配段和此地址段拼接

这个也分两种情况
1) location /abc
这种可以匹配 /abc 和 /abc/
2)location /abc/
这种只能匹配 /abc/ 匹配目录

容器中的JVM资源该如何被安全的限制

前言

Java与Docker的结合,虽然更好的解决了application的封装问题。但也存在着不兼容,比如Java并不能自动的发现Docker设置的内存限制,CPU限制。

这将导致JVM不能稳定服务业务!容器会杀死你JVM进程,而健康检查又将拉起你的JVM进程,进而导致你监控你的pod一天重启次数甚至能达到几百次。

我们希望当Java进程运行在容器中时,java能够自动识别到容器限制,获取到正确的内存和CPU信息,而不用每次都需要在kubernetes的yaml描述文件中显示的配置完容器,还需要配置JVM参数。

使用JVM MaxRAM参数或者解锁实验特性的JVM参数,升级JDK到10+,我们可以解决这个问题(也许吧~.~)。

首先Docker容器本质是是宿主机上的一个进程,它与宿主机共享一个/proc目录,也就是说我们在容器内看到的/proc/meminfo/proc/cpuinfo
与直接在宿主机上看到的一致,如下。

  • Hostcat /proc/meminfo MemTotal: 197869260 kB MemFree: 3698100 kB MemAvailable: 62230260 kB
  • 容器docker run -it --rm alpine cat /proc/meminfo MemTotal: 197869260 kB MemFree: 3677800 kB MemAvailable: 62210088 kB

那么Java是如何获取到Host的内存信息的呢?没错就是通过/proc/meminfo来获取到的。

默认情况下,JVM的Max Heap Size是系统内存的1/4,假如我们系统是8G,那么JVM将的默认Heap≈2G。

Docker通过CGroups完成的是对内存的限制,而/proc目录是已只读形式挂载到容器中的,由于默认情况下Java
压根就看不见CGroups的限制的内存大小,而默认使用/proc/meminfo中的信息作为内存信息进行启动,
这种不兼容情况会导致,如果容器分配的内存小于JVM的内存,JVM进程会被理解杀死。

内存限制不兼容

我们首先来看一组测试,这里我们采用一台内存为188G的物理机。

#free -g
              total        used        free      shared  buff/cache   available
Mem:            188         122           1           0          64          64

以下的测试中,我们将包含openjdk的hotspot虚拟机,IBM的openj9虚拟机。

以下测试中,我们把正确识别到限制的jdk,称之为安全(即不会超出容器限制不会被kill),反之称之为危险。

测试用例1(OPENJDK)

这一组测试我们使用最新的openjdk8-12,给容器限制内存为4G,看JDK默认参数下的最大堆为多少?看看我们默认参数下多少版本的JDK是安全的

  • 命令如下,如果你也想试试看,可以用一下命令。
   docker run -m 4GB  --rm  openjdk:8-jre-slim java  -XshowSettings:vm  -version
   docker run -m 4GB --rm  openjdk:9-jre-slim java  -XshowSettings:vm  -version
   docker run -m 4GB --rm  openjdk:10-jre-slim java -XshowSettings:vm  -version
   docker run -m 4GB --rm  openjdk:11-jre-slim java -XshowSettings:vm  -version
   docker run -m 4GB --rm  openjdk:12 java -XshowSettings:vm  -version
  • OpenJDK8(并没有识别容器限制,26.67G) 危险
[root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:8-jre-slim java  -XshowSettings:vm  -version

VM settings:
    Max. Heap Size (Estimated): 26.67G
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-2~deb9u1-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
  • OpenJDK8 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap (正确的识别容器限制,910.50M)安全
[root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:8-jre-slim java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm  -version
VM settings:
    Max. Heap Size (Estimated): 910.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-2~deb9u1-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
  • OpenJDK 9(并没有识别容器限制,26.67G)危险
[root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:9-jre-slim java  -XshowSettings:vm  -version
VM settings:
    Max. Heap Size (Estimated): 29.97G
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "9.0.4"
OpenJDK Runtime Environment (build 9.0.4+12-Debian-4)
OpenJDK 64-Bit Server VM (build 9.0.4+12-Debian-4, mixed mode)
  • OpenJDK 9 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap (正确的识别容器限制,1G)安全
[root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:9-jre-slim java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm  -version
VM settings:
    Max. Heap Size (Estimated): 1.00G
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "9.0.4"
OpenJDK Runtime Environment (build 9.0.4+12-Debian-4)
OpenJDK 64-Bit Server VM (build 9.0.4+12-Debian-4, mixed mode)
  • OpenJDK 10(正确的识别容器限制,1G)安全
[root@xiaoke-test ~]# docker run -m 32GB --rm  openjdk:10-jre-slim java -XshowSettings:vm -XX:MaxRAMFraction=1  -version
VM settings:
    Max. Heap Size (Estimated): 1.00G
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "10.0.2" 2018-07-17
OpenJDK Runtime Environment (build 10.0.2+13-Debian-2)
OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-2, mixed mode)
  • OpenJDK 11(正确的识别容器限制,1G)安全
[root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:11-jre-slim java -XshowSettings:vm  -version
VM settings:
    Max. Heap Size (Estimated): 1.00G
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment (build 11.0.1+13-Debian-3)
OpenJDK 64-Bit Server VM (build 11.0.1+13-Debian-3, mixed mode, sharing)
  • OpenJDK 12(正确的识别容器限制,1G)安全
[root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:12 java -XshowSettings:vm  -version
VM settings:
    Max. Heap Size (Estimated): 1.00G
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "12-ea" 2019-03-19
OpenJDK Runtime Environment (build 12-ea+23)
OpenJDK 64-Bit Server VM (build 12-ea+23, mixed mode, sharing)

测试用例2(IBMOPENJ9)

docker run -m 4GB --rm  adoptopenjdk/openjdk8-openj9:alpine-slim  java -XshowSettings:vm  -version
docker run -m 4GB --rm  adoptopenjdk/openjdk9-openj9:alpine-slim  java -XshowSettings:vm  -version
docker run -m 4GB --rm  adoptopenjdk/openjdk10-openj9:alpine-slim  java -XshowSettings:vm  -version
docker run -m 4GB --rm  adoptopenjdk/openjdk11-openj9:alpine-slim  java -XshowSettings:vm  -version
  • openjdk8-openj9 (正确的识别容器限制,3G)安全
    [root@xiaoke-test ~]# docker run -m 4GB --rm  adoptopenjdk/openjdk8-openj9:alpine-slim  java -XshowSettings:vm  -version
    VM settings:
        Max. Heap Size (Estimated): 3.00G
        Ergonomics Machine Class: server
        Using VM: Eclipse OpenJ9 VM
    
    openjdk version "1.8.0_192"
    OpenJDK Runtime Environment (build 1.8.0_192-b12_openj9)
    Eclipse OpenJ9 VM (build openj9-0.11.0, JRE 1.8.0 Linux amd64-64-Bit Compressed References 20181107_95 (JIT enabled, AOT enabled)
    OpenJ9   - 090ff9dcd
    OMR      - ea548a66
    JCL      - b5a3affe73 based on jdk8u192-b12)
  • openjdk9-openj9 (正确的识别容器限制,3G)安全
    [root@xiaoke-test ~]# docker run -m 4GB --rm  adoptopenjdk/openjdk9-openj9:alpine-slim  java -XshowSettings:vm  -version
    VM settings:
        Max. Heap Size (Estimated): 3.00G
        Using VM: Eclipse OpenJ9 VM
    
    openjdk version "9.0.4-adoptopenjdk"
    OpenJDK Runtime Environment (build 9.0.4-adoptopenjdk+12)
    Eclipse OpenJ9 VM (build openj9-0.9.0, JRE 9 Linux amd64-64-Bit Compressed References 20180814_248 (JIT enabled, AOT enabled)
    OpenJ9   - 24e53631
    OMR      - fad6bf6e
    JCL      - feec4d2ae based on jdk-9.0.4+12)
  • openjdk10-openj9 (正确的识别容器限制,3G)安全
    [root@xiaoke-test ~]# docker run -m 4GB --rm  adoptopenjdk/openjdk10-openj9:alpine-slim  java -XshowSettings:vm  -version
    VM settings:
        Max. Heap Size (Estimated): 3.00G
        Using VM: Eclipse OpenJ9 VM
    
    openjdk version "10.0.2-adoptopenjdk" 2018-07-17
    OpenJDK Runtime Environment (build 10.0.2-adoptopenjdk+13)
    Eclipse OpenJ9 VM (build openj9-0.9.0, JRE 10 Linux amd64-64-Bit Compressed References 20180813_102 (JIT enabled, AOT enabled)
    OpenJ9   - 24e53631
    OMR      - fad6bf6e
    JCL      - 7db90eda56 based on jdk-10.0.2+13)
  • openjdk11-openj9(正确的识别容器限制,3G)安全
    [root@xiaoke-test ~]# docker run -m 4GB --rm  adoptopenjdk/openjdk11-openj9:alpine-slim  java -XshowSettings:vm  -version
    VM settings:
        Max. Heap Size (Estimated): 3.00G
        Using VM: Eclipse OpenJ9 VM
    
    openjdk version "11.0.1" 2018-10-16
    OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.1+13)
    Eclipse OpenJ9 VM AdoptOpenJDK (build openj9-0.11.0, JRE 11 Linux amd64-64-Bit Compressed References 20181020_70 (JIT enabled, AOT enabled)
    OpenJ9   - 090ff9dc
    OMR      - ea548a66
    JCL      - f62696f378 based on jdk-11.0.1+13)

分析

分析之前我们先了解这么一个情况:

JavaMemory (MaxRAM) = 元数据+线程+代码缓存+OffHeap+Heap...

一般我们都只配置Heap即使用-Xmx来指定JVM可使用的最大堆。而JVM默认会使用它获取到的最大内存的1/4作为堆的原因也是如此。

安全性(即不会超过容器限制被容器kill)

OpenJdk

OpenJdk8-12,都能保证这个安全性的特点(8和9需要特殊参数,-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap)。

OpenJ9

IbmOpenJ9所有的版本都能识别到容器限制。

资源利用率

OpenJdk

自动识别到容器限制后,OpenJdk把最大堆设置为了大概容器内存的1/4,对内存的浪费不可谓不大。

当然可以配合另一个JVM参数来配置最大堆。-XX:MaxRAMFraction=int。下面是我整理的一个常见内存设置的表格,
从中我们可以看到似乎JVM默认的最大堆的取值为MaxRAMFraction=4,随着内存的增加,堆的闲置空间越来越大,在16G容器内存时,java堆只有不到4G。

MaxRAMFraction取值堆占比容器内存=1G容器内存=2G容器内存=4G容器内存=8G容器内存=16G
1≈90%910.50M1.78G3.56G7.11G14.22G
2≈50%455.50M910.50M1.78G3.56G7.11G
3≈33%304.00M608.00M1.19G2.37G4.74G
4≈25%228.00M455.50M910.50M1.78G3.56G

OpenJ9

关于OpenJ9的的详细介绍你可以从这里了解更多
对于内存利用率OpenJ9的策略是优于OpenJdk的。以下是OpenJ9的策略表格

容器内存<size>最大Java堆大小
小于1 GB50%<size>
1 GB – 2 GB<size>– 512 MB
大于2 GB大于2 GB

结论

  • 注意:这里我们说的是容器内存限制,和物理机内存不同,

自动档

  • 如果你想要的是,不显示的指定-Xmx,让Java进程自动的发现容器限制。

1.如果你想要的是jvm进程在容器中安全稳定的运行,不被容器kiil,并且你的JDK版本小于10(大于等于JDK10的版本不需要设置,参考前面的测试)
你需要额外设置JVM参数-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap,即可保证你的Java进程不会因为内存问题被容器Kill。
当然这个方式使用起来简单,可靠,缺点也很明显,资源利用率过低(参考前面的表格MaxRAMFraction=4)。

2.如果想在基础上我还想提高一些内存资源利用率,并且容器内存为1 GB – 4 GB,我建议你设置-XX:MaxRAMFraction=2,在大于8G的可以尝试设置-XX:MaxRAMFraction=1(参考上表格)。

手动挡

  • 如果你想要的是手动挡的体验,更加进一步的利用内存资源,那么你可能需要回到手动配置时代-Xmx。
  • 手动挡部分,请可以完全忽略上面我的BB。
    1.上面的我们说到了自动挡的配置,用起来很简单很舒服,自动发现容器限制,无需担心和思考去配置-Xmx。
    2.比如你有内存1G那么我建议你的-Xmx750M,2G建议配置-Xmx1700M,4G建议配置-Xmx3500-3700M,8G建议设置-Xmx7500-7600M,
    总之就是至少保留300M以上的内存留给JVM的其他内存。如果堆特别大,可以预留到1G甚至2G。
    3.手动挡用起来就没有那么舒服了,当然资源利用率相对而言就更高了。

a tiny but valid `init` for containers

tini

Using Tini has several benefits:

  • It protects you from software that accidentally creates zombie processes, which can (over time!) starve your entire system for PIDs (and make it unusable).
  • It ensures that the default signal handlers work for the software you run in your Docker image. For example, with Tini, SIGTERM properly terminates your process even if you didn’t explicitly install a signal handler for it.
  • It does so completely transparently! Docker images that work without Tini will work with Tini without any changes.

In Docker, you will want to use an entrypoint so you don’t have to remember to manually invoke Tini:

# Add Tini
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

# Run your program under Tini
CMD ["/your/program", "-and", "-its", "arguments"]
# or docker run your-image /your/program ...

find 之 prune

在查找某个目录的时候,如果要排除其中某个子目录可以用-path pathname -prune

例:查找根目录下24小时内修改过的文件,但是不要查询proc目录可用如下命令

find / -path /proc -prune -mtime -1 -type f
#排除多个目录
find / \( -path /proc -o -path /run \) -prune -mtime -1 -type f

例2:系统被病毒攻击,会产生很多可执行文件,如果发现被攻击时间是2天前

#全局查找命令如下
find / -mtime -3 -perm /111 -type -f 
#查找并显示详细信息
find / -mtime -3 -perm /111 -type -f -exec ls -la {} \;
#查找并删除文件
find / -mtime -3 -perm /111 -type -f -exec rm -f {} \;
#查找并删除文件 xargs
find / -mtime -3 -perm /111 -type -f  | xargs rm -f 

linux ssh 暴力破解 拦截方案

一 、背景介绍

正常机器如果开启ssh可能会被暴力破解用户密码,可以配置ssh的pam限制登录失败时可以锁定用户一段时间,就算密码输入正确也无法登录,等恢复之后才可以再次进入。

二、具体配置

具体的pam配置在/etc/pam.d下,对ssh服务的位置是:/etc/pam.d/sshd,打开第一行就可以发现引入了password-auth,然后可以直接编辑/etc/pam.d/password-auth,在最上面添加一行:
auth required pam_tally2.so deny=6 unlock_time=300 even_deny_root root_unlock_time=300

even_deny_root表示root用户也会限制

保存后,重启sshd服务即可生效 systemctl restart sshd

查看登录失败统计和清除次数同样使用下面的命令:

pam_tally2 --user root
# 或者简写-u
pam_tally2 -u root
# 清除次数
pam_tally2 --user root --reset

三、类似方案介绍

1、另外ssh还有个通用的配置就是连续尝试几次密码登录失败的话,可以直接断开数据包,这样也可以防止连接一次后不断尝试,编辑配置文件:/etc/ssh/sshd_config,修改下面配置:

MaxAuthTries 3

2、更高级的功能可以引入第三方软件——fail2ban,它可以更精确的拦截来源IP

[转]详解Tomcat 配置文件server.xml

前言

Tomcat隶属于Apache基金会,是开源的轻量级Web应用服务器,使用非常广泛。server.xml是Tomcat中最重要的配置文件,server.xml的每一个元素都对应了Tomcat中的一个组件;通过对xml文件中元素的配置,可以实现对Tomcat中各个组件的控制。因此,学习server.xml文件的配置,对于了解和使用Tomcat至关重要。

本文将通过实例,介绍server.xml中各个组件的配置,并详细说明Tomcat各个核心组件的作用以及各个组件之间的相互关系。

说明:由于server.xml文件中元素与Tomcat中组件的对应关系,后文中为了描述方便,“元素”和“组件”的使用不严格区分。

如果觉得文章对你有帮助,欢迎点赞或转载。文章有疏漏之处,欢迎批评指正。

目录

一、一个server.xml配置实例

二、server.xml文档的元素分类和整体结构

  1、整体结构

  2、元素分类

三、核心组件

  1、Server

  2、Service

  3、Connector

  4、Engine

  5、Host

  6、Context

四、核心组件的关联

  1、整体关系

  2、如何确定请求由谁处理?

  3、如何配置多个服务

五、其他组件

  1、Listener

  2、GlobalNamingResources与Realm

  3、Valve

六、参考文献

一、一个server.xml配置实例

server.xml位于$TOMCAT_HOME/conf目录下;下面是一个server.xml实例。后文中将结合该实例讲解server.xml中,各个元素的含义和作用;在阅读后续章节过程中,可以对照该xml文档便于理解。

 1 <Server port="8005" shutdown="SHUTDOWN">
 2   <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
 3   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
 4   <Listener className="org.apache.catalina.core.JasperListener" />
 5   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
 6   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
 7   <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
 8 
 9   <GlobalNamingResources>
10     <Resource name="UserDatabase" auth="Container"
11               type="org.apache.catalina.UserDatabase"
12               description="User database that can be updated and saved"
13               factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
14               pathname="conf/tomcat-users.xml" />
15   </GlobalNamingResources>
16  
17   <Service name="Catalina">
18     <Connector port="8080" protocol="HTTP/1.1"
19                connectionTimeout="20000"
20                redirectPort="8443" />
21     <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
22     <Engine name="Catalina" defaultHost="localhost">
23       <Realm className="org.apache.catalina.realm.LockOutRealm">
24         <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
25                resourceName="UserDatabase"/>
26       </Realm>
27  
28       <Host name="localhost"  appBase="webapps"
29             unpackWARs="true" autoDeploy="true">
30         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
31                prefix="localhost_access_log." suffix=".txt"
32                pattern="%h %l %u %t &quot;%r&quot; %s %b" />
33       </Host>
34     </Engine>
35   </Service>
36 </Server>

二、server.xml文档的元素分类和整体结构

1、整体结构

server.xml的整体结构如下:

 1 <Server>
 2     <Service>
 3         <Connector />
 4         <Connector />
 5         <Engine>
 6             <Host>
 7                 <Context /><!-- 现在常常使用自动部署,不推荐配置Context元素,Context小节有详细说明 -->
 8             </Host>
 9         </Engine>
10     </Service>
11 </Server>

该结构中只给出了Tomcat的核心组件,除了核心组件外,Tomcat还有一些其他组件,下面介绍一下组件的分类。

2、元素分类

server.xml文件中的元素可以分为以下4类:

(1)顶层元素:<Server>和<Service>

<Server>元素是整个配置文件的根元素,<Service>元素则代表一个Engine元素以及一组与之相连的Connector元素。

(2)连接器:<Connector>

<Connector>代表了外部客户端发送请求到特定Service的接口;同时也是外部客户端从特定Service接收响应的接口。

(3)容器:<Engine><Host><Context>

容器的功能是处理Connector接收进来的请求,并产生相应的响应。Engine、Host和Context都是容器,但它们不是平行的关系,而是父子关系:Engine包含Host,Host包含Context。一个Engine组件可以处理Service中的所有请求,一个Host组件可以处理发向一个特定虚拟主机的所有请求,一个Context组件可以处理一个特定Web应用的所有请求。

(4)内嵌组件:可以内嵌到容器中的组件。实际上,Server、Service、Connector、Engine、Host和Context是最重要的最核心的Tomcat组件,其他组件都可以归为内嵌组件。

下面将详细介绍Tomcat中各个核心组件的作用,以及相互之间的关系。

三、核心组件

本部分将分别介绍各个核心组件的作用、特点以及配置方式等。

1、Server

Server元素在最顶层,代表整个Tomcat容器,因此它必须是server.xml中唯一一个最外层的元素。一个Server元素中可以有一个或多个Service元素。

在第一部分的例子中,在最外层有一个<Server>元素,shutdown属性表示关闭Server的指令;port属性表示Server接收shutdown指令的端口号,设为-1可以禁掉该端口。

Server的主要任务,就是提供一个接口让客户端能够访问到这个Service集合,同时维护它所包含的所有的Service的声明周期,包括如何初始化、如何结束服务、如何找到客户端要访问的Service。

2、Service

Service的作用,是在Connector和Engine外面包了一层,把它们组装在一起,对外提供服务。一个Service可以包含多个Connector,但是只能包含一个Engine其中Connector的作用是从客户端接收请求,Engine的作用是处理接收进来的请求。

在第一部分的例子中,Server中包含一个名称为“Catalina”的Service。实际上,Tomcat可以提供多个Service,不同的Service监听不同的端口,后文会有介绍。

3、Connector

Connector的主要功能,是接收连接请求,创建Request和Response对象用于和请求端交换数据;然后分配线程让Engine来处理这个请求,并把产生的Request和Response对象传给Engine。

通过配置Connector,可以控制请求Service的协议及端口号。在第一部分的例子中,Service包含两个Connector:

1 <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
2 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

(1)通过配置第1个Connector,客户端可以通过8080端口号使用http协议访问Tomcat。其中,protocol属性规定了请求的协议,port规定了请求的端口号,redirectPort表示当强制要求https而请求是http时,重定向至端口号为8443的Connector,connectionTimeout表示连接的超时时间。

在这个例子中,Tomcat监听HTTP请求,使用的是8080端口,而不是正式的80端口;实际上,在正式的生产环境中,Tomcat也常常监听8080端口,而不是80端口。这是因为在生产环境中,很少将Tomcat直接对外开放接收请求,而是在Tomcat和客户端之间加一层代理服务器(如nginx),用于请求的转发、负载均衡、处理静态文件等;通过代理服务器访问Tomcat时,是在局域网中,因此一般仍使用8080端口。

(2)通过配置第2个Connector,客户端可以通过8009端口号使用AJP协议访问Tomcat。AJP协议负责和其他的HTTP服务器(如Apache)建立连接;在把Tomcat与其他HTTP服务器集成时,就需要用到这个连接器。之所以使用Tomcat和其他服务器集成,是因为Tomcat可以用作Servlet/JSP容器,但是对静态资源的处理速度较慢,不如Apache和IIS等HTTP服务器;因此常常将Tomcat与Apache等集成,前者作Servlet容器,后者处理静态资源,而AJP协议便负责Tomcat和Apache的连接。Tomcat与Apache等集成的原理如下图(图片来源):

关于Connector的更多内容,可以参考我的另一篇文章:详解tomcat的连接数与线程池

4、Engine

Engine组件在Service组件中有且只有一个;Engine是Service组件中的请求处理组件。Engine组件从一个或多个Connector中接收请求并处理,并将完成的响应返回给Connector,最终传递给客户端。

前面已经提到过,Engine、Host和Context都是容器,但它们不是平行的关系,而是父子关系:Engine包含Host,Host包含Context。

在第一部分的例子中,Engine的配置语句如下:

1 <Engine name="Catalina" defaultHost="localhost">

其中,name属性用于日志和错误信息,在整个Server中应该唯一。defaultHost属性指定了默认的host名称,当发往本机的请求指定的host名称不存在时,一律使用defaultHost指定的host进行处理;因此,defaultHost的值,必须与Engine中的一个Host组件的name属性值匹配。

5、Host

(1)Engine与Host

Host是Engine的子容器。Engine组件中可以内嵌1个或多个Host组件,每个Host组件代表Engine中的一个虚拟主机。Host组件至少有一个,且其中一个的name必须与Engine组件的defaultHost属性相匹配。

(2)Host的作用

Host虚拟主机的作用,是运行多个Web应用(一个Context代表一个Web应用),并负责安装、展开、启动和结束每个Web应用。

Host组件代表的虚拟主机,对应了服务器中一个网络名实体(如”www.test.com”,或IP地址”116.25.25.25”);为了使用户可以通过网络名连接Tomcat服务器,这个名字应该在DNS服务器上注册。

客户端通常使用主机名来标识它们希望连接的服务器;该主机名也会包含在HTTP请求头中。Tomcat从HTTP头中提取出主机名,寻找名称匹配的主机。如果没有匹配,请求将发送至默认主机。因此默认主机不需要是在DNS服务器中注册的网络名,因为任何与所有Host名称不匹配的请求,都会路由至默认主机。

(3)Host的配置

在第一部分的例子中,Host的配置如下:

1 <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">

下面对其中配置的属性进行说明:

name属性指定虚拟主机的主机名,一个Engine中有且仅有一个Host组件的name属性与Engine组件的defaultHost属性相匹配;一般情况下,主机名需要是在DNS服务器中注册的网络名,但是Engine指定的defaultHost不需要,原因在前面已经说明。

unpackWARs指定了是否将代表Web应用的WAR文件解压;如果为true,通过解压后的文件结构运行该Web应用,如果为false,直接使用WAR文件运行Web应用。

Host的autoDeploy和appBase属性,与Host内Web应用的自动部署有关;此外,本例中没有出现的xmlBase和deployOnStartup属性,也与Web应用的自动部署有关;将在下一节(Context)中介绍。

6、Context

(1)Context的作用

Context元素代表在特定虚拟主机上运行的一个Web应用。在后文中,提到Context、应用或Web应用,它们指代的都是Web应用。每个Web应用基于WAR文件,或WAR文件解压后对应的目录(这里称为应用目录)。

Context是Host的子容器,每个Host中可以定义任意多的Context元素。

在第一部分的例子中,可以看到server.xml配置文件中并没有出现Context元素的配置。这是因为,Tomcat开启了自动部署,Web应用没有在server.xml中配置静态部署,而是由Tomcat通过特定的规则自动部署。下面介绍一下Tomcat自动部署Web应用的机制。

(2)Web应用自动部署

Host的配置

要开启Web应用的自动部署,需要配置所在的虚拟主机;配置的方式就是前面提到的Host元素的deployOnStartup和autoDeploy属性。如果deployOnStartup和autoDeploy设置为true,则tomcat启动自动部署:当检测到新的Web应用或Web应用的更新时,会触发应用的部署(或重新部署)。二者的主要区别在于,deployOnStartup为true时,Tomcat在启动时检查Web应用,且检测到的所有Web应用视作新应用;autoDeploy为true时,Tomcat在运行时定期检查新的Web应用或Web应用的更新。除此之外,二者的处理相似。

通过配置deployOnStartup和autoDeploy可以开启虚拟主机自动部署Web应用;实际上,自动部署依赖于检查是否有新的或更改过的Web应用,而Host元素的appBase和xmlBase设置了检查Web应用更新的目录。

其中,appBase属性指定Web应用所在的目录,默认值是webapps,这是一个相对路径,代表Tomcat根目录下webapps文件夹。

xmlBase属性指定Web应用的XML配置文件所在的目录,默认值为conf/<engine_name>/<host_name>,例如第一部分的例子中,主机localhost的xmlBase的默认值是$TOMCAT_HOME/conf/Catalina/localhost。

检查Web应用更新

一个Web应用可能包括以下文件:XML配置文件,WAR包,以及一个应用目录(该目录包含Web应用的文件结构);其中XML配置文件位于xmlBase指定的目录,WAR包和应用目录位于appBase指定的目录。

Tomcat按照如下的顺序进行扫描,来检查应用更新:

A、扫描虚拟主机指定的xmlBase下的XML配置文件

B、扫描虚拟主机指定的appBase下的WAR文件

C、扫描虚拟主机指定的appBase下的应用目录

<Context>元素的配置

Context元素最重要的属性是docBase和path,此外reloadable属性也比较常用。

docBase指定了该Web应用使用的WAR包路径,或应用目录。需要注意的是,在自动部署场景下,docBase不在appBase目录中,才需要指定;如果docBase指定的WAR包或应用目录就在appBase中,则不需要指定,因为Tomcat会自动扫描appBase中的WAR包和应用目录,指定了反而会造成问题。

path指定了访问该Web应用的上下文路径,当请求到来时,Tomcat根据Web应用的 path属性与URI的匹配程度来选择Web应用处理相应请求。例如,Web应用app1的path属性是”/app1”,Web应用app2的path属性是”/app2”,那么请求/app1/index.html会交由app1来处理;而请求/app2/index.html会交由app2来处理。如果一个Context元素的path属性为””,那么这个Context是虚拟主机的默认Web应用;当请求的uri与所有的path都不匹配时,使用该默认Web应用来处理。

但是,需要注意的是,在自动部署场景下,不能指定path属性,path属性由配置文件的文件名、WAR文件的文件名或应用目录的名称自动推导出来。如扫描Web应用时,发现了xmlBase目录下的app1.xml,或appBase目录下的app1.WAR或app1应用目录,则该Web应用的path属性是”app1”。如果名称不是app1而是ROOT,则该Web应用是虚拟主机默认的Web应用,此时path属性推导为””。

reloadable属性指示tomcat是否在运行时监控在WEB-INF/classes和WEB-INF/lib目录下class文件的改动。如果值为true,那么当class文件改动时,会触发Web应用的重新加载。在开发环境下,reloadable设置为true便于调试;但是在生产环境中设置为true会给服务器带来性能压力,因此reloadable参数的默认值为false。

下面来看自动部署时,xmlBase下的XML配置文件app1.xml的例子:

1 <Context docBase="D:\Program Files\app1.war" reloadable="true"/>

在该例子中,docBase位于Host的appBase目录之外;path属性没有指定,而是根据app1.xml自动推导为”app1”;由于是在开发环境下,因此reloadable设置为true,便于开发调试。

自动部署举例

最典型的自动部署,就是当我们安装完Tomcat后,$TOMCAT_HOME/webapps目录下有如下文件夹:

当我们启动Tomcat后,可以使用http://localhost:8080/来访问Tomcat,其实访问的就是ROOT对应的Web应用;我们也可以通过http://localhost:8080/docs来访问docs应用,同理我们可以访问examples/host-manager/manager这几个Web应用。

(3)server.xml中静态部署Web应用

除了自动部署,我们也可以在server.xml中通过<context>元素静态部署Web应用。静态部署与自动部署是可以共存的。在实际应用中,并不推荐使用静态部署,因为server.xml 是不可动态重加载的资源,服务器一旦启动了以后,要修改这个文件,就得重启服务器才能重新加载。而自动部署可以在Tomcat运行时通过定期的扫描来实现,不需要重启服务器。

server.xml中使用Context元素配置Web应用,Context元素应该位于Host元素中。举例如下:

1 <Context path="/" docBase="D:\Program Files \app1.war" reloadable="true"/>

docBase:静态部署时,docBase可以在appBase目录下,也可以不在;本例中,docBase不在appBase目录下。

path:静态部署时,可以显式指定path属性,但是仍然受到了严格的限制:只有当自动部署完全关闭(deployOnStartup和autoDeploy都为false)或docBase不在appBase中时,才可以设置path属性。在本例中,docBase不在appBase中,因此path属性可以设置。

reloadable属性的用法与自动部署时相同。

四、核心组件的关联

1、整体关系

核心组件之间的整体关系,在上一部分有所介绍,这里总结一下:

Server元素在最顶层,代表整个Tomcat容器;一个Server元素中可以有一个或多个Service元素。

Service在Connector和Engine外面包了一层,把它们组装在一起,对外提供服务。一个Service可以包含多个Connector,但是只能包含一个Engine;Connector接收请求,Engine处理请求。

Engine、Host和Context都是容器,且 Engine包含Host,Host包含Context。每个Host组件代表Engine中的一个虚拟主机;每个Context组件代表在特定Host上运行的一个Web应用。

2、如何确定请求由谁处理?

当请求被发送到Tomcat所在的主机时,如何确定最终哪个Web应用来处理该请求呢?

(1)根据协议和端口号选定Service和Engine

Service中的Connector组件可以接收特定端口的请求,因此,当Tomcat启动时,Service组件就会监听特定的端口。在第一部分的例子中,Catalina这个Service监听了8080端口(基于HTTP协议)和8009端口(基于AJP协议)。当请求进来时,Tomcat便可以根据协议和端口号选定处理请求的Service;Service一旦选定,Engine也就确定。

通过在Server中配置多个Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。

(2)根据域名或IP地址选定Host

Service确定后,Tomcat在Service中寻找名称与域名/IP地址匹配的Host处理该请求。如果没有找到,则使用Engine中指定的defaultHost来处理该请求。在第一部分的例子中,由于只有一个Host(name属性为localhost),因此该Service/Engine的所有请求都交给该Host处理。

(3)根据URI选定Context/Web应用

这一点在Context一节有详细的说明:Tomcat根据应用的 path属性与URI的匹配程度来选择Web应用处理相应请求,这里不再赘述。

(4)举例

以请求http://localhost:8080/app1/index.html为例,首先通过协议和端口号(http和8080)选定Service;然后通过主机名(localhost)选定Host;然后通过uri(/app1/index.html)选定Web应用。

3、如何配置多个服务

通过在Server中配置多个Service服务,可以实现通过不同的端口号来访问同一台机器上部署的不同Web应用。

在server.xml中配置多服务的方法非常简单,分为以下几步:

(1)复制<Service>元素,放在当前<Service>后面。

(2)修改端口号:根据需要监听的端口号修改<Connector>元素的port属性;必须确保该端口没有被其他进程占用,否则Tomcat启动时会报错,而无法通过该端口访问Web应用。

以Win7为例,可以用如下方法找出某个端口是否被其他进程占用:netstat -aon|findstr “8081”发现8081端口被PID为2064的进程占用,tasklist |findstr “2064”发现该进程为FrameworkService.exe(这是McAfee杀毒软件的进程)。

(3)修改Service和Engine的name属性

(4)修改Host的appBase属性(如webapps2)

(5)Web应用仍然使用自动部署

(6)将要部署的Web应用(WAR包或应用目录)拷贝到新的appBase下。

以第一部分的server.xml为例,多个Service的配置如下:

 1 <?xml version='1.0' encoding='utf-8'?>
 2 <Server port="8005" shutdown="SHUTDOWN">
 3   <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
 4   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
 5   <Listener className="org.apache.catalina.core.JasperListener" />
 6   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
 7   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
 8   <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
 9 
10   <GlobalNamingResources>
11     <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" />
12   </GlobalNamingResources>
13 
14   <Service name="Catalina">
15     <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
16     <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
17     <Engine name="Catalina" defaultHost="localhost">
18       <Realm className="org.apache.catalina.realm.LockOutRealm">
19         <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
20                resourceName="UserDatabase"/>
21       </Realm>
22 
23       <Host name="localhost"  appBase="/opt/project/webapps" unpackWARs="true" autoDeploy="true">
24         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
25       </Host>
26     </Engine>
27   </Service>
28 
29   <Service name="Catalina2">
30     <Connector port="8084" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
31     <Connector port="8010" protocol="AJP/1.3" redirectPort="8443" />
32     <Engine name="Catalina2" defaultHost="localhost">
33       <Realm className="org.apache.catalina.realm.LockOutRealm">
34         <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
35                resourceName="UserDatabase"/>
36       </Realm>
37 
38       <Host name="localhost"  appBase="/opt/project/webapps2" unpackWARs="true" autoDeploy="true">
39         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
40       </Host>
41     </Engine>
42   </Service>
43 </Server>

再将原webapps下的docs目录拷贝到webapps2中,则通过如下两个接口都可以访问docs应用:

http://localhost:8080/docs/

http://localhost:8084/docs/

五、其他组件

除核心组件外,server.xml中还可以配置很多其他组件。下面只介绍第一部分例子中出现的组件,如果要了解更多内容,可以查看Tomcat官方文档

1、Listener

1   <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
2   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
3   <Listener className="org.apache.catalina.core.JasperListener" />
4   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
5   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
6   <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

Listener(即监听器)定义的组件,可以在特定事件发生时执行特定的操作;被监听的事件通常是Tomcat的启动和停止。

监听器可以在Server、Engine、Host或Context中,本例中的监听器都是在Server中。实际上,本例中定义的6个监听器,都只能存在于Server组件中。监听器不允许内嵌其他组件。

监听器需要配置的最重要的属性是className,该属性规定了监听器的具体实现类,该类必须实现了org.apache.catalina.LifecycleListener接口。

下面依次介绍例子中配置的监听器:

  • VersionLoggerListener:当Tomcat启动时,该监听器记录Tomcat、Java和操作系统的信息。该监听器必须是配置的第一个监听器。
  • AprLifecycleListener:Tomcat启动时,检查APR库,如果存在则加载。APR,即Apache Portable Runtime,是Apache可移植运行库,可以实现高可扩展性、高性能,以及与本地服务器技术更好的集成。
  • JasperListener:在Web应用启动之前初始化Jasper,Jasper是JSP引擎,把JVM不认识的JSP文件解析成java文件,然后编译成class文件供JVM使用。
  • JreMemoryLeakPreventionListener:与类加载器导致的内存泄露有关。
  • GlobalResourcesLifecycleListener:通过该监听器,初始化< GlobalNamingResources>标签中定义的全局JNDI资源;如果没有该监听器,任何全局资源都不能使用。< GlobalNamingResources>将在后文介绍。
  • ThreadLocalLeakPreventionListener:当Web应用因thread-local导致的内存泄露而要停止时,该监听器会触发线程池中线程的更新。当线程执行完任务被收回线程池时,活跃线程会一个一个的更新。只有当Web应用(即Context元素)的renewThreadsWhenStoppingContext属性设置为true时,该监听器才有效。

2、GlobalNamingResources与Realm

第一部分的例子中,Engine组件下定义了Realm组件:

1       <Realm className="org.apache.catalina.realm.LockOutRealm">
2         <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
3                resourceName="UserDatabase"/>
4       </Realm>

Realm,可以把它理解成“域”;Realm提供了一种用户密码与web应用的映射关系,从而达到角色安全管理的作用。在本例中,Realm的配置使用name为UserDatabase的资源实现。而该资源在Server元素中使用GlobalNamingResources配置:

1   <GlobalNamingResources>
2     <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" />
3   </GlobalNamingResources>

GlobalNamingResources元素定义了全局资源,通过配置可以看出,该配置是通过读取$TOMCAT_HOME/ conf/tomcat-users.xml实现的。

关于Tomcat域管理的更多内容,可以参考:Realm域管理

3、Valve

在第一部分的例子中,Host元素内定义了Valve组件:

1 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />

单词Valve的意思是“阀门”,在Tomcat中代表了请求处理流水线上的一个组件;Valve可以与Tomcat的容器(Engine、Host或Context)关联。

不同的Valve有不同的特性,下面介绍一下本例中出现的AccessLogValve。

AccessLogValve的作用是通过日志记录其所在的容器中处理的所有请求,在本例中,Valve放在Host下,便可以记录该Host处理的所有请求。AccessLogValve记录的日志就是访问日志,每天的请求会写到一个日志文件里。AccessLogValve可以与Engine、Host或Context关联;在本例中,只有一个Engine,Engine下只有一个Host,Host下只有一个Context,因此AccessLogValve放在三个容器下的作用其实是类似的。

本例的AccessLogValve属性的配置,使用的是默认的配置;下面介绍AccessLogValve中各个属性的作用:

(1)className:规定了Valve的类型,是最重要的属性;本例中,通过该属性规定了这是一个AccessLogValve。

(2)directory:指定日志存储的位置,本例中,日志存储在$TOMCAT_HOME/logs目录下。

(3)prefix:指定了日志文件的前缀。

(4)suffix:指定了日志文件的后缀。通过directory、prefix和suffix的配置,在$TOMCAT_HOME/logs目录下,可以看到如下所示的日志文件。

(5)pattern:指定记录日志的格式,本例中各项的含义如下:

  • %h:远程主机名或IP地址;如果有nginx等反向代理服务器进行请求分发,该主机名/IP地址代表的是nginx,否则代表的是客户端。后面远程的含义与之类似,不再解释。
  • %l:远程逻辑用户名,一律是”-”,可以忽略。
  • %u:授权的远程用户名,如果没有,则是”-”。
  • %t:访问的时间。
  • %r:请求的第一行,即请求方法(get/post等)、uri、及协议。
  • %s:响应状态,200,404等等。
  • %b:响应的数据量,不包括请求头,如果为0,则是””-。

例如,下面是访问日志中的一条记录

pattern的配置中,除了上述各项,还有一个非常常用的选项是%D,含义是请求处理的时间(单位是毫秒),对于统计分析请求的处理速度帮助很大。

开发人员可以充分利用访问日志,来分析问题、优化应用。例如,分析访问日志中各个接口被访问的比例,不仅可以为需求和运营人员提供数据支持,还可以使自己的优化有的放矢;分析访问日志中各个请求的响应状态码,可以知道服务器请求的成功率,并找出有问题的请求;分析访问日志中各个请求的响应时间,可以找出慢请求,并根据需要进行响应时间的优化。

六、参考文献

Tomcat官方文档

《How Tomcat Works》

《深入分析Java Web技术内幕》

Tomcat 6 —— Realm域管理

Tomcat Port 8009 与AJP13协议

使用Jasper引擎解析JSP

etcd 备份

环境说明:k8s 版本1.21.1 ,无docker

方法一 pod中执行备份

先exec 进入etcd的pod kubectl exec -nkube-system -it pod/etcd-master -- sh
执行命令:

etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt                  \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key     \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt    \
snapshot save /var/lib/etcd/etcd-snapshot.db

说明,etcd 会把宿主机的 /var/lib/etcd/ 目录mount 到container 的同名目录 /var/lib/etcd/ ,因此执行上述命令后 ,可以在宿主机的对应目录下找到备份文件

方法二 master安装etcdctl二进制文件备份

find 之 perm 篇

-perm 查找文件或目录的权限设置,返回匹配的项目。其中匹配规则有3种写法:

mode   完全匹配,         如 -perm g=w  只匹配 0020  (----w----)
-mode  满足所有设置位匹配 如 -perm -664  可以匹配 0777(转化下好理解rw*rw*r**)
/mode  满足任意一位匹配   如 -perm /220 ; /u+w,g+w ; /u=w,g=w  ,代表u 或则g 有写权限即匹配。()
+mode  已弃用,被/mode 取代