配置docker限制容器对cpu 内存和IO的资源使用

在使用 docker 运行容器时,一台主机上可能会运行几百个容器,这些容器虽然互相隔离,但是底层却使用着相同的 CPU、内存和磁盘资源。如果不对容器使用的资源进行限制,那么容器之间会互相影响,小的来说会导致容器资源使用不公平;大的来说,可能会导致主机和集群资源耗尽,服务完全不可用。

docker 作为容器的管理者,自然提供了控制容器资源的功能。正如使用内核的 namespace 来做容器之间的隔离,docker 也是通过内核的 cgroups 来做容器的资源限制。这篇文章就介绍如何使用 docker 来限制 CPU、内存和 IO,以及对应的 cgroups 文件。

NOTE:如果想要了解 cgroups 的更多信息,可以参考 kernel 文档 或者其他资源。

我本地测试的 docker 版本是 17.03.0 社区版:

➜  stress docker version
Client:
 Version:      17.03.0-ce
 API version:  1.26
 Go version:   go1.7.5
 Git commit:   60ccb22
 Built:        Thu Feb 23 11:02:43 2017
 OS/Arch:      linux/amd64

Server:
 Version:      17.03.0-ce
 API version:  1.26 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   60ccb22
 Built:        Thu Feb 23 11:02:43 2017
 OS/Arch:      linux/amd64
 Experimental: false

使用的是 ubuntu 16.04 系统,内核版本是 4.10.0:

➜  ~ uname -a
Linux cizixs-ThinkPad-T450 4.10.0-28-generic #32~16.04.2-Ubuntu SMP Thu Jul 20 10:19:48 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

NOTE: 不同版本和系统的功能会有差异,具体的使用方法和功能解释请以具体版本的 docker 官方文档为准。

我们使用 stress 容器来产生 CPU、内存和 IO 的压力,具体的使用请参考它的帮助文档。

1、CPU 资源

主机上的进程会通过时间分片机制使用 CPU,CPU 的量化单位是频率,也就是每秒钟能执行的运算次数。为容器限制 CPU 资源并不能改变 CPU 的运行频率,而是改变每个容器能使用的 CPU 时间片。理想状态下,CPU 应该一直处于运算状态(并且进程需要的计算量不会超过 CPU 的处理能力)。

docker 限制 CPU Share

docker 允许用户为每个容器设置一个数字,代表容器的 CPU share,默认情况下每个容器的 share 是 1024。要注意,这个 share 是相对的,本身并不能代表任何确定的意义。当主机上有多个容器运行时,每个容器占用的 CPU 时间比例为它的 share 在总额中的比例。举个例子,如果主机上有两个一直使用 CPU 的容器(为了简化理解,不考虑主机上其他进程),其 CPU share 都是 1024,那么两个容器 CPU 使用率都是 50%;如果把其中一个容器的 share 设置为 512,那么两者 CPU 的使用率分别为 67% 和 33%;如果删除 share 为 1024 的容器,剩下来容器的 CPU 使用率将会是 100%。

总结下来,这种情况下,docker 会根据主机上运行的容器和进程动态调整每个容器使用 CPU 的时间比例。这样的好处是能保证 CPU 尽可能处于运行状态,充分利用 CPU 资源,而且保证所有容器的相对公平;缺点是无法指定容器使用 CPU 的确定值。

docker 为容器设置 CPU share 的参数是 -c –cpu-shares,它的值是一个整数。

我的机器是 4 核 CPU,因此使用 stress 启动 4 个进程来产生计算压力:

➜  stress docker run --rm -it stress --cpu 4
stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 12000us
stress: dbug: [1] --> hogcpu worker 4 [7] forked
stress: dbug: [1] using backoff sleep of 9000us
stress: dbug: [1] --> hogcpu worker 3 [8] forked
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [9] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [10] forked

在另外一个 terminal 使用 htop 查看资源的使用情况:

未分类

从上图中可以看到,CPU 四个核资源都达到了 100%。四个 stress 进程 CPU 使用率没有达到 100% 是因为系统中还有其他机器在运行。

为了比较,我另外启动一个 share 为 512 的容器:

➜  stress docker run --rm -it -c 512 stress --cpu 4 
stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 12000us
stress: dbug: [1] --> hogcpu worker 4 [6] forked
stress: dbug: [1] using backoff sleep of 9000us
stress: dbug: [1] --> hogcpu worker 3 [7] forked
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [8] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [9] forked

因为默认情况下,容器的 CPU share 为 1024,所以这两个容器的 CPU 使用率应该大致为 2:1,下面是启动第二个容器之后的监控截图:

未分类

两个容器分别启动了四个 stress 进程,第一个容器 stress 进程 CPU 使用率都在 54% 左右,第二个容器 stress 进程 CPU 使用率在 25% 左右,比例关系大致为 2:1,符合之前的预期。

限制容器能使用的 CPU 核数

上面讲述的 -c –cpu-shares 参数只能限制容器使用 CPU 的比例,或者说优先级,无法确定地限制容器使用 CPU 的具体核数;从 1.13 版本之后,docker 提供了 –cpus 参数可以限定容器能使用的 CPU 核数。这个功能可以让我们更精确地设置容器 CPU 使用量,是一种更容易理解也因此更常用的手段。

–cpus 后面跟着一个浮点数,代表容器最多使用的核数,可以精确到小数点二位,也就是说容器最小可以使用 0.01 核 CPU。比如,我们可以限制容器只能使用 1.5 核数 CPU:

➜  ~ docker run --rm -it --cpus 1.5 stress --cpu 3
stress: info: [1] dispatching hogs: 3 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 9000us
stress: dbug: [1] --> hogcpu worker 3 [7] forked
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [8] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [9] forked

在容器里启动三个 stress 来跑 CPU 压力,如果不加限制,这个容器会导致 CPU 的使用率为 300% 左右(也就是说会占用三个核的计算能力)。实际的监控如下图:

未分类

可以看到,每个 stress 进程 CPU 使用率大约在 50%,总共的使用率为 150%,符合 1.5 核的设置。

如果设置的 –cpus 值大于主机的 CPU 核数,docker 会直接报错:

➜  ~ docker run --rm -it --cpus 8 stress --cpu 3
docker: Error response from daemon: Range of CPUs is from 0.01 to 4.00, as there are only 4 CPUs available.
See 'docker run --help'.

