Docker读书笔记

背景

之前有遇到一篇博文讲docker的,是一篇概要型的文章,对docker常见的命令做了简要的描述,算是启发型的吧。现下打算好好的看看docker的具体内容,为转k8s做准备。

此次阅读的书是《Docker技术入门与实践》,算是入门级的书籍,本文也是对读书过程中遇到的知识点做简要的描述。

内容

Docker安装

关于Docker的安装,网上一搜一大堆,没有必要做过多的记忆,毕竟这类事只要不换环境,基本一劳永逸。但是文中讲述了我一直有疑问的两个点:

  1. 为啥每次都要sudo才能启动docker的服务?

    因为当前用户不在docker用户组中,解决方案是将当前用户添加到用户组中即可

    1
    2
    3
    whoami

    sudo usermod -aG docker user_name
  2. docker生产环境中建议安装稳定版,安装后还需要检查是否开了调试模式,建议关闭

    1
    2
    # 查看是否开启了调试模式
    sudo docker version -f '{{.Server.Experimental}}'

镜像

Docker的组成一共三大块:镜像、容器、仓库,类似于git,仓库可以通过配置切换为阿里、腾讯等国内镜像源,仓库中存放的都是镜像,所以一般第一步还是配置镜像源(在搭建可视化的时候也讲过),这样速度更快。

1
2
3
4
5
6
7
# 修改/etc/docker/daemon.json文件,如无该文件,则创建一个即可,将其内容修改为如下:
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}

# 上述文件修改以后执行命令
sudo systemctl daemon-reload

然后拉取镜像。

1
2
3
4
5
6
7
8
9
10
11
# 拉取镜像
docker image pull [仓库地址/镜像名:版本号]

# 仓库地址可省略,则默认从自定义的仓库下载
docker image pull [镜像名:版本号]

# image可省略
docker pull [镜像名:版本号]

# 版本号也可省略,默认拉取latest版本的
docker pull [镜像名]

同样的,如果在生产环境中需要拉取镜像的时候,则一定要带软件的指定版本号,毕竟最新的版本意味着存在潜在的不稳定因素。

那么如何查看本地有多少的镜像呢?

1
2
3
4
sudo docker images

# 或者
sudo docker image ls

为了过滤images输出内容(少敲几个单词,就用docker images概述了),docker提供了如下参数(只列举了常用):

1
2
3
4
5
6
7
8
# -a:列出所有的镜像文件,包括临时文件
sudo docker images -a

# -q:只显示镜像对应的镜像ID
sudo docker images -q

# --no-trunc:对输出结果是否截断,true表示不截断,false表示截断
sudo docker images --no-trunc=true

此外docker还支持给镜像起别名,实际上都指向同一个images。so,我觉着没啥特别大的用

1
sudo docker tag [镜像名:版本] [别名:版本]

如果说镜像通过上述的命令就能将它的身世背景讲述清楚的话,那就太单纯了。如果你想看一个镜像的详细背景,则可以:

1
2
3
4
5
6
7
sudo docker image inspect [镜像名:版本]

# yeah,image是可以省略的
sudo docker inspect [镜像名:版本]

# 版本也可以省略,只要镜像名别重复
sudo docker inspect [镜像名]

But,如果你查看了结果,可能就会被它宏大的背景所hold住,所以为了方便我们检索其中需要的内容,则可以:

1
2
3
sudo docker image inspect -f {{'.字典的键名'}} [镜像名:版本]

# 如你所想,image和版本都可根据情况省略,但是-f却是需要的

除此之外,docker还记录了镜像创建时的指令,可通过如下方式查看:

1
sudo docker history [镜像名]

那么如果我们不知道仓库中到底有哪些版本怎么办呢?docker提供了搜索相关的指令

1
2
3
4
5
6
7
8
9
10
sudo docker search [镜像名]

# 当然,没有参数岂不太low
# -f:过滤
# --format string:格式化输出
# --limit int:限制输出的条数
# --no-trunc:不截断输出

# 以-f为例
sudo docker search -f is-official=true nginx --limit 10 --no-trunc

如果我们想要删除已存在的镜像,则有两种方式:标签、镜像ID

1
2
3
4
5
6
7
8
# 通过标签删除
sudo docker image rm [镜像名]

# 通过镜像ID
sudo docker image rm [镜像ID]

# 当然上面可以简写
sudo docker rmi [镜像]

同样的,删除镜像也提供了参数:

1
2
3
4
5
# -f:强制删除,不管有没有创建对应的容器
sudo docker rmi [镜像] -f

# -no-prune:不要清理未带标签的父镜像
sudo docker rmi [镜像] -no-prune

通过标签删除镜像时有一点需要注意:删除一个镜像多标签中的一个时,不会删除镜像本身;而如果该镜像只有一个标签,则会将镜像完全删除。通过镜像ID删除时,则会将该镜像对应的所有的标签全部删除

