使用 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
2
# 查看运行中的容器列表
docker ps

输出内容中:

  • CONTAINER ID:启动时生成的 ID;
  • IMAGE:该容器使用的镜像;
  • COMMAND:容器启动时执行的命令;
  • CREATED:容器创建时间;
  • STATUS:当前容器状态;
  • PORTS:当前容器所使用的默认端口号;
  • NAMES:启动时给容器设置的名称。

另外,docker ps -参数,有:

  • -a:查看所有容器,包括已停止运行的;
  • -q:静默模式,只显示容器编号;
  • -l:显示最近创建的容器;
  • -n 3:显示最近创建的 num(此处为 3)个容器;
  • –no-trunc:不截断输出,显示完整信息。
1
2
# 可用通过如下命令查看容器中正在运行进程:
docker top container-id/container-top
1
2
# 可用通过如下命令查看容器内部细节,返回为 json:
docker insepct container-id

进入容器

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
2
3
4
5
6
$ wget https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz  
$ tar -xzvf util-linux-2.24.tar.gz
$ cd util-linux-2.24/
$ ./configure --without-ncurses
$ make nsenter
$ sudo cp nsenter /usr/local/bin

安装好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
2
$ sudo nsenter --target 3326 --mount --uts --ipc --net --pid  
$ 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
2
3
4
docker cp 文件 container-id:目标文件/文件夹
eg.
docker cp /tmp/suzhuji.txt 7f237caad43b:/tmp
将宿主机tem文件夹下suzhujia.txt文件拷贝到容器7f237caad43b中tmp目录中

从容器拷贝文件到宿主机:

1
2
3
4
docker cp container-id:目标文件/文件夹 宿主机目标文件/文件夹
eg.
docker cp 7f237caad43b:/tmp/yum.log /tmp
将容器7f237caad43b中tmp目录下yum.log拷贝到宿主机/tmp目录下

停止、启动、重启容器

1
2
3
4
5
6
7
8
9
10
# 通过以下命令来停止运行中的容器,停止后需要使用docker start命令来重新启动
docker stop container-name/container-id
# 强制停止容器(类似强制关机):
docker kill container-name/container-id
# 停止所有正在运行的容器
docker kill $(docker ps -q)
# 通过以下命令启动容器:
docker start container-name/container-id
# 通过以下命令启动容器:
docker restart container-name/container-id

容器退出(或者显示调用docker stop)后会进入终止(exited)状态,此时可以通过 docker ps -a 查看,其中数据不会丢失,还可以通过docker start 来启动,只有删除容器才会清除数据。

删除容器

1
2
3
4
5
6
# 删除单个容器:
docker rm container-id
# 删除多个容器:
docker rm container-id container-id
# 删除所有容器:
docker rm $(docker ps -a -q )

另,docker rm -参数含义:

  • -f:强制删除,如果在运行中,先停止,再删除

查看容器日志

1
2
# 查看当前容器日志,可通过如下命令:
docker logs container-id/container-name

另,docker logs -参数含义:

  • -t:加入时间戳;
  • -f:跟随最新的日志打印;
  • –tail:显示最后多少条。

端口映射

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P(大写) 或 -p (小写) 参数来指定端口映射。启动容器的时候如果不指定对应参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。
Docker 的端口映射通过 -p 或 -P 参数实现,-p 和 -P 区别为:

  • -P : 随机映射一个49000~49900的端口到内部容器开放的网络端口
  • -p : 可以指定要映射的IP和端口,但是在一个指定端口上只可以绑定一个容器
1
2
3
# 把主机端口 8888 请求映射到 Docker 容器内部端口 8080
docker run --name tomcat1 -d tomcat
docker run --name tomcat2 -d -p 8888:8080 tomcat

使用docker ps查看端口映射情况,访问localhost:8080和localhost:8888,发现前者无法访问,后者可以。

端口映射格式

  1. ip:hostport:containerport #指定ip、指定主机port、指定容器port
    1
    2
    docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
    指定映射使用一个特定地址,比如 localhost地址 127.0.0.1
  2. ip::containerport #指定ip、未指定主机port、指定容器port
    1
    2
    3
    4
    docker 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
  3. hostport:container #未指定ip port、指定主机port、指定容器port
    1
    2
    docker run -d -p 5000:5000 training/webapp python app.py
    将本地的 5000 端口映射到容器的 5000 端口,默认会绑定本地所有接口上的所有地址

查看端口映射

1
2
# 可以通过如下命令查看容器映射了哪些端口及协议:
docker port container-id

如果返回空表示没有进行端口映射。

QA

  1. docker ps(等其他指令)没有响应

