1.1、Docker容器是什么
Docker容器运行起来本质上就是宿主机里面的一个进程。
1.2、容器的相关操作
| 操作 | 作用 | 
|---|---|
| docker run | 创建、启动一个容器 | 
| docker start | 启动指定容器 | 
| docker restart | 重新启动指定容器 | 
| docker stop | 停止指定容器 | 
| docker ps | 查看容器的状态 | 
| docker cp | 容器和宿主之间复制文件 | 
| docker exec | 执行容器中的命令 | 
| docker attach | 进入容器中 | 
| docker rm | 删除指定容器 | 
| docker inspect | 查看指定容器的详细信息 | 
| docker logs | 查看容器的操作日志 | 
1.3、进入容器的正确姿势
注意:最好不要进入容器进行操作,尤其是在生产环境中!
有时候, 我们只是想要频繁的使用Ubuntu、CentOS等容器,并且也不是生产环境, 那么进入容器去执行命令是个好主意!
进入容器的方式主要有四种:
- 使用docker exec -it <containerName|containerId> /bin/bash
 - 使用docker attach <containerName|containerId>
 - 使用SSH
 - 使用nsenter
 
1.3.1、docker exec
执行一个容器里面的命令。
docker exec的帮助:

docker exec的使用格式如下:
docker exec的参数与docker run的参数有几个是相同的意思:
| 参数 | 说明 | 
|---|---|
| -u <username|uid> | 指定进入的容器以哪个用户登陆,默认是root | 
| -e <key=value> | 设置进入后可以使用的环境变量,这样动态指定比较灵活 | 
| -d | 以后台方式执行,这样,我们执行完这条命令,还可以干其他事情,写脚本最常用 | 
| -i | 以交互方式运行,是阻塞式的 | 
| -t | 分配一个伪终端,这个参数通常与-i参数一起使用,然后, 在后面跟上容器里的/bin/bash,这样就把我们带到容器里去了。 | 
示例1:
sudo docker exec -u leleliu008 ubuntu ls ~
直接在宿主机中执行名称为ubuntu的容器里面的leleliu008用户的ls ~命令, 这样可以避免进入容器后再出来的时候,引起不必要的麻烦!
示例2:
sudo docker exec -u leleliu008 -it 3dada3f22bd2 /bin/bash
进入ubuntu的容器里面,以leleliu008用户权限!
退出容器要注意了,千万别使用exit命令,如果使用了exit命令,退出容器的同时把容器也停止掉了, 要想退出容器,还让容器继续运行,就得先使用CTRL + P再使用CTRL + Q快捷键, 这样就是退出容器但不停止容器!
如果该容器是生产环境的服务器,一不小心使用了exit命令,把容器给停止了,那就事大了!!所以,不建议直接进入容器做事情, 保险的方法还是在宿主机器中执行容器里的命令!
这么长的命令,每次都要敲多麻烦,实际上,你可以使用alias来给这么长的命令取个别名, 放在环境变量里面,以后就直接使用别名代替那么长的命令,如下:
alias docker-enter-ubuntu='sudo docker exec -u leleliu008 ubuntu'
从这里也可以看出,为什么要给容器起一个名字,用名字而不是containerId,在服务器上, 一般只会使用一个镜像创建一个容器,不会安装很多,所以,用名字更容易写脚本,而用containerId每次都要修改脚本,很麻烦。
docker exec -it 8c86e41d870f /bin/bash 命令详解 docker exec # 执行容器内命令的Docker命令 -it # 选项参数(-i:交互式,-t:分配伪终端) # 不加 -it 会执行 bash 但无法交互,命令执行后立即退出。 # 加 -it:进入类似本机终端的交互式界面,显示 root@container:/# 提示符。 8c86e41d870f # 容器ID(唯一标识目标容器) /bin/bash # 要在容器内执行的命令(启动Bash shell) 核心功能:进入容器的操作系统环境,以交互方式执行命令。 /bin/bash 是容器内的 shell 解释器,指定它是为了获取完整的交互式命令行环境。 -it 选项确保输入输出交互正常,而容器镜像的多样性(如 Debian/Alpine)要求根据实际情况选择合适的 shell 路径。 掌握这些细节后,可更灵活地操作容器内部环境。 执行单次命令(如查看文件): docker exec <id> cat /etc/os-release
不同镜像的 shell 差异与替代方案
| 镜像类型 | 默认 shell 路径 | 进入容器的替代命令 | 
|---|---|---|
| Debian/Ubuntu | /bin/bash | docker exec -it <id> /bin/bash | 
| Alpine | /bin/sh(BusyBox) | docker exec -it <id> /bin/sh | 
| 自定义镜像 | 可能无 shell | 需先通过 docker exec 执行 /bin/sh 或安装 bash | 
1.3.2、docker attach
当一个容器以后台方式启动后,我们想要进入这个容器,除了使用docker exec命令外,还可以使用此命令。
docker attach的帮助:

