原来虽然也用用docker,但一直都局限于docker pull、docker run完事,没有细致的去了解docker的运行机制以及docker-compose、dockerfile的编写等等,所以如何快速构建镜像、同时部署多个容器这一块始终是空白,这篇主要来填补一下这块空白。
![]()
Docker
docker作用及特点
docker官网的文档是这么描述docker这个技术的:
Docker provides the ability to package and run an application in a loosely isolated environment called a container. The isolation and security allow you to run many containers simultaneously on a given host. Containers are lightweight and contain everything needed to run the application, so you do not need to rely on what is currently installed on the host. You can easily share containers while you work, and be sure that everyone you share with gets the same container that works in the same way.
所以docker的核心是容器container
,这些容器之间独立且都运行在本地主机host
之上,并且这种容器非常便于分享、启停。安装也很简单,windows这里一般不用,一般都是Linux系统,直接apt-get
就行了,可以配置配置镜像加速源,就可以使用了。官方文档也给出了三个主要的应用场景:
- 一次性的环境,便于分享应用
- 弹性的云服务,得益于容器便于管理
- 便于组建微服务
你让我来说的话,其实排除掉你自己的应用(jar、war、go什么的),其实就俩部分:image
和container
,这俩的关系是这样的image -> container
,即容器是从镜像来的,而构建镜像的过程就是在为你的应用添加依赖、添加环境配置的过程。镜像构建好了是静态的,而容器是动态的,按照你构建好的镜像来创建一个动态的容器,并对这个容器做一些隔离(端口等),留出一些可以访问这个容器的途径,就可以使用这个容器了。
docker整体架构以及本地网络架构
docker属于C/S架构,我们一般好像都是本地直接命令docker xxxx
,实际上docker分为了docker client
和docker server
,所以你也可以去连接一个远程主机上的docker服务,来进行镜像和容器的管理。整体结构如下图:

常见命令
查看当前docker的环境配置变量
1 | # 查看client以及server的状态、配置(硬件、目录信息、镜像和容器、镜像源等等)、 |
搜索镜像
1 | # 查询指定名称的镜像 |
拉取指定的镜像
1 | # 拉取指定的名称:tag的镜像,你可以设定镜像源,官方地址:https://hub.docker.com |
查看当前仓库的所有镜像、查看镜像构建信息(包括基本信息(作者、父镜像等)、构建信息(EntryPoint、环境变量等))
1 | # list |
查看当前docker所有的容器(包括活动的、停止的)
1 | # 两个命令都可以 |
删除镜像
1 | # 比如docker rmi xxxxx或者docker rmi ubuntu:18.04 |
删除容器
1 | docker rm [ID] |
为一个镜像重新打标签
1 | # 如果出现依赖问题时,有时可能是tag导致的问题,比如拉取[name]:v1.4但是实际上镜像源只有[name]:1.4 |
Dockerfile
什么是Dockerfile?其实就是个文本文件,通过docker定义的几个关键字来描述你的需求,即通过文本指令来构建一个符合你需求的镜像,并依据这个镜像构造容器来搭建环境,达到快速部署、利于迭代的特点。
也可以把自己的应用比如jar包、war包啥的写入Dockerfile,构建你自己应用的镜像,并上传到公开仓库里供别人使用。
这里明确Dockerfile的目标是生成镜像而非生成容器。
如何编写Dockfile
首先我们来看一下常用的关键字及其含义:
关键字名称 | 关键字含义 | 例子及其释义 |
---|---|---|
FROM | 基础镜像 | FROM ubunu:18.04 -- 以ubuntu作为基础镜像进行改造 |
MAINTAINER | 维护者信息 | MAINTAINER floattest@test.com |
RUN | 在基础镜像基础上运行指定的命令 | RUN apt-get install wget git |
CMD | 相当于RUN,但是运行时间点不一样 | |
ENTRYPOINT | 类似CMD指令,但是不会被docker run的命令行所覆盖,并且这些命令行参数会被当做参数送给该指令指定的程序 | ENTRYPOINT ["app.jar",
"8080"]。 当然你也可以配合CMD命令来使用,使其为ENTRYPOINT传递可变参数:ENTRYPOINT["nginx", "-c"] CMD["/etc/nginx/nginx.conf"]。此时若执行 docker run nginx:test -c /etc/nginx/new.conf 则容器会执行nginx:test -c /etc/nginx/new.conf |
COPY | 将当前上下文目录中的文件复制到容器里面去 | COPY --chown=float:root
./app.jar。此外,可以使用--chown <user>:<group> 来指定该文件的拥有者和属组 |
ADD | 和COPY类似,但是有优点: 1.源文件为tar,且压缩格式为gzip、bzip、xz情况下会自动复制并解压到目标路径 |
ADD app.jar /home/float/app.jar |
WORKDIR | 相当于cd命令 | WORKDIR /home/float 切换到指定目录 |
USER | 相当于su <username> ,切换后续命令的执行用户 |
USER float 后续命令将使用float用户来执行 |
VOLUME | 定义匿名数据卷,启动容器时忘记挂载数据卷,会自动挂载到匿名卷。匿名数据卷可以防止容器逐渐变大、保护重要数据被重启影响 | VOLUME /opt/app_cache 格式:VOLUME <路径> |
EXPOSE | 声明端口,即这个服务的守护端口是什么,但是运行的时候会随机映射,除非你手动指定 | EXPOSE 8080 但是实际到主机是一个任意的端口,需要你自己去指定,这个关键字只是让你知道8080是守护端口 |
ONBUILD | 当前Dockerfile构建的镜像如果被集成到其他DockerFile的时候就会运行ONBUILD的指令,触发指令 | ONBUILD <其他指令> |
ENV | 设置环境变量,后续指令中就可以使用这个变量了 | ENV JAVA_HOME=/usr/local/jvm/jdk1.8.0_271/ |
ARG | 构建参数,作用于与ENV不通,只在当前Dockerfile中生效 | ARG PORT=8080 格式:ARG <参数名>[=<默认值>] |
一个Demo
这个Demo展示了一下更换apt源头并进行软件更新的过程,假设该文件名为ubuntuTest:
1 | FROM ubuntu:20.04 |
构建命令: docker build -f ubuntuTest -t myubuntu:0.1 .
这里有一个需要注意的点:docker
build命令的格式是这样的:Usage: docker build [OPTIONS] PATH | URL |
-,上面那个构建命令最后的.
即当前目录指的就是在build命令执行时的上下文目录,如果你需要使用到COPY命令的话这个参数是非常重要的,错误的上下会会导致找不到需要的文件。

