Archive for the ‘Linux’ category

理解 Linux backlog/somaxconn 内核参数

November 21st, 2019

引言

之前线上TcpExt.ListenOverflows,然后通过ss -tlnp 查看Send-Q偏小导致后端服务器 Socket accept 队列满,系统的 somaxconn 内核参数默认太小。

TCP SYN_REVD, ESTABELLISHED 状态对应的队列

TCP 建立连接时要经过 3 次握手,在客户端向服务器发起连接时,
对于服务器而言,一个完整的连接建立过程,服务器会经历 2 种 TCP 状态:SYN_REVD, ESTABELLISHED。

对应也会维护两个队列:
1. 一个存放 SYN 的队列(半连接队列)
2. 一个存放已经完成连接的队列(全连接队列)

当一个连接的状态是 SYN RECEIVED 时,它会被放在 SYN 队列中。
当它的状态变为 ESTABLISHED 时,它会被转移到另一个队列。
所以后端的应用程序只从已完成的连接的队列中获取请求。

如果一个服务器要处理大量网络连接,且并发性比较高,那么这两个队列长度就非常重要了。
因为,即使服务器的硬件配置非常高,服务器端程序性能很好,
但是这两个队列非常小,那么经常会出现客户端连接不上的现象,
因为这两个队列一旦满了后,很容易丢包,或者连接被复位。
所以,如果服务器并发访问量非常高,那么这两个队列的设置就非常重要了。

Linux backlog 参数意义

对于 Linux 而言,基本上任意语言实现的通信框架或服务器程序在构造 socket server 时,都提供了 backlog 这个参数,
因为在监听端口时,都会调用系统底层 API: int listen(int sockfd, int backlog);

listen 函数中 backlog 参数的定义如下:

Now it specifies the queue length for completely established sockets waiting to be accepted,
instead of the number of incomplete connection requests.
The maximum length of the queue for incomplete sockets can be set using the tcp_max_syn_backlog sysctl.
When syncookies are enabled there is no logical maximum length and this sysctl setting is ignored.
If the socket is of type AF_INET, and the backlog argument is greater than the constant SOMAXCONN(128 default),
it is silently truncated to SOMAXCONN.

backlog 参数描述的是服务器端 TCP ESTABELLISHED 状态对应的全连接队列长度。

全连接队列长度如何计算?
如果 backlog 大于内核参数 net.core.somaxconn,则以 net.core.somaxconn 为准,
即全连接队列长度 = min(backlog, 内核参数 net.core.somaxconn),net.core.somaxconn 默认为 128。
这个很好理解,net.core.somaxconn 定义了系统级别的全连接队列最大长度,
backlog 只是应用层传入的参数,不可能超过内核参数,所以 backlog 必须小于等于 net.core.somaxconn。

半连接队列长度如何计算?
半连接队列长度由内核参数 tcp_max_syn_backlog 决定,
当使用 SYN Cookie 时(就是内核参数 net.ipv4.tcp_syncookies = 1),这个参数无效,
半连接队列的最大长度为 backlog、内核参数 net.core.somaxconn、内核参数 tcp_max_syn_backlog 的最小值。
即半连接队列长度 = min(backlog, 内核参数 net.core.somaxconn,内核参数 tcp_max_syn_backlog)。
这个公式实际上规定半连接队列长度不能超过全连接队列长度。

其实,对于 Nginx/Tomcat 等这种 Web 服务器,都提供了 backlog 参数设置入口,
当然它们都会有默认值,通常这个默认值都不会太大(包括内核默认的半连接队列和全连接队列长度)。
如果应用并发访问非常高,只增大应用层 backlog 是没有意义的,因为可能内核参数关于连接队列设置的都很小,
一定要综合应用层 backlog 和内核参数一起看,通过公式很容易调整出正确的设置。

 

docker 常用操作

November 4th, 2019

sudo docker ps

sudo docker images

