前言

java 的 学习离不开 Docker 与 k8s,后续的工作中也不可避免的会使用到,于是乎,我决定学习一下 Docker 的基础知识与应用,并以此文进行记录!加油!

快速入门

Docker 是什么

当我们利用 Docker 安装应用时,Docker 会自动搜索并下载应用镜像(image)。镜像不仅包含应用本身,还包含应用运行所需要的环境、配置、系统函数库。Docker 会在运行镜像时创建一个隔离环境,称为容器(container)

Docker 与虚拟机的区别

  1. 架构层次

    虚拟机在宿主机的硬件之上运行一个完整的操作系统,每个虚拟机有独立的内核;而 Docker 是基于操作系统级别的虚拟化技术,所有容器共享宿主机的操作系统内核,容器之间使用隔离机制实现相对独立的运行环境。

  2. 资源开销

    每个虚拟机都包含一个完整的操作系统,因此需要更多的 CPU、内存和存储资源,启动时间也较长;而 Docker 由于共享宿主操作系统,资源开销小,启动时间也更加快速。

  3. 隔离性

    每个虚拟机都有独立的操作系统和资源分配,而 Docker 隔离性不如虚拟机。

  4. 使用场景

    Docker 更适合微服务架构、开发测试环境、CI/CD 流程和需要快速启动、部署的场景。

  5. 可移植性

    Docker 镜像轻量,具有极高的可移植性,可以轻松迁移到不同的环境中运行;而虚拟机镜像庞大,迁移和部署较复杂。

常用命令

命令 说明
docker pull 拉取镜像
docker push 推送镜像到 DockerRegistry
docker images 查看本地镜像
docker rmi 删除本地镜像
docker run 创建并运行容器(不能重复创建)
docker stop 停止指定容器
docker start 启动指定容器
docker restart 重新启动容器
docker rm 删除指定容器
docker ps 查看容器
docker logs 查看容器运行日志
docker exec 进入容器
docker save 保存镜像到本地压缩文件
docker load 加载本地压缩文件到镜像
docker inspect 查看容器详细信息
  • 创建并运行容器:

    1
    $ docker run -d --name xxx -p 3306:3306 -e yyy -v [pathA]:[pathB] [repository]:[tag] 
    • [repository]:[tag] 分别为指定运行的镜像名及其镜像版本;在没有指定 tag 时,默认是 latest,代表最新版本的镜像

    可选参数为:

    • -d 用于使容器在后台运行

    • --name 用于给容器起名字,名字必须唯一

    • -p 用于设置端口映射,格式为 -p 宿主机端口:容器内端口

    • -e 用于配置容器内进程运行时的一些参数,格式为 -e KEY=VALUEKEYVALUE 都由容器内进程决定

    • -v 用于挂载宿主机的目录到容器中,格式为 -v 宿主机目录:容器内目录,该部分与数据卷有关

  • 查看本地镜像:

    1
    $ docker images -a -q

    可选参数为:

    • -a 表示列出本地所有的镜像(含历史映像层)

    • -q 表示只显示镜像 ID

  • 查看容器:

    1
    $ docker ps --format -a

    可选参数为:

    • --format 表示格式化显示,可以自定义显示容器字段

    • -a 表示列出所有状态的容器(默认只显示运行中的容器)

  • 查看容器运行日志:

    1
    $ docker logs -f

    可选参数为:

    • -f 用于实时监控日志

数据卷命令

数据卷(volume)是一个虚拟目录,是容器内目录宿主机目录之间映射的桥梁。

命令 说明
docker volume create 创建数据卷
docker volume ls 查看所有数据卷
docker volume rm 删除指定数据卷
docker volume inspect 查看某个数据卷的详情
docker volume prune 清除数据卷

注意:容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。

自定义镜像

镜像就是包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包。构建镜像的过程其实就是把上述文件打包的过程。

  • 部署一个 Java 应用的步骤:
    1. 准备一个 Linux 服务器
    2. 安装 JRE 并配置环境变量
    3. 拷贝 jar 包
    4. 运行 jar 包
  • 构建一个 Java 镜像的步骤:
    1. 准备一个 Linux 运行环境
    2. 安装 JRE 并配置环境变量
    3. 拷贝 jar 包
    4. 编写运行脚本

上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合

