Docker 学习

422 阅读9分钟

什么是Docker

有部署经验的人可能知道,在一台电脑上可以运行的程序拿到另外一台电脑上可能就运行不起来了。因为两台电脑的环境可能不一样,而Docker就是为了解决这种问题而诞生的。简单来说,Docker就是一个装应用的容器,我们可以将任何形式的程序放入其中,为这些程序创建一个轻量级的、可移植的、自给自足的容器。这样我们在自己电脑上编译测试通过的容器就可以批量地在生产环境中部署。

Docker思想

Docker 的图标是一条运输集装箱的鲸鱼,这个图标很贴切地描述了 Docker 的核心思想:

  1. 集装箱
  2. 标准化
  3. 隔离
  • 集装箱
    如果我们把程序看成是货物,那么程序的迁移过程就可以看成是货物的运输。过去我们迁移程序,需要将程序、数据单独拷贝到新环境并安装各种应用依赖,这相当于零散地运输货物。而 Docker 将现实中集装箱运输的思想引入这一过程,将程序及其依赖的各种数据、运行环境等都装载到一个集装箱中整体进行迁移,避免了迁移后在新环境中可能产生的各种问题,大幅提高了程序迁移的效率。

  • 标准化
    Docker 还对程序的迁移中的各个环节都进行了标准化,具体可以细分为运输方式、存储方式和 API 接口的标准化。Docker 存在一个超级码头,在迁移程序时,只需先将装载程序的集装箱由鲸鱼运输到超级码头,然后再由鲸鱼将这些集装箱从超级码头运输到各自的目的地,这就是运输方式的标准化。过去迁移程序时,需要记录下程序迁移到的位置(目录),以便后续对其进行改动,而 Docker 将存储方式进行了标准化,使得用户无需再关注这些细节问题,只需运行一条命令就可以了。Docker 还提供了一系列接口,对容器中程序的运行、控制、查看、删除等操作都进行了标准化。

  • 隔离
    Docker 相当于为程序创建了一个轻量级的虚拟机,每个容器内的程序拥有独立的资源(CPU 和内存、磁盘等等),也可以进行独立的 IO。这就避免了因为某一个程序运行错误,例如陷入死循环,而大量占用 CPU、内存、磁盘,干扰到其他程序的正常运行。只需将程序运行在各自独立的容器中,即使一个容器中程序崩溃,其他容器中的程序依旧可以正常地运行。

镜像、仓库和容器

  • 镜像
    Docker 中的装载程序的集装箱被称为镜像,每一个镜像都包含了程序及其运行所需要的各种依赖环境。

  • 仓库
    运输过程中负责中转的超级码头称为仓库,仓库中存储了大量的 Docker 镜像,相当于堆积了大量集装箱的码头。

  • 容器
    容器就是运行程序的地方,或者说运行的镜像就是容器。

  • 运行过程
    通过 Docker 运行一个程序的过程就是:从仓库把镜像拉到本地,然后通过命令将镜像运行起来,变成容器。

Docker 镜像是一个分层的文件系统,或者说就是一堆文件的集合,它包含了程序及其依赖环境的所有文件,结构如下图所示:

可以看到,最下层是操作系统的引导文件,上面一层 Base Image 是一个 Linux 操作系统,再上面就是与我们程序相关的文件,每一个程序都可以添加一层,存储与这个应用相关的文件,这些程序层是我们可以控制的。最上面一层是运行时的容器,这不属于 Docker 镜像,Docker 镜像中所有的文件都是只读的,因而一个镜像是永久不会变的(镜像是用来创建容器的,Docker运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker 会从镜像仓库下载( 默认是 Docker Hub 公共注册服务器中的仓库))。

运行起来的镜像就是容器,可以把容器看成是一个虚拟机,只不过是采用分层的文件系统。如上图所示,容器包括下层只读的Docker镜像,以及最上层可以修改的镜像可写层,程序运行中所有对文件系统的修改都在这一层进行,这也是整个 Docker 容器中唯一可以修改的层。注意,在程序的运行过程中,难免要对 Docker 镜像中原有的文件做一些修改,而镜像是只读的,这怎么办呢?对于这种情况,Docker 会将这些文件拷贝到最上面的可写层,然后再做修改。Docker 在访问一个文件时,会从顶层开始查找,未找到才会查找下一层。因为这些被修改的文件已经被拷贝到了最上层,所以会访问这些文件的最新版本(因为容器是可以修改的,而镜像是不可以修改的,因而对于同一个镜像,我们可以生成多个不同的容器。它们独立运行,彼此没有干扰。)。

使用 Docker 的目的就是在不同的运行环境之间迁移程序,这个过程就需要使用 Docker 仓库:首先将镜像从源端传输到仓库,再从目标端将镜像拉取下来。Docker 其实就是一个存储管理镜像的服务器,Docker 官方就提供了 Docker Hub,国内也有许多公司提供了 Docker 仓库,例如网易蜂巢镜像中心。像 Ubuntu、CentOS、MySQL、tomcat 等常见的系统和软件,都有官方或者用户提供的 Docker 镜像。如果需要传输镜像是私有项目,或者需要保密,可以通过自己搭建镜像中心来完成。

