2013年Docker发布至今,被认为可能会改变软件行业,到现在,它确实在一定程度上改变了软件行业。
这篇文章主要介绍docker的基本使用,提供常用命令的快速查询。目的在于能让普通开发者在短时间内能对docker有一个直观的感受,进而使用docker快速搭建开发环境。
环境配置难
相信不少小伙伴都有过这样的体验。
- 想要学习linux,那么我们需要去安装linux系统或者安装linux虚拟机;
- 如果你是后端开发者,你可能要搭建php或java的开发环境(redis、mysql等),然后进行繁琐的环境变量等配置;
- 安装一个软件,然后发现这个软件又依赖一些其他的软件,需要逐个去安装;
- 跟着一个教程学习,发现按相同的方法自己的代码run不起来;
这些环境问题docker都可以解决,它可以提供一致的运行环境。
什么是Docker
Docker 最初是 dotCloud
公司创始人 Solomon Hykes (opens new window)在法国期间发起的一个公司内部项目,它是基于 dotCloud
公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源 (opens new window),主要项目代码在 GitHub (opens new window)上进行维护。
Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目 (opens new window)已经超过 5 万 7 千个星标和一万多个 fork
。甚至由于 Docker
项目的火爆,在 2013
年底,dotCloud 公司决定改名为 Docker (opens new window)。
Docker 使用 Google
公司推出的 Go 语言 (opens new window)进行开发实现,基于 Linux
内核的 cgroup (opens new window),namespace (opens new window),以及 OverlayFS (opens new window)类的 Union FS (opens new window)等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术 (opens new window)。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
与传统的虚拟机相比,docker有以下优点:
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为 MB | 一般为 GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
Docker的用途
Docker 的主要用途,目前有以下几类。
(1)提供一致的运行环境。 保证开发环境、测试环境、预发环境、生产环境的一致性,避免特定环境出现的bug。
(2)提供弹性的云服务。 因为Docker能提供一致的运行环境,我们可以很轻松的实现服务的迁移。另外,Docker的启动和销毁都很快,可以做到秒级、甚至毫秒级的启动时间,能实现快速的扩容和缩容。
(3)持续交付和部署。 对开发和运维(DevOps (opens new window))人员来说,使用 Docker
可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) (opens new window)系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) (opens new window)系统进行自动部署。
(4)组建微服务架构。 在单台服务器上,或者我们自己的笔记本电脑上,可以运行多个服务,包括服务依赖的后端资源,比如redis、mysql、kafka等,甚至我们还可以把这些后端资源按集群的方式部署。借助Docker我们可以在一台机器上模拟微服务架构。
安装Docker
Docker 的安装参考官方文档。
Docker还提供了桌面版本。
镜像(Image)
Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变。
Docker镜像是分层存储的,镜像构建时,会一层层构建,前一层是后一层的基础。不同的镜像可以复用"层",节省存储空间。
开发者可以自己制作镜像,也可以使用别人制作好的镜像。一般来说,比较常用的镜像在官方仓库Docker Hub上都可以找到。
从 Docker 镜像仓库获取镜像的命令是 docker pull
。其命令格式为:
$ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
复制代码
- Docker 镜像仓库地址:地址的格式一般是
<域名/IP>[:端口号]
。默认地址是 Docker Hub(docker.io
)。 - 仓库名:如之前所说,这里的仓库名是两段式名称,即
<用户名>/<软件名>
。对于 Docker Hub,如果不给出用户名,则默认为library
,也就是官方镜像。
➜ ~ docker pull ubuntu:20.04
20.04: Pulling from library/ubuntu
a31c7b29f4ad: Pull complete
Digest: sha256:b3e2e47d016c08b3396b5ebe06ab0b711c34e7f37b98c9d37abe794b71cea0a2
Status: Downloaded newer image for ubuntu:20.04
docker.io/library/ubuntu:20.04
复制代码
上面的命令是从官方仓库拉取Ubuntu的20.04版本的镜像。最后一行就是镜像的完整的名字。
使用同样的方式,我们再安装一下redis和mysql-server的镜像。
➜ ~ docker pull redis:latest
latest: Pulling from library/redis
33847f680f63: Pull complete
26a746039521: Pull complete
18d87da94363: Pull complete
5e118a708802: Pull complete
ecf0dbe7c357: Pull complete
46f280ba52da: Pull complete
Digest: sha256:cd0c68c5479f2db4b9e2c5fbfdb7a8acb77625322dd5b474578515422d3ddb59
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
➜ ~ docker pull mysql/mysql-server:5.7
5.7: Pulling from mysql/mysql-server
b5be27e1996f: Pull complete
091844bfb0ec: Pull complete
3cb5fb55841d: Pull complete
47644b99acea: Pull complete
3bd4cb0b78d1: Pull complete
a62c52d3a764: Pull complete
20396840d0b1: Pull complete
38b3da6a86f7: Pull complete
Digest: sha256:779c5ba746fe052579c50a0533eaf38ee1ab75d348534d5db092028242a32eed
Status: Downloaded newer image for mysql/mysql-server:5.7
docker.io/mysql/mysql-server:5.7
复制代码
实测,官方镜像的拉取速度还是可以的,如果你从官方下载镜像很慢,可以使用 镜像加速器 。
在桌面版Docker也可以看到我们刚才拉取的镜像。
如果你不喜欢命令行的操作,也完全可以在桌面版完成镜像的管理,容器的启动等操作。
镜像的操作
列出镜像
➜ ~ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest aa4d65e670d6 39 hours ago 105MB
mysql/mysql-server 5.7 8b7280d401c0 4 days ago 385MB
ubuntu 20.04 c29284518f49 11 days ago 72.8MB
复制代码
列表包含了 仓库名
、标签
、镜像 ID
、创建时间
以及 所占用的空间
。
查看镜像所占空间
➜ ~ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 3 0 563MB 563MB (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
复制代码
通过 docker system df
命令来便捷的查看镜像、容器、数据卷所占用的空间。因为这里我们还没有启动镜像,所以其他数据都是0。
删除镜像
如果要删除本地的镜像,可以使用 docker image rm
命令,其格式为:
$ docker image rm [选项] <镜像1> [<镜像2> ...]
复制代码
其中,<镜像>
可以是 镜像短 ID
、镜像长 ID
、镜像名
或者 镜像摘要
。
比如我们可以使用命令dokcer image rm redis
或 docker image rm aa4
来删除redis镜像。
➜ ~ docker image rm redis
Untagged: redis:latest
Untagged: redis@sha256:cd0c68c5479f2db4b9e2c5fbfdb7a8acb77625322dd5b474578515422d3ddb59
Deleted: sha256:aa4d65e670d6518e5da96ca9d1a76370a942970a8802e6d5cc6bcf058ab12ca7
Deleted: sha256:3bd00d38f5ca70200050477c527cc60cfdf82911d6fe03932e2bcae31a95cfa2
Deleted: sha256:22722fde392d188cfbe5bbd0c2451cc71cf5b000afc0e5114c1066bb5e113ec9
Deleted: sha256:38212b55ef525e86cd726cd83c1a82a6009c68d24771d6e93d439fdc88e66f0e
Deleted: sha256:188c498579cef37b65a93d6448c6b129fa07d5740fc213a18843ff22d80cd10d
Deleted: sha256:2117165cd53c98f13ec7af36c9d8acd239fc541c847efaccb49885decf615d68
Deleted: sha256:814bff7343242acfd20a2c841e041dd57c50f0cf844d4abd2329f78b992197f4
复制代码
删完了redis镜像,后面还是需要的,ok,那我们再装回来。
定制镜像
Dockerfile 是一个文本文件,其内包含了一条条的 指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
接下来,我们以linux命令行下两个小游戏为例构建一个镜像。
step 1
在本地新建一个sound.txt
的文件,里面随便写几句话。
step 2
新建一个名字为Dockerfile
的文件,里面写入以下命令:
FROM ubuntu:20.04
COPY ./sound.txt /
WORKDIR /
RUN apt-get update && apt-get -y install sl && apt-get -y install cowsay && echo "export PATH=/usr/games:$PATH" > /etc/bash.bashrc
复制代码
而 FROM
就是指定 基础镜像,我们构建的镜像是以ubuntu:20.04
为基础的。
COPY
是把当前目录(这个当前目录指的是使用构建命令时指定的目录)下的sound.txt
复制到构建的镜像的根目录下。
WORKDIR /
:指定接下来的工作路径为/
。
接下来的RUN
命令时执行linux的bash命令,安装 sl
和 cowsay
两个软件,因为默认是安装在了/usr/games
目录下,所以我们把/usr/games
添加到PATH
中。
step 3
执行docker build
命令构建镜像。
➜ Documents docker build -t ubuntu-game:0.1 .
[+] Building 0.3s (8/8) FINISHED
=> [internal] load build definition from Dockerfile 0.2s
=> => transferring dockerfile: 37B 0.2s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.1s
=> [internal] load metadata for docker.io/library/ubuntu:20.04 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 30B 0.0s
=> [1/4] FROM docker.io/library/ubuntu:20.04 0.0s
=> CACHED [2/4] COPY ./sound.txt / 0.0s
=> CACHED [3/4] RUN apt-get update && apt-get -y install sl && apt-get - 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:9535c4b11c3ffdd60225167968f9af8e5a69cf1d5df85 0.0s
=> => naming to docker.io/library/ubuntu-game:0.1
复制代码
docker build
命令后面跟的点,是用来指定当前目录为构建上下文的路径。
build命令运行成功后,使用docker image ls
命令就能看到我们构建的ubuntu-game
镜像。
step 4
看下效果。
docker run --name game -it ubuntu-game:0.1
复制代码
使用上面的命令启动镜像,会进入ubuntu-game
的终端,命令的解释会在下一节讲解容器的时候详细解释。
根目录下有我们创建的sound.txt
文件。
root@fe5f88e5a625:/# ls -l sound.txt
-rw-r--r-- 1 root root 49 Jul 25 05:05 sound.txt
复制代码
执行 source .bashrc
使配置的环境变量生效。
执行以下命令,就启动了cowsay
游戏,可以看到,一头牛吟出了杜甫的诗句。关于 cowsay
的更多玩法见Cowsay。
root@16a4b6b15b8c:/# cat sound.txt | cowsay
__
/ 风急天高猿啸哀,渚清沙白鸟 \
\ 飞回。 /
--
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
复制代码
root@98e83032d71c:/# sl
复制代码
执行sl
命令,会有一列火车在终端上自左而右,冒着白烟,轰隆隆的驶过。
容器
简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。
启动容器
例如,下面的命令输出一个 “Hello World”,之后终止容器。
➜ ~ docker run ubuntu:20.04 /bin/echo 'Hello world'
Hello world
复制代码
这跟在本地直接执行 /bin/echo 'hello world'
几乎感觉不出任何区别。
下面的命令则启动一个 bash 终端,允许用户进行交互。
➜ ~ docker run -t -i ubuntu:20.04 /bin/bash
root@1d52bb27322e:/#
复制代码
其中,-t
选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i
则让容器的标准输入保持打开。
在交互模式下,用户可以通过所创建的终端来输入命令,例如
root@1d52bb27322e:/# ls
bin dev home lib32 libx32 mnt proc run srv tmp var
boot etc lib lib64 media opt root sbin sys usr
复制代码
使用docker ps
或 docker container ls
命令可以查看正在运行的容器。
➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1d52bb27322e ubuntu:20.04 "/bin/bash" 2 minutes ago Up 2 minutes pensive_bouman
复制代码
使用docker ps -a
或 docker container ls -a
命令则可以查看所有的容器,包括已经停止运行的。
➜ ~ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1d52bb27322e ubuntu:20.04 "/bin/bash" 5 minutes ago Exited (0) 6 seconds ago pensive_bouman
24604b36f7e8 ubuntu:20.04 "/bin/echo 'Hello wo…" 5 minutes ago Exited (0) 5 minutes ago practical_shtern
98e83032d71c ubuntu-game:0.1 "bash" 19 minutes ago Exited (0) 11 minutes ago game
复制代码
已经停止运行的容器,可以再次启动吗?答案是可以的。
利用 docker container start
命令,直接将一个已经终止(exited
)的容器启动运行。
➜ ~ docker start 24
复制代码
上面启动容器的命令可以省略掉container,24表示要启动容器的id的短摘要,使用容器的长摘要或容器名字也是可以的。
容器后台运行和进入容器
需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d
参数来实现。
比如,我们在后台启动一个mysql的容器。
➜ ~ docker image ls mysql/mysql-server
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql/mysql-server 5.7 8b7280d401c0 4 days ago 385MB
复制代码
我们前面已经安装过mysql-server的镜像,现在把它启动起来。
➜ ~ docker run --name mysql -d mysql/mysql-server:5.7
9f6e2fd2501306db7ade6b940f11f3c3e7f0495976447541742e1de9481f9c7e
➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f6e2fd25013 mysql/mysql-server:5.7 "/entrypoint.sh mysq…" 4 seconds ago Up 2 seconds (health: starting) 3306/tcp, 33060/tcp mysql
复制代码
上面的命令是我们启动了mysql-server的容器,并给它起了个别名叫mysql。
我们还可以停止这个容器。
docker stop mysql
复制代码
重启容器。docker container restart
命令会将一个运行态的容器终止,然后再重新启动它。
后台运行的容器,要获取容器的输出信息,可以通过 docker container logs
命令。
要进入一个后台运行的容器,使用exec
命令。
docker exec
后边可以跟多个参数,这里主要说明 -i
-t
参数。
只用 -i
参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。
当 -i
-t
参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。
删除容器
可以使用 docker container rm
来删除一个处于终止状态的容器。例如
➜ ~ docker container rm mysql
mysql
复制代码
如果要删除一个运行中的容器,可以添加 -f
参数。Docker 会发送 SIGKILL
信号给容器。
用 docker container ls -a
命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。
➜ ~ docker container prune
复制代码
例子
运行 mysql-server
安装镜像:
docker pull mysql/mysql-server
复制代码
启动容器:
docker run --name=mysql -d -p 3306:3306 mysql/mysql-server:5.7
复制代码
-p 3306:3306
指的是把容器的3306端口映射到本地的3306端口,有了端口映射,我们就可以在本地使用mysql客户端访问mysql了。
运行 redis
安装镜像,从官方镜像拉取最新的redis镜像。
docker pull redis
复制代码
启动容器:
docker run --name redis -d -p 6379:6379 redis
复制代码
-p 6379:6379
指的是把容器的6379端口映射到本地的6379端口。
运行consul
安装镜像,从官方镜像拉取最新的consul镜像。
docker pull consul
复制代码
启动容器:
docker run --name consul -d -p 8500:8500 -p 8600:8600/udp consul
复制代码
上面的命令把8500和8600端口都映射到本地。
运行kafka
由于kafka依赖Zookeeper,我们使用Docker Compose的方式拉取镜像。
把下面的yml配置保存为名为docker-compose.yml
的文件,在文件所在的路径中,执行docker-compose up
命令,就能把zookeeper和kafka镜像安装了,并启动相应的容器。
version: '2'
services:
zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ports:
- 22181:2181
kafka:
image: confluentinc/cp-kafka:latest
depends_on:
- zookeeper
ports:
- 29092:29092
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
复制代码
镜像的拉取过程如下所示。
➜ docker compose up
[+] Running 15/15
⠿ zookeeper Pulled 72.1s
⠿ 96965a3a8424 Pull complete 12.8s
⠿ eb918a808c15 Pull complete 58.2s
⠿ 5c2fffeabbf7 Pull complete 58.5s
⠿ b479bf09eedc Pull complete 60.2s
⠿ 2e31c2ab64ea Pull complete 60.3s
⠿ e5161e1fdbdc Pull complete 60.4s
⠿ 5dcd26e8f603 Pull complete 66.5s
⠿ 27267efe7f14 Pull complete 66.7s
⠿ kafka Pulled 71.6s
⠿ 4d0d850cd4ad Pull complete 13.1s
⠿ 7a73120408f4 Pull complete 57.5s
⠿ 68bcf2239ce4 Pull complete 60.1s
⠿ e0f07497560d Pull complete 65.8s
⠿ b88780ca570c Pull complete 66.2s
[+] Running 3/3
⠿ Network documents_default Created 1.0s
⠿ Container documents_zookeeper_1 S... 2.6s
⠿ Container documents_kafka_1 Start... 3.8s
复制代码
总结
- Docker能帮助我们快速搭建一致性的环境;
- Docker可实现弹性扩容,非常适合微服务架构;
- 介绍了镜像和容器的概念以及相关的操作;
- 列举了使用容器快速搭建mysql、redis等后端资源的例子。