关注小众语言,记录、分享技术点滴!

0%

最近在工作中遇到一个问题,尝试直接将服务运行在高配(40core, 192GB;相比虚拟机来说) 的物理机上,但是发现服务打开的文件句柄达到 80 万左右就不能再开更多了。

80 万已经是一个不小的值了,通常情况下,Linux 默认的值都很小,例如:Debian 8(jessie) 给普通用户设置的 open file(s) 限制为 65536, 可以通过下面的命令查看当前限制。

1
2
3
$ ulimit -n
$ ulimit -Sn
$ ulimit -Hn

ulimit 是一个 shell(这里使用的是 bash) 内置命令,可以通过 type ulimit 验证。

-n 即表示查看或者设置 open file(s) 的限制,在 ulimit 中,每个限制都有两种类型:

  • -S, soft limit, 软限制,用户可以上调软限制到硬限制
  • -H, hard limit, 硬限制,非 root 用户不能修改

如果没有指明,则同时修改软限制和硬限制。

修改 ulimit

修改分为临时修改和永久修改,临时修改只对当前 session 有效,登出和重启后都恢复系统设置。

临时修改使用 ulimit 命令,以修改 open file(s) 为例。

1
2
3
$ ulimit -n 1024000
$ ulimit -n
1024000

永久修改需要修改 /etc/security/limits.conf 或者在 /etc/security/limits.d/ 目录下添加一个文件。具体格式参考 /etc/security/limits.conf,里面有详细说明。

open file(s) 上限

回到遇到的问题中来:服务打开 80 万个左右的文件句柄就不能再打开了。所以, 尝试将 ulimit 设置为 1000 万,结果提示出错:

1
2
$ ulimit -n 10000000
-bash: ulimit: open files: cannot modify limit: Operation not permitted

注意,使用的可以 root 用户,居然没有权限,然后尝试降低到:

  • 500 万,依然错误
  • 300 万,依然错误
  • 200 万,依然错误
  • 100 万,成功了

显然,这里有一个上限,大概在 100-200 万之间。

所以,解决问题的办法,在于怎样提高这个上限!

通过一番搜索,发现 open file(s) kernel 级别有 2 个配置,分别是:

fs.nr_open,进程级别
fs.file-max,系统级别

fs.nr_open 默认设置的上限是 1048576,所以用户的 open file(s) 不可能超过这个上限。

1
2
3
4
$ sysctl -w fs.nr_open=10000000
$ ulimit -n 10000000
$ ulimit -n
10000000

修改后即可设置更大的 open file(s) 了。

同样,对于 kernel 参数的修改,sysctl 命令修改的是当前运行时,如果需要永久修改, 则将配置添加到 /etc/sysctl.conf 中,例如:

1
2
$ echo "fs.nr_open = 10000000" >> /etc/sysctl.conf
$ echo "fs.file-max = 11000000" >> /etc/sysctl.conf

注意:fs.nr_open 总是应该小于等于 fs.file-max

如果要查看当前打开的文件数,使用下面的命令:

1
2
$ sysctl fs.file-nr
$ fs.file-nr = 1760 0 11000000

不过,增大这些值意味着能够打开更多的文件(在 Linux 中,everything is file,包括 socket),但是同时也意味着消耗更多的资源,所以基本上在物理机上才会遇到这种问题。

一、Go get命令出现terminal prompts disabled解决

原因go get disable “terminal prompt” by default(Go get 命令默认禁用terminal prompt,即终端提示)

解决方案

设置环境变量:GIT_TERMINAL_PROMPT=1, 可以把上面的命令写到 .bashrc.bash_profile 文件当中。

二、一键解决 go get golang.org/x 包失败

当我们使用 go getgo installgo mod 等命令时,会自动下载相应的包或依赖包。但由于众所周知的原因,类似于 golang.org/x/... 的包会出现下载失败的情况。如下所示:

1
2
3
$ go get -u golang.org/x/sys  

