内容简介:Docker是一个能够把开发的应用程序自动部署到容器的开源引擎,他设计的目的就是要加强程序员写代码的开发环境和应用部署的生产环境的一致性,降低开发环境的代码在生产环境无法正常执行的风险。推荐阅读Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,Ubuntu 系统可以使用这套脚本安装:安装完毕,可以执行
什么是Docker
Docker是一个能够把开发的应用程序自动部署到容器的开源引擎,他设计的目的就是要加强 程序员 写代码的开发环境和应用部署的生产环境的一致性,降低开发环境的代码在生产环境无法正常执行的风险。推荐阅读 HERE
安装Docker
Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,Ubuntu 系统可以使用这套脚本安装:
$ curl -fsSL get.docker.com -o get-docker.sh $ sudo sh get-docker.sh --mirror Aliyun
安装完毕,可以执行 docker info
来查看是否安装成功。
操作镜像(Image)
Docker 镜像是由文件系统叠加而成。可以将镜像理解为一个可执行的文件包,其中包含了运行容器所需的程序、资源、环境变量以及配置文件。
查找镜像
Docker 公司运营着一个公共的镜像仓库(Registry) Docker Hub ,可以让我们很方便的保存并分享自己制作的镜像。开发过程中,我们使用到的镜像大部分都是可以直接使用 Docker Hub
中已经存在的镜像,即使自己有新的需求,也只需要对已有的镜像进行相应的改动。
我们可以使用 docker search
命令来查找所有 Docker Hub
上的公共可用镜像:
$ docker search ubuntu
上图为命令返回结果截图,其中返回信息为: NAME:仓库名; DESCRIPTION:该镜像描述; STARS:用户评价,star 数; OFFICIAL:是否官方开发者管理的镜像,OK 即为是; * AUTOMATED:自动构建,表示该镜像是由 Docker Hub 的自动构建流程创建的。
拉取镜像
查找到镜像后,使用 docker pull
命令来拉取该仓库的镜像,以 ubuntu
镜像为例:
$ docker pull ubuntu:16.04 16.04: Pulling from library/ubuntu 7b8b6451c85f: Pull complete ab4d1096d9ba: Pull complete e6797d1788ac: Pull complete e25c5c290bde: Pull complete Digest: sha256:e547ecaba7d078800c358082088e6cc710c3affd1b975601792ec701c80cdd39 Status: Downloaded newer image for ubuntu:16.04
该命令指定了 ubuntu
镜像且标签为 16.04
,如果不指定镜像标签,则默认为 latest
。实际上每个镜像都带有一个标签,所以一个仓库可以存储多个镜像。
注意:如果没有梯子,国内拉取 Docker Hub
的镜像可能比较慢,建议配置 Docker 官方提供的中国加速地址: https://registry.docker-cn.com
$ vim /etc/docker/daemon.json { "registry-mirrors": [ "https://registry.docker-cn.com" ] }
将上面的 json 写入文件后,重启服务:
$ systemctl daemon-reload $ systemctl restart docker
查看配置是否生效:
$ docker info ... Registry Mirrors: https://registry.docker-cn.com/ ...
列出镜像
本地镜像都保存在 Docker 宿主机的 /var/lib/docker
目录下,由 Docker 管理并且给我们提供必需的控制命令接口。如果想要查看当前守护进程(docker daemon)中存放和管理的镜像,可以使用 docker images
命令:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE yublog_db latest 90ffef425f8a 2 weeks ago 373MB yublog_web latest 5e102ae5a68d 2 weeks ago 978MB ubuntu 16.04 a51debf7e1eb 6 weeks ago 116MB jupyter/base-notebook latest 65eb0b6c51aa 2 months ago 814MB redis latest 0a153379a539 3 months ago 83.4MB alpine 3.4 174b26fe09c7 3 months ago 4.82MB python 3 a9d071760c82 3 months ago 923MB nginx 1.13.12 ae513a47849c 8 months ago 109MB
其中,我们可以得到一个镜像列表,其中包含了镜像仓库名,镜像标签,镜像ID,创建时间以及占用空间。
删除镜像
对于不再需要的镜像,可以使用 docker rmi
命令来删除指定镜像:
$ docker rmi a51deb Untagged: ubuntu:16.04 Untagged: ubuntu@sha256:e547ecaba7d078800c358082088e6cc710c3affd1b975601792ec701c80cdd39 Deleted: sha256:a51debf7e1eb2018400cef1e4b01f2e9f591f6c739de7b5d6c142f954f3715a7 Deleted: sha256:ad38d0b8ff5e07d6875cb39931e2c79fb90cf142584ea813437b013d3639678f Deleted: sha256:8ae3e0d35735ff77e9ef2a15816747b01316225829ece78dbc41bc50eddb7dfe Deleted: sha256:a6a57518ff0cc0e30c0e5c964abc052038413f57cd570bd89ab4e4493741a5b3 Deleted: sha256:41c002c8a6fd36397892dc6dc36813aaa1be3298be4de93e4fe1f40b9c358d99
该命令后接镜像名或者镜像ID,需要注意的是如果该镜像创建了容器,则镜像不会被删除。 删除多个镜像:
$ docker rmi 90ffef425f8a 5e102ae5a68d
删除所有镜像:
$ docker rmi `docker images -a -q`
其中, -a
指令会列出一些仓库名和标签为 <none>
的镜像,这类镜像是虚悬镜像(dangling image),这类镜像已经没有任何价值,可以随意删除。 -q
指令表示只列出镜像的 ID。
构建镜像
运行一个容器的时候,我们需要使用 docker run
命令,然后指定使用哪个镜像作为容器运行的基础。虽然大部分镜像都可以在 Docker Hub
上找到,但是当没有找到满足我们需求的镜像时,我们就需要自己构建。Docker 可以让我们在一个启动的容器中(如 ubuntu)进行更改,然后使用 docker commit
命令提交更新。但是基本上在所有的情况下,不建议大家这么做,定制镜像应该使用 Dockerfile
来完成,然后使用 dcoker build
命令来构建。
Dockerfile 是 Docker 的镜像构建定义文件,其中包含构建镜像过程中需要执行的命令和其他操作。 Dockerfile
相对于 docker commit
来说更具备复用性,构建过程也更加透明。
手动构建镜像
$ docker run -it --name myweb ubuntu:16.04 root@4378d6da340c:/# apt-get -y update && apt-get -y install nginx root@4378d6da340c:/# echo 'daemon off;' >>/etc/nginx/nginx.conf root@4378d6da340c:/# exit
我们使用 docker run
命令启动一个名为 myweb
的容器,基于名为 ubuntu:16.04
的镜像,如果 docker 没有在本地发现该镜像,则会从 Docker Hub
上拉取。 -i
参数是保证容器开启了 STDIN
, -t
参数使容器分配一个伪tty终端。进入容器后,我们安装 nginx
,并且在 nginx
的配置文件中添加一行 daemon off;
保持 nginx
在前台一直运行,不会在退出容器后被杀死。接着使用 docker commit
名令提交对容器的修改,指定容器名称或ID,且指定一个目标镜像仓库和镜像名:
$ docker commit -m "first attempt" myweb kyu/myweb:1.0 $ docker run -d -p 80:80 --name webcontainer kyu/myweb:1.0 /bin/bash -c "service nginx start" $ curl -I http://localhost:80 HTTP/1.1 200 OK
提交镜像后,可以使用 docker images
命令查看到构建的镜像, docker inspect
命令接镜像名称或ID可以查看镜像的详细信息。 docker run
以该镜像为基础启动了一个名为 webcontainer
的容器,并且在容器启动时启动 nginx
服务器。由于Docker 容器默认是在前台执行,所有需要加 -d
参数让容器在后台执行, -p
参数控制 Docker 运行时,将容器的80端口映射到宿主机80端口。
Dockerfile定制镜像
我们需要创建一个目录,并且在该目录创建 Dockerfile
文件。该目录即为我们的镜像构建上下文环境。Dokcer 会在构建镜像时将上下文和该上下文中的文件和目录上传到 Docker 守护进程。这样守护进程就可以直接访问到我们想在镜像中放置的任何数据。
# Dockerfile FROM ubuntu:16.04 MAINTAINER kyu yukun@eager.live -y update \ && apt-get -y install nginx \ && echo 'daemon off;' >>/etc/nginx/nginx.conf EXPOSE 80 CMD ["service", "nginx", "start"]
上面的 Dockerfile
中其实只是写入一条条指令,这些指令会被顺序执行,并且一条指令就做了一次 commit
提交,每一次 commit
实际上就是在上一层镜像上再提交一层,构建一个新的镜像。所以合理安排其中的指令,尽量不要让镜像显得很多层很臃肿。
$ docker build -t kyu/myweb:1.1 . $ docker run -d -p 80:80 --name webcontainer2 kyu/myweb:1.1 $ curl -I http://localhost:80 HTTP/1.1 200 OK
docker build
命令用来构建镜像,Dockerfile 中的指令会被顺序执行, -t
参数用来指定镜像仓库和名称,建议大家为构建的镜像设置优雅的名称,以方便我们后续的管理。后面的 .
指定了当前目录的 Dockerfile。
Dockerfile指令
- FROM:
Dockerfile
文件第一条指令必须是FROM
,该指令指定一个基础镜像,后面的指令都是基于该镜像进行; - MAINTAINER:该指令主要是保存镜像构建者的信息;
- RUN:该指令用于在构建镜像中执行指定的命令,多条
RUN
指令,建议串联做一条指令; - EXPOSE:该指令表示容器内的应用使用了容器的那个端口;
- CMD:该指令指定容器__启动时__,需要执行的命令,在
dokcer run
启动容器时可以被覆盖; - ENTRYPOINT:该指令和
CMD
指令类似,不过该指令在容器启动时,不会被docker run
命令覆盖且该命令的参数会作为ENTRYPOINT
指令的参数,如果同时也定义了CMD
指令,则CMD
也会被当中参数传给ENTRYPOINT
的参数; - ADD:该指令将上下文环境下的文件或目录复制到镜像内,不能对构建目录外的文件进行
ADD
操作,要赋值的源文件可以也是一个url地址。如果复制的是一个压缩文件,该指令为自动解压缩到镜像路径。示例:ADD index.html /usr/share/nginx/html/index.html
- COPY:该指令和
ADD
类似,但是它不会做提取和解压的工作; - VOLUME:该指令会在基于该镜像创建的容器中建立挂载点,用于保存一些必要的数据,这些数据不会被提交到镜像中,且可以在多个容器间共享。该指令只能指定容器中的挂载点(可以指定多个),无法指定宿主机上对应的目录。示例:
VOLUME /data
- WORKDIR:该指令指定工作目录,后面构建的每一层镜像也就是每一条指令,都是在该指令指定的目录下工作,如果目录不存在,该指令会自动建立。示例:
WORKDIR /work
- ENV:该指令用于设置环境变量,后面顺序执行的指令可以直接使用该指令定义的变量,运行的容器中的应用也可以直接使用。示例:
ENV CONFIG=test
使用容器(Container)
容器是基于一个 Docker 镜像创建的,它还包含一个程序执行环境和一系列指令集合。镜像是 Docker 生命周期中构建打包阶段,而容器则是启动执行阶段。
启动容器
使用 docker run
命令可以启动一个容器,在上面构建镜像的时候已经提到了,不再举例。
列出容器
docker ps
可以列出运行中的容器,增加 -a
参数可以列出所有容器:
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b9e4ce3b55ee kyu/myweb:1.1 "service nginx start" 2 hours ago Up 2 hours 0.0.0.0:80->80/tcp webcontainer2 ad101983fa61 ubuntu:16.04 "/bin/bash" 25 hours ago Exited (130) 25 hours ago myweb
我们可以看到容器的ID,基于的镜像名称,启动时容器最后执行的命令,创建时间,运行状态,端口映射情况,以及容器名称。
停止容器
$ docker stop b9e4ce3b55ee
启动已停止的容器
$ docker start b9e4ce3b55ee
进入运行中的容器
大部分情况,我们的容器时在后台运行的,而如果我们想再次进入容器,可以使用 docker attach
和 docker exec
命令。以上面运行的 webcontainer2
容器为例:
docker attach
$ docker attach webcontainer2
因为,该容器内部在前台运行着 Nginx
,可以看到,我们虽然进入了容器内,却没有进入交互式会话的 shell
。此外,使用 docker attach
进入容器内部,如果使用 exit
退出容器的话,该容器也会跟着停止。
docker exec
$ docker exec -it webcontainer2 /bin/bash root@4378d6da340c:/# exit
该命令后面可以接 -i
-t
选项,让我们可以进入交互式会话,而该命令进入容器,并使用 exit
退出,并不会导致容器停止。由于该命令后面可以跟指定的选项,我们可以利用它来在运行中的容器内,执行后台任务( $ docker exec -d webcontainer2 touch /etc/new_file
)。
查看容器日志
$ docker logs webcontainer2 * Starting nginx nginx
查看最后 10 行日志输出:
$ docker logs --tail 10 webcontainer2
查看容器内进程
$ docker top webcontainer2
查看容器详细信息
$ docker inspect webcontainer2
删除容器
$ docker rm webcontainer2
删除所有容器:
$ docker rm `docker ps -a -q`
网络(Network)
Docker 推崇一个容器运行一个应用进程,而大部分情况下,一个服务不可能只包含一个应用进程。在 Docker 中,容器之间的连接且进行数据交换,是通过网络进行的,即 Docker Networking
。我们可以通过 Docker 封装的命令,很方便的为容器制定相应的通信方案。
创建网络
$ docker network create app e662833b17caa8701cc0e3f4a37a91cc14fd609712a52a1576f24993f9fe8288
docker network
命令创建一个桥接网络,并且命名为 app,该命令给我们返回了新建网络的ID。
列出网络
$ docker network ls NETWORK ID NAME DRIVER SCOPE e662833b17ca app bridge local ca3663583680 bridge bridge local 22d6c1b30299 host host local c81ee8057a47 none null local
Docker 默认创建的网络类型是 bridge
,在 docker network create
命令后添加 -d
选项可以自己选择网络类型为 overlay
。 overlay
网络允许跨多台宿主机进行容器间通信。
连接容器
$ docker run -d --net app --name cache redis:latest
连接网络只要在 --net
选项后面添加需要连接的网络名称。
查看网络详情
$ docker network inspect app [ ... "Containers": { "cdda3301a31f57604ceac29bf4cc0d8cf02e21e888ac9a28d6aa181482b262a6": { "Name": "cache", "EndpointID": "10920e91a17f85fce62b510a63f7cbd4d7a7bfa66017d5dc93f18a9eaffd5d89", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" } }, ... ]
可以看到该网络中多了一个连接的容器,以及他的 MAC 和 IP 地址。
容器间通信
$ docker run --net app --name test -it ubuntu:16.04 /bin/bash root@4378d6da340c:/# apt-get -y update && apt-get -y install redis-tools
要想连接到上面创建的 redis
容器,甚至不需要指定 redis
容器的 IP 地址,只要指定需要连接 redis
容器的容器名称。
root@4378d6da340c:/# redis-cli -h cache cache:6379>
已有容器连接网络
docker network connect
命令可以使正在运行的容器连接到已有的网络中。
$ docker network connect app webcontainer2 $ docker network inspect app
删除网络
$ docker network rm app
数据交互
在 Docker 中,实现容器内外或者容器之间的数据交互,需要使用到卷。卷是一个被选定的目录,可以绕过 Docker 的分层的联合文件系统(Union File System),提供数据共享的功能。
Bind Mount
挂载宿主机的目录和文件到容器内,只需指定宿主机的路径和容器内的路径,就可以形成挂载映射关系,对目录或文件的修改会直接生效,并且互相可见。
$ mkdir mydocker && cd mydocker $ touch hello.txt $ docker run --name test2 -it -v $PWD/hello.txt:/home/hello.txt ubuntu:16.04 /bin/bash root@4378d6da340c:/# ls /home/ hello.txt root@4378d6da340c:/# echo 'hello world' > /home/hello.txt root@4378d6da340c:/# cat /home/hello.txt hello world!!! root@4378d6da340c:/# exit $ cat hello.txt hello world!!!
使用 -v
或 --volume
选项,并且指定宿主机路径和容器内路径,格式: -v <host-path>:<container-path>
,如果容器内没有该路径,Docker 会自己创建。需要注意的是,这里指定的路径必须是绝对路径,这是为了避免定义数据卷名称的时候,出现混淆。在容器路径文件或目录后接 :ro
或者 :rw
可以指定容器内目录的读写状态:
$ mkdir hello $ docker run --name test3 -it -v $PWD/hello:/home/hello:ro ubuntu:16.04 /bin/bash
很多时候 Bind Mount
挂载方式,是非常方便的。比如在构建镜像时,不想把应用或者代码构建到镜像中,把宿主机的程序挂载到容器内是很方便的方式。对于程序代码改动频繁,又不想在开发中频繁重构镜像,很适合使用这种挂载方式。在指定一些如 nginx、tomcat 软件的配置文件时,把文件挂载到容器内,也是很好的选择。
Volume
数据卷也是从宿主机中挂载目录到 Docker 容器内,不过挂载的目录有 Docker 进行管理,定义数据卷时只需指定容器内的目录,并且数据卷默认会一直存在,即使引用的容器被删除。这种挂载方式适用于 Docker 写入本地的场景,比如我们想把数据库容器的数据写入到宿主机管理,并且可以让多个容器共享。
$ docker run -d --name db -it -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=password mysql:5.7
定义数据卷,仍然可以使用 -v
选项,不过我们只需要指定容器内的路径。为了方便我们记住数据卷,还可以指定数据卷的名称,格式: -v <name>:<container-path>
$ docker run -d --name db -it -v dbdata:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=password mysql:5.7 $ docker inspect db ... "Mounts": [ { "Type": "volume", "Name": "dbdata", "Source": "/var/lib/docker/volumes/7c2f70b0ed00045af78106f5e4d91f32d2064442b91e435b9e03ffe9c0f4df22/_data", "Destination": "/var/lib/mysql", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ], ...
查看容器的详情,可以看到容器中挂载的数据卷信息。其中 Name
是数据卷的名称,如果不指定,默认是数据卷的ID。 Source
信息是 Docker 为我们分配用于挂载的宿主机目录。
创建数据卷
除了在启动容器时指定数据卷外,还可以使用 docker volume
命令来创建和管理数据卷。
$ docker volume create mysqldb $ docker volume ls DRIVER VOLUME NAME local mysqldb ...
这样,在启动容器时,直接指定该数据卷即可,多个容器共享数据卷,只要指定数据卷名称或ID。
删除数据卷
$ docker volume rm mysqldb
如果有容器引用该数据卷,并且容器未被删除,则无法删除数据卷。数据卷在关联的容器被删除时不会一起被清除,所以在删除容器时加上 -v
选项可以一起删除关联的数据卷,当然需要保证该数据卷没有被其他容器所关联。
删除所以未被容器引用的数据卷:
$ docker volume prune -f
备份和迁移数据卷
如果想要备份或者迁移数据卷,可以使用 docker inspect
命令找到数据卷在宿主机上的位置,然后很轻松的实现备份或者迁移。
但是 Docker 为我们提供了更优雅的方法,我们可以利用数据卷容器共享的优势,指定一个 ubuntu
基础镜像,利用 --volumes-from
选项,挂载目标容器所有的卷,作为一个新的容器,然后执行 tar cvf
命令打包需要备份的目录。
$ mkdir backup $ docker --rm --volumes-from db -v $PWD/backup:/backup ubuntu tar cvf /backup/backup.tar /var/lib/mysql
命令解释:
- 启动以一个
ubuntu
作基础镜像的容器; -
--rm
选项指定该容器进程运行结束后,自动删除容器; -
--volumes-from
选项指定了该容器挂载了目标容器 db 的所有卷; -
-v
选项将宿主机的$PWD/backup
目录挂载为/backup
; - 将数据卷
/var/lib/mysql
的内容打包为backup.tar
参考
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Bootstrap入门之组件
- Android组件化入门:一步步搭建组件化架构
- Typescript 入门构建一个 react 组件
- OpenStack 入门及三大存储组件浅析
- ReactNative入门教程-组件生命周期函数
- 又一篇 iOS Extension 入门(3/3)— Today 小组件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
七周七语言(卷2)
【美】Bruce A. Tate(泰特)、Fred Daoud(达乌德)、Ian Dees(迪斯) / 7ML翻译组 / 人民邮电出版社 / 2016-12 / 59
深入研习对未来编程具有重要意义的7种语言 Lua、Factor、Elixir、Elm、Julia、Idris和MiniKanren 本书带领读者认识和学习7种编程语言,旨在帮助读者探索更为强大的编程工具。 本书延续了同系列的畅销书《七周七语言》《七周七数据库》和《七周七Web开发框架》的体例和风格。 全书共8章,前7章介绍了Lua、Factor、Elm、Elixir、Jul......一起来看看 《七周七语言(卷2)》 这本书的介绍吧!