socket远程拒绝_从socket权限问题重新认识docker架构

本文讨论了jenkins在Docker中构建时遇到的socket权限问题,深入解析Docker Engine的client-server架构,强调了Docker守护进程的socket通信方式。解决方案是通过映射宿主机的unix socket到jenkins容器并解决权限问题,特别是确保uid和gid的一致性,以允许容器内的docker client访问宿主机的docker守护进程。
摘要由CSDN通过智能技术生成
69cccf6012ac7d5c0ad069abbe2c542b.png

jenkins构建出现socket权限问题

docker运行的jenkins实现对spring boot的docker部署,使用docker-maven-plugin插件。默认情况下,此插件在docker内部通过localhost:2375访问docker守护进程。

问题:

#jenkins构建过程中 mvn clean package docker:build 报错14:48:53 [INFO] I/O exception (java.io.IOException) caught when processings request to {}->unix://localhost:80: No such file or directory14:48:53 [INFO] Retrying request to {}->unix://localhost:8014:48:53 [INFO] I/O exception (java.io.IOException) caught when processing request to {}->unix://localhost:80: No such file or directory14:48:53 [INFO] Retrying request to {}->unix://localhost:8014:48:53 [INFO] I/O exception (java.io.IOException) caught when processing request to {}->unix://localhost:80: No such file or directory14:48:53 [INFO] Retrying request to {}->unix://localhost:80

原因:

docker-maven-plugin在jenkis 容器内中可以理解为一个docker client,但是容器内无法通过localhost:2375访问docker守护进程,因此会报此错误。

思考:

我们是否需要在jenkins容器内部安装docker,以便docker client能够访问docker守护进程。

对于这个疑问,我们先从docker架构入手找下答案。

Docker Engine

Docker Engine是一个client-server应用,主要由以下组件:

  1. docker daemon,守护进程dockerd;
  2. REST API,client与server进行通信及操作的接口;
  3. docker CLI,命令行界面的客户端;
5d4da81bb31faea36a17033180b8e155.png

基于client-server架构,Docker的工作机制如下:

  1. Docker客户端与Docker守护进程进行对话,该守护进程完成了构建,运行和分发Docker容器的繁重工作;
  2. Docker客户端和守护程序可以在同一系统上运行,或者您可以将Docker客户端连接到远程Docker守护程序;
  3. Docker客户端和守护程序在UNIX套接字或网络接口上使用REST API进行通信;

因此我们在服务器上安装的docker其实是包含client-server的,信息如下:

root@test:~# docker versionClient: Version:           18.09.1 API version:       1.39 Go version:        go1.10.6 Git commit:        4c52b90 Built:             Wed Jan  9 19:35:23 2019 OS/Arch:           linux/amd64 Experimental:      falseServer: Docker Engine - Community Engine:  Version:          18.09.1  API version:      1.39 (minimum version 1.12)  Go version:       go1.10.6  Git commit:       4c52b90  Built:            Wed Jan  9 19:02:44 2019  OS/Arch:          linux/amd64  Experimental:     false

Daemon socket

Docker守护程序可以通过三种不同类型的Socket供Docker Engine API请求:unix,tcp和fd。

  1. unix:默认情况下,在/var/run/docker.sock上创建unix socket,需要root许可或Docker组成员身份;
  2. tcp:当我们需要远程访问守护进程dockered时,可以使用tcp socket;
  3. fd:在基于Systemd的系统上,您可以通过Systemd套接字激活与守护程序通信;
#unix socketdockerd -H unix:///var/run/docker.sock#tcp socketdockerd -H tcp://0.0.0.0:2375#fd socketdockerd -H fd://

Docker客户端可使用DOCKER_HOST环境变量来为客户端设置-H标志:

docker -H tcp://0.0.0.0:2375 ps或export DOCKER_HOST="tcp://0.0.0.0:2375"docker ps

通过不同的socket,server不仅可以提供给本地client调用,还可以满足远程client的调用。只不过我们日常操作都是基于在/var/run/docker.socket 上创建的unix socket 与本地的server进行交互。

#本地通过http方式访问,还可通过sdk方式访问curl -s --unix-socket /var/run/docker.sock http:/v1.39/info

解决方案

通过对docker engine 和 daemon socket的了解,jenkins容器内的docker client是否可以远程访问宿主机的docker守护进程, 这样可以在不增加镜像的大小的情况下解决问题。

联想到本地的client、server交互,我们通过映射宿主机的unix socket到容器内,使其像在本地一样和docker守护进程进行交互。

vim docker-compose.yml#将本地socket映射到容器内version: '3.7'services:  jenkins:    image: jenkins/jenkins:lts    container_name: jenkins    restart: always    ports:      - "8080:8080"      - "50000:50000"    volumes:      - "/media/yanggd/work/jenkins:/var/jenkins_home"      - "/App/maven:/usr/local/maven"      # 将本地socket映射到容器内      - "/var/run/docker.sock:/var/run/docker.sock"#启动docker docker-compose up -d

docker-compose重新启动后,jenkins构建再次报错:

14:58:02 [INFO] I/O exception (java.io.IOException) caught when processing request to {}->unix://localhost:80: Permission denied14:58:02 [INFO] Retrying request to {}->unix://localhost:8014:58:02 [INFO] I/O exception (java.io.IOException) caught when processing request to {}->unix://localhost:80: Permission denied14:58:02 [INFO] Retrying request to {}->unix://localhost:8014:58:02 [INFO] I/O exception (java.io.IOException) caught when processing request to {}->unix://localhost:80: Permission denied14:58:02 [INFO] Retrying request to {}->unix://localhost:80

看来jenkins容器内的docker client能访问宿主的unix socket,但是因为权限问题访问被拒绝。

具体分析如下:

  1. 从jenkins的官方镜像得知,jenkins容器内部默认使用jenkins用户且uid、gid均为1000。而为了保证jenkins的备份,我们已经在宿主机新增jenkins用户,并且已经对jenkins_home授权
  2. uninx socket需要root许可或Docker组成员身份,我们在宿主机将jenkins用户添加到docker组中,以实现普通用户对/var/run/docker.sock 的访问。
root@test:~# usermod -G docker jenkinsroot@test:~# id jenkinsuid=1001(jenkins) gid=1001(jenkins) groups=1001(jenkins),999(docker)#重启dockersystemctl restart docker

经过修改后,jenkins构建仍然报错,这是为什么呢?难道这种方案不行吗?

#登录容器dock exec -it jenkins /bin/bash#查看权限jenkins@3387b9e025bd:/$ cat /etc/group |grep 1000jenkins:x:1000:jenkins@3387b9e025bd:/$ cat /etc/passwd |grep 1000jenkins:x:1000:1000::/var/jenkins_home:/bin/bash#查看容器内的docker.sockjenkins@3387b9e025bd:~/workspace/helloworld$ ls -l /var/run/docker.sock srw-rw---- 1 root 999 0 Mar 25 07:42 /var/run/docker.sock

通过登录容器查看权限发现,我们虽然在宿主机上将jenkins用户加入到docker组中,但是在容器内部docker.sock 的属组为999,而jenkins的uid及gid都为1000,因此由于gid的不同,在宿主机上授权并不代表容器内也授权成功。我们需要保证宿主机和jenkins容器内部的uid、gid保持一致。

由于宿主机在本地更改uid、gid会影响工作使用,在此我使用chmod临时授权解决,但是在生产环境中一定要保持一致。

chmod 666 /var/run/docker.sock

注意:当docker重启启动后,需要重新授权。

总结

通过解决权限问题加深了对docker的了解,希望通过在docker的应用实践中,不断总结经验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值