# Docker

# Docker 简介

# Docker 镜像

是一种 UnionFS(联合文件系统),是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下.

镜像同时也很小,因为我们 pull 的镜像剔除了无用的东西,只是一个精简功能版的镜像

下载是一层一层下载,这样便于共享资源。

# Docker 容器数据卷

卷就是目录或文件,存在于一个或多个容器中,由 docker 挂载到容器,但不属于联合文件系统,因此能够绕过 Union File System 提供一些用于持续存储或共享数据的特性,卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会在容器删除时删除其挂载的数据卷。

dockerfile 也就是添加 容器数据卷的一种方式

# dockerfile

FROM        #基础镜像,当前新镜像是基于哪个镜像的
MAINTAINER    #镜像维护者的姓名和邮箱地址
RUN        #容器构建时需要运行的命令
EXPOSE        #当前容器对外暴露出的端口
WORKDIR        #指定在创建容器后,终端默认登陆的进来工作目录
ENV        #用来在构建镜像过程中设置环境变量
ADD        #将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
COPY        #类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置
VOLUME        #容器数据卷,用于数据保存和持久化工作
CMD        #指定一个容器启动时要运行的命令,Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
ENTRYPOINT     #指定一个容器启动时要运行的命令,ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数
ONBUILD        #当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后父镜像的onbuild被触发

例:

#Dockerfile
#制定node镜像的版本
FROM node:10-alpine
#移动当前目录下面的文件到app目录下
ADD . /app/
#进入到app目录下面,类似cd
WORKDIR /app
#安装依赖
RUN npm install
#对外暴露的端口
EXPOSE 3000
#程序启动脚本
CMD ["node", "app.js"]

# 优势

特性 容器 虚拟机
启动 秒级 分钟级
硬盘使用 一般为 MB 一般为 GB
性能 接近原生 弱于
系统支持量 单机支持上千个容器 一般几十个

# 流程整理

docker search nginx
docker pull nginx
docker images
docker run -it -p 8080:8080 nginx
docker ps
docker stop ff6

# Docker 安装

# apt升级
sudo apt-get update

# 添加相关软件包
apt-get install apt-transport-https
apt-get install ca-certificates curl software-properties-common

# 下载软件包的合法性,需要添加软件源的 GPG 密钥
curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add

# source.list 中添加 Docker 软件源
sudo add-apt-repository
"deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu  
$(lsb_release -cs)   
stable"

# 安装 Docker CE
sudo apt-get update
sudo apt-get install docker-ce

# Helloworld测试
docker run hello-world

# 安装 node

# 安装nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash

# 将nvm作为环境变量
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

#安装最新版 node
nvm install node

#安装pm2
npm i pm2 -g

# 自动化部署 CI/CD

# dockerfile
# build stage
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# production stage
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

逐行解析配置:

  • FROM node:lts-alpine as build-stage:基于 node lts-alpine 版本镜像,并通过构建阶段命名,将有 node 环境的阶段命名为 build-stage(包含 alpine 的镜像版本相比于 latest 版本更加小巧,更适合作为 docker 镜像使用)
  • WORKDIR /app:将工作区设为 /app,和其他系统文件隔离
  • COPY package*.json ./:拷贝 package.json/package-lock.json 到容器的 /app 目录
  • RUN npm install:运行 npm install 在容器中安装依赖
  • COPY . .:拷贝其他文件到容器 /app 目录,分两次拷贝是因为保持 node_modules 一致
  • RUN npm run build:运行 npm run build 在容器中构建

# docker 优化

# 利用镜像缓存

相对于项目的源文件来讲,package.json 是相对稳定的。如果没有新的安装包需要下载,则再次构建镜像时,无需重新构建依赖。则可以在 npm install 上节省一半的时间。

对于 ADD 来讲,如果需要添加的文件内容的 checksum 没有发生变化,则可以利用缓存。把 package.json/package-lock.json 与源文件分隔开写入镜像是一个很好的选择。目前,如果没有新的安装包更新的话,可以节省一半时间

...
# 首次添加此两个文件,充分利用缓存
ADD package.json package-lock.json /code
RUN npm install --production

...

# 减少 npm install 时间

  1. 选择时延低的 registry,需要企业技术基础建设支持
npm config set registry https://registry-npm.shanyue.tech/
  1. NODE_ENV=production,只安装生产环境必要的包(如果 dep 与 devDep 没有仔细分割开来,工作量很大,可以放弃)
  2. CI=true,npm 会在此环境变量下自动优化
  3. 结合 CI 的缓存功能,充分利用 npm cache
install:
  - npm ci
# keep the npm cache around to speed up installs
cache:
  directories:
    - "$HOME/.npm"
  1. 使用 npm ci 代替 npm i,既提升速度又保障应用安全性
npm ci

# 多阶段构建