使用docker一段时间后,系统中可能会遗留一些临时的镜像文件,这些文件并没有什么用处,徒耗空间,所以需要对其进行清理,可通过:

1
2
3
4
5
6
docker image prune 

# 同样也提供了参数:
# -f:强制删除,不用确认
# --filter filter:删除指定过滤器的内容
# -a:删除所有的无用镜像,不光是临时镜像

删除的镜像参数中,-a则会将所有没有container的镜像全部删除.

接下来说的内容,可能过不是很常用:自制镜像。制作镜像有三种方式:基于现有的容器、使用模板、基于dockerfile(后续单独讲)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 基于现有的容器创建镜像
docker container commit -m "备注信息" -a "作者名字" [容器ID] [镜像名:版本]

# container 可以省略
docker commit -m "备注信息" -a "作者名字" [容器ID] [镜像名:版本]

# 类似的参数还有:
# -m:类似版本备注,--comment="message"
# -a:作者,--author="shuai"
# -p:提交时暂停容器的运行,--pause=true
# -c:提交时会执行的命令,--change=[]


# 基于模版创建镜像
cat nginx.tar.gz | docker import - nginx:latest

那么将这些镜像导出为文件,或者将镜像文件导入到docker中呢?

1
2
3
4
5
6
7
8
# 将已存在的镜像打包导出为文件
sudo docker save -o [本地文件.tar] [镜像文件:版本]

# 将本地文件载入docker
sudo docker load -i [本地文件]

# 或者
sudo docker load < [本地文件]

至于自制的镜像上传,额,我觉着我现在还用不到吧!

容器

镜像相当于从互联网上下载了一个应用程序,而容器则相当于将这个镜像安装在本机器上。容器才是docker内部程序运行的襁褓,是docker技术栈的核心,而镜像算它爹吧。那么万物始于创建,container是基于image创建得来的。

1
2
3
4
sudo docker container create [镜像名:版本]

# 同样的,container和版本可视情况省略
sudo docker create [镜像名]

create只是基于镜像创建了容器,该容器还没有运行,如果需要运行,则:

1
2
3
4
sudo docker container start [容器ID]

# 省略container
sudo docke start [容器ID]

那么,这个容器ID从何而来呢?每创建一个容器,则系统会生成一个对应的容器ID,所以我们只需要打印当前有哪些容器即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看当前正在运行的容器。画风突变有木有,container呢。
sudo docker ps

# 直到我查了典籍(别人的),发现可以这样,
sudo docker container ps

# 还可以这样,嗯~好理解多了
sudo docker container ls

# 然后,加上-a查看所有的容器
sudo docker container ls -a

# 再结合一开始的,有时候,知识大概就是这么枯燥又无奈吧。
sudo docker ps -a

but,上面的还是有些繁琐,如果将创建和启动融合一下呢?yeah,可以的!it’s run

1
2
3
4
sudo docker container run [镜像名称]

# container 去掉
sudo docker run [镜像名称]

run的镜像或者版本如果不存在,则docker会自动到仓库下载对应的镜像后再启动。

说到这里,你会发现,居然没有提到一个参数,显然不合理,然后为啥不列,主要因为容器相关的参数之多,唯有实践中学习,譬如:

1
2
3
4
sudo docker run -it ubuntu:latest /bin/bash

# -i:表示创建将容器的标准输入打开
# -t:表示建立一个伪终端,并将其绑定到标准输入上

上述的方式,在用户退出容器后,容器就会自动的停止运行!what,显然很垃圾,我要的全天候24小时呵护呢?也是可以的

1
2
# -d:让容器以守护态运行
sudo docker run -d [容器ID]

那么对于那些运行的容器的日志如何查看呢?

1
2
3
4
5
6
7
8
9
sudo docker container logs [容器ID]

# 当然有的是参数
# --details:打印详细信息
# -f:持续的输出日志内容,--follow
# --since string:打印从某个时间段之后的日志
# --until string:打印某个时间段之前的内容
# -t:显示时间戳内容
# --tail string:显示最近若干日志

依我看,上面最常用的也就:-detail、-since string、-t

如果我们想暂停容器的运行呢?则可以通过以下方式:

1
2
3
4
5
6
7
8
# 暂停容器运行
sudo docker container pause [容器ID]

# 取消暂停状态
sudo docker container unpause [容器ID]

# container可省略
sudo docker pause [容器ID]

如果终止容器的运行,或者重启容器,则可以通过如下方式:

1
2
3
4
5
6
7
8
# 停止容器运行
sudo docker container stop -t [second] [容器ID]

# 重启容器
sudo docker container restart [容器ID]

# container和-t(--time)可省略,默认-t时间是10秒
sudo docker restart [容器ID]

容器运行期间,如果我们需要进入容器的内部,则可以通过如下方式:

1
2
3
sudo docker container exec [option] [容器ID] [additional]

# container可省略

