
一、概述
Docker 是开源的应用容器引擎,基于 go 语言,并遵从 Apache2.0 协议开源。
Docker 允许开发人员将相关应用和依赖库打包到一个轻量级的容器中
Docker 有三个基本概念:
- 镜像(Image):镜像就相当于一个文件系统,比如官方镜像 ubuntu:16.04 就包含了一整套的 Ubuntu16.04 最小系统的 root 文件系统
- 容器(Container):镜像是静态的定义,容器是镜像运行起来的实体
- 仓库(Reposityor):仓库可以用于保存镜像
1.1 优势
互联网企业生产环境的部署经理了三个阶段:
-
物理机部署:
直接在机器上进行环境部署和应用,但多个进程都部署在同一个机器上就会导致资源抢占,导致某些异常。
-
虚拟机部署:
从物理机上分配好 cpu 核数、内存、磁盘等,一个虚拟机一般都只部署一个应用,实现了进程间的资源隔离,可以解决直接在物理机上部署导致的资源抢占问题,一个物理机上可以部署多个虚拟机。
虚拟机虽然可以隔离资源,但随着时间的推移和一些软件的升级,难免有没有升级或管理到的版本,容易导致软件版本和配置逐渐碎片化,当线上出现问题时,排查问题会很棘手。
-
容器部署:
容器不局限于 docker,但 docker 是目前最流行的,docker 的核心在于镜像文件
镜像文件就是一个进程运行时需要依赖的库的集装箱
部署时,首先拉取指定版本的镜像文件,运行镜像文件即可启动容器,由于镜像文件是一样的,所以容器中的软件和库的版本也是一样的,不会出现配置碎片化的问题。如果要升级软件版本,则修改镜像文件即可。
1.2 Ubuntu Docker 安装
安装命令:
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
国内 daocloud 安装:
curl -sSL https://get.daocloud.io/docker | sh
验证 Docker 是否被正确安装:
sudo docker run hello-world
二、docker 配置
2.1 数据路径
默认 docker 会放在 /var/lib/docker 下,但通常系统盘都空间有限,所以可更改 docker 配置:
vim /etc/docker/daemon.json
{
"data-root": "/www/docker"
}
docker info | grep 'Docker Root Dir' 可看到数据路径
三、Dockerfile
Dockerfile 是用了构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明
Dockerfile 一般包含四部分:
- 基础镜像信息
- 维护者信息
- 镜像操作指令
- 容器启动时的执行指令
例如:
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu # 基础镜像
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com # 镜像维护者的信息
# Commands to update the image
# RUN 指令会对镜像执行命令,每运行一条 RUN 指令,镜像添加一层
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Commands when creating a new container
# CMD 来指定运行容器时的操作命令
CMD /usr/sbin/nginx
指令 | 作用 |
---|---|
FROM | 第一条指令必须为 FROM,表示从哪个基础镜像的基础上来新建镜像 |
MAINTAINER | 指定维护者信息 |
RUN | 在 docker build 的时候执行,会在 shell 终端运行命令 |
RUN [“executable”, “param1”, “param2”] | 使用 exec 执行,可以指定使用其他终端,如 RUN [“/bin/bash”, “-c”, “echo hello”] |
CMD | 指定启动容器时的执行命令,在 docker run 的时候执行,每个 Dockerfile 只能有一条 CMD 命令,如果指定了多条,只有最后一条会被执行,且如果用户启动容器的时候指定了运行的命令,则会覆盖掉 CMD 指定的命令 |
EXPOSE | 告诉 Docker 服务端容器暴露的端口号,供互联系统使用,在启动容器时使用 -p ,Docker 主机会自动分配一个端口到指定的端口 |
ENV | 指定一个环境变量,会被后续的 RUN 指令使用,并在容器运行时保持 |
COPY | 复制指令,从上下文目录中复制文件或者目录到容器里指定路径 |
ADD | 类似 COPY |
使用 Dockerfile 创建一个镜像:
sudo docker build -t ImageName:TagName dir
-t
:给镜像加一个 tagImageName
:镜像的名字TagName
:镜像的 Tag 名Dir
:Dockerfile 所在目录
实例:
docker build -t redis:v1.1 .
redis
:镜像名v1.1
:tag 标签.
:当前目录,即 Dockerfile 所在目录
如何查看镜像:
docker image
查看 docker 系统信息:
docker info [options]
查看 docker 版本信息:
docker version [options]
- 示例:
Dockerfile 如下:
From alpine:latest
RUN apk add squashfs-tools
然后 build 并 push 并 run
运行 docker build -t dockerhub.my.com/abc:latest .
docker push dockerhub.my.com/abc:latest
docker run -it -v path1/path2:/path3:path4 dockerhub.my.com/abc:latest sh
五、镜像
-
一般都可以从 Docker hub 来拉取常用的镜像
-
也可以使用
docker search
的方式来搜索镜像:docker search yolov5
查看 images 占用磁盘空间:
docker system df
5.1 镜像拉取
镜像拉取:
sudo docker pull NAME[:TAG]
NAME
:镜像的名称TAG
:镜像的 tag
拉取所有版本的镜像:
sudo docker pull -a NAME
-a
:表示拉取名字为NAME
的镜像的所有版本
dockerhub 页面如下:
假设要拉取 yolov5 的镜像,可以直接搜索 yolov5 :
然后执行下面的命令来拉取对应的镜像即可:
docker pull ultralytics/yolov5
5.2 镜像删除
删除镜像:
docker rmi hello-world
5.3 使用 docker save 将镜像保存成 tar 归档文件
docker save [options] IMAGE [IMAGE...]
- -o:输出到的文件
docker save -o test.tar runoob/ubuntu:v3
5.4 导入使用 docker save 导出的镜像
docker load [options]
--input, -i:指定导入的文件
--quiet,-q:精简输出信息
docker load < busybox.tar.gz
# Save、Load
docker system df 查看images占用磁盘空间
docker run -e 可以覆盖Dockerfile中的 "ENV设置"
Dockerfile的ONBUIILD比较好用, 用于做基础镜像
$ docker save alpine -o filename 可以把镜像存储为文件, 对文件后缀无要求
docker load -i filename 可以从文件存储为镜像
5.5 使用 docker import 从归档文件中创建镜像
docker import [options] file IMAGE[:TAG]
- -c:使用 docker 指令创建镜像
- -m:提交时的文字说明
示例:从 my_ubuntu_v3.tar 来创建镜像,命名为 runoob/ubuntu:v4
docker import my_ubuntu_v3.tar runoob/ubuntu:v4
docker images runoob/ubuntu:v4
REPOSITORY TAG IMAGE ID CREATED SIZE
runoob/ubuntu v4 63ce4a6d6bc3 20 seconds ago 142.1 MB
5.6 将本地镜像上传到镜像仓库
要上传的话要有镜像仓库的登录权限
可以将本地镜像推送到 docker 公共仓库——Dockerhub,也可以推送到私有的 docker 仓库中
这里演示如何推送到 dockerhub,要先注册并登录 dockerhub
docker push [options] NAME[:TAG]
示例:将本地镜像上传到仓库:
# 使用 docker login 登录
docker login
# 退出
docker logout
# 上传
docker push myapache:v1
5.7 常见镜像
5.7.1 Alpine
Alpine 是一种更轻型的 Linux 发行版(即和ubuntu、CentOS等是并列的),其因为用了 musl libc
和 busybox
技术从而使得镜像体积非常小,下表时官方镜像的大小比较:
REPOSITORY TAG IMAGE ID VIRTUAL SIZE
alpine latest 4e38e38c8ce0 4.799 MB
debian latest 4d6ce913b130 84.98 MB
ubuntu latest b39b81afc8ca 188.3 MB
centos latest 8efe422e6104 210 MB
ubuntu 里的包管理工具是 apt-get update xxx
,alpine 里的包管理工具是 apk add xxx
。
应用场景:如果我们有一个alpine的镜像,希望在其中安装一个包,可以先在 alpine的apk包仓库 中寻找,其界面如下图所示:
六、容器的一般操作
6.1 查看容器
docker ps -a # 查看所有的容器
docker ps # 查看所有运行的容器
docker ps --no-trunc # 不折行, 使结果都能可视化
sudo docker exec -it 'test' bash # 进入已经启动的容器,使用 exec 进入容器会不受原终端运行的影响, 如果在进入容器后执行 exit 命令,容器也不会停止,只会退出该容器
6.2 启动容器
拉取到镜像之后,要使用该镜像创建一个容器,才可以使用相关的内容
语法:
docker run [options] Image [command] [arg...]
例如:
docker run -it --name 'test' IMAGE:TAG
如果要在容器中使用英伟达显卡有两种方法:
# 方法一
sudo nvidia-docker run ...
# 方法二
sudo docker run --runtime-nvidia ...
nvidia-docker 是对 docker 的包装,使得容器能够看到并使用主机的 nvidia 显卡,也就是避免需要在容器中安装 cuda 或 gpu 驱动。
参数 | 作用 |
---|---|
-a stdin | 指定标准输入输出内容类型,可以选择 STDIN/STDOUT/STDERR |
-d | 启动容器后后台运行,不会直接进入容器,界面上会返回容器的 ID,使用后台运行方式启动的容器,想要进入容器的话可以使用 docker exec,使用 exit 退出后容器依然会运行 |
-i | 以交互模式运行容器,通常和 -t 同时使用(-it) |
-P | 随机端口映射,容器内部端口随机映射到主机的端口 |
-p | 指定端口映射,格式为 主机端口:容器端口 , |
-t | 为容器重新分配一个伪输入终端 |
–name | 为启动的容器设置一个名字,方便查看 |
-m | 设定容器使用内存的最大值 |
-w | 指定工作目录 |
-v | 将主机的目录挂载到 docker 的相同目录下,docker 目录下的内容会随着主机相同目录下的内容变化而变化 |
-e | 设置容器的环境变量,-e NVIDIA_VISIBLE_DEVICES=all,表示可使用主机的所有卡,-e NVIDIA_VISIBLE_DEVICES=1,2,表示可以使用 1 和 2 卡。 |
–shm-size | 共享内存大小,docker run 运行容器的时候,最好指定一下–shm-size,单位是字节,因为默认是docker容器中默认是64M,很多软件无法正常工作,可以设置为–shm-size=59g |
–net | 指定容器的网络模式:–net=host,容器和主机共享网络,主机的所有端口都可以用于容器,可能会出现端口冲突的情况。–net=bride,是默认的模式,-p 用于指定端口映射。–net=container 当前容器和 container 容器共享 network namespace,–net=none,容器有独立的 network namespace |
–ipc | –ipc=host,容器和主机共享内存 |
–privileged | –privleged=true,使容器拥有 root 权限 |
–gpus | –gpus all,使用主机的所有 gpu,–gpus 2,使用 2 个 gpu,–gpus ‘device=1,2’,使用 1 卡和 2 卡 |
6.3 进入容器
在使用 -d 的方式进入后台运行时,如果想要进入容器,可以使用下面两种方式:
docker attach 'test'
:使用 exit 退出容器会导致容器终止docker exec -it 'test' bash
:使用 exit 退出容器不会导致容器终止
6.4 停止容器
docker stop 'test'
6.5 重启容器
docker restart 'test'
6.6 删除容器
docker stop 'test'
docker rm 'test'
6.7 杀死容器中正在运行的程序
docker kill 'test'
6.8 在 docker 中杀死正在运行的进程
kill -9 pid
6.9 容器的连接
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。
- -P :是容器内部端口随机映射到主机的高端口。
- -p : 是容器内部端口绑定到指定的主机端口,-p
主机端口:容器端口
6.10 查看 docker 的创建历史
docker history [options] IMAGE
- -H:以可读格式打印镜像大小和日期
- –no-trunc:显示完整的提交记录
- -q:仅列出提交记录 id
6.11 设置容器的参数
通常普通用户没有 sudo 权限,但希望改 docker container 的 shm 参数,该怎么办呢? 可以按如下操作
# 停止当前正在运行的容器
docker stop container_id_or_name
# 从停止状态的容器创建一个新镜像
docker commit container_id_or_name new_image_name
# 使用新创建镜像启动一个新容器,并设置/dev/shm大小
docker run --shm-size=1g -d new_image_name
6.12 docker build
6.12.1 docker build --build-arg
首先有一个 Dockerfile
FROM ubuntu:latest # 基础镜像
ARG path # 通过 docker build --build-arg path=a 传入
ARG name # 通过 docker build --build-arg name=b 传入
WORKDIR /$name # 设置 container 的工作路径(即在哪个路径执行命令)
COPY $path /$name/ # docker build 时从 开发机的 $path 拷贝到 container 内的 /$name/
# 例如 从开发机的 mycli/dist 拷贝到 container 内的 /mycli 路径
ENTRYPOINT ["./mycli"] # 设置 container 启动时执行的命令
然后在 Makefile 可以通过 docker build --build-arg path=mycli/dist --build-arg name=mycli --tag=1.2.3 执行 docker build
最终 docker exec mycli:1.2.3 – sh
会进到 container 里的 WORKDIR 并执行 ENTRYPOINT 命令(即 cd /mycli && ./mycli)
七、docker-compose
docker-compose 相比 k8s 很轻量,可以方便调度和编排各 docker:
version: "3.8"
services:
nginx:
image: 'nginx:1.17.3'
container_name: 'my-nginx'
restart: always
network_mode: host
volumes:
- '/etc/localtime:/etc/localtime:ro'
- '/home/dir1:/home/dir2'
- '/var/www:/var/www'
- '/etc/nginx/nginx.conf:/etc/nginx/nginx.conf'
- '/etc/nginx/conf.d:/etc/nginx/conf.d'
logging:
driver: json-file
options:
max-file: '5'
max-size: 50m
八、升级 runc 版本
升级runc,搜 CVE-xxxxx 的 bug 号,看官方的 bug 描述(不要看别人的 bug 描述),然后下载 fixed 的版本,更新 runc 的二进制(一般在 /bin/runc, 可以用 which runc 搜到),然后 systemctl stop
docker && systemctl start docker && docker version 看 runc 的版本 && runc —version 看版本
# 下载
wget https://github.com/opencontainers/runc/releases/download/v1.1.12/runc.amd64
# 停止docker
systemctl stop docker
# 复制
which runc
cp /usr/bin/runc /usr/bin/runc.bak
cp runc.amd64 /usr/bin/runc
chmod +x /usr/bin/runc
# 启动
systemctl start docker
docker ps
docker version