如何退出无响应的 SSH 连接

经常出现ssh连接由于网络问题,“卡住”了。不能进行任何操作,也不能退出。 ssh文档里面也有写通过~.来断开ssh连接:

$ man ssh

ESCAPE CHARACTERS

 The supported escapes (assuming the default \`~') are:

 ~.      Disconnect.

 ~?      Display a list of escape characters.

但是,实际使用过程中发现,按了没有反应。 后来看了其他人的做法,发现两个技巧:

  1. ssh转义字符,只有在新行生效。所以最保险的按键方式是:回车 -> Shift+` -> .
  2. 由于输入两个~会导致ssh直接发送~。比如我们按~``~``~``. 会关闭第二层ssh,而不是当前失去响应的ssh会话。

所以为了保险起见,我的操作是这样子的: 回车``~``?,直到出现ssh的转义字符帮助(没有出现就继续按回车``~``? 然后按 ~``. 退出ssh


~``~``~``. 会关闭第二层ssh,对吗?

  1. ~``~是给第一层ssh会话发送~字符
  2. ~``~``~是给第二层ssh会话发送~字符
  3. ~``~``~``.是给第二层ssh会话发送~``.字符,即关闭第二层ssh会话

阅读更多

[译]UUID和Linux:你需要知道的一切

本文为 UUIDs and Linux: Everything you ever need to know Update 的翻译。

UUID在Linux中为何如此特别?我[指原作者,下同]不知道!但是这儿有你需要知道的、关于UUID在Linux下的一切!

背景

UUID是128位长的数字,表示为32位的16进制数字,被用于在软件开发中、在没有上下文的情况下唯一地标识信息。RFC 4122描述了UUID的规范,一个UUID的例子是:

13152fae-d25a-4d78-b318-74397eb08184

UUID在Linux最熟悉的场景应该是块设备的标识符了吧。Windows世界中的UUID以Microsoft的全局唯一标识符GUID的形式出现,这些标识符在Microsoft的组件对象模型[COM]中使用。

UUID以各种变体生成:最初大多数是从计算机的MAC派生的,随后使用了基于name的散列值。关于这个问题,比如有多少UUID,有多大概率会生成一个已有的UUID,这是来自维基百科上 UUID 的文章的一些数字:

在100年里,每秒生成十亿个UUID,生成重复的UUID的概率约为50%。如果每个人都有6亿个UUID,下一个UUID重复的概率约为50%。

fstab中的用法

就像之前提到的,UUID在Linux下被经常用于标识块设备。想象一下,你有两个硬盘通过USB连接上,它们不是持久存储,只能依赖两个设备的名字来区分:有时第一个USB硬盘是“sda”,有时它是“sdb”。所以要唯一寻址正确的磁盘,比如在 /etc/fstab 中,你必须添加如下条目:

阅读更多

通过DNS认证来部署Let's Encrypt

原来是通过http认证的方式来完成ACME 的 Identifier Validation Challenges,但是内网的机器就无法完成这个认证,今天看了下,LE支持dns认证了,所以实践了一下。 首先安装Certbot。 然后执行

certbot -d ssl-test.robberphex.com –manual –preferred-challenges dns certonly

对于MacOS用户来说,可以执行certbot –config-dir /usr/local/etc/letsencrypt –logs-dir /usr/local/var/log/letsencrypt –work-dir /usr/local/var/lib/letsencrypt -d ssl-test.robberphex.com –manual –preferred-challenges dns certonly

  • 需要输入邮箱
  • 同意用户协议
  • 同意记录IP
  • 设置域名的TXT记录 比如图中,设置_acme-challenge.ssl-test.robberphex.com的TXT记录为x-P6A_dQ4_ggZtPvX_bOUeaY7hSM_IS6o-Gzj3h7LBw,然后回车。
  • 提示证书生成成功

我们来一个最简版的配置:

server {
listen 8443 ssl;
server_name ssl-test.robberphex.com;

ssl\_certificate /usr/local/etc/letsencrypt/live/ssl-test.robberphex.com/fullchain.pem;
ssl\_certificate\_key /usr/local/etc/letsencrypt/live/ssl-test.robberphex.com/privkey.pem;

location / {
    default\_type "text/plain";
    return 200 "pong";
}

}

curl测试(不需要设置DNS):

$ curl -i –resolve ssl-test.robberphex.com:8443:127.0.0.1 https://ssl-test.robberphex.com:8443/
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Wed, 14 Dec 2016 07:33:41 GMT
Content-Type: text/plain
Content-Length: 4
Connection: keep-alive

pong

浏览器测试(需要设置DNS或者hosts文件): [caption id=”attachment_588” align=”alignnone” width=”1166”] 证书有效[/caption]

阅读更多

使用repox搭建sbt/maven镜像

最近sbt的速度实在是不能忍受了,所以使用repox搭建了sbt镜像。 搭建过程没有什么好说的,直接sbt assembly,然后

java -Xmx512m -jar target/scala-2.11/repox-assembly-0.1-SNAPSHOT.jar

就好了。


但是发现了两个repox的问题:

  1. 下载文件时,服务器全部下载完后,才能传输给sbt(不支持nginx那种“流式代理”),这在下载大文件时尤其明显。
  2. 有的时候,pom文件总是404 比如curl https://repo1.maven.org/maven2/org/w3c/css/sac/1.3/sac-1.3.pom -I的结果是200,curl http://106.75.27.110:8078/org/w3c/css/sac/1.3/sac-1.3.pom -I的结果就是404,重试好几次都不行;重启repox就好了。
阅读更多

Flume在hdfs产生大量日志文件的问题

线上两台flume向hdfs写日志,但是后来发现每5分钟产生的日志文件数量很多(远远超过两个的数量)。 后来找了一个时间观察下日志,发现如下日志:

16/12/13 11:38:11 INFO hdfs.BucketWriter: Closing idle bucketWriter hdfs://xxx/xx/xxx_log_20161213/.xxx_log.1481600173693.tmp at 1481600291480

根据日志找代码,发现是BucketWriter在配置了hdfs.idleTimeout的情况下,会在超时后关闭不活跃的日志文件flume官方文档对此参数的描述如下:

Timeout after which inactive files get closed (0 = disable automatic closing of idle files)

找到原因后,当然就很简单了,由于此参数默认是0,直接删除这个参数的相关配置就好了。每5分钟产生的日志数量就是两个了。

阅读更多

How to install/upgrade elasticsearch cluster via ansible?

First, we have a playbook:

es.yml
1
2
3
4
5
- hosts: es
become: yes
roles:
- elasticsearch
serial: "50%"

serial: "50%" means ansbile will run tasks in 50% of hosts (or less).

And, we should add a rule elasticsearch:

roles/elasticsearch/tasks/main.yml
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
---
- name: disable shard allocation for the cluster
uri: url=http://localhost:9200/_cluster/settings method=PUT body='{{ es_allocation.disable }}' body_format=json
  ignore_errors: yes

- name: download ealsticsearch deb pacakge
local_action:
module: get_url
url: "{{ elasticsearch_url }}"
dest: "cache/elasticsearch-{{ elasticsearch_version }}.deb"
checksum: "sha256:{{ elasticsearch_sha256 }}"
become: no

- name: check if elasticsearch is installed
command: dpkg-query --showformat='${Version}' -W elasticsearch
register: elasticsearch_version_check
failed_when: elasticsearch_version_check.rc > 1
changed_when: elasticsearch_version_check.rc == 1

- block:
- name: copy elasticsearch package
copy: src="cache/elasticsearch-{{ elasticsearch_version }}.deb" dest=/tmp/elasticsearch.deb
- name: install elasticsearch
apt: deb=/tmp/elasticsearch.deb
always:
- name: delete elasticsearch package
file: path=/tmp/elasticsearch.deb state=absent
when: elasticsearch_version_check.stdout != "{{ elasticsearch_version }}"

# configure elasticsearch
# install plugins

- name: restart elasticsearch
service: name=elasticsearch state=restarted

- name: wait for elasticsearch node to come back up
wait_for: port=9200 delay=15

- name: enable shard allocation for the cluster
uri: url=http://localhost:9200/_cluster/settings method=PUT body='{{ es_allocation.enable }}' body_format=json

- name: wait for cluster health to return to green
uri: url=http://localhost:9200/_cluster/health method=GET
register: response
until: "response.json.status == 'green'"
retries: 50
delay: 5
  1. we try to disable allocation for cluster upgrade
  2. download elasticsearch’s deb package to local cache
  3. install and configure, restart elasticsearch
  4. wait 9200 port is available, enable allocation, wait for cluster become green.

some vars:

roles/elasticsearch/vars/main.yml
1
2
3
4
5
6
7
8
---
elasticsearch_version: 2.3.2
elasticsearch_url: "https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/{{ elasticsearch_version }}/elasticsearch-{{ elasticsearch_version }}.deb"
elasticsearch_sha256: 3d474b0123ec8ad4ebfa089f8cde607033e6cbef28a6a0df318bdc3d2a546cd8

es_allocation:
disable: '{"transient":{"cluster.routing.allocation.enable":"none"}}'
enable: '{"transient":{"cluster.routing.allocation.enable": "all"}}'
阅读更多

APT Hash sum mismatch

工作当中遇到此问题,然后偶尔看见此文。 此文是 APT Hash sum mismatch 的翻译。

TL;DR

APT 仓库能够提供未压缩和压缩过的文件格式。常用的几个的压缩格式是 gzip, bzip 和lzma。 apt 的 Bug 在处理lzma文件(.xz)读写的时候会偶尔报告“Hash sum mismatch”错误。 本文提供一种变通方式,另外,在特定版本的 apt 中,该bug已经被修复。 使用如下系统的用户应该升级到系统提供的最新的 apt 版本:

  • Ubuntu Trusty (14.04) 及更新的版本
  • Debian Jessie (8) 及更新的版本

更早系统的用户应该升级系统或者使用如下的变通方法。 目前,我们决定关闭 packagecloud 的所有仓库中对 lzma 格式的支持。元数据仍然能够以 gzip, bzip 以及未压缩的格式来获取。packagecloud 仓库的所有者们并不需要做任何事情。

Hash sum mismatch 变通方法

用户可以通过两种方法强制 apt 不使用 lzma 格式的元数据。 第一种办法就是运行 apt-get 的时候添加额外的命令行参数:

apt-get update -o Acquire::CompressionTypes::Order::=gz

(注意:如果你偏向于使用 bzipped 格式,你也可以指定参数为“bz2”) 或者,你可以在全局的 apt 设置中指定这个选项,这样你就不必每次运行 apt 的时候输入这些。 步骤如下:

  1. 创建 /etc/apt/apt.conf.d/99compression-workaround 文件
  2. 添加这些文本到文件中: Acquire::CompressionTypes::Order:: "gz";
阅读更多

Let’s Encrypt/HTTPS配置记录

前几天收到Let’s Encrypt public beta的邮件,所以就尝试了下ssl+https+http/2。

Let’s Encrypt 配置

首先,我之前遇到过Error creating new authz 的错误,就一直没有尝试,后来发现v2ex上有人通过设置NS记录解决了问题(虽然文中说要设置MX记录,但是我只更换了NS记录到CloudFlare),生效时间可能是24h。 不过由于cf会友好地将所有流量切换到它的反代服务器,所以需要关闭此特性;另外,也需要关闭cf中HTTPS的开关,由自己的主机来处理HTTPS请求(所以我仅仅是将cf作为一个DNS服务器而已)。 另外有一个坑:由于我用的是nginx,letsencrypt这个客户端程序需要自己接受80端口的请求来验证服务器身份,所以需要临时关闭nginx。 最后开始生成服务器证书: 第一步:将letsencrypt项目clone到特定目录下:

git clone https://github.com/letsencrypt/letsencrypt.git /opt/letsencrypt

第二步:cd到该目录,然后执行如下命令生成证书:

./letsencrypt-auto –agree-dev-preview –server https://acme-v01.api.letsencrypt.org/directory certonly

依次填写邮箱、域名后,会在 /etc/letsencrypt/live// 目录下生成证书(nginx只用到fullchain.pem和privkey.pem) 注:此命令一定要添加–server选项,否则生成的证书并不被浏览器信任。 证书都已经生成,接下来配置nginx。

nginx配置

注:如果要开HTTP/2的话,就必须要添加nginx mainline的repo了:

[nginx]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

阅读更多

SSH 重用连接

有的时候需要经常 ssh 连接到服务器上,比如,我push一个分支,然后合并并push,然后push tag,然后删除该分支。期间至少需要四次SSH连接(不要问我为什么会有这么复杂的流程!) 另外,之前通过跳板机直连线上机器的方法,在差的网络环境中仍然很慢,也在考虑如何加速的问题。 综上,每次SSH连接的建立,需要首先建立TCP连接,然后认证之。有的时候网络慢,搞得这个过程很不爽,故有此文。 ssh_config的文档中有如下ControlMaster和ControlPath两个参数。ControlMaster表示是否需要多个SSH会话重用一个网络连接,这个当然要开启;ControlPath表示这个网络连接绑定到的本地sock地址。 我的配置如下:

ControlMaster auto
ControlPath ~/.ssh/%[email protected]%h:%p

以time ssh remote_host -C pwd命令为例,在默认配置下,一般执行时间是1s;如果开启了ControlMaster,首次连接的时间是1.2s(事实上,我也不知道为什么会稍微多一点);如果重用了之前的连接,则时间降为0.6s。 Tips: ssh -O check reomte_host可以看到共享的连接是不是还在;ssh -O exit reomte_host可以直接关闭后台共享的连接(会关闭现有的ssh会话);ssh  -O stop remote_host则表示后台共享的连接不会再接受新的会话(具体而言,不监听sock文件,下次的连接仍然会创建同样的sock文件)。 另外ControlPersist表示连接失效时间,建议设置之。

阅读更多

如何直接通过跳板机ssh到服务器

公司线上服务器都无法直接访问,必须通过一台跳板机来访问。比如要访问机器dev,则必须先ssh到跳板机gateway,然后再ssh到dev机器。

这样做自然可以减少攻击面,但是每次去dev机器执行命令,或者上传文件的时候,都要两次ssh,确实比较麻烦。

故Google之,可以使用ssh config中的ProxyCommand选项,比如我们先定义主机gateway的连接参数:

1
2
3
Host gateway
HostName gw.example.com
User XXX

然后我们需要定义主机dev的连接参数:

1
2
3
4
Host dev
HostName dev.ip.example.com
User XXX
ProxyCommand ssh -q -W %h:%p gateway

这样,ssh dev 命令就会先和gateway建立ssh连接,并把这个中间连接当作一个代理使用。不过需要注意的是,你的公钥除了必须在gateway上有之外,还必须在dev上有,这是和之前不一样的地方。

PS:之前考虑过ProxyCommand ssh gateway nc %h %p ,但是考虑到机器上可能没有nc命令。后来发现ssh有-W参数,果断用之。

_PPS:ProxyCommand参数中的-q是为了防止和跳板机的ssh连接产生多余的输出,比如不加-q就会导致每次断开连接的时候会多一句Killed by signal 1. _

如果这样做了,我们就可以玩一些高级的用法了,比如:

阅读更多