sudo docker run –name mysql -p 3306:3306 -v ~/mysql/data:/var/lib/mysql -e MYSQL_DATABASE=spider -e MYSQL_USER=admin -e MYSQL_PASSWORD=admin -e MYSQL_ROOT_PASSWORD=admin -d mysql

sudo docker run –name redis -p 6379:6379 -d redis

sudo docker run –name mongo -p 27017:27017 -v ~/mongo/db:/data/db -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=admin -d mongo

sudo docker exec -it mysql bash

sudo docker exec -it redis bash

 

docker run -it –name openjdk -d lzz5235/openjdk-8u202 /bin/bash 这样可以保证容器不被杀死

docker exec -it [name] /bin/bash 进入终端

 

 

 

Docker 编排工具简述

March 28th, 2017

编排是一个新的词汇,经过阅读才明白编排指的是容器的集群化和调度。另一类含义指的是容器管理,负责管理容器化应用和组件任务。

典型的编排工具有:Docker swarm mode、Kuberbetes和Mesosphere DCOS,这三个工具都提供相同的特性,但同时三个工具所处于的地位又不尽相同。

  1. 这些工具在容器集群中提供或者调度容器,还可以启动容器。会根据需求,例如资源和部署位置,在最佳VM中启动容器。

       2. 脚本保证你把指定的配置加载到容器中。

       3. 容器管理工具跟踪和监控容器的健康,将容器维持在集群中。正常情况下,监视工具会在容器崩溃时启动一个新实例。如果服务器故障,工具会在另一台服务器上重启容器。这些工具还会运行系统健康检查、报告容器不规律行为以及VM或服务器的不正常情况。

       4.需要部署新版本的容器或者升级容器中应用时,容器管理工具自动在集群中更新你的容器或应用。如果出现问题,它们允许你回滚到正确配置的版本。

       5.容器使用服务发现来找到它们的资源。

       6.你希望容器运行在哪里?你希望每个容器分配多少CPU?所有这些需求都可以通过设置正确的容器部署策略实现。

        7.容器要能够和已有的IT管理工具兼容。

=================

Docker Swarm mode 和 Docker Swarm 

这是两款独立的编排工具,Docker Swarm是一种较旧的独立产品,曾经用于管理Docker集群。而Swarm mode是Docker内置的集群管理器。Docker 1.12后,Swarm mode引入Docker ,成为Docker Engine的一部分。

Docker swarm mode 支持滚动更新、节点间传输层安全加密、负载均衡和简单的服务抽象。可以在多个主机之间传播容器负载,它允许你在多个主机平台上设置swarm(即群集)。

Kubernetes

Kubernetes最初由有谷歌开发的开源容器管理工具。这个工具提供高度的互操作性,以及自我修复、自动升级回滚以及存储编排。目前负载均衡做的还不是很好,但是我们仍然需要在Kubernets基础上加入监控日志系统。

Marathon

Marathon是为Mesosphere DC/OS和Apache Mesos设计的容器编排平台。Marathon有很多特性,包括高可用、服务发现、负载均衡。

综上所述Mesos和Kubernetes主要用于运行集群应用程序。Mesos专注于通用调度,以插件的方式提供多个不同的调度器。而Kubernetes者用来构建容器的分布式环境。对于可以结合三个工具的优缺点,来构建容器云达到更好的管理效果。

 

https://docs.docker.com/engine/swarm/

https://kubernetes.io/

https://github.com/mesosphere/marathon

docker 初体验

February 21st, 2017

Docker话说已经活了好几年了,业界开始逐步从热议到逐步落地使用,之前硕士搞过一年的LXC,虽然Docker这个技术新瓶装旧酒,但是由于加入了特有的hub机制,使其易于传播,易用性比LXC更具优势。

今天抽空使用了一下docker,准备将自己的 VPS 容器化,方便以后在不同主机中迁移。

Docker由三部分组成:一个运行docker命令的client, 一个包含images并以容器(container)形式运行image的主机,一个docker的images仓库。client与docker host上面的docker daemon通信。当然docker client和host可以运行于一台机器(我们做实验的时候是一台),默认的docker仓库是Docker Hub。

