Docker快速入门

12月 30, 2022 · 3 分钟阅读时长

一、Doker快速使用

1.1 构建一个容器

拉取并使用docker的ubuntu镜像构建一个交互式的容器。

sudo docker run --name containerName -i -t ubuntu /bin/bash

参数解释:

``–name`是对于容器的命名

-i保证容器的STDIN开启,保证持续的标准输入;

-t为docker为容器分配一个tty终端

ubuntu提供的是镜像名,先从本机查找之后,若无则去dockerHub上查找

/bin/bash告知容器需要运行的命令

1.2 关闭容器与查看

在容器内键入exit可以退出容器,之后在宿主机上可以通过使用

docker ps -a

来查看所拥有的全部容器。

1.3 启动并附着到容器

当知道容器的名字或者uuid的时候可以使用

sudo docker start containerName
sudo docker start UUID

来重新运行容器,重新运行的容器会沿用docker run时候的命令,此时我们可以使用

sudo docker attach containerName

重新进入容器的会话。

1.4 使用非交互的守护式容器

有时候需要长期运行的容器,并不需要交互式会话,这时候我们会选择使用守护式容器(daemonized container)

sudo docker run --name containerName -d ubuntu /bin/sh

参数解释:

-d将容器放在后台运行

1.5 查看守护式容器信息

对于在后台运行的守护式容器,我们可以获取容器的日志。

sudo docker logs -f containerName

参数解释:

-f跟踪式运行

在运行容器的时候可以选用不同的日志驱动,常见的有

  1. json-file(默认)
  2. syslog:禁用docker log命令,并将所有日志输出都重定向到syslog。
  3. none:禁用一切日志

另外也可以通过使用docker top name来查看到容器内的进程,可以使用docker stats name来查看容器内部的硬件统计信息

1.6 在守护式容器内部运行新的进程

在docker1.3之后可以使用docker exec命令在容器内部额外启动新的进程

sudo docker exec -d containerName command 

参数解释:

-d表示在后台运行

command是需要的命令

或者使用如下命令来启动前端的程序

sudo docker exec -i -t containerName command

1.7 守护式容器的关闭与重启

对于后台运行的守护式容器,可以使用docker stop containerName发送SIGTERM信号进行停止。

而对于快速停止则可以使用docker kill命令发送SIGKILL信号。

容器有可能因为某种错误而停止,可以通过在run的时候进行标记,来使其自动重启。

sudo docker run --name containerName --restrat=always -i -t ubuntu /bin/bash

这个属性具有两种标价,可以使用always来设置为自动重启,也可以使用on-failure:n中的n来指定最多重启次数。

1.8 删除容器

当容器已经不在使用的时候,可以通过rm命令

sudo docker rm -f containerName

参数解释:

使用f可以无需关闭容器强行删除,不然则需要在删除容器前先关闭当前的容器。

删除所有容器需要多个命令的组合,如下:

sudo docker rm `sudo docker ps -a -q`

在ps命令中,可以使用-a获得所有的容器,而-q的标志使得这条命令只返回id表

二、Docker的镜像与容器

2.1 基本概念介绍

  Docker镜像是由文件系统叠加而成,最底端是一个引导文件系统(bootfs)。接下来第二层是root文件系统(rootfs),这可以是一种或者多种操作系统。接下来docker会使用联合加载(union mount)的技术在root文件系统层上加载更多的只读系统,将各层文件系统叠加到一起。

  这样的文件系统就是docker的镜像,最底下是一个基础镜像,而最上面会叠加一个读写文件系统,我们的程序会在这上面运行。使用的机制是写时复制,当修改一个文件的时候,会从底层复制这个读写层,在对其修改后隐藏下面的层。

2.2 管理镜像

可以使用sudo docker images来列出当前所拥有的全部镜像,也可以使用

sudo docker images imageName

来查看本地同一个镜像名,不同版本(tag)的镜像。

  镜像可以从仓库下载,默认的仓库会保存在Docker公司的Registry服务(Docker hub)下。每一个仓库都可以存放很多镜像,如Ubuntu仓库中会保存很多版本的镜像。

  Docker Hub中有两种类型的仓库,分别是用户仓库(user repository)和顶层仓库(top-level repository)

用户仓库:userName/repositoryName

顶层仓库:Name

对于镜像文件,可以在使用run之前使用docker拉取相应的镜像文件

sudo docker pull image:tag

参数解释

tag是image的版本,都在image的仓库之中。

2.3 查找镜像

我们可以通过使用search命令查找,所有可用的公共镜像。

sudo docker search imageName

这个命令会在docker上查找所有包含这个名字的镜像,我们可以拉去这些镜像并用其创建容器。

2.4 构建镜像

  我们修改、更新和、管理镜像提供了两种方法。

  1. 使用docker commit命令(不推荐)
  2. 使用docker build命令和dockerfile文件

在使用commit命令的时候,我们先启动容器,做自己需要的更改退出容器,之后可运行

sudo docker commit -m "informations"  -a "author" id repositoryName/imageName:tag

参数解释:

id:容器的标识符

-m:指定新创建的镜像的提交信息

-a:作者的信息

repositoryName:选择的镜像仓库名称,通常为自己的用户名

imageName:镜像名

tag:标签名

注:这个命令提交的只是创建容器的镜像和容器当前状态的差别部分,使得更新十分轻量

2.5 基于Dockerfile来构建镜像

  使用基于DSL语法的dockerfile可以构建镜像,具有更高的重复性、透明性、幂等性。

2.5.1 快速尝试

首先可以创建如下的基础Dockerfile

# Version:0.0.1
From ubuntu
LABEL shenvinci maintainer="shenvinci@gmail.com"
RUN apt-get update && apt-get install -y nginx
EXPOSE 80

参数解释:

FROM指定了所用的基础镜像

LABLE可以告知镜像的作者以及邮箱

EXPOSE可以指定应用程序使用容器的端口

在使用Dockfile构建镜像的时候,每一条指令都会创建一个新的镜像并提交,这样的操作逻辑即使在某一条失败而没有正常的结束,仍可以保留一个最后的镜像文件,可以用于调试。

  默认情况下RUN指令会使用命令包装器来执行,而对于不希望在Shell中运行的可以使用exec格式来运行指令,这种方式运行使用一个数组来指定要运行的命令和每一个参数。

RUN ["apt-get","install","-y","nginx"]

当有dockerfile存在后,我们可以使用build命令来构建镜像文件。

docker build -t="repositoryName/imageName:tag" path

参数解释:

-t=用于表示镜像的仓库名,镜像名以及标签

path表示dockerfile所处的路径,这个路径也可以是远端仓库的位置

当使用构建的时候,dokerfile所处的上下文也会被传送到docker守护进程,如果有不想被传送的文件可以通过设置.dockerignore文件来选择需要过滤匹配的文件。

2.5.2 Dockerfile的缓存逻辑

  因为在构建镜像的过程中,每一个步骤都会被构建为单独的镜像层,当再次使用dockerfile进行构建的时候,就会从最新一次有发生更改的部分继续进行构建。如果需要忽略所有缓存,则可以使用--no-cache命令来忽略所有缓存构建。

  因为这种特性,我们通常选择相似的docker模板,这样就可以不必重新运行前面的指令来构建镜像,而对于需要刷新的部分,我们可以在之前插入环境变量语句,如

# Version:0.0.2
From ubuntu
LABEL shenvinci maintainer="shenvinci@gmail.com"
ENV REFRESHED_AT 2021-9-30
RUN apt-get update && apt-get install -y nginx
EXPOSE 80

参数解释

ENV:设置了一个名为REFRESHED_AT的环境变量,指明了模板最新更新的时间,当更改时间后,后面的所有内容都会重新运行,使得包可以进行刷新。

2.5.3 查看与使用新的镜像

docker images repositoryName/imageName
docker history repositoryName/imageName

上面的第一个代码,可以查看自己构建的镜像的具体状况,而需要查看具体的构建全步骤过程,则可以使用下面的,查看到每一层的镜像文件以及相应的构建指令。

  当我们使用这种自己构建的镜像运行时候,我们的run命令需要加入新的内容

sudo docker run -d -p 127.0.0.1:8080:80 --name containerName repositoryName/imageName command

参数解释

-p参数用于配置容器与宿主机的端口链接,如上述命令是把容器的80映射到宿主机127ip的8080端口上,当其中有参数被省略时,则会使用默认(随机)参数。当我们使用-P的时候,会将容器的80绑定到一个宿主机随机端口,然后将dockerfile中EXPOSE指定的端口全部公开.

command命令是直接在容器内运行的内容,通常用于打开需要的服务

  我们可以使用docker ps -l命令查看所有容器被映射的端口,也可以使用docker port uuid 80查看这一容器80端口被映射到的宿主机端口位置。

2.6 Dockerfile指令

2.6.1 CMD

  CMD指令用于指定容器启动时运行的命令(而RUN指令的只是在被构建时候运行),通常的格式为

CMD ["/bin/bash","-l"]

  Docker一直推荐使用数组来设置需要执行的命令。同时我们需要牢记docker run命令会覆盖CMD指令,同时要知道再Dockerfile中只能指定一条CMD指令,如果指定了多条则只有最后一条会被运行。

2.6.2 ENTRYPOINT

  这个指令与CMD十分类似,而最大的区别在于其不会被命令中的指令覆盖

实际上所有的指令都会作为参数传入ENTRYPOINT中。

在使用这个指令的时候我们也会使用数组进行内容的传输

ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"]

将这个命令,CMD命令以及docker run时选择的命令进行组合,可以得到默认参数的使用方法。

ENTRYPOINT ["/usr/sbin/nginx"]
CMD["-h"]
docker run -t -i repositoryName/imageName -g  "daemon off;"

在默认情况下,会使用CMD命令中的-h参数,但在run的时候如果添加了新的参数,则会对其进行覆盖,以此实现默认参数的效果。

2.6.3 WORKDIR

这个指令可以在容器内部设置一个工作目录(切换内部的路径),用于CMDENTRYPOINT的执行。

WORKDIR /opt/webapp
CMD command

我们为command指令设置了路径

我们可以在run的时候通过-w来覆盖工作的目录。

2.6.4 ENV

在镜像构建的过程中设置环境变量,这个环境变量可以在之后任何RUN指令中使用,就如同在命令前面指定了环境变量的前缀

ENV RVM_PATH=/home/rvm TARGET_FIR=/opt/app
RUN gem install unicorn
WORKDIR $TARGET_FIR

参数解释:

设置了RVM_PATHTARGET_FIR两个环境变量,第一个可以为第二条RUN指令提供前缀条件,变成如RVM_PATH=/home/rvm RUN gem install unicorn而第二个则被用于提供了工作目录的位置。

注意:在Dockerfile中设置的环境变量都是在容器中具有持久化作用的,而如果只是在使用docker run时候加入-e来传递的环境变量则只在运行的时候有效。

2.6.5 USER

指定这个镜像以什么用户去运行,可以通过指定用户名(或uid)以及组(或gid)来选择。如果不选择默认会使用root

USER user
USER user:group
USER uid
USER uid:gid

2.6.6 VOLUME

用来向基于镜像创建的容器添加卷(可以存在于一个或者多个容器内的特定的目录),卷可以绕过联合文件系统并提供共享以及持久化的数据功能,卷的功能可以让我们**把数据、数据库或者其他内容添加到镜像而不是将内容提交到镜像。**通常用来测试容器和内部的应用程序代码。管理日志,处理内部数据库。

VOLUME ["/opt/project",/data]

参数解释:

为基于此镜像的任何容器创建了这两个挂载点。

2.6.7 ADD

将构建环境下的文件和目录复制到镜像之中,需要指定源文件位置和目的文件位置两个参数。

ADD contentPath aimPath

参数解释:

contentPath是上下文的文件,通过末尾的字符来判断是目录还是文件,如果以/结尾的就被认为是目录。

在处理本地文件的时候,当归档文件被指定为源文件的时候,docker会把文件自动解开。(以URL指定目前不行)

aimPath是镜像内的目标位置,如果目录不存在的话,会自动创建这样的新目录,其模式为文件模式为0755

ADD指令会使得构建的缓存变得无效,当添加后,所有的后续指令都需要重新进行构建。

2.6.8 COPY

  与ADD命令基本类似,但不会做文件提取以及解压等方面的内容,需要注意,所有的文件源路径都必须是在Dockerfile的相对文件夹下,不能复制这个目录以外其他目录的内容。

2.6.9 LABEL

这个指令可以为docker添加元数据,以键值对的形式展现出来。

LABEL version="1.0" location="China" type="Web"

推荐将所有的元数据都放入一条LABEL指令中,防止构建过多的镜像层。

我们可以通过docker inspect repositoryName/imageName来查看容器内部的标签。

2.6.10 STOPSIGNAL

用于设置停止容器的时候发送什么系统调用信号给容器,这信号必须是内核系统调用表中合法的数字(9)或者SIGNAME格式中信号名称(如SIGKILL)。

2.6.11 ARG

用来定义可以在docker build命令运行的时候传递给构建运行的变量,在构建时候可以根据dockerfile中定义过的变量进行传递

ARG build
ARG webapp_user=user

参数解释

定义了两个变量,第二个给了一个默认值,接下来可以在使用docker build --build-arg build=1234这样的命令来添加参数

2.6.12 ONBUILD

为当前的镜像添加触发器,当这个镜像被用作为其他镜像的基础镜像的时候,该镜像中的触发器会被执行。触发器会在下一个继承他的镜像的FORM之后添加这些被触发的指令

ONBUILD ADD . /app/src
ONBUILD RUN cd /app

此处使用了两条命令,当有新的镜像Dockerfile继承当前的镜像时候,运行后回在FORM之后插入这两句话

注意:触发器只能被子镜像继承,当孙镜像运行时候则不会再进行继承。

2.7 删除自己的镜像

当我们不再需要一个镜像的时候,我们可以使用

sudo docker rmi repositoryName/imageName1 repositoryName/imageName2

来删除这个镜像,这一行为同时也会删除构建这个镜像的时候所产生的每一层镜像,后面也可以不断往后罗列镜像,这将删除这镜像列表里面全部的镜像。

2.8 Docker镜像的管理

我们可以把自己在本地构建完成的镜像推送到远程仓库(默认为DockerHub),通过使用

docker push repositoryName/imageName

就可以将自己的镜像上传至自己的远程仓库之中。

  而除了这种方式,我们可以选择使用自动构建,这只需要将Github中含有Dockerfile文件的仓库链接到Docker Hub上,当我们向github推送时候,dockerhub也会自动更新(但这种方式就不可以使用docker push来发布镜像)。

三、Docker实战操作

3.1 卷的挂载

有些时候外面不想把应用或者代码构建到镜像之中

  • 同时对代码进行开发与测试
  • 代码改动十分频繁,不想在开发过程中重构镜像
  • 希望在多个容器之间共享代码

这时候我们就需要使用docker的卷挂在来实现,通过在运行容器的时候使用-v 宿主机目录:容器内目录:读写权限来对本地与容器内的文件进行映射挂载。

docker run -d -p 80 --name web -v ./web:/var/www/html/website:ro repositoryName/imageName2 cmd

3.2 容器的链接

3.2.1Docker Networking

Docker Networking允许用户创建自己的网络,容器可以通过这个网络来互相通信,接下来是一些常用的管理指令。

sudo docker network create name #创建一个桥接网络并命名为name
sudo docker network inspect name #查看这个网络的信息
sudo docker network ls # 查看当前系统中的所有网络
sudo docker network rm # 删除一个网络

当有网络后,我们在运行容器时候可以加上–net=name的标识符,把容器加入指定的网络之中。在网络内部启动的容器,docker会感知到所有在这个网络下的容器,并把这些信息都通过到当前容器的/etc/hosts文件把所有地址都保存到DNS之中。

在网络之中的任何容器的地址,都可以通过hostname.app的形式被解析,当一个容器重启的时候,它们的IP地址也会被自动更新(即对容器底层的修改不会对程序的正常工作产生影响)

3.2.2 Docker链接

  在使用run对容器进行运行的时候,我们增加--link containerName:linkName创建了客户联系,此时被运行的容器被称为客户容器,而另一个则是服务,我们为这个服务增加了linkName作为别名,这可以让我们一致的访问容器公开的信息,且无须关注底层容器的名字。

  将容器连接在一起可以让客户容器任意访问另一个容器,而不用对外公开端口。

四、Docker编配和服务发现

4.1 Docker Compose

  Dockers Compose通常被用于简单的Docker容器编配使用YAML文件定义一组需要启动的容器,以及容器运行时的属性,这些容器可以被称为是服务。我们可以很快的使用Compose来创建多容器应用栈。

安装:使用pip install -U docker-compose可以进行最新版本的快速安装

  当应用的镜像构建完成之后,我们可以使用Compose来创建需要的服务,并定义启动时候需要的属性,这些属性都会被放置与YAML文件之中,样例:

web:
    image : shenvinci/flask
    command : python app.py
    ports :
    - "5000:5000"
    volumes :
    - .:/composeapp
    links :
    - redis

redis:
    image : redis 

参数解释:

开启了分别名为webredis的两个容器文件,并为其配置了相应的参数,Compose会使用这些参数生产多个容器并组成相应的容器栈。

当切换至有YAML文件的目录下后,我们有一些常用的指令

docker-compose up -d # 在后台运行容器栈
docker-compose ps # 列出本地docker-compose.yml 文件中正在运行的所有服务
docker-compose stop # 停止正在运行的所有服务
docker-compose kill # 强制杀死正在运行中的服务
docker-compose rm # 删除docker-compose服务