如果多个容器都设置了 –cpus ,并且它们之和超过主机的 CPU 核数,并不会导致容器失败或者退出,这些容器之间会竞争使用 CPU,具体分配的 CPU 数量取决于主机运行情况和容器的 CPU share 值。也就是说 –cpus 只能保证在 CPU 资源充足的情况下容器最多能使用的 CPU 数,docker 并不能保证在任何情况下容器都能使用这么多的 CPU(因为这根本是不可能的)。

限制容器运行在某些 CPU 核

现在的笔记本和服务器都会有多个 CPU,docker 也允许调度的时候限定容器运行在哪个 CPU 上。比如,我的主机上有 4 个核,可以通过 –cpuset 参数让容器只运行在前两个核上:

➜  ~ docker run --rm -it --cpuset-cpus=0,1 stress --cpu 2
stress: info: [1] dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [7] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [8] forked

这样,监控中可以看到只有前面两个核 CPU 达到了 100% 使用率。

未分类

–cpuset-cpus 参数可以和 -c –cpu-shares 一起使用,限制容器只能运行在某些 CPU 核上,并且配置了使用率。

限制容器运行在哪些核上并不是一个很好的做法,因为它需要实现知道主机上有多少 CPU 核,而且非常不灵活。除非有特别的需求,一般并不推荐在生产中这样使用。

CPU 信息的 cgroup 文件

所有和容器 CPU share 有关的配置都在 /sys/fs/cgroup/cpu/docker// 目录下面,其中 cpu.shares 保存了 CPU share 的值(其他文件的意义可以查看 cgroups 的官方文档):

➜  ~ ls /sys/fs/cgroup/cpu/docker/d93c9a660f4a13789d995d56024f160e2267f2dc26ce676daa66ea6435473f6f/   
cgroup.clone_children  cpuacct.stat   cpuacct.usage_all     cpuacct.usage_percpu_sys   cpuacct.usage_sys   cpu.cfs_period_us  cpu.shares  notify_on_release
cgroup.procs           cpuacct.usage  cpuacct.usage_percpu  cpuacct.usage_percpu_user  cpuacct.usage_user  cpu.cfs_quota_us   cpu.stat    tasks
➜  ~ cat /sys/fs/cgroup/cpu/docker/d93c9a660f4a13789d995d56024f160e2267f2dc26ce676daa66ea6435473f6f/cpu.shares 
1024

和 cpuset(限制 CPU 核)有关的文件在 /sys/fs/cgroup/cpuset/docker/ 目录下,其中 cpuset.cpus 保存了当前容器能使用的 CPU 核:

➜  ~ ls /sys/fs/cgroup/cpuset/docker/d93c9a660f4a13789d995d56024f160e2267f2dc26ce676daa66ea6435473f6f/
cgroup.clone_children  cpuset.cpus            cpuset.mem_exclusive   cpuset.memory_pressure     cpuset.mems                      notify_on_release
cgroup.procs           cpuset.effective_cpus  cpuset.mem_hardwall    cpuset.memory_spread_page  cpuset.sched_load_balance        tasks
cpuset.cpu_exclusive   cpuset.effective_mems  cpuset.memory_migrate  cpuset.memory_spread_slab  cpuset.sched_relax_domain_level

➜  ~ cat /sys/fs/cgroup/cpuset/docker/d93c9a660f4a13789d995d56024f160e2267f2dc26ce676daa66ea6435473f6f/cpuset.cpus
0-1

–cpus 限制 CPU 核数并不像上面两个参数一样有对应的文件对应,它是由 cpu.cfs_period_us 和 cpu.cfs_quota_us 两个文件控制的。如果容器的 –cpus 设置为 3,其对应的这两个文件值为:

➜  ~ cat /sys/fs/cgroup/cpu/docker/233a38cc641f2e4a1bec3434d88744517a2214aff9d8297e908fa13b9aa12e02/cpu.cfs_period_us 
100000
➜  ~ cat /sys/fs/cgroup/cpu/docker/233a38cc641f2e4a1bec3434d88744517a2214aff9d8297e908fa13b9aa12e02/cpu.cfs_quota_us 
300000

其实在 1.12 以及之前的版本,都是通过 –cpu-period 和 –cpu-quota 这两个参数控制容器能使用的 CPU 核数的。前者表示 CPU 的周期数,默认是 100000,单位是微秒,也就是 1s,一般不需要修改;后者表示容器的在上述 CPU 周期里能使用的 quota,真正能使用的 CPU 核数就是 cpu-quota / cpu-period,因此对于 3 核的容器,对应的 cpu-quota 值为 300000。

2、内存资源

默认情况下,docker 并没有对容器内存进行限制,也就是说容器可以使用主机提供的所有内存。这当然是非常危险的事情,如果某个容器运行了恶意的内存消耗软件,或者代码有内存泄露,很可能会导致主机内存耗尽,因此导致服务不可用。对于这种情况,docker 会设置 docker daemon 的 OOM(out of memory) 值,使其在内存不足的时候被杀死的优先级降低。另外,就是你可以为每个容器设置内存使用的上限,一旦超过这个上限,容器会被杀死,而不是耗尽主机的内存。

限制内存上限虽然能保护主机,但是也可能会伤害到容器里的服务。如果为服务设置的内存上限太小,会导致服务还在正常工作的时候就被 OOM 杀死;如果设置的过大,会因为调度器算法浪费内存。因此,合理的做法包括:

  • 为应用做内存压力测试,理解正常业务需求下使用的内存情况,然后才能进入生产环境使用
  • 一定要限制容器的内存使用上限
  • 尽量保证主机的资源充足,一旦通过监控发现资源不足,就进行扩容或者对容器进行迁移
  • 如果可以(内存资源充足的情况),尽量不要使用 swap,swap 的使用会导致内存计算复杂,对调度器非常不友好

docker 限制容器内存使用量

