开放 docker daemon 端口
总结下平时积累的 docker 使用经验与技巧
国内安装使用 docker
相比在线安装,这里推荐使用国内源下载离线安装包进行安装,但也可以通过国内镜像源加速在线安装。
在线安装
官方文档的 docker 安装 如果较慢的话,可以使用国内的镜像进行加速:
$ sudo apt-get update
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-common
# 这里把源替换为国内的源
$ curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/debian/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
使用离线安装包
如 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 里添加内容:
{
"registry-mirrors": [
"https://dockerhub.azk8s.cn",
"https://reg-mirror.qiniu.com",
"http://hub-mirror.c.163.com"
]
}
上面的是国内的几个有用的镜像源,除此之外还可以使用 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,开放端口分下面几个步骤:
# 1. 新建文件
$ sudo mkdir /etc/systemd/system/docker.service.d
$ sudo vim /etc/systemd/system/docker.service.d/startup_options.conf
# 以下为需要粘进去的内容
[Service]
# 必要步骤需要先清空
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376
# 2. 重启 systemd 和 docker.service
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker.service
# 测试一下端口是否开放成功
$ curl http://0.0.0.0:2376/info
nginx 转发 socket
也可以使用 nginx 作为转发,暴露 docker daemon 的接口,该 nginx 最方便的是作为容器运行,其 Dockerfile 如下:
FROM nginx:stable
RUN echo 'user root;\n\
worker_processes 1;\n\
error_log /var/log/nginx/error.log warn;\n\
pid /var/run/nginx.pid;\n\
events {\n\
worker_connections 1024;\n\
}\n\
stream {\n\
server {\n\
listen 80;\n\
proxy_pass unix:/var/run/docker.sock;\n\
}\n\
}\n' \
> /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
之后使用下面的命令构建和启动和测试该容器:
$ docker build . -t docker-socket-proxy
$ docker run -d -p 8376:80 -v /var/run/docker.sock:/var/run/docker.sock docker-socket-proxy:latest
$ curl http://0.0.0.0:8376/info
相比直接修改启动文件,可避免 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/
下的配置:
upstream docker {
server unix:/var/run/docker.sock fail_timeout=0;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80 default_server;
location /api/docker/ {
# 或者直接 proxy_pass http://unix :/var/run/docker.sock;
proxy_pass http://docker/ ;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_read_timeout 1h;
}
}
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:
$ sudo groupadd docker
# 重新登录终端生效
$ sudo usermod -aG docker ${USER}
其他有用的命令如下:
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 文件:
{
"Name": "nginx",
"TaskTemplate": {
"ContainerSpec": {
"Image": "nginx:stable",
"Env": [
"TZ=Asia/Shanghai"
]
}
},
"Mode": {
"Replicated": {
"Replicas": 2
}
},
"EndpointSpec": {
"Mode": "dnsrr"
},
"Labels": {
"test": "nginx"
}
}
可以由下面的命令创建:
curl --unix-socket /var/run/docker.sock \
http:/services/create \
-H "Content-Type: application/json" \
--request POST \
-d @nginx.json
减小镜像体积
- 对于 debian 和 pip 清除安装后的缓存
apt-get
需要rm -rf /var/cache/apt/* && rm -rf /var/lib/apt/lists/*
pip
需要rm -rf ~/.cache/pip
- 如 alpine 的镜像,单独安装 build 依赖包,并在之后清除
RUN apk --no-cache add --virtual build-dependencies \
build-base \
py-mysqldb \
gcc \
libc-dev \
libffi-dev \
mariadb-dev \
&& pip install -qq -r requirements.txt \
&& rm -rf .cache/pip \
&& apk del build-dependencies
RUN apk -q --no-cache add mariadb-client-libs
- 对于如 Spring Boot 的 fat jar,里面包含了很共有的依赖,这时候可以使用 Google 的 maven 插件 Jib 合理的将 fat jar 分散到不同的镜像层。
备份与还原
镜像导出与导入:
$ docker save openjdk:8-jre-stretch | gzip > openjdk.8-jre-stretch.tar.gz
$ zcat openjdk.8-jre-stretch.tar.gz | docker load
Volume 备份与还原 docker 没有提供命令,但是可以通过运行一个容器挂载需要备份的容器,然后将其打包,还原时候再逆操作一下:
# 设置备份 volume 到 VOL,设置备份版本到 BKTAG
$ VOL=mysql-data
$ BKTAG=untag
# 备份文件打包到当前文件夹
$ docker run --rm -v $(VOL):/volume -v $(PWD):/backup alpine \
tar cf /backup/$(VOL)-$(BKTAG).tar -C /volume ./
# 恢复备份
$ docker run --rm -v $(VOL):/volume -v $(PWD):/backup alpine \
sh -c "rm -rf /volume/* /volume/..?* /volume/.[!.]* ; tar -C /volume/ -xf /backup/$(VOL)-$(BKTAG).tar"
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:
volumes:
mysql-data:
driver: local
driver_opts:
type: none
o: bind
device: /var/lib/mysql
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 解析 RSA 加解密算法
下一篇: 不要相信一个熬夜的人说的每一句话
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论