docker使用流程就是client pull 从hub上把image拉到docker host,然后通过run命令指挥image到host上面弄一个container来跑这个image。

或者就是client 通过build命令在host上面创建一个自己的image,然后通过push命令把image推到仓库。然后别人就可以使用自己构建的镜像。

刚开始使用对image容易搞混乱,按照我的理解:image就是一个文件系统镜像,用户无法直接修改这个镜像,类似于一种二进制文件形式(其实是一种特殊的文件系统)里面运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。

我们使用docker run就可以将这个image运行起来,运行起来的image就是container。运行起来的container我们可以对其进行各种修改,每产生一个修改,就产生一个commition ID。后一个构建依赖前一个构建,因此和git版本管理很像!

image和container之间的关系类似程序与进程之间的关系!

Docker命令实例

  1. docker pull的格式是:
docker pull[选项] [Docker Registry地址] <仓库名>:<标签名>

2.docker images命令下载的images:

$docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu ml 14.04 b969ab9f929b 1 weeksago 210 MB

3.最最重要的就是docker run

$docker run -p 8080:80 -p 111:22 -i -t -name [docker-name] -d COMMAND

其中的-p指的是端口映射,-d 设置该容器以daemon模式运行,具体实例:

$ sudo docker run -i -t ubuntu:14.04 /bin/bash

docker run – 运行一个容器
-t – 分配一个(伪)tty (link is external)
-i – 交互模式 (so we can interact with it)
ubuntu:14.04 – 使用 ubuntu 基础镜像 14.04
/bin/bash – 运行命令 bash shell

4. docker start 其实就是docker run的缩写,创建好container之后就不用再输入很长的命令,而是由一个docker start代替。

$docker start [docker-name]

5. docker ps 可以显示出运行的所有容器

$ docker ps
CONTAINER ID       IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
e3a913872698       ubuntu:14.04       "bash"              11seconds ago      Up 10 seconds                           wizardly_elion
db1c25753e97       ubuntu:14.04       "bash"              21seconds ago      Up 21 seconds                           adoring_shannon

这几个命令在使用中使用频率最高。

Dockerfile是构建image的文件,类似于脚本命令。

# ubuntu 14.04 with vim and gcc
FROM ubuntu:14.04
MAINTAINER lzz<ldm5235@gmail.com>
RUN apt-get update && apt-getinstall –y vim gcc

其中FROM指定构建镜像的基础源镜像,如果本地没有指定的镜像,则会自动从 Docker 的公共库 pull 镜像下来。
FROM必须是 Dockerfile 中非注释行的第一个指令,即一个 Dockerfile 从FROM语句开始。FROM可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像。如果FROM语句没有指定镜像标签,则默认使用latest标签。
MAINTAINER <name> 指定创建镜像的用户

RUN 有两种使用方式
RUN “executable”, “param1”, “param2”
每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像,后续的RUN都在之前RUN提交后的镜像为基础,镜像是分层的,可以通过一个镜像的任何一个历史提交点来创建,类似源码的版本控制。

exec 方式会被解析为一个 JSON 数组,所以必须使用双引号而不是单引号。exec 方式不会调用一个命令 shell,所以也就不会继承相应的变量,如:

RUN [ “echo”, “$HOME” ]
这种方式是不会达到输出 HOME 变量的,正确的方式应该是这样的

RUN [ “sh”, “-c”, “echo”, “$HOME” ]
RUN产生的缓存在下一次构建的时候是不会失效的,会被重用,可以使用–no-cache选项,即docker build –no-cache,如此便不会缓存。

 

http://www.cnblogs.com/hustcat/p/3980244.html

http://blog.csdn.net/u011537073/article/details/52719363

SSH 使用 — 如何从外网连接内网计算机

June 11th, 2016

SSH是我们经常使用的工具,但是因为现在组网方式非常复杂,导致跨越NAT非常难因应对,同一局域网下的SSH使用,我这里就不再赘述。我们来聊聊如何从外网连接内网的ssh-server。

