使用 Docker
Docker容器命令
运行中的镜像我们称之为容器,有点类似程序和进程的概念。
运行容器
1 | docker run --name container-name -d image-name |
运行一个容器,使用 docker run 命令即可。 另,docker run -参数 含义:
- – name:为容器起一个名称;
- -d:detached,执行完这句命令后,控制台将不会阻塞,可以继续输入命令操作,不会阻塞,也就是启动守护式容器,如果执行 docker run –name mycentos -it centos 会进入启动容器的命令控制台,也就是启动交互式容器;
- -i:以交互方式运行容器,通常与 -t搭配使用;
- -t:为容器重新分配一个伪输入终端,通常与 -i 搭配使用;
- -P:随机端口映射;
- -p:指定端口映射,后面会有端口映射详细讲解;
- image-name:要运行的镜像名称;
如果以守护式方式启动 centos 容器,执行如下命令:docker run –name mycentos -d centos,会正常返回 container-id,但是通过 docker ps 查看,却发现没有在运行,通过 docker ps -a 发现,原来已经停止了,这是为什么呢?1
2# 接下来我们创建一个守护态的Docker容器
sudo docker run -itd ubuntu:14.04 /bin/bash
查看容器
1 | # 查看运行中的容器列表 |
输出内容中:
- CONTAINER ID:启动时生成的 ID;
- IMAGE:该容器使用的镜像;
- COMMAND:容器启动时执行的命令;
- CREATED:容器创建时间;
- STATUS:当前容器状态;
- PORTS:当前容器所使用的默认端口号;
- NAMES:启动时给容器设置的名称。
另外,docker ps -参数,有:
- -a:查看所有容器,包括已停止运行的;
- -q:静默模式,只显示容器编号;
- -l:显示最近创建的容器;
- -n 3:显示最近创建的 num(此处为 3)个容器;
- –no-trunc:不截断输出,显示完整信息。
1 | # 可用通过如下命令查看容器中正在运行进程: |
1 | # 可用通过如下命令查看容器内部细节,返回为 json: |
进入容器
docker attach
1 | sudo docker attach 44fc0f0582d9 |
但在,使用该命令有一个问题。当多个窗口同时使用该命令进入该容器时,所有的窗口都会同步显示。如果有一个窗口阻塞了,那么其他窗口也无法再进行操作。
另外,官方文档中说 attach 后可以通过 CTRL-C 来 detach,但实际上经过我的测试,如果 container 当前在运行 bash,CTRL-C 自然是当前行的输入,没有退出;如果 container 当前正在前台运行进程,如输出 nginx 的 access.log日志,CTRL-C 不仅会导致退出容器,而且还 stop 了。这不是我们想要的,detach 的意思按理应该是脱离容器终端,但容器依然运行。好在 attach 是可以带上 –sig-proxy=false 来确保 CTRL-D 或 CTRL-C 不会关闭容器。
1 | docker attach --sig-proxy=false 7f237caad43b |
因为这些原因,所以docker attach命令不太适合于生产环境,平时自己开发应用时可以使用该命令。
ssh
在生产环境中排除了使用docker attach命令进入容器之后,相信大家第一个想到的就是ssh。在镜像(或容器)中安装SSH Server,这样就能保证多人进入容器且相互之间不受干扰了,相信大家在当前的生产环境中(没有使用Docker的情况)也是这样做的。但是使用了Docker容器之后不建议使用ssh进入到Docker容器内。
但是不建议,原因在这。
nsenter
在上面两种方式都不适合的情况下,还有一种比较方便的方法,即使用nsenter进入Docker容器。
关于什么是nsenter请参考如下文章:
https://github.com/jpetazzo/nsenter
在了解了什么是nsenter之后,系统默认将我们需要的nsenter安装到主机中
如果没有安装的话,按下面步骤安装即可(注意是主机而非容器或镜像)
具体的安装命令如下:
1 | $ wget https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz |
安装好nsenter之后可以查看一下该命令的使用。
nsenter可以访问另一个进程的名称空间。所以为了连接到某个容器我们还需要获取该容器的第一个进程的PID。可以使用docker inspect命令来拿到该PID。
docker inspect命令使用如下:
1 | $ sudo docker inspect --help |
inspect命令可以分层级显示一个镜像或容器的信息。比如我们当前有一个正在运行的容器
可以使用docker inspect来查看该容器的详细信息。
1 | $ sudo docker inspect 44fc0f0582d9 |
由其该信息非常多,此处只截取了其中一部分进行展示。如果要显示该容器第一个进行的PID可以使用如下方式
1 | $ sudo docker inspect -f {{.State.Pid}} 44fc0f0582d9 |
在拿到该进程PID之后我们就可以使用nsenter命令访问该容器了。
1 | $ sudo nsenter --target 3326 --mount --uts --ipc --net --pid |
其中的3326即刚才拿到的进程的PID
当然,如果你认为每次都输入那么多参数太麻烦的话,网上也有许多做好的脚本供大家使用。
地址如下:
http://yeasy.gitbooks.io/docker_practice/content/container/enter.html
http://www.tuicool.com/articles/eYnUBrR
exec
除了上面几种做法之外,docker在1.3.X版本之后还提供了一个新的命令exec用于进入容器,这种方式相对更简单一些。
1 | sudo docker exec -it 775c7c9ee1e1 /bin/bash |
exec和attach的区别:
- attach:直接进入容器启动命令的终端,不会启动新的进程;
- exec:在容器中打开新的终端,并且可以启动新的进程,可在宿主机中直接执行操作容器的命令,比如
docker exec -it 7f237caad43b ls /tmp
列出容器 /tmp 目录下的文件
容器和宿主机互相拷贝文件
宿主机拷贝文件到容器:
1 | docker cp 文件 container-id:目标文件/文件夹 |
从容器拷贝文件到宿主机:
1 | docker cp container-id:目标文件/文件夹 宿主机目标文件/文件夹 |
停止、启动、重启容器
1 | # 通过以下命令来停止运行中的容器,停止后需要使用docker start命令来重新启动 |
容器退出(或者显示调用docker stop)后会进入终止(exited)状态,此时可以通过 docker ps -a 查看,其中数据不会丢失,还可以通过docker start 来启动,只有删除容器才会清除数据。
删除容器
1 | # 删除单个容器: |
另,docker rm -参数含义:
- -f:强制删除,如果在运行中,先停止,再删除
查看容器日志
1 | # 查看当前容器日志,可通过如下命令: |
另,docker logs -参数含义:
- -t:加入时间戳;
- -f:跟随最新的日志打印;
- –tail:显示最后多少条。
端口映射
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P(大写) 或 -p (小写) 参数来指定端口映射。启动容器的时候如果不指定对应参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。
Docker 的端口映射通过 -p 或 -P 参数实现,-p 和 -P 区别为:
- -P : 随机映射一个49000~49900的端口到内部容器开放的网络端口
- -p : 可以指定要映射的IP和端口,但是在一个指定端口上只可以绑定一个容器
1 | # 把主机端口 8888 请求映射到 Docker 容器内部端口 8080 |
使用docker ps
查看端口映射情况,访问localhost:8080和localhost:8888,发现前者无法访问,后者可以。
端口映射格式
- ip:hostport:containerport #指定ip、指定主机port、指定容器port
1
2docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
指定映射使用一个特定地址,比如 localhost地址 127.0.0.1 - ip::containerport #指定ip、未指定主机port、指定容器port
1
2
3
4docker run -d -p 127.0.0.1::5000 training/webapp python app.py
绑定 localhost 的任意端口到容器的 5000 端口,本地主机会自动分配一个端口
还可以使用 udp 标记来指定 udp 端口
docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py - hostport:container #未指定ip port、指定主机port、指定容器port
1
2docker run -d -p 5000:5000 training/webapp python app.py
将本地的 5000 端口映射到容器的 5000 端口,默认会绑定本地所有接口上的所有地址
查看端口映射
1 | # 可以通过如下命令查看容器映射了哪些端口及协议: |
如果返回空表示没有进行端口映射。
QA
- docker ps(等其他指令)没有响应
我的情况比较简单,是因为云主机内存不够了,所以没有响应。但是如果是某个容器出问题了就需要另外想办法了:docker之 docker ps无响应。
- 连接进入docker
https://stackoverflow.com/questions/30172605/how-to-get-into-a-docker-container
- 查看Docker日志
https://docs.docker.com/config/daemon/#read-the-logs
https://takacsmark.com/docker-logs/
docker logs <Container-ID>
、docker service logs <Service-ID>
命令能查看某一容器或服务的日志,但是怎么查看Docker守护进程的执行日志?
docker logs 和 docker service logs 命令会显示类似于终端中交互式运行命令的输出。UNIX 和 Linux 命令在运行时通常会打开三个 I/O 流,分别称为 STDIN,STDOUT 和 STDERR。STDIN 是命令的输入流,可能包括来自键盘的输入或来自另一个命令的输入。STDOUT 通常是命令的正常输出,而 STDERR 通常用于输出错误消息。默认情况下,docker logs 显示命令的 STDOUT 和 STDERR。
https://stackoverflow.com/questions/30969435/where-is-the-docker-daemon-log
比如Ubuntu:sudo journalctl -fu docker.service
- 运行容器直接退出
使用docker-logs命令(或者使用docker ps -a查看正在运行中的容器)发现容器启动后直接退出,原因是Docker容器后台运行,就必须有一个前台进程,进程结束,容器就会退出,如果不是那些一直挂起的命令(eg. top, tail等),就会自动退出。
正常情况下,启动服务只需启动相应的service即可,例如:service nginx start && service php5-fpm start
,但是这样做的话nginx和fpm均以后台进程模式运行,就会导致docker前台没有正在运行的应用,因此容器会立即自杀,因为已经没有事情能做了。
* 将要运行的程序以前台进程的形式运行,如果容器需要同时启动多个进程,那么只需要将其中的一个挂起到前台即可,比如对上面所说的 web 容器,只需要将启动指令修改为`service php5-fpm start && nginx -g "daemon off;"`
* 对于不知道怎么在前台运行的程序,只需要在启动的命令后面添加类似tail、top这种可以前台运行的程序,以上面所说的web容器为例,可以写成`service nginx start && service php5-fpm start && tail -f /var/log/nginx/error.log`,又如,在启动centos/ubuntu容器的时候,可以做一个死循环,持续输出任意内容,这样容器就不会认为没事可做而自杀了:`docker run -d centos /bin/sh -c "while true; do echo hello world; sleep 1; done"`。
* 可以加上-it选项,-i 开启了input(输入)功能 -t开启了一个连接容器里边的terminal(终端)(是不是相当于在容器内开启了一个bash进程?),如果希望在后台运行,可以再加上-d选项。
一般来说我们不推荐在同一个容器内运行多个应用程序,这会影响容器的效率,因此最后一种方法是最好的。
- 日志中出现 not support swap limit capabilities
https://segmentfault.com/q/1010000002888521
- 开启服务失败(elasticsearch)
在查看日志(journalctl -fu docker.service)的时候发现是内存不够用,在docker-compose.yml中去掉resources配置项就可以了。
如果没有设置限制,也有可能是物理机本身内存不够了,实在不行可以创建swap来凑空间。
- 使用远程主机作为docker-machine时“connection refused”
调用docker-machine env <docker-machine名>出现连接失败的情况
查看了一些资料但并没有什么卵用,/etc/default/docker这个文件根本没有被读进去。
据说是因为docker的daemon默认监听fd,而远程连接时用的是tcp。
根据这里的说明修改配置文件,但是发现无法重启docker服务,查看daemon日志发现-H的配置项重复了,而/etc/systemd/system/下并没有发现配置文件,在这里了解到原来配置文件还有一个位置/lib/systemd/system/,将相关内容(”-H fd://“)去掉就行了。
但是这样配置完毕后仍然不能在ECS上创建Docker Machine,最后采用了generic驱动成功了,具体配置见我的另一篇[App & Cloud]。
就优先级来说,/etc/docker/daemon.json > /etc/systemd/system/ > /lib/systemd/system/,优先级高的会覆盖低的。
- 对阿里云美西节点 docker-machine create 时 tls: DialWithDialer timed out
刚开始我也试着regenerate-certs了,但没什么卵用。
在stackoverflow上查不到相关的讨论。
官网相关页面上也没有。
github上对这个问题的讨论,也有了解决,在代码里把超时时间改大了,但还是写死的,我在连接美国服务器时仍然超时。
有的人提议提议将超时时间作为一个命令行参数提供出来,但是下面一个傻逼一直坚持说“suggest retrying”。
最后还是放弃了在这个节点上部署docker。
PS:在这里看到开发者的想法:用户是愚蠢的,不能把危险的选项公布出来,所以TLS任何方法不能关闭!WTF!!!随便搜索了一遍releases列表,没有发现对TLS的更改,所以我彻底放弃了。
- 如何让Docker启动时执行自定义命令/脚本?
如果官方提供的Dockerfile已经内置了这个功能是最好的(比如MySQL官方Dockerfile),如果没有的话,就需要自定义Dockerfile了(How to run bash command after startup?),ENTRYPOINT和CMD命令都可以用于执行命令,CMD会被接到ENTRYPOINT后面,主要用于提供一些可变的参数,docker run后面的参数其实就是CMD。
在docker-compose.yml文件中不适合自定义执行命令,command命令可以定义启动时命令,但是只能定义一条,我尝试command: a.sh && b.sh,结果也只是执行了前面的指令,所以就不要白费力气了。
-
官方镜像已有提供该功能
使用volumes将宿主机文件夹挂载到容器内
自定义镜像
怎么查看容器的启动命令
在宿主机上
docker inspect
在容器内部
ps -fe
,其中1号进程就是启动命令(有可能出现ps命令找不到的情况)查看这个镜像的Dockerfile,其中的ENTRYPOINT和CMD指定了容器的运行时执行命令
如何临时退出一个正在交互的容器的终端,而不终止它?
按Ctrl+p,后按Ctrl+q,如果按Ctrl+c会使容器内的应用进程终止,进而会使容器终止。
- 使用docker port 命令映射容器的端口时,系统报错Error: No public port ‘80’ published for …,是什么意思?
创建镜像时Dockerfile要指定正确的EXPOSE的端口,容器启动时指定PublishAllport=true
可以在一个容器中同时运行多个应用进程吗?
一般不推荐在同一个容器内运行多个应用进程,如果有类似需求,可以通过额外的进程管理机制,比如supervisord来管理所运行的进程如何控制容器占用系统资源(CPU,内存)的份额?
在使用docker create命令创建容器或使用docker run 创建并运行容器的时候,可以使用-c|–cpu-shares[=0]参数来调整同期使用CPU的权重,使用-m|–memory参数来调整容器使用内存的大小。