文章

Docker指南

Docker指南

介绍

Docker 是一个能够把开发的应用程序自动部署到容器的开源引擎,是一个轻量级容器管理引擎,它改变了传统软件的交付和运行方式。

Docker框架

Image

镜像类似于虚拟机镜像,可以理解为面向 Docker 引擎的只读模板,是由文件系统叠加而成。镜像是 Docker 运行容器的前提,用户基于镜像来运行容器。

镜像是分层的,AUFS(Advanced Union File System),Dockerfile 中每运行一条 RUN 指令,镜像添加新的一层。

镜像名

  1. 仓库名/镜像名:标签
  2. 如果没有指定任何标签,则自动为镜像设置一个latest标签

dangling镜像

dangling 镜像就是 docker images 命令中出现的,REPOSITORY 和 TAG 都显示为 <none> 的镜像

Container

容器类似于一个轻量级的沙箱,Docker 利用容器来运行和隔离应用,容器是镜像的一个运行实例。

Repository

仓库类似于代码仓库,是 Docker 集中存放镜像文件的地方

每个仓库集中存放一组关联镜像的集合,通过不同的 TAG 来区分,TAG 用来标记来自同一个仓库的不同镜像

Registry

注册服务器存放不同的仓库。 一个 Docker Registry 中可以包含多个 仓库Repository);每个仓库可以包含多个 标签Tag);每个标签对应一个镜像。

安装

  1. 更新apt包索引

    1
    
     sudo apt-get update
    
  2. 安装必要工具包

    1
    2
    3
    4
    5
    6
    7
    
     sudo apt-get install \
         apt-transport-https \
         ca-certificates \
         curl \
         gnupg-agent \
         software-properties-common \
         lsb-release
    
  3. 添加Docker GPG秘钥

    为了确认所下载软件包的合法性,需要添加软件源的 GPG 密钥

    1
    2
    3
    4
    5
    
     $ curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
    
    
     # 官方源
     # $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
    
  4. 向sources.list添加Docker软件源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
     $ echo \
     "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
     $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    
     # 官方源
     # $ echo \
     #   "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
     #   $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
  5. 更新apt

    1
    
     sudo apt-get update
    
  6. 安装Docker

    1
    
     sudo apt-get install docker-ce docker-ce-cli containerd.io
    
  7. 启动docker

    1
    2
    3
    4
    5
    
     # 将docker服务添加到系统启动时的自动运行列表
     sudo systemctl enable docker
    
     # 立即启动docker服务
     sudo systemctl start docker
    
  8. 验证是否安装成功

    1
    
     sudo docker run hello-world
    
  9. 建立docker用户组

    只有root用户和docker组的用户才有访问Docker引擎的权限

    1. 建立docker

      1
      
       sudo groupadd docker
      
    2. 将当前用户加入docker

      1
      
           sudo usermod -aG docker $USER
      
    3. 无权限运行docker

      1
      
       docker run -rm hello-world
      

      如果还是存在权限问题,注意/var/run/docker.sock的相关权限

      1
      
       ls -l /var/run/docker.sock
      

      如果需要,可以将docker.sock的所有者改为docker用户组

      1
      2
      
       sudo chown root:docker /var/run/docker.sock
       sudo chmod 0660 /var/run/docker.sock
      

镜像操作命令

创建镜像

1
2
3
4
5
# 基于已有的容器创建镜像
docker commit -m "message" -a "author" CONTAINER REPOSITORY:TAG

# 基于本地模板导入
cat XOS.tar | docker import - lqshow/test:v1.0

查看镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查找所有镜像
docker search image_name

# 列出所有镜像
# docker images 等价于 docker image ls
docker image ls


# 列出部分镜像
docker image ls ubuntu
docker image ls ubuntu:18.04

# 查看在monge:3.2之后创建的镜像
docker image ls --filter since=mongo:3.2

# 列出镜像的ID
docker image ls -q

# 列出镜像结果,只包含镜像ID和仓库名
docker image ls --format ": "

镜像操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 获取镜像
# docker pull [选项] [Docker Registry地址[:端口号]/]仓库名[:标签]
#
# docker镜像仓库地址格式一般是 <域名/IP>[:端口号] ,默认地址 Docker Hub(docker.io)
docker pull image_name

# 仓库名是两段式名称,即 <用户名>/<软件名>
# 对于Docker Hub,不给出用户名则默认为library即官方镜像
docker pull docker-reg.basebit.me:5000/basebit/xdp-sdk-jre8

# 运行镜像
docker run -it --rm  ubuntu:18.04 bash

# 发布镜像
docker push lqsow/static_web

# 重命名镜像
docker tag image_name new_image_name

# 获取镜像的创建历史
docker history image_name

# 查看镜像
docker inspect nginx:1.13.0-alping -f .ContainerConfig

导入导出镜像