在IDEA中使用(GoLand、PyCharm等同理)
新版本已经不需要手动去安装Docker插件了,没有的话自行在Pugins里安装。
这里我用一个SpringCloud的Eureka应用做Demo,代码很简单不做展示,主要说明流程。首先在项目根目录下创建Dockerfile:
1 | FROM openjdk:8-jdk-alpine3.9 |
然后我们在File | Settings | Build, Execution, Deployment | Docker
中添加我们的Docker服务器:

我这里使用的是本地windows的Docker
Desktop,你也可以使用Linux下的,红框处直接配置就可以了。然后我们在项目的Run/Debug Configuration
中添加Docker的执行方法:

红框处可以调整我们的构建参数、运行参数,然后最下面的Command preview
中可以预览即将执行的命令。Apply后我们运行就可以看到自动的根据我们的Dockerfile构建镜像、创建容器、启动容器:

可以看到成功的部署并且运行了,我们可以看一下docker中的信息:

可以看到正在运行中的容器以及我们构建好的镜像。
docker-compose
Ok,那上面说了Dockerfile了,方便吧?其实有更方便的,那就是docker-compose。这俩啥区别嘞?
你想想,Dockerfile是通过构建镜像->手动生成容器来进行工作的,那为啥不能把这两步直接合并,我直接docker-compose就能起来容器了多好?那这就是docker-compose的作用了,他的目标是容器,而非Dockerfile的目标镜像。
此外,扩展一下,如果你的环境不止一个镜像呢?每个镜像一个Dockerfile去构建,然后每次用的时候一个个手动创建容器?这也是个问题,而docker-compose就可以帮助你解决这个问题,通过编写docker-compose.yml文件来管理多个镜像、构造容器,实现一键启动环境。
上述特点也即官方总结的几个特点:
- Simplified control:管理多个容器
- Efficient collaboration:只需要YAML文本就可以复现
- Rapid application development:
- Portability across environments
- Extensive community and support
如何使用docker-compose
首先我们需要下载docker-compose,下面的项目主页里二进制版本也可以,或者就是使用pip来安装。下面也列出了官方详细的教程供学习。
- 官方项目主页:docker/compose: Define and run multi-container applications with Docker
- 官方教程:Overview of Docker Compose | Docker Documentation
推荐使用pip的方式安装,比较方便:
更新pip: python3 -m pip install --upgrade pip
安装docker-compose: python3 -m pip install docker-compose (Python >= 3.6)
docker-compose的命令十分简单,常用的就两个(在项目路径下)
- docker-compose up -- 创建并启动容器
- docker-compose down -- 停止并删除容器
当然前面也说了,docker-compose是配合你的dockerfile来进行工作的,实现多镜像多容器的自动管理。所以重头戏是怎么写docker-compose.yml
这个文件,那我们接下来就看一下如何编写。
如何编写docker-compose.yml
由于docker-compose由多个小单元构成(我这里指的单元就是service),所以我们先直接来看一个官方的例子迅速了解一下docker-compose.yml如何编写:
1 | version: "3.8" |