docker-compose自动部署项目(代码已在github开源)

13,555 阅读7分钟

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

docker-compose自动部署太爽啦!!

image.png 正好最近比较休闲,潦草的过了一遍docker,于是突发奇想 把自己的毕设系统利用docker-compose进行一键自动部署,尝尝鲜~~ 本文基于该项目,这是github地址!

What’s docker

Docker 是一个 开源 的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的 镜像 中,然后发布到任何流行的 Linux 或 Windows 机器上,可以实现 虚拟化。容器是完全使用 沙箱 机制,相互之间不会有任何接口。

What‘s docker-compose

docker-compose是基于 docker 的开源项目,托管于github上,调用docker服务的API实现对docker容器集群的快速编排,即通过一个单独的yaml文件,来定义一组相关的容器来为一个项目服务。

正篇

首先没有安装docker的同学需要先安装,这里建议大家下载个docker desktop,它会帮助大家一键安装docker环境,能够通过界面操控镜像容器,还支持Win,Mac等操作系统,总之对新人非常的友好下载地址

编写Dockerfile文件

dockerfile文件没有后缀名,文件命名就是 Dockerfile,内容如下

接下里会一一讲解内容的步骤以及过程

# 1.拉取node镜像
FROM node:14.17.6

# 2.更换当前目录为 /usr/local/workspace
WORKDIR /usr/local/workspace

# 3.复制宿主机 yuecaimasyer 目录下的文件到镜像当前目录下,即/usr/local/workspace
COPY ./yuecaimaster .

# 4.更换当前目录为 /usr/local/workspace/web
WORKDIR /usr/local/workspace/web

# 5.执行三个命令
RUN yarn install \ 
    && yarn build \
    && cp -r ./dist ../api/public
    
# 6.更换当前工作目录为 /usr/local/workspace/api
WORKDIR /usr/local/workspace/api

# 7.执行命令 yarn install 
RUN yarn install

# 8.暴露端口
EXPOSE 3100

# 9.启动容器时候执行的命令
CMD [ "yarn","serve" ]

step1. 首先是拉取镜像 FROM ,定制的镜像都是基于 FROM 的镜像,这里的 node 就是定制需要的基础镜像,后续的操作都是基于 node.

step2. WORKDIR /usr/local/workspace 这行代码的意思就相当于 cd /usr/local/workspace,更换当前的工作路径.

step3. COPY ./yuecaimaster . 把宿主机(您当前的主机)的当前工作路径下的yuecaimaster目录下的文件复制到 docker镜像的 /usr/local/workspace下,因为step2我们指定过docker的工作路径为 /usr/local/workspace

image.png

step4. 与setp 2 等同

step5. RUN用于执行命令、 注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如下面,docker会创建三层:

RUN yarn install 
RUN yarn build 
RUN cp -r ./dist ../api/public

于是为了避免以上现象,我们可以替换成一行命令 用 && 衔接,只需创建一层:

RUN yarn install \ 
    && yarn build \
    && cp -r ./dist ../api/public

step6, step7略过

step8. EXPOSE 3100 仅仅只是声明端口,这个端口不会被暴露给宿主机,需要在启动镜像的时候,也就是 docker run -P 时,docker容器端口3100会随机映射到宿主机的某个端口上。

step9. CMD [ "yarn","serve" ] 当我们启动一个新的容器的时候,容器会自动执行 yarn serve 命令,CMD 命令与 RUN 命令类似,但是CMD 是在容器启动阶段 即docker run -P 执行 ,RUN是在构建docker镜像的时候执行命令 即 docker build -t mydocker .,RUN和CMD执行的阶段完全不同.

然后我们执行 docker build -t mtdocker . 命令

image.png 执行docker images 查看我们的docker镜像

image.png 如果您已下载docker desktop 我们可以直接通过它来启动

image.png 也可以通过命令启动 docker run -P mydocker(随机映射端口给宿主机,docker run -p [宿主机端口号]:[镜像端口号] mydocker ,可自定义映射端口)

