Docker

87 阅读15分钟

什么是docker

解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术,docker三要素:

  1. 镜像文件-
  2. 容器虚拟化技术-
  3. dockerhub-存放docker镜像的仓库,dockerhub是最大的公开仓库,一般国内都是用阿里云的公开仓库

image.png

Docker概念

Docker 包括三个基本概念:

镜像

Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统

当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载

docker镜像相关命令

docker image help
# 查看本地所有镜像
docker images
# 拉取指定镜像
docker image pull 【imageName:version】
# 删除选定镜像
docker rmi -f [镜像ID] 
# 从 Docker Hub 网站搜索指定镜像
docker search httpd
# 查看镜像/容器/数据卷所占的空间
docker system df

列出本地主机上的镜像

root@iZuf63y0r6z2tc37u0q5l8Z:~# docker images
REPOSITORY    TAG          IMAGE ID       CREATED         SIZE
big-screen    latest       554298dc4da3   2 days ago      615MB

image.png

各个选项说明:

  • REPOSITORY: 表示镜像的仓库源
  • TAG: 镜像的标签
  • IMAGE ID: 镜像ID
  • CREATED: 镜像创建时间
  • SIZE: 镜像大小

同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,如 ubuntu 仓库源里,有 15.10、14.04 等多个不同的版本,我们使用 REPOSITORY:TAG 来定义不同的镜像

设置镜像的标签TAG

root@iZuf63y0r6z2tc37u0q5l8Z:~# docker images
REPOSITORY     TAG          IMAGE ID       CREATED          SIZE
big-screen      latest       554298dc4da3   2 days ago       615MB
root@iZuf63y0r6z2tc37u0q5l8Z:~# docker tag 554298dc4da3 big-screen:1.0
root@iZuf63y0r6z2tc37u0q5l8Z:~# docker images
REPOSITORY    TAG          IMAGE ID       CREATED          SIZE
big-screen     1.0          554298dc4da3   2 days ago       615MB
big-screen     latest       554298dc4da3   2 days ago       615MB

创建镜像的几种方式

当我们从 docker 镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行复制或更改。

  • 1、镜像迁移
  • 2、从已经创建的容器中更新镜像,并且提交这个镜像(docker commit)
  • 3、使用 Dockerfile 指令来创建一个新的镜像
镜像迁移

将服务器A上的镜像拷贝一份到服务器B上

# 保存一个或多个image到本地tar文件中
docker save 65bb5db22a5b -o 65bb5db22a5b.tar

新的机器加载该镜像

# 加载磁盘上镜像到docker中
docker load -i 65bb5db22a5b.tar
基于某容器创建一个新的镜像

容器打包为镜像(需暂停容器)

docker commit -m="image update" -a="matio" e218edb10161 test/ubuntu:v2

各个参数说明:

  • -m:  提交的描述信息
  • -a:  指定镜像作者
  • e218edb10161: 容器ID
  • test/ubuntu:v2:  新的镜像名

镜像打包为本地文件

docker save 65bb5db22a5b -o 65bb5db22a5b.tar

新环境导入该镜像

docker load -i 65bb5db22a5b.tar

然后使用我们的新镜像来启动一个容器

docker run -t -i test/ubuntu:v2 /bin/bash
导入导出容器

将容器导出到本地文件test.tar中

docker export 某容器id > test.tar

导入为新的镜像

docker import test.tar newimage:export

或者

# 导入为镜像
cat ./test.tar | docker import - test/ubuntu:v1
# 通过指定 URL 或者某个目录来导入
docker import http://example.com/exampleimage.tgz example/imagerepo
基于Dockerfile文件构建一个镜像
docker build -t test/centos:6.7 .

参数说明:

  • -t :指定要创建的目标镜像名
  • .  :Dockerfile 文件所在目录,可以指定Dockerfile 的绝对路径

docker虚悬镜像

root@td1:~# docker images 
REPOSITORY                   TAG       IMAGE ID       CREATED         SIZE
web                          ym        56518dbb1699   4 days ago      174MB
<none>                       <none>    d8edb1a7515c   6 weeks ago     644MB
<none>                       <none>    10a25c5ea4ec   6 weeks ago     644MB

REPOSITORY和TAG都是none的镜像就是虚悬镜像,建议删除

容器

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等