docker attach的使用格式:
docker attach <containerName|containerId>
示例1:
sudo docker attach mysql5.7
示例2:
sudo docker attach 3dada3f22bd2
如果你在多个终端中使用这个命令进入容器,你在其中一个终端中执行的操作,在其他终端中是同步显示的, 这到底是好事情还是坏事情,每个人的看法不一样,不能一概而论就说他不好!但是这个命令经常会卡死,就可能与这个特性有关。 一旦在生产环境中卡死,事大了!!!所以不建议使用这个命令!
再次强调:退出容器千万别使用exit命令,如果使用了exit命令,退出容器的同时把容器也停止掉了, 要想退出容器,还让容器继续运行,就得先使用CTRL + P再使用CTRL + Q快捷键, 这样就是退出容器但不停止容器!
如果该容器是生产环境的服务器,一不小心使用了exit命令,把容器给停止了,那就事大了!!所以,不建议直接进入容器做事情, 保险的方法还是在宿主机器中执行容器里的命令!
1.3.3、使用SSH进入容器
既然docker exec和docker attach进入容器这两种方法不推荐使用, 相信大家第一个想到的就是SSH。在没有使用Docker之前, 我们就是用SSH进入服务器进行操作的。
很不幸,仍然是不建议使用SSH进入到容器。至于为什么不建议这么做,请参考下面这篇文章:https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker
中文翻译版本:http://www.oschina.net/translate/why-you-dont-need-to-run-sshd-in-docker
建议你看原文,尤其是下面的帖子回复!非常精彩!
1.3.4、使用nsenter进入容器
nsenter是util-linux中的一个小工具, 所以,要使用它,就先安装util-linux。
安装完util-linux后,我们查看nsenter的使用帮助:

nsenter可以访问另一个进程的名称空间。而容器运行起来实际上就是宿主机器的一个进程。 所以为了进入某个容器我们还需要获取该容器的第一个进程的PID。 可以使用docker inspect命令来拿到该信息,如下:
sudo docker inspect -f {{.State.Pid}} ubuntu
这样,我们就可以使用nsenter命令访问该容器了:
sudo nsenter --target 3326 --mount --uts --ipc --net --pid
这么长的命令谁能记住呢?幸好有人帮我们做了简化工作,我们可以下载一个bash脚本.bashrc_docker,把它放到环境变量里面就可以使用了。
wget -P ~ https://github.com/yeasy/docker_practice/raw/master/_local/.bashrc_docker; echo "[ -f ~/.bashrc_docker ] && . ~/.bashrc_docker" >> ~/.bashrc; source ~/.bashrc
这个文件中定义了很多方便使用Docker的命令,例如docker-pid可以获取某个容器的PID; 而docker-enter可以进入容器或直接在容器内执行命令。
docker-pid <containerName|containerId> docker-enter <containerName|containerId>
1.4、容器之间的通信
一个Docker容器就是一个宿主机上的进程!所以,一般一个容器就是一个应用,比如, 我们使用nginx + Tomcat + MySQL开发一个Java Web应用, 我们使用Docker容器应该如何部署呢?
假设我们的这个Java Web App的名字叫做app1,所以,我们在创建容器的时候, 给容器命名的时候,都以app1开头,这样我们就很容器看出容器是哪个app使用了。
当然,nginx、Tomcat、MySQL这些软件都是可以被多个app同时使用的, 为了便于管理,我们就不共用了。
假设我们这里的nginx做负载均衡使用,MySQL使用了主从复制。
下面是app1的架构图:

我们需要创建5个Docker容器:
app1-mysql-master用作MySQL主服务器。
app1-mysql-slave用作MySQL从服务器。
app1-tomcat-load-1是第一个负载。它肯能访问app1-mysql-master容器, 也可能访问app1-mysql-slave容器。
app1-tomcat-load-2是第二个负载。它肯能访问app1-mysql-master容器, 也可能访问app1-mysql-slave容器。
app1-nginx是负载均衡调度器。 它只访问app1-tomcat-load-1和app1-tomcat-load-2这两个容器。
1.4.1、link机制实现容器之间的通信
很不幸,这种方式已经不建议使用了,被更好的方法取代了。官方为了兼容老版本, 这个特性仍然保留了,但是很可能在以后的某个版本被彻底废弃!
link原理图:

要link的容器被称为received-container。
被link的容器被称为source-container。
received-container容器可以访问source-container容器。
实现方式是通过docker run命令的--link <source-containerName|source-containerId:source-containerAlias>参数。
我们使用link机制实现一下这个例子:
1、创建app1-mysql-master容器:
sudo docker run -d -p 3306:3306 -v ~/app1/mysql/data/master:/usr/local/mysql/data --name app1-mysql-master mysql:5.6
2、创建app1-mysql-slave容器:
sudo docker run -d -p 3307:3306 -v ~/app1/mysql/data/slave:/usr/local/mysql/data --name app1-mysql-slave mysql:5.6
3、创建app1-tomcat-load-1容器:
sudo docker run -d -p 8080:8080 -v ~/app1/tomcat/load-1/log:/usr/local/tomcat/log --name app1-tomcat-load-1 --link app1-mysql-master:app1-mysql-master --link app1-mysql-slave:app1-mysql-slave tomcat:8
4、创建app1-tomcat-load-2容器:
sudo docker run -d -p 8081:8080 -v ~/app1/tomcat/load-2/log:/usr/local/tomcat/log --name app1-tomcat-load-2 --link app1-mysql-master:app1-mysql-master --link app1-mysql-slave:app1-mysql-slave tomcat:8
5、创建app1-nginx容器:
sudo docker run -d -p 80:80 -v ~/app1/nginx/conf:/usr/local/nginx/conf --name app1-nginx --link app1-tomcat-load-1:app1-tomcat-load-1 --link app1-tomcat-load-2:app1-tomcat-load-2 nginx:1.11