Skip to content

Instantly share code, notes, and snippets.

@dreamer2q
Last active August 26, 2020 17:12
Show Gist options
  • Save dreamer2q/331b22356462a8c003463707475ad9b8 to your computer and use it in GitHub Desktop.
Save dreamer2q/331b22356462a8c003463707475ad9b8 to your computer and use it in GitHub Desktop.
学习docker笔记

Docker 入门

最近开始写了个后台,需要进行部署。

因为环境是 docker swarm,这里记录一下学习 docker 笔记。

介绍

安装

一般不会在win上面安装吧?

这里只记录linux安装过程

  • debian
  1. 卸载旧版本
apt-get remove docker \
               docker-engine \
               docker.io
  1. APT安装
  • 安装依赖
apt-get install \
     apt-transport-https \
     ca-certificates \
     curl \
     gnupg2 \
     lsb-release \
     software-properties-common
  • 添加 GPG 密钥
curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/debian/gpg | sudo apt-key add -


# 官方源
# $ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
  • 添加源
sudo add-apt-repository \
   "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/debian \
   $(lsb_release -cs) \
   stable"

# 官方源
# sudo add-apt-repository \
#    "deb [arch=amd64] https://download.docker.com/linux/debian \
#    $(lsb_release -cs) \
#    stable"
  • 安装
apt install docker-ce # CE 即社区版(免费,支持周期 7 个月)
  • 启动
systemctl enable docker
systemctl start docker
  • 建立 docker 用户组
# 建立 docker gourp
groupadd docker
# 添加 用户到docker组
usermod -aG docker $USER
  • 测试安装
docker run hello-world

备注

由于网络的原因,请使用镜像安装。

使用

Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。

镜像加速器

对于使用 systemd 的系统,请在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件)

{
  "registry-mirrors": [
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com"
  ]
}

重启服务

systemctl daemon-reload
systemctl restart docker

获取镜像

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
docker pull ubuntu:18.04
18.04: Pulling from library/ubuntu
7595c8c21622: Pull complete
d13af8ca898f: Pull complete
70799171ddba: Pull complete
b6c12202c5ef: Pull complete
Digest: sha256:a61728f6128fb4a7a20efaa7597607ed6e69973ee9b9123e3b4fd28b7bba100b
Status: Downloaded newer image for ubuntu:18.04
docker.io/library/ubuntu:18.04
  • Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
  • 仓库名:即<用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。

从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性。

运行

docker run -it --rm \
    ubuntu:18.04 \
    bash
  • -it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。

  • --rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。

  • ubuntu:18.04:这是指用 ubuntu:18.04 镜像为基础来启动容器。

  • bash:放在镜像名后的是 命令,这里我们希望有个交互式 Shell,因此用的是 bash。

列出镜像

docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              18.04               2eb2d388e1a2        2 weeks ago         64.2MB
hello-world         latest              bf756fb1ae65        7 months ago        13.3kB
  • 镜像体积

列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。

docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              2                   1                   64.24MB             64.23MB (99%)
Containers          1                   0                   0B                  0B
Local Volumes       0                   0                   0B                  0B
Build Cache         0                   0                   0B                  0B
  • 虚悬镜像

虚悬镜像是由于新镜像覆盖了原来的镜像(镜像名称不变,但是 ID 变了),于是原来的镜像名称就消失了。

# 查看虚悬镜像
docker image ls -f dangling=true
# 删除
docker image prune
  • 中间层镜像

为了加速镜像构建、重复利用资源,Docker 会利用 中间层镜像。所以在使用一段时间后,可能会看到一些依赖的中间层镜像。默认的 docker image ls 列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加 -a 参数。

删除镜像(本地)

docker image rm [选项] <镜像1> [<镜像2> ...]
  • 用 ID、镜像名、摘要删除镜像
docker image rm bf7
Untagged: hello-world:latest
Untagged: hello-world@sha256:7f0a9f93b4aa3022c3a4c147a449bf11e0941a1fd0bf4a8e6c9408b2600777c5
Deleted: sha256:bf756fb1ae65adf866bd8c456593cd24beb6a0a061dedf42b26a993176745f6b
Deleted: sha256:9c27e219663c25e0f28493790cc0b88bc973ba3b1686355f221c38a36978ac63