对于option的参数部分则有如下选择:

1
2
3
4
5
6
7
8
9
10
# -d:在容器后台执行命令,--detach
# -t:分配伪终端,默认值为false,--tty=true|false
# -i:打开标准输入接受用户输入命令,默认值为false,--interactive=true|false
# -u:执行命令的用户名或ID,--user=""
# --detach-key="":指定将容器退回后台的按键
# -e:指定环境变量列表,--env=[]
# --privileged=true|false:是否给执行命令以高权限,默认值为false

# 最常用的应该就是-d、-t、-i、-u了
sudo docker exec -it 16659 /bin/bash

如果对于不需要的容器进行删除,则可通过rm,是不是倍感亲切:

1
2
3
4
sudo docker container rm [容器ID]

# container依旧可以忽略
sudo docker rm [容器ID]

然后对于删除容器的命令rm而言,它并不能删除处于运行状态下的容器,如果非要删除,则可以通过如下方式:

1
2
3
4
# -f:强行终止运行的容器,并删除它
sudo docker rm -f [容器ID]

# 与上类似的参数还有:-l(删除容器的链接,但是保留容器)、-v(删除容器挂载的数据卷)

有些时候呢,我们需要将容器迁移备份,抑或是打包到新的环境下运行,docker也提供这种功能

1
2
3
4
5
6
7
# 将已知的容器导出为文件
sudo docker export -o [文件.tar] [容器ID]

# 将容器文件导入到docker中,导入的内容会变为镜像
sudo docker import [文件.ID] [容器ID:版本]

# 如你所想,上面的将container省略了

此前在镜像中我们也讲到将镜像导出后导入,这个与容器导入为镜像的区别就在于:容器导入生成的镜像将丢失所有的历史记录和元数据。这个我听着也不懂!!

那么除上述内容以外,容器还有哪些非常实用的内容呢?查看容器详情是一个:

1
2
# containr可省略哈
sudo docker container inspect [容器ID]

查看容器内部内存状态:

1
2
# container依旧可省略哈
sudo docker container top [容器ID]

查看当下运行的容器所占用的内存资源:

1
2
3
4
5
6
7
sudo docker container stats

# 有几个参数得知道
# -a:表示显示所有的容器所占用的资源,包括未运行的
# --format string:格式化输出
# --no-stream:非持续输出
# --no-trunc:输出内容不截断

如果需要将主机文件复制到docker内部,则可以通过cp

1
2
3
4
5
sudo docker container cp [宿主机文件] [容器ID]:[路径]

# 有两个参数
# -a:打包模式,即复制进去的文件携带原本的UID和GID信息
# -L:跟随软连接,则会将软连接指向的文件内容复制进去

查看容器中文件的变更情况

1
sudo docker container diff [容器ID]

查看容器的端口映射情况

1
sudo docker container port [容器ID]

其实每个容器在启动后都会占用系统的资源,docker也可以针对任何一个容器设置其硬件资源。有时候容器已经创建,此时修改其资源的方式如下:

1
2
3
4
sudo docker update [资源配置名] [资源配置参数] [容器ID]

# 比如,如果希望启动容器的时候,能够自动的将对应的容器一并启动,则
sudo docker update --restart always ubuntu

关于资源配置:https://docs.docker.com/engine/reference/commandline/update/

仓库

Docker仓库存放的是镜像文件,用户可以自主上传自制的镜像,也可以下载别人制作的镜像,上传镜像的时候就需要账户和密码,这个直接在命令行就可以解决;

1
docker login

当然我们也可以下载第三方仓库的镜像

1
docker pull [仓库服务器]/[仓库名称]/[镜像名称:版本]

那么如果我们想自建一个docker私人版本库呢?docker也同样支持

1
2
3
4
5
# 5000是端口号,registry是私人仓库的镜像文件,2是其版本号
docker run -d -p 5000:5000 registry:2

# 镜像文件默认是存放在镜像中的:/var/lib/registry文件夹中,可以通过-v将镜像映射到本地的镜像文件夹
docker run -d -p 5000:5000 registry:2 -v /opt/lib/registry:/var/lib/registry

运行起来以后,则可以通过IP+端口(5000)上传和下载镜像文件了,以下举个例子:

1
2
# 修改现有镜像的标签,使其符合:[仓库地址:端口]/[镜像名称]
docker tag ubuntu:latest 192.168.85.129:80/test

然后上传到私人仓库。

1
docker push 192.168.85.129:80/test

此时会提示报错,因为docker新版要求ssl验证,为了跳过该验证,则还需要在daemon.json文件中配置免认证

1
2
3
4
5
# /etc/docker/daemon.json
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"],
"insecure-registries": ["192.168.85.129:80"]
}

然后就可以正常的上传了。

总结

虽然说docker 的命令较多,但是仔细观察会发现,不管是镜像、容器,其image、container都是可以省略的,唯一比较难以记忆的就是参数。