在 docker 启动参数中,和内存限制有关的包括(参数的值一般是内存大小,也就是一个正数,后面跟着内存单位 b、k、m、g,分别对应 bytes、KB、MB、和 GB):

  • -m –memory:容器能使用的最大内存大小,最小值为 4m
  • –memory-swap:容器能够使用的 swap 大小
  • –memory-swappiness:默认情况下,主机可以把容器使用的匿名页(anonymous page)swap 出来,你可以设置一个 0-100 之间的值,代表允许 swap 出来的比例
  • –memory-reservation:设置一个内存使用的 soft limit,如果 docker 发现主机内存不足,会执行 OOM 操作。这个值必须小于 –memory 设置的值
  • –kernel-memory:容器能够使用的 kernel memory 大小,最小值为 4m。
  • –oom-kill-disable:是否运行 OOM 的时候杀死容器。只有设置了 -m,才可以把这个选项设置为 false,否则容器会耗尽主机内存,而且导致主机应用被杀死

关于 –memory-swap 的设置必须解释一下,–memory-swap 必须在 –memory 也配置的情况下才能有用。

  • 如果 –memory-swap 的值大于 –memory,那么容器能使用的总内存(内存 + swap)为 –memory-swap 的值,能使用的 swap 值为 –memory-swap 减去 –memory 的值
  • 如果 –memory-swap 为 0,或者和 –memory 的值相同,那么容器能使用两倍于内存的 swap 大小,如果 –memory 对应的值是 200M,那么容器可以使用 400M swap
  • 如果 –memory-swap 的值为 -1,那么不限制 swap 的使用,也就是说主机有多少 swap,容器都可以使用

如果限制容器的内存使用为 64M,在申请 64M 资源的情况下,容器运行正常(如果主机上内存非常紧张,并不一定能保证这一点):

➜  docker run --rm -it -m 64m stress --vm 1 --vm-bytes 64M --vm-hang 0
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 67108864 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] sleeping forever with allocated memory
.....

而如果申请 100M 内存,会发现容器里的进程被 kill 掉了(worker 7 got signal 9,signal 9 就是 kill 信号)

➜  docker run --rm -it -m 64m stress --vm 1 --vm-bytes 100M --vm-hang 0
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 104857600 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: FAIL: [1] (415) <-- worker 7 got signal 9
stress: WARN: [1] (417) now reaping child worker processes
stress: FAIL: [1] (421) kill error: No such process
stress: FAIL: [1] (451) failed run completed in 0s

关于 swap 和 kernel memory 的限制就不在这里过多解释了,感兴趣的可以查看官方的文档。

内存信息的 cgroups 文件

对于 docker 来说,它的内存限制也是存放在 cgroups 文件系统的。对于某个容器,你可以在 sys/fs/cgroup/memory/docker/ 目录下看到容器内存相关的文件:

➜  ls /sys/fs/cgroup/memory/docker/b067fa0c58dcdd4fa856177fac0112655b605fcc9a0fe07e36950f0086f62f46 
cgroup.clone_children  memory.kmem.failcnt             memory.kmem.tcp.limit_in_bytes      memory.max_usage_in_bytes        memory.soft_limit_in_bytes  notify_on_release
cgroup.event_control   memory.kmem.limit_in_bytes      memory.kmem.tcp.max_usage_in_bytes  memory.move_charge_at_immigrate  memory.stat                 tasks
cgroup.procs           memory.kmem.max_usage_in_bytes  memory.kmem.tcp.usage_in_bytes      memory.numa_stat                 memory.swappiness
memory.failcnt         memory.kmem.slabinfo            memory.kmem.usage_in_bytes          memory.oom_control               memory.usage_in_bytes
memory.force_empty     memory.kmem.tcp.failcnt         memory.limit_in_bytes               memory.pressure_level            memory.use_hierarchy

而上面的内存限制对应的文件是 memory.limit_in_bytes:

➜  cat /sys/fs/cgroup/memory/docker/b067fa0c58dcdd4fa856177fac0112655b605fcc9a0fe07e36950f0086f62f46/memory.limit_in_bytes
67108864

3、IO 资源(磁盘)

对于磁盘来说,考量的参数是容量和读写速度,因此对容器的磁盘限制也应该从这两个维度出发。目前 docker 支持对磁盘的读写速度进行限制,但是并没有方法能限制容器能使用的磁盘容量(一旦磁盘 mount 到容器里,容器就能够使用磁盘的所有容量)。

➜  ~ docker run -it --rm ubuntu:16.04 bash

root@5229f756523c:/# time $(dd if=/dev/zero of=/tmp/test.data bs=10M count=100 && sync)
100+0 records in
100+0 records out
1048576000 bytes (1.0 GB) copied, 3.82859 s, 274 MB/s

real    0m4.124s
user    0m0.000s
sys 0m1.812s

限制磁盘的权重

通过 –blkio-weight 参数可以设置 block 的权重,这个权重和 –cpu-shares 类似,它是一个相对值,取值范围是 10-1000,当多个 block 去屑磁盘的时候,其读写速度和权重成反比。

不过在我的环境中,–blkio-weight 参数虽然设置了对应的 cgroups 值,但是并没有作用,不同 weight 容器的读写速度还是一样的。github 上有一个对应的 issue,但是没有详细的解答。

–blkio-weight-device 可以设置某个设备的权重值,测试下来虽然两个容器同时读的速度不同,但是并没有按照对应的比例来限制。

限制磁盘的读写速率

除了权重之外,docker 还允许你直接限制磁盘的读写速率,对应的参数有:

  • –device-read-bps:磁盘每秒最多可以读多少比特(bytes)
  • –device-write-bps:磁盘每秒最多可以写多少比特(bytes)

上面两个参数的值都是磁盘以及对应的速率,格式为 :[unit],device-path 表示磁盘所在的位置,限制 limit 为正整数,单位可以是 kb、mb 和 gb。

比如可以把设备的度速率限制在 1mb:

$ docker run -it --device /dev/sda:/dev/sda --device-read-bps /dev/sda:1mb ubuntu:16.04 bash
root@6c048edef769:/# cat /sys/fs/cgroup/blkio/blkio.throttle.read_bps_device 
8:0 1048576
root@6c048edef769:/# dd iflag=direct,nonblock if=/dev/sda of=/dev/null bs=5M count=10
10+0 records in
10+0 records out
52428800 bytes (52 MB) copied, 50.0154 s, 1.0 MB/s

从磁盘中读取 50m 花费了 50s 左右,说明磁盘速率限制起了作用。

另外两个参数可以限制磁盘读写频率(每秒能执行多少次读写操作):

  • –device-read-iops:磁盘每秒最多可以执行多少 IO 读操作
  • –device-write-iops:磁盘每秒最多可以执行多少 IO 写操作