因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 Untagged 的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 Delete 行为就不会发生。所以并非所有的 docker image rm 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。

当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变得非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己 docker pull 看到的层数不一样的原因。

除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。之前讲过,容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。

操作容器

启动

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。

  • 新建并启动
docker run ubuntu:18.04 /bin/echo 'Hello world'
Hello world

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止
  • 启动已终止容器

可以利用 docker container start 命令,直接将一个已经终止的容器启动运行。

守护态运行

更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d 参数来实现。

容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。

为了确保容器可以长久运行,请确保你的程序不会崩溃。

要获取容器的输出信息,可以通过 docker container logs 命令。

终止

docker container stop 来终止一个运行中的容器。 此外,当 Docker 容器中指定的应用终结时,容器也自动终止。

处于终止状态的容器,可以通过 docker container start 命令来重新启动。 此外,docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。

进入容器

  • attach

如果从这个 stdin 中 exit,会导致容器的停止。

  • exec

-i -t 参数

只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。 当 -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。

如果从这个 stdin 中 exit,不会导致容器的停止。这就是为什么推荐大家使用 docker exec 的原因。

导出和导入

  • 导出容器

如果要导出本地某个容器,可以使用 docker export 命令。

  • 导入容器快照

可以使用 docker import 从容器快照文件中再导入为镜像,例如

用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

删除

  • 删除容器

可以使用 docker container rm 来删除一个处于终止状态的容器。

  • 清理所有处于终止状态的容器
docker container prune

小结

刚刚接触docker还是得从安装说起。

虽然有自动化的脚本,但是我们学习的时候还是要自己手动安装。

这样出了问题,至少不会一脸懵逼。

接着就是docker的运行了。

docker 的运行就是一个后台 daemon 进程,我们在控制台上使用的docker各种命令无非就是通过协议来操作 daemon 罢了。理解这一点就更好的理解后台的swarm模式了。也因此,我们可以自己实现一个client,例如docker-compose等。

接着我们学习如何管理镜像docker image系列命令。

image可以看出是一个操作系统,而container则是运行操作系统的计算机。

最好我们学习如何管理容器,就如同如何管理一台台运行的电脑一般。

Docker 入门

仓库

Docker Hub

  • 注册
  • 登录

docker login 命令交互式的输入用户名及密码来完成在命令行界面登录 Docker Hub。 你可以通过 docker logout 退出登录。

docker 的登录可以类比于 github,只有你登录了才能拉取你的私有仓库。

  • 拉取镜像

你可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地。

根据是否是官方提供,可将镜像分为两类。 一种是类似 centos 这样的镜像,被称为基础镜像或根镜像。这些基础镜像由 Docker 公司创建、验证、支持、提供。这样的镜像往往使用单个单词作为名字。 还有一种类型,比如 tianon/centos 镜像,它是由 Docker Hub 的注册用户创建并维护的,往往带有用户名称前缀。可以通过前缀 username/ 来指定使用某个用户提供的镜像,比如 tianon 用户。

  • 推送镜像

用户也可以在登录后通过 docker push 命令来将自己的镜像推送到 Docker Hub。

  • 自动构建

类比于 github action, docker 的自动构建可以跟踪你的 commit,进而构建你的镜像(需要配置好 Dockerfile)。

不过我更喜欢用 action 打包好推送到 hub 上面。

私有仓库

有时候使用 Docker Hub 这样的公共仓库可能不方便,用户可以创建一个本地仓库供私人使用。

相比于创建自己的 registry 不如使用阿里云的 registry 服务。

  • 安装运行 docker-registry
# 获取官方 registry 镜像来运行
docker run -d -p 5000:5000 --restart=always --name registry registry

这将使用官方的 registry 镜像来启动私有仓库。默认情况下,仓库会被创建在容器的 /var/lib/registry 目录下。你可以通过 -v 参数来将镜像文件存放在本地的指定路径。

  • 在私有仓库上传、搜索、下载镜像
# 部署registry镜像
docker run -d -p 5000:5000 --restart=always --name registry registry