image.png 此时我们的镜像就启动好了,可以通过 docker ps -a 查看所有的容器

这样子构成的镜像看似完美,但还是缺少了某些服务,比如数据库,我们还需要另外的Dockerfile创建镜像来放数据库吗?答案是 YES. 但更好的方法去创建,请往下看

编写 docker-compose.yml

docker-compose陈述它是对容器集群的快速编排,同来yml文件来定义一组容器为项目服务。上文很显然我们还缺少一个数据库服务。开始编写yml文件

version: '2'

# 定义自己的docker网络 ,名字叫做mynet1,配置网段方便分配指定的IP
networks:
  mynet1:
    ipam:
      config:
        - subnet: 172.30.0.0/16
# 服务项目
services:
  web:
    # 容器名
    container_name: myweb
    
    # 依靠当前文件下的Dockerfile文件构建镜像
    build: .
    
    # 暴露镜像内部3100端口给宿主机3100端口
    ports:
      - "3100:3100"
      
    # 依赖数据库服务,需先等待数据库服务健康启动,在执行web服务
    depends_on:
      db:
        condition: service_healthy
        
    # 指定 mynet1 网络,指定ip地址
    networks:
      mynet1:
        ipv4_address: 172.30.0.2
  db:
    container_name: mysql
    # 构建镜像为mysql:5.7,会自动从docker.io上拉取镜像
    image: mysql:5.7
    
    # 当启动失败时 自动重启
    restart: "always"
    
    # 配置mysql环境,定义mysql用户,创建root密码,即可以通过 mysql -uroot -p123456登录
    environment:
      MYSQL_ROOT_HOST: "%"
      MYSQL_ROOT_PASSWORD: "123456"
      MYSQL_USER: "admin"
      MYSQL_PASS: "123456"
      # 创建数据库 名 yuecaidb
      MYSQL_DATABASE: "yuecaidb"
      
    # 这里我们可以暴露3306端口给宿主机
    ports:
      - "3306:3306"
      
    # 每隔3秒检查服务是否健康
    healthcheck:
      test: "/usr/bin/mysql --user=root --password=123456 --execute \"SHOW DATABASES;\""
      interval: 3s
      timeout: 1s
      retries: 5
    networks:
      mynet1:
        ipv4_address: 172.30.0.3

yml定义两个分别为 web和db 的服务,web服务直接利用上面的Dockerfile去构建镜像,然后暴露指定的端口给宿主机,db服务则是选择拉取镜像里的镜像,配置好参数方便我们的web服务连接数据库。

执行 docker-compose up -d

等待四五分钟会出现如下界面(我本地已经创建过镜像,所以它会自动帮我跳过构建镜像的过程)

image.png

通过docker desktop可以看到我们创建的容器集群,打开localhost:3100就可以访问到服务了

image.png

有兴趣的可以先去github把项目克隆下来试一下

槽点

这句话的大概意思就是告诉你 depends_on 不会让你的服务等待另外一个服务完全启动即ready阶段,而是running阶段,意味着如果serviceA依赖serviceB,depends_on不能保障serviceB比serviceA先进入ready阶段。

depends_on does not wait for db and redis to be “ready” 
before starting web - only until they have been started. 
If you need to wait for a service to be ready, see Controlling 
startup order for more on this problem and strategies for solving it.

web服务如果比数据库服务先启动,会出现连接失败的问题。这里我选择的解决方法是通过healthcheck检查数据库服务是否健康启动,让depend_on依赖数据库的状态service_healthy,然后再启动服务。网上也有比较好的开源工具 wait for,使用起来会更加复杂一些。

尾声

自己这一周在通过官网和社区文献的学习过程中比较痛苦,许多好的文献不是过于深奥就是没上下文,似乎对初学者不太友好(也许是自己领悟不高😣)!文章的目的也很单纯,利用所学的知识通过实践的方式分享给刚入坑的新人,希望对您们能有所帮助。如果文章有误希望能帮助我指出。

如果您有任何疑问,希望您不要吝啬于言语,社区会帮您解决问题!