docker 优势

对于传统虚拟机,有以下几个缺点:
1. 资源占用多,虚拟机启动的时候会独自启动一部分内存和部分磁盘,导致虚拟机运行的时候这些资源无法被其他程序占用,导致资源浪费
2. 虚拟机的启动慢
3. 冗余步骤多

docker的基本思想

docker的基本思想是将软件以及相关关联的Lib打包在一起,做为一个容器,他们共享一个内核,相较于传统的虚拟机,简洁很多,简要图如下
img

docker的基本组成

img

镜像(Image):docker镜像就好比一个模板,我们可以通过这个模板来创建容器服务,tomcat镜像===>run==>tomcat01 容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。

容器(container):docker利用容器技术,独立运行一个或者一组应用 通过镜像来创建,启动,停止,删除,基本命令! 目前就可以把这个容器理解为就是一个简易的linux系统。

仓库(repository):仓库就是存放 镜像(image)的地方!仓库又可以分为 公有仓库和私有仓库。

底层原理

  1. Docker有着比虚拟机更少的抽象层。
  2. docker利用的是宿主机的内核,vm需要是Guest OS
  1. Guest OS: 安装在虚拟机(Virtual Machine, VM)中的操作系统。它运行在虚拟化软件(Hypervisor 或虚拟机管理程序)提供的虚拟硬件之上。
  2. Host OS: 运行在物理机上的操作系统,负责管理硬件资源,并且提供给虚拟机使用。

img

docker 本质上是利用 Linux 的 CGroup(Linux 内核机制,提供 资源限制、统计和控制) 和 NameSpace(Linux 内核机制,提供资源 隔离,让进程看到的资源独立),实现轻量化隔离。

基本操作(hello-world)

image 是一个静态的文件集合,包含了运行应用程序所需的所有代码、库、配置文件和依赖项。它类似于一个“蓝图”或“模板”,定义了容器的内容和环境。

而 image 本质上是一个二进制文件,实际中开发可以继承另一个文件的 image 形成自己的 image。别人的 image 可以通过 Docker Hub 访问。

1
2
3
4
5
6
7
8
# 列出所有的 image 文件
docker images

# 或者使用下面的命令
docker image ls

# 删除
docker image rm [imageName]

从 DockerHub 抓取 image 的方法是

1
2
3
docker image pull library/hello-world
# 或者说,由于 hello-world 是默认的,那么可以省略 library
docker image pull hello-world

怎么运行呢?

container 是镜像的运行实例,可以理解为根据镜像“蓝图”构建的“实体”。容器是一个独立的运行环境,其中包含了镜像中定义的所有文件和配置。

1
2
3
4
5
# 运行一个 image,生成一个正在运行的容器,如果本地没有指定的容器,那么就会自动的从远端抓取 image
docker container run hello-world

# 但是这个是直接生成一个新的 container,想要使用之前的 container
docker container start [container ID]

img

如果我们想查看当前有哪些 container 正在运行,那么

1
2
3
4
5
6
7
# 以下 cli 可以列出正在运行的 container
docker container ls
docker ps

# 列出所有的 container
docker container ls --all
docker ps --all

img

现在,我们想要关闭正在运行的容器

1
2
3
4
5
# 这个相当于强制关闭,相当于发送 SIGKILL
docker container kill [containerId]

# 这个是正常关闭
docker container stop [containerID]

但是已经终止的 container 文件依然会占据文件内存,那么我们可以使用这个指令来删除

1
docker container rm [container ID]

img

再次查看,已经将 cae8003170ad 的 container 删除。
img


  1. 如果我们想要查看 container 的 log 输出应该怎么办呢(没有 -it )?

其中 -i 指的是创建标准化输入输出, -t 是创建一个终端
那么, -it 指的是创建一个标准化的虚拟终端

1
docker container logs [containerID]
  1. 如果我们现在有多个正在运行的 container,现在我想要进入到某一个 container 中,那么
1
docker container exec -it [containerID] /bin/bash
  1. docker container cp命令用于从正在运行的 Docker 容器里面,将文件拷贝到本
    机。下面是拷贝到当前目录的写法。
1
docker container cp [containID]:[/path/to/file] .

制作自己的 image

基础教程

这里是参考的Docker 入门教程 - 阮一峰的网络日志,以及自己的一些补充

首先准备相关源码

1
2
3
git clone https://github.com/ruanyf/koa-demos.git

cd koa-demos

编写 dockerfile

.gitignore 相似,docker 也有 .dockerignore,写入自己想要排除的文件即可。这样就可以不用打包在 image 中。

项目中已经有相关的 dockerfile

1
2
3
4
5
6
7
8
9
10
# 该 image 文件继承官方的 node image,冒号表示标签,这里标签是`8.4`,即8.4版本的 node。
FROM node:8.4
# 将当前目录下的所有文件(除了`.dockerignore`排除的路径),都拷贝进入 image 文件的`/app`目录。
COPY . /app
# 指定接下来的工作路径为`/app`。
WORKDIR /app
# 在`/app`目录下,运行`npm install`命令安装依赖。注意,安装后所有的依赖,都将打包进入 image 文件。
RUN ["npm", "install"]
# 将容器 3000 端口暴露出来, 允许外部连接这个端口。
EXPOSE 3000/tcp

