清理mac上Docker占用的磁盘空间

Docker依赖Linux系统的cgroup实现,在mac系统中运行的时候,Docker会启动一个虚拟机中的Linux内核,并在硬盘上放一个qcow2格式的磁盘镜像文件。这个文件会随着Docker的使用不断膨胀,即使删除不用的Docker Image和Container也不会缩小。我在测试完一个自动化工具的Dockerfile改写之后,Docker.qcow2文件就达到42GB了。

Google搜了一圏发现一个叫Théo Chamley的法国人写了一个脚本释放Docker.qcow2文件占用的空间。基本原理是用docker save命令保存要保留的image,然后关闭Docker,删除Docker.qcow2,再启动Docker,它会自动重建,最后用docker load命令恢复保留的image。

下面是这哥儿们写的脚本。

#!/bin/bash

# Copyright 2017 Théo Chamley
# Permission is hereby granted, free of charge, to any person obtaining a copy of 
# this software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
# to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or
# substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

IMAGES=$@

echo "This will remove all your current containers and images except for:"
echo ${IMAGES}
read -p "Are you sure? [yes/NO] " -n 1 -r
echo    # (optional) move to a new line
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
    exit 1
fi


TMP_DIR=$(mktemp -d)

pushd $TMP_DIR >/dev/null

open -a Docker
echo "=> Saving the specified images"
for image in ${IMAGES}; do
    echo "==> Saving ${image}"
    tar=$(echo -n ${image} | base64)
    docker save -o ${tar}.tar ${image}
    echo "==> Done."
done

echo "=> Cleaning up"
echo -n "==> Quiting Docker"
osascript -e 'quit app "Docker"'
while docker info >/dev/null 2>&1; do
    echo -n "."
    sleep 1
done;
echo ""

echo "==> Removing Docker.qcow2 file"
rm ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/Docker.qcow2

echo "==> Launching Docker"
open -a Docker
echo -n "==> Waiting for Docker to start"
until docker info >/dev/null 2>&1; do
    echo -n "."
    sleep 1
done;
echo ""

echo "=> Done."

echo "=> Loading saved images"
for image in ${IMAGES}; do
    echo "==> Loading ${image}"
    tar=$(echo -n ${image} | base64)
    docker load -q -i ${tar}.tar || exit 1
    echo "==> Done."
done

popd >/dev/null
rm -r ${TMP_DIR}

Théo Chamley的博客原文

用Bartender隐藏macOS通知栏的spotlight图标

一直用Bartender管理mac的通知栏,隐藏一些不常用的图标。但是从Mac OS X 10.11版开始,Spotlight的图标就无法被Bartender隐藏了。因为Mac OS默认会打开系统完整性保护(SIP)功能,拒绝其它程序修改Mac系统,所以Bartender无法修改Spotlight的显示状态。

想要隐藏spotlight图标需要先得把SIP暂时关闭:

  1. 重新启动电脑,在黑屏后,显示苹果标志前,按住键和R键,让Mac进入救援模式;
  2. 在救援模式的工具菜单中打开终端机,在终端中运行命令csrutil disable,关闭SIP;
  3. 退出终端重启电脑;
  4. 打开Bartender,现在可以设置Spotlight的隐藏设置了;
  5. 设置完成后,再次重启进入救援模式,方法同步骤1;
  6. 打开终端机,运行命令csrutil enable再次启用SIP;
  7. 重启到正常模式后,Bartender的对Spotlight的隐藏设置依然有效。

使用flock确保只有一个命令实例在运行

公司的机房设置了一个5分钟执行一次的数据同步脚本。一般每次同步只需要花费不到20秒即可完成,但网络出现问题时,就可能五分钟无法完成同步。脚本第二次启动可能干扰仍在运行的第一次脚本,需要有一个办法确保只有一个实例在运行。

Ubuntu系统提供了一个软件包叫run-one,可能实现这一功能,让我们来试试。首先安装软件包,以root权限运行:

# apt-get install run-one

安装好之后,运行run-one <要单实例运行的命令>,就可以确保只有一个实例运行了。比如:

# run-one tail -f /var/log/syslog

我们可以看到syslog的最后几行,并随着日志输出而滚动。此时如果打开另一个terminal,再次输入上述命令,会直接退出,并且exit code是1:

# run-one tail -f /var/log/syslog
# echo $?
1

