一、Docker简介
Docker是一个能够把开发的应用程序自动部署到容器的开源引擎
Docker依赖于写时复制(copy-on-write)模型,Docker拥有很高的性能,能充分利用系统资源,快速启动。
Docker鼓励面向服务的架构,推荐单个容器之运行一个应用程序或进程,形成一个分布式应用程序模型。
1.1 Docker组件
- Docker客户端和服务器,也称为Docker引擎;
- Docker镜像;
- Registry;
- Docker容器。
Docker是一个客户端/服务器(C/S)架构的程序。客户端向服务器或守护进程发起请求,服务器或守护进程将完成所有工作并返回结果。
1.2 Docker镜像
镜像是基于联合文件系统的一种层式结构,有一系列指令一步步构建出来。
1.3 Registry
1.4 容器
容器是启动或执行阶段,每个容器都包含一个软件镜像,容器里的软件镜像可以进行创建、启动、关闭、重启、以及销毁等操作。
像集装箱一样,Docker不关心容器里装了什么,按照相同的方式将“内容”装载进去;Docker不关心用户要把容器运往何方,Docker容器放别替换、叠加、分发并且尽量通用。
1.5 Docker的作用
容器可以为各种测试提供很好的沙盒环境(隔离性),并且标准性的特征非常适合为服务创建构建块。Docker的一些应用场景;
- 加速本地开发和构建流程,容器可以在开发环境中构建,提交到测试环境,并最终进入生产环境。
- 让独立的服务和应用程序在不同的环境中,得到相同的运行结果。
- 用Docker创建隔离的环境测试,Jenkins CI
- 提供软件及服务(SaaS)应用程序
- 高性能、超大规模的宿主机部署
二、Docker入门
docker info
可以查看docker程序的运行情况。
- 创建并运行一个容器
-i 参数保证容器的STDIN是开启的,-t参数分配一个伪tty终端,使用ubuntu镜像,在容器中使用/bin/bash命令启动了一个Bash shell。docker run -i -t ubuntu /bin/bash
可以使用docker help run 或 man docker-run来查看详细参数
使用docker run创建并进入容器后,输入exit会退出容器并停止运行。
docker ps docker ps -a
docker ps可以查看运行中的容器,docker ps -a可以查看所有容器。
- 命名容器
—name参数会给创建的容器命名,一个合法的容器名称只能包括:a~z、A~Z、0~9、下划线_、圆点.、横线-docker run --name bob_the_container -i -t ubuntu /bin/bash
- 重新启动已经停止的容器
类似可以使用docker create来创建容器但是不启动它。docker start bob_the_containerdocker start 容器ID
- 附着到容器上
附着到正在运行的容器上,可以使用docker attach命令。
docker attach bob_the_containerdocker attach 容器ID
- 创建守护式容器
长期运行的容器,没有交互式对话,非常适合应用程序和服务。
docker run --name daemon_dave -d ubuntu /bin/sh -c "while true;do echo hello world;sleep 1;done"
docker run的-d参数会将容器放到后台运行。
- 查看容器内部
会看到输出hello world,与tail -f类似,加上-f参数可以持续跟踪日志,可以通过ctrl+c退出跟踪。docker logs daemon_dave
我们也可以跟踪日志的某一片段,使用—tail参数即可,例如用docker logs —tail 10 daemon_dave获取日志最后10行内容。
此外,还可以用-t参数为每条日志增加时间戳。
需要注意的是docker的日志驱动可以通过—log-driver来实现,选项默认是json-file,其他可用选项包括 syslog,该选项禁用docker logs 命令,并将所有容器的日志输出重定向到Syslog。
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。
% docker top daemon_daveUID PID PPID C STIME TTY TIME CMDroot 70913 70886 0 07:15 ? 00:00:00 /bin/sh -c while true;do echo hello world;sleep 1;doneroot 70963 70913 0 07:16 ? 00:00:00 sleep 1
除了docker top命令,还可以用docerk stats命令显示一个或多个容器的统计信息。
% docker stats daemon_dave my_ubuntuCONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS77e29e3f1620 daemon_dave 0.19% 484KiB / 1.942GiB 0.02% 1.44kB / 0B 0B / 0B 28003557fe3c6 my_ubuntu 0.00% 0B / 0B 0.00% 0B / 0B 0B / 0B 0
- 容器内运行进程
可以使用docker exec在容器内部额外启动新的进程,可以后台运行也可以交互,该命令可以对正在运行中的容器进行维护、监控以及管理任务。使用docker exec附着到容器再退出,容器不会停止,这点与docker attach不同。
docker exec -d daemon_dave touch /etc./new_config_filedocker exec -i -t daemon_dave /bin/bash
停止容器
docker stop daemon_davedocker stop 容器ID
自动重启容器
可以通过—restart参数来实现
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次。
--restart=on-failure:5
- 查看容器
除了docker ps能查看容器信息,还可以用docker inspect获取更多容器信息。
% docker inspect daemon_dave[{"Id": "77e29e3f16203af5e5ebcd114a86fd67e5b969f225a85ef30c88c93d0ed24a4e","Created": "2021-07-23T08:14:05.9386997Z","Path": "/bin/sh","Args": ["-c","while true;do echo hello world;sleep 1;done"],"State": {"Status": "running","Running": true,"Paused": false,"Restarting": false,"OOMKilled": false,"Dead": false,"Pid": 70913,"ExitCode": 0,"Error": "","StartedAt": "2021-07-28T07:15:57.7735358Z","FinishedAt": "2021-07-27T04:42:09.9384633Z"},......
可以用-f或—format选项选定查看结果。
% docker inspect --format="{{.State.Running}}" daemon_davetrue% docker inspect --format="{{.NetworkSettings.IPAddress}}" daemon_dave172.17.0.2% docker inspect --format="{{.State.Running}}{{.NetworkSettings.IPAddress}}" daemon_dave my_ubuntutrue172.17.0.2false
- 删除容器
可以使用docker rm 命令删除容器,对于运行中的容器可以用-f参数删除。
目前没有办法一次删除所有容器,但可以通过小技巧实现:
docker rm `docker ps -a -q`
-a参数表示列出所有容器,-q参数表示只需要返回容器ID而不返回其他信息,然后传给docker rm命令。
三、Docker镜像和仓库
docker镜像是由文件系统叠加而成,
容器采用写时复制(copy on write)技术,创建一个新容器事,Docker会创建出一个镜像栈,栈的最顶层是读写层,下面是只读镜像层和配置数据。当需要修改一个文件的时候,会把只读层复制到读写层进行修改,只读版本依然存在,但是已经被读写层的文件副本取代了。
列出镜像
% docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEubuntu latest c29284518f49 2 weeks ago 72.8MBdocker/getting-started latest 083d7564d904 6 weeks ago 28MBdockerfile latest b44b0bbd7cde 2 months ago 213MB
镜像从何而来呢,镜像存在于仓库中,可以通过docker run 或 docker pull命令获取镜像。仓库存在于Registry中,默认Registry是Docker公司运营的Docker Hub,Docker Registry的代码是开源的,可以运行自己的私有Registry。
% docker pull ubuntu:12.0412.04: Pulling from library/ubuntud8868e50ac4c: Pull complete83251ac64627: Pull complete589bba2f1b36: Pull completed62ecaceda39: Pull complete6d93b41cfc6b: Pull completeDigest: sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005Status: Downloaded newer image for ubuntu:12.04% docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEubuntu latest c29284518f49 2 weeks ago 72.8MBdocker/getting-started latest 083d7564d904 6 weeks ago 28MBdockerfile latest b44b0bbd7cde 2 months ago 213MBubuntu 12.04 5b117edd0b76 4 years ago 104MB
为了区分同一个仓库中不同的镜像,Docker提供了标签功能,可以通过冒号+标签名选择镜像。
查找镜像
可以通过docker search命令来查找所有在Docker Hub中的镜像。
% docker search puppetNAME DESCRIPTION STARS OFFICIAL AUTOMATEDalekzonder/puppeteer GoogleChrome/puppeteer image and screenshots… 80 [OK]buildkite/puppeteer A Puppeteer Docker image based on Puppeteer’… 77 [OK]...
上面的命令在Docker Hub查找了所有带有puppet的镜像,并返回信息:
- 仓库名
- 镜像描述
- 用户评价(Stars)——反映一个镜像受欢迎程度
- 是否官方(Official)——由上游开发者管理的镜像
- 自动构建(Automated)——表示这个镜像由Docker Hub的自动构建流程创建的。
构建镜像
现在已经会拉取已经构建好带有定制内容的镜像了,那么该如何修改自己的镜像、更新和管理他们呢?构建docker镜像有如下两种方法:
- docker commit
- docker build 或 Dockerfile文件
docker commit 不建议使用,推荐使用更灵活强大的Dockerfile,然后使用docker build命令。并且一般来说我们并不是创建镜像,而是在已有镜像的基础上构建新的镜像。
- 创建Docker Hub账号
https://hub.docker.com/account/signup/加入Docker Hub。
经过邮箱验证后即可通过docker login命令登陆到Docker Hub。
% docker loginLogin 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.Username: astronauts224Password:Login Succeeded
这条命令会登录到Docker Hub工作,并将认证信息保存起来供后面使用。可以通过docker logout从一个Registry服务器登出。
3.1 docker commit创建镜像
这个命令就像是将容器进行修改,然后将修改提交形成一个新的镜像。先创建一个新的容器,基于ubuntu镜像,安装apache。
% docker run --name apache-dingzhi -i -t ubuntu /bin/bashroot@797569954ffc:/# apt-get -yqq updateroot@797569954ffc:/# apt-get -y install apache2Reading package lists... DoneBuilding dependency tree...
我们启动了一个容器apache-dingzhi并在里面安装了Apache,想将这个容器作为web服务器来运行,将当前状态保存下来,而不是每次创建一个新容器并再安装Apache。
- 提交定制容器
我们可以先exit退出容器,再docker commit提交定制容器。
% docker ps -l -q797569954ffc% docker commit 797569954ffc astronauts224/apache-dingzhisha256:5eed8d1797c940fc09b3ca19c20111bc6ed8814f26296e29bf58a8ed1808afda
先通过docker ps -l -q获取刚创建的容器ID,然后docker commit [容器ID] [仓库名]/[镜像名]来提交镜像。需要注意的是docker commit提交的仅仅是创建容器的镜像(ubuntu)与容器当前状态(安装apache)有差异的部分,这使改次更新非常轻量级。
检查新创建的镜像
% docker images astronauts224/apache-dingzhiREPOSITORY TAG IMAGE ID CREATED SIZEastronauts224/apache-dingzhi latest 5eed8d1797c9 6 minutes ago 216MB
-m参数:指定提交镜像的描述
-a参数:列出提交的镜像的作者信息% docker commit -m"A new image used as webserver" -a"YuHang Wang" 797569954ffc astronauts224/apache2:webserversha256:c4126908211665f5f9af7decbfa82df1d85b81ff96990110a09930f1b1e3662a% docker inspect astronauts224/apache2:webserver[{"Id": "sha256:c4126908211665f5f9af7decbfa82df1d85b81ff96990110a09930f1b1e3662a","RepoTags": ["astronauts224/apache2:webserver"],"RepoDigests": [],"Parent": "sha256:c29284518f497b8c5f49933e74e43ca5221e69c8251e780427f7d12f716625ff","Comment": "A new image used as webserver",..."DockerVersion": "20.10.7","Author": "YuHang Wang","Config": {...
如果想从新提交的镜像运行一个新容器:
docker run -i -t astronauts224/apache2:webserver /bin/bash
3.2 用Dockerfile构建镜像
推荐使用Dockerfile定义文件和docker build来构建镜像。Dockerfile基于DSL(Domain Specific Language)语法,Dockerfile更具备可重复性、透明性和幂等性。
创建一个实例仓库
% mkdir static_web% cd static_web% touch Dockerfile
创建一个目录static_web来存放Dockerfile,这个目录就是构建环境,Docker称这个环境为上下文。Docker构建镜像时会将上下文和上下文中的文件和目录上传到Docker守护进程,这样Docker守护进程就可以访问该上下文中的任何代码文件和数据。
第一个Dockerfile文件
# Version 0.0.1FROM ubuntu:14.04MAINTAINER YuHang Wang "1398838410@qq.com"RUN apt-get update && apt-get -y install nginxRUN echo "Hi,I am in your container" \>/usr/share/nginx/html/index.htmlEXPOSE 80
执行Dockerfile的过程:
- Docker从基础镜像运行一个容器
- 执行一条指令,对容器进行修改
- 执行类似docker commit的操作,提交一个新的镜像层
- 从新提交的镜像运行一个新的容器。
- 执行Dockerfile的下一条指令,循环2-4过程直到所有指令执行完毕
从Dockerfile的执行过程可以看出,如果Dockerfile执行失败(某条指令失败),用户可以得到一个可以使用的镜像。这非常有利于用户调试:使用最后创建的镜像运行一个容器,进入容器调试指令为什么会失败。
每个Dockerfile的指令必须大写,第一条指令必须是FROM。FROM指定一个已经存在的镜像,后续指令基于该镜像执行,这个镜像被称为基础镜像。
MAINTAINER指令用于标识镜像作者和联系方式。
RUN指令会在当前镜像中运行指定命令。默认RUN指令会在shell里用命令包装器/bin/sh -c来执行。如果不支持shell或不希望在shell中运行,可以用exec格式的RUN指令:
RUN ["apt-get","-y","install","nginx"]
EXPOSE指令可以指定容器对外公开的端口,出于安全,Docker并不会自动打开端口,需要用户指定打开那些端口。
- 基于Dockerfile构建镜像
执行docker build命令,Dockerfile中所有的指令都会被执行并且提交,并且在该命令成功结束后返回一个新的镜像。
% cd static_web% docker build -t="astronauts224/static_web""docker build" requires exactly 1 argument.See 'docker build --help'.Usage: docker build [OPTIONS] PATH | URL | -Build an image from a Dockerfilewangyuhang@wangyuhangdembp static_web % docker build -t="astronauts224/static_web" .[+] Building 282.1s (8/8) FINISHED=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 255B 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load metadata for docker.io/library/ubuntu:14.04 5.0s=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s=> [1/3] FROM docker.io/library/ubuntu:14.04@sha256:43cb19408de1e0ecf3 245.5s=> => resolve docker.io/library/ubuntu:14.04@sha256:43cb19408de1e0ecf3ba 0.0s=> => sha256:2e6e20c8e2e69fa5c3fcc310f419975cef5fbeb 70.69MB / 70.69MB 240.2s=> => sha256:0551a797c01db074ab0233ceb567e66b8ebdcb9de 72.66kB / 72.66kB 2.3s=> => sha256:512123a864da5e2a62949e65b67106292c5c704eff90cac 189B / 189B 1.9s=> => sha256:43cb19408de1e0ecf3ba5b5372ec98978963d6d0be4 1.20kB / 1.20kB 0.0s=> => sha256:881afbae521c910f764f7187dbfbca3cc10c26f8bafa458 945B / 945B 0.0s=> => sha256:13b66b487594a1f2b75396013bc05d29d9f527852d9 3.31kB / 3.31kB 0.0s=> => extracting sha256:2e6e20c8e2e69fa5c3fcc310f419975cef5fbeb6f7f2fe13 4.7s=> => extracting sha256:0551a797c01db074ab0233ceb567e66b8ebdcb9de9a2e7ba 0.1s=> => extracting sha256:512123a864da5e2a62949e65b67106292c5c704eff90cac2 0.0s=> [2/3] RUN apt-get update && apt-get -y install nginx 30.8s=> [3/3] RUN echo "Hi,I am in your container" >/usr/share/nginx/html 0.4s=> exporting to image 0.3s=> => exporting layers 0.3s=> => writing image sha256:4cc8396d557b48c5a02bc07249784d7d5bdd611308fda 0.0s=> => naming to docker.io/astronauts224/static_web 0.0s% docker images astronauts224/static_webREPOSITORY TAG IMAGE ID CREATED SIZEastronauts224/static_web latest 4cc8396d557b 12 minutes ago 232MB
使用docker build构建镜像,通过-t指定仓库名/镜像名,此外还可以用冒号指定标签,如:static_web:v1,如果没有指定标签,Docker会自动设置一个latest的标签。
上面命令中最后一个 . 是告诉Docker去本地目录找Dockerfile文件,也可以指定Git仓库的源地址来指定Dockerfile的位置,如下:
docker build -t="astronauts224/static_web:v1" \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。
再一次运行:
% docker build -t="astronauts224/static_web" .[+] Building 15.9s (6/7)=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 254B 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load metadata for docker.io/library/ubuntu:14.04 2.5s=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s=> CACHED [1/3] FROM docker.io/library/ubuntu:14.04@sha256:43cb19408de1e 0.0s=> ERROR [2/3] RUN apt-get update && apt-get -y install ngin 13.2s------> [2/3] RUN apt-get update && apt-get -y install ngin:#5 0.804 Get:1 http://security.ubuntu.com trusty-security InRelease [65.9 kB]... ...#5 10.49 Fetched 13.9 MB in 10s (1363 kB/s)#5 10.49 Reading package lists...#5 11.70 Reading package lists...#5 12.92 Building dependency tree...#5 13.08 Reading state information...#5 13.15 E: Unable to locate package ngin------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略过缓存。
docker build --no-cache -t="astronauts224/static_web" .
3.5 基于构建缓存的Dockerfile构建模版
构建缓存带来的一个好处就是实现简单的Dockerfile模版,比如在Dockerfile文件顶部增加包仓库或更新包。
FROM ubuntu:14.04MAINTAINER YuHang Wang "13989838410@qq.com"ENV REFRESHED_AT 2014-07-01RUN apt-get -qq update
在上面这个Dockerfile模版里通过ENV指令设置了一个REFRESHED_AT的环境变量,用这个环境变量来表示模版最后更新的时间。最后用RUN指令运行apt-get -qq update命令,用于刷新APT包的缓存。
有了这个模版,想要刷新一个构建时只需要修改环境变量ENV即可,这使得在构建镜像时会重置缓存,后续指令不会再依赖缓存,也就是说apt-get update会再次执行。
FROM fedora:20MAINTAINER YuHang Wang "13989838410@qq.com"ENV REFRESHED_AT 2014-07-01RUN yum -q makecache
新的镜像可以用docker images命令查看,如果像查看镜像的详细构建过程,可以使用docker history命令。
% docker history 4cc8396d557bIMAGE CREATED CREATED BY SIZE COMMENT4cc8396d557b 2 hours ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0<missing> 2 hours ago RUN /bin/sh -c echo "Hi,I am in your contain… 26B buildkit.dockerfile.v0<missing> 2 hours ago RUN /bin/sh -c apt-get update && apt-get -y … 35MB buildkit.dockerfile.v0<missing> 2 hours ago MAINTAINER YuHang Wang "1398838410@qq.com" 0B buildkit.dockerfile.v0<missing> 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B<missing> 4 months ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B<missing> 4 months ago /bin/sh -c [ -z "$(apt-get indextargets)" ] 0B<missing> 4 months ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 195kB<missing> 4 months ago /bin/sh -c #(nop) ADD file:276b5d943a4d284f8… 196MB
3.6 从新镜像启动容器
用构建的新镜像启动一个容器。
% docker run -d -p 80 --name nginx-web astronauts224/nginx-web:v1 nginx -g "daemon off;"cc275b334a8f58693450127808fcb0d56d97aa113996e9b610f06a7c867d77cc
这里使用docker run命令,以分离的方式(detached)在后台运行新的容器,指定容器的名字和使用的镜像,执行命令“nginx -g ‘daemon off;’”,以前台的方式启动nginx。
这里使用-p参数,用来控制Docker运行时公开哪些端口给外部宿主机。Docker可以用两种方法在宿主机上分配端口:
- Docker可以在宿主机上随机选择一个位于32768~61000的一个比较大的端口号映射到容器中的80端口。
- 可以在Docker宿主机中指定一个具体的端口号映射到容器中的80端口。
docker run会随机在宿主机打开一个端口,映射到容器中的80端口上。可以通过docker ps命令查看端口分配情况。
% docker ps -lCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMEScc275b334a8f 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端口映射到宿主机的哪个端口:
% docker port nginx-web 80 # 用容器ID也可以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;”
> 也可以通过在端口绑定时使用后缀/udp来指定UDP端口。- docker run 命令对外公开端口```bashdocker run -d -P --name nginx-web astronauts224/nginx-web:v1 nginx -g "daemon off;"
-P参数会公开所有在Dockerfile通过EXPOSE指令指定的端口,并绑定宿主机的一个随机端口上。
绑定好端口后,可以通过本地宿主机的IP或者127.0.0.1的localhost连接到容器访问web内容了。
% curl localhost:51172Hi,I am in your container
3.7 Dockerfile指令
1. CMD
CMD命令用于指定容器启动时要运行的命令。这和docker run后指定要运行的命令非常相似。
# 指定运行特定的命令docker run -i -t astronauts224/static_web /bin/true# 等效于使用CMD指令CMD ["/bin/true"]# 给CMD指令传递参数CMD ["/bin/bash","-l"]
docker run命令会覆盖CMD指令,如果同时在Dockerfile中指定了CMD指令,又在docker run中指定了命令,命令行的命令会覆盖Dockerfile中的CMD指令。
- 例子:
假设准备一个Dockerfile,里面有这样一句:CMD [“/bin/bash”]
然后生成镜像astronauts224/cmd_test,然后用这个镜像运行一个容器
% docker run -i -t astronauts224/cmd_test:v1[root@63730d9202c4 /]#
可以看到启动容器直接运行了命令/bin/bash。现在我们命令行指定一个命令:
% docker run -i -t astronauts224/cmd_test:v1 /bin/lsbin etc lib lost+found mnt proc run srv tmp vardev 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指令则需要写成:
CMD ["/usr/sbin/nginx","-g","daemon off;"]
而使用ENTYRYPOINT,可以写成:
ENTRYPOINT ["/usr/sbin/nginx"]# 启动新容器并传递参数docker run -i -t astronauts224/static_web -g "daemon off;"
我们可以组合使用ENTRYPOINT和CMD指令来完成一些巧妙的工作。比如在Dockerfile中指定:
ENTRYPOINT ["/usr/sbin/nginx"]CMD ["-h"]
此时运行一个容器的时候,如果在命令行指定参数比如 -g “daemon off;”,Nginx守护进程会以前台方式运行;如果命令行不指定参数,Nginx服务器会以/usr/sbin/nginx -h的方式启动,用来显示nginx的帮助信息。
这样我们构建镜像既可以运行一个默认命令,也可以通过docker run命令行指定覆盖的参数。
3. WORKDIR
WORKDIR用来镜像生成容器时,在容器内部设置一个工作目录,ENTERPOINT、CMD指定的程序会在这个工作目录下执行。
WORKDIR可以作为Dockerfile中后续一系列指令的工作目录,也可以作为最终的工作目录。
WORKDIR /opt/webapp/dbRUN bundle installWORKDIR /opt/webappENTERPOINT ["rackup"]
命令行覆盖工作目录,使用-w参数:
docker run -it -w /var/log/ ubuntu pwd
4. ENV
ENV TARGET_DIR /opt/appWORKDIR $TARGET_DIR# 运行时环境变量docker run -it -e "WORK_PORT=8080" ubuntu envHOME=/WORK_PORT=8080...
在镜像创建的容器中执行env可以查看环境变量,这叫做环境变量持久化。
5. USER
指定镜像启动的容器会以什么样的用户身份去运行。
USER userUSER user:groupUSER uidUSER uid:gidUSER user:gidUSER uid:group
6. VOLUME
VOLUME ["/opt/project"]VOLUME ["/opt/project","/data"]
这条指令为基于镜像创建的容器创建一个挂载点:/opt/project
docker cp命令与之相关,可以从容器复制文件或复制文件到容器。
7. ADD
对构建上下文的文件和目录复制到镜像中。
ADD software.lic /opt/application/software.lic
通过结尾是否以/结尾判断是目录还是文件,可以用URL作为文件源
ADD http://wordpress.org/latest.zip /root/wordpress.zip
8. COPY
COPY命令与ADD类似,本质区别在于COPY不会对文件提取和解压。
COPY conf.d/ /etc/apache2
目的位置必须是容器内部的绝对路径。源路径如果是目录,COPY会将整个目录复制;如果目的路径不存在,会创建这个目录。
9. LABEL
为Docker镜像添加元数据,元数据以键值对形式展现。
LABEL version="1.0"LABEL location="NewYork" type="data center" role="Web Server"
LABEL以label=”value” 形式,每条指令可以指定多个元数据,可以通过docker inspect查看。
推送镜像到Docker Hub
docker push astronauts224/static_web
删除镜像
docker rmi astronauts224/static_web
如果想删除Docker Hub上的镜像,需要登录后使用Delete repository连接来删除。
删除所有镜像:
docker rmi `docker images -a -q`
运行自己的Docker Registry
私有Registry:
- 利用Docker Hub上的私有仓库。
- 在防火墙后运行自己的Registry
运行基于容器的registry
docker run -p 5000:5000 registry:2
推送镜像到新的registry,先用新的registry为镜像打上标签。
# 查看镜像ID% docker images astronauts224/nginx-webREPOSITORY TAG IMAGE ID CREATED SIZEastronauts224/nginx-web v1 4cc8396d557b 47 hours ago 232MB# 打标签docker tag 4cc8396d557b wangyuhangdeMacBook-Pro.local:5000/astronauts224/nginx-web# 用push推送到新的registrydocker push wangyuhangdeMacBook-Pro.local:5000/astronauts224/nginx-web
