服务端工具

Linux 下的内核参数调优与 sysctl

最近在配置 influxdb 的 udp 端口的时候看到文档[1]里面提到:强烈建议修改 Linux 的默认网络 buffer 大小,否则会对性能有严重影响。感觉挺有意思,深入研究了一下 Linux 下 TCP 和 UDP 的一些参数配置。

influxdb 的推荐配置

首先查看这两个配置:

sysctl net.core.rmem_max  # 读取 buffer 最大值
sysctl net.core.rmem_default  # 读取 buffer 默认值

然后更新这两个配置为 25 MiB

sysctl -w net.core.rmem_max=26214400
sysctl -w net.core.rmem_default=26214400

最后写入到配置文件,以便重启之后依然生效

# /etc/sysctl.conf
net.core.rmem_max=26214400
net.core.rmem_default=26214400

这里的设置其实就是增大网络 buffer 的大小,避免收到的 udp 包过多的时候造成丢包或者阻塞的现象。

sysctl 命令

在上面的命令中,我们使用了 sysctl。sysctl 的 man page 也很简洁:sysctl – configure kernel parameters at runtime。也就是说在运行时调整内核参数。

sysctl -a 显示当前的所有参数
sysctl -w xxx=xxx 设定内核参数
sysctl xxx 显示内核参数的值
sysctl -fFILE 加载给定文件的值,默认是 /etc/sysctl.conf 也就是我们刚刚更改的文件

我们可以看下阿里云上默认的 ubuntu 机器的 /etc/sysctl.conf 文件:

tiger@iZ2ze0z0xdiqgv15k20evmZ [01:53:09 AM] [~]
-> % cat /etc/sysctl.conf
vm.swappiness = 0
net.ipv4.neigh.default.gc_stale_time=120

# see details in https://help.aliyun.com/knowledge_detail/39428.html
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce=2
net.ipv4.conf.all.arp_announce=2

# see details in https://help.aliyun.com/knowledge_detail/41334.html
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2

kernel.sysrq=1

sysctl 可以控制的配置大概分成了以下几类:

  • 内核子系统 (通常前缀为: kernel.)
  • 网络子系统 (通常前缀为: net.)
  • 虚拟内存子系统 (通常前缀为: vm.)
  • MDADM 子系统 (通常前缀为: dev.)

对应的参数除了使用 sysctl 读取以外,还可以直接通过文件系统读取:

cat /proc/sys/net/core/

甚至也可以写入来直接更新内核参数,这样证实了 Linux 下一切皆文件的设计理念。

在 docker 中如何设置 sysctl

我们知道在 docker 中,容器适合宿主机共享同一个内核的,那么显然容器是不能随便动宿主机的配置的。所以 /proc/sys 目录在容器中默认是只读挂载的。

要想在容器运行的时候更改内核参数,需要在 docker run 的时候添加参数:

  1. --cap-add=SYS_ADMIN
  2. --privileged
  3. --sysctl

以上三个参数任选其一,应该就可以了

在 kubernetes 中如何设置 sysctl

在 docker 中设置还好说,其实就是把默认的只读属性去掉就好了。在 kubernetes 的 pod 中要想设置 sysctl 就比较复杂了,因为你不知道 pod 究竟会调度到哪台机器上。在这里我没有深入研究了,毕竟 influxdb 这种有状态的服务还没有迁到 kubernetes 上,看了下官方文档[4],大概思路是:

  1. 给对应机器的 kubelet 指定参数,然后使用 taint 调度到这台机器上
  2. 对于一些安全的属性,可以直接在 pod 中使用 securityContext 设置
apiVersion: v1
kind: Pod
metadata:
  name: sysctl-example
spec:
  securityContext:
    sysctls:
    - name: kernel.shm_rmid_forced
      value: "0"
    - name: net.core.somaxconn
      value: "1024"
    - name: kernel.msgmax
      value: "65536"
  ...

参考资料

  1. https://docs.influxdata.com/influxdb/v1.7/supported_protocols/udp/
  2. https://stackoverflow.com/questions/54845095/cannot-run-sysctl-command-in-dockerfile
  3. https://www.kernel.org/doc/Documentation/sysctl/README
  4. https://kubernetes.io/zh/docs/tasks/administer-cluster/sysctl-cluster/

使用 systemd 部署守护进程

