docker 入门下篇

465 阅读9分钟

上篇在这里 docker 入门上篇

本文主要记录:

  • 创建 docker 镜像
  • docker-compose 的使用

创建镜像

我们开始使用的镜像都是别人制作好的,这些镜像可以用来创建多个不同的实例容器,但是不可以修改镜像本身,所以当我们想去定制自己的镜像时,需要自己创建一个新的镜像。创建镜像的方式有两种。

使用容器制作镜像

前面说到镜像本身是不可以修改的,所以没办法直接在镜像上做定制,但是容器是可以修改的,我们可以在容器上做一些自己需要的定制,然后再将容器反转为镜像,这种操作是可以的,用一张图表示:

要注意的是,容器反转成的镜像并不是覆盖源镜像,而是创建了一个新的镜像(不要被图误导)。

来查看下当前本地的镜像:

现在我们就来定制一个 centos 的镜像,首先创建一个新容器:

docker run -it --name=aa centos /bin/bash

创建完成进入容器,我们要拿这个容器生成一个镜像,为了与源镜像区分开,所以我们来做一点定制,只是简单示范下,比如发现容器里默认没有 vim 工具,所以使用 yum install vim 来安装一下 vim

安装完 vim 也相当于做了一个小定制,现在就将容器转换为镜像,使用命令:

docker commit 容器ID 镜像名称:版本号

操作如下:

如图,拿到容器的 id 后即可创建新的镜像。有个小细节就是当我们使用容器 id 时,可以不用输入完整的 id,只要前几位就行了。现在我们创建好了新的镜像 my_centos,为了验证可用性,我们用它来创建一个新的容器:

docker run -it --name=bb my_centos:1.0 /bin/bash

创建完成后使用 docker ps 来验证:

可以看到 bb 容器已经存在,而且先前的 aa 容器已经不在了,这说明当容器转换为镜像后,容器本身将删除。

另外,还记得我们之前在 aa 容器上做的定制嘛,我们安装了 vim 工具,现在我们在 bb 容器上使用 vim 命令后,发现是有这个工具的。

这说明定制生效。习惯用一张图来表示流程:

以上就是使用容器创建镜像的过程。

使用 Dockerfile 创建镜像

创建镜像最常用的方式就是使用 Dockerfile。Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。它的格式是这样的:

FROM scratch
ADD centos-7-x86_64-docker.tar.xz /

LABEL org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20191001"

CMD ["/bin/bash"]

这是 centos 镜像的 Dockerfile 文件,它有 4 条指令,关键词分别是:FROMADDLABELCMD,每一条指令构建一层,每条指令后的内容就是描述该层应当如何构建。

Dockerfile 文件的指令有很多,推荐看菜鸟教程的文档,我主要来介绍一下使用 Dockerfile 的过程。

使用 Dockerfile 创建 centos 镜像

接下来我们使用 Dockerfile 方式来定制一个自己的 centos 镜像。

创建一个新目录,并在目录中新建 Dockerfile 文件:

// 目录名和文件名都随意

mkdir docker-files
cd docker-files
touch centos_dockerfile

centos_dockerfile 中输入以下内容并保存:

FROM centos:7
MAINTAINER aaa
RUN yum install -y vim
WORKDIR /root
CMD /bin/bash

来逐行解释一下这个 Dockerfile 文件:

  • FROM centos:7:表示基础镜像使用 centos:7 这个版本。如果本地已有这个镜像就直接使用,如果没有会先下载到本地;
  • MAINTAINER aaa:作者名;
  • RUN yum install -y vim:运行安装 vim 的命令,加上 -y 参数是因为安装vim过程中会有确认步骤;
  • WORKDIR /root:设置工作目录,创建出来的容器进入时,默认所在的目录;
  • CMD /bin/bash:启动容器时默认执行的命令。

用到的指令虽然不多,但已经够我们创建一个新的镜像了,在 docker-files 目录中执行命令:

docker build -f ./centos_dockerfile -t centos_with_vim:1 .

注意命令最后还有一个 .,它是上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。使用 . 就是将当前路径指定为上下文路径,一般不要放无用的东西在上下文路径中。

-f 参数后面接的是 Dockerfile 文件,-t 参数后面接的是要创建的镜像的名称或加上版本。

执行命令时:

可以看到会根据 Dockerfile 文件中的指令而一层层地去构建镜像,完成后使用 docker images 可以查看构建出来的镜像:

现在来使用这个新镜像去创建容器:

docker run -it --name=cc centos_with_vim:1 /bin/bash

创建完成进入容器后,首先验证了工作目录确实是 /root,然后 vim 工具也是存在的,这些都是我们在 Dockerfile 中配置好的,为了对比,可以直接创建一个基于 centos:7 源镜像的容器:

