Docker概述和基本命令

文章目录

  • 简介
  • 基本操作
    • Docker基本命令
    • Dockerfile构建镜像基本流程
  • 详细操作
    • Docker镜像操作
    • Docker容器操作
    • Dockerfile详解
      • Dockerfile文件内容
      • 根据Dockerfile构建镜像
      • 将镜像推送到Docker Hub
      • 根据构建的镜像启动容器
      • 总结
      • 查看构建过程
      • 生产环境的多阶段构建
      • 最佳实践
        • 构建缓存
        • 合并镜像
  • 参考

简介

Docker 是一种运行于Linux和Windows上的软件,用于创建、管理和编排容器,每个容器相当于操作系统一个进程,每个容器有自己的命名空间和文件系统根路径

基本操作

Docker基本命令

命令 作用
systemctl start docker 启动docker
systemctl stop docker 停止docker
systemctl status docker 查看docker状态,启动失败也可以查看原因
systemctl enable docker 设置docker为开机启动
docker version 查看docker版本
docker image ls 查看docker的镜像
docker container run -it ubuntu:lastest /bin/bash 从ubuntu:lastest镜像启动容器,-it开启容器交互模式并切换到容器内部,/bin/bash表示容器内运行的进程
docker container ls 查看系统内全部处于运行状态的容器
docker container exec -it cname bash 将Shell连接到运行中容器cname的bash终端
docker container stop cname 停止运行容器cname
docker container rm cname 删除容器cname,删除容器之前要先停止运行容器
docker container ls -a 查看系统内全部容器包括运行中和停止的

Dockerfile构建镜像基本流程

FROM alpine
LABEL maintainer="[email protected]"
RUN apk add --update nodejs nodejs-npm
COPY . /src
WORKDIR /src
RUN npm install
EXPOSE 8080
ENTRYPOINT ["node", "./app.js"]
docker image build -t test:latest .

这行命令是在Dockerfile文件同目录下运行,构建一个镜像,
镜像名为test:latest,latest是标签表示比较老的版本
docker container run -d \
--name web1 \
--publish 8080:8080 \
test:latest

根据刚刚使用Dockerfile文件构建的镜像启动容器,web1为容器名,
publish 8080:8080表示容器外部8080端口和容器内部8080端口进行映射,
test:latest是镜像名称

详细操作

Docker镜像操作

命令 作用
docker image ls 查看docker的镜像
docker image rm image-name 删除名字为image-name的镜像,删除镜像之前,必须停止所有在该镜像上启动的容器
docker image pull ubuntu:latest 从远程仓库中拉取镜像ubuntu:latest,并且是从官方仓库拉取
docker image pull nigelpoulton/ubuntu:latest 从Docker Hub账户的个人仓库nigelpoulton拉取镜像ubuntu:latest
docker image ls --filter dangling=true filter是过滤操作,dangling=true表示返回所有悬虚镜像,即没有标签的镜像,标签就是latest这些
docker image ls --filter=reference="*:lateset" 使用reference完成过滤并且仅显示标签为latest的示例
docker image ls --format "{{ .Repository }} {{ .Tag }} {{ .Size }} " 返回全部镜像,但是只显式仓库、标签和大小信息,ubuntu:latest的 ubuntu 就是仓库名,一个仓库可以存储很多同类的镜像,它们以标签进行分别
docker search ubuntu 搜索所有名字字段包含ubuntu的镜像
docker search ubuntu --filter “is-official=true” 搜索所有名字字段包含ubuntu的镜像,且只返回官方镜像
docker image inspect ubuntu:latest 查看镜像 ubuntu:latest 的分层,分层是为了把共同的部分抽取出来,节省空间,加快拉取镜像和构建镜像速度
docker image pull alpine@sha256:c0537…3c0a77c6c 根据镜像摘要拉取镜像,为了区分相同的镜像的不同版本
docker image rm 02674b9cb179 通过镜像ID删除镜像
docker image rm $(docker image ls -q) -f 快速删除Docker主机的所有镜像

Docker容器操作

命令 作用
docker container ls 查看当前系统正在运行的容器列表
docker container run -it ubuntu:lastest /bin/bash 从ubuntu:lastest镜像启动容器,-it开启容器交互模式并切换到容器内部,/bin/bash表示容器内运行的进程
docker container exec -it 3027eb644874 bash 根据容器ID将当前终端重新连接到正在运行的容器内部,bash是运行的命令,且exec会新创建一个bash进程,所以退出该bash进程不会影响到原来容器内部的bash进程
docker container stop 3027eb644874 根据容器ID停止容器
docker container rm 3027eb644874 根据容器ID删除容器,删除容器之前必须停止容器
docker container start container-name 根据容器名启动容器
docker container ls -a 查看当前系统所以的容器列表,包括运行的和停止的
docker container run --name neversaydie -it --restart always alpine sh 根据镜像 alpine创建容器, --name设置容器名字为neversaydie,–restart always 配置该容器会在故障时一直尝试重启,除非执行了上面的stop命令显式停止容器,sh是要运行的命令
docker container run -d -name webserver -p 80:8080 tomcat 根据镜像tomcat创建一个容器webserver,将容器外部的端口80和容器内部的8080端口进行映射,即直接在容器外部通过80端口就能访问到容器内部的8080端口
docker container rm $(docker container ls -aq) -f 快速删除Docker主机的所有容器,-a表示把运行和停止的全部选择,-f表示force强制删除

Dockerfile详解

Dockerfile文件内容

FROM alpine
LABEL maintainer="[email protected]"
RUN apk add --update nodejs nodejs-npm
COPY . /src
WORKDIR /src
RUN npm install
EXPOSE 8080
ENTRYPOINT ["node", "./app.js"]
  1. FROM alpine 表示以alpine镜像作为当前镜像基础
  2. LABEL maintainer=“[email protected]” 指定维护者为[email protected],标签LABEL其实是一个键值对,在一个镜像中可以通过增加标签的方式来为镜像添加自定义元数据,此命令不会创建镜像层
  3. RUN apk add --update nodejs nodejs-npm 指令使用 alpine的apk包管理器将nodejs和nodejs-npm安装到当前镜像之中。RUN指令会在FROM指定的alpine基础镜像之上,新建一个镜像层来存储这些安装内容
  4. COPY . /src 指令将应用相关文件从构建上下文复制到当前镜像中,并且新建一个镜像层来存储,.(点) 指的的Dockerfile所在的当前目录,/src指的是镜像中的文件夹,即将Dockerfile所在的当前目录所有文件复制到镜像的/src文件夹中
  5. WORKDIR /src 为后续还未执行的指令指定工作目录,指定的是镜像的工作目录,即后续命令都在该工作目录下执行,此命令不会创建镜像层
  6. RUN npm install 指令会根据package.json中的配置下,使用npm来安装当前应用的相关依赖包。npm命令会在前面设置的工作目录中执行,并在镜像中新建镜像层来保存相应的依赖文件
  7. EXPOSE 8080 因为当前应用需要通过TCP端口对外提供一个Web服务,所以在Dockerfile中提供EXPOSE 8080指令来完成相应端口的设置,这个配置信息会作为镜像的元数据被保存下来,并不会产生新的镜像层
  8. ENTRYPOINT [“node”, “./app.js”] 指定了当前镜像的入口程序,就是命令 node ./app.js,ENTRYPOINT 指定的配置信息也是作为镜像的元数据被保存下来,而不是产生新的镜像层

根据Dockerfile构建镜像

docker image build -t web:latest .
  1. 一定要在命令后加一个.(点),这个表示Docker在进行构建时,使用当前目录作为构建上下文
  2. 在执行命令前,要确认当前目录是包含Dockerfile和应用代码的目录
  3. 提高这行命令,docker就根据Dockerfile将应用容器化变成一个镜像‘

将镜像推送到Docker Hub

docker login
docker image tag web:latest nigelpoulton/web:latest
docker image push nigelpoulton/web:latest
  1. docker login 是登录Docker Hub
  2. docker image tag web:latest nigelpoulton/web:latest 是给镜像打标签,Docker在镜像推送的过程中需要三个信息,Registry(镜像仓库服务),Repository(镜像仓库),Tag(镜像标签),Registry有默认值docker.io,Tag有默认值latest,但是Repository没有,打标签的时候nigelpoulton/web就是Repository,前面有nigelpoulton(Docker Hub 用户名)表示是第三方镜像,web:latest是旧标签,nigelpoulton/web:latest是新标签
  3. docker image push nigelpoulton/web:latest 将刚刚打好标签的镜像推送到Docker Hub
  4. 推送到哪个仓库根据的是 docker.io/nigelpoulton/web:latest 中的nigelpoulton(Docker Hub ID),一般只能推送到自己在DockerHub注册的仓库,所以nigelpoulton要改成自己的ID

根据构建的镜像启动容器

docker container run -d --name c1 \
-p 80:8080 \
web:latest

根据镜像web:latest创建容器c1,-d表示让容器以守护线程deamon的方式在后台运行,-p 80:8080 将主机的80端口与容器内的8080端口进行映射

总结

  1. Dockerfile中的注释行,都是以#开头的,除注释之外,每一行都是一条指令,指令的格式是 INSTRUCTION argument,指令是不区分大小写的,但通常采用大写的方式,可读性较高,部分指令会在镜像中创建新的镜像层,其他指令只会增加或修改镜像的元数据信息
  2. 新增镜像层的指令包括 FROM、RUN、COPY,而新增元数据的指令包括EXPOSE、WORKDIR、ENV以及ENTERPOINT,如果指令的作用是向镜像中添加新的文件或程序,那么这条指令就会新建镜像层,如果只是告诉Docker如何完成构建或者如何运行应用程序,那么就只会增加镜像的元数据

查看构建过程

docker image history web:latest

可以docker image history web:latest命令来查看构建镜像的过程中都执行了哪些指令

生产环境的多阶段构建

FROM node:latest AS storefront
WORKDIR /usr/src/atsea/app/react-app
COPY react-app .
RUN npm install
RUN npm run build

FROM maven:latest AS appserver
WORKDIR /usr/src/atsea
COPY pom.xml .
RUN mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
COPY . .
RUN mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests

FROM Java:8-jdk-alpine AS production
RUN adduser -Dh /home/gordon gordon
WORKDIR /static
COPY --from=storefront /usr/src/atsea/app/react-app/build/ .
WORKDIR /app
COPY --from=appserver /usr/src/atsea/target/Atsea-0.0.1-SNAPSHOT.jar .
ENTRYPOINT ["java", "-jar", "/app/Atsea-0.0.1-SNAPSHOT.jar"]
CMD ["--spring.profiles.active=postgres"]
  1. 这个Dockerfile中有3个FROM指令,每一个FROM指令构成一个单独的构建阶段,各个阶段在内部从0开始编号,也可以显式的使用AS来对每个阶段定义名字
  2. storefront 阶段拉取了大小超过600MB的node:latest镜像,然后设置了工作目录,复制一些应用代码进去,然后使用2个RUN指令执行npm操作。这会生成三个镜像层并显著增加镜像大小。指令执行结束后会得到一个比原镜像大得多的镜像,其中包含许多构建工具和少量应用程序代码
  3. appserver 阶段拉取了大小超过700MB的manven:latest镜像。然后通过两个COPY指令和2个RUN指令生成了4个镜像层。这个阶段同样会构建出一个比原镜像大得多的镜像,其中包含许多构建工具和少量应用程序代码
  4. production阶段拉取了 java:8-jdk-alpine 镜像,这个镜像大约150MB,明显小于前两个构建阶段用到的node和maven镜像。这个阶段会创建一个用户,设置工作目录,从storefront 阶段生成的镜像中复制一些应用代码过来(前端react打包后的代码)。之后,设置一个不同的工作目录,然后从appserver阶段生成的镜像中复制应用相关的代码(maven 打包的java代码),最后production设置当前应用程序为容器启动时的主程序
  5. 重点在于COPY --from 指令,它从之前的阶段构建的镜像中仅复制生产环境相关的应用代码,而不会复制生产环境不需要的构件
  6. 多阶段构件这种方式仅用到了一个Dockerfile,并且 docker image build 命令不需要增加额外参数

最佳实践

构建缓存

  1. 利用构建缓存,docker image build 命令从顶层开始解析Dockerfile中的指令并逐行执行。而对每一条指令,Docker都会检查缓存中是否已经有与该指令对应的镜像层
  2. 如果有,即为缓存命中(Cache Hit),并且会使用这个镜像层,如果没有,则是缓存未命中(Cache Miss),Docker 会基于该指令构建新的镜像层。缓存命中能显著加快构建过程
  3. 一旦有一条指令缓存未命中,则后续的整个构建过程中将不再使用缓存,所以在编写Dockerfile时需要尽量将易于发生编号的指令置于Dockerfile文件的后方执行。这意味着缓存未命中的情况将直到构建的后期才会出现
  4. 如果 docker image build 命令加入–nocache=true参数可以强制忽略对缓存的使用
  5. COPY和ADD指令执行之前会检查复制到镜像中的内容自上一次构建之后是否发生了变化,例如,有可能Dockerfile中的 COPY . /src 将应用目录的文件复制到镜像的src目录,这条指令和上次Dockerfile相比仍然没有发生变化,但是.(点)当前目录文件发生了变化,所以仍然不能使用原来的镜像缓存
  6. 为了应对这一问题,Docker会计算每一个被复制文件的Checksum只,并与缓存镜像层中同一文件的checksum进行对比。如果不匹配,那么就认为缓存无效并构建新的镜像层

合并镜像

  1. 合并镜像并非一个最佳实践,因为这种方式利弊参半
  2. 当镜像层数太多时,合并是一个不错的优化方式,当创建一个新的基础镜像是,以便基于它来构建其他镜像是,这个基础镜像最好被合并为一层
  3. 合并的镜像无法共享镜像层,会导致存储空间的低效利用(因为多层镜像层,有些底层的镜像层可以相互共享),而且push和pull操作的镜像体积会更大
  4. 执行docker image build命令时,可以通过增加 --squash参数来创建一个合并的镜像

参考

《深入浅出 Docker》

你可能感兴趣的:(docker,docker,容器,运维)