1
2
3
4
5
6
7
# 保存(导出)镜像
docker save -o <image_name>.tar <image_id>
docker save <image_id>  > <image_name>.tar

# 加载(导入)镜像
dokcer load --input img_java.tar
docker load < img_java.tar

更新镜像

直接拉取最新镜像

1
docker pull 镜像名:标签

如果拉取后显示

1
Status: Image is up to date for nginx:latest

代表本地镜像已经是最新的

如果提示

1
Status: Downloaded newer image for nginx:latest

就说明本地镜像不是最新的

删除镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 1. 当有该镜像创建的容器存在时,镜像文件是无法被删除的。正确做法是先删除依赖该镜像的相关容器,然后再删除此镜像。
# 2. 强行使用 -f 参数删除,会带来 none 镜像。

# 删除一个镜像
# 等价于docker image rm
# docker rmi [选项] <镜像1> [<镜像2> ...]
# <镜像>可以是镜像短ID,镜像长ID,镜像名或者镜像摘要
docker rmi image_name

# 删除多个镜像
docker rmi image_name image_name2

# 批量删除临时镜像文件
docker rmi $(docker images -q -f dangling=true)

# 删除所有镜像
docker rmi -f `docker images -a -q`

# 删除none镜像
docker rmi $(docker images | grep "none" | awk '{print $3}') 

# 批量删除repository ,tag 为none 的镜像
docker images |grep none|awk '{print $3}'|xargs docker rmi

# 镜像摘要
docker image ls --digests
docker image rm xxx@sha256:xxx

镜像的唯一标识是ID和摘要

一个镜像可以有多个标签

如果删除信息是Untagged,则是删除指定标签

Delete则是删除整个镜像

commit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 1. 用nginx镜像启动一个容器webserver,后台运行,本地8080端口映射到容器的80端口
docker run --name webserver -d -p 8080:80 nginx

# 2. 修改页面内容
docker exec -it webserver bash

echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
exit

# 3. 查看具体的改动
docker diff webserver

# 4. 使用commit保存
docker commit \
	--author "Jao" \
	--message "修改了网页" \
	webserver \
	nginx:v2

# 5. 查看新镜像
docker image ls nginx
docker history nginx:v2

# 6. 运行新镜像
docker run -d -p 8081:80 --name web2 nginx:v2

容器操作命令

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(exited)的容器重新启动。

创建启动容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 下面的命令创建一个随机名称容器并且运行Ubuntu
# 然后输出一个 “Hello World”,之后终止容器

# 1. 检查本地是否存在Ubuntu镜像,不存在则链接到 docker hub,自动下载到本地宿机
# 2. 如果不指定一个镜像的版本标签,如下只使用 Ubuntu,Docker 将默认使用 Ubuntu:latest 镜像
# 3. 容器的命名必须是唯一的
docker run ubuntu:18.04 /bin/echo 'Hello world'

# 下面的命令则启动一个 bash 终端,允许用户进行交互
# -t分配一个伪终端并绑定到容器的标准输入上
# -i让容器的标准输入打开
docker run -t -i ubuntu:18.04 /bin/bash

# 指定容器名
docker run -t -i --name test ubuntu:18.04 /bin/bash

# 后端运行并映射80端口
docker run -d -p 80:80 --name app_port basebit/xdp-sdk-jre8

# 设置环境变量
docker run --rm -it --name test \
--env MYHOME=/home \
--env TEST=/home \
--env PATH=$PATH:/home \
centos bash

# 运行多条指令
docker run -d  \
 --restart always \
 -p 13200:13200 \
 --volume /root/.m2:/root/.m2  \
 --env NODE_ENV=test  \
 enigma:0.0.1 \
 /bin/sh -c 'cd /data/project/service; npm run build-xfs; ./bin/start.sh'

启动参数

optionsdesc
-i保证容器中的 STDIN 是开启的,进行交互式操作
-t为要创建的容器分配一个伪 tty 终端
-d/–detach后台运行容器,并返回容器 ID
–name指定容器名称
-e/–env配置容器内的环境变量
–link链接到另外一个容器
–rm当容器退出时自动删除它
-v/–volume挂载数据卷(映射的卷),可以使用多次,即挂载多个数据卷
–restart重启策略( unless-stopped|always)
-p/–publish把容器端口映射到主机端口
-P对外公开在 Dockerfile 中的 EXPOSE 指令中设置的所有端口
–hostname设置容器host

加入参数-d,此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面,可以通过docker logs CONTAINERIDdocker container logs CONTAINERID去查看输出

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从 registry 下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

启动已终止容器

可以利用 docker container start 命令,直接将一个已经终止(exited)的容器启动运行。

终止容器

可以使用 docker container stop 来终止一个运行中的容器。

此外,当 Docker 容器中指定的应用终结时,容器也自动终止。

终止状态的容器可以用 docker container ls -a 命令看到。

处于终止状态的容器,可以通过 docker container start 命令来重新启动。

此外,docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。

