一、docker架构
二、Docker 基本命令
1、查看 Docker 版本
查看 Docker 版本包括 Docker 版本号、API 版本号、对应的 Git Commit、Containerd 和 runC的版本信息等。
# docker version
Client: Docker Engine - Community
Version: 20.10.4
API version: 1.40
Go version: go1.12.10
Git commit: 9013bf583a
Built: Fri Oct 18 15:52:22 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 20.10.4
API version: 1.40 (minimum version 1.12)
Go version: go1.12.10
Git commit: 9013bf583a
Built: Fri Oct 18 15:50:54 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.6
GitCommit: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc:
Version: 1.0.0-rc8
GitCommit: 425e105d5a03fabd737a126ad93d62a9eeede87f
docker-init:
Version: 0.18.0
GitCommit: fec3683
-
OCI:Open Container Initiative的简称,由Linux基金会主导开发OCI规范和标准,目的是
围绕容器格式和Runtime(运行时)制定的一个开放的工业化标准。
-
Containerd:Docker为了兼容OCI标准,将容器Runtime及其管理功能从Docker守护进程中
剥离出来,用于不启动Docker也能直接通过Containerd来管理容器。
-
RunC:Docker按照OCF(Open Container Format)开放容器格式标准制定的一个轻量级
工具,可以使用RunC不通过Docker引擎即可实现容器的启动、停止和资源隔离等功能。
2、查看 Docker 详细信息
# docker info
Client: # Client 端
Debug Mode: false # 是否开启 Debug 模式
Server: # Server 端
Containers: 8 # 容器个数
Running: 4 # 正在运行的容器个数
Paused: 0 # 暂停的容器个数
Stopped: 4 # 停止的容器个数
Images: 8 # 镜像个数
Server Version: 20.10.4 # 当前服务器 Docker Server 的版本
Storage Driver: overlay2 # 存储驱动,一般为 overlay2,性能好速度快
# 其他驱动 aufs、overlay、brtfs
Backing Filesystem: xfs # 服务器文件系统
Supports d_type: true # 目录条目类型,用来表示一个文件是文件、管道还是套接字。
# 在格式化 xfs 文件系统时,必须指定 ftype=1
Native Overlay Diff: true
Logging Driver: json-file
# 日志驱动,json-file 表示存在本地
Cgroup Driver: cgroupfs # 限制和隔离的驱动,生产环境建议使用 systemd
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries
splunk syslog # Docker 支持的日志驱动
Swarm: inactive # Docker 官方的容器编排工具,inactive 不开启,active 开启
...
Docker Root Dir: /var/lib/docker # Docker 根目录,生产环境建议使用 SSD 硬盘,或
者独立的磁盘,不要和系统盘用同一个磁盘。
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false # Docker 热更新,生产环境建议设置为 true
3、常用命令
搜索镜像
# docker search nginx
拉取下载镜像
# docker pull nginx
# docker pull nginx:1.15
查看本地镜像
# docker images | grep nginx-v2
更改镜像 tag
# docker tag nginx-v2 dotbalo/nginx-v2:test
# 如果是推送到自己公司内部的镜像仓库,可以使用如下命令 docker tag nginx-v2 你公司的
镜像仓库地址/nginx-v2:test
# docker images | grep nginx-v2
镜像仓库登录
# docker login
Login with your Docker ID to push and pull images from Docker Hub. If
you don't have a Docker ID, head over to https://hub.docker.com to create
one.
Username (dotbalo): username
Password: 输入仓库密码
Login Succeeded
推送本地镜像到远程仓库
docker push dotbalo/nginx-v2:test
启动容器
使用 run -ti 前台启动一个容器:
# docker run -ti nginx bash
root@23bc7ccabb09:/#
# 也可以使用-ti --rm 参数,表示前台启动的容器退出后即删除
如果一个镜像需要一直运行,可以使用-d 进行后台启动:
# docker run -tid nginx bash
1bcf5154d5c3a57d92a6796f526eac2cefd962aaca9cf4098689bfe830bb9e5e
# 也可以使用--restart=always,如果容器异常自动重启
端口映射
# docker run -ti -p 1111:80 nginx bash
root@cd676d572188:/#
查看日志
# docker logs -f 容器 ID/容器名称 --tail 1
W0805 09:58:41.745799 8 controller.go:1130] SSL certificate for
server "xxx" is about to expire (2020-06-19 03:44:03 +0000 UTC)
数据持久化
# docker run -ti -p 1111:80 -v /etc/hosts:/etc/hosts nginx bash
root@cd676d572188:/#
查看当前正在运行的容器
# docker ps
查看所有容器,包括已经退出的:
# docker ps -a
查看正在运行的容器(即显示出容器的 ID):
# docker ps -q
...
0d1a98b3c402
c1fd8ff1f7f2
86b1c069024b
...
查看所有容器的 ID,包括已经退出的:
# docker ps -aq
...
17019738d93d
b3bb2a592dfb
e0637b76afe3
0b74e028d0ae
65a1b5e1e501
...
进入容器
进入到一个后台运行的容器(即之前用-d 命令参数来指定后台运行方式的容器):
# docker ps | tail -1
86b1c069024b nginx:latest "nginx -g 'daemon ..." 4 days ago
Up 21 hours 80/tcp, 0.0.0.0:16443->16443/tcp nginx-lb
# docker exec -ti 86b1c069024b bash
root@nginx-lb:/#
文件拷贝
将本机的文件拷贝到容器,拷贝支持双向拷贝,也支持将容器的内部文件拷贝到宿主机:
# docker cp README.md 92aceec0dcdd327a709bf0ec83:/tmp
#exec 也可直接执行容器命令
# docker exec 92aceec0dcdd327a709bf0ec83 ls /tmp/
README.md
删除容器和镜像
删除已经退出的容器:
# docker ps -a |grep Exited | tail -3
600e5da5c196 3cab8e1b9802
"etcd --advertise-..." 4 days ago Exited (137) 21 hours ago
k8s_etcd_etcd-k8s-master01_kube-system_c94bb8ceba1b924e6e3175228b168fe0_0
5a1848d923a1 registry.cnhangzhou.aliyuncs.com/google_containers/pause-amd64:3.1 "/pause"
4 days ago Exited (0) 21 hours ago
k8s_POD_kube-scheduler-k8s-master01_kubesystem_9c27268d8e3e5c14fa0160192a2c7988_0
280fc86494f1 registry.cnhangzhou.aliyuncs.com/google_containers/pause-amd64:3.1 "/pause"
4 days ago Exited (0) 21 hours ago
k8s_POD_etcd-k8s-master01_kube-system_c94bb8ceba1b924e6e3175228b168fe0_0
# docker rm 600e5da5c196 5a1848d923a1 280fc86494f1
600e5da5c196
5a1848d923a1
280fc86494f1
# docker ps -a |grep Exited | grep -E
"600e5da5c196|5a1848d923a1|280fc86494f1"
删除本机镜像,比如删除 REPOSITORY 或 TAG 为 none 的镜像:
# docker images | grep none
<none><none> 7ad745acca31 2 days ago
5.83MB
dotbalo/canary <none> 00f40cc9b7f6 2 days ago
5.83MB
dotbalo/canary <none> 9b0f2f308931 2 days ago
5.83MB
<none><none> c3d2357e9cbd 2 days ago
4.41MB
dotbalo/nginx <none> 97c97cee03f9 3 days ago
109MB
# docker rmi 7ad745acca31 00f40cc9b7f6 9b0f2f308931 c3d2357e9cbd
97c97cee03f9
Deleted:
sha256:7ad745acca31e3f753a3d50e45b7868e9a1aa177369757a9724bccf0654abcb2
Deleted:
sha256:0546dcf8a97e167875d6563ef7f02ddd8ad3fc0d5f5c064b41e1ce67369b7e06
Untagged:
dotbalo/canary@sha256:cdd99e578cb2cb8e84eaf2e077c2195a40948c9621d32004a9b5f4
e82a408f4d
...
Deleted:
sha256:697f26740b36e9a5aee72a4ca01cc6f644b59092d49ae043de9857e09ca9637e
区分镜像的版本可以使用 tag 命令给镜像打标签:
# docker images | grep nginx | tail -1
nginx 1.7.9 84581e99d807 3 years ago
91.7MB
#不加 URL 为默认镜像仓库
# docker tag nginx dotbalo/nginx:v1
#加 URL 指定为其他镜像仓库
# docker tag nginx harbor.xxx.net/stage/nginx:v1
构建镜像
使用 docker build 通过 Dockerfile 制作镜像。注意最后的一个点( . ),表示使用当前目录
进行构建镜像:
# docker build -t image_name:image_tag .
查看容器运行信息
# docker inspect 1de2ef08945f | grep Port
"PortBindings": {
"HostPort": "13306"
"PublishAllPorts": false,
"ExposedPorts": {
"Ports": {
"HostPort": "13306"
查看镜像构建信息
[root@k8s-master02 ~]# docker history 0e55626662ef
IMAGE CREATED CREATED BY SIZE
COMMENT
...
<missing> 21 months ago /bin/sh -c #(nop) CMD ["mysqld"]
0B
<missing> 21 months ago /bin/sh -c #(nop) EXPOSE 3306/tcp 33060/tcp
0B
...
清理不用的镜像
docker image prune -a
三、Dockerfile 的编写
Dockerfile 的常用命令如下:
- FROM:继承基础镜像
- MAINTAINER:镜像制作作者的信息,已弃用,使用LABEL替代
- LABEL:k=v形式,将一些元数据添加至镜像
- RUN:用来执行shell命令
- EXPOSE:暴露端口号
- CMD:启动容器默认执行的命令,会被覆盖
- ENTRYPOINT:启动容器真正执行的命令,不会被覆盖
- ENV:配置环境变量
- ADD:复制文件到容器,一般拷贝文件,压缩包自动解压
- COPY:复制文件到容器,一般拷贝目录
- WORKDIR:设置容器的工作目录
- USER:容器使用的用户
- ARG:设置编译镜像时传入的参数
1、创建用户
# cat Dockerfile
# base image
FROM centos:7
MAINTAINER dot
RUN useradd dot
# 执行构建
# docker build -t centos:user .
2、添加环境变量
# cat Dockerfile
# base image
FROM centos:7
MAINTAINER dot
RUN useradd dot
RUN mkdir dot
ENV envir=test version=1.0
CMD echo "envir:$envir version:$version"
执行构建并启动测试:
# 执行构建
# docker build -t centos:env-cmd .
# 启动镜像验证 ENV 和 CMD
# docker run centos:env-cmd
envir:test version:1.0
3、ENTRYPOINT
# cat Dockerfile
# base image
FROM centos:7
MAINTAINER dot
RUN useradd dot
RUN mkdir dot
ENV envir=test version=1.0
#CMD echo "envir:$envir version:$version"
ENTRYPOINT echo "envir:$envir version:$version"
执行构建并测试:
# docker build -t centos:entrypoint .
# docker run --rm centos:entrypoint
envir:test version:1.0
4、CMD 和 ENTRYPOINT 区别
CMD 可以被覆盖:
# docker run --rm centos:env-cmd echo "cover..."
cover...
ENTRYPOINT 指定的不能被直接覆盖:
# docker run --rm centos:entrypoint cannot cover...
envir:test version:1.0
ENTRYPOINT 指定–entrypoint 参数,比如指定 entrypoint 为 ls,后置命令为/tmp,就相当于
ENTRYPOINT 是 ls,CMD 是/tmp
# docker run --rm --entrypoint=ls centos:entrypoint /tmp
anaconda-post.log
yum.log
5、ADD 和 COPY
使用 ADD 添加一个压缩包,使用 WORKDIR 改变工作目录:
# base image
FROM nginx
MAINTAINER dot
ADD ./index.tar.gz /usr/share/nginx/html/
WORKDIR /usr/share/nginx/html
使用 COPY 拷贝指定目录下的所有文件到容器,不包括本级目录。
此时只会拷贝 webroot 下的所有文件,不会将 webroot 文件夹拷贝过去:
# base image
FROM nginx
MAINTAINER dot
ADD ./index.tar.gz /usr/share/nginx/html/
WORKDIR /usr/share/nginx/html
COPY webroot/ .
6、更改启动用户
# base image
FROM centos:7
MAINTAINER dot
ADD ./index.tar.gz /usr/share/nginx/html/
WORKDIR /usr/share/nginx/html
COPY webroot/ .
RUN useradd -m tomcat -u 1001
USER 1001
LABEL 和 MAINTAINER
Dockerfile使用MAINTAINER定义作者信息,但是这个参数将来会被弃用,可以使用LABEL
进行替换:
# base image
FROM centos:7
# MAINTAINER dot # 即将废弃
LABEL maintainer="dot" version="demo"
LABEL multiple="true"
制作镜像并查看镜像信息:
# docker build -t test:label .
# docker inspect test:label | grep Labels -A 10
"Labels": {
"maintainer": "dot",
"multiple": "true",
"org.label-schema.build-date": "20181006",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"version": "demo"
}
},
...
7、Dockerfile 传参
使用 ARG 和 build-arg 传入动态变量:
# base image
FROM centos:7
# MAINTAINER dot # deprecated
LABEL maintainer="dot" version="demo"
LABEL multiple="true"
ARG USERNAME
ARG DIR="defaultValue"
RUN useradd -m $USERNAME -u 1001 && mkdir $DIR
# docker build --build-arg USERNAME="test_arg" -t test:arg .
# docker run -ti --rm test:arg bash
[root@fe7a40927736 /]# ls
bin dev home lib64 media opt root selinux sys usr
defaultValue etc lib lost+found mnt proc sbin srv tmp var
[root@fe7a40927736 /]# tail -1 /etc/passwd
test_arg:x:1001:1001::/home/test_arg:/bin/bash
四、镜像大小优化
基础镜像体积:
# docker images | grep user
centos user 9457226f9505 7 minutes ago
194MB
那这 194MB 是怎么来的呢?首先看一下创建用户的 Dockerfile:
# cat Dockerfile
# base image
FROM centos:7
MAINTAINER dot
RUN useradd dot
再来看一下执行 docker build 生成镜像时控制台的日志:
# docker build --no-cache -t centos:user .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM centos:7
---> d0957ffdf8a2
Step 2/3 : MAINTAINER dot
---> Running in ad0f527d2872
Removing intermediate container ad0f527d2872
---> 422cd518454d
Step 3/3 : RUN useradd dot
---> Running in a50a324f8776
Removing intermediate container a50a324f8776
---> 949582cb3192
Successfully built 949582cb3192
Successfully tagged centos:user
可以使用 docker history 看一下每个层的大小:
# docker history 949582cb3192
IMAGE CREATED CREATED BY
SIZE
949582cb3192 About an hour ago /bin/sh -c useradd dot
150kB
422cd518454d About an hour ago /bin/sh -c #(nop) MAINTAINER dot
0B
d0957ffdf8a2 17 months ago /bin/sh -c #(nop) CMD ["/bin/bash"]
0B
<missing> 17 months ago /bin/sh -c #(nop) LABEL org.labelschema.sc... 0B
<missing> 17 months ago /bin/sh -c #(nop) ADD
file:0065316a41144e95b... 194MB
<missing> 22 months ago /bin/sh -c #(nop) MAINTAINER
https://github... 0B
1、使用 Alpine 作为基础镜像
将之前创建用户的 Dockerfile 改为 Alpine 镜像
# cat Dockerfile
# base image
# FROM centos:7
FROM alpine:3.12
MAINTAINER dot
RUN adduser -D dot
Alpine 镜像创建用户的命令为 adduser,-D 表示不设置密码
执行构建并查看镜像大小:
# docker build -t alpine:user .
...
Successfully built 262f8225d7ec
...
# docker images | grep 262f8225d7ec
alpine user 262f8225d7ec 4 minutes ago
5.57MB
2、多阶段构建
创建 Go 语言 Hello World 程序:
# cat hw.go
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
单阶段构建:
# build step
FROM golang:1.14.4-alpine
WORKDIR /opt
COPY hw.go /opt
RUN go build /opt/hw.go
CMD "./hw"
执行构建并运行测试:
# docker build -t hw:one .
Successfully built e036098fbb2e
Successfully tagged hw:one
# docker run --rm hw:one
Hello World!
查看此时由一个阶段构建的镜像大小:
# docker images | grep e036098fbb2e
hw one e036098fbb2e 5 minutes ago
372MB
可以看到此时镜像大小为 372MB,但是上述的代码我们只需要构建步骤产生的二进制文件
hw 即可,这个文件大小可以进入到容器内部看一下:
# docker run --rm hw:one du -sh ./hw
2.0M ./hw
多阶段 Dockerfile 构建:
# cat Dockerfile
# 构建过程
FROM golang:1.14.4-alpine as builder
WORKDIR /opt
COPY hw.go /opt
RUN go build /opt/hw.go
# 生成应用镜像过程
FROM alpine:3.12
COPY --from=builder /opt/hw .
CMD [ "./hw" ]
再次构建后查看此时的镜像大小和运行镜像的效果:
# docker build -t hw:multi .
# docker images |grep multi
hw multi a3bba8204f75 6 minutes ago 2.07MB
# docker run --rm hw:multi
Hello World!
五、Docker 容器退出码
查看:
$ docker ps --filter "status=exited"
$ docker inspect <container-id> --format='{{.State.ExitCode}}'
1、常见退出码
Exit Code 0
- 退出代码0表示特定容器没有附加前台进程。
- 该退出代码是所有其他后续退出代码的例外。
- 这不一定意味着发生了不好的事情。如果开发人员想要在容器完成其工作后自动停止其容器,则使用此退出代码。
Exit Code 1
- 程序错误,或者 Dockerfile 中引用不存在的文件,如 entrypoint 中引用了错误的包
- 程序错误可以很简单,例如 “除以0”,也可以很复杂,比如空引用或者其他程序 crash
Exit Code 137
- 表明容器收到了 SIGKILL 信号,进程被杀掉,对应 kill -9
- 引发 SIGKILL 的是 Docker Kill。这可以由用户或由 Docker 守护程序来发起,手动执行:docker kill
- 137 比较常见,如果 pod 中的 limit 资源设置较小,会运行内存不足导致 OOMKilled,此时 state 中的 “OOMKilled” 值为 true,你可以在系统的 dmesg 中看到 oom 日志
Exit Code 139
- 表明容器收到了 SIGSEGV 信号,无效的内存引用,对应 kill -11
- 一般是代码有问题,或者 docker 的基础镜像有问题
Exit Code 143
- 表明容器收到了 SIGTERM 信号,终端关闭,对应 kill -15
- 一般对应 docker stop 命令
- 有时 docker stop 也会导致 Exit Code 137。发生在与代码无法处理 SIGTERM 的情况下,docker 进程等待十秒钟然后发出 SIGKILL 强制退出。
不常用的一些 Exit Code
- Exit Code 126: 权限问题或命令不可执行
- Exit Code 127: Shell 脚本中可能出现错字且字符无法识别的情况
- Exit Code 1 或 255:因为很多程序员写异常退出时习惯用 exit(1) 或 exit(-1),-1 会根据转换规则转成 255。这个一般是自定义 code,要看具体逻辑。
退出状态码的区间
- 必须在 0-255 之间,0 表示正常退出
- 外界将程序中断退出,状态码在 129-255
- 程序自身异常退出,状态码一般在 1-128
- 假如写代码指定的退出状态码时不在 0-255 之间,例如: exit(-1),这时会自动做一个转换,最终呈现的状态码还是会在 0-255 之间。我们把状态码记为 code,当指定的退出时状态码为负数,那么转换公式如下:
256 – (|code| % 256)
六、Docker容器的状态
docker下查看所有容器状态:
docker ps -a
返回的内容中,有一个字段是STATUS。
STATUS: 容器状态,有7种:
created(已创建)
restarting(重启中)
running(运行中)
removing(迁移中)
paused(暂停)
exited(停止)
dead(死亡)
这些状态中,最重要和常见的是除了restarting(重启中)和removing(迁移中)之外的五个状态,下面基本上网络上大部分的容器生命周期图都只包含五个状态:created(已创建),running(运行中),paused(暂停),exited(停止),dead(死亡)。
七、容器的生命周期
可以谷歌 container lifecycle去看看相关的内容,主要搜图,可以看到有很多图,就像进程的生命周期一样。
主要反映了created(已创建),running(运行中),paused(暂停),exited(停止),dead(死亡)这五个状态的一个变化关系。
下面这个图的started就约等于running。
下面这个图的stopped就约等于exited
源自: Docker Container Lifecycle Management: Create, Run, Pause, Stop And Delete
源自:docker基本命令
源自:docker命令图谱
源自:Docker系列03—Docker 基础入门
其实上面这个图最初应该是来自:
源自:Docker Internals-A Deep pe Into Docker For Engineers Interested In The Gritty Details.
参考:
https://www.php.cn/docker/491808.html