镜像结构

  • 层(Layer):添加安装包、依赖、配置等,每次操作都形成新的一层

    如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。

  • 入口(Entrypoint):镜像运行入口,一般是程序启动的脚本和参数

  • 基础镜像 (BaseImage):应用依赖的系统函数库、环境、配置、文件等

DockerFile

Docker 提供了自动打包镜像的功能。我们只需要用固定的语法写下来打包的过程中每一层要做的事情,交给 Docker 去执行即可。

这种记录镜像结构的文件就称为 Dockerfile,其对应的语法为:

指令 说明 示例
FROM 指定基础镜像 FROM centos:6
ENV 设置环境变量,可在后面指令使用 ENV key value
COPY 拷贝本地文件到镜像的指定目录 COPY ./xx.jar /tmp/app.jar
RUN 执行 Linux 的 shell 命令,一般是安装过程的命令 RUN yum install gcc
EXPOSE 指定容器运行时监听的端口,是给镜像使用者看的 EXPOSE 8080
ENTRYPOINT 镜像中应用的启动命令,容器运行时调用 ENTRYPOINT java -jar xx.jar

使用实例(以 Hexo 为例)

安装 docker

所使用的镜像源我是参考了这篇文章,但是镜像源过期较快,大家可以寻找更新的镜像源使用。

需要通过修改文件 /etc/docker/daemon.json 配置镜像源,一般情况下该文件需要自己手动去创建:

1
2
3
4
5
6
7
8
9
10
11
$ sudo mkdir -p /etc/docker  # 第一次配置需要先创建该文件夹
$ sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://dockerpull.com",
"https://docker.anyhub.us.kg" # 按照 json 列表格式
]
}
EOF
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

配置完毕后,可以使用 docker info 命令查看是否配置成功,如果出现的信息有 Registry Mirrors 字段,且其中镜像源与所配置的相同,则配置成功!

如果只是临时使用,可以使用如下命令:

1
$ docker pull [镜像源]/library/[镜像名称]:[镜像Tag]

搭建环境

首先使用 hexo pull 命令拉取 node 镜像,由于本博客使用的是 Node 长期支持版本 20.17.0,因此选择拉取 node:20 镜像:

1
$ docker pull node:20

拉取镜像

等待拉取镜像完成即可。

注:如果显示 Get xxxx request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers),证明镜像源未配置成功或者该镜像源已经不可用。

之后,创建博客目录:

1
2
3
4
5
# 在当前目录创建博客文件夹,我此处将 Hexo 文件夹创建在 ~/Documents 中
$ mkdir -p Hexo
$ cd Hexo
# 创建 Dockerfile
$ touch Dockerfile

Dockerfile 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 基础镜像
FROM node:20

# 维护者信息
MAINTAINER your_name <your_mail>

# 工作目录
WORKDIR /hexo

# 安装 Hexo
RUN npm install hexo-cli -g
RUN hexo init .
RUN npm install

# 设置 git
RUN git config --global user.name "your_name"
RUN git config --global user.email "your_mail"

# 映射端口
EXPOSE 4000

# 运行命令
CMD ["/bin/bash"]

运行下行命令,不要漏掉最后的 .,其代表当前目录:

1
$ docker build -t 'hexo:20' .

如下图所示,正在创建镜像中:

创建镜像

使用 docker images 命令,可以发现 hexo 镜像已经创建成功!

查看镜像列表

运行如下命令:

1
$ docker run -it --name="hexo" -p 4000:4000 -v $HOME/.ssh:/root/.ssh hexo:20 /bin/bash

会进入容器的 bash 环境 root@01d0bbdf480d:/hexo# ,其中 @ 后的一串字符是容器的 id。

在该环境中使用指令 hexo s,便如同《博客是怎样建成的》这篇文章一样,能够通过本地浏览器访问 http://localhost:4000 来进入博客!

注:如果需要退出该环境,可以使用 exit 命令或 Cltr + D 快捷键,退出后该容器会关闭,若仍需进入,可以使用 docker start [容器名] 再次启动,并可通过 docker attach [容器名](退出后 container 会关闭)或 docker exec -it [容器名] /bin/bash(退出后 container 仍在后台运行)进入。

使用主题

我的博客主题为 hexo-theme-butterfly,该部分以该主题为例,搭建方法按照参考文章《基于 Docker 的 Hexo 博客搭建》的复杂搭建方法,使用数据卷的形式,只保留几个必要的文件在博客目录。

