docker学习

Docker可以方便部署以及利用镜像快速学习一些项目,解决环境冲突等.重点关注Dockerfile和docker-compose文件的编写.

核心概念

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

Docker 是一个用于开发,交付和运行应用程序的开放平台。Docker 使您能够将应用程序与基础架构分开,从而可以快速交付软件。借助 Docker,您可以与管理应用程序相同的方式来管理基础架构。通过利用 Docker 的方法来快速交付,测试和部署代码,您可以大大减少编写代码和在生产环境中运行代码之间的延迟。

  • Web 应用的自动化打包和发布。
  • 自动化测试和持续集成、发布。
  • 在服务型环境中部署和调整数据库或其他的后台应用。
  • 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。

镜像(image)

镜像类似于一个打包好的软件或可执行程序,可以通过运行这个镜像得到容器(类似于一个进程).

镜像可以自己打包或从Docker HubDocker Hub Container Image Library | App Containerization获取,如果本地没有打包的默认从后者拉取.

自己打包需要Dockerfile定制这个”可执行程序”,有了Dockerfile后可以使用docker build .构建镜像.

.符号表示在这个目录下的Dockerfile文件.

一个镜像可以在多个容器运行,可以将镜像分享给别人.

使用docker run运行镜像,如果本地没有会自动docker pull拉取.

使用docker push可以将自己的镜像推送到Docker Hub.

这里我们创建一个小项目.

image-20230213175925727

创建一个服务监听端口.

image-20230213175946483

1
2
3
4
5
6
7
8
9
10
const express = require('express');
const app = express();
const port = 3000;
app.get('/',(req,res)=>
{
res.send('<p>你好</p>')

});
app.listen(port,()=>console.log("端口是3000"))

写好后就可以直接创建Dockerfile建立镜像了.

首先Dockerfile文件创建在项目根目录中.

需要先拉取镜像,可以在Docker Hub Container Image Library | App Containerization中查找.

首先我们用到了node,所以需要拉取相应的node镜像(一般node镜像肯定带了OS等底层的镜像)

image-20230213180312673

1
2
3
4
5
6
7
FROM node:19-alpine3.16
WORKDIR /myworkdir
COPY package.json .
RUN npm i
COPY . .
EXPOSE 3000
CMD ["node","app.js"]

docker会一行一行扫描代码.

FROM表示拉取node:19-alpine3.16,node:19-alpine3.16是镜像名,19-alpine3.16表示版本,node 19版本,alpine是一个操作系统常用于Docker构建.

1
docker build -t myimage:v1  .

如果不使用tag,则没有名字

image-20230213190037683

1
docker tag <IMAGE ID> <REPO:TAG>

image-20230213190203625

没有tag默认latest

使用docker push推送镜像

image-20230213190322542

使用docker rmi删除本地镜像

image-20230213190427515

使用docker pull拉取Docker Hub中的镜像.

image-20230213190537437

容器(container)

使用docker run <镜像名>运行镜像从而得到

-d参数后台运行

image-20230213191519991

使用docker images查询镜像信息.

使用docker ps查询容器信息.

这里并不能直接通过3000端口访问网页,因为并没有实际暴露.

Dockerfile文件中EXPOSE命令只是标识提醒作用.

1
docker run -d -p 3000:3000 --name mydocker proanimer/nodejs:test

-p参数表示端口映射 本地3000映射后者容器的3000.

--name参数表示给容器命名,否则有默认命名.

1
docker run -d -p 3000:3000 --name mydockerpro proanimer/nodejs:test           

使用docker stop <容器id>停止运行.

使用docker rm <容器id>删除窗口.

image-20230213192549208

修改文件后容器并不会跟着改变,因为修改的文件时本地镜像中的文件,容器是之前的镜像运行后的结果.我们需要与运行的容器直接交互.

使用docker exec -it <镜像名或id> /bin/sh

/bin/sh是容器中os的shell.

image-20230213195526472

image-20230213200936929

注意 如果在Windows上需要加上winpty,同时设定shell路径时是//bin/sh即需要多加上一个/

注意进入容器后很多工具比如vim可能都没有,所以需要用到路径映射.

设置volume,-v 把本地指定文件夹和容器指定文件夹绑定