说明我们使用 Dockerfile 方式创建特定镜像成功。

使用 Dockerfile 创建 koa 项目镜像

拿个我们前端熟悉的东西来试试 Dockerfile,来创建一个 koa 项目的镜像。

首先我们要有一个koa项目,这个就做一个简单的demo就好了,比如我的:

app.js 内容:

const Koa = require('koa')
const app = new Koa()

app.use(async ctx => {
    ctx.body = 'hello Docker!'
})

app.listen(3000)

package.json 内容:

{
  "name": "koa-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "koa": "^2.11.0"
  }
}

Dockerfile 就是之前说的配置文件,直接命名为 Dockerfile 时,就不需要加 -f 参数指定,它的内容是:

# 基础镜像指定为 node 镜像
FROM node:latest
# 指定作者信息,之前用的是 MAINTAINER,但推荐用 LABEL,格式 LABEL <key>=<value> ...
LABEL maintainer="pingzi"
# 移动当前目录下面的文件到app目录下
ADD . /app/
# 容器工作目录为 /app
WORKDIR /app
# 安装依赖
RUN npm install
# 暴露3000端口
EXPOSE 3000
# 执行 npm start
CMD ["npm", "start"]

还有一个 .dockerignore 文件,它的作用跟 .gitignore 类似,指定的内容就不会被打包到镜像中,这里在网上找的一份内容:

# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history
.idea
.node_modules
node_modules
.vscode

好了齐活,现在来创建镜像,还记得创建镜像的命令吗?在当前目录下执行:

docker build -t koa_image .

有了镜像后,可以创建容器啦,执行:

docker run -d --name=koa_demo -p 9000:3000 koa_image

不出意外的话,此时在浏览器中就可以访问到啦~

到这,Dockerfile 的一些基本概念已经说完了,说的比较浅显,还需多实践啊~

docker-compose

docker-compose 是编排容器的工具,作用是可以使用一条命令来定义和运行多容器。

我们知道每当开启一个容器服务时,都需要执行 docker run 命令,这在服务少的时候是完全可行的,但是当一个业务需要很多的容器服务时,比如同时需要使用 node镜像,mysql镜像,nginx镜像和redis镜像等,此时你就得敲各个容器的启动参数,环境变量,容器命名等等一系列的操作,相当繁琐。而 docker-compose 的出现,就是解决了这一痛点,它通过一个配置文件来管理多个Docker容器,只需一个命令既可启动多容器服务。

安装 docker-compose

Linux 上安装:

sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

添加可执行权限

sudo chmod +x /usr/local/bin/docker-compose

检查版本

docker-compose --version

另外,在 Mac 或者 Windows 上安装docekr客户端的,是默认包含Compose的,所以不需要单独安装。

简单试用 docker-compose

docker-compose 是通过配置文件来启动服务的,我们需要编写 docker-compose.yml 文件,这里我写了一个简单的例子:

version: '3'
services:
  mongo:
    image: mongo
    restart: always
    container_name: 'mongo-test'
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
    ports:
      - 27018:27017
    volumes:
      - /composetest/mongodb:/data/db
  redis:
    image: redis
    container_name: 'redis-test'
    ports:
      - 6380:6379

创建一个新目录 /composetest(目录名字随意),然后在目录中创建 docker-compose.yml 文件,文件中输入上面内容,保存,注意缩进。

现在来看下这个文件描述了什么:

  • version:指定本 yml 依从的 compose 哪个版本制定的,这里是版本 3;
  • services:指定多容器服务,这里指定了两个容器服务,分别是 mongoredis,注意这只是名字;
  • image:指定容器运行的镜像,不指定版本时默认最新版;
  • restart:重启容器,有4种情况
    • "no":是默认的重启策略,在任何情况下都不会重启容器
    • always:容器总是重新启动
    • on-failure:在容器非正常退出时(退出状态非0),才会重启容器
    • unless-stopped:在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器;
  • container_name:指定自定义容器的名称,而不是随机生成的名称;
  • environment:添加环境变量;
  • ports:端口映射,宿主机端口 : 容器端口;
  • volumes:数据卷挂载,宿主机目录 : 容器目录

总的来说,此描述文件定义了两个容器服务,一个是mongodb数据库的,一个是redis的,分别对应services下的mongoredis,现在来运行这个文件,在这个文件所在的目录下运行:

docker-compose up

此时两个容器就被创建好了。可以看到有了 Compose 配置文件后,只需要一个命令即可创建并启动所有服务,极大简化了操作。在实际工作中,当需要多个容器相互配合来完成某项任务的时候,就应该考虑使用 docker-compose 了。

结尾

关于 docker的实践,还是很欠缺,我还学的动🤣,就这样吧~