YN:如何使安装的服务开机启动?是更改 wantedby 吗?如果是,wantedby 的值应该是什么? 对于 nginx 这样的 daemon 服务如何管理?

大多数的 Linux 系统已经选择了 systemd 来作为进程管理器。之前打算使用 supervisord 来部署服务,思考之后发现还不如直接使用 systemd 呢。这篇文章简单介绍下 systemd。 # 例子

我们从一个例子开始,比如说我们有如下的 go 程序:

package main

import (
    fmt
    net/http
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, Hi there!)
}

func main() {
    http.HandleFunc(/, handler)
    http.ListenAndServe(:8181, nil)
}

编译到 /opt/listen/listen 这里。 首先我们添加一个用户,用来运行我们的服务:

adduser -r -M -s /bin/false www-data

记下这条命令,如果需要添加用户来运行服务,可以使用这条。

Unit 文件

Unit 文件定义了一个 systemd 服务。/usr/lib/systemd/system/ 存放了系统安装的软件的 unit 文件,/etc/systemd/system/ 存放了系统自带的服务的 unit 文件。 我们编辑 /etc/systemd/system/listen.service 文件:

[Unit]
Description=Listen

[Service]
User=www-data
Group=www-data
Restart=on-failure
ExecStart=/opt/listen/listen
WorkingDirectory=/opt/listen

Environment=VAR1=whatever VAR2=something else
EnvironmentFile=/path/to/file/with/variables

[Install]
WantedBy=multi-user.target

然后

sudo systemctl enable listen
sudo systemctl status listen
sudo systemctl start listen

其他一些常用的操作还包括:

systemctl start/stop/restart    
systemctl reload/reload-or-restart  
systemctl enable/disable    
systemctl status    
systemctl is-active 
systemctl is-enabled
systemctl is-failed
systemctl list-units [--all] [--state=…]    
systemctl list-unit-files
systemctl daemon-reload 
systemctl cat [unit-name]   
systemctl edit [uni-name]
systemctl list-dependencies [unit]

依赖管理

In that case add Requires=B and After=B to the [Unit] section of A. If the dependency is optional, add Wants=B and After=B instead. Note that Wants= and Requires= do not imply After=, meaning that if After= is not specified, the two units will be started in parallel. if you service depends on another service, use requires= + after= or wants= + after=

编辑

类型

Type: simple / forking 关于每个字段的含义,可以参考这篇文章

编辑

使用 journalctl 查看日志

首先吐槽一下, 为什么要使用 journal 这么一个拗口的单词, 叫做 logctl 不好么… “`

journalctl -u service-name.service
```

还可以添加 -b 仅查看本次重启之后的日志. 

 编辑

# 启动多个实例

