Docker 的个人学习记录。不包含 Docker 安装内容(默认已完成)。



1. Docker 的意义

介绍 docker 的文章实在太多了,所以这里就不长篇累牍地展开。简而言之,往大的方向说,企业可以通过使用容器工具 docker 和容器编排调度工具部署微服务应用、实现 DevOps 的交付方式;往小的方向说,在个人开发环境中使用 docker 是各种省心。

举例来说,我在本地开发环境为 MacOS、需要安装 Redis, 那么我可以选择从 Github 下载源码并编译,或通过 Homebrew 进行安装和各种环境变量的配置、或者下载 dmg 文件进行安装(如果有的话)。如果进一步想要使用不同的版本,并且在不同版本之间来回切换,在不使用 docker 的情况下各种配置真是让人头大。但是通过 docker, 我只需要下载不同版本的镜像并启动相应容器。如果期间有什么配置不满意,直接把容器删除即可。当某个软件版本过久已经彻底不想用了,则直接删除容器和镜像,再也不用操心哪里的配置没有删干净,简直是轻松愉快。

2. 官方教程

如果是从零开始学习,我比较推荐从官方教程开始,因为其中包含了容器的构建和发布、数据绑定(volume)、容器网络(network)和多容器应用 (docker-compose)的使用,涵盖了大部分基本使用场景。

运行官方教程:

1
docker run -d -p 80:80 docker/getting-started

该命令的解释:

  • docker run 为启动 docker 容器的命令。
  • -d--detach,表示容器以后台进程的方式运行。
  • -p 80:80 指定宿主与容器的端口映射关系,即 host-port:container-port
  • docker/getting-started 为镜像的名称。实际上完整的镜像名必须包含 tag,在此处 tag 被隐藏掉,默认值为 latest。即该镜像名等价于 docker/getting-started:latest

如果本地没有 docker/getting-started:latest 的镜像,docker 会自动下载。

容器启动后,在浏览器中访问 http://localhost 即可。

3. 主要概念和常用命令

主要名词:

  • 镜像 image: 可类比为一个软件的名称,或 OOP 中的类的概念。如 redis: docker pull redis
  • 标签 tag: 可类比为该软件的不同版本。如 redis 镜像相应的 tag 有: latest, 6.0, 5.0等不同版本。
  • 容器 container: 基于镜像和 tag 启动的虚拟容器,可类比为 OOP 中的对象实例。一个运行中的容器就相当于一台完备的运行中的 Linux 服务器。
  • volume: 允许容器共享宿主的目录或文件,避免容器销毁后内部数据丢失。分为交由 docker 管理(Named Volumes)和自己指定目录(Bind Mounts)两种方式。
  • network: 允许不同容器之间通过指定 network 来进行通讯。
  • docker-compose: 将多个 docker 容器组合运行。

常用的命令:

  • --help-h: 查看命令的解释,可一直往下展开,如:docker --help, docker image --help, docker image ls --help
  • docker image 镜像相关操作
    • docker image ls: 显示本地下载的镜像,等价于 docker images.
    • docker image rm <image-id|image-name>: 删除已下载的镜像
  • docker run: 运行容器
    • -v: 指定 volume, 如 -v $pwd:/container/path/
    • --name: 指定容器名,如果不指定则由 docker 随机生成。
    • --network: 指定容器之间的通讯网络。
  • docker build: 根据 Dockerfile 构建镜像。

4. demo 示例: 演示镜像构建及发布的过程

Demo 目标:用尽量小的内容演示完整的镜像构建过程。

  1. 定义一个在指定时间内打印内容的 shell script: counting.sh。
  2. 将脚本打包,创建我们自己的镜像。
  3. 运行容器,得到期待的结果。
  4. 上传至 docker hub.

该 demo 可在任意目录进行。

一、创建脚本

创建一个文本文件 counting.sh, 内容为:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/sh

start=0
end=30

while [ $start -lt $end ]
do
start=$(($start+1))
echo $start
sleep 1
done

脚本的内容非常简单:从 startend,每秒输出一次 start,即运行时间为 end 秒,该值可自行调整。

设置 end 秒结束是顺便观察容器在命令执行完会自行中止的行为。

脚本完成后,设置运行权限:

1
chmod u+x counting.sh

Linux 文件权限不在本文讨论范围,因此默认为当前用户具有 root 权限,不再展开。

可通过命令 ./counting.sh 运行脚本,查看效果。

二、创建 Dockerfile

Dockerfile 相当于一个指导构件过程的描述文件,且在最后指定容器启动时运行的命令。

在当前目录创建文本文件 Dockerfile, 内容为:

1
2
3
FROM alpine:latest
COPY ./counting.sh /
CMD ["/counting.sh"]

内容说明:

第一行 FROM 指令表明即将构件的镜像是基于 alpine:latest 镜像,可类比为 OOP 中类的继承。其中 alpine 指的是 Alpine Linux, 旨在使容器更节省空间。

