应用的容器化
Docker 的核心思想就是如何将应用整合到容器中,并且能在容器中实际运行。将应用整合到容器中并且运行起来的这个过程,称为“容器化(Containerizing)”,有时候也叫“Docker 化(Dockerizing)”。
应用容器化主要分为以下几个步骤:
- 编写应用代码
- 创建一个 Dockerfile,其中包括当前应用的描述、依赖以及该如何运行这个应用
- 对该 Dockerfile 执行 docker image build 命令
- 等待 Docker 将应用程序构建到整个 Docker 镜像中
一旦应用容器化完成,就能够以镜像的形式交付并以容器的方式运行了。
######### 应用容器化的基本命令 ########
#基于 Dockerfile 文件构建镜像 - t 表示为镜像打标签 - f 表示为 Dockerfile 指定路径和名称 - f 表示指定位于任意路径下的任意名称的 Dockerfile
$ docker image build
######### Dockerfile 指令 ###############
$ FROM #指定构建的基础镜像
$ RUN #在镜像层执行命令,每次使用将会构建新的镜像层
$ COPY #将文件作为一个新的层添加到镜像中,通常使用赋值的形式
$ EXPOSE #为镜像设置监听端口
$ ENTRYPOINT #指定镜像以容器方式默认运行的应用程序
$ LABEL #给镜像打标签,以键值对的形式
$ ENV #指定镜像的环境变量
$ ONBUILD #添加镜像触发器,以当前基础镜像构建新的镜像时,会触发相应的指令
$ HEALTHCHECK #增加自定义的心跳检测功能,多次使用只有最后一次有效
$ ADD #构建镜像时,复制上下文中的文件到镜像内
$ WORKDIR #指定镜像工作目录
...
更多 Dockerfile 文件指令可到官方文档查询:https://docs.docker.com/v17.09/engine/reference/builder/
单体应用容器化
-
获取源码
由于我所用的学习资料是
Nigel Poulton
所著的《深入浅出 Docker》,再次感谢作者的不吝贡献!关于接下来所分析的部分,我克隆了本书作者的Github
仓库源代码进行分析。$ git clone https://github.com/nigelpoulton/psweb.git #克隆源码 $ cd psweb/ $ ls app.js circle.yml Dockerfile package.json README.md test views
可以看出,整个应用容器化的结构已经一目了然了!
-
分析 Dockerfile
Dockerfile
文件是一个包含了对当前应用的描述,并且是镜像构建必不可少的一个文件。Dockerfile 能够在开发和部署两个过程中无缝切换,因此要像源代码一样重视它,将其视为 源代码控制系统 的一部分。在 Docker 当中,包含 应用文件的目录通常被称为构建上下文(Bulid Context);通常将 Dockerfile 文件放到构建上下文的根目录下。
说实话,构建上下文这个名字我始终觉得有些拗口,不过就姑且按照作者的理解来继续吧!这个概念在接下来的 Docker 学习中占据着非常重要的地位!
关于
Dockerfile
文件的命名方式,文件的开头字母必须是大写字母D
,除此以外的其他写法都是不被允许的。Dockerfile
文件主要包括两个用途:- 对当前应用的描述
- 指导 Docker 完成应用容器化
下面我们来分析 Dockerdfile
文件的每一项信息:
1 # Test web-app to use with Pluralsight courses and Docker Deep Dive book
2 # Linux x64
3 FROM alpine
4
5 LABEL maintainer="nigelpoulton@hotmail.com"
6
7 # Install Node and NPM
8 RUN apk add --update nodejs nodejs-npm
9
10 # Copy app to /src
11 COPY . /src
12
13 WORKDIR /src
14
15 # Install dependencies
16 RUN npm install
17
18 EXPOSE 8080
19
20 ENTRYPOINT ["node", "./app.js"]
- 每个
Dockerfile
文件的第一行是FROM
指令,用于指定当前的基础镜像,这里选用了alpine
。FROM
指令指定的镜像,会作为当前镜像的一个基础镜像层,当前应用的剩余内容会作为新增镜像层添加到基础镜像层上。此时的镜像层数为1
。 - 接下来的
LABEL
标签用于指定当前镜像的维护者是nigelpoulton@hotmail.com
。每个标签是一个 键值对(key-value)的形式,在一个镜像当中可以通过增加标签的方式来为镜像增加元数据,并且备注维护者信息有助于该镜像的维护,该指令不会创建新的镜像层。 -
RUN apk add --update nodejs nodejs-npm
指令使用的是alpine
的apk
包管理器,对node.js
和nodejs-npm
安装到当前镜像中。注意,此时构建的镜像是在FROM
指定的镜像层alpine
之上,此时的镜像层数为2
。 -
COPY . /src
指令表示将部署应用的相关文件 从构建上下文复制到当前镜像中/src
,并新建一层镜像存储,此时的镜像层数为3
。 -
WORKDIR
指令表示为尚未执行的指令设置 工作目录。该目录与镜像相关,并且会作为元数据记录到镜像配置中,但不会创建新的镜像层。 -
RUM npm install
指令会根据package.json
中配置信息,使用npm
来安装当前应用相关的依赖包,并创建新的镜像层来存储相关的数据文件,此时的镜像曾数为 4. -
EXPOSE 8080
表示为应用设置网络端口号为8080
,这些信息会作为元数据被保存下来,不会创建新的镜像层。 -
ENTRYPOINT ["node", "./app.js"]
指令表示 指定当前镜像的入口程序 ,这些信息会作为元数据被保存下来,也不会创建新的镜像层。到这里,整个Dockerfile
文件的指令已经全部执行完毕,一共构建了4
层镜像。
-
容器化应用 & 构建具体的镜像
在构建上下文目录中,执行
docker image build -t web:latest .
构建并生成一个名为web:latest
的镜像。注意最后的这个点不能省略!
$ docker image build -t web:latest . #构建镜像 Sending build context to Docker daemon 74.75kB Step 1/8 : FROM alpine ---> 965ea09ff2eb Step 2/8 : LABEL maintainer="nigelpoulton@hotmail.com" ---> Using cache ---> 7c39d759f1a2 Step 3/8 : RUN apk add --update nodejs nodejs-npm ---> Using cache ---> 9fc401a717dd Step 4/8 : COPY . /src ---> Using cache ---> 34d5b56b97b7 Step 5/8 : WORKDIR /src ---> Using cache ---> 8516e5745bdf Step 6/8 : RUN npm install ---> Using cache ---> eb6f3e2ecab6 Step 7/8 : EXPOSE 8080 ---> Using cache ---> 4687da82872d Step 8/8 : ENTRYPOINT ["node", "./app.js"] ---> Using cache ---> c862461d9b35 Successfully built c862461d9b35 #构建镜像 ID Successfully tagged web:latest #构建镜像名称 $ docker image ls #查看当前镜像 ID REPOSITORY TAG IMAGE ID CREATED SIZE web latest c862461d9b35 10 minutes ago 71.5MB $ docker image inspect web:latest #查看镜像的详细信息,如果有输出,证明镜像正确完成构建
-
推送镜像到具体仓库
前面已经提到了,Docker 公司提供了一个官方的存储仓库服务,即便Docker Hub
,在构建镜像完毕之后,可以将本地主机的镜像推送至docker hub
仓库,通过docker login
命令执行这一操作:$ docker login 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. Username: sunlingbot #填入你的用户名 Password: #仓库密码 WARNING! Your password will be stored unencrypted in /home/max-studio/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded #登录成功
在推送镜像之前,还需要为镜像 打标签,这是因为 Docker 在镜像推送的时候需要以下信息:
- Registry (镜像仓库服务)
- Repository (镜像仓库)
- Tag (镜像标签)
我们无需为 Registry
和Tag
指定值,如果没有为其指定值,Docker 会默认 Registry=docker.io(DNS 地址)
和Tag=latest(标签)
,但是 Repository
并没有默认值,而是从 REPOSITORY
属性值获取。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
web latest c862461d9b35 10 minutes ago 71.5MB
可以看到,镜像仓库的名称是 web
,这意味着执行docker image push
命令之后,会尝试将镜像推送至 docker.io/web:latest
的仓库中,但其实 sunlingbot
这个用户并没有 web
这个镜像仓库的访问权限,所以只能尝试推送到 sunlingbot
这个二级命名空间(namespace)之下。因此需要使用 sunlingbot
这个ID
,为当前镜像重新打一个标签。打标签的语法为:docker image tag <current-tag> <new-tag>
$ docker image tag web:latest sunlingbot/web:latest #打标签
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
web latest c862461d9b35 10 minutes ago 71.5MB
sunlingbot/web latest c862461d9b35 2 hours ago 71.5MB
现在开始推送镜像docker image push <current-tag>
:
$ docker image push sunlingbot/web:latest #推送镜像
The push refers to repository [docker.io/sunlingbot/web]
c730653e9d1d: Pushed
9162483c7edc: Pushed
71a8faec6d7a: Pushed
77cae8ab23bf: Mounted from library/alpine
latest: digest: sha256:dd7e83fcf7b416b259fbcb619d82425e23d42690af55fa981086d87dac484d33 size: 1160
-
运行应用程序
运行构建镜像的部署应用,只需要执行
docker container run
命令运行即可:启动一个名为test
的容器,-p
参数表示将容器8080
端口映射到 docker 主机的5000
端口,-d
参数表示让应用程序以守护线程的方式在后台运行。执行docker container ls
命令可以观察当前容器正在运行中!$ docker container run -d --name test \ #启动容器 > -p 5000:8080 \ #端口映射 <local-port>:<container-port> > sunlingbot/web:latest #标签 $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS 6ae07f5218d4 sunlingbot/web:latest "node ./app.js" 7 seconds ago Up 7 seconds PORTS NAMES 0.0.0.0:5000->8080/tcp test
- 部署应用测试
在浏览器输入 docker 主机的
ip:port
形式,观察到以下画面证明容器内的应用已经成功运行!