什么是Docker

以容器的方式构建运行分享应用程序。
容器化:以容器来部署应用程序。灵活、轻量、易用、轻耦合、稳定、安全。

images and containers

容器是运行中的进程,封装的功能,隔离其他容器。每个独立的容器拥有私有的文件系统,由docker image提供,一个镜像包含了所有程序运行所需,程序或二进制文件、运行时、以来及其他文件系统对象。

containers and virtual machines

容器原生的运行在Linux,与其他容器共享主机的核心。运行一个离散程序,相较于其他可执行程序不需要额外的内存,使得更轻量。相比而言,一个虚拟机运行一个成熟的来宾操作系统,通过管理程序虚拟访问主机资源。虚拟机所带来的大量开销远超过程序逻辑本身(VMs incur a lot of overhead beyond what is being consumed by your application logic)。

如何安装docker环境

  1. 下载docker应用程序
  2. 检查版本

    docker —version

  3. 安装hello world

    docker run hello-world docker image ls docker ps —all

构建并运行容器程序

开发工作流:
创建并测试程序各部分的独立容器->组装容器并提供内部构件使得形成完整应用程序->测试、分享、部署

示例程序node-bulletin-board

git clone xxx cd xxxx // define a container with Dockerfile docker build —tag xxxname:1.0 . // —publish : 要求docker将主机xxx端口的传入流量转发到容器的xxx(原因:容器有自己的端口;默认策略下防火墙不允许网络流量访问容器) // —detach : 要求docker后台运行此容器 // —name : 声明一个名称用来在后续命令中引用此容器 docker run —publish 8000:8080 —detach —name bb bulletinboard:1.0 // visit // —force == stop and rm docker rm —force bb

Dockerfile

Use the official image as a parent image.

FROM node:current-slim

Set the working directory.

WORKDIR /usr/src/app

Copy the file from your host to your current location.

COPY package.json .

Run the command inside your image filesystem.

RUN npm install

Inform Docker that the container is listening on the specified port at runtime.

EXPOSE 8080

Run the specified command within the container.

CMD [ “npm”, “start” ]

Copy the rest of your app’s source code from your host to your image filesystem.

COPY . .

Share images

  1. set up docker hub account
  2. create docker hub repository and push image

    最佳实践

    减小镜像体积

    小体积的镜像能够快速从网络拉取以及启动容器或服务时快速加载到内存中。
  • 选取合适的基础镜像,例如需要JDK的就以openjdk作为基础镜像,而不是ubuntu镜像再在Dockerfile中下载openjdk
  • 使用多级构建,减少镜像的层级。通过多个语句写在一个RUN命令中, 用&&符号链接即可
  • 如果又多个镜像大部分相同,可以考虑创建私有的基础镜像来共享组件,基于唯一镜像使用。docker只需要加载公共镜像一次,缓存起来。
  • 为了使调试镜像体积小,使用正式镜像作为基础镜像给调试镜像,额外的测试和debug工具能作为附加添加到正式镜像的顶层。
  • 使用有意义的tag标明版本信息,预定目的(正式、测试等),稳定性或其他有用信息来部署不同的环境,而不是依赖自动生成的latest tag。

    持久化应用数据

  • 避免以来容器可写的存储驱动,这会使得容器体积增加,从IO的角度说,比如使用卷或绑定挂载更高效。

  • 适合使用绑定挂载的情况是在开发阶段,当你想挂载你的资源目录或者容器中生成的二进制文件。生产环境下,应该使用卷来替代。
  • 对生产而言,通过服务使用secrets来存储敏感应用数据,通过配置作为非敏感数据,例如配置文件。如果当前使用的是独立容器,可以考虑迁移成单副本服务,这样就可以仅当作功能来提升服务。

    使用CI/CD来测试和部署

  • 当你检查到代码更改或创建一个拉去请求,使用docker hub或另外的CI/CD管道来自动化构建和标志一个docker镜像并测试。

  • 要求你的开发、测试、质量保证团队做镜像签名在部署到正式环境之前,通过这种方式,在镜像被部署到正式环境前,他将通过测试并且被签发。

    开发与正式环境的不同

    | 开发环境 | 正式环境 | | —- | —- | | 使用绑定挂载使容器可访问资源代码 | 使用卷存储容器数据 | | 使用Docker desktop在Mac或Windows | 使用Docker Engine,可以使用userns mapping更好的隔离Docker 进程和主机进程 | | 不用担心时间差 | 总是运行一个NTP客户端在docker主机,每个容器进程都和同一个NTP服务同步,如果使用swarm服务,确保各个docker节点同步相同时间。 |

构建镜像

Dockerfile最佳实践

docker 通过Dockerfile读取指令,自动构建镜像。Dockerfile是一个包含了所有命令的文本文件,用于构建给定的镜像。Dockerfile遵循特定的格式和指令集,可以通过reference中找到。
docker镜像由只读层构成,各只读层对应了Dockerfile的指令。各层堆叠在一起,每层是上一层的变化增量。
命令含义:

  • FROM // 确定基础镜像
  • COPY // 添加文件到你的docker client 目录下
  • RUN // 构建你的应用
  • CMD // 标识在容器中运行的指令

运行一个镜像生成容器,等同于添加了一个可写的层在底层层之上。所有的变化用来运行容器,例如写新文件、修改已存在文件、或删除,都是写这个可写的容器层

一般准则与建议

OpenJDK 开发实践

OpenJDK:免费开源的JavaSE实现,自JavaSE7后官方参考。

使用

创建一个Java实例

最直接使用镜像的方式是:使用一个Java容器用于构建和运行时环境,在你的Dockerfile中,使用如下命令来编译并运行你的项目:

FROM openjdk:7 COPY . /user/src/myapp WORKDIR /usr/src/myapp RUN javac Main.java CMD [“java”, “Main”]

构建镜像并运行

docker build -t my-java-app docker run -it —rm —name myrunning-app my-java-app

在Docker镜像中编译你的软件

使用容器进行编译而不再容器内部运行

docker run —rm -v “$PWD”: /usr/src/myapp -w /usr/src/myapp openjdk:7 javac Main.java

这将把当前目录作为当前容器的一个卷,将工作目录设置为这个卷,运行javac Main.java 命令,将会编译Main.java,并把生成的Java class文件输出到卷。

设置JVM CPU & RAM限制

容器启动JVM时尝试检查可用的CPU核心数及RAM来调整内部参数。当容器使用了设限的CPU/RAM,然后使用JVM标准系统API来探测,将返回全主机的值。对于较早版本的JVM,可能导致使用过多的CPU和内存分配错误。
在Linux容器内,OpenJDK8及之后的版本能够正确检测容器限制的CPU核心数及可用RAM数量。对于目前支持的OpenJDK版本来说默认开启。
在Windows Server(无Hyper-V)容器中,CPU可用核心数限制将不会生效。手动设置生效:

start /b /wait /affinity 0x3 path/to/java.exe …

带句点的环境变量命名

部分shells命令不支持环境变量名带句点(技术上不支持POSIX),因此跳过他们而不是通过,正如bash做的。如果你的应用需要环境变量,使用CMD [“java”, …] 或者是直接用bash而不是 /bin/sh .

镜像变种

openjdk镜像具有许多特色,每种设计都有应用场景。

常用操作库

docker相关

  • docker build —tag xxx_image_name:1.0 // 根据Dockerfile文件打包生成docker镜像
  • docker image ls // 列出所有docker镜像
  • docker run -itd —name xxx_container_name xxx_image_name // 根据镜像运行某容器
  • docker run -it —rm —name xxx_container_name xxx_image_name // 根据镜像运行某容器,退出后删除
  • docker ps -a // 列出所有docker容器
  • docker exec -it xxx_container_id bash // 对正在运行的docker容器执行某项命令
    • exit
    • Ctrl + D
  • docker cp xxx_container_name:xxx_container_path host_path // 将容器中文件拷贝到宿主目录下
  • docker inspect xxx_container_id | grep “pattern” // 查看当前容器id 对应的容器信息并查找
  • docker commit xxx_container_id xxx_new_image_name // 将某个正在运行的docker容器打成镜像

    Linux容器相关

    支持ssh登录

  • apt-get update

  • apt-get install openssh-client openssh-server
  • service ssh start

建议commit以后作为基础镜像使用