深入浅出Docker 1-6章 学习笔记

docker深入浅出

docker概览

docker安装

启动Hyper-V和容器特性

  1. 开始菜单-设置-搜索并选择“启用或关闭windows功能”-勾选Hyper-V和容器

    没有的话参考这个blog
  2. 官网下载安装,结束以后重启
  3. 如果提示WSL2有问题,去这里下载更新
  4. 输入docker verison,如果有类似消息
Client:
 Cloud integration: v1.0.22
 Version:           20.10.12
 API version:       1.41
 Go version:        go1.16.12
 Git commit:        e91ed57
 Built:             Mon Dec 13 11:44:07 2021   
 OS/Arch:           windows/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.12
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.12
  Git commit:       459d0df
  Built:            Mon Dec 13 11:43:56 2021   
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.12
  GitCommit:        7b11cfaabd73bb80907dd23182b9347b4245eb5d
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

开始玩吧

ubuntu安装

sudo apt install docker.io

存储驱动

可以通过docker system info查看

...
 Storage Driver: overlay2
...
  • 每个docker主机只能选择一种存储驱动
  • linux下修改/etc/docker/daemon.json,重启生效,不能在运行时修改,否则可能会找不到原有镜像和容器(改回原有驱动可恢复)
  • 如果想要切换存储引擎后继续使用原有镜像和容器,可将镜像保存在docker格式,上传到仓库后,修改本地docker引擎并重启,然后拉取到本地
  • 默认情况下device mapper使用loopback mounted sparse file作为底层实现,如果想提升docker在生产环境中的性能,需要修改为direct-lvm。该模式使用基于裸块设备(row block device)的LVM精简池来提升性能,具体太高深了,我不看

纵观docker

运维视角

docker有两个组件,客户端和docker daemon(服务端或引擎)

默认安装时client和daemon通过本地socket进行通信/var/run/docker.sock,windows则通过npipe:./pipe/docker_engine的管道进行通信
可以通过docker version查看client和server,运行docker version,显示如下:

Client:
 Version:           20.10.7
 API version:       1.41
 Go version:        go1.13.8
 Git commit:        20.10.7-0ubuntu5~20.04.2
 Built:             Mon Nov  1 00:34:17 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          20.10.7
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.8
  Git commit:       20.10.7-0ubuntu5~20.04.2
  Built:            Fri Oct 22 00:45:53 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.5.5-0ubuntu3~20.04.1
  GitCommit:        
 runc:
  Version:          1.0.1-0ubuntu2~20.04.1
  GitCommit:        
 docker-init:
  Version:          0.19.0
  GitCommit:        

如果提示缺少server权限,执行sudo chmod 666 /var/run/docker.sock,后重新查看

建议把docker加入sudo用户组,参考这个

镜像

可以理解为包含OS文件系统和应用的对象,指未运行的容器,可以理解为代码中的class,使用docker image ls查看镜像。每个镜像有自己的唯一ID

参考demo拉取镜像并运行

容器

启动容器docker container run -it alpine /bin/sh

使用Ctrl+PQ挂起容器

使用docker container ls查看运行状态容器

使用docker container exec -选项 容器名/ID shell名连接到容器
挂起容器时,可以使用docker container stopdocker container rm来停止并杀死容器

  • 这两个指令的区别在于,stop发SIGTERM信号给容器并等待容器关闭,rm发送SIGKILL强制关闭

开发视角

实例参考配套资源

查看dockerfile cat Dockerfile

# Test web-app to use with Pluralsight courses and Docker Deep Dive book
# Linux x64
FROM alpine

LABEL maintainer="nigelpoulton@hotmail.com"

# Install Node and NPM
RUN apk add --update nodejs nodejs-npm

# Copy app to /src
COPY . /src

WORKDIR /src

# Install dependencies
RUN  npm install

EXPOSE 8080

ENTRYPOINT ["node", "./app.js"]

其中每一行都是构建镜像的命令,使用docker build -t test:latest .根据dockerfile构建镜像,必须在包含应用代码和Dockerfile的目录下执行这条命令,结果:

Sending build context to Docker daemon  10.24kB
Step 1/8 : FROM alpine
 ---> c059bfaa849c
Step 2/8 : LABEL maintainer="nigelpoulton@hotmail.com"
 ---> Running in 7a3f72be141c
Removing intermediate container 7a3f72be141c
 ---> 829496dc94f4
Step 3/8 : RUN apk add --update nodejs nodejs-npm
 ---> Running in 0fcb5c6173d8
fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/community/x86_64/APKINDEX.tar.gz
ERROR: unable to select packages:
  nodejs-npm (no such package):
    required by: world[nodejs-npm]
The command '/bin/sh -c apk add --update nodejs nodejs-npm' returned a non-zero code: 1

却少包,这个问题在ubuntu上不好搞,把dockerfile里nodejs-npm改成npm,有警告但是可以编过。启动镜像

