Open lulujianglab opened 5 years ago
最近用 Docker 完成了 eggjs 后端项目的部署,不得不感叹,Docker 真的是太好用了。不仅能够一键安装 mysql,省去了很多搭环境的事宜,而且可以直接把项目发布到 Docker 容器上进行测试,等项目需要正式上线时,就直接把做好的 Docker 镜像部署上去就好了,省去了很多项目部署上线的风险
Docker
eggjs
mysql
Docker 是一个可以用来快速部署的轻量级虚拟技术,允许开发人员将自己的程序和运行环境一起打包,制作成一个 Docker 的 Image (镜像),然后部署到服务器上,通过下载这个 Image 就可以将程序跑起来,省去了每次都安装各种依赖和环境的麻烦
Image
操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务
Docker Registry
使用 Docker 容器部署应用快速方便,特别是应用较多时部署迁移等使用 Docker 会更方便。另外,在同一台服务器上不能同时运行多个 eggjs 应用,除非停止另外一个 eggjs 应用
更详细的介绍可以参考非官方 Docker 中文文档
为了后续更好的理解 Docker 命令的操作,我们先来大致理清下 Docker 的架构
上面这张图大致介绍了 Docker 的架构,中间是 host,也就是进行 Docker 操作的宿主机,宿主机上主要是运行 Docker Daemon 的核心程序,也就是负责做各种各样的操作,比如说下载 Docker 的镜像,比如说运行一个容器
Docker Daemon
那宿主机如何和 Docker Daemon 交互呢?实际上是通过在客户端用命令比如 build、run、put 交给 Daemon,Daemon 来做实际的操作
build
run
put
Daemon
右边的蓝的是互联网的 Sass 服务,叫做 registry,Daemon 可以和 registry 交互,比如说 push 一个 Image,拖拉一个 Image,实际上是所有 Docker 用户共享 Docker 镜像的服务
registry
push
简单来说,就是客户端和守护进程 Daemon 进行操作,把命令送给守护进程,守护进程来拖取镜像,运行容器,和远端的镜像仓库进行交互
我这里已经用 eggjs 开发了一个后端项目,然后需要构建一个镜像,然后基于这个 Image 运行一个 container。从而快速实现部署
container
大体流程
Dockerfile
Docker Image
具体操作如下
如果不知道 Dockerfile 文件怎么写,可以直接到 github 上查找 eggjs / docker,就可以看到完整的 Dockerfile 文件,直接拷贝粘贴到项目路径下即可
# 拉取要创建的新镜像的 base image(基础镜像),类似于面向对象里边的基础类 FROM node:8.11.3-alpine # 设置时区 ENV TIME_ZONE=Asia/Shanghai # 在容器内运行命令 RUN \ mkdir -p /usr/src/app \ && apk add --no-cache tzdata \ && echo "${TIME_ZONE}" > /etc/timezone \ && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime # 创建 docker 工作目录 WORKDIR /usr/src/app # 拷贝,把本机当前目录下的 package.json 拷贝到 Image 的 /usr/src/app/ 文件夹下 COPY package.json /usr/src/app/ # 使用 npm 安装 app 所需要的所有依赖 RUN npm i RUN npm i --registry=https://registry.npm.taobao.org # 拷贝本地的所有文件到路径中去 COPY . /usr/src/app # 暴露端口。如果程序是一个服务器,会监听一个或多个端口,可以用 EXPOSE 来表示这个端口 EXPOSE 7001 # 给容器指定一个执行入口 CMD npm run start
同时,我们还可以在 eggjs 的官网看到应用部署的文档,有两点需要防止踩坑的地方
PORT
以上修改都是在 package.json 文件中
Dockerfile 创建完成后,我们就可以在 Dockerfile 文件所在的目录下运行下面的 Docker 命令来构建一个 Image
docker build -t webshare-backend .
通过 -t 的参数,给它一个标签 share,然后给出一个 ., . 就是路径名 就是把这个路径底下的所有文件都送给 Docker Engine 让它来产生 Image
share
.
Docker Engine
运行完最后会出现 successfully,代表构建成功
接着我们就可以通过 Docker Images 来查看是否真的生成了这个文件
Docker Images
的确是生成了一个新的 Image, 打了一个 tag 是 latest,有一个 ImageId 和大小 size
tag
latest
ImageId
size
接着就可以运行这个 Image
docker run -d -p 7001:7001 webshare-backend
Demon
p
:
host
返回了一个容器的 id,这样就成功的用 Dockerfile 的方式来构建了一个自己的 Image
id
通过运行 docker ps 可以查看容器是否启动成功
docker ps
我们还可以用 curl localhost:7001 测试一下,会输出接口查询内容
curl localhost:7001
可以通过 docker push 命令,把自己创建的镜像上传到仓库中来共享
docker push
docker tag webshare-backend:latest 服务器ip/webshare-backend:1.0
docker login -u 账号 -p 密码 服务器IP地址
后面显示 Login Succeeded,就是登录成功了
Login Succeeded
这儿可能第一次会报 Service Unavailable,需要去根路径的 .docker 目录下的 daemon.json 里添加信任
Service Unavailable
.docker
daemon.json
docker push 服务器IP/目录名/webshare-backend:1.0
docker stop xxx (CONTAINER ID )
docker stop $(docker ps -a -q)
docker rm xxxx
docker rmi 9a52e8ccdd7a(image id)
docker rmi -f imageId
docker history webshare-backend
docker ps -a
docker logs xxx (CONTAINER ID )
docker cp src/. mycontainer:/target
Dockerfile 中的每一行都产生一个新层,存在 Image里的层是只读的(RO)
当 Image 被运行成为一个容器的时候,就会会产生一个新层,叫容器层 container layer,是可读可写的(RW)
container layer
分层的好处:如果有很多的容器和 Image,比如 A Image 有 10 层, B Image 有 7 层,他们之间可能有 5 层是共享的,那么无形之中,存储压力就会小很多
提供独立于容器之外的持久化存储
因为在容器中的改动是不会被保存的,Volume 提供了比较方便的、可以持久化存储的一个技巧,比如说运行一个数据库容器,数据库的真正数据应该是被持久化的,Volume 是可以实现的,并且还可以提供给容器之间的共享数据
Volume
最近用
Docker
完成了eggjs
后端项目的部署,不得不感叹,Docker
真的是太好用了。不仅能够一键安装mysql
,省去了很多搭环境的事宜,而且可以直接把项目发布到Docker
容器上进行测试,等项目需要正式上线时,就直接把做好的Docker
镜像部署上去就好了,省去了很多项目部署上线的风险Docker 是什么
Docker
是一个可以用来快速部署的轻量级虚拟技术,允许开发人员将自己的程序和运行环境一起打包,制作成一个Docker
的Image
(镜像),然后部署到服务器上,通过下载这个Image
就可以将程序跑起来,省去了每次都安装各种依赖和环境的麻烦Docker 镜像
操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)
Docker 容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体
仓库(Docker Registry)
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,
Docker Registry
就是这样的服务Docker 部署应用有什么优点
使用
Docker
容器部署应用快速方便,特别是应用较多时部署迁移等使用Docker
会更方便。另外,在同一台服务器上不能同时运行多个eggjs
应用,除非停止另外一个eggjs
应用更详细的介绍可以参考非官方 Docker 中文文档
Docker 的架构
为了后续更好的理解
Docker
命令的操作,我们先来大致理清下Docker
的架构上面这张图大致介绍了
Docker
的架构,中间是 host,也就是进行Docker
操作的宿主机,宿主机上主要是运行Docker Daemon
的核心程序,也就是负责做各种各样的操作,比如说下载Docker
的镜像,比如说运行一个容器那宿主机如何和
Docker Daemon
交互呢?实际上是通过在客户端用命令比如build
、run
、put
交给Daemon
,Daemon
来做实际的操作右边的蓝的是互联网的 Sass 服务,叫做
registry
,Daemon
可以和registry
交互,比如说push
一个Image
,拖拉一个Image
,实际上是所有Docker
用户共享Docker
镜像的服务简单来说,就是客户端和守护进程
Daemon
进行操作,把命令送给守护进程,守护进程来拖取镜像,运行容器,和远端的镜像仓库进行交互项目部署实践
我这里已经用
eggjs
开发了一个后端项目,然后需要构建一个镜像,然后基于这个Image
运行一个container
。从而快速实现部署大体流程
Docker
Dockerfile
文件Docker Image
container
,部署成功具体操作如下
创建 Dockerfile
如果不知道
Dockerfile
文件怎么写,可以直接到 github 上查找 eggjs / docker,就可以看到完整的 Dockerfile 文件,直接拷贝粘贴到项目路径下即可注意事项
同时,我们还可以在
eggjs
的官网看到应用部署的文档,有两点需要防止踩坑的地方Docker
已经是后台了,所以在部署的时候需要去掉 --daemon,不能运行在后台的后台Docker
里有环境变量PORT
,如果不加上,会默认使用Docker
里的环境变量,而这个变量PORT
的值是随机生成的数字,所以在正式部署的时候就会开启这个随机数生成的端口,从而报错以上修改都是在 package.json 文件中
构建 Image
Dockerfile
创建完成后,我们就可以在Dockerfile
文件所在的目录下运行下面的Docker
命令来构建一个Image
通过 -t 的参数,给它一个标签
share
,然后给出一个.
,.
就是路径名 就是把这个路径底下的所有文件都送给Docker Engine
让它来产生Image
运行完最后会出现 successfully,代表构建成功
接着我们就可以通过
Docker Images
来查看是否真的生成了这个文件的确是生成了一个新的
Image
, 打了一个tag
是latest
,有一个ImageId
和大小size
接着就可以运行这个
Image
运行镜像
Demon
守护进程,代表容器会在后台运行p
是做端口映射的,:
右边的是程序本身的端口,:
左边是本地的host
的端口,把本机的 7001 映射到container
的7001,这样外网就能通过本机的 7001 访问我们的 web 了返回了一个容器的
id
,这样就成功的用Dockerfile
的方式来构建了一个自己的Image
通过运行
docker ps
可以查看容器是否启动成功我们还可以用
curl localhost:7001
测试一下,会输出接口查询内容上传镜像
可以通过
docker push
命令,把自己创建的镜像上传到仓库中来共享latest
作为默认标签。后面显示
Login Succeeded
,就是登录成功了Docker
仓库更新应用
docker ps
命令列出运行的容器docker stop xxx (CONTAINER ID )
停止运行该容器docker stop $(docker ps -a -q)
暂停所有运行的容器docker rm xxxx
删除containerdocker rmi 9a52e8ccdd7a(image id)
删除imagedocker rmi -f imageId
其他常用命令
docker history webshare-backend
docker ps -a
docker logs xxx (CONTAINER ID )
docker cp src/. mycontainer:/target
在host
和container
之间拷贝文件镜像分层
Dockerfile
中的每一行都产生一个新层,存在Image
里的层是只读的(RO)当
Image
被运行成为一个容器的时候,就会会产生一个新层,叫容器层container layer
,是可读可写的(RW)分层的好处:如果有很多的容器和
Image
,比如 AImage
有 10 层, BImage
有 7 层,他们之间可能有 5 层是共享的,那么无形之中,存储压力就会小很多Volume( 数据卷)
提供独立于容器之外的持久化存储
因为在容器中的改动是不会被保存的,
Volume
提供了比较方便的、可以持久化存储的一个技巧,比如说运行一个数据库容器,数据库的真正数据应该是被持久化的,Volume
是可以实现的,并且还可以提供给容器之间的共享数据