容器内 hexo/ 目录下内容如图所示:

容器内文件

其中需要保留的目录和文件列表如下:

  • hexo 相关:
    • scaffolds/ 存放模版文件
    • source/ 存放用户资源文件
    • themes/ 存放主题
    • _config.yml 配置文件
    • _config.butterfly.yml 主题配置文件,可选,推荐将主题的配置文件置于根目录
  • git 相关:
    • .deploy_git/ 插件 hexo-deployer-git 用于存放 commit 记录的目录,如果不保留在本地会导致每次都重新 commit 全部文件导致远程的库会越来越大

简化目录的思路就是把上述文件留在本地然后每次启动应用时挂在到容器内部即可。

可以选择修改原先的 Dockerfile(基础镜像为 node:20)或新建 Dockerfile(可将上一步创建的 hexo:20 作为基础镜像,可省去安装 Hexo 和设置 git 的部分)。

由于我希望能够更简单的将 butterfly 主题的博客进行迁移,这里对原先的 Dockerfile 进行修改,增加 Hexo 插件的下载和数据卷的挂载命令,并修改运行命令,具体内容如下:

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
# 基础镜像
FROM node:20

# 维护者信息
MAINTAINER your_name <your_mail>

# 工作目录
WORKDIR /hexo

# 安装 Hexo
RUN npm install hexo-cli -g
RUN hexo init .
RUN npm install

# 设置 git
RUN git config --global user.name "your_name"
RUN git config --global user.email "your_mail"

# 安装 Hexo 插件
RUN npm install hexo-renderer-pug hexo-renderer-stylus --save # butterfly 主题渲染使用
RUN npm install hexo-deployer-git --save # git 部署

# 挂载 volume
VOLUME ["/hexo/.deploy_git", "/hexo/scaffolds", "/hexo/source", "/hexo/themes", "/root/.ssh"]

# 映射端口
EXPOSE 4000

# 运行命令
CMD ["/usr/bin/env", "hexo", "server"]

注意:

  1. 在 Docker 中,当挂载一个 volume 到容器时,默认情况下,volume 的内容会覆盖容器内挂载点的文件,而容器原有的文件将不可见。当进行挂载后,容器内所挂载的文件夹内容均会消失,这意味着安装的主题以及 hexo 初始化过程中在 source/ 中生成的文件均会消失。可以先不进行 hexo 初始化安装主题这两步,待容器启动、挂载完成后运行;或者复制入备份文件夹,容器启动后运行脚本文件进行恢复。
  2. 挂载过程中,docker 无法识别挂载点是文件还是目录,因此如果宿主机处没有对应文件或目录,docker 均会自动创建目录,导致 _config.yml 等文件无法成功挂载,可以将该部分文件先提前创建,或者将能够使用的博客文件提前拷贝至挂载处。

创建服务

到这里其实已经接近完成了,但每次启动容器都需要输入一长串 docker run 命令,如果你也觉得麻烦,可以一起创建一个 docker 的服务文件 docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
version: '3.8'  # 指定 docker-compose 文件的版本
services: # 定义一组容器(服务)
blog: # 服务名称
restart: always # 定义容器的重启策略,always 代表无论容器退出的原因,都会始终重启容器
build: # 指定了如何构建镜像,而不是直接从 DockerHub 上拉取现有镜像
dockerfile: Dockerfile # Dockerfile 文件名
image: blog:butterfly # 指定使用哪个 Docker 镜像
container_name: blog # 容器的名称
ports: # 设置端口映射
- "4000:4000"
volumes: # 设置数据卷
- $HOME/.ssh:/root/.ssh # $HOME 代表当前用户工作目录
- $PWD/.deploy_git:/hexo/.deploy_git # $PWD 代表当前目录
- $PWD/scaffolds:/hexo/scaffolds
- $PWD/source:/hexo/source
- $PWD/themes:/hexo/themes
- $PWD/_config.yml:/hexo/_config.yml

启动和关闭服务的命令为:

1
2
3
4
5
# 启动整个应用
$ docker-compose up -d

# 关闭整个应用
$ docker-compose down

参考文章

知乎 - 如何在 Ubuntu 上安装 Docker | Linux 中国

Github Pages - 基于Docker的Hexo博客搭建

CSDN - 如何进入、退出docker的container