除了run-one命令,run-one软件包中还提供了几个实用的命令:

  • run-one 只运行一个进程实例,如果实例已存在,直接退出
  • run-this-one 只运行一个进程实例,如果实例已存在,kill掉它并重新运行
  • run-one-constantlyrun-one一样,只运行一个进程实例,当进程退出时自动重新启动
  • keep-one-running run-one-constantly的别名
  • run-one-until-successrun-one-constantly相似,但只有当进程退出码不为0时才重新启动进程
  • run-one-until-failurerun-one-constantly相似,但只有当进程退出码为0时才重新启动进程

在CentOS系统中,并没有run-one软件包可供使用。不过我们可以利用flock锁定的机制自己实现只有一个进程运行。

可以写一个脚本:

#!/bin/bash

TODAY=`date +%Y%m%d`
(
  flock -xn 100 || exit 1
  scp root@myhost:/data/backups/$TODAY.log.gz /data/backups/myhost/
  gzip -d /data/backups/myhost/$TODAY.log.gz | xz -9 -e > /data/backups/myhost/$TODAY.log.xz && \
  rm /data/backups/myhost/$TODAY.log.gz
) 100>/tmp/backup_data.lock

上面的脚本中,flock命令会对/tmp/sync_data.lock文件加上写入锁(排它锁),分别在两个terminal中运行这个命令,会看到一个开始scp拷贝数据,另一个直接退出,退出状态码为1。

flock命令有三种写法:

  1. flock [-sxun][-w #] fd#
  2. flock [-sxon][-w #] file [-c] command...
  3. flock [-sxon][-w #] directory [-c] command...

我们前面用的是第一种,定义一个文件描述符,这种方法适合用于多行命令需要排它运行的场景。后两种flock的用法适合只有一条命令要运行的场景。flock-s选项表示要获取读取锁(共享锁);-x选项表示要获取写入锁(排它锁);-o选项表示在运行命令前关闭已取得锁定的文件,如果后面的命令可能产生不该取得锁定的子进程,这个选项会很有用;-n选项表示不要阻塞,如果无法取得锁,不要等待其它进程释放锁定,直接退出;-w 10表示等待10秒,如果10秒仍无法取得锁,就退出。

run-one是一个Bash脚本,通过查看run-one命令的源码得知,它也是利用flock实现的唯一化处理。如果不想自己编写带flock的程序,又想使用run-one提供的完整功能,可以去Github上下载run-one项目的代码。

SSH端口映射

SSH提供了两种端口映射方式,一种是本地端口映射,另一种是远程端口映射。

使用本地端口映射,SSH客户端会在本地监听一个端口,所有到该端口的连接全部转发到SSH服务器上指定的主机和端口号上。例如,我需要在我自己的电脑上访问VPS服务器上的数据库,但是服务器上的MySQL绑定在了127.0.0.1的IP地址上,可以用下面的命令把我电脑上的33306端口转发到服务器上的3306端口。

ssh xts.so -l root -L 33306:localhost:3306

运行这个命令后,我就可以直接用MySQL客户端连接自己电脑的33306端口访问服务器数据库了。

SSH的-L参数完整格式是-L [bind_address]:port:host:hostport,其中bind_address是本机(客户端)的绑定IP,可以省略,缺省值是127.0.0.1,如果需要允许其它电脑访问,可以监听0.0.0.0或者简单写个*port是本机开始监听的端口,这个端口的连接会转发到服务器上;host是相对于服务器而言的目标主机地址,域名、hostname、IP地址均可;hostport是目标主机的端口号。

3497093050.svg

如果需要在本地内网访问服务器机房内网的数据库,只需要指定binding_addresshost就可以了。

远程端口映射,SSH会在服务器上监听一个端口,所有到该端口的连接全部转发到SSH客户端指定的主机和端口号上。例如,我需要让本机的80端口可以在公网访问,却又不能独立使用公网IP,可以找一台VPS服务器作远程端口映射。

ssh xts.so -l root -R *:8080:localhost:80

运行这个命令后,用浏览器打开http://xts.so:8080/就相当于访问我本地电脑的80端口。

SSH的-R参数完整格式是-R [bind_address:]port:host:hostport,其中bind_address是远程(服务器)的绑定IP,可以省略,缺省值也是127.0.0.1;port是远程服务器上开始监听的端口;host是相对于本地电脑而言的目标主机地址;hostport是目标主机的端口号。

SSH远程映射也叫反向端口映射,因为常用于把内网主机的22端口暴露到公网。

ssh xts.so -l root -R *:2222:localhost:22 -N -f

这个命令可以把服务器的2222端口映射到本机的22端口,从而允许从Internet登录内网服务器。-N告诉ssh不要运行任何命令,此连接只用于端口映射。-f选项可以让ssh转入后台运行,这样ssh命令运行之后就能回到命令行,可以继续使用其它命令。