# 查看当前运行中的容器
docker ps      
# 查看所有容器,包括已停止的容器
docker ps -a   
# 停止某容器
docker stop [容器ID/Name] 
# 启动某容器
docker start [容器ID/Name] 
# 重启某容器
docker restart [容器ID/Name]  
# 删除选定的容器
docker rm -f [容器ID/Name]
# 查看容器内部运行的进程
docker top [容器ID/Name]
# 查看 Docker 的底层信息。它会返回一个 JSON 文件记录着 Docker 容器的配置和状态信息
docker inspect [容器ID/Name]
# 查询最后一次创建的容器
docker ps -l 

# 从容器复制文件到宿主机
docker cp my_container:/path/in/container /path/on/host

image.png

Docker容器连接

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。

下面我们来实现通过端口连接到一个 docker 容器。

我们创建了一个 python 应用的容器。

runoob@runoob:~$ docker run -d -P training/webapp python app.py
fce072cc88cee71b1cdceb57c2821d054a4a59f67da6b416fceb5593f059fc6d

我们使用 -P 绑定端口号,使用 docker ps 可以看到容器端口 5000 绑定主机端口 32768。

runoob@runoob:~$ docker ps
CONTAINER ID    IMAGE               COMMAND            ...           PORTS                     NAMES
fce072cc88ce    training/webapp     "python app.py"    ...     0.0.0.0:32768->5000/tcp   grave_hopper

我们也可以使用 -p 标识来指定容器端口绑定到主机端口。

两种方式的区别是:

  • -P: 是容器内部端口随机映射到主机的端口。
  • -p: 是容器内部端口绑定到指定的主机端口。
# 
docker run -d -p 5000:5000 training/webapp python app.py

# 指定容器绑定的网络地址,比如绑定 127.0.0.1。这样我们就可以通过访问 127.0.0.1:5001 来访问容器的 5000 端口
docker run -d -p 127.0.0.1:5001:5000 training/webapp python app.py

上面的例子中,默认都是绑定 tcp 端口,如果要绑定 UDP 端口,可以在端口后面加上  /udp

docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py

# 查看端口的绑定情况
docker port 容器id

Docker容器互联

端口映射并不是唯一把 docker 连接到另一个容器的方法。

docker 有一个连接系统允许将多个容器连接在一起,共享连接信息。

docker 连接会创建一个父子关系,其中父容器可以看到子容器的信息

先新建一个docker网络

docker network create -d bridge test-net

查看已经建好的docker网络

docker network ls

基于ubuntu镜像后台运行2个容器并连接到刚才新建的test-net网络

docker run -itd --name test1 --network test-net ubuntu /bin/bash

docker run -itd --name test2 --network test-net ubuntu /bin/bash

--name test1是新的容器名称

进入test1容器
docker exec -it test1 /bin/bash

docker attach也可以进入容器交互,但是使用exit退出后会停止容器,所以工作中很少用

通过 ping 来证明 test1 容器和 test2 容器建立了互联关系。如果容器内没有安装ping,那么可以进入容器后运行以下命令安装ping命令

apt-get update
apt install iputils-ping

运行以下命令测试test1和test2容器的连通性

ping test1
ping test2

这样,test1容器和test2容器建立了互联关系。如果你有多个容器之间需要互相连接,推荐使用 Docker Compose

Docker容器配置DNS

我们可以在宿主机的/etc/docker/daemon.json 文件中增加以下内容来设置全部容器的 DNS:

{
  "dns" : [
    "114.114.114.114",
    "8.8.8.8"
  ]
}

设置后,启动容器的 DNS 会自动配置为 114.114.114.114 和 8.8.8.8。配置完后需要重启 docker 才能生效。

查看容器的 DNS 是否生效可以使用以下命令,它会输出容器的 DNS 信息:

docker run -it --rm  镜像id  cat etc/resolv.conf

手动指定容器的DNS

如果只想在指定的容器设置 DNS,则可以使用以下命令:

docker run -it --rm -h host_ubuntu  --dns=114.114.114.114 --dns-search=test.com ubuntu

如果在容器启动时没有指定 --dns 和 --dns-search,Docker会默认用宿主机上的 /etc/resolv.conf 来配置容器的 DNS

参数说明:

  • --rm:容器退出时自动清理容器内部的文件系统。
  • -h HOSTNAME 或者 --hostname=HOSTNAME: 设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts。
  • --dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
  • --dns-search=DOMAIN: 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 host.example.com