[https://unix.stackexchange.com/questions/288236/have-systemd-spawn-n-processes](https://unix.stackexchange.com/questions/288236/have-systemd-spawn-n-processes "https://unix.stackexchange.com/questions/288236/have-systemd-spawn-n-processes") [http://0pointer.de/blog/projects/instances.html](http://0pointer.de/blog/projects/instances.html "http://0pointer.de/blog/projects/instances.html")

strace

尽管在线下会做充足的测试,但是线上出问题是难免的。当我们的程序在线上运行中遇到问题的时候,而我们又没有日志可以观察到底哪里出了问题,这时候可以使用 strace 命令。strace 可以直接根据 pid 附着到进程上,打印出进程的一些统计信息,为排查 bug 提供有意义的参考。

Strace 的选项

  1. c – See what time is spend and where (combine with -S for sorting)
  2. f – Track process including forked child processes
  3. o my-process-trace.txt – Log strace output to a file
  4. p 1234 – Track a process by PID
  5. P /tmp – Track a process when interacting with a path
  6. T – Display syscall duration in the output

Track by specific system call group

  1. e trace=ipc – Track communication between processes (IPC)
  2. e trace=memory – Track memory syscalls
  3. e trace=network – Track memory syscalls
  4. e trace=process – Track process calls (like fork, exec)
  5. e trace=signal – Track process signal handling (like HUP, exit)
  6. e trace=file – Track file related syscalls

Trace multiple syscalls

strace -e open,close

统计进程系统调用花费时间

strace -c -f -p 11084

一般来说要加上 -f 选项,这样才能跟踪多进程程序,也就是 fork 之后的进程。

strace -o output.txt -T -tt -e trace=all -p 28979

使用和这个命令可以统计每一个系统调用的时间

  1. 使用 struss 查看系统问题 https://www.ibm.com/developerworks/cn/linux/l-tsl/index.html
  2. strace 的详细用法 https://blog.csdn.net/uisoul/article/details/83143290
  3. https://blog.csdn.net/budong282712018/article/details/83151953
  4. https://blog.csdn.net/maple_leaves_for_me/article/details/42391979
  5. https://blog.51cto.com/5iwww/771031
  6. https://blog.csdn.net/lotluck/article/details/77943152
  7. https://linux-audit.com/the-ultimate-strace-cheat-sheet/

使用 caddy 运行 php

使用 caddy 运行 PHP

caddyfile

example.com {
    gzip
    root /srv
    fastcgi / 127.0.0.1:9000 php # php variant only
    on startup php-fpm7 # php variant only
}

example.com 记得改成自己的域名

docker-compose.yml

version: "3"

services:
  caddy:
    image: abiosoft/caddy:php
    environment:
      ACME_AGREE: 1
    volumes:
      - "/etc/Caddyfile:/etc/Caddyfile"
      - "/etc/caddy:/root/.caddy"
      - "/var/www/html:/srv"
    ports:
      - "80:80"
      - "443:443"
      - "2015:2015"

参考

  1. http://blog.extlux.com/2018/08/06/docker-%E8%BF%90%E8%A1%8C%E5%B8%A6php%E7%9A%84caddy/

systemd

YN:如何使安装的服务开机启动?是更改 wantedby 吗?如果是,wantedby 的值应该是什么? 对于 nginx 这样的 daemon 服务如何管理?

大多数的 Linux 系统已经选择了 systemd 来作为进程管理器。之前打算使用 supervisord 来部署服务,思考之后发现还不如直接使用 systemd 呢。这篇文章简单介绍下 systemd。 # 例子

我们从一个例子开始,比如说我们有如下的 go 程序:

<pre class="code">package main

import (
    fmt
    net/http
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, Hi there!)
}

func main() {
    http.HandleFunc(/, handler)
    http.ListenAndServe(:8181, nil)
}

编译到 /opt/listen/listen 这里。 首先我们添加一个用户,用来运行我们的服务:

<pre class="code">adduser -r -M -s /bin/false www-data

记下这条命令,如果需要添加用户来运行服务,可以使用这条。

Unit 文件

Unit 文件定义了一个 systemd 服务。/usr/lib/systemd/system/ 存放了系统安装的软件的 unit 文件,/etc/systemd/system/ 存放了系统自带的服务的 unit 文件。 我们编辑 /etc/systemd/system/listen.service 文件:

<pre class="code">[Unit]
Description=Listen

[Service]
User=www-data
Group=www-data
Restart=on-failure
ExecStart=/opt/listen/listen
WorkingDirectory=/opt/listen

Environment=VAR1=whatever VAR2=something else
EnvironmentFile=/path/to/file/with/variables

[Install]
WantedBy=multi-user.target

然后

<pre class="code">sudo systemctl enable listen
sudo systemctl status listen
sudo systemctl start listen

其他一些常用的操作还包括:

<pre class="code">systemctl start/stop/restart    
systemctl reload/reload-or-restart  
systemctl enable/disable    
systemctl status    
systemctl is-active 
systemctl is-enabled
systemctl is-failed
systemctl list-units [--all] [--state=…]    
systemctl list-unit-files
systemctl daemon-reload 
systemctl cat [unit-name]   
systemctl edit [uni-name]
systemctl list-dependencies [unit]

依赖管理

In that case add Requires=B and After=B to the [Unit] section of A. If the dependency is optional, add Wants=B and After=B instead. Note that Wants= and Requires= do not imply After=, meaning that if After= is not specified, the two units will be started in parallel. if you service depends on another service, use requires= + after= or wants= + after=

类型

Type: simple / forking 关于每个字段的含义,可以参考这篇文章

使用 journalctl 查看日志

首先吐槽一下, 为什么要使用 journal 这么一个拗口的单词, 叫做 logctl 不好么…

<pre class="code">journalctl -u service-name.service

还可以添加 -b 仅查看本次重启之后的日志.

启动多个实例

https://unix.stackexchange.com/questions/288236/have-systemd-spawn-n-processeshttp://0pointer.de/blog/projects/instances.html