Docker通过ENTRYPOINT完成用户自动创建和切换

本文主要接收怎么通过DockerfileEntryPoint做到,容器启动后的登录用户不是root,而是用户通过docker run参数指定的用户名和组名

本文的背景

这篇文章源自我上周工作中遇到的一个问题,我在k8s中通过docker启动jupyterlab服务,但是发现每次docker每次启动容器后的用户都默认是root,这不利于权限控制和用户管理。

所以,我就想:是否控制用户启动容器后的登录用户呢? 周末在家好好学习了下怎么通过Dockerfile对镜像进行定制,并通过EntryPoint指令完成了上诉的功能,现分享给大家,希望对大家有帮助。
 

问题分析

如果你对docker相关技术比较熟悉的化,你应该首先会想到docker run --users或者在Dockerfile中通过USER指令来完成,但这两种方式都有个缺陷:就是我必须提前创建好用户和用户组。

如果你docker的使用还有上层应用的化,那倒还好,你可以提前创建好用户和用户组,或者容器启动后手动切换到其他用户。

但是,如果我仅仅想通过docker run的方式启动,启动后便自动完成创建和切换的工作呢?例如我就是通过docker启动notebook(一种基于web的交互式编程服务),我想用户在notebook terminal中登录的用户是自己而不都是root

了解docker的同学应该知道,docker是定制镜像,一般是先编写Dockerfile,然后build成镜像,最后基于这个镜像通过docker run启动容器。

所以,现在问题关键就是:怎么通过docker run后面跟一些参数传入用户名和组名,然后容器启动的时候,能够得到指定的用户名和组名,完成用户和组的创建,并切换到此用户?
 

CMD和ENTRYPOINT指令

Dockerfile是通过一系列的指令,从某个基础镜像出发,加上自己定制的功能,完成定制镜像的工作。下面的代码就是一个简单的例子,它基于nginx镜像进行扩展,添加一个html文件,然后启动nginx服务。

FROM nginx
RUN echo '

Hello, Docker!

'
> /usr/share/nginx/html/index.html CMD ["nginx", "-g", "daemon off;"]

完整的Dockerfile指令可以参考DockerFile指令详解,这里我们介绍本文相关的CMDENTRYPOINT两个指令。

CMD是容器启动命令,docker技术中的容器和虚拟机不一样,它是个进程,所以需要指定进程运行的程序和相关参数,CMD命令有两种写法,shell写法和exec写法。

# shell写法,即CMD后面直接跟shell命令。实际会被包装成sh -c的方式完成执行
CMD echo $HOME
# exec写法,指定执行文件和参数,推荐方式
# 上诉命令等价于如下写法
CMD [ "sh", "-c", "echo $HOME" ]

上诉的过程,也能通过docker run [OPTIONS] IMAGE [COMMAND] [ARG...]的方式显式的写启动命令,这会覆盖DockerFile中的CMD指令,上诉工作等价写法:docker run yourImage echo "hello world"

ENTRYPOINT的格式和RUN指令格式一样,分为exec格式和shell格式,ENTRYPOINT的目的和CMD一样,都是在指定容器启动程序及参数。两者可以单独使用,但是两者一起出现时,微妙的事情发生了,CMD将会变成ENTRYPOINT的参数,启动命令将会变成: "",详细的组合方式可以参考ENTRYPOINT和CMD不通组合表现出的效果。例如下面启动命名是访问百度网站

FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "https://www.baidu.com" ]

如果我想仅仅得到网址的header信息,即加上-i参数,于是我如下方式启动容器,发现报错,说找不到-i这个执行命令,那是因为镜像后面显式定义的CMDdockerfile中定义的CMD给覆盖了,而-i不是shell命令。

$ docker run myImage -i
docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".

前面讲过了,ENTRYPOINTCMD一起使用的时候,CMD会变成ENTRYPOINT的参数,所以我们可以通过这种方式,让我们的参数控制容器的启动命令。于是,上述的Dockerfile变为如下方式:

FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "https://ip.cn" ]

# build后,我们再次通过 docker run myImage -i就ok了
# 因为 -i 作为CMD会作为参数拼接到后面,即 curl -s https://ip.cn -i

 

通过ENTRYPOINT完成用户自动创建和切换

经过上面的命令介绍后,现在回到正题,我们如何通过ENTRYPOINT完成用户创建和切换。ENTRYPOINT如果运行的命令比较复杂,是可以直接写shell脚本的,所以,Dockerfile如下:

FROM hub.c.163.com/library/nginx

# 添加到环境变量中,entrypoint.sh可以直接运行
COPY entrypoint.sh /usr/bin
ENTRYPOINT ["entrypoint.sh"]

其中的entrypoint.sh脚本为:

#!/bin/bash

if [ "$#" -ne "2" ]; then  
    echo "usage example: docker run -d image:version username groupname"  
    exit 2  
fi  
echo "username:"$2
echo "groupname:"$1

# 添加用户和组,并切换
groupadd $2
useradd -g $2 $1
su $1

# 让nginx前台方式运行,为了当前进程不退出
# 不然shell脚本执行完,主进程完成,容器就会退出了
exec nginx -g 'daemon off;'

运行结果如下,说明下,为了直观的看到效果,我通过交互式的方式启动docker,第一个参数需要传入/bin/bash参数,所以我临时将上面shell脚本的参数校验先去了,然后后面获取参数的index都加了1,正常使用不用管。

[root@centos-7 zhanhaitao]# docker run -it nginx:v4 /bin/bash zhanht zhanht
username:zhanht
groupname:zhanht
$ id
uid=1000(zhanht) gid=1000(zhanht) groups=1000(zhanht)

你可能感兴趣的:(软件开发)