首先我们可以选择偷懒的方式,设置主机别名,这样就不用每次都输入ssh user@remote -p port 了,我们可以在~/.ssh/config 里面追加以下内容:

Host lab
   HostName xxx.xxx.xxx.xxx
   User user
   Port port

保存之后,即可用 ssh lab 登入,如果还配置了公钥登入,那就连密码都不用输入了。

================

两台主机之间传输数据可以使用scp命令,它与ssh -p port 很类似,稍微的差别在与指定端口时用的是大写的 -P 而不是小写的。有了这些基础知识,下面我们聊聊如何从外网连接内网的ssh-server。为了实现这个功能,我们必须拥有一台公网主机,现在国内的vps非常便宜,阿里云或者腾讯云都可以。

假设现在我有一台处在公网的机器 bridge,这台机器拥有公网IP 。你在实验室也有一台机子lab,这台机子只能在实验室内部访问,但他可以访问公网,你希望能在任何地方都能访问这台机器。那么使用 ssh -R 可以轻松地做到这个事情。

lab$ ssh -R 10022:localhost:22 bridge
bridge$ ssh user@localhost -p 10022
lab$

只要上面这个过程成功了,就说明在你执行 ssh -R 10022:localhost:22 bridge(ssh -R 远端:本地 bridge,如果远端不添加0.0.0.0 就意味着只能bridge自己访问这个lab,如果添加了0.0.0.0就意味着可以其他机器可以通过bridge访问到)之后,你成功地将本地上的 22 端口反向转发到了 bridge 的 10022 端口。只要保持这个 ssh 不断,任何一台机器都可以首先连接到 bridge,然后通过 ssh user@localhost -p 10022 连回到 lab。

不过上面这么做并不稳健,如果因为网络波动导致 ssh -R 那个连接断了,那么从 bridge 就完全失去了到 lab 的控制。万幸的是,有一个叫做 autossh 的软件,可以自动的检测断线,并在断线的时候重连。在 Ubuntu 上你可以使用 sudo apt-get install autossh 来安装。

lab$ autossh -NfR 10022:localhost:22 bridge

上面这句话里面 -N 表示非不执行命令,只做端口转发;-f 表示在后台运行,-R 10022:localhost:22 就是把本地的22端口转发到远程的10022端口。我们也可以设置为开机时运行:在 /etc/rc.local 里面 exit 0 这句话之前加上

su - user -c autossh -NfR 10022:localhost:22 bridge

其中 user 是用户名。需要注意的是,开机时运行 autossh 需要配置公钥登入。

临时创建网站供查看

比如我在本地跑了一个网站,我想临时把我的网站发给朋友看看。在本地运行 python -m SimpleHTTPServer 即可在本地的8000端口启动一个网站,你可以在浏览器中通过 http://localhost:8000/ 看到。下面我们想让远方的朋友看到这个网站。

local$ ssh -NR 0.0.0.0:18000:localhost:8000 bridge

远方的朋友即可通过 http://bridge:18000/ 看到了。注意到这里和上面的命令有一个小小的不同,就是多了 0.0.0.0,这告诉 ssh,要把18000端口绑定在远端的所有IP上。如果像之前那样省略的话,缺省值是只绑定在 localhost,也就是只有在 jumpbox 本机才可以访问,而其他人都不能访问。

临时代理创建

比方说在本地的127.0.0.1:1080运行了HTTP代理服务,现在我想让另一台机子 remote 也能够使用这个HTTP代理。

local$ ssh -NR 11080:localhost:1080 remote
local$ ssh remote
remote$ export http_proxy=http://127.0.0.1:11080/
remote$ export https_proxy=http://127.0.0.1:11080/
remote$ curl http://ifconfig.co

用作 SOCKS5 代理

要是想要在家访问公司内网的一些网站,但是公司又没有提供进入内网的VPN,那怎么办呢?通过 ssh -D 可以在本地建立起一个 SOCKS5 代理:local$ ssh -ND 1080 workplace

 

【1】 ssh manual