得益于缓存,现在镜像构建时间已经快了不少。但是,此时镜像的体积依旧过于庞大,这也将会导致部署时间的加长。原因如下

考虑下每次 CI/CD 部署的流程

  1. 在构建服务器 (Runer) 构建镜像
  2. 把镜像推至镜像仓库服务器
  3. 在生产服务器拉取镜像,启动容器

# Docker Compose

Docker Compose 是 docker 提供的一个命令行工具,用来定义和运行由多个容器组成的应用。

使用 compose,我们可以通过 YAML 文件声明式的定义应用程序的各个服务,并由单个命令完成应用的创建和启动。

version: "3.1"
services:
  nginx:
    restart: always
    # image是指定服务的镜像名称或镜像ID。如果镜像在本地不存在,Compose将会尝试拉取镜像。
    image: nginx
    ports:
      - 8091:80
    #挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER]格式,或者使用[HOST:CONTAINER:ro]格式,后者对于容器来说,数据卷是只读的,可以有效保护宿主机的文件系统。
    volumes:
      - ./nginx/conf.d/:/etc/nginx/conf.d
      - ./frontend/dist/:/var/www/html/
      - ./static/:/static/
  app-pm2:
    container_name: app-pm2
    #构建容器
    #服务除了可以基于指定的镜像,还可以基于一份Dockerfile,在使用up启动时执行构建任务,构建标签是build,可以指定Dockerfile所在文件夹的路径。Compose将会利用Dockerfile自动构建镜像,然后使用镜像启动服务容器。
    build: ./backend
    ports:
      - "3000:3000"

# 启动

docker-compose up
// or
docker-compose up -d

# 配置镜像