go get golang.org/x/sys: unrecognized import path "golang.org/x/sys" (https fetch: Get https://golang.org/x/sys?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)

我们可以通过设置GOPROXY 环境变量来解决,我们知道从 Go 1.11 版本开始,官方支持了 go module 包依赖管理工具。其实还新增了 GOPROXY 环境变量。

解决方案

设置环境变量:GOPROXY=https://goproxy.io, 可以把上面的命令写到 .bashrc.bash_profile 文件当中。

三、总结

解决 terminal prompts disabled

export GIT_TERMINAL_PROMPT=1

解决 go get golang.org/x 包失败

export GOPROXY=https://goproxy.io

启用 Go Modules 功能

export GO111MODULE=on

报错信息:

1
fatal: unable to access 'https://github.com/***/***': SSL connect error

解决办法:

1
git config --global http.sslversion tlsv1

或者

1
git config --global http.sslVerify false   #设置跳过SSL证书验证

如果还不行,可能是ssh版本过低需要升级ssh。执行命令:

1
yum update nss     #redhat  centos

GraphicsMagick 是 ImageMagick 的另一个分支功能和 ImageMagick 类似。下面我们介绍一下在PHP7下如何安装扩展。

一、安装 GraphicsMagick 依赖

1
yum install GraphicsMagick-devel

二、选择手动方式安装gmagick(php扩展)
我们这里选择最新稳定版(gmagick-2.0.5RC1)

1
2
3
4
5
6
wget https://pecl.php.net/get/gmagick-2.0.5RC1.tgz  
tar -zxvf gmagick-2.0.5RC1.tgz
cd gmagick-2.0.5RC1
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install

三、选择pecl方式安装gmagick(php扩展)

1
pecl install gmagick

四、修改php.ini 增加 extension,同时reload php-fpm 完成。

ps:
安装过程中可能出现以下问题
linux ‘….’:is not a valid libtool object”错误
解决方法

1
make clean

然后在重新执行命令,原因是编译的时候有问题重新编译一下文件就解决了

错误描述:

1
2
3
4
5
6
yum install libmcrypt libmcrypt-devel

Setting up Install Process
No package libmcrypt available.
No package libmcrypt-devel available.
Error: Nothing to do

我们会看到centos yum从仓库中根本找不到这几个包。但我不想使用源码编译就想使用yum安装,怎么办?
解决方法:

1
2
yum install epel-release  #扩展包更新包
yum update #更新yum源

然后再重新

1
yum install libmcrypt libmcrypt-devel

把某些目录或文件加入.gitignore规则,发现并未生效,原因是.gitignore只能忽略那些原来没有被追踪的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。那么解决方法就是先把本地缓存删除(改变成未被追踪状态),然后再提交:

1
2
3
git rm -r --cached .
git add .
git commit -m 'update .gitignore'

一、环境版本
1、CentOS 7.4
2、OpenVPN 2.4.6
3、easy-rsa 3.0

二、安装

1
2
3
4
yum install epel-release
yum install -y openssl openssl-devel lzo lzo-devel pam pam-devel automake pkgconfig makecache
yum install -y openvpn
yum install -y easy-rsa

三、添加openvpn配置用户

#启动openvpn的用户

1
2
groupadd openvpn
useradd -g openvpn -M -s /sbin/nologin openvpn

四、创建配置文件

1
2
3
4
mkdir /etc/openvpn/
cp -r /usr/share/easy-rsa/ /etc/openvpn/
cp -r /usr/share/doc/easy-rsa-3.0.3/vars.example /etc/openvpn/easy-rsa/3.0/vars
cp -r /usr/share/doc/openvpn-2.4.6/sample/sample-config-files/server.conf /etc/openvpn/server.conf

五、修改easy-rsa密钥生成配置(修改第45、65、76、84-89、97、105、113、117、134、139、171、180、192行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
vim /etc/openvpn/easy-rsa/3.0/vars

set_var EASYRSA "$PWD"
set_var EASYRSA_PKI "$EASYRSA/pki"
set_var EASYRSA_DN "cn_only"
set_var EASYRSA_REQ_COUNTRY "CN"
set_var EASYRSA_REQ_PROVINCE "BEIJING"
set_var EASYRSA_REQ_CITY "BEIJING"
set_var EASYRSA_REQ_ORG "OpenVPN CERTIFICATE AUTHORITY"
set_var EASYRSA_REQ_EMAIL "110@qq.com"
set_var EASYRSA_REQ_OU "OpenVPN EASY CA"
set_var EASYRSA_KEY_SIZE 2048
set_var EASYRSA_ALGO rsa
set_var EASYRSA_CA_EXPIRE 7000
set_var EASYRSA_CERT_EXPIRE 3650
set_var EASYRSA_NS_SUPPORT "no"
set_var EASYRSA_NS_COMMENT "OpenVPN CERTIFICATE AUTHORITY"
set_var EASYRSA_EXT_DIR "$EASYRSA/x509-types"
set_var EASYRSA_SSL_CONF "$EASYRSA/openssl-1.0.cnf"
set_var EASYRSA_DIGEST "sha256"

六、生成ca证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cd /etc/openvpn/easy-rsa/3.0
./easyrsa init-pki
./easyrsa build-ca
#设置ca密码(输入两次):

#生成Diffie Hellman key exchange文件
./easyrsa gen-dh

#生成tls-auth key文件
cd /etc/openvpn
openvpn --genkey --secret ta.key

#使用gen-req来生成req
cd /etc/openvpn/easy-rsa/3.0
./easyrsa gen-req wwwserver

#签发服务端证书
./easyrsa sign-req server wwwserver

#生成客户端用户
./easyrsa build-client-full www001

七、修改openvpn服务端配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
vim /etc/openvpn/server.conf

port 1194
proto udp
dev tun
ca /etc/openvpn/easy-rsa/3.0/pki/ca.crt
cert /etc/openvpn/easy-rsa/3.0/pki/issued/wwwserver.crt
key /etc/openvpn/easy-rsa/3.0/pki/private/wwwserver.key
dh /etc/openvpn/easy-rsa/3.0.3/pki/dh.pem
tls-auth /etc/openvpn/ta.key 0
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 223.5.5.5"
push "dhcp-option DNS 114.114.114.114"
keepalive 10 120
cipher AES-256-CBC
comp-lzo
max-clients 50
user openvpn
group openvpn
persist-key
persist-tun
status openvpn-status.log
log-append openvpn.log
verb 3
mute 20

八、服务器防火墙和路由配置

在iptables中添加一条路由转发规则, 并保存:

1
2
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -j MASQUERADE
iptables-save

同时, 我们必须在sysctl中启用ip forward.
打开文件vi /etc/sysctl.conf, 并添加以下内容:

1
net.ipv4.ip_forward = 1

然后重启网络:

1
systemctl restart network.service

九、启动服务器

1
2
systemctl start openvpn@server
#启动时输入服务端证书密码:

十、客户端配置(下载客户端证书)

1
2
3
4
5
mkdir -p /etc/openvpn/client
cp -r /etc/openvpn/easy-rsa/3.0/pki/issued/www001.crt /etc/openvpn/client/
cp -r /etc/openvpn/easy-rsa/3.0/pki/private/www001.key /etc/openvpn/client/
cp -r /etc/openvpn/easy-rsa/3.0/pki/ca.crt /etc/openvpn/client/
cp -r /etc/openvpn/ta.key /etc/openvpn/client/

十一、修改客户端ovpn文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
client
dev tun
proto udp
remote xxx.xxx.xxx.xxx 1194 #改成外网IP
resolv-retry infinite
nobind
persist-key
persist-tun
comp-lzo
ca ca.crt
cert www001.crt
key www001.key
remote-cert-tls server
tls-auth ta.key 1
cipher AES-256-CBC
keepalive 10 120
verb 5

ps
1、如果是阿里云的专用网络,需要配置放行1194端口udp
2、如生成证书时输错密码(删除以下文件即可)

1
2
rm -rf /etc/openvpn/easy-rsa/3.0/pki/reqs/www001.req
rm -rf /etc/openvpn/easy-rsa/3.0/pki/private/www001.key

3、撤销证书

1
2
cd /etc/openvpn/easy-rsa/3.0
./easyrsa revoke www001

4、解决服务器每次都要输入Enter PEM pass phrase

1
openssl rsa -in server.key -out server.key.unsecure

服务器改用这个server.key.unsecure就不会每次提示了

ImageMagick是一个用于查看、编辑位图文件以及进行图像格式转换的开放源代码软件套装。ImageMagick官方提供了多种开发语言的扩展或者类库。下面我们介绍一下在PHP7下如何安装扩展。

一、安装ImageMagick依赖

1
yum install ImageMagick-devel

二、选择手动方式安装imagick(php扩展)
我们这里选择最新稳定版(imagick-3.4.0)

1
2
3
4
5
6
wget https://pecl.php.net/get/imagick-3.4.0.tgz
tar -zxvf imagick-3.4.0.tgz
cd imagick-3.4.0
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install

三、选择pecl方式安装imagick(php扩展)

1
pecl install imagick

四、修改php.ini 增加 extension,同时reload php-fpm 完成。

ps:
安装过程中可能出现以下问题
linux ‘….’:is not a valid libtool object”错误
解决方法

1
make clean

然后在重新执行命令,原因是编译的时候有问题重新编译一下文件就解决了

准备工作

一、系统要求
Docker CE 支持 64 位版本 CentOS 7,并且要求内核版本不低于 3.10。 CentOS 7 满足最低内核的要求,但由于内核版本比较低,部分功能(如 overlay2 存储层驱动)无法使用,并且部分功能可能不太稳定。

二、卸载旧版本
旧版本的 Docker 称为 docker 或者 docker-engine,使用以下命令卸载旧版本:

1
2
3
4
5
6
7
8
9
10
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

三、使用 yum 安装
执行以下命令安装依赖包:

1
2
3
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2

鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
执行下面的命令添加 yum 软件源:

1
2
3
4
5
6
7
8
yum-config-manager \
--add-repo \
https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo

# 官方源
$ yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo

如果需要最新版本的 Docker CE 请使用以下命令:

1
yum-config-manager --enable docker-ce

如果需要测试版本的 Docker CE 请使用以下命令:

1
yum-config-manager --enable docker-ce-test

安装 Docker CE
更新 yum 软件源缓存,并安装 docker-ce。

1
2
yum makecache fast
yum install docker-ce

或者

使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS 系统上可以使用这套脚本安装:

1
2
curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh --mirror Aliyun

执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker CE 的 Edge 版本安装在系统中。

启动 Docker CE

1
2
systemctl enable docker
systemctl start docker

建立 docker 用户组(如果使用root用户操作 可忽略)
默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。

建立 docker 组:

1
groupadd docker

将当前用户加入 docker 组:

1
usermod -aG docker $USER

退出当前终端并重新登录,进行如下测试。

测试 Docker 是否安装正确

1
docker version

添加内核参数
默认配置下,如果在 CentOS 使用 Docker CE 看到下面的这些警告信息:

WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

请添加内核配置参数以启用这些功能。

1
2
3
4
tee -a /etc/sysctl.conf <<-EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

然后重新加载 sysctl.conf 即可

1
sysctl -p

由于我们的部分业务场景是在微信(WebView)中打开https的web页面,在测试过程中却发现了一个问题,在Chrome中测试完全正常的https页面,在iOS的微信(WebView)中表现正常,但在Android中,不论是哪个版本的安卓系统,都不能正常打开页面,要么就是一片白,要么就是直接无法打开,解决这个问题,需要在服务器上配置完整的SSL证书链。
之后在Android自带的浏览器中测试,几乎所有的手机都出现下面这样的情况

证书链
看来Andorid的WebView不能打开页面应该是与这有关,造成这个问题的主要原因是我们服务器配置证书的证书链不全造成的。

证书链其实就是描述证书的签名环节,就比如是 A 颁发证书给 B ,B颁发证书给C,然后我们手里的就是证书C。当证书链不完整的情况下,也就是没有描述我们手中的证书C是由谁办法的,所以导致的浏览器不认为你这个证书是可信的授权证书。

每个设备中都会存有一些默认的可信的根证书,但很多CA是不使用根证书进行签名的,而是使用中间层证书进行签名,因为这样做能更快的进行替换(这句可能不对,原文是 because these can be rotated more frequently)。

如果你的服务器上没有中间件证书,这样的结果就是你的服务器上只有你的网站的证书,客户端的浏览器里只有CA的根证书,这样就会导致证书信任链不全,才导致了上面那两个截图中的问题。这种中间层证书不全的问题多出现在移动端的浏览器上(就我目前看,iOS8 iOS9 都没有出现问题,Andorid各个版本都有这个问题)。

当你服务器上的证书中的信任链不全的情况下,浏览器会认为当前的链接是一个不安全的,会阻止页面的打开。

解决方案
说清楚了原因,解决问题就很简单了,只要把我们的证书链补全就可以了。
从SSL证书服务商那里,你获得的crt证书文件大概是这个样子的:

—–BEGIN CERTIFICATE—–
# 证书内容
—–END CERTIFICATE—–

在你补全中间层证书和根证书后,新的crt证书文件看起来是这样的:

—–BEGIN CERTIFICATE—–
# 证书内容 1
—–END CERTIFICATE—–

—–BEGIN CERTIFICATE—–
# 证书内容 2
—–END CERTIFICATE—–

—–BEGIN CERTIFICATE—–
# 证书内容 3
—–END CERTIFICATE—–

这里包含了你的证书,以及从你的证书向上递归直至根证书的全部证书,这样就可以向浏览器证明你的链接是安全的。

补全证书链
比较方便的是使用这个在线的工具:
https://certificatechain.io
进入这个网站,粘贴进你的证书(只包含你的用户证书),或者上传你的证书,他就会给出补全后的证书文件,你只需要粘贴回你的文件或者下载覆盖就可以了,然后在服务器上重新部署就可以解决问题。
由于这里只需要上传证书,所以是安全的,不需要担心不安全的问题。
如果不喜欢用在线的工具,可以使用下面这个本地的工具,PHP写的,在命令行中运行:
Github ssl-certificate-chain-resolver

PS:
一般情况下
cert.pem 是证书
chain.pem 是证书链编码
fullchain.pem 是cert.pem和chain.pem的证书集合
privkey.pem 是私钥文件