第二行 COPY 指令,指的是在镜像构建过程中将当前宿主工作目录($pwd)的 counting.sh 文件复制至镜像的根目录 /.

第三行 CMD 指令,即为容器在启动时执行的命令。如果指定了多个 CMD 指令,则只有最后的生效。

三、构建镜像

构建的镜像名为 demo-image, 版本名为 v1:

1
docker build -t demo-image:v1 .

使用 docker build 命令构建镜像,-t 选项指定标签名,格式为 “image-name:tag-name”. 最后的一点 . 指出命令本次构建所需的 Dockerfile 所在的目录(当前目录)。

1
2
3
4
5
# 构建过程输出的部分内容
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM alpine:latest
---> d6e46aa2470d
# ...忽略 console 输出

当完成构建时,通过 docker images 可查看到该镜像:

docker images

四、运行容器

命令:

docker run --name mycontainer demo-image:v1

说明:

使用 docker run 命令启动容器,--name 参数显式指定容器的名称为 mycontainer. 容器名如果不显式指定则会由 docker 随机生成。最后的参数 demo-image:v1 指定了运行的镜像名和标签名(即软件版本)。

容器运行结果:

运行容器

同时 Docker Dashboard 也显示了 demo 容器 mycontainer 的运行状态:

Docker Dashboard

注意:
容器名可用于各种命令,如移除容器 docker rm -f <container-id|container-name>。通过自定义的容器名,可以省去执行实际操作前先用 docker psdocker container ls -a 查看 containerId 这一步。

容器名可通过命令 docker container rename 进行修改。

五、发布至镜像仓库

Docker hub 是 docker 官方维护的镜像仓库。该步骤会将本地构建的镜像发布到该仓库。

  • 注册并登录至 docker hub(过程略)。
  • 在 docker hub 中点击 “Create Reposotiry” 按钮创建项目仓库,填写仓库名为 “demo-repo”. 右侧提示为镜像发布的命令。

  • 本地命令行中登录: docker login -u <your-user-name> 并输入密码。

CLI 登录

  • 生成本地镜像与远程仓库的映射:docker tag demo-image:v1 scvthedefect/demo-repo:repo-v1; 注意这里特意区分了本地与远程仓库的镜像名和标签名。
  • 推送到远程仓库:docker push scvthedefect/demo-repo:repo-v1

推送成功1

推送成功2

  • 测试:打开 Play with Docker 网站,登录并新建实例(貌似要科学一下,否则部分资源加载不了)

可见在其他服务器中下载镜像和运行容器成功,测试完成。

5. 本地运行 MySQL

演示本地运行 MySQL, 版本号为 5.7.

  • 在官方仓库 Docker Hub 中搜索 MySQL 项目,可勾选左侧的过滤器:已验证的发布者、官方镜像,或按分类筛选等。

搜索页

  • 点击搜索结果,在项目主页的标签页 “Tags” 找到想要的版本,并用 docker pull 命令下载。

镜像主页

  • 启动 mysql 容器:
1
docker run -dp 3306:3306 --name mysql57 -v ~/docker-data/mysql57/:/var/lib/mysql/ -e MYSQL_ROOT_PASSWORD=myRootSecret mysql:5.7

命令说明:

  1. -dp 3306:3306: 以后台进程方式运行、且绑定宿主 3306 端口与容器 3306 端口绑定。
  2. --name mysql57: 自定义的容器名,方便后续操作无需先查 containerId.
  3. -v ~/docker-data/mysql57/:/var/lib/mysql/: 使用 Bind Mount 的方式,将 mysql 内的数据目录映射到宿主目录(~/docker-data/mysql57),即使该容器被删除,新运行的容器只要也指定该目录,即可实现数据的共享。
  4. -e MYSQL_ROOT_PASSWORD=myRootSecret: -e 指定了容器的环境变量,具体的值为。此处相当于安装 MySQL 后登录时要提供 root 密码。该密码任意指定即可。
  5. mysql:5.7: 镜像名与标签名。
  • 登录容器并使用 mysql 的 root 账号及密码登录 mysql client:

docker exec -it mysql57 /bin/bash

命令说明:

  1. docker exec <container> <command>: 表示在容器内执行一条命令。
  2. -it: --interactive--tty 的简写,即创建一个可交互的 tty 终端。
  3. 此处的 shell 为 /bin/bash, 对于基于 alpine 的镜像版本,可使用 /bin/shsh

登录 MySQL 容器

  • 退出容器:ctrl + p + q, 注意一定要按照顺序。

  • 其他

    • 使用 Navicat 登录连接 mysql.
    • 更多的 mysql 设置可参考 mysql 的 docker hub 主页。

6. 扩展阅读

  1. Docker 入门教程 - 阮一峰
  2. Nginx 容器教程 - 阮一峰
  3. 什么是Docker?原理,作用,限制和优势简介 - Red Hat
  4. 什么是容器编排?- Red Hat