2025年4月16日 星期三 乙巳(蛇)年 正月十七 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 云技术 > Docker

开放 docker daemon 端口

时间:12-14来源:作者:点击数:14

总结下平时积累的 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
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