# 使用 docker tag 将 ubuntu:18.04 这个镜像标记为 127.0.0.1:5000/ubuntu:18.04.d
docker tag ubuntu:18.04 127.0.0.1:5000/ubuntu:18.04.d

# 上传
docker push 127.0.0.1:5000/ubuntu:18.04.d

# 查看
curl 127.0.0.1:5000/v2/_catalog
{"repositories":["ubuntu"]}

docker image rm 127.0.0.1:5000/ubuntu:18.04.d

docker pull 27.0.0.1:5000/ubuntu:18.04.d
  • 注意事项

如果你不想使用 127.0.0.1:5000 作为仓库地址,比如想让本网段的其他主机也能把镜像推送到私有仓库。你就得把例如 192.168.199.100:5000 这样的内网地址作为私有仓库地址,这时你会发现无法成功推送镜像。 这是因为 Docker 默认不允许非 HTTPS 方式推送镜像。我们可以通过 Docker 的配置选项来取消这个限制。

/etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件)

{
  "registry-mirror": [
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com"
  ],
  "insecure-registries": ["192.168.199.100:5000"]
}

一般都很少在命令行里面搜索,直接去 hub.docker.com 里面搜索,里面有详细的使用说明。

配置仓库证书

好麻烦,不想弄。

数据卷

数据卷

  • 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
  • 可以在容器之间共享和重用
  • 对 数据卷 的修改会立马生效
  • 对 数据卷 的更新,不会影响镜像
  • 数据卷 默认会一直存在,即使容器被删除

注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看到的是挂载的 数据卷。

可以看出来,数据卷类属于我们的硬盘,用来进行持久化储存的,比如说数据库文件等。

创建数据卷

# 创建一个my-volumn数据卷,具体存放在/var/lib/docker/volumes/my-vol/_data
docker volumn create my-volumn

挂载数据卷

启动容器的时候可以指定--mount参数来挂载数据卷

$ docker run -d -P \
    --name web \
    # 下面是缩写,效果等同。
    # -v my-vol:/wepapp \
    --mount source=my-vol,target=/webapp \
    training/webapp \
    python app.py

删除

# 需要确保没有容器使用这个数据卷
docker volumn rm my-volumn

ps: 对于重要的数据卷一定要记得备份。

从主机中目录为数据卷

我们把主机的目录挂载为数据卷就可以和容器共享目录了,比如说nginx的配置目录。

$ docker run -d -P \
    --name web \
    # 缩写命令更好记啊
    # -v /src/webapp:/opt/webapp \
    --mount type=bind,source=/src/webapp,target=/opt/webapp,readonly \ # 加入 ,readonly 指定数据卷为只读
    training/webapp \
    python app.py

此外,还可以把文件挂载为数据卷,类似与共享文件。

容器网络

有了网络才能提供容器里面的服务。

从外部访问容器

  • 使用 -p 来进行端口映射

格式为 -p 5000:5000 或者 -p 127.0.0.1:5000:5000

和 ssh 的映射差不多啦。

容器之间互联

说实话,网络这块地方我还是有些地方没有弄明白。但是,了解基本的概念已经可以满足个人的需求了。

  • 创建网络

创建网络可以更好的管理和隔离不同的容器

docker network create -d bridge my-net
  • 让容器加入网络
docker run -it --rm --name busybox1 --network my-net busybox sh
docker run -it --rm --name busybox2 --network my-net busybox sh

我们指定--network的参数,来让容器加入到我们创建的网络中。

这里我们创建了两个容器,现在我们进入一个容器里面,来ping一下另一个容器。

可以发现,使用busybox1名称就可以ping通了。显然docker为我们做了很多事情。

当然,docker有时候不能一步到位,这时候需要我们自己配置dns

方法和配置主机的dns差不都,都是修改/etc/resolv.conf文件。由于容器的文件系统,只能临时有效果。

小结

现在我们应该可以愉快的使用别人的镜像来创建自己专属的容器,不过,等等,我们好像还只能使用别人的镜像。如果要跑自己写的程序呢?好像挺麻烦的。其实,前面少掉了关于如何构建自己的镜像,不过下一节会将到。

不管怎样,数据卷和网络还是非常重要的内容,因为这些是程序能否持久,能否提供服务的基础。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment