一、Docker简介

Docker是一个能够把开发的应用程序自动部署到容器的开源引擎
Docker依赖于写时复制(copy-on-write)模型,Docker拥有很高的性能,能充分利用系统资源,快速启动。
Docker鼓励面向服务的架构,推荐单个容器之运行一个应用程序或进程,形成一个分布式应用程序模型。

1.1 Docker组件

  • Docker客户端和服务器,也称为Docker引擎;
  • Docker镜像;
  • Registry;
  • Docker容器。

Docker是一个客户端/服务器(C/S)架构的程序。客户端向服务器或守护进程发起请求,服务器或守护进程将完成所有工作并返回结果。
image.png

1.2 Docker镜像

镜像是基于联合文件系统的一种层式结构,有一系列指令一步步构建出来。

1.3 Registry

用于保存用户构建的镜像,分为公有和私有两种。

1.4 容器

容器是启动或执行阶段,每个容器都包含一个软件镜像,容器里的软件镜像可以进行创建、启动、关闭、重启、以及销毁等操作。
像集装箱一样,Docker不关心容器里装了什么,按照相同的方式将“内容”装载进去;Docker不关心用户要把容器运往何方,Docker容器放别替换、叠加、分发并且尽量通用。

1.5 Docker的作用

容器可以为各种测试提供很好的沙盒环境(隔离性),并且标准性的特征非常适合为服务创建构建块。Docker的一些应用场景;

  • 加速本地开发和构建流程,容器可以在开发环境中构建,提交到测试环境,并最终进入生产环境。
  • 让独立的服务和应用程序在不同的环境中,得到相同的运行结果。
  • 用Docker创建隔离的环境测试,Jenkins CI
  • 提供软件及服务(SaaS)应用程序
  • 高性能、超大规模的宿主机部署

二、Docker入门

  1. docker info

可以查看docker程序的运行情况。


  • 创建并运行一个容器
    1. docker run -i -t ubuntu /bin/bash
    -i 参数保证容器的STDIN是开启的,-t参数分配一个伪tty终端,使用ubuntu镜像,在容器中使用/bin/bash命令启动了一个Bash shell。

    可以使用docker help run 或 man docker-run来查看详细参数

使用docker run创建并进入容器后,输入exit会退出容器并停止运行。

  1. docker ps docker ps -a

docker ps可以查看运行中的容器,docker ps -a可以查看所有容器。


  • 命名容器
    1. docker run --name bob_the_container -i -t ubuntu /bin/bash
    —name参数会给创建的容器命名,一个合法的容器名称只能包括:a~z、A~Z、0~9、下划线_、圆点.、横线-

  • 重新启动已经停止的容器
    1. docker start bob_the_container
    2. docker start 容器ID
    类似可以使用docker create来创建容器但是不启动它。

  • 附着到容器上

附着到正在运行的容器上,可以使用docker attach命令。

  1. docker attach bob_the_container
  2. docker attach 容器ID

  • 创建守护式容器

长期运行的容器,没有交互式对话,非常适合应用程序和服务。

  1. docker run --name daemon_dave -d ubuntu /bin/sh -c "while true;do echo hello world;sleep 1;done"

docker run的-d参数会将容器放到后台运行。


  • 查看容器内部
    1. docker logs daemon_dave
    会看到输出hello world,与tail -f类似,加上-f参数可以持续跟踪日志,可以通过ctrl+c退出跟踪。
    我们也可以跟踪日志的某一片段,使用—tail参数即可,例如用docker logs —tail 10 daemon_dave获取日志最后10行内容。
    此外,还可以用-t参数为每条日志增加时间戳。

需要注意的是docker的日志驱动可以通过—log-driver来实现,选项默认是json-file,其他可用选项包括 syslog,该选项禁用docker logs 命令,并将所有容器的日志输出重定向到Syslog。

  1. docker run --log-driver="syslog" --name daemon_dwayne -d ubuntu /bin/sh -c "while true;do echo hello world;sleep 1;done"

最后还有一个可用选项是none,这个选项会禁用所有容器的日志。


  • docker统计信息

查看容器内进程可以用docker top命令,可以看到容器内所有进程、运行进程的用户及进程ID。

  1. % docker top daemon_dave
  2. UID PID PPID C STIME TTY TIME CMD
  3. root 70913 70886 0 07:15 ? 00:00:00 /bin/sh -c while true;do echo hello world;sleep 1;done
  4. root 70963 70913 0 07:16 ? 00:00:00 sleep 1

除了docker top命令,还可以用docerk stats命令显示一个或多个容器的统计信息。

  1. % docker stats daemon_dave my_ubuntu
  2. CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
  3. 77e29e3f1620 daemon_dave 0.19% 484KiB / 1.942GiB 0.02% 1.44kB / 0B 0B / 0B 2
  4. 8003557fe3c6 my_ubuntu 0.00% 0B / 0B 0.00% 0B / 0B 0B / 0B 0

  • 容器内运行进程

可以使用docker exec在容器内部额外启动新的进程,可以后台运行也可以交互,该命令可以对正在运行中的容器进行维护、监控以及管理任务。使用docker exec附着到容器再退出,容器不会停止,这点与docker attach不同。

  1. docker exec -d daemon_dave touch /etc./new_config_file
  2. docker exec -i -t daemon_dave /bin/bash

  • 停止容器

    1. docker stop daemon_dave
    2. docker stop 容器ID
  • 自动重启容器

可以通过—restart参数来实现

  1. docker run --restart=always --name daemon_dave -d ubuntu /bin/sh -c "while true;do echo hello world;sleep 1;done"

—restart参数设置为always时无论容器的退出代码是什么容器都会重启,如果设置为on-failure,容器只有在退出代码非0的时候会自动重启,on-failure还有参数可以设置如on-failure:5代表最多重启5次。

  1. --restart=on-failure:5

  • 查看容器