仓库

仓库可看成一个代码控制中心,用来保存镜像

Docker官方提供了一个公共仓库服务Docker Hub

用户登录后,可以通过 docker push 命令将自己的镜像推送到 Docker Hub

docker tag ubuntu:18.04 自己的Docker账号/ubuntu:18.04

docker pull太慢解决办法

sudo mkdir -p /etc/docker 
sudo tee /etc/docker/daemon.json <<-'EOF' 
{
  "registry-mirrors": [
    "https://21g5ywxm.mirror.aliyuncs.com",
    "https://registry.docker-cn.com",
    "http://hub-mirror.c.163.com",
    "http://mirrors.ustc.edu.cn/",
    "https://mirrors.tuna.tsinghua.edu.cn/",
    "http://mirrors.sohu.com/"
  ]
}
EOF 
sudo systemctl daemon-reload 
sudo systemctl restart docker
镜像源地址
Docker中国区官方registry.docker-cn.com
网易hub-mirror.c.163.com
中科大mirrors.ustc.edu.cn/
清华大学mirrors.tuna.tsinghua.edu.cn/
搜狐mirrors.sohu.com/

docker run

image.png

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

--name 指定容器名称,否则docker会随机生成一个容器名称

docker run -d -P --name test training/webapp python app.py

基于镜像training/webapp创建并后台启动容器test,然后运行python app.py命令(-P是容器内部使用的网络端口随机映射到宿主机端口)

-d 以后台模式运行docker容器,然后输出该容器id

root@iZuf63y0r6z2tc37u0q5l8Z:~#  docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
6a8686ce3f9f5e87dd96f7160d1d16b1eec8dc1c04f3555863898190db5bb20c
root@iZuf63y0r6z2tc37u0q5l8Z:~# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS          PORTS    NAMES
6a8686ce3f9f   ubuntu:15.10          "/bin/sh -c 'while t…"   15 seconds ago   Up 15 seconds             angry_bose

基于镜像ubuntu:15.10以后台模式运行docker容器,然后输出该容器id。因为没有手动指定容器名称,所以docker为该容器默认指定其名称为angry_bose

-t  在新容器内指定一个伪终端或终端

docker run -t -i ubuntu:15.10 /bin/bash 

参数说明:

  • -i: 交互式操作。
  • -t: 终端。
  • ubuntu:15.10: 这是指用 ubuntu 15.10 版本镜像为基础来启动容器。
  • /bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash

-i  允许你对容器内的标准输入 (STDIN) 进行交互

-p 容器内部端口绑定到指定的主机端口

docker run -d -p 5000:5000 training/webapp python app.py

支持多组配置,比如-p 5000:5000 -p 5001:5002 127.0.0.1:5009:5009。默认都是绑定tcp端口,也支持绑定udp端口,比如-p 5000:5000/tcp -p 5001:5001/udp

使用 docker port 可以查看指定容器端口和宿主机端口的映射情况。

-P 将容器内部使用的网络端口随机映射到我们使用的主机上

docker run -d -P training/webapp python app.py

使用 docker port 可以查看指定容器端口和宿主机端口的映射情况

--network 连接到指定的docker网络

-v 挂载volumes

  • 匿名挂载(匿名卷):在进行数据卷挂载的时候不指定宿主机的数据卷目录,-v命令之后直接跟上容器内数据卷所在的路径
  • 具名挂载(命名卷):在进行数据卷挂载的时候既指定宿主机数据卷所在路径,又指定容器数据卷所在路径
#匿名挂载(匿名卷)
docker run -d -p 6379:6379 --name mycentos -v /src/volume01
 
#具名挂载(命名卷) -v 宿主机数据卷所在路径:容器数据卷所在路径
docker run -d -p 6379:6379 --name mycentos -v /home/docker_volume:/src/volume01

命名卷在用过一次之后,以后挂载容器的时候还是可以继续使用,所以一般在需要保存数据的时候使用命名卷的方式。匿名卷则是随着容器的建立而建立,随着容器的关闭而消亡。匿名卷一般用来存储无关痛痒的数据

两种退出方式

image.png

Docker hello world