我的情况比较简单,是因为云主机内存不够了,所以没有响应。但是如果是某个容器出问题了就需要另外想办法了:docker之 docker ps无响应

  1. 连接进入docker

https://stackoverflow.com/questions/30172605/how-to-get-into-a-docker-container

  1. 查看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

查看已经退出的容器的日志

  1. 运行容器直接退出

使用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选项。

一般来说我们不推荐在同一个容器内运行多个应用程序,这会影响容器的效率,因此最后一种方法是最好的。

  1. 日志中出现 not support swap limit capabilities

https://segmentfault.com/q/1010000002888521

  1. 开启服务失败(elasticsearch)

在查看日志(journalctl -fu docker.service)的时候发现是内存不够用,在docker-compose.yml中去掉resources配置项就可以了。

如果没有设置限制,也有可能是物理机本身内存不够了,实在不行可以创建swap来凑空间。

  1. 使用远程主机作为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/,优先级高的会覆盖低的。

  1. 对阿里云美西节点 docker-machine create 时 tls: DialWithDialer timed out

刚开始我也试着regenerate-certs了,但没什么卵用

在stackoverflow上查不到相关的讨论

官网相关页面上也没有。

github上对这个问题的讨论,也有了解决,在代码里把超时时间改大了,但还是写死的,我在连接美国服务器时仍然超时。

有的人提议提议将超时时间作为一个命令行参数提供出来,但是下面一个傻逼一直坚持说“suggest retrying”。

最后还是放弃了在这个节点上部署docker。

PS:在这里看到开发者的想法:用户是愚蠢的,不能把危险的选项公布出来,所以TLS任何方法不能关闭!WTF!!!随便搜索了一遍releases列表,没有发现对TLS的更改,所以我彻底放弃了。

  1. 如何让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,结果也只是执行了前面的指令,所以就不要白费力气了。

  1. docker 中怎么修改应用的配置?

    • 官方镜像已有提供该功能

    • 使用volumes将宿主机文件夹挂载到容器内

    • 自定义镜像

  2. 怎么查看容器的启动命令

    • 在宿主机上docker inspect

    • 在容器内部ps -fe,其中1号进程就是启动命令(有可能出现ps命令找不到的情况)

    • 查看这个镜像的Dockerfile,其中的ENTRYPOINT和CMD指定了容器的运行时执行命令

  3. 如何临时退出一个正在交互的容器的终端,而不终止它?

按Ctrl+p,后按Ctrl+q,如果按Ctrl+c会使容器内的应用进程终止,进而会使容器终止。

  1. 使用docker port 命令映射容器的端口时,系统报错Error: No public port ‘80’ published for …,是什么意思?

创建镜像时Dockerfile要指定正确的EXPOSE的端口,容器启动时指定PublishAllport=true

  1. 可以在一个容器中同时运行多个应用进程吗?
    一般不推荐在同一个容器内运行多个应用进程,如果有类似需求,可以通过额外的进程管理机制,比如supervisord来管理所运行的进程

  2. 如何控制容器占用系统资源(CPU,内存)的份额?
    在使用docker create命令创建容器或使用docker run 创建并运行容器的时候,可以使用-c|–cpu-shares[=0]参数来调整同期使用CPU的权重,使用-m|–memory参数来调整容器使用内存的大小。

参考

容器

  1. 命令
  2. Docker 分配宿主机网段 IP
  3. docker容器网络通信原理分析
  4. Service 之间如何通信?- 每天5分钟玩转 Docker 容器技术(101)

容器编排

Swarm基本使用

  1. Play with Docker classroom: Service Discovery under Docker Swarm Mode

  2. 运维之我的docker-swarm集群中删除节点和服务

  3. docker swarm 搭建及跨主机网络互连案例分析

  4. Docker Swarm 入门一篇文章就够了

Swarm运维

  1. Docker 引擎的 Swarm 模式:入门教程

  2. 生产环境中使用Docker Swarm的一些建议

  3. 云计算之路-阿里云上:重启 manager 节点引发 docker swarm 集群宕机

Swarm原理

  1. Consul入门

  2. Service Discovery with Docker and Consul: part 1

  3. Docker Reference Architecture: Universal Control Plane 2.0 Service Discovery and Load Balancing

  4. 浮动IP(FLOAT IP)

持续部署

  1. Docker持续部署图文详解

服务发现

  1. docker swarm获取客户端IP

OpenStack(开源云计算平台)

  1. KVM and Docker LXC Benchmarking with OpenStack

  2. OpenStack

  3. OpenStack-Docker-wiki

客户端

  1. spotify/docker-client
  2. how to copy file from file system into running container
  3. 单元测试 DefaultDockerClientTest.java