Docker 技巧
Contents
总结下平时积累的 docker 使用经验与技巧
国内安装使用 docker
相比在线安装,这里推荐使用国内源下载离线安装包进行安装,但也可以通过国内镜像源加速在线安装。
在线安装
官方文档的 docker 安装 如果较慢的话,可以使用国内的镜像进行加速:
|
|
使用离线安装包
如 Ubuntu 16 代号是 xenial,就可在 https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/dists/xenial/pool/stable/amd64/
下载 containerd docker-ce-cli docker-ce 三个 deb 离线包,对于不同的版本替换上面 url 中的关键字即可。
之后使用 sudo dpkg -i
安装三个包(最后安装 docker-ce),之后使用 sudo systemctl start docker
启动即可。
如果不确定版本之间的关系。可以先安装 docker-ce,会提示失败然后显示其依赖的版本要求。
使用预编译文件安装
这里并不推荐使用 https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/static/stable/x86_64/docker-19.03.5.tgz
链接下载可执行文件然后自己编写 docker 的 systemd 启动项,该方法过于繁琐且容易出错,这里就不叙述了。
国内 docker 镜像源
下载 docker 镜像时,使用默认的 Docker Hub 可能有点慢,可以使用国内的镜像源,修改 /etc/docker/daemon.json
文件(如果没有该文件可先 touch 一个),在 registry-mirrors 里添加内容:
|
|
上面的是国内的几个有用的镜像源,除此之外还可以使用 Redhat 的 Quay.io,不过这个是单独的 registry 而不是 docker hub 的镜像,所以资源可能少点。修改完后注意 sudo systemctl restart docker
重启服务,之后使用 docker info
检查是否添加上了。
开放 docker daemon 端口
原有的 docker daemon 使用的是 unix socket 即 unix:///var/run/docker.sock
进行 RESTful 接口的交互。为了方便二次开发和平时测试,需要将其开放为 TCP 端口的方式。这里介绍修改启动参数和使用 nginx 转发两种方式。
对于修改启动参数,不同的 linux 的 init 系统有着不同的方式,对于 Ubuntu 16 来说其 init 为 systemd,开放端口分下面几个步骤:
|
|
nginx 转发 socket
也可以使用 nginx 作为转发,暴露 docker daemon 的接口,该 nginx 最方便的是作为容器运行,其 Dockerfile 如下:
|
|
之后使用下面的命令构建和启动和测试该容器:
|
|
相比直接修改启动文件,可避免 docker daemon 重启,同时该容器作为 service 运行时,可以通过域名直接访问,这样依赖于 docker api 的服务在部署时候就不必部署在 manager 节点之上且强耦合与管理节点的 ip,而是直接通过域名的方式访问部署在 manager 节点上的 docker-socket-proxy 服务,实现任意节点的部署。
docker-socket-proxy 的使命是将 unix socket 暴露成 tcp ,docker api 虽然是 http 的协议,但是 nginx 没有必要去使用该层次信息,作为 tcp 透传即可(nginx 1.9 以上支持)。所以在 nginx.conf 配置里将 http 配置块省去,并直接添加上 tcp,修改原来的 www-data user 为 root 以便访问 unix socket 文件。
如果要用到 http 层的信息的话,自己改写 nginx 配置文件使用 http 转发即可,不过需要注意的是:对于 docker api 中的 WebSocket 接口和有 Transfer-Encoding: chunked
头的 HTTP 请求(如日志查看)来说需要特殊处理下。
nginx 代理 socket
首先给出 nginx http 代理 docker daemon socket 到 /api/docker/
下的配置:
|
|
WebSocket proxying 需要显式的说明,nginx 才会处理。但是对于不是 ws 的接口怎么办?
在 nginx 的文档当中可以找到使用 map 的方式定义变量,根据客户端请求中 $http_upgrade
的值,来构造改变 $connection_upgrade
,参考 Nginx 支持 WebSocket 反向代理-学习小结。
上面 nginx 的官方文档里提到默认的 proxy_read_timeout 是一分钟,如果没有读写动作的话会自动断开,这对对于 log 的 Transfer-Encoding: chunked
头 HTTP 请求来说也是一样的,文档里建议设置周期的 ping frames 去激活连接,但是对于 log 和 shell 来说的话不适用,所以这里就直接配置为 proxy_read_timeout 1h
,超过一小时没有读写才断开。
docker-cli 连接远程的 dockerd
可以使用命令 docker -H 127.0.0.1:2576 ps
的方式使用本机的 docker-cli 访问其他开放的 dockerd(docker daemon),之后 alias 一下,可以更方便的使用。
或者是修改环境变量 DOCKER_HOST 以变更 docker-cli 的默认 dockerd。
对于 windows 来说,如果不想在本地安装 docker,而是想在本地使用 docker 命令即 docker-cli 连接到远程的 docker 可以在 win 的包管理 choco 工具下搜索到 Docker CLI 然后安装,或者是直接在 docker-cli-builder 下载他人编译好的,之后同 -H
参数或者 DOCKER_HOST
环境变量指定远程 dockerd,这样就可以在 win 下的 cmd 或 powershell 中使用 docker 命令了。
docker 命令
如果使用的 ohmyzsh,可在 ~/.zshrc
里面的 plugin
项里加入 docker 插件以提高补全率,同时将当前用户加入 docker 组,这样使用 docker 时不用加 sudo:
|
|
其他有用的命令如下:
docker ps
- 一行太长可以使用
docker ps -a| less -S
滚屏查看 docker ps -s
可以看见容器大小docker ps --no-trunc
完全展开信息docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}"
定义输出信息
- 一行太长可以使用
删除所有的镜像和容器,其中的
-q
参数是只显示 id 列表,达到迭代删除的目的docker image rm $(docker image ls -q)
docker container rm $(docker ps -q)
根据 docker-compose 去删除容器
docker -f xxxx.yml stop
docker -f xxxx.yml rm
查看容器返回值
docker inspect ID --format='{{.State.ExitCode}}'
查看服务日志
docker service logs -f --tail 10 ServerName
使用 curl 命令创建 service
可以直接利用 curl 调用 dockerd 进行一些操作,比如可以 inspect 一下已有的 service 的配置粘贴到文件内,然后 curl 去创建,这样便于使用脚本批量的根据文件创建 docker 资源。如下面的 nginx service 的 nginx.json 文件:
|
|
可以由下面的命令创建:
|
|
减小镜像体积
对于 debian 和 pip 清除安装后的缓存
apt-get
需要rm -rf /var/cache/apt/* && rm -rf /var/lib/apt/lists/*
pip
需要rm -rf ~/.cache/pip
如 alpine 的镜像,单独安装 build 依赖包,并在之后清除
|
|
- 对于如 Spring Boot 的 fat jar,里面包含了很共有的依赖,这时候可以使用 Google 的 maven 插件 Jib 合理的将 fat jar 分散到不同的镜像层。
备份与还原
镜像导出与导入:
|
|
Volume 备份与还原 docker 没有提供命令,但是可以通过运行一个容器挂载需要备份的容器,然后将其打包,还原时候再逆操作一下:
|
|
Docker 实践技巧
Nginx remote ip 不正确
docker 通过 Nginx 负载均衡时候,容器内获取到的 HTTP 协议的 remote ip 是不正确的,参考 解决办法是可以将 /etc/default/docker
添加 DOCKER_OPTS="--userland-proxy=false"
ENTRYPOINT 中的环境变量
对于 java 类的镜像,在 dockerfile 中一般会定义 ENV JAVA_OPTS=""
这样的环境变量,然后在 ENTRYPOINT 里作为 JVM 启动参数。
但是,如 ENTRYPOINT exec java $JAVA_OPTS -jar /app.jar
和 ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /my.jar"]
的方式设定环境变量给 JVM 都是错误的。
正确的方式是 ENTRYPOINT ["/bin/bash", "-c", "java $JAVA_OPTS -jar app.jar"]
时区设置
以 stretch (Debian) 为基础镜像的可以以 TZ=Asia/Shanghai
环境变量指定时区,这样可以在运行时候(docker run, compose)指定时区,或者在 Dockerfile 里直接 ENV TZ=Asia/Shanghai
。
其他基础镜像可使用挂载本地时区文件的方式 /etc/localtime:/etc/localtime:ro
来完成。
其他技巧
docker-compose 里面定义一个直接从文件系统挂载的 volume:
|
|