docker run -d \
 --name web1 \
 --publish 8080:8080 \
 test:latest`

ifconfig查看docker IP,浏览器输入IP:8080看到Hello Pluralsighters!!!

这就是应用构建到容器的过程

docker 引擎

这章不影响使用,只讲原理,可以跳过

简介

docker引擎主要有client、守护进程daemon、containerd以及runc

详解

整体架构:

  • client:docker命令
  • daemon:API和其他特性
  • containerd:容器supervisor,开启,停止等生命周期管理,不能push和pull
  • shim:启动无守护进程容器
  • runc:容易运行时,OCI规范的参考实现,轻量的对libcontainer进行封装的命令行交互工具
  • 运行容器
    K8s通过cri-containerd使用containerd

    容器启动过程:
  • 输入命令docker run --name ctr1 -it alpine:latest sh
  • client将命令转换为合适的API格式,发送给正确的API端点
  • daemon实现API,接受指令后指示containerd启动新容器,通过gPRC与containerd进行通信
  • containerd将docker镜像转换为OCI bundle(镜像),指示runc创建容器
  • runc与操作系统内核通信,基于必要工具创建容器,容器进程作为runc子进程启动,启动后runc退出,但是关联的containerd-shim进程会成为容器父进程,其职责为:

保持STDIN和STDOUT流开启,从而当daemon重启的时候,容器不会因为pipe关闭终止

将容器退出状态反馈给daemon

daemon的功能:镜像管理、镜像构建、REST API、身份验证、安全、核心网络与编排

docker 镜像

简介

可以理解为class,常见仓库为docker hub,镜像由多个层组成,每层叠加形成一个独立的对象。镜像内部是一个精简的操作系统,同时包含应用运行必须的文件和依赖包。

详解

镜像是一种build-time结构,容器是一种run-time结构

镜像和容器

通常用docker container rundocker service create从镜像启动一个或多个容器

容器启动后,而这就变成相互依赖的关系,在镜像上启动的容器全部停止前,镜像是无法删除的

镜像比较小

通常docker镜像只有一个精简的shell甚至没有shell,镜像不包含内核

容器可以共享宿主机内核

拉取镜像

linux本地镜像位于/var/lib/docker/overlay2,使用docker image pull 仓库:tag拉取镜像

镜像仓库

类似于git,默认为docker hub,不指定tag时默认拉取latest,latest不一定是最新的

从非官方账户拉取时docker image pull 账户/仓库:tag

从第三方镜像服务仓库拉取docker pull URL/账户/仓库:tag

一个镜像可以有多个不同的标签

过滤docker image ls内容

通过–filter参数来过滤,例如悬需镜像docker image ls --filter dangling=true

没有标签的镜像被称为悬虚镜像,当为新镜像打一个旧镜像已经使用过的标签时,标签会生效到新镜像,旧镜像成为悬虚镜像

可以通过docker image prune移除所有悬虚镜像,添加-a移除没有被容器使用的镜像

docker支持的过滤器:

  • dangling:true or false,仅返回悬虚或者非悬虚镜像
  • before:镜像名或ID,返回之前的所有镜像
  • since:同上,返回之后
  • label:根据label过滤,ls默认不现实label

其他的过滤方式可以使用reference

通过CLI方式搜索Docker Hub

docker search命令允许通过CLI的方式搜索Docker Hub,可以通过NAME进行匹配,例如docker search nigelpoulto
结果有点长我就不放了,可以使用--filter "is-official=true"过滤官方镜像。可以使用--limit指定行数,默认结果只有25行

镜像和分层

docker镜像由一些松耦合的只读镜像层组成,执行docker image pull ubuntu:latest的输出:

latest: Pulling from library/ubuntu
08c01a0ec47e: Pull complete 
Digest: sha256:669e010b58baf5beb2836b253c1fd5768333f0d1dbcb834f7c07a4dc93f474be
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest

Pull complate那行在结束前显示的为pull fs layer,还可以通过docker image inspect ubuntu:latest查看镜像分层

...
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:36ffdceb4c77bf34325fb695e64ea447f688797f2f1e3af224c29593310578d2"
            ]
        },
...

所有docker镜像都起始于一个基础镜像层,例如基于Ubuntu16.04创建一个镜像,就是第一层,然后给镜像添加Python包,就是第二层,继续添加一个安全补丁,就会创建第三层。更新底层添加的文件也需要创建新的层。

共享镜像层

如果存在本地文件,在拉取镜像时有的layer会提示Alread exists,docker通过这种方式实现共享镜像层

根据摘要拉取镜像

由于标签可变,还可能多个镜像打同一个标签,可以通过镜像摘要(Image Digest)区分。

每个镜像都有一个基于内容的散列值,这里用摘要代指这个值,通过docker image ls --digests alpine查看

目前只支持拉取到本地后查看摘要,然后执行docker image pull apline@sha256:散列值拉取制定镜像

镜像散列值

镜像是一系列松耦合的独立层的集合,镜像的唯一标识是一个加密ID,每个镜像层也有一个加密ID区分。由于拉取过程中需要压缩,会改变内容和散列值,相应地docker hub会提供一个分发散列值,这是一个压缩版镜像的散列值,用于校验

多架构的镜像

即一个镜像标签之下可以支持多个平台和架构。为了实现这个特性,镜像仓库API支持两种重要的结构:Manifest列表和Manifest

Manifest列表指某个镜像标签支持的架构列表,支持的每种架构都有自己的Mainfest定义

假设要在Raspberry Pi(基于ARM架构的Linux)上运行Docker,拉取镜像时,Docker client会调用Docker Hub镜像仓库的API完成拉取,如果镜像有Manifest列表,并且存有Linux on ARM这一项,则Dcoker Client就会找到ARM架构对应的Manifest并解析出组成该镜像的镜像层加密ID,然后从Docker Hub二进制存储中拉取每个镜像层

所有官方镜像都支持Manifest列表

删除镜像

docker image rm ID,只能删除没有运行状态容器的镜像

docker image rm $(docker image ls -q) -f删除全部镜像

上一篇:Web前端 Canvas图片加载、拖拽、缩放、绘制多边形、打点,坐标偏移获取和颜色获取 例程


下一篇:C++写一个Array