上面两个参数的值都是磁盘以及对应的 IO 上限,格式为 :,limit 为正整数,表示磁盘 IO 上限数。

比如,我们可以让磁盘每秒最多读 100 次:

➜  ~ docker run -it --device /dev/sda:/dev/sda --device-read-iops /dev/sda:100 ubuntu:16.04 bash
root@2e3026e9ccd2:/# dd iflag=direct,nonblock if=/dev/sda of=/dev/null bs=1k count=1000
1000+0 records in
1000+0 records out
1024000 bytes (1.0 MB) copied, 9.9159 s, 103 kB/s

从测试中可以看出,容器设置了读操作的 iops 为 100,在容器内部从 block 中读取 1m 数据(每次 1k,一共要读 1000 次),共计耗时约 10s,换算起来就是 100 iops/s,符合预期结果。

写操作 bps 和 iops 与读类似,这里就不再重复了,感兴趣的可以自己实验。

磁盘信息的 cgroups 文件

容器中磁盘限制的 cgroups 文件位于 /sys/fs/cgroup/blkio/docker/ 目录:

➜  ~ ls /sys/fs/cgroup/blkio/docker/1402c1682cba743b4d80f638da3d4272b2ebdb6dc6c2111acfe9c7f7aeb72917/                               
blkio.io_merged                   blkio.io_serviced                blkio.leaf_weight                blkio.throttle.io_serviced        blkio.time_recursive   tasks
blkio.io_merged_recursive         blkio.io_serviced_recursive      blkio.leaf_weight_device         blkio.throttle.read_bps_device    blkio.weight
blkio.io_queued                   blkio.io_service_time            blkio.reset_stats                blkio.throttle.read_iops_device   blkio.weight_device
blkio.io_queued_recursive         blkio.io_service_time_recursive  blkio.sectors                    blkio.throttle.write_bps_device   cgroup.clone_children
blkio.io_service_bytes            blkio.io_wait_time               blkio.sectors_recursive          blkio.throttle.write_iops_device  cgroup.procs
blkio.io_service_bytes_recursive  blkio.io_wait_time_recursive     blkio.throttle.io_service_bytes  blkio.time                        notify_on_release

其中 blkio.throttle.read_iops_device 对应了设备的读 IOPS,前面一列是设备的编号,可以通过 cat /proc/partitions 查看设备和分区的设备号;后面是 IOPS 上限值:

➜  ~ cat /sys/fs/cgroup/blkio/docker/1402c1682cba743b4d80f638da3d4272b2ebdb6dc6c2111acfe9c7f7aeb72917/blkio.throttle.read_iops_device 
8:0 100

blkio.throttle.read_bps_device 对应了设备的读速率,格式和 IOPS 类似,只是第二列的值为 bps:

➜  ~ cat /sys/fs/cgroup/blkio/docker/9de94493f1ab4437d9c2c42fab818f12c7e82dddc576f356c555a2db7bc61e21/blkio.throttle.read_bps_device 
8:0 1048576

总结

从上面的实验可以看出来,CPU 和内存的资源限制已经是比较成熟和易用,能够满足大部分用户的需求。磁盘限制也是不错的,虽然现在无法动态地限制容量,但是限制磁盘读写速度也能应对很多场景。

至于网络,docker 现在并没有给出网络限制的方案,也不会在可见的未来做这件事情,因为目前网络是通过插件来实现的,和容器本身的功能相对独立,不是很容易实现,扩展性也很差。docker 社区已经有很多呼声,也有 issue 是关于网络流量限制的: issue 26767、issue 37、issue 4763。

资源限制一方面可以让我们为容器(应用)设置合理的 CPU、内存等资源,方便管理;另外一方面也能有效地预防恶意的攻击和异常,对容器来说是非常重要的功能。如果你需要在生产环境使用容器,请务必要花时间去做这件事情。

Docker安装部署MySQL5.7

1、进入Linux后,使用Docker命令下载MySQL,命令如:

docker pull mysql:5.7

运行该命令后,则会显示以下日志:

