最近搭建DroneCI的时候入坑了Docker,才发现这是个很超级厉害的容器引擎。
熟悉了一些基本的CLI之后,我就把所有服务都迁移到了Docker上,真的是太方便好用了,以后就是换服务器了,也能快速迁移和部署。
当然在使用的过程中还是遇到了一些坑,但只要查查官方文档和stackoverflow社区,就会发现这些都不是问题。
为了避免后面的朋友踩坑,我就记录一下我遇到的最实用的三个小技巧吧:
- 使容器可以输入文字到stdin
- 前台容器转到后台运行
- 限制单个容器的CPU资源
1.使容器可以输入文字到stdin
默认情况下,容器内部是关闭stdin输入流的。也就是说无法读取外部的输入。
我在尝试把mirai-console移到容器里时就出现了这个问题,mirai-console是个机器人程序,有时候需要用控制台执行一些管理命令之类的,可以理解为和minecraft服务器是一样的用法。
在外面启动就一切正常,一放到容器里就出现红字Closing input service...
,查过源代码得知这个报错是因为stdin输入流遇到了eof异常,也就是没有任何字节可以读取。
这个问题我排查了很久,最后stackoverflow上给出了答案:给容器加上-it
参数就能正常输入文字了。
有时候我们经常会这样用sudo docker run -it alpine /bin/sh
,只有加上-it
参数才能正常使用sh
命令。
这里的-it
参数全拼是--interactive
和--tty
查官方文档可得知,--interactive
的作用是保持stdin的开启,就算没有attach也是如此
另一个参数--tty
解释起来就比较复杂了,简单点说就算创建一个虚拟的TTY环境(简称PTY),而不是直接运行子进程(裸运行)。
之前我在做MShell插件的时候就遇到了这个PTY和直接运行子进程的区别,最明显的地方就是调用screen
命令的时候,如果没有PTY环境,screen命令就无法获取窗口的字符宽高,也就启动不起来。窗口的字符宽高就算PTY环境特有的一个属性。
所以其实只加-i
参数在容器里就已经能从stdin读取输入了,但一般为了兼容性,大家还是会不约而同的加上-t
参数,合起来就是-it
参数
关于PseudoTYY,可以参考这篇文章
如果在使用docker-compose.yml
,则可以这样设置参数,和使用-it
效果是一样的
version: "3.7"
services:
mirai-qq-bot:
image: openjdk:16-alpine
restart: always
working_dir: /app
entrypoint: ["java", "-jar", "mcl.jar"]
// 相当于 -i 参数
stdin_open: true
// 相当于 -t 参数
tty: true
2.前台容器转到后台运行
有时启动容器的时候不小心忘了加-d
(分离)参数,就会导致挂在前台,不能退出。
用Ctrl + C
快捷键虽然输入可以退出,但是会连着容器一起停止运行,虽然可以重新启动然后加上-d
参数,但是有的容器启动和退出非常慢,非常的不方便。
有两个办法可以解决,一个是比较暴力的关闭窗口,一个是比较温柔的快捷键方法
首先说比较暴力的方法吧,很简单,直接叉掉ssh窗口使其会话强行终止,那么容器就会自己断开了,同时还不会停止运行。这一点和直接在ssh窗口里运行程序不一样。
另一个比较温柔的方法是使用快捷键Ctrl + P -> Q
,即先按住Ctrl键,然后点击一下P键,不要松开Ctrl键,接着点一下Q键,那么就可以很优雅地断开容器了。
但是,这个方法有个前提条件,启动的时候必须要加上-it
参数,否则按快捷键是无效的。只能使用前面比较暴力的办法退出了。像这样做也是迫不得已的,因为docker只提供了attach命令,没有提供detach命令,就只好出此下策了。
3.限制单个容器的CPU资源
有时候需要限制单个容器的CPU使用率,避免占用过多资源,对其它程序造成影响。
好在Docker也提供了这么一个功能,只需要传几个参数进去就可以实现CPU分配了。
具体的参数和平台有关,不同的平台有不同的参数(Windows,就你特殊!)
参数 | ||
---|---|---|
docker run --cpu-count |
CPU count (Windows only) | |
docker run --cpu-percent |
CPU percent (Windows only) | |
docker run --cpu-period |
Limit CPU CFS (Completely Fair Scheduler) period | |
docker run --cpu-quota |
Limit CPU CFS (Completely Fair Scheduler) quota | |
docker run --cpus |
(API 1.25+ )Number of CPUs |
核心方面:如果是Win平台,可以使用--cpu-count
参数控制使用的核心数量。如果是非Win平台可以使用--cpus
参数来控制可用的核心数
CPU使用率方面:如果是Win平台,可以使用--cpu-percent
参数控制每个核心的最大使用率(最大应该是100,但我没有实际测试过)。非Win平台就比较麻烦一点,需要填写两个参数:--cpu-period
和--cpu-quota
这两个参数都是内核的CFS(完全公平调度器)算法的参数,CFS有很多参数,如果感兴趣可以参考这篇文章
限制CPU使用率原理大致是限制执行时间来实现的。给定的CPU时间用完以后,就会被节流(throttlling)直到分配周期结束,才会从新开始分配CPU时间。
如果有多个进行在运行时,具体的调度可以看下面的图(来源:Kubernetes生产实践系列之三十一:Kubernetes基础技术之CPU资源的调度和管理(CFS))
对于大部分一些对CPU实时性不敏感的常规应用来说,--cpu-period
一般设置为100000,也就是100ms,意思是每100ms为一个CFS分配周期。另一个参数--cpu-quota
这表示在这个周期里,运行进程执行多少时间,单位也是微秒,比如给80000(80毫秒)。这两个参数设置好以后,就可以确定的该容器最大的单核CPU占用率为
如果想要限制单核使用率到40%,就只需要将--cpu-quota
参数设置为40000就好了
结尾
好像一不小心又写的太硬核了,写着写着就开始介绍一些底层的东西了。
Docker是个很方便的东西,只要打包好了镜像,可以到处迁移。无论宿主机是什么系统都可以屏蔽差异。就连Windows也是如此,当Docker法线你电脑上安装了WSL之后,会直接用WSL来运行Docker,该不该夸Docker聪明呢?
对于一些能增加生产力的好东西吧,我总是比较畏惧,对各种东西抱有偏见,但是一旦被迫上手之后就发现再也离不开了。
之前因为写mirai插件入坑的kotlin语言,上手后才发现这语言是如此的厉害又方便,真的是爱不释手。
这次接触Docker也是因为要搭建一个DroneCI服务,但它又只支持Docker部署,没法学了下Docker。结果发现Docker也是如此的好用方便。以至于我直接把我7个项目全丢进了Docker里。不仅方便迁移,还能提供隔离的效果,减少恶意软件入侵宿主机的风险。
好了,Docker技巧就分享到这了,如果有机会,再继续做第二期的内容吧。