查看容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 列出正在运行的容器
docker ps

# 列出全部容器
docker ps -a

# 列出最后一次运行的容器
docker ps -l

# 获取容器元数据
docker inspect CONTAINER
docker inspect --format='' CONTAINER

# 查看容器进程
docker top CONTAINER

# 查容器的端口映射
docker port CONTAINER

容器日志

1
2
3
4
5
6
7
8
9
10
11
# 跟踪日志输出, 效果类似于tail -f
docker logs -f CONTAINER

# 获取日志最后10行
docker logs --tail 10 CONTAINER 

# 获取最新日志
docker logs -ft --tail 0 CONTAINER

# 匹配日志
docker logs CONTAINER 2>&1| grep "ERROR"

容器操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 从容器里拷贝文件到本地
docker cp a136a050da25:/home/file.txt ./

# 从本地拷贝文件到容器里
docker cp Dockerfile xdp-workspace-api:/data/project/service/logs/workspace/

# 容器重名
docker rename CONTAINER NEW_NAME

# 停止、启动、杀死、重启一个容器  
docker stop CONTAINER
docker start CONTAINER
docker kill CONTAINER
docker restart CONTAINER

附加进入容器

需要进入容器进行操作时,推荐使用docker exec命令

附加到一个运行的容器上面(前提是此Container已经运行中)

命令输完后,需按下回车才能进入该会话 退出容器(不关闭容器):Ctrl+P+Q 退出容器(关闭):exit

1
2
3
4
5
6
7
8
9
$ docker run -dit ubuntu
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
243c32535da7        ubuntu:latest       "/bin/bash"         18 seconds ago      Up 17 seconds                           nostalgic_hypatia

$ docker attach 243c
root@243c32535da7:/#

如果从这个 stdin 中 exit,会导致容器的停止。

运行交互式的容器

docker exec 后边可以跟多个参数

只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。

-i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ docker run -dit ubuntu
69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
69d137adef7a        ubuntu:latest       "/bin/bash"         18 seconds ago      Up 17 seconds                           zealous_swirles

$ docker exec -i 69d1 bash
ls
bin
boot
dev
...

$ docker exec -it 69d1 bash
root@69d137adef7a:/#

此外,exec还有以下用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 在容器中打开交互式任务
docker exec -it a136a050da25 /bin/bash

# 以 root 权限进入容器
docker exec -it -u root docker-jenkins /bin/bash

# 在容器内创建空文件
docker exec -d a136a050da25 touch /home/new_file

# 查看环境变量
docker exec -it 7984cfc7d174 env

# 查看host文件
docker exec -it 7984cfc7d174 cat /etc/hosts

如果从这个 stdin 中 exit,不会导致容器的停止。所以推荐使用docker exec

导入导出

  • 导出容器快照到本地镜像 导出一个已经创建的容器到一个文件

    1
    2
    3
    4
    
      $ docker container ls -a
      CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                    PORTS               NAMES
      7691a814370e        ubuntu:18.04        "/bin/bash"         36 hours ago        Exited (0) 21 hours ago                       test
      $ docker export 7691a814370e > ubuntu.tar
    
  • 导入容器快照为镜像 导出的文件可以使用docker import命令导入成为镜像 通过该方式导入,仅保存容器当前的快照状态,需要重新指定标签等元数据信息

    1
    2
    3
    4
    5
    
      # 本地容器
      $ cat ubuntu.tar | docker import - test/ubuntu:v1.0
      $ docker image ls
      REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
      test/ubuntu         v1.0                9d37a6082e97        About a minute ago   171.3 MB
    

    此外,也可以通过指定 URL 或者某个目录来导入,例如

    1
    
      $ docker import http://example.com/exampleimage.tgz example/imagerepo
    

删除容器

运行中的容器是不可以删除的,必须先stop或kill

如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器。

1
2
3
4
5
6
7
8
9
10
11
12
# 删除单个容器
docker stop CONTAINER && docker rm CONTAINER

# 删除所有容器
docker rm 'docker ps -a -q'

# 删除所有退出的容器
docker stop $(docker ps -a | grep "Exited" | awk '{print $1 }')
docker rm $(docker ps -a | grep "Exited" | awk '{print $1 }')

# 清理掉所有处于终止状态的容器
$ docker container prune

查看使用情况

1
2
3
4
5
# 查看占用分布
docker system df

# 查看空间占用细节
docker system df -v

清理空间

1
2
3
4
5
6
7
8
9
10
11
# 清除所有未被使用的镜像和悬空镜像
docker system prune -a

# 强制删除
docker system prune -f

# 删除悬空的镜像
docker image prune

# 删除无用的容器
docker container prune

悬空镜像指的是没有被任何标签(tag)引用的镜像

查看节点容器运行情况

1
docker stats --no-stream --format "table \t\t\t" | sort -k 4 -h
本文由作者按照 CC BY 4.0 进行授权