[root@localhost ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
ad74af05f5a2: Pull complete 
0639788facc8: Pull complete 
de70fa77eb2b: Pull complete 
724179e94999: Pull complete 
50c77fb16ba6: Pull complete 
d51f459239fb: Pull complete 
937bbdd4305a: Pull complete 
35369f9634e1: Pull complete 
f6016aab25f1: Pull complete 
5f1901e920da: Pull complete 
fdf808213c5b: Pull complete 
Digest: sha256:96edf37370df96d2a4ee1715cc5c7820a0ec6286551a927981ed50f0273d9b43
Status: Downloaded newer image for mysql:5.7

2、先查看本机都有哪些镜像,命令如下:

docker images
[root@localhost ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mysql               5.7                 c73c7527c03a        8 days ago          412MB
hello-world         latest              1815c82652c0        7 weeks ago         1.84kB
java                latest              d23bdf5b1b1b        6 months ago        643MB
learn/tutorial      latest              a7876479f1aa        4 years ago         128MB

3.然后启动我们的mysql的docker容器,命令如下:

docker run --name mysql5.7 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7

注意,这里的容器名字叫:mysql5.7,mysql的root用户密码是:123456,映射宿主机子的端口3306到容器的端口3306,仓库名mysql和标签(tag)唯一确定了要指定的镜像,其实如果这里只有一个mysql也有必要要tag,执行该命令返回的结果是:

9238d9feb10a0c553d950451add144727b659a0972ccf04d7c59c3bfa198ed20

4.查看已经运行的的所有容器,命令如:docker ps

[root@localhost ~]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
9238d9feb10a        mysql:5.7           "docker-entrypoint..."   12 seconds ago      Up 9 seconds        0.0.0.0:3306->3306/tcp   mysql5.7
[root@localhost ~]#

5.使用mysql的工具,比如navicat连接成功如下:

未分类

docker导出镜像及导入镜像并启动容器

备份镜像方法

docker commit -p 30b8f18f20b4 container-backup

备份到本地方法

docker save -o ~/container-backup.tar container-backup

导入本地镜像

docker load -i ~/container-backup.tar

启动本地镜像

docker load -i ~/container-backup.tar

启动容易方法方法

docker run -d -p 812:80 -p 2222:22 -p 3308:3306 --name centos2lnmp -v /Users/haha/wwwroot/docker:/home/wwwroot/default/ centos6

进入容器方法

docker exec -it centos6 /bin/bash

用awk实现按列求和及求平均值

平常的统计中肯定需要用上对取出来的一列数据进行求和求平均值。使用awk很容易实现。具体演示的命令如下:

[root@123 build]# ll
total 336
-rw-rw-r-- 1 1000 1000   3330 Mar 14 21:17 ax_check_compile_flag.m4
-rw-rw-r-- 1 1000 1000   2200 Mar 14 21:17 build2.mk
-rwxrwxr-x 1 1000 1000   2165 Mar 14 21:17 buildcheck.sh
-rw-rw-r-- 1 1000 1000   2510 Mar 14 21:17 build.mk
-rwxrwxr-x 1 1000 1000    381 Mar 14 21:17 config-stubs
-rw-rw-r-- 1 1000 1000    700 Mar 14 21:17 genif.sh
-rw-rw-r-- 1 1000 1000 230377 Mar 14 21:17 libtool.m4
-rw-rw-r-- 1 1000 1000   2225 Mar 14 21:17 mkdep.awk
-rw-rw-r-- 1 1000 1000   1556 Mar 14 21:17 order_by_dep.awk
-rw-rw-r-- 1 1000 1000    122 Mar 14 21:17 print_include.awk
-rw-rw-r-- 1 1000 1000    410 Mar 14 21:17 scan_makefile_in.awk
-rwxrwxr-x 1 1000 1000  65761 Mar 14 21:17 shtool
[root@123 build]# ll | awk '{print $5}'
3330
2200
2165
2510
381
700
230377
2225
1556
122
410
65761
[root@123 build]# ll | awk '{a=a+$5;b++;}END{print a,b,a/b}'
311737 13 23979.8
[root@123 build]# ll | awk '/build/ {print $9,$5}'                  
build2.mk 2200
buildcheck.sh 2165
build.mk 2510
[root@123 build]# ll | awk '/build/ {a=a+$5;b++}END{print a,b,a/b}' 
6875 3 2291.67
[root@123 build]# 

Linux awk命令详解

awk :适用程序,一种unix工具

就是一个强大的文本分析工具,相对于grep查找、sed的编辑,awk在对数据分析并生成报告的时候,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种处理。

  • awk是用来操作数据和产生报表的一种编程语言。数据可能来自标准输入、一个或多个文件或一个进程的输出等。awk可以用在命令行里进行简单操作,也可应用到较大的应用程序中。
  • awk是一门编程语法,作为unix工具来使用简化了很多,但是仍然有许多编程语言的特性,可以对目标进行一系列的处理。

  • 如果抛出awk的BEGIN和END,对文件的每行,awk都分两个阶段处理:

1、读取该行内容,分配临时寄存器,分配域名等操作;
2、对域做各种处理并输出;

  • awk基本用途

1、简单输出,如 awk ‘{print 1,NF}’ — print的规则
2、作为分隔符使用

单字符分隔符:打印系统中用户名和其他使用shell类型
单字符分隔符,管道连续使用awk:打印nginx日志中的访问目录。
多字符分隔符:抓取apache详细版本
多字符多个分隔符:截取ip地址
正则分隔符:截取ip地址

[root@admin test]# ls -l
total 16
-rw-r--r--. 1 root root    0 Jun 15 11:03 aa.jpg
-rw-r--r--. 1 root root   12 Jun 15 10:48 ls.jpg
-rw-r--r--. 1 root root  200 Jun 15 11:31 result.jpg
-rw-r--r--. 1 root root    0 Jun 15 11:04 right.jpg
drwxr-xr-x. 2 root root 4096 Jun 20 06:39 sd.ex
-rw-r--r--. 1 root root   49 Jun 15 11:04 wrong.jpg

通过awk打印出第一列:

[root@admin test]# ls -l | awk '{print $1}'
total
-rw-r--r--.
-rw-r--r--.
-rw-r--r--.
-rw-r--r--.
drwxr-xr-x.
-rw-r--r--.

打印前两列,也就是前两个域:

[root@admin test]# ls -l | awk '{print $1,$2}'
total 16
-rw-r--r--. 1
-rw-r--r--. 1
-rw-r--r--. 1
-rw-r--r--. 1
drwxr-xr-x. 2
-rw-r--r--. 1

在awk中,抛开BEGIN和END不看,’{}’ 这是一个固定写法。

$ : 符号表示域,域之间通过默认的分隔符 空格 分开,如果有多个空格就会变成一个空格,第一个域为$1,第二个为$2等…

打印最后一列:

[root@admin test]# ls -l | awk '{print $NF}'
16
aa.jpg
ls.jpg
result.jpg
right.jpg
sd.ex
wrong.jpg

$NF : 就代表最后一列的意思。
如果想打印倒数第二列,则使用$(NF -1)。

[root@admin test]# ls -l | awk '{print $(NF - 1)}'
total
11:03
10:48
11:31
11:04
06:39
11:04

-F :参数-F是改变awk的默认分隔符,可以支持正则表达式。

[root@admin test]# ls -l
total 16
-rw-r--r--. 1 root root    0 Jun 15 11:03 aa.jpg
-rw-r--r--. 1 root root   12 Jun 15 10:48 ls.jpg
-rw-r--r--. 1 root root  200 Jun 15 11:31 result.jpg
-rw-r--r--. 1 root root    0 Jun 15 11:04 right.jpg
drwxr-xr-x. 2 root root 4096 Jun 20 06:39 sd.ex
-rw-r--r--. 1 root root   49 Jun 15 11:04 wrong.jpg

awk默认的分隔符是 空格,空格前一列就是$1,后面是$2,$3…..
当我们改变默认分隔符为 分号 “:”时,打印一下结果:

[root@admin test]# ls -l | awk -F":" '{print $NF}'
total 16
03 aa.jpg
48 ls.jpg
31 result.jpg
04 right.jpg
39 sd.ex
04 wrong.jpg

通过-F 参数改变默认分隔符,以及其支持正则表达式的特性,精确的抽出ifconfig 文件中的ip地址。

#精确的抽取ip地址
[root@admin test]# ifconfig | grep "inet addr" | awk -F "addr:|  *" '{print $4}'
192.168.1.6
127.0.0.1

分析:

grep "inet addr" 表示先抽取出含有ip的那行,然后通过管道交给awk去处理

-F "addr:|  *" :表示该变默认分隔符为addr:或者连续多个空格

$4 :表示改变默认分隔符以后,ip地址在第4个域。

用grep实现抽取ip :

[root@admin home]# ifconfig | egrep -o "[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*"
192.168.1.6
192.168.1.255
255.255.255.0
127.0.0.1
255.0.0.0

然后在用head和tail抽取想要的ip即可。

使用sed实现抽取ip

[root@admin home]# ifconfig |grep "inet addr" | sed 's/inet addr://' | sed 's/Bcast.*//' | head -1
192.168.1.6
或者:
[root@admin home]# ifconfig |grep "inet addr" | sed 's/^.*addr://;s/  *.*//' | head -1
192.168.1.6

awk匹配打印

注:需要匹配的内容是写在{}外面的,通过//来体现匹配。
匹配打印实际上是文件每行读取的时候做的处理。

1、整行中匹配内容

awk '/sth/{print $1}' ---打印匹配sth的行的第一个域,这是对整行进行操作
awk '!/sth/{print $1}'  ---打印不匹配sth的行的第一个域

如匹配打印sd关键字:

[root@admin test]# ls
aa.jpg  ls.jpg  result.jpg  right.jpg  sd.ex  wrong.jpg

[root@admin test]# ls | awk '/sd/{print $1}'
sd.ex

2、域匹配内容

匹配某个文件中以默认分隔符分隔的$1域中含有关键字sth的行,然后打印出第一个域,命令如下:

awk '$1~/sth/{print $1}' 

3、改变awk中的默认分隔符

如打印出passwd文件中,以冒号 : 分隔的第一个域中含有关键字wcx行的第一个域:

[root@admin etc]# cat passwd | awk -F":" '$1~/wcx/{print $1}'
wcx

awk判断打印

判断打印实际上是文件每行读取后做的处理。
格式例如:

awk '{if($1=="wcx")print $1}'

if 判断必须是进入某一行以后才能做的,所以必须是在花括号{} 里面。而模式匹配是先匹配到内容的行才能进行下一步操作,所以模式匹配实在花括号{}外边。

[root@admin etc]# cat ./passwd | awk -F ":" '{if($1 == "wcx")print}
wcx:x:501:501::/home/wcx:/bin/bash

上面命令中,如果passwd文件中,以冒号分隔的第一个域==”wcx”,那么就打印出来,这是等于判断,所以只有wcx的行能出来。

BEGIN和END

在Unix awk中两个特别的表达式,BEGIN和END,这两者都可用于pattern中(参考前面的awk语法),提供BEGIN和END的作用是给程序赋予初始状态和在程序结束之后执行一些扫尾的工作。
任何在BEGIN之后列出的操作(在{}内)将在Unix awk开始扫描输入之前执行,而END之后列出的操作将在扫描完全部的输入之后执行。因此,通常使用BEGIN来显示变量和预置(初始化)变量,使用END来输出最终结果。

awk的数组

有一个awk.txt文件,其内容如下:

[root@admin home]# cat awk.txt
kk kekea
jj d32
jame 23
sr
sr
kk
jame
wcx
jame 

统计一下awk.txt文件中每个人名出现的次数:

[root@admin home]# cat awk.txt | awk '{a[$1]++}END{for (i in a)print i,a[i]}'
wcx 1
jj 1
kk 2
sr 2
jame 3

解题思路分析:

  • {a[$1]++} :awk开始扫描管道传过来的内容,但是么有存储功能,现在我们需要计算每个人名出现的额次数,所以需要把扫描到的相同的人名分别存储起来,这时候就必须用数组,并且这里的数组都是hash数组,就是键值对的数组,这里的键—对应的就是对应域中的人名,如kk,值 — 对应的就是kk这个人名出现的次数。数组a中的下标$1代表的是第一个域中列名,如wcx。awk开始逐行扫描,每扫到一个相同的人名,对应的hash数组里的值就+1,如a[kk]++,直到扫到文件的结尾。(注:数组的默认值为空,如果要做++操作了,就会把里的值变成0)

  • END : END之后列出的操作将在Unix awk开始扫描完全部的输入之后执行。上面扫描完以后,就开始执行END后面的操作了。

  • {for (i in a)print i,a[i]} :利用for循环去遍历刚刚产生的hash数组,然后打印出数组下标所代表的值,也就是人名以及对数组中的值,也就是对应人名出现的次数。这里的i,就代表数组的下标,也就是域中的人名,如kk。

awk输出分隔符

awk默认的输出分隔符也是空格,如果想改变默认输出分隔符怎么做?

[root@admin home]# cat awk.txt | awk '{print $1,$2}'
kk kekea
jj d32
jame 23
sr 234
sr edr
kk 23
jame rt
wcx 88
jame 34

运行结果看出,打印时,$1和$2间加了逗号以后,输出的文本中域间就都是用空格隔开的。

如果我不加逗号,改为加一串别的字符时,输出后就以加入的字符作为分隔符了。如:

[root@admin home]# cat awk.txt | awk '{print $1"--"$2"BB"}'
kk--kekeaBB
jj--d32BB
jame--23BB
sr--234BB
sr--edrBB
kk--23BB
jame--rtBB
wcx--88BB
jame--34BB

通过awk批量修改扩展名

[root@admin test]# ls
aa.jpg  ls.jpg  result.jpg  right.jpg  wrong.jpg

将test目录下的以.jpg结尾的文件改成.txt结尾的文件:

[root@admin test]# ls | awk -F "." '{print "mv "$1"."$2" "$1".txt"}'|sh
[root@admin test]# ls
aa.txt  ls.txt  result.txt  right.txt  wrong.txt

或者:
[root@admin test]# ls | awk -F "." '{print "mv "$0" "$1".txt"}' | sh
#$0表示整行的内容

思路分析,同样改名也是需要构造mv a.txt a.jpg这样类似的语句的,这就用到了上面讲的输出时修改默认分隔符的操作了。构造出的mv语句再传给sh去执行就可以实现改名的操作。

awk内置变量使用介绍

未分类

我们将逐渐揭开 awk 功能的神秘面纱,在本节中,我们将介绍 awk 内置built-in变量的概念。你可以在 awk 中使用两种类型的变量,它们是:用户自定义user-defined变量和内置变量。awk 内置变量已经有预先定义的值了,但我们也可以谨慎地修改这些值.

awk 内置变量包括

  • FILENAME : 当前输入文件名称
  • NR : 当前输入行编号(是指输入行 1,2,3……等)
  • NF : 当前输入行的字段编号
  • OFS : 输出字段分隔符
  • FS : 输入字段分隔符
  • ORS : 输出记录分隔符
  • RS : 输入记录分隔符

FILENAME

让我们继续演示一些使用上述 awk 内置变量的方法,想要读取当前输入文件的名称,你可以使用 FILENAME 内置变量,如下:

$ awk ' { print FILENAME } ' ~/domains.txt

未分类

你会看到,每一行都会对应输出一次文件名,那是你使用 FILENAME 内置变量时 awk 默认的行为。我们可以使用 NR 来统计一个输入文件的行数(记录),谨记:它也会计算空行,正如我们将要在下面的例子中看到的那样。 输出文件内容 当我们使用 cat 命令查看文件 domains.txt 时,会发现它有 14 行文本和 2 个空行:

$ cat ~/domains.txt

未分类

awk 统计行数

$ awk ' END { print "Number of records in file is: ", NR } ' ~/domains.txt

未分类

awk 统计文件中的字段数

$ awk '{ "Record:",NR,"has",NF,"fields" ; }' ~/names.txt

未分类

FS 内置变量

你也可以使用 FS 内置变量指定一个输入文件分隔符,它会定义 awk 如何将输入行划分成字段。FS 默认值为“空格”和“制表符”,但我们也能将 FS 值修改为任何字符来让 awk 根据情况切分输入行。有两种方法可以达到目的:第一种方法是使用 FS 内置变量;第二种方法是使用 awk 的 -F 选项。来看 Linux 系统上的 /etc/passwd 文件,该文件中的各字段是使用 冒号(:) 分隔的,因此,当我们想要过滤出某些字段时,可以将冒号(:) 指定为新的输入字段分隔符, awk 过滤密码文件中的各字段 . 我们可以使用 -F 选项,如下:

$ awk -F':' '{ print $1, $4 ;}' /etc/passwd

未分类

此外,我们也可以利用 FS 内置变量,如下:

$ awk ' BEGIN { FS=“:” ; } { print $1, $4 ; } ' /etc/passwd

未分类

使用 OFS 内置变量

使用 OFS 内置变量来指定一个用于输出的字段分隔符,它会定义如何使用指定的字符分隔输出字 段 使用 awk输出的分隔符:

$ awk -F':' ' BEGIN { OFS="==>" ;} { print $1, $4 ;}' /etc/passwd

未分类

在本节中,我们已经学习了使用含有预定义值的 awk 内置变量的理念。但我们也能够修改这些值,虽然并不推荐这样做,除非你明白自己在做什么,并且充分理解(这些变量值)。

Ubuntu下设置APT的http代理

在公司的网络中,系统有时候需要设置了网络代理之后,才能正常的访问外网,那么下面将会介绍如何在ubuntu linux系统中设置网络代理。

1、编辑/etc/apt/apt.conf 配置文件(如果/etc/apt/目录下没有apt.conf文件,那么需要手动创建)

2、按照下面的格式,将网络代理配置信息加入到apt.conf文件里。

Acquire::http::proxy “http://user:passwd@proxyserver:port”;

例如: Acquire::http::Proxy “http://192.168.0.1:80“;

3、保存退出当前配置文件

4、运行 sudo apt-get update 命令,来检测ubuntu系统是否能够正常更新。

ubuntutest@ubuntutest:~$ sudo apt-get update
[sudo] password for ubuntutest:
命中:1 http://archive.ubuntu.com/ubuntu xenial InRelease
获取:2 http://security.ubuntu.com/ubuntu xenial-security InRelease [102 kB]
获取:3 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB]
获取:4 http://archive.ubuntu.com/ubuntu xenial-backports InRelease [102 kB]
已下载 306 kB,耗时 1秒 (250 kB/s)
正在读取软件包列表... 完成

CentOS 7.0安装LAMP服务器(PHP+MariaDB+Apache)

1、关闭firewall:

systemctl stop firew
alld.service #停止firewall
systemctl disable firewalld.service #禁止firewall开机启动

2、安装iptables防火墙(#可不安装)

yum install iptables-services #安装
vi /etc/sysconfig/iptables #编辑防火墙配置文件
//配置文件:
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
//:wq! #保存退出

关闭 SELINUX

vi /etc/selinux/config
#SELINUX=enforcing #注释掉
#SELINUXTYPE=targeted #注释掉
SELINUX=disabled #增加
:wq! #保存退出
setenforce 0 #使配置立即生效

一、Apache安装

yum install httpd #根据提示,输入Y安装即可成功安装
systemctl start httpd.service #启动apache
systemctl stop httpd.service #停止apache
systemctl restart httpd.service #重启apache
systemctl enable httpd.service #设置apache开机启动

二、安装MariaDB

yum install mariadb mariadb-server 
//#询问是否要安装,输入Y即可自动安装,直到安装完成
systemctl start mariadb.service #启动MariaDB
systemctl stop mariadb.service #停止MariaDB
systemctl restart mariadb.service #重启MariaDB
systemctl enable mariadb.service #设置开机启动
cp /usr/share/mysql/my-huge.cnf /etc/my.cnf 
//拷贝配置文件(注意:如果/etc目录下面默认有一个my.cnf,直接覆盖即可)

设置密码

mysql_secure_installation
systemctl restart mariadb.service

三、安装PHP

//主程序
yum install php
//安装模块
yum install php-mysql php-gd libjpeg* php-ldap php-odbc php-pear php-xml php-xmlrpc php-mbstring php-bcmath php-mhash
systemctl restart mariadb.service #重启MariaDB
systemctl restart httpd.service #重启apache

四、安装phpMyAdmin

//主程序
sudo yum install phpmyadmin php-mcrypt
//修改配置文件
vi /etc/httpd/conf.d/phpMyAdmin.conf 

<Directory /usr/share/phpMyAdmin/>
  AddDefaultCharset UTF-8

  <IfModule mod_authz_core.c>
   # Apache 2.4
   <RequireAny>
    #Require ip 127.0.0.1
    #Require ip ::1
    Require all granted
   </RequireAny>
  </IfModule>
  <IfModule !mod_authz_core.c>
   Order Deny,Allow
   Deny from All
   Allow from 127.0.0.1
   Allow from ::1
  </IfModule>
</Directory>

<Directory /usr/share/phpMyAdmin/setup/>
  <IfModule mod_authz_core.c>
   # Apache 2.4
   <RequireAny>
    #Require ip 127.0.0.1
    #Require ip ::1
    Require all granted
   </RequireAny>
  </IfModule>
  <IfModule !mod_authz_core.c>
   Order Deny,Allow
   Deny from All
   Allow from 127.0.0.1
   Allow from ::1
  </IfModule>
</Directory>

systemctl restart httpd #重启httpd

Apache如何安装配置证书?

格式说明

首先 Apache 使用的证书是 .pem 格式的。什么是 .pem 格式,就是以 —–BEGIN xxx—– 开头的文件,如:

-----BEGIN CERTIFICATE-----
MIID6jCCAtKgAwIBAgIBFDANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJDTjER  
MA8GA1UECAwIU2hhbmdoYWkxDzANBgNVBAoMBmRlZXB6ejEPMA0GA1UECwwGZGVl  
cHp6MRMwEQYDVQQDDApkZWVwenouY29tMSIwIAYJKoZIhvcNAQkBFhNkZWVwenou  
... ...
-----END CERTIFICATE-----

当然现在 openssl 新版签发的证书,在 —–BEGIN xxx—– 之前还有一段信息,我们这里就不举例了。

所以,当我门从证书服务商那里下载证书的时候需要注意证书的格式问题。选择 Apache 进行下载。

一般的,证书服务商会提供三个文件:

example.com.ca.crt  
example.com.crt  
example.com.key  
  • example.com.ca.crt,CA 中间证书。
  • example.com.crt,你的服务器证书文件。
  • example.com.key,你的私钥。

还有可能是如下文件:

example.com.crt  
example.com.key  
  • example.com.key 你的私钥文。
  • example.com.crt 是你的证书文件或证书链文件。用文本编辑器打开,如果看到两张上面 pem 内容,即站点证书+CA中间证书。你需要将它们分别存为两个文件,如 example.com.ca.crt 和 example.com.crt。如果不是,请联系证书服务商或到 这里 补全证书链。

安装证书

首先,确保 ssl 模块已经加载进 apache 了:

$ a2enmod ssl

如果你看到了 Module ssl already enabled 这样的信息就说明你成功了。如果你看到了 Enabling module ssl,那么你还需要用下面的命令重启apache:

$ service apache2 restart

最后像下面这样修改你的虚拟主机文件(通常在/etc/apache2/sites-enabled 下):

DocumentRoot /var/www/html/  
ServerName example.com  
SSLEngine on  
SSLCertificateFile /usr/local/ssl/crt/example.com.crt  
SSLCertificateKeyFile /usr/local/ssl/example.com.key  
SSLCACertificateFile /usr/local/ssl/example.com.ca.crt  

你现在应该可以用 https://example.com(注意使用 https 而不是 http)来访问你的网站了,并可以看到SSL的进度条了(通常在你浏览器中用一把锁来表示)。

使用zabbix监控apache性能

原理

监控原理跟之前写的监控nginx差不多,都是利用web服务器自身提供的状态信息页获取运行状态信息。apache的监控状态信息如下:

Total Accesses: 252523

Total kBytes: 2154830

CPULoad: 2.72004

Uptime: 16624

ReqPerSec: 15.1903

BytesPerSec: 132733

BytesPerReq: 8738

BusyWorkers: 1

IdleWorkers: 9

Scoreboard: 

我们一般只需要这四个数据:ReqPerSec、BytesPerSec、BusyWorkers、IdleWorkers

开启apache status

创建状态页配置文件/etc/httpd/conf.d/status.conf

Listen 89
<VirtualHost *:89>
    CustomLog /dev/null common
    ErrorLog /dev/null
    <Location "/server-status">
        SetHandler server-status
        Require ip 192.168.7.227
    </Location>
</VirtualHost>

CustomLog 和 ErrorLog在这里的作用是将日志写入/dev/null,即关闭咋apache状态页面日志记录。但是CustomLog和ErrorLog指令不能放到Location里,所以就新建一个VirtualHost。

访问http://192.168.7.227:89/server-status?auto即可得到上面的状态信息。我这里测试用的就是zabbix-server安装时自带的apache,所以限定可以访问的IP和apache服务器的IP是同一个。

创建zabbix模板

/etc/zabbix/zabbix_agentd.d/apache_status.sh

#!/bin/bash

URL="http://192.168.7.227:89/server-status?auto"

function ReqPerSec(){
/usr/bin/curl -s $URL |grep ReqPerSec|awk '{print $2}'
}

function BytesPerSec(){
/usr/bin/curl -s $URL |grep BytesPerSec|awk '{print $2}'
}

function BusyWorkers(){
/usr/bin/curl -s $URL |grep BusyWorkers|awk '{print $2}'
}

function IdleWorkers(){
/usr/bin/curl -s $URL |grep IdleWorkers|awk '{print $2}'
}

function ping(){
/usr/sbin/pidof httpd|wc -l
}

#根据脚本参数执行对应函数
$1

/etc/zabbix/zabbix_agentd.d/apache_status.conf
Bash

UserParameter=apache.status[*],/etc/zabbix/zabbix_agentd.d/apache_status.sh $1

重启zabbix-agent后就可以创建apache监控模板了。

items配置都和nginx监控大同小异,特别看下这个带单位的:单位我这里写的Bps,Bytes每秒的意思,但在zabbix中,它会自动帮你换算成KBps:

未分类

并不是说zabbix很聪明知道你这个单位什么意思,它只是简单的除以1000然后加上一个大写“K”在前面而已,加入你的单位是”obe”,那么就显示成了Kobe,哈哈^_^

未分类

再添加3个Graphs,模板就算做完了:

未分类

最后附上版本文件,适用于zabbix-3.2.7-1:https://github.com/dmli30/shell/blob/master/zabbix/apache_status_templates.xml