docker 初步探究及研究心得

参考:Docker 教程

Nodejs Docker 镜像体积优化实践

Dockerfile 与 docker-compose.yml 文件的作用及自动部署的实现

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

出于一些原因,本人习惯用 centos7,因此下面就以它为基础进行探究。其他版本的操作系统在菜鸟教程上都有,按部就班照做就行。

在 centos 下还是使用 yum 来安装包最方便,因此

sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io

另外再提一下镜像加速,国内访问镜像时有时会卡住,因此需要配置下镜像。

例如:

  • 网易:https://hub-mirror.c.163.com/
  • 阿里云:https://<你的 ID>.mirror.aliyuncs.com
  • 七牛云加速器:https://reg-mirror.qiniu.com

对于使用 systemd 的系统,请在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件):

{"registry-mirrors":["https://reg-mirror.qiniu.com/"]}

之后重新启动服务:

sudo systemctl daemon-reload
sudo systemctl restart docker

Dockerfile 的使用

下面就结合本人自己的体验来说明下 docker 的使用。

docker 以 Dockerfile 为配置,所以需要在根目录下新建一个 Dockerfile 文件,一个简单的例子如下

# Dockerfile
FROM node  #意思是基于node这个镜像进行开发

COPY . /home/app    #将当前项目的文件复制到/home/app

RUN cd /home/app && npm install #安装依赖

WORKDIR /home/app #设置工作目录

CMD ['npm', 'start'] #运行

在项目根目录执行

docker build -t myapp .
docker images

应该就能看见一个 myapp 镜像了

但直接基于node镜像进行开发是非常不推荐的,只要实际操作过的人就会发现node这个镜像的大小有 918MB,可以说大的离谱,在生产环境中是绝对不能用的

因此需要进行优化,最简单的办法就是选择一个更小的镜像,例如 node:alpine,这个镜像的大小只有 100MB,整整减少了800MB,这是一个非常大的优化。

但是还可以更小

  1. npm 只安装生产环境依赖,即npm install --production

  2. 使用基础版本的 Alpine 镜像自行安装 Nodejs,即

      FROM alpine:latest
    
      RUN apk add --no-cache --update nodejs nodejs-npm
    
      COPY . /home/app
    
      RUN cd /home/app && npm install --production
    
      WORKDIR /home/app
    
      CMD ['npm', 'start']
    
  3. 使用脚本来清除无关文件

    这个脚本就是清除掉 node_modules 中的一些无关文件,都是些开发环境会用到的东西,但在生产环境就是累赘了。

    #!/bin/bash
    find node_modules -type f | grep -E "(.idea|.vscode|benchmark.js|.eslintrc.js|changelog|CHANGELOG|AUTHORS|license|LICENSE|LICENCE|.travis.yml|.eslintrc.json|.eslintrc.yml|Makefile|.npmignore|.DS_Store|.jshintrc|.eslintrc.BSD|.editorconfig|tsconfig.json|.coveralls.yml|appveyor.yml|.gitattributes|.eslintignore|.eslintrc|.eslintignore.BSD|.babelrc|Thumbs.db|Jenkinsfile|Gulpfile|Gruntfile|gulpfile|gruntfile|.tern-project|.stylelintrc|stylelint.config.js|.stylelintrc.json|.stylelintrc.yml|.stylelintrc.yaml|.stylelintrc.js|htmllint.js|.npmrc|.flowconfig|.documentup.json|.yarn-metadata.json|.gitlab-ci.yml|circle.yml|CHANGES|.yarn-integrity|.yarnclean|.yo-rc.json|jest.config.js|karma.conf.js|wallaby.js|wallaby.conf.js|.prettierrc|.prettierrc.yml|.prettierrc.yaml|.prettierrc.toml|.prettierrc.js|.prettierrc.json|prettierrc.config.js|tslint.json)" | xargs rm -rf;
    find node_modules -type f | grep -E "\.(md|mdon|markdown|log|ts|swp|jst|coffee|txt|BSD|m?js.map)$" | xargs rm -f;
    find node_modules -type d | grep -E "(examples|example|.github|@types)" | xargs rm -rf;
    

最后给大家看看我自己写的 Dockerfile,和网上的有所不同,针对大陆服务器进行了优化

FROM alpine:latest
RUN echo "http://mirrors.aliyun.com/alpine/v3.11/main/" > /etc/apk/repositories && echo "http://mirrors.aliyun.com/alpine/v3.11/community/" >> /etc/apk/repositories && apk update && apk add --no-cache --update "nodejs=12.15.0-r1" nodejs-npm #使用阿里云镜像加速,锁定nodejs版本以免日后版本升级出现问题,生产环境建议锁定版本

ENV NODE_ENV production

WORKDIR /home/app

COPY package.json clean-nm.sh /home/app/

RUN npm install --production --registry=https://registry.npm.taobao.org && sh ./clean-nm.sh

COPY . /home/app

EXPOSE 8080

CMD ["npm", "start"]

docker-compose.yml 的使用

按照 docker 官方的建议,每一个容器只启动一个进程,这样便于管理和解耦。但实际生产环境中往往还会用到其他应用,例如 mongdb、redis、nginx 等等。

因此需要 docker-compose.yml 来配置多个镜像,实现集群部署

在根目录下创建并编辑 docker-compose.yaml 文件:vi docker-compose.yaml,编辑内容如下:

version: "3.6"
services:
  flask-web:
    build: .
    ports:
        - "5000:9999"
    container_name: flask-web
  redis:
    image: redis
    container_name: redis

version:版本注释,不可缺少的字段。
services:该层级下指明使用镜像开启容器的具体配置,是最主要的配置项。
flask-web、redis:自定义的该 service 名字。
build:Dockerfile 的路径,使用它来创建一个定制的镜像,或者可使用 image 指定已有镜像。
image:指定使用已有镜像。
ports:开启容器后暴露的端口映射。
container_name:指定开启容器后的容器名。

注意:docker-compose.yaml 必须按格式规范来写,最好使用两个空格来表示层级关系。每个参数前面都有一个空格。编写完后使用docker-compose config检查是否有语法错误。

大致就是如此。

下面演示一个 nodejs+redis+mongo 的例子

version: "3"

services:
    docker-test:
        build: .    #打包当前项目
        restart: always
        ports:
            - "8080:8080"    #指定端口映射
        environment: #配置环境变量
            NODE_ENV: production
            CACHE_TYPE: redis
            REDIS_URL: "redis://redis:6379/"    
            MONGO_URL: "mongodb://mongo:27017/docker"
        depends_on:
            - redis

    redis:
        image: redis:alpine #这里也建议锁版本
        restart: always
        volumes:
            - redis-data:/data
    mongo:
        image: mongo:4.2.6 #这里也建议锁版本
        restart: always
        volumes:
            - mongo-data:/data/db
volumes:
    redis-data:
    mongo-data:


评论

发表回复