理解 Docker Client/Server 架构, 找寻 Docker Desktop 替代品

本文继上篇 摆脱 Docker Desktop 即将到来的收费 进一步寻找符合自己需求的 Docker Desktop 替代品,前面试用过 hyperkit + minikube, Podman, 最终还是确定了用 docker-machine。之所以选择了它是基于下面几个需求:

  1. 连接 VPN 后 Docker 还要能继续工作 (通过 socket 文件和 localhost 与 Docker Host 通信不惧怕 VPN 连接. 因为 VPN 会接管路由表,所以用 IP 来连接 Docker Host 的话,VPN 连上后将可能无法访问 Docker Host)
  2. 能与 IDE 进行集成开发与调试  (IntelliJ IDEA 能与 Docker Desktop, Docker Machine, TCP socket 和 SSH 上的 Docker Host 集成调试,但无法与 Daemonless 的 Podman 集成)
  3. DOCKER_HOST 能是远程机器 (由于 Podman 设计为 Daemonless,也就没有 Docker Host, 无法进行远程构建)

本文力图更深入的理解 Docker 的架构来解释最后选择的来由,清楚了原理后可以自主创建一个 Docker Host,连 docker-machine 也可以不用。比如创建一个 AWS EC2 实例作为 Docker Host, 然后在本地执行 docker 命令进行镜像的构建与容器的运行,这时候镜像构建过程与容器执行的环境是在 EC2 上,再也不用先把本地的文件上传(scp 或 rsync) 到 EC2 上,然后 ssh  到 EC2 去执行 docker 命令了。

一个小插曲:本人曾经随手在  ~/Downloads 目录下建立一个只有 FROM busybox 一行的 Dockerfile 文件,然后运行 docker build ...  命令,结果每次都提示磁盘空间不足,本机磁盘还非常宽裕,Docker Machine 也分配了 20 G 内存,怎么会不够了呢?登入到 Docker Machine  后 df 确实没空间了。四处找原因,原来是 docker build ... 一执行,不管 3721 首先把当前目录下的所有文件全部拷贝到 Docker Machine 中去,~/Downloads 目录中下了几十个 G 的内容,所以把 Docker Machine 给挤暴掉。解决办法就是要把  Dockerfile 放到一个没有无用文件的独立目录中去,这也是为什么 Dockerfile 中的 COPY 命令只能从当前目录中拷贝文件的原因。 阅读全文 >>

Python 中创建 PostgreSQL 数据库连接池

征战 Java 多年,习惯于使用数据库之前都必须创建一个连接池,即使是单线程的应用,只要有多个方法中需用到数据库连接,建立一两个连接的也会考虑先池化他们。连接池的好处多多,1) 如果反复创建连接相当耗时,2) 对于单个连接一路用到底的应用,有连接池时避免了数据库连接对象传来传去,3) 忘记关连接了,连接池幸许还能帮忙在一定时长后关掉,当然密集取连接的应用势将耗尽连接,3) 一个应用打开连接的数量是可控的

接触到 Python 后,在使用 PostgreSQL 也自然而然的考虑创建连接池,使用时从池中取,用完后还回去,而不是每次需要连接时创建一个物理的。Python 连接 PostgreSQL 是主要有两个包,py-postgresqlpsycopg2, 而本文的实例将使用后者。

Psycopg 在 psycopg2.pool 模块中提供了两个连接池的实现在,它们都继承自 psycopg2.pool.AbstractConnectionPool, 该抽象类的基本方法是

  1. getconn(key=None): 获取连接
  2. putconn(conn, key=None, close=False): 归还连接
  3. closeall(): 关闭连接池中的所有连接

阅读全文 >>

Python zipfile 只借助内存进行压缩与解压缩

Python zipfile 模块压缩与解压缩通常是对物理磁盘文件进行操作,比如参照官方的例子,生成压缩文件的代码是

with zipfile.ZipFile('spam.zip', 'w') as myzip:
    myzip.write('eggs.txt')
    myzip.write('beef.txt')

这样就生成了一个包含两个文件的压缩包 spam.zip, 相当于命令 zip spam.zip egges.txt beef.txt 的效果。用  unzip -l spam.zip 命令就能看到其中的两个文件。相应的解压缩的代码如下

