f10@t's blog

docker-compose及dockerfile总结

字数统计: 2.7k阅读时长: 10 min
2021/07/06

原来虽然也用用docker,但一直都局限于docker pull、docker run完事,没有细致的去了解docker的运行机制以及docker-compose、dockerfile的编写等等,所以如何快速构建镜像、同时部署多个容器这一块始终是空白,这篇主要来填补一下这块空白。

logo

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什么的),其实就俩部分:imagecontainer,这俩的关系是这样的image -> container即容器是从镜像来的,而构建镜像的过程就是在为你的应用添加依赖、添加环境配置的过程。镜像构建好了是静态的,而容器是动态的,按照你构建好的镜像来创建一个动态的容器,并对这个容器做一些隔离(端口等),留出一些可以访问这个容器的途径,就可以使用这个容器了。

docker整体架构以及本地网络架构

docker属于C/S架构,我们一般好像都是本地直接命令docker xxxx,实际上docker分为了docker clientdocker server,所以你也可以去连接一个远程主机上的docker服务,来进行镜像和容器的管理。整体结构如下图:

architecture

常见命令

查看当前docker的环境配置变量

1
2
# 查看client以及server的状态、配置(硬件、目录信息、镜像和容器、镜像源等等)、
docker info

搜索镜像

1
2
3
# 查询指定名称的镜像
docker search [name]
# 如查询nginx:docker search nginx

拉取指定的镜像

1
2
3
# 拉取指定的名称:tag的镜像,你可以设定镜像源,官方地址:https://hub.docker.com
docker pull [name]:[tag]
# 如docker pull ubuntu:18.04

查看当前仓库的所有镜像、查看镜像构建信息(包括基本信息(作者、父镜像等)、构建信息(EntryPoint、环境变量等))

1
2
3
4
# list
docker images
# 查看镜像构建信息
docker inspect [ID]

查看当前docker所有的容器(包括活动的、停止的)

1
2
3
# 两个命令都可以
docker ps -a
docker container ls -a

删除镜像

1
2
# 比如docker rmi xxxxx或者docker rmi ubuntu:18.04
docker rmi [ID]or[name:tag]

删除容器

1
docker rm [ID]

为一个镜像重新打标签

1
2
3
4
5
6
7
# 如果出现依赖问题时,有时可能是tag导致的问题,比如拉取[name]:v1.4但是实际上镜像源只有[name]:1.4
# 先拉取这个需要的镜像
docker pull xxxx:[old_tag]
# 重新打tag
docker tag xxxx:[old_tag] xxxx:[new_tag]
# 删除原有镜像
docker rmi xxxx:[old_tag]

Dockerfile

什么是Dockerfile?其实就是个文本文件,通过docker定义的几个关键字来描述你的需求,即通过文本指令来构建一个符合你需求的镜像,并依据这个镜像构造容器来搭建环境,达到快速部署、利于迭代的特点。

也可以把自己的应用比如jar包、war包啥的写入Dockerfile,构建你自己应用的镜像,并上传到公开仓库里供别人使用。

这里明确Dockerfile的目标是生成镜像而非生成容器

如何编写Dockfile

首先我们来看一下常用的关键字及其含义:

关键字名称 关键字含义 例子及其释义
FROM 基础镜像 FROM ubunu:18.04 -- 以ubuntu作为基础镜像进行改造
MAINTAINER 维护者信息 MAINTAINER float
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FROM ubuntu:20.04

MAINTAINER lzwgiter<float311@163.com>

# 自定义环境变量
ENV APTPATH /etc/apt

# 切换地址
WORKDIR $APTPATH

RUN cp -a /etc/apt/sources.list /etc/apt/sources.list.bak
RUN sed -i "s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list
RUN sed -i "s@http://.*security.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list
RUN apt-get update

EXPOSE 22

CMD echo "END"

构建命令: docker build -f ubuntuTest -t myubuntu:0.1 .

这里有一个需要注意的点:docker build命令的格式是这样的:Usage: docker build [OPTIONS] PATH | URL | -,上面那个构建命令最后的.即当前目录指的就是在build命令执行时的上下文目录,如果你需要使用到COPY命令的话这个参数是非常重要的,错误的上下会会导致找不到需要的文件。

image-20210808120831016

在IDEA中使用(GoLand、PyCharm等同理)

新版本已经不需要手动去安装Docker插件了,没有的话自行在Pugins里安装。

这里我用一个SpringCloud的Eureka应用做Demo,代码很简单不做展示,主要说明流程。首先在项目根目录下创建Dockerfile:

1
2
3
4
FROM openjdk:8-jdk-alpine3.9
MAINTAINER lzwgier<float311@163.com>
COPY ./target/Eureka-1-0.0.1-SNAPSHOT.jar Eureka-1.jar
ENTRYPOINT ["java", "-jar", "Eureka-1.jar", "&"]

然后我们在File | Settings | Build, Execution, Deployment | Docker中添加我们的Docker服务器:

image-20210810151857295

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

image-20210810152240025

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

image-20210810153513632

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

image-20210810153737315

可以看到正在运行中的容器以及我们构建好的镜像。

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来安装。下面也列出了官方详细的教程供学习。

推荐使用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
2
3
4
5
6
7
8
9
10
11
12
13
version: "3.8"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
image: redis
volumes:
logvolume01: {}

CATALOG
  1. 1. Docker
    1. 1.1. docker作用及特点
    2. 1.2. docker整体架构以及本地网络架构
    3. 1.3. 常见命令
  2. 2. Dockerfile
    1. 2.1. 如何编写Dockfile
    2. 2.2. 一个Demo
    3. 2.3. 在IDEA中使用(GoLand、PyCharm等同理)
  3. 3. docker-compose
    1. 3.1. 如何使用docker-compose
    2. 3.2. 如何编写docker-compose.yml