补充一下 dockerfile 的关键词

  • ADD将主机上的源文件复制到容器文件系统的目标位置。
  • CMD可用于在容器内执行特定命令。
  • ENTRYPOINT设置一个默认应用程序,每次使用该镜像创建容器时都会使用该程序。
  • ENV设置环境变量。
  • EXPOSE关联一个特定的端口,以实现容器与外部世界之间的网络连接。
  • FROM定义用于启动构建过程的基础镜像。
  • MAINTAINER定义镜像创建者的全名和电子邮件地址。
  • RUN是 Dockerfile 的核心执行指令,也称为 run Dockerfile 命令。
  • USER设置运行容器的 UID(或用户名)。
  • VOLUME用于启用从容器到主机上目录的访问。
  • WORKDIR设置使用 CMD 定义的命令要执行的路径。
  • LABEL允许您向 Docker 镜像添加标签。

创建 image 文件

1
2
3
4
# docker build -f /path/DataFile -t 镜像名 [:TAG] .
docker image build -t koa-demo .
# 或者
docker image build -t koa-demo:0.0.1 .

上面代码中,-t参数用来指定 image 文件的名字,后面还可以用冒号指定标签。如果不指定,默认的标签就是latest。最后的那个点表示 Dockerfile 文件所在的路径,上例是当前路径,所以是一个点。

img

炸了,是我自己忘记配置相关镜像源了。。。

MyChat

这是我自己的项目,想着复习一些 dockerfile 怎么写的,也算是一个小总结,但是本质上也是套上面的模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 构建阶段, 其中 alpine 表示超级精简版
FROM golang:1.24-alpine AS builder

# 配置 golang 相关环境变量
ENV GO111MODULE=on GOPROXY=https://goproxy.cn,direct CGO_ENABLED=0

# 工作目录
WORKDIR /MyChat

# 相关依赖包下载
COPY go.mod go.sum ./
RUN go mod download

# 拷贝相关代码,并且执行编译指令
COPY . .
RUN go build -o mychat .

# 运行阶段
FROM alpine:latest

WORKDIR /MyChat

# 拷贝相关配置文件,其中 asset 的复制其实没有必要
COPY --from=builder /MyChat/mychat .
COPY --from=builder /MyChat/config ./config
COPY --from=builder /MyChat/asset ./asset

# 服务运行端口
EXPOSE 8080

# 执行编译过后的文件
CMD ["./mychat"]

随后执行编译:

1
2
# docker build -f /path/DataFile -t 镜像名 [:TAG] .
docker build -t mychat:lastest .

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
❯ docker build -t mychat:lastest .
[+] Building 29.4s (21/21) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.33kB 0.0s
=> [internal] load metadata for docker.io/library/golang:1.24-alpine 1.2s
=> [internal] load metadata for docker.io/library/alpine:latest 1.2s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 586B 0.0s
=> CACHED [builder 1/7] FROM docker.io/library/golang:1.24-alpine@sha256:fc2cff6625f3c1c92e6c85938ac5bd09034ad0d 0.0s
=> CACHED [stage-1 1/8] FROM docker.io/library/alpine:latest@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9be 0.0s
=> [internal] load build context 0.1s
=> => transferring context: 6.44kB 0.0s
=> [builder 2/7] RUN apk add --no-cache git ca-certificates tzdata 6.0s
=> [stage-1 2/8] RUN apk --no-cache add ca-certificates tzdata 3.1s
=> [stage-1 3/8] RUN addgroup -g 1001 -S appgroup && adduser -u 1001 -S appuser -G appgroup 0.2s
=> [stage-1 4/8] WORKDIR /MyChat 0.1s
=> [builder 3/7] WORKDIR /MyChat 0.1s
=> [builder 4/7] COPY go.mod go.sum ./ 0.1s
=> [builder 5/7] RUN go mod download 10.2s
=> [builder 6/7] COPY . . 0.1s
=> [builder 7/7] RUN go build -a -installsuffix cgo -o mychat . 10.9s
=> [stage-1 5/8] COPY --from=builder /MyChat/mychat . 0.1s
=> [stage-1 6/8] COPY --from=builder /MyChat/config ./config 0.0s
=> [stage-1 7/8] COPY --from=builder /MyChat/asset ./asset 0.1s
=> [stage-1 8/8] RUN mkdir -p /MyChat/asset/upload && chown -R appuser:appgroup /MyChat 0.3s
=> exporting to image 0.2s
=> => exporting layers 0.2s
=> => writing image sha256:ddff526eeaf7a97d8de01d66e52f1a82eaf1fcf30bd155d4b735a2b100890fbf 0.0s
=> => naming to docker.io/library/mychat:lastest 0.0s

然后执行,正常的话会返回一串 hash 值,随后就可以随意进行操作了:

1
docker run -d -p 8080:8080 --name MyChat mychat:lastest

我们可以进入到容器中:

1
docker exec -it MyChat sh

然后查看容器内的文件,我们发现就是我们 COPY 的文件:

1
2
/MyChat # ls
asset config mychat