这里使用

image-20230213210813343注意如果是git bash for windows.使用下面命令,

1
docker run -d  -v F://docker_exp1://myworkdir -p 3000:3000 --name mypro proanimer/nodejs:test

image-20230213220937554

发现挂载路径有/hostmnt,这是git bash比较特别的,表示宿主机.

如果是在windows的powershell.

image-20230213220122719

检查挂载路径,image-20230213221258083

两者这里不同,但本质上是一样的.

注意,这里路径映射时是依照宿主机的,即如果宿主机/dir上有A,B文件,容器路径中/dir中有C文件,将这两个路径映射,则实际用时是A,B文件.

同时使用-v 目录对这个容器中的目录不进行同步.本地只读后增加:ro

image-20230213225237389

ro模式

(1)文件:容器内不能修改,会提示read-only

(2)文件夹:容器内不能修改、新增、删除文件夹中的文件,会提示read-only

docker rm -fv同时把volume删除.

Dockerfile

通过写Dockerfile定制镜像文件,使用docker build创建镜像.

FROM

FROM:定制的镜像都是基于FROM的镜像

RUN

RUN:用于执行后面跟着的命令行命令。有以下两种格式:

shell 格式:

1
2
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。

exec 格式:

1
2
3
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:

1
2
3
4
FROM centos
RUN **yum** -y **install** **wget**
RUN **wget** -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN **tar** -xvf redis.tar.gz

以上执行会创建 3 层镜像。可简化为以下格式:

1
2
3
4
FROM centos
RUN yum -y install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz

如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。

CMD

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build即构建时运行.

作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

1
2
3
CMD <shell 命令> 
CMD ["<可执行文件或命令>","<param1>","<param2>",...]
CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

ENTRYPOINT

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

但是, 如果运行 docker run 时使用了 —entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

1
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

WORKDIR

指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。

docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。

格式:

1
WORKDIR <工作目录路径>

VOLUME

定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

作用:

  • 避免重要的数据,因容器重启而丢失,这是非常致命的。
  • 避免容器不断变大。

格式:

1
2
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

EXPOSE

仅仅只是声明端口. 注意并没有实际作用

作用:

  • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
  • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

格式:

1
EXPOSE <端口1> [<端口2>...]

ENV

设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

格式:

1
2
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:

1
2
3
4
ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"

ARG

构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

构建命令 docker build 中可以用 —build-arg <参数名>=<值> 来覆盖。

格式:

1
ARG <参数名>[=<默认值>]

ONBUILD

用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

格式:

1
ONBUILD <其它指令>

LABEL

LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下:

1
LABEL <key>=<value> <key>=<value> <key> =<value> ...

比如我们可以添加镜像的作者:

1
LABEL org.opencontainers.image.authors="runoob"

docker-compose

启动多个容器处理应用.

yaml文件格式.

基本语法

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • ‘#’表示注释

创建docker-compose.yml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# yaml 配置实例
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}

services下都是镜像,build表示通过Dockerfile构建.

ports和volumes用于映射端口和数据卷.

使用命令docker-compose up -d --build-d表示后台,—build表示如果镜像修改就会重建.

version

指定本 yml 依从的 compose 哪个版本制定的。

build

指定为构建镜像上下文路径:

volumes

将主机的数据卷或着文件挂载到容器里。

1
2
3
4
5
6
7
version: "3.7"
services:
db:
image: postgres:latest
volumes:
- "/localhost/postgres.sock:/var/run/postgres/postgres.sock"
- "/localhost/data:/var/lib/postgresql/data"

expose

暴露端口,但不映射到宿主机,只允许能被连接的服务访问。仅可以指定内部端口为参数,如下所示:

1
2
3
expose:
- "3000"
- "8000"

链接到其它服务中的容器。使用服务名称(同时作为别名),或者服务名称:服务别名(如 SERVICE:ALIAS),例如

1
2
3
4
links:
- db
- db:database
- redis

使用docker-compose down -v删除镜像同时删除volume

参考资料

  1. Docker入门之docker-compose - minseo - 博客园 (cnblogs.com)
-------------本文结束感谢您的阅读-------------
感谢阅读.

欢迎关注我的其它发布渠道