root@iZuf63y0r6z2tc37u0q5l8Z:/#  docker run ubuntu:15.10 /bin/echo "Hello world"
Unable to find image 'ubuntu:15.10' locally
15.10: Pulling from library/ubuntu
7dcf5a444392: Pull complete 
759aa75f3cee: Pull complete 
3fa871dc8a2b: Pull complete 
224c42ae46e7: Pull complete 
Digest: sha256:02521a2d079595241c6793b2044f02eecf294034f31d6e235ac4b2b54ffc41f3
Status: Downloaded newer image for ubuntu:15.10
Hello world

docker run ubuntu:15.10 /bin/echo "Hello world"参数解析:

  • docker:  Docker 的二进制执行文件。
  • run:  与前面的 docker 组合来运行一个容器。
  • ubuntu:15.10 指定要运行的镜像,Docker 首先从本地主机上查找镜像是否存在,如果不存在,Docker 就会从镜像仓库 Docker Hub 下载公共镜像。
  • /bin/echo "Hello world": 在启动的容器里执行的命令

以上命令完整的意思可以解释为:Docker 以 ubuntu15.10 镜像创建一个新容器,然后在容器里执行 bin/echo "Hello world",然后输出结果。

运行交互式的容器

通过 docker 的两个参数 -i -t,让 docker 运行的容器实现 "对话" 的能力

root@iZuf63y0r6z2tc37u0q5l8Z:~# docker run -i -t ubuntu:15.10 /bin/bash

# 此时我们已进入一个 ubuntu15.10 系统的容器
root@94490b385929:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@94490b385929:/# cat /proc/version
Linux version 5.4.0-182-generic (buildd@lcy02-amd64-069) (gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)) #202-Ubuntu SMP Fri Apr 26 12:29:36 UTC 2024
root@94490b385929:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

# 退出当前的容器,返回到当前的主机中
root@94490b385929:/# exit
exit

docker run -i -t ubuntu:15.10 /bin/bash:基于镜像ubuntu:15.10启动一个容器,参数为以命令行模式进入该容器

-i -t也可以缩写成-it,参数解释:

  • -t:  在新容器内指定一个伪终端或终端。
  • -i:  允许你对容器内的标准输入 (STDIN) 进行交互。

通过上面的命令启动一个docker容器后,执行exit会退出该容器,并且该容器也会停止。通过docker ps -a也可以看到该容器,只是该容器已经处于停止状态

启动容器(后台模式)

root@iZuf63y0r6z2tc37u0q5l8Z:~# docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
2e35da88847a6fef3ec63954488e7c0cc2bdbba5549e6ad317e3b2a373ceabff
root@iZuf63y0r6z2tc37u0q5l8Z:~# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                                  NAMES
2e35da88847a   ubuntu:15.10          "/bin/sh -c 'while t…"   3 seconds ago   Up 3 seconds                                

在输出中,我们没有看到期望的 "hello world",而是一串长字符 2e35da88847a6fef3ec63954488e7c0cc2bdbba5549e6ad317e3b2a373ceabff。这个长字符串叫做容器 ID,对每个容器来说都是唯一的。通过docker ps查看正在运行的docker容器。参数解释:

CONTAINER ID:  容器ID

IMAGE:  使用的镜像

COMMAND:  启动容器时运行的命令

CREATED:  容器的创建时间

STATUS:  容器状态。状态有7种:

  • created(已创建)
  • restarting(重启中)
  • running 或 Up(运行中)
  • removing(迁移中)
  • paused(暂停)
  • exited(停止)
  • dead(死亡)

PORTS:  容器的端口信息和使用的连接类型(tcp\udp)

NAMES:  自动分配的容器名称

在宿主主机内使用 docker logs 命令,查看容器内的标准输出

docker logs 2e35da88847a
# 等同tail -f
docker logs -f 2e35da88847a

停止该docker容器

docker stop 2e35da88847a

进入容器

在使用 -d 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入

  • docker attach:如果从这个容器退出,会导致容器的停止
  • docker exec:推荐使用,因为此命令会退出容器终端,但不会导致容器的停止
docker exec -it 某容器id /bin/bash

导入导出容器

# 将容器导出到本地文件test.tar中
docker export 某容器id > test.tar
# 导入为镜像
cat ./test.tar | docker import - test/ubuntu:v1
# 通过指定 URL 或者某个目录来导入
docker import http://example.com/exampleimage.tgz example/imagerepo