Docker常用命令

  • Ubuntu下安装docker
apt-get install -y docker.io
  • 拉取镜像
docker pull [options] image_name[:tag]

options:镜像拉取的参数
tag:表示拉取的镜像的版本,缺省情况默认拉取latest最新版本
  • 查看本地镜像
docker images [options] [repository[:tag]

options:表示可选参数
repository:镜像的名称
tag:镜像的版本
  • 运行镜像
docker run -d [options] image_name[:tag] [command] [arg...]

options:可选参数,-d表示后台运行
tag:镜像的版本,本地可能存在镜像的多个版本
command:表示镜像运行起来后要运行什么命令
arg:命令对应的参数
  • 查看运行的容器状态
docker ps
  • 停止容器运行
docker stop image_name
  • 删除镜像
docker rmi -f image_name
  • 删除容器
docker rm -f name

name:容器名
  • 运行的容器打包为镜像
docker commit name

name:容器名称
  • 标记本地镜像
docker tag image_name standard_image_name

image_name:本地镜像名
standard_image_name:hub.c.163.com/{用户名}/{镜像名:版本}
  • 登录网易云镜像仓库
docker login 
  • 推送本地镜像到网易云镜像仓库
docker push hub.c.163.com/{用户名}/{镜像名:版本}

第一个Docker应用 - hello-world

  • 步骤一:拉取镜像
  • 步骤二:参看镜像
  1. repository:镜像的名称
  2. tag:镜像的版本
  3. image id:镜像的id(64位,默认显示前16位)
  4. created:镜像的创建时间
  5. size:镜像的大小
  • 步骤三:运行镜像

  • 步骤四:删除镜像

  • 运行hello-world,docker所做的操作:

  1. Docker 客户端连接到 Docker 后台进程
  2. Docker 后台进程从 Docker Hub 拉取了 hello-world 镜像。
  3. Docker 后台进程为 hello-world 镜像创建了一个新的容器,并且运行了一个可执行程序,产生我们看到的这些输出信息。
  4. Docker 后台进程将输出信息变成输出流传送到 Docker 客户端,再由 Docker 客户端将信息发送到我们的终端。
  • 图解运行Docker镜像的全过程:

运行Nginx镜像

  • 拉取镜像

  • 运行镜像

  • 查看容器状态

  • 查看容器内部信息

容器运行起来之后,我们经常还需要进入容器去查看程序运行的情况。这时可以使用 docker exec 命令在容器中运行指定的命令

docker exec [options] container command [arg...]

container:容器名
command:在容器中运行的命令
options:可选参数:-i 让容器的标准输入保持打开、-t 分配一个伪终端。
因为 nginx 容器环境为 Linux 系统,所以我们可以在终端内执行 ls、pwd 等常用的 Linux 命令
  • 停止容器运行

  • 端口映射
    至此我们已经成功地运行了 nginx 容器,但是还无法通过浏览器访问。Docker 容器的网络分为三种:桥接(Bridge)、宿主机共享(Host)和无网络(None):在 Bridge 模式下,Docker 会产生一个名为 docker0 的网桥与宿主机的网卡相连,然后每个容器再虚拟出自己的网卡与 docker0 相连,每个容器拥有自己的 IP 和端口;而在 Host 模式下,Docker 容器不会虚拟出自己的网卡,而是直接使用宿主机上的 IP 和端口,与宿主机共享网络环境。在 Bridge 模式下,Docker 可以在容器的端口与宿主机的端口之间建立映射,这样访问宿主机的端口就可以访问到容器的端口。(Docker 容器默认使用 Bridge 的网络连接方式。)

制作自己的镜像 - Java Web Jpress

docker pull hub.c.163.com/library/tomcat:latest

因为tomcat的运行需要Java环境的支持,所以tomcar镜像中包含了JDK,我们不需要重复添加,下面切换到jpress war包所在的目录,开始编写Dockfile
  • 生成Dockerfile文件
touch Dockerfile
  • 编辑Dockerfile文件
vim Dockerfile

from hub.c.163.com/library/tomcat
maintainer Beyond 1192126986@foxmail.com
copy jpress-web-newest.war /usr/local/tomcat/webapps

第一行通过from语句,指定在tomcat镜像的基础上构建镜像
第二行可选,添加镜像的作者名称和邮箱
第三行通过copy语句,将jpress war包拷贝到tomcat的webapps目录下,这样jpress就能跟随tomcat启动起来
  • 构建新镜像
docker build -t jpress:latest .

docker build :构建镜像
-t:指定镜像的名称和版本
.:表示Dockfile所在的路径
  • 查看构建的镜像
docker images
  • 运行Jpress镜像
docker run -d -p 8081:8080 jpress
  • 查看端口是否绑定
netstat -na | grep 8081
  • 浏览器访问jpress

  • 拉取MySQL镜像

docker pull hub.c.163.com/library/mysql:latest
  • 运行MySQL镜像
docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=jpress hub.c.163.com/library/mysql

-e:指定MySQL的环境变量,设置root用户的密码和创建数据库名称

推送本地镜像到个人镜像仓库

参考链接:如何构建镜像