除了docker ps能查看容器信息,还可以用docker inspect获取更多容器信息。

  1. % docker inspect daemon_dave
  2. [
  3. {
  4. "Id": "77e29e3f16203af5e5ebcd114a86fd67e5b969f225a85ef30c88c93d0ed24a4e",
  5. "Created": "2021-07-23T08:14:05.9386997Z",
  6. "Path": "/bin/sh",
  7. "Args": [
  8. "-c",
  9. "while true;do echo hello world;sleep 1;done"
  10. ],
  11. "State": {
  12. "Status": "running",
  13. "Running": true,
  14. "Paused": false,
  15. "Restarting": false,
  16. "OOMKilled": false,
  17. "Dead": false,
  18. "Pid": 70913,
  19. "ExitCode": 0,
  20. "Error": "",
  21. "StartedAt": "2021-07-28T07:15:57.7735358Z",
  22. "FinishedAt": "2021-07-27T04:42:09.9384633Z"
  23. },
  24. ......

可以用-f或—format选项选定查看结果。

  1. % docker inspect --format="{{.State.Running}}" daemon_dave
  2. true
  3. % docker inspect --format="{{.NetworkSettings.IPAddress}}" daemon_dave
  4. 172.17.0.2
  5. % docker inspect --format="{{.State.Running}}{{.NetworkSettings.IPAddress}}" daemon_dave my_ubuntu
  6. true172.17.0.2
  7. false

  • 删除容器

可以使用docker rm 命令删除容器,对于运行中的容器可以用-f参数删除。
目前没有办法一次删除所有容器,但可以通过小技巧实现:

  1. docker rm `docker ps -a -q`

-a参数表示列出所有容器,-q参数表示只需要返回容器ID而不返回其他信息,然后传给docker rm命令。

三、Docker镜像和仓库

docker镜像是由文件系统叠加而成,
image.png
容器采用写时复制(copy on write)技术,创建一个新容器事,Docker会创建出一个镜像栈,栈的最顶层是读写层,下面是只读镜像层和配置数据。当需要修改一个文件的时候,会把只读层复制到读写层进行修改,只读版本依然存在,但是已经被读写层的文件副本取代了。


  • 列出镜像

    1. % docker images
    2. REPOSITORY TAG IMAGE ID CREATED SIZE
    3. ubuntu latest c29284518f49 2 weeks ago 72.8MB
    4. docker/getting-started latest 083d7564d904 6 weeks ago 28MB
    5. dockerfile latest b44b0bbd7cde 2 months ago 213MB

    镜像从何而来呢,镜像存在于仓库中,可以通过docker run 或 docker pull命令获取镜像。仓库存在于Registry中,默认Registry是Docker公司运营的Docker Hub,Docker Registry的代码是开源的,可以运行自己的私有Registry。

    1. % docker pull ubuntu:12.04
    2. 12.04: Pulling from library/ubuntu
    3. d8868e50ac4c: Pull complete
    4. 83251ac64627: Pull complete
    5. 589bba2f1b36: Pull complete
    6. d62ecaceda39: Pull complete
    7. 6d93b41cfc6b: Pull complete
    8. Digest: sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005
    9. Status: Downloaded newer image for ubuntu:12.04
    10. % docker images
    11. REPOSITORY TAG IMAGE ID CREATED SIZE
    12. ubuntu latest c29284518f49 2 weeks ago 72.8MB
    13. docker/getting-started latest 083d7564d904 6 weeks ago 28MB
    14. dockerfile latest b44b0bbd7cde 2 months ago 213MB
    15. ubuntu 12.04 5b117edd0b76 4 years ago 104MB

    为了区分同一个仓库中不同的镜像,Docker提供了标签功能,可以通过冒号+标签名选择镜像。

  • 查找镜像

可以通过docker search命令来查找所有在Docker Hub中的镜像。

  1. % docker search puppet
  2. NAME DESCRIPTION STARS OFFICIAL AUTOMATED
  3. alekzonder/puppeteer GoogleChrome/puppeteer image and screenshots 80 [OK]
  4. buildkite/puppeteer A Puppeteer Docker image based on Puppeteer’… 77 [OK]
  5. ...

上面的命令在Docker Hub查找了所有带有puppet的镜像,并返回信息:

  • 仓库名
  • 镜像描述
  • 用户评价(Stars)——反映一个镜像受欢迎程度
  • 是否官方(Official)——由上游开发者管理的镜像
  • 自动构建(Automated)——表示这个镜像由Docker Hub的自动构建流程创建的。

    构建镜像

    现在已经会拉取已经构建好带有定制内容的镜像了,那么该如何修改自己的镜像、更新和管理他们呢?构建docker镜像有如下两种方法:
  1. docker commit
  2. docker build 或 Dockerfile文件

docker commit 不建议使用,推荐使用更灵活强大的Dockerfile,然后使用docker build命令。并且一般来说我们并不是创建镜像,而是在已有镜像的基础上构建新的镜像。

  • 创建Docker Hub账号

https://hub.docker.com/account/signup/加入Docker Hub。
经过邮箱验证后即可通过docker login命令登陆到Docker Hub。

  1. % docker login
  2. 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.
  3. Username: astronauts224
  4. Password:
  5. Login Succeeded

这条命令会登录到Docker Hub工作,并将认证信息保存起来供后面使用。可以通过docker logout从一个Registry服务器登出。

3.1 docker commit创建镜像

这个命令就像是将容器进行修改,然后将修改提交形成一个新的镜像。先创建一个新的容器,基于ubuntu镜像,安装apache。

  1. % docker run --name apache-dingzhi -i -t ubuntu /bin/bash
  2. root@797569954ffc:/# apt-get -yqq update
  3. root@797569954ffc:/# apt-get -y install apache2
  4. Reading package lists... Done
  5. Building dependency tree
  6. ...

我们启动了一个容器apache-dingzhi并在里面安装了Apache,想将这个容器作为web服务器来运行,将当前状态保存下来,而不是每次创建一个新容器并再安装Apache。

  • 提交定制容器

我们可以先exit退出容器,再docker commit提交定制容器。

  1. % docker ps -l -q
  2. 797569954ffc
  3. % docker commit 797569954ffc astronauts224/apache-dingzhi
  4. sha256:5eed8d1797c940fc09b3ca19c20111bc6ed8814f26296e29bf58a8ed1808afda

先通过docker ps -l -q获取刚创建的容器ID,然后docker commit [容器ID] [仓库名]/[镜像名]来提交镜像。需要注意的是docker commit提交的仅仅是创建容器的镜像(ubuntu)与容器当前状态(安装apache)有差异的部分,这使改次更新非常轻量级。

  • 检查新创建的镜像

    1. % docker images astronauts224/apache-dingzhi
    2. REPOSITORY TAG IMAGE ID CREATED SIZE
    3. astronauts224/apache-dingzhi latest 5eed8d1797c9 6 minutes ago 216MB

    -m参数:指定提交镜像的描述
    -a参数:列出提交的镜像的作者信息

    1. % docker commit -m"A new image used as webserver" -a"YuHang Wang" 797569954ffc astronauts224/apache2:webserver
    2. sha256:c4126908211665f5f9af7decbfa82df1d85b81ff96990110a09930f1b1e3662a
    3. % docker inspect astronauts224/apache2:webserver
    4. [
    5. {
    6. "Id": "sha256:c4126908211665f5f9af7decbfa82df1d85b81ff96990110a09930f1b1e3662a",
    7. "RepoTags": [
    8. "astronauts224/apache2:webserver"
    9. ],
    10. "RepoDigests": [],
    11. "Parent": "sha256:c29284518f497b8c5f49933e74e43ca5221e69c8251e780427f7d12f716625ff",
    12. "Comment": "A new image used as webserver",
    13. ...
    14. "DockerVersion": "20.10.7",
    15. "Author": "YuHang Wang",
    16. "Config": {
    17. ...

    如果想从新提交的镜像运行一个新容器:

    1. docker run -i -t astronauts224/apache2:webserver /bin/bash

    3.2 用Dockerfile构建镜像

    推荐使用Dockerfile定义文件和docker build来构建镜像。Dockerfile基于DSL(Domain Specific Language)语法,Dockerfile更具备可重复性、透明性和幂等性。

  • 创建一个实例仓库

    1. % mkdir static_web
    2. % cd static_web
    3. % touch Dockerfile

    创建一个目录static_web来存放Dockerfile,这个目录就是构建环境,Docker称这个环境为上下文。Docker构建镜像时会将上下文和上下文中的文件和目录上传到Docker守护进程,这样Docker守护进程就可以访问该上下文中的任何代码文件和数据。

  • 第一个Dockerfile文件

    1. # Version 0.0.1
    2. FROM ubuntu:14.04
    3. MAINTAINER YuHang Wang "1398838410@qq.com"
    4. RUN apt-get update && apt-get -y install nginx
    5. RUN echo "Hi,I am in your container" \
    6. >/usr/share/nginx/html/index.html
    7. EXPOSE 80

    执行Dockerfile的过程:

  1. Docker从基础镜像运行一个容器
  2. 执行一条指令,对容器进行修改
  3. 执行类似docker commit的操作,提交一个新的镜像层
  4. 从新提交的镜像运行一个新的容器。
  5. 执行Dockerfile的下一条指令,循环2-4过程直到所有指令执行完毕

从Dockerfile的执行过程可以看出,如果Dockerfile执行失败(某条指令失败),用户可以得到一个可以使用的镜像。这非常有利于用户调试:使用最后创建的镜像运行一个容器,进入容器调试指令为什么会失败。

每个Dockerfile的指令必须大写,第一条指令必须是FROM。FROM指定一个已经存在的镜像,后续指令基于该镜像执行,这个镜像被称为基础镜像。
MAINTAINER指令用于标识镜像作者和联系方式。
RUN指令会在当前镜像中运行指定命令。默认RUN指令会在shell里用命令包装器/bin/sh -c来执行。如果不支持shell或不希望在shell中运行,可以用exec格式的RUN指令:

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

EXPOSE指令可以指定容器对外公开的端口,出于安全,Docker并不会自动打开端口,需要用户指定打开那些端口。

  • 基于Dockerfile构建镜像

执行docker build命令,Dockerfile中所有的指令都会被执行并且提交,并且在该命令成功结束后返回一个新的镜像。

  1. % cd static_web
  2. % docker build -t="astronauts224/static_web"
  3. "docker build" requires exactly 1 argument.
  4. See 'docker build --help'.
  5. Usage: docker build [OPTIONS] PATH | URL | -
  6. Build an image from a Dockerfile
  7. wangyuhang@wangyuhangdembp static_web % docker build -t="astronauts224/static_web" .
  8. [+] Building 282.1s (8/8) FINISHED
  9. => [internal] load build definition from Dockerfile 0.0s
  10. => => transferring dockerfile: 255B 0.0s
  11. => [internal] load .dockerignore 0.0s
  12. => => transferring context: 2B 0.0s
  13. => [internal] load metadata for docker.io/library/ubuntu:14.04 5.0s
  14. => [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
  15. => [1/3] FROM docker.io/library/ubuntu:14.04@sha256:43cb19408de1e0ecf3 245.5s
  16. => => resolve docker.io/library/ubuntu:14.04@sha256:43cb19408de1e0ecf3ba 0.0s
  17. => => sha256:2e6e20c8e2e69fa5c3fcc310f419975cef5fbeb 70.69MB / 70.69MB 240.2s
  18. => => sha256:0551a797c01db074ab0233ceb567e66b8ebdcb9de 72.66kB / 72.66kB 2.3s
  19. => => sha256:512123a864da5e2a62949e65b67106292c5c704eff90cac 189B / 189B 1.9s
  20. => => sha256:43cb19408de1e0ecf3ba5b5372ec98978963d6d0be4 1.20kB / 1.20kB 0.0s
  21. => => sha256:881afbae521c910f764f7187dbfbca3cc10c26f8bafa458 945B / 945B 0.0s
  22. => => sha256:13b66b487594a1f2b75396013bc05d29d9f527852d9 3.31kB / 3.31kB 0.0s
  23. => => extracting sha256:2e6e20c8e2e69fa5c3fcc310f419975cef5fbeb6f7f2fe13 4.7s
  24. => => extracting sha256:0551a797c01db074ab0233ceb567e66b8ebdcb9de9a2e7ba 0.1s
  25. => => extracting sha256:512123a864da5e2a62949e65b67106292c5c704eff90cac2 0.0s
  26. => [2/3] RUN apt-get update && apt-get -y install nginx 30.8s
  27. => [3/3] RUN echo "Hi,I am in your container" >/usr/share/nginx/html 0.4s
  28. => exporting to image 0.3s
  29. => => exporting layers 0.3s
  30. => => writing image sha256:4cc8396d557b48c5a02bc07249784d7d5bdd611308fda 0.0s
  31. => => naming to docker.io/astronauts224/static_web 0.0s
  32. % docker images astronauts224/static_web
  33. REPOSITORY TAG IMAGE ID CREATED SIZE
  34. astronauts224/static_web latest 4cc8396d557b 12 minutes ago 232MB

使用docker build构建镜像,通过-t指定仓库名/镜像名,此外还可以用冒号指定标签,如:static_web:v1,如果没有指定标签,Docker会自动设置一个latest的标签。
上面命令中最后一个 . 是告诉Docker去本地目录找Dockerfile文件,也可以指定Git仓库的源地址来指定Dockerfile的位置,如下:

  1. docker build -t="astronauts224/static_web:v1" \
  2. git@github.com:astronauts224/static_web:v1

docker build可以通过-f指定一个区别于Dockerfile的构建源,例如:
docker build -t=”astronauts224/static_web:v1” -f file/to/path
这个文件可以不用命名为Dockerfile,但是必须存在于构建上下文中。

  • 将构建上下文上传到Docker守护进程

transferring context: 2B 可以看出上下文被上传到Docker守护进程,之后Dockerfile中的每条命令都会顺序执行。在构建上下文的根目录如果存在.dockerignore命名的文件,该文件会按行进行分割,防止被上传到Docker守护进程中。

3.3 指令失败

我们将Dockerfile中的软件包故意写错,如:ngin。
再一次运行:

  1. % docker build -t="astronauts224/static_web" .
  2. [+] Building 15.9s (6/7)
  3. => [internal] load build definition from Dockerfile 0.0s
  4. => => transferring dockerfile: 254B 0.0s
  5. => [internal] load .dockerignore 0.0s
  6. => => transferring context: 2B 0.0s
  7. => [internal] load metadata for docker.io/library/ubuntu:14.04 2.5s
  8. => [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
  9. => CACHED [1/3] FROM docker.io/library/ubuntu:14.04@sha256:43cb19408de1e 0.0s
  10. => ERROR [2/3] RUN apt-get update && apt-get -y install ngin 13.2s
  11. ------
  12. > [2/3] RUN apt-get update && apt-get -y install ngin:
  13. #5 0.804 Get:1 http://security.ubuntu.com trusty-security InRelease [65.9 kB]
  14. ... ...
  15. #5 10.49 Fetched 13.9 MB in 10s (1363 kB/s)
  16. #5 10.49 Reading package lists...
  17. #5 11.70 Reading package lists...
  18. #5 12.92 Building dependency tree...
  19. #5 13.08 Reading state information...
  20. #5 13.15 E: Unable to locate package ngin
  21. ------
  22. executor failed running [/bin/sh -c apt-get update && apt-get -y install ngin]: exit code: 100

出错了,这个时候可以用docker run基于这次构建的镜像创建一个容器,然后再次运行apt-get -y install ngin,来进一步调试是哪里出错了。

3.4 Dockerfile和构建缓存

每一步构建过程都会将结果提交,所以Docker构建镜像的过程就很聪明,它将之前的镜像层作为缓存。

比如指令失败的例子,我们调试好只需要修改第一个RUN指令,前面的FROM、MAINTAINER都不需要修改,所以Docker将前面进行缓存并并作为开始点,再次进行构建时,Docker直接从修改的指令开始。

然而,有的时候要确保构建过程不被缓存,就需要使用—no-cache参数。比如缓存了apt-get update,那么Docker就不会刷新APT包的缓存了,此时用户若想获得每个包的是最新版本,就需要使用—no-cache略过缓存。

  1. docker build --no-cache -t="astronauts224/static_web" .

3.5 基于构建缓存的Dockerfile构建模版

构建缓存带来的一个好处就是实现简单的Dockerfile模版,比如在Dockerfile文件顶部增加包仓库或更新包。

  1. FROM ubuntu:14.04
  2. MAINTAINER YuHang Wang "13989838410@qq.com"
  3. ENV REFRESHED_AT 2014-07-01
  4. RUN apt-get -qq update

在上面这个Dockerfile模版里通过ENV指令设置了一个REFRESHED_AT的环境变量,用这个环境变量来表示模版最后更新的时间。最后用RUN指令运行apt-get -qq update命令,用于刷新APT包的缓存。
有了这个模版,想要刷新一个构建时只需要修改环境变量ENV即可,这使得在构建镜像时会重置缓存,后续指令不会再依赖缓存,也就是说apt-get update会再次执行。

  1. FROM fedora:20
  2. MAINTAINER YuHang Wang "13989838410@qq.com"
  3. ENV REFRESHED_AT 2014-07-01
  4. RUN yum -q makecache

新的镜像可以用docker images命令查看,如果像查看镜像的详细构建过程,可以使用docker history命令。

  1. % docker history 4cc8396d557b
  2. IMAGE CREATED CREATED BY SIZE COMMENT
  3. 4cc8396d557b 2 hours ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
  4. <missing> 2 hours ago RUN /bin/sh -c echo "Hi,I am in your contain… 26B buildkit.dockerfile.v0
  5. <missing> 2 hours ago RUN /bin/sh -c apt-get update && apt-get -y … 35MB buildkit.dockerfile.v0
  6. <missing> 2 hours ago MAINTAINER YuHang Wang "1398838410@qq.com" 0B buildkit.dockerfile.v0
  7. <missing> 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
  8. <missing> 4 months ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
  9. <missing> 4 months ago /bin/sh -c [ -z "$(apt-get indextargets)" ] 0B
  10. <missing> 4 months ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 195kB
  11. <missing> 4 months ago /bin/sh -c #(nop) ADD file:276b5d943a4d284f8… 196MB

3.6 从新镜像启动容器

用构建的新镜像启动一个容器。

  1. % docker run -d -p 80 --name nginx-web astronauts224/nginx-web:v1 nginx -g "daemon off;"
  2. cc275b334a8f58693450127808fcb0d56d97aa113996e9b610f06a7c867d77cc

这里使用docker run命令,以分离的方式(detached)在后台运行新的容器,指定容器的名字和使用的镜像,执行命令“nginx -g ‘daemon off;’”,以前台的方式启动nginx。

这里使用-p参数,用来控制Docker运行时公开哪些端口给外部宿主机。Docker可以用两种方法在宿主机上分配端口:

  • Docker可以在宿主机上随机选择一个位于32768~61000的一个比较大的端口号映射到容器中的80端口。
  • 可以在Docker宿主机中指定一个具体的端口号映射到容器中的80端口。

docker run会随机在宿主机打开一个端口,映射到容器中的80端口上。可以通过docker ps命令查看端口分配情况。

  1. % docker ps -l
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. cc275b334a8f astronauts224/nginx-web:v1 "nginx -g 'daemon of…" 11 minutes ago Up 11 minutes 0.0.0.0:51172->80/tcp nginx-web

可以看到宿主机的端口51172被映射到了容器中的80端口。我们也可以通过docker port来查看容器和端口的映射情况,比如我们像查看nginx-web容器中的80端口映射到宿主机的哪个端口:

  1. % docker port nginx-web 80 # 用容器ID也可以
  2. 0.0.0.0:51172
  • 通过-p参数映射特定端口 ```bash

    映射到宿主机80端口,这种直接绑定的做法,如果有多个容器运行,只有一个能成功绑定

    docker run -d -p 80:80 —name nginx-web astronauts224/nginx-web:v1 nginx -g “daemon off;”

映射到宿主机8080端口

docker run -d -p 8080:80 —name nginx-web astronauts224/nginx-web:v1 nginx -g “daemon off;”

绑定特定的网络接口(即IP地址)

docker run -d -p 127.0.0.1:80:80 —name nginx-web astronauts224/nginx-web:v1 nginx -g “daemon off;”

绑定特定的网络接口的随机端口

docker run -d -p 127.0.0.1::80 —name nginx-web astronauts224/nginx-web:v1 nginx -g “daemon off;”

  1. > 也可以通过在端口绑定时使用后缀/udp来指定UDP端口。
  2. - docker run 命令对外公开端口
  3. ```bash
  4. docker run -d -P --name nginx-web astronauts224/nginx-web:v1 nginx -g "daemon off;"

-P参数会公开所有在Dockerfile通过EXPOSE指令指定的端口,并绑定宿主机的一个随机端口上。

绑定好端口后,可以通过本地宿主机的IP或者127.0.0.1的localhost连接到容器访问web内容了。

  1. % curl localhost:51172
  2. Hi,I am in your container

3.7 Dockerfile指令

1. CMD

CMD命令用于指定容器启动时要运行的命令。这和docker run后指定要运行的命令非常相似。

  1. # 指定运行特定的命令
  2. docker run -i -t astronauts224/static_web /bin/true
  3. # 等效于使用CMD指令
  4. CMD ["/bin/true"]
  5. # 给CMD指令传递参数
  6. CMD ["/bin/bash","-l"]

docker run命令会覆盖CMD指令,如果同时在Dockerfile中指定了CMD指令,又在docker run中指定了命令,命令行的命令会覆盖Dockerfile中的CMD指令。

  • 例子:

假设准备一个Dockerfile,里面有这样一句:CMD [“/bin/bash”]
然后生成镜像astronauts224/cmd_test,然后用这个镜像运行一个容器

  1. % docker run -i -t astronauts224/cmd_test:v1
  2. [root@63730d9202c4 /]#

可以看到启动容器直接运行了命令/bin/bash。现在我们命令行指定一个命令:

  1. % docker run -i -t astronauts224/cmd_test:v1 /bin/ls
  2. bin etc lib lost+found mnt proc run srv tmp var
  3. dev home lib64 media opt root sbin sys usr

可以看到启动的容器运行了命令行命令/bin/ls,而没有执行CMD中的指令。

在Dockerfile中只能指定一条CMD指令,如果指定了多条CMD指令,也只会运行最后一个CMD指令

2. ENTRYPOINT

ENTRYPOINT和CMD指令非常类似。docker run命令行会覆盖CMD指令,而不会覆盖ENTRYPOINT,实际上docker run的所有参数都会传递给ENTRYPOINT指令。

  • 例子:

重新构建镜像astronauts224/static_web,在镜像中添加CMD指令则需要写成:

  1. CMD ["/usr/sbin/nginx","-g","daemon off;"]

而使用ENTYRYPOINT,可以写成:

  1. ENTRYPOINT ["/usr/sbin/nginx"]
  2. # 启动新容器并传递参数
  3. docker run -i -t astronauts224/static_web -g "daemon off;"

我们可以组合使用ENTRYPOINT和CMD指令来完成一些巧妙的工作。比如在Dockerfile中指定:

  1. ENTRYPOINT ["/usr/sbin/nginx"]
  2. CMD ["-h"]

此时运行一个容器的时候,如果在命令行指定参数比如 -g “daemon off;”,Nginx守护进程会以前台方式运行;如果命令行不指定参数,Nginx服务器会以/usr/sbin/nginx -h的方式启动,用来显示nginx的帮助信息。
这样我们构建镜像既可以运行一个默认命令,也可以通过docker run命令行指定覆盖的参数。

3. WORKDIR

WORKDIR用来镜像生成容器时,在容器内部设置一个工作目录,ENTERPOINT、CMD指定的程序会在这个工作目录下执行。
WORKDIR可以作为Dockerfile中后续一系列指令的工作目录,也可以作为最终的工作目录。

  1. WORKDIR /opt/webapp/db
  2. RUN bundle install
  3. WORKDIR /opt/webapp
  4. ENTERPOINT ["rackup"]

命令行覆盖工作目录,使用-w参数:

  1. docker run -it -w /var/log/ ubuntu pwd

4. ENV

  1. ENV TARGET_DIR /opt/app
  2. WORKDIR $TARGET_DIR
  3. # 运行时环境变量
  4. docker run -it -e "WORK_PORT=8080" ubuntu env
  5. HOME=/
  6. WORK_PORT=8080
  7. ...

在镜像创建的容器中执行env可以查看环境变量,这叫做环境变量持久化。

5. USER

指定镜像启动的容器会以什么样的用户身份去运行。

  1. USER user
  2. USER user:group
  3. USER uid
  4. USER uid:gid
  5. USER user:gid
  6. USER uid:group

不指定用户默认是root,命令行用-u参数指定。

6. VOLUME

  1. VOLUME ["/opt/project"]
  2. VOLUME ["/opt/project","/data"]

这条指令为基于镜像创建的容器创建一个挂载点:/opt/project
docker cp命令与之相关,可以从容器复制文件或复制文件到容器。

7. ADD

对构建上下文的文件和目录复制到镜像中。

  1. ADD software.lic /opt/application/software.lic

通过结尾是否以/结尾判断是目录还是文件,可以用URL作为文件源

  1. ADD http://wordpress.org/latest.zip /root/wordpress.zip

8. COPY

COPY命令与ADD类似,本质区别在于COPY不会对文件提取和解压。

  1. COPY conf.d/ /etc/apache2

目的位置必须是容器内部的绝对路径。源路径如果是目录,COPY会将整个目录复制;如果目的路径不存在,会创建这个目录。

9. LABEL

为Docker镜像添加元数据,元数据以键值对形式展现。

  1. LABEL version="1.0"
  2. LABEL location="NewYork" type="data center" role="Web Server"

LABEL以label=”value” 形式,每条指令可以指定多个元数据,可以通过docker inspect查看。

推送镜像到Docker Hub

  1. docker push astronauts224/static_web

删除镜像

  1. docker rmi astronauts224/static_web

如果想删除Docker Hub上的镜像,需要登录后使用Delete repository连接来删除。
删除所有镜像:

  1. docker rmi `docker images -a -q`

运行自己的Docker Registry

私有Registry:

  • 利用Docker Hub上的私有仓库。
  • 在防火墙后运行自己的Registry

运行基于容器的registry

  1. docker run -p 5000:5000 registry:2

推送镜像到新的registry,先用新的registry为镜像打上标签。

  1. # 查看镜像ID
  2. % docker images astronauts224/nginx-web
  3. REPOSITORY TAG IMAGE ID CREATED SIZE
  4. astronauts224/nginx-web v1 4cc8396d557b 47 hours ago 232MB
  5. # 打标签
  6. docker tag 4cc8396d557b wangyuhangdeMacBook-Pro.local:5000/astronauts224/nginx-web
  7. # 用push推送到新的registry
  8. docker push wangyuhangdeMacBook-Pro.local:5000/astronauts224/nginx-web

四、测试使用Docker