{
  "registry-mirrors": [
"https://registry.docker-cn.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"http://dockerhub.azk8s.cn/" ],
  "insecure-registries": [],
  "debug": true,
  "experimental": false
}

# docker 常用命令

# 镜像命令

docker images                    #查看当前Docker中的镜像
docker search 某个镜像名字                #查询某个镜像
docker pull 某个镜像名字                #拉取(下载)某个镜像
docker rmi 某个镜像名字ID                 #删除某个镜像

# 容器基本命令(切记,有镜像才能创建容器)

docker run [OPTIONS] image [COMMAND] [ARG...]   #新建并启动容器
docker ps [OPTIONS]                             #列出当前所有正在运行的容器
exit                        #容器停止退出
ctrl+P+Q                    #容器不停止退出
docker start 容器ID或者容器名            #启动容器
docker restart 容器ID或者容器名            #重启容器
docker stop 容器ID或者容器名            #停止容器
docker kill 容器ID或者容器名            #强制停止容器
docker rm 容器ID                    #删除已停止的容器
docker rm -f $(docker ps -a -q)            #一次性删除多个容器
docker ps -a -q | xargs docker rm        #一次性删除多个容器

# 容器重要命令

docker run -d 容器名                #启动守护式容器
docker logs -f -t --tail 容器ID            #查看容器日志,-t是加入时间戳,-f是最新的日志打印,--tail数字显示最后多少条

docker top 容器ID                #查看容器内运行的进程
docker inspect 容器ID                #查看容器内部细节
docker exec -it 容器ID bashShell            #进入正在运行的容器并以命令行交互
docker attach 容器ID                #重新进入Docker容器
docker cp  容器ID:容器内路径 目的主机路径        #从容器内拷贝文件到主机上

# docker 命令帮助

Commands:
    attach    Attach to a running container                 # 当前 shell 下 attach 连接指定运行镜像
    build     Build an image from a Dockerfile              # 通过 Dockerfile 定制镜像
    commit    Create a new image from a container's changes # 提交当前容器为新的镜像
    cp        Copy files/folders from the containers filesystem to the host path
              # 从容器中拷贝指定文件或者目录到宿主机中
    create    Create a new container                        # 创建一个新的容器,同 run,但不启动容器
    diff      Inspect changes on a container's filesystem   # 查看 docker 容器变化
    events    Get real time events from the server          # 从 docker 服务获取容器实时事件
    exec      Run a command in an existing container        # 在已存在的容器上运行命令
    export    Stream the contents of a container as a tar archive
              # 导出容器的内容流作为一个 tar 归档文件[对应 import ]
    history   Show the history of an image                  # 展示一个镜像形成历史
    images    List images                                   # 列出系统当前镜像
    import    Create a new filesystem image from the contents of a tarball
              # 从tar包中的内容创建一个新的文件系统映像[对应 export]
    info      Display system-wide information               # 显示系统相关信息
    inspect   Return low-level information on a container   # 查看容器详细信息
    kill      Kill a running container                      # kill 指定 docker 容器
    load      Load an image from a tar archive              # 从一个 tar 包中加载一个镜像[对应 save]
    login     Register or Login to the docker registry server
              # 注册或者登陆一个 docker 源服务器
    logout    Log out from a Docker registry server         # 从当前 Docker registry 退出
    logs      Fetch the logs of a container                 # 输出当前容器日志信息
    port      Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
              # 查看映射端口对应的容器内部源端口
    pause     Pause all processes within a container        # 暂停容器
    ps        List containers                               # 列出容器列表
    pull      Pull an image or a repository from the docker registry server
              # 从docker镜像源服务器拉取指定镜像或者库镜像
    push      Push an image or a repository to the docker registry server
              # 推送指定镜像或者库镜像至docker源服务器
    restart   Restart a running container                   # 重启运行的容器
    rm        Remove one or more containers                 # 移除一个或者多个容器
    rmi       Remove one or more images
              # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]
    run       Run a command in a new container
              # 创建一个新的容器并运行一个命令
    save      Save an image to a tar archive                # 保存一个镜像为一个 tar 包[对应 load]
    search    Search for an image on the Docker Hub         # 在 docker hub 中搜索镜像
    start     Start a stopped containers                    # 启动容器
    stop      Stop a running containers                     # 停止容器
    tag       Tag an image into a repository                # 给源中镜像打标签
    top       Lookup the running processes of a container   # 查看容器中运行的进程信息
    unpause   Unpause a paused container                    # 取消暂停容器
    version   Show the docker version information           # 查看 docker 版本号
    wait      Block until a container stops, then print its exit code
              # 截取容器停止时的退出状态值
Run 'docker COMMAND --help' for more information on a command.

# docker options

Usage of docker:
  --api-enable-cors=false                Enable CORS headers in the remote API                      # 远程 API 中开启 CORS 头
  -b, --bridge=""                        Attach containers to a pre-existing network bridge         # 桥接网络
                                           use 'none' to disable container networking
  --bip=""                               Use this CIDR notation address for the network bridge's IP, not compatible with -b
                                         # 和 -b 选项不兼容,具体没有测试过
  -d, --daemon=false                     Enable daemon mode                                         # daemon 模式
  -D, --debug=false                      Enable debug mode                                          # debug 模式
  --dns=[]                               Force docker to use specific DNS servers                   # 强制 docker 使用指定 dns 服务器
  --dns-search=[]                        Force Docker to use specific DNS search domains            # 强制 docker 使用指定 dns 搜索域
  -e, --exec-driver="native"             Force the docker runtime to use a specific exec driver     # 强制 docker 运行时使用指定执行驱动器
  --fixed-cidr=""                        IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)
                                           this subnet must be nested in the bridge subnet (which is defined by -b or --bip)
  -G, --group="docker"                   Group to assign the unix socket specified by -H when running in daemon mode
                                           use '' (the empty string) to disable setting of a group
  -g, --graph="/var/lib/docker"          Path to use as the root of the docker runtime              # 容器运行的根目录路径
  -H, --host=[]                          The socket(s) to bind to in daemon mode                    # daemon 模式下 docker 指定绑定方式[tcp or 本地 socket]
                                           specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
  --icc=true                             Enable inter-container communication                       # 跨容器通信
  --insecure-registry=[]                 Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16)
  --ip="0.0.0.0"                         Default IP address to use when binding container ports     # 指定监听地址,默认所有 ip
  --ip-forward=true                      Enable net.ipv4.ip_forward                                 # 开启转发
  --ip-masq=true                         Enable IP masquerading for bridge's IP range
  --iptables=true                        Enable Docker's addition of iptables rules                 # 添加对应 iptables 规则
  --mtu=0                                Set the containers network MTU                             # 设置网络 mtu
                                           if no value is provided: default to the default route MTU or 1500 if no default route is available
  -p, --pidfile="/var/run/docker.pid"    Path to use for daemon PID file                            # 指定 pid 文件位置
  --registry-mirror=[]                   Specify a preferred Docker registry mirror
  -s, --storage-driver=""                Force the docker runtime to use a specific storage driver  # 强制 docker 运行时使用指定存储驱动
  --selinux-enabled=false                Enable selinux support                                     # 开启 selinux 支持
  --storage-opt=[]                       Set storage driver options                                 # 设置存储驱动选项
  --tls=false                            Use TLS; implied by tls-verify flags                       # 开启 tls
  --tlscacert="/root/.docker/ca.pem"     Trust only remotes providing a certificate signed by the CA given here
  --tlscert="/root/.docker/cert.pem"     Path to TLS certificate file                               # tls 证书文件位置
  --tlskey="/root/.docker/key.pem"       Path to TLS key file                                       # tls key 文件位置
  --tlsverify=false                      Use TLS and verify the remote (daemon: verify client, client: verify daemon) # 使用 tls 并确认远程控制主机
  -v, --version=false                    Print version information and quit                         # 输出 docker 版本信息
Last Updated: 12/22/2022, 9:53:26 AM