with zipfile.ZipFile('spam.zip', 'r') as myzip:
    print(myzip.filelist())  # 可获得压缩包中的文件列表信息
    myzip.extractall()

同样是把压缩包 spam.zip 解压缩文件到当前目录中,相当于命令 unzip spam.zip 的效果。

前面顺便也是熟悉一下 zipfile 模块的常见用法,但有时候我们可能从数据库中,从网络上收到的是字节数据,希望直接处理字节的压缩解压缩,而不借助于中间的磁盘文件,因为通过磁盘文件来处理必须进行善后处理以及可能的资源的竞争,在内存宽裕的情况下效率也是个问题。 阅读全文 >>

Python Poetry 项目中相对路径模块引用的问题

最近一直在折腾 Python 项目,通过对几个 Python 项目依赖管理与构建工具的对比,最后选择了 Poetry。它管理依赖,构建与发布包还是简单的多,不需要处理 setup.py, setup.cfg 和 Makefile 文件, 甚至都不需要了解 wheel 是什么就能往 PyPI 发布包了。

可是,别看 Poetry 的官网一直守护着一副小清新的形像,其实照样处处是坑,其中一个就是与相对引用有关的问题。我们来看下什么样的现像,最后的结论就是:在 Python 中避免使用相对路径引用,因为相对路径的上下文经常在变,然后必要时先执行 poetry install, 甚至把入口代码拉到包外头去。

什么是相对引用与绝对路径引用,比如在一个包 my_package 中有两个模块(Python 文件) app.py 和 utils, app.py 中对 utils 资源的引用可以写成

from utils import md5               # 不确定 utils 是一个包还是一个模块,有点像是隐式相对路径模块引用
from .utils import md5              # 同一目录中的 utils 模块
import .utils
from ..utils import md5             # 上一级目录中的 utils 模块 (如果 utils.py 在与 app.py 上一级目录的话)
from my_package.utils import md5  # 绝对引用,总是从包名开始

注意 from 后面的 ...,相对路径引用不能直接 import, 如不能 import .utils.md5

阅读全文 >>

Lambda + API Gateway 创建需 API Key 验证的 API

希望在标题上尽量包含更多的信息,原本命题为: Lambda + API Gateway 创建需 API Key 验证的 API(Docker + Python + Terraform), 但是觉得太长了,于是只取了前半部份。仍然要在开头部分强调一下本文件打算要实现什么

  1. 在 AWS  用 Lambda  和  API Gateway 创建 API
  2. 创建的 API 是 public 的,需要用 x-api-key 来验证
  3. Lambda 的实现代码打包在了一个 Docker 镜像中
  4. 整个 AWS 的基础架构(包括 ECR, Lambda, API Gateway 及权限等)是由 Terraform 脚本创建管理的

目标明确,我们直冲到代码的目录结构来,项目目录为 api-gateway-demo, Github 上的链接为 api-gateway-demo. 后面详叙还会把其中每一个文件的内部给列出来 阅读全文 >>

Python 依赖管理与构建工具(CookieCutter, PyScaffold, PyBuilder, Poetry)

Python 历时这么久以来至今还未有一个事实上标准的项目管理及构建工具,以至于造成 Python 项目的结构与构建方式五花八门。这或许是体现了 Python 的自由意志。不像 Java 在经历了最初的手工构建,到半自动化的 Ant, 再到 Maven 基本就是事实上的标准了。其间 Maven 还接受了其他的 Gradle(Android 项目主推), SBT(主要是 Scala 项目), Ant+Ivy, Buildr 等的挑战,但都很难撼动 Maven 的江湖地位,而且其他的差不多遵循了 Maven 的目录布局。

回到 Python,产生过 pip, pipenv, conda 那样的包管理工具,但对项目的目录布局没有任何约定。关于构建很多还是延续了传统的 Makefile 的方式,再就是加上 setup.py 和 build.py 用程序代码来进行安装与构建。关于项目目录布局,有做成项目模板的,然后做成工具来应用项目模板。下面大概浏览一下四个工具的使用

  1. CookieCutter
  2. PyScaffold
  3. PyBuilder
  4. Poetry

阅读全文 >>

找到 Linux 后台程序的打印输出

