docker深入浅出
docker概览
略
docker安装
启动Hyper-V和容器特性
- 开始菜单-设置-搜索并选择“启用或关闭windows功能”-勾选Hyper-V和容器
没有的话参考这个blog - 去官网下载安装,结束以后重启
- 如果提示WSL2有问题,去这里下载更新
- 输入
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 stop
和docker 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 run
和docker 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
删除全部镜像