最近才狠命的折腾数码日购入的一个  DS920+ NAS,在上面用计划任务来同步两个目录的文件,似乎是在登陆它的 SSH 后的 rsync 同步命令与放在计划任务里的执行效果不一样。于是想看看计划任务里的 rsync -av --delete folder1 folder 时的控制台输出。到哪里去找呢?登陆到 SSH 于只能用 ps 命令看到它的进程 ID

yanbin@nas:~$ ps -ef|grep rsync
SynoRsy+ 10270 1 0 Sep28 ? 00:00:00 /usr/bin/rsync --daemon
root 14067 14066 51 23:03 ? 00:00:02 rsync -av folder1/ folder2/
root 14076 14067 0 23:03 ? 00:00:00 rsync -av folder1/ folder2/
root 14077 14076 58 23:03 ? 00:00:02 rsync -av folder1/ folder2/
Google 了一下找到了这篇 See the STDOUT redirect of a running process, 关键就是用 ls -l /proc/<pid>/fd 列出进程所有打开的文件描述符

阅读全文 >>

用 Python 定义 Schema 并生成 Parquet 文件

原来用 Java 和 Python 实现过 Avro 转换成 Parquet 格式,所以 Schema 都是在 Avro 中定义的。这里要尝试的是如何定义 Parquet 的 Schema, 然后据此填充数据并生成 Parquet 文件。

本文将演示两个例子,一个是没有层级的两个字段,另一个是含于嵌套级别的字段,将要使用到的 Python 模块有 pandas 和 pyarrow

简单字段定义

定义 Schema 并生成 Parquet 文件

阅读全文 >>

AWS DynamoDB 的常用操作

AWS 提供的 NoSQL 数据库有 DynamoDB, DocumentDB(即 MongoDB), 和 Keyspaces(即 Cassandra)。还有一个神秘的早已消失于 AWS 控制台之外的 SimpleDB,它只能通过 API 才能使用。因为 AWS 有意要把它藏起来,不愿被新用户看到它,希望用 DynamoDB 替代它,关于用 aws cli 如何体验 AWS SimpleDB 可见本文后面部分。

DynamoDB 所设计的读写容量参数的概念,AWS 为其标榜是为保证一致性与明确的性能表现,实际上不如说是一个赚钱的计量单位,为了钱反而是把一个简单的事情弄复杂了。当需要全局索引时,必须为全局索引设定读写容量,连索引的钱也不放过。本文只为体验对 DynamoDB 的常用操作,不管吞吐量的问题,所以不用关心读写容量的问题。

DynamoDB 后端用 SSD 存储,不像 Elasticache 是把数据放在内存当,对了 Elasticache 也是 AWS 提供了 NoSQL 服务。DynamoDB 每条记录(Item) 的大小限制为 400K. 阅读全文 >>

创建 AWS EC2 实例时 userdata 的一些知识

我们在初始一个 AWS EC2 实例时,可以通过 user data 让 EC2 第一次启动后做些事情,可以放置 shell script 或 cloud-init 指令。在控制台设置 user data 可用明文文本,由 awscli 创建时可使用一个文件,或者通过 API 用 base64 编码的内容。

下面是 user data 被执行时需知晓的一些知识

  1. 是脚本时必须以 #! 开始,俗称 Shebang, 如 #!/bin/bash
  2. user data是以 root 身份执行,所以不要用 sudo, 当然创建的目录或文件的 owner 也是 root,需要 ec2-user 用户访问的话需要 chmod 修改文件权限,或者直接用 chown ec2-user:ec2-user -R abc 修改文件的所有者()
  3. 脚本不能交互,有交互时必须想办法跳过用户输入,如 apt install -y xzy, 带个  -y 标记
  4. 如果脚本中需访问 AWS 资源,权限由 Instance Profile 所指定的 IAM role 决定
  5. user data 中的脚本会被存储在  /var/lib/cloud/instances/<instance-id>/user-data.txt 文件中,因此也可以从这里验证 user data 是否设置正确。或者在 EC2 实例上访问 http://169.254.169.254/latest/user-data 也能看到 user data 的内容。并且在 EC2 实例初始化后不被删除,所以以此实例为基础来创建一个新的 AMI 需把它删除了
  6. user data 的大小限制为 16 KB, 指 base64 编码前的大小
  7. cloud-init 的输出日志在 /var/log/cloud-init-output.log, 它会捕获 cloud-init 控制台的输出内容

阅读全文 >>